diff options
Diffstat (limited to 'app/Controller')
61 files changed, 1415 insertions, 476 deletions
diff --git a/app/Controller/ActionController.php b/app/Controller/ActionController.php index 097640f6..c935125a 100644 --- a/app/Controller/ActionController.php +++ b/app/Controller/ActionController.php @@ -33,6 +33,7 @@ class ActionController extends BaseController 'colors_list' => $this->colorModel->getList(), 'categories_list' => $this->categoryModel->getList($project['id']), 'links_list' => $this->linkModel->getList(0, false), + 'swimlane_list' => $this->swimlaneModel->getList($project['id']), 'title' => t('Automatic actions') ))); } diff --git a/app/Controller/ActionCreationController.php b/app/Controller/ActionCreationController.php index 9b228f28..1629e68f 100644 --- a/app/Controller/ActionCreationController.php +++ b/app/Controller/ActionCreationController.php @@ -84,6 +84,7 @@ class ActionCreationController extends BaseController 'priorities_list' => $this->projectTaskPriorityModel->getPriorities($project), 'project' => $project, 'available_actions' => $this->actionManager->getAvailableActions(), + 'swimlane_list' => $this->swimlaneModel->getList($project['id']), 'events' => $this->actionManager->getCompatibleEvents($values['action_name']), ))); } diff --git a/app/Controller/ActivityController.php b/app/Controller/ActivityController.php index 9f9841af..476e4aac 100644 --- a/app/Controller/ActivityController.php +++ b/app/Controller/ActivityController.php @@ -40,6 +40,7 @@ class ActivityController extends BaseController 'task' => $task, 'project' => $this->projectModel->getById($task['project_id']), 'events' => $this->helper->projectActivity->getTaskEvents($task['id']), + 'tags' => $this->taskTagModel->getList($task['id']), ))); } } diff --git a/app/Controller/AnalyticController.php b/app/Controller/AnalyticController.php index cf3ba034..38169fd7 100644 --- a/app/Controller/AnalyticController.php +++ b/app/Controller/AnalyticController.php @@ -31,9 +31,7 @@ class AnalyticController extends BaseController 'project' => $project, 'average' => $this->averageLeadCycleTimeAnalytic->build($project['id']), 'metrics' => $this->projectDailyStatsModel->getRawMetrics($project['id'], $from, $to), - 'date_format' => $this->configModel->get('application_date_format'), - 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()), - 'title' => t('Lead and Cycle time for "%s"', $project['name']), + 'title' => t('Lead and cycle time'), ))); } @@ -42,12 +40,12 @@ class AnalyticController extends BaseController * * @access public */ - public function compareHours() + public function timeComparison() { $project = $this->getProject(); $paginator = $this->paginator - ->setUrl('AnalyticController', 'compareHours', array('project_id' => $project['id'])) + ->setUrl('AnalyticController', 'timeComparison', array('project_id' => $project['id'])) ->setMax(30) ->setOrder(TaskModel::TABLE.'.id') ->setQuery($this->taskQuery @@ -56,11 +54,11 @@ class AnalyticController extends BaseController ) ->calculate(); - $this->response->html($this->helper->layout->analytic('analytic/compare_hours', array( + $this->response->html($this->helper->layout->analytic('analytic/time_comparison', array( 'project' => $project, 'paginator' => $paginator, 'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']), - 'title' => t('Compare hours for "%s"', $project['name']), + 'title' => t('Estimated vs actual time'), ))); } @@ -76,7 +74,7 @@ class AnalyticController extends BaseController $this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array( 'project' => $project, 'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']), - 'title' => t('Average time spent into each column for "%s"', $project['name']), + 'title' => t('Average time into each column'), ))); } @@ -85,14 +83,14 @@ class AnalyticController extends BaseController * * @access public */ - public function tasks() + public function taskDistribution() { $project = $this->getProject(); - $this->response->html($this->helper->layout->analytic('analytic/tasks', array( + $this->response->html($this->helper->layout->analytic('analytic/task_distribution', array( 'project' => $project, 'metrics' => $this->taskDistributionAnalytic->build($project['id']), - 'title' => t('Task repartition for "%s"', $project['name']), + 'title' => t('Task distribution'), ))); } @@ -101,14 +99,14 @@ class AnalyticController extends BaseController * * @access public */ - public function users() + public function userDistribution() { $project = $this->getProject(); - $this->response->html($this->helper->layout->analytic('analytic/users', array( + $this->response->html($this->helper->layout->analytic('analytic/user_distribution', array( 'project' => $project, 'metrics' => $this->userDistributionAnalytic->build($project['id']), - 'title' => t('User repartition for "%s"', $project['name']), + 'title' => t('User repartition'), ))); } @@ -119,7 +117,7 @@ class AnalyticController extends BaseController */ public function cfd() { - $this->commonAggregateMetrics('analytic/cfd', 'total', 'Cumulative flow diagram for "%s"'); + $this->commonAggregateMetrics('analytic/cfd', 'total', t('Cumulative flow diagram')); } /** @@ -129,7 +127,7 @@ class AnalyticController extends BaseController */ public function burndown() { - $this->commonAggregateMetrics('analytic/burndown', 'score', 'Burndown chart for "%s"'); + $this->commonAggregateMetrics('analytic/burndown', 'score', t('Burndown chart')); } /** @@ -155,9 +153,7 @@ class AnalyticController extends BaseController 'display_graph' => $display_graph, 'metrics' => $display_graph ? $this->projectDailyColumnStatsModel->getAggregatedMetrics($project['id'], $from, $to, $column) : array(), 'project' => $project, - 'date_format' => $this->configModel->get('application_date_format'), - 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()), - 'title' => t($title, $project['name']), + 'title' => $title, ))); } @@ -169,8 +165,8 @@ class AnalyticController extends BaseController $to = $this->request->getStringParam('to', date('Y-m-d')); if (! empty($values)) { - $from = $values['from']; - $to = $values['to']; + $from = $this->dateParser->getIsoDate($values['from']); + $to = $this->dateParser->getIsoDate($values['to']); } return array($from, $to); diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php index 45cf39a5..34b9c8cc 100644 --- a/app/Controller/AppController.php +++ b/app/Controller/AppController.php @@ -16,18 +16,19 @@ class AppController extends Base * Forbidden page * * @access public - * @param bool $withoutLayout + * @param bool $withoutLayout + * @param string $message */ - public function accessForbidden($withoutLayout = false) + public function accessForbidden($withoutLayout = false, $message = '') { if ($this->request->isAjax()) { - $this->response->json(array('message' => 'Access Forbidden'), 403); + $this->response->json(array('message' => $message ?: t('Access Forbidden')), 403); + } else { + $this->response->html($this->helper->layout->app('app/forbidden', array( + 'title' => t('Access Forbidden'), + 'no_layout' => $withoutLayout, + ))); } - - $this->response->html($this->helper->layout->app('app/forbidden', array( - 'title' => t('Access Forbidden'), - 'no_layout' => $withoutLayout, - ))); } /** diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php index 9b721f06..ecb76e9c 100644 --- a/app/Controller/BoardAjaxController.php +++ b/app/Controller/BoardAjaxController.php @@ -3,7 +3,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; -use Kanboard\Formatter\BoardFormatter; +use Kanboard\Model\UserMetadataModel; /** * Class BoardAjaxController @@ -28,10 +28,14 @@ class BoardAjaxController extends BaseController $values = $this->request->getJson(); + if (! $this->helper->projectRole->canMoveTask($project_id, $values['src_column_id'], $values['dst_column_id'])) { + throw new AccessForbiddenException(e("You don't have the permission to move this task")); + } + $result =$this->taskPositionModel->movePosition( $project_id, $values['task_id'], - $values['column_id'], + $values['dst_column_id'], $values['position'], $values['swimlane_id'] ); @@ -88,7 +92,7 @@ class BoardAjaxController extends BaseController */ public function collapse() { - $this->changeDisplayMode(true); + $this->changeDisplayMode(1); } /** @@ -98,19 +102,19 @@ class BoardAjaxController extends BaseController */ public function expand() { - $this->changeDisplayMode(false); + $this->changeDisplayMode(0); } /** * Change display mode * * @access private - * @param boolean $mode + * @param int $mode */ private function changeDisplayMode($mode) { $project_id = $this->request->getIntegerParam('project_id'); - $this->userSession->setBoardDisplayMode($project_id, $mode); + $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, $mode); if ($this->request->isAjax()) { $this->response->html($this->renderBoard($project_id)); @@ -134,7 +138,7 @@ class BoardAjaxController extends BaseController 'board_highlight_period' => $this->configModel->get('board_highlight_period'), 'swimlanes' => $this->taskLexer ->build($this->userSession->getFilters($project_id)) - ->format(BoardFormatter::getInstance($this->container)->withProjectId($project_id)) + ->format($this->boardFormatter->withProjectId($project_id)) )); } } diff --git a/app/Controller/BoardTooltipController.php b/app/Controller/BoardTooltipController.php index 134d728e..79b9b509 100644 --- a/app/Controller/BoardTooltipController.php +++ b/app/Controller/BoardTooltipController.php @@ -2,6 +2,8 @@ namespace Kanboard\Controller; +use Kanboard\Model\UserMetadataModel; + /** * Board Tooltip * @@ -75,10 +77,11 @@ class BoardTooltipController extends BaseController public function comments() { $task = $this->getTask(); + $commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC'); $this->response->html($this->template->render('board/tooltip_comments', array( 'task' => $task, - 'comments' => $this->commentModel->getAll($task['id'], $this->userSession->getCommentSorting()) + 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection) ))); } diff --git a/app/Controller/BoardViewController.php b/app/Controller/BoardViewController.php index 97c99d11..9ef77e54 100644 --- a/app/Controller/BoardViewController.php +++ b/app/Controller/BoardViewController.php @@ -3,7 +3,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; -use Kanboard\Formatter\BoardFormatter; +use Kanboard\Model\TaskModel; /** * Board controller @@ -28,11 +28,15 @@ class BoardViewController extends BaseController throw AccessForbiddenException::getInstance()->withoutLayout(); } + $query = $this->taskFinderModel + ->getExtendedQuery() + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN); + $this->response->html($this->helper->layout->app('board/view_public', array( 'project' => $project, - 'swimlanes' => BoardFormatter::getInstance($this->container) + 'swimlanes' => $this->boardFormatter ->withProjectId($project['id']) - ->withQuery($this->taskFinderModel->getExtendedQuery()) + ->withQuery($query) ->format() , 'title' => $project['name'], @@ -63,7 +67,7 @@ class BoardViewController extends BaseController 'board_highlight_period' => $this->configModel->get('board_highlight_period'), 'swimlanes' => $this->taskLexer ->build($search) - ->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id'])) + ->format($this->boardFormatter->withProjectId($project['id'])) ))); } } diff --git a/app/Controller/CalendarController.php b/app/Controller/CalendarController.php index e5114f02..5ad253e1 100644 --- a/app/Controller/CalendarController.php +++ b/app/Controller/CalendarController.php @@ -29,7 +29,6 @@ class CalendarController extends BaseController 'project' => $project, 'title' => $project['name'], 'description' => $this->helper->projectHeader->getDescription($project), - 'check_interval' => $this->configModel->get('board_private_refresh_interval'), ))); } diff --git a/app/Controller/ColumnController.php b/app/Controller/ColumnController.php index e3f9bfff..69167976 100644 --- a/app/Controller/ColumnController.php +++ b/app/Controller/ColumnController.php @@ -25,7 +25,7 @@ class ColumnController extends BaseController $this->response->html($this->helper->layout->project('column/index', array( 'columns' => $columns, 'project' => $project, - 'title' => t('Edit board') + 'title' => t('Edit columns') ))); } @@ -49,7 +49,6 @@ class ColumnController extends BaseController 'values' => $values, 'errors' => $errors, 'project' => $project, - 'title' => t('Add a new column') ))); } @@ -61,20 +60,29 @@ class ColumnController extends BaseController public function save() { $project = $this->getProject(); - $values = $this->request->getValues(); + $values = $this->request->getValues() + array('hide_in_dashboard' => 0); list($valid, $errors) = $this->columnValidator->validateCreation($values); if ($valid) { - if ($this->columnModel->create($project['id'], $values['title'], $values['task_limit'], $values['description'], $values['hide_in_dashboard']) !== false) { + $result = $this->columnModel->create( + $project['id'], + $values['title'], + $values['task_limit'], + $values['description'], + $values['hide_in_dashboard'] + ); + + if ($result !== false) { $this->flash->success(t('Column created successfully.')); - return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true); + $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true); + return; } else { $errors['title'] = array(t('Another column with the same name exists in the project')); } } - return $this->create($values, $errors); + $this->create($values, $errors); } /** @@ -94,7 +102,6 @@ class ColumnController extends BaseController 'values' => $values ?: $column, 'project' => $project, 'column' => $column, - 'title' => t('Edit column "%s"', $column['title']) ))); } @@ -106,20 +113,29 @@ class ColumnController extends BaseController public function update() { $project = $this->getProject(); - $values = $this->request->getValues(); + $values = $this->request->getValues() + array('hide_in_dashboard' => 0); list($valid, $errors) = $this->columnValidator->validateModification($values); if ($valid) { - if ($this->columnModel->update($values['id'], $values['title'], $values['task_limit'], $values['description'], $values['hide_in_dashboard']) !== false) { + $result = $this->columnModel->update( + $values['id'], + $values['title'], + $values['task_limit'], + $values['description'], + $values['hide_in_dashboard'] + ); + + if ($result) { $this->flash->success(t('Board updated successfully.')); - return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id']))); + $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true); + return; } else { $this->flash->failure(t('Unable to update this board.')); } } - return $this->edit($values, $errors); + $this->edit($values, $errors); } /** @@ -152,7 +168,6 @@ class ColumnController extends BaseController $this->response->html($this->helper->layout->project('column/remove', array( 'column' => $this->columnModel->getById($this->request->getIntegerParam('column_id')), 'project' => $project, - 'title' => t('Remove a column from a board') ))); } diff --git a/app/Controller/ColumnMoveRestrictionController.php b/app/Controller/ColumnMoveRestrictionController.php new file mode 100644 index 00000000..b12f6b77 --- /dev/null +++ b/app/Controller/ColumnMoveRestrictionController.php @@ -0,0 +1,102 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\AccessForbiddenException; + +/** + * Class ColumnMoveRestrictionController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ColumnMoveRestrictionController extends BaseController +{ + /** + * Show form to create a new column restriction + * + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $role_id = $this->request->getIntegerParam('role_id'); + $role = $this->projectRoleModel->getById($project['id'], $role_id); + + $this->response->html($this->template->render('column_move_restriction/create', array( + 'project' => $project, + 'role' => $role, + 'columns' => $this->columnModel->getList($project['id']), + 'values' => $values + array('project_id' => $project['id'], 'role_id' => $role['role_id']), + 'errors' => $errors, + ))); + } + + /** + * Save new column restriction + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->columnMoveRestrictionValidator->validateCreation($values); + + if ($valid) { + $restriction_id = $this->columnMoveRestrictionModel->create( + $project['id'], + $values['role_id'], + $values['src_column_id'], + $values['dst_column_id'] + ); + + if ($restriction_id !== false) { + $this->flash->success(t('The column restriction has been created successfully.')); + } else { + $this->flash->failure(t('Unable to create this column restriction.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } else { + $this->create($values, $errors); + } + } + + /** + * Confirm suppression + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $restriction_id = $this->request->getIntegerParam('restriction_id'); + + $this->response->html($this->helper->layout->project('column_move_restriction/remove', array( + 'project' => $project, + 'restriction' => $this->columnMoveRestrictionModel->getById($project['id'], $restriction_id), + ))); + } + + /** + * Remove a restriction + * + * @access public + */ + public function remove() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + $restriction_id = $this->request->getIntegerParam('restriction_id'); + + if ($this->columnMoveRestrictionModel->remove($restriction_id)) { + $this->flash->success(t('Column restriction removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this restriction.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/ColumnRestrictionController.php b/app/Controller/ColumnRestrictionController.php new file mode 100644 index 00000000..ce2a1ca8 --- /dev/null +++ b/app/Controller/ColumnRestrictionController.php @@ -0,0 +1,103 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\AccessForbiddenException; + +/** + * Class ColumnMoveRestrictionController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ColumnRestrictionController extends BaseController +{ + /** + * Show form to create a new column restriction + * + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $role_id = $this->request->getIntegerParam('role_id'); + $role = $this->projectRoleModel->getById($project['id'], $role_id); + + $this->response->html($this->template->render('column_restriction/create', array( + 'project' => $project, + 'role' => $role, + 'rules' => $this->columnRestrictionModel->getRules(), + 'columns' => $this->columnModel->getList($project['id']), + 'values' => $values + array('project_id' => $project['id'], 'role_id' => $role['role_id']), + 'errors' => $errors, + ))); + } + + /** + * Save new column restriction + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->columnRestrictionValidator->validateCreation($values); + + if ($valid) { + $restriction_id = $this->columnRestrictionModel->create( + $project['id'], + $values['role_id'], + $values['column_id'], + $values['rule'] + ); + + if ($restriction_id !== false) { + $this->flash->success(t('The column restriction has been created successfully.')); + } else { + $this->flash->failure(t('Unable to create this column restriction.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } else { + $this->create($values, $errors); + } + } + + /** + * Confirm suppression + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $restriction_id = $this->request->getIntegerParam('restriction_id'); + + $this->response->html($this->helper->layout->project('column_restriction/remove', array( + 'project' => $project, + 'restriction' => $this->columnRestrictionModel->getById($project['id'], $restriction_id), + ))); + } + + /** + * Remove a restriction + * + * @access public + */ + public function remove() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + $restriction_id = $this->request->getIntegerParam('restriction_id'); + + if ($this->columnRestrictionModel->remove($restriction_id)) { + $this->flash->success(t('Column restriction removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this restriction.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/CommentController.php b/app/Controller/CommentController.php index 2a8c258a..526bd2bf 100644 --- a/app/Controller/CommentController.php +++ b/app/Controller/CommentController.php @@ -4,6 +4,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; use Kanboard\Core\Controller\PageNotFoundException; +use Kanboard\Model\UserMetadataModel; /** * Comment Controller @@ -47,6 +48,7 @@ class CommentController extends BaseController */ public function create(array $values = array(), array $errors = array()) { + $project = $this->getProject(); $task = $this->getTask(); if (empty($values)) { @@ -56,10 +58,13 @@ class CommentController extends BaseController ); } - $this->response->html($this->template->render('comment/create', array( + $values['project_id'] = $task['project_id']; + + $this->response->html($this->helper->layout->task('comment/create', array( 'values' => $values, 'errors' => $errors, 'task' => $task, + 'project' => $project, ))); } @@ -82,10 +87,10 @@ class CommentController extends BaseController $this->flash->failure(t('Unable to create your comment.')); } - return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true); + $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); } - - return $this->create($values, $errors); } /** @@ -102,8 +107,14 @@ class CommentController extends BaseController $task = $this->getTask(); $comment = $this->getComment(); + if (empty($values)) { + $values = $comment; + } + + $values['project_id'] = $task['project_id']; + $this->response->html($this->template->render('comment/edit', array( - 'values' => empty($values) ? $comment : $values, + 'values' => $values, 'errors' => $errors, 'comment' => $comment, 'task' => $task, @@ -183,9 +194,16 @@ class CommentController extends BaseController { $task = $this->getTask(); - $order = $this->userSession->getCommentSorting() === 'ASC' ? 'DESC' : 'ASC'; - $this->userSession->setCommentSorting($order); + $oldDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC'); + $newDirection = $oldDirection === 'ASC' ? 'DESC' : 'ASC'; - $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments')); + $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, $newDirection); + + $this->response->redirect($this->helper->url->to( + 'TaskViewController', + 'show', + array('task_id' => $task['id'], 'project_id' => $task['project_id']), + 'comments' + )); } } diff --git a/app/Controller/ConfigController.php b/app/Controller/ConfigController.php index 8285ee13..8572316e 100644 --- a/app/Controller/ConfigController.php +++ b/app/Controller/ConfigController.php @@ -76,7 +76,6 @@ class ConfigController extends BaseController 'languages' => $this->languageModel->getLanguages(), 'timezones' => $this->timezoneModel->getTimezones(), 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()), - 'datetime_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateTimeFormats()), 'time_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getTimeFormats()), 'title' => t('Settings').' > '.t('Application settings'), ))); diff --git a/app/Controller/CurrencyController.php b/app/Controller/CurrencyController.php index ad590035..155e229e 100644 --- a/app/Controller/CurrencyController.php +++ b/app/Controller/CurrencyController.php @@ -11,21 +11,33 @@ namespace Kanboard\Controller; class CurrencyController extends BaseController { /** - * Display all currency rates and form + * Display all currency rates + * + * @access public + */ + public function show() + { + $this->response->html($this->helper->layout->config('currency/show', array( + 'application_currency' => $this->configModel->get('application_currency'), + 'rates' => $this->currencyModel->getAll(), + 'currencies' => $this->currencyModel->getCurrencies(), + 'title' => t('Settings') . ' > ' . t('Currency rates'), + ))); + } + + /** + * Add or change currency rate * * @access public * @param array $values * @param array $errors */ - public function index(array $values = array(), array $errors = array()) + public function create(array $values = array(), array $errors = array()) { - $this->response->html($this->helper->layout->config('currency/index', array( - 'config_values' => array('application_currency' => $this->configModel->get('application_currency')), - 'values' => $values, - 'errors' => $errors, - 'rates' => $this->currencyModel->getAll(), + $this->response->html($this->template->render('currency/create', array( + 'values' => $values, + 'errors' => $errors, 'currencies' => $this->currencyModel->getCurrencies(), - 'title' => t('Settings').' > '.t('Currency rates'), ))); } @@ -34,7 +46,7 @@ class CurrencyController extends BaseController * * @access public */ - public function create() + public function save() { $values = $this->request->getValues(); list($valid, $errors) = $this->currencyValidator->validateCreation($values); @@ -42,13 +54,34 @@ class CurrencyController extends BaseController if ($valid) { if ($this->currencyModel->create($values['currency'], $values['rate'])) { $this->flash->success(t('The currency rate have been added successfully.')); - return $this->response->redirect($this->helper->url->to('CurrencyController', 'index')); + $this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true); + return; } else { $this->flash->failure(t('Unable to add this currency rate.')); } } - return $this->index($values, $errors); + $this->create($values, $errors); + } + + /** + * Change reference currency + * + * @access public + * @param array $values + * @param array $errors + */ + public function change(array $values = array(), array $errors = array()) + { + if (empty($values)) { + $values['application_currency'] = $this->configModel->get('application_currency'); + } + + $this->response->html($this->template->render('currency/change', array( + 'values' => $values, + 'errors' => $errors, + 'currencies' => $this->currencyModel->getCurrencies(), + ))); } /** @@ -56,7 +89,7 @@ class CurrencyController extends BaseController * * @access public */ - public function reference() + public function update() { $values = $this->request->getValues(); @@ -66,6 +99,6 @@ class CurrencyController extends BaseController $this->flash->failure(t('Unable to save your settings.')); } - $this->response->redirect($this->helper->url->to('CurrencyController', 'index')); + $this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true); } } diff --git a/app/Controller/CustomFilterController.php b/app/Controller/CustomFilterController.php index e5f674cd..dfe1ffc4 100644 --- a/app/Controller/CustomFilterController.php +++ b/app/Controller/CustomFilterController.php @@ -18,17 +18,13 @@ class CustomFilterController extends BaseController * Display list of filters * * @access public - * @param array $values - * @param array $errors * @throws \Kanboard\Core\Controller\PageNotFoundException */ - public function index(array $values = array(), array $errors = array()) + public function index() { $project = $this->getProject(); $this->response->html($this->helper->layout->project('custom_filter/index', array( - 'values' => $values + array('project_id' => $project['id']), - 'errors' => $errors, 'project' => $project, 'custom_filters' => $this->customFilterModel->getAll($project['id'], $this->userSession->getId()), 'title' => t('Custom filters'), @@ -36,6 +32,24 @@ class CustomFilterController extends BaseController } /** + * Show creation form for custom filters + * + * @access public + * @param array $values + * @param array $errors + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->template->render('custom_filter/create', array( + 'values' => $values + array('project_id' => $project['id']), + 'errors' => $errors, + 'project' => $project, + ))); + } + + /** * Save a new custom filter * * @access public @@ -52,13 +66,14 @@ class CustomFilterController extends BaseController if ($valid) { if ($this->customFilterModel->create($values) !== false) { $this->flash->success(t('Your custom filter have been created successfully.')); - return $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id']))); + $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id'])), true); + return; } else { $this->flash->failure(t('Unable to create your custom filter.')); } } - return $this->index($values, $errors); + $this->create($values, $errors); } /** @@ -152,13 +167,14 @@ class CustomFilterController extends BaseController if ($valid) { if ($this->customFilterModel->update($values)) { $this->flash->success(t('Your custom filter have been updated successfully.')); - return $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id']))); + $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id'])), true); + return; } else { $this->flash->failure(t('Unable to update custom filter.')); } } - return $this->edit($values, $errors); + $this->edit($values, $errors); } private function checkPermission(array $project, array $filter) diff --git a/app/Controller/DashboardController.php b/app/Controller/DashboardController.php index 44874546..f32f8552 100644 --- a/app/Controller/DashboardController.php +++ b/app/Controller/DashboardController.php @@ -2,9 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Model\ProjectModel; -use Kanboard\Model\SubtaskModel; - /** * Dashboard Controller * @@ -14,63 +11,6 @@ use Kanboard\Model\SubtaskModel; class DashboardController extends BaseController { /** - * Get project pagination - * - * @access private - * @param integer $user_id - * @param string $action - * @param integer $max - * @return \Kanboard\Core\Paginator - */ - private function getProjectPaginator($user_id, $action, $max) - { - return $this->paginator - ->setUrl('DashboardController', $action, array('pagination' => 'projects', 'user_id' => $user_id)) - ->setMax($max) - ->setOrder(ProjectModel::TABLE.'.name') - ->setQuery($this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id))) - ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects'); - } - - /** - * Get task pagination - * - * @access private - * @param integer $user_id - * @param string $action - * @param integer $max - * @return \Kanboard\Core\Paginator - */ - private function getTaskPaginator($user_id, $action, $max) - { - return $this->paginator - ->setUrl('DashboardController', $action, array('pagination' => 'tasks', 'user_id' => $user_id)) - ->setMax($max) - ->setOrder('tasks.id') - ->setQuery($this->taskFinderModel->getUserQuery($user_id)) - ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks'); - } - - /** - * Get subtask pagination - * - * @access private - * @param integer $user_id - * @param string $action - * @param integer $max - * @return \Kanboard\Core\Paginator - */ - private function getSubtaskPaginator($user_id, $action, $max) - { - return $this->paginator - ->setUrl('DashboardController', $action, array('pagination' => 'subtasks', 'user_id' => $user_id)) - ->setMax($max) - ->setOrder('tasks.id') - ->setQuery($this->subtaskModel->getUserQuery($user_id, array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS))) - ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); - } - - /** * Dashboard overview * * @access public @@ -80,10 +20,10 @@ class DashboardController extends BaseController $user = $this->getUser(); $this->response->html($this->helper->layout->dashboard('dashboard/show', array( - 'title' => t('Dashboard'), - 'project_paginator' => $this->getProjectPaginator($user['id'], 'show', 10), - 'task_paginator' => $this->getTaskPaginator($user['id'], 'show', 10), - 'subtask_paginator' => $this->getSubtaskPaginator($user['id'], 'show', 10), + '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, ))); } @@ -98,8 +38,8 @@ class DashboardController extends BaseController $user = $this->getUser(); $this->response->html($this->helper->layout->dashboard('dashboard/tasks', array( - 'title' => t('My tasks'), - 'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50), + 'title' => t('Tasks overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'tasks', 50), 'user' => $user, ))); } @@ -114,8 +54,8 @@ class DashboardController extends BaseController $user = $this->getUser(); $this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array( - 'title' => t('My subtasks'), - 'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50), + 'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'subtasks', 50), 'user' => $user, ))); } @@ -130,8 +70,8 @@ class DashboardController extends BaseController $user = $this->getUser(); $this->response->html($this->helper->layout->dashboard('dashboard/projects', array( - 'title' => t('My projects'), - 'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25), + 'title' => t('Projects overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'projects', 25), 'user' => $user, ))); } @@ -146,7 +86,7 @@ class DashboardController extends BaseController $user = $this->getUser(); $this->response->html($this->helper->layout->dashboard('dashboard/activity', array( - 'title' => t('My activity stream'), + 'title' => t('Activity stream for %s', $this->helper->user->getFullname($user)), 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']), 100), 'user' => $user, ))); @@ -159,9 +99,11 @@ class DashboardController extends BaseController */ public function calendar() { + $user = $this->getUser(); + $this->response->html($this->helper->layout->dashboard('dashboard/calendar', array( - 'title' => t('My calendar'), - 'user' => $this->getUser(), + 'title' => t('Calendar for %s', $this->helper->user->getFullname($user)), + 'user' => $user, ))); } @@ -175,7 +117,7 @@ class DashboardController extends BaseController $user = $this->getUser(); $this->response->html($this->helper->layout->dashboard('dashboard/notifications', array( - 'title' => t('My notifications'), + 'title' => t('Notifications for %s', $this->helper->user->getFullname($user)), 'notifications' => $this->userUnreadNotificationModel->getAll($user['id']), 'user' => $user, ))); diff --git a/app/Controller/ExportController.php b/app/Controller/ExportController.php index 27046c76..19f73a7c 100644 --- a/app/Controller/ExportController.php +++ b/app/Controller/ExportController.php @@ -24,29 +24,29 @@ class ExportController extends BaseController private function common($model, $method, $filename, $action, $page_title) { $project = $this->getProject(); - $from = $this->request->getStringParam('from'); - $to = $this->request->getStringParam('to'); - if ($from && $to) { - $data = $this->$model->$method($project['id'], $from, $to); - $this->response->withFileDownload($filename.'.csv'); - $this->response->csv($data); - } else { + if ($this->request->isPost()) { + $values = $this->request->getValues(); + $from = empty($values['from']) ? '' : $values['from']; + $to = empty($values['to']) ? '' : $values['to']; - $this->response->html($this->helper->layout->project('export/'.$action, array( - 'values' => array( - 'controller' => 'ExportController', - 'action' => $action, + if ($from && $to) { + $data = $this->$model->$method($project['id'], $from, $to); + $this->response->withFileDownload($filename.'.csv'); + $this->response->csv($data); + return; + } + } else { + $this->response->html($this->template->render('export/'.$action, array( + 'values' => array( 'project_id' => $project['id'], - 'from' => $from, - 'to' => $to, + 'from' => '', + 'to' => '', ), - 'errors' => array(), - 'date_format' => $this->configModel->get('application_date_format'), - 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()), + 'errors' => array(), 'project' => $project, - 'title' => $page_title, - ), 'export/sidebar')); + 'title' => $page_title, + ))); } } diff --git a/app/Controller/ExternalTaskCreationController.php b/app/Controller/ExternalTaskCreationController.php new file mode 100644 index 00000000..a1985adb --- /dev/null +++ b/app/Controller/ExternalTaskCreationController.php @@ -0,0 +1,97 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\ExternalTask\ExternalTaskException; + +/** + * External Task Creation Controller + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ExternalTaskCreationController extends BaseController +{ + public function step1(array $values = array(), $errorMessage = '') + { + $project = $this->getProject(); + $providerName = $this->request->getStringParam('provider_name'); + $taskProvider = $this->externalTaskManager->getProvider($providerName); + + if (empty($values)) { + $values = array( + 'swimlane_id' => $this->request->getIntegerParam('swimlane_id'), + 'column_id' => $this->request->getIntegerParam('column_id'), + ); + } + + $this->response->html($this->template->render('external_task_creation/step1', array( + 'project' => $project, + 'values' => $values, + 'error_message' => $errorMessage, + 'provider_name' => $providerName, + 'template' => $taskProvider->getImportFormTemplate(), + ))); + } + + public function step2(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $providerName = $this->request->getStringParam('provider_name'); + + try { + $taskProvider = $this->externalTaskManager->getProvider($providerName); + + if (empty($values)) { + $values = $this->request->getValues(); + $externalTask = $taskProvider->fetch($taskProvider->buildTaskUri($values)); + + $values = $externalTask->getFormValues() + array( + 'external_uri' => $externalTask->getUri(), + 'external_provider' => $providerName, + 'project_id' => $project['id'], + 'swimlane_id' => $values['swimlane_id'], + 'column_id' => $values['column_id'], + 'color_id' => $this->colorModel->getDefaultColor(), + 'owner_id' => $this->userSession->getId(), + ); + } else { + $externalTask = $taskProvider->fetch($values['external_uri']); + } + + $this->response->html($this->template->render('external_task_creation/step2', array( + 'project' => $project, + 'external_task' => $externalTask, + 'provider_name' => $providerName, + 'values' => $values, + 'errors' => $errors, + 'template' => $taskProvider->getCreationFormTemplate(), + 'columns_list' => $this->columnModel->getList($project['id']), + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, true), + 'categories_list' => $this->categoryModel->getList($project['id']), + 'swimlanes_list' => $this->swimlaneModel->getList($project['id'], false, true), + ))); + } catch (ExternalTaskException $e) { + $this->step1($values, $e->getMessage()); + } + } + + public function step3() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateCreation($values); + + if (! $valid) { + $this->step2($values, $errors); + } else if (! $this->helper->projectRole->canCreateTaskInColumn($project['id'], $values['column_id'])) { + $this->flash->failure(t('You cannot create tasks in this column.')); + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); + } else { + $taskId = $this->taskCreationModel->create($values); + $this->flash->success(t('Task created successfully.')); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $project['id'], 'task_id' => $taskId)), true); + } + } +} diff --git a/app/Controller/ExternalTaskViewController.php b/app/Controller/ExternalTaskViewController.php new file mode 100644 index 00000000..18bc15c1 --- /dev/null +++ b/app/Controller/ExternalTaskViewController.php @@ -0,0 +1,30 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\ExternalTask\ExternalTaskException; + +/** + * Class ExternalTaskViewController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ExternalTaskViewController extends BaseController +{ + public function show() + { + try { + $task = $this->getTask(); + $taskProvider = $this->externalTaskManager->getProvider($task['external_provider']); + $externalTask = $taskProvider->fetch($task['external_uri']); + + $this->response->html($this->template->render($taskProvider->getViewTemplate(), array( + 'task' => $task, + 'external_task' => $externalTask, + ))); + } catch (ExternalTaskException $e) { + $this->response->html('<div class="alert alert-error">'.$e->getMessage().'</div>'); + } + } +} diff --git a/app/Controller/FeedController.php b/app/Controller/FeedController.php index cf2b1088..f9b0ed7c 100644 --- a/app/Controller/FeedController.php +++ b/app/Controller/FeedController.php @@ -2,7 +2,11 @@ namespace Kanboard\Controller; +use DateTime; use Kanboard\Core\Controller\AccessForbiddenException; +use PicoFeed\Syndication\AtomFeedBuilder; +use PicoFeed\Syndication\AtomItemBuilder; +use PicoFeed\Syndication\FeedBuilder; /** * Atom/RSS Feed controller @@ -27,10 +31,15 @@ class FeedController extends BaseController throw AccessForbiddenException::getInstance()->withoutLayout(); } - $this->response->xml($this->template->render('feed/user', array( - 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id'])), - 'user' => $user, - ))); + $events = $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id'])); + + $feedBuilder = AtomFeedBuilder::create() + ->withTitle(e('Project activities for %s', $this->helper->user->getFullname($user))) + ->withFeedUrl($this->helper->url->to('FeedController', 'user', array('token' => $user['token']), '', true)) + ->withSiteUrl($this->helper->url->base()) + ->withDate(new DateTime()); + + $this->response->xml($this->buildFeedItems($events, $feedBuilder)->build()); } /** @@ -47,9 +56,44 @@ class FeedController extends BaseController throw AccessForbiddenException::getInstance()->withoutLayout(); } - $this->response->xml($this->template->render('feed/project', array( - 'events' => $this->helper->projectActivity->getProjectEvents($project['id']), - 'project' => $project, - ))); + $events = $this->helper->projectActivity->getProjectEvents($project['id']); + + $feedBuilder = AtomFeedBuilder::create() + ->withTitle(e('%s\'s activity', $project['name'])) + ->withFeedUrl($this->helper->url->to('FeedController', 'project', array('token' => $project['token']), '', true)) + ->withSiteUrl($this->helper->url->base()) + ->withDate(new DateTime()); + + $this->response->xml($this->buildFeedItems($events, $feedBuilder)->build()); + } + + /** + * Build feed items + * + * @access protected + * @param array $events + * @param FeedBuilder $feedBuilder + * @return FeedBuilder + */ + protected function buildFeedItems(array $events, FeedBuilder $feedBuilder) + { + foreach ($events as $event) { + $itemDate = new DateTime(); + $itemDate->setTimestamp($event['date_creation']); + + $itemUrl = $this->helper->url->to('TaskViewController', 'show', array('task_id' => $event['task_id']), '', true); + + $feedBuilder + ->withItem(AtomItemBuilder::create($feedBuilder) + ->withTitle($event['event_title']) + ->withUrl($itemUrl.'#event-'.$event['id']) + ->withAuthor($event['author']) + ->withPublishedDate($itemDate) + ->withUpdatedDate($itemDate) + ->withContent($event['event_content']) + ); + } + + return $feedBuilder; } } diff --git a/app/Controller/FileViewerController.php b/app/Controller/FileViewerController.php index 518f5b0b..49568912 100644 --- a/app/Controller/FileViewerController.php +++ b/app/Controller/FileViewerController.php @@ -15,11 +15,11 @@ class FileViewerController extends BaseController /** * Get file content from object storage * - * @access private + * @access protected * @param array $file * @return string */ - private function getFileContent(array $file) + protected function getFileContent(array $file) { $content = ''; @@ -35,6 +35,30 @@ class FileViewerController extends BaseController } /** + * Output file with cache + * + * @param array $file + * @param $mimetype + */ + protected function renderFileWithCache(array $file, $mimetype) + { + $etag = md5($file['path']); + + if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') { + $this->response->status(304); + } else { + try { + $this->response->withContentType($mimetype); + $this->response->withCache(5 * 86400, $etag); + $this->response->send(); + $this->objectStorage->output($file['path']); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + } + } + } + + /** * Show file content in a popover * * @access public @@ -65,21 +89,18 @@ class FileViewerController extends BaseController public function image() { $file = $this->getFile(); - $etag = md5($file['path']); - $this->response->withContentType($this->helper->file->getImageMimeType($file['name'])); - $this->response->withCache(5 * 86400, $etag); - - if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') { - $this->response->status(304); - } else { + $this->renderFileWithCache($file, $this->helper->file->getImageMimeType($file['name'])); + } - try { - $this->response->send(); - $this->objectStorage->output($file['path']); - } catch (ObjectStorageException $e) { - $this->logger->error($e->getMessage()); - } - } + /** + * Display file in browser + * + * @access public + */ + public function browser() + { + $file = $this->getFile(); + $this->renderFileWithCache($file, $this->helper->file->getBrowserViewType($file['name'])); } /** diff --git a/app/Controller/GroupAjaxController.php b/app/Controller/GroupAjaxController.php index 496e9ef2..308bba9e 100644 --- a/app/Controller/GroupAjaxController.php +++ b/app/Controller/GroupAjaxController.php @@ -2,8 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Formatter\GroupAutoCompleteFormatter; - /** * Group Ajax Controller * @@ -20,7 +18,7 @@ class GroupAjaxController extends BaseController public function autocomplete() { $search = $this->request->getStringParam('term'); - $formatter = new GroupAutoCompleteFormatter($this->groupManager->find($search)); - $this->response->json($formatter->format()); + $groups = $this->groupManager->find($search); + $this->response->json($this->groupAutoCompleteFormatter->withGroups($groups)->format()); } } diff --git a/app/Controller/ICalendarController.php b/app/Controller/ICalendarController.php index e354c6f1..4fe8b78a 100644 --- a/app/Controller/ICalendarController.php +++ b/app/Controller/ICalendarController.php @@ -7,7 +7,6 @@ use Kanboard\Core\Filter\QueryBuilder; use Kanboard\Filter\TaskAssigneeFilter; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskStatusFilter; -use Kanboard\Formatter\TaskICalFormatter; use Kanboard\Model\TaskModel; use Eluceo\iCal\Component\Calendar as iCalendar; @@ -94,8 +93,6 @@ class ICalendarController extends BaseController $end = $this->request->getStringParam('end', strtotime('+6 months')); $this->helper->ical->addTaskDateDueEvents($queryBuilder, $calendar, $start, $end); - - $formatter = new TaskICalFormatter($this->container); - $this->response->ical($formatter->setCalendar($calendar)->format()); + $this->response->ical($this->taskICalFormatter->setCalendar($calendar)->format()); } } diff --git a/app/Controller/LinkController.php b/app/Controller/LinkController.php index 477b25a4..2ad8a2b5 100644 --- a/app/Controller/LinkController.php +++ b/app/Controller/LinkController.php @@ -16,11 +16,11 @@ class LinkController extends BaseController /** * Get the current link * - * @access private + * @access protected * @return array * @throws PageNotFoundException */ - private function getLink() + protected function getLink() { $link = $this->linkModel->getById($this->request->getIntegerParam('link_id')); @@ -32,19 +32,31 @@ class LinkController extends BaseController } /** - * List of links + * List of labels + * + * @access public + */ + public function show() + { + $this->response->html($this->helper->layout->config('link/show', array( + 'links' => $this->linkModel->getMergedList(), + 'title' => t('Settings').' > '.t('Link labels'), + ))); + } + + /** + * Add new link label * * @access public * @param array $values * @param array $errors */ - public function index(array $values = array(), array $errors = array()) + public function create(array $values = array(), array $errors = array()) { - $this->response->html($this->helper->layout->config('link/index', array( - 'links' => $this->linkModel->getMergedList(), + $this->response->html($this->template->render('link/create', array( + 'links' => $this->linkModel->getMergedList(), 'values' => $values, 'errors' => $errors, - 'title' => t('Settings').' > '.t('Task\'s links'), ))); } @@ -61,21 +73,22 @@ class LinkController extends BaseController if ($valid) { if ($this->linkModel->create($values['label'], $values['opposite_label']) !== false) { $this->flash->success(t('Link added successfully.')); - return $this->response->redirect($this->helper->url->to('LinkController', 'index')); + $this->response->redirect($this->helper->url->to('LinkController', 'show'), true); + return; } else { $this->flash->failure(t('Unable to create your link.')); } } - return $this->index($values, $errors); + $this->create($values, $errors); } /** * Edit form * * @access public - * @param array $values - * @param array $errors + * @param array $values + * @param array $errors * @throws PageNotFoundException */ public function edit(array $values = array(), array $errors = array()) @@ -83,12 +96,11 @@ class LinkController extends BaseController $link = $this->getLink(); $link['label'] = t($link['label']); - $this->response->html($this->helper->layout->config('link/edit', array( + $this->response->html($this->template->render('link/edit', array( 'values' => $values ?: $link, 'errors' => $errors, 'labels' => $this->linkModel->getList($link['id']), - 'link' => $link, - 'title' => t('Link modification') + 'link' => $link, ))); } @@ -105,13 +117,14 @@ class LinkController extends BaseController if ($valid) { if ($this->linkModel->update($values)) { $this->flash->success(t('Link updated successfully.')); - return $this->response->redirect($this->helper->url->to('LinkController', 'index')); + $this->response->redirect($this->helper->url->to('LinkController', 'show'), true); + return; } else { $this->flash->failure(t('Unable to update your link.')); } } - return $this->edit($values, $errors); + $this->edit($values, $errors); } /** @@ -123,9 +136,8 @@ class LinkController extends BaseController { $link = $this->getLink(); - $this->response->html($this->helper->layout->config('link/remove', array( + $this->response->html($this->template->render('link/remove', array( 'link' => $link, - 'title' => t('Remove a link') ))); } @@ -145,6 +157,6 @@ class LinkController extends BaseController $this->flash->failure(t('Unable to remove this link.')); } - $this->response->redirect($this->helper->url->to('LinkController', 'index')); + $this->response->redirect($this->helper->url->to('LinkController', 'show'), true); } } diff --git a/app/Controller/PasswordResetController.php b/app/Controller/PasswordResetController.php index a1780ed9..6189f946 100644 --- a/app/Controller/PasswordResetController.php +++ b/app/Controller/PasswordResetController.php @@ -109,7 +109,7 @@ class PasswordResetController extends BaseController $token = $this->passwordResetModel->create($username); if ($token !== false) { - $user = $this->userModel->getByUsername($username); + $user = $this->userCacheDecorator->getByUsername($username); $this->emailClient->send( $user['email'], diff --git a/app/Controller/PluginController.php b/app/Controller/PluginController.php index 7b9d64d9..dbb739d6 100644 --- a/app/Controller/PluginController.php +++ b/app/Controller/PluginController.php @@ -23,6 +23,7 @@ class PluginController extends BaseController { $this->response->html($this->helper->layout->plugin('plugin/show', array( 'plugins' => $this->pluginLoader->getPlugins(), + 'incompatible_plugins' => $this->pluginLoader->getIncompatiblePlugins(), 'title' => t('Installed Plugins'), 'is_configured' => Installer::isConfigured(), ))); diff --git a/app/Controller/ProjectEditController.php b/app/Controller/ProjectEditController.php index 228d681c..ae39fdf3 100644 --- a/app/Controller/ProjectEditController.php +++ b/app/Controller/ProjectEditController.php @@ -11,51 +11,23 @@ namespace Kanboard\Controller; class ProjectEditController extends BaseController { /** - * General edition (most common operations) + * Edit project * * @access public * @param array $values * @param array $errors */ - public function edit(array $values = array(), array $errors = array()) + public function show(array $values = array(), array $errors = array()) { - $this->renderView('project_edit/general', $values, $errors); - } - - /** - * Change start and end dates - * - * @access public - * @param array $values - * @param array $errors - */ - public function dates(array $values = array(), array $errors = array()) - { - $this->renderView('project_edit/dates', $values, $errors); - } - - /** - * Change project description - * - * @access public - * @param array $values - * @param array $errors - */ - public function description(array $values = array(), array $errors = array()) - { - $this->renderView('project_edit/description', $values, $errors); - } + $project = $this->getProject(); - /** - * Change task priority - * - * @access public - * @param array $values - * @param array $errors - */ - public function priority(array $values = array(), array $errors = array()) - { - $this->renderView('project_edit/task_priority', $values, $errors); + $this->response->html($this->helper->layout->project('project_edit/show', array( + 'owners' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true), + 'values' => empty($values) ? $project : $values, + 'errors' => $errors, + 'project' => $project, + 'title' => t('Edit project') + ))); } /** @@ -67,67 +39,42 @@ class ProjectEditController extends BaseController { $project = $this->getProject(); $values = $this->request->getValues(); - $redirect = $this->request->getStringParam('redirect', 'edit'); - $values = $this->prepareValues($redirect, $project, $values); + $values = $this->prepareValues($project, $values); 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('ProjectEditController', $redirect, array('project_id' => $project['id'])), true); + return $this->response->redirect($this->helper->url->to('ProjectEditController', 'show', array('project_id' => $project['id'])), true); } else { $this->flash->failure(t('Unable to update this project.')); } } - return $this->$redirect($values, $errors); + return $this->show($values, $errors); } /** * Prepare form values * * @access private - * @param string $redirect * @param array $project * @param array $values * @return array */ - private function prepareValues($redirect, array $project, array $values) + private function prepareValues(array $project, array $values) { - if ($redirect === 'edit') { - if (isset($values['is_private'])) { - if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { - unset($values['is_private']); - } - } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) { - if ($this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { - $values += array('is_private' => 0); - } + if (isset($values['is_private'])) { + if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { + unset($values['is_private']); + } + } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) { + if ($this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) { + $values += array('is_private' => 0); } } return $values; } - - /** - * Common method to render different views - * - * @access private - * @param string $template - * @param array $values - * @param array $errors - */ - private function renderView($template, array $values, array $errors) - { - $project = $this->getProject(); - - $this->response->html($this->helper->layout->project($template, array( - 'owners' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true), - 'values' => empty($values) ? $project : $values, - 'errors' => $errors, - 'project' => $project, - 'title' => t('Edit project') - ))); - } } diff --git a/app/Controller/ProjectFileController.php b/app/Controller/ProjectFileController.php index cbe48679..9c38f684 100644 --- a/app/Controller/ProjectFileController.php +++ b/app/Controller/ProjectFileController.php @@ -21,7 +21,7 @@ class ProjectFileController extends BaseController $this->response->html($this->template->render('project_file/create', array( 'project' => $project, - 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')), + 'max_size' => $this->helper->text->phpToBytes(get_upload_max_size()), ))); } diff --git a/app/Controller/ProjectGanttController.php b/app/Controller/ProjectGanttController.php index a70d9eee..8239005e 100644 --- a/app/Controller/ProjectGanttController.php +++ b/app/Controller/ProjectGanttController.php @@ -5,7 +5,6 @@ namespace Kanboard\Controller; use Kanboard\Filter\ProjectIdsFilter; use Kanboard\Filter\ProjectStatusFilter; use Kanboard\Filter\ProjectTypeFilter; -use Kanboard\Formatter\ProjectGanttFormatter; use Kanboard\Model\ProjectModel; /** @@ -30,7 +29,7 @@ class ProjectGanttController extends BaseController $filter->getQuery()->asc(ProjectModel::TABLE.'.start_date'); $this->response->html($this->helper->layout->app('project_gantt/show', array( - 'projects' => $filter->format(new ProjectGanttFormatter($this->container)), + 'projects' => $filter->format($this->projectGanttFormatter), 'title' => t('Gantt chart for all projects'), ))); } diff --git a/app/Controller/ProjectListController.php b/app/Controller/ProjectListController.php index e1172400..4de73c97 100644 --- a/app/Controller/ProjectListController.php +++ b/app/Controller/ProjectListController.php @@ -20,7 +20,7 @@ class ProjectListController extends BaseController if ($this->userSession->isAdmin()) { $project_ids = $this->projectModel->getAllIds(); } else { - $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + $project_ids = $this->projectPermissionModel->getProjectIds($this->userSession->getId()); } $nb_projects = count($project_ids); diff --git a/app/Controller/ProjectOverviewController.php b/app/Controller/ProjectOverviewController.php index abdff657..eb002936 100644 --- a/app/Controller/ProjectOverviewController.php +++ b/app/Controller/ProjectOverviewController.php @@ -23,7 +23,7 @@ class ProjectOverviewController extends BaseController 'title' => $project['name'], 'description' => $this->helper->projectHeader->getDescription($project), 'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']), - 'roles' => $this->role->getProjectRoles(), + 'roles' => $this->projectRoleModel->getList($project['id']), 'events' => $this->helper->projectActivity->getProjectEvents($project['id'], 10), 'images' => $this->projectFileModel->getAllImages($project['id']), 'files' => $this->projectFileModel->getAllDocuments($project['id']), diff --git a/app/Controller/ProjectPermissionController.php b/app/Controller/ProjectPermissionController.php index f3ca6ed9..56777b25 100644 --- a/app/Controller/ProjectPermissionController.php +++ b/app/Controller/ProjectPermissionController.php @@ -52,7 +52,7 @@ class ProjectPermissionController extends BaseController 'project' => $project, 'users' => $this->projectUserRoleModel->getUsers($project['id']), 'groups' => $this->projectGroupRoleModel->getGroups($project['id']), - 'roles' => $this->role->getProjectRoles(), + 'roles' => $this->projectRoleModel->getList($project['id']), 'values' => $values, 'errors' => $errors, 'title' => t('Project Permissions'), @@ -132,7 +132,7 @@ class ProjectPermissionController extends BaseController if (! empty($project) && ! empty($values) && $this->projectUserRoleModel->changeUserRole($project['id'], $values['id'], $values['role'])) { $this->response->json(array('status' => 'ok')); } else { - $this->response->json(array('status' => 'error')); + $this->response->json(array('status' => 'error'), 500); } } @@ -147,7 +147,7 @@ class ProjectPermissionController extends BaseController $values = $this->request->getValues(); if (empty($values['group_id']) && ! empty($values['external_id'])) { - $values['group_id'] = $this->groupModel->create($values['name'], $values['external_id']); + $values['group_id'] = $this->groupModel->getOrCreateExternalGroupId($values['name'], $values['external_id']); } if ($this->projectGroupRoleModel->addGroup($project['id'], $values['group_id'], $values['role'])) { diff --git a/app/Controller/ProjectRoleController.php b/app/Controller/ProjectRoleController.php new file mode 100644 index 00000000..95503750 --- /dev/null +++ b/app/Controller/ProjectRoleController.php @@ -0,0 +1,162 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\AccessForbiddenException; + +/** + * Class ProjectRoleController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ProjectRoleController extends BaseController +{ + /** + * Show roles and permissions + */ + public function show() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->project('project_role/show', array( + 'project' => $project, + 'roles' => $this->projectRoleModel->getAllWithRestrictions($project['id']), + 'title' => t('Custom Project Roles'), + ))); + } + + /** + * Show form to create new role + * + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->template->render('project_role/create', array( + 'project' => $project, + 'values' => $values + array('project_id' => $project['id']), + 'errors' => $errors, + ))); + } + + /** + * Save new role + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->projectRoleValidator->validateCreation($values); + + if ($valid) { + $role_id = $this->projectRoleModel->create($project['id'], $values['role']); + + if ($role_id !== false) { + $this->flash->success(t('Your custom project role has been created successfully.')); + } else { + $this->flash->failure(t('Unable to create custom project role.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } else { + $this->create($values, $errors); + } + } + + /** + * Show form to change existing role + * + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + */ + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $role = $this->getRole($project['id']); + + if (empty($values)) { + $values = $role; + } + + $this->response->html($this->template->render('project_role/edit', array( + 'role' => $role, + 'project' => $project, + 'values' => $values, + 'errors' => $errors, + ))); + } + + /** + * Update role + */ + public function update() + { + $project = $this->getProject(); + $role = $this->getRole($project['id']); + + $values = $this->request->getValues(); + + list($valid, $errors) = $this->projectRoleValidator->validateModification($values); + + if ($valid) { + if ($this->projectRoleModel->update($role['role_id'], $project['id'], $values['role'])) { + $this->flash->success(t('Your custom project role has been updated successfully.')); + } else { + $this->flash->failure(t('Unable to update custom project role.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } else { + $this->edit($values, $errors); + } + } + + /** + * Confirm suppression + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $role = $this->getRole($project['id']); + + $this->response->html($this->helper->layout->project('project_role/remove', array( + 'project' => $project, + 'role' => $role, + ))); + } + + /** + * Remove a custom role + * + * @access public + */ + public function remove() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + $role_id = $this->request->getIntegerParam('role_id'); + + if ($this->projectRoleModel->remove($project['id'], $role_id)) { + $this->flash->success(t('Custom project role removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this project role.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } + + protected function getRole($project_id) + { + $role_id = $this->request->getIntegerParam('role_id'); + return $this->projectRoleModel->getById($project_id, $role_id); + } +} diff --git a/app/Controller/ProjectRoleRestrictionController.php b/app/Controller/ProjectRoleRestrictionController.php new file mode 100644 index 00000000..4fa9b13b --- /dev/null +++ b/app/Controller/ProjectRoleRestrictionController.php @@ -0,0 +1,96 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\AccessForbiddenException; + +/** + * Class ProjectRoleRestrictionController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class ProjectRoleRestrictionController extends BaseController +{ + /** + * Show form to create a new project restriction + * + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $role_id = $this->request->getIntegerParam('role_id'); + $role = $this->projectRoleModel->getById($project['id'], $role_id); + + $this->response->html($this->template->render('project_role_restriction/create', array( + 'project' => $project, + 'role' => $role, + 'values' => $values + array('project_id' => $project['id'], 'role_id' => $role['role_id']), + 'errors' => $errors, + 'restrictions' => $this->projectRoleRestrictionModel->getRules(), + ))); + } + + /** + * Save new restriction + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $restriction_id = $this->projectRoleRestrictionModel->create( + $project['id'], + $values['role_id'], + $values['rule'] + ); + + if ($restriction_id !== false) { + $this->flash->success(t('The project restriction has been created successfully.')); + } else { + $this->flash->failure(t('Unable to create this project restriction.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } + + /** + * Confirm suppression + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $restriction_id = $this->request->getIntegerParam('restriction_id'); + + $this->response->html($this->helper->layout->project('project_role_restriction/remove', array( + 'project' => $project, + 'restriction' => $this->projectRoleRestrictionModel->getById($project['id'], $restriction_id), + 'restrictions' => $this->projectRoleRestrictionModel->getRules(), + ))); + } + + /** + * Remove a restriction + * + * @access public + */ + public function remove() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + $restriction_id = $this->request->getIntegerParam('restriction_id'); + + if ($this->projectRoleRestrictionModel->remove($restriction_id)) { + $this->flash->success(t('Project restriction removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this restriction.')); + } + + $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/ProjectUserOverviewController.php b/app/Controller/ProjectUserOverviewController.php index 686de830..5faf5790 100644 --- a/app/Controller/ProjectUserOverviewController.php +++ b/app/Controller/ProjectUserOverviewController.php @@ -122,9 +122,9 @@ class ProjectUserOverviewController extends BaseController { $project = $this->getProject(); - return $this->response->html($this->template->render('project_user_overview/tooltip_users', array( + $this->response->html($this->template->render('project_user_overview/tooltip_users', array( 'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']), - 'roles' => $this->role->getProjectRoles(), + 'roles' => $this->projectRoleModel->getList($project['id']), ))); } } diff --git a/app/Controller/SearchController.php b/app/Controller/SearchController.php index 8557b182..c9213085 100644 --- a/app/Controller/SearchController.php +++ b/app/Controller/SearchController.php @@ -14,7 +14,7 @@ class SearchController extends BaseController { public function index() { - $projects = $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId()); + $projects = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); $search = urldecode($this->request->getStringParam('search')); $nb_tasks = 0; diff --git a/app/Controller/SubtaskController.php b/app/Controller/SubtaskController.php index 93dab5cd..134b057e 100644 --- a/app/Controller/SubtaskController.php +++ b/app/Controller/SubtaskController.php @@ -27,10 +27,7 @@ class SubtaskController extends BaseController $task = $this->getTask(); if (empty($values)) { - $values = array( - 'task_id' => $task['id'], - 'another_subtask' => $this->request->getIntegerParam('another_subtask', 0) - ); + $values = $this->prepareValues($task); } $this->response->html($this->template->render('subtask/create', array( @@ -40,6 +37,24 @@ class SubtaskController extends BaseController 'task' => $task, ))); } + + /** + * Prepare form values + * + * @access protected + * @param array $task + * @return array + */ + protected function prepareValues(array $task) + { + $values = array( + 'task_id' => $task['id'], + 'another_subtask' => $this->request->getIntegerParam('another_subtask', 0) + ); + + $values = $this->hook->merge('controller:subtask:form:default', $values, array('default_values' => $values)); + return $values; + } /** * Validation and creation @@ -168,7 +183,7 @@ class SubtaskController extends BaseController $values = $this->request->getJson(); if (! empty($values) && $this->helper->user->hasProjectAccess('SubtaskController', 'movePosition', $project_id)) { - $result = $this->subtaskModel->changePosition($task_id, $values['subtask_id'], $values['position']); + $result = $this->subtaskPositionModel->changePosition($task_id, $values['subtask_id'], $values['position']); $this->response->json(array('result' => $result)); } else { throw new AccessForbiddenException(); diff --git a/app/Controller/SubtaskConverterController.php b/app/Controller/SubtaskConverterController.php index 65bcd2da..404c50d0 100644 --- a/app/Controller/SubtaskConverterController.php +++ b/app/Controller/SubtaskConverterController.php @@ -26,7 +26,7 @@ class SubtaskConverterController extends BaseController $project = $this->getProject(); $subtask = $this->getSubtask(); - $task_id = $this->subtaskModel->convertToTask($project['id'], $subtask['id']); + $task_id = $this->subtaskTaskConversionModel->convertToTask($project['id'], $subtask['id']); if ($task_id !== false) { $this->flash->success(t('Subtask converted to task successfully.')); diff --git a/app/Controller/SubtaskRestrictionController.php b/app/Controller/SubtaskRestrictionController.php index 084fc0d9..cb642e1c 100644 --- a/app/Controller/SubtaskRestrictionController.php +++ b/app/Controller/SubtaskRestrictionController.php @@ -27,7 +27,7 @@ class SubtaskRestrictionController extends BaseController SubtaskModel::STATUS_TODO => t('Todo'), SubtaskModel::STATUS_DONE => t('Done'), ), - 'subtask_inprogress' => $this->subtaskModel->getSubtaskInProgress($this->userSession->getId()), + 'subtask_inprogress' => $this->subtaskStatusModel->getSubtaskInProgress($this->userSession->getId()), 'subtask' => $subtask, 'task' => $task, ))); diff --git a/app/Controller/SubtaskStatusController.php b/app/Controller/SubtaskStatusController.php index 699951fe..d4d356c3 100644 --- a/app/Controller/SubtaskStatusController.php +++ b/app/Controller/SubtaskStatusController.php @@ -20,7 +20,7 @@ class SubtaskStatusController extends BaseController $task = $this->getTask(); $subtask = $this->getSubtask(); - $status = $this->subtaskModel->toggleStatus($subtask['id']); + $status = $this->subtaskStatusModel->toggleStatus($subtask['id']); if ($this->request->getIntegerParam('refresh-table') === 0) { $subtask['status'] = $status; diff --git a/app/Controller/TaskAjaxController.php b/app/Controller/TaskAjaxController.php index f9feff15..6d0b3fc2 100644 --- a/app/Controller/TaskAjaxController.php +++ b/app/Controller/TaskAjaxController.php @@ -5,8 +5,10 @@ namespace Kanboard\Controller; use Kanboard\Filter\TaskIdExclusionFilter; use Kanboard\Filter\TaskIdFilter; use Kanboard\Filter\TaskProjectsFilter; +use Kanboard\Filter\TaskStartsWithIdFilter; +use Kanboard\Filter\TaskStatusFilter; use Kanboard\Filter\TaskTitleFilter; -use Kanboard\Formatter\TaskAutoCompleteFormatter; +use Kanboard\Model\TaskModel; /** * Task Ajax Controller @@ -19,7 +21,6 @@ class TaskAjaxController extends BaseController /** * Task auto-completion (Ajax) * - * @access public */ public function autocomplete() { @@ -43,7 +44,27 @@ class TaskAjaxController extends BaseController $filter->withFilter(new TaskTitleFilter($search)); } - $this->response->json($filter->format(new TaskAutoCompleteFormatter($this->container))); + $this->response->json($filter->format($this->taskAutoCompleteFormatter)); + } + } + + /** + * Task ID suggest menu + */ + public function suggest() + { + $taskId = $this->request->getIntegerParam('search'); + $projectIds = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + + if (empty($projectIds)) { + $this->response->json(array()); + } else { + $filter = $this->taskQuery + ->withFilter(new TaskProjectsFilter($projectIds)) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskStartsWithIdFilter($taskId)); + + $this->response->json($filter->format($this->taskSuggestMenuFormatter)); } } } diff --git a/app/Controller/TaskBulkController.php b/app/Controller/TaskBulkController.php index 80ff8f4f..51447f32 100644 --- a/app/Controller/TaskBulkController.php +++ b/app/Controller/TaskBulkController.php @@ -47,7 +47,12 @@ class TaskBulkController extends BaseController $values = $this->request->getValues(); list($valid, $errors) = $this->taskValidator->validateBulkCreation($values); - if ($valid) { + if (! $valid) { + $this->show($values, $errors); + } else if (! $this->helper->projectRole->canCreateTaskInColumn($project['id'], $values['column_id'])) { + $this->flash->failure(t('You cannot create tasks in this column.')); + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); + } else { $this->createTasks($project, $values); $this->response->redirect($this->helper->url->to( 'BoardViewController', @@ -55,8 +60,6 @@ class TaskBulkController extends BaseController array('project_id' => $project['id']), 'swimlane-'. $values['swimlane_id'] ), true); - } else { - $this->show($values, $errors); } } diff --git a/app/Controller/TaskCreationController.php b/app/Controller/TaskCreationController.php index 8636d02a..cafd7e06 100644 --- a/app/Controller/TaskCreationController.php +++ b/app/Controller/TaskCreationController.php @@ -2,6 +2,8 @@ namespace Kanboard\Controller; +use Kanboard\Core\Controller\PageNotFoundException; + /** * Task Creation Controller * @@ -14,26 +16,18 @@ class TaskCreationController extends BaseController * Display a form to create a new task * * @access public - * @param array $values - * @param array $errors - * @throws \Kanboard\Core\Controller\PageNotFoundException + * @param array $values + * @param array $errors + * @throws PageNotFoundException */ public function show(array $values = array(), array $errors = array()) { $project = $this->getProject(); $swimlanes_list = $this->swimlaneModel->getList($project['id'], false, true); + $values += $this->prepareValues($swimlanes_list); - if (empty($values)) { - $values = array( - 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)), - 'column_id' => $this->request->getIntegerParam('column_id'), - 'color_id' => $this->colorModel->getDefaultColor(), - 'owner_id' => $this->userSession->getId(), - ); - - $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); - $values = $this->hook->merge('controller:task-creation:form:default', $values, array('default_values' => $values)); - } + $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); + $values = $this->hook->merge('controller:task-creation:form:default', $values, array('default_values' => $values)); $this->response->html($this->template->render('task_creation/show', array( 'project' => $project, @@ -43,7 +37,6 @@ class TaskCreationController extends BaseController 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private']), 'categories_list' => $this->categoryModel->getList($project['id']), 'swimlanes_list' => $swimlanes_list, - 'title' => $project['name'].' > '.t('New task') ))); } @@ -59,19 +52,51 @@ class TaskCreationController extends BaseController list($valid, $errors) = $this->taskValidator->validateCreation($values); - if ($valid && $this->taskCreationModel->create($values)) { + if (! $valid) { + $this->flash->failure(t('Unable to create your task.')); + $this->show($values, $errors); + } else if (! $this->helper->projectRole->canCreateTaskInColumn($project['id'], $values['column_id'])) { + $this->flash->failure(t('You cannot create tasks in this column.')); + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); + } else { + $task_id = $this->taskCreationModel->create($values); $this->flash->success(t('Task created successfully.')); - return $this->afterSave($project, $values); + $this->afterSave($project, $values, $task_id); + } + } + + /** + * Duplicate created tasks to multiple projects + * + * @throws PageNotFoundException + */ + public function duplicateProjects() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + if (isset($values['project_ids'])) { + foreach ($values['project_ids'] as $project_id) { + $this->taskProjectDuplicationModel->duplicateToProject($values['task_id'], $project_id); + } } - $this->flash->failure(t('Unable to create your task.')); - return $this->show($values, $errors); + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); } - private function afterSave(array $project, array &$values) + /** + * Executed after the task is saved + * + * @param array $project + * @param array $values + * @param integer $task_id + */ + protected function afterSave(array $project, array &$values, $task_id) { - if (isset($values['another_task']) && $values['another_task'] == 1) { - return $this->show(array( + if (isset($values['duplicate_multiple_projects']) && $values['duplicate_multiple_projects'] == 1) { + $this->chooseProjects($project, $task_id); + } elseif (isset($values['another_task']) && $values['another_task'] == 1) { + $this->show(array( 'owner_id' => $values['owner_id'], 'color_id' => $values['color_id'], 'category_id' => isset($values['category_id']) ? $values['category_id'] : 0, @@ -79,8 +104,47 @@ class TaskCreationController extends BaseController 'swimlane_id' => isset($values['swimlane_id']) ? $values['swimlane_id'] : 0, 'another_task' => 1, )); + } else { + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); } + } + + /** + * Prepare form values + * + * @access protected + * @param array $swimlanes_list + * @return array + */ + protected function prepareValues(array $swimlanes_list) + { + $values = array( + 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)), + 'column_id' => $this->request->getIntegerParam('column_id'), + 'color_id' => $this->colorModel->getDefaultColor(), + 'owner_id' => $this->userSession->getId(), + ); + + return $values; + } - return $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); + /** + * Choose projects + * + * @param array $project + * @param integer $task_id + */ + protected function chooseProjects(array $project, $task_id) + { + $task = $this->taskFinderModel->getById($task_id); + $projects = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); + unset($projects[$project['id']]); + + $this->response->html($this->template->render('task_creation/duplicate_projects', array( + 'project' => $project, + 'task' => $task, + 'projects_list' => $projects, + 'values' => array('task_id' => $task['id']) + ))); } } diff --git a/app/Controller/TaskExternalLinkController.php b/app/Controller/TaskExternalLinkController.php index 9c04eb00..df23f87b 100644 --- a/app/Controller/TaskExternalLinkController.php +++ b/app/Controller/TaskExternalLinkController.php @@ -76,12 +76,23 @@ class TaskExternalLinkController extends BaseController $values = $this->request->getValues(); list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); - if ($valid && $this->taskExternalLinkModel->create($values) !== false) { - $this->flash->success(t('Link added successfully.')); - return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); + if ($valid) { + if ($this->taskExternalLinkModel->create($values) !== false) { + $this->flash->success(t('Link added successfully.')); + } else { + $this->flash->success(t('Unable to create your link.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); + } else { + $provider = $this->externalLinkManager->getProvider($values['link_type']); + $this->response->html($this->template->render('task_external_link/create', array( + 'values' => $values, + 'errors' => $errors, + 'dependencies' => $provider->getDependencies(), + 'task' => $task, + ))); } - - return $this->edit($values, $errors); } /** diff --git a/app/Controller/TaskFileController.php b/app/Controller/TaskFileController.php index 77c0c026..8a0971e4 100644 --- a/app/Controller/TaskFileController.php +++ b/app/Controller/TaskFileController.php @@ -40,7 +40,7 @@ class TaskFileController extends BaseController $this->response->html($this->template->render('task_file/create', array( 'task' => $task, - 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')), + 'max_size' => $this->helper->text->phpToBytes(get_upload_max_size()), ))); } diff --git a/app/Controller/TaskGanttController.php b/app/Controller/TaskGanttController.php index 868368e1..b03b9d00 100644 --- a/app/Controller/TaskGanttController.php +++ b/app/Controller/TaskGanttController.php @@ -3,7 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Filter\TaskProjectFilter; -use Kanboard\Formatter\TaskGanttFormatter; use Kanboard\Model\TaskModel; /** @@ -35,7 +34,7 @@ class TaskGanttController extends BaseController 'title' => $project['name'], 'description' => $this->helper->projectHeader->getDescription($project), 'sorting' => $sorting, - 'tasks' => $filter->format(new TaskGanttFormatter($this->container)), + 'tasks' => $filter->format($this->taskGanttFormatter), ))); } diff --git a/app/Controller/TaskGanttCreationController.php b/app/Controller/TaskGanttCreationController.php deleted file mode 100644 index 4361ede3..00000000 --- a/app/Controller/TaskGanttCreationController.php +++ /dev/null @@ -1,70 +0,0 @@ -<?php - -namespace Kanboard\Controller; - -/** - * Class TaskGanttCreationController - * - * @package Kanboard\Controller - * @author Frederic Guillot - */ -class TaskGanttCreationController extends BaseController -{ - /** - * Simplified form to create a new task - * - * @access public - * @param array $values - * @param array $errors - * @throws \Kanboard\Core\Controller\PageNotFoundException - */ - public function show(array $values = array(), array $errors = array()) - { - $project = $this->getProject(); - - $values = $values + array( - 'project_id' => $project['id'], - 'column_id' => $this->columnModel->getFirstColumnId($project['id']), - 'position' => 1 - ); - - $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); - $values = $this->hook->merge('controller:gantt:task:form:default', $values, array('default_values' => $values)); - - $this->response->html($this->template->render('task_gantt_creation/show', array( - 'project' => $project, - 'errors' => $errors, - 'values' => $values, - 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private']), - 'categories_list' => $this->categoryModel->getList($project['id']), - 'swimlanes_list' => $this->swimlaneModel->getList($project['id'], false, true), - 'title' => $project['name'].' > '.t('New task') - ))); - } - - /** - * Validate and save a new task - * - * @access public - */ - public function save() - { - $project = $this->getProject(); - $values = $this->request->getValues(); - - list($valid, $errors) = $this->taskValidator->validateCreation($values); - - if ($valid) { - $task_id = $this->taskCreationModel->create($values); - - if ($task_id !== false) { - $this->flash->success(t('Task created successfully.')); - return $this->response->redirect($this->helper->url->to('TaskGanttController', 'show', array('project_id' => $project['id']))); - } else { - $this->flash->failure(t('Unable to create your task.')); - } - } - - return $this->show($values, $errors); - } -} diff --git a/app/Controller/TaskImportController.php b/app/Controller/TaskImportController.php index aff2d390..2e323979 100644 --- a/app/Controller/TaskImportController.php +++ b/app/Controller/TaskImportController.php @@ -23,15 +23,14 @@ class TaskImportController extends BaseController { $project = $this->getProject(); - $this->response->html($this->helper->layout->project('task_import/show', array( + $this->response->html($this->template->render('task_import/show', array( 'project' => $project, 'values' => $values, 'errors' => $errors, - 'max_size' => ini_get('upload_max_filesize'), + 'max_size' => get_upload_max_size(), 'delimiters' => Csv::getDelimiters(), 'enclosures' => Csv::getEnclosures(), - 'title' => t('Import tasks from CSV file'), - ), 'task_import/sidebar')); + ))); } /** @@ -58,7 +57,7 @@ class TaskImportController extends BaseController $this->flash->failure(t('Nothing have been imported!')); } - $this->response->redirect($this->helper->url->to('TaskImportController', 'show', array('project_id' => $project['id']))); + $this->response->redirect($this->helper->url->to('TaskImportController', 'show', array('project_id' => $project['id'])), true); } } diff --git a/app/Controller/TaskModificationController.php b/app/Controller/TaskModificationController.php index b064123a..520bf70e 100644 --- a/app/Controller/TaskModificationController.php +++ b/app/Controller/TaskModificationController.php @@ -2,6 +2,10 @@ namespace Kanboard\Controller; +use Kanboard\Core\Controller\AccessForbiddenException; +use Kanboard\Core\ExternalTask\AccessForbiddenException as ExternalTaskAccessForbiddenException; +use Kanboard\Core\ExternalTask\ExternalTaskException; + /** * Task Modification controller * @@ -38,13 +42,12 @@ class TaskModificationController extends BaseController if (empty($values)) { $values = $task; - $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); - $values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values)); - $values = $this->dateParser->format($values, array('date_due'), $this->dateParser->getUserDateFormat()); - $values = $this->dateParser->format($values, array('date_started'), $this->dateParser->getUserDateTimeFormat()); } - $this->response->html($this->template->render('task_modification/show', array( + $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); + $values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values)); + + $params = array( 'project' => $project, 'values' => $values, 'errors' => $errors, @@ -52,7 +55,29 @@ class TaskModificationController extends BaseController 'tags' => $this->taskTagModel->getList($task['id']), 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']), 'categories_list' => $this->categoryModel->getList($task['project_id']), - ))); + ); + + $this->renderTemplate($task, $params); + } + + protected function renderTemplate(array &$task, array &$params) + { + if (empty($task['external_uri'])) { + $this->response->html($this->template->render('task_modification/show', $params)); + } else { + + try { + $taskProvider = $this->externalTaskManager->getProvider($task['external_provider']); + $params['template'] = $taskProvider->getModificationFormTemplate(); + $params['external_task'] = $taskProvider->fetch($task['external_uri']); + } catch (ExternalTaskAccessForbiddenException $e) { + throw new AccessForbiddenException($e->getMessage()); + } catch (ExternalTaskException $e) { + $params['error_message'] = $e->getMessage(); + } + + $this->response->html($this->template->render('external_task_modification/show', $params)); + } } /** @@ -67,7 +92,7 @@ class TaskModificationController extends BaseController list($valid, $errors) = $this->taskValidator->validateModification($values); - if ($valid && $this->taskModificationModel->update($values)) { + if ($valid && $this->updateTask($task, $values, $errors)) { $this->flash->success(t('Task updated successfully.')); $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true); } else { @@ -75,4 +100,23 @@ class TaskModificationController extends BaseController $this->edit($values, $errors); } } + + protected function updateTask(array &$task, array &$values, array &$errors) + { + $result = $this->taskModificationModel->update($values); + + if ($result && ! empty($task['external_uri'])) { + try { + $taskProvider = $this->externalTaskManager->getProvider($task['external_provider']); + $result = $taskProvider->save($task['external_uri'], $values, $errors); + } catch (ExternalTaskAccessForbiddenException $e) { + throw new AccessForbiddenException($e->getMessage()); + } catch (ExternalTaskException $e) { + $this->logger->error($e->getMessage()); + $result = false; + } + } + + return $result; + } } diff --git a/app/Controller/TaskMovePositionController.php b/app/Controller/TaskMovePositionController.php new file mode 100644 index 00000000..39687b79 --- /dev/null +++ b/app/Controller/TaskMovePositionController.php @@ -0,0 +1,51 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\AccessForbiddenException; +use Kanboard\Model\TaskModel; + +/** + * Class TaskMovePositionController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class TaskMovePositionController extends BaseController +{ + public function show() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('task_move_position/show', array( + 'task' => $task, + 'board' => $this->boardFormatter + ->withProjectId($task['project_id']) + ->withQuery($this->taskFinderModel->getExtendedQuery() + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->neq(TaskModel::TABLE.'.id', $task['id']) + ) + ->format() + ))); + } + + public function save() + { + $task = $this->getTask(); + $values = $this->request->getJson(); + + if (! $this->helper->projectRole->canMoveTask($task['project_id'], $task['column_id'], $values['column_id'])) { + throw new AccessForbiddenException(e('You are not allowed to move this task.')); + } + + $this->taskPositionModel->movePosition( + $task['project_id'], + $task['id'], + $values['column_id'], + $values['position'], + $values['swimlane_id'] + ); + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))); + } +} diff --git a/app/Controller/TaskStatusController.php b/app/Controller/TaskStatusController.php index 82b4f9c4..56d38400 100644 --- a/app/Controller/TaskStatusController.php +++ b/app/Controller/TaskStatusController.php @@ -52,11 +52,11 @@ class TaskStatusController extends BaseController $this->flash->failure($failure_message); } - return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true); + } else { + $this->response->html($this->template->render($template, array( + 'task' => $task, + ))); } - - return $this->response->html($this->template->render($template, array( - 'task' => $task, - ))); } } diff --git a/app/Controller/TaskSuppressionController.php b/app/Controller/TaskSuppressionController.php index 600107c9..019bd97c 100644 --- a/app/Controller/TaskSuppressionController.php +++ b/app/Controller/TaskSuppressionController.php @@ -19,7 +19,7 @@ class TaskSuppressionController extends BaseController { $task = $this->getTask(); - if (! $this->helper->user->canRemoveTask($task)) { + if (! $this->helper->projectRole->canRemoveTask($task)) { throw new AccessForbiddenException(); } @@ -37,7 +37,7 @@ class TaskSuppressionController extends BaseController $task = $this->getTask(); $this->checkCSRFParam(); - if (! $this->helper->user->canRemoveTask($task)) { + if (! $this->helper->projectRole->canRemoveTask($task)) { throw new AccessForbiddenException(); } diff --git a/app/Controller/TaskViewController.php b/app/Controller/TaskViewController.php index f40f8bea..31b9de11 100644 --- a/app/Controller/TaskViewController.php +++ b/app/Controller/TaskViewController.php @@ -4,6 +4,7 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; use Kanboard\Core\Controller\PageNotFoundException; +use Kanboard\Model\UserMetadataModel; /** * Task Controller @@ -22,7 +23,6 @@ class TaskViewController extends BaseController { $project = $this->projectModel->getByToken($this->request->getStringParam('token')); - // Token verification if (empty($project)) { throw AccessForbiddenException::getInstance()->withoutLayout(); } @@ -62,23 +62,14 @@ class TaskViewController extends BaseController { $task = $this->getTask(); $subtasks = $this->subtaskModel->getAll($task['id']); - - $values = array( - 'id' => $task['id'], - 'date_started' => $task['date_started'], - 'time_estimated' => $task['time_estimated'] ?: '', - 'time_spent' => $task['time_spent'] ?: '', - ); - - $values = $this->dateParser->format($values, array('date_started'), $this->dateParser->getUserDateTimeFormat()); + $commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC'); $this->response->html($this->helper->layout->task('task/show', array( 'task' => $task, 'project' => $this->projectModel->getById($task['project_id']), - 'values' => $values, 'files' => $this->taskFileModel->getAllDocuments($task['id']), 'images' => $this->taskFileModel->getAllImages($task['id']), - 'comments' => $this->commentModel->getAll($task['id'], $this->userSession->getCommentSorting()), + 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection), 'subtasks' => $subtasks, 'internal_links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']), 'external_links' => $this->taskExternalLinkModel->getAll($task['id']), @@ -102,6 +93,7 @@ class TaskViewController extends BaseController 'lead_time' => $this->taskAnalyticModel->getLeadTime($task), 'cycle_time' => $this->taskAnalyticModel->getCycleTime($task), 'time_spent_columns' => $this->taskAnalyticModel->getTimeSpentByColumn($task), + 'tags' => $this->taskTagModel->getList($task['id']), ))); } @@ -126,6 +118,7 @@ class TaskViewController extends BaseController 'task' => $task, 'project' => $this->projectModel->getById($task['project_id']), 'subtask_paginator' => $subtask_paginator, + 'tags' => $this->taskTagModel->getList($task['id']), ))); } @@ -142,6 +135,7 @@ class TaskViewController extends BaseController 'task' => $task, 'project' => $this->projectModel->getById($task['project_id']), 'transitions' => $this->transitionModel->getAllByTask($task['id']), + 'tags' => $this->taskTagModel->getList($task['id']), ))); } } diff --git a/app/Controller/UserAjaxController.php b/app/Controller/UserAjaxController.php index ed180471..17567a00 100644 --- a/app/Controller/UserAjaxController.php +++ b/app/Controller/UserAjaxController.php @@ -3,7 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Filter\UserNameFilter; -use Kanboard\Formatter\UserAutoCompleteFormatter; use Kanboard\Model\UserModel; /** @@ -24,7 +23,7 @@ class UserAjaxController extends BaseController $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(new UserAutoCompleteFormatter($this->container))); + $this->response->json($filter->format($this->userAutoCompleteFormatter)); } /** @@ -35,9 +34,10 @@ class UserAjaxController extends BaseController public function mention() { $project_id = $this->request->getStringParam('project_id'); - $query = $this->request->getStringParam('q'); + $query = $this->request->getStringParam('search'); $users = $this->projectPermissionModel->findUsernames($project_id, $query); - $this->response->json($users); + + $this->response->json($this->userMentionFormatter->withUsers($users)->format()); } /** diff --git a/app/Controller/UserApiAccessController.php b/app/Controller/UserApiAccessController.php new file mode 100644 index 00000000..e03514d5 --- /dev/null +++ b/app/Controller/UserApiAccessController.php @@ -0,0 +1,50 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Security\Token; + +/** + * Class UserApiAccessController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class UserApiAccessController extends BaseController +{ + public function show() + { + $user = $this->getUser(); + + return $this->response->html($this->helper->layout->user('user_api_access/show', array( + 'user' => $user, + 'title' => t('API User Access'), + ))); + } + + public function generate() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + $this->userModel->update(array( + 'id' => $user['id'], + 'api_access_token' => Token::getToken(), + )); + + $this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id']))); + } + + public function remove() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + $this->userModel->update(array( + 'id' => $user['id'], + 'api_access_token' => null, + )); + + $this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id']))); + } +}
\ No newline at end of file diff --git a/app/Controller/UserCreationController.php b/app/Controller/UserCreationController.php index 9c873f85..27f1687b 100644 --- a/app/Controller/UserCreationController.php +++ b/app/Controller/UserCreationController.php @@ -22,10 +22,7 @@ class UserCreationController extends BaseController */ public function show(array $values = array(), array $errors = array()) { - $isRemote = $this->request->getIntegerParam('remote') == 1 || (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1); - $template = $isRemote ? 'user_creation/remote' : 'user_creation/local'; - - $this->response->html($this->template->render($template, array( + $this->response->html($this->template->render('user_creation/show', array( 'timezones' => $this->timezoneModel->getTimezones(true), 'languages' => $this->languageModel->getLanguages(true), 'roles' => $this->role->getApplicationRoles(), @@ -57,7 +54,7 @@ class UserCreationController extends BaseController * * @param array $values */ - private function createUser(array $values) + protected function createUser(array $values) { $project_id = empty($values['project_id']) ? 0 : $values['project_id']; unset($values['project_id']); diff --git a/app/Controller/UserCredentialController.php b/app/Controller/UserCredentialController.php index 4021dc37..98fe967d 100644 --- a/app/Controller/UserCredentialController.php +++ b/app/Controller/UserCredentialController.php @@ -106,4 +106,21 @@ class UserCredentialController extends BaseController return $this->changeAuthentication($values, $errors); } + + /** + * Unlock user + */ + public function unlock() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + if ($this->userLockingModel->resetFailedLogin($user['username'])) { + $this->flash->success(t('User unlocked successfully.')); + } else { + $this->flash->failure(t('Unable to unlock the user.')); + } + + $this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id']))); + } } diff --git a/app/Controller/UserImportController.php b/app/Controller/UserImportController.php index fec9a31d..6a9d5992 100644 --- a/app/Controller/UserImportController.php +++ b/app/Controller/UserImportController.php @@ -23,7 +23,7 @@ class UserImportController extends BaseController $this->response->html($this->template->render('user_import/show', array( 'values' => $values, 'errors' => $errors, - 'max_size' => ini_get('upload_max_filesize'), + 'max_size' => get_upload_max_size(), 'delimiters' => Csv::getDelimiters(), 'enclosures' => Csv::getEnclosures(), ))); diff --git a/app/Controller/UserInviteController.php b/app/Controller/UserInviteController.php new file mode 100644 index 00000000..8c77940c --- /dev/null +++ b/app/Controller/UserInviteController.php @@ -0,0 +1,107 @@ +<?php + +namespace Kanboard\Controller; + +use Kanboard\Core\Controller\PageNotFoundException; +use Kanboard\Core\Security\Role; +use Kanboard\Notification\MailNotification; + +/** + * Class UserInviteController + * + * @package Kanboard\Controller + * @author Frederic Guillot + */ +class UserInviteController extends BaseController +{ + public function show(array $values = array(), array $errors = array()) + { + $this->response->html($this->template->render('user_invite/show', array( + 'projects' => $this->projectModel->getList(), + 'errors' => $errors, + 'values' => $values, + ))); + } + + public function save() + { + $values = $this->request->getValues(); + + if (! empty($values['emails']) && isset($values['project_id'])) { + $emails = explode("\r\n", trim($values['emails'])); + $nb = $this->inviteModel->createInvites($emails, $values['project_id']); + $this->flash->success($nb > 1 ? t('%d invitations were sent.', $nb) : t('%d invitation was sent.', $nb)); + } + + $this->response->redirect($this->helper->url->to('UserListController', 'show')); + } + + public function signup(array $values = array(), array $errors = array()) + { + $invite = $this->getInvite(); + + $this->response->html($this->helper->layout->app('user_invite/signup', array( + 'no_layout' => true, + 'not_editable' => true, + 'token' => $invite['token'], + 'errors' => $errors, + 'values' => $values + array('email' => $invite['email']), + 'timezones' => $this->timezoneModel->getTimezones(true), + 'languages' => $this->languageModel->getLanguages(true), + ))); + } + + public function register() + { + $invite = $this->getInvite(); + + $values = $this->request->getValues(); + list($valid, $errors) = $this->userValidator->validateCreation($values); + + if ($valid) { + $this->createUser($invite, $values); + } else { + $this->signup($values, $errors); + } + } + + protected function getInvite() + { + $token = $this->request->getStringParam('token'); + + if (empty($token)) { + throw PageNotFoundException::getInstance()->withoutLayout(); + } + + $invite = $this->inviteModel->getByToken($token); + + if (empty($invite)) { + throw PageNotFoundException::getInstance()->withoutLayout(); + } + + return $invite; + } + + protected function createUser(array $invite, array $values) + { + $user_id = $this->userModel->create($values); + + if ($user_id !== false) { + if ($invite['project_id'] != 0) { + $this->projectUserRoleModel->addUser($invite['project_id'], $user_id, Role::PROJECT_MEMBER); + } + + if (! empty($values['notifications_enabled'])) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, array(MailNotification::TYPE)); + } + + $this->inviteModel->remove($invite['email']); + + $this->flash->success(t('User created successfully.')); + $this->response->redirect($this->helper->url->to('AuthController', 'login')); + } else { + $this->flash->failure(t('Unable to create this user.')); + $this->response->redirect($this->helper->url->to('UserInviteController', 'signup')); + } + } +} diff --git a/app/Controller/UserListController.php b/app/Controller/UserListController.php index 31fcdd44..888583fa 100644 --- a/app/Controller/UserListController.php +++ b/app/Controller/UserListController.php @@ -17,12 +17,7 @@ class UserListController extends BaseController */ public function show() { - $paginator = $this->paginator - ->setUrl('UserListController', 'show') - ->setMax(30) - ->setOrder('username') - ->setQuery($this->userModel->getQuery()) - ->calculate(); + $paginator = $this->userPagination->getListingPaginator(); $this->response->html($this->helper->layout->app('user_list/show', array( 'title' => t('Users').' ('.$paginator->getTotal().')', |