summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Core/Lexer.php2
-rw-r--r--app/Model/TaskFilter.php27
-rw-r--r--app/Model/TaskFinder.php3
-rw-r--r--app/Template/listing/show.php18
-rw-r--r--app/Template/search/results.php20
-rw-r--r--docs/search.markdown9
-rw-r--r--tests/units/LexerTest.php25
-rw-r--r--tests/units/TaskFilterTest.php59
8 files changed, 138 insertions, 25 deletions
diff --git a/app/Core/Lexer.php b/app/Core/Lexer.php
index 3887dc82..0a237254 100644
--- a/app/Core/Lexer.php
+++ b/app/Core/Lexer.php
@@ -33,6 +33,7 @@ class Lexer
"/^(category:)/" => 'T_CATEGORY',
"/^(column:)/" => 'T_COLUMN',
"/^(project:)/" => 'T_PROJECT',
+ "/^(swimlane:)/" => 'T_SWIMLANE',
"/^(ref:)/" => 'T_REFERENCE',
"/^(reference:)/" => 'T_REFERENCE',
"/^(\s+)/" => 'T_WHITESPACE',
@@ -116,6 +117,7 @@ class Lexer
case 'T_CATEGORY':
case 'T_COLUMN':
case 'T_PROJECT':
+ case 'T_SWIMLANE':
$next = next($tokens);
if ($next !== false && $next['token'] === 'T_STRING') {
diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php
index 377ec3c6..822397ec 100644
--- a/app/Model/TaskFilter.php
+++ b/app/Model/TaskFilter.php
@@ -71,6 +71,9 @@ class TaskFilter extends Base
case 'T_REFERENCE':
$this->filterByReference($value);
break;
+ case 'T_SWIMLANE':
+ $this->filterBySwimlaneName($value);
+ break;
}
}
@@ -247,6 +250,30 @@ class TaskFilter extends Base
}
/**
+ * Filter by swimlane name
+ *
+ * @access public
+ * @param array $values List of swimlane name
+ * @return TaskFilter
+ */
+ public function filterBySwimlaneName(array $values)
+ {
+ $this->query->beginOr();
+
+ foreach ($values as $swimlane) {
+ if ($swimlane === 'default') {
+ $this->query->eq(Task::TABLE.'.swimlane_id', 0);
+ }
+ else {
+ $this->query->ilike(Swimlane::TABLE.'.name', $swimlane);
+ $this->query->addCondition(Task::TABLE.'.swimlane_id=0 AND '.Project::TABLE.'.default_swimlane '.$this->db->getDriver()->getOperator('ILIKE')." '$swimlane'");
+ }
+ }
+
+ $this->query->closeOr();
+ }
+
+ /**
* Filter by category id
*
* @access public
diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php
index 2b0453a5..47a67a35 100644
--- a/app/Model/TaskFinder.php
+++ b/app/Model/TaskFinder.php
@@ -88,11 +88,14 @@ class TaskFinder extends Base
Category::TABLE.'.name AS category_name',
Category::TABLE.'.description AS category_description',
Board::TABLE.'.title AS column_name',
+ Swimlane::TABLE.'.name AS swimlane_name',
+ Project::TABLE.'.default_swimlane',
Project::TABLE.'.name AS project_name'
)
->join(User::TABLE, 'id', 'owner_id', Task::TABLE)
->join(Category::TABLE, 'id', 'category_id', Task::TABLE)
->join(Board::TABLE, 'id', 'column_id', Task::TABLE)
+ ->join(Swimlane::TABLE, 'id', 'swimlane_id', Task::TABLE)
->join(Project::TABLE, 'id', 'project_id', Task::TABLE);
}
diff --git a/app/Template/listing/show.php b/app/Template/listing/show.php
index 06940678..fc8a607b 100644
--- a/app/Template/listing/show.php
+++ b/app/Template/listing/show.php
@@ -10,13 +10,12 @@
<table class="table-fixed table-small">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
- <th class="column-8"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th>
- <th class="column-8"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Swimlane'), 'tasks.swimlane_id') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th>
<th><?= $paginator->order(t('Title'), 'tasks.title') ?></th>
<th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th>
<th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th>
- <th class="column-10"><?= $paginator->order(t('Date created'), 'tasks.date_creation') ?></th>
- <th class="column-10"><?= $paginator->order(t('Date completed'), 'tasks.date_completed') ?></th>
<th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $task): ?>
@@ -25,6 +24,9 @@
<?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
+ <?= $this->e($task['swimlane_name'] ?: $task['default_swimlane']) ?>
+ </td>
+ <td>
<?= $this->e($task['column_name']) ?>
</td>
<td>
@@ -44,14 +46,6 @@
<?= dt('%B %e, %Y', $task['date_due']) ?>
</td>
<td>
- <?= dt('%B %e, %Y', $task['date_creation']) ?>
- </td>
- <td>
- <?php if ($task['date_completed']): ?>
- <?= dt('%B %e, %Y', $task['date_completed']) ?>
- <?php endif ?>
- </td>
- <td>
<?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?>
<?= t('Open') ?>
<?php else: ?>
diff --git a/app/Template/search/results.php b/app/Template/search/results.php
index 1d8cc6e2..04cb6a19 100644
--- a/app/Template/search/results.php
+++ b/app/Template/search/results.php
@@ -1,14 +1,13 @@
<table class="table-fixed table-small">
<tr>
<th class="column-8"><?= $paginator->order(t('Project'), 'tasks.project_id') ?></th>
- <th class="column-8"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
- <th class="column-8"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th>
- <th class="column-8"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th>
+ <th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Swimlane'), 'tasks.swimlane_id') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th>
<th><?= $paginator->order(t('Title'), 'tasks.title') ?></th>
<th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th>
<th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th>
- <th class="column-10"><?= $paginator->order(t('Date created'), 'tasks.date_creation') ?></th>
- <th class="column-10"><?= $paginator->order(t('Date completed'), 'tasks.date_completed') ?></th>
<th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $task): ?>
@@ -20,6 +19,9 @@
<?= $this->url->link('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
</td>
<td>
+ <?= $this->e($task['swimlane_name'] ?: $task['default_swimlane']) ?>
+ </td>
+ <td>
<?= $this->e($task['column_name']) ?>
</td>
<td>
@@ -39,14 +41,6 @@
<?= dt('%B %e, %Y', $task['date_due']) ?>
</td>
<td>
- <?= dt('%B %e, %Y', $task['date_creation']) ?>
- </td>
- <td>
- <?php if ($task['date_completed']): ?>
- <?= dt('%B %e, %Y', $task['date_completed']) ?>
- <?php endif ?>
- </td>
- <td>
<?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?>
<?= t('Open') ?>
<?php else: ?>
diff --git a/docs/search.markdown b/docs/search.markdown
index 3d00b158..d0e71203 100644
--- a/docs/search.markdown
+++ b/docs/search.markdown
@@ -151,6 +151,15 @@ Attribute: **column**
- Find tasks by column name: `column:"Work in progress"`
- Find tasks for several columns: `column:"Backlog" column:ready`
+Search by swimlane
+------------------
+
+Attribute: **swimlane**
+
+- Find tasks by swimlane: `swimlane:"Version 42"`
+- Find tasks in the default swimlane: `swimlane:default`
+- Find tasks into several swimlanes: `swimlane:"Version 1.2" swimlane:"Version 1.3"`
+
Search by external reference
----------------------------
diff --git a/tests/units/LexerTest.php b/tests/units/LexerTest.php
index bf0ffdd0..8710f79a 100644
--- a/tests/units/LexerTest.php
+++ b/tests/units/LexerTest.php
@@ -6,6 +6,31 @@ use Core\Lexer;
class LexerTest extends Base
{
+ public function testSwimlaneQuery()
+ {
+ $lexer = new Lexer;
+
+ $this->assertEquals(
+ array(array('match' => 'swimlane:', 'token' => 'T_SWIMLANE'), array('match' => 'Version 42', 'token' => 'T_STRING')),
+ $lexer->tokenize('swimlane:"Version 42"')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'swimlane:', 'token' => 'T_SWIMLANE'), array('match' => 'v3', 'token' => 'T_STRING')),
+ $lexer->tokenize('swimlane:v3')
+ );
+
+ $this->assertEquals(
+ array('T_SWIMLANE' => array('v3')),
+ $lexer->map($lexer->tokenize('swimlane:v3'))
+ );
+
+ $this->assertEquals(
+ array('T_SWIMLANE' => array('Version 42', 'v3')),
+ $lexer->map($lexer->tokenize('swimlane:"Version 42" swimlane:v3'))
+ );
+ }
+
public function testAssigneeQuery()
{
$lexer = new Lexer;
diff --git a/tests/units/TaskFilterTest.php b/tests/units/TaskFilterTest.php
index 14a9b1c0..58d02524 100644
--- a/tests/units/TaskFilterTest.php
+++ b/tests/units/TaskFilterTest.php
@@ -9,6 +9,7 @@ use Model\TaskCreation;
use Model\DateParser;
use Model\Category;
use Model\Config;
+use Model\Swimlane;
class TaskFilterTest extends Base
{
@@ -286,6 +287,64 @@ class TaskFilterTest extends Base
$this->assertEmpty($tasks);
}
+ public function testSearchWithSwimlane()
+ {
+ $p = new Project($this->container);
+ $tc = new TaskCreation($this->container);
+ $tf = new TaskFilter($this->container);
+ $s = new Swimlane($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'My project A')));
+ $this->assertEquals(1, $s->create(1, 'Version 1.1'));
+ $this->assertEquals(2, $s->create(1, 'Version 1.2'));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task1', 'swimlane_id' => 1)));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task2', 'swimlane_id' => 2)));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'task3', 'swimlane_id' => 0)));
+
+ $tf->search('swimlane:"Version 1.1"');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('task1', $tasks[0]['title']);
+ $this->assertEquals('Version 1.1', $tasks[0]['swimlane_name']);
+
+ $tf->search('swimlane:"versioN 1.2"');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('task2', $tasks[0]['title']);
+ $this->assertEquals('Version 1.2', $tasks[0]['swimlane_name']);
+
+ $tf->search('swimlane:"Default swimlane"');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('task3', $tasks[0]['title']);
+ $this->assertEquals('Default swimlane', $tasks[0]['default_swimlane']);
+ $this->assertEquals('', $tasks[0]['swimlane_name']);
+
+ $tf->search('swimlane:default');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('task3', $tasks[0]['title']);
+ $this->assertEquals('Default swimlane', $tasks[0]['default_swimlane']);
+ $this->assertEquals('', $tasks[0]['swimlane_name']);
+
+ $tf->search('swimlane:"Version 1.1" swimlane:"Version 1.2"');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(2, $tasks);
+ $this->assertEquals('task1', $tasks[0]['title']);
+ $this->assertEquals('Version 1.1', $tasks[0]['swimlane_name']);
+ $this->assertEquals('task2', $tasks[1]['title']);
+ $this->assertEquals('Version 1.2', $tasks[1]['swimlane_name']);
+
+ $tf->search('swimlane:"not found"');
+ $tasks = $tf->findAll();
+ $this->assertEmpty($tasks);
+ }
+
public function testSearchWithColumn()
{
$p = new Project($this->container);