Refactor ProjectActivity model to use Filter and Formatter interface

This commit is contained in:
Frederic Guillot
2016-04-10 12:13:42 -04:00
parent 7b74f55a28
commit 2eadfb2291
23 changed files with 649 additions and 212 deletions

View File

@@ -33,7 +33,7 @@ class Me extends Base
public function getMyActivityStream()
{
$project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
return $this->projectActivity->getProjects($project_ids, 100);
return $this->helper->projectActivity->getProjectsEvents($project_ids, 100);
}
public function createMyPrivateProject($name, $description = null)

View File

@@ -53,13 +53,13 @@ class Project extends Base
public function getProjectActivities(array $project_ids)
{
return $this->projectActivity->getProjects($project_ids);
return $this->helper->projectActivity->getProjectsEvents($project_ids);
}
public function getProjectActivity($project_id)
{
$this->checkProjectPermission($project_id);
return $this->projectActivity->getProject($project_id);
return $this->helper->projectActivity->getProjectEvents($project_id);
}
public function createProject($name, $description = null)

View File

@@ -20,7 +20,7 @@ class Activity extends Base
$project = $this->getProject();
$this->response->html($this->helper->layout->app('activity/project', array(
'events' => $this->projectActivity->getProject($project['id']),
'events' => $this->helper->projectActivity->getProjectEvents($project['id']),
'project' => $project,
'title' => t('%s\'s activity', $project['name'])
)));
@@ -39,7 +39,7 @@ class Activity extends Base
'title' => $task['title'],
'task' => $task,
'project' => $this->project->getById($task['project_id']),
'events' => $this->projectActivity->getTask($task['id']),
'events' => $this->helper->projectActivity->getTaskEvents($task['id']),
)));
}
}

View File

@@ -157,7 +157,7 @@ class App extends Base
$this->response->html($this->helper->layout->dashboard('app/activity', array(
'title' => t('My activity stream'),
'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id']), 100),
'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermission->getActiveProjectIds($user['id']), 100),
'user' => $user,
)));
}

View File

@@ -26,7 +26,7 @@ class Feed extends Base
}
$this->response->xml($this->template->render('feed/user', array(
'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id'])),
'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermission->getActiveProjectIds($user['id'])),
'user' => $user,
)));
}
@@ -47,7 +47,7 @@ class Feed extends Base
}
$this->response->xml($this->template->render('feed/project', array(
'events' => $this->projectActivity->getProject($project['id']),
'events' => $this->helper->projectActivity->getProjectEvents($project['id']),
'project' => $project,
)));
}

View File

@@ -24,7 +24,7 @@ class ProjectOverview extends Base
'description' => $this->helper->projectHeader->getDescription($project),
'users' => $this->projectUserRole->getAllUsersGroupedByRole($project['id']),
'roles' => $this->role->getProjectRoles(),
'events' => $this->projectActivity->getProject($project['id'], 10),
'events' => $this->helper->projectActivity->getProjectEvents($project['id'], 10),
'images' => $this->projectFile->getAllImages($project['id']),
'files' => $this->projectFile->getAllDocuments($project['id']),
)));

View File

@@ -129,10 +129,12 @@ use Pimple\Container;
* @property \Kanboard\Export\TransitionExport $transitionExport
* @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery
* @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery
* @property \Kanboard\Core\Filter\QueryBuilder $projectActivityQuery
* @property \Kanboard\Core\Filter\QueryBuilder $userQuery
* @property \Kanboard\Core\Filter\QueryBuilder $projectQuery
* @property \Kanboard\Core\Filter\QueryBuilder $taskQuery
* @property \Kanboard\Core\Filter\LexerBuilder $taskLexer
* @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer
* @property \Psr\Log\LoggerInterface $logger
* @property \PicoDb\Database $db
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher

View File

@@ -26,6 +26,7 @@ use Pimple\Container;
* @property \Kanboard\Helper\UserHelper $user
* @property \Kanboard\Helper\LayoutHelper $layout
* @property \Kanboard\Helper\ProjectHeaderHelper $projectHeader
* @property \Kanboard\Helper\ProjectActivityHelper $projectActivity
*/
class Helper
{

View File

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

View File

@@ -0,0 +1,43 @@
<?php
namespace Kanboard\Filter;
use Kanboard\Core\Filter\FilterInterface;
use Kanboard\Model\ProjectActivity;
/**
* Filter activity events by projectIds
*
* @package filter
* @author Frederic Guillot
*/
class ProjectActivityProjectIdsFilter extends BaseFilter implements FilterInterface
{
/**
* Get search attribute
*
* @access public
* @return string[]
*/
public function getAttributes()
{
return array('project_ids');
}
/**
* Apply filter
*
* @access public
* @return FilterInterface
*/
public function apply()
{
if (empty($this->value)) {
$this->query->eq(ProjectActivity::TABLE.'.project_id', 0);
} else {
$this->query->in(ProjectActivity::TABLE.'.project_id', $this->value);
}
return $this;
}
}

View File

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

View File

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

View File

@@ -0,0 +1,61 @@
<?php
namespace Kanboard\Formatter;
use Kanboard\Core\Filter\FormatterInterface;
class ProjectActivityEventFormatter extends BaseFormatter implements FormatterInterface
{
/**
* Apply formatter
*
* @access public
* @return array
*/
public function format()
{
$events = $this->query->findAll();
foreach ($events as &$event) {
$event += $this->unserializeEvent($event['data']);
unset($event['data']);
$event['author'] = $event['author_name'] ?: $event['author_username'];
$event['event_title'] = $this->notification->getTitleWithAuthor($event['author'], $event['event_name'], $event);
$event['event_content'] = $this->renderEvent($event);
}
return $events;
}
/**
* Decode event data, supports unserialize() and json_decode()
*
* @access protected
* @param string $data Serialized data
* @return array
*/
protected function unserializeEvent($data)
{
if ($data{0} === 'a') {
return unserialize($data);
}
return json_decode($data, true) ?: array();
}
/**
* Get the event html content
*
* @access protected
* @param array $params Event properties
* @return string
*/
protected function renderEvent(array $params)
{
return $this->template->render(
'event/'.str_replace('.', '_', $params['event_name']),
$params
);
}
}

View File

@@ -0,0 +1,78 @@
<?php
namespace Kanboard\Helper;
use Kanboard\Core\Base;
use Kanboard\Filter\ProjectActivityProjectIdFilter;
use Kanboard\Filter\ProjectActivityProjectIdsFilter;
use Kanboard\Filter\ProjectActivityTaskIdFilter;
use Kanboard\Formatter\ProjectActivityEventFormatter;
use Kanboard\Model\ProjectActivity;
/**
* Project Activity Helper
*
* @package helper
* @author Frederic Guillot
*/
class ProjectActivityHelper extends Base
{
/**
* Get project activity events
*
* @access public
* @param integer $project_id
* @param int $limit
* @return array
*/
public function getProjectEvents($project_id, $limit = 50)
{
$queryBuilder = $this->projectActivityQuery
->withFilter(new ProjectActivityProjectIdFilter($project_id));
$queryBuilder->getQuery()
->desc(ProjectActivity::TABLE.'.id')
->limit($limit)
;
return $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
}
/**
* Get projects activity events
*
* @access public
* @param int[] $project_ids
* @param int $limit
* @return array
*/
public function getProjectsEvents(array $project_ids, $limit = 50)
{
$queryBuilder = $this->projectActivityQuery
->withFilter(new ProjectActivityProjectIdsFilter($project_ids));
$queryBuilder->getQuery()
->desc(ProjectActivity::TABLE.'.id')
->limit($limit)
;
return $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
}
/**
* Get task activity events
*
* @access public
* @param integer $task_id
* @return array
*/
public function getTaskEvents($task_id)
{
$queryBuilder = $this->projectActivityQuery
->withFilter(new ProjectActivityTaskIdFilter($task_id));
$queryBuilder->getQuery()->desc(ProjectActivity::TABLE.'.id');
return $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
}
}

View File

@@ -53,115 +53,25 @@ class ProjectActivity extends Base
}
/**
* Get all events for the given project
* Get query
*
* @access public
* @param integer $project_id Project id
* @param integer $limit Maximum events number
* @param integer $start Timestamp of earliest activity
* @param integer $end Timestamp of latest activity
* @return array
* @return Table
*/
public function getProject($project_id, $limit = 50, $start = null, $end = null)
public function getQuery()
{
return $this->getProjects(array($project_id), $limit, $start, $end);
}
/**
* Get all events for the given projects list
*
* @access public
* @param integer[] $project_ids Projects id
* @param integer $limit Maximum events number
* @param integer $start Timestamp of earliest activity
* @param integer $end Timestamp of latest activity
* @return array
*/
public function getProjects(array $project_ids, $limit = 50, $start = null, $end = null)
{
if (empty($project_ids)) {
return array();
}
$query = $this
->db
->table(self::TABLE)
->columns(
self::TABLE.'.*',
User::TABLE.'.username AS author_username',
User::TABLE.'.name AS author_name',
User::TABLE.'.email',
User::TABLE.'.avatar_path'
)
->in('project_id', $project_ids)
->join(User::TABLE, 'id', 'creator_id')
->desc(self::TABLE.'.id')
->limit($limit);
return $this->getEvents($query, $start, $end);
}
/**
* Get all events for the given task
*
* @access public
* @param integer $task_id Task id
* @param integer $limit Maximum events number
* @param integer $start Timestamp of earliest activity
* @param integer $end Timestamp of latest activity
* @return array
*/
public function getTask($task_id, $limit = 50, $start = null, $end = null)
{
$query = $this
->db
->table(self::TABLE)
->columns(
self::TABLE.'.*',
User::TABLE.'.username AS author_username',
User::TABLE.'.name AS author_name',
User::TABLE.'.email',
User::TABLE.'.avatar_path'
)
->eq('task_id', $task_id)
->join(User::TABLE, 'id', 'creator_id')
->desc(self::TABLE.'.id')
->limit($limit);
return $this->getEvents($query, $start, $end);
}
/**
* Common function to return events
*
* @access public
* @param Table $query PicoDb Query
* @param integer $start Timestamp of earliest activity
* @param integer $end Timestamp of latest activity
* @return array
*/
private function getEvents(Table $query, $start, $end)
{
if (! is_null($start)) {
$query->gte('date_creation', $start);
}
if (! is_null($end)) {
$query->lte('date_creation', $end);
}
$events = $query->findAll();
foreach ($events as &$event) {
$event += $this->decode($event['data']);
unset($event['data']);
$event['author'] = $event['author_name'] ?: $event['author_username'];
$event['event_title'] = $this->notification->getTitleWithAuthor($event['author'], $event['event_name'], $event);
$event['event_content'] = $this->getContent($event);
}
return $events;
return $this
->db
->table(ProjectActivity::TABLE)
->columns(
ProjectActivity::TABLE.'.*',
'uc.username AS author_username',
'uc.name AS author_name',
'uc.email',
'uc.avatar_path'
)
->join(Task::TABLE, 'id', 'task_id')
->left(User::TABLE, 'uc', 'id', ProjectActivity::TABLE, 'creator_id');
}
/**
@@ -179,35 +89,4 @@ class ProjectActivity extends Base
$this->db->table(self::TABLE)->in('id', $ids)->remove();
}
}
/**
* Get the event html content
*
* @access public
* @param array $params Event properties
* @return string
*/
public function getContent(array $params)
{
return $this->template->render(
'event/'.str_replace('.', '_', $params['event_name']),
$params
);
}
/**
* Decode event data, supports unserialize() and json_decode()
*
* @access public
* @param string $data Serialized data
* @return array
*/
public function decode($data)
{
if ($data{0} === 'a') {
return unserialize($data);
}
return json_decode($data, true) ?: array();
}
}

View File

@@ -4,6 +4,7 @@ namespace Kanboard\ServiceProvider;
use Kanboard\Core\Filter\LexerBuilder;
use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\ProjectActivityTaskTitleFilter;
use Kanboard\Filter\TaskAssigneeFilter;
use Kanboard\Filter\TaskCategoryFilter;
use Kanboard\Filter\TaskColorFilter;
@@ -45,6 +46,25 @@ class FilterProvider implements ServiceProviderInterface
* @return \Pimple\Container
*/
public function register(Container $container)
{
$this->createUserFilter($container);
$this->createProjectFilter($container);
$this->createTaskFilter($container);
return $container;
}
public function createUserFilter(Container $container)
{
$container['userQuery'] = $container->factory(function ($c) {
$builder = new QueryBuilder();
$builder->withQuery($c['db']->table(User::TABLE));
return $builder;
});
return $container;
}
public function createProjectFilter(Container $container)
{
$container['projectGroupRoleQuery'] = $container->factory(function ($c) {
$builder = new QueryBuilder();
@@ -58,18 +78,32 @@ class FilterProvider implements ServiceProviderInterface
return $builder;
});
$container['userQuery'] = $container->factory(function ($c) {
$builder = new QueryBuilder();
$builder->withQuery($c['db']->table(User::TABLE));
return $builder;
});
$container['projectQuery'] = $container->factory(function ($c) {
$builder = new QueryBuilder();
$builder->withQuery($c['db']->table(Project::TABLE));
return $builder;
});
$container['projectActivityLexer'] = $container->factory(function ($c) {
$builder = new LexerBuilder();
$builder->withQuery($c['projectActivity']->getQuery());
$builder->withFilter(new ProjectActivityTaskTitleFilter());
return $builder;
});
$container['projectActivityQuery'] = $container->factory(function ($c) {
$builder = new QueryBuilder();
$builder->withQuery($c['projectActivity']->getQuery());
return $builder;
});
return $container;
}
public function createTaskFilter(Container $container)
{
$container['taskQuery'] = $container->factory(function ($c) {
$builder = new QueryBuilder();
$builder->withQuery($c['taskFinder']->getExtendedQuery());

View File

@@ -30,6 +30,7 @@ class HelperProvider implements ServiceProviderInterface
$container['helper']->register('user', '\Kanboard\Helper\UserHelper');
$container['helper']->register('avatar', '\Kanboard\Helper\AvatarHelper');
$container['helper']->register('projectHeader', '\Kanboard\Helper\ProjectHeaderHelper');
$container['helper']->register('projectActivity', '\Kanboard\Helper\ProjectActivityHelper');
$container['template'] = new Template($container['helper']);