看板初始化提交
This commit is contained in:
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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))
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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']))
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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'
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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').' > '.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').' > '.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').' > '.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').' > '.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').' > '.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').' > '.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').' > '.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').' > '.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));
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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') . ' > ' . 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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -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>');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
]));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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).')';
|
||||
}
|
||||
}
|
||||
@@ -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').' > '.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);
|
||||
}
|
||||
}
|
||||
@@ -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')
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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']
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -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']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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').' > '.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'));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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']]));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'])));
|
||||
}
|
||||
}
|
||||
@@ -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!'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user