summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrederic Guillot <fred@kanboard.net>2015-03-01 18:03:58 -0500
committerFrederic Guillot <fred@kanboard.net>2015-03-01 18:03:58 -0500
commit35d99ec5d3e5d18e83537c68831be02d40f36d8e (patch)
tree35a86751ad752be286c9e908211dacfc5d174e59
parentda425e4187e5e8e7feb9783c9d75bcaa9fa0c7ec (diff)
Add subtask position
-rw-r--r--app/Controller/Subtask.php20
-rw-r--r--app/Model/Subtask.php82
-rw-r--r--app/Schema/Mysql.php24
-rw-r--r--app/Schema/Postgres.php24
-rw-r--r--app/Schema/Sqlite.php26
-rw-r--r--app/Template/subtask/show.php12
-rw-r--r--app/Template/task/show.php2
-rw-r--r--tests/units/SubtaskTest.php75
8 files changed, 256 insertions, 9 deletions
diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php
index c7ec00d1..385785a1 100644
--- a/app/Controller/Subtask.php
+++ b/app/Controller/Subtask.php
@@ -185,7 +185,7 @@ class Subtask extends Base
if ($redirect === 'board') {
$this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
-
+
$this->response->html($this->template->render('board/subtasks', array(
'subtasks' => $this->subtask->getAll($task['id']),
'task' => $task,
@@ -259,4 +259,22 @@ class Subtask extends Base
$this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
}
+
+ /**
+ * Move subtask position
+ *
+ * @access public
+ */
+ public function movePosition()
+ {
+ $this->checkCSRFParam();
+ $project_id = $this->request->getIntegerParam('project_id');
+ $task_id = $this->request->getIntegerParam('task_id');
+ $subtask_id = $this->request->getIntegerParam('subtask_id');
+ $direction = $this->request->getStringParam('direction');
+ $method = $direction === 'up' ? 'moveUp' : 'moveDown';
+
+ $this->subtask->$method($task_id, $subtask_id);
+ $this->response->redirect($this->helper->url('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks');
+ }
}
diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php
index aa4a6f81..2d108eab 100644
--- a/app/Model/Subtask.php
+++ b/app/Model/Subtask.php
@@ -122,7 +122,7 @@ class Subtask extends Base
->eq('task_id', $task_id)
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
->join(User::TABLE, 'id', 'user_id')
- ->asc(self::TABLE.'.id')
+ ->asc(self::TABLE.'.position')
->filter(array($this, 'addStatusName'))
->findAll();
}
@@ -164,6 +164,22 @@ class Subtask extends Base
}
/**
+ * Get the position of the last column for a given project
+ *
+ * @access public
+ * @param integer $task_id Task id
+ * @return integer
+ */
+ public function getLastPosition($task_id)
+ {
+ return (int) $this->db
+ ->table(self::TABLE)
+ ->eq('task_id', $task_id)
+ ->desc('position')
+ ->findOneColumn('position');
+ }
+
+ /**
* Create a new subtask
*
* @access public
@@ -173,6 +189,8 @@ class Subtask extends Base
public function create(array $values)
{
$this->prepare($values);
+ $values['position'] = $this->getLastPosition($values['task_id']) + 1;
+
$subtask_id = $this->persist(self::TABLE, $values);
if ($subtask_id) {
@@ -209,6 +227,64 @@ class Subtask extends Base
}
/**
+ * Move a subtask down, increment the position value
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $subtask_id
+ * @return boolean
+ */
+ public function moveDown($task_id, $subtask_id)
+ {
+ $subtasks = $this->db->hashtable(self::TABLE)->eq('task_id', $task_id)->asc('position')->getAll('id', 'position');
+ $positions = array_flip($subtasks);
+
+ if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] < count($subtasks)) {
+
+ $position = ++$subtasks[$subtask_id];
+ $subtasks[$positions[$position]]--;
+
+ $this->db->startTransaction();
+ $this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
+ $this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $subtasks[$positions[$position]]));
+ $this->db->closeTransaction();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Move a subtask up, decrement the position value
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $subtask_id
+ * @return boolean
+ */
+ public function moveUp($task_id, $subtask_id)
+ {
+ $subtasks = $this->db->hashtable(self::TABLE)->eq('task_id', $task_id)->asc('position')->getAll('id', 'position');
+ $positions = array_flip($subtasks);
+
+ if (isset($subtasks[$subtask_id]) && $subtasks[$subtask_id] > 1) {
+
+ $position = --$subtasks[$subtask_id];
+ $subtasks[$positions[$position]]++;
+
+ $this->db->startTransaction();
+ $this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
+ $this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $subtasks[$positions[$position]]));
+ $this->db->closeTransaction();
+
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
* Change the status of subtask
*
* Todo -> In progress -> Done -> Todo -> etc...
@@ -286,9 +362,9 @@ class Subtask extends Base
return $this->db->transaction(function ($db) use ($src_task_id, $dst_task_id) {
$subtasks = $db->table(Subtask::TABLE)
- ->columns('title', 'time_estimated')
+ ->columns('title', 'time_estimated', 'position')
->eq('task_id', $src_task_id)
- ->asc('id') // Explicit sorting for postgresql
+ ->asc('position')
->findAll();
foreach ($subtasks as &$subtask) {
diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php
index 1c5cdc3a..21c304c1 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -6,7 +6,29 @@ use PDO;
use Core\Security;
use Model\Link;
-const VERSION = 48;
+const VERSION = 49;
+
+function version_49($pdo)
+{
+ $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
+
+ $task_id = 0;
+ $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
+
+ $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
+ $rq->execute();
+
+ foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
+
+ if ($task_id != $subtask['task_id']) {
+ $position = 1;
+ $task_id = $subtask['task_id'];
+ }
+
+ $urq->execute(array($position, $subtask['id']));
+ $position++;
+ }
+}
function version_48($pdo)
{
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
index aa3f60f7..df003267 100644
--- a/app/Schema/Postgres.php
+++ b/app/Schema/Postgres.php
@@ -6,7 +6,29 @@ use PDO;
use Core\Security;
use Model\Link;
-const VERSION = 29;
+const VERSION = 30;
+
+function version_30($pdo)
+{
+ $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
+
+ $task_id = 0;
+ $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
+
+ $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
+ $rq->execute();
+
+ foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
+
+ if ($task_id != $subtask['task_id']) {
+ $position = 1;
+ $task_id = $subtask['task_id'];
+ }
+
+ $urq->execute(array($position, $subtask['id']));
+ $position++;
+ }
+}
function version_29($pdo)
{
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index 6fc2d07e..9134760f 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -6,7 +6,31 @@ use Core\Security;
use PDO;
use Model\Link;
-const VERSION = 47;
+const VERSION = 48;
+
+function version_48($pdo)
+{
+ $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1');
+
+ // Migrate all subtasks position
+
+ $task_id = 0;
+ $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?');
+
+ $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC');
+ $rq->execute();
+
+ foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) {
+
+ if ($task_id != $subtask['task_id']) {
+ $position = 1;
+ $task_id = $subtask['task_id'];
+ }
+
+ $urq->execute(array($position, $subtask['id']));
+ $position++;
+ }
+}
function version_47($pdo)
{
diff --git a/app/Template/subtask/show.php b/app/Template/subtask/show.php
index 1d55d1ee..c7ac652a 100644
--- a/app/Template/subtask/show.php
+++ b/app/Template/subtask/show.php
@@ -20,7 +20,7 @@
<?php if (! isset($not_editable)): ?>
<?= $this->toggleSubtaskStatus($subtask, 'task') ?>
<?php else: ?>
- <?= $this->render('subtask/icons', array('subtask' => $subtask)) . $this->e($subtask['status_name']) ?>
+ <?= $this->render('subtask/icons', array('subtask' => $subtask)) . $this->e($subtask['title']) ?>
<?php endif ?>
</td>
<td>
@@ -40,6 +40,16 @@
<?php if (! isset($not_editable)): ?>
<td>
<ul>
+ <?php if ($subtask['position'] > 1): ?>
+ <li>
+ <?= $this->a(t('Move Up'), 'subtask', 'movePosition', array('project_id' => $project['id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'direction' => 'up'), true) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($subtask['position'] != 0 && $subtask['position'] != count($subtasks)): ?>
+ <li>
+ <?= $this->a(t('Move Down'), 'subtask', 'movePosition', array('project_id' => $project['id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id'], 'direction' => 'down'), true) ?>
+ </li>
+ <?php endif ?>
<li>
<?= $this->a(t('Edit'), 'subtask', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>
</li>
diff --git a/app/Template/task/show.php b/app/Template/task/show.php
index 1ff2ef43..d51b5542 100644
--- a/app/Template/task/show.php
+++ b/app/Template/task/show.php
@@ -2,7 +2,7 @@
<?= $this->render('task/time', array('task' => $task, 'values' => $values, 'date_format' => $date_format, 'date_formats' => $date_formats)) ?>
<?= $this->render('task/show_description', array('task' => $task)) ?>
<?= $this->render('tasklink/show', array('task' => $task, 'links' => $links)) ?>
-<?= $this->render('subtask/show', array('task' => $task, 'subtasks' => $subtasks)) ?>
+<?= $this->render('subtask/show', array('task' => $task, 'subtasks' => $subtasks, 'project' => $project)) ?>
<?= $this->render('task/timesheet', array('task' => $task)) ?>
<?= $this->render('file/show', array('task' => $task, 'files' => $files)) ?>
<?= $this->render('task/comments', array('task' => $task, 'comments' => $comments, 'project' => $project)) ?>
diff --git a/tests/units/SubtaskTest.php b/tests/units/SubtaskTest.php
index 62475186..eb1a3fd3 100644
--- a/tests/units/SubtaskTest.php
+++ b/tests/units/SubtaskTest.php
@@ -11,6 +11,78 @@ use Model\User;
class SubTaskTest extends Base
{
+ public function testMoveUp()
+ {
+ $tc = new TaskCreation($this->container);
+ $s = new Subtask($this->container);
+ $p = new Project($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test1')));
+ $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+
+ $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1)));
+ $this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1)));
+ $this->assertEquals(3, $s->create(array('title' => 'subtask #3', 'task_id' => 1)));
+
+ $subtask = $s->getById(1);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(1, $subtask['position']);
+
+ $subtask = $s->getById(2);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(2, $subtask['position']);
+
+ $subtask = $s->getById(3);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(3, $subtask['position']);
+
+ $this->assertTrue($s->moveUp(1, 2));
+
+ $subtask = $s->getById(1);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(2, $subtask['position']);
+
+ $subtask = $s->getById(2);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(1, $subtask['position']);
+
+ $subtask = $s->getById(3);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(3, $subtask['position']);
+
+ $this->assertFalse($s->moveUp(1, 2));
+ }
+
+ public function testMoveDown()
+ {
+ $tc = new TaskCreation($this->container);
+ $s = new Subtask($this->container);
+ $p = new Project($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test1')));
+ $this->assertEquals(1, $tc->create(array('title' => 'test 1', 'project_id' => 1)));
+
+ $this->assertEquals(1, $s->create(array('title' => 'subtask #1', 'task_id' => 1)));
+ $this->assertEquals(2, $s->create(array('title' => 'subtask #2', 'task_id' => 1)));
+ $this->assertEquals(3, $s->create(array('title' => 'subtask #3', 'task_id' => 1)));
+
+ $this->assertTrue($s->moveDown(1, 1));
+
+ $subtask = $s->getById(1);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(2, $subtask['position']);
+
+ $subtask = $s->getById(2);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(1, $subtask['position']);
+
+ $subtask = $s->getById(3);
+ $this->assertNotEmpty($subtask);
+ $this->assertEquals(3, $subtask['position']);
+
+ $this->assertFalse($s->moveDown(1, 3));
+ }
+
public function testDuplicate()
{
$tc = new TaskCreation($this->container);
@@ -53,5 +125,8 @@ class SubTaskTest extends Base
$this->assertEquals(0, $subtasks[0]['user_id']);
$this->assertEquals(0, $subtasks[1]['user_id']);
+
+ $this->assertEquals(1, $subtasks[0]['position']);
+ $this->assertEquals(2, $subtasks[1]['position']);
}
}