diff options
Diffstat (limited to 'app/Controller')
40 files changed, 676 insertions, 549 deletions
diff --git a/app/Controller/ActionController.php b/app/Controller/ActionController.php index c935125a..43acf590 100644 --- a/app/Controller/ActionController.php +++ b/app/Controller/ActionController.php @@ -46,9 +46,10 @@ class ActionController extends BaseController public function confirm() { $project = $this->getProject(); + $action = $this->getAction($project); $this->response->html($this->helper->layout->project('action/remove', array( - 'action' => $this->actionModel->getById($this->request->getIntegerParam('action_id')), + 'action' => $action, 'available_events' => $this->eventManager->getAll(), 'available_actions' => $this->actionManager->getAvailableActions(), 'project' => $project, @@ -65,7 +66,7 @@ class ActionController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $action = $this->actionModel->getById($this->request->getIntegerParam('action_id')); + $action = $this->getAction($project); if (! empty($action) && $this->actionModel->remove($action['id'])) { $this->flash->success(t('Action removed successfully.')); diff --git a/app/Controller/ActionCreationController.php b/app/Controller/ActionCreationController.php index 7fee58d1..abb214e6 100644 --- a/app/Controller/ActionCreationController.php +++ b/app/Controller/ActionCreationController.php @@ -35,8 +35,9 @@ class ActionCreationController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; - if (empty($values['action_name']) || empty($values['project_id'])) { + if (empty($values['action_name'])) { return $this->create(); } @@ -57,8 +58,9 @@ class ActionCreationController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; - if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) { + if (empty($values['action_name']) || empty($values['event_name'])) { $this->create(); return; } @@ -109,6 +111,7 @@ class ActionCreationController extends BaseController */ private function doCreation(array $project, array $values) { + $values['project_id'] = $project['id']; list($valid, ) = $this->actionValidator->validateCreation($values); if ($valid) { diff --git a/app/Controller/ActivityController.php b/app/Controller/ActivityController.php index a1734af1..7a58e670 100644 --- a/app/Controller/ActivityController.php +++ b/app/Controller/ActivityController.php @@ -19,7 +19,7 @@ class ActivityController extends BaseController { $user = $this->getUser(); - $this->response->html($this->helper->layout->dashboard('activity/user', array( + $this->response->html($this->template->render('activity/user', array( 'title' => t('Activity stream for %s', $this->helper->user->getFullname($user)), 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']), 100), 'user' => $user, diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 34b9c8cc..645cae7f 100644 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -27,7 +27,7 @@ class AppController extends Base $this->response->html($this->helper->layout->app('app/forbidden', array( 'title' => t('Access Forbidden'), 'no_layout' => $withoutLayout, - ))); + )), 403); } } diff --git a/app/Controller/BaseController.php b/app/Controller/BaseController.php index 5233e27f..43ecfaab 100644 --- a/app/Controller/BaseController.php +++ b/app/Controller/BaseController.php @@ -74,13 +74,14 @@ abstract class BaseController extends Base { $task_id = $this->request->getIntegerParam('task_id'); $file_id = $this->request->getIntegerParam('file_id'); + $project_id = $this->request->getIntegerParam('project_id'); $model = 'projectFileModel'; if ($task_id > 0) { $model = 'taskFileModel'; - $project_id = $this->taskFinderModel->getProjectId($task_id); + $task_project_id = $this->taskFinderModel->getProjectId($task_id); - if ($project_id !== $this->request->getIntegerParam('project_id')) { + if ($project_id != $task_project_id) { throw new AccessForbiddenException(); } } @@ -91,6 +92,12 @@ abstract class BaseController extends Base throw new PageNotFoundException(); } + if (isset($file['task_id']) && $file['task_id'] != $task_id) { + throw new AccessForbiddenException(); + } else if (isset($file['project_id']) && $file['project_id'] != $project_id) { + throw new AccessForbiddenException(); + } + $file['model'] = $model; return $file; } @@ -138,14 +145,7 @@ abstract class BaseController extends Base return $user; } - /** - * Get the current subtask - * - * @access protected - * @return array - * @throws PageNotFoundException - */ - protected function getSubtask() + protected function getSubtask(array $task) { $subtask = $this->subtaskModel->getById($this->request->getIntegerParam('subtask_id')); @@ -153,6 +153,149 @@ abstract class BaseController extends Base throw new PageNotFoundException(); } + if ($subtask['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + return $subtask; } + + protected function getComment(array $task) + { + $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id')); + + if (empty($comment)) { + throw new PageNotFoundException(); + } + + if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) { + throw new AccessForbiddenException(); + } + + if ($comment['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $comment; + } + + protected function getExternalTaskLink(array $task) + { + $link = $this->taskExternalLinkModel->getById($this->request->getIntegerParam('link_id')); + + if (empty($link)) { + throw new PageNotFoundException(); + } + + if ($link['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $link; + } + + protected function getInternalTaskLink(array $task) + { + $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id')); + + if (empty($link)) { + throw new PageNotFoundException(); + } + + if ($link['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $link; + } + + protected function getColumn(array $project) + { + $column = $this->columnModel->getById($this->request->getIntegerParam('column_id')); + + if (empty($column)) { + throw new PageNotFoundException(); + } + + if ($column['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $column; + } + + protected function getSwimlane(array $project) + { + $swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id')); + + if (empty($swimlane)) { + throw new PageNotFoundException(); + } + + if ($swimlane['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $swimlane; + } + + protected function getCategory(array $project) + { + $category = $this->categoryModel->getById($this->request->getIntegerParam('category_id')); + + if (empty($category)) { + throw new PageNotFoundException(); + } + + if ($category['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $category; + } + + protected function getProjectTag(array $project) + { + $tag = $this->tagModel->getById($this->request->getIntegerParam('tag_id')); + + if (empty($tag)) { + throw new PageNotFoundException(); + } + + if ($tag['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $tag; + } + + protected function getAction(array $project) + { + $action = $this->actionModel->getById($this->request->getIntegerParam('action_id')); + + if (empty($action)) { + throw new PageNotFoundException(); + } + + if ($action['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $action; + } + + protected function getCustomFilter(array $project) + { + $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + + if (empty($filter)) { + throw new PageNotFoundException(); + } + + if ($filter['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $filter; + } } diff --git a/app/Controller/CalendarController.php b/app/Controller/CalendarController.php deleted file mode 100644 index e764549d..00000000 --- a/app/Controller/CalendarController.php +++ /dev/null @@ -1,120 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -use Kanboard\Filter\TaskAssigneeFilter; -use Kanboard\Filter\TaskProjectFilter; -use Kanboard\Filter\TaskStatusFilter; -use Kanboard\Model\TaskModel; - -/** - * Calendar Controller - * - * @package Kanboard\Controller - * @author Frederic Guillot - * @author Timo Litzbarski - */ -class CalendarController extends BaseController -{ - /** - * Show calendar view for a user - * - * @access public - */ - public function user() - { - $user = $this->getUser(); - - $this->response->html($this->helper->layout->app('calendar/user', array( - 'user' => $user, - ))); - } - - /** - * Show calendar view for a project - * - * @access public - */ - public function project() - { - $project = $this->getProject(); - - $this->response->html($this->helper->layout->app('calendar/project', array( - 'project' => $project, - 'title' => $project['name'], - 'description' => $this->helper->projectHeader->getDescription($project), - ))); - } - - /** - * Get tasks to display on the calendar (project view) - * - * @access public - */ - public function projectEvents() - { - $project_id = $this->request->getIntegerParam('project_id'); - $start = $this->request->getStringParam('start'); - $end = $this->request->getStringParam('end'); - $search = $this->userSession->getFilters($project_id); - $queryBuilder = $this->taskLexer->build($search)->withFilter(new TaskProjectFilter($project_id)); - - $events = $this->helper->calendar->getTaskDateDueEvents(clone($queryBuilder), $start, $end); - $events = array_merge($events, $this->helper->calendar->getTaskEvents(clone($queryBuilder), $start, $end)); - - $events = $this->hook->merge('controller:calendar:project:events', $events, array( - 'project_id' => $project_id, - 'start' => $start, - 'end' => $end, - )); - - $this->response->json($events); - } - - /** - * Get tasks to display on the calendar (user view) - * - * @access public - */ - public function userEvents() - { - $user_id = $this->request->getIntegerParam('user_id'); - $start = $this->request->getStringParam('start'); - $end = $this->request->getStringParam('end'); - $queryBuilder = $this->taskQuery - ->withFilter(new TaskAssigneeFilter($user_id)) - ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)); - - $events = $this->helper->calendar->getTaskDateDueEvents(clone($queryBuilder), $start, $end); - $events = array_merge($events, $this->helper->calendar->getTaskEvents(clone($queryBuilder), $start, $end)); - - if ($this->configModel->get('calendar_user_subtasks_time_tracking') == 1) { - $events = array_merge($events, $this->helper->calendar->getSubtaskTimeTrackingEvents($user_id, $start, $end)); - } - - $events = $this->hook->merge('controller:calendar:user:events', $events, array( - 'user_id' => $user_id, - 'start' => $start, - 'end' => $end, - )); - - $this->response->json($events); - } - - /** - * Update task due date - * - * @access public - */ - public function save() - { - if ($this->request->isAjax() && $this->request->isPost()) { - $values = $this->request->getJson(); - - $this->taskModificationModel->update(array( - 'id' => $values['task_id'], - 'date_due' => substr($values['date_due'], 0, 10), - )); - } - } -} diff --git a/app/Controller/CategoryController.php b/app/Controller/CategoryController.php index 69bbad5a..e3f2406b 100644 --- a/app/Controller/CategoryController.php +++ b/app/Controller/CategoryController.php @@ -13,24 +13,6 @@ use Kanboard\Core\Controller\PageNotFoundException; class CategoryController extends BaseController { /** - * Get the category (common method between actions) - * - * @access private - * @return array - * @throws PageNotFoundException - */ - private function getCategory() - { - $category = $this->categoryModel->getById($this->request->getIntegerParam('category_id')); - - if (empty($category)) { - throw new PageNotFoundException(); - } - - return $category; - } - - /** * List of categories for a given project * * @access public @@ -72,8 +54,9 @@ class CategoryController extends BaseController public function save() { $project = $this->getProject(); - $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + list($valid, $errors) = $this->categoryValidator->validateCreation($values); if ($valid) { @@ -100,7 +83,7 @@ class CategoryController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $category = $this->getCategory(); + $category = $this->getCategory($project); $this->response->html($this->template->render('category/edit', array( 'values' => empty($values) ? $category : $values, @@ -117,8 +100,12 @@ class CategoryController extends BaseController public function update() { $project = $this->getProject(); + $category = $this->getCategory($project); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['id'] = $category['id']; + list($valid, $errors) = $this->categoryValidator->validateModification($values); if ($valid) { @@ -141,7 +128,7 @@ class CategoryController extends BaseController public function confirm() { $project = $this->getProject(); - $category = $this->getCategory(); + $category = $this->getCategory($project); $this->response->html($this->helper->layout->project('category/remove', array( 'project' => $project, @@ -158,7 +145,7 @@ class CategoryController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $category = $this->getCategory(); + $category = $this->getCategory($project); if ($this->categoryModel->remove($category['id'])) { $this->flash->success(t('Category removed successfully.')); diff --git a/app/Controller/ColumnController.php b/app/Controller/ColumnController.php index 7047d30e..8e4712d9 100644 --- a/app/Controller/ColumnController.php +++ b/app/Controller/ColumnController.php @@ -61,6 +61,7 @@ class ColumnController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues() + array('hide_in_dashboard' => 0); + $values['project_id'] = $project['id']; list($valid, $errors) = $this->columnValidator->validateCreation($values); @@ -95,7 +96,7 @@ class ColumnController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $column = $this->columnModel->getById($this->request->getIntegerParam('column_id')); + $column = $this->getColumn($project); $this->response->html($this->helper->layout->project('column/edit', array( 'errors' => $errors, @@ -113,7 +114,11 @@ class ColumnController extends BaseController public function update() { $project = $this->getProject(); + $column = $this->getColumn($project); + $values = $this->request->getValues() + array('hide_in_dashboard' => 0); + $values['project_id'] = $project['id']; + $values['id'] = $column['id']; list($valid, $errors) = $this->columnValidator->validateModification($values); @@ -164,9 +169,10 @@ class ColumnController extends BaseController public function confirm() { $project = $this->getProject(); + $column = $this->getColumn($project); $this->response->html($this->helper->layout->project('column/remove', array( - 'column' => $this->columnModel->getById($this->request->getIntegerParam('column_id')), + 'column' => $column, 'project' => $project, ))); } @@ -178,11 +184,11 @@ class ColumnController extends BaseController */ public function remove() { - $project = $this->getProject(); $this->checkCSRFParam(); - $column_id = $this->request->getIntegerParam('column_id'); + $project = $this->getProject(); + $column = $this->getColumn($project); - if ($this->columnModel->remove($column_id)) { + if ($this->columnModel->remove($column['id'])) { $this->flash->success(t('Column removed successfully.')); } else { $this->flash->failure(t('Unable to remove this column.')); diff --git a/app/Controller/ColumnMoveRestrictionController.php b/app/Controller/ColumnMoveRestrictionController.php index b12f6b77..9a75bf75 100644 --- a/app/Controller/ColumnMoveRestrictionController.php +++ b/app/Controller/ColumnMoveRestrictionController.php @@ -49,7 +49,8 @@ class ColumnMoveRestrictionController extends BaseController $project['id'], $values['role_id'], $values['src_column_id'], - $values['dst_column_id'] + $values['dst_column_id'], + isset($values['only_assigned']) && $values['only_assigned'] == 1 ); if ($restriction_id !== false) { diff --git a/app/Controller/CommentController.php b/app/Controller/CommentController.php index 9a89103e..a29491a3 100644 --- a/app/Controller/CommentController.php +++ b/app/Controller/CommentController.php @@ -14,29 +14,6 @@ use Kanboard\Core\Controller\PageNotFoundException; class CommentController extends BaseController { /** - * Get the current comment - * - * @access protected - * @return array - * @throws PageNotFoundException - * @throws AccessForbiddenException - */ - protected function getComment() - { - $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id')); - - if (empty($comment)) { - throw new PageNotFoundException(); - } - - if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) { - throw new AccessForbiddenException(); - } - - return $comment; - } - - /** * Add comment form * * @access public @@ -49,14 +26,6 @@ class CommentController extends BaseController { $project = $this->getProject(); $task = $this->getTask(); - - if (empty($values)) { - $values = array( - 'user_id' => $this->userSession->getId(), - 'task_id' => $task['id'], - ); - } - $values['project_id'] = $task['project_id']; $this->response->html($this->helper->layout->task('comment/create', array( @@ -106,7 +75,7 @@ class CommentController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $comment = $this->getComment(); + $comment = $this->getComment($task); if (empty($values)) { $values = $comment; @@ -130,9 +99,13 @@ class CommentController extends BaseController public function update() { $task = $this->getTask(); - $this->getComment(); + $comment = $this->getComment($task); $values = $this->request->getValues(); + $values['id'] = $comment['id']; + $values['task_id'] = $task['id']; + $values['user_id'] = $comment['user_id']; + list($valid, $errors) = $this->commentValidator->validateModification($values); if ($valid) { @@ -157,7 +130,7 @@ class CommentController extends BaseController public function confirm() { $task = $this->getTask(); - $comment = $this->getComment(); + $comment = $this->getComment($task); $this->response->html($this->template->render('comment/remove', array( 'comment' => $comment, @@ -175,7 +148,7 @@ class CommentController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); - $comment = $this->getComment(); + $comment = $this->getComment($task); if ($this->commentModel->remove($comment['id'])) { $this->flash->success(t('Comment removed successfully.')); diff --git a/app/Controller/CommentMailController.php b/app/Controller/CommentMailController.php index e30d6a55..144c9a13 100644 --- a/app/Controller/CommentMailController.php +++ b/app/Controller/CommentMailController.php @@ -20,6 +20,7 @@ class CommentMailController extends BaseController 'errors' => $errors, 'task' => $task, 'project' => $project, + 'members' => $this->projectPermissionModel->getMembersWithEmail($project['id']), ))); } diff --git a/app/Controller/ConfigController.php b/app/Controller/ConfigController.php index 6b85d1f9..2ea04b35 100644 --- a/app/Controller/ConfigController.php +++ b/app/Controller/ConfigController.php @@ -46,12 +46,6 @@ class ConfigController extends BaseController 'disable_private_project' => 0, ); break; - case 'integrations': - $values += array('integration_gravatar' => 0); - break; - case 'calendar': - $values += array('calendar_user_subtasks_time_tracking' => 0); - break; } if ($this->configModel->save($values)) { @@ -128,18 +122,6 @@ class ConfigController extends BaseController } /** - * Display the calendar settings page - * - * @access public - */ - public function calendar() - { - $this->response->html($this->helper->layout->config('config/calendar', array( - 'title' => t('Settings').' > '.t('Calendar settings'), - ))); - } - - /** * Display the integration settings page * * @access public diff --git a/app/Controller/CustomFilterController.php b/app/Controller/CustomFilterController.php index dfe1ffc4..1bf1617e 100644 --- a/app/Controller/CustomFilterController.php +++ b/app/Controller/CustomFilterController.php @@ -59,6 +59,7 @@ class CustomFilterController extends BaseController $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; $values['user_id'] = $this->userSession->getId(); list($valid, $errors) = $this->customFilterValidator->validateCreation($values); @@ -84,7 +85,7 @@ class CustomFilterController extends BaseController public function confirm() { $project = $this->getProject(); - $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + $filter = $this->getCustomFilter($project); $this->response->html($this->helper->layout->project('custom_filter/remove', array( 'project' => $project, @@ -102,7 +103,7 @@ class CustomFilterController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + $filter = $this->getCustomFilter($project); $this->checkPermission($project, $filter); @@ -153,6 +154,8 @@ class CustomFilterController extends BaseController $this->checkPermission($project, $filter); $values = $this->request->getValues(); + $values['id'] = $filter['id']; + $values['project_id'] = $project['id']; if (! isset($values['is_shared'])) { $values += array('is_shared' => 0); diff --git a/app/Controller/DashboardController.php b/app/Controller/DashboardController.php index 7fdc53ff..6758381e 100644 --- a/app/Controller/DashboardController.php +++ b/app/Controller/DashboardController.php @@ -19,12 +19,11 @@ class DashboardController extends BaseController { $user = $this->getUser(); - $this->response->html($this->helper->layout->dashboard('dashboard/show', array( - 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)), - 'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', 10), - 'task_paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'show', 10), - 'subtask_paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'show', 10), - 'user' => $user, + $this->response->html($this->helper->layout->dashboard('dashboard/overview', array( + 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)), + 'user' => $user, + 'overview_paginator' => $this->dashboardPagination->getOverview($user['id']), + 'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', 10), ))); } @@ -55,8 +54,9 @@ class DashboardController extends BaseController $this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array( 'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)), - 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'subtasks', 50), + 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id']), 'user' => $user, + 'nb_subtasks' => $this->subtaskModel->countByAssigneeAndTaskStatus($user['id']), ))); } diff --git a/app/Controller/ICalendarController.php b/app/Controller/ICalendarController.php index 4fe8b78a..38475a32 100644 --- a/app/Controller/ICalendarController.php +++ b/app/Controller/ICalendarController.php @@ -5,6 +5,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; use Kanboard\Core\Filter\QueryBuilder; use Kanboard\Filter\TaskAssigneeFilter; +use Kanboard\Filter\TaskDueDateRangeFilter; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskStatusFilter; use Kanboard\Model\TaskModel; @@ -18,81 +19,97 @@ use Eluceo\iCal\Component\Calendar as iCalendar; */ class ICalendarController extends BaseController { - /** - * Get user iCalendar - * - * @access public - */ public function user() { $token = $this->request->getStringParam('token'); $user = $this->userModel->getByToken($token); - // Token verification if (empty($user)) { throw AccessForbiddenException::getInstance()->withoutLayout(); } - // Common filter - $queryBuilder = new QueryBuilder(); - $queryBuilder - ->withQuery($this->taskFinderModel->getICalQuery()) - ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) - ->withFilter(new TaskAssigneeFilter($user['id'])); + $startRange = strtotime('-2 months'); + $endRange = strtotime('+6 months'); + + $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started'); - // Calendar properties $calendar = new iCalendar('Kanboard'); $calendar->setName($user['name'] ?: $user['username']); $calendar->setDescription($user['name'] ?: $user['username']); $calendar->setPublishedTTL('PT1H'); - $this->renderCalendar($queryBuilder, $calendar); + $queryDueDateOnly = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange))) + ->withFilter(new TaskAssigneeFilter($user['id'])) + ->getQuery(); + + $queryStartAndDueDate = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskAssigneeFilter($user['id'])) + ->getQuery() + ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due')); + + $this->response->ical($this->taskICalFormatter + ->setCalendar($calendar) + ->addTasksWithDueDateOnly($queryDueDateOnly) + ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due') + ->format()); } - /** - * Get project iCalendar - * - * @access public - */ public function project() { $token = $this->request->getStringParam('token'); $project = $this->projectModel->getByToken($token); - // Token verification if (empty($project)) { throw AccessForbiddenException::getInstance()->withoutLayout(); } - // Common filter - $queryBuilder = new QueryBuilder(); - $queryBuilder - ->withQuery($this->taskFinderModel->getICalQuery()) - ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) - ->withFilter(new TaskProjectFilter($project['id'])); + $startRange = strtotime('-2 months'); + $endRange = strtotime('+6 months'); + + $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started'); - // Calendar properties $calendar = new iCalendar('Kanboard'); $calendar->setName($project['name']); $calendar->setDescription($project['name']); $calendar->setPublishedTTL('PT1H'); - $this->renderCalendar($queryBuilder, $calendar); + $queryDueDateOnly = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskProjectFilter($project['id'])) + ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange))) + ->getQuery(); + + $queryStartAndDueDate = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskProjectFilter($project['id'])) + ->getQuery() + ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due')); + + $this->response->ical($this->taskICalFormatter + ->setCalendar($calendar) + ->addTasksWithDueDateOnly($queryDueDateOnly) + ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due') + ->format()); } - /** - * Common method to render iCal events - * - * @access private - * @param QueryBuilder $queryBuilder - * @param iCalendar $calendar - */ - private function renderCalendar(QueryBuilder $queryBuilder, iCalendar $calendar) + protected function getConditionForTasksWithStartAndDueDate($start_time, $end_time, $start_column, $end_column) { - $start = $this->request->getStringParam('start', strtotime('-2 month')); - $end = $this->request->getStringParam('end', strtotime('+6 months')); + $start_column = $this->db->escapeIdentifier($start_column); + $end_column = $this->db->escapeIdentifier($end_column); + + $conditions = array( + "($start_column >= '$start_time' AND $start_column <= '$end_time')", + "($start_column <= '$start_time' AND $end_column >= '$start_time')", + "($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))", + ); - $this->helper->ical->addTaskDateDueEvents($queryBuilder, $calendar, $start, $end); - $this->response->ical($this->taskICalFormatter->setCalendar($calendar)->format()); + return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')'; } } diff --git a/app/Controller/PredefinedTaskDescriptionController.php b/app/Controller/PredefinedTaskDescriptionController.php new file mode 100644 index 00000000..1b7eca12 --- /dev/null +++ b/app/Controller/PredefinedTaskDescriptionController.php @@ -0,0 +1,116 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\PageNotFoundException; + +/** + * Predefined Task Description Controller + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class PredefinedTaskDescriptionController extends BaseController +{ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->template->render('predefined_task_description/create', array( + 'values' => $values, + 'errors' => $errors, + 'project' => $project, + ))); + } + + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values); + + if ($valid) { + if ($this->predefinedTaskDescriptionModel->create($project['id'], $values['title'], $values['description']) !== false) { + $this->flash->success(t('Template created successfully.')); + } else { + $this->flash->failure(t('Unable to create this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->create($values, $errors); + } + } + + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id')); + + $this->response->html($this->template->render('predefined_task_description/edit', array( + 'values' => empty($values) ? $template : $values, + 'template' => $template, + 'errors' => $errors, + 'project' => $project, + ))); + } + + public function update() + { + $project = $this->getProject(); + $template = $this->getTemplate($project); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values); + + if ($valid) { + if ($this->predefinedTaskDescriptionModel->update($project['id'], $template['id'], $values['title'], $values['description']) !== false) { + $this->flash->success(t('Template updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->edit($values, $errors); + } + } + + public function confirm() + { + $project = $this->getProject(); + $template = $this->getTemplate($project); + + $this->response->html($this->template->render('predefined_task_description/remove', array( + 'template' => $template, + 'project' => $project, + ))); + } + + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $template = $this->getTemplate($project); + + if ($this->predefinedTaskDescriptionModel->remove($project['id'], $template['id'])) { + $this->flash->success(t('Template removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } + + protected function getTemplate(array $project) + { + $template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id')); + + if (empty($template)) { + throw new PageNotFoundException(); + } + + return $template; + } +}
\ No newline at end of file diff --git a/app/Controller/ProjectEditController.php b/app/Controller/ProjectEditController.php index ae39fdf3..dd534508 100644 --- a/app/Controller/ProjectEditController.php +++ b/app/Controller/ProjectEditController.php @@ -65,6 +65,8 @@ class ProjectEditController extends BaseController */ private function prepareValues(array $project, array $values) { + $values['id'] = $project['id']; + if (isset($values['is_private'])) { if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { unset($values['is_private']); diff --git a/app/Controller/ProjectGanttController.php b/app/Controller/ProjectGanttController.php deleted file mode 100644 index 8239005e..00000000 --- a/app/Controller/ProjectGanttController.php +++ /dev/null @@ -1,56 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -use Kanboard\Filter\ProjectIdsFilter; -use Kanboard\Filter\ProjectStatusFilter; -use Kanboard\Filter\ProjectTypeFilter; -use Kanboard\Model\ProjectModel; - -/** - * Projects Gantt Controller - * - * @package Kanboard\Controller - * @author Frederic Guillot - */ -class ProjectGanttController extends BaseController -{ - /** - * Show Gantt chart for all projects - */ - public function show() - { - $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); - $filter = $this->projectQuery - ->withFilter(new ProjectTypeFilter(ProjectModel::TYPE_TEAM)) - ->withFilter(new ProjectStatusFilter(ProjectModel::ACTIVE)) - ->withFilter(new ProjectIdsFilter($project_ids)); - - $filter->getQuery()->asc(ProjectModel::TABLE.'.start_date'); - - $this->response->html($this->helper->layout->app('project_gantt/show', array( - 'projects' => $filter->format($this->projectGanttFormatter), - 'title' => t('Gantt chart for all projects'), - ))); - } - - /** - * Save new project start date and end date - */ - public function save() - { - $values = $this->request->getJson(); - - $result = $this->projectModel->update(array( - 'id' => $values['id'], - 'start_date' => $this->dateParser->getIsoDate(strtotime($values['start'])), - 'end_date' => $this->dateParser->getIsoDate(strtotime($values['end'])), - )); - - if (! $result) { - $this->response->json(array('message' => 'Unable to save project'), 400); - } else { - $this->response->json(array('message' => 'OK'), 201); - } - } -} diff --git a/app/Controller/ProjectPermissionController.php b/app/Controller/ProjectPermissionController.php index 56777b25..3fb6c090 100644 --- a/app/Controller/ProjectPermissionController.php +++ b/app/Controller/ProjectPermissionController.php @@ -60,25 +60,6 @@ class ProjectPermissionController extends BaseController } /** - * Allow everybody - * - * @access public - */ - public function allowEverybody() - { - $project = $this->getProject(); - $values = $this->request->getValues() + array('is_everybody_allowed' => 0); - - if ($this->projectModel->update($values)) { - $this->flash->success(t('Project updated successfully.')); - } else { - $this->flash->failure(t('Unable to update this project.')); - } - - $this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id']))); - } - - /** * Add user to the project * * @access public @@ -88,6 +69,10 @@ class ProjectPermissionController extends BaseController $project = $this->getProject(); $values = $this->request->getValues(); + if (empty($values['user_id']) && ! empty($values['external_id']) && ! empty($values['external_id_column'])) { + $values['user_id'] = $this->userModel->getOrCreateExternalUserId($values['username'], $values['name'], $values['external_id_column'], $values['external_id']); + } + if (empty($values['user_id'])) { $this->flash->failure(t('User not found.')); } elseif ($this->projectUserRoleModel->addUser($values['project_id'], $values['user_id'], $values['role'])) { diff --git a/app/Controller/ProjectPredefinedContentController.php b/app/Controller/ProjectPredefinedContentController.php new file mode 100644 index 00000000..709d6b79 --- /dev/null +++ b/app/Controller/ProjectPredefinedContentController.php @@ -0,0 +1,50 @@ +<?php + +namespace Kanboard\Controller; + +/** + * Project Predefined Content Controller + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ProjectPredefinedContentController extends BaseController +{ + public function show(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->project('project_predefined_content/show', array( + 'values' => empty($values) ? $project : $values, + 'errors' => $errors, + 'project' => $project, + 'predefined_task_descriptions' => $this->predefinedTaskDescriptionModel->getAll($project['id']), + 'title' => t('Predefined Contents'), + ))); + } + + public function update() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $values = array( + 'id' => $project['id'], + 'name' => $project['name'], + 'predefined_email_subjects' => isset($values['predefined_email_subjects']) ? $values['predefined_email_subjects'] : '', + ); + + list($valid, $errors) = $this->projectValidator->validateModification($values); + + if ($valid) { + if ($this->projectModel->update($values)) { + $this->flash->success(t('Project updated successfully.')); + return $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + } + + return $this->show($values, $errors); + } +} diff --git a/app/Controller/ProjectStatusController.php b/app/Controller/ProjectStatusController.php index 5bc445be..4fdbb6ea 100644 --- a/app/Controller/ProjectStatusController.php +++ b/app/Controller/ProjectStatusController.php @@ -95,6 +95,6 @@ class ProjectStatusController extends BaseController $this->flash->failure(t('Unable to remove this project.')); } - $this->response->redirect($this->helper->url->to('ProjectListController', 'show'), true); + $this->response->redirect($this->helper->url->to('ProjectListController', 'show')); } } diff --git a/app/Controller/ProjectTagController.php b/app/Controller/ProjectTagController.php index d225f0ca..c45e71e1 100644 --- a/app/Controller/ProjectTagController.php +++ b/app/Controller/ProjectTagController.php @@ -2,8 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Core\Controller\AccessForbiddenException; - /** * Class ProjectTagController * @@ -27,10 +25,6 @@ class ProjectTagController extends BaseController { $project = $this->getProject(); - if (empty($values)) { - $values['project_id'] = $project['id']; - } - $this->response->html($this->template->render('project_tag/create', array( 'project' => $project, 'values' => $values, @@ -42,6 +36,8 @@ class ProjectTagController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + list($valid, $errors) = $this->tagValidator->validateCreation($values); if ($valid) { @@ -60,8 +56,7 @@ class ProjectTagController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); + $tag = $this->getProjectTag($project); if (empty($values)) { $values = $tag; @@ -78,14 +73,12 @@ class ProjectTagController extends BaseController public function update() { $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); + $tag = $this->getProjectTag($project); $values = $this->request->getValues(); - list($valid, $errors) = $this->tagValidator->validateModification($values); + $values['project_id'] = $project['id']; + $values['id'] = $tag['id']; - if ($tag['project_id'] != $project['id']) { - throw new AccessForbiddenException(); - } + list($valid, $errors) = $this->tagValidator->validateModification($values); if ($valid) { if ($this->tagModel->update($values['id'], $values['name'])) { @@ -103,8 +96,7 @@ class ProjectTagController extends BaseController public function confirm() { $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); + $tag = $this->getProjectTag($project); $this->response->html($this->template->render('project_tag/remove', array( 'tag' => $tag, @@ -116,14 +108,9 @@ class ProjectTagController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $tag_id = $this->request->getIntegerParam('tag_id'); - $tag = $this->tagModel->getById($tag_id); - - if ($tag['project_id'] != $project['id']) { - throw new AccessForbiddenException(); - } + $tag = $this->getProjectTag($project); - if ($this->tagModel->remove($tag_id)) { + if ($this->tagModel->remove($tag['id'])) { $this->flash->success(t('Tag removed successfully.')); } else { $this->flash->failure(t('Unable to remove this tag.')); diff --git a/app/Controller/SubtaskController.php b/app/Controller/SubtaskController.php index 5fa55f6b..d5db9b2e 100644 --- a/app/Controller/SubtaskController.php +++ b/app/Controller/SubtaskController.php @@ -66,29 +66,51 @@ class SubtaskController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $subtasks = explode("\r\n", isset($values['title']) ? $values['title'] : ''); + $subtasksAdded = 0; - list($valid, $errors) = $this->subtaskValidator->validateCreation($values); + foreach ($subtasks as $subtask) { + $subtask = trim($subtask); - if ($valid) { - if ($this->subtaskModel->create($values) !== false) { - $this->flash->success(t('Sub-task added successfully.')); - } else { - $this->flash->failure(t('Unable to create your sub-task.')); - } + if (! empty($subtask)) { + $subtaskValues = $values; + $subtaskValues['title'] = $subtask; + + list($valid, $errors) = $this->subtaskValidator->validateCreation($subtaskValues); + + if (! $valid) { + $this->create($values, $errors); + return false; + } - if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { - return $this->create(array( - 'project_id' => $task['project_id'], - 'task_id' => $task['id'], - 'user_id' => $values['user_id'], - 'another_subtask' => 1 - )); + if (! $this->subtaskModel->create($subtaskValues)) { + $this->flash->failure(t('Unable to create your sub-task.')); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true); + return false; + } + + $subtasksAdded++; } + } - return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true); + if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { + return $this->create(array( + 'project_id' => $task['project_id'], + 'task_id' => $task['id'], + 'user_id' => $values['user_id'], + 'another_subtask' => 1, + 'subtasks_added' => $subtasksAdded, + )); + } else if ($subtasksAdded > 0) { + if ($subtasksAdded === 1) { + $this->flash->success(t('Subtask added successfully.')); + } else { + $this->flash->success(t('%d subtasks added successfully.', $subtasksAdded)); + } } - return $this->create($values, $errors); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true); } /** @@ -103,7 +125,7 @@ class SubtaskController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask/edit', array( 'values' => empty($values) ? $subtask : $values, @@ -123,9 +145,12 @@ class SubtaskController extends BaseController public function update() { $task = $this->getTask(); - $this->getSubtask(); + $subtask = $this->getSubtask($task); $values = $this->request->getValues(); + $values['id'] = $subtask['id']; + $values['task_id'] = $task['id']; + list($valid, $errors) = $this->subtaskValidator->validateModification($values); if ($valid) { @@ -149,7 +174,7 @@ class SubtaskController extends BaseController public function confirm() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask/remove', array( 'subtask' => $subtask, @@ -166,7 +191,7 @@ class SubtaskController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); if ($this->subtaskModel->remove($subtask['id'])) { $this->flash->success(t('Sub-task removed successfully.')); diff --git a/app/Controller/SubtaskConverterController.php b/app/Controller/SubtaskConverterController.php index 404c50d0..dafbb7e0 100644 --- a/app/Controller/SubtaskConverterController.php +++ b/app/Controller/SubtaskConverterController.php @@ -13,7 +13,7 @@ class SubtaskConverterController extends BaseController public function show() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask_converter/show', array( 'subtask' => $subtask, @@ -24,7 +24,8 @@ class SubtaskConverterController extends BaseController public function save() { $project = $this->getProject(); - $subtask = $this->getSubtask(); + $task = $this->getTask(); + $subtask = $this->getSubtask($task); $task_id = $this->subtaskTaskConversionModel->convertToTask($project['id'], $subtask['id']); diff --git a/app/Controller/SubtaskRestrictionController.php b/app/Controller/SubtaskRestrictionController.php index 0e207674..315b023d 100644 --- a/app/Controller/SubtaskRestrictionController.php +++ b/app/Controller/SubtaskRestrictionController.php @@ -20,7 +20,7 @@ class SubtaskRestrictionController extends BaseController public function show() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $this->response->html($this->template->render('subtask_restriction/show', array( 'status_list' => array( @@ -41,21 +41,19 @@ class SubtaskRestrictionController extends BaseController public function save() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); $values = $this->request->getValues(); // Change status of the previous "in progress" subtask $this->subtaskModel->update(array( 'id' => $values['id'], 'status' => $values['status'], - 'task_id' => $task['id'], )); // Set the current subtask to "in progress" $this->subtaskModel->update(array( 'id' => $subtask['id'], 'status' => SubtaskModel::STATUS_INPROGRESS, - 'task_id' => $task['id'], )); $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); diff --git a/app/Controller/SubtaskStatusController.php b/app/Controller/SubtaskStatusController.php index 72feb685..c912848e 100644 --- a/app/Controller/SubtaskStatusController.php +++ b/app/Controller/SubtaskStatusController.php @@ -18,12 +18,21 @@ class SubtaskStatusController extends BaseController public function change() { $task = $this->getTask(); - $subtask = $this->getSubtask(); + $subtask = $this->getSubtask($task); + $fragment = $this->request->getStringParam('fragment'); $status = $this->subtaskStatusModel->toggleStatus($subtask['id']); $subtask['status'] = $status; - $this->response->html($this->helper->subtask->renderToggleStatus($task, $subtask)); + if ($fragment === 'table') { + $html = $this->renderTable($task); + } elseif ($fragment === 'rows') { + $html = $this->renderRows($task); + } else { + $html = $this->helper->subtask->renderToggleStatus($task, $subtask); + } + + $this->response->html($html); } /** @@ -34,19 +43,58 @@ class SubtaskStatusController extends BaseController public function timer() { $task = $this->getTask(); - $subtaskId = $this->request->getIntegerParam('subtask_id'); + $subtask = $this->getSubtask($task); $timer = $this->request->getStringParam('timer'); if ($timer === 'start') { - $this->subtaskTimeTrackingModel->logStartTime($subtaskId, $this->userSession->getId()); + $this->subtaskTimeTrackingModel->logStartTime($subtask['id'], $this->userSession->getId()); } elseif ($timer === 'stop') { - $this->subtaskTimeTrackingModel->logEndTime($subtaskId, $this->userSession->getId()); + $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $this->userSession->getId()); $this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']); } $this->response->html($this->template->render('subtask/timer', array( 'task' => $task, - 'subtask' => $this->subtaskModel->getByIdWithDetails($subtaskId), + 'subtask' => $this->subtaskModel->getByIdWithDetails($subtask['id']), ))); } + + /** + * Render table + * + * @access protected + * @param array $task + * @return string + */ + protected function renderTable(array $task) + { + return $this->template->render('subtask/table', array( + 'task' => $task, + 'subtasks' => $this->subtaskModel->getAll($task['id']), + 'editable' => true, + )); + } + + /** + * Render task list rows + * + * @access protected + * @param array $task + * @return string + */ + protected function renderRows(array $task) + { + $userId = $this->request->getIntegerParam('user_id'); + + if ($userId > 0) { + $task['subtasks'] = $this->subtaskModel->getAllByTaskIdsAndAssignee(array($task['id']), $userId); + } else { + $task['subtasks'] = $this->subtaskModel->getAll($task['id']); + } + + return $this->template->render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $userId, + )); + } } diff --git a/app/Controller/SwimlaneController.php b/app/Controller/SwimlaneController.php index 0d81d83c..e6368b24 100644 --- a/app/Controller/SwimlaneController.php +++ b/app/Controller/SwimlaneController.php @@ -3,8 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; -use Kanboard\Core\Controller\PageNotFoundException; -use Kanboard\Model\SwimlaneModel; /** * Swimlanes Controller @@ -15,24 +13,6 @@ use Kanboard\Model\SwimlaneModel; class SwimlaneController extends BaseController { /** - * Get the swimlane (common method between actions) - * - * @access private - * @return array - * @throws PageNotFoundException - */ - private function getSwimlane() - { - $swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id')); - - if (empty($swimlane)) { - throw new PageNotFoundException(); - } - - return $swimlane; - } - - /** * List of swimlanes for a given project * * @access public @@ -78,6 +58,8 @@ class SwimlaneController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + list($valid, $errors) = $this->swimlaneValidator->validateCreation($values); if ($valid) { @@ -104,7 +86,7 @@ class SwimlaneController extends BaseController public function edit(array $values = array(), array $errors = array()) { $project = $this->getProject(); - $swimlane = $this->getSwimlane(); + $swimlane = $this->getSwimlane($project); $this->response->html($this->helper->layout->project('swimlane/edit', array( 'values' => empty($values) ? $swimlane : $values, @@ -121,8 +103,11 @@ class SwimlaneController extends BaseController public function update() { $project = $this->getProject(); - + $swimlane = $this->getSwimlane($project); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['id'] = $swimlane['id']; + list($valid, $errors) = $this->swimlaneValidator->validateModification($values); if ($valid) { @@ -145,7 +130,7 @@ class SwimlaneController extends BaseController public function confirm() { $project = $this->getProject(); - $swimlane = $this->getSwimlane(); + $swimlane = $this->getSwimlane($project); $this->response->html($this->helper->layout->project('swimlane/remove', array( 'project' => $project, @@ -162,9 +147,9 @@ class SwimlaneController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $swimlane_id = $this->request->getIntegerParam('swimlane_id'); + $swimlane = $this->getSwimlane($project); - if ($this->swimlaneModel->remove($project['id'], $swimlane_id)) { + if ($this->swimlaneModel->remove($project['id'], $swimlane['id'])) { $this->flash->success(t('Swimlane removed successfully.')); } else { $this->flash->failure(t('Unable to remove this swimlane.')); @@ -182,9 +167,9 @@ class SwimlaneController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $swimlane_id = $this->request->getIntegerParam('swimlane_id'); + $swimlane = $this->getSwimlane($project); - if ($this->swimlaneModel->disable($project['id'], $swimlane_id)) { + if ($this->swimlaneModel->disable($project['id'], $swimlane['id'])) { $this->flash->success(t('Swimlane updated successfully.')); } else { $this->flash->failure(t('Unable to update this swimlane.')); @@ -202,9 +187,9 @@ class SwimlaneController extends BaseController { $this->checkCSRFParam(); $project = $this->getProject(); - $swimlane_id = $this->request->getIntegerParam('swimlane_id'); + $swimlane = $this->getSwimlane($project); - if ($this->swimlaneModel->enable($project['id'], $swimlane_id)) { + if ($this->swimlaneModel->enable($project['id'], $swimlane['id'])) { $this->flash->success(t('Swimlane updated successfully.')); } else { $this->flash->failure(t('Unable to update this swimlane.')); diff --git a/app/Controller/TaskBulkController.php b/app/Controller/TaskBulkController.php index 4345a68f..0f49fb76 100644 --- a/app/Controller/TaskBulkController.php +++ b/app/Controller/TaskBulkController.php @@ -35,6 +35,7 @@ class TaskBulkController extends BaseController 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private'] == 1), 'colors_list' => $this->colorModel->getList(), 'categories_list' => $this->categoryModel->getList($project['id']), + 'task_description_templates' => $this->predefinedTaskDescriptionModel->getList($project['id']), ))); } @@ -85,8 +86,18 @@ class TaskBulkController extends BaseController 'owner_id' => empty($values['owner_id']) ? 0 : $values['owner_id'], 'color_id' => $values['color_id'], 'project_id' => $project['id'], + 'description' => $this->getTaskDescription($project, $values), )); } } } + + protected function getTaskDescription(array $project, array $values) + { + if (empty($values['task_description_template_id'])) { + return ''; + } + + return $this->predefinedTaskDescriptionModel->getDescriptionById($project['id'], $values['task_description_template_id']); + } } diff --git a/app/Controller/TaskCreationController.php b/app/Controller/TaskCreationController.php index 0d808c54..d050a60d 100644 --- a/app/Controller/TaskCreationController.php +++ b/app/Controller/TaskCreationController.php @@ -49,6 +49,7 @@ class TaskCreationController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); + $values['project_id'] = $project['id']; list($valid, $errors) = $this->taskValidator->validateCreation($values); diff --git a/app/Controller/TaskExternalLinkController.php b/app/Controller/TaskExternalLinkController.php index df23f87b..946451fc 100644 --- a/app/Controller/TaskExternalLinkController.php +++ b/app/Controller/TaskExternalLinkController.php @@ -74,6 +74,8 @@ class TaskExternalLinkController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); if ($valid) { @@ -108,22 +110,14 @@ class TaskExternalLinkController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $link_id = $this->request->getIntegerParam('link_id'); - - if ($link_id > 0) { - $values = $this->taskExternalLinkModel->getById($link_id); - } - - if (empty($values)) { - throw new PageNotFoundException(); - } - - $provider = $this->externalLinkManager->getProvider($values['link_type']); + $link = $this->getExternalTaskLink($task); + $provider = $this->externalLinkManager->getProvider($link['link_type']); $this->response->html($this->template->render('task_external_link/edit', array( - 'values' => $values, - 'errors' => $errors, - 'task' => $task, + 'values' => empty($values) ? $link : $values, + 'errors' => $errors, + 'task' => $task, + 'link' => $link, 'dependencies' => $provider->getDependencies(), ))); } @@ -136,7 +130,12 @@ class TaskExternalLinkController extends BaseController public function update() { $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); + $values = $this->request->getValues(); + $values['id'] = $link['id']; + $values['task_id'] = $link['task_id']; + list($valid, $errors) = $this->externalLinkValidator->validateModification($values); if ($valid && $this->taskExternalLinkModel->update($values)) { @@ -155,12 +154,7 @@ class TaskExternalLinkController extends BaseController public function confirm() { $task = $this->getTask(); - $link_id = $this->request->getIntegerParam('link_id'); - $link = $this->taskExternalLinkModel->getById($link_id); - - if (empty($link)) { - throw new PageNotFoundException(); - } + $link = $this->getExternalTaskLink($task); $this->response->html($this->template->render('task_external_link/remove', array( 'link' => $link, @@ -177,8 +171,9 @@ class TaskExternalLinkController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); - if ($this->taskExternalLinkModel->remove($this->request->getIntegerParam('link_id'))) { + if ($this->taskExternalLinkModel->remove($link['id'])) { $this->flash->success(t('Link removed successfully.')); } else { $this->flash->failure(t('Unable to remove this link.')); diff --git a/app/Controller/TaskGanttController.php b/app/Controller/TaskGanttController.php deleted file mode 100644 index b03b9d00..00000000 --- a/app/Controller/TaskGanttController.php +++ /dev/null @@ -1,61 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -use Kanboard\Filter\TaskProjectFilter; -use Kanboard\Model\TaskModel; - -/** - * Tasks Gantt Controller - * - * @package Kanboard\Controller - * @author Frederic Guillot - */ -class TaskGanttController extends BaseController -{ - /** - * Show Gantt chart for one project - */ - public function show() - { - $project = $this->getProject(); - $search = $this->helper->projectHeader->getSearchQuery($project); - $sorting = $this->request->getStringParam('sorting', 'board'); - $filter = $this->taskLexer->build($search)->withFilter(new TaskProjectFilter($project['id'])); - - if ($sorting === 'date') { - $filter->getQuery()->asc(TaskModel::TABLE.'.date_started')->asc(TaskModel::TABLE.'.date_creation'); - } else { - $filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position'); - } - - $this->response->html($this->helper->layout->app('task_gantt/show', array( - 'project' => $project, - 'title' => $project['name'], - 'description' => $this->helper->projectHeader->getDescription($project), - 'sorting' => $sorting, - 'tasks' => $filter->format($this->taskGanttFormatter), - ))); - } - - /** - * Save new task start date and due date - */ - public function save() - { - $this->getProject(); - $values = $this->request->getJson(); - - $result = $this->taskModificationModel->update(array( - 'id' => $values['id'], - 'date_started' => strtotime($values['start']), - 'date_due' => strtotime($values['end']), - )); - - if (! $result) { - $this->response->json(array('message' => 'Unable to save task'), 400); - } else { - $this->response->json(array('message' => 'OK'), 201); - } - } -} diff --git a/app/Controller/TaskImportController.php b/app/Controller/TaskImportController.php index 2e323979..d6506880 100644 --- a/app/Controller/TaskImportController.php +++ b/app/Controller/TaskImportController.php @@ -3,6 +3,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Csv; +use Kanboard\Import\TaskImport; /** * Task Import controller @@ -45,14 +46,15 @@ class TaskImportController extends BaseController if (! file_exists($filename)) { $this->show($values, array('file' => array(t('Unable to read your file')))); } else { - $this->taskImport->projectId = $project['id']; + $taskImport = new TaskImport($this->container); + $taskImport->setProjectId($project['id']); $csv = new Csv($values['delimiter'], $values['enclosure']); - $csv->setColumnMapping($this->taskImport->getColumnMapping()); - $csv->read($filename, array($this->taskImport, 'import')); + $csv->setColumnMapping($taskImport->getColumnMapping()); + $csv->read($filename, array($taskImport, 'importTask')); - if ($this->taskImport->counter > 0) { - $this->flash->success(t('%d task(s) have been imported successfully.', $this->taskImport->counter)); + if ($taskImport->getNumberOfImportedTasks() > 0) { + $this->flash->success(t('%d task(s) have been imported successfully.', $taskImport->getNumberOfImportedTasks())); } else { $this->flash->failure(t('Nothing have been imported!')); } @@ -67,7 +69,8 @@ class TaskImportController extends BaseController */ public function template() { + $taskImport = new TaskImport($this->container); $this->response->withFileDownload('tasks.csv'); - $this->response->csv(array($this->taskImport->getColumnMapping())); + $this->response->csv(array($taskImport->getColumnMapping())); } } diff --git a/app/Controller/TaskInternalLinkController.php b/app/Controller/TaskInternalLinkController.php index a140f1ff..02cc15c4 100644 --- a/app/Controller/TaskInternalLinkController.php +++ b/app/Controller/TaskInternalLinkController.php @@ -14,24 +14,6 @@ use Kanboard\Core\Controller\PageNotFoundException; class TaskInternalLinkController extends BaseController { /** - * Get the current link - * - * @access private - * @return array - * @throws PageNotFoundException - */ - private function getTaskLink() - { - $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id')); - - if (empty($link)) { - throw new PageNotFoundException(); - } - - return $link; - } - - /** * Creation form * * @access public @@ -44,6 +26,11 @@ class TaskInternalLinkController extends BaseController { $task = $this->getTask(); + if (empty($values)) { + $values['another_tasklink'] = $this->request->getIntegerParam('another_tasklink', 0); + $values = $this->hook->merge('controller:tasklink:form:default', $values, array('default_values' => $values)); + } + $this->response->html($this->template->render('task_internal_link/create', array( 'values' => $values, 'errors' => $errors, @@ -61,12 +48,23 @@ class TaskInternalLinkController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['task_id'] = $task['id']; list($valid, $errors) = $this->taskLinkValidator->validateCreation($values); if ($valid) { - if ($this->taskLinkModel->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) { + if ($this->taskLinkModel->create($values['task_id'], $values['opposite_task_id'], $values['link_id']) !== false) { $this->flash->success(t('Link added successfully.')); + + if (isset($values['another_tasklink']) && $values['another_tasklink'] == 1) { + return $this->create(array( + 'project_id' => $task['project_id'], + 'task_id' => $task['id'], + 'link_id' => $values['link_id'], + 'another_tasklink' => 1 + )); + } + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); } @@ -89,7 +87,7 @@ class TaskInternalLinkController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); - $task_link = $this->getTaskLink(); + $task_link = $this->getInternalTaskLink($task); if (empty($values)) { $opposite_task = $this->taskFinderModel->getById($task_link['opposite_task_id']); @@ -114,7 +112,11 @@ class TaskInternalLinkController extends BaseController public function update() { $task = $this->getTask(); + $task_link = $this->getInternalTaskLink($task); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $values['id'] = $task_link['id']; list($valid, $errors) = $this->taskLinkValidator->validateModification($values); @@ -138,7 +140,7 @@ class TaskInternalLinkController extends BaseController public function confirm() { $task = $this->getTask(); - $link = $this->getTaskLink(); + $link = $this->getInternalTaskLink($task); $this->response->html($this->template->render('task_internal_link/remove', array( 'link' => $link, @@ -155,8 +157,9 @@ class TaskInternalLinkController extends BaseController { $this->checkCSRFParam(); $task = $this->getTask(); + $link = $this->getInternalTaskLink($task); - if ($this->taskLinkModel->remove($this->request->getIntegerParam('link_id'))) { + if ($this->taskLinkModel->remove($link['id'])) { $this->flash->success(t('Link removed successfully.')); } else { $this->flash->failure(t('Unable to remove this link.')); diff --git a/app/Controller/TaskMailController.php b/app/Controller/TaskMailController.php index e95ddf03..feb96a68 100644 --- a/app/Controller/TaskMailController.php +++ b/app/Controller/TaskMailController.php @@ -20,6 +20,7 @@ class TaskMailController extends BaseController 'errors' => $errors, 'task' => $task, 'project' => $project, + 'members' => $this->projectPermissionModel->getMembersWithEmail($project['id']), ))); } @@ -33,6 +34,13 @@ class TaskMailController extends BaseController if ($valid) { $this->sendByEmail($values, $task); $this->flash->success(t('Task sent by email successfully.')); + + $this->commentModel->create(array( + 'comment' => t('This task was sent by email to "%s" with subject "%s".', $values['email'], $values['subject']), + 'user_id' => $this->userSession->getId(), + 'task_id' => $task['id'], + )); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true); } else { $this->create($values, $errors); diff --git a/app/Controller/TaskModificationController.php b/app/Controller/TaskModificationController.php index 520bf70e..338ed540 100644 --- a/app/Controller/TaskModificationController.php +++ b/app/Controller/TaskModificationController.php @@ -22,7 +22,11 @@ class TaskModificationController extends BaseController public function start() { $task = $this->getTask(); - $this->taskModificationModel->update(array('id' => $task['id'], 'date_started' => time())); + $values = array('id' => $task['id'], 'date_started' => time()); + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + $this->taskModificationModel->update($values); $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']))); } @@ -38,6 +42,11 @@ class TaskModificationController extends BaseController public function edit(array $values = array(), array $errors = array()) { $task = $this->getTask(); + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + $project = $this->projectModel->getById($task['project_id']); if (empty($values)) { @@ -89,6 +98,8 @@ class TaskModificationController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['id'] = $task['id']; + $values['project_id'] = $task['project_id']; list($valid, $errors) = $this->taskValidator->validateModification($values); @@ -103,6 +114,14 @@ class TaskModificationController extends BaseController protected function updateTask(array &$task, array &$values, array &$errors) { + if (isset($values['owner_id']) && $values['owner_id'] != $task['owner_id'] && !$this->helper->projectRole->canChangeAssignee($task)) { + throw new AccessForbiddenException(t('You are not allowed to change the assignee.')); + } + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + $result = $this->taskModificationModel->update($values); if ($result && ! empty($task['external_uri'])) { diff --git a/app/Controller/TaskRecurrenceController.php b/app/Controller/TaskRecurrenceController.php index c6fdfa37..7f14cb53 100644 --- a/app/Controller/TaskRecurrenceController.php +++ b/app/Controller/TaskRecurrenceController.php @@ -47,6 +47,7 @@ class TaskRecurrenceController extends BaseController { $task = $this->getTask(); $values = $this->request->getValues(); + $values['id'] = $task['id']; list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); diff --git a/app/Controller/UserAjaxController.php b/app/Controller/UserAjaxController.php index 17567a00..57596202 100644 --- a/app/Controller/UserAjaxController.php +++ b/app/Controller/UserAjaxController.php @@ -2,9 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Filter\UserNameFilter; -use Kanboard\Model\UserModel; - /** * User Ajax Controller * @@ -21,9 +18,8 @@ class UserAjaxController extends BaseController public function autocomplete() { $search = $this->request->getStringParam('term'); - $filter = $this->userQuery->withFilter(new UserNameFilter($search)); - $filter->getQuery()->asc(UserModel::TABLE.'.name')->asc(UserModel::TABLE.'.username'); - $this->response->json($filter->format($this->userAutoCompleteFormatter)); + $users = $this->userManager->find($search); + $this->response->json($this->userAutoCompleteFormatter->withUsers($users)->format()); } /** diff --git a/app/Controller/UserApiAccessController.php b/app/Controller/UserApiAccessController.php index 0f1c891e..f9628ae6 100644 --- a/app/Controller/UserApiAccessController.php +++ b/app/Controller/UserApiAccessController.php @@ -32,7 +32,7 @@ class UserApiAccessController extends BaseController 'api_access_token' => Token::getToken(), )); - $this->renderResponse(); + $this->renderResponse($user); } public function remove() @@ -45,10 +45,10 @@ class UserApiAccessController extends BaseController 'api_access_token' => null, )); - $this->renderResponse(); + $this->renderResponse($user); } - protected function renderResponse() + protected function renderResponse(array $user) { if ($this->request->isAjax()) { $this->show(); diff --git a/app/Controller/UserCredentialController.php b/app/Controller/UserCredentialController.php index 23e7edba..a8b90b7b 100644 --- a/app/Controller/UserCredentialController.php +++ b/app/Controller/UserCredentialController.php @@ -43,6 +43,14 @@ class UserCredentialController extends BaseController list($valid, $errors) = $this->userValidator->validatePasswordModification($values); + if (! $this->userSession->isAdmin()) { + $values = array( + 'id' => $this->userSession->getId(), + 'password' => isset($values['password']) ? $values['password'] : '', + 'confirmation' => isset($values['confirmation']) ? $values['confirmation'] : '', + ); + } + if ($valid) { if ($this->userModel->update($values)) { $this->flash->success(t('Password modified successfully.')); diff --git a/app/Controller/UserModificationController.php b/app/Controller/UserModificationController.php index ed145921..f4916f6f 100644 --- a/app/Controller/UserModificationController.php +++ b/app/Controller/UserModificationController.php @@ -47,9 +47,14 @@ class UserModificationController extends BaseController $values = $this->request->getValues(); if (! $this->userSession->isAdmin()) { - if (isset($values['role'])) { - unset($values['role']); - } + $values = array( + 'id' => $this->userSession->getId(), + 'username' => isset($values['username']) ? $values['username'] : '', + 'name' => isset($values['name']) ? $values['name'] : '', + 'email' => isset($values['email']) ? $values['email'] : '', + 'timezone' => isset($values['timezone']) ? $values['timezone'] : '', + 'language' => isset($values['language']) ? $values['language'] : '', + ); } list($valid, $errors) = $this->userValidator->validateModification($values); |