Apply column restrictions to the board

This commit is contained in:
Frederic Guillot
2016-09-08 22:33:16 -04:00
parent fedf4ea2de
commit 75470c7242
14 changed files with 383 additions and 169 deletions

View File

@@ -28,11 +28,21 @@ class BoardAjaxController extends BaseController
} }
$values = $this->request->getJson(); $values = $this->request->getJson();
$canMoveTask = $this->columnMoveRestrictionModel->isAllowed(
$project_id,
$this->helper->user->getProjectUserRole($project_id),
$values['src_column_id'],
$values['dst_column_id']
);
if (! $canMoveTask) {
throw new AccessForbiddenException("You don't have the permission to move this task");
}
$result =$this->taskPositionModel->movePosition( $result =$this->taskPositionModel->movePosition(
$project_id, $project_id,
$values['task_id'], $values['task_id'],
$values['column_id'], $values['dst_column_id'],
$values['position'], $values['position'],
$values['swimlane_id'] $values['swimlane_id']
); );

View File

@@ -10,168 +10,171 @@ use Pimple\Container;
* @package core * @package core
* @author Frederic Guillot * @author Frederic Guillot
* *
* @property \Kanboard\Analytic\TaskDistributionAnalytic $taskDistributionAnalytic * @property \Kanboard\Analytic\TaskDistributionAnalytic $taskDistributionAnalytic
* @property \Kanboard\Analytic\UserDistributionAnalytic $userDistributionAnalytic * @property \Kanboard\Analytic\UserDistributionAnalytic $userDistributionAnalytic
* @property \Kanboard\Analytic\EstimatedTimeComparisonAnalytic $estimatedTimeComparisonAnalytic * @property \Kanboard\Analytic\EstimatedTimeComparisonAnalytic $estimatedTimeComparisonAnalytic
* @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic * @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic
* @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic
* @property \Kanboard\Core\Action\ActionManager $actionManager * @property \Kanboard\Core\Action\ActionManager $actionManager
* @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager
* @property \Kanboard\Core\Cache\MemoryCache $memoryCache * @property \Kanboard\Core\Cache\MemoryCache $memoryCache
* @property \Kanboard\Core\Cache\BaseCache $cacheDriver * @property \Kanboard\Core\Cache\BaseCache $cacheDriver
* @property \Kanboard\Core\Event\EventManager $eventManager * @property \Kanboard\Core\Event\EventManager $eventManager
* @property \Kanboard\Core\Group\GroupManager $groupManager * @property \Kanboard\Core\Group\GroupManager $groupManager
* @property \Kanboard\Core\Http\Client $httpClient * @property \Kanboard\Core\Http\Client $httpClient
* @property \Kanboard\Core\Http\OAuth2 $oauth * @property \Kanboard\Core\Http\OAuth2 $oauth
* @property \Kanboard\Core\Http\RememberMeCookie $rememberMeCookie * @property \Kanboard\Core\Http\RememberMeCookie $rememberMeCookie
* @property \Kanboard\Core\Http\Request $request * @property \Kanboard\Core\Http\Request $request
* @property \Kanboard\Core\Http\Response $response * @property \Kanboard\Core\Http\Response $response
* @property \Kanboard\Core\Http\Router $router * @property \Kanboard\Core\Http\Router $router
* @property \Kanboard\Core\Http\Route $route * @property \Kanboard\Core\Http\Route $route
* @property \Kanboard\Core\Queue\QueueManager $queueManager * @property \Kanboard\Core\Queue\QueueManager $queueManager
* @property \Kanboard\Core\Mail\Client $emailClient * @property \Kanboard\Core\Mail\Client $emailClient
* @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage * @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage
* @property \Kanboard\Core\Plugin\Hook $hook * @property \Kanboard\Core\Plugin\Hook $hook
* @property \Kanboard\Core\Plugin\Loader $pluginLoader * @property \Kanboard\Core\Plugin\Loader $pluginLoader
* @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager
* @property \Kanboard\Core\Security\AccessMap $applicationAccessMap * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap
* @property \Kanboard\Core\Security\AccessMap $projectAccessMap * @property \Kanboard\Core\Security\AccessMap $projectAccessMap
* @property \Kanboard\Core\Security\AccessMap $apiAccessMap * @property \Kanboard\Core\Security\AccessMap $apiAccessMap
* @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap * @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap
* @property \Kanboard\Core\Security\Authorization $applicationAuthorization * @property \Kanboard\Core\Security\Authorization $applicationAuthorization
* @property \Kanboard\Core\Security\Authorization $projectAuthorization * @property \Kanboard\Core\Security\Authorization $projectAuthorization
* @property \Kanboard\Core\Security\Authorization $apiAuthorization * @property \Kanboard\Core\Security\Authorization $apiAuthorization
* @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization * @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization
* @property \Kanboard\Core\Security\Role $role * @property \Kanboard\Core\Security\Role $role
* @property \Kanboard\Core\Security\Token $token * @property \Kanboard\Core\Security\Token $token
* @property \Kanboard\Core\Session\FlashMessage $flash * @property \Kanboard\Core\Session\FlashMessage $flash
* @property \Kanboard\Core\Session\SessionManager $sessionManager * @property \Kanboard\Core\Session\SessionManager $sessionManager
* @property \Kanboard\Core\Session\SessionStorage $sessionStorage * @property \Kanboard\Core\Session\SessionStorage $sessionStorage
* @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager * @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager
* @property \Kanboard\Core\User\GroupSync $groupSync * @property \Kanboard\Core\User\GroupSync $groupSync
* @property \Kanboard\Core\User\UserProfile $userProfile * @property \Kanboard\Core\User\UserProfile $userProfile
* @property \Kanboard\Core\User\UserSync $userSync * @property \Kanboard\Core\User\UserSync $userSync
* @property \Kanboard\Core\User\UserSession $userSession * @property \Kanboard\Core\User\UserSession $userSession
* @property \Kanboard\Core\DateParser $dateParser * @property \Kanboard\Core\DateParser $dateParser
* @property \Kanboard\Core\Helper $helper * @property \Kanboard\Core\Helper $helper
* @property \Kanboard\Core\Paginator $paginator * @property \Kanboard\Core\Paginator $paginator
* @property \Kanboard\Core\Template $template * @property \Kanboard\Core\Template $template
* @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator * @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator
* @property \Kanboard\Model\ActionModel $actionModel * @property \Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator $columnMoveRestrictionCacheDecorator
* @property \Kanboard\Model\ActionParameterModel $actionParameterModel * @property \Kanboard\Model\ActionModel $actionModel
* @property \Kanboard\Model\AvatarFileModel $avatarFileModel * @property \Kanboard\Model\ActionParameterModel $actionParameterModel
* @property \Kanboard\Model\BoardModel $boardModel * @property \Kanboard\Model\AvatarFileModel $avatarFileModel
* @property \Kanboard\Model\CategoryModel $categoryModel * @property \Kanboard\Model\BoardModel $boardModel
* @property \Kanboard\Model\ColorModel $colorModel * @property \Kanboard\Model\CategoryModel $categoryModel
* @property \Kanboard\Model\ColumnModel $columnModel * @property \Kanboard\Model\ColorModel $colorModel
* @property \Kanboard\Model\CommentModel $commentModel * @property \Kanboard\Model\ColumnModel $columnModel
* @property \Kanboard\Model\ConfigModel $configModel * @property \Kanboard\Model\ColumnMoveRestrictionModel $columnMoveRestrictionModel
* @property \Kanboard\Model\CurrencyModel $currencyModel * @property \Kanboard\Model\CommentModel $commentModel
* @property \Kanboard\Model\CustomFilterModel $customFilterModel * @property \Kanboard\Model\ConfigModel $configModel
* @property \Kanboard\Model\TaskFileModel $taskFileModel * @property \Kanboard\Model\CurrencyModel $currencyModel
* @property \Kanboard\Model\ProjectFileModel $projectFileModel * @property \Kanboard\Model\CustomFilterModel $customFilterModel
* @property \Kanboard\Model\GroupModel $groupModel * @property \Kanboard\Model\TaskFileModel $taskFileModel
* @property \Kanboard\Model\GroupMemberModel $groupMemberModel * @property \Kanboard\Model\ProjectFileModel $projectFileModel
* @property \Kanboard\Model\LanguageModel $languageModel * @property \Kanboard\Model\GroupModel $groupModel
* @property \Kanboard\Model\LastLoginModel $lastLoginModel * @property \Kanboard\Model\GroupMemberModel $groupMemberModel
* @property \Kanboard\Model\LinkModel $linkModel * @property \Kanboard\Model\LanguageModel $languageModel
* @property \Kanboard\Model\NotificationModel $notificationModel * @property \Kanboard\Model\LastLoginModel $lastLoginModel
* @property \Kanboard\Model\PasswordResetModel $passwordResetModel * @property \Kanboard\Model\LinkModel $linkModel
* @property \Kanboard\Model\ProjectModel $projectModel * @property \Kanboard\Model\NotificationModel $notificationModel
* @property \Kanboard\Model\ProjectActivityModel $projectActivityModel * @property \Kanboard\Model\PasswordResetModel $passwordResetModel
* @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel * @property \Kanboard\Model\ProjectModel $projectModel
* @property \Kanboard\Model\ProjectDailyColumnStatsModel $projectDailyColumnStatsModel * @property \Kanboard\Model\ProjectActivityModel $projectActivityModel
* @property \Kanboard\Model\ProjectDailyStatsModel $projectDailyStatsModel * @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel
* @property \Kanboard\Model\ProjectMetadataModel $projectMetadataModel * @property \Kanboard\Model\ProjectDailyColumnStatsModel $projectDailyColumnStatsModel
* @property \Kanboard\Model\ProjectPermissionModel $projectPermissionModel * @property \Kanboard\Model\ProjectDailyStatsModel $projectDailyStatsModel
* @property \Kanboard\Model\ProjectUserRoleModel $projectUserRoleModel * @property \Kanboard\Model\ProjectMetadataModel $projectMetadataModel
* @property \Kanboard\Model\ProjectGroupRoleModel $projectGroupRoleModel * @property \Kanboard\Model\ProjectPermissionModel $projectPermissionModel
* @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel * @property \Kanboard\Model\ProjectUserRoleModel $projectUserRoleModel
* @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel * @property \Kanboard\Model\ProjectGroupRoleModel $projectGroupRoleModel
* @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel * @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel
* @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel * @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel
* @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel * @property \Kanboard\Model\ProjectRoleModel $projectRoleModel
* @property \Kanboard\Model\SubtaskModel $subtaskModel * @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel
* @property \Kanboard\Model\SubtaskPositionModel $subtaskPositionModel * @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel
* @property \Kanboard\Model\SubtaskStatusModel $subtaskStatusModel * @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel
* @property \Kanboard\Model\SubtaskTaskConversionModel $subtaskTaskConversionModel * @property \Kanboard\Model\SubtaskModel $subtaskModel
* @property \Kanboard\Model\SubtaskTimeTrackingModel $subtaskTimeTrackingModel * @property \Kanboard\Model\SubtaskPositionModel $subtaskPositionModel
* @property \Kanboard\Model\SwimlaneModel $swimlaneModel * @property \Kanboard\Model\SubtaskStatusModel $subtaskStatusModel
* @property \Kanboard\Model\TagDuplicationModel $tagDuplicationModel * @property \Kanboard\Model\SubtaskTaskConversionModel $subtaskTaskConversionModel
* @property \Kanboard\Model\TagModel $tagModel * @property \Kanboard\Model\SubtaskTimeTrackingModel $subtaskTimeTrackingModel
* @property \Kanboard\Model\TaskModel $taskModel * @property \Kanboard\Model\SwimlaneModel $swimlaneModel
* @property \Kanboard\Model\TaskAnalyticModel $taskAnalyticModel * @property \Kanboard\Model\TagDuplicationModel $tagDuplicationModel
* @property \Kanboard\Model\TaskCreationModel $taskCreationModel * @property \Kanboard\Model\TagModel $tagModel
* @property \Kanboard\Model\TaskDuplicationModel $taskDuplicationModel * @property \Kanboard\Model\TaskModel $taskModel
* @property \Kanboard\Model\TaskProjectDuplicationModel $taskProjectDuplicationModel * @property \Kanboard\Model\TaskAnalyticModel $taskAnalyticModel
* @property \Kanboard\Model\TaskProjectMoveModel $taskProjectMoveModel * @property \Kanboard\Model\TaskCreationModel $taskCreationModel
* @property \Kanboard\Model\TaskRecurrenceModel $taskRecurrenceModel * @property \Kanboard\Model\TaskDuplicationModel $taskDuplicationModel
* @property \Kanboard\Model\TaskExternalLinkModel $taskExternalLinkModel * @property \Kanboard\Model\TaskProjectDuplicationModel $taskProjectDuplicationModel
* @property \Kanboard\Model\TaskFinderModel $taskFinderModel * @property \Kanboard\Model\TaskProjectMoveModel $taskProjectMoveModel
* @property \Kanboard\Model\TaskLinkModel $taskLinkModel * @property \Kanboard\Model\TaskRecurrenceModel $taskRecurrenceModel
* @property \Kanboard\Model\TaskModificationModel $taskModificationModel * @property \Kanboard\Model\TaskExternalLinkModel $taskExternalLinkModel
* @property \Kanboard\Model\TaskPositionModel $taskPositionModel * @property \Kanboard\Model\TaskFinderModel $taskFinderModel
* @property \Kanboard\Model\TaskStatusModel $taskStatusModel * @property \Kanboard\Model\TaskLinkModel $taskLinkModel
* @property \Kanboard\Model\TaskTagModel $taskTagModel * @property \Kanboard\Model\TaskModificationModel $taskModificationModel
* @property \Kanboard\Model\TaskMetadataModel $taskMetadataModel * @property \Kanboard\Model\TaskPositionModel $taskPositionModel
* @property \Kanboard\Model\TimezoneModel $timezoneModel * @property \Kanboard\Model\TaskStatusModel $taskStatusModel
* @property \Kanboard\Model\TransitionModel $transitionModel * @property \Kanboard\Model\TaskTagModel $taskTagModel
* @property \Kanboard\Model\UserModel $userModel * @property \Kanboard\Model\TaskMetadataModel $taskMetadataModel
* @property \Kanboard\Model\UserLockingModel $userLockingModel * @property \Kanboard\Model\TimezoneModel $timezoneModel
* @property \Kanboard\Model\UserMentionModel $userMentionModel * @property \Kanboard\Model\TransitionModel $transitionModel
* @property \Kanboard\Model\UserNotificationModel $userNotificationModel * @property \Kanboard\Model\UserModel $userModel
* @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel * @property \Kanboard\Model\UserLockingModel $userLockingModel
* @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel * @property \Kanboard\Model\UserMentionModel $userMentionModel
* @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel * @property \Kanboard\Model\UserNotificationModel $userNotificationModel
* @property \Kanboard\Model\UserMetadataModel $userMetadataModel * @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel
* @property \Kanboard\Pagination\TaskPagination $taskPagination * @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel
* @property \Kanboard\Pagination\SubtaskPagination $subtaskPagination * @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel
* @property \Kanboard\Pagination\ProjectPagination $projectPagination * @property \Kanboard\Model\UserMetadataModel $userMetadataModel
* @property \Kanboard\Pagination\UserPagination $userPagination * @property \Kanboard\Pagination\TaskPagination $taskPagination
* @property \Kanboard\Validator\ActionValidator $actionValidator * @property \Kanboard\Pagination\SubtaskPagination $subtaskPagination
* @property \Kanboard\Validator\AuthValidator $authValidator * @property \Kanboard\Pagination\ProjectPagination $projectPagination
* @property \Kanboard\Validator\ColumnValidator $columnValidator * @property \Kanboard\Pagination\UserPagination $userPagination
* @property \Kanboard\Validator\CategoryValidator $categoryValidator * @property \Kanboard\Validator\ActionValidator $actionValidator
* @property \Kanboard\Validator\CommentValidator $commentValidator * @property \Kanboard\Validator\AuthValidator $authValidator
* @property \Kanboard\Validator\CurrencyValidator $currencyValidator * @property \Kanboard\Validator\ColumnValidator $columnValidator
* @property \Kanboard\Validator\CustomFilterValidator $customFilterValidator * @property \Kanboard\Validator\CategoryValidator $categoryValidator
* @property \Kanboard\Validator\ExternalLinkValidator $externalLinkValidator * @property \Kanboard\Validator\CommentValidator $commentValidator
* @property \Kanboard\Validator\GroupValidator $groupValidator * @property \Kanboard\Validator\CurrencyValidator $currencyValidator
* @property \Kanboard\Validator\LinkValidator $linkValidator * @property \Kanboard\Validator\CustomFilterValidator $customFilterValidator
* @property \Kanboard\Validator\PasswordResetValidator $passwordResetValidator * @property \Kanboard\Validator\ExternalLinkValidator $externalLinkValidator
* @property \Kanboard\Validator\ProjectValidator $projectValidator * @property \Kanboard\Validator\GroupValidator $groupValidator
* @property \Kanboard\Validator\SubtaskValidator $subtaskValidator * @property \Kanboard\Validator\LinkValidator $linkValidator
* @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator * @property \Kanboard\Validator\PasswordResetValidator $passwordResetValidator
* @property \Kanboard\Validator\TagValidator $tagValidator * @property \Kanboard\Validator\ProjectValidator $projectValidator
* @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator
* @property \Kanboard\Validator\TaskValidator $taskValidator * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator
* @property \Kanboard\Validator\UserValidator $userValidator * @property \Kanboard\Validator\TagValidator $tagValidator
* @property \Kanboard\Import\TaskImport $taskImport * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator
* @property \Kanboard\Import\UserImport $userImport * @property \Kanboard\Validator\TaskValidator $taskValidator
* @property \Kanboard\Export\SubtaskExport $subtaskExport * @property \Kanboard\Validator\UserValidator $userValidator
* @property \Kanboard\Export\TaskExport $taskExport * @property \Kanboard\Import\TaskImport $taskImport
* @property \Kanboard\Export\TransitionExport $transitionExport * @property \Kanboard\Import\UserImport $userImport
* @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery * @property \Kanboard\Export\SubtaskExport $subtaskExport
* @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery * @property \Kanboard\Export\TaskExport $taskExport
* @property \Kanboard\Core\Filter\QueryBuilder $projectActivityQuery * @property \Kanboard\Export\TransitionExport $transitionExport
* @property \Kanboard\Core\Filter\QueryBuilder $userQuery * @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery
* @property \Kanboard\Core\Filter\QueryBuilder $projectQuery * @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery
* @property \Kanboard\Core\Filter\QueryBuilder $taskQuery * @property \Kanboard\Core\Filter\QueryBuilder $projectActivityQuery
* @property \Kanboard\Core\Filter\LexerBuilder $taskLexer * @property \Kanboard\Core\Filter\QueryBuilder $userQuery
* @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer * @property \Kanboard\Core\Filter\QueryBuilder $projectQuery
* @property \Kanboard\Job\CommentEventJob $commentEventJob * @property \Kanboard\Core\Filter\QueryBuilder $taskQuery
* @property \Kanboard\Job\SubtaskEventJob $subtaskEventJob * @property \Kanboard\Core\Filter\LexerBuilder $taskLexer
* @property \Kanboard\Job\TaskEventJob $taskEventJob * @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer
* @property \Kanboard\Job\TaskFileEventJob $taskFileEventJob * @property \Kanboard\Job\CommentEventJob $commentEventJob
* @property \Kanboard\Job\TaskLinkEventJob $taskLinkEventJob * @property \Kanboard\Job\SubtaskEventJob $subtaskEventJob
* @property \Kanboard\Job\ProjectFileEventJob $projectFileEventJob * @property \Kanboard\Job\TaskEventJob $taskEventJob
* @property \Kanboard\Job\NotificationJob $notificationJob * @property \Kanboard\Job\TaskFileEventJob $taskFileEventJob
* @property \Kanboard\Job\ProjectMetricJob $projectMetricJob * @property \Kanboard\Job\TaskLinkEventJob $taskLinkEventJob
* @property \Psr\Log\LoggerInterface $logger * @property \Kanboard\Job\ProjectFileEventJob $projectFileEventJob
* @property \PicoDb\Database $db * @property \Kanboard\Job\NotificationJob $notificationJob
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher * @property \Kanboard\Job\ProjectMetricJob $projectMetricJob
* @property \Symfony\Component\Console\Application $cli * @property \Psr\Log\LoggerInterface $logger
* @property \JsonRPC\Server $api * @property \PicoDb\Database $db
* @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
* @property \Symfony\Component\Console\Application $cli
* @property \JsonRPC\Server $api
*/ */
abstract class Base abstract class Base
{ {

View File

@@ -12,6 +12,7 @@ use Pimple\Container;
* *
* @property \Kanboard\Helper\AppHelper $app * @property \Kanboard\Helper\AppHelper $app
* @property \Kanboard\Helper\AssetHelper $asset * @property \Kanboard\Helper\AssetHelper $asset
* @property \Kanboard\Helper\BoardHelper $board
* @property \Kanboard\Helper\CalendarHelper $calendar * @property \Kanboard\Helper\CalendarHelper $calendar
* @property \Kanboard\Helper\DateHelper $dt * @property \Kanboard\Helper\DateHelper $dt
* @property \Kanboard\Helper\FileHelper $file * @property \Kanboard\Helper\FileHelper $file

View File

@@ -0,0 +1,57 @@
<?php
namespace Kanboard\Decorator;
use Kanboard\Core\Cache\CacheInterface;
use Kanboard\Model\ColumnMoveRestrictionModel;
/**
* Class ColumnMoveRestrictionCacheDecorator
*
* @package Kanboard\Decorator
* @author Frederic Guillot
*/
class ColumnMoveRestrictionCacheDecorator
{
protected $cachePrefix = 'column_move_restriction:';
/**
* @var CacheInterface
*/
protected $cache;
/**
* @var ColumnMoveRestrictionModel
*/
protected $columnMoveRestrictionModel;
/**
* ColumnMoveRestrictionDecorator constructor.
*
* @param CacheInterface $cache
* @param ColumnMoveRestrictionModel $columnMoveRestrictionModel
*/
public function __construct(CacheInterface $cache, ColumnMoveRestrictionModel $columnMoveRestrictionModel)
{
$this->cache = $cache;
$this->columnMoveRestrictionModel = $columnMoveRestrictionModel;
}
/**
* Proxy method to get column Ids
* @param int $project_id
* @return array|mixed
*/
public function getAllSrcColumns($project_id)
{
$key = $this->cachePrefix.$project_id;
$columnIds = $this->cache->get($key);
if ($columnIds === null) {
$columnIds = $this->columnMoveRestrictionModel->getAllSrcColumns($project_id);
$this->cache->set($key, $columnIds);
}
return $columnIds;
}
}

View File

@@ -79,6 +79,11 @@ class BoardTaskFormatter extends BaseFormatter implements FormatterInterface
{ {
$tasks = array_values(array_filter($this->tasks, array($this, 'filterTasks'))); $tasks = array_values(array_filter($this->tasks, array($this, 'filterTasks')));
array_merge_relation($tasks, $this->tags, 'tags', 'id'); array_merge_relation($tasks, $this->tags, 'tags', 'id');
foreach ($tasks as &$task) {
$task['is_draggable'] = $this->helper->board->isDraggable($task);
}
return $tasks; return $tasks;
} }

View File

@@ -24,4 +24,20 @@ class BoardHelper extends Base
{ {
return $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, 0) == 1; return $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, 0) == 1;
} }
/**
* Return true if the task can be moved by the connected user
*
* @param array $task
* @return bool
*/
public function isDraggable(array $task)
{
if ($task['is_active'] == 1 && $this->helper->user->hasProjectAccess('BoardViewController', 'save', $task['project_id'])) {
$srcColumnIds = $this->columnMoveRestrictionCacheDecorator->getAllSrcColumns($task['project_id']);
return ! isset($srcColumnIds[$task['column_id']]);
}
return false;
}
} }

View File

@@ -42,7 +42,8 @@ class ColumnMoveRestrictionModel extends Base
*/ */
public function getAll($project_id) public function getAll($project_id)
{ {
return $this->db->table(self::TABLE) return $this->db
->table(self::TABLE)
->columns( ->columns(
'restriction_id', 'restriction_id',
'src_column_id', 'src_column_id',
@@ -58,6 +59,20 @@ class ColumnMoveRestrictionModel extends Base
->findAll(); ->findAll();
} }
/**
* Get all source column Ids
*
* @param int $project_id
* @return array
*/
public function getAllSrcColumns($project_id)
{
return $this->db
->hashtable(self::TABLE)
->eq(self::TABLE.'.project_id', $project_id)
->getAll('src_column_id', 'src_column_id');
}
/** /**
* Create a new column restriction * Create a new column restriction
* *

View File

@@ -4,6 +4,7 @@ namespace Kanboard\ServiceProvider;
use Kanboard\Core\Cache\FileCache; use Kanboard\Core\Cache\FileCache;
use Kanboard\Core\Cache\MemoryCache; use Kanboard\Core\Cache\MemoryCache;
use Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator;
use Kanboard\Decorator\MetadataCacheDecorator; use Kanboard\Decorator\MetadataCacheDecorator;
use Pimple\Container; use Pimple\Container;
use Pimple\ServiceProviderInterface; use Pimple\ServiceProviderInterface;
@@ -46,6 +47,13 @@ class CacheProvider implements ServiceProviderInterface
); );
}; };
$container['columnMoveRestrictionCacheDecorator'] = function($c) {
return new ColumnMoveRestrictionCacheDecorator(
$c['memoryCache'],
$c['columnMoveRestrictionModel']
);
};
return $container; return $container;
} }
} }

View File

@@ -34,6 +34,7 @@ class ClassProvider implements ServiceProviderInterface
'CategoryModel', 'CategoryModel',
'ColorModel', 'ColorModel',
'ColumnModel', 'ColumnModel',
'ColumnMoveRestrictionModel',
'CommentModel', 'CommentModel',
'ConfigModel', 'ConfigModel',
'CurrencyModel', 'CurrencyModel',
@@ -55,6 +56,7 @@ class ClassProvider implements ServiceProviderInterface
'ProjectNotificationModel', 'ProjectNotificationModel',
'ProjectMetadataModel', 'ProjectMetadataModel',
'ProjectGroupRoleModel', 'ProjectGroupRoleModel',
'ProjectRoleModel',
'ProjectTaskDuplicationModel', 'ProjectTaskDuplicationModel',
'ProjectTaskPriorityModel', 'ProjectTaskPriorityModel',
'ProjectUserRoleModel', 'ProjectUserRoleModel',

View File

@@ -1,6 +1,7 @@
<div class=" <div class="
task-board task-board
<?= $task['is_active'] == 1 ? ($this->user->hasProjectAccess('BoardViewController', 'save', $task['project_id']) ? 'draggable-item ' : '').'task-board-status-open '.($task['date_modification'] > (time() - $board_highlight_period) ? 'task-board-recent' : '') : 'task-board-status-closed' ?> <?= $task['is_draggable'] ? 'draggable-item ' : '' ?>
<?= $task['is_active'] == 1 ? 'task-board-status-open '.($task['date_modification'] > (time() - $board_highlight_period) ? 'task-board-recent' : '') : 'task-board-status-closed' ?>
color-<?= $task['color_id'] ?>" color-<?= $task['color_id'] ?>"
data-task-id="<?= $task['id'] ?>" data-task-id="<?= $task['id'] ?>"
data-column-id="<?= $task['column_id'] ?>" data-column-id="<?= $task['column_id'] ?>"

File diff suppressed because one or more lines are too long

View File

@@ -34,7 +34,7 @@ Kanboard.BoardDragAndDrop.prototype.dragAndDrop = function() {
if (newColumnId != taskColumnId || newSwimlaneId != taskSwimlaneId || newPosition != taskPosition) { if (newColumnId != taskColumnId || newSwimlaneId != taskSwimlaneId || newPosition != taskPosition) {
self.changeTaskState(taskId); self.changeTaskState(taskId);
self.save(taskId, newColumnId, newPosition, newSwimlaneId); self.save(taskId, taskColumnId, newColumnId, newPosition, newSwimlaneId);
} }
}, },
start: function(event, ui) { start: function(event, ui) {
@@ -62,7 +62,7 @@ Kanboard.BoardDragAndDrop.prototype.changeTaskState = function(taskId) {
task.find('.task-board-saving-icon').show(); task.find('.task-board-saving-icon').show();
}; };
Kanboard.BoardDragAndDrop.prototype.save = function(taskId, columnId, position, swimlaneId) { Kanboard.BoardDragAndDrop.prototype.save = function(taskId, srcColumnId, dstColumnId, position, swimlaneId) {
var self = this; var self = this;
self.app.showLoadingIcon(); self.app.showLoadingIcon();
self.savingInProgress = true; self.savingInProgress = true;
@@ -75,7 +75,8 @@ Kanboard.BoardDragAndDrop.prototype.save = function(taskId, columnId, position,
processData: false, processData: false,
data: JSON.stringify({ data: JSON.stringify({
"task_id": taskId, "task_id": taskId,
"column_id": columnId, "src_column_id": srcColumnId,
"dst_column_id": dstColumnId,
"swimlane_id": swimlaneId, "swimlane_id": swimlaneId,
"position": position "position": position
}), }),

View File

@@ -123,6 +123,7 @@ class BoardFormatterTest extends Base
$this->assertSame(0, $board[2]['columns'][3]['nb_tasks']); $this->assertSame(0, $board[2]['columns'][3]['nb_tasks']);
$this->assertEquals('Task 8', $board[2]['columns'][2]['tasks'][0]['title']); $this->assertEquals('Task 8', $board[2]['columns'][2]['tasks'][0]['title']);
$this->assertArrayHasKey('is_draggable', $board[2]['columns'][2]['tasks'][0]);
} }
public function testFormatWithoutDefaultSwimlane() public function testFormatWithoutDefaultSwimlane()

View File

@@ -0,0 +1,94 @@
<?php
use Kanboard\Core\Security\Role;
use Kanboard\Helper\BoardHelper;
use Kanboard\Model\ColumnMoveRestrictionModel;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\ProjectRoleModel;
use Kanboard\Model\ProjectUserRoleModel;
use Kanboard\Model\TaskCreationModel;
use Kanboard\Model\TaskFinderModel;
use Kanboard\Model\TaskStatusModel;
use Kanboard\Model\UserModel;
require_once __DIR__.'/../Base.php';
class BoardHelperTest extends Base
{
public function testIsDraggableWithProjectMember()
{
$boardHelper = new BoardHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertTrue($projectUserRole->addUser(1, 2, Role::PROJECT_MEMBER));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
$task = $taskFinderModel->getById(1);
$this->assertTrue($boardHelper->isDraggable($task));
}
public function testIsDraggableWithClosedTask()
{
$boardHelper = new BoardHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$taskStatusModel = new TaskStatusModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertTrue($projectUserRole->addUser(1, 2, Role::PROJECT_MEMBER));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
$this->assertTrue($taskStatusModel->close(1));
$task = $taskFinderModel->getById(1);
$this->assertFalse($boardHelper->isDraggable($task));
}
public function testIsDraggableWithColumnRestrictions()
{
$boardHelper = new BoardHelper($this->container);
$projectModel = new ProjectModel($this->container);
$taskCreationModel = new TaskCreationModel($this->container);
$taskFinderModel = new TaskFinderModel($this->container);
$projectUserRole = new ProjectUserRoleModel($this->container);
$userModel = new UserModel($this->container);
$projectRoleModel = new ProjectRoleModel($this->container);
$columnMoveRestrictionModel = new ColumnMoveRestrictionModel($this->container);
$this->container['sessionStorage']->user = array(
'id' => 2,
'role' => Role::APP_USER,
);
$this->assertEquals(2, $userModel->create(array('username' => 'user')));
$this->assertEquals(1, $projectModel->create(array('name' => 'Test')));
$this->assertEquals(1, $projectRoleModel->create(1, 'Custom Role'));
$this->assertEquals(1, $columnMoveRestrictionModel->create(1, 1, 2, 3));
$this->assertTrue($projectUserRole->addUser(1, 2, 'Custom Role'));
$this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'column_id' => 2)));
$task = $taskFinderModel->getById(1);
$this->assertFalse($boardHelper->isDraggable($task));
}
}