Task duplication

This commit is contained in:
Frédéric Guillot 2014-03-19 21:49:39 -04:00
parent 41dd9b2eff
commit ab63ffafc5
9 changed files with 344 additions and 25 deletions

View File

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

View File

@ -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',
);

View File

@ -255,4 +255,6 @@ return array(
// 'Position' => '',
// 'Move Up' => '',
// 'Move Down' => '',
// 'Duplicate to another project' => '',
// 'Duplicate' => '',
);

View File

@ -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'),
);

View File

@ -16,6 +16,11 @@ use \SimpleValidator\Validators;
*/
class Board extends Base
{
/**
* SQL table name
*
* @var string
*/
const TABLE = 'columns';
/**

View File

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

View File

@ -8,8 +8,7 @@
<?= Helper\form_label(t('Title'), 'title') ?>
<?= Helper\form_text('title', $values, $errors, array('autofocus required')) ?><br/>
<?= Helper\form_label(t('Project'), 'project_id') ?>
<?= Helper\form_select('project_id', $projects_list, $values, $errors) ?><br/>
<?= Helper\form_hidden('project_id', $values) ?>
<?= Helper\form_label(t('Column'), 'column_id') ?>
<?= Helper\form_select('column_id', $columns_list, $values, $errors) ?><br/>
@ -30,7 +29,9 @@
<?= Helper\form_textarea('description', $values, $errors) ?><br/>
<div class="form-help"><a href="http://kanboard.net/documentation/syntax-guide" target="_blank" rel="noreferrer"><?= t('Write your text in Markdown') ?></a></div>
<?= Helper\form_checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?>
<?php if (! isset($duplicate)): ?>
<?= Helper\form_checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?>
<?php endif ?>
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>

View File

@ -3,6 +3,8 @@
<h2>#<?= $task['id'] ?> - <?= Helper\escape($task['title']) ?></h2>
<ul>
<li><a href="?controller=board&amp;action=show&amp;project_id=<?= $task['project_id'] ?>"><?= t('Back to the board') ?></a></li>
<li><a href="?controller=task&amp;action=duplicate&amp;project_id=<?= $task['project_id'] ?>&amp;task_id=<?= $task['id'] ?>"><?= t('Duplicate') ?></a></li>
<!-- <li><a href="#"><?= t('Duplicate to another project') ?></a></li> -->
</ul>
</div>
<section>

View File

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