From 75470c72428c8d8f278d160369558ab31b137fb1 Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Thu, 8 Sep 2016 22:33:16 -0400 Subject: Apply column restrictions to the board --- app/Controller/BoardAjaxController.php | 12 +- app/Core/Base.php | 327 +++++++++++---------- app/Core/Helper.php | 1 + .../ColumnMoveRestrictionCacheDecorator.php | 57 ++++ app/Formatter/BoardTaskFormatter.php | 5 + app/Helper/BoardHelper.php | 16 + app/Model/ColumnMoveRestrictionModel.php | 17 +- app/ServiceProvider/CacheProvider.php | 8 + app/ServiceProvider/ClassProvider.php | 2 + app/Template/board/task_private.php | 3 +- assets/js/app.min.js | 2 +- assets/js/src/BoardDragAndDrop.js | 7 +- tests/units/Formatter/BoardFormatterTest.php | 1 + tests/units/Helper/BoardHelperTest.php | 94 ++++++ 14 files changed, 383 insertions(+), 169 deletions(-) create mode 100644 app/Decorator/ColumnMoveRestrictionCacheDecorator.php create mode 100644 tests/units/Helper/BoardHelperTest.php diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php index ccd47667..5e771fd6 100644 --- a/app/Controller/BoardAjaxController.php +++ b/app/Controller/BoardAjaxController.php @@ -28,11 +28,21 @@ class BoardAjaxController extends BaseController } $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( $project_id, $values['task_id'], - $values['column_id'], + $values['dst_column_id'], $values['position'], $values['swimlane_id'] ); diff --git a/app/Core/Base.php b/app/Core/Base.php index 3b7c5e66..747b1917 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -10,168 +10,171 @@ use Pimple\Container; * @package core * @author Frederic Guillot * - * @property \Kanboard\Analytic\TaskDistributionAnalytic $taskDistributionAnalytic - * @property \Kanboard\Analytic\UserDistributionAnalytic $userDistributionAnalytic - * @property \Kanboard\Analytic\EstimatedTimeComparisonAnalytic $estimatedTimeComparisonAnalytic - * @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic - * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic - * @property \Kanboard\Core\Action\ActionManager $actionManager - * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager - * @property \Kanboard\Core\Cache\MemoryCache $memoryCache - * @property \Kanboard\Core\Cache\BaseCache $cacheDriver - * @property \Kanboard\Core\Event\EventManager $eventManager - * @property \Kanboard\Core\Group\GroupManager $groupManager - * @property \Kanboard\Core\Http\Client $httpClient - * @property \Kanboard\Core\Http\OAuth2 $oauth - * @property \Kanboard\Core\Http\RememberMeCookie $rememberMeCookie - * @property \Kanboard\Core\Http\Request $request - * @property \Kanboard\Core\Http\Response $response - * @property \Kanboard\Core\Http\Router $router - * @property \Kanboard\Core\Http\Route $route - * @property \Kanboard\Core\Queue\QueueManager $queueManager - * @property \Kanboard\Core\Mail\Client $emailClient - * @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage - * @property \Kanboard\Core\Plugin\Hook $hook - * @property \Kanboard\Core\Plugin\Loader $pluginLoader - * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager - * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap - * @property \Kanboard\Core\Security\AccessMap $projectAccessMap - * @property \Kanboard\Core\Security\AccessMap $apiAccessMap - * @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap - * @property \Kanboard\Core\Security\Authorization $applicationAuthorization - * @property \Kanboard\Core\Security\Authorization $projectAuthorization - * @property \Kanboard\Core\Security\Authorization $apiAuthorization - * @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization - * @property \Kanboard\Core\Security\Role $role - * @property \Kanboard\Core\Security\Token $token - * @property \Kanboard\Core\Session\FlashMessage $flash - * @property \Kanboard\Core\Session\SessionManager $sessionManager - * @property \Kanboard\Core\Session\SessionStorage $sessionStorage - * @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager - * @property \Kanboard\Core\User\GroupSync $groupSync - * @property \Kanboard\Core\User\UserProfile $userProfile - * @property \Kanboard\Core\User\UserSync $userSync - * @property \Kanboard\Core\User\UserSession $userSession - * @property \Kanboard\Core\DateParser $dateParser - * @property \Kanboard\Core\Helper $helper - * @property \Kanboard\Core\Paginator $paginator - * @property \Kanboard\Core\Template $template - * @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator - * @property \Kanboard\Model\ActionModel $actionModel - * @property \Kanboard\Model\ActionParameterModel $actionParameterModel - * @property \Kanboard\Model\AvatarFileModel $avatarFileModel - * @property \Kanboard\Model\BoardModel $boardModel - * @property \Kanboard\Model\CategoryModel $categoryModel - * @property \Kanboard\Model\ColorModel $colorModel - * @property \Kanboard\Model\ColumnModel $columnModel - * @property \Kanboard\Model\CommentModel $commentModel - * @property \Kanboard\Model\ConfigModel $configModel - * @property \Kanboard\Model\CurrencyModel $currencyModel - * @property \Kanboard\Model\CustomFilterModel $customFilterModel - * @property \Kanboard\Model\TaskFileModel $taskFileModel - * @property \Kanboard\Model\ProjectFileModel $projectFileModel - * @property \Kanboard\Model\GroupModel $groupModel - * @property \Kanboard\Model\GroupMemberModel $groupMemberModel - * @property \Kanboard\Model\LanguageModel $languageModel - * @property \Kanboard\Model\LastLoginModel $lastLoginModel - * @property \Kanboard\Model\LinkModel $linkModel - * @property \Kanboard\Model\NotificationModel $notificationModel - * @property \Kanboard\Model\PasswordResetModel $passwordResetModel - * @property \Kanboard\Model\ProjectModel $projectModel - * @property \Kanboard\Model\ProjectActivityModel $projectActivityModel - * @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel - * @property \Kanboard\Model\ProjectDailyColumnStatsModel $projectDailyColumnStatsModel - * @property \Kanboard\Model\ProjectDailyStatsModel $projectDailyStatsModel - * @property \Kanboard\Model\ProjectMetadataModel $projectMetadataModel - * @property \Kanboard\Model\ProjectPermissionModel $projectPermissionModel - * @property \Kanboard\Model\ProjectUserRoleModel $projectUserRoleModel - * @property \Kanboard\Model\ProjectGroupRoleModel $projectGroupRoleModel - * @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel - * @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel - * @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel - * @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel - * @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel - * @property \Kanboard\Model\SubtaskModel $subtaskModel - * @property \Kanboard\Model\SubtaskPositionModel $subtaskPositionModel - * @property \Kanboard\Model\SubtaskStatusModel $subtaskStatusModel - * @property \Kanboard\Model\SubtaskTaskConversionModel $subtaskTaskConversionModel - * @property \Kanboard\Model\SubtaskTimeTrackingModel $subtaskTimeTrackingModel - * @property \Kanboard\Model\SwimlaneModel $swimlaneModel - * @property \Kanboard\Model\TagDuplicationModel $tagDuplicationModel - * @property \Kanboard\Model\TagModel $tagModel - * @property \Kanboard\Model\TaskModel $taskModel - * @property \Kanboard\Model\TaskAnalyticModel $taskAnalyticModel - * @property \Kanboard\Model\TaskCreationModel $taskCreationModel - * @property \Kanboard\Model\TaskDuplicationModel $taskDuplicationModel - * @property \Kanboard\Model\TaskProjectDuplicationModel $taskProjectDuplicationModel - * @property \Kanboard\Model\TaskProjectMoveModel $taskProjectMoveModel - * @property \Kanboard\Model\TaskRecurrenceModel $taskRecurrenceModel - * @property \Kanboard\Model\TaskExternalLinkModel $taskExternalLinkModel - * @property \Kanboard\Model\TaskFinderModel $taskFinderModel - * @property \Kanboard\Model\TaskLinkModel $taskLinkModel - * @property \Kanboard\Model\TaskModificationModel $taskModificationModel - * @property \Kanboard\Model\TaskPositionModel $taskPositionModel - * @property \Kanboard\Model\TaskStatusModel $taskStatusModel - * @property \Kanboard\Model\TaskTagModel $taskTagModel - * @property \Kanboard\Model\TaskMetadataModel $taskMetadataModel - * @property \Kanboard\Model\TimezoneModel $timezoneModel - * @property \Kanboard\Model\TransitionModel $transitionModel - * @property \Kanboard\Model\UserModel $userModel - * @property \Kanboard\Model\UserLockingModel $userLockingModel - * @property \Kanboard\Model\UserMentionModel $userMentionModel - * @property \Kanboard\Model\UserNotificationModel $userNotificationModel - * @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel - * @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel - * @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel - * @property \Kanboard\Model\UserMetadataModel $userMetadataModel - * @property \Kanboard\Pagination\TaskPagination $taskPagination - * @property \Kanboard\Pagination\SubtaskPagination $subtaskPagination - * @property \Kanboard\Pagination\ProjectPagination $projectPagination - * @property \Kanboard\Pagination\UserPagination $userPagination - * @property \Kanboard\Validator\ActionValidator $actionValidator - * @property \Kanboard\Validator\AuthValidator $authValidator - * @property \Kanboard\Validator\ColumnValidator $columnValidator - * @property \Kanboard\Validator\CategoryValidator $categoryValidator - * @property \Kanboard\Validator\CommentValidator $commentValidator - * @property \Kanboard\Validator\CurrencyValidator $currencyValidator - * @property \Kanboard\Validator\CustomFilterValidator $customFilterValidator - * @property \Kanboard\Validator\ExternalLinkValidator $externalLinkValidator - * @property \Kanboard\Validator\GroupValidator $groupValidator - * @property \Kanboard\Validator\LinkValidator $linkValidator - * @property \Kanboard\Validator\PasswordResetValidator $passwordResetValidator - * @property \Kanboard\Validator\ProjectValidator $projectValidator - * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator - * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator - * @property \Kanboard\Validator\TagValidator $tagValidator - * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator - * @property \Kanboard\Validator\TaskValidator $taskValidator - * @property \Kanboard\Validator\UserValidator $userValidator - * @property \Kanboard\Import\TaskImport $taskImport - * @property \Kanboard\Import\UserImport $userImport - * @property \Kanboard\Export\SubtaskExport $subtaskExport - * @property \Kanboard\Export\TaskExport $taskExport - * @property \Kanboard\Export\TransitionExport $transitionExport - * @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery - * @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery - * @property \Kanboard\Core\Filter\QueryBuilder $projectActivityQuery - * @property \Kanboard\Core\Filter\QueryBuilder $userQuery - * @property \Kanboard\Core\Filter\QueryBuilder $projectQuery - * @property \Kanboard\Core\Filter\QueryBuilder $taskQuery - * @property \Kanboard\Core\Filter\LexerBuilder $taskLexer - * @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer - * @property \Kanboard\Job\CommentEventJob $commentEventJob - * @property \Kanboard\Job\SubtaskEventJob $subtaskEventJob - * @property \Kanboard\Job\TaskEventJob $taskEventJob - * @property \Kanboard\Job\TaskFileEventJob $taskFileEventJob - * @property \Kanboard\Job\TaskLinkEventJob $taskLinkEventJob - * @property \Kanboard\Job\ProjectFileEventJob $projectFileEventJob - * @property \Kanboard\Job\NotificationJob $notificationJob - * @property \Kanboard\Job\ProjectMetricJob $projectMetricJob - * @property \Psr\Log\LoggerInterface $logger - * @property \PicoDb\Database $db - * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher - * @property \Symfony\Component\Console\Application $cli - * @property \JsonRPC\Server $api + * @property \Kanboard\Analytic\TaskDistributionAnalytic $taskDistributionAnalytic + * @property \Kanboard\Analytic\UserDistributionAnalytic $userDistributionAnalytic + * @property \Kanboard\Analytic\EstimatedTimeComparisonAnalytic $estimatedTimeComparisonAnalytic + * @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic + * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic + * @property \Kanboard\Core\Action\ActionManager $actionManager + * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager + * @property \Kanboard\Core\Cache\MemoryCache $memoryCache + * @property \Kanboard\Core\Cache\BaseCache $cacheDriver + * @property \Kanboard\Core\Event\EventManager $eventManager + * @property \Kanboard\Core\Group\GroupManager $groupManager + * @property \Kanboard\Core\Http\Client $httpClient + * @property \Kanboard\Core\Http\OAuth2 $oauth + * @property \Kanboard\Core\Http\RememberMeCookie $rememberMeCookie + * @property \Kanboard\Core\Http\Request $request + * @property \Kanboard\Core\Http\Response $response + * @property \Kanboard\Core\Http\Router $router + * @property \Kanboard\Core\Http\Route $route + * @property \Kanboard\Core\Queue\QueueManager $queueManager + * @property \Kanboard\Core\Mail\Client $emailClient + * @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage + * @property \Kanboard\Core\Plugin\Hook $hook + * @property \Kanboard\Core\Plugin\Loader $pluginLoader + * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager + * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap + * @property \Kanboard\Core\Security\AccessMap $projectAccessMap + * @property \Kanboard\Core\Security\AccessMap $apiAccessMap + * @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap + * @property \Kanboard\Core\Security\Authorization $applicationAuthorization + * @property \Kanboard\Core\Security\Authorization $projectAuthorization + * @property \Kanboard\Core\Security\Authorization $apiAuthorization + * @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization + * @property \Kanboard\Core\Security\Role $role + * @property \Kanboard\Core\Security\Token $token + * @property \Kanboard\Core\Session\FlashMessage $flash + * @property \Kanboard\Core\Session\SessionManager $sessionManager + * @property \Kanboard\Core\Session\SessionStorage $sessionStorage + * @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager + * @property \Kanboard\Core\User\GroupSync $groupSync + * @property \Kanboard\Core\User\UserProfile $userProfile + * @property \Kanboard\Core\User\UserSync $userSync + * @property \Kanboard\Core\User\UserSession $userSession + * @property \Kanboard\Core\DateParser $dateParser + * @property \Kanboard\Core\Helper $helper + * @property \Kanboard\Core\Paginator $paginator + * @property \Kanboard\Core\Template $template + * @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator + * @property \Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator $columnMoveRestrictionCacheDecorator + * @property \Kanboard\Model\ActionModel $actionModel + * @property \Kanboard\Model\ActionParameterModel $actionParameterModel + * @property \Kanboard\Model\AvatarFileModel $avatarFileModel + * @property \Kanboard\Model\BoardModel $boardModel + * @property \Kanboard\Model\CategoryModel $categoryModel + * @property \Kanboard\Model\ColorModel $colorModel + * @property \Kanboard\Model\ColumnModel $columnModel + * @property \Kanboard\Model\ColumnMoveRestrictionModel $columnMoveRestrictionModel + * @property \Kanboard\Model\CommentModel $commentModel + * @property \Kanboard\Model\ConfigModel $configModel + * @property \Kanboard\Model\CurrencyModel $currencyModel + * @property \Kanboard\Model\CustomFilterModel $customFilterModel + * @property \Kanboard\Model\TaskFileModel $taskFileModel + * @property \Kanboard\Model\ProjectFileModel $projectFileModel + * @property \Kanboard\Model\GroupModel $groupModel + * @property \Kanboard\Model\GroupMemberModel $groupMemberModel + * @property \Kanboard\Model\LanguageModel $languageModel + * @property \Kanboard\Model\LastLoginModel $lastLoginModel + * @property \Kanboard\Model\LinkModel $linkModel + * @property \Kanboard\Model\NotificationModel $notificationModel + * @property \Kanboard\Model\PasswordResetModel $passwordResetModel + * @property \Kanboard\Model\ProjectModel $projectModel + * @property \Kanboard\Model\ProjectActivityModel $projectActivityModel + * @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel + * @property \Kanboard\Model\ProjectDailyColumnStatsModel $projectDailyColumnStatsModel + * @property \Kanboard\Model\ProjectDailyStatsModel $projectDailyStatsModel + * @property \Kanboard\Model\ProjectMetadataModel $projectMetadataModel + * @property \Kanboard\Model\ProjectPermissionModel $projectPermissionModel + * @property \Kanboard\Model\ProjectUserRoleModel $projectUserRoleModel + * @property \Kanboard\Model\ProjectGroupRoleModel $projectGroupRoleModel + * @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel + * @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel + * @property \Kanboard\Model\ProjectRoleModel $projectRoleModel + * @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel + * @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel + * @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel + * @property \Kanboard\Model\SubtaskModel $subtaskModel + * @property \Kanboard\Model\SubtaskPositionModel $subtaskPositionModel + * @property \Kanboard\Model\SubtaskStatusModel $subtaskStatusModel + * @property \Kanboard\Model\SubtaskTaskConversionModel $subtaskTaskConversionModel + * @property \Kanboard\Model\SubtaskTimeTrackingModel $subtaskTimeTrackingModel + * @property \Kanboard\Model\SwimlaneModel $swimlaneModel + * @property \Kanboard\Model\TagDuplicationModel $tagDuplicationModel + * @property \Kanboard\Model\TagModel $tagModel + * @property \Kanboard\Model\TaskModel $taskModel + * @property \Kanboard\Model\TaskAnalyticModel $taskAnalyticModel + * @property \Kanboard\Model\TaskCreationModel $taskCreationModel + * @property \Kanboard\Model\TaskDuplicationModel $taskDuplicationModel + * @property \Kanboard\Model\TaskProjectDuplicationModel $taskProjectDuplicationModel + * @property \Kanboard\Model\TaskProjectMoveModel $taskProjectMoveModel + * @property \Kanboard\Model\TaskRecurrenceModel $taskRecurrenceModel + * @property \Kanboard\Model\TaskExternalLinkModel $taskExternalLinkModel + * @property \Kanboard\Model\TaskFinderModel $taskFinderModel + * @property \Kanboard\Model\TaskLinkModel $taskLinkModel + * @property \Kanboard\Model\TaskModificationModel $taskModificationModel + * @property \Kanboard\Model\TaskPositionModel $taskPositionModel + * @property \Kanboard\Model\TaskStatusModel $taskStatusModel + * @property \Kanboard\Model\TaskTagModel $taskTagModel + * @property \Kanboard\Model\TaskMetadataModel $taskMetadataModel + * @property \Kanboard\Model\TimezoneModel $timezoneModel + * @property \Kanboard\Model\TransitionModel $transitionModel + * @property \Kanboard\Model\UserModel $userModel + * @property \Kanboard\Model\UserLockingModel $userLockingModel + * @property \Kanboard\Model\UserMentionModel $userMentionModel + * @property \Kanboard\Model\UserNotificationModel $userNotificationModel + * @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel + * @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel + * @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel + * @property \Kanboard\Model\UserMetadataModel $userMetadataModel + * @property \Kanboard\Pagination\TaskPagination $taskPagination + * @property \Kanboard\Pagination\SubtaskPagination $subtaskPagination + * @property \Kanboard\Pagination\ProjectPagination $projectPagination + * @property \Kanboard\Pagination\UserPagination $userPagination + * @property \Kanboard\Validator\ActionValidator $actionValidator + * @property \Kanboard\Validator\AuthValidator $authValidator + * @property \Kanboard\Validator\ColumnValidator $columnValidator + * @property \Kanboard\Validator\CategoryValidator $categoryValidator + * @property \Kanboard\Validator\CommentValidator $commentValidator + * @property \Kanboard\Validator\CurrencyValidator $currencyValidator + * @property \Kanboard\Validator\CustomFilterValidator $customFilterValidator + * @property \Kanboard\Validator\ExternalLinkValidator $externalLinkValidator + * @property \Kanboard\Validator\GroupValidator $groupValidator + * @property \Kanboard\Validator\LinkValidator $linkValidator + * @property \Kanboard\Validator\PasswordResetValidator $passwordResetValidator + * @property \Kanboard\Validator\ProjectValidator $projectValidator + * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator + * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator + * @property \Kanboard\Validator\TagValidator $tagValidator + * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator + * @property \Kanboard\Validator\TaskValidator $taskValidator + * @property \Kanboard\Validator\UserValidator $userValidator + * @property \Kanboard\Import\TaskImport $taskImport + * @property \Kanboard\Import\UserImport $userImport + * @property \Kanboard\Export\SubtaskExport $subtaskExport + * @property \Kanboard\Export\TaskExport $taskExport + * @property \Kanboard\Export\TransitionExport $transitionExport + * @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery + * @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery + * @property \Kanboard\Core\Filter\QueryBuilder $projectActivityQuery + * @property \Kanboard\Core\Filter\QueryBuilder $userQuery + * @property \Kanboard\Core\Filter\QueryBuilder $projectQuery + * @property \Kanboard\Core\Filter\QueryBuilder $taskQuery + * @property \Kanboard\Core\Filter\LexerBuilder $taskLexer + * @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer + * @property \Kanboard\Job\CommentEventJob $commentEventJob + * @property \Kanboard\Job\SubtaskEventJob $subtaskEventJob + * @property \Kanboard\Job\TaskEventJob $taskEventJob + * @property \Kanboard\Job\TaskFileEventJob $taskFileEventJob + * @property \Kanboard\Job\TaskLinkEventJob $taskLinkEventJob + * @property \Kanboard\Job\ProjectFileEventJob $projectFileEventJob + * @property \Kanboard\Job\NotificationJob $notificationJob + * @property \Kanboard\Job\ProjectMetricJob $projectMetricJob + * @property \Psr\Log\LoggerInterface $logger + * @property \PicoDb\Database $db + * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher + * @property \Symfony\Component\Console\Application $cli + * @property \JsonRPC\Server $api */ abstract class Base { diff --git a/app/Core/Helper.php b/app/Core/Helper.php index 43151be8..c98b3c5e 100644 --- a/app/Core/Helper.php +++ b/app/Core/Helper.php @@ -12,6 +12,7 @@ use Pimple\Container; * * @property \Kanboard\Helper\AppHelper $app * @property \Kanboard\Helper\AssetHelper $asset + * @property \Kanboard\Helper\BoardHelper $board * @property \Kanboard\Helper\CalendarHelper $calendar * @property \Kanboard\Helper\DateHelper $dt * @property \Kanboard\Helper\FileHelper $file diff --git a/app/Decorator/ColumnMoveRestrictionCacheDecorator.php b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php new file mode 100644 index 00000000..331bdebb --- /dev/null +++ b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php @@ -0,0 +1,57 @@ +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; + } +} diff --git a/app/Formatter/BoardTaskFormatter.php b/app/Formatter/BoardTaskFormatter.php index 3bf171b1..5956ae35 100644 --- a/app/Formatter/BoardTaskFormatter.php +++ b/app/Formatter/BoardTaskFormatter.php @@ -79,6 +79,11 @@ class BoardTaskFormatter extends BaseFormatter implements FormatterInterface { $tasks = array_values(array_filter($this->tasks, array($this, 'filterTasks'))); array_merge_relation($tasks, $this->tags, 'tags', 'id'); + + foreach ($tasks as &$task) { + $task['is_draggable'] = $this->helper->board->isDraggable($task); + } + return $tasks; } diff --git a/app/Helper/BoardHelper.php b/app/Helper/BoardHelper.php index f5df3db2..c3d28dc4 100644 --- a/app/Helper/BoardHelper.php +++ b/app/Helper/BoardHelper.php @@ -24,4 +24,20 @@ class BoardHelper extends Base { 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; + } } diff --git a/app/Model/ColumnMoveRestrictionModel.php b/app/Model/ColumnMoveRestrictionModel.php index fa44edd1..63e739bf 100644 --- a/app/Model/ColumnMoveRestrictionModel.php +++ b/app/Model/ColumnMoveRestrictionModel.php @@ -42,7 +42,8 @@ class ColumnMoveRestrictionModel extends Base */ public function getAll($project_id) { - return $this->db->table(self::TABLE) + return $this->db + ->table(self::TABLE) ->columns( 'restriction_id', 'src_column_id', @@ -58,6 +59,20 @@ class ColumnMoveRestrictionModel extends Base ->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 * diff --git a/app/ServiceProvider/CacheProvider.php b/app/ServiceProvider/CacheProvider.php index fac44d53..90d63f81 100644 --- a/app/ServiceProvider/CacheProvider.php +++ b/app/ServiceProvider/CacheProvider.php @@ -4,6 +4,7 @@ namespace Kanboard\ServiceProvider; use Kanboard\Core\Cache\FileCache; use Kanboard\Core\Cache\MemoryCache; +use Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator; use Kanboard\Decorator\MetadataCacheDecorator; use Pimple\Container; use Pimple\ServiceProviderInterface; @@ -46,6 +47,13 @@ class CacheProvider implements ServiceProviderInterface ); }; + $container['columnMoveRestrictionCacheDecorator'] = function($c) { + return new ColumnMoveRestrictionCacheDecorator( + $c['memoryCache'], + $c['columnMoveRestrictionModel'] + ); + }; + return $container; } } diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php index d837500a..e79ffcee 100644 --- a/app/ServiceProvider/ClassProvider.php +++ b/app/ServiceProvider/ClassProvider.php @@ -34,6 +34,7 @@ class ClassProvider implements ServiceProviderInterface 'CategoryModel', 'ColorModel', 'ColumnModel', + 'ColumnMoveRestrictionModel', 'CommentModel', 'ConfigModel', 'CurrencyModel', @@ -55,6 +56,7 @@ class ClassProvider implements ServiceProviderInterface 'ProjectNotificationModel', 'ProjectMetadataModel', 'ProjectGroupRoleModel', + 'ProjectRoleModel', 'ProjectTaskDuplicationModel', 'ProjectTaskPriorityModel', 'ProjectUserRoleModel', diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php index 94b396a6..01da46db 100644 --- a/app/Template/board/task_private.php +++ b/app/Template/board/task_private.php @@ -1,6 +1,7 @@
',ready:function(){for(var t=[],e=0;e',ready:function(){var t=[this.labelSpent],e=[this.labelEstimated],o=[];for(var a in this.metrics)t.push(this.metrics[a].time_spent),e.push(this.metrics[a].time_estimated),o.push("open"===a?this.labelOpen:this.labelClosed);c3.generate({data:{columns:[t,e],type:"bar"},bar:{width:{ratio:.2}},axis:{x:{type:"category",categories:o}},legend:{show:!0}})}}),Vue.component("chart-project-user-distribution",{props:["metrics"],template:'
',ready:function(){for(var t=[],e=0;e {{ labelOr }} {{ labelCancel }}',data:function(){return{loading:!1}},computed:{isLoading:function(){return this.loading}},methods:{onSubmit:function(){this.loading=!0,this.callback()},onCancel:function(){_KB.get("Popover").close()}}}),Vue.component("task-move-position",{props:["board","saveUrl"],template:"#template-task-move-position",data:function(){return{swimlaneId:0,columnId:0,position:1,columns:[],tasks:[],positionChoice:"before"}},ready:function(){this.columns=this.board[0].columns,this.columnId=this.columns[0].id,this.tasks=this.columns[0].tasks},methods:{onChangeSwimlane:function(){var t=this;this.columnId=0,this.position=1,this.columns=[],this.tasks=[],this.positionChoice="before",this.board.forEach(function(e){e.id===t.swimlaneId&&(t.columns=e.columns,t.tasks=t.columns[0].tasks,t.columnId=t.columns[0].id)})},onChangeColumn:function(){var t=this;this.position=1,this.tasks=[],this.positionChoice="before",this.columns.forEach(function(e){e.id==t.columnId&&(t.tasks=e.tasks)})},onSubmit:function(){"after"==this.positionChoice&&this.position++,$.ajax({cache:!1,url:this.saveUrl,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:this.columnId,swimlane_id:this.swimlaneId,position:this.position}),complete:function(){window.location.reload(!0)}})}}});var Kanboard={};Kanboard.Accordion=function(t){this.app=t},Kanboard.Accordion.prototype.listen=function(){$(document).on("click",".accordion-toggle",function(t){var e=$(this).parents(".accordion-section");t.preventDefault(),e.hasClass("accordion-collapsed")?(e.find(".accordion-content").show(),e.removeClass("accordion-collapsed")):(e.find(".accordion-content").hide(),e.addClass("accordion-collapsed"))})},Kanboard.App=function(){this.controllers={}},Kanboard.App.prototype.get=function(t){return this.controllers[t]},Kanboard.App.prototype.execute=function(){for(var t in Kanboard)if("App"!==t){var e=new Kanboard[t](this);this.controllers[t]=e,"function"==typeof e.execute&&e.execute(),"function"==typeof e.listen&&e.listen(),"function"==typeof e.focus&&e.focus(),"function"==typeof e.keyboardShortcuts&&e.keyboardShortcuts()}this.focus(),this.chosen(),this.keyboardShortcuts(),this.datePicker(),this.autoComplete(),this.tagAutoComplete(),new Vue({el:"body"})},Kanboard.App.prototype.keyboardShortcuts=function(){var t=this;Mousetrap.bindGlobal("mod+enter",function(){var e=$("form");1==e.length?e.submit():e.length>1&&("INPUT"===document.activeElement.tagName||"TEXTAREA"===document.activeElement.tagName?$(document.activeElement).parents("form").submit():t.get("Popover").isOpen()&&$("#popover-container form").submit())}),Mousetrap.bind("b",function(t){t.preventDefault(),$("#board-selector").trigger("chosen:open")}),Mousetrap.bindGlobal("esc",function(){t.get("Popover").close(),t.get("Dropdown").close()}),Mousetrap.bind("?",function(){t.get("Popover").open($("body").data("keyboard-shortcut-url"))})},Kanboard.App.prototype.focus=function(){$(document).on("focus",".auto-select",function(){$(this).select()}),$(document).on("mouseup",".auto-select",function(t){t.preventDefault()})},Kanboard.App.prototype.chosen=function(){$(".chosen-select").each(function(){var t=$(this).data("search-threshold");void 0===t&&(t=10),$(this).chosen({width:"180px",no_results_text:$(this).data("notfound"),disable_search_threshold:t})}),$(".select-auto-redirect").change(function(){var t=new RegExp($(this).data("redirect-regex"),"g");window.location=$(this).data("redirect-url").replace(t,$(this).val())})},Kanboard.App.prototype.datePicker=function(){var t=$("body"),e=t.data("js-date-format"),o=t.data("js-time-format"),a=t.data("js-lang");$.datepicker.setDefaults($.datepicker.regional[a]),$.timepicker.setDefaults($.timepicker.regional[a]),$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:e,constrainInput:!1}),$(".form-datetime").datetimepicker({dateFormat:e,timeFormat:o,constrainInput:!1})},Kanboard.App.prototype.tagAutoComplete=function(){$(".tag-autocomplete").select2({tags:!0})},Kanboard.App.prototype.autoComplete=function(){$(".autocomplete").each(function(){var t=$(this),e=t.data("dst-field"),o=t.data("dst-extra-field");""==$("#form-"+e).val()&&t.parent().find("button[type=submit]").attr("disabled","disabled"),t.autocomplete({source:t.data("search-url"),minLength:1,select:function(a,n){$("input[name="+e+"]").val(n.item.id),o&&$("input[name="+o+"]").val(n.item[o]),t.parent().find("button[type=submit]").removeAttr("disabled")}})})},Kanboard.App.prototype.hasId=function(t){return!!document.getElementById(t)},Kanboard.App.prototype.showLoadingIcon=function(){$("body").append(' ')},Kanboard.App.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()},Kanboard.App.prototype.formatDuration=function(t){return t>=86400?Math.round(t/86400)+"d":t>=3600?Math.round(t/3600)+"h":t>=60?Math.round(t/60)+"m":t+"s"},Kanboard.App.prototype.isVisible=function(){var t="";return"undefined"!=typeof document.hidden?t="visibilityState":"undefined"!=typeof document.mozHidden?t="mozVisibilityState":"undefined"!=typeof document.msHidden?t="msVisibilityState":"undefined"!=typeof document.webkitHidden&&(t="webkitVisibilityState"),""==t||"visible"==document[t]},Kanboard.AvgTimeColumnChart=function(t){this.app=t},Kanboard.AvgTimeColumnChart.prototype.execute=function(){this.app.hasId("analytic-avg-time-column")&&this.show()},Kanboard.AvgTimeColumnChart.prototype.show=function(){var t=$("#chart"),e=t.data("metrics"),o=[t.data("label")],a=[];for(var n in e)o.push(e[n].average),a.push(e[n].title);c3.generate({data:{columns:[o],type:"bar"},bar:{width:{ratio:.5}},axis:{x:{type:"category",categories:a},y:{tick:{format:this.app.formatDuration}}},legend:{show:!1}})},Kanboard.BoardCollapsedMode=function(t){this.app=t},Kanboard.BoardCollapsedMode.prototype.keyboardShortcuts=function(){var t=this;t.app.hasId("board")&&Mousetrap.bind("s",function(){t.toggle()})},Kanboard.BoardCollapsedMode.prototype.toggle=function(){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr("href"),success:function(e){$(".filter-display-mode").toggle(),t.app.get("BoardDragAndDrop").refresh(e)}})},Kanboard.BoardColumnView=function(t){this.app=t},Kanboard.BoardColumnView.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardColumnView.prototype.listen=function(){var t=this;$(document).on("click",".board-toggle-column-view",function(){t.toggle($(this).data("column-id"))})},Kanboard.BoardColumnView.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardColumnView.prototype.render=function(){var t=this;$(".board-column-header").each(function(){var e=$(this).data("column-id");localStorage.getItem("hidden_column_"+e)&&t.hideColumn(e)})},Kanboard.BoardColumnView.prototype.toggle=function(t){localStorage.getItem("hidden_column_"+t)?this.showColumn(t):this.hideColumn(t)},Kanboard.BoardColumnView.prototype.hideColumn=function(t){$(".board-column-"+t+" .board-column-expanded").hide(),$(".board-column-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t+" .board-column-expanded").hide(),$(".board-column-header-"+t+" .board-column-collapsed").show(),$(".board-column-header-"+t).each(function(){$(this).removeClass("board-column-compact"),$(this).addClass("board-column-header-collapsed")}),$(".board-column-"+t).each(function(){$(this).addClass("board-column-task-collapsed")}),$(".board-column-"+t+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+t).height())}),localStorage.setItem("hidden_column_"+t,1)},Kanboard.BoardColumnView.prototype.showColumn=function(t){$(".board-column-"+t+" .board-column-expanded").show(),$(".board-column-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t+" .board-column-expanded").show(),$(".board-column-header-"+t+" .board-column-collapsed").hide(),$(".board-column-header-"+t).removeClass("board-column-header-collapsed"),$(".board-column-"+t).removeClass("board-column-task-collapsed"),0==localStorage.getItem("horizontal_scroll")&&$(".board-column-header-"+t).addClass("board-column-compact"),localStorage.removeItem("hidden_column_"+t)},Kanboard.BoardHorizontalScrolling=function(t){this.app=t},Kanboard.BoardHorizontalScrolling.prototype.execute=function(){this.app.hasId("board")&&this.render()},Kanboard.BoardHorizontalScrolling.prototype.listen=function(){var t=this;$(document).on("click",".filter-toggle-scrolling",function(e){e.preventDefault(),t.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.keyboardShortcuts=function(){var t=this;t.app.hasId("board")&&Mousetrap.bind("c",function(){t.toggle()})},Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered=function(){this.render()},Kanboard.BoardHorizontalScrolling.prototype.toggle=function(){var t=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",0==t?1:0),this.render()},Kanboard.BoardHorizontalScrolling.prototype.render=function(){0==localStorage.getItem("horizontal_scroll")?($(".filter-wide").show(),$(".filter-compact").hide(),$("#board-container").addClass("board-container-compact"),$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")):($(".filter-wide").hide(),$(".filter-compact").show(),$("#board-container").removeClass("board-container-compact"),$("#board th").removeClass("board-column-compact"))},Kanboard.BoardPolling=function(t){this.app=t},Kanboard.BoardPolling.prototype.execute=function(){if(this.app.hasId("board")){var t=parseInt($("#board").attr("data-check-interval"));t>0&&window.setInterval(this.check.bind(this),1e3*t)}},Kanboard.BoardPolling.prototype.check=function(){if(this.app.isVisible()&&!this.app.get("BoardDragAndDrop").savingInProgress){var t=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:$("#board").data("check-url"),statusCode:{200:function(e){t.app.get("BoardDragAndDrop").refresh(e)},304:function(){t.app.hideLoadingIcon()}}})}},Kanboard.BoardTask=function(t){this.app=t},Kanboard.BoardTask.prototype.listen=function(){var t=this;$(document).on("click",".task-board-change-assignee",function(e){e.preventDefault(),e.stopPropagation(),t.app.get("Popover").open($(this).data("url"))}),$(document).on("click",".task-board",function(t){"A"!=t.target.tagName&&"IMG"!=t.target.tagName&&(window.location=$(this).data("task-url"))})},Kanboard.BoardTask.prototype.keyboardShortcuts=function(){var t=this;t.app.hasId("board")&&Mousetrap.bind("n",function(){t.app.get("Popover").open($("#board").data("task-creation-url"))})},Kanboard.BurndownChart=function(t){this.app=t},Kanboard.BurndownChart.prototype.execute=function(){this.app.hasId("analytic-burndown")&&this.show()},Kanboard.BurndownChart.prototype.show=function(){for(var t=$("#chart"),e=t.data("metrics"),o=[[t.data("label-total")]],a=[],n=d3.time.format("%Y-%m-%d"),i=d3.time.format(t.data("date-format")),r=0;r0&&(void 0==o[0][r]&&o[0].push(0),o[0][r]+=e[r][s]),0==s&&a.push(i(n.parse(e[r][s]))));c3.generate({data:{columns:o},axis:{x:{type:"category",categories:a}}})},Kanboard.Calendar=function(t){this.app=t},Kanboard.Calendar.prototype.execute=function(){var t=$("#calendar");1==t.length&&this.show(t)},Kanboard.Calendar.prototype.show=function(t){t.fullCalendar({lang:$("body").data("js-lang"),editable:!0,eventLimit:!0,defaultView:"month",header:{left:"prev,next today",center:"title",right:"month,agendaWeek,agendaDay"},eventDrop:function(e){$.ajax({cache:!1,url:t.data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({task_id:e.id,date_due:e.start.format()})})},viewRender:function(){var e=t.data("check-url"),o={start:t.fullCalendar("getView").start.format(),end:t.fullCalendar("getView").end.format()};for(var a in o)e+="&"+a+"="+o[a];$.getJSON(e,function(e){t.fullCalendar("removeEvents"),t.fullCalendar("addEventSource",e),t.fullCalendar("rerenderEvents")})}})},Kanboard.Column=function(t){this.app=t},Kanboard.Column.prototype.listen=function(){this.dragAndDrop()},Kanboard.Column.prototype.dragAndDrop=function(){var t=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")}),$(".columns-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(t,e){return e.children().each(function(){$(this).width($(this).width())}),e},stop:function(e,o){var a=o.item;a.removeClass("draggable-item-selected"),t.savePosition(a.data("column-id"),a.index()+1)},start:function(t,e){e.item.addClass("draggable-item-selected")}}).disableSelection()},Kanboard.Column.prototype.savePosition=function(t,e){var o=$(".columns-table").data("save-position-url"),a=this;this.app.showLoadingIcon(),$.ajax({cache:!1,url:o,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({column_id:t,position:e}),complete:function(){a.app.hideLoadingIcon()}})},Kanboard.CumulativeFlowDiagram=function(t){this.app=t},Kanboard.CumulativeFlowDiagram.prototype.execute=function(){this.app.hasId("analytic-cfd")&&this.show()},Kanboard.CumulativeFlowDiagram.prototype.show=function(){for(var t=$("#chart"),e=t.data("metrics"),o=[],a=[],n=[],i=d3.time.format("%Y-%m-%d"),r=d3.time.format(t.data("date-format")),s=0;s0&&a.push(e[s][d])):(o[d].push(e[s][d]),0==d&&n.push(r(i.parse(e[s][d]))));c3.generate({data:{columns:o,type:"area-spline",groups:[a]},axis:{x:{type:"category",categories:n}}})},Kanboard.Dropdown=function(t){this.app=t},Kanboard.Dropdown.prototype.listen=function(){var t=this;$(document).on("click",function(){t.close()}),$(document).on("click",".dropdown-menu",function(e){e.preventDefault(),e.stopImmediatePropagation(),t.close();var o=$(this).next("ul"),a=$(this).offset();$("body").append(jQuery("
",{id:"dropdown"})),o.clone().appendTo("#dropdown");var n=$("#dropdown ul");n.addClass("dropdown-submenu-open");var i=n.outerHeight(),r=n.outerWidth();a.top+i-$(window).scrollTop()<$(window).height()||$(window).scrollTop()+a.top$(window).width()?n.css("left",a.left-r+$(this).outerWidth()):n.css("left",a.left)}),$(document).on("click",".dropdown-submenu-open li",function(t){$(t.target).is("li")&&$(this).find("a:visible")[0].click()})},Kanboard.Dropdown.prototype.close=function(){$("#dropdown").remove()},Kanboard.Dropdown.prototype.onPopoverOpened=function(){this.close()},Kanboard.FileUpload=function(t){this.app=t,this.files=[],this.currentFile=0},Kanboard.FileUpload.prototype.onPopoverOpened=function(){var t=document.getElementById("file-dropzone"),e=this;t&&(t.ondragover=t.ondragenter=function(t){t.stopPropagation(),t.preventDefault()},t.ondrop=function(t){t.stopPropagation(),t.preventDefault(),e.files=t.dataTransfer.files,e.show(),$("#file-error-max-size").hide()},$(document).on("click","#file-browser",function(t){t.preventDefault(),$("#file-form-element").get(0).click()}),$(document).on("click","#file-upload-button",function(t){t.preventDefault(),e.currentFile=0,e.checkFiles()}),$("#file-form-element").change(function(){e.files=document.getElementById("file-form-element").files,e.show(),$("#file-error-max-size").hide()}))},Kanboard.FileUpload.prototype.show=function(){if($("#file-list").remove(),this.files.length>0){$("#file-upload-button").prop("disabled",!1),$("#file-dropzone-inner").hide();for(var t=jQuery("