When creating a new project, have the possibility to select another project to duplicate
This commit is contained in:
parent
1500ff92b2
commit
fc21d3873e
|
|
@ -10,6 +10,7 @@ Breaking changes:
|
|||
|
||||
New features:
|
||||
|
||||
* When creating a new project, have the possibility to select another project to duplicate
|
||||
* Add a "Me" button to assignee form element
|
||||
* Add external links for tasks with plugin api
|
||||
* Add project owner (Directly Responsible Individual)
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -1,6 +1,6 @@
|
|||
BUILD_DIR = /tmp
|
||||
|
||||
CSS_APP = $(addprefix assets/css/src/, $(addsuffix .css, base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown screenshot filters gantt))
|
||||
CSS_APP = $(addprefix assets/css/src/, $(addsuffix .css, base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown screenshot filters gantt project))
|
||||
CSS_PRINT = $(addprefix assets/css/src/, $(addsuffix .css, print links table board task comment subtask markdown))
|
||||
CSS_VENDOR = $(addprefix assets/css/vendor/, $(addsuffix .css, jquery-ui.min jquery-ui-timepicker-addon.min chosen.min fullcalendar.min font-awesome.min c3.min))
|
||||
|
||||
|
|
|
|||
|
|
@ -11,18 +11,19 @@ use Symfony\Component\Console\Command\Command;
|
|||
* @package console
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Kanboard\Model\Notification $notification
|
||||
* @property \Kanboard\Model\Project $project
|
||||
* @property \Kanboard\Model\ProjectPermission $projectPermission
|
||||
* @property \Kanboard\Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
|
||||
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
|
||||
* @property \Kanboard\Model\SubtaskExport $subtaskExport
|
||||
* @property \Kanboard\Model\OverdueNotification $overdueNotification
|
||||
* @property \Kanboard\Model\Task $task
|
||||
* @property \Kanboard\Model\TaskExport $taskExport
|
||||
* @property \Kanboard\Model\TaskFinder $taskFinder
|
||||
* @property \Kanboard\Model\Transition $transition
|
||||
* @property \Kanboard\Model\Notification $notification
|
||||
* @property \Kanboard\Model\Project $project
|
||||
* @property \Kanboard\Model\ProjectPermission $projectPermission
|
||||
* @property \Kanboard\Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
|
||||
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
|
||||
* @property \Kanboard\Model\SubtaskExport $subtaskExport
|
||||
* @property \Kanboard\Model\OverdueNotification $overdueNotification
|
||||
* @property \Kanboard\Model\Task $task
|
||||
* @property \Kanboard\Model\TaskExport $taskExport
|
||||
* @property \Kanboard\Model\TaskFinder $taskFinder
|
||||
* @property \Kanboard\Model\Transition $transition
|
||||
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
|
||||
*/
|
||||
abstract class Base extends Command
|
||||
{
|
||||
|
|
|
|||
|
|
@ -171,14 +171,15 @@ class Project extends Base
|
|||
$project = $this->getProject();
|
||||
|
||||
if ($this->request->getStringParam('duplicate') === 'yes') {
|
||||
$values = array_keys($this->request->getValues());
|
||||
if ($this->projectDuplication->duplicate($project['id'], $values) !== false) {
|
||||
$project_id = $this->projectDuplication->duplicate($project['id'], array_keys($this->request->getValues()), $this->userSession->getId());
|
||||
|
||||
if ($project_id !== false) {
|
||||
$this->flash->success(t('Project cloned successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to clone this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('project', 'index'));
|
||||
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/duplicate', array(
|
||||
|
|
@ -240,57 +241,4 @@ class Project extends Base
|
|||
'title' => t('Project activation')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$is_private = isset($values['is_private']) && $values['is_private'] == 1;
|
||||
|
||||
$this->response->html($this->template->layout('project/new', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'is_private' => $is_private,
|
||||
'title' => $is_private ? t('New private project') : t('New project'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a private project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function createPrivate(array $values = array(), array $errors = array())
|
||||
{
|
||||
$values['is_private'] = 1;
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->projectValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
$project_id = $this->project->create($values, $this->userSession->getId(), true);
|
||||
|
||||
if ($project_id > 0) {
|
||||
$this->flash->success(t('Your project have been created successfully.'));
|
||||
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
|
||||
}
|
||||
|
||||
$this->flash->failure(t('Unable to create your project.'));
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
<?php
|
||||
|
||||
namespace Kanboard\Controller;
|
||||
|
||||
/**
|
||||
* Project Creation Controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectCreation extends Base
|
||||
{
|
||||
/**
|
||||
* Display a form to create a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$is_private = isset($values['is_private']) && $values['is_private'] == 1;
|
||||
$projects_list = array(0 => t('Do not duplicate anything')) + $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
|
||||
$this->response->html($this->template->layout('project_creation/create', array(
|
||||
'board_selector' => $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId()),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'is_private' => $is_private,
|
||||
'projects_list' => $projects_list,
|
||||
'title' => $is_private ? t('New private project') : t('New project'),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a private project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function createPrivate(array $values = array(), array $errors = array())
|
||||
{
|
||||
$values['is_private'] = 1;
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->projectValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
$project_id = $this->createOrDuplicate($values);
|
||||
|
||||
if ($project_id > 0) {
|
||||
$this->flash->success(t('Your project have been created successfully.'));
|
||||
return $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
|
||||
}
|
||||
|
||||
$this->flash->failure(t('Unable to create your project.'));
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create or duplicate a project
|
||||
*
|
||||
* @access private
|
||||
* @param array $values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function createOrDuplicate(array $values)
|
||||
{
|
||||
if ($values['src_project_id'] == 0) {
|
||||
return $this->createNewProject($values);
|
||||
}
|
||||
|
||||
return $this->duplicateNewProject($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a new project
|
||||
*
|
||||
* @access private
|
||||
* @param array $values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function createNewProject(array $values)
|
||||
{
|
||||
$project = array(
|
||||
'name' => $values['name'],
|
||||
'is_private' => $values['is_private'],
|
||||
);
|
||||
|
||||
return $this->project->create($project, $this->userSession->getId(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creatte from another project
|
||||
*
|
||||
* @access private
|
||||
* @param array $values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function duplicateNewProject(array $values)
|
||||
{
|
||||
$selection = array();
|
||||
|
||||
foreach ($this->projectDuplication->getOptionalSelection() as $item) {
|
||||
if (isset($values[$item]) && $values[$item] == 1) {
|
||||
$selection[] = $item;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->projectDuplication->duplicate(
|
||||
$values['src_project_id'],
|
||||
$selection,
|
||||
$this->userSession->getId(),
|
||||
$values['name'],
|
||||
$values['is_private'] == 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -89,11 +89,11 @@ class ProjectEdit extends Base
|
|||
{
|
||||
if ($redirect === 'edit') {
|
||||
if (isset($values['is_private'])) {
|
||||
if (! $this->helper->user->hasProjectAccess('project', 'create', $project['id'])) {
|
||||
if (! $this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) {
|
||||
unset($values['is_private']);
|
||||
}
|
||||
} elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) {
|
||||
if ($this->helper->user->hasProjectAccess('project', 'create', $project['id'])) {
|
||||
if ($this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) {
|
||||
$values += array('is_private' => 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,24 @@ use Kanboard\Core\Security\Role;
|
|||
*/
|
||||
class ProjectPermission extends Base
|
||||
{
|
||||
/**
|
||||
* Permissions are only available for team projects
|
||||
*
|
||||
* @access protected
|
||||
* @param integer $project_id Default project id
|
||||
* @return array
|
||||
*/
|
||||
protected function getProject($project_id = 0)
|
||||
{
|
||||
$project = parent::getProject($project_id);
|
||||
|
||||
if ($project['is_private'] == 1) {
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show all permissions
|
||||
*
|
||||
|
|
@ -62,6 +80,7 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function addUser()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) {
|
||||
|
|
@ -70,7 +89,7 @@ class ProjectPermission extends Base
|
|||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -81,19 +100,16 @@ class ProjectPermission extends Base
|
|||
public function removeUser()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$user_id = $this->request->getIntegerParam('user_id');
|
||||
|
||||
$values = array(
|
||||
'project_id' => $this->request->getIntegerParam('project_id'),
|
||||
'user_id' => $this->request->getIntegerParam('user_id'),
|
||||
);
|
||||
|
||||
if ($this->projectUserRole->removeUser($values['project_id'], $values['user_id'])) {
|
||||
if ($this->projectUserRole->removeUser($project['id'], $user_id)) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -103,10 +119,10 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function changeUserRole()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getJson();
|
||||
|
||||
if (! empty($project_id) && ! empty($values) && $this->projectUserRole->changeUserRole($project_id, $values['id'], $values['role'])) {
|
||||
if (! empty($project) && ! empty($values) && $this->projectUserRole->changeUserRole($project['id'], $values['id'], $values['role'])) {
|
||||
$this->response->json(array('status' => 'ok'));
|
||||
} else {
|
||||
$this->response->json(array('status' => 'error'));
|
||||
|
|
@ -120,19 +136,20 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function addGroup()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (empty($values['group_id']) && ! empty($values['external_id'])) {
|
||||
$values['group_id'] = $this->group->create($values['name'], $values['external_id']);
|
||||
}
|
||||
|
||||
if ($this->projectGroupRole->addGroup($values['project_id'], $values['group_id'], $values['role'])) {
|
||||
if ($this->projectGroupRole->addGroup($project['id'], $values['group_id'], $values['role'])) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -143,19 +160,16 @@ class ProjectPermission extends Base
|
|||
public function removeGroup()
|
||||
{
|
||||
$this->checkCSRFParam();
|
||||
$project = $this->getProject();
|
||||
$group_id = $this->request->getIntegerParam('group_id');
|
||||
|
||||
$values = array(
|
||||
'project_id' => $this->request->getIntegerParam('project_id'),
|
||||
'group_id' => $this->request->getIntegerParam('group_id'),
|
||||
);
|
||||
|
||||
if ($this->projectGroupRole->removeGroup($values['project_id'], $values['group_id'])) {
|
||||
if ($this->projectGroupRole->removeGroup($project['id'], $group_id)) {
|
||||
$this->flash->success(t('Project updated successfully.'));
|
||||
} else {
|
||||
$this->flash->failure(t('Unable to update this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $values['project_id'])));
|
||||
$this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -165,10 +179,10 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function changeGroupRole()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getJson();
|
||||
|
||||
if (! empty($project_id) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project_id, $values['id'], $values['role'])) {
|
||||
if (! empty($project) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project['id'], $values['id'], $values['role'])) {
|
||||
$this->response->json(array('status' => 'ok'));
|
||||
} else {
|
||||
$this->response->json(array('status' => 'error'));
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class Taskduplication extends Base
|
|||
private function chooseDestination(array $task, $template)
|
||||
{
|
||||
$values = array();
|
||||
$projects_list = $this->projectUserRole->getProjectsByUser($this->userSession->getId(), array(ProjectModel::ACTIVE));
|
||||
$projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
|
||||
|
||||
unset($projects_list[$task['project_id']]);
|
||||
|
||||
|
|
|
|||
|
|
@ -120,8 +120,6 @@ class ExternalLinkManager extends Base
|
|||
*/
|
||||
public function find()
|
||||
{
|
||||
$provider = null;
|
||||
|
||||
if ($this->userInputType === self::TYPE_AUTO) {
|
||||
$provider = $this->findProvider();
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Kanboard\Model;
|
||||
|
||||
use Kanboard\Core\Security\Role;
|
||||
|
||||
/**
|
||||
* Project Duplication
|
||||
*
|
||||
|
|
@ -11,6 +13,28 @@ namespace Kanboard\Model;
|
|||
*/
|
||||
class ProjectDuplication extends Base
|
||||
{
|
||||
/**
|
||||
* Get list of optional models to duplicate
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getOptionalSelection()
|
||||
{
|
||||
return array('category', 'projectPermission', 'action', 'swimlane', 'task');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get list of all possible models to duplicate
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getPossibleSelection()
|
||||
{
|
||||
return array('board', 'category', 'projectPermission', 'action', 'swimlane', 'task');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a valid project name for the duplication
|
||||
*
|
||||
|
|
@ -30,79 +54,107 @@ class ProjectDuplication extends Base
|
|||
return $name.$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a project with all settings
|
||||
*
|
||||
* @param integer $src_project_id Project Id
|
||||
* @param array $selection Selection of optional project parts to duplicate
|
||||
* @param integer $owner_id Owner of the project
|
||||
* @param string $name Name of the project
|
||||
* @param boolean $private Force the project to be private
|
||||
* @return integer Cloned Project Id
|
||||
*/
|
||||
public function duplicate($src_project_id, $selection = array('projectPermission', 'category', 'action'), $owner_id = 0, $name = null, $private = null)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Get the cloned project Id
|
||||
$dst_project_id = $this->copy($src_project_id, $owner_id, $name, $private);
|
||||
|
||||
if (! $dst_project_id) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone Columns, Categories, Permissions and Actions
|
||||
foreach ($this->getPossibleSelection() as $model) {
|
||||
|
||||
// Skip if optional part has not been selected
|
||||
if (in_array($model, $this->getOptionalSelection()) && ! in_array($model, $selection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip permissions for private projects
|
||||
if ($private && $model === 'projectPermission') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->$model->duplicate($src_project_id, $dst_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (! $this->makeOwnerManager($dst_project_id, $owner_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return (int) $dst_project_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a project from another one
|
||||
*
|
||||
* @param integer $project_id Project Id
|
||||
* @return integer Cloned Project Id
|
||||
* @access private
|
||||
* @param integer $src_project_id
|
||||
* @param integer $owner_id
|
||||
* @param string $name
|
||||
* @param boolean $private
|
||||
* @return integer
|
||||
*/
|
||||
public function copy($project_id)
|
||||
private function copy($src_project_id, $owner_id = 0, $name = null, $private = null)
|
||||
{
|
||||
$project = $this->project->getById($project_id);
|
||||
$project = $this->project->getById($src_project_id);
|
||||
$is_private = empty($project['is_private']) ? 0 : 1;
|
||||
|
||||
$values = array(
|
||||
'name' => $this->getClonedProjectName($project['name']),
|
||||
'is_active' => true,
|
||||
'last_modified' => 0,
|
||||
'name' => $name ?: $this->getClonedProjectName($project['name']),
|
||||
'is_active' => 1,
|
||||
'last_modified' => time(),
|
||||
'token' => '',
|
||||
'is_public' => 0,
|
||||
'is_private' => empty($project['is_private']) ? 0 : 1,
|
||||
'is_private' => $private ? 1 : $is_private,
|
||||
'owner_id' => $owner_id,
|
||||
);
|
||||
|
||||
if (! $this->db->table(Project::TABLE)->save($values)) {
|
||||
return 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->db->getLastId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone a project with all settings
|
||||
* Make sure that the creator of the duplicated project is alsp owner
|
||||
*
|
||||
* @param integer $project_id Project Id
|
||||
* @param array $part_selection Selection of optional project parts to duplicate. Possible options: 'swimlane', 'action', 'category', 'task'
|
||||
* @return integer Cloned Project Id
|
||||
* @access private
|
||||
* @param integer $dst_project_id
|
||||
* @param integer $owner_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_id, $part_selection = array('category', 'action'))
|
||||
private function makeOwnerManager($dst_project_id, $owner_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
if ($owner_id > 0) {
|
||||
$this->projectUserRole->removeUser($dst_project_id, $owner_id);
|
||||
|
||||
// Get the cloned project Id
|
||||
$clone_project_id = $this->copy($project_id);
|
||||
|
||||
if (! $clone_project_id) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clone Columns, Categories, Permissions and Actions
|
||||
$optional_parts = array('swimlane', 'action', 'category');
|
||||
foreach (array('board', 'category', 'projectPermission', 'action', 'swimlane') as $model) {
|
||||
|
||||
// Skip if optional part has not been selected
|
||||
if (in_array($model, $optional_parts) && ! in_array($model, $part_selection)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! $this->$model->duplicate($project_id, $clone_project_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
if (! $this->projectUserRole->addUser($dst_project_id, $owner_id, Role::PROJECT_MANAGER)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
// Clone Tasks if in $part_selection
|
||||
if (in_array('task', $part_selection)) {
|
||||
$tasks = $this->taskFinder->getAll($project_id);
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
if (! $this->taskDuplication->duplicateToProject($task['id'], $clone_project_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int) $clone_project_id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -199,4 +199,25 @@ class Task extends Base
|
|||
|
||||
return round(($position * 100) / count($columns), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to duplicate all tasks to another project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $src_project_id
|
||||
* @param integer $dst_project_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($src_project_id, $dst_project_id)
|
||||
{
|
||||
$task_ids = $this->taskFinder->getAllIds($src_project_id, array(Task::STATUS_OPEN, Task::STATUS_CLOSED));
|
||||
|
||||
foreach ($task_ids as $task_id) {
|
||||
if (! $this->taskDuplication->duplicateToProject($task_id, $dst_project_id)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -179,6 +179,23 @@ class TaskFinder extends Base
|
|||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tasks for a given project and status
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* @param array $status
|
||||
* @return array
|
||||
*/
|
||||
public function getAllIds($project_id, array $status = array(Task::STATUS_OPEN))
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq(Task::TABLE.'.project_id', $project_id)
|
||||
->in(Task::TABLE.'.is_active', $status)
|
||||
->findAllByColumn('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get overdue tasks query
|
||||
*
|
||||
|
|
|
|||
|
|
@ -128,8 +128,7 @@ class AuthenticationProvider implements ServiceProviderInterface
|
|||
$acl->add('Gantt', array('projects', 'saveProjectDate'), Role::APP_MANAGER);
|
||||
$acl->add('Group', '*', Role::APP_ADMIN);
|
||||
$acl->add('Link', '*', Role::APP_ADMIN);
|
||||
$acl->add('Project', array('users', 'allowEverybody', 'allow', 'role', 'revoke', 'create'), Role::APP_MANAGER);
|
||||
$acl->add('ProjectPermission', '*', Role::APP_USER);
|
||||
$acl->add('ProjectCreation', 'create', Role::APP_MANAGER);
|
||||
$acl->add('Projectuser', '*', Role::APP_MANAGER);
|
||||
$acl->add('Twofactor', 'disable', Role::APP_ADMIN);
|
||||
$acl->add('UserImport', '*', Role::APP_ADMIN);
|
||||
|
|
|
|||
|
|
@ -43,10 +43,12 @@ class RouteProvider implements ServiceProviderInterface
|
|||
$container['route']->addRoute('search', 'search', 'index');
|
||||
$container['route']->addRoute('search/:search', 'search', 'index');
|
||||
|
||||
// ProjectCreation routes
|
||||
$container['route']->addRoute('project/create', 'ProjectCreation', 'create');
|
||||
$container['route']->addRoute('project/create/private', 'ProjectCreation', 'createPrivate');
|
||||
|
||||
// Project routes
|
||||
$container['route']->addRoute('projects', 'project', 'index');
|
||||
$container['route']->addRoute('project/create', 'project', 'create');
|
||||
$container['route']->addRoute('project/create/private', 'project', 'createPrivate');
|
||||
$container['route']->addRoute('project/:project_id', 'project', 'show');
|
||||
$container['route']->addRoute('p/:project_id', 'project', 'show');
|
||||
$container['route']->addRoute('project/:project_id/customer-filter', 'customfilter', 'index');
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
<section id="main">
|
||||
<div class="page-header page-header-mobile">
|
||||
<ul>
|
||||
<?php if ($this->user->hasAccess('project', 'create')): ?>
|
||||
<?php if ($this->user->hasAccess('ProjectCreation', 'create')): ?>
|
||||
<li>
|
||||
<i class="fa fa-plus fa-fw"></i>
|
||||
<?= $this->url->link(t('New project'), 'project', 'create') ?>
|
||||
<?= $this->url->link(t('New project'), 'ProjectCreation', 'create') ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<i class="fa fa-lock fa-fw"></i>
|
||||
<?= $this->url->link(t('New private project'), 'project', 'createPrivate') ?>
|
||||
<?= $this->url->link(t('New private project'), 'ProjectCreation', 'createPrivate') ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-search fa-fw"></i>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,6 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<ul>
|
||||
<?php if ($this->user->hasAccess('project', 'create')): ?>
|
||||
<li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'project', 'create', array('private' => 1)) ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('Projects list'), 'project', 'index') ?>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -31,14 +31,26 @@
|
|||
</select>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<li class="user-links">
|
||||
<?php if ($this->user->hasNotifications()): ?>
|
||||
<span class="notification">
|
||||
<?= $this->url->link('<i class="fa fa-bell web-notification-icon"></i>', 'app', 'notifications', array('user_id' => $this->user->getId()), false, '', t('Unread notifications')) ?>
|
||||
</span>
|
||||
<?php endif ?>
|
||||
|
||||
<span class="dropdown">
|
||||
<div class="dropdown">
|
||||
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-plus fa-fw"></i><i class="fa fa-caret-down"></i></a>
|
||||
<ul>
|
||||
<?php if ($this->user->hasAccess('ProjectCreation', 'create')): ?>
|
||||
<li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'ProjectCreation', 'create') ?></li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'ProjectCreation', 'createPrivate') ?>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="dropdown">
|
||||
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-user fa-fw"></i><i class="fa fa-caret-down"></i></a>
|
||||
<ul>
|
||||
<li class="no-hover"><strong><?= $this->e($this->user->getFullname()) ?></strong></li>
|
||||
|
|
@ -46,7 +58,7 @@
|
|||
<li><?= $this->url->link(t('My profile'), 'user', 'show', array('user_id' => $this->user->getId())) ?></li>
|
||||
<li><?= $this->url->link(t('Logout'), 'auth', 'logout') ?></li>
|
||||
</ul>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
|||
|
|
@ -10,13 +10,17 @@
|
|||
|
||||
<?= $this->form->csrf() ?>
|
||||
|
||||
<?php if ($project['is_private'] == 0): ?>
|
||||
<?= $this->form->checkbox('projectPermission', t('Permissions'), 1, true) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $this->form->checkbox('category', t('Categories'), 1, true) ?>
|
||||
<?= $this->form->checkbox('action', t('Actions'), 1, true) ?>
|
||||
<?= $this->form->checkbox('swimlane', t('Swimlanes'), 1, false) ?>
|
||||
<?= $this->form->checkbox('task', t('Tasks'), 1, false) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Duplicate') ?>" class="btn btn-red"/>
|
||||
<input type="submit" value="<?= t('Duplicate') ?>" class="btn btn-red">
|
||||
<?= t('or') ?> <?= $this->url->link(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<ul>
|
||||
<?php if ($this->user->hasAccess('project', 'create')): ?>
|
||||
<li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li>
|
||||
<?php endif ?>
|
||||
<li><i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'project', 'createPrivate') ?></li>
|
||||
<?php if ($this->user->hasAccess('projectuser', 'managers')): ?>
|
||||
<li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('Users overview'), 'projectuser', 'managers') ?></li>
|
||||
<?php endif ?>
|
||||
|
|
|
|||
|
|
@ -1,24 +0,0 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<ul>
|
||||
<li><i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('All projects'), 'project', 'index') ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<form method="post" action="<?= $this->url->href('project', 'save') ?>" autocomplete="off">
|
||||
|
||||
<?= $this->form->csrf() ?>
|
||||
<?= $this->form->hidden('is_private', $values) ?>
|
||||
<?= $this->form->label(t('Name'), 'name') ?>
|
||||
<?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <?= $this->url->link(t('cancel'), 'project', 'index') ?>
|
||||
</div>
|
||||
</form>
|
||||
<?php if (isset($is_private) && $is_private): ?>
|
||||
<div class="alert alert-info">
|
||||
<p><?= t('There is no user management for private projects.') ?></p>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<ul>
|
||||
<li><i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('All projects'), 'project', 'index') ?></li>
|
||||
</ul>
|
||||
</div>
|
||||
<form class="form-popover" id="project-creation-form" method="post" action="<?= $this->url->href('ProjectCreation', 'save') ?>" autocomplete="off">
|
||||
|
||||
<?= $this->form->csrf() ?>
|
||||
<?= $this->form->hidden('is_private', $values) ?>
|
||||
|
||||
<?= $this->form->label(t('Name'), 'name') ?>
|
||||
<?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
|
||||
|
||||
<?php if (count($projects_list) > 1): ?>
|
||||
<?= $this->form->label(t('Create from another project'), 'src_project_id') ?>
|
||||
<?= $this->form->select('src_project_id', $projects_list, $values) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="project-creation-options" <?= isset($values['src_project_id']) && $values['src_project_id'] > 0 ? '' : 'style="display: none"' ?>>
|
||||
<p class="alert"><?= t('Which parts of the project do you want to duplicate?') ?></p>
|
||||
|
||||
<?php if (! $is_private): ?>
|
||||
<?= $this->form->checkbox('projectPermission', t('Permissions'), 1, true) ?>
|
||||
<?php endif ?>
|
||||
|
||||
<?= $this->form->checkbox('category', t('Categories'), 1, true) ?>
|
||||
<?= $this->form->checkbox('action', t('Actions'), 1, true) ?>
|
||||
<?= $this->form->checkbox('swimlane', t('Swimlanes'), 1, true) ?>
|
||||
<?= $this->form->checkbox('task', t('Tasks'), 1, false) ?>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue">
|
||||
</div>
|
||||
</form>
|
||||
<?php if ($is_private): ?>
|
||||
<div class="alert alert-info">
|
||||
<p><?= t('There is no user management for private projects.') ?></p>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<?= $this->form->select('owner_id', $owners, $values, $errors) ?>
|
||||
</div>
|
||||
|
||||
<?php if ($this->user->hasProjectAccess('project', 'create', $project['id'])): ?>
|
||||
<?php if ($this->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])): ?>
|
||||
<hr>
|
||||
<?= $this->form->checkbox('is_private', t('Private project'), 1, $project['is_private'] == 1) ?>
|
||||
<p class="form-help"><?= t('Private projects do not have users and groups management.') ?></p>
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
<section id="main">
|
||||
<div class="page-header">
|
||||
<ul>
|
||||
<?php if ($this->user->hasAccess('project', 'create')): ?>
|
||||
<li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'project', 'create') ?></li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<i class="fa fa-lock fa-fw"></i>
|
||||
<?= $this->url->link(t('New private project'), 'project', 'create', array('private' => 1)) ?>
|
||||
</li>
|
||||
<li>
|
||||
<i class="fa fa-folder fa-fw"></i>
|
||||
<?= $this->url->link(t('Projects list'), 'project', 'index') ?>
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -55,6 +55,11 @@ nav .active a {
|
|||
color: #d40000;
|
||||
}
|
||||
|
||||
/* user links on the left */
|
||||
header .user-links .dropdown {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
/* title tooltip */
|
||||
header h1 .tooltip {
|
||||
opacity: 0.3;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
.project-creation-options {
|
||||
max-width: 500px;
|
||||
border-left: 3px dotted #efefef;
|
||||
margin-top: 20px;
|
||||
padding-left: 15px;
|
||||
padding-bottom: 5px;
|
||||
padding-top: 5px;
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
|
|
@ -15,4 +15,14 @@ Project.prototype.listen = function() {
|
|||
})
|
||||
});
|
||||
});
|
||||
|
||||
$('#project-creation-form #form-src_project_id').on('change', function() {
|
||||
var srcProjectId = $(this).val();
|
||||
|
||||
if (srcProjectId == 0) {
|
||||
$(".project-creation-options").hide();
|
||||
} else {
|
||||
$(".project-creation-options").show();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ class UserHelperTest extends Base
|
|||
);
|
||||
|
||||
$this->assertTrue($helper->hasAccess('user', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('project', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('project', 'createPrivate'));
|
||||
$this->assertTrue($helper->hasAccess('ProjectCreation', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate'));
|
||||
}
|
||||
|
||||
public function testHasAccessForManagers()
|
||||
|
|
@ -50,8 +50,8 @@ class UserHelperTest extends Base
|
|||
);
|
||||
|
||||
$this->assertFalse($helper->hasAccess('user', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('project', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('project', 'createPrivate'));
|
||||
$this->assertTrue($helper->hasAccess('ProjectCreation', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate'));
|
||||
}
|
||||
|
||||
public function testHasAccessForUsers()
|
||||
|
|
@ -64,8 +64,8 @@ class UserHelperTest extends Base
|
|||
);
|
||||
|
||||
$this->assertFalse($helper->hasAccess('user', 'create'));
|
||||
$this->assertFalse($helper->hasAccess('project', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('project', 'createPrivate'));
|
||||
$this->assertFalse($helper->hasAccess('ProjectCreation', 'create'));
|
||||
$this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate'));
|
||||
}
|
||||
|
||||
public function testHasProjectAccessForAdmins()
|
||||
|
|
|
|||
|
|
@ -6,8 +6,11 @@ use Kanboard\Model\Action;
|
|||
use Kanboard\Model\Project;
|
||||
use Kanboard\Model\Category;
|
||||
use Kanboard\Model\ProjectUserRole;
|
||||
use Kanboard\Model\ProjectGroupRole;
|
||||
use Kanboard\Model\ProjectDuplication;
|
||||
use Kanboard\Model\User;
|
||||
use Kanboard\Model\Group;
|
||||
use Kanboard\Model\GroupMember;
|
||||
use Kanboard\Model\Swimlane;
|
||||
use Kanboard\Model\Task;
|
||||
use Kanboard\Model\TaskCreation;
|
||||
|
|
@ -16,7 +19,14 @@ use Kanboard\Core\Security\Role;
|
|||
|
||||
class ProjectDuplicationTest extends Base
|
||||
{
|
||||
public function testProjectName()
|
||||
public function testGetSelections()
|
||||
{
|
||||
$projectDuplicationModel = new ProjectDuplication($this->container);
|
||||
$this->assertCount(5, $projectDuplicationModel->getOptionalSelection());
|
||||
$this->assertCount(6, $projectDuplicationModel->getPossibleSelection());
|
||||
}
|
||||
|
||||
public function testGetClonedProjectName()
|
||||
{
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
|
|
@ -29,19 +39,6 @@ class ProjectDuplicationTest extends Base
|
|||
$this->assertEquals(str_repeat('a', 42).' (Clone)', $pd->getClonedProjectName(str_repeat('a', 60)));
|
||||
}
|
||||
|
||||
public function testCopyProjectWithLongName()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => str_repeat('a', 50))));
|
||||
$this->assertEquals(2, $pd->duplicate(1));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals(str_repeat('a', 42).' (Clone)', $project['name']);
|
||||
}
|
||||
|
||||
public function testClonePublicProject()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
|
|
@ -53,8 +50,10 @@ class ProjectDuplicationTest extends Base
|
|||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('Public (Clone)', $project['name']);
|
||||
$this->assertEquals(1, $project['is_active']);
|
||||
$this->assertEquals(0, $project['is_private']);
|
||||
$this->assertEquals(0, $project['is_public']);
|
||||
$this->assertEquals(0, $project['owner_id']);
|
||||
$this->assertEmpty($project['token']);
|
||||
}
|
||||
|
||||
|
|
@ -62,6 +61,7 @@ class ProjectDuplicationTest extends Base
|
|||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
$pp = new ProjectUserRole($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'Private', 'is_private' => 1), 1, true));
|
||||
$this->assertEquals(2, $pd->duplicate(1));
|
||||
|
|
@ -69,14 +69,112 @@ class ProjectDuplicationTest extends Base
|
|||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('Private (Clone)', $project['name']);
|
||||
$this->assertEquals(1, $project['is_active']);
|
||||
$this->assertEquals(1, $project['is_private']);
|
||||
$this->assertEquals(0, $project['is_public']);
|
||||
$this->assertEquals(0, $project['owner_id']);
|
||||
$this->assertEmpty($project['token']);
|
||||
|
||||
$pp = new ProjectUserRole($this->container);
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 1));
|
||||
}
|
||||
|
||||
$this->assertEquals(array(1 => 'admin'), $pp->getAssignableUsers(1));
|
||||
$this->assertEquals(array(1 => 'admin'), $pp->getAssignableUsers(2));
|
||||
public function testCloneSharedProject()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'Shared')));
|
||||
$this->assertTrue($p->update(array('id' => 1, 'is_public' => 1, 'token' => 'test')));
|
||||
|
||||
$project = $p->getById(1);
|
||||
$this->assertEquals('test', $project['token']);
|
||||
$this->assertEquals(1, $project['is_public']);
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('Shared (Clone)', $project['name']);
|
||||
$this->assertEquals('', $project['token']);
|
||||
$this->assertEquals(0, $project['is_public']);
|
||||
}
|
||||
|
||||
public function testCloneInactiveProject()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'Inactive')));
|
||||
$this->assertTrue($p->update(array('id' => 1, 'is_active' => 0)));
|
||||
|
||||
$project = $p->getById(1);
|
||||
$this->assertEquals(0, $project['is_active']);
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('Inactive (Clone)', $project['name']);
|
||||
$this->assertEquals(1, $project['is_active']);
|
||||
}
|
||||
|
||||
public function testCloneProjectWithOwner()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
$projectUserRoleModel = new ProjectUserRole($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'Owner')));
|
||||
|
||||
$project = $p->getById(1);
|
||||
$this->assertEquals(0, $project['owner_id']);
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('Owner (Clone)', $project['name']);
|
||||
$this->assertEquals(1, $project['owner_id']);
|
||||
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $projectUserRoleModel->getUserRole(2, 1));
|
||||
}
|
||||
|
||||
public function testCloneProjectWithDifferentName()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'Owner')));
|
||||
|
||||
$project = $p->getById(1);
|
||||
$this->assertEquals(0, $project['owner_id']);
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1, 'Foobar'));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('Foobar', $project['name']);
|
||||
$this->assertEquals(1, $project['owner_id']);
|
||||
}
|
||||
|
||||
public function testCloneProjectAndForceItToBePrivate()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'Owner')));
|
||||
|
||||
$project = $p->getById(1);
|
||||
$this->assertEquals(0, $project['owner_id']);
|
||||
$this->assertEquals(0, $project['is_private']);
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1, 'Foobar', true));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('Foobar', $project['name']);
|
||||
$this->assertEquals(1, $project['owner_id']);
|
||||
$this->assertEquals(1, $project['is_private']);
|
||||
}
|
||||
|
||||
public function testCloneProjectWithCategories()
|
||||
|
|
@ -98,16 +196,9 @@ class ProjectDuplicationTest extends Base
|
|||
$this->assertEquals('P1 (Clone)', $project['name']);
|
||||
|
||||
$categories = $c->getAll(2);
|
||||
$this->assertNotempty($categories);
|
||||
$this->assertEquals(3, count($categories));
|
||||
|
||||
$this->assertEquals(4, $categories[0]['id']);
|
||||
$this->assertCount(3, $categories);
|
||||
$this->assertEquals('C1', $categories[0]['name']);
|
||||
|
||||
$this->assertEquals(5, $categories[1]['id']);
|
||||
$this->assertEquals('C2', $categories[1]['name']);
|
||||
|
||||
$this->assertEquals(6, $categories[2]['id']);
|
||||
$this->assertEquals('C3', $categories[2]['name']);
|
||||
}
|
||||
|
||||
|
|
@ -119,28 +210,115 @@ class ProjectDuplicationTest extends Base
|
|||
$u = new User($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(2, $u->create(array('username' => 'unittest1', 'password' => 'unittest')));
|
||||
$this->assertEquals(3, $u->create(array('username' => 'unittest2', 'password' => 'unittest')));
|
||||
$this->assertEquals(4, $u->create(array('username' => 'unittest3', 'password' => 'unittest')));
|
||||
$this->assertEquals(2, $u->create(array('username' => 'user1')));
|
||||
$this->assertEquals(3, $u->create(array('username' => 'user2')));
|
||||
$this->assertEquals(4, $u->create(array('username' => 'user3')));
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1')));
|
||||
$this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MEMBER));
|
||||
|
||||
$this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MANAGER));
|
||||
$this->assertTrue($pp->addUser(1, 3, Role::PROJECT_MEMBER));
|
||||
$this->assertTrue($pp->addUser(1, 4, Role::PROJECT_MANAGER));
|
||||
$this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(1, 2));
|
||||
$this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(1, 3));
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(1, 4));
|
||||
$this->assertTrue($pp->addUser(1, 4, Role::PROJECT_VIEWER));
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertNotEmpty($project);
|
||||
$this->assertEquals('P1 (Clone)', $project['name']);
|
||||
|
||||
$this->assertEquals(3, count($pp->getUsers(2)));
|
||||
$this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(2, 2));
|
||||
$this->assertCount(3, $pp->getUsers(2));
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 2));
|
||||
$this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(2, 3));
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 4));
|
||||
$this->assertEquals(Role::PROJECT_VIEWER, $pp->getUserRole(2, 4));
|
||||
}
|
||||
|
||||
public function testCloneProjectWithUsersAndOverrideOwner()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$c = new Category($this->container);
|
||||
$pp = new ProjectUserRole($this->container);
|
||||
$u = new User($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(2, $u->create(array('username' => 'user1')));
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1'), 2));
|
||||
|
||||
$project = $p->getById(1);
|
||||
$this->assertEquals(2, $project['owner_id']);
|
||||
|
||||
$this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MANAGER));
|
||||
$this->assertTrue($pp->addUser(1, 1, Role::PROJECT_MEMBER));
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 1));
|
||||
|
||||
$this->assertCount(2, $pp->getUsers(2));
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 2));
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 1));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertEquals(1, $project['owner_id']);
|
||||
}
|
||||
|
||||
public function testCloneTeamProjectToPrivatProject()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$c = new Category($this->container);
|
||||
$pp = new ProjectUserRole($this->container);
|
||||
$u = new User($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
|
||||
$this->assertEquals(2, $u->create(array('username' => 'user1')));
|
||||
$this->assertEquals(3, $u->create(array('username' => 'user2')));
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1'), 2));
|
||||
|
||||
$project = $p->getById(1);
|
||||
$this->assertEquals(2, $project['owner_id']);
|
||||
$this->assertEquals(0, $project['is_private']);
|
||||
|
||||
$this->assertTrue($pp->addUser(1, 2, Role::PROJECT_MANAGER));
|
||||
$this->assertTrue($pp->addUser(1, 1, Role::PROJECT_MEMBER));
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('projectPermission'), 3, 'My private project', true));
|
||||
|
||||
$this->assertCount(1, $pp->getUsers(2));
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 3));
|
||||
|
||||
$project = $p->getById(2);
|
||||
$this->assertEquals(3, $project['owner_id']);
|
||||
$this->assertEquals(1, $project['is_private']);
|
||||
}
|
||||
|
||||
public function testCloneProjectWithGroups()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$c = new Category($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
$userModel = new User($this->container);
|
||||
$groupModel = new Group($this->container);
|
||||
$groupMemberModel = new GroupMember($this->container);
|
||||
$projectGroupRoleModel = new ProjectGroupRole($this->container);
|
||||
$projectUserRoleModel = new ProjectUserRole($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1')));
|
||||
|
||||
$this->assertEquals(1, $groupModel->create('G1'));
|
||||
$this->assertEquals(2, $groupModel->create('G2'));
|
||||
$this->assertEquals(3, $groupModel->create('G3'));
|
||||
|
||||
$this->assertEquals(2, $userModel->create(array('username' => 'user1')));
|
||||
$this->assertEquals(3, $userModel->create(array('username' => 'user2')));
|
||||
$this->assertEquals(4, $userModel->create(array('username' => 'user3')));
|
||||
|
||||
$this->assertTrue($groupMemberModel->addUser(1, 2));
|
||||
$this->assertTrue($groupMemberModel->addUser(2, 3));
|
||||
$this->assertTrue($groupMemberModel->addUser(3, 4));
|
||||
|
||||
$this->assertTrue($projectGroupRoleModel->addGroup(1, 1, Role::PROJECT_MANAGER));
|
||||
$this->assertTrue($projectGroupRoleModel->addGroup(1, 2, Role::PROJECT_MEMBER));
|
||||
$this->assertTrue($projectGroupRoleModel->addGroup(1, 3, Role::PROJECT_VIEWER));
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1));
|
||||
|
||||
$this->assertCount(3, $projectGroupRoleModel->getGroups(2));
|
||||
$this->assertEquals(Role::PROJECT_MANAGER, $projectUserRoleModel->getUserRole(2, 2));
|
||||
$this->assertEquals(Role::PROJECT_MEMBER, $projectUserRoleModel->getUserRole(2, 3));
|
||||
$this->assertEquals(Role::PROJECT_VIEWER, $projectUserRoleModel->getUserRole(2, 4));
|
||||
}
|
||||
|
||||
public function testCloneProjectWithActionTaskAssignCurrentUser()
|
||||
|
|
@ -199,68 +377,6 @@ class ProjectDuplicationTest extends Base
|
|||
$this->assertEquals(5, $actions[0]['params']['category_id']);
|
||||
}
|
||||
|
||||
public function testCloneProjectWithSwimlanesAndTasks()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
$s = new Swimlane($this->container);
|
||||
$tc = new TaskCreation($this->container);
|
||||
$tf = new TaskFinder($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1')));
|
||||
|
||||
// create initial swimlanes
|
||||
$this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1')));
|
||||
$this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2')));
|
||||
$this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3')));
|
||||
|
||||
$default_swimlane1 = $s->getDefault(1);
|
||||
$default_swimlane1['default_swimlane'] = 'New Default';
|
||||
|
||||
$this->assertTrue($s->updateDefault($default_swimlane1));
|
||||
|
||||
//create initial tasks
|
||||
$this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1)));
|
||||
$this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1)));
|
||||
$this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
|
||||
|
||||
$this->assertNotFalse($pd->duplicate(1, array('category', 'action', 'swimlane', 'task')));
|
||||
$project = $p->getByName('P1 (Clone)');
|
||||
$this->assertNotFalse($project);
|
||||
$project_id = $project['id'];
|
||||
|
||||
// Check if Swimlanes have been duplicated
|
||||
$swimlanes = $s->getAll($project_id);
|
||||
|
||||
$this->assertCount(3, $swimlanes);
|
||||
$this->assertEquals(4, $swimlanes[0]['id']);
|
||||
$this->assertEquals('S1', $swimlanes[0]['name']);
|
||||
$this->assertEquals(5, $swimlanes[1]['id']);
|
||||
$this->assertEquals('S2', $swimlanes[1]['name']);
|
||||
$this->assertEquals(6, $swimlanes[2]['id']);
|
||||
$this->assertEquals('S3', $swimlanes[2]['name']);
|
||||
$new_default = $s->getDefault($project_id);
|
||||
$this->assertEquals('New Default', $new_default['default_swimlane']);
|
||||
|
||||
// Check if Tasks have been duplicated
|
||||
|
||||
$tasks = $tf->getAll($project_id);
|
||||
|
||||
$this->assertCount(3, $tasks);
|
||||
// $this->assertEquals(4, $tasks[0]['id']);
|
||||
$this->assertEquals('T1', $tasks[0]['title']);
|
||||
// $this->assertEquals(5, $tasks[1]['id']);
|
||||
$this->assertEquals('T2', $tasks[1]['title']);
|
||||
// $this->assertEquals(6, $tasks[2]['id']);
|
||||
$this->assertEquals('T3', $tasks[2]['title']);
|
||||
|
||||
$p->remove($project_id);
|
||||
|
||||
$this->assertFalse($p->exists($project_id));
|
||||
$this->assertCount(0, $s->getAll($project_id));
|
||||
$this->assertCount(0, $tf->getAll($project_id));
|
||||
}
|
||||
|
||||
public function testCloneProjectWithSwimlanes()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
|
|
@ -269,30 +385,22 @@ class ProjectDuplicationTest extends Base
|
|||
$tc = new TaskCreation($this->container);
|
||||
$tf = new TaskFinder($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1')));
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1', 'default_swimlane' => 'New Default')));
|
||||
|
||||
// create initial swimlanes
|
||||
$this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1')));
|
||||
$this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2')));
|
||||
$this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3')));
|
||||
|
||||
$default_swimlane1 = $s->getDefault(1);
|
||||
$default_swimlane1['default_swimlane'] = 'New Default';
|
||||
// create initial tasks
|
||||
$this->assertEquals(1, $tc->create(array('title' => 'T0', 'project_id' => 1, 'swimlane_id' => 0)));
|
||||
$this->assertEquals(2, $tc->create(array('title' => 'T1', 'project_id' => 1, 'swimlane_id' => 1)));
|
||||
$this->assertEquals(3, $tc->create(array('title' => 'T2', 'project_id' => 1, 'swimlane_id' => 2)));
|
||||
$this->assertEquals(4, $tc->create(array('title' => 'T3', 'project_id' => 1, 'swimlane_id' => 3)));
|
||||
|
||||
$this->assertTrue($s->updateDefault($default_swimlane1));
|
||||
|
||||
//create initial tasks
|
||||
$this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1)));
|
||||
$this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1)));
|
||||
$this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
|
||||
|
||||
$this->assertNotFalse($pd->duplicate(1, array('category', 'action', 'swimlane')));
|
||||
$project = $p->getByName('P1 (Clone)');
|
||||
$this->assertNotFalse($project);
|
||||
$project_id = $project['id'];
|
||||
|
||||
$swimlanes = $s->getAll($project_id);
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('category', 'swimlane')));
|
||||
|
||||
$swimlanes = $s->getAll(2);
|
||||
$this->assertCount(3, $swimlanes);
|
||||
$this->assertEquals(4, $swimlanes[0]['id']);
|
||||
$this->assertEquals('S1', $swimlanes[0]['name']);
|
||||
|
|
@ -300,11 +408,12 @@ class ProjectDuplicationTest extends Base
|
|||
$this->assertEquals('S2', $swimlanes[1]['name']);
|
||||
$this->assertEquals(6, $swimlanes[2]['id']);
|
||||
$this->assertEquals('S3', $swimlanes[2]['name']);
|
||||
$new_default = $s->getDefault($project_id);
|
||||
$this->assertEquals('New Default', $new_default['default_swimlane']);
|
||||
|
||||
// Check if Tasks have NOT been duplicated
|
||||
$this->assertCount(0, $tf->getAll($project_id));
|
||||
$swimlane = $s->getDefault(2);
|
||||
$this->assertEquals('New Default', $swimlane['default_swimlane']);
|
||||
|
||||
// Check if tasks are NOT been duplicated
|
||||
$this->assertCount(0, $tf->getAll(2));
|
||||
}
|
||||
|
||||
public function testCloneProjectWithTasks()
|
||||
|
|
@ -317,38 +426,62 @@ class ProjectDuplicationTest extends Base
|
|||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1')));
|
||||
|
||||
// create initial tasks
|
||||
$this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1)));
|
||||
$this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2)));
|
||||
$this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3)));
|
||||
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('category', 'action', 'task')));
|
||||
|
||||
// Check if Tasks have been duplicated
|
||||
$tasks = $tf->getAll(2);
|
||||
$this->assertCount(3, $tasks);
|
||||
$this->assertEquals('T1', $tasks[0]['title']);
|
||||
$this->assertEquals('T2', $tasks[1]['title']);
|
||||
$this->assertEquals('T3', $tasks[2]['title']);
|
||||
}
|
||||
|
||||
public function testCloneProjectWithSwimlanesAndTasks()
|
||||
{
|
||||
$p = new Project($this->container);
|
||||
$pd = new ProjectDuplication($this->container);
|
||||
$s = new Swimlane($this->container);
|
||||
$tc = new TaskCreation($this->container);
|
||||
$tf = new TaskFinder($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'P1', 'default_swimlane' => 'New Default')));
|
||||
|
||||
// create initial swimlanes
|
||||
$this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1')));
|
||||
$this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2')));
|
||||
$this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3')));
|
||||
|
||||
$default_swimlane1 = $s->getDefault(1);
|
||||
$default_swimlane1['default_swimlane'] = 'New Default';
|
||||
|
||||
$this->assertTrue($s->updateDefault($default_swimlane1));
|
||||
|
||||
//create initial tasks
|
||||
// create initial tasks
|
||||
$this->assertEquals(1, $tc->create(array('title' => 'T1', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1)));
|
||||
$this->assertEquals(2, $tc->create(array('title' => 'T2', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 1)));
|
||||
$this->assertEquals(3, $tc->create(array('title' => 'T3', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
|
||||
|
||||
$this->assertNotFalse($pd->duplicate(1, array('category', 'action', 'task')));
|
||||
$project = $p->getByName('P1 (Clone)');
|
||||
$this->assertNotFalse($project);
|
||||
$project_id = $project['id'];
|
||||
$this->assertEquals(2, $pd->duplicate(1, array('projectPermission', 'swimlane', 'task')));
|
||||
|
||||
// Check if Swimlanes have NOT been duplicated
|
||||
$this->assertCount(0, $s->getAll($project_id));
|
||||
// Check if Swimlanes have been duplicated
|
||||
$swimlanes = $s->getAll(2);
|
||||
$this->assertCount(3, $swimlanes);
|
||||
$this->assertEquals(4, $swimlanes[0]['id']);
|
||||
$this->assertEquals('S1', $swimlanes[0]['name']);
|
||||
$this->assertEquals(5, $swimlanes[1]['id']);
|
||||
$this->assertEquals('S2', $swimlanes[1]['name']);
|
||||
$this->assertEquals(6, $swimlanes[2]['id']);
|
||||
$this->assertEquals('S3', $swimlanes[2]['name']);
|
||||
|
||||
$swimlane = $s->getDefault(2);
|
||||
$this->assertEquals('New Default', $swimlane['default_swimlane']);
|
||||
|
||||
// Check if Tasks have been duplicated
|
||||
$tasks = $tf->getAll($project_id);
|
||||
$tasks = $tf->getAll(2);
|
||||
|
||||
$this->assertCount(3, $tasks);
|
||||
//$this->assertEquals(4, $tasks[0]['id']);
|
||||
$this->assertEquals('T1', $tasks[0]['title']);
|
||||
//$this->assertEquals(5, $tasks[1]['id']);
|
||||
$this->assertEquals('T2', $tasks[1]['title']);
|
||||
//$this->assertEquals(6, $tasks[2]['id']);
|
||||
$this->assertEquals('T3', $tasks[2]['title']);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue