summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/Controller/Projectinfo.php8
-rw-r--r--app/Core/Lexer.php142
-rw-r--r--app/Model/Color.php23
-rw-r--r--app/Model/TaskFilter.php140
-rw-r--r--app/ServiceProvider/ClassProvider.php1
-rw-r--r--docs/index.markdown1
-rw-r--r--docs/search.markdown106
-rw-r--r--tests/units/LexerTest.php193
-rw-r--r--tests/units/TaskFilterTest.php170
9 files changed, 781 insertions, 3 deletions
diff --git a/app/Controller/Projectinfo.php b/app/Controller/Projectinfo.php
index a9498f43..c30c1652 100644
--- a/app/Controller/Projectinfo.php
+++ b/app/Controller/Projectinfo.php
@@ -46,9 +46,11 @@ class Projectinfo extends Base
if ($search !== '') {
- $paginator
- ->setQuery($this->taskFinder->getSearchQuery($project['id'], $search))
- ->calculate();
+ // $paginator
+ // ->setQuery($this->taskFinder->getSearchQuery($project['id'], $search))
+ // ->calculate();
+
+ $paginator->setQuery($this->taskFilter->search($search)->filterByProject($project['id'])->getQuery())->calculate();
$nb_tasks = $paginator->getTotal();
}
diff --git a/app/Core/Lexer.php b/app/Core/Lexer.php
new file mode 100644
index 00000000..a81965c5
--- /dev/null
+++ b/app/Core/Lexer.php
@@ -0,0 +1,142 @@
+<?php
+
+namespace Core;
+
+/**
+ * Lexer
+ *
+ * @package core
+ * @author Frederic Guillot
+ */
+class Lexer
+{
+ /**
+ * Current position
+ *
+ * @access private
+ * @var integer
+ */
+ private $offset = 0;
+
+ /**
+ * Token map
+ *
+ * @access private
+ * @var array
+ */
+ private $tokenMap = array(
+ "/^(assignee:)/" => 'T_ASSIGNEE',
+ "/^(color:)/" => 'T_COLOR',
+ "/^(due:)/" => 'T_DUE',
+ "/^(title:)/" => 'T_TITLE',
+ "/^(\s+)/" => 'T_WHITESPACE',
+ '/^([<=>]{0,2}[0-9]{4}-[0-9]{2}-[0-9]{2})/' => 'T_DATE',
+ '/^(yesterday|tomorrow|today)/' => 'T_DATE',
+ '/^("(.*?)")/' => 'T_STRING',
+ "/^(\w+)/" => 'T_STRING',
+ );
+
+ /**
+ * Tokenize input string
+ *
+ * @access public
+ * @param string $input
+ * @return array
+ */
+ public function tokenize($input)
+ {
+ $tokens = array();
+ $this->offset = 0;
+
+ while (isset($input[$this->offset])) {
+
+ $result = $this->match(substr($input, $this->offset));
+
+ if ($result === false) {
+ return array();
+ }
+
+ $tokens[] = $result;
+ }
+
+ return $tokens;
+ }
+
+ /**
+ * Find a token that match and move the offset
+ *
+ * @access public
+ * @param string $string
+ * @return array|boolean
+ */
+ public function match($string)
+ {
+ foreach ($this->tokenMap as $pattern => $name) {
+ if (preg_match($pattern, $string, $matches)) {
+
+ $this->offset += strlen($matches[1]);
+
+ return array(
+ 'match' => trim($matches[1], '"'),
+ 'token' => $name,
+ );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Change the output of tokenizer to be easily parsed by the database filter
+ *
+ * Example: ['T_ASSIGNEE' => ['user1', 'user2'], 'T_TITLE' => 'task title']
+ *
+ * @access public
+ * @param array $tokens
+ * @return array
+ */
+ public function map(array $tokens)
+ {
+ $map = array(
+ 'T_TITLE' => '',
+ );
+
+ while (false !== ($token = current($tokens))) {
+
+ switch ($token['token']) {
+ case 'T_ASSIGNEE':
+ case 'T_COLOR':
+ $next = next($tokens);
+
+ if ($next !== false && $next['token'] === 'T_STRING') {
+ $map[$token['token']][] = $next['match'];
+ }
+
+ break;
+
+ case 'T_DUE':
+ $next = next($tokens);
+
+ if ($next !== false && $next['token'] === 'T_DATE') {
+ $map[$token['token']] = $next['match'];
+ }
+
+ break;
+
+ default:
+ $map['T_TITLE'] .= $token['match'];
+ break;
+ }
+
+ next($tokens);
+ }
+
+ $map['T_TITLE'] = trim($map['T_TITLE']);
+
+ if (empty($map['T_TITLE'])) {
+ unset($map['T_TITLE']);
+ }
+
+ return $map;
+ }
+}
diff --git a/app/Model/Color.php b/app/Model/Color.php
index 9a21f691..a35aff8f 100644
--- a/app/Model/Color.php
+++ b/app/Model/Color.php
@@ -100,6 +100,29 @@ class Color extends Base
);
/**
+ * Find a color id from the name or the id
+ *
+ * @access public
+ * @param string $color
+ * @return string
+ */
+ public function find($color)
+ {
+ $color = strtolower($color);
+
+ foreach ($this->default_colors as $color_id => $params) {
+ if ($color_id === $color) {
+ return $color_id;
+ }
+ else if ($color === strtolower($params['name'])) {
+ return $color_id;
+ }
+ }
+
+ return '';
+ }
+
+ /**
* Get available colors
*
* @access public
diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php
index bd03a8bc..4f306b14 100644
--- a/app/Model/TaskFilter.php
+++ b/app/Model/TaskFilter.php
@@ -24,6 +24,42 @@ class TaskFilter extends Base
public $query;
/**
+ * Apply filters according to the search input
+ *
+ * @access public
+ * @param string $input
+ * @return TaskFilter
+ */
+ public function search($input)
+ {
+ $tree = $this->lexer->map($this->lexer->tokenize($input));
+ $this->query = $this->taskFinder->getExtendedQuery();
+
+ if (empty($tree)) {
+ $this->query->addCondition('1 = 0');
+ }
+
+ foreach ($tree as $filter => $value) {
+ switch ($filter) {
+ case 'T_ASSIGNEE':
+ $this->filterByAssignee($value);
+ break;
+ case 'T_COLOR':
+ $this->filterByColors($value);
+ break;
+ case 'T_DUE':
+ $this->filterByDueDate($value);
+ break;
+ case 'T_TITLE':
+ $this->filterByTitle($value);
+ break;
+ }
+ }
+
+ return $this;
+ }
+
+ /**
* Create a new query
*
* @access public
@@ -164,6 +200,35 @@ class TaskFilter extends Base
}
/**
+ * Filter by assignee names
+ *
+ * @access public
+ * @param array $values List of assignees
+ * @return TaskFilter
+ */
+ public function filterByAssignee(array $values)
+ {
+ $this->query->beginOr();
+
+ foreach ($values as $assignee) {
+
+ switch ($assignee) {
+ case 'me':
+ $this->query->eq('owner_id', $this->userSession->getId());
+ break;
+ case 'nobody':
+ $this->query->eq('owner_id', 0);
+ break;
+ default:
+ $this->query->ilike(User::TABLE.'.username', '%'.$assignee.'%');
+ $this->query->ilike(User::TABLE.'.name', '%'.$assignee.'%');
+ }
+ }
+
+ $this->query->closeOr();
+ }
+
+ /**
* Filter by color
*
* @access public
@@ -180,6 +245,26 @@ class TaskFilter extends Base
}
/**
+ * Filter by colors
+ *
+ * @access public
+ * @param array $colors
+ * @return TaskFilter
+ */
+ public function filterByColors(array $colors)
+ {
+ $this->query->beginOr();
+
+ foreach ($colors as $color) {
+ $this->filterByColor($this->color->find($color));
+ }
+
+ $this->query->closeOr();
+
+ return $this;
+ }
+
+ /**
* Filter by column
*
* @access public
@@ -228,6 +313,18 @@ class TaskFilter extends Base
}
/**
+ * Filter by due date
+ *
+ * @access public
+ * @param string $date ISO8601 date format
+ * @return TaskFilter
+ */
+ public function filterByDueDate($date)
+ {
+ return $this->filterWithOperator('date_due', $date, true);
+ }
+
+ /**
* Filter by due date (range)
*
* @access public
@@ -295,6 +392,17 @@ class TaskFilter extends Base
}
/**
+ * Get the PicoDb query
+ *
+ * @access public
+ * @return \PicoDb\Table
+ */
+ public function getQuery()
+ {
+ return $this->query;
+ }
+
+ /**
* Format the results to the ajax autocompletion
*
* @access public
@@ -465,4 +573,36 @@ class TaskFilter extends Base
return $vEvent;
}
+
+ /**
+ * Filter with an operator
+ *
+ * @access public
+ * @param string $field
+ * @param string $value
+ * @param boolean $is_date
+ * @return TaskFilter
+ */
+ private function filterWithOperator($field, $value, $is_date)
+ {
+ $operators = array(
+ '<=' => 'lte',
+ '>=' => 'gte',
+ '<' => 'lt',
+ '>' => 'gt',
+ );
+
+ foreach ($operators as $operator => $method) {
+
+ if (strpos($value, $operator) === 0) {
+ $value = substr($value, strlen($operator));
+ $this->query->$method($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value);
+ return $this;
+ }
+ }
+
+ $this->query->eq($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value);
+
+ return $this;
+ }
}
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index 4ecd357b..1fa0d0ef 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -67,6 +67,7 @@ class ClassProvider implements ServiceProviderInterface
'EmailClient',
'Helper',
'HttpClient',
+ 'Lexer',
'MemoryCache',
'Request',
'Session',
diff --git a/docs/index.markdown b/docs/index.markdown
index 6eb6e6ef..77c6db2a 100644
--- a/docs/index.markdown
+++ b/docs/index.markdown
@@ -71,6 +71,7 @@ Using Kanboard
### More
+- [Search syntax](search.markdown)
- [Command line interface](cli.markdown)
- [Syntax guide](syntax-guide.markdown)
- [Frequently asked questions](faq.markdown)
diff --git a/docs/search.markdown b/docs/search.markdown
new file mode 100644
index 00000000..c6d5b76e
--- /dev/null
+++ b/docs/search.markdown
@@ -0,0 +1,106 @@
+Advanced Search Syntax
+======================
+
+Kanboard use a simple query language for advanced search.
+
+Example of query
+----------------
+
+This example will returns all tasks assigned to me with a due date for tomorrow and that have a title that contains "my title":
+
+```
+assigne:me due:tomorrow my title
+```
+
+Search by assignee
+------------------
+
+Attribute: **assignee**
+
+Query with the full name:
+
+```
+assignee:"Frederic Guillot"
+```
+
+Query with the username:
+
+```
+assignee:fguillot
+```
+
+Multiple assignee lookup:
+
+```
+assignee:user1 assignee:"John Doe"
+```
+
+Kanboard will search tasks assigned to the "user1" or "John Doe".
+
+Query for unassigned tasks:
+
+```
+assignee:nobody
+```
+
+Query for my assigned tasks
+
+```
+assignee:me
+```
+
+Search by color
+---------------
+
+Attribute: **color**
+
+Query to search by color id:
+
+```
+color:blue
+```
+
+Query to search by color name:
+
+```
+color:"Deep Orange"
+```
+
+Search by due date
+------------------
+
+Attribute: **due**
+
+Query to search tasks due today:
+
+```
+due:today
+```
+
+Query to search tasks due tomorrow:
+
+```
+due:tomorrow
+```
+
+Query to search tasks due yesterday:
+
+```
+due:yesterday
+```
+
+Query to search tasks due with the exact date:
+
+```
+due:2015-06-29
+```
+
+The date must use the ISO8601 format: **YYYY-MM-DD**.
+
+Operators supported:
+
+- Greater than: **due:>2015-06-29**
+- Lower than: **due:<2015-06-29**
+- Greater than or equal: **due:>=2015-06-29**
+- Lower than or equal: **due:<=2015-06-29**
+
diff --git a/tests/units/LexerTest.php b/tests/units/LexerTest.php
new file mode 100644
index 00000000..4abe451b
--- /dev/null
+++ b/tests/units/LexerTest.php
@@ -0,0 +1,193 @@
+<?php
+
+require_once __DIR__.'/Base.php';
+
+use Core\Lexer;
+
+class LexerTest extends Base
+{
+ public function testAssigneeQuery()
+ {
+ $lexer = new Lexer;
+
+ $this->assertEquals(
+ array(array('match' => 'assignee:', 'token' => 'T_ASSIGNEE'), array('match' => 'me', 'token' => 'T_STRING')),
+ $lexer->tokenize('assignee:me')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'assignee:', 'token' => 'T_ASSIGNEE'), array('match' => 'everybody', 'token' => 'T_STRING')),
+ $lexer->tokenize('assignee:everybody')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'assignee:', 'token' => 'T_ASSIGNEE'), array('match' => 'nobody', 'token' => 'T_STRING')),
+ $lexer->tokenize('assignee:nobody')
+ );
+
+ $this->assertEquals(
+ array('T_ASSIGNEE' => array('nobody')),
+ $lexer->map($lexer->tokenize('assignee:nobody'))
+ );
+
+ $this->assertEquals(
+ array('T_ASSIGNEE' => array('John Doe', 'me')),
+ $lexer->map($lexer->tokenize('assignee:"John Doe" assignee:me'))
+ );
+ }
+
+ public function testColorQuery()
+ {
+ $lexer = new Lexer;
+
+ $this->assertEquals(
+ array(array('match' => 'color:', 'token' => 'T_COLOR'), array('match' => 'Blue', 'token' => 'T_STRING')),
+ $lexer->tokenize('color:Blue')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'color:', 'token' => 'T_COLOR'), array('match' => 'Dark Grey', 'token' => 'T_STRING')),
+ $lexer->tokenize('color:"Dark Grey"')
+ );
+
+ $this->assertEquals(
+ array('T_COLOR' => array('Blue')),
+ $lexer->map($lexer->tokenize('color:Blue'))
+ );
+
+ $this->assertEquals(
+ array('T_COLOR' => array('Dark Grey')),
+ $lexer->map($lexer->tokenize('color:"Dark Grey"'))
+ );
+
+ $this->assertEquals(
+ array(),
+ $lexer->map($lexer->tokenize('color: '))
+ );
+ }
+
+ public function testDueDateQuery()
+ {
+ $lexer = new Lexer;
+
+ $this->assertEquals(
+ array(array('match' => 'due:', 'token' => 'T_DUE'), array('match' => '2015-05-01', 'token' => 'T_DATE')),
+ $lexer->tokenize('due:2015-05-01')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'due:', 'token' => 'T_DUE'), array('match' => '<2015-05-01', 'token' => 'T_DATE')),
+ $lexer->tokenize('due:<2015-05-01')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'due:', 'token' => 'T_DUE'), array('match' => '>2015-05-01', 'token' => 'T_DATE')),
+ $lexer->tokenize('due:>2015-05-01')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'due:', 'token' => 'T_DUE'), array('match' => '<=2015-05-01', 'token' => 'T_DATE')),
+ $lexer->tokenize('due:<=2015-05-01')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'due:', 'token' => 'T_DUE'), array('match' => '>=2015-05-01', 'token' => 'T_DATE')),
+ $lexer->tokenize('due:>=2015-05-01')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'due:', 'token' => 'T_DUE'), array('match' => 'yesterday', 'token' => 'T_DATE')),
+ $lexer->tokenize('due:yesterday')
+ );
+
+ $this->assertEquals(
+ array(array('match' => 'due:', 'token' => 'T_DUE'), array('match' => 'tomorrow', 'token' => 'T_DATE')),
+ $lexer->tokenize('due:tomorrow')
+ );
+
+ $this->assertEquals(
+ array(),
+ $lexer->tokenize('due:#2015-05-01')
+ );
+
+ $this->assertEquals(
+ array(),
+ $lexer->tokenize('due:01-05-1024')
+ );
+
+ $this->assertEquals(
+ array('T_DUE' => '2015-05-01'),
+ $lexer->map($lexer->tokenize('due:2015-05-01'))
+ );
+
+ $this->assertEquals(
+ array('T_DUE' => '<2015-05-01'),
+ $lexer->map($lexer->tokenize('due:<2015-05-01'))
+ );
+
+ $this->assertEquals(
+ array('T_DUE' => 'today'),
+ $lexer->map($lexer->tokenize('due:today'))
+ );
+ }
+
+ public function testMultipleCriterias()
+ {
+ $lexer = new Lexer;
+
+ $this->assertEquals(
+ array('T_COLOR' => array('Dark Grey'), 'T_ASSIGNEE' => array('Fred G'), 'T_TITLE' => 'my task title'),
+ $lexer->map($lexer->tokenize('color:"Dark Grey" assignee:"Fred G" my task title'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'my title', 'T_COLOR' => array('yellow')),
+ $lexer->map($lexer->tokenize('my title color:yellow'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'my title', 'T_DUE' => '2015-04-01'),
+ $lexer->map($lexer->tokenize('my title due:2015-04-01'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'awesome', 'T_DUE' => '<=2015-04-01'),
+ $lexer->map($lexer->tokenize('due:<=2015-04-01 awesome'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'awesome', 'T_DUE' => 'today'),
+ $lexer->map($lexer->tokenize('due:today awesome'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'my title', 'T_COLOR' => array('yellow'), 'T_DUE' => '2015-04-01'),
+ $lexer->map($lexer->tokenize('my title color:yellow due:2015-04-01'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'my title', 'T_COLOR' => array('yellow'), 'T_DUE' => '2015-04-01', 'T_ASSIGNEE' => array('John Doe')),
+ $lexer->map($lexer->tokenize('my title color:yellow due:2015-04-01 assignee:"John Doe"'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'my title'),
+ $lexer->map($lexer->tokenize('my title color:'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'my title'),
+ $lexer->map($lexer->tokenize('my title color:assignee:'))
+ );
+
+ $this->assertEquals(
+ array('T_TITLE' => 'my title'),
+ $lexer->map($lexer->tokenize('my title '))
+ );
+
+ $this->assertEquals(
+ array(),
+ $lexer->map($lexer->tokenize('color:assignee:'))
+ );
+ }
+}
diff --git a/tests/units/TaskFilterTest.php b/tests/units/TaskFilterTest.php
index 1872d429..e509371c 100644
--- a/tests/units/TaskFilterTest.php
+++ b/tests/units/TaskFilterTest.php
@@ -2,10 +2,180 @@
require_once __DIR__.'/Base.php';
+use Model\Project;
+use Model\User;
use Model\TaskFilter;
+use Model\TaskCreation;
+use Model\DateParser;
class TaskFilterTest extends Base
{
+ public function testSearchWithEmptyResult()
+ {
+ $dp = new DateParser($this->container);
+ $p = new Project($this->container);
+ $tc = new TaskCreation($this->container);
+ $tf = new TaskFilter($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test')));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'my task title is awesome', 'date_due' => $dp->getTimestampFromIsoFormat('-2 days'))));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'my task title is amazing', 'date_due' => $dp->getTimestampFromIsoFormat('+1 day'))));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'Bob at work', 'date_due' => $dp->getTimestampFromIsoFormat('-1 day'))));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'youpi', 'date_due' => $dp->getTimestampFromIsoFormat(time()))));
+
+ $this->assertEmpty($tf->search('search something')->findAll());
+ }
+
+ public function testSearchWithDueDate()
+ {
+ $dp = new DateParser($this->container);
+ $p = new Project($this->container);
+ $tc = new TaskCreation($this->container);
+ $tf = new TaskFilter($this->container);
+
+ $this->assertEquals(1, $p->create(array('name' => 'test')));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'my task title is awesome', 'date_due' => $dp->getTimestampFromIsoFormat('-2 days'))));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'my task title is amazing', 'date_due' => $dp->getTimestampFromIsoFormat('+1 day'))));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'Bob at work', 'date_due' => $dp->getTimestampFromIsoFormat('-1 day'))));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'youpi', 'date_due' => $dp->getTimestampFromIsoFormat(time()))));
+
+ $tf->search('due:>'.date('Y-m-d'));
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('my task title is amazing', $tasks[0]['title']);
+
+ $tf->search('due:>='.date('Y-m-d'));
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(2, $tasks);
+ $this->assertEquals('my task title is amazing', $tasks[0]['title']);
+ $this->assertEquals('youpi', $tasks[1]['title']);
+
+ $tf->search('due:<'.date('Y-m-d'));
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(2, $tasks);
+ $this->assertEquals('my task title is awesome', $tasks[0]['title']);
+ $this->assertEquals('Bob at work', $tasks[1]['title']);
+
+ $tf->search('due:<='.date('Y-m-d'));
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(3, $tasks);
+ $this->assertEquals('my task title is awesome', $tasks[0]['title']);
+ $this->assertEquals('Bob at work', $tasks[1]['title']);
+ $this->assertEquals('youpi', $tasks[2]['title']);
+
+ $tf->search('due:tomorrow');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('my task title is amazing', $tasks[0]['title']);
+
+ $tf->search('due:yesterday');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('Bob at work', $tasks[0]['title']);
+
+ $tf->search('due:today');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('youpi', $tasks[0]['title']);
+ }
+
+ public function testSearchWithColor()
+ {
+ $p = new Project($this->container);
+ $u = new User($this->container);
+ $tc = new TaskCreation($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')));
+
+ $tf->search('color:"Light Green"');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('my task title is awesome', $tasks[0]['title']);
+
+ $tf->search('color:"Light Green" amazing');
+ $tasks = $tf->findAll();
+ $this->assertEmpty($tasks);
+
+ $tf->search('color:"plop');
+ $tasks = $tf->findAll();
+ $this->assertEmpty($tasks);
+
+ $tf->search('color:unknown');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(3, $tasks);
+
+ $tf->search('color:blue amazing');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('my task title is amazing', $tasks[0]['title']);
+
+ $tf->search('color:blue color:Yellow');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(2, $tasks);
+ $this->assertEquals('my task title is amazing', $tasks[0]['title']);
+ $this->assertEquals('Bob at work', $tasks[1]['title']);
+ }
+
+ public function testSearchWithAssignee()
+ {
+ $p = new Project($this->container);
+ $u = new User($this->container);
+ $tc = new TaskCreation($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', 'owner_id' => 1)));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'my task title is amazing', 'owner_id' => 0)));
+ $this->assertNotFalse($tc->create(array('project_id' => 1, 'title' => 'Bob at work', 'owner_id' => 2)));
+
+ $tf->search('assignee:john');
+ $tasks = $tf->findAll();
+ $this->assertEmpty($tasks);
+
+ $tf->search('assignee:admin my task title');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('my task title is awesome', $tasks[0]['title']);
+
+ $tf->search('my task title');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(2, $tasks);
+ $this->assertEquals('my task title is awesome', $tasks[0]['title']);
+ $this->assertEquals('my task title is amazing', $tasks[1]['title']);
+
+ $tf->search('my task title assignee:nobody');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(1, $tasks);
+ $this->assertEquals('my task title is amazing', $tasks[0]['title']);
+
+ $tf->search('assignee:"Bob ryan" assignee:nobody');
+ $tasks = $tf->findAll();
+ $this->assertNotEmpty($tasks);
+ $this->assertCount(2, $tasks);
+ $this->assertEquals('my task title is amazing', $tasks[0]['title']);
+ $this->assertEquals('Bob at work', $tasks[1]['title']);
+ }
+
public function testCopy()
{
$tf = new TaskFilter($this->container);