From ab63ffafc565e75c73c27910abd465bebb09306e Mon Sep 17 00:00:00 2001 From: Frédéric Guillot Date: Wed, 19 Mar 2014 21:49:39 -0400 Subject: Task duplication --- controllers/task.php | 109 ++++++++++++++++++--- locales/fr_FR/translations.php | 2 + locales/pl_PL/translations.php | 2 + models/acl.php | 2 +- models/board.php | 5 + models/task.php | 214 +++++++++++++++++++++++++++++++++++++++-- templates/task_new.php | 7 +- templates/task_show.php | 2 + tests/TaskTest.php | 26 +++++ 9 files changed, 344 insertions(+), 25 deletions(-) diff --git a/controllers/task.php b/controllers/task.php index 75063b7b..0dae014e 100644 --- a/controllers/task.php +++ b/controllers/task.php @@ -4,9 +4,19 @@ namespace Controller; require_once __DIR__.'/base.php'; +/** + * Task controller + * + * @package controller + * @author Frederic Guillot + */ class Task extends Base { - // Webhook to create a task (useful for external software) + /** + * Webhook to create a task (useful for external software) + * + * @access public + */ public function add() { $token = $this->request->getStringParam('token'); @@ -41,7 +51,11 @@ class Task extends Base $this->response->text('FAILED'); } - // Display the template show task, common between different task view + /** + * Display the template show task, common between different task view + * + * @access public + */ private function showTask(array $task, array $comment_form = array(), array $description_form = array()) { if (empty($comment_form)) { @@ -77,7 +91,11 @@ class Task extends Base ))); } - // Show a task + /** + * Show a task + * + * @access public + */ public function show() { $task = $this->task->getById($this->request->getIntegerParam('task_id'), true); @@ -88,7 +106,11 @@ class Task extends Base $this->showTask($task); } - // Add a comment + /** + * Add a comment + * + * @access public + */ public function comment() { $task = $this->task->getById($this->request->getIntegerParam('task_id'), true); @@ -117,7 +139,11 @@ class Task extends Base ); } - // Add a description from the show task page + /** + * Add a description from the show task page + * + * @access public + */ public function description() { $task = $this->task->getById($this->request->getIntegerParam('task_id'), true); @@ -147,7 +173,11 @@ class Task extends Base ); } - // Display a form to create a new task + /** + * Display a form to create a new task + * + * @access public + */ public function create() { $project_id = $this->request->getIntegerParam('project_id'); @@ -171,7 +201,11 @@ class Task extends Base ))); } - // Validate and save a new task + /** + * Validate and save a new task + * + * @access public + */ public function save() { $values = $this->request->getValues(); @@ -210,7 +244,11 @@ class Task extends Base ))); } - // Display a form to edit a task + /** + * Display a form to edit a task + * + * @access public + */ public function edit() { $task = $this->task->getById($this->request->getIntegerParam('task_id')); @@ -233,7 +271,11 @@ class Task extends Base ))); } - // Validate and update a task + /** + * Validate and update a task + * + * @access public + */ public function update() { $values = $this->request->getValues(); @@ -263,7 +305,11 @@ class Task extends Base ))); } - // Hide a task + /** + * Hide a task + * + * @access public + */ public function close() { $task = $this->task->getById($this->request->getIntegerParam('task_id')); @@ -280,7 +326,11 @@ class Task extends Base $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); } - // Confirmation dialog before to close a task + /** + * Confirmation dialog before to close a task + * + * @access public + */ public function confirmClose() { $task = $this->task->getById($this->request->getIntegerParam('task_id')); @@ -295,7 +345,11 @@ class Task extends Base ))); } - // Open a task + /** + * Open a task + * + * @access public + */ public function open() { $task = $this->task->getById($this->request->getIntegerParam('task_id')); @@ -312,7 +366,11 @@ class Task extends Base $this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']); } - // Confirmation dialog before to open a task + /** + * Confirmation dialog before to open a task + * + * @access public + */ public function confirmOpen() { $task = $this->task->getById($this->request->getIntegerParam('task_id')); @@ -326,4 +384,29 @@ class Task extends Base 'title' => t('Open a task') ))); } + + /** + * Duplicate a task (fill the form for a new task) + * + * @access public + */ + public function duplicate() + { + $task = $this->task->getById($this->request->getIntegerParam('task_id')); + + if (! $task) $this->notfound(); + $this->checkProjectPermissions($task['project_id']); + + $this->response->html($this->template->layout('task_new', array( + 'errors' => array(), + 'values' => $task, + 'projects_list' => $this->project->getListByStatus(\Model\Project::ACTIVE), + 'columns_list' => $this->board->getColumnsList($task['project_id']), + 'users_list' => $this->project->getUsersList($task['project_id']), + 'colors_list' => $this->task->getColors(), + 'duplicate' => true, + 'menu' => 'tasks', + 'title' => t('New task') + ))); + } } diff --git a/locales/fr_FR/translations.php b/locales/fr_FR/translations.php index 3b84029e..4c604e28 100644 --- a/locales/fr_FR/translations.php +++ b/locales/fr_FR/translations.php @@ -252,4 +252,6 @@ return array( 'Position' => 'Position', 'Move Up' => 'Déplacer vers le haut', 'Move Down' => 'Déplacer vers le bas', + 'Duplicate to another project' => 'Dupliquer dans un autre projet', + 'Duplicate' => 'Dupliquer', ); diff --git a/locales/pl_PL/translations.php b/locales/pl_PL/translations.php index 458ad25d..c030bebf 100644 --- a/locales/pl_PL/translations.php +++ b/locales/pl_PL/translations.php @@ -255,4 +255,6 @@ return array( // 'Position' => '', // 'Move Up' => '', // 'Move Down' => '', + // 'Duplicate to another project' => '', + // 'Duplicate' => '', ); diff --git a/models/acl.php b/models/acl.php index 25386254..fe23bbb4 100644 --- a/models/acl.php +++ b/models/acl.php @@ -18,7 +18,7 @@ class Acl extends Base 'app' => array('index'), 'board' => array('index', 'show', 'assign', 'assigntask', 'save'), 'project' => array('tasks', 'index', 'forbidden'), - 'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'confirmclose', 'open', 'confirmopen', 'comment', 'description'), + 'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'confirmclose', 'open', 'confirmopen', 'comment', 'description', 'duplicate'), 'user' => array('index', 'edit', 'update', 'forbidden', 'logout', 'index'), 'config' => array('index'), ); diff --git a/models/board.php b/models/board.php index ae306508..723ffd85 100644 --- a/models/board.php +++ b/models/board.php @@ -16,6 +16,11 @@ use \SimpleValidator\Validators; */ class Board extends Base { + /** + * SQL table name + * + * @var string + */ const TABLE = 'columns'; /** diff --git a/models/task.php b/models/task.php index fe0f6350..cbe37feb 100644 --- a/models/task.php +++ b/models/task.php @@ -8,9 +8,34 @@ require_once __DIR__.'/comment.php'; use \SimpleValidator\Validator; use \SimpleValidator\Validators; +/** + * Task model + * + * @package model + * @author Frederic Guillot + */ class Task extends Base { + /** + * SQL table name + * + * @var string + */ const TABLE = 'tasks'; + + /** + * Task status + * + * @var integer + */ + const STATUS_OPEN = 1; + const STATUS_CLOSED = 0; + + /** + * Events + * + * @var string + */ const EVENT_MOVE_COLUMN = 'task.move.column'; const EVENT_MOVE_POSITION = 'task.move.position'; const EVENT_UPDATE = 'task.update'; @@ -18,6 +43,12 @@ class Task extends Base const EVENT_CLOSE = 'task.close'; const EVENT_OPEN = 'task.open'; + /** + * Get available colors + * + * @access public + * @return array + */ public function getColors() { return array( @@ -31,6 +62,14 @@ class Task extends Base ); } + /** + * Fetch one task + * + * @access public + * @param integer $task_id Task id + * @param boolean $more If true, fetch all related information + * @return array + */ public function getById($task_id, $more = false) { if ($more) { @@ -67,7 +106,15 @@ class Task extends Base } } - public function getAllByProjectId($project_id, array $status = array(1, 0)) + /** + * Get all tasks for a given project + * + * @access public + * @param integer $project_id Project id + * @param array $status List of status id + * @return array + */ + public function getAllByProjectId($project_id, array $status = array(self::STATUS_OPEN, self::STATUS_CLOSED)) { return $this->db->table(self::TABLE) ->columns( @@ -95,7 +142,15 @@ class Task extends Base ->findAll(); } - public function countByProjectId($project_id, $status = array(1, 0)) + /** + * Count all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param array $status List of status id + * @return integer + */ + public function countByProjectId($project_id, array $status = array(self::STATUS_OPEN, self::STATUS_CLOSED)) { return $this->db ->table(self::TABLE) @@ -104,7 +159,16 @@ class Task extends Base ->count(); } - public function getAllByColumnId($project_id, $column_id, $status = array(1)) + /** + * Get all tasks for a given column + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param array $status List of status id + * @return array + */ + public function getAllByColumnId($project_id, $column_id, array $status = array(self::STATUS_OPEN)) { $tasks = $this->db ->table(self::TABLE) @@ -125,7 +189,16 @@ class Task extends Base return $tasks; } - public function countByColumnId($project_id, $column_id, $status = array(1)) + /** + * Count the number of tasks for a given column and status + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param array $status List of status id + * @return integer + */ + public function countByColumnId($project_id, $column_id, array $status = array(self::STATUS_OPEN)) { return $this->db ->table(self::TABLE) @@ -135,6 +208,55 @@ class Task extends Base ->count(); } + /** + * Duplicate a task + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function duplicate($task_id) + { + $this->db->startTransaction(); + + $boardModel = new Board($this->db, $this->event); + + // Get the original task + $task = $this->getById($task_id); + + // Cleanup data + unset($task['id']); + unset($task['date_completed']); + + // Assign new values + $task['date_creation'] = time(); + $task['is_active'] = 1; + $task['position'] = $this->countByColumnId($task['project_id'], $task['column_id']); + + // Save task + if (! $this->db->table(self::TABLE)->save($task)) { + $this->db->cancelTransaction(); + return false; + } + + $task_id = $this->db->getConnection()->getLastId(); + + $this->db->closeTransaction(); + + // Trigger events + $this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $task); + + return $task_id; + } + + /** + * Duplicate a task to another project (always copy to the first column) + * + * @access public + * @param integer $task_id Task id + * @param integer $project_id Destination project id + * @return boolean + */ public function duplicateToAnotherProject($task_id, $project_id) { $this->db->startTransaction(); @@ -172,6 +294,13 @@ class Task extends Base return $task_id; } + /** + * Create a task + * + * @access public + * @param array $values Form values + * @return boolean + */ public function create(array $values) { $this->db->startTransaction(); @@ -204,6 +333,13 @@ class Task extends Base return $task_id; } + /** + * Update a task + * + * @access public + * @param array $values Form values + * @return boolean + */ public function update(array $values) { // Prepare data @@ -241,7 +377,13 @@ class Task extends Base return $result; } - // Mark a task closed + /** + * Mark a task closed + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ public function close($task_id) { $result = $this->db @@ -259,7 +401,13 @@ class Task extends Base return $result; } - // Mark a task open + /** + * Mark a task open + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ public function open($task_id) { $result = $this->db @@ -277,13 +425,27 @@ class Task extends Base return $result; } - // Remove a task + /** + * Remove a task + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ public function remove($task_id) { return $this->db->table(self::TABLE)->eq('id', $task_id)->remove(); } - // Move a task to another column or to another position + /** + * Move a task to another column or to another position + * + * @access public + * @param integer $task_id Task id + * @param integer $column_id Column id + * @param integer $position Position (must be greater than 1) + * @return boolean + */ public function move($task_id, $column_id, $position) { return $this->update(array( @@ -293,6 +455,13 @@ class Task extends Base )); } + /** + * Validate task creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ public function validateCreation(array $values) { $v = new Validator($values, array( @@ -314,6 +483,13 @@ class Task extends Base ); } + /** + * Validate description creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ public function validateDescriptionCreation(array $values) { $v = new Validator($values, array( @@ -328,6 +504,13 @@ class Task extends Base ); } + /** + * Validate task modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ public function validateModification(array $values) { $v = new Validator($values, array( @@ -351,6 +534,13 @@ class Task extends Base ); } + /** + * Validate assignee change + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ public function validateAssigneeModification(array $values) { $v = new Validator($values, array( @@ -368,6 +558,14 @@ class Task extends Base ); } + /** + * Parse a date (different format for each locale) to a timestamp + * + * @access public + * @param string $value Date to parse + * @param string $format Date format + * @return integer + */ public function getTimestampFromDate($value, $format) { $date = \DateTime::createFromFormat($format, $value); diff --git a/templates/task_new.php b/templates/task_new.php index 88ffd53f..4280bf80 100644 --- a/templates/task_new.php +++ b/templates/task_new.php @@ -8,8 +8,7 @@
- -
+
@@ -30,7 +29,9 @@
- + + +
diff --git a/templates/task_show.php b/templates/task_show.php index cc1d9e25..986b240d 100644 --- a/templates/task_show.php +++ b/templates/task_show.php @@ -3,6 +3,8 @@

# -

diff --git a/tests/TaskTest.php b/tests/TaskTest.php index a3417e91..5ddb1860 100644 --- a/tests/TaskTest.php +++ b/tests/TaskTest.php @@ -20,6 +20,32 @@ class TaskTest extends Base $this->assertEquals(0, $t->getTimestampFromDate('5-3-2014', 'd/m/Y')); } + public function testDuplicateTask() + { + $t = new Task($this->db, $this->event); + $p = new Project($this->db, $this->event); + + // We create a task and a project + $this->assertEquals(1, $p->create(array('name' => 'test1'))); + $this->assertEquals(1, $t->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1))); + + $task = $t->getById(1); + $this->assertNotEmpty($task); + $this->assertEquals(0, $task['position']); + + // We duplicate our task + $this->assertEquals(2, $t->duplicate(1)); + $this->assertEquals(Task::EVENT_CREATE, $this->event->getLastTriggeredEvent()); + + // Check the values of the duplicated task + $task = $t->getById(2); + $this->assertNotEmpty($task); + $this->assertEquals(Task::STATUS_OPEN, $task['is_active']); + $this->assertEquals(1, $task['project_id']); + $this->assertEquals(1, $task['owner_id']); + $this->assertEquals(1, $task['position']); + } + public function testDuplicateToAnotherProject() { $t = new Task($this->db, $this->event); -- cgit v1.2.3