Add new search attributes: created, modified and updated
This commit is contained in:
@@ -28,6 +28,9 @@ class Lexer
|
|||||||
"/^(assignee:)/" => 'T_ASSIGNEE',
|
"/^(assignee:)/" => 'T_ASSIGNEE',
|
||||||
"/^(color:)/" => 'T_COLOR',
|
"/^(color:)/" => 'T_COLOR',
|
||||||
"/^(due:)/" => 'T_DUE',
|
"/^(due:)/" => 'T_DUE',
|
||||||
|
"/^(updated:)/" => 'T_UPDATED',
|
||||||
|
"/^(modified:)/" => 'T_UPDATED',
|
||||||
|
"/^(created:)/" => 'T_CREATED',
|
||||||
"/^(status:)/" => 'T_STATUS',
|
"/^(status:)/" => 'T_STATUS',
|
||||||
"/^(description:)/" => 'T_DESCRIPTION',
|
"/^(description:)/" => 'T_DESCRIPTION',
|
||||||
"/^(category:)/" => 'T_CATEGORY',
|
"/^(category:)/" => 'T_CATEGORY',
|
||||||
@@ -128,6 +131,8 @@ class Lexer
|
|||||||
|
|
||||||
case 'T_STATUS':
|
case 'T_STATUS':
|
||||||
case 'T_DUE':
|
case 'T_DUE':
|
||||||
|
case 'T_UPDATED':
|
||||||
|
case 'T_CREATED':
|
||||||
case 'T_DESCRIPTION':
|
case 'T_DESCRIPTION':
|
||||||
case 'T_REFERENCE':
|
case 'T_REFERENCE':
|
||||||
$next = next($tokens);
|
$next = next($tokens);
|
||||||
|
|||||||
@@ -50,6 +50,12 @@ class TaskFilter extends Base
|
|||||||
case 'T_DUE':
|
case 'T_DUE':
|
||||||
$this->filterByDueDate($value);
|
$this->filterByDueDate($value);
|
||||||
break;
|
break;
|
||||||
|
case 'T_UPDATED':
|
||||||
|
$this->filterByModificationDate($value);
|
||||||
|
break;
|
||||||
|
case 'T_CREATED':
|
||||||
|
$this->filterByCreationDate($value);
|
||||||
|
break;
|
||||||
case 'T_TITLE':
|
case 'T_TITLE':
|
||||||
$this->filterByTitle($value);
|
$this->filterByTitle($value);
|
||||||
break;
|
break;
|
||||||
@@ -576,6 +582,22 @@ class TaskFilter extends Base
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter by creation date
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $date ISO8601 date format
|
||||||
|
* @return TaskFilter
|
||||||
|
*/
|
||||||
|
public function filterByCreationDate($date)
|
||||||
|
{
|
||||||
|
if ($date === 'recently') {
|
||||||
|
return $this->filterRecentlyDate(Task::TABLE.'.date_creation');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->filterWithOperator(Task::TABLE.'.date_creation', $date, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filter by creation date
|
* Filter by creation date
|
||||||
*
|
*
|
||||||
@@ -596,6 +618,22 @@ class TaskFilter extends Base
|
|||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter by modification date
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $date ISO8601 date format
|
||||||
|
* @return TaskFilter
|
||||||
|
*/
|
||||||
|
public function filterByModificationDate($date)
|
||||||
|
{
|
||||||
|
if ($date === 'recently') {
|
||||||
|
return $this->filterRecentlyDate(Task::TABLE.'.date_modification');
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->filterWithOperator(Task::TABLE.'.date_modification', $date, true);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all results of the filter
|
* Get all results of the filter
|
||||||
*
|
*
|
||||||
@@ -826,7 +864,6 @@ class TaskFilter extends Base
|
|||||||
);
|
);
|
||||||
|
|
||||||
foreach ($operators as $operator => $method) {
|
foreach ($operators as $operator => $method) {
|
||||||
|
|
||||||
if (strpos($value, $operator) === 0) {
|
if (strpos($value, $operator) === 0) {
|
||||||
$value = substr($value, strlen($operator));
|
$value = substr($value, strlen($operator));
|
||||||
$this->query->$method($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value);
|
$this->query->$method($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value);
|
||||||
@@ -834,7 +871,32 @@ class TaskFilter extends Base
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->query->eq($field, $is_date ? $this->dateParser->getTimestampFromIsoFormat($value) : $value);
|
if ($is_date) {
|
||||||
|
$timestamp = $this->dateParser->getTimestampFromIsoFormat($value);
|
||||||
|
$this->query->gte($field, $timestamp);
|
||||||
|
$this->query->lte($field, $timestamp + 86399);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$this->query->eq($field, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the board_highlight_period for the "recently" keyword
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @param string $field
|
||||||
|
* @return TaskFilter
|
||||||
|
*/
|
||||||
|
private function filterRecentlyDate($field)
|
||||||
|
{
|
||||||
|
$duration = $this->config->get('board_highlight_period', 0);
|
||||||
|
|
||||||
|
if ($duration > 0) {
|
||||||
|
$this->query->gte($field, time() - $duration);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,95 +31,61 @@ Search by assignee
|
|||||||
|
|
||||||
Attribute: **assignee**
|
Attribute: **assignee**
|
||||||
|
|
||||||
Query with the full name:
|
- Query with the full name: `assignee:"Frederic Guillot"`
|
||||||
|
- Query with the username: `assignee:fguillot`
|
||||||
|
- Multiple assignee lookup: `assignee:user1 assignee:"John Doe"`
|
||||||
|
- Query for unassigned tasks: `assignee:nobody`
|
||||||
|
- Query for my assigned tasks: `assignee:me`
|
||||||
|
|
||||||
```
|
Note: Kanboard will also search in assigned subtasks with the status todo and in progress.
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
Note: Results will also include subtasks assignee with the status todo or in progress.
|
|
||||||
|
|
||||||
Search by color
|
Search by color
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Attribute: **color**
|
Attribute: **color**
|
||||||
|
|
||||||
Query to search by color id:
|
- Query to search by color id: `color:blue`
|
||||||
|
- Query to search by color name: `color:"Deep Orange"`
|
||||||
```
|
|
||||||
color:blue
|
|
||||||
```
|
|
||||||
|
|
||||||
Query to search by color name:
|
|
||||||
|
|
||||||
```
|
|
||||||
color:"Deep Orange"
|
|
||||||
```
|
|
||||||
|
|
||||||
Search by due date
|
Search by due date
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Attribute: **due**
|
Attribute: **due**
|
||||||
|
|
||||||
Query to search tasks due today:
|
- Search tasks due today: `due:today`
|
||||||
|
- Search tasks due tomorrow: `due:tomorrow`
|
||||||
```
|
- Search tasks due yesterday: `due:yesterday`
|
||||||
due:today
|
- Search tasks due with the exact date: `due:2015-06-29`
|
||||||
```
|
|
||||||
|
|
||||||
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**.
|
The date must use the ISO8601 format: **YYYY-MM-DD**.
|
||||||
|
|
||||||
Operators supported:
|
All string formats supported by the `strtotime()` function are supported, by example `next Thursday`, `-2 days`, `+2 months`, `tomorrow`, etc...
|
||||||
|
|
||||||
|
Operators supported with a date:
|
||||||
|
|
||||||
- Greater than: **due:>2015-06-29**
|
- Greater than: **due:>2015-06-29**
|
||||||
- Lower than: **due:<2015-06-29**
|
- Lower than: **due:<2015-06-29**
|
||||||
- Greater than or equal: **due:>=2015-06-29**
|
- Greater than or equal: **due:>=2015-06-29**
|
||||||
- Lower than or equal: **due:<=2015-06-29**
|
- Lower than or equal: **due:<=2015-06-29**
|
||||||
|
|
||||||
|
Search by modification date
|
||||||
|
---------------------------
|
||||||
|
|
||||||
|
Attribute: **modified** or **updated**
|
||||||
|
|
||||||
|
The date formats are the same as the due date.
|
||||||
|
|
||||||
|
There is also a filter by recently modified tasks: `modified:recently`.
|
||||||
|
|
||||||
|
This query will use the same value as the board highlight period configured in settings.
|
||||||
|
|
||||||
|
Search by creation date
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Attribute: **created**
|
||||||
|
|
||||||
|
Works in the same way as the modification date queries.
|
||||||
|
|
||||||
Search by description
|
Search by description
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
|
|||||||
@@ -311,6 +311,71 @@ class LexerTest extends Base
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testModifiedQuery()
|
||||||
|
{
|
||||||
|
$lexer = new Lexer;
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('match' => 'modified:', 'token' => 'T_UPDATED'), array('match' => '2015-05-01', 'token' => 'T_DATE')),
|
||||||
|
$lexer->tokenize('modified:2015-05-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('match' => 'modified:', 'token' => 'T_UPDATED'), array('match' => '<2015-05-01', 'token' => 'T_DATE')),
|
||||||
|
$lexer->tokenize('modified:<2015-05-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('match' => 'modified:', 'token' => 'T_UPDATED'), array('match' => '>2015-05-01', 'token' => 'T_DATE')),
|
||||||
|
$lexer->tokenize('modified:>2015-05-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('match' => 'updated:', 'token' => 'T_UPDATED'), array('match' => '<=2015-05-01', 'token' => 'T_DATE')),
|
||||||
|
$lexer->tokenize('updated:<=2015-05-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('match' => 'updated:', 'token' => 'T_UPDATED'), array('match' => '>=2015-05-01', 'token' => 'T_DATE')),
|
||||||
|
$lexer->tokenize('updated:>=2015-05-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('match' => 'updated:', 'token' => 'T_UPDATED'), array('match' => 'yesterday', 'token' => 'T_DATE')),
|
||||||
|
$lexer->tokenize('updated:yesterday')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(array('match' => 'updated:', 'token' => 'T_UPDATED'), array('match' => 'tomorrow', 'token' => 'T_DATE')),
|
||||||
|
$lexer->tokenize('updated:tomorrow')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(),
|
||||||
|
$lexer->tokenize('updated:#2015-05-01')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array(),
|
||||||
|
$lexer->tokenize('modified:01-05-1024')
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array('T_UPDATED' => '2015-05-01'),
|
||||||
|
$lexer->map($lexer->tokenize('modified:2015-05-01'))
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array('T_UPDATED' => '<2015-05-01'),
|
||||||
|
$lexer->map($lexer->tokenize('modified:<2015-05-01'))
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertEquals(
|
||||||
|
array('T_UPDATED' => 'today'),
|
||||||
|
$lexer->map($lexer->tokenize('modified:today'))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function testMultipleCriterias()
|
public function testMultipleCriterias()
|
||||||
{
|
{
|
||||||
$lexer = new Lexer;
|
$lexer = new Lexer;
|
||||||
|
|||||||
Reference in New Issue
Block a user