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