看板初始化提交

This commit is contained in:
zephyr
2026-06-01 21:23:12 -07:00
commit 54a842f4ab
2104 changed files with 241695 additions and 0 deletions
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace Kanboard\Controller;
/**
* Automatic Actions Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ActionController extends BaseController
{
/**
* List of automatic actions for a given project
*
* @access public
*/
public function index()
{
$project = $this->getProject();
$actions = $this->actionModel->getAllByProject($project['id']);
$this->response->html($this->helper->layout->project('action/index', array(
'values' => array('project_id' => $project['id']),
'project' => $project,
'actions' => $actions,
'available_actions' => $this->actionManager->getAvailableActions(),
'available_events' => $this->eventManager->getAll(),
'available_params' => $this->actionManager->getAvailableParameters($actions),
'columns_list' => $this->columnModel->getList($project['id']),
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']),
'projects_list' => $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId()),
'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')
)));
}
/**
* Confirmation dialog before removing an action
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$action = $this->getAction($project);
$this->response->html($this->helper->layout->project('action/remove', array(
'action' => $action,
'available_events' => $this->eventManager->getAll(),
'available_actions' => $this->actionManager->getAvailableActions(),
'project' => $project,
'title' => t('Remove an action')
)));
}
/**
* Remove an action
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$action = $this->getAction($project);
if (! empty($action) && $this->actionModel->remove($action['id'])) {
$this->flash->success(t('Action removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this action.'));
}
$this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id'])));
}
}
+127
View File
@@ -0,0 +1,127 @@
<?php
namespace Kanboard\Controller;
/**
* Action Creation Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ActionCreationController extends BaseController
{
/**
* Show the form (step 1)
*
* @access public
*/
public function create()
{
$project = $this->getProject();
$this->response->html($this->template->render('action_creation/create', array(
'project' => $project,
'values' => array('project_id' => $project['id']),
'available_actions' => $this->actionManager->getAvailableActions(),
)));
}
/**
* Choose the event according to the action (step 2)
*
* @access public
*/
public function event()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
if (empty($values['action_name'])) {
return $this->create();
}
return $this->response->html($this->template->render('action_creation/event', array(
'values' => $values,
'project' => $project,
'available_actions' => $this->actionManager->getAvailableActions(),
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
)));
}
/**
* Define action parameters (step 3)
*
* @access public
*/
public function params()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
if (empty($values['action_name']) || empty($values['event_name'])) {
$this->create();
return;
}
$action = $this->actionManager->getAction($values['action_name']);
$action_params = $action->getActionRequiredParameters();
if (empty($action_params)) {
$this->doCreation($project, $values + array('params' => array()));
}
$projects_list = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
unset($projects_list[$project['id']]);
$this->response->html($this->template->render('action_creation/params', array(
'values' => $values,
'action_params' => $action_params,
'columns_list' => $this->columnModel->getList($project['id']),
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']),
'projects_list' => $projects_list,
'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($project['id']),
'links_list' => $this->linkModel->getList(0, false),
'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']),
)));
}
/**
* Save the action (last step)
*
* @access public
*/
public function save()
{
$this->doCreation($this->getProject(), $this->request->getValues());
}
/**
* Common method to save the action
*
* @access private
* @param array $project Project properties
* @param array $values Form values
*/
private function doCreation(array $project, array $values)
{
$values['project_id'] = $project['id'];
list($valid, ) = $this->actionValidator->validateCreation($values);
if ($valid) {
if ($this->actionModel->create($values) !== false) {
$this->flash->success(t('Your automatic action has been created successfully.'));
} else {
$this->flash->failure(t('Unable to create your automatic action.'));
}
}
$this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id'])));
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace Kanboard\Controller;
/**
* Activity Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ActivityController extends BaseController
{
/**
* Activity page for a user
*
* @access public
*/
public function user()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('activity/user', array(
'title' => t('Activity stream for %s', $this->helper->user->getFullname($user)),
'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']), 100),
'user' => $user,
)));
}
/**
* Activity page for a project
*
* @access public
*/
public function project()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->app('activity/project', array(
'title' => t('%s\'s activity', $project['name']),
'events' => $this->helper->projectActivity->getProjectEvents($project['id']),
'project' => $project,
)));
}
/**
* Display task activities
*
* @access public
*/
public function task()
{
$task = $this->getTask();
$this->response->html($this->helper->layout->task('activity/task', array(
'title' => $task['title'],
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'events' => $this->helper->projectActivity->getTaskEvents($task['id']),
'tags' => $this->taskTagModel->getTagsByTask($task['id']),
)));
}
}
+192
View File
@@ -0,0 +1,192 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\TaskModel;
/**
* Project Analytic Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class AnalyticController extends BaseController
{
/**
* Show average Lead and Cycle time
*
* @access public
*/
public function leadAndCycleTime()
{
$project = $this->getProject();
list($from, $to) = $this->getDates();
$this->response->html($this->helper->layout->analytic('analytic/lead_cycle_time', array(
'values' => array(
'from' => $from,
'to' => $to,
),
'project' => $project,
'average' => $this->averageLeadCycleTimeAnalytic->build($project['id']),
'metrics' => $this->projectDailyStatsModel->getRawMetrics($project['id'], $from, $to),
'title' => t('Lead and cycle time'),
)));
}
/**
* Show comparison between actual and estimated hours chart
*
* @access public
*/
public function timeComparison()
{
$project = $this->getProject();
$paginator = $this->paginator
->setUrl('AnalyticController', 'timeComparison', array('project_id' => $project['id']))
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
->setQuery(
$this->taskQuery
->withFilter(new TaskProjectFilter($project['id']))
->getQuery()
)
->calculate();
$this->response->html($this->helper->layout->analytic('analytic/time_comparison', array(
'project' => $project,
'paginator' => $paginator,
'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']),
'title' => t('Estimated vs actual time'),
)));
}
/**
* Show average time spent by column
*
* @access public
*/
public function averageTimeByColumn()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array(
'project' => $project,
'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']),
'title' => t('Average time into each column'),
)));
}
/**
* Show tasks distribution graph
*
* @access public
*/
public function taskDistribution()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->analytic('analytic/task_distribution', array(
'project' => $project,
'metrics' => $this->taskDistributionAnalytic->build($project['id']),
'title' => t('Task distribution'),
)));
}
/**
* Show users repartition
*
* @access public
*/
public function userDistribution()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->analytic('analytic/user_distribution', array(
'project' => $project,
'metrics' => $this->userDistributionAnalytic->build($project['id']),
'title' => t('User repartition'),
)));
}
/**
* Show cumulative flow diagram
*
* @access public
*/
public function cfd()
{
$this->commonAggregateMetrics('analytic/cfd', 'total', t('Cumulative flow diagram'));
}
/**
* Show burndown chart
*
* @access public
*/
public function burndown()
{
$this->commonAggregateMetrics('analytic/burndown', 'score', t('Burndown chart'));
}
/**
* Estimated vs actual time per column
*
* @access public
*/
public function estimatedVsActualByColumn()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->analytic('analytic/estimated_actual_column', array(
'project' => $project,
'metrics' => $this->estimatedActualColumnAnalytic->build($project['id']),
'title' => t('Estimated vs actual time per column'),
)));
}
/**
* Common method for CFD and Burdown chart
*
* @access private
* @param string $template
* @param string $column
* @param string $title
*/
private function commonAggregateMetrics($template, $column, $title)
{
$project = $this->getProject();
list($from, $to) = $this->getDates();
$displayGraph = $this->projectDailyColumnStatsModel->countDays($project['id'], $from, $to) >= 2;
$metrics = $displayGraph ? $this->projectDailyColumnStatsModel->getAggregatedMetrics($project['id'], $from, $to, $column) : array();
$this->response->html($this->helper->layout->analytic($template, array(
'values' => array(
'from' => $from,
'to' => $to,
),
'display_graph' => $displayGraph,
'metrics' => $metrics,
'project' => $project,
'title' => $title,
)));
}
private function getDates()
{
$values = $this->request->getValues();
$from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week')));
$to = $this->request->getStringParam('to', date('Y-m-d'));
if (! empty($values)) {
$from = $this->dateParser->getIsoDate($values['from']);
$to = $this->dateParser->getIsoDate($values['to']);
}
return array($from, $to);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Base;
/**
* Class AppController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class AppController extends Base
{
/**
* Forbidden page
*
* @access public
* @param bool $withoutLayout
* @param string $message
*/
public function accessForbidden($withoutLayout = false, $message = '')
{
if ($this->request->isAjax()) {
$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,
)), 403);
}
}
/**
* Page not found
*
* @access public
* @param boolean $withoutLayout
*/
public function notFound($withoutLayout = false)
{
$this->response->html($this->helper->layout->app('app/notfound', array(
'title' => t('Page not found'),
'no_layout' => $withoutLayout,
)));
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace Kanboard\Controller;
/**
* Authentication Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class AuthController extends BaseController
{
/**
* Display the form login
*
* @access public
* @param array $values
* @param array $errors
*/
public function login(array $values = array(), array $errors = array())
{
if ($this->userSession->isLogged()) {
$this->response->redirect($this->helper->url->to('DashboardController', 'show'));
} else {
$showCaptcha = false;
if (! empty($values['username']) && $this->userLockingModel->hasCaptcha($values['username'])) {
$showCaptcha = true;
} elseif ($this->captchaModel->isLocked($this->request->getIpAddress())) {
$showCaptcha = true;
}
$this->response->html($this->helper->layout->app('auth/index', array(
'captcha' => $showCaptcha,
'errors' => $errors,
'values' => $values,
'no_layout' => true,
'title' => t('Login')
)));
}
}
/**
* Check credentials
*
* @access public
*/
public function check()
{
$values = $this->request->getValues();
if (REMEMBER_ME_AUTH) {
session_set('hasRememberMe', ! empty($values['remember_me']));
}
list($valid, $errors) = $this->authValidator->validateForm($values);
if ($valid) {
$this->redirectAfterLogin();
} else {
$this->login($values, $errors);
}
}
/**
* Logout and destroy session
*
* @access public
*/
public function logout()
{
if (! DISABLE_LOGOUT) {
$this->checkCSRFParam();
$this->sessionManager->close();
$this->response->redirect($this->helper->url->to('AuthController', 'login'));
} else {
$this->response->redirect($this->helper->url->to('DashboardController', 'show'));
}
}
}
+122
View File
@@ -0,0 +1,122 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
use Kanboard\Core\Thumbnail;
/**
* Avatar File Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class AvatarFileController extends BaseController
{
/**
* Display avatar page
*/
public function show()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('avatar_file/show', array(
'user' => $user,
)));
}
/**
* Upload Avatar
*/
public function upload()
{
$this->checkCSRFParam();
$user = $this->getUser();
if (! $this->request->getFileInfo('avatar')['name']) {
$this->flash->failure(t('You must select a file to upload as your avatar!'));
} elseif (! $this->avatarFileModel->isAvatarImage($this->request->getFileInfo('avatar')['name'])) {
$this->flash->failure(t('The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)'));
} else {
if (! $this->avatarFileModel->uploadImageFile($user['id'], $this->request->getFileInfo('avatar'))) {
$this->flash->failure(t('Unable to upload files, check the permissions of your data folder.'));
}
}
$this->renderResponse($user['id']);
}
/**
* Remove Avatar image
*/
public function remove()
{
$this->checkCSRFParam();
$user = $this->getUser();
$this->avatarFileModel->remove($user['id']);
$this->userSession->refresh($user['id']);
$this->renderResponse($user['id']);
}
/**
* Show Avatar image (public)
*/
public function image()
{
$user_id = $this->request->getIntegerParam('user_id');
$size = $this->request->getStringParam('size', 48);
$hash = $this->request->getStringParam('hash');
if ($size > 100) {
$this->response->status(400);
return;
}
$filename = $this->avatarFileModel->getFilename($user_id);
$etag = md5($filename.$size);
if ($hash !== $etag) {
$this->response->status(404);
return;
}
$this->response->withCache(365 * 86400, $etag);
$this->response->withContentType('image/png');
if ($this->request->getHeader('If-None-Match') !== '"'.$etag.'"') {
$this->response->send();
$this->render($filename, $size);
} else {
$this->response->status(304);
}
}
/**
* Render thumbnail from object storage
*
* @access private
* @param string $filename
* @param integer $size
*/
private function render($filename, $size)
{
try {
$blob = $this->objectStorage->get($filename);
Thumbnail::createFromString($blob)
->resize($size, $size)
->toOutput();
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
}
protected function renderResponse($userId)
{
if ($this->request->isAjax()) {
$this->show();
} else {
$this->response->redirect($this->helper->url->to('AvatarFileController', 'show', array('user_id' => $userId)));
}
}
}
+338
View File
@@ -0,0 +1,338 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Base;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Base Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
abstract class BaseController extends Base
{
/**
* Check if the CSRF token from the URL is correct
*
* @access protected
*/
protected function checkCSRFParam()
{
if (! $this->token->validateCSRFToken($this->request->getStringParam('csrf_token'))) {
throw new AccessForbiddenException();
}
}
protected function checkReusableCSRFParam()
{
if (! $this->token->validateReusableCSRFToken($this->request->getRawValue('csrf_token'))) {
throw new AccessForbiddenException();
}
}
protected function checkReusableGETCSRFParam()
{
if (! $this->token->validateReusableCSRFToken($this->request->getStringParam('csrf_token'))) {
throw new AccessForbiddenException();
}
}
protected function checkCSRFForm()
{
if (! $this->token->validateCSRFToken($this->request->getRawValue('csrf_token'))) {
throw new AccessForbiddenException();
}
}
/**
* Check webhook token
*
* @access protected
*/
protected function checkWebhookToken()
{
if (! hash_equals($this->configModel->get('webhook_token'), $this->request->getStringParam('token'))) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
}
/**
* Common method to get a task for task views
*
* @access protected
* @return array
* @throws PageNotFoundException
* @throws AccessForbiddenException
*/
protected function getTask()
{
$project_id = $this->request->getIntegerParam('project_id');
$task = $this->taskFinderModel->getDetails($this->request->getIntegerParam('task_id'));
if (empty($task)) {
throw new PageNotFoundException();
}
if ($project_id !== 0 && $project_id != $task['project_id']) {
throw new AccessForbiddenException();
}
return $task;
}
/**
* Get Task or Project file
*
* @access protected
* @return array
* @throws PageNotFoundException
*/
protected function getFile()
{
$project_id = $this->request->getIntegerParam('project_id');
$task_id = $this->request->getIntegerParam('task_id');
$file_id = $this->request->getIntegerParam('file_id');
$model = 'projectFileModel';
if ($task_id > 0) {
$model = 'taskFileModel';
}
$file = $this->$model->getById($file_id);
if (empty($file)) {
throw new PageNotFoundException();
}
if (isset($file['task_id']) && $file['task_id'] != $task_id) {
throw new PageNotFoundException();
}
if (isset($file['project_id']) && $file['project_id'] != $project_id) {
throw new PageNotFoundException();
}
$file['model'] = $model;
return $file;
}
/**
* Common method to get a project
*
* @access protected
* @param integer $project_id Default project id
* @return array
* @throws PageNotFoundException
*/
protected function getProject($project_id = 0)
{
$project_id = $this->request->getIntegerParam('project_id', $project_id);
$project = $this->projectModel->getByIdWithOwnerAndTaskCount($project_id);
if (empty($project)) {
throw new PageNotFoundException();
}
return $project;
}
/**
* Common method to get the user
*
* @access protected
* @return array
* @throws PageNotFoundException
* @throws AccessForbiddenException
*/
protected function getUser()
{
$user = $this->userModel->getById($this->request->getIntegerParam('user_id', $this->userSession->getId()));
if (empty($user)) {
throw new PageNotFoundException();
}
if (! $this->userSession->isAdmin() && $this->userSession->getId() != $user['id']) {
// Always returns a 404 otherwise people might guess which user exist.
throw new PageNotFoundException();
}
return $user;
}
protected function getSubtask(array $task)
{
$subtask = $this->subtaskModel->getById($this->request->getIntegerParam('subtask_id'));
if (empty($subtask)) {
throw new PageNotFoundException();
}
if ($subtask['task_id'] != $task['id']) {
throw new AccessForbiddenException();
}
return $subtask;
}
protected function getComment(array $task)
{
$comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id'));
if (empty($comment)) {
throw new PageNotFoundException();
}
if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) {
throw new AccessForbiddenException();
}
if ($comment['task_id'] != $task['id']) {
throw new AccessForbiddenException();
}
return $comment;
}
protected function getExternalTaskLink(array $task)
{
$link = $this->taskExternalLinkModel->getById($this->request->getIntegerParam('link_id'));
if (empty($link)) {
throw new PageNotFoundException();
}
if ($link['task_id'] != $task['id']) {
throw new AccessForbiddenException();
}
return $link;
}
protected function getInternalTaskLink(array $task)
{
$link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id'));
if (empty($link)) {
throw new PageNotFoundException();
}
if ($link['task_id'] != $task['id']) {
throw new AccessForbiddenException();
}
return $link;
}
protected function getColumn(array $project)
{
$column = $this->columnModel->getById($this->request->getIntegerParam('column_id'));
if (empty($column)) {
throw new PageNotFoundException();
}
if ($column['project_id'] != $project['id']) {
throw new AccessForbiddenException();
}
return $column;
}
protected function getSwimlane(array $project)
{
$swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id'));
if (empty($swimlane)) {
throw new PageNotFoundException();
}
if ($swimlane['project_id'] != $project['id']) {
throw new AccessForbiddenException();
}
return $swimlane;
}
protected function getCategory(array $project)
{
$category = $this->categoryModel->getById($this->request->getIntegerParam('category_id'));
if (empty($category)) {
throw new PageNotFoundException();
}
if ($category['project_id'] != $project['id']) {
throw new AccessForbiddenException();
}
return $category;
}
protected function getProjectTag(array $project)
{
$tag = $this->tagModel->getById($this->request->getIntegerParam('tag_id'));
if (empty($tag)) {
throw new PageNotFoundException();
}
if ($tag['project_id'] != $project['id']) {
throw new AccessForbiddenException();
}
return $tag;
}
protected function getAction(array $project)
{
$action = $this->actionModel->getById($this->request->getIntegerParam('action_id'));
if (empty($action)) {
throw new PageNotFoundException();
}
if ($action['project_id'] != $project['id']) {
throw new AccessForbiddenException();
}
return $action;
}
protected function getCustomFilter(array $project)
{
$filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id'));
if (empty($filter)) {
throw new PageNotFoundException();
}
if ($filter['project_id'] != $project['id']) {
throw new AccessForbiddenException();
}
return $filter;
}
/**
* Redirect the user after the authentication
*
* @access protected
*/
protected function redirectAfterLogin()
{
$redirectUri = $this->helper->url->to('DashboardController', 'show');
if (session_exists('redirectAfterLogin')) {
if ($this->request->isSafeRedirectUri(session_get('redirectAfterLogin'))) {
$redirectUri = session_get('redirectAfterLogin');
}
session_remove('redirectAfterLogin');
}
$this->response->redirect($redirectUri);
}
}
+150
View File
@@ -0,0 +1,150 @@
<?php
namespace Kanboard\Controller;
use Exception;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Model\UserMetadataModel;
/**
* Class BoardAjaxController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class BoardAjaxController extends BaseController
{
/**
* Save new task positions (Ajax request made by the drag and drop)
*
* @access public
*/
public function save()
{
$this->checkReusableGETCSRFParam();
$project_id = $this->request->getIntegerParam('project_id');
if (! $project_id || ! $this->request->isAjax()) {
throw new AccessForbiddenException();
}
$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"));
}
try {
$result =$this->taskPositionModel->movePosition(
$project_id,
$values['task_id'],
$values['dst_column_id'],
$values['position'],
$values['swimlane_id']
);
if (! $result) {
$this->response->status(400);
} else {
$this->response->html($this->renderBoard($project_id), 201);
}
} catch (Exception $e) {
$this->response->html('<div class="alert alert-error">'.$e->getMessage().'</div>');
}
}
/**
* Check if the board have been changed
*
* @access public
*/
public function check()
{
$project_id = $this->request->getIntegerParam('project_id');
$timestamp = $this->request->getIntegerParam('timestamp');
if (! $project_id || ! $this->request->isAjax()) {
throw new AccessForbiddenException();
} elseif (! $this->projectModel->isModifiedSince($project_id, $timestamp)) {
$this->response->status(304);
} else {
$this->response->html($this->renderBoard($project_id));
}
}
/**
* Reload the board with new filters
*
* @access public
*/
public function reload()
{
$project_id = $this->request->getIntegerParam('project_id');
if (! $project_id || ! $this->request->isAjax()) {
throw new AccessForbiddenException();
}
$values = $this->request->getJson();
$this->userSession->setFilters($project_id, empty($values['search']) ? '' : $values['search']);
$this->response->html($this->renderBoard($project_id));
}
/**
* Enable collapsed mode
*
* @access public
*/
public function collapse()
{
$this->changeDisplayMode(1);
}
/**
* Enable expanded mode
*
* @access public
*/
public function expand()
{
$this->changeDisplayMode(0);
}
/**
* Change display mode
*
* @access private
* @param int $mode
*/
private function changeDisplayMode($mode)
{
$project_id = $this->request->getIntegerParam('project_id');
$this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, $mode);
if ($this->request->isAjax()) {
$this->response->html($this->renderBoard($project_id));
} else {
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project_id)));
}
}
/**
* Render board
*
* @access protected
* @param integer $project_id
* @return string
*/
protected function renderBoard($project_id)
{
return $this->template->render('board/table_container', array(
'project' => $this->projectModel->getById($project_id),
'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'),
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($this->userSession->getFilters($project_id))
->format($this->boardFormatter->withProjectId($project_id))
));
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Kanboard\Controller;
/**
* Board Popover Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class BoardPopoverController extends BaseController
{
/**
* Confirmation before to close all column tasks
*
* @access public
*/
public function confirmCloseColumnTasks()
{
$project = $this->getProject();
$column_id = $this->request->getIntegerParam('column_id');
$swimlane_id = $this->request->getIntegerParam('swimlane_id');
$this->response->html($this->template->render('board_popover/close_all_tasks_column', array(
'project' => $project,
'nb_tasks' => $this->taskFinderModel->countByColumnAndSwimlaneId($project['id'], $column_id, $swimlane_id),
'column' => $this->columnModel->getColumnTitleById($column_id),
'swimlane' => $this->swimlaneModel->getNameById($swimlane_id),
'values' => array('column_id' => $column_id, 'swimlane_id' => $swimlane_id),
)));
}
/**
* Close all column tasks
*
* @access public
*/
public function closeColumnTasks()
{
$project = $this->getProject();
$values = $this->request->getValues();
$this->taskStatusModel->closeTasksBySwimlaneAndColumn($values['swimlane_id'], $values['column_id']);
$this->flash->success(t('All tasks of the column "%s" and the swimlane "%s" have been closed successfully.', $this->columnModel->getColumnTitleById($values['column_id']), $this->swimlaneModel->getNameById($values['swimlane_id'])));
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])));
}
}
+112
View File
@@ -0,0 +1,112 @@
<?php
namespace Kanboard\Controller;
/**
* Board Tooltip
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class BoardTooltipController extends BaseController
{
/**
* Get links on mouseover
*
* @access public
*/
public function tasklinks()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tooltip_tasklinks', array(
'links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']),
'task' => $task,
)));
}
/**
* Get links on mouseover
*
* @access public
*/
public function externallinks()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tooltip_external_links', array(
'links' => $this->taskExternalLinkModel->getAll($task['id']),
'task' => $task,
)));
}
/**
* Get subtasks on mouseover
*
* @access public
*/
public function subtasks()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tooltip_subtasks', array(
'subtasks' => $this->subtaskModel->getAll($task['id']),
'task' => $task,
)));
}
/**
* Display all attachments during the task mouseover
*
* @access public
*/
public function attachments()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tooltip_files', array(
'files' => $this->taskFileModel->getAll($task['id']),
'task' => $task,
)));
}
/**
* Display task description
*
* @access public
*/
public function description()
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tooltip_description', array(
'task' => $task
)));
}
/**
* Get recurrence information on mouseover
*
* @access public
*/
public function recurrence()
{
$task = $this->getTask();
$this->response->html($this->template->render('task_recurrence/info', array(
'task' => $task,
'recurrence_trigger_list' => $this->taskRecurrenceModel->getRecurrenceTriggerList(),
'recurrence_timeframe_list' => $this->taskRecurrenceModel->getRecurrenceTimeframeList(),
'recurrence_basedate_list' => $this->taskRecurrenceModel->getRecurrenceBasedateList(),
)));
}
/**
* Display swimlane description in tooltip
*
* @access public
*/
public function swimlane()
{
$this->getProject();
$swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id'));
$this->response->html($this->template->render('board/tooltip_description', array('task' => $swimlane)));
}
}
+72
View File
@@ -0,0 +1,72 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Model\TaskModel;
/**
* Board controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class BoardViewController extends BaseController
{
/**
* Display the public version of a board
* Access checked by a simple token, no user login, read only, auto-refresh
*
* @access public
*/
public function readonly()
{
$token = $this->request->getStringParam('token');
$project = $this->projectModel->getByToken($token);
if (empty($project)) {
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' => $this->boardFormatter
->withProjectId($project['id'])
->withQuery($query)
->format(),
'title' => $project['name'],
'description' => $project['description'],
'no_layout' => true,
'not_editable' => true,
'board_public_refresh_interval' => $this->configModel->get('board_public_refresh_interval'),
'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'),
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
)));
}
/**
* Show a board for a given project
*
* @access public
*/
public function show()
{
$project = $this->getProject();
$search = $this->helper->projectHeader->getSearchQuery($project);
$this->response->html($this->helper->layout->app('board/view_private', array(
'project' => $project,
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'),
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($search)
->format($this->boardFormatter->withProjectId($project['id']))
)));
}
}
+29
View File
@@ -0,0 +1,29 @@
<?php
namespace Kanboard\Controller;
use Gregwar\Captcha\CaptchaBuilder;
/**
* Captcha Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class CaptchaController extends BaseController
{
/**
* Display captcha image
*
* @access public
*/
public function image()
{
$this->response->withContentType('image/jpeg')->send();
$builder = new CaptchaBuilder;
$builder->build();
session_set('captcha', $builder->getPhrase());
$builder->output();
}
}
+161
View File
@@ -0,0 +1,161 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Category Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class CategoryController extends BaseController
{
/**
* List of categories for a given project
*
* @access public
* @throws PageNotFoundException
*/
public function index()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('category/index', array(
'categories' => $this->categoryModel->getAll($project['id']),
'project' => $project,
'colors' => $this->colorModel->getList(),
'title' => t('Categories'),
)));
}
/**
* Show form to create new category
*
* @param array $values
* @param array $errors
*/
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->template->render('category/create', array(
'values' => $values + array('project_id' => $project['id']),
'colors' => $this->colorModel->getList(),
'errors' => $errors,
'project' => $project,
)));
}
/**
* Validate and save a new category
*
* @access public
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
list($valid, $errors) = $this->categoryValidator->validateCreation($values);
if ($valid) {
if ($this->categoryModel->create($values) !== false) {
$this->flash->success(t('Your category has been created successfully.'));
$this->response->redirect($this->helper->url->to('CategoryController', 'index', array('project_id' => $project['id'])), true);
return;
} else {
$errors = array('name' => array(t('Another category with the same name exists in this project')));
}
}
$this->create($values, $errors);
}
/**
* Edit a category (display the form)
*
* @access public
* @param array $values
* @param array $errors
* @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$category = $this->getCategory($project);
$this->response->html($this->template->render('category/edit', array(
'values' => empty($values) ? $category : $values,
'colors' => $this->colorModel->getList(),
'errors' => $errors,
'project' => $project,
)));
}
/**
* Edit a category (validate the form and update the database)
*
* @access public
*/
public function update()
{
$project = $this->getProject();
$category = $this->getCategory($project);
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
$values['id'] = $category['id'];
list($valid, $errors) = $this->categoryValidator->validateModification($values);
if ($valid) {
if ($this->categoryModel->update($values)) {
$this->flash->success(t('This category has been updated successfully.'));
return $this->response->redirect($this->helper->url->to('CategoryController', 'index', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to update this category.'));
}
}
return $this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a category
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$category = $this->getCategory($project);
$this->response->html($this->helper->layout->project('category/remove', array(
'project' => $project,
'category' => $category,
)));
}
/**
* Remove a category
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$category = $this->getCategory($project);
if ($this->categoryModel->remove($category['id'])) {
$this->flash->success(t('Category removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this category.'));
}
$this->response->redirect($this->helper->url->to('CategoryController', 'index', array('project_id' => $project['id'])));
}
}
+200
View File
@@ -0,0 +1,200 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Column Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ColumnController extends BaseController
{
/**
* Display columns list
*
* @access public
*/
public function index()
{
$project = $this->getProject();
$columns = $this->columnModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->project('column/index', array(
'columns' => $columns,
'project' => $project,
'title' => t('Edit columns')
)));
}
/**
* Show form to create a new column
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
if (empty($values)) {
$values = array('project_id' => $project['id']);
}
$this->response->html($this->template->render('column/create', array(
'values' => $values,
'errors' => $errors,
'project' => $project,
)));
}
/**
* Validate and add a new column
*
* @access public
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues() + array('hide_in_dashboard' => 0);
$values['project_id'] = $project['id'];
list($valid, $errors) = $this->columnValidator->validateCreation($values);
if ($valid) {
$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.'));
$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'));
}
}
$this->create($values, $errors);
}
/**
* Display a form to edit a column
*
* @access public
* @param array $values
* @param array $errors
*/
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$column = $this->getColumn($project);
$this->response->html($this->helper->layout->project('column/edit', array(
'errors' => $errors,
'values' => $values ?: $column,
'project' => $project,
'column' => $column,
)));
}
/**
* Validate and update a column
*
* @access public
*/
public function update()
{
$project = $this->getProject();
$column = $this->getColumn($project);
$values = $this->request->getValues() + array('hide_in_dashboard' => 0);
$values['project_id'] = $project['id'];
$values['id'] = $column['id'];
list($valid, $errors) = $this->columnValidator->validateModification($values);
if ($valid) {
$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.'));
$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.'));
}
}
$this->edit($values, $errors);
}
/**
* Move column position
*
* @access public
*/
public function move()
{
$this->checkReusableGETCSRFParam();
$project = $this->getProject();
$values = $this->request->getJson();
if (! empty($values) && isset($values['column_id']) && isset($values['position'])) {
$result = $this->columnModel->changePosition($project['id'], $values['column_id'], $values['position']);
$this->response->json(array('result' => $result));
} else {
throw new AccessForbiddenException();
}
}
/**
* Confirm column suppression
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$column = $this->getColumn($project);
$this->response->html($this->helper->layout->project('column/remove', array(
'column' => $column,
'project' => $project,
)));
}
/**
* Remove a column
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$column = $this->getColumn($project);
if ($this->columnModel->remove($column['id'])) {
$this->flash->success(t('Column removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this column.'));
}
$this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])));
}
}
@@ -0,0 +1,103 @@
<?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'],
isset($values['only_assigned']) && $values['only_assigned'] == 1
);
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'])));
}
}
@@ -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'])));
}
}
+178
View File
@@ -0,0 +1,178 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Comment Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class CommentController extends BaseController
{
/**
* Add comment form
*
* @access public
* @param array $values
* @param array $errors
* @throws AccessForbiddenException
* @throws PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$values['project_id'] = $task['project_id'];
$this->response->html($this->helper->layout->task('comment/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task
)));
}
/**
* Add a comment
*
* @access public
*/
public function save()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['task_id'] = $task['id'];
$values['user_id'] = $this->userSession->getId();
list($valid, $errors) = $this->commentValidator->validateCreation($values);
if ($valid) {
if ($this->commentModel->create($values) !== false) {
$this->flash->success(t('Comment added successfully.'));
} else {
$this->flash->failure(t('Unable to create your comment.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true);
} else {
$this->create($values, $errors);
}
}
/**
* Edit a comment
*
* @access public
* @param array $values
* @param array $errors
* @throws AccessForbiddenException
* @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$comment = $this->getComment($task);
if (empty($values)) {
$values = $comment;
}
$values['project_id'] = $task['project_id'];
$this->response->html($this->template->render('comment/edit', array(
'values' => $values,
'errors' => $errors,
'comment' => $comment,
'task' => $task,
)));
}
/**
* Update and validate a comment
*
* @access public
*/
public function update()
{
$task = $this->getTask();
$comment = $this->getComment($task);
$values = $this->request->getValues();
$values['id'] = $comment['id'];
$values['task_id'] = $task['id'];
$values['user_id'] = $comment['user_id'];
list($valid, $errors) = $this->commentValidator->validateModification($values);
if ($valid) {
if ($this->commentModel->update($values)) {
$this->flash->success(t('Comment updated successfully.'));
} else {
$this->flash->failure(t('Unable to update your comment.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
return;
}
$this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a comment
*
* @access public
*/
public function confirm()
{
$task = $this->getTask();
$comment = $this->getComment($task);
$this->response->html($this->template->render('comment/remove', array(
'comment' => $comment,
'task' => $task,
'title' => t('Remove a comment')
)));
}
/**
* Remove a comment
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$task = $this->getTask();
$comment = $this->getComment($task);
if ($this->commentModel->remove($comment['id'])) {
$this->flash->success(t('Comment removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this comment.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true);
}
/**
* Toggle comment sorting
*
* @access public
*/
public function toggleSorting()
{
$this->checkReusableGETCSRFParam();
$task = $this->getTask();
$this->helper->comment->toggleSorting();
$this->response->redirect($this->helper->url->to(
'TaskViewController',
'show',
array('task_id' => $task['id']),
'comments'
));
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Model\UserMetadataModel;
/**
* Class CommentListController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class CommentListController extends BaseController
{
public function show()
{
$task = $this->getTask();
$commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
$this->response->html($this->template->render('comment_list/show', array(
'task' => $task,
'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection),
'editable' => $this->helper->user->hasProjectAccess('CommentController', 'edit', $task['project_id']),
)));
}
public function save()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['task_id'] = $task['id'];
$values['user_id'] = $this->userSession->getId();
list($valid, ) = $this->commentValidator->validateCreation($values);
if ($valid && $this->commentModel->create($values) !== false) {
$this->flash->success(t('Comment added successfully.'));
}
$this->show();
}
public function toggleSorting()
{
$this->helper->comment->toggleSorting();
$this->show();
}
}
+74
View File
@@ -0,0 +1,74 @@
<?php
namespace Kanboard\Controller;
/**
* Class CommentMailController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class CommentMailController extends BaseController
{
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$this->response->html($this->helper->layout->task('comment_mail/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'members' => $this->projectPermissionModel->getMembersWithEmail($task['project_id']),
)));
}
public function save()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['task_id'] = $task['id'];
$values['user_id'] = $this->userSession->getId();
list($valid, $errors) = $this->commentValidator->validateEmailCreation($values);
if ($valid) {
$this->sendByEmail($values, $task);
$values = $this->prepareComment($values);
if ($this->commentModel->create($values) !== false) {
$this->flash->success(t('Comment sent by email successfully.'));
} else {
$this->flash->failure(t('Unable to create your comment.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true);
} else {
$this->create($values, $errors);
}
}
protected function sendByEmail(array $values, array $task)
{
$html = $this->template->render('comment_mail/email', array('email' => $values, 'task' => $task));
$emails = explode_csv_field($values['emails']);
foreach ($emails as $email) {
$this->emailClient->send(
$email,
$email,
$values['subject'],
$html
);
}
}
protected function prepareComment(array $values)
{
$values['comment'] .= "\n\n_".t('Sent by email to "%s" (%s)', $values['emails'], $values['subject']).'_';
unset($values['subject']);
unset($values['emails']);
return $values;
}
}
+254
View File
@@ -0,0 +1,254 @@
<?php
namespace Kanboard\Controller;
/**
* Config Controller
*
* @package Kanboard/Controller
* @author Frederic Guillot
*/
class ConfigController extends BaseController
{
/**
* Display the about page
*
* @access public
*/
public function index()
{
$this->response->html($this->helper->layout->config('config/about', array(
'db_size' => $this->configModel->getDatabaseSize(),
'db_version' => $this->db->getDriver()->getDatabaseVersion(),
'db_options' => $this->configModel->getDatabaseOptions(),
'user_agent' => $this->request->getServerVariable('HTTP_USER_AGENT'),
'title' => t('Settings').' &gt; '.t('About'),
)));
}
/**
* Save settings
*
*/
public function save()
{
$values = $this->request->getValues();
$redirect = $this->request->getStringParam('redirect', 'application');
switch ($redirect) {
case 'application':
$values += array('password_reset' => 0, 'notifications_enabled' => 0);
break;
case 'project':
$values += array(
'subtask_restriction' => 0,
'subtask_time_tracking' => 0,
'cfd_include_closed_tasks' => 0,
'disable_private_project' => 0,
);
break;
}
list($valid, $errors) = $this->configValidator->validate($values);
if (!$valid) {
switch ($redirect) {
case 'email':
return $this->email($values, $errors);
case 'project':
return $this->project($values, $errors);
case 'board':
return $this->board($values, $errors);
default:
return $this->application($values, $errors);
}
}
if ($this->configModel->save($values)) {
$this->languageModel->loadCurrentLanguage();
$this->flash->success(t('Settings saved successfully.'));
} else {
$this->flash->failure(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url->to('ConfigController', $redirect));
}
/**
* Display the application settings page
*
* @access public
*/
public function application(array $values = [], array $errors = [])
{
$this->response->html($this->helper->layout->config('config/application', array(
'mail_transports' => $this->emailClient->getAvailableTransports(),
'languages' => $this->languageModel->getLanguages(),
'timezones' => $this->timezoneModel->getTimezones(),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats(true)),
'time_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getTimeFormats()),
'title' => t('Settings').' &gt; '.t('Application settings'),
'errors' => $errors,
'values' => $values,
)));
}
/**
* Display the email settings page
*
* @access public
*/
public function email(array $values = [], array $errors = [])
{
if (empty($values)) {
$values = $this->configModel->getAll();
}
if (empty($values['mail_transport'])) {
$values['mail_transport'] = MAIL_TRANSPORT;
}
$this->response->html($this->helper->layout->config('config/email', array(
'values' => $values,
'mail_transports' => $this->emailClient->getAvailableTransports(),
'title' => t('Settings').' &gt; '.t('Email settings'),
'errors' => $errors,
)));
}
/**
* Display the project settings page
*
* @access public
*/
public function project(array $values = [], array $errors = [])
{
$this->response->html($this->helper->layout->config('config/project', array(
'colors' => $this->colorModel->getList(),
'default_columns' => implode(', ', $this->boardModel->getDefaultColumns()),
'title' => t('Settings').' &gt; '.t('Project settings'),
'errors' => $errors,
'values' => $values,
)));
}
/**
* Display the board settings page
*
* @access public
*/
public function board(array $values = [], array $errors = [])
{
$this->response->html($this->helper->layout->config('config/board', array(
'title' => t('Settings').' &gt; '.t('Board settings'),
'errors' => $errors,
'values' => $values,
)));
}
/**
* Display the integration settings page
*
* @access public
*/
public function integrations()
{
$this->response->html($this->helper->layout->config('config/integrations', array(
'title' => t('Settings').' &gt; '.t('Integrations'),
)));
}
/**
* Display the webhook settings page
*
* @access public
*/
public function webhook()
{
$this->response->html($this->helper->layout->config('config/webhook', array(
'title' => t('Settings').' &gt; '.t('Webhook settings'),
)));
}
/**
* Display the api settings page
*
* @access public
*/
public function api()
{
$this->response->html($this->helper->layout->config('config/api', array(
'title' => t('Settings').' &gt; '.t('API'),
)));
}
/**
* Download the Sqlite database
*
* @access public
*/
public function downloadDb()
{
$this->checkCSRFParam();
$this->response->withFileDownload('db.sqlite.gz');
$this->response->binary($this->configModel->downloadDatabase());
}
/**
* Optimize the Sqlite database
*
* @access public
*/
public function optimizeDb()
{
$this->checkCSRFParam();
$this->configModel->optimizeDatabase();
$this->flash->success(t('Database optimization done.'));
$this->response->redirect($this->helper->url->to('ConfigController', 'index'));
}
/**
* Display the Sqlite database upload page
*
* @access public
*/
public function uploadDb()
{
$this->response->html($this->template->render('config/upload_db'));
}
/**
* Replace current Sqlite db with uploaded file
*
* @access public
*/
public function saveUploadedDb()
{
$this->checkCSRFParam();
$filename = $this->request->getFilePath('file');
if (!file_exists($filename) || !$this->configModel->uploadDatabase($filename)) {
$this->flash->failure(t('Unable to read uploaded file.'));
} else {
$this->flash->success(t('Database uploaded successfully.'));
}
$this->response->redirect($this->helper->url->to('ConfigController', 'index'));
}
/**
* Regenerate webhook token
*
* @access public
*/
public function token()
{
$type = $this->request->getStringParam('type');
$this->checkCSRFParam();
$this->configModel->regenerateToken($type.'_token');
$this->flash->success(t('Token regenerated.'));
$this->response->redirect($this->helper->url->to('ConfigController', $type));
}
}
+32
View File
@@ -0,0 +1,32 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Controller\BaseController;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\NullOutput;
/**
* Class CronjobController
*
* Runs cron jobs via URL
*
* @package Kanboard\Controller
*/
class CronjobController extends BaseController
{
public function run()
{
$this->checkWebhookToken();
$input = new ArrayInput(array(
'command' => 'cronjob',
));
$output = new NullOutput();
$this->cli->setAutoExit(false);
$this->cli->run($input, $output);
$this->response->html('Cronjob executed');
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Kanboard\Controller;
/**
* Currency Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class CurrencyController extends BaseController
{
/**
* 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 create(array $values = array(), array $errors = array())
{
$this->response->html($this->template->render('currency/create', array(
'values' => $values,
'errors' => $errors,
'currencies' => $this->currencyModel->getCurrencies(),
)));
}
/**
* Validate and save a new currency rate
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->currencyValidator->validateCreation($values);
if ($valid) {
if ($this->currencyModel->create($values['currency'], $values['rate'])) {
$this->flash->success(t('The currency rate has been added successfully.'));
$this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true);
return;
} else {
$this->flash->failure(t('Unable to add this currency rate.'));
}
}
$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(),
)));
}
/**
* Save reference currency
*
* @access public
*/
public function update()
{
$values = $this->request->getValues();
if ($this->configModel->save($values)) {
$this->flash->success(t('Settings saved successfully.'));
} else {
$this->flash->failure(t('Unable to save your settings.'));
}
$this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true);
}
}
+193
View File
@@ -0,0 +1,193 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Security\Role;
/**
* Custom Filter Controller
*
* @package Kanboard\Controller
* @author Timo Litzbarski
* @author Frederic Guillot
*/
class CustomFilterController extends BaseController
{
/**
* Display list of filters
*
* @access public
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function index()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('custom_filter/index', array(
'project' => $project,
'custom_filters' => $this->customFilterModel->getAll($project['id'], $this->userSession->getId()),
'title' => t('Custom filters'),
)));
}
/**
* 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
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
$values['user_id'] = $this->userSession->getId();
list($valid, $errors) = $this->customFilterValidator->validateCreation($values);
if ($valid) {
if ($this->customFilterModel->create($values) !== false) {
$this->flash->success(t('Your custom filter has been created successfully.'));
$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.'));
}
}
$this->create($values, $errors);
}
/**
* Confirmation dialog before removing a custom filter
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$filter = $this->getCustomFilter($project);
$this->response->html($this->helper->layout->project('custom_filter/remove', array(
'project' => $project,
'filter' => $filter,
'title' => t('Remove a custom filter')
)));
}
/**
* Remove a custom filter
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$filter = $this->getCustomFilter($project);
$this->checkPermission($project, $filter);
if ($this->customFilterModel->remove($filter['id'])) {
$this->flash->success(t('Custom filter removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this custom filter.'));
}
$this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id'])));
}
/**
* Edit a custom filter (display the form)
*
* @access public
* @param array $values
* @param array $errors
* @throws AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id'));
$this->checkPermission($project, $filter);
$this->response->html($this->helper->layout->project('custom_filter/edit', array(
'values' => empty($values) ? $filter : $values,
'errors' => $errors,
'project' => $project,
'filter' => $filter,
'title' => t('Edit custom filter')
)));
}
/**
* Edit a custom filter (validate the form and update the database)
*
* @access public
*/
public function update()
{
$project = $this->getProject();
$filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id'));
$this->checkPermission($project, $filter);
$values = $this->request->getValues();
$values['id'] = $filter['id'];
$values['project_id'] = $project['id'];
if (! isset($values['is_shared'])) {
$values += array('is_shared' => 0);
}
if (! isset($values['append'])) {
$values += array('append' => 0);
}
list($valid, $errors) = $this->customFilterValidator->validateModification($values);
if ($valid) {
if ($this->customFilterModel->update($values)) {
$this->flash->success(t('Your custom filter has been updated successfully.'));
$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.'));
}
}
$this->edit($values, $errors);
}
private function checkPermission(array $project, array $filter)
{
$userID = $this->userSession->getId();
if ($filter['user_id'] != $userID) {
if ($this->projectUserRoleModel->getUserRole($project['id'], $userID) !== Role::PROJECT_MANAGER && ! $this->userSession->isAdmin()) {
throw new AccessForbiddenException();
}
}
}
}
+78
View File
@@ -0,0 +1,78 @@
<?php
namespace Kanboard\Controller;
/**
* Dashboard Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class DashboardController extends BaseController
{
/**
* Dashboard overview
*
* @access public
*/
public function show()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/overview', array(
'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)),
'user' => $user,
'overview_paginator' => $this->dashboardPagination->getOverview($user['id']),
'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', DASHBOARD_MAX_PROJECTS),
)));
}
/**
* My tasks
*
* @access public
*/
public function tasks()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/tasks', array(
'title' => t('Tasks overview for %s', $this->helper->user->getFullname($user)),
'paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'tasks', 50),
'user' => $user,
)));
}
/**
* My subtasks
*
* @access public
*/
public function subtasks()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array(
'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)),
'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id']),
'user' => $user,
'nb_subtasks' => $this->subtaskModel->countByAssigneeAndTaskStatus($user['id']),
)));
}
/**
* My projects
*
* @access public
*/
public function projects()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/projects', array(
'title' => t('Projects overview for %s', $this->helper->user->getFullname($user)),
'paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'projects', 25),
'user' => $user,
)));
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Controller;
use Parsedown;
/**
* Documentation Viewer
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class DocumentationController extends BaseController
{
public function shortcuts()
{
$this->response->html($this->template->render('config/keyboard_shortcuts'));
}
}
+114
View File
@@ -0,0 +1,114 @@
<?php
namespace Kanboard\Controller;
/**
* Export Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ExportController extends BaseController
{
/**
* Common export method
*
* @access private
* @param string $model
* @param string $method
* @param string $filename
* @param string $action
* @param string $page_title
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
private function common($model, $method, $filename, $action, $page_title)
{
$project = $this->getProject();
if ($this->request->isPost()) {
$from = $this->request->getRawValue('from');
$to = $this->request->getRawValue('to');
if ($from && $to) {
$data = $this->$model->$method($project['id'], $from, $to);
$this->response->withFileDownload($filename.'.csv');
$this->response->csv($data, $this->request->getRawValue('bom') === '1');
}
} else {
$this->response->html($this->template->render('export/'.$action, array(
'values' => array(
'project_id' => $project['id'],
'from' => '',
'to' => '',
),
'errors' => array(),
'project' => $project,
'title' => $page_title,
)));
}
}
/**
* Task export
*
* @access public
*/
public function tasks()
{
$this->common('taskExport', 'export', t('Tasks'), 'tasks', t('Tasks Export'));
}
/**
* Subtask export
*
* @access public
*/
public function subtasks()
{
$this->common('subtaskExport', 'export', t('Subtasks'), 'subtasks', t('Subtasks Export'));
}
/**
* Daily project summary export
*
* @access public
*/
public function summary()
{
$project = $this->getProject();
if ($this->request->isPost()) {
$from = $this->request->getRawValue('from');
$to = $this->request->getRawValue('to');
if ($from && $to) {
$from = $this->dateParser->getIsoDate($from);
$to = $this->dateParser->getIsoDate($to);
$data = $this->projectDailyColumnStatsModel->getAggregatedMetrics($project['id'], $from, $to);
$this->response->withFileDownload(t('Summary').'.csv');
$this->response->csv($data, $this->request->getRawValue('bom') === '1');
}
} else {
$this->response->html($this->template->render('export/summary', array(
'values' => array(
'project_id' => $project['id'],
'from' => '',
'to' => '',
),
'errors' => array(),
'project' => $project,
'title' => t('Daily project summary export'),
)));
}
}
/**
* Transition export
*
* @access public
*/
public function transitions()
{
$this->common('transitionExport', 'export', t('Transitions'), 'transitions', t('Task transitions export'));
}
}
@@ -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), $project['id']);
$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'], $project['id']);
}
$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, $project['is_private'] == 1),
'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);
} elseif (! $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('task_id' => $taskId)), true);
}
}
}
@@ -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'], $task['project_id']);
$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>');
}
}
}
+58
View File
@@ -0,0 +1,58 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Atom/RSS Feed controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class FeedController extends BaseController
{
/**
* RSS feed for a user
*
* @access public
*/
public function user()
{
$token = $this->request->getStringParam('token');
$user = $this->userModel->getByToken($token);
if (empty($user)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
$events = $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']));
$this->response->xml($this->template->render('feed/user', [
'user' => $user,
'events' => $events,
]));
}
/**
* RSS feed for a project
*
* @access public
*/
public function project()
{
$token = $this->request->getStringParam('token');
$project = $this->projectModel->getByToken($token);
if (empty($project)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
$events = $this->helper->projectActivity->getProjectEvents($project['id']);
$this->response->xml($this->template->render('feed/project', [
'project' => $project,
'events' => $events,
]));
}
}
+163
View File
@@ -0,0 +1,163 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
* File Viewer Controller
*
* @package Kanbaord\Controller
* @author Frederic Guillot
*/
class FileViewerController extends BaseController
{
/**
* Get file content from object storage
*
* @access protected
* @param array $file
* @return string
*/
protected function getFileContent(array $file)
{
$content = '';
try {
if ($file['is_image'] == 0) {
$content = $this->objectStorage->get($file['path']);
}
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
return $content;
}
/**
* Output file with cache
*
* @param array $file
* @param $mimetype
*/
protected function renderFileWithCache(array $file, $mimetype)
{
if ($this->request->getHeader('If-None-Match') === '"'.$file['etag'].'"') {
$this->response->status(304);
} else {
try {
$this->response->withContentType($mimetype);
$this->response->withCache(5 * 86400, $file['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
*/
public function show()
{
$file = $this->getFile();
$type = $this->helper->file->getPreviewType($file['name']);
$params = ['file_id' => $file['id']];
if (array_key_exists('etag', $file)) {
$params['etag'] = $file['etag'];
}
$project_id = $this->request->getIntegerParam('project_id');
if ($project_id !== 0) {
$params['project_id'] = $project_id;
}
if ($file['model'] === 'taskFileModel') {
$params['task_id'] = $file['task_id'];
}
$this->response->html($this->template->render('file_viewer/show', array(
'file' => $file,
'params' => $params,
'type' => $type,
'content' => $this->getFileContent($file),
)));
}
/**
* Display image
*
* @access public
*/
public function image()
{
$file = $this->getFile();
$this->renderFileWithCache($file, $this->helper->file->getImageMimeType($file['name']));
}
/**
* Display file in browser
*
* @access public
*/
public function browser()
{
$file = $this->getFile();
$this->renderFileWithCache($file, $this->helper->file->getBrowserViewType($file['name']));
}
/**
* Display image thumbnail
*
* @access public
*/
public function thumbnail()
{
$file = $this->getFile();
$model = $file['model'];
$filename = $this->$model->getThumbnailPath($file['path']);
$this->response->withCache(5 * 86400, $file['etag']);
$this->response->withContentType('image/png');
if ($this->request->getHeader('If-None-Match') === '"'.$file['etag'].'"') {
$this->response->status(304);
} else {
$this->response->send();
try {
$this->objectStorage->output($filename);
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
// Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19
$data = $this->objectStorage->get($file['path']);
$this->$model->generateThumbnailFromData($file['path'], $data);
$this->objectStorage->output($this->$model->getThumbnailPath($file['path']));
}
}
}
/**
* File download
*
* @access public
*/
public function download()
{
try {
$file = $this->getFile();
$this->response->withFileDownload($file['name']);
$this->response->send();
$this->objectStorage->output($file['path']);
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace Kanboard\Controller;
/**
* Group Ajax Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class GroupAjaxController extends BaseController
{
/**
* Group auto-completion (Ajax)
*
* @access public
*/
public function autocomplete()
{
$search = $this->request->getStringParam('term');
$groups = $this->groupManager->find($search);
$this->response->json($this->groupAutoCompleteFormatter->withGroups($groups)->format());
}
}
@@ -0,0 +1,49 @@
<?php
namespace Kanboard\Controller;
/**
* Class GroupCreationController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class GroupCreationController extends BaseController
{
/**
* Display a form to create a new group
*
* @access public
* @param array $values
* @param array $errors
*/
public function show(array $values = array(), array $errors = array())
{
$this->response->html($this->template->render('group_creation/show', array(
'errors' => $errors,
'values' => $values,
)));
}
/**
* Validate and save a new group
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->groupValidator->validateCreation($values);
if ($valid) {
if ($this->groupModel->create($values['name']) !== false) {
$this->flash->success(t('Group created successfully.'));
return $this->response->redirect($this->helper->url->to('GroupListController', 'index'), true);
} else {
$this->flash->failure(t('Unable to create your group.'));
}
}
return $this->show($values, $errors);
}
}
+186
View File
@@ -0,0 +1,186 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Model\GroupModel;
use Kanboard\Model\UserModel;
/**
* Group Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class GroupListController extends BaseController
{
/**
* List all groups
*
* @access public
*/
public function index()
{
$search = $this->request->getStringParam('search');
$query = $this->groupModel->getQuery();
if ($search !== '') {
$query->ilike('groups.name', '%'.$search.'%');
}
$paginator = $this->paginator
->setUrl('GroupListController', 'index')
->setMax(30)
->setOrder(GroupModel::TABLE.'.name')
->setQuery($query)
->calculate();
$this->response->html($this->helper->layout->app('group/index', array(
'title' => t('Groups').' ('.$paginator->getTotal().')',
'paginator' => $paginator,
'values' => array(
'search' => $search
),
)));
}
/**
* List all users
*
* @access public
*/
public function users()
{
$group_id = $this->request->getIntegerParam('group_id');
$group = $this->groupModel->getById($group_id);
$paginator = $this->paginator
->setUrl('GroupListController', 'users', array('group_id' => $group_id))
->setMax(30)
->setOrder(UserModel::TABLE.'.username')
->setQuery($this->groupMemberModel->getQuery($group_id))
->calculate();
$this->response->html($this->helper->layout->app('group/users', array(
'title' => t('Members of %s', $group['name']).' ('.$paginator->getTotal().')',
'paginator' => $paginator,
'group' => $group,
)));
}
/**
* Form to associate a user to a group
*
* @access public
* @param array $values
* @param array $errors
*/
public function associate(array $values = array(), array $errors = array())
{
$group_id = $this->request->getIntegerParam('group_id');
$group = $this->groupModel->getById($group_id);
if (empty($values)) {
$values['group_id'] = $group_id;
}
$this->response->html($this->template->render('group/associate', array(
'users' => $this->userModel->prepareList($this->groupMemberModel->getNotMembers($group_id)),
'group' => $group,
'errors' => $errors,
'values' => $values,
)));
}
/**
* Add user to a group
*
* @access public
*/
public function addUser()
{
$values = $this->request->getValues();
if (isset($values['group_id']) && isset($values['user_id'])) {
if ($this->groupMemberModel->addUser($values['group_id'], $values['user_id'])) {
$this->flash->success(t('Group member added successfully.'));
return $this->response->redirect($this->helper->url->to('GroupListController', 'users', array('group_id' => $values['group_id'])), true);
} else {
$this->flash->failure(t('Unable to add group member.'));
}
}
return $this->associate($values);
}
/**
* Confirmation dialog to remove a user from a group
*
* @access public
*/
public function dissociate()
{
$group_id = $this->request->getIntegerParam('group_id');
$user_id = $this->request->getIntegerParam('user_id');
$group = $this->groupModel->getById($group_id);
$user = $this->userModel->getById($user_id);
$this->response->html($this->template->render('group/dissociate', array(
'group' => $group,
'user' => $user,
)));
}
/**
* Remove a user from a group
*
* @access public
*/
public function removeUser()
{
$this->checkCSRFParam();
$group_id = $this->request->getIntegerParam('group_id');
$user_id = $this->request->getIntegerParam('user_id');
if ($this->groupMemberModel->removeUser($group_id, $user_id)) {
$this->flash->success(t('User removed successfully from this group.'));
} else {
$this->flash->failure(t('Unable to remove this user from the group.'));
}
$this->response->redirect($this->helper->url->to('GroupListController', 'users', array('group_id' => $group_id)), true);
}
/**
* Confirmation dialog to remove a group
*
* @access public
*/
public function confirm()
{
$group_id = $this->request->getIntegerParam('group_id');
$group = $this->groupModel->getById($group_id);
$this->response->html($this->template->render('group/remove', array(
'group' => $group,
)));
}
/**
* Remove a group
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$group_id = $this->request->getIntegerParam('group_id');
if ($this->groupModel->remove($group_id)) {
$this->flash->success(t('Group removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this group.'));
}
$this->response->redirect($this->helper->url->to('GroupListController', 'index'), true);
}
}
@@ -0,0 +1,53 @@
<?php
namespace Kanboard\Controller;
/**
* Class GroupModificationController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class GroupModificationController extends BaseController
{
/**
* Display a form to update a group
*
* @access public
* @param array $values
* @param array $errors
*/
public function show(array $values = array(), array $errors = array())
{
if (empty($values)) {
$values = $this->groupModel->getById($this->request->getIntegerParam('group_id'));
}
$this->response->html($this->template->render('group_modification/show', array(
'errors' => $errors,
'values' => $values,
)));
}
/**
* Validate and save a group
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->groupValidator->validateModification($values);
if ($valid) {
if ($this->groupModel->update($values) !== false) {
$this->flash->success(t('Group updated successfully.'));
return $this->response->redirect($this->helper->url->to('GroupListController', 'index'), true);
} else {
$this->flash->failure(t('Unable to update your group.'));
}
}
return $this->show($values, $errors);
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\TaskAssigneeFilter;
use Kanboard\Filter\TaskDueDateRangeFilter;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Filter\TaskStatusFilter;
use Kanboard\Model\TaskModel;
use Eluceo\iCal\Component\Calendar as iCalendar;
/**
* iCalendar Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ICalendarController extends BaseController
{
public function user()
{
$token = $this->request->getStringParam('token');
$user = $this->userModel->getByToken($token);
if (empty($user)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
$startRange = strtotime('-2 months');
$endRange = strtotime('+6 months');
$startColumn = $this->configModel->get('calendar_user_tasks', 'date_started');
$calendar = new iCalendar('Kanboard');
$calendar->setName($user['name'] ?: $user['username']);
$calendar->setDescription($user['name'] ?: $user['username']);
$calendar->setPublishedTTL('PT1H');
$queryDueDateOnly = QueryBuilder::create()
->withQuery($this->taskFinderModel->getICalQuery())
->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange)))
->withFilter(new TaskAssigneeFilter($user['id']))
->getQuery();
$queryStartAndDueDate = QueryBuilder::create()
->withQuery($this->taskFinderModel->getICalQuery())
->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
->withFilter(new TaskAssigneeFilter($user['id']))
->getQuery()
->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due'));
$this->response->ical($this->taskICalFormatter
->setCalendar($calendar)
->addTasksWithDueDateOnly($queryDueDateOnly)
->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due')
->format());
}
public function project()
{
$token = $this->request->getStringParam('token');
$project = $this->projectModel->getByToken($token);
if (empty($project)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
$startRange = strtotime('-2 months');
$endRange = strtotime('+6 months');
$startColumn = $this->configModel->get('calendar_project_tasks', 'date_started');
$calendar = new iCalendar('Kanboard');
$calendar->setName($project['name']);
$calendar->setDescription($project['name']);
$calendar->setPublishedTTL('PT1H');
$queryDueDateOnly = QueryBuilder::create()
->withQuery($this->taskFinderModel->getICalQuery())
->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
->withFilter(new TaskProjectFilter($project['id']))
->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange)))
->getQuery();
$queryStartAndDueDate = QueryBuilder::create()
->withQuery($this->taskFinderModel->getICalQuery())
->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
->withFilter(new TaskProjectFilter($project['id']))
->getQuery()
->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due'));
$this->response->ical($this->taskICalFormatter
->setCalendar($calendar)
->addTasksWithDueDateOnly($queryDueDateOnly)
->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due')
->format());
}
protected function getConditionForTasksWithStartAndDueDate($start_time, $end_time, $start_column, $end_column)
{
$start_time = (int) $start_time;
$end_time = (int) $end_time;
$start_column = $this->db->escapeIdentifier($start_column);
$end_column = $this->db->escapeIdentifier($end_column);
$conditions = array(
"($start_column >= '$start_time' AND $start_column <= '$end_time')",
"($start_column <= '$start_time' AND $end_column >= '$start_time')",
"($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))",
);
return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')';
}
}
+162
View File
@@ -0,0 +1,162 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Link Controller
*
* @package Kanboard\Controller
* @author Olivier Maridat
* @author Frederic Guillot
*/
class LinkController extends BaseController
{
/**
* Get the current link
*
* @access protected
* @return array
* @throws PageNotFoundException
*/
protected function getLink()
{
$link = $this->linkModel->getById($this->request->getIntegerParam('link_id'));
if (empty($link)) {
throw new PageNotFoundException();
}
return $link;
}
/**
* 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 create(array $values = array(), array $errors = array())
{
$this->response->html($this->template->render('link/create', array(
'links' => $this->linkModel->getMergedList(),
'values' => $values,
'errors' => $errors,
)));
}
/**
* Validate and save a new link
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->linkValidator->validateCreation($values);
if ($valid) {
if ($this->linkModel->create($values['label'], $values['opposite_label']) !== false) {
$this->flash->success(t('Link added successfully.'));
$this->response->redirect($this->helper->url->to('LinkController', 'show'), true);
return;
} else {
$this->flash->failure(t('Unable to create your link.'));
}
}
$this->create($values, $errors);
}
/**
* Edit form
*
* @access public
* @param array $values
* @param array $errors
* @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$link = $this->getLink();
$link['label'] = t($link['label']);
$this->response->html($this->template->render('link/edit', array(
'values' => $values ?: $link,
'errors' => $errors,
'labels' => $this->linkModel->getList($link['id']),
'link' => $link,
)));
}
/**
* Edit a link (validate the form and update the database)
*
* @access public
*/
public function update()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->linkValidator->validateModification($values);
if ($valid) {
if ($this->linkModel->update($values)) {
$this->flash->success(t('Link updated successfully.'));
$this->response->redirect($this->helper->url->to('LinkController', 'show'), true);
return;
} else {
$this->flash->failure(t('Unable to update your link.'));
}
}
$this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a link
*
* @access public
*/
public function confirm()
{
$link = $this->getLink();
$this->response->html($this->template->render('link/remove', array(
'link' => $link,
)));
}
/**
* Remove a link
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$link = $this->getLink();
if ($this->linkModel->remove($link['id'])) {
$this->flash->success(t('Link removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('LinkController', 'show'), true);
}
}
+130
View File
@@ -0,0 +1,130 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Security\OAuthAuthenticationProviderInterface;
/**
* OAuth Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class OAuthController extends BaseController
{
/**
* Redirect to the provider if no code received
*
* @access private
* @param string $provider
*/
protected function step1($provider)
{
$code = $this->request->getStringParam('code');
$state = $this->request->getStringParam('state');
if (! empty($code)) {
$this->step2($provider, $code, $state);
} else {
$this->response->redirect($this->authenticationManager->getProvider($provider)->getService()->getAuthorizationUrl());
}
}
/**
* Link or authenticate the user
*
* @access protected
* @param string $providerName
* @param string $code
* @param string $state
*/
protected function step2($providerName, $code, $state)
{
$provider = $this->authenticationManager->getProvider($providerName);
$provider->setCode($code);
$hasValidState = $provider->getService()->isValidateState($state);
if ($this->userSession->isLogged()) {
if ($hasValidState) {
$this->link($provider);
} else {
$this->flash->failure(t('The OAuth2 state parameter is invalid'));
$this->response->redirect($this->helper->url->to('UserViewController', 'external', array('user_id' => $this->userSession->getId())));
}
} else {
if ($hasValidState) {
$this->authenticate($providerName);
} else {
$this->authenticationFailure(t('The OAuth2 state parameter is invalid'));
}
}
}
/**
* Link the account
*
* @access protected
* @param OAuthAuthenticationProviderInterface $provider
*/
protected function link(OAuthAuthenticationProviderInterface $provider)
{
if (! $provider->authenticate()) {
$this->flash->failure(t('External authentication failed'));
} else {
$this->userProfile->assign($this->userSession->getId(), $provider->getUser());
$this->flash->success(t('Your external account is linked to your profile successfully.'));
}
$this->response->redirect($this->helper->url->to('UserViewController', 'external', array('user_id' => $this->userSession->getId())));
}
/**
* Unlink external account
*
* @access public
*/
public function unlink()
{
$backend = $this->request->getStringParam('backend');
$this->checkCSRFParam();
if ($this->authenticationManager->getProvider($backend)->unlink($this->userSession->getId())) {
$this->flash->success(t('Your external account is not linked anymore to your profile.'));
} else {
$this->flash->failure(t('Unable to unlink your external account.'));
}
$this->response->redirect($this->helper->url->to('UserViewController', 'external', array('user_id' => $this->userSession->getId())));
}
/**
* Authenticate the account
*
* @access protected
* @param string $providerName
*/
protected function authenticate($providerName)
{
if ($this->authenticationManager->oauthAuthentication($providerName)) {
$this->redirectAfterLogin();
} else {
$this->authenticationFailure(t('External authentication failed'));
}
}
/**
* Show login failure page
*
* @access protected
* @param string $message
*/
protected function authenticationFailure($message)
{
$this->response->html($this->helper->layout->app('auth/index', array(
'errors' => array('login' => $message),
'values' => array(),
'no_layout' => true,
'title' => t('Login')
)));
}
}
+132
View File
@@ -0,0 +1,132 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Password Reset Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class PasswordResetController extends BaseController
{
/**
* Show the form to reset the password
*
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\BaseException
*/
public function create(array $values = array(), array $errors = array())
{
$this->checkActivation();
$this->response->html($this->helper->layout->app('password_reset/create', array(
'errors' => $errors,
'values' => $values,
'no_layout' => true,
)));
}
/**
* Validate and send the email
*/
public function save()
{
$this->checkActivation();
$values = $this->request->getValues();
list($valid, $errors) = $this->passwordResetValidator->validateCreation($values);
if ($valid) {
$this->sendEmail($values['username']);
$this->response->redirect($this->helper->url->to('AuthController', 'login'));
} else {
$this->create($values, $errors);
}
}
/**
* Show the form to set a new password
*
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\BaseException
*/
public function change(array $values = array(), array $errors = array())
{
$this->checkActivation();
$token = $this->request->getStringParam('token');
$user_id = $this->passwordResetModel->getUserIdByToken($token);
if ($user_id !== false) {
$this->response->html($this->helper->layout->app('password_reset/change', array(
'token' => $token,
'errors' => $errors,
'values' => $values,
'no_layout' => true,
)));
} else {
$this->response->redirect($this->helper->url->to('AuthController', 'login'));
}
}
/**
* Set the new password
*/
public function update()
{
$this->checkActivation();
$token = $this->request->getStringParam('token');
$values = $this->request->getValues();
list($valid, $errors) = $this->passwordResetValidator->validateModification($values);
if ($valid) {
$user_id = $this->passwordResetModel->getUserIdByToken($token);
if ($user_id !== false) {
$this->userModel->update(array('id' => $user_id, 'password' => $values['password']));
$this->passwordResetModel->disable($user_id);
}
return $this->response->redirect($this->helper->url->to('AuthController', 'login'));
}
return $this->change($values, $errors);
}
/**
* Send the email
*
* @param string $username
*/
protected function sendEmail($username)
{
$token = $this->passwordResetModel->create($username);
if ($token !== false) {
$user = $this->userCacheDecorator->getByUsername($username);
$this->emailClient->send(
$user['email'],
$user['name'] ?: $user['username'],
t('Password Reset for Kanboard'),
$this->template->render('password_reset/email', array('token' => $token))
);
}
}
/**
* Check feature availability
*/
protected function checkActivation()
{
if ($this->configModel->get('password_reset', 0) == 0) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
}
}
+153
View File
@@ -0,0 +1,153 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Plugin\Directory;
use Kanboard\Core\Plugin\Installer;
use Kanboard\Core\Plugin\PluginInstallerException;
/**
* Class PluginController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class PluginController extends BaseController
{
/**
* Display the plugin page
*
* @access public
*/
public function show()
{
$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(),
)));
}
/**
* Display list of available plugins
*/
public function directory()
{
$installedPlugins = [];
$availablePlugins = [];
$isConfigured = Installer::isConfigured();
if ($isConfigured) {
foreach ($this->pluginLoader->getPlugins() as $plugin) {
$installedPlugins[$plugin->getPluginName()] = $plugin->getPluginVersion();
}
$availablePlugins = Directory::getInstance($this->container)->getAvailablePlugins();
}
$this->response->html($this->helper->layout->plugin('plugin/directory', array(
'installed_plugins' => $installedPlugins,
'available_plugins' => $availablePlugins,
'title' => t('Plugin Directory'),
'is_configured' => $isConfigured,
)));
}
/**
* Install plugin from URL
*
* @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function install()
{
if (! Installer::isConfigured()) {
throw new AccessForbiddenException();
}
$this->checkCSRFParam();
$pluginArchiveUrl = urldecode($this->request->getStringParam('archive_url'));
try {
$installer = new Installer($this->container);
$installer->install($pluginArchiveUrl);
$this->flash->success(t('Plugin installed successfully.'));
} catch (PluginInstallerException $e) {
$this->flash->failure($e->getMessage());
}
$this->response->redirect($this->helper->url->to('PluginController', 'show'));
}
/**
* Update plugin from URL
*
* @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function update()
{
if (! Installer::isConfigured()) {
throw new AccessForbiddenException();
}
$this->checkCSRFParam();
$pluginArchiveUrl = urldecode($this->request->getStringParam('archive_url'));
try {
$installer = new Installer($this->container);
$installer->update($pluginArchiveUrl);
$this->flash->success(t('Plugin updated successfully.'));
} catch (PluginInstallerException $e) {
$this->flash->failure($e->getMessage());
}
$this->response->redirect($this->helper->url->to('PluginController', 'show'));
}
/**
* Confirmation before to remove the plugin
*/
public function confirm()
{
if (! Installer::isConfigured()) {
throw new AccessForbiddenException();
}
$pluginId = $this->request->getStringParam('pluginId');
$plugins = array_merge(
$this->pluginLoader->getPlugins(),
$this->pluginLoader->getIncompatiblePlugins()
);
$this->response->html($this->template->render('plugin/remove', array(
'plugin_id' => $pluginId,
'plugin' => $plugins[$pluginId],
)));
}
/**
* Remove a plugin
*
* @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function uninstall()
{
if (! Installer::isConfigured()) {
throw new AccessForbiddenException();
}
$this->checkCSRFParam();
$pluginId = $this->request->getStringParam('pluginId');
try {
$installer = new Installer($this->container);
$installer->uninstall($pluginId);
$this->flash->success(t('Plugin removed successfully.'));
} catch (PluginInstallerException $e) {
$this->flash->failure($e->getMessage());
}
$this->response->redirect($this->helper->url->to('PluginController', 'show'));
}
}
@@ -0,0 +1,116 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Predefined Task Description Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class PredefinedTaskDescriptionController extends BaseController
{
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->template->render('predefined_task_description/create', array(
'values' => $values,
'errors' => $errors,
'project' => $project,
)));
}
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values);
if ($valid) {
if ($this->predefinedTaskDescriptionModel->create($project['id'], $values['title'], $values['description']) !== false) {
$this->flash->success(t('Template created successfully.'));
} else {
$this->flash->failure(t('Unable to create this template.'));
}
$this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true);
} else {
$this->create($values, $errors);
}
}
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id'));
$this->response->html($this->template->render('predefined_task_description/edit', array(
'values' => empty($values) ? $template : $values,
'template' => $template,
'errors' => $errors,
'project' => $project,
)));
}
public function update()
{
$project = $this->getProject();
$template = $this->getTemplate($project);
$values = $this->request->getValues();
list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values);
if ($valid) {
if ($this->predefinedTaskDescriptionModel->update($project['id'], $template['id'], $values['title'], $values['description']) !== false) {
$this->flash->success(t('Template updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this template.'));
}
$this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true);
} else {
$this->edit($values, $errors);
}
}
public function confirm()
{
$project = $this->getProject();
$template = $this->getTemplate($project);
$this->response->html($this->template->render('predefined_task_description/remove', array(
'template' => $template,
'project' => $project,
)));
}
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$template = $this->getTemplate($project);
if ($this->predefinedTaskDescriptionModel->remove($project['id'], $template['id'])) {
$this->flash->success(t('Template removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this template.'));
}
$this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true);
}
protected function getTemplate(array $project)
{
$template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id'));
if (empty($template)) {
throw new PageNotFoundException();
}
return $template;
}
}
@@ -0,0 +1,44 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Duplicate automatic action from another project
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectActionDuplicationController extends BaseController
{
public function show()
{
$project = $this->getProject();
$projects = $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId());
unset($projects[$project['id']]);
$this->response->html($this->template->render('project_action_duplication/show', array(
'project' => $project,
'projects_list' => $projects,
)));
}
public function save()
{
$project = $this->getProject();
$src_project_id = $this->request->getValue('src_project_id');
if (empty($src_project_id) || ! $this->projectPermissionModel->isUserAllowed($src_project_id, $this->userSession->getId())) {
throw new AccessForbiddenException();
}
if ($this->actionModel->duplicate($src_project_id, $project['id'])) {
$this->flash->success(t('Actions duplicated successfully.'));
} else {
$this->flash->failure(t('Unable to duplicate actions.'));
}
$this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id'])));
}
}
@@ -0,0 +1,139 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Project Creation Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectCreationController extends BaseController
{
/**
* Display a form to create a new project
*
* @access public
* @param array $values
* @param array $errors
*/
public function create(array $values = array(), array $errors = array())
{
$is_private = isset($values['is_private']) && $values['is_private'] == 1;
$projects_list = array(0 => t('Do not duplicate anything')) + $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
$this->response->html($this->helper->layout->app('project_creation/create', array(
'values' => $values,
'errors' => $errors,
'is_private' => $is_private,
'projects_list' => $projects_list,
'title' => $is_private ? t('New personal project') : t('New project'),
)));
}
/**
* Display a form to create a private project
*
* @access public
* @param array $values
* @param array $errors
*/
public function createPrivate(array $values = array(), array $errors = array())
{
$values['is_private'] = 1;
$this->create($values, $errors);
}
/**
* Validate and save a new project
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->projectValidator->validateCreation($values);
if ($valid) {
$project_id = $this->createOrDuplicate($values);
if ($project_id > 0) {
$this->flash->success(t('Your project has been created successfully.'));
return $this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project_id)));
}
$this->flash->failure(t('Unable to create your project.'));
}
return $this->create($values, $errors);
}
/**
* Create or duplicate a project
*
* @access private
* @param array $values
* @return boolean|integer
*/
private function createOrDuplicate(array $values)
{
if (empty($values['src_project_id'])) {
return $this->createNewProject($values);
}
return $this->duplicateNewProject($values);
}
/**
* Save a new project
*
* @access private
* @param array $values
* @return boolean|integer
*/
private function createNewProject(array $values)
{
$project = array(
'name' => $values['name'],
'is_private' => $values['is_private'],
'identifier' => $values['identifier'],
'per_swimlane_task_limits' => array_key_exists('per_swimlane_task_limits', $values) ? $values['per_swimlane_task_limits'] : 0,
'task_limit' => $values['task_limit'],
);
return $this->projectModel->create($project, $this->userSession->getId(), true);
}
/**
* Create from another project
*
* @access private
* @param array $values
* @return boolean|integer
*/
private function duplicateNewProject(array $values)
{
$selection = array();
if (! $this->projectPermissionModel->isUserAllowed($values['src_project_id'], $this->userSession->getId())) {
throw new AccessForbiddenException();
}
foreach ($this->projectDuplicationModel->getOptionalSelection() as $item) {
if (isset($values[$item]) && $values[$item] == 1) {
$selection[] = $item;
}
}
return $this->projectDuplicationModel->duplicate(
$values['src_project_id'],
$selection,
$this->userSession->getId(),
$values['name'],
$values['is_private'] == 1,
$values['identifier']
);
}
}
+82
View File
@@ -0,0 +1,82 @@
<?php
namespace Kanboard\Controller;
/**
* Project Edit Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectEditController extends BaseController
{
/**
* Edit project
*
* @access public
* @param array $values
* @param array $errors
*/
public function show(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$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')
)));
}
/**
* Validate and update a project
*
* @access public
*/
public function update()
{
$project = $this->getProject();
$values = $this->request->getValues();
$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', 'show', array('project_id' => $project['id'])), true);
} else {
$this->flash->failure(t('Unable to update this project.'));
}
}
return $this->show($values, $errors);
}
/**
* Prepare form values
*
* @access private
* @param array $project
* @param array $values
* @return array
*/
private function prepareValues(array $project, array $values)
{
$values['id'] = $project['id'];
if (isset($values['is_private'])) {
if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) {
unset($values['is_private']);
}
} 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;
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace Kanboard\Controller;
/**
* Project File Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectFileController extends BaseController
{
/**
* File upload form
*
* @access public
*/
public function create()
{
$project = $this->getProject();
$this->response->html($this->template->render('project_file/create', array(
'project' => $project,
'max_size' => get_upload_max_size(),
)));
}
/**
* Save uploaded files
*
* @access public
*/
public function save()
{
$this->checkReusableCSRFParam();
$project = $this->getProject();
$result = $this->projectFileModel->uploadFiles($project['id'], $this->request->getFileInfo('files'));
if ($this->request->isAjax()) {
if (! $result) {
$this->response->json(array('message' => t('Unable to upload files, check the permissions of your data folder.')), 500);
} else {
$this->response->json(array('message' => 'OK'));
}
} else {
if (! $result) {
$this->flash->failure(t('Unable to upload files, check the permissions of your data folder.'));
}
$this->response->redirect($this->helper->url->to('ProjectOverviewController', 'show', array('project_id' => $project['id'])), true);
}
}
/**
* Remove a file
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$file = $this->getFile();
if ($this->projectFileModel->remove($file['id'])) {
$this->flash->success(t('File removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this file.'));
}
$this->response->redirect($this->helper->url->to('ProjectOverviewController', 'show', array('project_id' => $project['id'])));
}
/**
* Confirmation dialog before removing a file
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$file = $this->getFile();
$this->response->html($this->template->render('project_file/remove', array(
'project' => $project,
'file' => $file,
)));
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Kanboard\Controller;
/**
* Class ProjectListController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectListController extends BaseController
{
/**
* List of projects
*
* @access public
*/
public function show()
{
if ($this->userSession->isAdmin()) {
$projectIds = $this->projectModel->getAllIds();
} else {
$projectIds = $this->projectPermissionModel->getProjectIds($this->userSession->getId());
}
$query = $this->projectModel->getQueryByProjectIds($projectIds);
$search = $this->request->getStringParam('search');
if ($search !== '') {
$query->ilike('projects.name', '%' . $search . '%');
}
$paginator = $this->paginator
->setUrl('ProjectListController', 'show')
->setMax(20)
->setOrder('name')
->setQuery($query)
->calculate();
$this->response->html($this->helper->layout->dashboard('project_list/listing', array(
'paginator' => $paginator,
'title' => t('Projects') . ' (' . $paginator->getTotal() . ')',
'values' => array('search' => $search),
'user' => $this->getUser(),
)));
}
}
@@ -0,0 +1,33 @@
<?php
namespace Kanboard\Controller;
/**
* Project Overview Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectOverviewController extends BaseController
{
/**
* Show project overview
*/
public function show()
{
$project = $this->getProject();
$columns = $this->columnModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->app('project_overview/show', array(
'project' => $project,
'columns' => $columns,
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']),
'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']),
)));
}
}
@@ -0,0 +1,221 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Security\Role;
/**
* Project Permission Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectPermissionController extends BaseController
{
/**
* Permissions are only available for team projects
*
* @access protected
* @param integer $project_id Default project id
* @return array
* @throws AccessForbiddenException
*/
protected function getProject($project_id = 0)
{
$project = parent::getProject($project_id);
if ($project['is_private'] == 1) {
throw new AccessForbiddenException();
}
return $project;
}
/**
* Show all permissions
*
* @access public
* @param array $values
* @param array $errors
* @throws AccessForbiddenException
*/
public function index(array $values = array(), array $errors = array())
{
$project = $this->getProject();
if (empty($values)) {
$values['role'] = Role::PROJECT_MEMBER;
}
$this->response->html($this->helper->layout->project('project_permission/index', array(
'project' => $project,
'users' => $this->projectUserRoleModel->getUsers($project['id']),
'groups' => $this->projectGroupRoleModel->getGroups($project['id']),
'roles' => $this->projectRoleModel->getList($project['id']),
'values' => $values,
'errors' => $errors,
'title' => t('Project Permissions'),
)));
}
/**
* Add user to the project
*
* @access public
*/
public function addUser()
{
$this->checkCSRFForm();
$project = $this->getProject();
$values = $this->request->getValues();
if (empty($values['user_id']) && ! empty($values['external_id']) && ! empty($values['external_id_column'])) {
$values['user_id'] = $this->userModel->getOrCreateExternalUserId($values['username'], $values['name'], $values['external_id_column'], $values['external_id']);
}
if (empty($values['user_id'])) {
$this->flash->failure(t('User not found.'));
} elseif ($this->projectUserRoleModel->addUser($project['id'], $values['user_id'], $values['role'])) {
$this->flash->success(t('Project updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id'])));
}
/**
* Revoke user access
*
* @access public
*/
public function removeUser()
{
$this->checkCSRFParam();
$project = $this->getProject();
$user_id = $this->request->getIntegerParam('user_id');
if ($this->projectUserRoleModel->removeUser($project['id'], $user_id)) {
$this->flash->success(t('Project updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id'])));
}
/**
* Change user role
*
* @access public
*/
public function changeUserRole()
{
$this->checkReusableGETCSRFParam();
$project = $this->getProject();
if (! $this->request->isAjax()) {
$this->response->json(array('status' => 'error'), 400);
return;
}
$values = $this->request->getJson();
if (empty($project) ||
empty($values)
) {
$this->response->json(array('status' => 'error'), 500);
return;
}
$userRole = $this->projectUserRoleModel->getUserRole($project['id'], $values['id']);
$usersGroupedByRole = $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']);
if ($userRole === 'project-manager' &&
$values['role'] !== 'project-manager' &&
count($usersGroupedByRole['project-manager']) <= 1
) {
$this->response->json(array('status' => 'error'), 500);
return;
}
$this->projectUserRoleModel->changeUserRole($project['id'], $values['id'], $values['role']);
$this->response->json(array('status' => 'ok'));
}
/**
* Add group to the project
*
* @access public
*/
public function addGroup()
{
$this->checkCSRFForm();
$project = $this->getProject();
$values = $this->request->getValues();
if (empty($values['group_id']) && ! empty($values['external_id'])) {
$values['group_id'] = $this->groupModel->getOrCreateExternalGroupId($values['name'], $values['external_id']);
}
if (empty($values['group_id'])) {
$this->flash->failure(t('Unable to find this group.'));
} else {
if ($this->projectGroupRoleModel->addGroup($project['id'], $values['group_id'], $values['role'])) {
$this->flash->success(t('Project updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this project.'));
}
}
$this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id'])));
}
/**
* Revoke group access
*
* @access public
*/
public function removeGroup()
{
$this->checkCSRFParam();
$project = $this->getProject();
$group_id = $this->request->getIntegerParam('group_id');
if ($this->projectGroupRoleModel->removeGroup($project['id'], $group_id)) {
$this->flash->success(t('Project updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id'])));
}
/**
* Change group role
*
* @access public
*/
public function changeGroupRole()
{
$this->checkReusableGETCSRFParam();
$project = $this->getProject();
if (! $this->request->isAjax()) {
$this->response->json(array('status' => 'error'), 400);
return;
}
$values = $this->request->getJson();
if (! empty($project) && ! empty($values) && $this->projectGroupRoleModel->changeGroupRole($project['id'], $values['id'], $values['role'])) {
$this->response->json(array('status' => 'ok'));
} else {
$this->response->json(array('status' => 'error'));
}
}
}
@@ -0,0 +1,50 @@
<?php
namespace Kanboard\Controller;
/**
* Project Predefined Content Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectPredefinedContentController extends BaseController
{
public function show(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('project_predefined_content/show', array(
'values' => empty($values) ? $project : $values,
'errors' => $errors,
'project' => $project,
'predefined_task_descriptions' => $this->predefinedTaskDescriptionModel->getAll($project['id']),
'title' => t('Predefined Contents'),
)));
}
public function update()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values = array(
'id' => $project['id'],
'name' => $project['name'],
'predefined_email_subjects' => isset($values['predefined_email_subjects']) ? $values['predefined_email_subjects'] : '',
);
list($valid, $errors) = $this->projectValidator->validateModification($values);
if ($valid) {
if ($this->projectModel->update($values)) {
$this->flash->success(t('Project updated successfully.'));
return $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true);
} else {
$this->flash->failure(t('Unable to update this project.'));
}
}
return $this->show($values, $errors);
}
}
+162
View File
@@ -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);
}
}
@@ -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'])));
}
}
+100
View File
@@ -0,0 +1,100 @@
<?php
namespace Kanboard\Controller;
/**
* Class ProjectStatusController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectStatusController extends BaseController
{
/**
* Enable a project (confirmation dialog box)
*/
public function confirmEnable()
{
$project = $this->getProject();
$this->response->html($this->template->render('project_status/enable', array(
'project' => $project,
)));
}
/**
* Enable the project
*/
public function enable()
{
$project = $this->getProject();
$this->checkCSRFParam();
if ($this->projectModel->enable($project['id'])) {
$this->flash->success(t('Project activated successfully.'));
} else {
$this->flash->failure(t('Unable to activate this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project['id'])), true);
}
/**
* Disable a project (confirmation dialog box)
*/
public function confirmDisable()
{
$project = $this->getProject();
$this->response->html($this->template->render('project_status/disable', array(
'project' => $project,
)));
}
/**
* Disable a project
*/
public function disable()
{
$project = $this->getProject();
$this->checkCSRFParam();
if ($this->projectModel->disable($project['id'])) {
$this->flash->success(t('Project disabled successfully.'));
} else {
$this->flash->failure(t('Unable to disable this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project['id'])), true);
}
/**
* Remove a project (confirmation dialog box)
*/
public function confirmRemove()
{
$project = $this->getProject();
$this->response->html($this->template->render('project_status/remove', array(
'project' => $project,
'title' => t('Remove project')
)));
}
/**
* Remove a project
*/
public function remove()
{
$project = $this->getProject();
$this->checkCSRFParam();
if ($this->projectModel->remove($project['id'])) {
$this->flash->success(t('Project removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectListController', 'show'));
}
}
+182
View File
@@ -0,0 +1,182 @@
<?php
namespace Kanboard\Controller;
/**
* Class ProjectTagController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectTagController extends BaseController
{
public function index()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('project_tag/index', array(
'project' => $project,
'tags' => $this->tagModel->getAllByProject($project['id']),
'colors' => $this->colorModel->getList(),
'title' => t('Project tags management'),
)));
}
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->template->render('project_tag/create', array(
'project' => $project,
'values' => $values,
'colors' => $this->colorModel->getList(),
'errors' => $errors,
)));
}
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
list($valid, $errors) = $this->tagValidator->validateCreation($values);
if ($valid) {
if ($this->tagModel->create($project['id'], $values['name'], $values['color_id']) > 0) {
$this->flash->success(t('Tag created successfully.'));
} else {
$this->flash->failure(t('Unable to create this tag.'));
}
$this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
} else {
$this->create($values, $errors);
}
}
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$tag = $this->getProjectTag($project);
if (empty($values)) {
$values = $tag;
}
$this->response->html($this->template->render('project_tag/edit', array(
'project' => $project,
'tag' => $tag,
'values' => $values,
'colors' => $this->colorModel->getList(),
'errors' => $errors,
)));
}
public function update()
{
$project = $this->getProject();
$tag = $this->getProjectTag($project);
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
$values['id'] = $tag['id'];
list($valid, $errors) = $this->tagValidator->validateModification($values);
if ($valid) {
if ($this->tagModel->update($values['id'], $values['name'], $values['color_id'])) {
$this->flash->success(t('Tag updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this tag.'));
}
$this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
} else {
$this->edit($values, $errors);
}
}
public function confirm()
{
$project = $this->getProject();
$tag = $this->getProjectTag($project);
$this->response->html($this->template->render('project_tag/remove', array(
'tag' => $tag,
'project' => $project,
)));
}
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$tag = $this->getProjectTag($project);
if ($this->tagModel->remove($tag['id'])) {
$this->flash->success(t('Tag removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this tag.'));
}
$this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
}
/**
* Confirm dialog to make a tag global
*
* @return void
*/
public function confirmMakeGlobalTag()
{
$project = $this->getProject();
$tag = $this->getProjectTag($project);
$this->response->html($this->template->render('project_tag/make_global', array(
'tag' => $tag,
'project' => $project,
)));
}
/**
* Make a tag global and flash result
*
* @return void
*/
public function makeGlobalTag()
{
if ($this->userSession->isAdmin()) {
$project = $this->getProject();
$tag = $this->getProjectTag($project);
if ($this->tagModel->update($tag['id'], $tag['name'], $tag['color_id'], 0)) {
$this->flash->success(t('Tag updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this tag.'));
}
$this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
}
}
/**
* Update project tag settings
*
* @return void
*/
public function updateSettings()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['enable_global_tags'] = array_key_exists('enable_global_tags', $values) ? $values['enable_global_tags'] : 0;
if ($this->projectModel->changeGlobalTagUsage($project['id'], $values['enable_global_tags'])) {
$this->flash->success(t('Project updated successfully.'));
return $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
} else {
$this->flash->failure(t('Unable to update this project.'));
}
}
}
@@ -0,0 +1,130 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Model\UserModel;
use Kanboard\Model\TaskModel;
use Kanboard\Core\Security\Role;
/**
* Project User overview
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectUserOverviewController extends BaseController
{
private function common()
{
$user_id = $this->request->getIntegerParam('user_id', UserModel::EVERYBODY_ID);
if ($this->userSession->isAdmin()) {
$project_ids = $this->projectModel->getAllIds();
} else {
$project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
}
return array($user_id, $project_ids, $this->userModel->getActiveUsersList(true));
}
private function role($role, $action, $title, $title_user)
{
list($user_id, $project_ids, $users) = $this->common();
$query = $this->projectPermissionModel->getQueryByRole($project_ids, $role)->callback(array($this->projectModel, 'applyColumnStats'));
if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) {
$query->eq(UserModel::TABLE.'.id', $user_id);
$title = t($title_user, $users[$user_id]);
}
$paginator = $this->paginator
->setUrl('ProjectUserOverviewController', $action, array('user_id' => $user_id))
->setMax(30)
->setOrder('projects.name')
->setQuery($query)
->calculate();
$this->response->html($this->helper->layout->projectUser('project_user_overview/roles', array(
'paginator' => $paginator,
'title' => $title,
'user_id' => $user_id,
'users' => $users,
)));
}
private function tasks($is_active, $action, $title, $title_user)
{
list($user_id, $project_ids, $users) = $this->common();
$query = $this->taskFinderModel->getProjectUserOverviewQuery($project_ids, $is_active);
if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) {
$query->eq(TaskModel::TABLE.'.owner_id', $user_id);
$title = t($title_user, $users[$user_id]);
}
$paginator = $this->paginator
->setUrl('ProjectUserOverviewController', $action, array('user_id' => $user_id))
->setMax(50)
->setOrder(TaskModel::TABLE.'.id')
->setQuery($query)
->calculate();
$this->response->html($this->helper->layout->projectUser('project_user_overview/tasks', array(
'paginator' => $paginator,
'title' => $title,
'user_id' => $user_id,
'users' => $users,
)));
}
/**
* Display the list of project managers
*
*/
public function managers()
{
$this->role(Role::PROJECT_MANAGER, 'managers', t('People who are project managers'), 'Projects where "%s" is manager');
}
/**
* Display the list of project members
*
*/
public function members()
{
$this->role(Role::PROJECT_MEMBER, 'members', t('People who are project members'), 'Projects where "%s" is member');
}
/**
* Display the list of open taks
*
*/
public function opens()
{
$this->tasks(TaskModel::STATUS_OPEN, 'opens', t('Open tasks'), 'Open tasks assigned to "%s"');
}
/**
* Display the list of closed tasks
*
*/
public function closed()
{
$this->tasks(TaskModel::STATUS_CLOSED, 'closed', t('Closed tasks'), 'Closed tasks assigned to "%s"');
}
/**
* Users tooltip
*/
public function users()
{
$project = $this->getProject();
$this->response->html($this->template->render('project_user_overview/tooltip_users', array(
'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']),
'roles' => $this->projectRoleModel->getList($project['id']),
)));
}
}
+228
View File
@@ -0,0 +1,228 @@
<?php
namespace Kanboard\Controller;
/**
* Class ProjectViewController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class ProjectViewController extends BaseController
{
/**
* Show the project information page
*
* @access public
*/
public function show()
{
$project = $this->getProject();
$columns = $this->columnModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->project('project_view/show', array(
'project' => $project,
'columns' => $columns,
'title' => $project['name'],
)));
}
/**
* Public access management
*
* @access public
*/
public function share()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('project_view/share', array(
'project' => $project,
'title' => t('Public access'),
)));
}
/**
* Change project sharing
*
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function updateSharing()
{
$project = $this->getProject();
$this->checkCSRFParam();
$switch = $this->request->getStringParam('switch');
if ($this->projectModel->{$switch.'PublicAccess'}($project['id'])) {
$this->flash->success(t('Project updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectViewController', 'share', array('project_id' => $project['id'])));
}
/**
* Integrations page
*
* @access public
*/
public function integrations()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('project_view/integrations', array(
'project' => $project,
'title' => t('Integrations'),
'webhook_token' => $this->configModel->get('webhook_token'),
'values' => $this->projectMetadataModel->getAll($project['id']),
'errors' => array(),
)));
}
/**
* Update integrations
*
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function updateIntegrations()
{
$project = $this->getProject();
$this->projectMetadataModel->save($project['id'], $this->request->getValues());
$this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('ProjectViewController', 'integrations', array('project_id' => $project['id'])));
}
/**
* Display project notifications
*
* @access public
*/
public function notifications()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('project_view/notifications', array(
'notifications' => $this->projectNotificationModel->readSettings($project['id']),
'types' => $this->projectNotificationTypeModel->getTypes(),
'project' => $project,
'title' => t('Notifications'),
)));
}
/**
* Update notifications
*
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function updateNotifications()
{
$project = $this->getProject();
$values = $this->request->getValues();
$this->projectNotificationModel->saveSettings($project['id'], $values);
$this->flash->success(t('Project updated successfully.'));
$this->response->redirect($this->helper->url->to('ProjectViewController', 'notifications', array('project_id' => $project['id'])));
}
/**
* Duplicate a project
*
* @author Antonio Rabelo
* @author Michael Lüpkes
* @access public
*/
public function duplicate()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('project_view/duplicate', array(
'project' => $project,
'title' => t('Clone this project')
)));
}
/**
* Do project duplication
*/
public function doDuplication()
{
$this->checkCSRFForm();
$project = $this->getProject();
$values = $this->request->getRawFormValues();
$project_id = $this->projectDuplicationModel->duplicate($project['id'], array_keys($values), $this->userSession->getId());
if ($project_id !== false) {
$this->flash->success(t('Project cloned successfully.'));
} else {
$this->flash->failure(t('Unable to clone this project.'));
}
$this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project_id)));
}
/**
* Import another project's tasks into the currently opened project.
*
* @return void
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function importTasks()
{
$project = $this->getProject();
// Fetch list of projects to copy tasks from.
// Remove current project from the list of the user's projects.
$otherProjects = array_filter(
$this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()),
static function ($projectId) use ($project) {
return (int) $project['id'] !== $projectId;
},
ARRAY_FILTER_USE_KEY
);
$this->response->html($this->helper->layout->project('project_view/importTasks', array(
'project' => $project,
'title' => t('Import tasks from another project'),
'projects' => $otherProjects,
'values' => [],
'errors' => [],
)));
}
/**
* Handle a form submission to copy tasks of a project.
*
* @return void
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function doTasksImport()
{
$this->checkCSRFForm();
$project = $this->getProject();
$values = $this->request->getValues();
$srcProjectId = isset($values['src_project_id']) ? (int) $values['src_project_id'] : 0;
if (empty($srcProjectId) || !$this->projectPermissionModel->isUserAllowed($srcProjectId, $this->userSession->getId())) {
$this->response->redirect($this->helper->url->to('ProjectViewController', 'importTasks', array('project_id' => $project['id'])));
return;
}
if ($this->projectTaskDuplicationModel->duplicate($srcProjectId, $project['id'])) {
$this->flash->success(t('Tasks copied successfully.'));
} else {
$this->flash->failure(t('Unable to copy tasks.'));
$this->response->redirect($this->helper->url->to('ProjectViewController', 'importTasks', array('project_id' => $project['id'])));
return;
}
$this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project['id'])));
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Filter\TaskProjectsFilter;
use Kanboard\Model\TaskModel;
/**
* Search Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class SearchController extends BaseController
{
public function index()
{
$projects = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
$search = urldecode($this->request->getStringParam('search'));
$nb_tasks = 0;
$paginator = $this->paginator
->setUrl('SearchController', 'index', array('search' => $search))
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
->setDirection('DESC');
if ($search !== '' && ! empty($projects)) {
$paginator
->setFormatter($this->taskListFormatter)
->setQuery(
$this->taskLexer
->build($search)
->withFilter(new TaskProjectsFilter(array_keys($projects)))
->getQuery()
)
->calculate();
$nb_tasks = $paginator->getTotal();
}
$this->response->html($this->helper->layout->app('search/index', array(
'values' => array(
'search' => $search,
'controller' => 'SearchController',
'action' => 'index',
),
'paginator' => $paginator,
'title' => t('Search tasks').($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
)));
}
public function activity()
{
$search = urldecode($this->request->getStringParam('search'));
$events = $this->helper->projectActivity->searchEvents($search);
$nb_events = count($events);
$this->response->html($this->helper->layout->app('search/activity', array(
'values' => array(
'search' => $search,
'controller' => 'SearchController',
'action' => 'activity',
),
'title' => t('Search in activity stream').($nb_events > 0 ? ' ('.$nb_events.')' : ''),
'nb_events' => $nb_events,
'events' => $events,
)));
}
}
+222
View File
@@ -0,0 +1,222 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Subtask controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class SubtaskController extends BaseController
{
/**
* Creation form
*
* @access public
* @param array $values
* @param array $errors
* @throws AccessForbiddenException
* @throws PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
if (empty($values)) {
$values = $this->prepareValues($task);
}
$this->response->html($this->template->render('subtask/create', array(
'values' => $values,
'errors' => $errors,
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']),
'task' => $task,
)));
}
/**
* Prepare form values
*
* @access protected
* @param array $task
* @return array
*/
protected function prepareValues(array $task)
{
$values = array(
'task_id' => $task['id'],
'user_id' => $task['owner_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
*
* @access public
*/
public function save()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['task_id'] = $task['id'];
$subtasks = explode("\r\n", isset($values['title']) ? $values['title'] : '');
$subtasksAdded = 0;
foreach ($subtasks as $subtask) {
$subtask = trim($subtask);
if (! empty($subtask)) {
$subtaskValues = $values;
$subtaskValues['title'] = $subtask;
list($valid, $errors) = $this->subtaskValidator->validateCreation($subtaskValues);
if (! $valid) {
$this->create($values, $errors);
return false;
}
if (! $this->subtaskModel->create($subtaskValues)) {
$this->flash->failure(t('Unable to create your sub-task.'));
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'subtasks'), true);
return false;
}
$subtasksAdded++;
}
}
if (isset($values['another_subtask']) && $values['another_subtask'] == 1) {
return $this->create(array(
'project_id' => $task['project_id'],
'task_id' => $task['id'],
'user_id' => $values['user_id'],
'another_subtask' => 1,
'subtasks_added' => $subtasksAdded,
));
} elseif ($subtasksAdded > 0) {
if ($subtasksAdded === 1) {
$this->flash->success(t('Subtask added successfully.'));
} else {
$this->flash->success(t('%d subtasks added successfully.', $subtasksAdded));
}
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'subtasks'), true);
}
/**
* Edit form
*
* @access public
* @param array $values
* @param array $errors
* @throws AccessForbiddenException
* @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$this->response->html($this->template->render('subtask/edit', array(
'values' => empty($values) ? $subtask : $values,
'errors' => $errors,
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']),
'status_list' => $this->subtaskModel->getStatusList(),
'subtask' => $subtask,
'task' => $task,
)));
}
/**
* Update and validate a subtask
*
* @access public
*/
public function update()
{
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$values = $this->request->getValues();
$values['id'] = $subtask['id'];
$values['task_id'] = $task['id'];
list($valid, $errors) = $this->subtaskValidator->validateModification($values);
if ($valid) {
if ($this->subtaskModel->update($values)) {
$this->flash->success(t('Sub-task updated successfully.'));
} else {
$this->flash->failure(t('Unable to update your sub-task.'));
}
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
return $this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a subtask
*
* @access public
*/
public function confirm()
{
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$this->response->html($this->template->render('subtask/remove', array(
'subtask' => $subtask,
'task' => $task,
)));
}
/**
* Remove a subtask
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$task = $this->getTask();
$subtask = $this->getSubtask($task);
if ($this->subtaskModel->remove($subtask['id'])) {
$this->flash->success(t('Sub-task removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this sub-task.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
/**
* Move subtask position
*
* @access public
*/
public function movePosition()
{
$task = $this->getTask();
$values = $this->request->getJson();
if (! empty($values) && $this->helper->user->hasProjectAccess('SubtaskController', 'movePosition', $task['project_id'])) {
$result = $this->subtaskPositionModel->changePosition($task['id'], $values['subtask_id'], $values['position']);
$this->response->json(array('result' => $result));
} else {
throw new AccessForbiddenException();
}
}
}
@@ -0,0 +1,45 @@
<?php
namespace Kanboard\Controller;
/**
* Class SubtaskConverterController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class SubtaskConverterController extends BaseController
{
public function show()
{
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$this->response->html($this->template->render('subtask_converter/show', array(
'subtask' => $subtask,
'task' => $task,
)));
}
public function save()
{
$task = $this->getTask();
$subtask = $this->getSubtask($task);
if (! $this->helper->projectRole->canCreateTaskInColumn($task['project_id'], $task['column_id'])) {
$this->flash->failure(t('You cannot create tasks in this column.'));
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', ['task_id' => $task['id']]));
return;
}
$task_id = $this->subtaskTaskConversionModel->convertToTask($task['project_id'], $subtask['id']);
if ($task_id !== false) {
$this->flash->success(t('Subtask converted to task successfully.'));
} else {
$this->flash->failure(t('Unable to convert the subtask.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task_id)), true);
}
}
@@ -0,0 +1,64 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Model\SubtaskModel;
/**
* Subtask Restriction
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class SubtaskRestrictionController extends BaseController
{
/**
* Show popup
*
* @access public
*/
public function show()
{
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$subtaskInProgress = $this->subtaskStatusModel->getSubtaskInProgress($this->userSession->getId());
$this->response->html($this->template->render('subtask_restriction/show', array(
'status_list' => array(
SubtaskModel::STATUS_TODO => 'Todo',
SubtaskModel::STATUS_DONE => 'Done',
),
'subtask_inprogress' => $subtaskInProgress,
'subtask' => $subtask,
'task' => $task,
)));
}
/**
* Change status of the in progress subtask and the other subtask
*
* @access public
*/
public function save()
{
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$values = $this->request->getValues();
if (! empty($values)) {
// Change status of the previous "in progress" subtask
$this->subtaskModel->update(array(
'id' => $values['id'],
'status' => $values['status'],
));
// Set the current subtask to "in progress"
$this->subtaskModel->update(array(
'id' => $subtask['id'],
'status' => SubtaskModel::STATUS_INPROGRESS,
));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace Kanboard\Controller;
/**
* Subtask Status
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class SubtaskStatusController extends BaseController
{
/**
* Change status to the next status: Toto -> In Progress -> Done
*
* @access public
*/
public function change()
{
$this->checkReusableGETCSRFParam();
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$fragment = $this->request->getStringParam('fragment');
$status = $this->subtaskStatusModel->toggleStatus($subtask['id']);
$subtask['status'] = $status;
if ($fragment === 'table') {
$html = $this->renderTable($task);
} elseif ($fragment === 'rows') {
$html = $this->renderRows($task);
} else {
$html = $this->helper->subtask->renderToggleStatus($task, $subtask);
}
$this->response->html($html);
}
/**
* Start/stop timer for subtasks
*
* @access public
*/
public function timer()
{
$this->checkReusableGETCSRFParam();
$task = $this->getTask();
$subtask = $this->getSubtask($task);
$timer = $this->request->getStringParam('timer');
if ($timer === 'start') {
$this->subtaskTimeTrackingModel->logStartTime($subtask['id'], $this->userSession->getId());
} elseif ($timer === 'stop') {
$this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $this->userSession->getId());
$this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']);
}
$this->response->html($this->template->render('subtask/timer', array(
'task' => $task,
'subtask' => $this->subtaskModel->getByIdWithDetails($subtask['id']),
)));
}
/**
* Render table
*
* @access protected
* @param array $task
* @return string
*/
protected function renderTable(array $task)
{
return $this->template->render('subtask/table', array(
'task' => $task,
'subtasks' => $this->subtaskModel->getAll($task['id']),
'editable' => true,
));
}
/**
* Render task list rows
*
* @access protected
* @param array $task
* @return string
*/
protected function renderRows(array $task)
{
$userId = $this->request->getIntegerParam('user_id');
if ($userId > 0) {
$task['subtasks'] = $this->subtaskModel->getAllByTaskIdsAndAssignee(array($task['id']), $userId);
} else {
$task['subtasks'] = $this->subtaskModel->getAll($task['id']);
}
return $this->template->render('task_list/task_subtasks', array(
'task' => $task,
'user_id' => $userId,
));
}
}
+219
View File
@@ -0,0 +1,219 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Swimlanes Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class SwimlaneController extends BaseController
{
/**
* List of swimlanes for a given project
*
* @access public
*/
public function index()
{
$project = $this->getProject();
$swimlanes = $this->swimlaneModel->getAllWithTaskCount($project['id']);
$this->response->html($this->helper->layout->project('swimlane/index', array(
'active_swimlanes' => $swimlanes['active'],
'inactive_swimlanes' => $swimlanes['inactive'],
'project' => $project,
'title' => t('Swimlanes')
)));
}
/**
* Create a new swimlane
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function create(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->template->render('swimlane/create', array(
'values' => $values + array('project_id' => $project['id']),
'errors' => $errors,
'project' => $project,
)));
}
/**
* Validate and save a new swimlane
*
* @access public
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
list($valid, $errors) = $this->swimlaneValidator->validateCreation($values);
if ($valid) {
if ($this->swimlaneModel->create($project['id'], $values['name'], $values['description'], $values['task_limit']) !== false) {
$this->flash->success(t('Your swimlane has been created successfully.'));
$this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id'])), true);
return;
} else {
$errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
}
}
$this->create($values, $errors);
}
/**
* Edit a swimlane (display the form)
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$swimlane = $this->getSwimlane($project);
$this->response->html($this->helper->layout->project('swimlane/edit', array(
'values' => empty($values) ? $swimlane : $values,
'errors' => $errors,
'project' => $project,
)));
}
/**
* Edit a swimlane (validate the form and update the database)
*
* @access public
*/
public function update()
{
$project = $this->getProject();
$swimlane = $this->getSwimlane($project);
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
$values['id'] = $swimlane['id'];
list($valid, $errors) = $this->swimlaneValidator->validateModification($values);
if ($valid) {
if ($this->swimlaneModel->update($values['id'], $values)) {
$this->flash->success(t('Swimlane updated successfully.'));
return $this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id'])));
} else {
$errors = array('name' => array(t('Another swimlane with the same name exists in the project')));
}
}
return $this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a swimlane
*
* @access public
*/
public function confirm()
{
$project = $this->getProject();
$swimlane = $this->getSwimlane($project);
$this->response->html($this->helper->layout->project('swimlane/remove', array(
'project' => $project,
'swimlane' => $swimlane,
)));
}
/**
* Remove a swimlane
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$project = $this->getProject();
$swimlane = $this->getSwimlane($project);
if ($this->swimlaneModel->remove($project['id'], $swimlane['id'])) {
$this->flash->success(t('Swimlane removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this swimlane.'));
}
$this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id'])));
}
/**
* Disable a swimlane
*
* @access public
*/
public function disable()
{
$this->checkCSRFParam();
$project = $this->getProject();
$swimlane = $this->getSwimlane($project);
if ($this->swimlaneModel->disable($project['id'], $swimlane['id'])) {
$this->flash->success(t('Swimlane updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this swimlane.'));
}
$this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id'])));
}
/**
* Enable a swimlane
*
* @access public
*/
public function enable()
{
$this->checkCSRFParam();
$project = $this->getProject();
$swimlane = $this->getSwimlane($project);
if ($this->swimlaneModel->enable($project['id'], $swimlane['id'])) {
$this->flash->success(t('Swimlane updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this swimlane.'));
}
$this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id'])));
}
/**
* Move swimlane position
*
* @access public
*/
public function move()
{
$this->checkReusableGETCSRFParam();
$project = $this->getProject();
$values = $this->request->getJson();
if (! empty($values) && isset($values['swimlane_id']) && isset($values['position'])) {
$result = $this->swimlaneModel->changePosition($project['id'], $values['swimlane_id'], $values['position']);
$this->response->json(array('result' => $result));
} else {
throw new AccessForbiddenException();
}
}
}
+124
View File
@@ -0,0 +1,124 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Class TagController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TagController extends BaseController
{
public function index()
{
$this->response->html($this->helper->layout->config('tag/index', array(
'tags' => $this->tagModel->getAllByProject(0),
'colors' => $this->colorModel->getList(),
'title' => t('Settings').' &gt; '.t('Global tags management'),
)));
}
public function create(array $values = array(), array $errors = array())
{
if (empty($values)) {
$values['project_id'] = 0;
}
$this->response->html($this->template->render('tag/create', array(
'values' => $values,
'colors' => $this->colorModel->getList(),
'errors' => $errors,
)));
}
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->tagValidator->validateCreation($values);
if ($valid) {
if ($this->tagModel->create(0, $values['name'], $values['color_id']) > 0) {
$this->flash->success(t('Tag created successfully.'));
} else {
$this->flash->failure(t('Unable to create this tag.'));
}
$this->response->redirect($this->helper->url->to('TagController', 'index'));
} else {
$this->create($values, $errors);
}
}
public function edit(array $values = array(), array $errors = array())
{
$tag_id = $this->request->getIntegerParam('tag_id');
$tag = $this->tagModel->getById($tag_id);
if (empty($values)) {
$values = $tag;
}
$this->response->html($this->template->render('tag/edit', array(
'tag' => $tag,
'values' => $values,
'colors' => $this->colorModel->getList(),
'errors' => $errors,
)));
}
public function update()
{
$tag_id = $this->request->getIntegerParam('tag_id');
$tag = $this->tagModel->getById($tag_id);
$values = $this->request->getValues();
list($valid, $errors) = $this->tagValidator->validateModification($values);
if ($tag['project_id'] != 0) {
throw new AccessForbiddenException();
}
if ($valid) {
if ($this->tagModel->update($values['id'], $values['name'], $values['color_id'])) {
$this->flash->success(t('Tag updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this tag.'));
}
$this->response->redirect($this->helper->url->to('TagController', 'index'));
} else {
$this->edit($values, $errors);
}
}
public function confirm()
{
$tag_id = $this->request->getIntegerParam('tag_id');
$tag = $this->tagModel->getById($tag_id);
$this->response->html($this->template->render('tag/remove', array(
'tag' => $tag,
)));
}
public function remove()
{
$this->checkCSRFParam();
$tag_id = $this->request->getIntegerParam('tag_id');
$tag = $this->tagModel->getById($tag_id);
if ($tag['project_id'] != 0) {
throw new AccessForbiddenException();
}
if ($this->tagModel->remove($tag_id)) {
$this->flash->success(t('Tag removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this tag.'));
}
$this->response->redirect($this->helper->url->to('TagController', 'index'));
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
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\Model\TaskModel;
/**
* Task Ajax Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskAjaxController extends BaseController
{
/**
* Task auto-completion (Ajax)
*
*/
public function autocomplete()
{
$search = $this->request->getStringParam('term');
$project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
$exclude_task_ids = $this->request->getStringParam('exclude_task_ids');
$exclude_task_ids = array_filter(array_map('trim', explode(',', $exclude_task_ids)));
if (empty($project_ids)) {
$this->response->json(array());
} else {
$filter = $this->taskQuery->withFilter(new TaskProjectsFilter($project_ids));
if (! empty($exclude_task_ids)) {
$filter->withFilter(new TaskIdExclusionFilter($exclude_task_ids));
}
if (ctype_digit((string) $search)) {
$filter->withFilter(new TaskIdFilter($search));
} else {
$filter->withFilter(new TaskTitleFilter($search));
}
$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));
}
}
/**
* Task edit preview
*/
public function preview()
{
$text = $this->request->getRawValue('text');
if (empty($text)) {
$this->response->json(array());
} else {
$preview = $this->helper->text->markdown($text);
$this->response->json(array($preview));
}
}
}
@@ -0,0 +1,102 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
class TaskBulkChangePropertyController extends BaseController
{
public function show(array $values = [], array $errors = [])
{
$project = $this->getProject();
if (empty($values)) {
$values['task_ids'] = $this->request->getStringParam('task_ids');
}
$this->response->html($this->template->render('task_bulk_change_property/show', [
'project' => $project,
'values' => $values,
'errors' => $errors,
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']),
'categories_list' => $this->categoryModel->getList($project['id']),
'internallink_list' => $this->linkModel->getList(),
]));
}
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$taskIDs = explode(',', $values['task_ids']);
foreach ($taskIDs as $taskID) {
if ($this->taskFinderModel->getProjectId($taskID) != $project['id']) {
throw new AccessForbiddenException();
}
$changes = [];
if (isset($values['change_color']) && $values['change_color'] == 1) {
$changes['color_id'] = $values['color_id'];
}
if (isset($values['change_assignee']) && $values['change_assignee'] == 1) {
$changes['owner_id'] = $values['owner_id'];
}
if (isset($values['change_priority']) && $values['change_priority'] == 1) {
$changes['priority'] = $values['priority'];
}
if (isset($values['change_category']) && $values['change_category'] == 1) {
$changes['category_id'] = $values['category_id'];
}
if (isset($values['change_tags']) && $values['change_tags'] == 1) {
$changes['tags'] = $values['tags'];
if (isset($values['change_tags_only_add_new']) && $values['change_tags_only_add_new'] == 1) {
$changes['tags_only_add_new'] = $values['change_tags_only_add_new'];
}
}
if (isset($values['change_due_date']) && $values['change_due_date'] == 1) {
$changes['date_due'] = $values['date_due'];
}
if (isset($values['change_start_date']) && $values['change_start_date'] == 1) {
$changes['date_started'] = $values['date_started'];
}
if (isset($values['change_estimated_time']) && $values['change_estimated_time'] == 1) {
$changes['time_estimated'] = $values['time_estimated'];
}
if (isset($values['change_spent_time']) && $values['change_spent_time'] == 1) {
$changes['time_spent'] = $values['time_spent'];
}
if (isset($values['change_score']) && $values['change_score'] == 1) {
$changes['score'] = $values['score'];
}
if (isset($values['change_internallink']) && $values['change_internallink'] == 1) {
$this->taskLinkModel->create($taskID, $values['opposite_task_id'], $values['link_id']);
}
if (isset($values['change_internallink_remove']) && $values['change_internallink_remove'] == 1) {
$task_link_ids = $this->taskLinkModel->getAll($taskID);
foreach ($task_link_ids as $task_link_id) {
$this->taskLinkModel->remove($task_link_id['id']);
}
}
if (! empty($changes)) {
$changes['id'] = $taskID;
$this->taskModificationModel->update($changes);
}
}
$this->response->redirect($this->helper->url->to('TaskListController', 'show', ['project_id' => $project['id']]), true);
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace Kanboard\Controller;
/**
* Class TaskBulkController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskBulkController extends BaseController
{
/**
* Show the form
*
* @param array $values
* @param array $errors
*/
public function show(array $values = array(), array $errors = array())
{
$project = $this->getProject();
if (empty($values)) {
$values = array(
'swimlane_id' => $this->request->getIntegerParam('swimlane_id'),
'column_id' => $this->request->getIntegerParam('column_id'),
'project_id' => $project['id'],
);
}
$this->response->html($this->template->render('task_bulk/show', array(
'project' => $project,
'values' => $values,
'errors' => $errors,
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private'] == 1),
'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($project['id']),
'task_description_templates' => $this->predefinedTaskDescriptionModel->getList($project['id']),
)));
}
/**
* Save all tasks in the database
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
list($valid, $errors) = $this->taskValidator->validateBulkCreation($values);
if (! $valid) {
$this->show($values, $errors);
} elseif (! $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',
'show',
array('project_id' => $project['id']),
'swimlane-'. $values['swimlane_id']
), true);
}
}
/**
* Create all tasks
*
* @param array $project
* @param array $values
*/
protected function createTasks(array $project, array $values)
{
$tasks = preg_split('/\r\n|[\r\n]/', $values['tasks']);
foreach ($tasks as $title) {
$title = trim($title);
if (! empty($title)) {
$this->taskCreationModel->create(array(
'title' => $title,
'column_id' => $values['column_id'],
'swimlane_id' => $values['swimlane_id'],
'category_id' => empty($values['category_id']) ? 0 : $values['category_id'],
'owner_id' => empty($values['owner_id']) ? 0 : $values['owner_id'],
'color_id' => $values['color_id'],
'project_id' => $project['id'],
'description' => $this->getTaskDescription($project, $values),
'tags' => $values['tags'],
'priority' => $values['priority'],
'score' => $values['score'],
'time_estimated' => $values['time_estimated'],
'date_due' => $values['date_due'],
));
}
}
}
protected function getTaskDescription(array $project, array $values)
{
if (empty($values['task_description_template_id'])) {
return '';
}
return $this->predefinedTaskDescriptionModel->getDescriptionById($project['id'], $values['task_description_template_id']);
}
}
@@ -0,0 +1,44 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
class TaskBulkMoveColumnController extends BaseController
{
public function show(array $values = [], array $errors = [])
{
$project = $this->getProject();
if (empty($values)) {
$values['task_ids'] = $this->request->getStringParam('task_ids');
}
$this->response->html($this->template->render('task_bulk_move_column/show', [
'project' => $project,
'values' => $values,
'errors' => $errors,
'columns' => $this->columnModel->getList($project['id']),
'swimlanes' => $this->swimlaneModel->getList($project['id'], false, true),
]));
}
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$taskIDs = explode(',', $values['task_ids']);
foreach ($taskIDs as $taskID) {
$task = $this->taskFinderModel->getById($taskID);
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->moveBottom($project['id'], $taskID, $values['swimlane_id'], $values['column_id']);
}
$this->response->redirect($this->helper->url->to('TaskListController', 'show', ['project_id' => $project['id']]), true);
}
}
+187
View File
@@ -0,0 +1,187 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* Task Creation Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskCreationController extends BaseController
{
/**
* Display a form to create a new task
*
* @access public
* @param array $values
* @param array $errors
* @throws PageNotFoundException
*/
public function show(array $values = array(), $screenshot = '', array $files = array(), array $errors = array())
{
$project = $this->getProject();
$swimlanesList = $this->swimlaneModel->getList($project['id'], false, true);
$values += $this->prepareValues($project['is_private'], $swimlanesList);
$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,
'errors' => $errors,
'values' => $values + array('project_id' => $project['id']),
'columns_list' => $this->columnModel->getList($project['id']),
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private'] == 1),
'categories_list' => $this->categoryModel->getList($project['id']),
'swimlanes_list' => $swimlanesList,
'screenshot' => $screenshot,
'files' => $files,
)));
}
/**
* Validate and save a new task
*
* @access public
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$values['project_id'] = $project['id'];
$files = $this->request->getFileInfo('files');
$screenshot = null;
if (array_key_exists('screenshot', $values)) {
$screenshot = $values['screenshot'];
unset($values['screenshot']);
}
list($valid, $errors) = $this->taskValidator->validateCreation($values);
if (! $valid) {
$this->flash->failure(t('Unable to create your task.'));
$this->show($values, $screenshot, $files, $errors);
} elseif (! $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);
if ($task_id === 0) {
$this->flash->failure(t('Unable to create this task.'));
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
return;
}
if ($screenshot) {
$this->taskFileModel->uploadScreenshot($task_id, $screenshot);
}
if (isset($files['name'][0]) && $files['name'][0] !== '') {
$filesUploaded = $this->taskFileModel->uploadFiles($task_id, $files);
if (! $filesUploaded) {
$this->flash->failure(t('Unable to upload files, check the permissions of your data folder.'));
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', ['project_id' => $project['id']]), true);
return;
}
}
$this->flash->success(t('Task created successfully.'));
$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) {
if (! $this->projectPermissionModel->isUserAllowed($project_id, $this->userSession->getId())) {
throw new AccessForbiddenException();
}
$this->taskProjectDuplicationModel->duplicateToProject($values['task_id'], $project_id);
}
}
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
}
/**
* 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['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,
'column_id' => $values['column_id'],
'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 bool $isPrivateProject
* @param array $swimlanesList
* @return array
*/
protected function prepareValues($isPrivateProject, array $swimlanesList)
{
$values = array(
'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanesList)),
'column_id' => $this->request->getIntegerParam('column_id'),
'color_id' => $this->colorModel->getDefaultColor(),
);
if ($isPrivateProject) {
$values['owner_id'] = $this->userSession->getId();
}
return $values;
}
/**
* 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'])
)));
}
}
@@ -0,0 +1,159 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Task Duplication controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskDuplicationController extends BaseController
{
/**
* Duplicate a task
*
* @access public
*/
public function duplicate()
{
$task = $this->getTask();
if ($this->request->getStringParam('confirmation') === 'yes') {
$this->checkCSRFParam();
$task_id = $this->taskDuplicationModel->duplicate($task['id']);
if ($task_id > 0) {
$this->flash->success(t('Task created successfully.'));
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task_id)));
} else {
$this->flash->failure(t('Unable to create this task.'));
return $this->response->redirect($this->helper->url->to('TaskDuplicationController', 'duplicate', array('task_id' => $task['id'])), true);
}
}
return $this->response->html($this->template->render('task_duplication/duplicate', array(
'task' => $task,
)));
}
/**
* Move a task to another project
*
* @access public
*/
public function move()
{
$task = $this->getTask();
if ($this->request->isPost()) {
$values = $this->request->getValues();
list($valid, ) = $this->taskValidator->validateProjectModification($values);
if ($valid) {
if (! $this->projectPermissionModel->isUserAllowed($values['project_id'], $this->userSession->getId())) {
throw new AccessForbiddenException();
}
if ($this->taskProjectMoveModel->moveToProject(
$task['id'],
$values['project_id'],
$values['swimlane_id'],
$values['column_id'],
$values['category_id'],
$values['owner_id']
)) {
$this->flash->success(t('Task updated successfully.'));
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])));
}
}
$this->flash->failure(t('Unable to update your task.'));
}
return $this->chooseDestination($task, 'task_duplication/move');
}
/**
* Duplicate a task to another project
*
* @access public
*/
public function copy()
{
$task = $this->getTask();
if ($this->request->isPost()) {
$values = $this->request->getValues();
list($valid, ) = $this->taskValidator->validateProjectModification($values);
if ($valid) {
if (! $this->projectPermissionModel->isUserAllowed($values['project_id'], $this->userSession->getId())) {
throw new AccessForbiddenException();
}
$task_id = $this->taskProjectDuplicationModel->duplicateToProject(
$task['id'],
$values['project_id'],
$values['swimlane_id'],
$values['column_id'],
$values['category_id'],
$values['owner_id']
);
if ($task_id > 0) {
$this->flash->success(t('Task created successfully.'));
return $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $task['project_id'])), true);
}
}
$this->flash->failure(t('Unable to create your task.'));
}
return $this->chooseDestination($task, 'task_duplication/copy');
}
/**
* Choose destination when move/copy task to another project
*
* @access private
* @param array $task
* @param string $template
*/
private function chooseDestination(array $task, $template)
{
$values = array();
$projects_list = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
unset($projects_list[$task['project_id']]);
if (! empty($projects_list)) {
$dst_project_id = $this->request->getIntegerParam('dst_project_id', key($projects_list));
$swimlanes_list = $this->swimlaneModel->getList($dst_project_id, false, true);
$columns_list = $this->columnModel->getList($dst_project_id);
$categories_list = $this->categoryModel->getList($dst_project_id);
$users_list = $this->projectUserRoleModel->getAssignableUsersList($dst_project_id);
$values = $this->taskDuplicationModel->checkDestinationProjectValues($task);
$values['project_id'] = $dst_project_id;
} else {
$swimlanes_list = array();
$columns_list = array();
$categories_list = array();
$users_list = array();
}
$this->response->html($this->template->render($template, array(
'values' => $values,
'task' => $task,
'projects_list' => $projects_list,
'swimlanes_list' => $swimlanes_list,
'columns_list' => $columns_list,
'categories_list' => $categories_list,
'users_list' => $users_list,
)));
}
}
@@ -0,0 +1,184 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
/**
* Task External Link Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskExternalLinkController extends BaseController
{
/**
* First creation form
*
* @access public
* @param array $values
* @param array $errors
* @throws PageNotFoundException
* @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function find(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$this->response->html($this->template->render('task_external_link/find', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'types' => $this->externalLinkManager->getTypes(),
)));
}
/**
* Second creation form
*
* @access public
*/
public function create()
{
$task = $this->getTask();
$values = $this->request->getValues();
try {
$provider = $this->externalLinkManager->setUserInput($values)->find();
$link = $provider->getLink();
$this->response->html($this->template->render('task_external_link/create', array(
'values' => array(
'title' => $link->getTitle(),
'url' => $link->getUrl(),
'link_type' => $provider->getType(),
),
'dependencies' => $provider->getDependencies(),
'errors' => array(),
'task' => $task,
)));
} catch (ExternalLinkProviderNotFound $e) {
$errors = array('text' => array(t('Unable to fetch link information.')));
$this->find($values, $errors);
}
}
/**
* Save link
*
* @access public
*/
public function save()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['task_id'] = $task['id'];
list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
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'])), 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,
)));
}
}
/**
* Edit form
*
* @access public
* @param array $values
* @param array $errors
* @throws ExternalLinkProviderNotFound
* @throws PageNotFoundException
* @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$link = $this->getExternalTaskLink($task);
$provider = $this->externalLinkManager->getProvider($link['link_type']);
$this->response->html($this->template->render('task_external_link/edit', array(
'values' => empty($values) ? $link : $values,
'errors' => $errors,
'task' => $task,
'link' => $link,
'dependencies' => $provider->getDependencies(),
)));
}
/**
* Update link
*
* @access public
*/
public function update()
{
$task = $this->getTask();
$link = $this->getExternalTaskLink($task);
$values = $this->request->getValues();
$values['id'] = $link['id'];
$values['task_id'] = $link['task_id'];
list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
if ($valid && $this->taskExternalLinkModel->update($values)) {
$this->flash->success(t('Link updated successfully.'));
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
return $this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a link
*
* @access public
*/
public function confirm()
{
$task = $this->getTask();
$link = $this->getExternalTaskLink($task);
$this->response->html($this->template->render('task_external_link/remove', array(
'link' => $link,
'task' => $task,
)));
}
/**
* Remove a link
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$task = $this->getTask();
$link = $this->getExternalTaskLink($task);
if ($this->taskExternalLinkModel->remove($link['id'])) {
$this->flash->success(t('Link removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])));
}
}
+108
View File
@@ -0,0 +1,108 @@
<?php
namespace Kanboard\Controller;
/**
* Task File Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskFileController extends BaseController
{
/**
* Screenshot
*
* @access public
*/
public function screenshot()
{
$task = $this->getTask();
if ($this->request->isPost() && $this->taskFileModel->uploadScreenshot($task['id'], $this->request->getValue('screenshot')) !== false) {
$this->flash->success(t('Screenshot uploaded successfully.'));
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
return $this->response->html($this->template->render('task_file/screenshot', array(
'task' => $task,
)));
}
/**
* File upload form
*
* @access public
*/
public function create()
{
$task = $this->getTask();
$this->response->html($this->template->render('task_file/create', array(
'task' => $task,
'max_size' => get_upload_max_size(),
)));
}
/**
* File upload (save files)
*
* @access public
*/
public function save()
{
$this->checkReusableCSRFParam();
$task = $this->getTask();
$result = $this->taskFileModel->uploadFiles($task['id'], $this->request->getFileInfo('files'));
if ($this->request->isAjax()) {
if (! $result) {
$this->response->json(array('message' => t('Unable to upload files, check the permissions of your data folder.')), 500);
} else {
$this->response->json(array('message' => 'OK'));
}
} else {
if (! $result) {
$this->flash->failure(t('Unable to upload files, check the permissions of your data folder.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
}
/**
* Remove a file
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$task = $this->getTask();
$file = $this->getFile();
if ($file['task_id'] == $task['id'] && $this->taskFileModel->remove($file['id'])) {
$this->flash->success(t('File removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this file.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])));
}
/**
* Confirmation dialog before removing a file
*
* @access public
*/
public function confirm()
{
$task = $this->getTask();
$file = $this->getFile();
$this->response->html($this->template->render('task_file/remove', array(
'task' => $task,
'file' => $file,
)));
}
}
+76
View File
@@ -0,0 +1,76 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Csv;
use Kanboard\Import\TaskImport;
/**
* Task Import controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskImportController extends BaseController
{
/**
* Upload the file and ask settings
*
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function show(array $values = array(), array $errors = array())
{
$project = $this->getProject();
$this->response->html($this->template->render('task_import/show', array(
'project' => $project,
'values' => $values,
'errors' => $errors,
'max_size' => get_upload_max_size(),
'delimiters' => Csv::getDelimiters(),
'enclosures' => Csv::getEnclosures(),
)));
}
/**
* Process CSV file
*/
public function save()
{
$project = $this->getProject();
$values = $this->request->getValues();
$filename = $this->request->getFilePath('file');
if (! file_exists($filename)) {
$this->show($values, array('file' => array(t('Unable to read your file'))));
} else {
$taskImport = new TaskImport($this->container);
$taskImport->setProjectId($project['id']);
$csv = new Csv($values['delimiter'], $values['enclosure']);
$csv->setColumnMapping($taskImport->getColumnMapping());
$csv->read($filename, array($taskImport, 'importTask'));
if ($taskImport->getNumberOfImportedTasks() > 0) {
$this->flash->success(t('%d task(s) have been imported successfully.', $taskImport->getNumberOfImportedTasks()));
} else {
$this->flash->failure(t('Nothing has been imported!'));
}
$this->response->redirect($this->helper->url->to('TaskImportController', 'show', array('project_id' => $project['id'])), true);
}
}
/**
* Generate template
*
*/
public function template()
{
$taskImport = new TaskImport($this->container);
$this->response->withFileDownload('tasks.csv');
$this->response->csv(array($taskImport->getColumnMapping()));
}
}
@@ -0,0 +1,183 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
/**
* TaskInternalLink Controller
*
* @package Kanboard\Controller
* @author Olivier Maridat
* @author Frederic Guillot
*/
class TaskInternalLinkController extends BaseController
{
/**
* Creation form
*
* @access public
* @param array $values
* @param array $errors
* @throws PageNotFoundException
* @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
if (empty($values)) {
$values['another_tasklink'] = $this->request->getIntegerParam('another_tasklink', 0);
$values = $this->hook->merge('controller:tasklink:form:default', $values, array('default_values' => $values));
}
$this->response->html($this->template->render('task_internal_link/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'labels' => $this->linkModel->getList(0, false),
)));
}
/**
* Validation and creation
*
* @access public
*/
public function save()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['task_id'] = $task['id'];
list($valid, $errors) = $this->taskLinkValidator->validateCreation($values);
if ($valid) {
$opposite_task = $this->taskFinderModel->getById($values['opposite_task_id']);
if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) {
throw new AccessForbiddenException();
}
if ($this->taskLinkModel->create($values['task_id'], $values['opposite_task_id'], $values['link_id']) !== false) {
$this->flash->success(t('Link added successfully.'));
if (isset($values['another_tasklink']) && $values['another_tasklink'] == 1) {
return $this->create(array(
'project_id' => $task['project_id'],
'task_id' => $task['id'],
'link_id' => $values['link_id'],
'another_tasklink' => 1
));
}
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
$errors = array('title' => array(t('The exact same link already exists')));
$this->flash->failure(t('Unable to create your link.'));
}
return $this->create($values, $errors);
}
/**
* Edit form
*
* @access public
* @param array $values
* @param array $errors
* @throws PageNotFoundException
* @throws \Kanboard\Core\Controller\AccessForbiddenException
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$task_link = $this->getInternalTaskLink($task);
if (empty($values)) {
$opposite_task = $this->taskFinderModel->getById($task_link['opposite_task_id']);
$values = $task_link;
$values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title'];
}
$this->response->html($this->template->render('task_internal_link/edit', array(
'values' => $values,
'errors' => $errors,
'task_link' => $task_link,
'task' => $task,
'labels' => $this->linkModel->getList(0, false)
)));
}
/**
* Validation and update
*
* @access public
*/
public function update()
{
$task = $this->getTask();
$task_link = $this->getInternalTaskLink($task);
$values = $this->request->getValues();
$values['task_id'] = $task['id'];
$values['id'] = $task_link['id'];
list($valid, $errors) = $this->taskLinkValidator->validateModification($values);
if ($valid) {
$opposite_task = $this->taskFinderModel->getById($values['opposite_task_id']);
if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) {
throw new AccessForbiddenException();
}
if ($this->taskLinkModel->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
$this->flash->success(t('Link updated successfully.'));
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])).'#links');
}
$this->flash->failure(t('Unable to update your link.'));
}
return $this->edit($values, $errors);
}
/**
* Confirmation dialog before removing a link
*
* @access public
*/
public function confirm()
{
$task = $this->getTask();
$link = $this->getInternalTaskLink($task);
$this->response->html($this->template->render('task_internal_link/remove', array(
'link' => $link,
'task' => $task,
)));
}
/**
* Remove a link
*
* @access public
*/
public function remove()
{
$this->checkCSRFParam();
$task = $this->getTask();
$link = $this->getInternalTaskLink($task);
if ($this->taskLinkModel->remove($link['id'])) {
$this->flash->success(t('Link removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this link.'));
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])));
}
}
+71
View File
@@ -0,0 +1,71 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\TaskModel;
/**
* Task List Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskListController extends BaseController
{
/**
* Show list view for projects
*
* @access public
*/
public function show()
{
$project = $this->getProject();
$search = $this->helper->projectHeader->getSearchQuery($project);
if ($this->request->getIntegerParam('show_subtasks') !== 0 ||
$this->request->getIntegerParam('hide_subtasks') !== 0 ||
$this->request->getStringParam('direction') !== '' ||
$this->request->getStringParam('order') !== '') {
$this->checkReusableGETCSRFParam();
}
if ($this->request->getIntegerParam('show_subtasks')) {
session_set('subtaskListToggle', true);
} elseif ($this->request->getIntegerParam('hide_subtasks')) {
session_set('subtaskListToggle', false);
}
if ($this->userSession->hasSubtaskListActivated()) {
$formatter = $this->taskListSubtaskFormatter;
} else {
$formatter = $this->taskListFormatter;
}
list($order, $direction) = $this->userSession->getListOrder($project['id']);
$direction = $this->request->getStringParam('direction', $direction);
$order = $this->request->getStringParam('order', $order);
$this->userSession->setListOrder($project['id'], $order, $direction);
$paginator = $this->paginator
->setUrl('TaskListController', 'show', array('project_id' => $project['id'], 'csrf_token' => $this->token->getReusableCSRFToken()))
->setMax(30)
->setOrder($order)
->setDirection($direction)
->setFormatter($formatter)
->setQuery(
$this->taskLexer
->build($search)
->withFilter(new TaskProjectFilter($project['id']))
->getQuery()
)
->calculate();
$this->response->html($this->helper->layout->app('task_list/listing', array(
'project' => $project,
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'paginator' => $paginator,
)));
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace Kanboard\Controller;
/**
* Class TaskMailController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskMailController extends BaseController
{
public function create(array $values = array(), array $errors = array())
{
$task = $this->getTask();
$this->response->html($this->helper->layout->task('task_mail/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'members' => $this->projectPermissionModel->getMembersWithEmail($task['project_id']),
)));
}
public function send()
{
$task = $this->getTask();
$values = $this->request->getValues();
list($valid, $errors) = $this->taskValidator->validateEmailCreation($values);
if ($valid) {
$this->sendByEmail($values, $task);
$this->flash->success(t('Task sent by email successfully.'));
$this->commentModel->create(array(
'comment' => t('This task was sent by email to "%s" with subject "%s".', $values['emails'], $values['subject']),
'user_id' => $this->userSession->getId(),
'task_id' => $task['id'],
));
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true);
} else {
$this->create($values, $errors);
}
}
protected function sendByEmail(array $values, array $task)
{
$emails = explode_csv_field($values['emails']);
$html = $this->template->render('task_mail/email', array('task' => $task));
foreach ($emails as $email) {
$this->emailClient->send(
$email,
$email,
$values['subject'],
$html
);
}
}
}
@@ -0,0 +1,182 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\ExternalTask\AccessForbiddenException as ExternalTaskAccessForbiddenException;
use Kanboard\Core\ExternalTask\ExternalTaskException;
/**
* Task Modification controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskModificationController extends BaseController
{
public function assignToMe()
{
$this->checkReusableGETCSRFParam();
$task = $this->getTask();
$values = ['id' => $task['id'], 'owner_id' => $this->userSession->getId()];
if (! $this->helper->projectRole->canUpdateTask($task)) {
throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.'));
}
if (! $this->helper->projectRole->canChangeAssignee($task)) {
throw new AccessForbiddenException(t('You are not allowed to change the assignee.'));
}
$this->taskModificationModel->update($values);
$this->redirectAfterQuickAction($task);
}
/**
* Set the start date automatically
*
* @access public
*/
public function start()
{
$this->checkReusableGETCSRFParam();
$task = $this->getTask();
$values = ['id' => $task['id'], 'date_started' => time()];
if (! $this->helper->projectRole->canUpdateTask($task)) {
throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.'));
}
$this->taskModificationModel->update($values);
$this->redirectAfterQuickAction($task);
}
protected function redirectAfterQuickAction(array $task)
{
switch ($this->request->getStringParam('redirect')) {
case 'board':
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', ['project_id' => $task['project_id']]));
break;
case 'list':
$this->response->redirect($this->helper->url->to('TaskListController', 'show', ['project_id' => $task['project_id']]));
break;
case 'dashboard':
$this->response->redirect($this->helper->url->to('DashboardController', 'show', [], 'project-tasks-'.$task['project_id']));
break;
case 'dashboard-tasks':
$this->response->redirect($this->helper->url->to('DashboardController', 'tasks', ['user_id' => $this->userSession->getId()]));
break;
default:
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', ['task_id' => $task['id']]));
}
}
/**
* Display a form to edit a task
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
if (! $this->helper->projectRole->canUpdateTask($task)) {
throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.'));
}
$project = $this->projectModel->getById($task['project_id']);
if (empty($values)) {
$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));
$params = array(
'project' => $project,
'values' => $values,
'errors' => $errors,
'task' => $task,
'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'], $task['project_id']);
} 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));
}
}
/**
* Validate and update a task
*
* @access public
*/
public function update()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['id'] = $task['id'];
$values['project_id'] = $task['project_id'];
list($valid, $errors) = $this->taskValidator->validateModification($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('task_id' => $task['id'])), true);
} else {
$this->flash->failure(t('Unable to update your task.'));
$this->edit($values, $errors);
}
}
protected function updateTask(array &$task, array &$values, array &$errors)
{
if (isset($values['owner_id']) && $values['owner_id'] != $task['owner_id'] && !$this->helper->projectRole->canChangeAssignee($task)) {
throw new AccessForbiddenException(t('You are not allowed to change the assignee.'));
}
if (! $this->helper->projectRole->canUpdateTask($task)) {
throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.'));
}
$result = $this->taskModificationModel->update($values);
if ($result && ! empty($task['external_uri'])) {
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;
}
}
@@ -0,0 +1,53 @@
<?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()
{
$this->checkReusableGETCSRFParam();
$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'])));
}
}
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Kanboard\Controller;
/**
* Task Popover
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskPopoverController extends BaseController
{
/**
* Screenshot popover
*
* @access public
*/
public function screenshot()
{
$task = $this->getTask();
$this->response->html($this->template->render('task_file/screenshot', array(
'task' => $task,
)));
}
}
@@ -0,0 +1,66 @@
<?php
namespace Kanboard\Controller;
/**
* Task Recurrence controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskRecurrenceController extends BaseController
{
/**
* Edit recurrence form
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
{
$task = $this->getTask();
if (empty($values)) {
$values = $task;
}
$this->response->html($this->template->render('task_recurrence/edit', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
'recurrence_status_list' => $this->taskRecurrenceModel->getRecurrenceStatusList(),
'recurrence_trigger_list' => $this->taskRecurrenceModel->getRecurrenceTriggerList(),
'recurrence_timeframe_list' => $this->taskRecurrenceModel->getRecurrenceTimeframeList(),
'recurrence_basedate_list' => $this->taskRecurrenceModel->getRecurrenceBasedateList(),
)));
}
/**
* Update recurrence form
*
* @access public
*/
public function update()
{
$task = $this->getTask();
$values = $this->request->getValues();
$values['id'] = $task['id'];
list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values);
if ($valid) {
if ($this->taskModificationModel->update($values)) {
$this->flash->success(t('Task updated successfully.'));
} else {
$this->flash->failure(t('Unable to update your task.'));
}
return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
}
return $this->edit($values, $errors);
}
}
+43
View File
@@ -0,0 +1,43 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
class TaskReorderController extends BaseController
{
public function reorderColumn()
{
$this->checkCSRFParam();
$project = $this->getProject();
if (! $this->helper->user->hasProjectAccess('TaskModificationController', 'update', $project['id'])) {
throw new AccessForbiddenException();
}
$swimlaneID = $this->request->getIntegerParam('swimlane_id');
$columnID = $this->request->getIntegerParam('column_id');
$direction = $this->request->getStringParam('direction');
$sort = $this->request->getStringParam('sort');
switch ($sort) {
case 'id':
$this->taskReorderModel->reorderByTaskId($project['id'], $swimlaneID, $columnID, $direction);
break;
case 'priority':
$this->taskReorderModel->reorderByPriority($project['id'], $swimlaneID, $columnID, $direction);
break;
case 'assignee-priority':
$this->taskReorderModel->reorderByAssigneeAndPriority($project['id'], $swimlaneID, $columnID, $direction);
break;
case 'assignee':
$this->taskReorderModel->reorderByAssignee($project['id'], $swimlaneID, $columnID, $direction);
break;
case 'due-date':
$this->taskReorderModel->reorderByDueDate($project['id'], $swimlaneID, $columnID, $direction);
break;
}
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', ['project_id' => $project['id']]));
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace Kanboard\Controller;
/**
* Task Status controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskStatusController extends BaseController
{
/**
* Close a task
*
* @access public
*/
public function close()
{
$this->changeStatus('close', 'task_status/close', t('Task closed successfully.'), t('Unable to close this task.'));
}
/**
* Open a task
*
* @access public
*/
public function open()
{
$this->changeStatus('open', 'task_status/open', t('Task opened successfully.'), t('Unable to open this task.'));
}
/**
* Common method to change status
*
* @access private
* @param string $method
* @param string $template
* @param string $success_message
* @param string $failure_message
*/
private function changeStatus($method, $template, $success_message, $failure_message)
{
$task = $this->getTask();
if ($this->request->getStringParam('confirmation') === 'yes') {
$this->checkCSRFParam();
if ($this->taskStatusModel->$method($task['id'])) {
$this->flash->success($success_message);
} else {
$this->flash->failure($failure_message);
}
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true);
} else {
$this->response->html($this->template->render($template, array(
'task' => $task,
)));
}
}
}
@@ -0,0 +1,53 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* Class TaskSuppressionController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskSuppressionController extends BaseController
{
/**
* Confirmation dialog box before to remove the task
*/
public function confirm()
{
$task = $this->getTask();
if (! $this->helper->projectRole->canRemoveTask($task)) {
throw new AccessForbiddenException();
}
$this->response->html($this->template->render('task_suppression/remove', array(
'task' => $task,
'redirect' => $this->request->getStringParam('redirect'),
)));
}
/**
* Remove a task
*/
public function remove()
{
$task = $this->getTask();
$this->checkCSRFParam();
if (! $this->helper->projectRole->canRemoveTask($task)) {
throw new AccessForbiddenException();
}
if ($this->taskModel->remove($task['id'])) {
$this->flash->success(t('Task removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this task.'));
}
$redirect = $this->request->getStringParam('redirect') === '';
$this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $task['project_id'])), $redirect);
}
}
+144
View File
@@ -0,0 +1,144 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Core\Security\Role;
use Kanboard\Model\UserMetadataModel;
/**
* Task Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class TaskViewController extends BaseController
{
/**
* Public access (display a task)
*
* @access public
*/
public function readonly()
{
$project = $this->projectModel->getByToken($this->request->getStringParam('token'));
if (empty($project)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
$task = $this->taskFinderModel->getDetails($this->request->getIntegerParam('task_id'));
if (empty($task)) {
throw PageNotFoundException::getInstance()->withoutLayout();
}
if ($task['project_id'] != $project['id']) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
$this->response->html($this->helper->layout->app('task/public', array(
'project' => $project,
'comments' => array_filter($this->commentModel->getAll($task['id']), function ($comment) {
return $comment['visibility'] === Role::APP_USER;
}),
'subtasks' => $this->subtaskModel->getAll($task['id']),
'links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']),
'task' => $task,
'columns_list' => $this->columnModel->getList($task['project_id']),
'colors_list' => $this->colorModel->getList(),
'tags' => $this->taskTagModel->getTagsByTask($task['id']),
'title' => $task['title'],
'no_layout' => true,
'auto_refresh' => true,
'not_editable' => true,
)));
}
/**
* Show a task
*
* @access public
*/
public function show()
{
$task = $this->getTask();
$subtasks = $this->subtaskModel->getAll($task['id']);
$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']),
'files' => $this->taskFileModel->getAllDocuments($task['id']),
'images' => $this->taskFileModel->getAllImages($task['id']),
'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection),
'subtasks' => $subtasks,
'internal_links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']),
'external_links' => $this->taskExternalLinkModel->getAll($task['id']),
'link_label_list' => $this->linkModel->getList(0, false),
'tags' => $this->taskTagModel->getTagsByTask($task['id']),
)));
}
/**
* Display task analytics
*
* @access public
*/
public function analytics()
{
$task = $this->getTask();
$this->response->html($this->helper->layout->task('task/analytics', array(
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'lead_time' => $this->taskAnalyticModel->getLeadTime($task),
'cycle_time' => $this->taskAnalyticModel->getCycleTime($task),
'time_spent_columns' => $this->taskAnalyticModel->getTimeSpentByColumn($task),
'tags' => $this->taskTagModel->getTagsByTask($task['id']),
)));
}
/**
* Display the time tracking details
*
* @access public
*/
public function timetracking()
{
$task = $this->getTask();
$subtask_paginator = $this->paginator
->setUrl('TaskViewController', 'timetracking', array('task_id' => $task['id'], 'pagination' => 'subtasks'))
->setMax(15)
->setOrder('start')
->setDirection('DESC')
->setQuery($this->subtaskTimeTrackingModel->getTaskQuery($task['id']))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
$this->response->html($this->helper->layout->task('task/time_tracking_details', array(
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'subtask_paginator' => $subtask_paginator,
'tags' => $this->taskTagModel->getTagsByTask($task['id']),
)));
}
/**
* Display the task transitions
*
* @access public
*/
public function transitions()
{
$task = $this->getTask();
$this->response->html($this->helper->layout->task('task/transitions', array(
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'transitions' => $this->transitionModel->getAllByTask($task['id']),
'tags' => $this->taskTagModel->getTagsByTask($task['id']),
)));
}
}
+232
View File
@@ -0,0 +1,232 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use PHPQRCode;
/**
* Two Factor Auth controller
*
* @package Kanboard/Controller
* @author Frederic Guillot
*/
class TwoFactorController extends UserViewController
{
/**
* Only the current user can access to 2FA settings
*
* @access private
* @param array $user
* @throws AccessForbiddenException
*/
private function checkCurrentUser(array $user)
{
if ($user['id'] != $this->userSession->getId()) {
throw new AccessForbiddenException();
}
}
/**
* Show form to disable/enable 2FA
*
* @access public
*/
public function index()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
session_remove('twoFactorSecret');
$this->response->html($this->helper->layout->user('twofactor/index', array(
'user' => $user,
'provider' => $this->authenticationManager->getPostAuthenticationProvider()->getName(),
)));
}
/**
* Show page with secret and test form
*
* @access public
*/
public function show()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$label = $user['email'] ?: $user['username'];
$provider = $this->authenticationManager->getPostAuthenticationProvider();
if (! session_exists('twoFactorSecret')) {
$provider->generateSecret();
$provider->beforeCode();
session_set('twoFactorSecret', $provider->getSecret());
} else {
$provider->setSecret(session_get('twoFactorSecret'));
}
$this->response->html($this->helper->layout->user('twofactor/show', array(
'user' => $user,
'secret' => session_get('twoFactorSecret'),
'key_url' => $provider->getKeyUrl($label),
)));
}
/**
* Test code and save secret
*
* @access public
*/
public function test()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$values = $this->request->getValues();
$provider = $this->authenticationManager->getPostAuthenticationProvider();
$provider->setCode(empty($values['code']) ? '' : $values['code']);
$provider->setSecret(session_get('twoFactorSecret'));
if ($provider->authenticate()) {
$this->flash->success(t('The two factor authentication code is valid.'));
$this->userModel->update(array(
'id' => $user['id'],
'twofactor_activated' => 1,
'twofactor_secret' => $this->authenticationManager->getPostAuthenticationProvider()->getSecret(),
));
session_remove('twoFactorSecret');
$this->userSession->disablePostAuthentication();
$this->response->redirect($this->helper->url->to('TwoFactorController', 'index', array('user_id' => $user['id'])), true);
} else {
$this->flash->failure(t('The two factor authentication code is not valid.'));
if ($this->request->isAjax()) {
$this->show();
} else {
$this->response->redirect($this->helper->url->to('TwoFactorController', 'show', array('user_id' => $user['id'])));
}
}
}
/**
* Disable 2FA for the current user
*
* @access public
*/
public function deactivate()
{
$this->checkCSRFForm();
$user = $this->getUser();
$this->checkCurrentUser($user);
$this->userModel->update(array(
'id' => $user['id'],
'twofactor_activated' => 0,
'twofactor_secret' => '',
));
// Allow the user to test or disable the feature
$this->userSession->disablePostAuthentication();
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('TwoFactorController', 'index', array('user_id' => $user['id'])), true);
}
/**
* Check 2FA
*
* @access public
*/
public function check()
{
$user = $this->getUser();
$this->checkCurrentUser($user);
$values = $this->request->getValues();
$provider = $this->authenticationManager->getPostAuthenticationProvider();
$provider->setCode(empty($values['code']) ? '' : $values['code']);
$provider->setSecret($user['twofactor_secret']);
if ($provider->authenticate()) {
$this->userSession->setPostAuthenticationAsValidated();
$this->flash->success(t('The two factor authentication code is valid.'));
if (REMEMBER_ME_AUTH && session_is_true('hasRememberMe')) {
$session = $this->rememberMeSessionModel->create($this->userSession->getId(), $this->request->getIpAddress(), $this->request->getUserAgent());
$this->rememberMeCookie->write($session['token'], $session['sequence'], $session['expiration']);
}
$this->response->redirect($this->helper->url->to('DashboardController', 'show'));
} else {
$this->flash->failure(t('The two factor authentication code is not valid.'));
$this->response->redirect($this->helper->url->to('TwoFactorController', 'code'));
}
}
/**
* Ask the 2FA code
*
* @access public
*/
public function code()
{
if (! session_exists('twoFactorBeforeCodeCalled')) {
$provider = $this->authenticationManager->getPostAuthenticationProvider();
$provider->beforeCode();
session_set('twoFactorBeforeCodeCalled', true);
}
$this->response->html($this->helper->layout->app('twofactor/check', array(
'title' => t('Check two factor authentication code'),
'no_layout' => true,
)));
}
/**
* Disable 2FA for a user
*
* @access public
*/
public function disable()
{
$user = $this->getUser();
if ($this->request->getStringParam('disable') === 'yes') {
$this->checkCSRFParam();
$this->userModel->update(array(
'id' => $user['id'],
'twofactor_activated' => 0,
'twofactor_secret' => '',
));
$this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])), true);
} else {
$this->response->html($this->helper->layout->user('twofactor/disable', array(
'user' => $user,
)));
}
}
/**
* Render QR Code image
*/
public function qrcode()
{
if (session_exists('twoFactorSecret')) {
$user = $this->getUser();
$provider = $this->authenticationManager->getPostAuthenticationProvider();
$provider->setSecret(session_get('twoFactorSecret'));
$url = $provider->getKeyUrl($user['email'] ?: $user['username']);
if (! empty($url)) {
PHPQRCode\QRcode::png($url, false, 'L', 6, 0);
}
}
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace Kanboard\Controller;
/**
* User Ajax Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserAjaxController extends BaseController
{
/**
* User auto-completion (Ajax)
*
* @access public
*/
public function autocomplete()
{
$search = $this->request->getStringParam('term');
$users = $this->userManager->find($search);
$this->response->json($this->userAutoCompleteFormatter->withUsers($users)->format());
}
/**
* User mention auto-completion (Ajax)
*
* @access public
*/
public function mention()
{
$project_id = $this->request->getStringParam('project_id');
$query = $this->request->getStringParam('search');
$users = $this->projectPermissionModel->findUsernames($project_id, $query);
$this->response->json($this->userMentionFormatter->withUsers($users)->format());
}
/**
* Check if the user is connected
*
* @access public
*/
public function status()
{
$this->response->text('OK');
}
}
@@ -0,0 +1,59 @@
<?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->renderResponse($user);
}
public function remove()
{
$user = $this->getUser();
$this->checkCSRFParam();
$this->userModel->update(array(
'id' => $user['id'],
'api_access_token' => null,
));
$this->renderResponse($user);
}
protected function renderResponse(array $user)
{
if ($this->request->isAjax()) {
$this->show();
} else {
$this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id'])));
}
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Security\Role;
use Kanboard\Notification\MailNotification;
use Kanboard\Notification\WebNotification;
/**
* Class UserCreationController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserCreationController extends BaseController
{
/**
* Display a form to create a new user
*
* @access public
* @param array $values
* @param array $errors
*/
public function show(array $values = array(), array $errors = array())
{
$this->response->html($this->template->render('user_creation/show', array(
'themes' => $this->themeModel->getThemes(),
'timezones' => $this->timezoneModel->getTimezones(true),
'languages' => $this->languageModel->getLanguages(true),
'roles' => $this->role->getApplicationRoles(),
'projects' => $this->projectModel->getList(),
'errors' => $errors,
'values' => $values + array('role' => Role::APP_USER),
)));
}
/**
* Validate and save a new user
*
* @access public
*/
public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->userValidator->validateCreation($values);
if ($valid) {
$this->createUser($values);
} else {
$this->show($values, $errors);
}
}
/**
* Create user
*
* @param array $values
*/
protected function createUser(array $values)
{
$project_id = empty($values['project_id']) ? 0 : $values['project_id'];
unset($values['project_id']);
$user_id = $this->userModel->create($values);
if ($user_id !== false) {
if ($project_id !== 0) {
$this->projectUserRoleModel->addUser($project_id, $user_id, Role::PROJECT_MEMBER);
}
if ($this->configModel->get('notifications_enabled', 0) == 1) {
$this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]);
} elseif (! empty($values['notifications_enabled'])) {
$this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE]);
}
$this->flash->success(t('User created successfully.'));
$this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user_id)));
} else {
$this->flash->failure(t('Unable to create your user.'));
$this->response->redirect($this->helper->url->to('UserListController', 'show'));
}
}
}
+134
View File
@@ -0,0 +1,134 @@
<?php
namespace Kanboard\Controller;
/**
* Class UserCredentialController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserCredentialController extends BaseController
{
/**
* Password modification form
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function changePassword(array $values = array(), array $errors = array())
{
$user = $this->getUser();
return $this->response->html($this->helper->layout->user('user_credential/password', array(
'values' => $values + array('id' => $user['id']),
'errors' => $errors,
'user' => $user,
)));
}
/**
* Save new password
*
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function savePassword()
{
$user = $this->getUser();
$values = $this->request->getValues();
list($valid, $errors) = $this->userValidator->validatePasswordModification($values);
if (! $this->userSession->isAdmin()) {
$values = array(
'id' => $this->userSession->getId(),
'password' => isset($values['password']) ? $values['password'] : '',
'confirmation' => isset($values['confirmation']) ? $values['confirmation'] : '',
);
}
if ($valid) {
if ($this->userModel->update($values)) {
$this->flash->success(t('Password modified successfully.'));
$this->userLockingModel->resetFailedLogin($user['username']);
$this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])), true);
return;
} else {
$this->flash->failure(t('Unable to change the password.'));
}
}
$this->changePassword($values, $errors);
}
/**
* Display a form to edit authentication
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function changeAuthentication(array $values = array(), array $errors = array())
{
$user = $this->getUser();
if (empty($values)) {
$values = $user;
unset($values['password']);
}
return $this->response->html($this->helper->layout->user('user_credential/authentication', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
)));
}
/**
* Save authentication
*
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function saveAuthentication()
{
$user = $this->getUser();
$values = $this->request->getValues() + array('disable_login_form' => 0, 'is_ldap_user' => 0);
list($valid, $errors) = $this->userValidator->validateModification($values);
if ($valid) {
if ($this->userModel->update($values)) {
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('UserCredentialController', 'changeAuthentication', array('user_id' => $user['id'])), true);
return;
} else {
$this->flash->failure(t('Unable to update this user.'));
}
}
$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'])));
}
}
+84
View File
@@ -0,0 +1,84 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Csv;
use Kanboard\Core\Controller\AccessForbiddenException;
/**
* User Import controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserImportController extends BaseController
{
/**
* Upload the file and ask settings
*
* @param array $values
* @param array $errors
*/
public function show(array $values = array(), array $errors = array())
{
$this->response->html($this->template->render('user_import/show', array(
'values' => $values,
'errors' => $errors,
'max_size' => get_upload_max_size(),
'delimiters' => Csv::getDelimiters(),
'enclosures' => Csv::getEnclosures(),
)));
}
/**
* Submit form
*/
public function save()
{
$values = $this->request->getValues();
// Note: $values is empty when the CSRF token is invalid.
if (empty($values)) {
throw new AccessForbiddenException();
}
$filename = $this->request->getFilePath('file');
if (! file_exists($filename)) {
$this->flash->failure(t('Unable to read your file'));
} else {
$this->importFile($values, $filename);
}
$this->response->redirect($this->helper->url->to('UserListController', 'show'));
}
/**
* Generate template
*
*/
public function template()
{
$this->response->withFileDownload('users.csv');
$this->response->csv(array($this->userImport->getColumnMapping()));
}
/**
* Process file
*
* @param array $values
* @param $filename
*/
private function importFile(array $values, $filename)
{
$csv = new Csv($values['delimiter'], $values['enclosure']);
$csv->setColumnMapping($this->userImport->getColumnMapping());
$csv->read($filename, array($this->userImport, 'import'));
if ($this->userImport->counter > 0) {
$this->flash->success(t('%d user(s) have been imported successfully.', $this->userImport->counter));
} else {
$this->flash->failure(t('Nothing has been imported!'));
}
}
}
+119
View File
@@ -0,0 +1,119 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Core\Security\Role;
use Kanboard\Notification\MailNotification;
use Kanboard\Notification\WebNotification;
/**
* 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']),
'themes' => $this->themeModel->getThemes(),
'timezones' => $this->timezoneModel->getTimezones(true),
'languages' => $this->languageModel->getLanguages(true),
)));
}
public function register()
{
$invite = $this->getInvite();
$input = $this->request->getValues();
$values = array();
foreach (['username', 'name', 'email', 'password', 'confirmation', 'timezone', 'language', 'notifications_enabled'] as $field) {
if (array_key_exists($field, $input)) {
$values[$field] = $input[$field];
}
}
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 ($this->configModel->get('notifications_enabled', 0) == 1) {
$this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]);
} elseif (! empty($values['notifications_enabled'])) {
$this->userNotificationTypeModel->saveSelectedTypes($user_id, [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'));
}
}
}
+60
View File
@@ -0,0 +1,60 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Filter\UserNameFilter;
/**
* Class User List Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserListController extends BaseController
{
/**
* List all users
*
* @access public
*/
public function show()
{
$paginator = $this->userPagination->getListingPaginator();
$this->response->html($this->helper->layout->app('user_list/listing', array(
'title' => t('Users').' ('.$paginator->getTotal().')',
'paginator' => $paginator,
'values' => array(),
)));
}
/**
* Search in users
*
* @access public
*/
public function search()
{
$search = $this->request->getStringParam('search');
$paginator = $this->userPagination->getListingPaginator();
if ($search !== '' && ! $paginator->isEmpty()) {
$paginator = $paginator
->setUrl('UserListController', 'search', array('search' => $search))
->setQuery(
$this->userQuery
->withFilter(new UserNameFilter($search))
->getQuery()
)
->calculate();
}
$this->response->html($this->helper->layout->app('user_list/listing', array(
'title' => t('Users').' ('.$paginator->getTotal().')',
'values' => array(
'search' => $search,
),
'paginator' => $paginator
)));
}
}
@@ -0,0 +1,77 @@
<?php
namespace Kanboard\Controller;
/**
* Class UserModificationController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserModificationController extends BaseController
{
/**
* Display a form to edit user information
*
* @access public
* @param array $values
* @param array $errors
* @throws \Kanboard\Core\Controller\AccessForbiddenException
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
public function show(array $values = array(), array $errors = array())
{
$user = $this->getUser();
if (empty($values)) {
$values = $user;
unset($values['password']);
}
return $this->response->html($this->helper->layout->user('user_modification/show', array(
'values' => $values,
'errors' => $errors,
'user' => $user,
'themes' => $this->themeModel->getThemes(),
'timezones' => $this->timezoneModel->getTimezones(true),
'languages' => $this->languageModel->getLanguages(true),
'roles' => $this->role->getApplicationRoles(),
)));
}
/**
* Save user information
*/
public function save()
{
$user = $this->getUser();
$values = $this->request->getValues();
if (! $this->userSession->isAdmin()) {
$values = array(
'id' => $this->userSession->getId(),
'username' => isset($values['username']) ? $values['username'] : '',
'name' => isset($values['name']) ? $values['name'] : '',
'email' => isset($values['email']) ? $values['email'] : '',
'theme' => isset($values['theme']) ? $values['theme'] : '',
'timezone' => isset($values['timezone']) ? $values['timezone'] : '',
'language' => isset($values['language']) ? $values['language'] : '',
'filter' => isset($values['filter']) ? $values['filter'] : '',
);
}
list($valid, $errors) = $this->userValidator->validateModification($values);
if ($valid) {
if ($this->userModel->update($values)) {
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])), true);
return;
} else {
$this->flash->failure(t('Unable to update this user.'));
}
}
$this->show($values, $errors);
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
namespace Kanboard\Controller;
/**
* User Status Controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserStatusController extends BaseController
{
/**
* Confirm remove a user
*
* @access public
*/
public function confirmRemove()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_status/remove', array(
'user' => $user,
)));
}
/**
* Remove a user
*
* @access public
*/
public function remove()
{
$user = $this->getUser();
$this->checkCSRFParam();
if ($this->userModel->remove($user['id'])) {
$this->flash->success(t('User removed successfully.'));
} else {
$this->flash->failure(t('Unable to remove this user.'));
}
$this->response->redirect($this->helper->url->to('UserListController', 'show'));
}
/**
* Confirm enable a user
*
* @access public
*/
public function confirmEnable()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_status/enable', array(
'user' => $user,
)));
}
/**
* Enable a user
*
* @access public
*/
public function enable()
{
$user = $this->getUser();
$this->checkCSRFParam();
if ($this->userModel->enable($user['id'])) {
$this->flash->success(t('User activated successfully.'));
} else {
$this->flash->failure(t('Unable to enable this user.'));
}
$this->response->redirect($this->helper->url->to('UserListController', 'show'));
}
/**
* Confirm disable a user
*
* @access public
*/
public function confirmDisable()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_status/disable', array(
'user' => $user,
)));
}
/**
* Disable a user
*
* @access public
*/
public function disable()
{
$user = $this->getUser();
$this->checkCSRFParam();
if ($this->userModel->disable($user['id'])) {
$this->flash->success(t('User disabled successfully.'));
} else {
$this->flash->failure(t('Unable to disable this user.'));
}
$this->response->redirect($this->helper->url->to('UserListController', 'show'));
}
}
+235
View File
@@ -0,0 +1,235 @@
<?php
namespace Kanboard\Controller;
use Kanboard\Core\Controller\PageNotFoundException;
use Kanboard\Model\ProjectModel;
/**
* Class UserViewController
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class UserViewController extends BaseController
{
/**
* Public user profile
*
* @access public
* @throws PageNotFoundException
*/
public function profile()
{
$user = $this->userModel->getById($this->request->getIntegerParam('user_id'));
if (empty($user)) {
throw new PageNotFoundException();
}
$this->response->html($this->helper->layout->app('user_view/profile', array(
'title' => $user['name'] ?: $user['username'],
'user' => $user,
)));
}
/**
* Display user information
*
* @access public
*/
public function show()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_view/show', array(
'user' => $user,
'themes' => $this->themeModel->getThemes(),
'timezones' => $this->timezoneModel->getTimezones(true),
'languages' => $this->languageModel->getLanguages(true),
)));
}
/**
* Display timesheet
*
* @access public
*/
public function timesheet()
{
$user = $this->getUser();
$subtask_paginator = $this->paginator
->setUrl('UserViewController', 'timesheet', array('user_id' => $user['id'], 'pagination' => 'subtasks'))
->setMax(20)
->setOrder('start')
->setDirection('DESC')
->setQuery($this->subtaskTimeTrackingModel->getUserQuery($user['id']))
->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
$this->response->html($this->helper->layout->user('user_view/timesheet', array(
'subtask_paginator' => $subtask_paginator,
'user' => $user,
)));
}
/**
* Display last password reset
*
* @access public
*/
public function passwordReset()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_view/password_reset', array(
'tokens' => $this->passwordResetModel->getAll($user['id']),
'user' => $user,
)));
}
/**
* Display last connections
*
* @access public
*/
public function lastLogin()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_view/last', array(
'last_logins' => $this->lastLoginModel->getAll($user['id']),
'user' => $user,
)));
}
/**
* Display user sessions
*
* @access public
*/
public function sessions()
{
if (! REMEMBER_ME_AUTH) {
$this->response->status(404);
return;
}
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_view/sessions', array(
'sessions' => $this->rememberMeSessionModel->getAll($user['id']),
'user' => $user,
)));
}
/**
* Remove a "RememberMe" token
*
* @access public
*/
public function removeSession()
{
$this->checkCSRFParam();
$user = $this->getUser();
$this->rememberMeSessionModel->remove($this->request->getIntegerParam('id'));
if ($this->request->isAjax()) {
$this->sessions();
} else {
$this->response->redirect($this->helper->url->to('UserViewController', 'sessions', array('user_id' => $user['id'])), true);
}
}
/**
* Display user notifications
*
* @access public
*/
public function notifications()
{
$user = $this->getUser();
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->userNotificationModel->saveSettings($user['id'], $values);
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('UserViewController', 'notifications', array('user_id' => $user['id'])), true);
return;
}
$this->response->html($this->helper->layout->user('user_view/notifications', array(
'projects' => $this->projectUserRoleModel->getProjectsByUser($user['id'], array(ProjectModel::ACTIVE)),
'notifications' => $this->userNotificationModel->readSettings($user['id']),
'types' => $this->userNotificationTypeModel->getTypes(),
'filters' => $this->userNotificationFilterModel->getFilters(),
'user' => $user,
)));
}
/**
* Display user integrations
*
* @access public
*/
public function integrations()
{
$user = $this->getUser();
if ($this->request->isPost()) {
$values = $this->request->getValues();
$this->userMetadataModel->save($user['id'], $values);
$this->flash->success(t('User updated successfully.'));
$this->response->redirect($this->helper->url->to('UserViewController', 'integrations', array('user_id' => $user['id'])), true);
return;
}
$this->response->html($this->helper->layout->user('user_view/integrations', array(
'user' => $user,
'values' => $this->userMetadataModel->getAll($user['id']),
)));
}
/**
* Display external accounts
*
* @access public
*/
public function external()
{
$user = $this->getUser();
$this->response->html($this->helper->layout->user('user_view/external', array(
'last_logins' => $this->lastLoginModel->getAll($user['id']),
'user' => $user,
)));
}
/**
* Public access management
*
* @access public
*/
public function share()
{
$user = $this->getUser();
$switch = $this->request->getStringParam('switch');
if ($switch === 'enable' || $switch === 'disable') {
$this->checkCSRFParam();
if ($this->userModel->{$switch . 'PublicAccess'}($user['id'])) {
$this->flash->success(t('User updated successfully.'));
} else {
$this->flash->failure(t('Unable to update this user.'));
}
if (! $this->request->isAjax()) {
$this->response->redirect($this->helper->url->to('UserViewController', 'share', array('user_id' => $user['id'])), true);
return;
}
$user = $this->getUser();
}
$this->response->html($this->helper->layout->user('user_view/share', array(
'user' => $user,
'title' => t('Public access'),
)));
}
}
@@ -0,0 +1,96 @@
<?php
namespace Kanboard\Controller;
/**
* Web notification controller
*
* @package Kanboard\Controller
* @author Frederic Guillot
*/
class WebNotificationController extends BaseController
{
/**
* My notifications
*
* @access public
*/
public function show()
{
$user = $this->getUser();
$notifications = $this->userUnreadNotificationModel->getAll($user['id']);
$this->response->html($this->template->render('web_notification/show', array(
'notifications' => $notifications,
'nb_notifications' => count($notifications),
'user' => $user,
)));
}
/**
* Mark all notifications as read
*
* @access public
*/
public function flush()
{
$this->checkReusableGETCSRFParam();
$userId = $this->getUserId();
$this->userUnreadNotificationModel->markAllAsRead($userId);
$this->show();
}
/**
* Mark a notification as read
*
* @access public
*/
public function remove()
{
$this->checkReusableGETCSRFParam();
$user_id = $this->getUserId();
$notification_id = $this->request->getIntegerParam('notification_id');
$this->userUnreadNotificationModel->markAsRead($user_id, $notification_id);
$this->show();
}
/**
* Redirect to the task and mark notification as read
*/
public function redirect()
{
$user_id = $this->getUserId();
$notification_id = $this->request->getIntegerParam('notification_id');
$notification = $this->userUnreadNotificationModel->getById($notification_id);
$this->userUnreadNotificationModel->markAsRead($user_id, $notification_id);
if (empty($notification)) {
$this->show();
} elseif ($this->helper->text->contains($notification['event_name'], 'comment')) {
$this->response->redirect($this->helper->url->to(
'TaskViewController',
'show',
array('task_id' => $this->notificationModel->getTaskIdFromEvent($notification['event_name'], $notification['event_data'])),
'comment-'.$notification['event_data']['comment']['id']
));
} else {
$this->response->redirect($this->helper->url->to(
'TaskViewController',
'show',
array('task_id' => $this->notificationModel->getTaskIdFromEvent($notification['event_name'], $notification['event_data']))
));
}
}
private function getUserId()
{
$user_id = $this->request->getIntegerParam('user_id');
if (! $this->userSession->isAdmin() && $user_id != $this->userSession->getId()) {
$user_id = $this->userSession->getId();
}
return $user_id;
}
}