From 9e278a9370e3b651a4a545c0c0c0c256088ed187 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Fri, 24 Jun 2016 08:50:57 -0400 Subject: Use BoardFormatter to generate the board --- app/Filter/BaseFilter.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'app/Filter') diff --git a/app/Filter/BaseFilter.php b/app/Filter/BaseFilter.php index 79a664be..e029f4e1 100644 --- a/app/Filter/BaseFilter.php +++ b/app/Filter/BaseFilter.php @@ -43,8 +43,7 @@ abstract class BaseFilter */ public static function getInstance($value = null) { - $self = new static($value); - return $self; + return new static($value); } /** -- cgit v1.2.3 From b2e92480c29acb15586bc8ea305c8416927a667c Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Fri, 24 Jun 2016 11:40:58 -0400 Subject: Added filter class for tags --- app/Filter/TaskTagFilter.php | 74 +++++++++++++++++++ app/Model/TaskTagModel.php | 20 ++--- app/ServiceProvider/FilterProvider.php | 4 + doc/search.markdown | 6 ++ tests/units/Base.php | 4 +- tests/units/Core/Filter/LexerTest.php | 12 +++ tests/units/Filter/TaskTagFilterTest.php | 121 +++++++++++++++++++++++++++++++ tests/units/Model/TaskTagModelTest.php | 14 ++++ 8 files changed, 243 insertions(+), 12 deletions(-) create mode 100644 app/Filter/TaskTagFilter.php create mode 100644 tests/units/Filter/TaskTagFilterTest.php (limited to 'app/Filter') diff --git a/app/Filter/TaskTagFilter.php b/app/Filter/TaskTagFilter.php new file mode 100644 index 00000000..01b6f625 --- /dev/null +++ b/app/Filter/TaskTagFilter.php @@ -0,0 +1,74 @@ +db = $db; + return $this; + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $task_ids = $this->db + ->table(TagModel::TABLE) + ->ilike(TagModel::TABLE.'.name', $this->value) + ->asc(TagModel::TABLE.'.project_id') + ->join(TaskTagModel::TABLE, 'tag_id', 'id') + ->findAllByColumn(TaskTagModel::TABLE.'.task_id'); + + if (empty($task_ids)) { + $task_ids = array(-1); + } + + $this->query->in(TaskModel::TABLE.'.id', $task_ids); + + return $this; + } +} diff --git a/app/Model/TaskTagModel.php b/app/Model/TaskTagModel.php index 3dd1dd88..91dfd224 100644 --- a/app/Model/TaskTagModel.php +++ b/app/Model/TaskTagModel.php @@ -74,9 +74,9 @@ class TaskTagModel extends Base * Add or update a list of tags to a task * * @access public - * @param integer $project_id - * @param integer $task_id - * @param string[] $tags + * @param integer $project_id + * @param integer $task_id + * @param string[] $tags * @return boolean */ public function save($project_id, $task_id, array $tags) @@ -123,10 +123,10 @@ class TaskTagModel extends Base * Associate missing tags * * @access protected - * @param integer $project_id - * @param integer $task_id - * @param array $task_tags - * @param array $tags + * @param integer $project_id + * @param integer $task_id + * @param array $task_tags + * @param string[] $tags * @return bool */ protected function associateTags($project_id, $task_id, $task_tags, $tags) @@ -146,9 +146,9 @@ class TaskTagModel extends Base * Dissociate removed tags * * @access protected - * @param integer $task_id - * @param array $task_tags - * @param array $tags + * @param integer $task_id + * @param array $task_tags + * @param string[] $tags * @return bool */ protected function dissociateTags($task_id, $task_tags, $tags) diff --git a/app/ServiceProvider/FilterProvider.php b/app/ServiceProvider/FilterProvider.php index cdef9ed8..20281a09 100644 --- a/app/ServiceProvider/FilterProvider.php +++ b/app/ServiceProvider/FilterProvider.php @@ -26,6 +26,7 @@ use Kanboard\Filter\TaskReferenceFilter; use Kanboard\Filter\TaskStatusFilter; use Kanboard\Filter\TaskSubtaskAssigneeFilter; use Kanboard\Filter\TaskSwimlaneFilter; +use Kanboard\Filter\TaskTagFilter; use Kanboard\Filter\TaskTitleFilter; use Kanboard\Model\ProjectModel; use Kanboard\Model\ProjectGroupRoleModel; @@ -163,6 +164,9 @@ class FilterProvider implements ServiceProviderInterface ->setDatabase($c['db']) ) ->withFilter(new TaskSwimlaneFilter()) + ->withFilter(TaskTagFilter::getInstance() + ->setDatabase($c['db']) + ) ->withFilter(new TaskTitleFilter(), true) ; diff --git a/doc/search.markdown b/doc/search.markdown index 37bb8625..ab8e0b5a 100644 --- a/doc/search.markdown +++ b/doc/search.markdown @@ -152,6 +152,12 @@ Attribute: **comment** - Find comments that contains this title: `comment:"My comment message"` +### Search by tags + +Attribute: **tag** + +- Example: `tag:"My tag"` + Activity stream search ---------------------- diff --git a/tests/units/Base.php b/tests/units/Base.php index f7bee241..9dbfb280 100644 --- a/tests/units/Base.php +++ b/tests/units/Base.php @@ -48,8 +48,8 @@ abstract class Base extends PHPUnit_Framework_TestCase new Stopwatch ); - $this->container['db']->logQueries = true; - $this->container['logger'] = new Logger; + $this->container['db']->getStatementHandler()->withLogging(); + $this->container['logger'] = new Logger(); $this->container['httpClient'] = $this ->getMockBuilder('\Kanboard\Core\Http\Client') diff --git a/tests/units/Core/Filter/LexerTest.php b/tests/units/Core/Filter/LexerTest.php index c72231c4..b777531d 100644 --- a/tests/units/Core/Filter/LexerTest.php +++ b/tests/units/Core/Filter/LexerTest.php @@ -202,4 +202,16 @@ class LexerTest extends Base $this->assertSame($expected, $lexer->tokenize('६Δↈ五一')); } + + public function testTokenizeWithMultipleValues() + { + $lexer = new Lexer(); + $lexer->addToken("/^(tag:)/", 'T_TAG'); + + $expected = array( + 'T_TAG' => array('tag 1', 'tag2'), + ); + + $this->assertSame($expected, $lexer->tokenize('tag:"tag 1" tag:tag2')); + } } diff --git a/tests/units/Filter/TaskTagFilterTest.php b/tests/units/Filter/TaskTagFilterTest.php new file mode 100644 index 00000000..a36d3475 --- /dev/null +++ b/tests/units/Filter/TaskTagFilterTest.php @@ -0,0 +1,121 @@ +container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test2'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test3'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + $this->assertTrue($taskTagModel->save(1, 2, array('My tag 3'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag 3'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(2, $tasks); + $this->assertEquals('test1', $tasks[0]['title']); + $this->assertEquals('test2', $tasks[1]['title']); + } + + public function testWithSingleMatch() + { + $taskFinderModel = new TaskFinderModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test2'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test3'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + $this->assertTrue($taskTagModel->save(1, 2, array('My tag 3'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag 2'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(1, $tasks); + $this->assertEquals('test1', $tasks[0]['title']); + } + + public function testWithNoMatch() + { + $taskFinderModel = new TaskFinderModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test2'))); + $this->assertEquals(3, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test3'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + $this->assertTrue($taskTagModel->save(1, 2, array('My tag 3'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag 42'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(0, $tasks); + } + + public function testWithSameTagInMultipleProjects() + { + $taskFinderModel = new TaskFinderModel($this->container); + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + $query = $taskFinderModel->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(2, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 2, 'title' => 'test2'))); + + $this->assertTrue($taskTagModel->save(1, 1, array('My tag'))); + $this->assertTrue($taskTagModel->save(2, 2, array('My tag'))); + + $filter = new TaskTagFilter(); + $filter->setDatabase($this->container['db']); + $filter->withQuery($query); + $filter->withValue('my tag'); + $filter->apply(); + + $tasks = $query->findAll(); + $this->assertCount(2, $tasks); + $this->assertEquals('test1', $tasks[0]['title']); + $this->assertEquals('test2', $tasks[1]['title']); + } +} diff --git a/tests/units/Model/TaskTagModelTest.php b/tests/units/Model/TaskTagModelTest.php index 819f55b8..73bbeac1 100644 --- a/tests/units/Model/TaskTagModelTest.php +++ b/tests/units/Model/TaskTagModelTest.php @@ -110,4 +110,18 @@ class TaskTagModelTest extends Base $this->assertEquals($expected, $tags); } + + public function testGetTagsForTasksWithEmptyList() + { + $projectModel = new ProjectModel($this->container); + $taskCreationModel = new TaskCreationModel($this->container); + $taskTagModel = new TaskTagModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test1'))); + $this->assertTrue($taskTagModel->save(1, 1, array('My tag 1', 'My tag 2', 'My tag 3'))); + + $tags = $taskTagModel->getTagsByTasks(array()); + $this->assertEquals(array(), $tags); + } } -- cgit v1.2.3 From 48ee733f9e2d0519150a881b18bf5510d6ed8e29 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Thu, 14 Jul 2016 11:29:15 -0400 Subject: Added search filter for task priority --- ChangeLog | 1 + app/Filter/TaskPriorityFilter.php | 38 ++++++++++++++++++++++ app/ServiceProvider/FilterProvider.php | 2 ++ tests/units/Filter/TaskPriorityFilterTest.php | 47 +++++++++++++++++++++++++++ 4 files changed, 88 insertions(+) create mode 100644 app/Filter/TaskPriorityFilter.php create mode 100644 tests/units/Filter/TaskPriorityFilterTest.php (limited to 'app/Filter') diff --git a/ChangeLog b/ChangeLog index 881c3020..097ec68e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,7 @@ Version 1.0.32 (unreleased) New features: * New automated action to close tasks without activity in a specific column +* Added search filter for task priority * Added the possibility to hide tasks in dashboard for a specific column Improvements: diff --git a/app/Filter/TaskPriorityFilter.php b/app/Filter/TaskPriorityFilter.php new file mode 100644 index 00000000..75f6ae3d --- /dev/null +++ b/app/Filter/TaskPriorityFilter.php @@ -0,0 +1,38 @@ +query->eq(TaskModel::TABLE.'.priority', $this->value); + return $this; + } +} diff --git a/app/ServiceProvider/FilterProvider.php b/app/ServiceProvider/FilterProvider.php index 20281a09..436288dc 100644 --- a/app/ServiceProvider/FilterProvider.php +++ b/app/ServiceProvider/FilterProvider.php @@ -21,6 +21,7 @@ use Kanboard\Filter\TaskDueDateFilter; use Kanboard\Filter\TaskIdFilter; use Kanboard\Filter\TaskLinkFilter; use Kanboard\Filter\TaskModificationDateFilter; +use Kanboard\Filter\TaskPriorityFilter; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskReferenceFilter; use Kanboard\Filter\TaskStatusFilter; @@ -137,6 +138,7 @@ class FilterProvider implements ServiceProviderInterface ->withFilter(TaskColorFilter::getInstance() ->setColorModel($c['colorModel']) ) + ->withFilter(new TaskPriorityFilter()) ->withFilter(new TaskColumnFilter()) ->withFilter(new TaskCommentFilter()) ->withFilter(TaskCreationDateFilter::getInstance() diff --git a/tests/units/Filter/TaskPriorityFilterTest.php b/tests/units/Filter/TaskPriorityFilterTest.php new file mode 100644 index 00000000..4c95ddce --- /dev/null +++ b/tests/units/Filter/TaskPriorityFilterTest.php @@ -0,0 +1,47 @@ +container); + $taskCreation = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); + $query = $taskFinder->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1, 'priority' => 2))); + + $filter = new TaskPriorityFilter(); + $filter->withQuery($query); + $filter->withValue(2); + $filter->apply(); + + $this->assertCount(1, $query->findAll()); + } + + public function testWithNoPriority() + { + $taskFinder = new TaskFinderModel($this->container); + $taskCreation = new TaskCreationModel($this->container); + $projectModel = new ProjectModel($this->container); + $query = $taskFinder->getExtendedQuery(); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1))); + + $filter = new TaskPriorityFilter(); + $filter->withQuery($query); + $filter->withValue(2); + $filter->apply(); + + $this->assertCount(0, $query->findAll()); + } +} -- cgit v1.2.3 From 2363da274c412cdd0dc2fc68b8f45222f4467a0a Mon Sep 17 00:00:00 2001 From: Christopher Geelen Date: Wed, 27 Jul 2016 15:06:01 +0200 Subject: ADD: TaskMovedDateFilter --- app/Filter/TaskMovedDateFilter.php | 38 +++++++++++++++++++++++ app/ServiceProvider/FilterProvider.php | 4 +++ tests/units/Filter/TaskMovedDateFilterTest.php | 43 ++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 app/Filter/TaskMovedDateFilter.php create mode 100644 tests/units/Filter/TaskMovedDateFilterTest.php (limited to 'app/Filter') diff --git a/app/Filter/TaskMovedDateFilter.php b/app/Filter/TaskMovedDateFilter.php new file mode 100644 index 00000000..d57b7d23 --- /dev/null +++ b/app/Filter/TaskMovedDateFilter.php @@ -0,0 +1,38 @@ +applyDateFilter(TaskModel::TABLE.'.date_moved'); + return $this; + } +} diff --git a/app/ServiceProvider/FilterProvider.php b/app/ServiceProvider/FilterProvider.php index 436288dc..443d3588 100644 --- a/app/ServiceProvider/FilterProvider.php +++ b/app/ServiceProvider/FilterProvider.php @@ -21,6 +21,7 @@ use Kanboard\Filter\TaskDueDateFilter; use Kanboard\Filter\TaskIdFilter; use Kanboard\Filter\TaskLinkFilter; use Kanboard\Filter\TaskModificationDateFilter; +use Kanboard\Filter\TaskMovedDateFilter; use Kanboard\Filter\TaskPriorityFilter; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskReferenceFilter; @@ -158,6 +159,9 @@ class FilterProvider implements ServiceProviderInterface ->withFilter(TaskModificationDateFilter::getInstance() ->setDateParser($c['dateParser']) ) + ->withFilter(TaskMovedDateFilter::getInstance() + ->setDateParser($c['dateParser']) + ) ->withFilter(new TaskProjectFilter()) ->withFilter(new TaskReferenceFilter()) ->withFilter(new TaskStatusFilter()) diff --git a/tests/units/Filter/TaskMovedDateFilterTest.php b/tests/units/Filter/TaskMovedDateFilterTest.php new file mode 100644 index 00000000..9b721913 --- /dev/null +++ b/tests/units/Filter/TaskMovedDateFilterTest.php @@ -0,0 +1,43 @@ +container); + $taskCreation = new TaskCreationModel($this->container); + $taskModification = new TaskModificationModel($this->container); + $projectModel = new ProjectModel($this->container); + + $this->assertEquals(1, $projectModel->create(array('name' => 'Test'))); + $this->assertEquals(1, $taskCreation->create(array('title' => 'Test1', 'project_id' => 1))); + $this->assertTrue($taskModification->update(array('id' => 1, 'date_moved' => time()))); + $this->assertEquals(2, $taskCreation->create(array('title' => 'Test2', 'project_id' => 1))); + $this->assertTrue($taskModification->update(array('id' => 2, 'date_moved' => strtotime('-1days')))); + $this->assertEquals(3, $taskCreation->create(array('title' => 'Test3', 'project_id' => 1))); + $this->assertTrue($taskModification->update(array('id' => 3, 'date_moved' => strtotime('-3days')))); + + $query = $taskFinder->getExtendedQuery(); + $filter = new TaskMovedDateFilter('>='.date('Y-m-d', strtotime('-1days'))); + $filter->setDateParser($this->container['dateParser']); + $filter->withQuery($query)->apply(); + + $this->assertCount(2, $query->findAll()); + + $query = $taskFinder->getExtendedQuery(); + $filter = new TaskMovedDateFilter('setDateParser($this->container['dateParser']); + $filter->withQuery($query)->apply(); + + $this->assertCount(1, $query->findAll()); + } +} -- cgit v1.2.3