From aa6fffb05a7d84a36991341610675499b6ea8a79 Mon Sep 17 00:00:00 2001 From: Frédéric Guillot Date: Sat, 15 Nov 2014 21:49:06 -0500 Subject: Add pagination and sorting to the dashboard --- app/Controller/App.php | 132 +++++++++++++++++++++++++++++++++-- app/Controller/Project.php | 8 +-- app/Model/Project.php | 34 --------- app/Model/ProjectAnalytic.php | 2 +- app/Model/ProjectPaginator.php | 49 +++++++++++++ app/Model/SubTask.php | 32 --------- app/Model/SubtaskPaginator.php | 68 ++++++++++++++++++ app/Model/TaskFinder.php | 94 +------------------------ app/Model/TaskPaginator.php | 139 +++++++++++++++++++++++++++++++++++++ app/Template/app/dashboard.php | 26 +++++++ app/Template/app/index.php | 114 ------------------------------ app/Template/app/projects.php | 33 +++++++++ app/Template/app/subtasks.php | 31 +++++++++ app/Template/app/tasks.php | 31 +++++++++ app/helpers.php | 6 +- assets/css/app.css | 7 +- assets/css/base.css | 1 + assets/css/pagination.css | 6 +- scripts/create-random-projects.php | 18 +++++ scripts/create-random-tasks.php | 49 ++++++++++--- scripts/create-random-users.php | 2 +- 21 files changed, 582 insertions(+), 300 deletions(-) create mode 100644 app/Model/ProjectPaginator.php create mode 100644 app/Model/SubtaskPaginator.php create mode 100644 app/Model/TaskPaginator.php create mode 100644 app/Template/app/dashboard.php delete mode 100644 app/Template/app/index.php create mode 100644 app/Template/app/projects.php create mode 100644 app/Template/app/subtasks.php create mode 100644 app/Template/app/tasks.php create mode 100755 scripts/create-random-projects.php diff --git a/app/Controller/App.php b/app/Controller/App.php index 993149c5..2458800a 100644 --- a/app/Controller/App.php +++ b/app/Controller/App.php @@ -20,17 +20,137 @@ 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'); + $user_id = $this->acl->getUserId(); $projects = $this->projectPermission->getMemberProjects($user_id); $project_ids = array_keys($projects); - $this->response->html($this->template->layout('app/index', array( + $params = array( + 'title' => t('Dashboard'), 'board_selector' => $this->projectPermission->getAllowedProjects($user_id), 'events' => $this->projectActivity->getProjects($project_ids, 10), - 'tasks' => $this->taskFinder->getAllTasksByUser($user_id), - 'subtasks' => $this->subTask->getAllByUser($user_id, array(SubTask::STATUS_TODO, SubTask::STATUS_INPROGRESS)), - 'projects' => $this->project->getSummary($project_ids), - 'title' => t('Dashboard'), - ))); + ); + + $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)); + } + + /** + * Get tasks pagination + * + * @access public + */ + private function getTaskPagination($user_id, $paginate, $offset, $order, $direction) + { + $limit = 10; + + if (! in_array($order, array('tasks.id', 'project_name', 'title'))) { + $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 + */ + private function getSubtaskPagination($user_id, $paginate, $offset, $order, $direction) + { + $status = array(SubTask::STATUS_TODO, SubTask::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 + */ + private function getProjectPagination($project_ids, $paginate, $offset, $order, $direction) + { + $limit = 5; + + 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, + ) + ); } } diff --git a/app/Controller/Project.php b/app/Controller/Project.php index a479b1d6..cac5e0b8 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -428,8 +428,8 @@ class Project extends Base $limit = 25; if ($search !== '') { - $tasks = $this->taskFinder->search($project['id'], $search, $offset, $limit, $order, $direction); - $nb_tasks = $this->taskFinder->countSearch($project['id'], $search); + $tasks = $this->taskPaginator->searchTasks($project['id'], $search, $offset, $limit, $order, $direction); + $nb_tasks = $this->taskPaginator->countSearchTasks($project['id'], $search); } $this->response->html($this->template->layout('project_search', array( @@ -472,8 +472,8 @@ class Project extends Base $offset = $this->request->getIntegerParam('offset', 0); $limit = 25; - $tasks = $this->taskFinder->getClosedTasks($project['id'], $offset, $limit, $order, $direction); - $nb_tasks = $this->taskFinder->countByProjectId($project['id'], array(TaskModel::STATUS_CLOSED)); + $tasks = $this->taskPaginator->closedTasks($project['id'], $offset, $limit, $order, $direction); + $nb_tasks = $this->taskPaginator->countClosedTasks($project['id']); $this->response->html($this->template->layout('project_tasks', array( 'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()), diff --git a/app/Model/Project.php b/app/Model/Project.php index 2abee2aa..8b842519 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -95,40 +95,6 @@ class Project extends Base return (bool) $this->db->table(self::TABLE)->eq('id', $project_id)->eq('is_private', 1)->count(); } - /** - * 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 $status Project status - * @param string $order Sort on this column - * @param string $direction Sorting direction - * @return array Project properties - */ - public function getSummary(array $project_ids, $status = self::ACTIVE, $order = 'name', $direction = 'asc') - { - if (empty($project_ids)) { - return array(); - } - - $projects = $this->db->table(self::TABLE) - ->in('id', $project_ids) - ->eq('is_active', $status) - ->orderby($order, $direction) - ->findAll(); - - foreach ($projects as &$project) { - - $project['columns'] = $this->board->getColumns($project['id']); - - foreach ($project['columns'] as &$column) { - $column['nb_tasks'] = $this->taskFinder->countByColumnId($project['id'], $column['id']); - } - } - - return $projects; - } - /** * Get all projects, optionaly fetch stats for each project and can check users permissions * diff --git a/app/Model/ProjectAnalytic.php b/app/Model/ProjectAnalytic.php index 8ecbf1f0..b62248eb 100644 --- a/app/Model/ProjectAnalytic.php +++ b/app/Model/ProjectAnalytic.php @@ -57,7 +57,7 @@ class ProjectAnalytic extends Base foreach ($tasks as $task) { - $user = $users[$task['owner_id']]; + $user = isset($users[$task['owner_id']]) ? $users[$task['owner_id']] : $users[0]; $total++; if (! isset($metrics[$user])) { diff --git a/app/Model/ProjectPaginator.php b/app/Model/ProjectPaginator.php new file mode 100644 index 00000000..9f1c39f0 --- /dev/null +++ b/app/Model/ProjectPaginator.php @@ -0,0 +1,49 @@ +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']); + + foreach ($project['columns'] as &$column) { + $column['nb_tasks'] = $this->taskFinder->countByColumnId($project['id'], $column['id']); + } + } + + return $projects; + } +} diff --git a/app/Model/SubTask.php b/app/Model/SubTask.php index f414da7f..886ad1f3 100644 --- a/app/Model/SubTask.php +++ b/app/Model/SubTask.php @@ -92,38 +92,6 @@ class SubTask extends Base return $subtasks; } - /** - * Get all subtasks assigned to a user - * - * @access public - * @param integer $user_id User id - * @param array $status List of status - * @return array - */ - public function getAllByUser($user_id, array $status) - { - $status_list = $this->getStatusList(); - $subtasks = $this->db->table(self::TABLE) - ->columns( - self::TABLE.'.*', - Task::TABLE.'.project_id', - Task::TABLE.'.color_id', - Project::TABLE.'.name AS project_name' - ) - ->eq('user_id', $user_id) - ->in(self::TABLE.'.status', $status) - ->join(Task::TABLE, 'id', 'task_id') - ->join(Project::TABLE, 'id', 'project_id', Task::TABLE) - ->asc(Task::TABLE.'.id') - ->findAll(); - - foreach ($subtasks as &$subtask) { - $subtask['status_name'] = $status_list[$subtask['status']]; - } - - return $subtasks; - } - /** * Get a subtask by the id * diff --git a/app/Model/SubtaskPaginator.php b/app/Model/SubtaskPaginator.php new file mode 100644 index 00000000..8ccbd696 --- /dev/null +++ b/app/Model/SubtaskPaginator.php @@ -0,0 +1,68 @@ +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(); + } +} diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index 56795152..0e581025 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -15,10 +15,10 @@ class TaskFinder extends Base /** * Common request to fetch a list of tasks * - * @access private + * @access public * @return \PicoDb\Table */ - private function prepareRequestList() + public function getQuery() { return $this->db ->table(Task::TABLE) @@ -50,51 +50,6 @@ class TaskFinder extends Base ->join(User::TABLE, 'id', 'owner_id'); } - /** - * 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 search($project_id, $search, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'DESC') - { - return $this->prepareRequestList() - ->eq('project_id', $project_id) - ->like('title', '%'.$search.'%') - ->offset($offset) - ->limit($limit) - ->orderBy($column, $direction) - ->findAll(); - } - - /** - * 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 getClosedTasks($project_id, $offset = 0, $limit = 25, $column = 'tasks.date_completed', $direction = 'DESC') - { - return $this->prepareRequestList() - ->eq('project_id', $project_id) - ->eq('is_active', Task::STATUS_CLOSED) - ->offset($offset) - ->limit($limit) - ->orderBy($column, $direction) - ->findAll(); - } - /** * Get all tasks shown on the board (sorted by position) * @@ -104,40 +59,13 @@ class TaskFinder extends Base */ public function getTasksOnBoard($project_id) { - return $this->prepareRequestList() + return $this->getQuery() ->eq('project_id', $project_id) ->eq('is_active', Task::STATUS_OPEN) ->asc('tasks.position') ->findAll(); } - /** - * Get all open tasks for a given user - * - * @access public - * @param integer $user_id User id - * @return array - */ - public function getAllTasksByUser($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) - ->asc('tasks.id') - ->findAll(); - } - /** * Get all tasks for a given project and status * @@ -295,22 +223,6 @@ class TaskFinder extends Base ->count(); } - /** - * 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 countSearch($project_id, $search) - { - return $this->db->table(Task::TABLE) - ->eq('project_id', $project_id) - ->like('title', '%'.$search.'%') - ->count(); - } - /** * Return true if the task exists * diff --git a/app/Model/TaskPaginator.php b/app/Model/TaskPaginator.php new file mode 100644 index 00000000..4ae3566c --- /dev/null +++ b/app/Model/TaskPaginator.php @@ -0,0 +1,139 @@ +taskFinder->getQuery() + ->eq('project_id', $project_id) + ->like('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) + ->like('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 + * @param array $status List of status 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(); + } +} diff --git a/app/Template/app/dashboard.php b/app/Template/app/dashboard.php new file mode 100644 index 00000000..9bcd69f6 --- /dev/null +++ b/app/Template/app/dashboard.php @@ -0,0 +1,26 @@ +
+ +
+
+ $projects, 'pagination' => $project_pagination)) ?> + $tasks, 'pagination' => $task_pagination)) ?> + $subtasks, 'pagination' => $subtask_pagination)) ?> +
+
+

+ $events)) ?> +
+
+
\ No newline at end of file diff --git a/app/Template/app/index.php b/app/Template/app/index.php deleted file mode 100644 index 637bc402..00000000 --- a/app/Template/app/index.php +++ /dev/null @@ -1,114 +0,0 @@ -
- -
-
-

- -

- - - - - - - - - - - - - - -
 
- $project['id']), false, 'dashboard-table-link') ?> - - - ', 'project', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Settings')) ?>  - - $project['id'])) ?> - - - - - -
- - -

- -

- - - - - - - - - - - - - - - - -
 
- $task['id'])) ?> - - $task['project_id'])) ?> - - $task['id'])) ?> - - -
- - -

- -

- - - - - - - - - - - - - - - - -
 
- $subtask['task_id'])) ?> - - $subtask['project_id'])) ?> - - - - $subtask['task_id'])) ?> -
- - -
-
-

- $events)) ?> -
-
\ No newline at end of file diff --git a/app/Template/app/projects.php b/app/Template/app/projects.php new file mode 100644 index 00000000..2c13a052 --- /dev/null +++ b/app/Template/app/projects.php @@ -0,0 +1,33 @@ +

+ +

+ + + + + + + + + + + + + + +
+ $project['id']), false, 'dashboard-table-link') ?> + + + ', 'project', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Settings')) ?>  + + $project['id'])) ?> + + + + + +
+ + + \ No newline at end of file diff --git a/app/Template/app/subtasks.php b/app/Template/app/subtasks.php new file mode 100644 index 00000000..f51ef1db --- /dev/null +++ b/app/Template/app/subtasks.php @@ -0,0 +1,31 @@ +

+ +

+ + + + + + + + + + + + + + + + +
+ $subtask['task_id'])) ?> + + $subtask['project_id'])) ?> + + + + $subtask['task_id'])) ?> +
+ + + \ No newline at end of file diff --git a/app/Template/app/tasks.php b/app/Template/app/tasks.php new file mode 100644 index 00000000..d1e1ed4e --- /dev/null +++ b/app/Template/app/tasks.php @@ -0,0 +1,31 @@ +

+ +

+ + + + + + + + + + + + + + + + +
+ $task['id'])) ?> + + $task['project_id'])) ?> + + $task['id'])) ?> + + +
+ + + \ No newline at end of file diff --git a/app/helpers.php b/app/helpers.php index 14d6a5ee..b53e59e7 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -618,8 +618,8 @@ function paginate(array $pagination) { extract($pagination); - $html = '