summaryrefslogtreecommitdiff
path: root/app/Controller
diff options
context:
space:
mode:
Diffstat (limited to 'app/Controller')
-rw-r--r--app/Controller/Action.php47
-rw-r--r--app/Controller/Activity.php5
-rw-r--r--app/Controller/Analytic.php108
-rw-r--r--app/Controller/App.php79
-rw-r--r--app/Controller/Auth.php43
-rw-r--r--app/Controller/Base.php259
-rw-r--r--app/Controller/Board.php220
-rw-r--r--app/Controller/BoardPopover.php135
-rw-r--r--app/Controller/BoardTooltip.php126
-rw-r--r--app/Controller/Calendar.php2
-rw-r--r--app/Controller/Captcha.php29
-rw-r--r--app/Controller/Category.php24
-rw-r--r--app/Controller/Column.php92
-rw-r--r--app/Controller/Comment.php49
-rw-r--r--app/Controller/Config.php58
-rw-r--r--app/Controller/Currency.php30
-rw-r--r--app/Controller/Customfilter.php41
-rw-r--r--app/Controller/Doc.php4
-rw-r--r--app/Controller/Export.php6
-rw-r--r--app/Controller/Feed.php4
-rw-r--r--app/Controller/File.php192
-rw-r--r--app/Controller/FileViewer.php116
-rw-r--r--app/Controller/Gantt.php33
-rw-r--r--app/Controller/Group.php250
-rw-r--r--app/Controller/GroupHelper.php24
-rw-r--r--app/Controller/Ical.php3
-rw-r--r--app/Controller/Link.php38
-rw-r--r--app/Controller/Listing.php5
-rw-r--r--app/Controller/Oauth.php83
-rw-r--r--app/Controller/PasswordReset.php120
-rw-r--r--app/Controller/Project.php261
-rw-r--r--app/Controller/ProjectCreation.php125
-rw-r--r--app/Controller/ProjectEdit.php125
-rw-r--r--app/Controller/ProjectFile.php79
-rw-r--r--app/Controller/ProjectOverview.php29
-rw-r--r--app/Controller/ProjectPermission.php191
-rw-r--r--app/Controller/Projectuser.php51
-rw-r--r--app/Controller/Search.php5
-rw-r--r--app/Controller/Subtask.php160
-rw-r--r--app/Controller/SubtaskRestriction.php61
-rw-r--r--app/Controller/SubtaskStatus.php72
-rw-r--r--app/Controller/Swimlane.php129
-rw-r--r--app/Controller/Task.php32
-rw-r--r--app/Controller/TaskExternalLink.php185
-rw-r--r--app/Controller/TaskFile.php98
-rw-r--r--app/Controller/TaskHelper.php57
-rw-r--r--app/Controller/TaskImport.php6
-rw-r--r--app/Controller/TaskRecurrence.php61
-rw-r--r--app/Controller/Taskcreation.php44
-rw-r--r--app/Controller/Taskduplication.php24
-rw-r--r--app/Controller/Tasklink.php65
-rw-r--r--app/Controller/Taskmodification.php142
-rw-r--r--app/Controller/Taskstatus.php46
-rw-r--r--app/Controller/Timer.php34
-rw-r--r--app/Controller/Twofactor.php113
-rw-r--r--app/Controller/User.php178
-rw-r--r--app/Controller/UserHelper.php37
-rw-r--r--app/Controller/UserImport.php6
-rw-r--r--app/Controller/UserStatus.php111
-rw-r--r--app/Controller/Webhook.php53
60 files changed, 2967 insertions, 1838 deletions
diff --git a/app/Controller/Action.php b/app/Controller/Action.php
index 37d1c248..6c324324 100644
--- a/app/Controller/Action.php
+++ b/app/Controller/Action.php
@@ -18,17 +18,18 @@ class Action extends Base
public function index()
{
$project = $this->getProject();
+ $actions = $this->action->getAllByProject($project['id']);
- $this->response->html($this->projectLayout('action/index', array(
+ $this->response->html($this->helper->layout->project('action/index', array(
'values' => array('project_id' => $project['id']),
'project' => $project,
- 'actions' => $this->action->getAllByProject($project['id']),
- 'available_actions' => $this->action->getAvailableActions(),
- 'available_events' => $this->action->getAvailableEvents(),
- 'available_params' => $this->action->getAllActionParameters(),
- 'columns_list' => $this->board->getColumnsList($project['id']),
- 'users_list' => $this->projectPermission->getMemberList($project['id']),
- 'projects_list' => $this->project->getList(false),
+ 'actions' => $actions,
+ 'available_actions' => $this->actionManager->getAvailableActions(),
+ 'available_events' => $this->eventManager->getAll(),
+ 'available_params' => $this->actionManager->getAvailableParameters($actions),
+ 'columns_list' => $this->column->getList($project['id']),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']),
+ 'projects_list' => $this->projectUserRole->getProjectsByUser($this->userSession->getId()),
'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($project['id']),
'links_list' => $this->link->getList(0, false),
@@ -50,10 +51,10 @@ class Action extends Base
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
}
- $this->response->html($this->projectLayout('action/event', array(
+ $this->response->html($this->helper->layout->project('action/event', array(
'values' => $values,
'project' => $project,
- 'events' => $this->action->getCompatibleEvents($values['action_name']),
+ 'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
'title' => t('Automatic actions')
)));
}
@@ -72,21 +73,21 @@ class Action extends Base
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
}
- $action = $this->action->load($values['action_name'], $values['project_id'], $values['event_name']);
+ $action = $this->actionManager->getAction($values['action_name']);
$action_params = $action->getActionRequiredParameters();
if (empty($action_params)) {
$this->doCreation($project, $values + array('params' => array()));
}
- $projects_list = $this->project->getList(false);
+ $projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
unset($projects_list[$project['id']]);
- $this->response->html($this->projectLayout('action/params', array(
+ $this->response->html($this->helper->layout->project('action/params', array(
'values' => $values,
'action_params' => $action_params,
- 'columns_list' => $this->board->getColumnsList($project['id']),
- 'users_list' => $this->projectPermission->getMemberList($project['id']),
+ 'columns_list' => $this->column->getList($project['id']),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']),
'projects_list' => $projects_list,
'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($project['id']),
@@ -115,13 +116,13 @@ class Action extends Base
*/
private function doCreation(array $project, array $values)
{
- list($valid, ) = $this->action->validateCreation($values);
+ list($valid, ) = $this->actionValidator->validateCreation($values);
if ($valid) {
if ($this->action->create($values) !== false) {
- $this->session->flash(t('Your automatic action have been created successfully.'));
+ $this->flash->success(t('Your automatic action have been created successfully.'));
} else {
- $this->session->flashError(t('Unable to create your automatic action.'));
+ $this->flash->failure(t('Unable to create your automatic action.'));
}
}
@@ -137,10 +138,10 @@ class Action extends Base
{
$project = $this->getProject();
- $this->response->html($this->projectLayout('action/remove', array(
+ $this->response->html($this->helper->layout->project('action/remove', array(
'action' => $this->action->getById($this->request->getIntegerParam('action_id')),
- 'available_events' => $this->action->getAvailableEvents(),
- 'available_actions' => $this->action->getAvailableActions(),
+ 'available_events' => $this->eventManager->getAll(),
+ 'available_actions' => $this->actionManager->getAvailableActions(),
'project' => $project,
'title' => t('Remove an action')
)));
@@ -158,9 +159,9 @@ class Action extends Base
$action = $this->action->getById($this->request->getIntegerParam('action_id'));
if (! empty($action) && $this->action->remove($action['id'])) {
- $this->session->flash(t('Action removed successfully.'));
+ $this->flash->success(t('Action removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this action.'));
+ $this->flash->failure(t('Unable to remove this action.'));
}
$this->response->redirect($this->helper->url->to('action', 'index', array('project_id' => $project['id'])));
diff --git a/app/Controller/Activity.php b/app/Controller/Activity.php
index 24327c23..db520ebe 100644
--- a/app/Controller/Activity.php
+++ b/app/Controller/Activity.php
@@ -19,8 +19,7 @@ class Activity extends Base
{
$project = $this->getProject();
- $this->response->html($this->template->layout('activity/project', array(
- 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
+ $this->response->html($this->helper->layout->app('activity/project', array(
'events' => $this->projectActivity->getProject($project['id']),
'project' => $project,
'title' => t('%s\'s activity', $project['name'])
@@ -36,7 +35,7 @@ class Activity extends Base
{
$task = $this->getTask();
- $this->response->html($this->taskLayout('activity/task', array(
+ $this->response->html($this->helper->layout->task('activity/task', array(
'title' => $task['title'],
'task' => $task,
'events' => $this->projectActivity->getTask($task['id']),
diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php
index 1082b462..6ce062c4 100644
--- a/app/Controller/Analytic.php
+++ b/app/Controller/Analytic.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Model\Task as TaskModel;
+
/**
* Project Analytic controller
*
@@ -11,22 +13,6 @@ namespace Kanboard\Controller;
class Analytic extends Base
{
/**
- * Common layout for analytic views
- *
- * @access private
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- private function layout($template, array $params)
- {
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
- $params['content_for_sublayout'] = $this->template->render($template, $params);
-
- return $this->template->layout('analytic/layout', $params);
- }
-
- /**
* Show average Lead and Cycle time
*
* @access public
@@ -34,33 +20,49 @@ class Analytic extends Base
public function leadAndCycleTime()
{
$project = $this->getProject();
- $values = $this->request->getValues();
-
- $this->projectDailyStats->updateTotals($project['id'], date('Y-m-d'));
-
- $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week')));
- $to = $this->request->getStringParam('to', date('Y-m-d'));
+ list($from, $to) = $this->getDates();
- if (! empty($values)) {
- $from = $values['from'];
- $to = $values['to'];
- }
-
- $this->response->html($this->layout('analytic/lead_cycle_time', array(
+ $this->response->html($this->helper->layout->analytic('analytic/lead_cycle_time', array(
'values' => array(
'from' => $from,
'to' => $to,
),
'project' => $project,
- 'average' => $this->projectAnalytic->getAverageLeadAndCycleTime($project['id']),
+ 'average' => $this->averageLeadCycleTimeAnalytic->build($project['id']),
'metrics' => $this->projectDailyStats->getRawMetrics($project['id'], $from, $to),
'date_format' => $this->config->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
+ 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
'title' => t('Lead and Cycle time for "%s"', $project['name']),
)));
}
/**
+ * Show comparison between actual and estimated hours chart
+ *
+ * @access public
+ */
+ public function compareHours()
+ {
+ $project = $this->getProject();
+ $params = $this->getProjectFilters('analytic', 'compareHours');
+ $query = $this->taskFilter->create()->filterByProject($params['project']['id'])->getQuery();
+
+ $paginator = $this->paginator
+ ->setUrl('analytic', 'compareHours', array('project_id' => $project['id']))
+ ->setMax(30)
+ ->setOrder(TaskModel::TABLE.'.id')
+ ->setQuery($query)
+ ->calculate();
+
+ $this->response->html($this->helper->layout->analytic('analytic/compare_hours', array(
+ 'project' => $project,
+ 'paginator' => $paginator,
+ 'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']),
+ 'title' => t('Compare hours for "%s"', $project['name']),
+ )));
+ }
+
+ /**
* Show average time spent by column
*
* @access public
@@ -69,9 +71,9 @@ class Analytic extends Base
{
$project = $this->getProject();
- $this->response->html($this->layout('analytic/avg_time_columns', array(
+ $this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array(
'project' => $project,
- 'metrics' => $this->projectAnalytic->getAverageTimeSpentByColumn($project['id']),
+ 'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']),
'title' => t('Average time spent into each column for "%s"', $project['name']),
)));
}
@@ -85,9 +87,9 @@ class Analytic extends Base
{
$project = $this->getProject();
- $this->response->html($this->layout('analytic/tasks', array(
+ $this->response->html($this->helper->layout->analytic('analytic/tasks', array(
'project' => $project,
- 'metrics' => $this->projectAnalytic->getTaskRepartition($project['id']),
+ 'metrics' => $this->taskDistributionAnalytic->build($project['id']),
'title' => t('Task repartition for "%s"', $project['name']),
)));
}
@@ -101,9 +103,9 @@ class Analytic extends Base
{
$project = $this->getProject();
- $this->response->html($this->layout('analytic/users', array(
+ $this->response->html($this->helper->layout->analytic('analytic/users', array(
'project' => $project,
- 'metrics' => $this->projectAnalytic->getUserRepartition($project['id']),
+ 'metrics' => $this->userDistributionAnalytic->build($project['id']),
'title' => t('User repartition for "%s"', $project['name']),
)));
}
@@ -132,25 +134,18 @@ class Analytic extends Base
* Common method for CFD and Burdown chart
*
* @access private
+ * @param string $template
+ * @param string $column
+ * @param string $title
*/
private function commonAggregateMetrics($template, $column, $title)
{
$project = $this->getProject();
- $values = $this->request->getValues();
-
- $this->projectDailyColumnStats->updateTotals($project['id'], date('Y-m-d'));
-
- $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week')));
- $to = $this->request->getStringParam('to', date('Y-m-d'));
-
- if (! empty($values)) {
- $from = $values['from'];
- $to = $values['to'];
- }
+ list($from, $to) = $this->getDates();
$display_graph = $this->projectDailyColumnStats->countDays($project['id'], $from, $to) >= 2;
- $this->response->html($this->layout($template, array(
+ $this->response->html($this->helper->layout->analytic($template, array(
'values' => array(
'from' => $from,
'to' => $to,
@@ -159,8 +154,23 @@ class Analytic extends Base
'metrics' => $display_graph ? $this->projectDailyColumnStats->getAggregatedMetrics($project['id'], $from, $to, $column) : array(),
'project' => $project,
'date_format' => $this->config->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
+ 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
'title' => t($title, $project['name']),
)));
}
+
+ private function getDates()
+ {
+ $values = $this->request->getValues();
+
+ $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week')));
+ $to = $this->request->getStringParam('to', date('Y-m-d'));
+
+ if (! empty($values)) {
+ $from = $values['from'];
+ $to = $values['to'];
+ }
+
+ return array($from, $to);
+ }
}
diff --git a/app/Controller/App.php b/app/Controller/App.php
index 2fae004c..1ce74506 100644
--- a/app/Controller/App.php
+++ b/app/Controller/App.php
@@ -13,22 +13,6 @@ use Kanboard\Model\Subtask as SubtaskModel;
class App extends Base
{
/**
- * Common layout for dashboard views
- *
- * @access private
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- private function layout($template, array $params)
- {
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
- $params['content_for_sublayout'] = $this->template->render($template, $params);
-
- return $this->template->layout('app/layout', $params);
- }
-
- /**
* Get project pagination
*
* @access private
@@ -42,7 +26,7 @@ class App extends Base
->setUrl('app', $action, array('pagination' => 'projects', 'user_id' => $user_id))
->setMax($max)
->setOrder('name')
- ->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveMemberProjectIds($user_id)))
+ ->setQuery($this->project->getQueryColumnStats($this->projectPermission->getActiveProjectIds($user_id)))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
}
@@ -101,7 +85,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->layout('app/overview', array(
+ $this->response->html($this->helper->layout->dashboard('app/overview', array(
'title' => t('Dashboard'),
'project_paginator' => $this->getProjectPaginator($user['id'], 'index', 10),
'task_paginator' => $this->getTaskPaginator($user['id'], 'index', 10),
@@ -119,7 +103,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->layout('app/tasks', array(
+ $this->response->html($this->helper->layout->dashboard('app/tasks', array(
'title' => t('My tasks'),
'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50),
'user' => $user,
@@ -135,7 +119,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->layout('app/subtasks', array(
+ $this->response->html($this->helper->layout->dashboard('app/subtasks', array(
'title' => t('My subtasks'),
'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50),
'user' => $user,
@@ -151,7 +135,7 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->layout('app/projects', array(
+ $this->response->html($this->helper->layout->dashboard('app/projects', array(
'title' => t('My projects'),
'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25),
'user' => $user,
@@ -167,9 +151,9 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->layout('app/activity', array(
+ $this->response->html($this->helper->layout->dashboard('app/activity', array(
'title' => t('My activity stream'),
- 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveMemberProjectIds($user['id']), 100),
+ 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id']), 100),
'user' => $user,
)));
}
@@ -181,7 +165,7 @@ class App extends Base
*/
public function calendar()
{
- $this->response->html($this->layout('app/calendar', array(
+ $this->response->html($this->helper->layout->dashboard('app/calendar', array(
'title' => t('My calendar'),
'user' => $this->getUser(),
)));
@@ -196,55 +180,10 @@ class App extends Base
{
$user = $this->getUser();
- $this->response->html($this->layout('app/notifications', array(
+ $this->response->html($this->helper->layout->dashboard('app/notifications', array(
'title' => t('My notifications'),
'notifications' => $this->userUnreadNotification->getAll($user['id']),
'user' => $user,
)));
}
-
- /**
- * Render Markdown text and reply with the HTML Code
- *
- * @access public
- */
- public function preview()
- {
- $payload = $this->request->getJson();
-
- if (empty($payload['text'])) {
- $this->response->html('<p>'.t('Nothing to preview...').'</p>');
- }
-
- $this->response->html($this->helper->text->markdown($payload['text']));
- }
-
- /**
- * Task autocompletion (Ajax)
- *
- * @access public
- */
- public function autocomplete()
- {
- $search = $this->request->getStringParam('term');
- $projects = $this->projectPermission->getActiveMemberProjectIds($this->userSession->getId());
-
- if (empty($projects)) {
- $this->response->json(array());
- }
-
- $filter = $this->taskFilterAutoCompleteFormatter
- ->create()
- ->filterByProjects($projects)
- ->excludeTasks(array($this->request->getIntegerParam('exclude_task_id')));
-
- // Search by task id or by title
- if (ctype_digit($search)) {
- $filter->filterById($search);
- } else {
- $filter->filterByTitle($search);
- }
-
- $this->response->json($filter->format());
- }
}
diff --git a/app/Controller/Auth.php b/app/Controller/Auth.php
index 95ad8d9e..46b5a546 100644
--- a/app/Controller/Auth.php
+++ b/app/Controller/Auth.php
@@ -2,8 +2,6 @@
namespace Kanboard\Controller;
-use Gregwar\Captcha\CaptchaBuilder;
-
/**
* Authentication controller
*
@@ -23,8 +21,8 @@ class Auth extends Base
$this->response->redirect($this->helper->url->to('app', 'index'));
}
- $this->response->html($this->template->layout('auth/index', array(
- 'captcha' => isset($values['username']) && $this->authentication->hasCaptcha($values['username']),
+ $this->response->html($this->helper->layout->app('auth/index', array(
+ 'captcha' => ! empty($values['username']) && $this->userLocking->hasCaptcha($values['username']),
'errors' => $errors,
'values' => $values,
'no_layout' => true,
@@ -40,16 +38,11 @@ class Auth extends Base
public function check()
{
$values = $this->request->getValues();
- list($valid, $errors) = $this->authentication->validateForm($values);
+ $this->sessionStorage->hasRememberMe = ! empty($values['remember_me']);
+ list($valid, $errors) = $this->authValidator->validateForm($values);
if ($valid) {
- if (! empty($this->session['login_redirect']) && ! filter_var($this->session['login_redirect'], FILTER_VALIDATE_URL)) {
- $redirect = $this->session['login_redirect'];
- unset($this->session['login_redirect']);
- $this->response->redirect($redirect);
- }
-
- $this->response->redirect($this->helper->url->to('app', 'index'));
+ $this->redirectAfterLogin();
}
$this->login($values, $errors);
@@ -62,23 +55,27 @@ class Auth extends Base
*/
public function logout()
{
- $this->authentication->backend('rememberMe')->destroy($this->userSession->getId());
- $this->session->close();
- $this->response->redirect($this->helper->url->to('auth', 'login'));
+ if (! DISABLE_LOGOUT) {
+ $this->sessionManager->close();
+ $this->response->redirect($this->helper->url->to('auth', 'login'));
+ } else {
+ $this->response->redirect($this->helper->url->to('auth', 'index'));
+ }
}
/**
- * Display captcha image
+ * Redirect the user after the authentication
*
- * @access public
+ * @access private
*/
- public function captcha()
+ private function redirectAfterLogin()
{
- $this->response->contentType('image/jpeg');
+ if (isset($this->sessionStorage->redirectAfterLogin) && ! empty($this->sessionStorage->redirectAfterLogin) && ! filter_var($this->sessionStorage->redirectAfterLogin, FILTER_VALIDATE_URL)) {
+ $redirect = $this->sessionStorage->redirectAfterLogin;
+ unset($this->sessionStorage->redirectAfterLogin);
+ $this->response->redirect($redirect);
+ }
- $builder = new CaptchaBuilder;
- $builder->build();
- $this->session['captcha'] = $builder->getPhrase();
- $builder->output();
+ $this->response->redirect($this->helper->url->to('app', 'index'));
}
}
diff --git a/app/Controller/Base.php b/app/Controller/Base.php
index a955b12c..884c439c 100644
--- a/app/Controller/Base.php
+++ b/app/Controller/Base.php
@@ -2,11 +2,7 @@
namespace Kanboard\Controller;
-use Pimple\Container;
-use Kanboard\Core\Security;
-use Kanboard\Core\Request;
-use Kanboard\Core\Response;
-use Symfony\Component\EventDispatcher\Event;
+use Kanboard\Core\Security\Role;
/**
* Base controller
@@ -17,54 +13,22 @@ use Symfony\Component\EventDispatcher\Event;
abstract class Base extends \Kanboard\Core\Base
{
/**
- * Request instance
- *
- * @accesss protected
- * @var \Kanboard\Core\Request
- */
- protected $request;
-
- /**
- * Response instance
- *
- * @accesss protected
- * @var \Kanboard\Core\Response
- */
- protected $response;
-
- /**
- * Constructor
- *
- * @access public
- * @param \Pimple\Container $container
- */
- public function __construct(Container $container)
- {
- $this->container = $container;
- $this->request = new Request;
- $this->response = new Response;
-
- if (DEBUG) {
- $this->container['logger']->debug('START_REQUEST='.$_SERVER['REQUEST_URI']);
- }
- }
-
- /**
- * Destructor
+ * Method executed before each action
*
* @access public
*/
- public function __destruct()
+ public function beforeAction()
{
- if (DEBUG) {
- foreach ($this->container['db']->getLogMessages() as $message) {
- $this->container['logger']->debug($message);
- }
+ $this->sessionManager->open();
+ $this->dispatcher->dispatch('app.bootstrap');
+ $this->sendHeaders();
+ $this->authenticationManager->checkCurrentSession();
- $this->container['logger']->debug('SQL_QUERIES={nb}', array('nb' => $this->container['db']->nbQueries));
- $this->container['logger']->debug('RENDERING={time}', array('time' => microtime(true) - @$_SERVER['REQUEST_TIME_FLOAT']));
- $this->container['logger']->debug('MEMORY='.$this->helper->text->bytes(memory_get_usage()));
- $this->container['logger']->debug('END_REQUEST='.$_SERVER['REQUEST_URI']);
+ if (! $this->applicationAuthorization->isAllowed($this->router->getController(), $this->router->getAction(), Role::APP_PUBLIC)) {
+ $this->handleAuthentication();
+ $this->handlePostAuthentication();
+ $this->checkApplicationAuthorization();
+ $this->checkProjectAuthorization();
}
}
@@ -73,7 +37,7 @@ abstract class Base extends \Kanboard\Core\Base
*
* @access private
*/
- private function sendHeaders($action)
+ private function sendHeaders()
{
// HTTP secure headers
$this->response->csp($this->container['cspRules']);
@@ -81,7 +45,7 @@ abstract class Base extends \Kanboard\Core\Base
$this->response->xss();
// Allow the public board iframe inclusion
- if (ENABLE_XFRAME && $action !== 'readonly') {
+ if (ENABLE_XFRAME && $this->router->getAction() !== 'readonly') {
$this->response->xframe();
}
@@ -91,53 +55,34 @@ abstract class Base extends \Kanboard\Core\Base
}
/**
- * Method executed before each action
- *
- * @access public
- */
- public function beforeAction($controller, $action)
- {
- // Start the session
- $this->session->open($this->helper->url->dir());
- $this->sendHeaders($action);
- $this->container['dispatcher']->dispatch('session.bootstrap', new Event);
-
- if (! $this->acl->isPublicAction($controller, $action)) {
- $this->handleAuthentication();
- $this->handle2FA($controller, $action);
- $this->handleAuthorization($controller, $action);
-
- $this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
- }
- }
-
- /**
* Check authentication
*
- * @access public
+ * @access private
*/
- public function handleAuthentication()
+ private function handleAuthentication()
{
- if (! $this->authentication->isAuthenticated()) {
+ if (! $this->userSession->isLogged() && ! $this->authenticationManager->preAuthentication()) {
if ($this->request->isAjax()) {
$this->response->text('Not Authorized', 401);
}
- $this->session['login_redirect'] = $this->request->getUri();
+ $this->sessionStorage->redirectAfterLogin = $this->request->getUri();
$this->response->redirect($this->helper->url->to('auth', 'login'));
}
}
/**
- * Check 2FA
+ * Handle Post-Authentication (2FA)
*
- * @access public
+ * @access private
*/
- public function handle2FA($controller, $action)
+ private function handlePostAuthentication()
{
+ $controller = strtolower($this->router->getController());
+ $action = strtolower($this->router->getAction());
$ignore = ($controller === 'twofactor' && in_array($action, array('code', 'check'))) || ($controller === 'auth' && $action === 'logout');
- if ($ignore === false && $this->userSession->has2FA() && ! $this->userSession->check2FA()) {
+ if ($ignore === false && $this->userSession->hasPostAuthentication() && ! $this->userSession->isPostAuthenticationValidated()) {
if ($this->request->isAjax()) {
$this->response->text('Not Authorized', 401);
}
@@ -147,11 +92,23 @@ abstract class Base extends \Kanboard\Core\Base
}
/**
- * Check page access and authorization
+ * Check application authorization
*
- * @access public
+ * @access private
*/
- public function handleAuthorization($controller, $action)
+ private function checkApplicationAuthorization()
+ {
+ if (! $this->helper->user->hasAccess($this->router->getController(), $this->router->getAction())) {
+ $this->forbidden();
+ }
+ }
+
+ /**
+ * Check project authorization
+ *
+ * @access private
+ */
+ private function checkProjectAuthorization()
{
$project_id = $this->request->getIntegerParam('project_id');
$task_id = $this->request->getIntegerParam('task_id');
@@ -161,7 +118,7 @@ abstract class Base extends \Kanboard\Core\Base
$project_id = $this->taskFinder->getProjectId($task_id);
}
- if (! $this->acl->isAllowed($controller, $action, $project_id)) {
+ if ($project_id > 0 && ! $this->helper->user->hasProjectAccess($this->router->getController(), $this->router->getAction(), $project_id)) {
$this->forbidden();
}
}
@@ -169,12 +126,12 @@ abstract class Base extends \Kanboard\Core\Base
/**
* Application not found page (404 error)
*
- * @access public
+ * @access protected
* @param boolean $no_layout Display the layout or not
*/
- public function notfound($no_layout = false)
+ protected function notfound($no_layout = false)
{
- $this->response->html($this->template->layout('app/notfound', array(
+ $this->response->html($this->helper->layout->app('app/notfound', array(
'title' => t('Page not found'),
'no_layout' => $no_layout,
)));
@@ -183,12 +140,16 @@ abstract class Base extends \Kanboard\Core\Base
/**
* Application forbidden page
*
- * @access public
+ * @access protected
* @param boolean $no_layout Display the layout or not
*/
- public function forbidden($no_layout = false)
+ protected function forbidden($no_layout = false)
{
- $this->response->html($this->template->layout('app/forbidden', array(
+ if ($this->request->isAjax()) {
+ $this->response->text('Access Forbidden', 403);
+ }
+
+ $this->response->html($this->helper->layout->app('app/forbidden', array(
'title' => t('Access Forbidden'),
'no_layout' => $no_layout,
)));
@@ -201,7 +162,7 @@ abstract class Base extends \Kanboard\Core\Base
*/
protected function checkCSRFParam()
{
- if (! Security::validateCSRFToken($this->request->getStringParam('csrf_token'))) {
+ if (! $this->token->validateCSRFToken($this->request->getStringParam('csrf_token'))) {
$this->forbidden();
}
}
@@ -219,43 +180,6 @@ abstract class Base extends \Kanboard\Core\Base
}
/**
- * Common layout for task views
- *
- * @access protected
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- protected function taskLayout($template, array $params)
- {
- $content = $this->template->render($template, $params);
- $params['task_content_for_layout'] = $content;
- $params['title'] = $params['task']['project_name'].' &gt; '.$params['task']['title'];
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
-
- return $this->template->layout('task/layout', $params);
- }
-
- /**
- * Common layout for project views
- *
- * @access protected
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- protected function projectLayout($template, array $params, $sidebar_template = 'project/sidebar')
- {
- $content = $this->template->render($template, $params);
- $params['project_content_for_layout'] = $content;
- $params['title'] = $params['project']['name'] === $params['title'] ? $params['title'] : $params['project']['name'].' &gt; '.$params['title'];
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
- $params['sidebar_template'] = $sidebar_template;
-
- return $this->template->layout('project/layout', $params);
- }
-
- /**
* Common method to get a task for task views
*
* @access protected
@@ -278,6 +202,36 @@ abstract class Base extends \Kanboard\Core\Base
}
/**
+ * Get Task or Project file
+ *
+ * @access protected
+ */
+ protected function getFile()
+ {
+ $task_id = $this->request->getIntegerParam('task_id');
+ $file_id = $this->request->getIntegerParam('file_id');
+ $model = 'projectFile';
+
+ if ($task_id > 0) {
+ $model = 'taskFile';
+ $project_id = $this->taskFinder->getProjectId($task_id);
+
+ if ($project_id !== $this->request->getIntegerParam('project_id')) {
+ $this->forbidden();
+ }
+ }
+
+ $file = $this->$model->getById($file_id);
+
+ if (empty($file)) {
+ $this->notfound();
+ }
+
+ $file['model'] = $model;
+ return $file;
+ }
+
+ /**
* Common method to get a project
*
* @access protected
@@ -287,11 +241,10 @@ abstract class Base extends \Kanboard\Core\Base
protected function getProject($project_id = 0)
{
$project_id = $this->request->getIntegerParam('project_id', $project_id);
- $project = $this->project->getById($project_id);
+ $project = $this->project->getByIdWithOwner($project_id);
if (empty($project)) {
- $this->session->flashError(t('Project not found.'));
- $this->response->redirect($this->helper->url->to('project', 'index'));
+ $this->notfound();
}
return $project;
@@ -319,15 +272,35 @@ abstract class Base extends \Kanboard\Core\Base
}
/**
+ * Get the current subtask
+ *
+ * @access protected
+ * @return array
+ */
+ protected function getSubtask()
+ {
+ $subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
+
+ if (empty($subtask)) {
+ $this->notfound();
+ }
+
+ return $subtask;
+ }
+
+ /**
* Common method to get project filters
*
* @access protected
+ * @param string $controller
+ * @param string $action
+ * @return array
*/
protected function getProjectFilters($controller, $action)
{
$project = $this->getProject();
$search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id']));
- $board_selector = $this->projectPermission->getAllowedProjects($this->userSession->getId());
+ $board_selector = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
unset($board_selector[$project['id']]);
$filters = array(
@@ -344,6 +317,30 @@ abstract class Base extends \Kanboard\Core\Base
'board_selector' => $board_selector,
'filters' => $filters,
'title' => $project['name'],
+ 'description' => $this->getProjectDescription($project),
);
}
+
+ /**
+ * Get project description
+ *
+ * @access protected
+ * @param array &$project
+ * @return string
+ */
+ protected function getProjectDescription(array &$project)
+ {
+ if ($project['owner_id'] > 0) {
+ $description = t('Project owner: ').'**'.$this->template->e($project['owner_name'] ?: $project['owner_username']).'**'.PHP_EOL.PHP_EOL;
+
+ if (! empty($project['description'])) {
+ $description .= '***'.PHP_EOL.PHP_EOL;
+ $description .= $project['description'];
+ }
+ } else {
+ $description = $project['description'];
+ }
+
+ return $description;
+ }
}
diff --git a/app/Controller/Board.php b/app/Controller/Board.php
index 2d75db89..199f1703 100644
--- a/app/Controller/Board.php
+++ b/app/Controller/Board.php
@@ -27,7 +27,7 @@ class Board extends Base
}
// Display the board with a specific layout
- $this->response->html($this->template->layout('board/view_public', array(
+ $this->response->html($this->helper->layout->app('board/view_public', array(
'project' => $project,
'swimlanes' => $this->board->getBoard($project['id']),
'title' => $project['name'],
@@ -49,12 +49,11 @@ class Board extends Base
{
$params = $this->getProjectFilters('board', 'show');
- $this->response->html($this->template->layout('board/view_private', array(
+ $this->response->html($this->helper->layout->app('board/view_private', array(
'categories_list' => $this->category->getList($params['project']['id'], false),
- 'users_list' => $this->projectPermission->getMemberList($params['project']['id'], false),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
'swimlanes' => $this->taskFilter->search($params['filters']['search'])->getBoard($params['project']['id']),
- 'description' => $params['project']['description'],
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
'board_highlight_period' => $this->config->get('board_highlight_period'),
) + $params));
@@ -73,10 +72,6 @@ class Board extends Base
return $this->response->status(403);
}
- if (! $this->projectPermission->isUserAllowed($project_id, $this->userSession->getId())) {
- $this->response->text('Forbidden', 403);
- }
-
$values = $this->request->getJson();
$result =$this->taskPosition->movePosition(
@@ -101,22 +96,18 @@ class Board extends Base
*/
public function check()
{
- if (! $this->request->isAjax()) {
- return $this->response->status(403);
- }
-
$project_id = $this->request->getIntegerParam('project_id');
$timestamp = $this->request->getIntegerParam('timestamp');
- if (! $this->projectPermission->isUserAllowed($project_id, $this->userSession->getId())) {
- $this->response->text('Forbidden', 403);
+ if (! $project_id || ! $this->request->isAjax()) {
+ return $this->response->status(403);
}
if (! $this->project->isModifiedSince($project_id, $timestamp)) {
return $this->response->status(304);
}
- $this->response->html($this->renderBoard($project_id));
+ return $this->response->html($this->renderBoard($project_id));
}
/**
@@ -126,14 +117,10 @@ class Board extends Base
*/
public function reload()
{
- if (! $this->request->isAjax()) {
- return $this->response->status(403);
- }
-
$project_id = $this->request->getIntegerParam('project_id');
- if (! $this->projectPermission->isUserAllowed($project_id, $this->userSession->getId())) {
- $this->response->text('Forbidden', 403);
+ if (! $project_id || ! $this->request->isAjax()) {
+ return $this->response->status(403);
}
$values = $this->request->getJson();
@@ -143,195 +130,6 @@ class Board extends Base
}
/**
- * Get links on mouseover
- *
- * @access public
- */
- public function tasklinks()
- {
- $task = $this->getTask();
- $this->response->html($this->template->render('board/tooltip_tasklinks', array(
- 'links' => $this->taskLink->getAll($task['id']),
- 'task' => $task,
- )));
- }
-
- /**
- * Get subtasks on mouseover
- *
- * @access public
- */
- public function subtasks()
- {
- $task = $this->getTask();
- $this->response->html($this->template->render('board/tooltip_subtasks', array(
- 'subtasks' => $this->subtask->getAll($task['id']),
- 'task' => $task,
- )));
- }
-
- /**
- * Display all attachments during the task mouseover
- *
- * @access public
- */
- public function attachments()
- {
- $task = $this->getTask();
-
- $this->response->html($this->template->render('board/tooltip_files', array(
- 'files' => $this->file->getAll($task['id']),
- 'task' => $task,
- )));
- }
-
- /**
- * Display comments during a task mouseover
- *
- * @access public
- */
- public function comments()
- {
- $task = $this->getTask();
-
- $this->response->html($this->template->render('board/tooltip_comments', array(
- 'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting())
- )));
- }
-
- /**
- * Display task description
- *
- * @access public
- */
- public function description()
- {
- $task = $this->getTask();
-
- $this->response->html($this->template->render('board/tooltip_description', array(
- 'task' => $task
- )));
- }
-
- /**
- * Change a task assignee directly from the board
- *
- * @access public
- */
- public function changeAssignee()
- {
- $task = $this->getTask();
- $project = $this->project->getById($task['project_id']);
-
- $this->response->html($this->template->render('board/popover_assignee', array(
- 'values' => $task,
- 'users_list' => $this->projectPermission->getMemberList($project['id']),
- 'project' => $project,
- )));
- }
-
- /**
- * Validate an assignee modification
- *
- * @access public
- */
- public function updateAssignee()
- {
- $values = $this->request->getValues();
-
- list($valid, ) = $this->taskValidator->validateAssigneeModification($values);
-
- if ($valid && $this->taskModification->update($values)) {
- $this->session->flash(t('Task updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update your task.'));
- }
-
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id'])));
- }
-
- /**
- * Change a task category directly from the board
- *
- * @access public
- */
- public function changeCategory()
- {
- $task = $this->getTask();
- $project = $this->project->getById($task['project_id']);
-
- $this->response->html($this->template->render('board/popover_category', array(
- 'values' => $task,
- 'categories_list' => $this->category->getList($project['id']),
- 'project' => $project,
- )));
- }
-
- /**
- * Validate a category modification
- *
- * @access public
- */
- public function updateCategory()
- {
- $values = $this->request->getValues();
-
- list($valid, ) = $this->taskValidator->validateCategoryModification($values);
-
- if ($valid && $this->taskModification->update($values)) {
- $this->session->flash(t('Task updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update your task.'));
- }
-
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id'])));
- }
-
- /**
- * Screenshot popover
- *
- * @access public
- */
- public function screenshot()
- {
- $task = $this->getTask();
-
- $this->response->html($this->template->render('file/screenshot', array(
- 'task' => $task,
- 'redirect' => 'board',
- )));
- }
-
- /**
- * Get recurrence information on mouseover
- *
- * @access public
- */
- public function recurrence()
- {
- $task = $this->getTask();
-
- $this->response->html($this->template->render('task/recurring_info', array(
- 'task' => $task,
- 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
- 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
- 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(),
- )));
- }
-
- /**
- * Display swimlane description in tooltip
- *
- * @access public
- */
- public function swimlane()
- {
- $this->getProject();
- $swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id'));
- $this->response->html($this->template->render('board/tooltip_description', array('task' => $swimlane)));
- }
-
- /**
* Enable collapsed mode
*
* @access public
@@ -355,6 +153,7 @@ class Board extends Base
* Change display mode
*
* @access private
+ * @param boolean $mode
*/
private function changeDisplayMode($mode)
{
@@ -372,6 +171,7 @@ class Board extends Base
* Render board
*
* @access private
+ * @param integer $project_id
*/
private function renderBoard($project_id)
{
diff --git a/app/Controller/BoardPopover.php b/app/Controller/BoardPopover.php
new file mode 100644
index 00000000..63dab302
--- /dev/null
+++ b/app/Controller/BoardPopover.php
@@ -0,0 +1,135 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Board Popover
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class BoardPopover extends Base
+{
+ /**
+ * Change a task assignee directly from the board
+ *
+ * @access public
+ */
+ public function changeAssignee()
+ {
+ $task = $this->getTask();
+ $project = $this->project->getById($task['project_id']);
+
+ $this->response->html($this->template->render('board/popover_assignee', array(
+ 'values' => $task,
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id']),
+ 'project' => $project,
+ )));
+ }
+
+ /**
+ * Validate an assignee modification
+ *
+ * @access public
+ */
+ public function updateAssignee()
+ {
+ $values = $this->request->getValues();
+
+ list($valid, ) = $this->taskValidator->validateAssigneeModification($values);
+
+ if ($valid && $this->taskModification->update($values)) {
+ $this->flash->success(t('Task updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update your task.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id'])));
+ }
+
+ /**
+ * Change a task category directly from the board
+ *
+ * @access public
+ */
+ public function changeCategory()
+ {
+ $task = $this->getTask();
+ $project = $this->project->getById($task['project_id']);
+
+ $this->response->html($this->template->render('board/popover_category', array(
+ 'values' => $task,
+ 'categories_list' => $this->category->getList($project['id']),
+ 'project' => $project,
+ )));
+ }
+
+ /**
+ * Validate a category modification
+ *
+ * @access public
+ */
+ public function updateCategory()
+ {
+ $values = $this->request->getValues();
+
+ list($valid, ) = $this->taskValidator->validateCategoryModification($values);
+
+ if ($valid && $this->taskModification->update($values)) {
+ $this->flash->success(t('Task updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update your task.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $values['project_id'])));
+ }
+
+ /**
+ * Screenshot popover
+ *
+ * @access public
+ */
+ public function screenshot()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->template->render('task_file/screenshot', array(
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * Confirmation before to close all column tasks
+ *
+ * @access public
+ */
+ public function confirmCloseColumnTasks()
+ {
+ $project = $this->getProject();
+ $column_id = $this->request->getIntegerParam('column_id');
+ $swimlane_id = $this->request->getIntegerParam('swimlane_id');
+
+ $this->response->html($this->template->render('board/popover_close_all_tasks_column', array(
+ 'project' => $project,
+ 'nb_tasks' => $this->taskFinder->countByColumnAndSwimlaneId($project['id'], $column_id, $swimlane_id),
+ 'column' => $this->column->getColumnTitleById($column_id),
+ 'swimlane' => $this->swimlane->getNameById($swimlane_id) ?: t($project['default_swimlane']),
+ 'values' => array('column_id' => $column_id, 'swimlane_id' => $swimlane_id),
+ )));
+ }
+
+ /**
+ * Close all column tasks
+ *
+ * @access public
+ */
+ public function closeColumnTasks()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ $this->taskStatus->closeTasksBySwimlaneAndColumn($values['swimlane_id'], $values['column_id']);
+ $this->flash->success(t('All tasks of the column "%s" and the swimlane "%s" have been closed successfully.', $this->column->getColumnTitleById($values['column_id']), $this->swimlane->getNameById($values['swimlane_id']) ?: t($project['default_swimlane'])));
+ $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])));
+ }
+}
diff --git a/app/Controller/BoardTooltip.php b/app/Controller/BoardTooltip.php
new file mode 100644
index 00000000..bc07ce09
--- /dev/null
+++ b/app/Controller/BoardTooltip.php
@@ -0,0 +1,126 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Board Tooltip
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class BoardTooltip extends Base
+{
+ /**
+ * Get links on mouseover
+ *
+ * @access public
+ */
+ public function tasklinks()
+ {
+ $task = $this->getTask();
+ $this->response->html($this->template->render('board/tooltip_tasklinks', array(
+ 'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * Get links on mouseover
+ *
+ * @access public
+ */
+ public function externallinks()
+ {
+ $task = $this->getTask();
+ $this->response->html($this->template->render('board/tooltip_external_links', array(
+ 'links' => $this->taskExternalLink->getAll($task['id']),
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * Get subtasks on mouseover
+ *
+ * @access public
+ */
+ public function subtasks()
+ {
+ $task = $this->getTask();
+ $this->response->html($this->template->render('board/tooltip_subtasks', array(
+ 'subtasks' => $this->subtask->getAll($task['id']),
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * Display all attachments during the task mouseover
+ *
+ * @access public
+ */
+ public function attachments()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->template->render('board/tooltip_files', array(
+ 'files' => $this->taskFile->getAll($task['id']),
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * Display comments during a task mouseover
+ *
+ * @access public
+ */
+ public function comments()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->template->render('board/tooltip_comments', array(
+ 'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting())
+ )));
+ }
+
+ /**
+ * Display task description
+ *
+ * @access public
+ */
+ public function description()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->template->render('board/tooltip_description', array(
+ 'task' => $task
+ )));
+ }
+
+ /**
+ * Get recurrence information on mouseover
+ *
+ * @access public
+ */
+ public function recurrence()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->template->render('task_recurrence/info', array(
+ 'task' => $task,
+ 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
+ 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
+ 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(),
+ )));
+ }
+
+ /**
+ * Display swimlane description in tooltip
+ *
+ * @access public
+ */
+ public function swimlane()
+ {
+ $this->getProject();
+ $swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id'));
+ $this->response->html($this->template->render('board/tooltip_description', array('task' => $swimlane)));
+ }
+}
diff --git a/app/Controller/Calendar.php b/app/Controller/Calendar.php
index 67a402d3..a0a25e41 100644
--- a/app/Controller/Calendar.php
+++ b/app/Controller/Calendar.php
@@ -20,7 +20,7 @@ class Calendar extends Base
*/
public function show()
{
- $this->response->html($this->template->layout('calendar/show', array(
+ $this->response->html($this->helper->layout->app('calendar/show', array(
'check_interval' => $this->config->get('board_private_refresh_interval'),
) + $this->getProjectFilters('calendar', 'show')));
}
diff --git a/app/Controller/Captcha.php b/app/Controller/Captcha.php
new file mode 100644
index 00000000..fcf081ea
--- /dev/null
+++ b/app/Controller/Captcha.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Gregwar\Captcha\CaptchaBuilder;
+
+/**
+ * Captcha Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Captcha extends Base
+{
+ /**
+ * Display captcha image
+ *
+ * @access public
+ */
+ public function image()
+ {
+ $this->response->contentType('image/jpeg');
+
+ $builder = new CaptchaBuilder;
+ $builder->build();
+ $this->sessionStorage->captcha = $builder->getPhrase();
+ $builder->output();
+ }
+}
diff --git a/app/Controller/Category.php b/app/Controller/Category.php
index 4aefd9fe..258a3b78 100644
--- a/app/Controller/Category.php
+++ b/app/Controller/Category.php
@@ -22,7 +22,7 @@ class Category extends Base
$category = $this->category->getById($this->request->getIntegerParam('category_id'));
if (empty($category)) {
- $this->session->flashError(t('Category not found.'));
+ $this->flash->failure(t('Category not found.'));
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project_id)));
}
@@ -38,7 +38,7 @@ class Category extends Base
{
$project = $this->getProject();
- $this->response->html($this->projectLayout('category/index', array(
+ $this->response->html($this->helper->layout->project('category/index', array(
'categories' => $this->category->getList($project['id'], false),
'values' => $values + array('project_id' => $project['id']),
'errors' => $errors,
@@ -57,14 +57,14 @@ class Category extends Base
$project = $this->getProject();
$values = $this->request->getValues();
- list($valid, $errors) = $this->category->validateCreation($values);
+ list($valid, $errors) = $this->categoryValidator->validateCreation($values);
if ($valid) {
if ($this->category->create($values)) {
- $this->session->flash(t('Your category have been created successfully.'));
+ $this->flash->success(t('Your category have been created successfully.'));
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to create your category.'));
+ $this->flash->failure(t('Unable to create your category.'));
}
}
@@ -81,7 +81,7 @@ class Category extends Base
$project = $this->getProject();
$category = $this->getCategory($project['id']);
- $this->response->html($this->projectLayout('category/edit', array(
+ $this->response->html($this->helper->layout->project('category/edit', array(
'values' => empty($values) ? $category : $values,
'errors' => $errors,
'project' => $project,
@@ -99,14 +99,14 @@ class Category extends Base
$project = $this->getProject();
$values = $this->request->getValues();
- list($valid, $errors) = $this->category->validateModification($values);
+ list($valid, $errors) = $this->categoryValidator->validateModification($values);
if ($valid) {
if ($this->category->update($values)) {
- $this->session->flash(t('Your category have been updated successfully.'));
+ $this->flash->success(t('Your category have been updated successfully.'));
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to update your category.'));
+ $this->flash->failure(t('Unable to update your category.'));
}
}
@@ -123,7 +123,7 @@ class Category extends Base
$project = $this->getProject();
$category = $this->getCategory($project['id']);
- $this->response->html($this->projectLayout('category/remove', array(
+ $this->response->html($this->helper->layout->project('category/remove', array(
'project' => $project,
'category' => $category,
'title' => t('Remove a category')
@@ -142,9 +142,9 @@ class Category extends Base
$category = $this->getCategory($project['id']);
if ($this->category->remove($category['id'])) {
- $this->session->flash(t('Category removed successfully.'));
+ $this->flash->success(t('Category removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this category.'));
+ $this->flash->failure(t('Unable to remove this category.'));
}
$this->response->redirect($this->helper->url->to('category', 'index', array('project_id' => $project['id'])));
diff --git a/app/Controller/Column.php b/app/Controller/Column.php
index d28fb293..66073b56 100644
--- a/app/Controller/Column.php
+++ b/app/Controller/Column.php
@@ -15,20 +15,12 @@ class Column extends Base
*
* @access public
*/
- public function index(array $values = array(), array $errors = array())
+ public function index()
{
$project = $this->getProject();
- $columns = $this->board->getColumns($project['id']);
+ $columns = $this->column->getAll($project['id']);
- foreach ($columns as $column) {
- $values['title['.$column['id'].']'] = $column['title'];
- $values['description['.$column['id'].']'] = $column['description'];
- $values['task_limit['.$column['id'].']'] = $column['task_limit'] ?: null;
- }
-
- $this->response->html($this->projectLayout('column/index', array(
- 'errors' => $errors,
- 'values' => $values + array('project_id' => $project['id']),
+ $this->response->html($this->helper->layout->project('column/index', array(
'columns' => $columns,
'project' => $project,
'title' => t('Edit board')
@@ -36,33 +28,48 @@ class Column extends Base
}
/**
- * Validate and add a new column
+ * Show form to create a new column
*
* @access public
*/
- public function create()
+ public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
- $columns = $this->board->getColumnsList($project['id']);
- $data = $this->request->getValues();
- $values = array();
- foreach ($columns as $column_id => $column_title) {
- $values['title['.$column_id.']'] = $column_title;
+ if (empty($values)) {
+ $values = array('project_id' => $project['id']);
}
- list($valid, $errors) = $this->board->validateCreation($data);
+ $this->response->html($this->template->render('column/create', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'project' => $project,
+ 'title' => t('Add a new column')
+ )));
+ }
+
+ /**
+ * Validate and add a new column
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->columnValidator->validateCreation($values);
if ($valid) {
- if ($this->board->addColumn($project['id'], $data['title'], $data['task_limit'], $data['description'])) {
- $this->session->flash(t('Board updated successfully.'));
- $this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
+ if ($this->column->create($project['id'], $values['title'], $values['task_limit'], $values['description'])) {
+ $this->flash->success(t('Column created successfully.'));
+ return $this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])), true);
} else {
- $this->session->flashError(t('Unable to update this board.'));
+ $errors['title'] = array(t('Another column with the same name exists in the project'));
}
}
- $this->index($values, $errors);
+ $this->create($values, $errors);
}
/**
@@ -73,9 +80,9 @@ class Column extends Base
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
- $column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
+ $column = $this->column->getById($this->request->getIntegerParam('column_id'));
- $this->response->html($this->projectLayout('column/edit', array(
+ $this->response->html($this->helper->layout->project('column/edit', array(
'errors' => $errors,
'values' => $values ?: $column,
'project' => $project,
@@ -94,14 +101,14 @@ class Column extends Base
$project = $this->getProject();
$values = $this->request->getValues();
- list($valid, $errors) = $this->board->validateModification($values);
+ list($valid, $errors) = $this->columnValidator->validateModification($values);
if ($valid) {
- if ($this->board->updateColumn($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
- $this->session->flash(t('Board updated successfully.'));
+ if ($this->column->update($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
+ $this->flash->success(t('Board updated successfully.'));
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to update this board.'));
+ $this->flash->failure(t('Unable to update this board.'));
}
}
@@ -109,22 +116,21 @@ class Column extends Base
}
/**
- * Move a column up or down
+ * Move column position
*
* @access public
*/
public function move()
{
- $this->checkCSRFParam();
$project = $this->getProject();
- $column_id = $this->request->getIntegerParam('column_id');
- $direction = $this->request->getStringParam('direction');
+ $values = $this->request->getJson();
- if ($direction === 'up' || $direction === 'down') {
- $this->board->{'move'.$direction}($project['id'], $column_id);
+ if (! empty($values) && isset($values['column_id']) && isset($values['position'])) {
+ $result = $this->column->changePosition($project['id'], $values['column_id'], $values['position']);
+ return $this->response->json(array('result' => $result));
}
- $this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
+ $this->forbidden();
}
/**
@@ -136,8 +142,8 @@ class Column extends Base
{
$project = $this->getProject();
- $this->response->html($this->projectLayout('column/remove', array(
- 'column' => $this->board->getColumn($this->request->getIntegerParam('column_id')),
+ $this->response->html($this->helper->layout->project('column/remove', array(
+ 'column' => $this->column->getById($this->request->getIntegerParam('column_id')),
'project' => $project,
'title' => t('Remove a column from a board')
)));
@@ -152,12 +158,12 @@ class Column extends Base
{
$project = $this->getProject();
$this->checkCSRFParam();
- $column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
+ $column_id = $this->request->getIntegerParam('column_id');
- if (! empty($column) && $this->board->removeColumn($column['id'])) {
- $this->session->flash(t('Column removed successfully.'));
+ if ($this->column->remove($column_id)) {
+ $this->flash->success(t('Column removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this column.'));
+ $this->flash->failure(t('Unable to remove this column.'));
}
$this->response->redirect($this->helper->url->to('column', 'index', array('project_id' => $project['id'])));
diff --git a/app/Controller/Comment.php b/app/Controller/Comment.php
index d6cbbf1e..da3213e0 100644
--- a/app/Controller/Comment.php
+++ b/app/Controller/Comment.php
@@ -21,13 +21,11 @@ class Comment extends Base
$comment = $this->comment->getById($this->request->getIntegerParam('comment_id'));
if (empty($comment)) {
- $this->notfound();
+ return $this->notfound();
}
if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
- $this->response->html($this->template->layout('comment/forbidden', array(
- 'title' => t('Access Forbidden')
- )));
+ return $this->forbidden();
}
return $comment;
@@ -41,7 +39,6 @@ class Comment extends Base
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
- $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
if (empty($values)) {
$values = array(
@@ -50,16 +47,7 @@ class Comment extends Base
);
}
- if ($ajax) {
- $this->response->html($this->template->render('comment/create', array(
- 'values' => $values,
- 'errors' => $errors,
- 'task' => $task,
- 'ajax' => $ajax,
- )));
- }
-
- $this->response->html($this->taskLayout('comment/create', array(
+ $this->response->html($this->helper->layout->task('comment/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
@@ -76,22 +64,17 @@ class Comment extends Base
{
$task = $this->getTask();
$values = $this->request->getValues();
- $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
- list($valid, $errors) = $this->comment->validateCreation($values);
+ list($valid, $errors) = $this->commentValidator->validateCreation($values);
if ($valid) {
if ($this->comment->create($values)) {
- $this->session->flash(t('Comment added successfully.'));
+ $this->flash->success(t('Comment added successfully.'));
} else {
- $this->session->flashError(t('Unable to create your comment.'));
+ $this->flash->failure(t('Unable to create your comment.'));
}
- if ($ajax) {
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
}
$this->create($values, $errors);
@@ -107,7 +90,7 @@ class Comment extends Base
$task = $this->getTask();
$comment = $this->getComment();
- $this->response->html($this->taskLayout('comment/edit', array(
+ $this->response->html($this->helper->layout->task('comment/edit', array(
'values' => empty($values) ? $comment : $values,
'errors' => $errors,
'comment' => $comment,
@@ -124,19 +107,19 @@ class Comment extends Base
public function update()
{
$task = $this->getTask();
- $comment = $this->getComment();
+ $this->getComment();
$values = $this->request->getValues();
- list($valid, $errors) = $this->comment->validateModification($values);
+ list($valid, $errors) = $this->commentValidator->validateModification($values);
if ($valid) {
if ($this->comment->update($values)) {
- $this->session->flash(t('Comment updated successfully.'));
+ $this->flash->success(t('Comment updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update your comment.'));
+ $this->flash->failure(t('Unable to update your comment.'));
}
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comment-'.$comment['id']));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), false);
}
$this->edit($values, $errors);
@@ -152,7 +135,7 @@ class Comment extends Base
$task = $this->getTask();
$comment = $this->getComment();
- $this->response->html($this->taskLayout('comment/remove', array(
+ $this->response->html($this->helper->layout->task('comment/remove', array(
'comment' => $comment,
'task' => $task,
'title' => t('Remove a comment')
@@ -171,9 +154,9 @@ class Comment extends Base
$comment = $this->getComment();
if ($this->comment->remove($comment['id'])) {
- $this->session->flash(t('Comment removed successfully.'));
+ $this->flash->success(t('Comment removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this comment.'));
+ $this->flash->failure(t('Unable to remove this comment.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'));
diff --git a/app/Controller/Config.php b/app/Controller/Config.php
index 47b844e4..e811f870 100644
--- a/app/Controller/Config.php
+++ b/app/Controller/Config.php
@@ -11,24 +11,6 @@ namespace Kanboard\Controller;
class Config extends Base
{
/**
- * Common layout for config views
- *
- * @access private
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- private function layout($template, array $params)
- {
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
- $params['values'] = $this->config->getAll();
- $params['errors'] = array();
- $params['config_content_for_layout'] = $this->template->render($template, $params);
-
- return $this->template->layout('config/layout', $params);
- }
-
- /**
* Common method between pages
*
* @access private
@@ -40,8 +22,16 @@ class Config extends Base
$values = $this->request->getValues();
switch ($redirect) {
+ case 'application':
+ $values += array('password_reset' => 0);
+ break;
case 'project':
- $values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0, 'cfd_include_closed_tasks' => 0);
+ $values += array(
+ 'subtask_restriction' => 0,
+ 'subtask_time_tracking' => 0,
+ 'cfd_include_closed_tasks' => 0,
+ 'disable_private_project' => 0,
+ );
break;
case 'integrations':
$values += array('integration_gravatar' => 0);
@@ -53,9 +43,9 @@ class Config extends Base
if ($this->config->save($values)) {
$this->config->reload();
- $this->session->flash(t('Settings saved successfully.'));
+ $this->flash->success(t('Settings saved successfully.'));
} else {
- $this->session->flashError(t('Unable to save your settings.'));
+ $this->flash->failure(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url->to('config', $redirect));
@@ -69,7 +59,7 @@ class Config extends Base
*/
public function index()
{
- $this->response->html($this->layout('config/about', array(
+ $this->response->html($this->helper->layout->config('config/about', array(
'db_size' => $this->config->getDatabaseSize(),
'title' => t('Settings').' &gt; '.t('About'),
)));
@@ -82,7 +72,7 @@ class Config extends Base
*/
public function plugins()
{
- $this->response->html($this->layout('config/plugins', array(
+ $this->response->html($this->helper->layout->config('config/plugins', array(
'plugins' => $this->pluginLoader->plugins,
'title' => t('Settings').' &gt; '.t('Plugins'),
)));
@@ -97,10 +87,12 @@ class Config extends Base
{
$this->common('application');
- $this->response->html($this->layout('config/application', array(
+ $this->response->html($this->helper->layout->config('config/application', array(
'languages' => $this->config->getLanguages(),
'timezones' => $this->config->getTimezones(),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
+ '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').' &gt; '.t('Application settings'),
)));
}
@@ -114,7 +106,7 @@ class Config extends Base
{
$this->common('project');
- $this->response->html($this->layout('config/project', array(
+ $this->response->html($this->helper->layout->config('config/project', array(
'colors' => $this->color->getList(),
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
'title' => t('Settings').' &gt; '.t('Project settings'),
@@ -130,7 +122,7 @@ class Config extends Base
{
$this->common('board');
- $this->response->html($this->layout('config/board', array(
+ $this->response->html($this->helper->layout->config('config/board', array(
'title' => t('Settings').' &gt; '.t('Board settings'),
)));
}
@@ -144,7 +136,7 @@ class Config extends Base
{
$this->common('calendar');
- $this->response->html($this->layout('config/calendar', array(
+ $this->response->html($this->helper->layout->config('config/calendar', array(
'title' => t('Settings').' &gt; '.t('Calendar settings'),
)));
}
@@ -158,7 +150,7 @@ class Config extends Base
{
$this->common('integrations');
- $this->response->html($this->layout('config/integrations', array(
+ $this->response->html($this->helper->layout->config('config/integrations', array(
'title' => t('Settings').' &gt; '.t('Integrations'),
)));
}
@@ -172,7 +164,7 @@ class Config extends Base
{
$this->common('webhook');
- $this->response->html($this->layout('config/webhook', array(
+ $this->response->html($this->helper->layout->config('config/webhook', array(
'title' => t('Settings').' &gt; '.t('Webhook settings'),
)));
}
@@ -184,7 +176,7 @@ class Config extends Base
*/
public function api()
{
- $this->response->html($this->layout('config/api', array(
+ $this->response->html($this->helper->layout->config('config/api', array(
'title' => t('Settings').' &gt; '.t('API'),
)));
}
@@ -210,7 +202,7 @@ class Config extends Base
{
$this->checkCSRFParam();
$this->config->optimizeDatabase();
- $this->session->flash(t('Database optimization done.'));
+ $this->flash->success(t('Database optimization done.'));
$this->response->redirect($this->helper->url->to('config', 'index'));
}
@@ -226,7 +218,7 @@ class Config extends Base
$this->checkCSRFParam();
$this->config->regenerateToken($type.'_token');
- $this->session->flash(t('Token regenerated.'));
+ $this->flash->success(t('Token regenerated.'));
$this->response->redirect($this->helper->url->to('config', $type));
}
}
diff --git a/app/Controller/Currency.php b/app/Controller/Currency.php
index 9d6b0249..ecaa9834 100644
--- a/app/Controller/Currency.php
+++ b/app/Controller/Currency.php
@@ -11,34 +11,18 @@ namespace Kanboard\Controller;
class Currency extends Base
{
/**
- * Common layout for config views
- *
- * @access private
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- private function layout($template, array $params)
- {
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
- $params['config_content_for_layout'] = $this->template->render($template, $params);
-
- return $this->template->layout('config/layout', $params);
- }
-
- /**
* Display all currency rates and form
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
- $this->response->html($this->layout('currency/index', array(
+ $this->response->html($this->helper->layout->config('currency/index', array(
'config_values' => array('application_currency' => $this->config->get('application_currency')),
'values' => $values,
'errors' => $errors,
'rates' => $this->currency->getAll(),
- 'currencies' => $this->config->getCurrencies(),
+ 'currencies' => $this->currency->getCurrencies(),
'title' => t('Settings').' &gt; '.t('Currency rates'),
)));
}
@@ -51,14 +35,14 @@ class Currency extends Base
public function create()
{
$values = $this->request->getValues();
- list($valid, $errors) = $this->currency->validate($values);
+ list($valid, $errors) = $this->currencyValidator->validateCreation($values);
if ($valid) {
if ($this->currency->create($values['currency'], $values['rate'])) {
- $this->session->flash(t('The currency rate have been added successfully.'));
+ $this->flash->success(t('The currency rate have been added successfully.'));
$this->response->redirect($this->helper->url->to('currency', 'index'));
} else {
- $this->session->flashError(t('Unable to add this currency rate.'));
+ $this->flash->failure(t('Unable to add this currency rate.'));
}
}
@@ -76,9 +60,9 @@ class Currency extends Base
if ($this->config->save($values)) {
$this->config->reload();
- $this->session->flash(t('Settings saved successfully.'));
+ $this->flash->success(t('Settings saved successfully.'));
} else {
- $this->session->flashError(t('Unable to save your settings.'));
+ $this->flash->failure(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url->to('currency', 'index'));
diff --git a/app/Controller/Customfilter.php b/app/Controller/Customfilter.php
index a152c668..41da0b11 100644
--- a/app/Controller/Customfilter.php
+++ b/app/Controller/Customfilter.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Security\Role;
+
/**
* Custom Filter management
*
@@ -19,7 +21,7 @@ class Customfilter extends Base
{
$project = $this->getProject();
- $this->response->html($this->projectLayout('custom_filter/index', array(
+ $this->response->html($this->helper->layout->project('custom_filter/index', array(
'values' => $values + array('project_id' => $project['id']),
'errors' => $errors,
'project' => $project,
@@ -40,14 +42,14 @@ class Customfilter extends Base
$values = $this->request->getValues();
$values['user_id'] = $this->userSession->getId();
- list($valid, $errors) = $this->customFilter->validateCreation($values);
+ list($valid, $errors) = $this->customFilterValidator->validateCreation($values);
if ($valid) {
if ($this->customFilter->create($values)) {
- $this->session->flash(t('Your custom filter have been created successfully.'));
+ $this->flash->success(t('Your custom filter have been created successfully.'));
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to create your custom filter.'));
+ $this->flash->failure(t('Unable to create your custom filter.'));
}
}
@@ -55,6 +57,23 @@ class Customfilter extends Base
}
/**
+ * Confirmation dialog before removing a custom filter
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $filter = $this->customFilter->getById($this->request->getIntegerParam('filter_id'));
+
+ $this->response->html($this->helper->layout->project('custom_filter/remove', array(
+ 'project' => $project,
+ 'filter' => $filter,
+ 'title' => t('Remove a custom filter')
+ )));
+ }
+
+ /**
* Remove a custom filter
*
* @access public
@@ -68,9 +87,9 @@ class Customfilter extends Base
$this->checkPermission($project, $filter);
if ($this->customFilter->remove($filter['id'])) {
- $this->session->flash(t('Custom filter removed successfully.'));
+ $this->flash->success(t('Custom filter removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this custom filter.'));
+ $this->flash->failure(t('Unable to remove this custom filter.'));
}
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
@@ -88,7 +107,7 @@ class Customfilter extends Base
$this->checkPermission($project, $filter);
- $this->response->html($this->projectLayout('custom_filter/edit', array(
+ $this->response->html($this->helper->layout->project('custom_filter/edit', array(
'values' => empty($values) ? $filter : $values,
'errors' => $errors,
'project' => $project,
@@ -119,14 +138,14 @@ class Customfilter extends Base
$values += array('append' => 0);
}
- list($valid, $errors) = $this->customFilter->validateModification($values);
+ list($valid, $errors) = $this->customFilterValidator->validateModification($values);
if ($valid) {
if ($this->customFilter->update($values)) {
- $this->session->flash(t('Your custom filter have been updated successfully.'));
+ $this->flash->success(t('Your custom filter have been updated successfully.'));
$this->response->redirect($this->helper->url->to('customfilter', 'index', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to update custom filter.'));
+ $this->flash->failure(t('Unable to update custom filter.'));
}
}
@@ -137,7 +156,7 @@ class Customfilter extends Base
{
$user_id = $this->userSession->getId();
- if ($filter['user_id'] != $user_id && (! $this->projectPermission->isManager($project['id'], $user_id) || ! $this->userSession->isAdmin())) {
+ if ($filter['user_id'] != $user_id && ($this->projectUserRole->getUserRole($project['id'], $user_id) === Role::PROJECT_MANAGER || ! $this->userSession->isAdmin())) {
$this->forbidden();
}
}
diff --git a/app/Controller/Doc.php b/app/Controller/Doc.php
index 32413048..6f309d48 100644
--- a/app/Controller/Doc.php
+++ b/app/Controller/Doc.php
@@ -52,8 +52,6 @@ class Doc extends Base
}
}
- $this->response->html($this->template->layout('doc/show', $this->readFile($filename) + array(
- 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
- )));
+ $this->response->html($this->helper->layout->app('doc/show', $this->readFile($filename)));
}
}
diff --git a/app/Controller/Export.php b/app/Controller/Export.php
index cdedcb88..726edd42 100644
--- a/app/Controller/Export.php
+++ b/app/Controller/Export.php
@@ -27,7 +27,7 @@ class Export extends Base
$this->response->csv($data);
}
- $this->response->html($this->projectLayout('export/'.$action, array(
+ $this->response->html($this->helper->layout->project('export/'.$action, array(
'values' => array(
'controller' => 'export',
'action' => $action,
@@ -37,7 +37,7 @@ class Export extends Base
),
'errors' => array(),
'date_format' => $this->config->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
+ 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
'project' => $project,
'title' => $page_title,
), 'export/sidebar'));
@@ -70,7 +70,7 @@ class Export extends Base
*/
public function summary()
{
- $this->common('ProjectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
+ $this->common('projectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
}
/**
diff --git a/app/Controller/Feed.php b/app/Controller/Feed.php
index 95b81fb8..8457c383 100644
--- a/app/Controller/Feed.php
+++ b/app/Controller/Feed.php
@@ -25,10 +25,8 @@ class Feed extends Base
$this->forbidden(true);
}
- $projects = $this->projectPermission->getActiveMemberProjects($user['id']);
-
$this->response->xml($this->template->render('feed/user', array(
- 'events' => $this->projectActivity->getProjects(array_keys($projects)),
+ 'events' => $this->projectActivity->getProjects($this->projectPermission->getActiveProjectIds($user['id'])),
'user' => $user,
)));
}
diff --git a/app/Controller/File.php b/app/Controller/File.php
deleted file mode 100644
index 4d771e2f..00000000
--- a/app/Controller/File.php
+++ /dev/null
@@ -1,192 +0,0 @@
-<?php
-
-namespace Kanboard\Controller;
-
-use Kanboard\Core\ObjectStorage\ObjectStorageException;
-
-/**
- * File controller
- *
- * @package controller
- * @author Frederic Guillot
- */
-class File extends Base
-{
- /**
- * Screenshot
- *
- * @access public
- */
- public function screenshot()
- {
- $task = $this->getTask();
-
- if ($this->request->isPost() && $this->file->uploadScreenshot($task['project_id'], $task['id'], $this->request->getValue('screenshot')) !== false) {
- $this->session->flash(t('Screenshot uploaded successfully.'));
-
- if ($this->request->getStringParam('redirect') === 'board') {
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
- }
-
- $this->response->html($this->taskLayout('file/screenshot', array(
- 'task' => $task,
- 'redirect' => 'task',
- )));
- }
-
- /**
- * File upload form
- *
- * @access public
- */
- public function create()
- {
- $task = $this->getTask();
-
- $this->response->html($this->taskLayout('file/new', array(
- 'task' => $task,
- 'max_size' => ini_get('upload_max_filesize'),
- )));
- }
-
- /**
- * File upload (save files)
- *
- * @access public
- */
- public function save()
- {
- $task = $this->getTask();
-
- if (! $this->file->uploadFiles($task['project_id'], $task['id'], 'files')) {
- $this->session->flashError(t('Unable to upload the file.'));
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
- }
-
- /**
- * File download
- *
- * @access public
- */
- public function download()
- {
- try {
- $task = $this->getTask();
- $file = $this->file->getById($this->request->getIntegerParam('file_id'));
-
- if ($file['task_id'] != $task['id']) {
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
- }
-
- $this->response->forceDownload($file['name']);
- $this->objectStorage->output($file['path']);
- } catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
- }
- }
-
- /**
- * Open a file (show the content in a popover)
- *
- * @access public
- */
- public function open()
- {
- $task = $this->getTask();
- $file = $this->file->getById($this->request->getIntegerParam('file_id'));
-
- if ($file['task_id'] == $task['id']) {
- $this->response->html($this->template->render('file/open', array(
- 'file' => $file,
- 'task' => $task,
- )));
- }
- }
-
- /**
- * Display image
- *
- * @access public
- */
- public function image()
- {
- try {
- $task = $this->getTask();
- $file = $this->file->getById($this->request->getIntegerParam('file_id'));
-
- if ($file['task_id'] == $task['id']) {
- $this->response->contentType($this->file->getImageMimeType($file['name']));
- $this->objectStorage->output($file['path']);
- }
- } catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
- }
- }
-
- /**
- * Display image thumbnails
- *
- * @access public
- */
- public function thumbnail()
- {
- $this->response->contentType('image/jpeg');
-
- try {
- $task = $this->getTask();
- $file = $this->file->getById($this->request->getIntegerParam('file_id'));
-
- if ($file['task_id'] == $task['id']) {
- $this->objectStorage->output($this->file->getThumbnailPath($file['path']));
- }
- } catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
-
- // Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19
- $data = $this->objectStorage->get($file['path']);
- $this->file->generateThumbnailFromData($file['path'], $data);
- $this->objectStorage->output($this->file->getThumbnailPath($file['path']));
- }
- }
-
- /**
- * Remove a file
- *
- * @access public
- */
- public function remove()
- {
- $this->checkCSRFParam();
- $task = $this->getTask();
- $file = $this->file->getById($this->request->getIntegerParam('file_id'));
-
- if ($file['task_id'] == $task['id'] && $this->file->remove($file['id'])) {
- $this->session->flash(t('File removed successfully.'));
- } else {
- $this->session->flashError(t('Unable to remove this file.'));
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
- }
-
- /**
- * Confirmation dialog before removing a file
- *
- * @access public
- */
- public function confirm()
- {
- $task = $this->getTask();
- $file = $this->file->getById($this->request->getIntegerParam('file_id'));
-
- $this->response->html($this->taskLayout('file/remove', array(
- 'task' => $task,
- 'file' => $file,
- )));
- }
-}
diff --git a/app/Controller/FileViewer.php b/app/Controller/FileViewer.php
new file mode 100644
index 00000000..bc91c3d8
--- /dev/null
+++ b/app/Controller/FileViewer.php
@@ -0,0 +1,116 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\ObjectStorage\ObjectStorageException;
+
+/**
+ * File Viewer Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class FileViewer extends Base
+{
+ /**
+ * Get file content from object storage
+ *
+ * @access private
+ * @param array $file
+ * @return string
+ */
+ private function getFileContent(array $file)
+ {
+ $content = '';
+
+ try {
+
+ if ($file['is_image'] == 0) {
+ $content = $this->objectStorage->get($file['path']);
+ }
+
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
+
+ return $content;
+ }
+
+ /**
+ * Show file content in a popover
+ *
+ * @access public
+ */
+ public function show()
+ {
+ $file = $this->getFile();
+ $type = $this->helper->file->getPreviewType($file['name']);
+ $params = array('file_id' => $file['id'], 'project_id' => $this->request->getIntegerParam('project_id'));
+
+ if ($file['model'] === 'taskFile') {
+ $params['task_id'] = $file['task_id'];
+ }
+
+ $this->response->html($this->template->render('file_viewer/show', array(
+ 'file' => $file,
+ 'params' => $params,
+ 'type' => $type,
+ 'content' => $this->getFileContent($file),
+ )));
+ }
+
+ /**
+ * Display image
+ *
+ * @access public
+ */
+ public function image()
+ {
+ try {
+ $file = $this->getFile();
+ $this->response->contentType($this->helper->file->getImageMimeType($file['name']));
+ $this->objectStorage->output($file['path']);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
+ }
+
+ /**
+ * Display image thumbnail
+ *
+ * @access public
+ */
+ public function thumbnail()
+ {
+ $this->response->contentType('image/jpeg');
+
+ try {
+ $file = $this->getFile();
+ $model = $file['model'];
+ $this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+
+ // Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19
+ $data = $this->objectStorage->get($file['path']);
+ $this->$model->generateThumbnailFromData($file['path'], $data);
+ $this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
+ }
+ }
+
+ /**
+ * File download
+ *
+ * @access public
+ */
+ public function download()
+ {
+ try {
+ $file = $this->getFile();
+ $this->response->forceDownload($file['name']);
+ $this->objectStorage->output($file['path']);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
+ }
+}
diff --git a/app/Controller/Gantt.php b/app/Controller/Gantt.php
index 24d94f02..9ffa277f 100644
--- a/app/Controller/Gantt.php
+++ b/app/Controller/Gantt.php
@@ -20,13 +20,12 @@ class Gantt extends Base
if ($this->userSession->isAdmin()) {
$project_ids = $this->project->getAllIds();
} else {
- $project_ids = $this->projectPermission->getMemberProjectIds($this->userSession->getId());
+ $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
}
- $this->response->html($this->template->layout('gantt/projects', array(
+ $this->response->html($this->helper->layout->app('gantt/projects', array(
'projects' => $this->projectGanttFormatter->filter($project_ids)->format(),
'title' => t('Gantt chart for all projects'),
- 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
)));
}
@@ -65,8 +64,8 @@ class Gantt extends Base
$filter->getQuery()->asc('column_position')->asc(TaskModel::TABLE.'.position');
}
- $this->response->html($this->template->layout('gantt/project', $params + array(
- 'users_list' => $this->projectPermission->getMemberList($params['project']['id'], false),
+ $this->response->html($this->helper->layout->app('gantt/project', $params + array(
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
'sorting' => $sorting,
'tasks' => $filter->format(),
)));
@@ -102,19 +101,23 @@ class Gantt extends Base
{
$project = $this->getProject();
+ $values = $values + array(
+ 'project_id' => $project['id'],
+ 'column_id' => $this->column->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('gantt/task_creation', array(
+ 'project' => $project,
'errors' => $errors,
- 'values' => $values + array(
- 'project_id' => $project['id'],
- 'column_id' => $this->board->getFirstColumn($project['id']),
- 'position' => 1
- ),
- 'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true),
+ 'values' => $values,
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], true, false, true),
'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($project['id']),
'swimlanes_list' => $this->swimlane->getList($project['id'], false, true),
- 'date_format' => $this->config->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
'title' => $project['name'].' &gt; '.t('New task')
)));
}
@@ -135,10 +138,10 @@ class Gantt extends Base
$task_id = $this->taskCreation->create($values);
if ($task_id !== false) {
- $this->session->flash(t('Task created successfully.'));
+ $this->flash->success(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('gantt', 'project', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to create your task.'));
+ $this->flash->failure(t('Unable to create your task.'));
}
}
diff --git a/app/Controller/Group.php b/app/Controller/Group.php
new file mode 100644
index 00000000..fa47f428
--- /dev/null
+++ b/app/Controller/Group.php
@@ -0,0 +1,250 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Group Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Group extends Base
+{
+ /**
+ * List all groups
+ *
+ * @access public
+ */
+ public function index()
+ {
+ $paginator = $this->paginator
+ ->setUrl('group', 'index')
+ ->setMax(30)
+ ->setOrder('name')
+ ->setQuery($this->group->getQuery())
+ ->calculate();
+
+ $this->response->html($this->helper->layout->app('group/index', array(
+ 'title' => t('Groups').' ('.$paginator->getTotal().')',
+ 'paginator' => $paginator,
+ )));
+ }
+
+ /**
+ * List all users
+ *
+ * @access public
+ */
+ public function users()
+ {
+ $group_id = $this->request->getIntegerParam('group_id');
+ $group = $this->group->getById($group_id);
+
+ $paginator = $this->paginator
+ ->setUrl('group', 'users', array('group_id' => $group_id))
+ ->setMax(30)
+ ->setOrder('username')
+ ->setQuery($this->groupMember->getQuery($group_id))
+ ->calculate();
+
+ $this->response->html($this->helper->layout->app('group/users', array(
+ 'title' => t('Members of %s', $group['name']).' ('.$paginator->getTotal().')',
+ 'paginator' => $paginator,
+ 'group' => $group,
+ )));
+ }
+
+ /**
+ * Display a form to create a new group
+ *
+ * @access public
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $this->response->html($this->helper->layout->app('group/create', array(
+ 'errors' => $errors,
+ 'values' => $values,
+ 'title' => t('New group')
+ )));
+ }
+
+ /**
+ * Validate and save a new group
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->groupValidator->validateCreation($values);
+
+ if ($valid) {
+ if ($this->group->create($values['name']) !== false) {
+ $this->flash->success(t('Group created successfully.'));
+ $this->response->redirect($this->helper->url->to('group', 'index'));
+ } else {
+ $this->flash->failure(t('Unable to create your group.'));
+ }
+ }
+
+ $this->create($values, $errors);
+ }
+
+ /**
+ * Display a form to update a group
+ *
+ * @access public
+ */
+ public function edit(array $values = array(), array $errors = array())
+ {
+ if (empty($values)) {
+ $values = $this->group->getById($this->request->getIntegerParam('group_id'));
+ }
+
+ $this->response->html($this->helper->layout->app('group/edit', array(
+ 'errors' => $errors,
+ 'values' => $values,
+ 'title' => t('Edit group')
+ )));
+ }
+
+ /**
+ * Validate and save a group
+ *
+ * @access public
+ */
+ public function update()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->groupValidator->validateModification($values);
+
+ if ($valid) {
+ if ($this->group->update($values) !== false) {
+ $this->flash->success(t('Group updated successfully.'));
+ $this->response->redirect($this->helper->url->to('group', 'index'));
+ } else {
+ $this->flash->failure(t('Unable to update your group.'));
+ }
+ }
+
+ $this->edit($values, $errors);
+ }
+
+ /**
+ * Form to associate a user to a group
+ *
+ * @access public
+ */
+ public function associate(array $values = array(), array $errors = array())
+ {
+ $group_id = $this->request->getIntegerParam('group_id');
+ $group = $this->group->getbyId($group_id);
+
+ if (empty($values)) {
+ $values['group_id'] = $group_id;
+ }
+
+ $this->response->html($this->helper->layout->app('group/associate', array(
+ 'users' => $this->user->prepareList($this->groupMember->getNotMembers($group_id)),
+ 'group' => $group,
+ 'errors' => $errors,
+ 'values' => $values,
+ 'title' => t('Add group member to "%s"', $group['name']),
+ )));
+ }
+
+ /**
+ * Add user to a group
+ *
+ * @access public
+ */
+ public function addUser()
+ {
+ $values = $this->request->getValues();
+
+ if (isset($values['group_id']) && isset($values['user_id'])) {
+ if ($this->groupMember->addUser($values['group_id'], $values['user_id'])) {
+ $this->flash->success(t('Group member added successfully.'));
+ $this->response->redirect($this->helper->url->to('group', 'users', array('group_id' => $values['group_id'])));
+ } else {
+ $this->flash->failure(t('Unable to add group member.'));
+ }
+ }
+
+ $this->associate($values);
+ }
+
+ /**
+ * Confirmation dialog to remove a user from a group
+ *
+ * @access public
+ */
+ public function dissociate()
+ {
+ $group_id = $this->request->getIntegerParam('group_id');
+ $user_id = $this->request->getIntegerParam('user_id');
+ $group = $this->group->getById($group_id);
+ $user = $this->user->getById($user_id);
+
+ $this->response->html($this->helper->layout->app('group/dissociate', array(
+ 'group' => $group,
+ 'user' => $user,
+ 'title' => t('Remove user from group "%s"', $group['name']),
+ )));
+ }
+
+ /**
+ * Remove a user from a group
+ *
+ * @access public
+ */
+ public function removeUser()
+ {
+ $this->checkCSRFParam();
+ $group_id = $this->request->getIntegerParam('group_id');
+ $user_id = $this->request->getIntegerParam('user_id');
+
+ if ($this->groupMember->removeUser($group_id, $user_id)) {
+ $this->flash->success(t('User removed successfully from this group.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this user from the group.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('group', 'users', array('group_id' => $group_id)));
+ }
+
+ /**
+ * Confirmation dialog to remove a group
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $group_id = $this->request->getIntegerParam('group_id');
+ $group = $this->group->getById($group_id);
+
+ $this->response->html($this->helper->layout->app('group/remove', array(
+ 'group' => $group,
+ 'title' => t('Remove group'),
+ )));
+ }
+
+ /**
+ * Remove a group
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $group_id = $this->request->getIntegerParam('group_id');
+
+ if ($this->group->remove($group_id)) {
+ $this->flash->success(t('Group removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this group.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('group', 'index'));
+ }
+}
diff --git a/app/Controller/GroupHelper.php b/app/Controller/GroupHelper.php
new file mode 100644
index 00000000..34f522a6
--- /dev/null
+++ b/app/Controller/GroupHelper.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Group Helper
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class GroupHelper extends Base
+{
+ /**
+ * Group autocompletion (Ajax)
+ *
+ * @access public
+ */
+ public function autocomplete()
+ {
+ $search = $this->request->getStringParam('term');
+ $groups = $this->groupManager->find($search);
+ $this->response->json($this->groupAutoCompleteFormatter->setGroups($groups)->format());
+ }
+}
diff --git a/app/Controller/Ical.php b/app/Controller/Ical.php
index f8e9e25f..f1ea6d8f 100644
--- a/app/Controller/Ical.php
+++ b/app/Controller/Ical.php
@@ -3,6 +3,7 @@
namespace Kanboard\Controller;
use Kanboard\Model\TaskFilter;
+use Kanboard\Model\Task as TaskModel;
use Eluceo\iCal\Component\Calendar as iCalendar;
/**
@@ -31,6 +32,7 @@ class Ical extends Base
// Common filter
$filter = $this->taskFilterICalendarFormatter
->create()
+ ->filterByStatus(TaskModel::STATUS_OPEN)
->filterByOwner($user['id']);
// Calendar properties
@@ -60,6 +62,7 @@ class Ical extends Base
// Common filter
$filter = $this->taskFilterICalendarFormatter
->create()
+ ->filterByStatus(TaskModel::STATUS_OPEN)
->filterByProject($project['id']);
// Calendar properties
diff --git a/app/Controller/Link.php b/app/Controller/Link.php
index 0eb3d679..ec7ab1af 100644
--- a/app/Controller/Link.php
+++ b/app/Controller/Link.php
@@ -12,22 +12,6 @@ namespace Kanboard\Controller;
class Link extends Base
{
/**
- * Common layout for config views
- *
- * @access private
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- private function layout($template, array $params)
- {
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
- $params['config_content_for_layout'] = $this->template->render($template, $params);
-
- return $this->template->layout('config/layout', $params);
- }
-
- /**
* Get the current link
*
* @access private
@@ -51,7 +35,7 @@ class Link extends Base
*/
public function index(array $values = array(), array $errors = array())
{
- $this->response->html($this->layout('link/index', array(
+ $this->response->html($this->helper->layout->config('link/index', array(
'links' => $this->link->getMergedList(),
'values' => $values,
'errors' => $errors,
@@ -67,14 +51,14 @@ class Link extends Base
public function save()
{
$values = $this->request->getValues();
- list($valid, $errors) = $this->link->validateCreation($values);
+ list($valid, $errors) = $this->linkValidator->validateCreation($values);
if ($valid) {
if ($this->link->create($values['label'], $values['opposite_label']) !== false) {
- $this->session->flash(t('Link added successfully.'));
+ $this->flash->success(t('Link added successfully.'));
$this->response->redirect($this->helper->url->to('link', 'index'));
} else {
- $this->session->flashError(t('Unable to create your link.'));
+ $this->flash->failure(t('Unable to create your link.'));
}
}
@@ -91,7 +75,7 @@ class Link extends Base
$link = $this->getLink();
$link['label'] = t($link['label']);
- $this->response->html($this->layout('link/edit', array(
+ $this->response->html($this->helper->layout->config('link/edit', array(
'values' => $values ?: $link,
'errors' => $errors,
'labels' => $this->link->getList($link['id']),
@@ -108,14 +92,14 @@ class Link extends Base
public function update()
{
$values = $this->request->getValues();
- list($valid, $errors) = $this->link->validateModification($values);
+ list($valid, $errors) = $this->linkValidator->validateModification($values);
if ($valid) {
if ($this->link->update($values)) {
- $this->session->flash(t('Link updated successfully.'));
+ $this->flash->success(t('Link updated successfully.'));
$this->response->redirect($this->helper->url->to('link', 'index'));
} else {
- $this->session->flashError(t('Unable to update your link.'));
+ $this->flash->failure(t('Unable to update your link.'));
}
}
@@ -131,7 +115,7 @@ class Link extends Base
{
$link = $this->getLink();
- $this->response->html($this->layout('link/remove', array(
+ $this->response->html($this->helper->layout->config('link/remove', array(
'link' => $link,
'title' => t('Remove a link')
)));
@@ -148,9 +132,9 @@ class Link extends Base
$link = $this->getLink();
if ($this->link->remove($link['id'])) {
- $this->session->flash(t('Link removed successfully.'));
+ $this->flash->success(t('Link removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this link.'));
+ $this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('link', 'index'));
diff --git a/app/Controller/Listing.php b/app/Controller/Listing.php
index b9c851f5..c784dd50 100644
--- a/app/Controller/Listing.php
+++ b/app/Controller/Listing.php
@@ -30,8 +30,11 @@ class Listing extends Base
->setQuery($query)
->calculate();
- $this->response->html($this->template->layout('listing/show', $params + array(
+ $this->response->html($this->helper->layout->app('listing/show', $params + array(
'paginator' => $paginator,
+ 'categories_list' => $this->category->getList($params['project']['id'], false),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($params['project']['id'], false),
+ 'custom_filters_list' => $this->customFilter->getAll($params['project']['id'], $this->userSession->getId()),
)));
}
}
diff --git a/app/Controller/Oauth.php b/app/Controller/Oauth.php
index 8c701cf7..452faecd 100644
--- a/app/Controller/Oauth.php
+++ b/app/Controller/Oauth.php
@@ -11,49 +11,19 @@ namespace Kanboard\Controller;
class Oauth extends Base
{
/**
- * Link or authenticate a Google account
- *
- * @access public
- */
- public function google()
- {
- $this->step1('google');
- }
-
- /**
- * Link or authenticate a Github account
- *
- * @access public
- */
- public function github()
- {
- $this->step1('github');
- }
-
- /**
- * Link or authenticate a Gitlab account
- *
- * @access public
- */
- public function gitlab()
- {
- $this->step1('gitlab');
- }
-
- /**
* Unlink external account
*
* @access public
*/
- public function unlink($backend = '')
+ public function unlink()
{
- $backend = $this->request->getStringParam('backend', $backend);
+ $backend = $this->request->getStringParam('backend');
$this->checkCSRFParam();
- if ($this->authentication->backend($backend)->unlink($this->userSession->getId())) {
- $this->session->flash(t('Your external account is not linked anymore to your profile.'));
+ if ($this->authenticationManager->getProvider($backend)->unlink($this->userSession->getId())) {
+ $this->flash->success(t('Your external account is not linked anymore to your profile.'));
} else {
- $this->session->flashError(t('Unable to unlink your external account.'));
+ $this->flash->failure(t('Unable to unlink your external account.'));
}
$this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
@@ -63,46 +33,52 @@ class Oauth extends Base
* Redirect to the provider if no code received
*
* @access private
+ * @param string $provider
*/
- private function step1($backend)
+ protected function step1($provider)
{
$code = $this->request->getStringParam('code');
if (! empty($code)) {
- $this->step2($backend, $code);
+ $this->step2($provider, $code);
} else {
- $this->response->redirect($this->authentication->backend($backend)->getService()->getAuthorizationUrl());
+ $this->response->redirect($this->authenticationManager->getProvider($provider)->getService()->getAuthorizationUrl());
}
}
/**
* Link or authenticate the user
*
- * @access private
+ * @access protected
+ * @param string $provider
+ * @param string $code
*/
- private function step2($backend, $code)
+ protected function step2($provider, $code)
{
- $profile = $this->authentication->backend($backend)->getProfile($code);
+ $this->authenticationManager->getProvider($provider)->setCode($code);
if ($this->userSession->isLogged()) {
- $this->link($backend, $profile);
+ $this->link($provider);
}
- $this->authenticate($backend, $profile);
+ $this->authenticate($provider);
}
/**
* Link the account
*
- * @access private
+ * @access protected
+ * @param string $provider
*/
- private function link($backend, $profile)
+ protected function link($provider)
{
- if (empty($profile)) {
- $this->session->flashError(t('External authentication failed'));
+ $authProvider = $this->authenticationManager->getProvider($provider);
+
+ if (! $authProvider->authenticate()) {
+ $this->flash->failure(t('External authentication failed'));
} else {
- $this->session->flash(t('Your external account is linked to your profile successfully.'));
- $this->authentication->backend($backend)->updateUser($this->userSession->getId(), $profile);
+ $this->userProfile->assign($this->userSession->getId(), $authProvider->getUser());
+ $this->flash->success(t('Your external account is linked to your profile successfully.'));
}
$this->response->redirect($this->helper->url->to('user', 'external', array('user_id' => $this->userSession->getId())));
@@ -111,14 +87,15 @@ class Oauth extends Base
/**
* Authenticate the account
*
- * @access private
+ * @access protected
+ * @param string $provider
*/
- private function authenticate($backend, $profile)
+ protected function authenticate($provider)
{
- if (! empty($profile) && $this->authentication->backend($backend)->authenticate($profile['id'])) {
+ if ($this->authenticationManager->oauthAuthentication($provider)) {
$this->response->redirect($this->helper->url->to('app', 'index'));
} else {
- $this->response->html($this->template->layout('auth/index', array(
+ $this->response->html($this->helper->layout->app('auth/index', array(
'errors' => array('login' => t('External authentication failed')),
'values' => array(),
'no_layout' => true,
diff --git a/app/Controller/PasswordReset.php b/app/Controller/PasswordReset.php
new file mode 100644
index 00000000..f6a0eb8e
--- /dev/null
+++ b/app/Controller/PasswordReset.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Password Reset Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class PasswordReset extends Base
+{
+ /**
+ * Show the form to reset the password
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $this->checkActivation();
+
+ $this->response->html($this->helper->layout->app('password_reset/create', array(
+ 'errors' => $errors,
+ 'values' => $values,
+ 'no_layout' => true,
+ )));
+ }
+
+ /**
+ * Validate and send the email
+ */
+ public function save()
+ {
+ $this->checkActivation();
+
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->passwordResetValidator->validateCreation($values);
+
+ if ($valid) {
+ $this->sendEmail($values['username']);
+ $this->response->redirect($this->helper->url->to('auth', 'login'));
+ }
+
+ $this->create($values, $errors);
+ }
+
+ /**
+ * Show the form to set a new password
+ */
+ public function change(array $values = array(), array $errors = array())
+ {
+ $this->checkActivation();
+
+ $token = $this->request->getStringParam('token');
+ $user_id = $this->passwordReset->getUserIdByToken($token);
+
+ if ($user_id !== false) {
+ $this->response->html($this->helper->layout->app('password_reset/change', array(
+ 'token' => $token,
+ 'errors' => $errors,
+ 'values' => $values,
+ 'no_layout' => true,
+ )));
+ }
+
+ $this->response->redirect($this->helper->url->to('auth', 'login'));
+ }
+
+ /**
+ * Set the new password
+ */
+ public function update()
+ {
+ $this->checkActivation();
+
+ $token = $this->request->getStringParam('token');
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->passwordResetValidator->validateModification($values);
+
+ if ($valid) {
+ $user_id = $this->passwordReset->getUserIdByToken($token);
+
+ if ($user_id !== false) {
+ $this->user->update(array('id' => $user_id, 'password' => $values['password']));
+ $this->passwordReset->disable($user_id);
+ }
+
+ $this->response->redirect($this->helper->url->to('auth', 'login'));
+ }
+
+ $this->change($values, $errors);
+ }
+
+ /**
+ * Send the email
+ */
+ private function sendEmail($username)
+ {
+ $token = $this->passwordReset->create($username);
+
+ if ($token !== false) {
+ $user = $this->user->getByUsername($username);
+
+ $this->emailClient->send(
+ $user['email'],
+ $user['name'] ?: $user['username'],
+ t('Password Reset for Kanboard'),
+ $this->template->render('password_reset/email', array('token' => $token))
+ );
+ }
+ }
+
+ /**
+ * Check feature availability
+ */
+ private function checkActivation()
+ {
+ if ($this->config->get('password_reset', 0) == 0) {
+ $this->response->redirect($this->helper->url->to('auth', 'login'));
+ }
+ }
+}
diff --git a/app/Controller/Project.php b/app/Controller/Project.php
index f30d70e2..cdfbd94a 100644
--- a/app/Controller/Project.php
+++ b/app/Controller/Project.php
@@ -20,7 +20,7 @@ class Project extends Base
if ($this->userSession->isAdmin()) {
$project_ids = $this->project->getAllIds();
} else {
- $project_ids = $this->projectPermission->getMemberProjectIds($this->userSession->getId());
+ $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
}
$nb_projects = count($project_ids);
@@ -29,11 +29,10 @@ class Project extends Base
->setUrl('project', 'index')
->setMax(20)
->setOrder('name')
- ->setQuery($this->project->getQueryProjectDetails($project_ids))
+ ->setQuery($this->project->getQueryColumnStats($project_ids))
->calculate();
- $this->response->html($this->template->layout('project/index', array(
- 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
+ $this->response->html($this->helper->layout->app('project/index', array(
'paginator' => $paginator,
'nb_projects' => $nb_projects,
'title' => t('Projects').' ('.$nb_projects.')'
@@ -49,7 +48,7 @@ class Project extends Base
{
$project = $this->getProject();
- $this->response->html($this->projectLayout('project/show', array(
+ $this->response->html($this->helper->layout->project('project/show', array(
'project' => $project,
'stats' => $this->project->getTaskStats($project['id']),
'title' => $project['name'],
@@ -70,15 +69,15 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->{$switch.'PublicAccess'}($project['id'])) {
- $this->session->flash(t('Project updated successfully.'));
+ $this->flash->success(t('Project updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update this project.'));
+ $this->flash->failure(t('Unable to update this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'share', array('project_id' => $project['id'])));
}
- $this->response->html($this->projectLayout('project/share', array(
+ $this->response->html($this->helper->layout->project('project/share', array(
'project' => $project,
'title' => t('Public access'),
)));
@@ -95,11 +94,11 @@ class Project extends Base
if ($this->request->isPost()) {
$this->projectMetadata->save($project['id'], $this->request->getValues());
- $this->session->flash(t('Project updated successfully.'));
+ $this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'integrations', array('project_id' => $project['id'])));
}
- $this->response->html($this->projectLayout('project/integrations', array(
+ $this->response->html($this->helper->layout->project('project/integrations', array(
'project' => $project,
'title' => t('Integrations'),
'webhook_token' => $this->config->get('webhook_token'),
@@ -120,11 +119,11 @@ class Project extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->projectNotification->saveSettings($project['id'], $values);
- $this->session->flash(t('Project updated successfully.'));
+ $this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('project', 'notifications', array('project_id' => $project['id'])));
}
- $this->response->html($this->projectLayout('project/notifications', array(
+ $this->response->html($this->helper->layout->project('project/notifications', array(
'notifications' => $this->projectNotification->readSettings($project['id']),
'types' => $this->projectNotificationType->getTypes(),
'project' => $project,
@@ -133,171 +132,6 @@ class Project extends Base
}
/**
- * Display a form to edit a project
- *
- * @access public
- */
- public function edit(array $values = array(), array $errors = array())
- {
- $project = $this->getProject();
-
- $this->response->html($this->projectLayout('project/edit', array(
- 'values' => empty($values) ? $project : $values,
- 'errors' => $errors,
- 'project' => $project,
- 'title' => t('Edit project')
- )));
- }
-
- /**
- * Validate and update a project
- *
- * @access public
- */
- public function update()
- {
- $project = $this->getProject();
- $values = $this->request->getValues();
-
- if (isset($values['is_private'])) {
- if (! $this->helper->user->isProjectAdministrationAllowed($project['id'])) {
- unset($values['is_private']);
- }
- } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) {
- if ($this->helper->user->isProjectAdministrationAllowed($project['id'])) {
- $values += array('is_private' => 0);
- }
- }
-
- list($valid, $errors) = $this->project->validateModification($values);
-
- if ($valid) {
- if ($this->project->update($values)) {
- $this->session->flash(t('Project updated successfully.'));
- $this->response->redirect($this->helper->url->to('project', 'edit', array('project_id' => $project['id'])));
- } else {
- $this->session->flashError(t('Unable to update this project.'));
- }
- }
-
- $this->edit($values, $errors);
- }
-
- /**
- * Users list for the selected project
- *
- * @access public
- */
- public function users()
- {
- $project = $this->getProject();
-
- $this->response->html($this->projectLayout('project/users', array(
- 'project' => $project,
- 'users' => $this->projectPermission->getAllUsers($project['id']),
- 'title' => t('Edit project access list')
- )));
- }
-
- /**
- * Allow everybody
- *
- * @access public
- */
- public function allowEverybody()
- {
- $project = $this->getProject();
- $values = $this->request->getValues() + array('is_everybody_allowed' => 0);
- list($valid, ) = $this->projectPermission->validateProjectModification($values);
-
- if ($valid) {
- if ($this->project->update($values)) {
- $this->session->flash(t('Project updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update this project.'));
- }
- }
-
- $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $project['id'])));
- }
-
- /**
- * Allow a specific user (admin only)
- *
- * @access public
- */
- public function allow()
- {
- $values = $this->request->getValues();
- list($valid, ) = $this->projectPermission->validateUserModification($values);
-
- if ($valid) {
- if ($this->projectPermission->addMember($values['project_id'], $values['user_id'])) {
- $this->session->flash(t('Project updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update this project.'));
- }
- }
-
- $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id'])));
- }
-
- /**
- * Change the role of a project member
- *
- * @access public
- */
- public function role()
- {
- $this->checkCSRFParam();
-
- $values = array(
- 'project_id' => $this->request->getIntegerParam('project_id'),
- 'user_id' => $this->request->getIntegerParam('user_id'),
- 'is_owner' => $this->request->getIntegerParam('is_owner'),
- );
-
- list($valid, ) = $this->projectPermission->validateUserModification($values);
-
- if ($valid) {
- if ($this->projectPermission->changeRole($values['project_id'], $values['user_id'], $values['is_owner'])) {
- $this->session->flash(t('Project updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update this project.'));
- }
- }
-
- $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id'])));
- }
-
- /**
- * Revoke user access (admin only)
- *
- * @access public
- */
- public function revoke()
- {
- $this->checkCSRFParam();
-
- $values = array(
- 'project_id' => $this->request->getIntegerParam('project_id'),
- 'user_id' => $this->request->getIntegerParam('user_id'),
- );
-
- list($valid, ) = $this->projectPermission->validateUserModification($values);
-
- if ($valid) {
- if ($this->projectPermission->revokeMember($values['project_id'], $values['user_id'])) {
- $this->session->flash(t('Project updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update this project.'));
- }
- }
-
- $this->response->redirect($this->helper->url->to('project', 'users', array('project_id' => $values['project_id'])));
- }
-
- /**
* Remove a project
*
* @access public
@@ -310,15 +144,15 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->remove($project['id'])) {
- $this->session->flash(t('Project removed successfully.'));
+ $this->flash->success(t('Project removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this project.'));
+ $this->flash->failure(t('Unable to remove this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'index'));
}
- $this->response->html($this->projectLayout('project/remove', array(
+ $this->response->html($this->helper->layout->project('project/remove', array(
'project' => $project,
'title' => t('Remove project')
)));
@@ -336,17 +170,18 @@ class Project extends Base
$project = $this->getProject();
if ($this->request->getStringParam('duplicate') === 'yes') {
- $values = array_keys($this->request->getValues());
- if ($this->projectDuplication->duplicate($project['id'], $values) !== false) {
- $this->session->flash(t('Project cloned successfully.'));
+ $project_id = $this->projectDuplication->duplicate($project['id'], array_keys($this->request->getValues()), $this->userSession->getId());
+
+ if ($project_id !== false) {
+ $this->flash->success(t('Project cloned successfully.'));
} else {
- $this->session->flashError(t('Unable to clone this project.'));
+ $this->flash->failure(t('Unable to clone this project.'));
}
- $this->response->redirect($this->helper->url->to('project', 'index'));
+ $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
}
- $this->response->html($this->projectLayout('project/duplicate', array(
+ $this->response->html($this->helper->layout->project('project/duplicate', array(
'project' => $project,
'title' => t('Clone this project')
)));
@@ -365,15 +200,15 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->disable($project['id'])) {
- $this->session->flash(t('Project disabled successfully.'));
+ $this->flash->success(t('Project disabled successfully.'));
} else {
- $this->session->flashError(t('Unable to disable this project.'));
+ $this->flash->failure(t('Unable to disable this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
}
- $this->response->html($this->projectLayout('project/disable', array(
+ $this->response->html($this->helper->layout->project('project/disable', array(
'project' => $project,
'title' => t('Project activation')
)));
@@ -392,59 +227,17 @@ class Project extends Base
$this->checkCSRFParam();
if ($this->project->enable($project['id'])) {
- $this->session->flash(t('Project activated successfully.'));
+ $this->flash->success(t('Project activated successfully.'));
} else {
- $this->session->flashError(t('Unable to activate this project.'));
+ $this->flash->failure(t('Unable to activate this project.'));
}
$this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project['id'])));
}
- $this->response->html($this->projectLayout('project/enable', array(
+ $this->response->html($this->helper->layout->project('project/enable', array(
'project' => $project,
'title' => t('Project activation')
)));
}
-
- /**
- * Display a form to create a new project
- *
- * @access public
- */
- public function create(array $values = array(), array $errors = array())
- {
- $is_private = $this->request->getIntegerParam('private', $this->userSession->isAdmin() || $this->userSession->isProjectAdmin() ? 0 : 1);
-
- $this->response->html($this->template->layout('project/new', array(
- 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
- 'values' => empty($values) ? array('is_private' => $is_private) : $values,
- 'errors' => $errors,
- 'is_private' => $is_private,
- 'title' => $is_private ? t('New private project') : t('New project'),
- )));
- }
-
- /**
- * Validate and save a new project
- *
- * @access public
- */
- public function save()
- {
- $values = $this->request->getValues();
- list($valid, $errors) = $this->project->validateCreation($values);
-
- if ($valid) {
- $project_id = $this->project->create($values, $this->userSession->getId(), true);
-
- if ($project_id > 0) {
- $this->session->flash(t('Your project have been created successfully.'));
- $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
- }
-
- $this->session->flashError(t('Unable to create your project.'));
- }
-
- $this->create($values, $errors);
- }
}
diff --git a/app/Controller/ProjectCreation.php b/app/Controller/ProjectCreation.php
new file mode 100644
index 00000000..88f41fcd
--- /dev/null
+++ b/app/Controller/ProjectCreation.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Project Creation Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class ProjectCreation extends Base
+{
+ /**
+ * Display a form to create a new project
+ *
+ * @access public
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $is_private = isset($values['is_private']) && $values['is_private'] == 1;
+ $projects_list = array(0 => t('Do not duplicate anything')) + $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
+
+ $this->response->html($this->helper->layout->app('project_creation/create', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'is_private' => $is_private,
+ 'projects_list' => $projects_list,
+ 'title' => $is_private ? t('New private project') : t('New project'),
+ )));
+ }
+
+ /**
+ * Display a form to create a private project
+ *
+ * @access public
+ */
+ public function createPrivate(array $values = array(), array $errors = array())
+ {
+ $values['is_private'] = 1;
+ $this->create($values, $errors);
+ }
+
+ /**
+ * Validate and save a new project
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->projectValidator->validateCreation($values);
+
+ if ($valid) {
+ $project_id = $this->createOrDuplicate($values);
+
+ if ($project_id > 0) {
+ $this->flash->success(t('Your project have been created successfully.'));
+ return $this->response->redirect($this->helper->url->to('project', 'show', array('project_id' => $project_id)));
+ }
+
+ $this->flash->failure(t('Unable to create your project.'));
+ }
+
+ $this->create($values, $errors);
+ }
+
+ /**
+ * Create or duplicate a project
+ *
+ * @access private
+ * @param array $values
+ * @return boolean|integer
+ */
+ private function createOrDuplicate(array $values)
+ {
+ if (empty($values['src_project_id'])) {
+ return $this->createNewProject($values);
+ }
+
+ return $this->duplicateNewProject($values);
+ }
+
+ /**
+ * Save a new project
+ *
+ * @access private
+ * @param array $values
+ * @return boolean|integer
+ */
+ private function createNewProject(array $values)
+ {
+ $project = array(
+ 'name' => $values['name'],
+ 'is_private' => $values['is_private'],
+ );
+
+ return $this->project->create($project, $this->userSession->getId(), true);
+ }
+
+ /**
+ * Creatte from another project
+ *
+ * @access private
+ * @param array $values
+ * @return boolean|integer
+ */
+ private function duplicateNewProject(array $values)
+ {
+ $selection = array();
+
+ foreach ($this->projectDuplication->getOptionalSelection() as $item) {
+ if (isset($values[$item]) && $values[$item] == 1) {
+ $selection[] = $item;
+ }
+ }
+
+ return $this->projectDuplication->duplicate(
+ $values['src_project_id'],
+ $selection,
+ $this->userSession->getId(),
+ $values['name'],
+ $values['is_private'] == 1
+ );
+ }
+}
diff --git a/app/Controller/ProjectEdit.php b/app/Controller/ProjectEdit.php
new file mode 100644
index 00000000..f4a3a7cb
--- /dev/null
+++ b/app/Controller/ProjectEdit.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Project Edit Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class ProjectEdit extends Base
+{
+ /**
+ * General edition (most common operations)
+ *
+ * @access public
+ */
+ public function edit(array $values = array(), array $errors = array())
+ {
+ $this->renderView('project_edit/general', $values, $errors);
+ }
+
+ /**
+ * Change start and end dates
+ *
+ * @access public
+ */
+ public function dates(array $values = array(), array $errors = array())
+ {
+ $this->renderView('project_edit/dates', $values, $errors);
+ }
+
+ /**
+ * Change project description
+ *
+ * @access public
+ */
+ public function description(array $values = array(), array $errors = array())
+ {
+ $this->renderView('project_edit/description', $values, $errors);
+ }
+
+ /**
+ * Change task priority
+ *
+ * @access public
+ */
+ public function priority(array $values = array(), array $errors = array())
+ {
+ $this->renderView('project_edit/task_priority', $values, $errors);
+ }
+
+ /**
+ * Validate and update a project
+ *
+ * @access public
+ */
+ public function update()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+ $redirect = $this->request->getStringParam('redirect', 'edit');
+
+ $values = $this->prepareValues($redirect, $project, $values);
+ list($valid, $errors) = $this->projectValidator->validateModification($values);
+
+ if ($valid) {
+ if ($this->project->update($values)) {
+ $this->flash->success(t('Project updated successfully.'));
+ $this->response->redirect($this->helper->url->to('ProjectEdit', $redirect, array('project_id' => $project['id'])));
+ } else {
+ $this->flash->failure(t('Unable to update this project.'));
+ }
+ }
+
+ $this->$redirect($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)
+ {
+ if ($redirect === 'edit') {
+ if (isset($values['is_private'])) {
+ if (! $this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) {
+ unset($values['is_private']);
+ }
+ } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) {
+ if ($this->helper->user->hasProjectAccess('ProjectCreation', 'create', $project['id'])) {
+ $values += array('is_private' => 0);
+ }
+ }
+ }
+
+ return $values;
+ }
+
+ /**
+ * Common metthod 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->projectUserRole->getAssignableUsersList($project['id'], true),
+ 'values' => empty($values) ? $project : $values,
+ 'errors' => $errors,
+ 'project' => $project,
+ 'title' => t('Edit project')
+ )));
+ }
+}
diff --git a/app/Controller/ProjectFile.php b/app/Controller/ProjectFile.php
new file mode 100644
index 00000000..96764a92
--- /dev/null
+++ b/app/Controller/ProjectFile.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Project File Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class ProjectFile extends Base
+{
+ /**
+ * File upload form
+ *
+ * @access public
+ */
+ public function create()
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->template->render('project_file/create', array(
+ 'project' => $project,
+ 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
+ )));
+ }
+
+ /**
+ * Save uploaded files
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+
+ if (! $this->projectFile->uploadFiles($project['id'], $this->request->getFileInfo('files'))) {
+ $this->flash->failure(t('Unable to upload the file.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectOverview', 'show', array('project_id' => $project['id'])), true);
+ }
+
+ /**
+ * Remove a file
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $project = $this->getProject();
+ $file = $this->projectFile->getById($this->request->getIntegerParam('file_id'));
+
+ if ($this->projectFile->remove($file['id'])) {
+ $this->flash->success(t('File removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this file.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectOverview', 'show', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Confirmation dialog before removing a file
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $file = $this->projectFile->getById($this->request->getIntegerParam('file_id'));
+
+ $this->response->html($this->template->render('project_file/remove', array(
+ 'project' => $project,
+ 'file' => $file,
+ )));
+ }
+}
diff --git a/app/Controller/ProjectOverview.php b/app/Controller/ProjectOverview.php
new file mode 100644
index 00000000..b0687ed3
--- /dev/null
+++ b/app/Controller/ProjectOverview.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Project Overview Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class ProjectOverview extends Base
+{
+ /**
+ * Show project overview
+ */
+ public function show()
+ {
+ $params = $this->getProjectFilters('ProjectOverview', 'show');
+ $params['users'] = $this->projectUserRole->getAllUsersGroupedByRole($params['project']['id']);
+ $params['roles'] = $this->role->getProjectRoles();
+ $params['events'] = $this->projectActivity->getProject($params['project']['id'], 10);
+ $params['images'] = $this->projectFile->getAllImages($params['project']['id']);
+ $params['files'] = $this->projectFile->getAllDocuments($params['project']['id']);
+
+ $this->project->getColumnStats($params['project']);
+
+ $this->response->html($this->helper->layout->app('project_overview/show', $params));
+ }
+}
diff --git a/app/Controller/ProjectPermission.php b/app/Controller/ProjectPermission.php
new file mode 100644
index 00000000..800da02f
--- /dev/null
+++ b/app/Controller/ProjectPermission.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Security\Role;
+
+/**
+ * Project Permission
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class ProjectPermission extends Base
+{
+ /**
+ * Permissions are only available for team projects
+ *
+ * @access protected
+ * @param integer $project_id Default project id
+ * @return array
+ */
+ protected function getProject($project_id = 0)
+ {
+ $project = parent::getProject($project_id);
+
+ if ($project['is_private'] == 1) {
+ $this->forbidden();
+ }
+
+ return $project;
+ }
+
+ /**
+ * Show all permissions
+ *
+ * @access public
+ */
+ public function index(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+
+ if (empty($values)) {
+ $values['role'] = Role::PROJECT_MEMBER;
+ }
+
+ $this->response->html($this->helper->layout->project('project_permission/index', array(
+ 'project' => $project,
+ 'users' => $this->projectUserRole->getUsers($project['id']),
+ 'groups' => $this->projectGroupRole->getGroups($project['id']),
+ 'roles' => $this->role->getProjectRoles(),
+ 'values' => $values,
+ 'errors' => $errors,
+ 'title' => t('Project Permissions'),
+ )));
+ }
+
+ /**
+ * Allow everybody
+ *
+ * @access public
+ */
+ public function allowEverybody()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues() + array('is_everybody_allowed' => 0);
+
+ if ($this->project->update($values)) {
+ $this->flash->success(t('Project updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this project.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Add user to the project
+ *
+ * @access public
+ */
+ public function addUser()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ if ($this->projectUserRole->addUser($values['project_id'], $values['user_id'], $values['role'])) {
+ $this->flash->success(t('Project updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this project.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Revoke user access
+ *
+ * @access public
+ */
+ public function removeUser()
+ {
+ $this->checkCSRFParam();
+ $project = $this->getProject();
+ $user_id = $this->request->getIntegerParam('user_id');
+
+ if ($this->projectUserRole->removeUser($project['id'], $user_id)) {
+ $this->flash->success(t('Project updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this project.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Change user role
+ *
+ * @access public
+ */
+ public function changeUserRole()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getJson();
+
+ if (! empty($project) && ! empty($values) && $this->projectUserRole->changeUserRole($project['id'], $values['id'], $values['role'])) {
+ $this->response->json(array('status' => 'ok'));
+ } else {
+ $this->response->json(array('status' => 'error'));
+ }
+ }
+
+ /**
+ * Add group to the project
+ *
+ * @access public
+ */
+ public function addGroup()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ if (empty($values['group_id']) && ! empty($values['external_id'])) {
+ $values['group_id'] = $this->group->create($values['name'], $values['external_id']);
+ }
+
+ if ($this->projectGroupRole->addGroup($project['id'], $values['group_id'], $values['role'])) {
+ $this->flash->success(t('Project updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this project.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Revoke group access
+ *
+ * @access public
+ */
+ public function removeGroup()
+ {
+ $this->checkCSRFParam();
+ $project = $this->getProject();
+ $group_id = $this->request->getIntegerParam('group_id');
+
+ if ($this->projectGroupRole->removeGroup($project['id'], $group_id)) {
+ $this->flash->success(t('Project updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this project.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectPermission', 'index', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Change group role
+ *
+ * @access public
+ */
+ public function changeGroupRole()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getJson();
+
+ if (! empty($project) && ! empty($values) && $this->projectGroupRole->changeGroupRole($project['id'], $values['id'], $values['role'])) {
+ $this->response->json(array('status' => 'ok'));
+ } else {
+ $this->response->json(array('status' => 'error'));
+ }
+ }
+}
diff --git a/app/Controller/Projectuser.php b/app/Controller/Projectuser.php
index 18829b3c..a6d4fe4e 100644
--- a/app/Controller/Projectuser.php
+++ b/app/Controller/Projectuser.php
@@ -4,6 +4,7 @@ namespace Kanboard\Controller;
use Kanboard\Model\User as UserModel;
use Kanboard\Model\Task as TaskModel;
+use Kanboard\Core\Security\Role;
/**
* Project User overview
@@ -13,23 +14,6 @@ use Kanboard\Model\Task as TaskModel;
*/
class Projectuser extends Base
{
- /**
- * Common layout for users overview views
- *
- * @access private
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- private function layout($template, array $params)
- {
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
- $params['content_for_sublayout'] = $this->template->render($template, $params);
- $params['filter'] = array('user_id' => $params['user_id']);
-
- return $this->template->layout('project_user/layout', $params);
- }
-
private function common()
{
$user_id = $this->request->getIntegerParam('user_id', UserModel::EVERYBODY_ID);
@@ -37,19 +21,19 @@ class Projectuser extends Base
if ($this->userSession->isAdmin()) {
$project_ids = $this->project->getAllIds();
} else {
- $project_ids = $this->projectPermission->getMemberProjectIds($this->userSession->getId());
+ $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
}
- return array($user_id, $project_ids, $this->user->getList(true));
+ return array($user_id, $project_ids, $this->user->getActiveUsersList(true));
}
- private function role($is_owner, $action, $title, $title_user)
+ private function role($role, $action, $title, $title_user)
{
list($user_id, $project_ids, $users) = $this->common();
- $query = $this->projectPermission->getQueryByRole($project_ids, $is_owner)->callback(array($this->project, 'applyColumnStats'));
+ $query = $this->projectPermission->getQueryByRole($project_ids, $role)->callback(array($this->project, 'applyColumnStats'));
- if ($user_id !== UserModel::EVERYBODY_ID) {
+ if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) {
$query->eq(UserModel::TABLE.'.id', $user_id);
$title = t($title_user, $users[$user_id]);
}
@@ -61,7 +45,7 @@ class Projectuser extends Base
->setQuery($query)
->calculate();
- $this->response->html($this->layout('project_user/roles', array(
+ $this->response->html($this->helper->layout->projectUser('project_user/roles', array(
'paginator' => $paginator,
'title' => $title,
'user_id' => $user_id,
@@ -75,7 +59,7 @@ class Projectuser extends Base
$query = $this->taskFinder->getProjectUserOverviewQuery($project_ids, $is_active);
- if ($user_id !== UserModel::EVERYBODY_ID) {
+ if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) {
$query->eq(TaskModel::TABLE.'.owner_id', $user_id);
$title = t($title_user, $users[$user_id]);
}
@@ -87,7 +71,7 @@ class Projectuser extends Base
->setQuery($query)
->calculate();
- $this->response->html($this->layout('project_user/tasks', array(
+ $this->response->html($this->helper->layout->projectUser('project_user/tasks', array(
'paginator' => $paginator,
'title' => $title,
'user_id' => $user_id,
@@ -101,7 +85,7 @@ class Projectuser extends Base
*/
public function managers()
{
- $this->role(1, 'managers', t('People who are project managers'), 'Projects where "%s" is manager');
+ $this->role(Role::PROJECT_MANAGER, 'managers', t('People who are project managers'), 'Projects where "%s" is manager');
}
/**
@@ -110,7 +94,7 @@ class Projectuser extends Base
*/
public function members()
{
- $this->role(0, 'members', t('People who are project members'), 'Projects where "%s" is member');
+ $this->role(ROLE::PROJECT_MEMBER, 'members', t('People who are project members'), 'Projects where "%s" is member');
}
/**
@@ -130,4 +114,17 @@ class Projectuser extends Base
{
$this->tasks(TaskModel::STATUS_CLOSED, 'closed', t('Closed tasks'), 'Closed tasks assigned to "%s"');
}
+
+ /**
+ * Users tooltip
+ */
+ public function users()
+ {
+ $project = $this->getProject();
+
+ return $this->response->html($this->template->render('project_user/tooltip_users', array(
+ 'users' => $this->projectUserRole->getAllUsersGroupedByRole($project['id']),
+ 'roles' => $this->role->getProjectRoles(),
+ )));
+ }
}
diff --git a/app/Controller/Search.php b/app/Controller/Search.php
index 0aff9073..9b9b9e65 100644
--- a/app/Controller/Search.php
+++ b/app/Controller/Search.php
@@ -12,7 +12,7 @@ class Search extends Base
{
public function index()
{
- $projects = $this->projectPermission->getAllowedProjects($this->userSession->getId());
+ $projects = $this->projectUserRole->getProjectsByUser($this->userSession->getId());
$search = urldecode($this->request->getStringParam('search'));
$nb_tasks = 0;
@@ -36,8 +36,7 @@ class Search extends Base
$nb_tasks = $paginator->getTotal();
}
- $this->response->html($this->template->layout('search/index', array(
- 'board_selector' => $projects,
+ $this->response->html($this->helper->layout->app('search/index', array(
'values' => array(
'search' => $search,
'controller' => 'search',
diff --git a/app/Controller/Subtask.php b/app/Controller/Subtask.php
index 4ef3e74e..8ca0ce92 100644
--- a/app/Controller/Subtask.php
+++ b/app/Controller/Subtask.php
@@ -2,8 +2,6 @@
namespace Kanboard\Controller;
-use Kanboard\Model\Subtask as SubtaskModel;
-
/**
* Subtask controller
*
@@ -13,20 +11,19 @@ use Kanboard\Model\Subtask as SubtaskModel;
class Subtask extends Base
{
/**
- * Get the current subtask
- *
- * @access private
- * @return array
+ * Show list of subtasks
*/
- private function getSubtask()
+ public function show()
{
- $subtask = $this->subtask->getById($this->request->getIntegerParam('subtask_id'));
-
- if (empty($subtask)) {
- $this->notfound();
- }
+ $task = $this->getTask();
- return $subtask;
+ $this->response->html($this->helper->layout->task('subtask/show', array(
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
+ 'task' => $task,
+ 'project' => $this->getProject(),
+ 'subtasks' => $this->subtask->getAll($task['id']),
+ 'editable' => true,
+ )));
}
/**
@@ -45,10 +42,10 @@ class Subtask extends Base
);
}
- $this->response->html($this->taskLayout('subtask/create', array(
+ $this->response->html($this->helper->layout->task('subtask/create', array(
'values' => $values,
'errors' => $errors,
- 'users_list' => $this->projectPermission->getMemberList($task['project_id']),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
'task' => $task,
)));
}
@@ -63,20 +60,20 @@ class Subtask extends Base
$task = $this->getTask();
$values = $this->request->getValues();
- list($valid, $errors) = $this->subtask->validateCreation($values);
+ list($valid, $errors) = $this->subtaskValidator->validateCreation($values);
if ($valid) {
if ($this->subtask->create($values)) {
- $this->session->flash(t('Sub-task added successfully.'));
+ $this->flash->success(t('Sub-task added successfully.'));
} else {
- $this->session->flashError(t('Unable to create your sub-task.'));
+ $this->flash->failure(t('Unable to create your sub-task.'));
}
if (isset($values['another_subtask']) && $values['another_subtask'] == 1) {
- $this->response->redirect($this->helper->url->to('subtask', 'create', array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1)));
+ return $this->create(array('project_id' => $task['project_id'], 'task_id' => $task['id'], 'another_subtask' => 1));
}
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'), true);
}
$this->create($values, $errors);
@@ -92,10 +89,10 @@ class Subtask extends Base
$task = $this->getTask();
$subtask = $this->getSubTask();
- $this->response->html($this->taskLayout('subtask/edit', array(
+ $this->response->html($this->helper->layout->task('subtask/edit', array(
'values' => empty($values) ? $subtask : $values,
'errors' => $errors,
- 'users_list' => $this->projectPermission->getMemberList($task['project_id']),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
'status_list' => $this->subtask->getStatusList(),
'subtask' => $subtask,
'task' => $task,
@@ -113,16 +110,16 @@ class Subtask extends Base
$this->getSubtask();
$values = $this->request->getValues();
- list($valid, $errors) = $this->subtask->validateModification($values);
+ list($valid, $errors) = $this->subtaskValidator->validateModification($values);
if ($valid) {
if ($this->subtask->update($values)) {
- $this->session->flash(t('Sub-task updated successfully.'));
+ $this->flash->success(t('Sub-task updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update your sub-task.'));
+ $this->flash->failure(t('Unable to update your sub-task.'));
}
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
$this->edit($values, $errors);
@@ -138,7 +135,7 @@ class Subtask extends Base
$task = $this->getTask();
$subtask = $this->getSubtask();
- $this->response->html($this->taskLayout('subtask/remove', array(
+ $this->response->html($this->helper->layout->task('subtask/remove', array(
'subtask' => $subtask,
'task' => $task,
)));
@@ -156,102 +153,12 @@ class Subtask extends Base
$subtask = $this->getSubtask();
if ($this->subtask->remove($subtask['id'])) {
- $this->session->flash(t('Sub-task removed successfully.'));
+ $this->flash->success(t('Sub-task removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this sub-task.'));
+ $this->flash->failure(t('Unable to remove this sub-task.'));
}
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id']), 'subtasks'));
- }
-
- /**
- * Change status to the next status: Toto -> In Progress -> Done
- *
- * @access public
- */
- public function toggleStatus()
- {
- $task = $this->getTask();
- $subtask = $this->getSubtask();
- $redirect = $this->request->getStringParam('redirect', 'task');
-
- $this->subtask->toggleStatus($subtask['id']);
-
- if ($redirect === 'board') {
- $this->session['has_subtask_inprogress'] = $this->subtask->hasSubtaskInProgress($this->userSession->getId());
-
- $this->response->html($this->template->render('board/tooltip_subtasks', array(
- 'subtasks' => $this->subtask->getAll($task['id']),
- 'task' => $task,
- )));
- }
-
- $this->toggleRedirect($task, $redirect);
- }
-
- /**
- * Handle subtask restriction (popover)
- *
- * @access public
- */
- public function subtaskRestriction()
- {
- $task = $this->getTask();
- $subtask = $this->getSubtask();
-
- $this->response->html($this->template->render('subtask/restriction_change_status', array(
- 'status_list' => array(
- SubtaskModel::STATUS_TODO => t('Todo'),
- SubtaskModel::STATUS_DONE => t('Done'),
- ),
- 'subtask_inprogress' => $this->subtask->getSubtaskInProgress($this->userSession->getId()),
- 'subtask' => $subtask,
- 'task' => $task,
- 'redirect' => $this->request->getStringParam('redirect'),
- )));
- }
-
- /**
- * Change status of the in progress subtask and the other subtask
- *
- * @access public
- */
- public function changeRestrictionStatus()
- {
- $task = $this->getTask();
- $subtask = $this->getSubtask();
- $values = $this->request->getValues();
-
- // Change status of the previous in progress subtask
- $this->subtask->update(array(
- 'id' => $values['id'],
- 'status' => $values['status'],
- ));
-
- // Set the current subtask to in pogress
- $this->subtask->update(array(
- 'id' => $subtask['id'],
- 'status' => SubtaskModel::STATUS_INPROGRESS,
- ));
-
- $this->toggleRedirect($task, $values['redirect']);
- }
-
- /**
- * Redirect to the right page
- *
- * @access private
- */
- private function toggleRedirect(array $task, $redirect)
- {
- switch ($redirect) {
- case 'board':
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
- case 'dashboard':
- $this->response->redirect($this->helper->url->to('app', 'index'));
- default:
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'subtasks'));
- }
+ $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
/**
@@ -261,14 +168,15 @@ class Subtask extends Base
*/
public function movePosition()
{
- $this->checkCSRFParam();
$project_id = $this->request->getIntegerParam('project_id');
$task_id = $this->request->getIntegerParam('task_id');
- $subtask_id = $this->request->getIntegerParam('subtask_id');
- $direction = $this->request->getStringParam('direction');
- $method = $direction === 'up' ? 'moveUp' : 'moveDown';
+ $values = $this->request->getJson();
+
+ if (! empty($values) && $this->helper->user->hasProjectAccess('Subtask', 'movePosition', $project_id)) {
+ $result = $this->subtask->changePosition($task_id, $values['subtask_id'], $values['position']);
+ return $this->response->json(array('result' => $result));
+ }
- $this->subtask->$method($task_id, $subtask_id);
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id), 'subtasks'));
+ $this->forbidden();
}
}
diff --git a/app/Controller/SubtaskRestriction.php b/app/Controller/SubtaskRestriction.php
new file mode 100644
index 00000000..56024867
--- /dev/null
+++ b/app/Controller/SubtaskRestriction.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Model\Subtask as SubtaskModel;
+
+/**
+ * Subtask Restriction
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class SubtaskRestriction extends Base
+{
+ /**
+ * Show popup
+ *
+ * @access public
+ */
+ public function popover()
+ {
+ $task = $this->getTask();
+ $subtask = $this->getSubtask();
+
+ $this->response->html($this->template->render('subtask_restriction/popover', array(
+ 'status_list' => array(
+ SubtaskModel::STATUS_TODO => t('Todo'),
+ SubtaskModel::STATUS_DONE => t('Done'),
+ ),
+ 'subtask_inprogress' => $this->subtask->getSubtaskInProgress($this->userSession->getId()),
+ 'subtask' => $subtask,
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * Change status of the in progress subtask and the other subtask
+ *
+ * @access public
+ */
+ public function update()
+ {
+ $task = $this->getTask();
+ $subtask = $this->getSubtask();
+ $values = $this->request->getValues();
+
+ // Change status of the previous "in progress" subtask
+ $this->subtask->update(array(
+ 'id' => $values['id'],
+ 'status' => $values['status'],
+ ));
+
+ // Set the current subtask to "in progress"
+ $this->subtask->update(array(
+ 'id' => $subtask['id'],
+ 'status' => SubtaskModel::STATUS_INPROGRESS,
+ ));
+
+ $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
+ }
+}
diff --git a/app/Controller/SubtaskStatus.php b/app/Controller/SubtaskStatus.php
new file mode 100644
index 00000000..4fb82fc0
--- /dev/null
+++ b/app/Controller/SubtaskStatus.php
@@ -0,0 +1,72 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Subtask Status
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class SubtaskStatus extends Base
+{
+ /**
+ * Change status to the next status: Toto -> In Progress -> Done
+ *
+ * @access public
+ */
+ public function change()
+ {
+ $task = $this->getTask();
+ $subtask = $this->getSubtask();
+
+ $status = $this->subtask->toggleStatus($subtask['id']);
+
+ if ($this->request->getIntegerParam('refresh-table') === 0) {
+ $subtask['status'] = $status;
+ $html = $this->helper->subtask->toggleStatus($subtask, $task['project_id']);
+ } else {
+ $html = $this->renderTable($task);
+ }
+
+ $this->response->html($html);
+ }
+
+ /**
+ * Start/stop timer for subtasks
+ *
+ * @access public
+ */
+ public function timer()
+ {
+ $task = $this->getTask();
+ $subtask_id = $this->request->getIntegerParam('subtask_id');
+ $timer = $this->request->getStringParam('timer');
+
+ if ($timer === 'start') {
+ $this->subtaskTimeTracking->logStartTime($subtask_id, $this->userSession->getId());
+ } elseif ($timer === 'stop') {
+ $this->subtaskTimeTracking->logEndTime($subtask_id, $this->userSession->getId());
+ $this->subtaskTimeTracking->updateTaskTimeTracking($task['id']);
+ }
+
+ $this->response->html($this->renderTable($task));
+ }
+
+ /**
+ * Render table
+ *
+ * @access private
+ * @param array $task
+ * @return string
+ */
+ private function renderTable(array $task)
+ {
+ return $this->template->render('subtask/table', array(
+ 'task' => $task,
+ 'subtasks' => $this->subtask->getAll($task['id']),
+ 'editable' => true,
+ 'redirect' => 'task',
+ ));
+ }
+}
diff --git a/app/Controller/Swimlane.php b/app/Controller/Swimlane.php
index 0b29f598..8270a16f 100644
--- a/app/Controller/Swimlane.php
+++ b/app/Controller/Swimlane.php
@@ -24,7 +24,7 @@ class Swimlane extends Base
$swimlane = $this->swimlane->getById($this->request->getIntegerParam('swimlane_id'));
if (empty($swimlane)) {
- $this->session->flashError(t('Swimlane not found.'));
+ $this->flash->failure(t('Swimlane not found.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project_id)));
}
@@ -36,18 +36,32 @@ class Swimlane extends Base
*
* @access public
*/
- public function index(array $values = array(), array $errors = array())
+ public function index()
{
$project = $this->getProject();
- $this->response->html($this->projectLayout('swimlane/index', array(
+ $this->response->html($this->helper->layout->project('swimlane/index', array(
'default_swimlane' => $this->swimlane->getDefault($project['id']),
'active_swimlanes' => $this->swimlane->getAllByStatus($project['id'], SwimlaneModel::ACTIVE),
'inactive_swimlanes' => $this->swimlane->getAllByStatus($project['id'], SwimlaneModel::INACTIVE),
+ 'project' => $project,
+ 'title' => t('Swimlanes')
+ )));
+ }
+
+ /**
+ * Create a new swimlane
+ *
+ * @access public
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->template->render('swimlane/create', array(
'values' => $values + array('project_id' => $project['id']),
'errors' => $errors,
'project' => $project,
- 'title' => t('Swimlanes')
)));
}
@@ -60,18 +74,35 @@ class Swimlane extends Base
{
$project = $this->getProject();
$values = $this->request->getValues();
- list($valid, $errors) = $this->swimlane->validateCreation($values);
+ list($valid, $errors) = $this->swimlaneValidator->validateCreation($values);
if ($valid) {
if ($this->swimlane->create($values)) {
- $this->session->flash(t('Your swimlane have been created successfully.'));
+ $this->flash->success(t('Your swimlane have been created successfully.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to create your swimlane.'));
+ $errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
}
}
- $this->index($values, $errors);
+ $this->create($values, $errors);
+ }
+
+ /**
+ * Edit default swimlane (display the form)
+ *
+ * @access public
+ */
+ public function editDefault(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $swimlane = $this->swimlane->getDefault($project['id']);
+
+ $this->response->html($this->helper->layout->project('swimlane/edit_default', array(
+ 'values' => empty($values) ? $swimlane : $values,
+ 'errors' => $errors,
+ 'project' => $project,
+ )));
}
/**
@@ -79,23 +110,23 @@ class Swimlane extends Base
*
* @access public
*/
- public function change()
+ public function updateDefault()
{
$project = $this->getProject();
$values = $this->request->getValues() + array('show_default_swimlane' => 0);
- list($valid, ) = $this->swimlane->validateDefaultModification($values);
+ list($valid, $errors) = $this->swimlaneValidator->validateDefaultModification($values);
if ($valid) {
if ($this->swimlane->updateDefault($values)) {
- $this->session->flash(t('The default swimlane have been updated successfully.'));
- $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
+ $this->flash->success(t('The default swimlane have been updated successfully.'));
+ $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])), true);
} else {
- $this->session->flashError(t('Unable to update this swimlane.'));
+ $this->flash->failure(t('Unable to update this swimlane.'));
}
}
- $this->index();
+ $this->editDefault($values, $errors);
}
/**
@@ -108,11 +139,10 @@ class Swimlane extends Base
$project = $this->getProject();
$swimlane = $this->getSwimlane($project['id']);
- $this->response->html($this->projectLayout('swimlane/edit', array(
+ $this->response->html($this->helper->layout->project('swimlane/edit', array(
'values' => empty($values) ? $swimlane : $values,
'errors' => $errors,
'project' => $project,
- 'title' => t('Swimlanes')
)));
}
@@ -126,14 +156,14 @@ class Swimlane extends Base
$project = $this->getProject();
$values = $this->request->getValues();
- list($valid, $errors) = $this->swimlane->validateModification($values);
+ list($valid, $errors) = $this->swimlaneValidator->validateModification($values);
if ($valid) {
if ($this->swimlane->update($values)) {
- $this->session->flash(t('Swimlane updated successfully.'));
+ $this->flash->success(t('Swimlane updated successfully.'));
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
} else {
- $this->session->flashError(t('Unable to update this swimlane.'));
+ $errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
}
}
@@ -150,10 +180,9 @@ class Swimlane extends Base
$project = $this->getProject();
$swimlane = $this->getSwimlane($project['id']);
- $this->response->html($this->projectLayout('swimlane/remove', array(
+ $this->response->html($this->helper->layout->project('swimlane/remove', array(
'project' => $project,
'swimlane' => $swimlane,
- 'title' => t('Remove a swimlane')
)));
}
@@ -169,9 +198,9 @@ class Swimlane extends Base
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
if ($this->swimlane->remove($project['id'], $swimlane_id)) {
- $this->session->flash(t('Swimlane removed successfully.'));
+ $this->flash->success(t('Swimlane removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this swimlane.'));
+ $this->flash->failure(t('Unable to remove this swimlane.'));
}
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
@@ -189,9 +218,28 @@ class Swimlane extends Base
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
if ($this->swimlane->disable($project['id'], $swimlane_id)) {
- $this->session->flash(t('Swimlane updated successfully.'));
+ $this->flash->success(t('Swimlane updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update this swimlane.'));
+ $this->flash->failure(t('Unable to update this swimlane.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Disable default swimlane
+ *
+ * @access public
+ */
+ public function disableDefault()
+ {
+ $this->checkCSRFParam();
+ $project = $this->getProject();
+
+ if ($this->swimlane->disableDefault($project['id'])) {
+ $this->flash->success(t('Swimlane updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this swimlane.'));
}
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
@@ -209,41 +257,48 @@ class Swimlane extends Base
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
if ($this->swimlane->enable($project['id'], $swimlane_id)) {
- $this->session->flash(t('Swimlane updated successfully.'));
+ $this->flash->success(t('Swimlane updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update this swimlane.'));
+ $this->flash->failure(t('Unable to update this swimlane.'));
}
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
}
/**
- * Move up a swimlane
+ * Enable default swimlane
*
* @access public
*/
- public function moveup()
+ public function enableDefault()
{
$this->checkCSRFParam();
$project = $this->getProject();
- $swimlane_id = $this->request->getIntegerParam('swimlane_id');
- $this->swimlane->moveUp($project['id'], $swimlane_id);
+ if ($this->swimlane->enableDefault($project['id'])) {
+ $this->flash->success(t('Swimlane updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this swimlane.'));
+ }
+
$this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
}
/**
- * Move down a swimlane
+ * Move swimlane position
*
* @access public
*/
- public function movedown()
+ public function move()
{
- $this->checkCSRFParam();
$project = $this->getProject();
- $swimlane_id = $this->request->getIntegerParam('swimlane_id');
+ $values = $this->request->getJson();
- $this->swimlane->moveDown($project['id'], $swimlane_id);
- $this->response->redirect($this->helper->url->to('swimlane', 'index', array('project_id' => $project['id'])));
+ if (! empty($values) && isset($values['swimlane_id']) && isset($values['position'])) {
+ $result = $this->swimlane->changePosition($project['id'], $values['swimlane_id'], $values['position']);
+ return $this->response->json(array('result' => $result));
+ }
+
+ $this->forbidden();
}
}
diff --git a/app/Controller/Task.php b/app/Controller/Task.php
index 894802d8..539d377b 100644
--- a/app/Controller/Task.php
+++ b/app/Controller/Task.php
@@ -30,13 +30,13 @@ class Task extends Base
$this->notfound(true);
}
- $this->response->html($this->template->layout('task/public', array(
+ $this->response->html($this->helper->layout->app('task/public', array(
'project' => $project,
'comments' => $this->comment->getAll($task['id']),
'subtasks' => $this->subtask->getAll($task['id']),
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
'task' => $task,
- 'columns_list' => $this->board->getColumnsList($task['project_id']),
+ 'columns_list' => $this->column->getList($task['project_id']),
'colors_list' => $this->color->getList(),
'title' => $task['title'],
'no_layout' => true,
@@ -62,23 +62,21 @@ class Task extends Base
'time_spent' => $task['time_spent'] ?: '',
);
- $this->dateParser->format($values, array('date_started'), 'Y-m-d H:i');
+ $values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', 'm/d/Y H:i'));
- $this->response->html($this->taskLayout('task/show', array(
+ $this->response->html($this->helper->layout->task('task/show', array(
'project' => $this->project->getById($task['project_id']),
- 'files' => $this->file->getAllDocuments($task['id']),
- 'images' => $this->file->getAllImages($task['id']),
+ 'files' => $this->taskFile->getAllDocuments($task['id']),
+ 'images' => $this->taskFile->getAllImages($task['id']),
'comments' => $this->comment->getAll($task['id'], $this->userSession->getCommentSorting()),
'subtasks' => $subtasks,
'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
'task' => $task,
'values' => $values,
'link_label_list' => $this->link->getList(0, false),
- 'columns_list' => $this->board->getColumnsList($task['project_id']),
+ 'columns_list' => $this->column->getList($task['project_id']),
'colors_list' => $this->color->getList(),
- 'users_list' => $this->projectPermission->getMemberList($task['project_id'], true, false, false),
- 'date_format' => $this->config->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id'], true, false, false),
'title' => $task['project_name'].' &gt; '.$task['title'],
'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
@@ -95,7 +93,7 @@ class Task extends Base
{
$task = $this->getTask();
- $this->response->html($this->taskLayout('task/analytics', array(
+ $this->response->html($this->helper->layout->task('task/analytics', array(
'title' => $task['title'],
'task' => $task,
'lead_time' => $this->taskAnalytic->getLeadTime($task),
@@ -114,14 +112,14 @@ class Task extends Base
$task = $this->getTask();
$subtask_paginator = $this->paginator
- ->setUrl('task', 'timesheet', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'pagination' => 'subtasks'))
+ ->setUrl('task', 'timetracking', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'pagination' => 'subtasks'))
->setMax(15)
->setOrder('start')
->setDirection('DESC')
->setQuery($this->subtaskTimeTracking->getTaskQuery($task['id']))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
- $this->response->html($this->taskLayout('task/time_tracking_details', array(
+ $this->response->html($this->helper->layout->task('task/time_tracking_details', array(
'task' => $task,
'subtask_paginator' => $subtask_paginator,
)));
@@ -136,7 +134,7 @@ class Task extends Base
{
$task = $this->getTask();
- $this->response->html($this->taskLayout('task/transitions', array(
+ $this->response->html($this->helper->layout->task('task/transitions', array(
'task' => $task,
'transitions' => $this->transition->getAllByTask($task['id']),
)));
@@ -159,15 +157,15 @@ class Task extends Base
$this->checkCSRFParam();
if ($this->task->remove($task['id'])) {
- $this->session->flash(t('Task removed successfully.'));
+ $this->flash->success(t('Task removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this task.'));
+ $this->flash->failure(t('Unable to remove this task.'));
}
$this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
}
- $this->response->html($this->taskLayout('task/remove', array(
+ $this->response->html($this->helper->layout->task('task/remove', array(
'task' => $task,
)));
}
diff --git a/app/Controller/TaskExternalLink.php b/app/Controller/TaskExternalLink.php
new file mode 100644
index 00000000..f26922dd
--- /dev/null
+++ b/app/Controller/TaskExternalLink.php
@@ -0,0 +1,185 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
+
+/**
+ * Task External Link Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class TaskExternalLink extends Base
+{
+ /**
+ * Creation form
+ *
+ * @access public
+ */
+ public function show()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->helper->layout->task('task_external_link/show', array(
+ 'links' => $this->taskExternalLink->getAll($task['id']),
+ 'task' => $task,
+ 'title' => t('List of external links'),
+ )));
+ }
+
+ /**
+ * First creation form
+ *
+ * @access public
+ */
+ public function find(array $values = array(), array $errors = array())
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->helper->layout->task('task_external_link/find', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'task' => $task,
+ 'types' => $this->externalLinkManager->getTypes(),
+ )));
+ }
+
+ /**
+ * Second creation form
+ *
+ * @access public
+ */
+ public function create()
+ {
+ try {
+
+ $task = $this->getTask();
+ $values = $this->request->getValues();
+
+ $provider = $this->externalLinkManager->setUserInput($values)->find();
+ $link = $provider->getLink();
+
+ $this->response->html($this->helper->layout->task('task_external_link/create', array(
+ 'values' => array(
+ 'title' => $link->getTitle(),
+ 'url' => $link->getUrl(),
+ 'link_type' => $provider->getType(),
+ ),
+ 'dependencies' => $provider->getDependencies(),
+ 'errors' => array(),
+ 'task' => $task,
+ )));
+
+ } catch (ExternalLinkProviderNotFound $e) {
+ $errors = array('text' => array(t('Unable to fetch link information.')));
+ $this->find($values, $errors);
+ }
+ }
+
+ /**
+ * Save link
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $task = $this->getTask();
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
+
+ if ($valid && $this->taskExternalLink->create($values)) {
+ $this->flash->success(t('Link added successfully.'));
+ return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ }
+
+ $this->edit($values, $errors);
+ }
+
+ /**
+ * Edit form
+ *
+ * @access public
+ */
+ public function edit(array $values = array(), array $errors = array())
+ {
+ $task = $this->getTask();
+ $link_id = $this->request->getIntegerParam('link_id');
+
+ if ($link_id > 0) {
+ $values = $this->taskExternalLink->getById($link_id);
+ }
+
+ if (empty($values)) {
+ return $this->notfound();
+ }
+
+ $provider = $this->externalLinkManager->getProvider($values['link_type']);
+
+ $this->response->html($this->helper->layout->task('task_external_link/edit', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'task' => $task,
+ 'dependencies' => $provider->getDependencies(),
+ )));
+ }
+
+ /**
+ * Update link
+ *
+ * @access public
+ */
+ public function update()
+ {
+ $task = $this->getTask();
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
+
+ if ($valid && $this->taskExternalLink->update($values)) {
+ $this->flash->success(t('Link updated successfully.'));
+ return $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ }
+
+ $this->edit($values, $errors);
+ }
+
+ /**
+ * Confirmation dialog before removing a link
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $task = $this->getTask();
+ $link_id = $this->request->getIntegerParam('link_id');
+ $link = $this->taskExternalLink->getById($link_id);
+
+ if (empty($link)) {
+ return $this->notfound();
+ }
+
+ $this->response->html($this->helper->layout->task('task_external_link/remove', array(
+ 'link' => $link,
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * Remove a link
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $task = $this->getTask();
+
+ if ($this->taskExternalLink->remove($this->request->getIntegerParam('link_id'))) {
+ $this->flash->success(t('Link removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this link.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('TaskExternalLink', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
+ }
+}
diff --git a/app/Controller/TaskFile.php b/app/Controller/TaskFile.php
new file mode 100644
index 00000000..2b0152a7
--- /dev/null
+++ b/app/Controller/TaskFile.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Task File Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class TaskFile extends Base
+{
+ /**
+ * Screenshot
+ *
+ * @access public
+ */
+ public function screenshot()
+ {
+ $task = $this->getTask();
+
+ if ($this->request->isPost() && $this->taskFile->uploadScreenshot($task['id'], $this->request->getValue('screenshot')) !== false) {
+ $this->flash->success(t('Screenshot uploaded successfully.'));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ }
+
+ $this->response->html($this->template->render('task_file/screenshot', array(
+ 'task' => $task,
+ )));
+ }
+
+ /**
+ * File upload form
+ *
+ * @access public
+ */
+ public function create()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->template->render('task_file/create', array(
+ 'task' => $task,
+ 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
+ )));
+ }
+
+ /**
+ * File upload (save files)
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $task = $this->getTask();
+
+ if (! $this->taskFile->uploadFiles($task['id'], $this->request->getFileInfo('files'))) {
+ $this->flash->failure(t('Unable to upload the file.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ }
+
+ /**
+ * Remove a file
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $task = $this->getTask();
+ $file = $this->taskFile->getById($this->request->getIntegerParam('file_id'));
+
+ if ($file['task_id'] == $task['id'] && $this->taskFile->remove($file['id'])) {
+ $this->flash->success(t('File removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this file.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
+ }
+
+ /**
+ * Confirmation dialog before removing a file
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $task = $this->getTask();
+ $file = $this->taskFile->getById($this->request->getIntegerParam('file_id'));
+
+ $this->response->html($this->template->render('task_file/remove', array(
+ 'task' => $task,
+ 'file' => $file,
+ )));
+ }
+}
diff --git a/app/Controller/TaskHelper.php b/app/Controller/TaskHelper.php
new file mode 100644
index 00000000..236af33e
--- /dev/null
+++ b/app/Controller/TaskHelper.php
@@ -0,0 +1,57 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Task Ajax Helper
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class TaskHelper extends Base
+{
+ /**
+ * Render Markdown text and reply with the HTML Code
+ *
+ * @access public
+ */
+ public function preview()
+ {
+ $payload = $this->request->getJson();
+
+ if (empty($payload['text'])) {
+ $this->response->html('<p>'.t('Nothing to preview...').'</p>');
+ }
+
+ $this->response->html($this->helper->text->markdown($payload['text']));
+ }
+
+ /**
+ * Task autocompletion (Ajax)
+ *
+ * @access public
+ */
+ public function autocomplete()
+ {
+ $search = $this->request->getStringParam('term');
+ $projects = $this->projectPermission->getActiveProjectIds($this->userSession->getId());
+
+ if (empty($projects)) {
+ $this->response->json(array());
+ }
+
+ $filter = $this->taskFilterAutoCompleteFormatter
+ ->create()
+ ->filterByProjects($projects)
+ ->excludeTasks(array($this->request->getIntegerParam('exclude_task_id')));
+
+ // Search by task id or by title
+ if (ctype_digit($search)) {
+ $filter->filterById($search);
+ } else {
+ $filter->filterByTitle($search);
+ }
+
+ $this->response->json($filter->format());
+ }
+}
diff --git a/app/Controller/TaskImport.php b/app/Controller/TaskImport.php
index 0e9d2169..460c608c 100644
--- a/app/Controller/TaskImport.php
+++ b/app/Controller/TaskImport.php
@@ -20,7 +20,7 @@ class TaskImport extends Base
{
$project = $this->getProject();
- $this->response->html($this->projectLayout('task_import/step1', array(
+ $this->response->html($this->helper->layout->project('task_import/step1', array(
'project' => $project,
'values' => $values,
'errors' => $errors,
@@ -52,9 +52,9 @@ class TaskImport extends Base
$csv->read($filename, array($this->taskImport, 'import'));
if ($this->taskImport->counter > 0) {
- $this->session->flash(t('%d task(s) have been imported successfully.', $this->taskImport->counter));
+ $this->flash->success(t('%d task(s) have been imported successfully.', $this->taskImport->counter));
} else {
- $this->session->flashError(t('Nothing have been imported!'));
+ $this->flash->failure(t('Nothing have been imported!'));
}
$this->response->redirect($this->helper->url->to('taskImport', 'step1', array('project_id' => $project['id'])));
diff --git a/app/Controller/TaskRecurrence.php b/app/Controller/TaskRecurrence.php
new file mode 100644
index 00000000..f02f3cdc
--- /dev/null
+++ b/app/Controller/TaskRecurrence.php
@@ -0,0 +1,61 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * Task Recurrence controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class TaskRecurrence extends Base
+{
+ /**
+ * Edit recurrence form
+ *
+ * @access public
+ */
+ public function edit(array $values = array(), array $errors = array())
+ {
+ $task = $this->getTask();
+
+ if (empty($values)) {
+ $values = $task;
+ }
+
+ $this->response->html($this->helper->layout->task('task_recurrence/edit', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'task' => $task,
+ 'recurrence_status_list' => $this->task->getRecurrenceStatusList(),
+ 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
+ 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
+ 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(),
+ )));
+ }
+
+ /**
+ * Update recurrence form
+ *
+ * @access public
+ */
+ public function update()
+ {
+ $task = $this->getTask();
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values);
+
+ if ($valid) {
+ if ($this->taskModification->update($values)) {
+ $this->flash->success(t('Task updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update your task.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
+ }
+
+ $this->edit($values, $errors);
+ }
+}
diff --git a/app/Controller/Taskcreation.php b/app/Controller/Taskcreation.php
index e47cd1b7..1d8a0e29 100644
--- a/app/Controller/Taskcreation.php
+++ b/app/Controller/Taskcreation.php
@@ -18,30 +18,29 @@ class Taskcreation extends Base
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
- $method = $this->request->isAjax() ? 'render' : 'layout';
$swimlanes_list = $this->swimlane->getList($project['id'], false, true);
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->request->getStringParam('color_id', $this->color->getDefaultColor()),
- 'owner_id' => $this->request->getIntegerParam('owner_id'),
- 'another_task' => $this->request->getIntegerParam('another_task'),
+ 'color_id' => $this->color->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));
}
- $this->response->html($this->template->$method('task_creation/form', array(
- 'ajax' => $this->request->isAjax(),
+ $this->response->html($this->template->render('task_creation/form', array(
+ 'project' => $project,
'errors' => $errors,
'values' => $values + array('project_id' => $project['id']),
- 'columns_list' => $this->board->getColumnsList($project['id']),
- 'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true),
+ 'columns_list' => $this->column->getList($project['id']),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($project['id'], true, false, true),
'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($project['id']),
'swimlanes_list' => $swimlanes_list,
- 'date_format' => $this->config->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
'title' => $project['name'].' &gt; '.t('New task')
)));
}
@@ -59,26 +58,27 @@ class Taskcreation extends Base
list($valid, $errors) = $this->taskValidator->validateCreation($values);
if ($valid && $this->taskCreation->create($values)) {
- $this->session->flash(t('Task created successfully.'));
- $this->afterSave($project, $values);
- } else {
- $this->session->flashError(t('Unable to create your task.'));
+ $this->flash->success(t('Task created successfully.'));
+ return $this->afterSave($project, $values);
}
+ $this->flash->failure(t('Unable to create your task.'));
$this->create($values, $errors);
}
private function afterSave(array $project, array &$values)
{
if (isset($values['another_task']) && $values['another_task'] == 1) {
- unset($values['title']);
- unset($values['description']);
-
- if (! $this->request->isAjax()) {
- $this->response->redirect($this->helper->url->to('taskcreation', 'create', $values));
- }
- } else {
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])));
+ return $this->create(array(
+ 'owner_id' => $values['owner_id'],
+ 'color_id' => $values['color_id'],
+ 'category_id' => isset($values['category_id']) ? $values['category_id'] : 0,
+ 'column_id' => $values['column_id'],
+ 'swimlane_id' => isset($values['swimlane_id']) ? $values['swimlane_id'] : 0,
+ 'another_task' => 1,
+ ));
}
+
+ $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $project['id'])));
}
}
diff --git a/app/Controller/Taskduplication.php b/app/Controller/Taskduplication.php
index 79f498fc..7641a48d 100644
--- a/app/Controller/Taskduplication.php
+++ b/app/Controller/Taskduplication.php
@@ -24,15 +24,15 @@ class Taskduplication extends Base
$task_id = $this->taskDuplication->duplicate($task['id']);
if ($task_id > 0) {
- $this->session->flash(t('Task created successfully.'));
+ $this->flash->success(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task_id)));
} else {
- $this->session->flashError(t('Unable to create this task.'));
- $this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
+ $this->flash->failure(t('Unable to create this task.'));
+ $this->response->redirect($this->helper->url->to('taskduplication', 'duplicate', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
}
- $this->response->html($this->taskLayout('task_duplication/duplicate', array(
+ $this->response->html($this->helper->layout->task('task_duplication/duplicate', array(
'task' => $task,
)));
}
@@ -56,11 +56,11 @@ class Taskduplication extends Base
$values['column_id'],
$values['category_id'],
$values['owner_id'])) {
- $this->session->flash(t('Task updated successfully.'));
+ $this->flash->success(t('Task updated successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task['id'])));
}
- $this->session->flashError(t('Unable to update your task.'));
+ $this->flash->failure(t('Unable to update your task.'));
}
$this->chooseDestination($task, 'task_duplication/move');
@@ -86,12 +86,12 @@ class Taskduplication extends Base
);
if ($task_id > 0) {
- $this->session->flash(t('Task created successfully.'));
+ $this->flash->success(t('Task created successfully.'));
$this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $values['project_id'], 'task_id' => $task_id)));
}
}
- $this->session->flashError(t('Unable to create your task.'));
+ $this->flash->failure(t('Unable to create your task.'));
}
$this->chooseDestination($task, 'task_duplication/copy');
@@ -107,7 +107,7 @@ class Taskduplication extends Base
private function chooseDestination(array $task, $template)
{
$values = array();
- $projects_list = $this->projectPermission->getActiveMemberProjects($this->userSession->getId());
+ $projects_list = $this->projectUserRole->getActiveProjectsByUser($this->userSession->getId());
unset($projects_list[$task['project_id']]);
@@ -115,9 +115,9 @@ class Taskduplication extends Base
$dst_project_id = $this->request->getIntegerParam('dst_project_id', key($projects_list));
$swimlanes_list = $this->swimlane->getList($dst_project_id, false, true);
- $columns_list = $this->board->getColumnsList($dst_project_id);
+ $columns_list = $this->column->getList($dst_project_id);
$categories_list = $this->category->getList($dst_project_id);
- $users_list = $this->projectPermission->getMemberList($dst_project_id);
+ $users_list = $this->projectUserRole->getAssignableUsersList($dst_project_id);
$values = $this->taskDuplication->checkDestinationProjectValues($task);
$values['project_id'] = $dst_project_id;
@@ -128,7 +128,7 @@ class Taskduplication extends Base
$users_list = array();
}
- $this->response->html($this->taskLayout($template, array(
+ $this->response->html($this->helper->layout->task($template, array(
'values' => $values,
'task' => $task,
'projects_list' => $projects_list,
diff --git a/app/Controller/Tasklink.php b/app/Controller/Tasklink.php
index 587769ee..fdb4fada 100644
--- a/app/Controller/Tasklink.php
+++ b/app/Controller/Tasklink.php
@@ -22,13 +22,32 @@ class Tasklink extends Base
$link = $this->taskLink->getById($this->request->getIntegerParam('link_id'));
if (empty($link)) {
- $this->notfound();
+ return $this->notfound();
}
return $link;
}
/**
+ * Show links
+ *
+ * @access public
+ */
+ public function show()
+ {
+ $task = $this->getTask();
+ $project = $this->project->getById($task['project_id']);
+
+ $this->response->html($this->helper->layout->task('tasklink/show', array(
+ 'links' => $this->taskLink->getAllGroupedByLabel($task['id']),
+ 'task' => $task,
+ 'project' => $project,
+ 'editable' => true,
+ 'is_public' => false,
+ )));
+ }
+
+ /**
* Creation form
*
* @access public
@@ -36,20 +55,8 @@ class Tasklink extends Base
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
- $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
-
- if ($ajax && empty($errors)) {
- $this->response->html($this->template->render('tasklink/create', array(
- 'values' => $values,
- 'errors' => $errors,
- 'task' => $task,
- 'labels' => $this->link->getList(0, false),
- 'title' => t('Add a new link'),
- 'ajax' => $ajax,
- )));
- }
- $this->response->html($this->taskLayout('tasklink/create', array(
+ $this->response->html($this->helper->layout->task('tasklink/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
@@ -67,23 +74,17 @@ class Tasklink extends Base
{
$task = $this->getTask();
$values = $this->request->getValues();
- $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
- list($valid, $errors) = $this->taskLink->validateCreation($values);
+ list($valid, $errors) = $this->taskLinkValidator->validateCreation($values);
if ($valid) {
if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
- $this->session->flash(t('Link added successfully.'));
-
- if ($ajax) {
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
+ $this->flash->success(t('Link added successfully.'));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links', true);
}
$errors = array('title' => array(t('The exact same link already exists')));
- $this->session->flashError(t('Unable to create your link.'));
+ $this->flash->failure(t('Unable to create your link.'));
}
$this->create($values, $errors);
@@ -105,7 +106,7 @@ class Tasklink extends Base
$values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title'];
}
- $this->response->html($this->taskLayout('tasklink/edit', array(
+ $this->response->html($this->helper->layout->task('tasklink/edit', array(
'values' => $values,
'errors' => $errors,
'task_link' => $task_link,
@@ -125,15 +126,15 @@ class Tasklink extends Base
$task = $this->getTask();
$values = $this->request->getValues();
- list($valid, $errors) = $this->taskLink->validateModification($values);
+ list($valid, $errors) = $this->taskLinkValidator->validateModification($values);
if ($valid) {
if ($this->taskLink->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
- $this->session->flash(t('Link updated successfully.'));
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
+ $this->flash->success(t('Link updated successfully.'));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
}
- $this->session->flashError(t('Unable to update your link.'));
+ $this->flash->failure(t('Unable to update your link.'));
}
$this->edit($values, $errors);
@@ -149,7 +150,7 @@ class Tasklink extends Base
$task = $this->getTask();
$link = $this->getTaskLink();
- $this->response->html($this->taskLayout('tasklink/remove', array(
+ $this->response->html($this->helper->layout->task('tasklink/remove', array(
'link' => $link,
'task' => $task,
)));
@@ -166,9 +167,9 @@ class Tasklink extends Base
$task = $this->getTask();
if ($this->taskLink->remove($this->request->getIntegerParam('link_id'))) {
- $this->session->flash(t('Link removed successfully.'));
+ $this->flash->success(t('Link removed successfully.'));
} else {
- $this->session->flashError(t('Unable to remove this link.'));
+ $this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
diff --git a/app/Controller/Taskmodification.php b/app/Controller/Taskmodification.php
index b1105dcc..306d34c0 100644
--- a/app/Controller/Taskmodification.php
+++ b/app/Controller/Taskmodification.php
@@ -23,71 +23,48 @@ class Taskmodification extends Base
}
/**
- * Update time tracking information
+ * Edit description form
*
* @access public
*/
- public function time()
+ public function description(array $values = array(), array $errors = array())
{
$task = $this->getTask();
- $values = $this->request->getValues();
- list($valid, ) = $this->taskValidator->validateTimeModification($values);
-
- if ($valid && $this->taskModification->update($values)) {
- $this->session->flash(t('Task updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update your task.'));
+ if (empty($values)) {
+ $values = array('id' => $task['id'], 'description' => $task['description']);
}
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
+ $this->response->html($this->helper->layout->task('task_modification/edit_description', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'task' => $task,
+ )));
}
/**
- * Edit description form
+ * Update description
*
* @access public
*/
- public function description()
+ public function updateDescription()
{
$task = $this->getTask();
- $ajax = $this->request->isAjax() || $this->request->getIntegerParam('ajax');
-
- if ($this->request->isPost()) {
- $values = $this->request->getValues();
-
- list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values);
+ $values = $this->request->getValues();
- if ($valid) {
- if ($this->taskModification->update($values)) {
- $this->session->flash(t('Task updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update your task.'));
- }
+ list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values);
- if ($ajax) {
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
- } else {
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
- }
+ if ($valid) {
+ if ($this->taskModification->update($values)) {
+ $this->flash->success(t('Task updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update your task.'));
}
- } else {
- $values = $task;
- $errors = array();
- }
- $params = array(
- 'values' => $values,
- 'errors' => $errors,
- 'task' => $task,
- 'ajax' => $ajax,
- );
-
- if ($ajax) {
- $this->response->html($this->template->render('task_modification/edit_description', $params));
- } else {
- $this->response->html($this->taskLayout('task_modification/edit_description', $params));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
}
+
+ $this->description($values, $errors);
}
/**
@@ -98,33 +75,26 @@ class Taskmodification extends Base
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
- $ajax = $this->request->isAjax();
+ $project = $this->project->getById($task['project_id']);
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));
}
- $this->dateParser->format($values, array('date_due'));
+ $values = $this->dateParser->format($values, array('date_due'), $this->config->get('application_date_format', 'm/d/Y'));
+ $values = $this->dateParser->format($values, array('date_started'), $this->config->get('application_datetime_format', 'm/d/Y H:i'));
- $params = array(
+ $this->response->html($this->helper->layout->task('task_modification/edit_task', array(
+ 'project' => $project,
'values' => $values,
'errors' => $errors,
'task' => $task,
- 'users_list' => $this->projectPermission->getMemberList($task['project_id']),
+ 'users_list' => $this->projectUserRole->getAssignableUsersList($task['project_id']),
'colors_list' => $this->color->getList(),
'categories_list' => $this->category->getList($task['project_id']),
- 'date_format' => $this->config->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats(),
- 'ajax' => $ajax,
- );
-
- if ($ajax) {
- $html = $this->template->render('task_modification/edit_task', $params);
- } else {
- $html = $this->taskLayout('task_modification/edit_task', $params);
- }
-
- $this->response->html($html);
+ )));
}
/**
@@ -140,57 +110,11 @@ class Taskmodification extends Base
list($valid, $errors) = $this->taskValidator->validateModification($values);
if ($valid && $this->taskModification->update($values)) {
- $this->session->flash(t('Task updated successfully.'));
-
- if ($this->request->isAjax()) {
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
- } else {
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
- }
+ $this->flash->success(t('Task updated successfully.'));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
} else {
- $this->session->flashError(t('Unable to update your task.'));
+ $this->flash->failure(t('Unable to update your task.'));
$this->edit($values, $errors);
}
}
-
- /**
- * Edit recurrence form
- *
- * @access public
- */
- public function recurrence()
- {
- $task = $this->getTask();
-
- if ($this->request->isPost()) {
- $values = $this->request->getValues();
-
- list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values);
-
- if ($valid) {
- if ($this->taskModification->update($values)) {
- $this->session->flash(t('Task updated successfully.'));
- } else {
- $this->session->flashError(t('Unable to update your task.'));
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])));
- }
- } else {
- $values = $task;
- $errors = array();
- }
-
- $params = array(
- 'values' => $values,
- 'errors' => $errors,
- 'task' => $task,
- 'recurrence_status_list' => $this->task->getRecurrenceStatusList(),
- 'recurrence_trigger_list' => $this->task->getRecurrenceTriggerList(),
- 'recurrence_timeframe_list' => $this->task->getRecurrenceTimeframeList(),
- 'recurrence_basedate_list' => $this->task->getRecurrenceBasedateList(),
- );
-
- $this->response->html($this->taskLayout('task_modification/edit_recurrence', $params));
- }
}
diff --git a/app/Controller/Taskstatus.php b/app/Controller/Taskstatus.php
index c0421ea7..c07f2cc5 100644
--- a/app/Controller/Taskstatus.php
+++ b/app/Controller/Taskstatus.php
@@ -17,9 +17,7 @@ class Taskstatus extends Base
*/
public function close()
{
- $task = $this->getTask();
- $this->changeStatus($task, 'close', t('Task closed successfully.'), t('Unable to close this task.'));
- $this->renderTemplate($task, 'task_status/close');
+ $this->changeStatus('close', 'task_status/close', t('Task closed successfully.'), t('Unable to close this task.'));
}
/**
@@ -29,44 +27,36 @@ class Taskstatus extends Base
*/
public function open()
{
- $task = $this->getTask();
- $this->changeStatus($task, 'open', t('Task opened successfully.'), t('Unable to open this task.'));
- $this->renderTemplate($task, 'task_status/open');
+ $this->changeStatus('open', 'task_status/open', t('Task opened successfully.'), t('Unable to open this task.'));
}
- private function changeStatus(array $task, $method, $success_message, $failure_message)
+ /**
+ * Common method to change status
+ *
+ * @access private
+ * @param string $method
+ * @param string $template
+ * @param string $success_message
+ * @param string $failure_message
+ */
+ private function changeStatus($method, $template, $success_message, $failure_message)
{
+ $task = $this->getTask();
+
if ($this->request->getStringParam('confirmation') === 'yes') {
$this->checkCSRFParam();
if ($this->taskStatus->$method($task['id'])) {
- $this->session->flash($success_message);
+ $this->flash->success($success_message);
} else {
- $this->session->flashError($failure_message);
+ $this->flash->failure($failure_message);
}
- if ($this->request->getStringParam('redirect') === 'board') {
- $this->response->redirect($this->helper->url->to('board', 'show', array('project_id' => $task['project_id'])));
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
- }
- }
-
- private function renderTemplate(array $task, $template)
- {
- $redirect = $this->request->getStringParam('redirect');
-
- if ($this->request->isAjax()) {
- $this->response->html($this->template->render($template, array(
- 'task' => $task,
- 'redirect' => $redirect,
- )));
+ return $this->response->redirect($this->helper->url->to('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
}
- $this->response->html($this->taskLayout($template, array(
+ $this->response->html($this->helper->layout->task($template, array(
'task' => $task,
- 'redirect' => $redirect,
)));
}
}
diff --git a/app/Controller/Timer.php b/app/Controller/Timer.php
deleted file mode 100644
index 0267fcdd..00000000
--- a/app/Controller/Timer.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace Kanboard\Controller;
-
-/**
- * Time Tracking controller
- *
- * @package controller
- * @author Frederic Guillot
- */
-class Timer extends Base
-{
- /**
- * Start/stop timer for subtasks
- *
- * @access public
- */
- public function subtask()
- {
- $project_id = $this->request->getIntegerParam('project_id');
- $task_id = $this->request->getIntegerParam('task_id');
- $subtask_id = $this->request->getIntegerParam('subtask_id');
- $timer = $this->request->getStringParam('timer');
-
- if ($timer === 'start') {
- $this->subtaskTimeTracking->logStartTime($subtask_id, $this->userSession->getId());
- } elseif ($timer === 'stop') {
- $this->subtaskTimeTracking->logEndTime($subtask_id, $this->userSession->getId());
- $this->subtaskTimeTracking->updateTaskTimeTracking($task_id);
- }
-
- $this->response->redirect($this->helper->url->to('task', 'show', array('project_id' => $project_id, 'task_id' => $task_id)).'#subtasks');
- }
-}
diff --git a/app/Controller/Twofactor.php b/app/Controller/Twofactor.php
index 179241f8..10292261 100644
--- a/app/Controller/Twofactor.php
+++ b/app/Controller/Twofactor.php
@@ -2,10 +2,6 @@
namespace Kanboard\Controller;
-use Otp\Otp;
-use Otp\GoogleAuthenticator;
-use Base32\Base32;
-
/**
* Two Factor Auth controller
*
@@ -27,7 +23,7 @@ class Twofactor extends User
}
/**
- * Index
+ * Show form to disable/enable 2FA
*
* @access public
*/
@@ -35,68 +31,98 @@ class Twofactor extends User
{
$user = $this->getUser();
$this->checkCurrentUser($user);
+ unset($this->sessionStorage->twoFactorSecret);
+
+ $this->response->html($this->helper->layout->user('twofactor/index', array(
+ 'user' => $user,
+ 'provider' => $this->authenticationManager->getPostAuthenticationProvider()->getName(),
+ )));
+ }
+
+ /**
+ * Show page with secret and test form
+ *
+ * @access public
+ */
+ public function show()
+ {
+ $user = $this->getUser();
+ $this->checkCurrentUser($user);
$label = $user['email'] ?: $user['username'];
+ $provider = $this->authenticationManager->getPostAuthenticationProvider();
- $this->response->html($this->layout('twofactor/index', array(
+ if (! isset($this->sessionStorage->twoFactorSecret)) {
+ $provider->generateSecret();
+ $provider->beforeCode();
+ $this->sessionStorage->twoFactorSecret = $provider->getSecret();
+ } else {
+ $provider->setSecret($this->sessionStorage->twoFactorSecret);
+ }
+
+ $this->response->html($this->helper->layout->user('twofactor/show', array(
'user' => $user,
- 'qrcode_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getQrCodeUrl('totp', $label, $user['twofactor_secret']) : '',
- 'key_url' => $user['twofactor_activated'] == 1 ? GoogleAuthenticator::getKeyUri('totp', $label, $user['twofactor_secret']) : '',
+ 'secret' => $this->sessionStorage->twoFactorSecret,
+ 'qrcode_url' => $provider->getQrCodeUrl($label),
+ 'key_url' => $provider->getKeyUrl($label),
)));
}
/**
- * Enable/disable 2FA
+ * Test code and save secret
*
* @access public
*/
- public function save()
+ public function test()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$values = $this->request->getValues();
- if (isset($values['twofactor_activated']) && $values['twofactor_activated'] == 1) {
+ $provider = $this->authenticationManager->getPostAuthenticationProvider();
+ $provider->setCode(empty($values['code']) ? '' : $values['code']);
+ $provider->setSecret($this->sessionStorage->twoFactorSecret);
+
+ if ($provider->authenticate()) {
+ $this->flash->success(t('The two factor authentication code is valid.'));
+
$this->user->update(array(
'id' => $user['id'],
'twofactor_activated' => 1,
- 'twofactor_secret' => GoogleAuthenticator::generateRandom(),
+ 'twofactor_secret' => $this->authenticationManager->getPostAuthenticationProvider()->getSecret(),
));
- } else {
- $this->user->update(array(
- 'id' => $user['id'],
- 'twofactor_activated' => 0,
- 'twofactor_secret' => '',
- ));
- }
- // Allow the user to test or disable the feature
- $_SESSION['user']['twofactor_activated'] = false;
+ unset($this->sessionStorage->twoFactorSecret);
+ $this->userSession->disablePostAuthentication();
- $this->session->flash(t('User updated successfully.'));
- $this->response->redirect($this->helper->url->to('twofactor', 'index', array('user_id' => $user['id'])));
+ $this->response->redirect($this->helper->url->to('twofactor', 'index', array('user_id' => $user['id'])));
+ } else {
+ $this->flash->failure(t('The two factor authentication code is not valid.'));
+ $this->response->redirect($this->helper->url->to('twofactor', 'show', array('user_id' => $user['id'])));
+ }
}
/**
- * Test 2FA
+ * Disable 2FA for the current user
*
* @access public
*/
- public function test()
+ public function deactivate()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
- $otp = new Otp;
- $values = $this->request->getValues();
+ $this->user->update(array(
+ 'id' => $user['id'],
+ 'twofactor_activated' => 0,
+ 'twofactor_secret' => '',
+ ));
- if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
- $this->session->flash(t('The two factor authentication code is valid.'));
- } else {
- $this->session->flashError(t('The two factor authentication code is not valid.'));
- }
+ // Allow the user to test or disable the feature
+ $this->userSession->disablePostAuthentication();
+ $this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('twofactor', 'index', array('user_id' => $user['id'])));
}
@@ -110,15 +136,18 @@ class Twofactor extends User
$user = $this->getUser();
$this->checkCurrentUser($user);
- $otp = new Otp;
$values = $this->request->getValues();
- if (! empty($values['code']) && $otp->checkTotp(Base32::decode($user['twofactor_secret']), $values['code'])) {
- $this->session['2fa_validated'] = true;
- $this->session->flash(t('The two factor authentication code is valid.'));
+ $provider = $this->authenticationManager->getPostAuthenticationProvider();
+ $provider->setCode(empty($values['code']) ? '' : $values['code']);
+ $provider->setSecret($user['twofactor_secret']);
+
+ if ($provider->authenticate()) {
+ $this->userSession->validatePostAuthentication();
+ $this->flash->success(t('The two factor authentication code is valid.'));
$this->response->redirect($this->helper->url->to('app', 'index'));
} else {
- $this->session->flashError(t('The two factor authentication code is not valid.'));
+ $this->flash->failure(t('The two factor authentication code is not valid.'));
$this->response->redirect($this->helper->url->to('twofactor', 'code'));
}
}
@@ -130,7 +159,13 @@ class Twofactor extends User
*/
public function code()
{
- $this->response->html($this->template->layout('twofactor/check', array(
+ if (! isset($this->sessionStorage->twoFactorBeforeCodeCalled)) {
+ $provider = $this->authenticationManager->getPostAuthenticationProvider();
+ $provider->beforeCode();
+ $this->sessionStorage->twoFactorBeforeCodeCalled = true;
+ }
+
+ $this->response->html($this->helper->layout->app('twofactor/check', array(
'title' => t('Check two factor authentication code'),
)));
}
@@ -156,7 +191,7 @@ class Twofactor extends User
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
}
- $this->response->html($this->layout('twofactor/disable', array(
+ $this->response->html($this->helper->layout->user('twofactor/disable', array(
'user' => $user,
)));
}
diff --git a/app/Controller/User.php b/app/Controller/User.php
index b5f4edeb..f7d7d2e0 100644
--- a/app/Controller/User.php
+++ b/app/Controller/User.php
@@ -2,7 +2,9 @@
namespace Kanboard\Controller;
-use Kanboard\Model\NotificationType;
+use Kanboard\Notification\Mail as MailNotification;
+use Kanboard\Model\Project as ProjectModel;
+use Kanboard\Core\Security\Role;
/**
* User controller
@@ -13,27 +15,6 @@ use Kanboard\Model\NotificationType;
class User extends Base
{
/**
- * Common layout for user views
- *
- * @access protected
- * @param string $template Template name
- * @param array $params Template parameters
- * @return string
- */
- protected function layout($template, array $params)
- {
- $content = $this->template->render($template, $params);
- $params['user_content_for_layout'] = $content;
- $params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
-
- if (isset($params['user'])) {
- $params['title'] = ($params['user']['name'] ?: $params['user']['username']).' (#'.$params['user']['id'].')';
- }
-
- return $this->template->layout('user/layout', $params);
- }
-
- /**
* List all users
*
* @access public
@@ -48,11 +29,32 @@ class User extends Base
->calculate();
$this->response->html(
- $this->template->layout('user/index', array(
- 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
+ $this->helper->layout->app('user/index', array(
'title' => t('Users').' ('.$paginator->getTotal().')',
'paginator' => $paginator,
- )));
+ )
+ ));
+ }
+
+ /**
+ * Public user profile
+ *
+ * @access public
+ */
+ public function profile()
+ {
+ $user = $this->user->getById($this->request->getIntegerParam('user_id'));
+
+ if (empty($user)) {
+ $this->notfound();
+ }
+
+ $this->response->html(
+ $this->helper->layout->app('user/profile', array(
+ 'title' => $user['name'] ?: $user['username'],
+ 'user' => $user,
+ )
+ ));
}
/**
@@ -64,13 +66,13 @@ class User extends Base
{
$is_remote = $this->request->getIntegerParam('remote') == 1 || (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1);
- $this->response->html($this->template->layout($is_remote ? 'user/create_remote' : 'user/create_local', array(
+ $this->response->html($this->helper->layout->app($is_remote ? 'user/create_remote' : 'user/create_local', array(
'timezones' => $this->config->getTimezones(true),
'languages' => $this->config->getLanguages(true),
- 'board_selector' => $this->projectPermission->getAllowedProjects($this->userSession->getId()),
+ 'roles' => $this->role->getApplicationRoles(),
'projects' => $this->project->getList(),
'errors' => $errors,
- 'values' => $values,
+ 'values' => $values + array('role' => Role::APP_USER),
'title' => t('New user')
)));
}
@@ -83,7 +85,7 @@ class User extends Base
public function save()
{
$values = $this->request->getValues();
- list($valid, $errors) = $this->user->validateCreation($values);
+ list($valid, $errors) = $this->userValidator->validateCreation($values);
if ($valid) {
$project_id = empty($values['project_id']) ? 0 : $values['project_id'];
@@ -92,16 +94,16 @@ class User extends Base
$user_id = $this->user->create($values);
if ($user_id !== false) {
- $this->projectPermission->addMember($project_id, $user_id);
+ $this->projectUserRole->addUser($project_id, $user_id, Role::PROJECT_MEMBER);
if (! empty($values['notifications_enabled'])) {
- $this->userNotificationType->saveSelectedTypes($user_id, array(NotificationType::TYPE_EMAIL));
+ $this->userNotificationType->saveSelectedTypes($user_id, array(MailNotification::TYPE));
}
- $this->session->flash(t('User created successfully.'));
+ $this->flash->success(t('User created successfully.'));
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user_id)));
} else {
- $this->session->flashError(t('Unable to create your user.'));
+ $this->flash->failure(t('Unable to create your user.'));
$values['project_id'] = $project_id;
}
}
@@ -117,7 +119,7 @@ class User extends Base
public function show()
{
$user = $this->getUser();
- $this->response->html($this->layout('user/show', array(
+ $this->response->html($this->helper->layout->user('user/show', array(
'user' => $user,
'timezones' => $this->config->getTimezones(true),
'languages' => $this->config->getLanguages(true),
@@ -141,13 +143,27 @@ class User extends Base
->setQuery($this->subtaskTimeTracking->getUserQuery($user['id']))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
- $this->response->html($this->layout('user/timesheet', array(
+ $this->response->html($this->helper->layout->user('user/timesheet', array(
'subtask_paginator' => $subtask_paginator,
'user' => $user,
)));
}
/**
+ * Display last password reset
+ *
+ * @access public
+ */
+ public function passwordReset()
+ {
+ $user = $this->getUser();
+ $this->response->html($this->helper->layout->user('user/password_reset', array(
+ 'tokens' => $this->passwordReset->getAll($user['id']),
+ 'user' => $user,
+ )));
+ }
+
+ /**
* Display last connections
*
* @access public
@@ -155,7 +171,7 @@ class User extends Base
public function last()
{
$user = $this->getUser();
- $this->response->html($this->layout('user/last', array(
+ $this->response->html($this->helper->layout->user('user/last', array(
'last_logins' => $this->lastLogin->getAll($user['id']),
'user' => $user,
)));
@@ -169,8 +185,8 @@ class User extends Base
public function sessions()
{
$user = $this->getUser();
- $this->response->html($this->layout('user/sessions', array(
- 'sessions' => $this->authentication->backend('rememberMe')->getAll($user['id']),
+ $this->response->html($this->helper->layout->user('user/sessions', array(
+ 'sessions' => $this->rememberMeSession->getAll($user['id']),
'user' => $user,
)));
}
@@ -184,8 +200,8 @@ class User extends Base
{
$this->checkCSRFParam();
$user = $this->getUser();
- $this->authentication->backend('rememberMe')->remove($this->request->getIntegerParam('id'));
- $this->response->redirect($this->helper->url->to('user', 'session', array('user_id' => $user['id'])));
+ $this->rememberMeSession->remove($this->request->getIntegerParam('id'));
+ $this->response->redirect($this->helper->url->to('user', 'sessions', array('user_id' => $user['id'])));
}
/**
@@ -200,12 +216,12 @@ class User extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->userNotification->saveSettings($user['id'], $values);
- $this->session->flash(t('User updated successfully.'));
+ $this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('user', 'notifications', array('user_id' => $user['id'])));
}
- $this->response->html($this->layout('user/notifications', array(
- 'projects' => $this->projectPermission->getMemberProjects($user['id']),
+ $this->response->html($this->helper->layout->user('user/notifications', array(
+ 'projects' => $this->projectUserRole->getProjectsByUser($user['id'], array(ProjectModel::ACTIVE)),
'notifications' => $this->userNotification->readSettings($user['id']),
'types' => $this->userNotificationType->getTypes(),
'filters' => $this->userNotificationFilter->getFilters(),
@@ -225,11 +241,11 @@ class User extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->userMetadata->save($user['id'], $values);
- $this->session->flash(t('User updated successfully.'));
+ $this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('user', 'integrations', array('user_id' => $user['id'])));
}
- $this->response->html($this->layout('user/integrations', array(
+ $this->response->html($this->helper->layout->user('user/integrations', array(
'user' => $user,
'values' => $this->userMetadata->getall($user['id']),
)));
@@ -243,7 +259,7 @@ class User extends Base
public function external()
{
$user = $this->getUser();
- $this->response->html($this->layout('user/external', array(
+ $this->response->html($this->helper->layout->user('user/external', array(
'last_logins' => $this->lastLogin->getAll($user['id']),
'user' => $user,
)));
@@ -263,15 +279,15 @@ class User extends Base
$this->checkCSRFParam();
if ($this->user->{$switch.'PublicAccess'}($user['id'])) {
- $this->session->flash(t('User updated successfully.'));
+ $this->flash->success(t('User updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update this user.'));
+ $this->flash->failure(t('Unable to update this user.'));
}
$this->response->redirect($this->helper->url->to('user', 'share', array('user_id' => $user['id'])));
}
- $this->response->html($this->layout('user/share', array(
+ $this->response->html($this->helper->layout->user('user/share', array(
'user' => $user,
'title' => t('Public access'),
)));
@@ -290,20 +306,20 @@ class User extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
- list($valid, $errors) = $this->user->validatePasswordModification($values);
+ list($valid, $errors) = $this->userValidator->validatePasswordModification($values);
if ($valid) {
if ($this->user->update($values)) {
- $this->session->flash(t('Password modified successfully.'));
+ $this->flash->success(t('Password modified successfully.'));
} else {
- $this->session->flashError(t('Unable to change the password.'));
+ $this->flash->failure(t('Unable to change the password.'));
}
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
}
}
- $this->response->html($this->layout('user/password', array(
+ $this->response->html($this->helper->layout->user('user/password', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
@@ -326,38 +342,32 @@ class User extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues();
- if ($this->userSession->isAdmin()) {
- $values += array('is_admin' => 0, 'is_project_admin' => 0);
- } else {
- // Regular users can't be admin
- if (isset($values['is_admin'])) {
- unset($values['is_admin']);
- }
-
- if (isset($values['is_project_admin'])) {
- unset($values['is_project_admin']);
+ if (! $this->userSession->isAdmin()) {
+ if (isset($values['role'])) {
+ unset($values['role']);
}
}
- list($valid, $errors) = $this->user->validateModification($values);
+ list($valid, $errors) = $this->userValidator->validateModification($values);
if ($valid) {
if ($this->user->update($values)) {
- $this->session->flash(t('User updated successfully.'));
+ $this->flash->success(t('User updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update your user.'));
+ $this->flash->failure(t('Unable to update your user.'));
}
$this->response->redirect($this->helper->url->to('user', 'show', array('user_id' => $user['id'])));
}
}
- $this->response->html($this->layout('user/edit', array(
+ $this->response->html($this->helper->layout->user('user/edit', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
'timezones' => $this->config->getTimezones(true),
'languages' => $this->config->getLanguages(true),
+ 'roles' => $this->role->getApplicationRoles(),
)));
}
@@ -376,49 +386,23 @@ class User extends Base
if ($this->request->isPost()) {
$values = $this->request->getValues() + array('disable_login_form' => 0, 'is_ldap_user' => 0);
- list($valid, $errors) = $this->user->validateModification($values);
+ list($valid, $errors) = $this->userValidator->validateModification($values);
if ($valid) {
if ($this->user->update($values)) {
- $this->session->flash(t('User updated successfully.'));
+ $this->flash->success(t('User updated successfully.'));
} else {
- $this->session->flashError(t('Unable to update your user.'));
+ $this->flash->failure(t('Unable to update your user.'));
}
$this->response->redirect($this->helper->url->to('user', 'authentication', array('user_id' => $user['id'])));
}
}
- $this->response->html($this->layout('user/authentication', array(
+ $this->response->html($this->helper->layout->user('user/authentication', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
)));
}
-
- /**
- * Remove a user
- *
- * @access public
- */
- public function remove()
- {
- $user = $this->getUser();
-
- if ($this->request->getStringParam('confirmation') === 'yes') {
- $this->checkCSRFParam();
-
- if ($this->user->remove($user['id'])) {
- $this->session->flash(t('User removed successfully.'));
- } else {
- $this->session->flashError(t('Unable to remove this user.'));
- }
-
- $this->response->redirect($this->helper->url->to('user', 'index'));
- }
-
- $this->response->html($this->layout('user/remove', array(
- 'user' => $user,
- )));
- }
}
diff --git a/app/Controller/UserHelper.php b/app/Controller/UserHelper.php
new file mode 100644
index 00000000..041ed2c8
--- /dev/null
+++ b/app/Controller/UserHelper.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * User Helper
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class UserHelper extends Base
+{
+ /**
+ * User autocompletion (Ajax)
+ *
+ * @access public
+ */
+ public function autocomplete()
+ {
+ $search = $this->request->getStringParam('term');
+ $users = $this->userFilterAutoCompleteFormatter->create($search)->filterByUsernameOrByName()->format();
+ $this->response->json($users);
+ }
+
+ /**
+ * User mention autocompletion (Ajax)
+ *
+ * @access public
+ */
+ public function mention()
+ {
+ $project_id = $this->request->getStringParam('project_id');
+ $query = $this->request->getStringParam('q');
+ $users = $this->projectPermission->findUsernames($project_id, $query);
+ $this->response->json($users);
+ }
+}
diff --git a/app/Controller/UserImport.php b/app/Controller/UserImport.php
index 32b9a865..debd69e5 100644
--- a/app/Controller/UserImport.php
+++ b/app/Controller/UserImport.php
@@ -18,7 +18,7 @@ class UserImport extends Base
*/
public function step1(array $values = array(), array $errors = array())
{
- $this->response->html($this->template->layout('user_import/step1', array(
+ $this->response->html($this->helper->layout->app('user_import/step1', array(
'values' => $values,
'errors' => $errors,
'max_size' => ini_get('upload_max_filesize'),
@@ -46,9 +46,9 @@ class UserImport extends Base
$csv->read($filename, array($this->userImport, 'import'));
if ($this->userImport->counter > 0) {
- $this->session->flash(t('%d user(s) have been imported successfully.', $this->userImport->counter));
+ $this->flash->success(t('%d user(s) have been imported successfully.', $this->userImport->counter));
} else {
- $this->session->flashError(t('Nothing have been imported!'));
+ $this->flash->failure(t('Nothing have been imported!'));
}
$this->response->redirect($this->helper->url->to('userImport', 'step1'));
diff --git a/app/Controller/UserStatus.php b/app/Controller/UserStatus.php
new file mode 100644
index 00000000..b8ee5c91
--- /dev/null
+++ b/app/Controller/UserStatus.php
@@ -0,0 +1,111 @@
+<?php
+
+namespace Kanboard\Controller;
+
+/**
+ * User Status Controller
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class UserStatus extends Base
+{
+ /**
+ * Confirm remove a user
+ *
+ * @access public
+ */
+ public function confirmRemove()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->helper->layout->user('user_status/remove', array(
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Remove a user
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ if ($this->user->remove($user['id'])) {
+ $this->flash->success(t('User removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this user.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('user', 'index'));
+ }
+
+ /**
+ * Confirm enable a user
+ *
+ * @access public
+ */
+ public function confirmEnable()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->helper->layout->user('user_status/enable', array(
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Enable a user
+ *
+ * @access public
+ */
+ public function enable()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ if ($this->user->enable($user['id'])) {
+ $this->flash->success(t('User activated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to enable this user.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('user', 'index'));
+ }
+
+ /**
+ * Confirm disable a user
+ *
+ * @access public
+ */
+ public function confirmDisable()
+ {
+ $user = $this->getUser();
+
+ $this->response->html($this->helper->layout->user('user_status/disable', array(
+ 'user' => $user,
+ )));
+ }
+
+ /**
+ * Disable a user
+ *
+ * @access public
+ */
+ public function disable()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ if ($this->user->disable($user['id'])) {
+ $this->flash->success(t('User disabled successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to disable this user.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('user', 'index'));
+ }
+}
diff --git a/app/Controller/Webhook.php b/app/Controller/Webhook.php
index a7e9bde4..0eafe3e5 100644
--- a/app/Controller/Webhook.php
+++ b/app/Controller/Webhook.php
@@ -39,57 +39,4 @@ class Webhook extends Base
$this->response->text('FAILED');
}
-
- /**
- * Handle Github webhooks
- *
- * @access public
- */
- public function github()
- {
- $this->checkWebhookToken();
-
- $this->githubWebhook->setProjectId($this->request->getIntegerParam('project_id'));
-
- $result = $this->githubWebhook->parsePayload(
- $this->request->getHeader('X-Github-Event'),
- $this->request->getJson()
- );
-
- echo $result ? 'PARSED' : 'IGNORED';
- }
-
- /**
- * Handle Gitlab webhooks
- *
- * @access public
- */
- public function gitlab()
- {
- $this->checkWebhookToken();
-
- $this->gitlabWebhook->setProjectId($this->request->getIntegerParam('project_id'));
- $result = $this->gitlabWebhook->parsePayload($this->request->getJson());
-
- echo $result ? 'PARSED' : 'IGNORED';
- }
-
- /**
- * Handle Bitbucket webhooks
- *
- * @access public
- */
- public function bitbucket()
- {
- $this->checkWebhookToken();
-
- $this->bitbucketWebhook->setProjectId($this->request->getIntegerParam('project_id'));
-
- $result = $this->bitbucketWebhook->parsePayload(
- $this->request->getHeader('X-Event-Key'),
- $this->request->getJson()
- );
-
- echo $result ? 'PARSED' : 'IGNORED';
- }
}