Pagination refactoring

This commit is contained in:
Frederic Guillot 2015-01-18 17:59:41 -05:00
parent 74e4a7b064
commit 7c1c14cf64
21 changed files with 772 additions and 656 deletions

View File

@ -29,157 +29,44 @@ class App extends Base
*/
public function index()
{
$paginate = $this->request->getStringParam('paginate', 'userTasks');
$offset = $this->request->getIntegerParam('offset', 0);
$direction = $this->request->getStringParam('direction');
$order = $this->request->getStringParam('order');
$status = array(SubTaskModel::STATUS_TODO, SubTaskModel::STATUS_INPROGRESS);
$user_id = $this->userSession->getId();
$projects = $this->projectPermission->getMemberProjects($user_id);
$project_ids = array_keys($projects);
$params = array(
$task_paginator = $this->paginator
->setUrl('app', 'index', array('pagination' => 'tasks'))
->setMax(10)
->setOrder('tasks.id')
->setQuery($this->taskFinder->getUserQuery($user_id))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
$subtask_paginator = $this->paginator
->setUrl('app', 'index', array('pagination' => 'subtasks'))
->setMax(10)
->setOrder('tasks.id')
->setQuery($this->subTask->getUserQuery($user_id, $status))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
$project_paginator = $this->paginator
->setUrl('app', 'index', array('pagination' => 'projects'))
->setMax(10)
->setOrder('name')
->setQuery($this->project->getQueryColumnStats($project_ids))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
$this->response->html($this->template->layout('app/dashboard', array(
'title' => t('Dashboard'),
'board_selector' => $this->projectPermission->getAllowedProjects($user_id),
'events' => $this->projectActivity->getProjects($project_ids, 10),
);
$params += $this->getTaskPagination($user_id, $paginate, $offset, $order, $direction);
$params += $this->getSubtaskPagination($user_id, $paginate, $offset, $order, $direction);
$params += $this->getProjectPagination($project_ids, $paginate, $offset, $order, $direction);
$this->response->html($this->template->layout('app/dashboard', $params));
'task_paginator' => $task_paginator,
'subtask_paginator' => $subtask_paginator,
'project_paginator' => $project_paginator,
)));
}
/**
* Get tasks pagination
*
* @access public
* @param integer $user_id
* @param string $paginate
* @param integer $offset
* @param string $order
* @param string $direction
*/
private function getTaskPagination($user_id, $paginate, $offset, $order, $direction)
{
$limit = 10;
if (! in_array($order, array('tasks.id', 'project_name', 'title', 'date_due'))) {
$order = 'tasks.id';
$direction = 'ASC';
}
if ($paginate === 'userTasks') {
$tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit, $order, $direction);
}
else {
$offset = 0;
$tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit);
}
return array(
'tasks' => $tasks,
'task_pagination' => array(
'controller' => 'app',
'action' => 'index',
'params' => array('paginate' => 'userTasks'),
'direction' => $direction,
'order' => $order,
'total' => $this->taskPaginator->countUserTasks($user_id),
'offset' => $offset,
'limit' => $limit,
)
);
}
/**
* Get subtasks pagination
*
* @access public
* @param integer $user_id
* @param string $paginate
* @param integer $offset
* @param string $order
* @param string $direction
*/
private function getSubtaskPagination($user_id, $paginate, $offset, $order, $direction)
{
$status = array(SubTaskModel::STATUS_TODO, SubTaskModel::STATUS_INPROGRESS);
$limit = 10;
if (! in_array($order, array('tasks.id', 'project_name', 'status', 'title'))) {
$order = 'tasks.id';
$direction = 'ASC';
}
if ($paginate === 'userSubtasks') {
$subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit, $order, $direction);
}
else {
$offset = 0;
$subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit);
}
return array(
'subtasks' => $subtasks,
'subtask_pagination' => array(
'controller' => 'app',
'action' => 'index',
'params' => array('paginate' => 'userSubtasks'),
'direction' => $direction,
'order' => $order,
'total' => $this->subtaskPaginator->countUserSubtasks($user_id, $status),
'offset' => $offset,
'limit' => $limit,
)
);
}
/**
* Get projects pagination
*
* @access public
* @param array $project_ids
* @param string $paginate
* @param integer $offset
* @param string $order
* @param string $direction
*/
private function getProjectPagination(array $project_ids, $paginate, $offset, $order, $direction)
{
$limit = 10;
if (! in_array($order, array('id', 'name'))) {
$order = 'name';
$direction = 'ASC';
}
if ($paginate === 'projectSummaries') {
$projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit, $order, $direction);
}
else {
$offset = 0;
$projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit);
}
return array(
'projects' => $projects,
'project_pagination' => array(
'controller' => 'app',
'action' => 'index',
'params' => array('paginate' => 'projectSummaries'),
'direction' => $direction,
'order' => $order,
'total' => count($project_ids),
'offset' => $offset,
'limit' => $limit,
)
);
}
/**
* Render Markdown Text and reply with the HTML Code
* Render Markdown text and reply with the HTML Code
*
* @access public
*/

View File

@ -51,7 +51,7 @@ class Project extends Base
$this->response->html($this->projectLayout('project/show', array(
'project' => $project,
'stats' => $this->project->getStats($project['id']),
'stats' => $this->project->getTaskStats($project['id']),
'title' => $project['name'],
)));
}
@ -425,38 +425,32 @@ class Project extends Base
{
$project = $this->getProject();
$search = $this->request->getStringParam('search');
$direction = $this->request->getStringParam('direction', 'DESC');
$order = $this->request->getStringParam('order', 'tasks.id');
$offset = $this->request->getIntegerParam('offset', 0);
$tasks = array();
$nb_tasks = 0;
$limit = 25;
$paginator = $this->paginator
->setUrl('project', 'search', array('search' => $search, 'project_id' => $project['id']))
->setMax(30)
->setOrder('tasks.id')
->setDirection('DESC');
if ($search !== '') {
$tasks = $this->taskPaginator->searchTasks($project['id'], $search, $offset, $limit, $order, $direction);
$nb_tasks = $this->taskPaginator->countSearchTasks($project['id'], $search);
$paginator
->setQuery($this->taskFinder->getSearchQuery($project['id'], $search))
->calculate();
$nb_tasks = $paginator->getTotal();
}
$this->response->html($this->template->layout('project/search', array(
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
'tasks' => $tasks,
'nb_tasks' => $nb_tasks,
'pagination' => array(
'controller' => 'project',
'action' => 'search',
'params' => array('search' => $search, 'project_id' => $project['id']),
'direction' => $direction,
'order' => $order,
'total' => $nb_tasks,
'offset' => $offset,
'limit' => $limit,
),
'values' => array(
'search' => $search,
'controller' => 'project',
'action' => 'search',
'project_id' => $project['id'],
),
'paginator' => $paginator,
'project' => $project,
'columns' => $this->board->getColumnsList($project['id']),
'categories' => $this->category->getList($project['id'], false),
@ -472,32 +466,21 @@ class Project extends Base
public function tasks()
{
$project = $this->getProject();
$direction = $this->request->getStringParam('direction', 'DESC');
$order = $this->request->getStringParam('order', 'tasks.date_completed');
$offset = $this->request->getIntegerParam('offset', 0);
$limit = 25;
$tasks = $this->taskPaginator->closedTasks($project['id'], $offset, $limit, $order, $direction);
$nb_tasks = $this->taskPaginator->countClosedTasks($project['id']);
$paginator = $this->paginator
->setUrl('project', 'tasks', array('project_id' => $project['id']))
->setMax(30)
->setOrder('tasks.id')
->setDirection('DESC')
->setQuery($this->taskFinder->getClosedTaskQuery($project['id']))
->calculate();
$this->response->html($this->template->layout('project/tasks', array(
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
'pagination' => array(
'controller' => 'project',
'action' => 'tasks',
'params' => array('project_id' => $project['id']),
'direction' => $direction,
'order' => $order,
'total' => $nb_tasks,
'offset' => $offset,
'limit' => $limit,
),
'project' => $project,
'columns' => $this->board->getColumnsList($project['id']),
'categories' => $this->category->getList($project['id'], false),
'tasks' => $tasks,
'nb_tasks' => $nb_tasks,
'title' => t('Completed tasks for "%s"', $project['name']).' ('.$nb_tasks.')'
'paginator' => $paginator,
'title' => t('Completed tasks for "%s"', $project['name']).' ('.$paginator->getTotal().')'
)));
}

View File

@ -115,31 +115,19 @@ class User extends Base
*/
public function index()
{
$direction = $this->request->getStringParam('direction', 'ASC');
$order = $this->request->getStringParam('order', 'username');
$offset = $this->request->getIntegerParam('offset', 0);
$limit = 25;
$users = $this->user->paginate($offset, $limit, $order, $direction);
$nb_users = $this->user->count();
$paginator = $this->paginator
->setUrl('user', 'index')
->setMax(30)
->setOrder('username')
->setQuery($this->user->getQuery())
->calculate();
$this->response->html(
$this->template->layout('user/index', array(
'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
'projects' => $this->project->getList(),
'nb_users' => $nb_users,
'users' => $users,
'title' => t('Users').' ('.$nb_users.')',
'pagination' => array(
'controller' => 'user',
'action' => 'index',
'direction' => $direction,
'order' => $order,
'total' => $nb_users,
'offset' => $offset,
'limit' => $limit,
'params' => array(),
),
'title' => t('Users').' ('.$paginator->getTotal().')',
'paginator' => $paginator,
)));
}

View File

@ -462,73 +462,7 @@ class Helper
$values += $params;
return '?'.http_build_query($values, '');
}
/**
* Pagination links
*
* @param array $pagination Pagination information
* @return string
*/
public function paginate(array $pagination)
{
extract($pagination);
if ($pagination['offset'] === 0 && ($total - $pagination['offset']) <= $limit) {
return '';
}
$html = '<div class="pagination">';
$html .= '<span class="pagination-previous">';
if ($pagination['offset'] > 0) {
$offset = $pagination['offset'] - $limit;
$html .= $this->a('&larr; '.t('Previous'), $controller, $action, $params + compact('offset', 'order', 'direction'));
}
else {
$html .= '&larr; '.t('Previous');
}
$html .= '</span>';
$html .= '<span class="pagination-next">';
if (($total - $pagination['offset']) > $limit) {
$offset = $pagination['offset'] + $limit;
$html .= $this->a(t('Next').' &rarr;', $controller, $action, $params + compact('offset', 'order', 'direction'));
}
else {
$html .= t('Next').' &rarr;';
}
$html .= '</span>';
$html .= '</div>';
return $html;
}
/**
* Column sorting (work with pagination)
*
* @param string $label Column title
* @param string $column SQL column name
* @param array $pagination Pagination information
* @return string
*/
public function order($label, $column, array $pagination)
{
extract($pagination);
$prefix = '';
if ($order === $column) {
$prefix = $direction === 'DESC' ? '&#9660; ' : '&#9650; ';
$direction = $direction === 'DESC' ? 'ASC' : 'DESC';
}
$order = $column;
return $prefix.$this->a($label, $controller, $action, $params + compact('offset', 'order', 'direction'));
return '?'.http_build_query($values);
}
/**

461
app/Core/Paginator.php Normal file
View File

@ -0,0 +1,461 @@
<?php
namespace Core;
use Pimple\Container;
use PicoDb\Table;
/**
* Paginator helper
*
* @package core
* @author Frederic Guillot
*/
class Paginator
{
/**
* Container instance
*
* @access private
* @var \Pimple\Container
*/
private $container;
/**
* Total number of items
*
* @access private
* @var integer
*/
private $total = 0;
/**
* Page number
*
* @access private
* @var integer
*/
private $page = 1;
/**
* Offset
*
* @access private
* @var integer
*/
private $offset = 0;
/**
* Limit
*
* @access private
* @var integer
*/
private $limit = 0;
/**
* Sort by this column
*
* @access private
* @var integer
*/
private $order = '';
/**
* Sorting direction
*
* @access private
* @var integer
*/
private $direction = 'ASC';
/**
* Slice of items
*
* @access private
* @var array
*/
private $items = array();
/**
* PicoDb Table instance
*
* @access private
* @var \Picodb\Table
*/
private $query = null;
/**
* Controller name
*
* @access private
* @var string
*/
private $controller = '';
/**
* Action name
*
* @access private
* @var string
*/
private $action = '';
/**
* Url params
*
* @access private
* @var array
*/
private $params = array();
/**
* Constructor
*
* @access public
* @param \Pimple\Container $container
*/
public function __construct(Container $container)
{
$this->container = $container;
}
/**
* Set a PicoDb query
*
* @access public
* @param \PicoDb\Table
* @return Paginator
*/
public function setQuery(Table $query)
{
$this->query = $query;
$this->total = $this->query->count();
return $this;
}
/**
* Execute a PicoDb query
*
* @access public
* @return array
*/
public function executeQuery()
{
if ($this->query !== null) {
return $this->query
->offset($this->offset)
->limit($this->limit)
->orderBy($this->order, $this->direction)
->findAll();
}
return array();
}
/**
* Set url parameters
*
* @access public
* @param string $controller
* @param string $action
* @param array $params
* @return Paginator
*/
public function setUrl($controller, $action, array $params = array())
{
$this->controller = $controller;
$this->action = $action;
$this->params = $params;
return $this;
}
/**
* Add manually items
*
* @access public
* @param array $items
* @return Paginator
*/
public function setCollection(array $items)
{
$this->items = $items;
return $this;
}
/**
* Return the items
*
* @access public
* @return array
*/
public function getCollection()
{
return $this->items ?: $this->executeQuery();
}
/**
* Set the total number of items
*
* @access public
* @param integer $total
* @return Paginator
*/
public function setTotal($total)
{
$this->total = $total;
return $this;
}
/**
* Get the total number of items
*
* @access public
* @return integer
*/
public function getTotal()
{
return $this->total;
}
/**
* Set the default page number
*
* @access public
* @param integer $page
* @return Paginator
*/
public function setPage($page)
{
$this->page = $page;
return $this;
}
/**
* Set the default column order
*
* @access public
* @param string $order
* @return Paginator
*/
public function setOrder($order)
{
$this->order = $order;
return $this;
}
/**
* Set the default sorting direction
*
* @access public
* @param string $direction
* @return Paginator
*/
public function setDirection($direction)
{
$this->direction = $direction;
return $this;
}
/**
* Set the maximum number of items per page
*
* @access public
* @param integer $limit
* @return Paginator
*/
public function setMax($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Return true if the collection is empty
*
* @access public
* @return boolean
*/
public function isEmpty()
{
return $this->total === 0;
}
/**
* Execute the offset calculation only if the $condition is true
*
* @access public
* @param boolean $condition
* @return Paginator
*/
public function calculateOnlyIf($condition)
{
if ($condition) {
$this->calculate();
}
return $this;
}
/**
* Calculate the offset value accoring to url params and the page number
*
* @access public
* @return Paginator
*/
public function calculate()
{
$this->page = $this->container['request']->getIntegerParam('page', 1);
$this->direction = $this->container['request']->getStringParam('direction', $this->direction);
$this->order = $this->container['request']->getStringParam('order', $this->order);
if ($this->page < 1) {
$this->page = 1;
}
$this->offset = ($this->page - 1) * $this->limit;
return $this;
}
/**
* Get url params for link generation
*
* @access public
* @param integer $page
* @param string $order
* @param string $direction
* @return string
*/
public function getUrlParams($page, $order, $direction)
{
$params = array(
'page' => $page,
'order' => $order,
'direction' => $direction,
);
return array_merge($this->params, $params);
}
/**
* Generate the previous link
*
* @access public
* @return string
*/
public function generatePreviousLink()
{
$html = '<span class="pagination-previous">';
if ($this->offset > 0) {
$html .= $this->container['helper']->a(
'&larr; '.t('Previous'),
$this->controller,
$this->action,
$this->getUrlParams($this->page - 1, $this->order, $this->direction)
);
}
else {
$html .= '&larr; '.t('Previous');
}
$html .= '</span>';
return $html;
}
/**
* Generate the next link
*
* @access public
* @return string
*/
public function generateNextLink()
{
$html = '<span class="pagination-next">';
if (($this->total - $this->offset) > $this->limit) {
$html .= $this->container['helper']->a(
t('Next').' &rarr;',
$this->controller,
$this->action,
$this->getUrlParams($this->page + 1, $this->order, $this->direction)
);
}
else {
$html .= t('Next').' &rarr;';
}
$html .= '</span>';
return $html;
}
/**
* Return true if there is no pagination to show
*
* @access public
* @return boolean
*/
public function hasNothingtoShow()
{
return $this->offset === 0 && ($this->total - $this->offset) <= $this->limit;
}
/**
* Generation pagination links
*
* @access public
* @return string
*/
public function toHtml()
{
$html = '';
if (! $this->hasNothingtoShow()) {
$html .= '<div class="pagination">';
$html .= $this->generatePreviousLink();
$html .= $this->generateNextLink();
$html .= '</div>';
}
return $html;
}
/**
* Magic method to output pagination links
*
* @access public
* @return string
*/
public function __toString()
{
return $this->toHtml();
}
/**
* Column sorting
*
* @param string $label Column title
* @param string $column SQL column name
* @return string
*/
public function order($label, $column)
{
$prefix = '';
$direction = 'ASC';
if ($this->order === $column) {
$prefix = $this->direction === 'DESC' ? '&#9660; ' : '&#9650; ';
$direction = $this->direction === 'DESC' ? 'ASC' : 'DESC';
}
return $prefix.$this->container['helper']->a(
$label,
$this->controller,
$this->action,
$this->getUrlParams($this->page, $column, $direction)
);
}
}

View File

@ -95,7 +95,7 @@ class Project extends Base
}
/**
* Get all projects, optionaly fetch stats for each project and can check users permissions
* Get all projects
*
* @access public
* @param bool $filter_permissions If true, remove projects not allowed for the current user
@ -188,7 +188,7 @@ class Project extends Base
* @param integer $project_id Project id
* @return array
*/
public function getStats($project_id)
public function getTaskStats($project_id)
{
$stats = array();
$stats['nb_active_tasks'] = 0;
@ -207,6 +207,60 @@ class Project extends Base
return $stats;
}
/**
* Get stats for each column of a project
*
* @access public
* @param array $project
* @return array
*/
public function getColumnStats(array &$project)
{
$project['columns'] = $this->board->getColumns($project['id']);
$stats = $this->board->getColumnStats($project['id']);
foreach ($project['columns'] as &$column) {
$column['nb_tasks'] = isset($stats[$column['id']]) ? $stats[$column['id']] : 0;
}
return $project;
}
/**
* Apply column stats to a collection of projects (filter callback)
*
* @access public
* @param array $projects
* @return array
*/
public function applyColumnStats(array $projects)
{
foreach ($projects as &$project) {
$this->getColumnStats($project);
}
return $projects;
}
/**
* Get project summary for a list of project
*
* @access public
* @param array $project_ids List of project id
* @return \PicoDb\Table
*/
public function getQueryColumnStats(array $project_ids)
{
if (empty($project_ids)) {
return $this->db->table(Project::TABLE)->limit(0);
}
return $this->db
->table(Project::TABLE)
->in('id', $project_ids)
->filter(array($this, 'applyColumnStats'));
}
/**
* Create a project from another one.
*

View File

@ -1,50 +0,0 @@
<?php
namespace Model;
/**
* Project Paginator
*
* @package model
* @author Frederic Guillot
*/
class ProjectPaginator extends Base
{
/**
* Get project summary for a list of project (number of tasks for each column)
*
* @access public
* @param array $project_ids List of project id
* @param integer $offset Offset
* @param integer $limit Limit
* @param string $column Sorting column
* @param string $direction Sorting direction
* @return array
*/
public function projectSummaries(array $project_ids, $offset = 0, $limit = 25, $column = 'name', $direction = 'asc')
{
if (empty($project_ids)) {
return array();
}
$projects = $this->db
->table(Project::TABLE)
->in('id', $project_ids)
->offset($offset)
->limit($limit)
->orderBy($column, $direction)
->findAll();
foreach ($projects as &$project) {
$project['columns'] = $this->board->getColumns($project['id']);
$stats = $this->board->getColumnStats($project['id']);
foreach ($project['columns'] as &$column) {
$column['nb_tasks'] = isset($stats[$column['id']]) ? $stats[$column['id']] : 0;
}
}
return $projects;
}
}

View File

@ -65,6 +65,48 @@ class SubTask extends Base
);
}
/**
* Add subtask status status to the resultset
*
* @access public
* @param array $subtasks Subtasks
* @return array
*/
public function addStatusName(array $subtasks)
{
$status = $this->getStatusList();
foreach ($subtasks as &$subtask) {
$subtask['status_name'] = $status[$subtask['status']];
}
return $subtasks;
}
/**
* Get the query to fetch subtasks assigned to a user
*
* @access public
* @param integer $user_id User id
* @param array $status List of status
* @return \PicoDb\Table
*/
public function getUserQuery($user_id, array $status)
{
return $this->db->table(SubTask::TABLE)
->columns(
SubTask::TABLE.'.*',
Task::TABLE.'.project_id',
Task::TABLE.'.color_id',
Project::TABLE.'.name AS project_name'
)
->eq('user_id', $user_id)
->in(SubTask::TABLE.'.status', $status)
->join(Task::TABLE, 'id', 'task_id')
->join(Project::TABLE, 'id', 'project_id', Task::TABLE)
->filter(array($this, 'addStatusName'));
}
/**
* Get all subtasks for a given task
*
@ -74,19 +116,14 @@ class SubTask extends Base
*/
public function getAll($task_id)
{
$status = $this->getStatusList();
$subtasks = $this->db->table(self::TABLE)
->eq('task_id', $task_id)
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
->join(User::TABLE, 'id', 'user_id')
->asc(self::TABLE.'.id')
->findAll();
foreach ($subtasks as &$subtask) {
$subtask['status_name'] = $status[$subtask['status']];
}
return $subtasks;
return $this->db
->table(self::TABLE)
->eq('task_id', $task_id)
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
->join(User::TABLE, 'id', 'user_id')
->asc(self::TABLE.'.id')
->filter(array($this, 'addStatusName'))
->findAll();
}
/**
@ -101,18 +138,13 @@ class SubTask extends Base
{
if ($more) {
$subtask = $this->db->table(self::TABLE)
->eq(self::TABLE.'.id', $subtask_id)
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
->join(User::TABLE, 'id', 'user_id')
->findOne();
if ($subtask) {
$status = $this->getStatusList();
$subtask['status_name'] = $status[$subtask['status']];
}
return $subtask;
return $this->db
->table(self::TABLE)
->eq(self::TABLE.'.id', $subtask_id)
->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name')
->join(User::TABLE, 'id', 'user_id')
->filter(array($this, 'addStatusName'))
->findOne();
}
return $this->db->table(self::TABLE)->eq('id', $subtask_id)->findOne();

View File

@ -1,68 +0,0 @@
<?php
namespace Model;
/**
* Subtask Paginator
*
* @package model
* @author Frederic Guillot
*/
class SubtaskPaginator extends Base
{
/**
* Get all subtasks assigned to a user
*
* @access public
* @param integer $user_id User id
* @param array $status List of status
* @param integer $offset Offset
* @param integer $limit Limit
* @param string $column Sorting column
* @param string $direction Sorting direction
* @return array
*/
public function userSubtasks($user_id, array $status, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'asc')
{
$status_list = $this->subTask->getStatusList();
$subtasks = $this->db->table(SubTask::TABLE)
->columns(
SubTask::TABLE.'.*',
Task::TABLE.'.project_id',
Task::TABLE.'.color_id',
Project::TABLE.'.name AS project_name'
)
->eq('user_id', $user_id)
->in(SubTask::TABLE.'.status', $status)
->join(Task::TABLE, 'id', 'task_id')
->join(Project::TABLE, 'id', 'project_id', Task::TABLE)
->offset($offset)
->limit($limit)
->orderBy($column, $direction)
->findAll();
foreach ($subtasks as &$subtask) {
$subtask['status_name'] = $status_list[$subtask['status']];
}
return $subtasks;
}
/**
* Count all subtasks assigned to the user
*
* @access public
* @param integer $user_id User id
* @param array $status List of status
* @return integer
*/
public function countUserSubtasks($user_id, array $status)
{
return $this->db
->table(SubTask::TABLE)
->eq('user_id', $user_id)
->in('status', $status)
->count();
}
}

View File

@ -13,12 +13,66 @@ use PDO;
class TaskFinder extends Base
{
/**
* Common request to fetch a list of tasks
* Get query for closed tasks
*
* @access public
* @param integer $project_id Project id
* @return \PicoDb\Table
*/
public function getClosedTaskQuery($project_id)
{
return $this->getExtendedQuery()
->eq('project_id', $project_id)
->eq('is_active', Task::STATUS_CLOSED);
}
/**
* Get query for task search
*
* @access public
* @param integer $project_id Project id
* @param string $search Search terms
* @return \PicoDb\Table
*/
public function getSearchQuery($project_id, $search)
{
return $this->getExtendedQuery()
->eq('project_id', $project_id)
->ilike('title', '%'.$search.'%');
}
/**
* Get query for assigned user tasks
*
* @access public
* @param integer $user_id User id
* @return \PicoDb\Table
*/
public function getUserQuery($user_id)
{
return $this->db
->table(Task::TABLE)
->columns(
'tasks.id',
'tasks.title',
'tasks.date_due',
'tasks.date_creation',
'tasks.project_id',
'tasks.color_id',
'projects.name AS project_name'
)
->join(Project::TABLE, 'id', 'project_id')
->eq('tasks.owner_id', $user_id)
->eq('tasks.is_active', Task::STATUS_OPEN);
}
/**
* Extended query
*
* @access public
* @return \PicoDb\Table
*/
public function getQuery()
public function getExtendedQuery()
{
return $this->db
->table(Task::TABLE)
@ -62,7 +116,7 @@ class TaskFinder extends Base
*/
public function getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id = 0)
{
return $this->getQuery()
return $this->getExtendedQuery()
->eq('project_id', $project_id)
->eq('column_id', $column_id)
->eq('swimlane_id', $swimlane_id)

View File

@ -1,138 +0,0 @@
<?php
namespace Model;
/**
* Task Paginator model
*
* @package model
* @author Frederic Guillot
*/
class TaskPaginator extends Base
{
/**
* Task search with pagination
*
* @access public
* @param integer $project_id Project id
* @param string $search Search terms
* @param integer $offset Offset
* @param integer $limit Limit
* @param string $column Sorting column
* @param string $direction Sorting direction
* @return array
*/
public function searchTasks($project_id, $search, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'DESC')
{
return $this->taskFinder->getQuery()
->eq('project_id', $project_id)
->ilike('title', '%'.$search.'%')
->offset($offset)
->limit($limit)
->orderBy($column, $direction)
->findAll();
}
/**
* Count the number of tasks for a custom search
*
* @access public
* @param integer $project_id Project id
* @param string $search Search terms
* @return integer
*/
public function countSearchTasks($project_id, $search)
{
return $this->db->table(Task::TABLE)
->eq('project_id', $project_id)
->ilike('title', '%'.$search.'%')
->count();
}
/**
* Get all completed tasks with pagination
*
* @access public
* @param integer $project_id Project id
* @param integer $offset Offset
* @param integer $limit Limit
* @param string $column Sorting column
* @param string $direction Sorting direction
* @return array
*/
public function closedTasks($project_id, $offset = 0, $limit = 25, $column = 'tasks.date_completed', $direction = 'DESC')
{
return $this->taskFinder->getQuery()
->eq('project_id', $project_id)
->eq('is_active', Task::STATUS_CLOSED)
->offset($offset)
->limit($limit)
->orderBy($column, $direction)
->findAll();
}
/**
* Count all closed tasks
*
* @access public
* @param integer $project_id Project id
* @return integer
*/
public function countClosedTasks($project_id)
{
return $this->db
->table(Task::TABLE)
->eq('project_id', $project_id)
->eq('is_active', Task::STATUS_CLOSED)
->count();
}
/**
* Get all open tasks for a given user
*
* @access public
* @param integer $user_id User id
* @param integer $offset Offset
* @param integer $limit Limit
* @param string $column Sorting column
* @param string $direction Sorting direction
* @return array
*/
public function userTasks($user_id, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'ASC')
{
return $this->db
->table(Task::TABLE)
->columns(
'tasks.id',
'tasks.title',
'tasks.date_due',
'tasks.date_creation',
'tasks.project_id',
'tasks.color_id',
'projects.name AS project_name'
)
->join(Project::TABLE, 'id', 'project_id')
->eq('tasks.owner_id', $user_id)
->eq('tasks.is_active', Task::STATUS_OPEN)
->offset($offset)
->limit($limit)
->orderBy($column, $direction)
->findAll();
}
/**
* Count all tasks assigned to the user
*
* @access public
* @param integer $user_id User id
* @return integer
*/
public function countUserTasks($user_id)
{
return $this->db
->table(Task::TABLE)
->eq('owner_id', $user_id)
->eq('is_active', Task::STATUS_OPEN)
->count();
}
}

View File

@ -28,6 +28,30 @@ class User extends Base
*/
const EVERYBODY_ID = -1;
/**
* Get query to fetch all users
*
* @access public
* @return \PicoDb\Table
*/
public function getQuery()
{
return $this->db
->table(self::TABLE)
->columns(
'id',
'username',
'name',
'email',
'is_admin',
'default_project_id',
'is_ldap_user',
'notifications_enabled',
'google_id',
'github_id'
);
}
/**
* Return the full name
*
@ -112,54 +136,7 @@ class User extends Base
*/
public function getAll()
{
return $this->db
->table(self::TABLE)
->asc('username')
->columns(
'id',
'username',
'name',
'email',
'is_admin',
'default_project_id',
'is_ldap_user',
'notifications_enabled',
'google_id',
'github_id'
)
->findAll();
}
/**
* Get all users with pagination
*
* @access public
* @param integer $offset Offset
* @param integer $limit Limit
* @param string $column Sorting column
* @param string $direction Sorting direction
* @return array
*/
public function paginate($offset = 0, $limit = 25, $column = 'username', $direction = 'ASC')
{
return $this->db
->table(self::TABLE)
->columns(
'id',
'username',
'name',
'email',
'is_admin',
'default_project_id',
'is_ldap_user',
'notifications_enabled',
'google_id',
'github_id'
)
->offset($offset)
->limit($limit)
->orderBy($column, $direction)
->findAll();
return $this->getQuery()->asc('username')->findAll();
}
/**

View File

@ -2,6 +2,7 @@
namespace ServiceProvider;
use Core\Paginator;
use Model\Config;
use Model\Project;
use Model\Webhook;
@ -28,10 +29,8 @@ class ClassProvider implements ServiceProviderInterface
'ProjectActivity',
'ProjectAnalytic',
'ProjectDailySummary',
'ProjectPaginator',
'ProjectPermission',
'SubTask',
'SubtaskPaginator',
'SubtaskExport',
'Swimlane',
'Task',
@ -41,7 +40,6 @@ class ClassProvider implements ServiceProviderInterface
'TaskFinder',
'TaskFilter',
'TaskModification',
'TaskPaginator',
'TaskPermission',
'TaskPosition',
'TaskStatus',
@ -57,6 +55,7 @@ class ClassProvider implements ServiceProviderInterface
'Session',
'MemoryCache',
'FileCache',
'Request',
),
'Integration' => array(
'GitlabWebhook',
@ -77,5 +76,9 @@ class ClassProvider implements ServiceProviderInterface
};
}
}
$container['paginator'] = $container->factory(function ($c) {
return new Paginator($c);
});
}
}

View File

@ -14,9 +14,9 @@
</div>
<section id="dashboard">
<div class="dashboard-left-column">
<?= $this->render('app/projects', array('projects' => $projects, 'pagination' => $project_pagination)) ?>
<?= $this->render('app/tasks', array('tasks' => $tasks, 'pagination' => $task_pagination)) ?>
<?= $this->render('app/subtasks', array('subtasks' => $subtasks, 'pagination' => $subtask_pagination)) ?>
<?= $this->render('app/projects', array('paginator' => $project_paginator)) ?>
<?= $this->render('app/tasks', array('paginator' => $task_paginator)) ?>
<?= $this->render('app/subtasks', array('paginator' => $subtask_paginator)) ?>
</div>
<div class="dashboard-right-column">
<h2><?= t('Activity stream') ?></h2>

View File

@ -1,14 +1,14 @@
<h2><?= t('My projects') ?></h2>
<?php if (empty($projects)): ?>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('Your are not member of any project.') ?></p>
<?php else: ?>
<table class="table-fixed">
<tr>
<th class="column-8"><?= $this->order('Id', 'id', $pagination) ?></th>
<th class="column-20"><?= $this->order(t('Project'), 'name', $pagination) ?></th>
<th class="column-8"><?= $paginator->order('Id', 'id') ?></th>
<th class="column-20"><?= $paginator->order(t('Project'), 'name') ?></th>
<th><?= t('Columns') ?></th>
</tr>
<?php foreach ($projects as $project): ?>
<?php foreach ($paginator->getCollection() as $project): ?>
<tr>
<td>
<?= $this->a('#'.$project['id'], 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link') ?>
@ -17,6 +17,7 @@
<?php if ($this->isManager($project['id'])): ?>
<?= $this->a('<i class="fa fa-cog"></i>', 'project', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Settings')) ?>&nbsp;
<?php endif ?>
<?= $this->a('<i class="fa fa-calendar"></i>', 'calendar', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Calendar')) ?>&nbsp;
<?= $this->a($this->e($project['name']), 'board', 'show', array('project_id' => $project['id'])) ?>
</td>
@ -30,5 +31,5 @@
<?php endforeach ?>
</table>
<?= $this->paginate($pagination) ?>
<?= $paginator ?>
<?php endif ?>

View File

@ -1,15 +1,15 @@
<h2><?= t('My subtasks') ?></h2>
<?php if (empty($subtasks)): ?>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
<?php else: ?>
<table class="table-fixed">
<tr>
<th class="column-10"><?= $this->order('Id', 'tasks.id', $pagination) ?></th>
<th class="column-20"><?= $this->order(t('Project'), 'project_name', $pagination) ?></th>
<th class="column-15"><?= $this->order(t('Status'), 'status', $pagination) ?></th>
<th><?= $this->order(t('Subtask'), 'title', $pagination) ?></th>
<th class="column-10"><?= $paginator->order('Id', 'tasks.id') ?></th>
<th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th>
<th class="column-15"><?= $paginator->order(t('Status'), 'status') ?></th>
<th><?= $paginator->order(t('Subtask'), 'title') ?></th>
</tr>
<?php foreach ($subtasks as $subtask): ?>
<?php foreach ($paginator->getCollection() as $subtask): ?>
<tr>
<td class="task-table color-<?= $subtask['color_id'] ?>">
<?= $this->a('#'.$subtask['task_id'], 'task', 'show', array('task_id' => $subtask['task_id'], 'project_id' => $subtask['project_id'])) ?>
@ -27,5 +27,5 @@
<?php endforeach ?>
</table>
<?= $this->paginate($pagination) ?>
<?= $paginator ?>
<?php endif ?>

View File

@ -1,15 +1,15 @@
<h2><?= t('My tasks') ?></h2>
<?php if (empty($tasks)): ?>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
<?php else: ?>
<table class="table-fixed">
<tr>
<th class="column-8"><?= $this->order('Id', 'tasks.id', $pagination) ?></th>
<th class="column-20"><?= $this->order(t('Project'), 'project_name', $pagination) ?></th>
<th><?= $this->order(t('Task'), 'title', $pagination) ?></th>
<th class="column-20"><?= $this->order(t('Due date'), 'date_due', $pagination) ?></th>
<th class="column-8"><?= $paginator->order('Id', 'tasks.id') ?></th>
<th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th>
<th><?= $paginator->order(t('Task'), 'title') ?></th>
<th class="column-20"><?= $paginator->order(t('Due date'), 'date_due') ?></th>
</tr>
<?php foreach ($tasks as $task): ?>
<?php foreach ($paginator->getCollection() as $task): ?>
<tr>
<td class="task-table color-<?= $task['color_id'] ?>">
<?= $this->a('#'.$task['id'], 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
@ -27,5 +27,5 @@
<?php endforeach ?>
</table>
<?= $this->paginate($pagination) ?>
<?= $paginator ?>
<?php endif ?>

View File

@ -28,14 +28,13 @@
<input type="submit" value="<?= t('Search') ?>" class="btn btn-blue"/>
</form>
<?php if (empty($tasks) && ! empty($values['search'])): ?>
<?php if (! empty($values['search']) && $paginator->isEmpty()): ?>
<p class="alert"><?= t('Nothing found.') ?></p>
<?php elseif (! empty($tasks)): ?>
<?php elseif (! $paginator->isEmpty()): ?>
<?= $this->render('task/table', array(
'tasks' => $tasks,
'paginator' => $paginator,
'categories' => $categories,
'columns' => $columns,
'pagination' => $pagination,
)) ?>
<?php endif ?>

View File

@ -20,14 +20,13 @@
</ul>
</div>
<section>
<?php if (empty($tasks)): ?>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No task') ?></p>
<?php else: ?>
<?= $this->render('task/table', array(
'tasks' => $tasks,
'paginator' => $paginator,
'categories' => $categories,
'columns' => $columns,
'pagination' => $pagination,
)) ?>
<?php endif ?>
</section>

View File

@ -1,16 +1,16 @@
<table class="table-fixed table-small">
<tr>
<th class="column-8"><?= $this->order(t('Id'), 'tasks.id', $pagination) ?></th>
<th class="column-8"><?= $this->order(t('Column'), 'tasks.column_id', $pagination) ?></th>
<th class="column-8"><?= $this->order(t('Category'), 'tasks.category_id', $pagination) ?></th>
<th><?= $this->order(t('Title'), 'tasks.title', $pagination) ?></th>
<th class="column-10"><?= $this->order(t('Assignee'), 'users.username', $pagination) ?></th>
<th class="column-10"><?= $this->order(t('Due date'), 'tasks.date_due', $pagination) ?></th>
<th class="column-10"><?= $this->order(t('Date created'), 'tasks.date_creation', $pagination) ?></th>
<th class="column-10"><?= $this->order(t('Date completed'), 'tasks.date_completed', $pagination) ?></th>
<th class="column-5"><?= $this->order(t('Status'), 'tasks.is_active', $pagination) ?></th>
<th class="column-8"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
<th class="column-8"><?= $paginator->order(t('Column'), 'tasks.column_id') ?></th>
<th class="column-8"><?= $paginator->order(t('Category'), 'tasks.category_id') ?></th>
<th><?= $paginator->order(t('Title'), 'tasks.title') ?></th>
<th class="column-10"><?= $paginator->order(t('Assignee'), 'users.username') ?></th>
<th class="column-10"><?= $paginator->order(t('Due date'), 'tasks.date_due') ?></th>
<th class="column-10"><?= $paginator->order(t('Date created'), 'tasks.date_creation') ?></th>
<th class="column-10"><?= $paginator->order(t('Date completed'), 'tasks.date_completed') ?></th>
<th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
</tr>
<?php foreach ($tasks as $task): ?>
<?php foreach ($paginator->getCollection() as $task): ?>
<tr>
<td class="task-table color-<?= $task['color_id'] ?>">
<?= $this->a('#'.$this->e($task['id']), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', t('View this task')) ?>
@ -53,4 +53,4 @@
<?php endforeach ?>
</table>
<?= $this->paginate($pagination) ?>
<?= $paginator ?>

View File

@ -7,22 +7,22 @@
<?php endif ?>
</div>
<section>
<?php if (empty($users)): ?>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No user') ?></p>
<?php else: ?>
<table>
<tr>
<th><?= $this->order(t('Id'), 'id', $pagination) ?></th>
<th><?= $this->order(t('Username'), 'username', $pagination) ?></th>
<th><?= $this->order(t('Name'), 'name', $pagination) ?></th>
<th><?= $this->order(t('Email'), 'email', $pagination) ?></th>
<th><?= $this->order(t('Administrator'), 'is_admin', $pagination) ?></th>
<th><?= $this->order(t('Default project'), 'default_project_id', $pagination) ?></th>
<th><?= $this->order(t('Notifications'), 'notifications_enabled', $pagination) ?></th>
<th><?= $paginator->order(t('Id'), 'id') ?></th>
<th><?= $paginator->order(t('Username'), 'username') ?></th>
<th><?= $paginator->order(t('Name'), 'name') ?></th>
<th><?= $paginator->order(t('Email'), 'email') ?></th>
<th><?= $paginator->order(t('Administrator'), 'is_admin') ?></th>
<th><?= $paginator->order(t('Default project'), 'default_project_id') ?></th>
<th><?= $paginator->order(t('Notifications'), 'notifications_enabled') ?></th>
<th><?= t('External accounts') ?></th>
<th><?= $this->order(t('Account type'), 'is_ldap_user', $pagination) ?></th>
<th><?= $paginator->order(t('Account type'), 'is_ldap_user') ?></th>
</tr>
<?php foreach ($users as $user): ?>
<?php foreach ($paginator->getCollection() as $user): ?>
<tr>
<td>
<?= $this->a('#'.$user['id'], 'user', 'show', array('user_id' => $user['id'])) ?>
@ -66,7 +66,7 @@
<?php endforeach ?>
</table>
<?= $this->paginate($pagination) ?>
<?= $paginator ?>
<?php endif ?>
</section>
</section>