Added search in activity stream

This commit is contained in:
Frederic Guillot 2016-04-10 15:18:20 -04:00
parent 2eadfb2291
commit 9f0166502b
54 changed files with 1066 additions and 110 deletions

View File

@ -3,6 +3,7 @@ Version 1.0.28 (unreleased)
New features: New features:
* Search in activity stream
* Search in comments * Search in comments
* Search by task creator * Search by task creator

View File

@ -46,4 +46,22 @@ class Search extends Base
'title' => t('Search tasks').($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '') 'title' => t('Search tasks').($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
))); )));
} }
public function activity()
{
$search = urldecode($this->request->getStringParam('search'));
$events = $this->helper->projectActivity->searchEvents($search);
$nb_events = count($events);
$this->response->html($this->helper->layout->app('search/activity', array(
'values' => array(
'search' => $search,
'controller' => 'search',
'action' => 'activity',
),
'title' => t('Search in activity stream').($nb_events > 0 ? ' ('.$nb_events.')' : ''),
'nb_events' => $nb_events,
'events' => $events,
)));
}
} }

View File

@ -0,0 +1,103 @@
<?php
namespace Kanboard\Filter;
use Kanboard\Core\DateParser;
/**
* Base date filter class
*
* @package filter
* @author Frederic Guillot
*/
abstract class BaseDateFilter extends BaseFilter
{
/**
* DateParser object
*
* @access protected
* @var DateParser
*/
protected $dateParser;
/**
* Set DateParser object
*
* @access public
* @param DateParser $dateParser
* @return $this
*/
public function setDateParser(DateParser $dateParser)
{
$this->dateParser = $dateParser;
return $this;
}
/**
* Parse operator in the input string
*
* @access protected
* @return string
*/
protected function parseOperator()
{
$operators = array(
'<=' => 'lte',
'>=' => 'gte',
'<' => 'lt',
'>' => 'gt',
);
foreach ($operators as $operator => $method) {
if (strpos($this->value, $operator) === 0) {
$this->value = substr($this->value, strlen($operator));
return $method;
}
}
return '';
}
/**
* Apply a date filter
*
* @access protected
* @param string $field
*/
protected function applyDateFilter($field)
{
$method = $this->parseOperator();
$timestamp = $this->dateParser->getTimestampFromIsoFormat($this->value);
if ($method !== '') {
$this->query->$method($field, $this->getTimestampFromOperator($method, $timestamp));
} else {
$this->query->gte($field, $timestamp);
$this->query->lte($field, $timestamp + 86399);
}
}
/**
* Get timestamp from the operator
*
* @access public
* @param string $method
* @param integer $timestamp
* @return integer
*/
protected function getTimestampFromOperator($method, $timestamp)
{
switch ($method) {
case 'lte':
return $timestamp + 86399;
case 'lt':
return $timestamp;
case 'gte':
return $timestamp;
case 'gt':
return $timestamp + 86400;
}
return $timestamp;
}
}

View File

@ -72,48 +72,4 @@ abstract class BaseFilter
$this->value = $value; $this->value = $value;
return $this; return $this;
} }
/**
* Parse operator in the input string
*
* @access protected
* @return string
*/
protected function parseOperator()
{
$operators = array(
'<=' => 'lte',
'>=' => 'gte',
'<' => 'lt',
'>' => 'gt',
);
foreach ($operators as $operator => $method) {
if (strpos($this->value, $operator) === 0) {
$this->value = substr($this->value, strlen($operator));
return $method;
}
}
return '';
}
/**
* Apply a date filter
*
* @access protected
* @param string $field
*/
protected function applyDateFilter($field)
{
$timestamp = strtotime($this->value);
$method = $this->parseOperator();
if ($method !== '') {
$this->query->$method($field, $timestamp);
} else {
$this->query->gte($field, $timestamp);
$this->query->lte($field, $timestamp + 86399);
}
}
} }

View File

@ -0,0 +1,38 @@
<?php
namespace Kanboard\Filter;
use Kanboard\Core\Filter\FilterInterface;
use Kanboard\Model\ProjectActivity;
/**
* Filter activity events by creation date
*
* @package filter
* @author Frederic Guillot
*/
class ProjectActivityCreationDateFilter extends BaseDateFilter implements FilterInterface
{
/**
* Get search attribute
*
* @access public
* @return string[]
*/
public function getAttributes()
{
return array('created');
}
/**
* Apply filter
*
* @access public
* @return FilterInterface
*/
public function apply()
{
$this->applyDateFilter(ProjectActivity::TABLE.'.date_creation');
return $this;
}
}

View File

@ -0,0 +1,65 @@
<?php
namespace Kanboard\Filter;
use Kanboard\Core\Filter\FilterInterface;
use Kanboard\Model\ProjectActivity;
/**
* Filter activity events by creator
*
* @package filter
* @author Frederic Guillot
*/
class ProjectActivityCreatorFilter extends BaseFilter implements FilterInterface
{
/**
* Current user id
*
* @access private
* @var int
*/
private $currentUserId = 0;
/**
* Set current user id
*
* @access public
* @param integer $userId
* @return TaskAssigneeFilter
*/
public function setCurrentUserId($userId)
{
$this->currentUserId = $userId;
return $this;
}
/**
* Get search attribute
*
* @access public
* @return string[]
*/
public function getAttributes()
{
return array('creator');
}
/**
* Apply filter
*
* @access public
* @return string
*/
public function apply()
{
if ($this->value === 'me') {
$this->query->eq(ProjectActivity::TABLE . '.creator_id', $this->currentUserId);
} else {
$this->query->beginOr();
$this->query->ilike('uc.username', '%'.$this->value.'%');
$this->query->ilike('uc.name', '%'.$this->value.'%');
$this->query->closeOr();
}
}
}

View File

@ -21,7 +21,7 @@ class ProjectActivityProjectIdsFilter extends BaseFilter implements FilterInterf
*/ */
public function getAttributes() public function getAttributes()
{ {
return array('project_ids'); return array('projects');
} }
/** /**

View File

@ -0,0 +1,38 @@
<?php
namespace Kanboard\Filter;
use Kanboard\Core\Filter\FilterInterface;
use Kanboard\Model\Project;
/**
* Filter activity events by project name
*
* @package filter
* @author Frederic Guillot
*/
class ProjectActivityProjectNameFilter extends BaseFilter implements FilterInterface
{
/**
* Get search attribute
*
* @access public
* @return string[]
*/
public function getAttributes()
{
return array('project');
}
/**
* Apply filter
*
* @access public
* @return FilterInterface
*/
public function apply()
{
$this->query->ilike(Project::TABLE.'.name', '%'.$this->value.'%');
return $this;
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace Kanboard\Filter;
use Kanboard\Core\Filter\FilterInterface;
use Kanboard\Model\Task;
/**
* Filter activity events by task status
*
* @package filter
* @author Frederic Guillot
*/
class ProjectActivityTaskStatusFilter extends BaseFilter implements FilterInterface
{
/**
* Get search attribute
*
* @access public
* @return string[]
*/
public function getAttributes()
{
return array('status');
}
/**
* Apply filter
*
* @access public
* @return FilterInterface
*/
public function apply()
{
if ($this->value === 'open') {
$this->query->eq(Task::TABLE.'.is_active', Task::STATUS_OPEN);
} elseif ($this->value === 'closed') {
$this->query->eq(Task::TABLE.'.is_active', Task::STATUS_CLOSED);
}
return $this;
}
}

View File

@ -3,7 +3,6 @@
namespace Kanboard\Filter; namespace Kanboard\Filter;
use Kanboard\Core\Filter\FilterInterface; use Kanboard\Core\Filter\FilterInterface;
use Kanboard\Model\Task;
/** /**
* Filter activity events by task title * Filter activity events by task title
@ -11,7 +10,7 @@ use Kanboard\Model\Task;
* @package filter * @package filter
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class ProjectActivityTaskTitleFilter extends BaseFilter implements FilterInterface class ProjectActivityTaskTitleFilter extends TaskTitleFilter implements FilterInterface
{ {
/** /**
* Get search attribute * Get search attribute
@ -23,16 +22,4 @@ class ProjectActivityTaskTitleFilter extends BaseFilter implements FilterInterfa
{ {
return array('title'); return array('title');
} }
/**
* Apply filter
*
* @access public
* @return FilterInterface
*/
public function apply()
{
$this->query->ilike(Task::TABLE.'.title', '%'.$this->value.'%');
return $this;
}
} }

View File

@ -11,7 +11,7 @@ use Kanboard\Model\Task;
* @package filter * @package filter
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskCompletionDateFilter extends BaseFilter implements FilterInterface class TaskCompletionDateFilter extends BaseDateFilter implements FilterInterface
{ {
/** /**
* Get search attribute * Get search attribute

View File

@ -11,7 +11,7 @@ use Kanboard\Model\Task;
* @package filter * @package filter
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskCreationDateFilter extends BaseFilter implements FilterInterface class TaskCreationDateFilter extends BaseDateFilter implements FilterInterface
{ {
/** /**
* Get search attribute * Get search attribute

View File

@ -11,7 +11,7 @@ use Kanboard\Model\Task;
* @package filter * @package filter
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskDueDateFilter extends BaseFilter implements FilterInterface class TaskDueDateFilter extends BaseDateFilter implements FilterInterface
{ {
/** /**
* Get search attribute * Get search attribute

View File

@ -11,7 +11,7 @@ use Kanboard\Model\Task;
* @package filter * @package filter
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskModificationDateFilter extends BaseFilter implements FilterInterface class TaskModificationDateFilter extends BaseDateFilter implements FilterInterface
{ {
/** /**
* Get search attribute * Get search attribute

View File

@ -32,7 +32,12 @@ class TaskProjectsFilter extends BaseFilter implements FilterInterface
*/ */
public function apply() public function apply()
{ {
$this->query->in(Task::TABLE.'.project_id', $this->value); if (empty($this->value)) {
$this->query->eq(Task::TABLE.'.project_id', 0);
} else {
$this->query->in(Task::TABLE.'.project_id', $this->value);
}
return $this; return $this;
} }
} }

View File

@ -11,7 +11,7 @@ use Kanboard\Model\Task;
* @package filter * @package filter
* @author Frederic Guillot * @author Frederic Guillot
*/ */
class TaskStartDateFilter extends BaseFilter implements FilterInterface class TaskStartDateFilter extends BaseDateFilter implements FilterInterface
{ {
/** /**
* Get search attribute * Get search attribute

View File

@ -17,6 +17,33 @@ use Kanboard\Model\ProjectActivity;
*/ */
class ProjectActivityHelper extends Base class ProjectActivityHelper extends Base
{ {
/**
* Search events
*
* @access public
* @param string $search
* @return array
*/
public function searchEvents($search)
{
$projects = $this->projectUserRole->getProjectsByUser($this->userSession->getId());
$events = array();
if ($search !== '') {
$queryBuilder = $this->projectActivityLexer->build($search);
$queryBuilder
->withFilter(new ProjectActivityProjectIdsFilter(array_keys($projects)))
->getQuery()
->desc(ProjectActivity::TABLE.'.id')
->limit(500)
;
$events = $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
}
return $events;
}
/** /**
* Get project activity events * Get project activity events
* *

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
'Upload my avatar image' => 'Mein Avatar Bild hochladen', 'Upload my avatar image' => 'Mein Avatar Bild hochladen',
'Remove my image' => 'Mein Bild entfernen', 'Remove my image' => 'Mein Bild entfernen',
'The OAuth2 state parameter is invalid' => 'Der OAuth2 Statusparameter ist ungültig', 'The OAuth2 state parameter is invalid' => 'Der OAuth2 Statusparameter ist ungültig',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
'Upload my avatar image' => 'Uploader mon image d\'avatar', 'Upload my avatar image' => 'Uploader mon image d\'avatar',
'Remove my image' => 'Supprimer mon image', 'Remove my image' => 'Supprimer mon image',
'The OAuth2 state parameter is invalid' => 'Le paramètre "state" de OAuth2 est invalide', 'The OAuth2 state parameter is invalid' => 'Le paramètre "state" de OAuth2 est invalide',
'User not found.' => 'Utilisateur introuvable.',
'Search in activity stream' => 'Chercher dans le flux d\'activité',
'My activities' => 'Mes activités',
'Activity until yesterday' => 'Activités jusqu\'à hier',
'Activity until today' => 'Activités jusqu\'à aujourd\'hui',
'Search by creator: ' => 'Rechercher par créateur : ',
'Search by creation date: ' => 'Rechercher par date de création : ',
'Search by task status: ' => 'Rechercher par le statut des tâches : ',
'Search by task title: ' => 'Rechercher par le titre des tâches : ',
'Activity stream search' => 'Recherche dans le flux d\'activité',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
'Upload my avatar image' => 'Enviar a minha imagem de avatar', 'Upload my avatar image' => 'Enviar a minha imagem de avatar',
'Remove my image' => 'Remover a minha imagem', 'Remove my image' => 'Remover a minha imagem',
'The OAuth2 state parameter is invalid' => 'O parâmetro "state" de OAuth2 não é válido', 'The OAuth2 state parameter is invalid' => 'O parâmetro "state" de OAuth2 não é válido',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
'Upload my avatar image' => 'Enviar a minha imagem de avatar', 'Upload my avatar image' => 'Enviar a minha imagem de avatar',
'Remove my image' => 'Remover a minha imagem', 'Remove my image' => 'Remover a minha imagem',
'The OAuth2 state parameter is invalid' => 'O parametro de estado do OAuth2 é inválido', 'The OAuth2 state parameter is invalid' => 'O parametro de estado do OAuth2 é inválido',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1152,5 +1152,15 @@ return array(
'Avatar' => 'Аватар', 'Avatar' => 'Аватар',
'Upload my avatar image' => 'Загрузить моё изображение для аватара', 'Upload my avatar image' => 'Загрузить моё изображение для аватара',
'Remove my image' => 'Удалить моё изображение', 'Remove my image' => 'Удалить моё изображение',
'The OAuth2 state parameter is invalid' => 'Параметр состояние OAuth2 неправильный' 'The OAuth2 state parameter is invalid' => 'Параметр состояние OAuth2 неправильный',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -1153,4 +1153,14 @@ return array(
// 'Upload my avatar image' => '', // 'Upload my avatar image' => '',
// 'Remove my image' => '', // 'Remove my image' => '',
// 'The OAuth2 state parameter is invalid' => '', // 'The OAuth2 state parameter is invalid' => '',
// 'User not found.' => '',
// 'Search in activity stream' => '',
// 'My activities' => '',
// 'Activity until yesterday' => '',
// 'Activity until today' => '',
// 'Search by creator: ' => '',
// 'Search by creation date: ' => '',
// 'Search by task status: ' => '',
// 'Search by task title: ' => '',
// 'Activity stream search' => '',
); );

View File

@ -71,6 +71,7 @@ class ProjectActivity extends Base
'uc.avatar_path' 'uc.avatar_path'
) )
->join(Task::TABLE, 'id', 'task_id') ->join(Task::TABLE, 'id', 'task_id')
->join(Project::TABLE, 'id', 'project_id')
->left(User::TABLE, 'uc', 'id', ProjectActivity::TABLE, 'creator_id'); ->left(User::TABLE, 'uc', 'id', ProjectActivity::TABLE, 'creator_id');
} }

View File

@ -4,6 +4,10 @@ namespace Kanboard\ServiceProvider;
use Kanboard\Core\Filter\LexerBuilder; use Kanboard\Core\Filter\LexerBuilder;
use Kanboard\Core\Filter\QueryBuilder; use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\ProjectActivityCreationDateFilter;
use Kanboard\Filter\ProjectActivityCreatorFilter;
use Kanboard\Filter\ProjectActivityProjectNameFilter;
use Kanboard\Filter\ProjectActivityTaskStatusFilter;
use Kanboard\Filter\ProjectActivityTaskTitleFilter; use Kanboard\Filter\ProjectActivityTaskTitleFilter;
use Kanboard\Filter\TaskAssigneeFilter; use Kanboard\Filter\TaskAssigneeFilter;
use Kanboard\Filter\TaskCategoryFilter; use Kanboard\Filter\TaskCategoryFilter;
@ -86,8 +90,18 @@ class FilterProvider implements ServiceProviderInterface
$container['projectActivityLexer'] = $container->factory(function ($c) { $container['projectActivityLexer'] = $container->factory(function ($c) {
$builder = new LexerBuilder(); $builder = new LexerBuilder();
$builder->withQuery($c['projectActivity']->getQuery()); $builder
$builder->withFilter(new ProjectActivityTaskTitleFilter()); ->withQuery($c['projectActivity']->getQuery())
->withFilter(new ProjectActivityTaskTitleFilter(), true)
->withFilter(new ProjectActivityTaskStatusFilter())
->withFilter(new ProjectActivityProjectNameFilter())
->withFilter(ProjectActivityCreationDateFilter::getInstance()
->setDateParser($c['dateParser'])
)
->withFilter(ProjectActivityCreatorFilter::getInstance()
->setCurrentUserId($c['userSession']->getId())
)
;
return $builder; return $builder;
}); });
@ -124,17 +138,23 @@ class FilterProvider implements ServiceProviderInterface
) )
->withFilter(new TaskColumnFilter()) ->withFilter(new TaskColumnFilter())
->withFilter(new TaskCommentFilter()) ->withFilter(new TaskCommentFilter())
->withFilter(new TaskCreationDateFilter()) ->withFilter(TaskCreationDateFilter::getInstance()
->setDateParser($c['dateParser'])
)
->withFilter(TaskCreatorFilter::getInstance() ->withFilter(TaskCreatorFilter::getInstance()
->setCurrentUserId($c['userSession']->getId()) ->setCurrentUserId($c['userSession']->getId())
) )
->withFilter(new TaskDescriptionFilter()) ->withFilter(new TaskDescriptionFilter())
->withFilter(new TaskDueDateFilter()) ->withFilter(TaskDueDateFilter::getInstance()
->setDateParser($c['dateParser'])
)
->withFilter(new TaskIdFilter()) ->withFilter(new TaskIdFilter())
->withFilter(TaskLinkFilter::getInstance() ->withFilter(TaskLinkFilter::getInstance()
->setDatabase($c['db']) ->setDatabase($c['db'])
) )
->withFilter(new TaskModificationDateFilter()) ->withFilter(TaskModificationDateFilter::getInstance()
->setDateParser($c['dateParser'])
)
->withFilter(new TaskProjectFilter()) ->withFilter(new TaskProjectFilter())
->withFilter(new TaskReferenceFilter()) ->withFilter(new TaskReferenceFilter())
->withFilter(new TaskStatusFilter()) ->withFilter(new TaskStatusFilter())

View File

@ -42,7 +42,7 @@ class RouteProvider implements ServiceProviderInterface
// Search routes // Search routes
$container['route']->addRoute('search', 'search', 'index'); $container['route']->addRoute('search', 'search', 'index');
$container['route']->addRoute('search/:search', 'search', 'index'); $container['route']->addRoute('search/activity', 'search', 'activity');
// ProjectCreation routes // ProjectCreation routes
$container['route']->addRoute('project/create', 'ProjectCreation', 'create'); $container['route']->addRoute('project/create', 'ProjectCreation', 'create');
@ -62,6 +62,7 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('project/:project_id/enable', 'project', 'enable'); $container['route']->addRoute('project/:project_id/enable', 'project', 'enable');
$container['route']->addRoute('project/:project_id/permissions', 'ProjectPermission', 'index'); $container['route']->addRoute('project/:project_id/permissions', 'ProjectPermission', 'index');
$container['route']->addRoute('project/:project_id/import', 'taskImport', 'step1'); $container['route']->addRoute('project/:project_id/import', 'taskImport', 'step1');
$container['route']->addRoute('project/:project_id/activity', 'activity', 'project');
// Project Overview // Project Overview
$container['route']->addRoute('project/:project_id/overview', 'ProjectOverview', 'show'); $container['route']->addRoute('project/:project_id/overview', 'ProjectOverview', 'show');

View File

@ -0,0 +1,14 @@
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('Default filters') ?>"><i class="fa fa-filter fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li><a href="#" class="filter-helper filter-reset" data-filter="" title="<?= t('Keyboard shortcut: "%s"', 'r') ?>"><?= t('Reset filters') ?></a></li>
<li><a href="#" class="filter-helper" data-filter="creator:me"><?= t('My activities') ?></a></li>
<li><a href="#" class="filter-helper" data-filter="created:<=<?= date('Y-m-d', strtotime('yesterday')) ?>"><?= t('Activity until yesterday') ?></a></li>
<li><a href="#" class="filter-helper" data-filter="created:<=<?= date('Y-m-d')?>"><?= t('Activity until today') ?></a></li>
<li><a href="#" class="filter-helper" data-filter="status:closed"><?= t('Closed tasks') ?></a></li>
<li><a href="#" class="filter-helper" data-filter="status:open"><?= t('Open tasks') ?></a></li>
<li>
<?= $this->url->doc(t('View advanced search syntax'), 'search') ?>
</li>
</ul>
</div>

View File

@ -0,0 +1,39 @@
<section id="main">
<div class="page-header">
<ul>
<li>
<i class="fa fa-search fa-fw"></i>
<?= $this->url->link(t('Search tasks'), 'search', 'index') ?>
</li>
</ul>
</div>
<div class="filter-box">
<form method="get" action="<?= $this->url->dir() ?>" class="search">
<?= $this->form->hidden('controller', $values) ?>
<?= $this->form->hidden('action', $values) ?>
<?= $this->form->text('search', $values, array(), array(empty($values['search']) ? 'autofocus' : '', 'placeholder="'.t('Search').'"'), 'form-input-large') ?>
<?= $this->render('activity/filter_dropdown') ?>
</form>
</div>
<?php if (empty($values['search'])): ?>
<div class="listing">
<h3><?= t('Advanced search') ?></h3>
<p><?= t('Example of query: ') ?><strong>project:"My project" creator:me</strong></p>
<ul>
<li><?= t('Search by project: ') ?><strong>project:"My project"</strong></li>
<li><?= t('Search by creator: ') ?><strong>creator:admin</strong></li>
<li><?= t('Search by creation date: ') ?><strong>created:today</strong></li>
<li><?= t('Search by task status: ') ?><strong>status:open</strong></li>
<li><?= t('Search by task title: ') ?><strong>title:"My task"</strong></li>
</ul>
<p><i class="fa fa-external-link fa-fw"></i><?= $this->url->doc(t('View advanced search syntax'), 'search') ?></p>
</div>
<?php elseif (! empty($values['search']) && $nb_events === 0): ?>
<p class="alert"><?= t('Nothing found.') ?></p>
<?php else: ?>
<?= $this->render('event/events', array('events' => $events)) ?>
<?php endif ?>
</section>

View File

@ -2,8 +2,8 @@
<div class="page-header"> <div class="page-header">
<ul> <ul>
<li> <li>
<i class="fa fa-folder fa-fw"></i> <i class="fa fa-search fa-fw"></i>
<?= $this->url->link(t('All projects'), 'project', 'index') ?> <?= $this->url->link(t('Activity stream search'), 'search', 'activity') ?>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -1,7 +1,8 @@
Advanced Search Syntax Advanced Search Syntax
====================== ======================
Kanboard uses a simple query language for advanced search. Kanboard uses a simple query language for advanced search.
You can search in tasks, comments, subtasks, links but also in the activity stream.
Example of query Example of query
---------------- ----------------
@ -12,23 +13,23 @@ This example will return all tasks assigned to me with a due date for tomorrow a
assigne:me due:tomorrow my title assigne:me due:tomorrow my title
``` ```
Search by task id or title Global search
-------------------------- -------------
### Search by task id or title
- Search by task id: `#123` - Search by task id: `#123`
- Search by task id and task title: `123` - Search by task id and task title: `123`
- Search by task title: anything that doesn't match any search attributes - Search by task title: anything that doesn't match any search attributes
Search by status ### Search by status
----------------
Attribute: **status** Attribute: **status**
- Query to find open tasks: `status:open` - Query to find open tasks: `status:open`
- Query to find closed tasks: `status:closed` - Query to find closed tasks: `status:closed`
Search by assignee ### Search by assignee
------------------
Attribute: **assignee** Attribute: **assignee**
@ -38,8 +39,7 @@ Attribute: **assignee**
- Query for unassigned tasks: `assignee:nobody` - Query for unassigned tasks: `assignee:nobody`
- Query for my assigned tasks: `assignee:me` - Query for my assigned tasks: `assignee:me`
Search by task creator ### Search by task creator
----------------------
Attribute: **creator** Attribute: **creator**
@ -47,23 +47,20 @@ Attribute: **creator**
- Tasks created by John Doe: `creator:"John Doe"` - Tasks created by John Doe: `creator:"John Doe"`
- Tasks created by the user id #1: `creator:1` - Tasks created by the user id #1: `creator:1`
Search by subtask assignee ### Search by subtask assignee
--------------------------
Attribute: **subtask:assignee** Attribute: **subtask:assignee**
- Example: `subtask:assignee:"John Doe"` - Example: `subtask:assignee:"John Doe"`
Search by color ### Search by color
---------------
Attribute: **color** Attribute: **color**
- Query to search by color id: `color:blue` - Query to search by color id: `color:blue`
- Query to search by color name: `color:"Deep Orange"` - Query to search by color name: `color:"Deep Orange"`
Search by the due date ### Search by the due date
----------------------
Attribute: **due** Attribute: **due**
@ -83,8 +80,7 @@ Operators supported with a date:
- 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 ### Search by modification date
---------------------------
Attribute: **modified** or **updated** Attribute: **modified** or **updated**
@ -94,29 +90,25 @@ 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. This query will use the same value as the board highlight period configured in settings.
Search by creation date ### Search by creation date
-----------------------
Attribute: **created** Attribute: **created**
Works in the same way as the modification date queries. Works in the same way as the modification date queries.
Search by description ### Search by description
---------------------
Attribute: **description** or **desc** Attribute: **description** or **desc**
Example: `description:"text search"` Example: `description:"text search"`
Search by external reference ### Search by external reference
----------------------------
The task reference is an external id of your task, by example a ticket number from another software. The task reference is an external id of your task, by example a ticket number from another software.
- Find tasks with a reference: `ref:1234` or `reference:TICKET-1234` - Find tasks with a reference: `ref:1234` or `reference:TICKET-1234`
Search by category ### Search by category
------------------
Attribute: **category** Attribute: **category**
@ -124,8 +116,7 @@ Attribute: **category**
- Find all tasks that have those categories: `category:"Bug" category:"Improvements"` - Find all tasks that have those categories: `category:"Bug" category:"Improvements"`
- Find tasks with no category assigned: `category:none` - Find tasks with no category assigned: `category:none`
Search by project ### Search by project
-----------------
Attribute: **project** Attribute: **project**
@ -133,16 +124,14 @@ Attribute: **project**
- Find tasks by project id: `project:23` - Find tasks by project id: `project:23`
- Find tasks for several projects: `project:"My project A" project:"My project B"` - Find tasks for several projects: `project:"My project A" project:"My project B"`
Search by columns ### Search by columns
-----------------
Attribute: **column** Attribute: **column**
- Find tasks by column name: `column:"Work in progress"` - Find tasks by column name: `column:"Work in progress"`
- Find tasks for several columns: `column:"Backlog" column:ready` - Find tasks for several columns: `column:"Backlog" column:ready`
Search by swim-lane ### Search by swim-lane
-------------------
Attribute: **swimlane** Attribute: **swimlane**
@ -150,17 +139,41 @@ Attribute: **swimlane**
- Find tasks in the default swim-lane: `swimlane:default` - Find tasks in the default swim-lane: `swimlane:default`
- Find tasks into several swim-lanes: `swimlane:"Version 1.2" swimlane:"Version 1.3"` - Find tasks into several swim-lanes: `swimlane:"Version 1.2" swimlane:"Version 1.3"`
Search by task link ### Search by task link
------------------
Attribute: **link** Attribute: **link**
- Find tasks by link name: `link:"is a milestone of"` - Find tasks by link name: `link:"is a milestone of"`
- Find tasks into several links: `link:"is a milestone of" link:"relates to"` - Find tasks into several links: `link:"is a milestone of" link:"relates to"`
Search by comment ### Search by comment
-----------------
Attribute: **comment** Attribute: **comment**
- Find comments that contains this title: `comment:"My comment message"` - Find comments that contains this title: `comment:"My comment message"`
Activity stream search
----------------------
### Search events by task title
Attribute: **title** or none (default)
- Example: `title:"My task"`
- Search by task id: `#123`
### Search events by task status
Attribute: **status**
### Search by event creator
Attribute: **creator**
### Search by event creation date
Attribute: **created**
### Search events by project
Attribute: **project**

View File

@ -0,0 +1,117 @@
<?php
use Kanboard\Filter\ProjectActivityCreationDateFilter;
use Kanboard\Model\Project;
use Kanboard\Model\ProjectActivity;
use Kanboard\Model\TaskCreation;
use Kanboard\Model\TaskFinder;
use Kanboard\Model\Task;
require_once __DIR__.'/../Base.php';
class ProjectActivityCreationDateFilterTest extends Base
{
public function testWithToday()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreationDateFilter('today');
$filter->setDateParser($this->container['dateParser']);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
}
public function testWithYesterday()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreationDateFilter('yesterday');
$filter->setDateParser($this->container['dateParser']);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(0, $events);
}
public function testWithIsoDate()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreationDateFilter(date('Y-m-d'));
$filter->setDateParser($this->container['dateParser']);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
}
public function testWithOperatorAndIsoDate()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreationDateFilter('>='.date('Y-m-d'));
$filter->setDateParser($this->container['dateParser']);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreationDateFilter('<'.date('Y-m-d'));
$filter->setDateParser($this->container['dateParser']);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(0, $events);
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreationDateFilter('>'.date('Y-m-d'));
$filter->setDateParser($this->container['dateParser']);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(0, $events);
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreationDateFilter('>='.date('Y-m-d'));
$filter->setDateParser($this->container['dateParser']);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
}
}

View File

@ -0,0 +1,91 @@
<?php
use Kanboard\Filter\ProjectActivityCreatorFilter;
use Kanboard\Model\Project;
use Kanboard\Model\ProjectActivity;
use Kanboard\Model\TaskCreation;
use Kanboard\Model\TaskFinder;
use Kanboard\Model\Task;
require_once __DIR__.'/../Base.php';
class ProjectActivityCreatorFilterTest extends Base
{
public function testWithUsername()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreatorFilter('admin');
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
}
public function testWithAnotherUsername()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreatorFilter('John Doe');
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(0, $events);
}
public function testWithCurrentUser()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreatorFilter('me');
$filter->setCurrentUserId(1);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
}
public function testWithAnotherCurrentUser()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityCreatorFilter('me');
$filter->setCurrentUserId(2);
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(0, $events);
}
}

View File

@ -0,0 +1,35 @@
<?php
use Kanboard\Filter\ProjectActivityProjectNameFilter;
use Kanboard\Model\Project;
use Kanboard\Model\ProjectActivity;
use Kanboard\Model\TaskCreation;
use Kanboard\Model\TaskFinder;
use Kanboard\Model\Task;
require_once __DIR__.'/../Base.php';
class ProjectActivityProjectNameFilterTest extends Base
{
public function testFilterByProjectName()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$query = $projectActivityModel->getQuery();
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(2, $projectModel->create(array('name' => 'P2')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertEquals(2, $taskCreation->create(array('title' => 'Test', 'project_id' => 2)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$this->assertNotFalse($projectActivityModel->createEvent(2, 2, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(2))));
$filter = new ProjectActivityProjectNameFilter('P1');
$filter->withQuery($query)->apply();
$this->assertCount(1, $query->findAll());
}
}

View File

@ -0,0 +1,49 @@
<?php
use Kanboard\Filter\ProjectActivityTaskStatusFilter;
use Kanboard\Model\Project;
use Kanboard\Model\ProjectActivity;
use Kanboard\Model\TaskCreation;
use Kanboard\Model\TaskFinder;
use Kanboard\Model\Task;
use Kanboard\Model\TaskStatus;
require_once __DIR__.'/../Base.php';
class ProjectActivityTaskStatusFilterTest extends Base
{
public function testFilterByTaskStatus()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$taskStatus = new TaskStatus($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertEquals(2, $taskCreation->create(array('title' => 'Test', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$this->assertNotFalse($projectActivityModel->createEvent(1, 2, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(2))));
$this->assertTrue($taskStatus->close(1));
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityTaskStatusFilter('open');
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
$this->assertEquals(2, $events[0]['task_id']);
$query = $projectActivityModel->getQuery();
$filter = new ProjectActivityTaskStatusFilter('closed');
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
$this->assertEquals(1, $events[0]['task_id']);
}
}

View File

@ -11,7 +11,7 @@ require_once __DIR__.'/../Base.php';
class ProjectActivityTaskTitleFilterTest extends Base class ProjectActivityTaskTitleFilterTest extends Base
{ {
public function testFilterByTaskId() public function testWithFullTitle()
{ {
$taskFinder = new TaskFinder($this->container); $taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container); $taskCreation = new TaskCreation($this->container);
@ -31,4 +31,49 @@ class ProjectActivityTaskTitleFilterTest extends Base
$filter->withQuery($query)->apply(); $filter->withQuery($query)->apply();
$this->assertCount(1, $query->findAll()); $this->assertCount(1, $query->findAll());
} }
public function testWithPartialTitle()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$query = $projectActivityModel->getQuery();
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test1', 'project_id' => 1)));
$this->assertEquals(2, $taskCreation->create(array('title' => 'Test2', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$this->assertNotFalse($projectActivityModel->createEvent(1, 2, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(2))));
$filter = new ProjectActivityTaskTitleFilter('test');
$filter->withQuery($query)->apply();
$this->assertCount(2, $query->findAll());
}
public function testWithId()
{
$taskFinder = new TaskFinder($this->container);
$taskCreation = new TaskCreation($this->container);
$projectModel = new Project($this->container);
$projectActivityModel = new ProjectActivity($this->container);
$query = $projectActivityModel->getQuery();
$this->assertEquals(1, $projectModel->create(array('name' => 'P1')));
$this->assertEquals(1, $taskCreation->create(array('title' => 'Test1', 'project_id' => 1)));
$this->assertEquals(2, $taskCreation->create(array('title' => 'Test2', 'project_id' => 1)));
$this->assertNotFalse($projectActivityModel->createEvent(1, 1, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(1))));
$this->assertNotFalse($projectActivityModel->createEvent(1, 2, 1, Task::EVENT_CREATE, array('task' => $taskFinder->getById(2))));
$filter = new ProjectActivityTaskTitleFilter('#2');
$filter->withQuery($query)->apply();
$events = $query->findAll();
$this->assertCount(1, $events);
$this->assertEquals(2, $events[0]['task_id']);
}
} }