When creating a new project, have the possibility to select another project to duplicate

This commit is contained in:
Frederic Guillot
2016-01-31 17:46:19 -05:00
parent 1500ff92b2
commit fc21d3873e
30 changed files with 694 additions and 342 deletions

View File

@@ -10,6 +10,7 @@ Breaking changes:
New features: 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 a "Me" button to assignee form element
* Add external links for tasks with plugin api * Add external links for tasks with plugin api
* Add project owner (Directly Responsible Individual) * Add project owner (Directly Responsible Individual)

View File

@@ -1,6 +1,6 @@
BUILD_DIR = /tmp 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_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)) 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))

View File

@@ -11,18 +11,19 @@ use Symfony\Component\Console\Command\Command;
* @package console * @package console
* @author Frederic Guillot * @author Frederic Guillot
* *
* @property \Kanboard\Model\Notification $notification * @property \Kanboard\Model\Notification $notification
* @property \Kanboard\Model\Project $project * @property \Kanboard\Model\Project $project
* @property \Kanboard\Model\ProjectPermission $projectPermission * @property \Kanboard\Model\ProjectPermission $projectPermission
* @property \Kanboard\Model\ProjectAnalytic $projectAnalytic * @property \Kanboard\Model\ProjectAnalytic $projectAnalytic
* @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats * @property \Kanboard\Model\ProjectDailyColumnStats $projectDailyColumnStats
* @property \Kanboard\Model\ProjectDailyStats $projectDailyStats * @property \Kanboard\Model\ProjectDailyStats $projectDailyStats
* @property \Kanboard\Model\SubtaskExport $subtaskExport * @property \Kanboard\Model\SubtaskExport $subtaskExport
* @property \Kanboard\Model\OverdueNotification $overdueNotification * @property \Kanboard\Model\OverdueNotification $overdueNotification
* @property \Kanboard\Model\Task $task * @property \Kanboard\Model\Task $task
* @property \Kanboard\Model\TaskExport $taskExport * @property \Kanboard\Model\TaskExport $taskExport
* @property \Kanboard\Model\TaskFinder $taskFinder * @property \Kanboard\Model\TaskFinder $taskFinder
* @property \Kanboard\Model\Transition $transition * @property \Kanboard\Model\Transition $transition
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
*/ */
abstract class Base extends Command abstract class Base extends Command
{ {

View File

@@ -171,14 +171,15 @@ class Project extends Base
$project = $this->getProject(); $project = $this->getProject();
if ($this->request->getStringParam('duplicate') === 'yes') { if ($this->request->getStringParam('duplicate') === 'yes') {
$values = array_keys($this->request->getValues()); $project_id = $this->projectDuplication->duplicate($project['id'], array_keys($this->request->getValues()), $this->userSession->getId());
if ($this->projectDuplication->duplicate($project['id'], $values) !== false) {
if ($project_id !== false) {
$this->flash->success(t('Project cloned successfully.')); $this->flash->success(t('Project cloned successfully.'));
} else { } else {
$this->flash->failure(t('Unable to clone this project.')); $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( $this->response->html($this->projectLayout('project/duplicate', array(
@@ -240,57 +241,4 @@ class Project extends Base
'title' => t('Project activation') '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);
}
} }

View File

@@ -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
);
}
}

View File

@@ -89,11 +89,11 @@ class ProjectEdit extends Base
{ {
if ($redirect === 'edit') { if ($redirect === 'edit') {
if (isset($values['is_private'])) { 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']); unset($values['is_private']);
} }
} elseif ($project['is_private'] == 1 && ! isset($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); $values += array('is_private' => 0);
} }
} }

View File

@@ -12,6 +12,24 @@ use Kanboard\Core\Security\Role;
*/ */
class ProjectPermission extends Base 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 * Show all permissions
* *
@@ -62,6 +80,7 @@ class ProjectPermission extends Base
*/ */
public function addUser() public function addUser()
{ {
$project = $this->getProject();
$values = $this->request->getValues(); $values = $this->request->getValues();
if ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) { 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->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() public function removeUser()
{ {
$this->checkCSRFParam(); $this->checkCSRFParam();
$project = $this->getProject();
$user_id = $this->request->getIntegerParam('user_id');
$values = array( if ($this->projectUserRole->removeUser($project['id'], $user_id)) {
'project_id' => $this->request->getIntegerParam('project_id'),
'user_id' => $this->request->getIntegerParam('user_id'),
);
if ($this->projectUserRole->removeUser($values['project_id'], $values['user_id'])) {
$this->flash->success(t('Project updated successfully.')); $this->flash->success(t('Project updated successfully.'));
} else { } else {
$this->flash->failure(t('Unable to update this project.')); $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() public function changeUserRole()
{ {
$project_id = $this->request->getIntegerParam('project_id'); $project = $this->getProject();
$values = $this->request->getJson(); $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')); $this->response->json(array('status' => 'ok'));
} else { } else {
$this->response->json(array('status' => 'error')); $this->response->json(array('status' => 'error'));
@@ -120,19 +136,20 @@ class ProjectPermission extends Base
*/ */
public function addGroup() public function addGroup()
{ {
$project = $this->getProject();
$values = $this->request->getValues(); $values = $this->request->getValues();
if (empty($values['group_id']) && ! empty($values['external_id'])) { if (empty($values['group_id']) && ! empty($values['external_id'])) {
$values['group_id'] = $this->group->create($values['name'], $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.')); $this->flash->success(t('Project updated successfully.'));
} else { } else {
$this->flash->failure(t('Unable to update this project.')); $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() public function removeGroup()
{ {
$this->checkCSRFParam(); $this->checkCSRFParam();
$project = $this->getProject();
$group_id = $this->request->getIntegerParam('group_id');
$values = array( if ($this->projectGroupRole->removeGroup($project['id'], $group_id)) {
'project_id' => $this->request->getIntegerParam('project_id'),
'group_id' => $this->request->getIntegerParam('group_id'),
);
if ($this->projectGroupRole->removeGroup($values['project_id'], $values['group_id'])) {
$this->flash->success(t('Project updated successfully.')); $this->flash->success(t('Project updated successfully.'));
} else { } else {
$this->flash->failure(t('Unable to update this project.')); $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() public function changeGroupRole()
{ {
$project_id = $this->request->getIntegerParam('project_id'); $project = $this->getProject();
$values = $this->request->getJson(); $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')); $this->response->json(array('status' => 'ok'));
} else { } else {
$this->response->json(array('status' => 'error')); $this->response->json(array('status' => 'error'));

View File

@@ -109,7 +109,7 @@ class Taskduplication extends Base
private function chooseDestination(array $task, $template) private function chooseDestination(array $task, $template)
{ {
$values = array(); $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']]); unset($projects_list[$task['project_id']]);

View File

@@ -120,8 +120,6 @@ class ExternalLinkManager extends Base
*/ */
public function find() public function find()
{ {
$provider = null;
if ($this->userInputType === self::TYPE_AUTO) { if ($this->userInputType === self::TYPE_AUTO) {
$provider = $this->findProvider(); $provider = $this->findProvider();
} else { } else {

View File

@@ -2,6 +2,8 @@
namespace Kanboard\Model; namespace Kanboard\Model;
use Kanboard\Core\Security\Role;
/** /**
* Project Duplication * Project Duplication
* *
@@ -11,6 +13,28 @@ namespace Kanboard\Model;
*/ */
class ProjectDuplication extends Base 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 * Get a valid project name for the duplication
* *
@@ -30,79 +54,107 @@ class ProjectDuplication extends Base
return $name.$suffix; 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 * Create a project from another one
* *
* @param integer $project_id Project Id * @access private
* @return integer Cloned Project Id * @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( $values = array(
'name' => $this->getClonedProjectName($project['name']), 'name' => $name ?: $this->getClonedProjectName($project['name']),
'is_active' => true, 'is_active' => 1,
'last_modified' => 0, 'last_modified' => time(),
'token' => '', 'token' => '',
'is_public' => 0, '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)) { if (! $this->db->table(Project::TABLE)->save($values)) {
return 0; return false;
} }
return $this->db->getLastId(); 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 * @access private
* @param array $part_selection Selection of optional project parts to duplicate. Possible options: 'swimlane', 'action', 'category', 'task' * @param integer $dst_project_id
* @return integer Cloned 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 if (! $this->projectUserRole->addUser($dst_project_id, $owner_id, Role::PROJECT_MANAGER)) {
$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();
return false; return false;
} }
} }
$this->db->closeTransaction(); return true;
// 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;
} }
} }

View File

@@ -199,4 +199,25 @@ class Task extends Base
return round(($position * 100) / count($columns), 1); 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;
}
} }

View File

@@ -179,6 +179,23 @@ class TaskFinder extends Base
->findAll(); ->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 * Get overdue tasks query
* *

View File

@@ -128,8 +128,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('Gantt', array('projects', 'saveProjectDate'), Role::APP_MANAGER); $acl->add('Gantt', array('projects', 'saveProjectDate'), Role::APP_MANAGER);
$acl->add('Group', '*', Role::APP_ADMIN); $acl->add('Group', '*', Role::APP_ADMIN);
$acl->add('Link', '*', Role::APP_ADMIN); $acl->add('Link', '*', Role::APP_ADMIN);
$acl->add('Project', array('users', 'allowEverybody', 'allow', 'role', 'revoke', 'create'), Role::APP_MANAGER); $acl->add('ProjectCreation', 'create', Role::APP_MANAGER);
$acl->add('ProjectPermission', '*', Role::APP_USER);
$acl->add('Projectuser', '*', Role::APP_MANAGER); $acl->add('Projectuser', '*', Role::APP_MANAGER);
$acl->add('Twofactor', 'disable', Role::APP_ADMIN); $acl->add('Twofactor', 'disable', Role::APP_ADMIN);
$acl->add('UserImport', '*', Role::APP_ADMIN); $acl->add('UserImport', '*', Role::APP_ADMIN);

View File

@@ -43,10 +43,12 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('search', 'search', 'index'); $container['route']->addRoute('search', 'search', 'index');
$container['route']->addRoute('search/: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 // Project routes
$container['route']->addRoute('projects', 'project', 'index'); $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('project/:project_id', 'project', 'show');
$container['route']->addRoute('p/:project_id', 'project', 'show'); $container['route']->addRoute('p/:project_id', 'project', 'show');
$container['route']->addRoute('project/:project_id/customer-filter', 'customfilter', 'index'); $container['route']->addRoute('project/:project_id/customer-filter', 'customfilter', 'index');

View File

@@ -1,15 +1,15 @@
<section id="main"> <section id="main">
<div class="page-header page-header-mobile"> <div class="page-header page-header-mobile">
<ul> <ul>
<?php if ($this->user->hasAccess('project', 'create')): ?> <?php if ($this->user->hasAccess('ProjectCreation', 'create')): ?>
<li> <li>
<i class="fa fa-plus fa-fw"></i> <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> </li>
<?php endif ?> <?php endif ?>
<li> <li>
<i class="fa fa-lock fa-fw"></i> <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>
<li> <li>
<i class="fa fa-search fa-fw"></i> <i class="fa fa-search fa-fw"></i>

View File

@@ -1,12 +1,6 @@
<section id="main"> <section id="main">
<div class="page-header"> <div class="page-header">
<ul> <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> <li>
<i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('Projects list'), 'project', 'index') ?> <i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('Projects list'), 'project', 'index') ?>
</li> </li>

View File

@@ -31,14 +31,26 @@
</select> </select>
</li> </li>
<?php endif ?> <?php endif ?>
<li> <li class="user-links">
<?php if ($this->user->hasNotifications()): ?> <?php if ($this->user->hasNotifications()): ?>
<span class="notification"> <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')) ?> <?= $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> </span>
<?php endif ?> <?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> <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> <ul>
<li class="no-hover"><strong><?= $this->e($this->user->getFullname()) ?></strong></li> <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('My profile'), 'user', 'show', array('user_id' => $this->user->getId())) ?></li>
<li><?= $this->url->link(t('Logout'), 'auth', 'logout') ?></li> <li><?= $this->url->link(t('Logout'), 'auth', 'logout') ?></li>
</ul> </ul>
</span> </div>
</li> </li>
</ul> </ul>
</nav> </nav>

View File

@@ -10,13 +10,17 @@
<?= $this->form->csrf() ?> <?= $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('category', t('Categories'), 1, true) ?>
<?= $this->form->checkbox('action', t('Actions'), 1, true) ?> <?= $this->form->checkbox('action', t('Actions'), 1, true) ?>
<?= $this->form->checkbox('swimlane', t('Swimlanes'), 1, false) ?> <?= $this->form->checkbox('swimlane', t('Swimlanes'), 1, false) ?>
<?= $this->form->checkbox('task', t('Tasks'), 1, false) ?> <?= $this->form->checkbox('task', t('Tasks'), 1, false) ?>
<div class="form-actions"> <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'])) ?> <?= t('or') ?> <?= $this->url->link(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?>
</div> </div>
</form> </form>

View File

@@ -1,10 +1,6 @@
<section id="main"> <section id="main">
<div class="page-header"> <div class="page-header">
<ul> <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')): ?> <?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> <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('Users overview'), 'projectuser', 'managers') ?></li>
<?php endif ?> <?php endif ?>

View File

@@ -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>

View File

@@ -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>

View File

@@ -24,7 +24,7 @@
<?= $this->form->select('owner_id', $owners, $values, $errors) ?> <?= $this->form->select('owner_id', $owners, $values, $errors) ?>
</div> </div>
<?php if ($this->user->hasProjectAccess('project', 'create', $project['id'])): ?> <?php if ($this->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])): ?>
<hr> <hr>
<?= $this->form->checkbox('is_private', t('Private project'), 1, $project['is_private'] == 1) ?> <?= $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> <p class="form-help"><?= t('Private projects do not have users and groups management.') ?></p>

View File

@@ -1,13 +1,6 @@
<section id="main"> <section id="main">
<div class="page-header"> <div class="page-header">
<ul> <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> <li>
<i class="fa fa-folder fa-fw"></i> <i class="fa fa-folder fa-fw"></i>
<?= $this->url->link(t('Projects list'), 'project', 'index') ?> <?= $this->url->link(t('Projects list'), 'project', 'index') ?>

File diff suppressed because one or more lines are too long

View File

@@ -55,6 +55,11 @@ nav .active a {
color: #d40000; color: #d40000;
} }
/* user links on the left */
header .user-links .dropdown {
margin-left: 15px;
}
/* title tooltip */ /* title tooltip */
header h1 .tooltip { header h1 .tooltip {
opacity: 0.3; opacity: 0.3;

View File

@@ -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

View File

@@ -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();
}
});
}; };

View File

@@ -36,8 +36,8 @@ class UserHelperTest extends Base
); );
$this->assertTrue($helper->hasAccess('user', 'create')); $this->assertTrue($helper->hasAccess('user', 'create'));
$this->assertTrue($helper->hasAccess('project', 'create')); $this->assertTrue($helper->hasAccess('ProjectCreation', 'create'));
$this->assertTrue($helper->hasAccess('project', 'createPrivate')); $this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate'));
} }
public function testHasAccessForManagers() public function testHasAccessForManagers()
@@ -50,8 +50,8 @@ class UserHelperTest extends Base
); );
$this->assertFalse($helper->hasAccess('user', 'create')); $this->assertFalse($helper->hasAccess('user', 'create'));
$this->assertTrue($helper->hasAccess('project', 'create')); $this->assertTrue($helper->hasAccess('ProjectCreation', 'create'));
$this->assertTrue($helper->hasAccess('project', 'createPrivate')); $this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate'));
} }
public function testHasAccessForUsers() public function testHasAccessForUsers()
@@ -64,8 +64,8 @@ class UserHelperTest extends Base
); );
$this->assertFalse($helper->hasAccess('user', 'create')); $this->assertFalse($helper->hasAccess('user', 'create'));
$this->assertFalse($helper->hasAccess('project', 'create')); $this->assertFalse($helper->hasAccess('ProjectCreation', 'create'));
$this->assertTrue($helper->hasAccess('project', 'createPrivate')); $this->assertTrue($helper->hasAccess('ProjectCreation', 'createPrivate'));
} }
public function testHasProjectAccessForAdmins() public function testHasProjectAccessForAdmins()

View File

@@ -6,8 +6,11 @@ use Kanboard\Model\Action;
use Kanboard\Model\Project; use Kanboard\Model\Project;
use Kanboard\Model\Category; use Kanboard\Model\Category;
use Kanboard\Model\ProjectUserRole; use Kanboard\Model\ProjectUserRole;
use Kanboard\Model\ProjectGroupRole;
use Kanboard\Model\ProjectDuplication; use Kanboard\Model\ProjectDuplication;
use Kanboard\Model\User; use Kanboard\Model\User;
use Kanboard\Model\Group;
use Kanboard\Model\GroupMember;
use Kanboard\Model\Swimlane; use Kanboard\Model\Swimlane;
use Kanboard\Model\Task; use Kanboard\Model\Task;
use Kanboard\Model\TaskCreation; use Kanboard\Model\TaskCreation;
@@ -16,7 +19,14 @@ use Kanboard\Core\Security\Role;
class ProjectDuplicationTest extends Base 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); $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))); $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() public function testClonePublicProject()
{ {
$p = new Project($this->container); $p = new Project($this->container);
@@ -53,8 +50,10 @@ class ProjectDuplicationTest extends Base
$project = $p->getById(2); $project = $p->getById(2);
$this->assertNotEmpty($project); $this->assertNotEmpty($project);
$this->assertEquals('Public (Clone)', $project['name']); $this->assertEquals('Public (Clone)', $project['name']);
$this->assertEquals(1, $project['is_active']);
$this->assertEquals(0, $project['is_private']); $this->assertEquals(0, $project['is_private']);
$this->assertEquals(0, $project['is_public']); $this->assertEquals(0, $project['is_public']);
$this->assertEquals(0, $project['owner_id']);
$this->assertEmpty($project['token']); $this->assertEmpty($project['token']);
} }
@@ -62,6 +61,7 @@ class ProjectDuplicationTest extends Base
{ {
$p = new Project($this->container); $p = new Project($this->container);
$pd = new ProjectDuplication($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(1, $p->create(array('name' => 'Private', 'is_private' => 1), 1, true));
$this->assertEquals(2, $pd->duplicate(1)); $this->assertEquals(2, $pd->duplicate(1));
@@ -69,14 +69,112 @@ class ProjectDuplicationTest extends Base
$project = $p->getById(2); $project = $p->getById(2);
$this->assertNotEmpty($project); $this->assertNotEmpty($project);
$this->assertEquals('Private (Clone)', $project['name']); $this->assertEquals('Private (Clone)', $project['name']);
$this->assertEquals(1, $project['is_active']);
$this->assertEquals(1, $project['is_private']); $this->assertEquals(1, $project['is_private']);
$this->assertEquals(0, $project['is_public']); $this->assertEquals(0, $project['is_public']);
$this->assertEquals(0, $project['owner_id']);
$this->assertEmpty($project['token']); $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)); public function testCloneSharedProject()
$this->assertEquals(array(1 => 'admin'), $pp->getAssignableUsers(2)); {
$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() public function testCloneProjectWithCategories()
@@ -98,16 +196,9 @@ class ProjectDuplicationTest extends Base
$this->assertEquals('P1 (Clone)', $project['name']); $this->assertEquals('P1 (Clone)', $project['name']);
$categories = $c->getAll(2); $categories = $c->getAll(2);
$this->assertNotempty($categories); $this->assertCount(3, $categories);
$this->assertEquals(3, count($categories));
$this->assertEquals(4, $categories[0]['id']);
$this->assertEquals('C1', $categories[0]['name']); $this->assertEquals('C1', $categories[0]['name']);
$this->assertEquals(5, $categories[1]['id']);
$this->assertEquals('C2', $categories[1]['name']); $this->assertEquals('C2', $categories[1]['name']);
$this->assertEquals(6, $categories[2]['id']);
$this->assertEquals('C3', $categories[2]['name']); $this->assertEquals('C3', $categories[2]['name']);
} }
@@ -119,28 +210,115 @@ class ProjectDuplicationTest extends Base
$u = new User($this->container); $u = new User($this->container);
$pd = new ProjectDuplication($this->container); $pd = new ProjectDuplication($this->container);
$this->assertEquals(2, $u->create(array('username' => 'unittest1', 'password' => 'unittest'))); $this->assertEquals(2, $u->create(array('username' => 'user1')));
$this->assertEquals(3, $u->create(array('username' => 'unittest2', 'password' => 'unittest'))); $this->assertEquals(3, $u->create(array('username' => 'user2')));
$this->assertEquals(4, $u->create(array('username' => 'unittest3', 'password' => 'unittest'))); $this->assertEquals(4, $u->create(array('username' => 'user3')));
$this->assertEquals(1, $p->create(array('name' => 'P1'))); $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, 3, Role::PROJECT_MEMBER));
$this->assertTrue($pp->addUser(1, 4, Role::PROJECT_MANAGER)); $this->assertTrue($pp->addUser(1, 4, Role::PROJECT_VIEWER));
$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->assertEquals(2, $pd->duplicate(1)); $this->assertEquals(2, $pd->duplicate(1));
$project = $p->getById(2); $this->assertCount(3, $pp->getUsers(2));
$this->assertNotEmpty($project); $this->assertEquals(Role::PROJECT_MANAGER, $pp->getUserRole(2, 2));
$this->assertEquals('P1 (Clone)', $project['name']);
$this->assertEquals(3, count($pp->getUsers(2)));
$this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(2, 2));
$this->assertEquals(Role::PROJECT_MEMBER, $pp->getUserRole(2, 3)); $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() public function testCloneProjectWithActionTaskAssignCurrentUser()
@@ -199,68 +377,6 @@ class ProjectDuplicationTest extends Base
$this->assertEquals(5, $actions[0]['params']['category_id']); $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() public function testCloneProjectWithSwimlanes()
{ {
$p = new Project($this->container); $p = new Project($this->container);
@@ -269,30 +385,22 @@ class ProjectDuplicationTest extends Base
$tc = new TaskCreation($this->container); $tc = new TaskCreation($this->container);
$tf = new TaskFinder($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 // create initial swimlanes
$this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1'))); $this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1')));
$this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2'))); $this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2')));
$this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3'))); $this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3')));
$default_swimlane1 = $s->getDefault(1); // create initial tasks
$default_swimlane1['default_swimlane'] = 'New Default'; $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)); $this->assertEquals(2, $pd->duplicate(1, array('category', 'swimlane')));
//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);
$swimlanes = $s->getAll(2);
$this->assertCount(3, $swimlanes); $this->assertCount(3, $swimlanes);
$this->assertEquals(4, $swimlanes[0]['id']); $this->assertEquals(4, $swimlanes[0]['id']);
$this->assertEquals('S1', $swimlanes[0]['name']); $this->assertEquals('S1', $swimlanes[0]['name']);
@@ -300,11 +408,12 @@ class ProjectDuplicationTest extends Base
$this->assertEquals('S2', $swimlanes[1]['name']); $this->assertEquals('S2', $swimlanes[1]['name']);
$this->assertEquals(6, $swimlanes[2]['id']); $this->assertEquals(6, $swimlanes[2]['id']);
$this->assertEquals('S3', $swimlanes[2]['name']); $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 $swimlane = $s->getDefault(2);
$this->assertCount(0, $tf->getAll($project_id)); $this->assertEquals('New Default', $swimlane['default_swimlane']);
// Check if tasks are NOT been duplicated
$this->assertCount(0, $tf->getAll(2));
} }
public function testCloneProjectWithTasks() public function testCloneProjectWithTasks()
@@ -317,38 +426,62 @@ class ProjectDuplicationTest extends Base
$this->assertEquals(1, $p->create(array('name' => 'P1'))); $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 // create initial swimlanes
$this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1'))); $this->assertEquals(1, $s->create(array('project_id' => 1, 'name' => 'S1')));
$this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2'))); $this->assertEquals(2, $s->create(array('project_id' => 1, 'name' => 'S2')));
$this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3'))); $this->assertEquals(3, $s->create(array('project_id' => 1, 'name' => 'S3')));
$default_swimlane1 = $s->getDefault(1); // create initial tasks
$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(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(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->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'))); $this->assertEquals(2, $pd->duplicate(1, array('projectPermission', 'swimlane', 'task')));
$project = $p->getByName('P1 (Clone)');
$this->assertNotFalse($project);
$project_id = $project['id'];
// Check if Swimlanes have NOT been duplicated // Check if Swimlanes have been duplicated
$this->assertCount(0, $s->getAll($project_id)); $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 // Check if Tasks have been duplicated
$tasks = $tf->getAll($project_id); $tasks = $tf->getAll(2);
$this->assertCount(3, $tasks); $this->assertCount(3, $tasks);
//$this->assertEquals(4, $tasks[0]['id']);
$this->assertEquals('T1', $tasks[0]['title']); $this->assertEquals('T1', $tasks[0]['title']);
//$this->assertEquals(5, $tasks[1]['id']);
$this->assertEquals('T2', $tasks[1]['title']); $this->assertEquals('T2', $tasks[1]['title']);
//$this->assertEquals(6, $tasks[2]['id']);
$this->assertEquals('T3', $tasks[2]['title']); $this->assertEquals('T3', $tasks[2]['title']);
} }
} }