diff options
author | Olivier Maridat <olivier.maridat@trialog.com> | 2015-11-26 15:33:44 +0100 |
---|---|---|
committer | Olivier Maridat <olivier.maridat@trialog.com> | 2015-11-26 15:33:44 +0100 |
commit | f837e70a2d74eb37c4c5de7e4f54c8bf8ec78db7 (patch) | |
tree | c146511e70537f7224835931ef1d3b0b2f516d3e | |
parent | e582d4047b061f0c17e6366fed2bf1cabd624c10 (diff) |
Add filter by task link
-rw-r--r-- | app/Core/Lexer.php | 2 | ||||
-rw-r--r-- | app/Model/TaskFilter.php | 41 | ||||
-rw-r--r-- | doc/search.markdown | 8 | ||||
-rw-r--r-- | tests/units/Core/LexerTest.php | 25 | ||||
-rw-r--r-- | tests/units/Model/TaskFilterTest.php | 60 |
5 files changed, 136 insertions, 0 deletions
diff --git a/app/Core/Lexer.php b/app/Core/Lexer.php index ca2ef895..df2d90ae 100644 --- a/app/Core/Lexer.php +++ b/app/Core/Lexer.php @@ -39,6 +39,7 @@ class Lexer "/^(swimlane:)/" => 'T_SWIMLANE', "/^(ref:)/" => 'T_REFERENCE', "/^(reference:)/" => 'T_REFERENCE', + "/^(link:)/" => 'T_LINK', "/^(\s+)/" => 'T_WHITESPACE', '/^([<=>]{0,2}[0-9]{4}-[0-9]{2}-[0-9]{2})/' => 'T_DATE', '/^(yesterday|tomorrow|today)/' => 'T_DATE', @@ -118,6 +119,7 @@ class Lexer case 'T_COLUMN': case 'T_PROJECT': case 'T_SWIMLANE': + case 'T_LINK': $next = next($tokens); if ($next !== false && $next['token'] === 'T_STRING') { diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php index 137a7a8e..7ceb4a97 100644 --- a/app/Model/TaskFilter.php +++ b/app/Model/TaskFilter.php @@ -30,6 +30,7 @@ class TaskFilter extends Base 'T_COLUMN' => 'filterByColumnName', 'T_REFERENCE' => 'filterByReference', 'T_SWIMLANE' => 'filterBySwimlaneName', + 'T_LINK' => 'filterByLinkName', ); /** @@ -108,6 +109,22 @@ class TaskFilter extends Base } /** + * Create a new link query + * + * @access public + * @return \PicoDb\Table + */ + public function createLinkQuery() + { + return $this->db->table(TaskLink::TABLE) + ->columns( + TaskLink::TABLE.'.task_id', + Link::TABLE.'.label' + ) + ->join(Link::TABLE, 'id', 'link_id', TaskLink::TABLE); + } + + /** * Clone the filter * * @access public @@ -507,6 +524,30 @@ class TaskFilter extends Base } /** + * Filter by link + * + * @access public + * @param array $values List of links + * @return TaskFilter + */ + public function filterByLinkName(array $values) + { + $this->query->beginOr(); + + $link_query = $this->createLinkQuery()->in(Link::TABLE.'.label', $values); + $matching_task_ids = $link_query->findAllByColumn('task_id'); + if (empty($matching_task_ids)) { + $this->query->eq(Task::TABLE.'.id', 0); + } else { + $this->query->in(Task::TABLE.'.id', $matching_task_ids); + } + + $this->query->closeOr(); + + return $this; + } + + /** * Filter by due date * * @access public diff --git a/doc/search.markdown b/doc/search.markdown index 34a20bc6..889d453f 100644 --- a/doc/search.markdown +++ b/doc/search.markdown @@ -136,3 +136,11 @@ Attribute: **swimlane** - Find tasks in the default swimlane: `swimlane:default` - Find tasks into several swimlanes: `swimlane:"Version 1.2" swimlane:"Version 1.3"` +Search by task link +------------------ + +Attribute: **link** + +- Find tasks by link name: `link:"is a milestone of"` +- Find tasks into several links: `link:"is a milestone of" link:"relates to"` + diff --git a/tests/units/Core/LexerTest.php b/tests/units/Core/LexerTest.php index 9e14ff6b..55370aab 100644 --- a/tests/units/Core/LexerTest.php +++ b/tests/units/Core/LexerTest.php @@ -116,6 +116,31 @@ class LexerTest extends Base ); } + public function testLinkQuery() + { + $lexer = new Lexer; + + $this->assertEquals( + array(array('match' => 'link:', 'token' => 'T_LINK'), array('match' => 'is a milestone of', 'token' => 'T_STRING')), + $lexer->tokenize('link:"is a milestone of"') + ); + + $this->assertEquals( + array('T_LINK' => array('is a milestone of')), + $lexer->map($lexer->tokenize('link:"is a milestone of"')) + ); + + $this->assertEquals( + array('T_LINK' => array('is a milestone of', 'fixes')), + $lexer->map($lexer->tokenize('link:"is a milestone of" link:fixes')) + ); + + $this->assertEquals( + array(), + $lexer->map($lexer->tokenize('link: ')) + ); + } + public function testColumnQuery() { $lexer = new Lexer; diff --git a/tests/units/Model/TaskFilterTest.php b/tests/units/Model/TaskFilterTest.php index b668b7cc..daa193b2 100644 --- a/tests/units/Model/TaskFilterTest.php +++ b/tests/units/Model/TaskFilterTest.php @@ -6,6 +6,7 @@ use Kanboard\Model\Project; use Kanboard\Model\User; use Kanboard\Model\TaskFilter; use Kanboard\Model\TaskCreation; +use Kanboard\Model\TaskLink; use Kanboard\Core\DateParser; use Kanboard\Model\Category; use Kanboard\Model\Subtask; @@ -552,6 +553,65 @@ class TaskFilterTest extends Base $this->assertEquals('task3', $tasks[0]['title']); } + public function testSearchWithLink() + { + $p = new Project($this->container); + $u = new User($this->container); + $tc = new TaskCreation($this->container); + $tl = new TaskLink($this->container); + $tf = new TaskFilter($this->container); + + $this->assertEquals(1, $p->create(array('name' => 'test'))); + $this->assertEquals(2, $u->create(array('username' => 'bob', 'name' => 'Bob Ryan'))); + $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'my task title is awesome', 'color_id' => 'light_green'))); + $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'my task title is amazing', 'color_id' => 'blue'))); + $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'Bob at work'))); + $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'I have a bad feeling about that'))); + $this->assertEquals(1, $tl->create(1, 2, 9)); // #1 is a milestone of #2 + $this->assertEquals(3, $tl->create(2, 1, 2)); // #2 blocks #1 + $this->assertEquals(5, $tl->create(3, 2, 2)); // #3 blocks #2 + + $tf->search('link:"is a milestone of"'); + $tasks = $tf->findAll(); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('my task title is awesome', $tasks[0]['title']); + + $tf->search('link:"is a milestone of" amazing'); + $tasks = $tf->findAll(); + $this->assertEmpty($tasks); + + $tf->search('link:"unknown"'); + $tasks = $tf->findAll(); + $this->assertEmpty($tasks); + + $tf->search('link:unknown'); + $tasks = $tf->findAll(); + $this->assertEmpty($tasks); + + $tf->search('link:blocks amazing'); + $tasks = $tf->findAll(); + $this->assertNotEmpty($tasks); + $this->assertCount(1, $tasks); + $this->assertEquals('my task title is amazing', $tasks[0]['title']); + + $tf->search('link:"is a milestone of" link:blocks'); + $tasks = $tf->findAll(); + $this->assertNotEmpty($tasks); + $this->assertCount(3, $tasks); + $this->assertEquals('my task title is awesome', $tasks[0]['title']); + $this->assertEquals('my task title is amazing', $tasks[1]['title']); + $this->assertEquals('Bob at work', $tasks[2]['title']); + + $tf->search('link:"is a milestone of" link:blocks link:unknown'); + $tasks = $tf->findAll(); + $this->assertNotEmpty($tasks); + $this->assertCount(3, $tasks); + $this->assertEquals('my task title is awesome', $tasks[0]['title']); + $this->assertEquals('my task title is amazing', $tasks[1]['title']); + $this->assertEquals('Bob at work', $tasks[2]['title']); + } + public function testCopy() { $tf = new TaskFilter($this->container); |