diff options
31 files changed, 233 insertions, 93 deletions
@@ -5,6 +5,7 @@ New features: * Add slideshow for images * Add API calls to manage tags +* Offer the possibility to override internal formatter objects from plugins Improvements: @@ -16,6 +17,7 @@ Improvements: Bug fixes: +* Fix compatibility issue with PHP 5.3 for array_combine function * Fix wrong controller name on project activity page when using filters Version 1.0.35 (Dec 4, 2016) diff --git a/app/Api/Procedure/BoardProcedure.php b/app/Api/Procedure/BoardProcedure.php index 674b5466..69daaf09 100644 --- a/app/Api/Procedure/BoardProcedure.php +++ b/app/Api/Procedure/BoardProcedure.php @@ -3,7 +3,6 @@ namespace Kanboard\Api\Procedure; use Kanboard\Api\Authorization\ProjectAuthorization; -use Kanboard\Formatter\BoardFormatter; /** * Board API controller @@ -17,7 +16,7 @@ class BoardProcedure extends BaseProcedure { ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id); - return BoardFormatter::getInstance($this->container) + return $this->boardFormatter ->withProjectId($project_id) ->withQuery($this->taskFinderModel->getExtendedQuery()) ->format(); diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php index 484ef67d..ecb76e9c 100644 --- a/app/Controller/BoardAjaxController.php +++ b/app/Controller/BoardAjaxController.php @@ -3,7 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; -use Kanboard\Formatter\BoardFormatter; use Kanboard\Model\UserMetadataModel; /** @@ -139,7 +138,7 @@ class BoardAjaxController extends BaseController 'board_highlight_period' => $this->configModel->get('board_highlight_period'), 'swimlanes' => $this->taskLexer ->build($this->userSession->getFilters($project_id)) - ->format(BoardFormatter::getInstance($this->container)->withProjectId($project_id)) + ->format($this->boardFormatter->withProjectId($project_id)) )); } } diff --git a/app/Controller/BoardViewController.php b/app/Controller/BoardViewController.php index 10165908..9ef77e54 100644 --- a/app/Controller/BoardViewController.php +++ b/app/Controller/BoardViewController.php @@ -3,7 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; -use Kanboard\Formatter\BoardFormatter; use Kanboard\Model\TaskModel; /** @@ -35,7 +34,7 @@ class BoardViewController extends BaseController $this->response->html($this->helper->layout->app('board/view_public', array( 'project' => $project, - 'swimlanes' => BoardFormatter::getInstance($this->container) + 'swimlanes' => $this->boardFormatter ->withProjectId($project['id']) ->withQuery($query) ->format() @@ -68,7 +67,7 @@ class BoardViewController extends BaseController 'board_highlight_period' => $this->configModel->get('board_highlight_period'), 'swimlanes' => $this->taskLexer ->build($search) - ->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id'])) + ->format($this->boardFormatter->withProjectId($project['id'])) ))); } } diff --git a/app/Controller/GroupAjaxController.php b/app/Controller/GroupAjaxController.php index 496e9ef2..308bba9e 100644 --- a/app/Controller/GroupAjaxController.php +++ b/app/Controller/GroupAjaxController.php @@ -2,8 +2,6 @@ namespace Kanboard\Controller; -use Kanboard\Formatter\GroupAutoCompleteFormatter; - /** * Group Ajax Controller * @@ -20,7 +18,7 @@ class GroupAjaxController extends BaseController public function autocomplete() { $search = $this->request->getStringParam('term'); - $formatter = new GroupAutoCompleteFormatter($this->groupManager->find($search)); - $this->response->json($formatter->format()); + $groups = $this->groupManager->find($search); + $this->response->json($this->groupAutoCompleteFormatter->withGroups($groups)->format()); } } diff --git a/app/Controller/ICalendarController.php b/app/Controller/ICalendarController.php index e354c6f1..4fe8b78a 100644 --- a/app/Controller/ICalendarController.php +++ b/app/Controller/ICalendarController.php @@ -7,7 +7,6 @@ use Kanboard\Core\Filter\QueryBuilder; use Kanboard\Filter\TaskAssigneeFilter; use Kanboard\Filter\TaskProjectFilter; use Kanboard\Filter\TaskStatusFilter; -use Kanboard\Formatter\TaskICalFormatter; use Kanboard\Model\TaskModel; use Eluceo\iCal\Component\Calendar as iCalendar; @@ -94,8 +93,6 @@ class ICalendarController extends BaseController $end = $this->request->getStringParam('end', strtotime('+6 months')); $this->helper->ical->addTaskDateDueEvents($queryBuilder, $calendar, $start, $end); - - $formatter = new TaskICalFormatter($this->container); - $this->response->ical($formatter->setCalendar($calendar)->format()); + $this->response->ical($this->taskICalFormatter->setCalendar($calendar)->format()); } } diff --git a/app/Controller/ProjectGanttController.php b/app/Controller/ProjectGanttController.php index a70d9eee..8239005e 100644 --- a/app/Controller/ProjectGanttController.php +++ b/app/Controller/ProjectGanttController.php @@ -5,7 +5,6 @@ namespace Kanboard\Controller; use Kanboard\Filter\ProjectIdsFilter; use Kanboard\Filter\ProjectStatusFilter; use Kanboard\Filter\ProjectTypeFilter; -use Kanboard\Formatter\ProjectGanttFormatter; use Kanboard\Model\ProjectModel; /** @@ -30,7 +29,7 @@ class ProjectGanttController extends BaseController $filter->getQuery()->asc(ProjectModel::TABLE.'.start_date'); $this->response->html($this->helper->layout->app('project_gantt/show', array( - 'projects' => $filter->format(new ProjectGanttFormatter($this->container)), + 'projects' => $filter->format($this->projectGanttFormatter), 'title' => t('Gantt chart for all projects'), ))); } diff --git a/app/Controller/TaskAjaxController.php b/app/Controller/TaskAjaxController.php index 609dd23c..6d0b3fc2 100644 --- a/app/Controller/TaskAjaxController.php +++ b/app/Controller/TaskAjaxController.php @@ -8,8 +8,6 @@ use Kanboard\Filter\TaskProjectsFilter; use Kanboard\Filter\TaskStartsWithIdFilter; use Kanboard\Filter\TaskStatusFilter; use Kanboard\Filter\TaskTitleFilter; -use Kanboard\Formatter\TaskAutoCompleteFormatter; -use Kanboard\Formatter\TaskSuggestMenuFormatter; use Kanboard\Model\TaskModel; /** @@ -46,7 +44,7 @@ class TaskAjaxController extends BaseController $filter->withFilter(new TaskTitleFilter($search)); } - $this->response->json($filter->format(new TaskAutoCompleteFormatter($this->container))); + $this->response->json($filter->format($this->taskAutoCompleteFormatter)); } } @@ -66,7 +64,7 @@ class TaskAjaxController extends BaseController ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) ->withFilter(new TaskStartsWithIdFilter($taskId)); - $this->response->json($filter->format(new TaskSuggestMenuFormatter($this->container))); + $this->response->json($filter->format($this->taskSuggestMenuFormatter)); } } } diff --git a/app/Controller/TaskGanttController.php b/app/Controller/TaskGanttController.php index 868368e1..b03b9d00 100644 --- a/app/Controller/TaskGanttController.php +++ b/app/Controller/TaskGanttController.php @@ -3,7 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Filter\TaskProjectFilter; -use Kanboard\Formatter\TaskGanttFormatter; use Kanboard\Model\TaskModel; /** @@ -35,7 +34,7 @@ class TaskGanttController extends BaseController 'title' => $project['name'], 'description' => $this->helper->projectHeader->getDescription($project), 'sorting' => $sorting, - 'tasks' => $filter->format(new TaskGanttFormatter($this->container)), + 'tasks' => $filter->format($this->taskGanttFormatter), ))); } diff --git a/app/Controller/TaskMovePositionController.php b/app/Controller/TaskMovePositionController.php index cb4afd04..caa074c9 100644 --- a/app/Controller/TaskMovePositionController.php +++ b/app/Controller/TaskMovePositionController.php @@ -3,7 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Core\Controller\AccessForbiddenException; -use Kanboard\Formatter\BoardFormatter; use Kanboard\Model\TaskModel; /** @@ -20,7 +19,7 @@ class TaskMovePositionController extends BaseController $this->response->html($this->template->render('task_move_position/show', array( 'task' => $task, - 'board' => BoardFormatter::getInstance($this->container) + 'board' => $this->boardFormatter ->withProjectId($task['project_id']) ->withQuery($this->taskFinderModel->getExtendedQuery() ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) diff --git a/app/Controller/UserAjaxController.php b/app/Controller/UserAjaxController.php index d93bfe9a..17567a00 100644 --- a/app/Controller/UserAjaxController.php +++ b/app/Controller/UserAjaxController.php @@ -3,8 +3,6 @@ namespace Kanboard\Controller; use Kanboard\Filter\UserNameFilter; -use Kanboard\Formatter\UserAutoCompleteFormatter; -use Kanboard\Formatter\UserMentionFormatter; use Kanboard\Model\UserModel; /** @@ -25,7 +23,7 @@ class UserAjaxController extends BaseController $search = $this->request->getStringParam('term'); $filter = $this->userQuery->withFilter(new UserNameFilter($search)); $filter->getQuery()->asc(UserModel::TABLE.'.name')->asc(UserModel::TABLE.'.username'); - $this->response->json($filter->format(new UserAutoCompleteFormatter($this->container))); + $this->response->json($filter->format($this->userAutoCompleteFormatter)); } /** @@ -39,11 +37,7 @@ class UserAjaxController extends BaseController $query = $this->request->getStringParam('search'); $users = $this->projectPermissionModel->findUsernames($project_id, $query); - $this->response->json( - UserMentionFormatter::getInstance($this->container) - ->withUsers($users) - ->format() - ); + $this->response->json($this->userMentionFormatter->withUsers($users)->format()); } /** diff --git a/app/Core/Base.php b/app/Core/Base.php index 881cccbd..d3c264d1 100644 --- a/app/Core/Base.php +++ b/app/Core/Base.php @@ -62,6 +62,21 @@ use Pimple\Container; * @property \Kanboard\Decorator\ColumnRestrictionCacheDecorator $columnRestrictionCacheDecorator * @property \Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator $columnMoveRestrictionCacheDecorator * @property \Kanboard\Decorator\ProjectRoleRestrictionCacheDecorator $projectRoleRestrictionCacheDecorator + * @property \Kanboard\Formatter\BoardColumnFormatter $boardColumnFormatter + * @property \Kanboard\Formatter\BoardFormatter $boardFormatter + * @property \Kanboard\Formatter\BoardSwimlaneFormatter $boardSwimlaneFormatter + * @property \Kanboard\Formatter\BoardTaskFormatter $boardTaskFormatter + * @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter + * @property \Kanboard\Formatter\ProjectActivityEventFormatter $projectActivityEventFormatter + * @property \Kanboard\Formatter\ProjectGanttFormatter $projectGanttFormatter + * @property \Kanboard\Formatter\SubtaskTimeTrackingCalendarFormatter $subtaskTimeTrackingCalendarFormatter + * @property \Kanboard\Formatter\TaskAutoCompleteFormatter $taskAutoCompleteFormatter + * @property \Kanboard\Formatter\TaskCalendarFormatter $taskCalendarFormatter + * @property \Kanboard\Formatter\TaskGanttFormatter $taskGanttFormatter + * @property \Kanboard\Formatter\TaskICalFormatter $taskICalFormatter + * @property \Kanboard\Formatter\TaskSuggestMenuFormatter $taskSuggestMenuFormatter + * @property \Kanboard\Formatter\UserAutoCompleteFormatter $userAutoCompleteFormatter + * @property \Kanboard\Formatter\UserMentionFormatter $userMentionFormatter * @property \Kanboard\Model\ActionModel $actionModel * @property \Kanboard\Model\ActionParameterModel $actionParameterModel * @property \Kanboard\Model\AvatarFileModel $avatarFileModel diff --git a/app/Core/ExternalTask/ExternalTaskManager.php b/app/Core/ExternalTask/ExternalTaskManager.php index 2ce6f106..102ec459 100644 --- a/app/Core/ExternalTask/ExternalTaskManager.php +++ b/app/Core/ExternalTask/ExternalTaskManager.php @@ -48,6 +48,11 @@ class ExternalTaskManager public function getProvidersList() { $providers = array_keys($this->providers); - return array_combine($providers, $providers); + + if (count($providers)) { + return array_combine($providers, $providers); + } + + return array(); } } diff --git a/app/Core/Filter/FormatterInterface.php b/app/Core/Filter/FormatterInterface.php index b7c04c51..0ff84976 100644 --- a/app/Core/Filter/FormatterInterface.php +++ b/app/Core/Filter/FormatterInterface.php @@ -17,7 +17,7 @@ interface FormatterInterface * * @access public * @param Table $query - * @return FormatterInterface + * @return $this */ public function withQuery(Table $query); diff --git a/app/Core/Tool.php b/app/Core/Tool.php index 9b8820eb..6e457641 100644 --- a/app/Core/Tool.php +++ b/app/Core/Tool.php @@ -41,7 +41,7 @@ class Tool } /** - * Build dependency injection container from an array + * Build dependency injection containers from an array * * @static * @access public @@ -64,6 +64,29 @@ class Tool } /** + * Build dependency injection container from an array + * + * @static + * @access public + * @param Container $container + * @param array $namespaces + * @return Container + */ + public static function buildFactories(Container $container, array $namespaces) + { + foreach ($namespaces as $namespace => $classes) { + foreach ($classes as $name) { + $class = '\\Kanboard\\'.$namespace.'\\'.$name; + $container[lcfirst($name)] = $container->factory(function ($c) use ($class) { + return new $class($c); + }); + } + } + + return $container; + } + + /** * Build dependency injection container for custom helpers from an array * * @static diff --git a/app/Formatter/BoardColumnFormatter.php b/app/Formatter/BoardColumnFormatter.php index 85d31b5c..0d59f54e 100644 --- a/app/Formatter/BoardColumnFormatter.php +++ b/app/Formatter/BoardColumnFormatter.php @@ -79,7 +79,7 @@ class BoardColumnFormatter extends BaseFormatter implements FormatterInterface { foreach ($this->columns as &$column) { $column['id'] = (int) $column['id']; - $column['tasks'] = BoardTaskFormatter::getInstance($this->container) + $column['tasks'] = $this->boardTaskFormatter ->withTasks($this->tasks) ->withTags($this->tags) ->withSwimlaneId($this->swimlaneId) diff --git a/app/Formatter/BoardFormatter.php b/app/Formatter/BoardFormatter.php index df443a52..3f47bfa9 100644 --- a/app/Formatter/BoardFormatter.php +++ b/app/Formatter/BoardFormatter.php @@ -59,7 +59,7 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface $task_ids = array_column($tasks, 'id'); $tags = $this->taskTagModel->getTagsByTasks($task_ids); - return BoardSwimlaneFormatter::getInstance($this->container) + return $this->boardSwimlaneFormatter ->withSwimlanes($swimlanes) ->withColumns($columns) ->withTasks($tasks) diff --git a/app/Formatter/BoardSwimlaneFormatter.php b/app/Formatter/BoardSwimlaneFormatter.php index ce67c8a8..18db259d 100644 --- a/app/Formatter/BoardSwimlaneFormatter.php +++ b/app/Formatter/BoardSwimlaneFormatter.php @@ -82,7 +82,7 @@ class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface foreach ($this->swimlanes as &$swimlane) { $swimlane['id'] = (int) $swimlane['id']; - $swimlane['columns'] = BoardColumnFormatter::getInstance($this->container) + $swimlane['columns'] = $this->boardColumnFormatter ->withSwimlaneId($swimlane['id']) ->withColumns($this->columns) ->withTasks($this->tasks) diff --git a/app/Formatter/GroupAutoCompleteFormatter.php b/app/Formatter/GroupAutoCompleteFormatter.php index 4d552886..d811de7f 100644 --- a/app/Formatter/GroupAutoCompleteFormatter.php +++ b/app/Formatter/GroupAutoCompleteFormatter.php @@ -12,36 +12,26 @@ use PicoDb\Table; * @package formatter * @author Frederic Guillot */ -class GroupAutoCompleteFormatter implements FormatterInterface +class GroupAutoCompleteFormatter extends BaseFormatter implements FormatterInterface { /** * Groups found * - * @access private + * @access protected * @var GroupProviderInterface[] */ - private $groups; + protected $groups; /** - * Format groups for the ajax auto-completion + * Set groups * * @access public * @param GroupProviderInterface[] $groups + * @return $this */ - public function __construct(array $groups) + public function withGroups(array $groups) { $this->groups = $groups; - } - - /** - * Set query - * - * @access public - * @param Table $query - * @return FormatterInterface - */ - public function withQuery(Table $query) - { return $this; } diff --git a/app/Formatter/UserAutoCompleteFormatter.php b/app/Formatter/UserAutoCompleteFormatter.php index cd23a2a4..c81af00a 100644 --- a/app/Formatter/UserAutoCompleteFormatter.php +++ b/app/Formatter/UserAutoCompleteFormatter.php @@ -14,7 +14,7 @@ use Kanboard\Core\Filter\FormatterInterface; class UserAutoCompleteFormatter extends BaseFormatter implements FormatterInterface { /** - * Format the tasks for the ajax autocompletion + * Format the tasks for the ajax auto-completion * * @access public * @return array @@ -24,11 +24,11 @@ class UserAutoCompleteFormatter extends BaseFormatter implements FormatterInterf $users = $this->query->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name')->findAll(); foreach ($users as &$user) { - $user['value'] = $user['username'].' (#'.$user['id'].')'; - if (empty($user['name'])) { + $user['value'] = $user['username'].' (#'.$user['id'].')'; $user['label'] = $user['username']; } else { + $user['value'] = $user['name'].' (#'.$user['id'].')'; $user['label'] = $user['name'].' ('.$user['username'].')'; } } diff --git a/app/Helper/CalendarHelper.php b/app/Helper/CalendarHelper.php index 4f78b673..0942177d 100644 --- a/app/Helper/CalendarHelper.php +++ b/app/Helper/CalendarHelper.php @@ -5,8 +5,6 @@ namespace Kanboard\Helper; use Kanboard\Core\Base; use Kanboard\Core\Filter\QueryBuilder; use Kanboard\Filter\TaskDueDateRangeFilter; -use Kanboard\Formatter\SubtaskTimeTrackingCalendarFormatter; -use Kanboard\Formatter\TaskCalendarFormatter; /** * Calendar Helper @@ -44,7 +42,7 @@ class CalendarHelper extends Base */ public function getTaskDateDueEvents(QueryBuilder $queryBuilder, $start, $end) { - $formatter = new TaskCalendarFormatter($this->container); + $formatter = $this->taskCalendarFormatter; $formatter->setFullDay(); $formatter->setColumns('date_due'); @@ -73,7 +71,7 @@ class CalendarHelper extends Base 'date_due' )); - $formatter = new TaskCalendarFormatter($this->container); + $formatter = $this->taskCalendarFormatter; $formatter->setColumns($startColumn, 'date_due'); return $queryBuilder->format($formatter); @@ -90,8 +88,7 @@ class CalendarHelper extends Base */ public function getSubtaskTimeTrackingEvents($user_id, $start, $end) { - $formatter = new SubtaskTimeTrackingCalendarFormatter($this->container); - return $formatter + return $this->subtaskTimeTrackingCalendarFormatter ->withQuery($this->subtaskTimeTrackingModel->getUserQuery($user_id) ->addCondition($this->getCalendarCondition( $this->dateParser->getTimestampFromIsoFormat($start), diff --git a/app/Helper/ICalHelper.php b/app/Helper/ICalHelper.php index dc399bf8..95723417 100644 --- a/app/Helper/ICalHelper.php +++ b/app/Helper/ICalHelper.php @@ -5,7 +5,6 @@ namespace Kanboard\Helper; use Kanboard\Core\Base; use Kanboard\Core\Filter\QueryBuilder; use Kanboard\Filter\TaskDueDateRangeFilter; -use Kanboard\Formatter\TaskICalFormatter; use Eluceo\iCal\Component\Calendar as iCalendar; /** @@ -29,10 +28,10 @@ class ICalHelper extends Base { $queryBuilder->withFilter(new TaskDueDateRangeFilter(array($start, $end))); - $formatter = new TaskICalFormatter($this->container); - $formatter->setColumns('date_due'); - $formatter->setCalendar($calendar); - $formatter->withQuery($queryBuilder->getQuery()); - $formatter->addFullDayEvents(); + $this->taskICalFormatter + ->setColumns('date_due') + ->setCalendar($calendar) + ->withQuery($queryBuilder->getQuery()) + ->addFullDayEvents(); } } diff --git a/app/Helper/ProjectActivityHelper.php b/app/Helper/ProjectActivityHelper.php index 704cd4fe..480db3d5 100644 --- a/app/Helper/ProjectActivityHelper.php +++ b/app/Helper/ProjectActivityHelper.php @@ -6,7 +6,6 @@ use Kanboard\Core\Base; use Kanboard\Filter\ProjectActivityProjectIdFilter; use Kanboard\Filter\ProjectActivityProjectIdsFilter; use Kanboard\Filter\ProjectActivityTaskIdFilter; -use Kanboard\Formatter\ProjectActivityEventFormatter; use Kanboard\Model\ProjectActivityModel; /** @@ -38,7 +37,7 @@ class ProjectActivityHelper extends Base ->limit(500) ; - $events = $queryBuilder->format(new ProjectActivityEventFormatter($this->container)); + $events = $queryBuilder->format($this->projectActivityEventFormatter); } return $events; @@ -62,7 +61,7 @@ class ProjectActivityHelper extends Base ->limit($limit) ; - return $queryBuilder->format(new ProjectActivityEventFormatter($this->container)); + return $queryBuilder->format($this->projectActivityEventFormatter); } /** @@ -83,7 +82,7 @@ class ProjectActivityHelper extends Base ->limit($limit) ; - return $queryBuilder->format(new ProjectActivityEventFormatter($this->container)); + return $queryBuilder->format($this->projectActivityEventFormatter); } /** @@ -100,6 +99,6 @@ class ProjectActivityHelper extends Base $queryBuilder->getQuery()->desc(ProjectActivityModel::TABLE.'.id'); - return $queryBuilder->format(new ProjectActivityEventFormatter($this->container)); + return $queryBuilder->format($this->projectActivityEventFormatter); } } diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index f55c595d..ffebcd6a 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -491,7 +491,7 @@ return array( 'All swimlanes' => 'Все дорожки', 'All colors' => 'Все цвета', 'Moved to column %s' => 'Перемещена в колонку %s', - 'User dashboard' => 'Пользователь панели мониторинга', + 'User dashboard' => 'Панель управления', 'Allow only one subtask in progress at the same time for a user' => 'Разрешена только одна подзадача в разработке одновременно для одного пользователя', 'Edit column "%s"' => 'Редактировать колонку "%s"', 'Select the new status of the subtask: "%s"' => 'Выбрать новый статус для подзадачи: "%s"', @@ -563,10 +563,10 @@ return array( 'EUR - Euro' => 'EUR - Евро', 'GBP - British Pound' => 'GBP - Британский фунт', 'INR - Indian Rupee' => 'INR - Индийский рупий', - 'JPY - Japanese Yen' => 'JPY - Японскай йена', + 'JPY - Japanese Yen' => 'JPY - Японская йена', 'NZD - New Zealand Dollar' => 'NZD - Новозеландский доллар', 'RSD - Serbian dinar' => 'RSD - Сербский динар', - // 'CNY - Chinese Yuan' => '', + 'CNY - Chinese Yuan' => 'CNY - Китайский юань', 'USD - US Dollar' => 'USD - доллар США', 'Destination column' => 'Колонка назначения', 'Move the task to another column when assigned to a user' => 'Переместить задачу в другую колонку, когда она назначена пользователю', @@ -601,7 +601,7 @@ return array( 'Assign a color when the task is moved to a specific column' => 'Назначить цвет, когда задача перемещается в определенную колонку', '%s via Kanboard' => '%s через Канборд', 'Burndown chart' => 'Диаграмма сгорания', - 'This chart show the task complexity over the time (Work Remaining).' => 'Эта диаграмма показывают сложность задачи по времени (оставшейся работы).', + 'This chart show the task complexity over the time (Work Remaining).' => 'Эта диаграмма показывает сложность задачи по времени (оставшейся работы).', 'Screenshot taken %s' => 'Скриншот сделан %s', 'Add a screenshot' => 'Прикрепить картинку', 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Сделайте скриншот и нажмите CTRL+V или ⌘+V для вложения', @@ -951,13 +951,13 @@ return array( 'Estimated Time' => 'Запланировано времени', 'Actual Time' => 'Затрачено времени', 'Estimated vs actual time' => 'Запланировано и реально затрачено времени', - 'RUB - Russian Ruble' => 'Руб - Российский рубль', + 'RUB - Russian Ruble' => 'РУБ - Российский рубль', 'Assign the task to the person who does the action when the column is changed' => 'Назначить задачу пользователю, который произвёл изменение в колонке', 'Close a task in a specific column' => 'Закрыть задачу в выбранной колонке', 'Time-based One-time Password Algorithm' => 'Зависимый от времени, одноразовый алгоритм пароля', - 'Two-Factor Provider: ' => 'Провайдер двух-факторной авторизации: ', - 'Disable two-factor authentication' => 'Отключить двух-факторную авторизацию', - 'Enable two-factor authentication' => 'Включить двух-факторную авторизацию', + 'Two-Factor Provider: ' => 'Провайдер двухфакторной авторизации: ', + 'Disable two-factor authentication' => 'Отключить двухфакторную авторизацию', + 'Enable two-factor authentication' => 'Включить двухфакторную авторизацию', 'There is no integration registered at the moment.' => 'Интеграции в данный момент не зарегистрированы.', 'Password Reset for Kanboard' => 'Сброс пароля для Kanboard', 'Forgot password?' => 'Забыли пароль?', @@ -1278,15 +1278,15 @@ return array( 'This field is required' => 'Заполните это поле', 'Moving a task is not permitted' => 'Перемещение задачи не разрешено', 'This value must be in the range %d to %d' => 'Значение должно находиться в диапазоне от %d до %d', - // 'You are not allowed to move this task.' => '', - // 'API User Access' => '', - // 'Preview' => '', - // 'Write' => '', - // 'Write your text in Markdown' => '', - // 'New External Task: %s' => '', - // 'No personal API access token registered.' => '', - // 'Your personal API access token is "%s"' => '', - // 'Remove your token' => '', - // 'Generate a new token' => '', + 'You are not allowed to move this task.' => 'Вам не разрешено перемещать эту задачу.', + 'API User Access' => 'Доступ к API', + 'Preview' => 'Предпросмотр', + 'Write' => 'Редактирование', + 'Write your text in Markdown' => 'Добавьте Ваше описание в формате Markdown', + 'New External Task: %s' => 'Новая внешняя задача: %s', + 'No personal API access token registered.' => 'Персональные токены доступа к API не созданы.', + 'Your personal API access token is "%s"' => 'Ваш персональный токен доступа к API: "%s"', + 'Remove your token' => 'Удалить токен', + 'Generate a new token' => 'Сгенерировать новый токен', // 'Showing %d-%d of %d' => '', ); diff --git a/app/ServiceProvider/FormatterProvider.php b/app/ServiceProvider/FormatterProvider.php new file mode 100644 index 00000000..dbba3f3c --- /dev/null +++ b/app/ServiceProvider/FormatterProvider.php @@ -0,0 +1,48 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\Tool; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * Class FormatterProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class FormatterProvider implements ServiceProviderInterface +{ + protected $formatters = array( + 'Formatter' => array( + 'BoardColumnFormatter', + 'BoardFormatter', + 'BoardSwimlaneFormatter', + 'BoardTaskFormatter', + 'GroupAutoCompleteFormatter', + 'ProjectActivityEventFormatter', + 'ProjectGanttFormatter', + 'SubtaskTimeTrackingCalendarFormatter', + 'TaskAutoCompleteFormatter', + 'TaskCalendarFormatter', + 'TaskGanttFormatter', + 'TaskICalFormatter', + 'TaskSuggestMenuFormatter', + 'UserAutoCompleteFormatter', + 'UserMentionFormatter', + ) + ); + + /** + * Registers services on the given container. + * + * @param Container $container + * @return Container + */ + public function register(Container $container) + { + Tool::buildFactories($container, $this->formatters); + return $container; + } +} diff --git a/app/common.php b/app/common.php index 6ebb839e..fd55c0bb 100644 --- a/app/common.php +++ b/app/common.php @@ -48,6 +48,7 @@ $container->register(new Kanboard\ServiceProvider\ExternalLinkProvider()); $container->register(new Kanboard\ServiceProvider\ExternalTaskProvider()); $container->register(new Kanboard\ServiceProvider\AvatarProvider()); $container->register(new Kanboard\ServiceProvider\FilterProvider()); +$container->register(new Kanboard\ServiceProvider\FormatterProvider()); $container->register(new Kanboard\ServiceProvider\JobProvider()); $container->register(new Kanboard\ServiceProvider\QueueProvider()); $container->register(new Kanboard\ServiceProvider\ApiProvider()); diff --git a/doc/plugin-overrides.markdown b/doc/plugin-overrides.markdown index 96a09e47..3b94bd60 100644 --- a/doc/plugin-overrides.markdown +++ b/doc/plugin-overrides.markdown @@ -40,3 +40,34 @@ You can still use the original template using the "kanboard:" prefix: ```php <?= $this->render('kanboard:header') ?> ``` + +Formatter Overrides +------------------- + +Here an example to override formatter objects in Kanboard: + +```php +class MyFormatter extends UserAutoCompleteFormatter +{ + public function format() + { + $users = parent::format(); + + foreach ($users as &$user) { + $user['label'] = 'something'; // Do something useful here + } + + return $users; + } +} + +class Plugin extends Base +{ + public function initialize() + { + $this->container['userAutoCompleteFormatter'] = $this->container->factory(function ($c) { + return new MyFormatter($c); + }); + } +} +``` diff --git a/tests/units/Base.php b/tests/units/Base.php index 1b986fcb..1c93e9b8 100644 --- a/tests/units/Base.php +++ b/tests/units/Base.php @@ -47,6 +47,7 @@ abstract class Base extends PHPUnit_Framework_TestCase $this->container->register(new Kanboard\ServiceProvider\RouteProvider()); $this->container->register(new Kanboard\ServiceProvider\AvatarProvider()); $this->container->register(new Kanboard\ServiceProvider\FilterProvider()); + $this->container->register(new Kanboard\ServiceProvider\FormatterProvider()); $this->container->register(new Kanboard\ServiceProvider\JobProvider()); $this->container->register(new Kanboard\ServiceProvider\QueueProvider()); $this->container->register(new Kanboard\ServiceProvider\ExternalTaskProvider()); diff --git a/tests/units/Core/ExternalTask/ExternalTaskManagerTest.php b/tests/units/Core/ExternalTask/ExternalTaskManagerTest.php index e6f4e069..8eec64de 100644 --- a/tests/units/Core/ExternalTask/ExternalTaskManagerTest.php +++ b/tests/units/Core/ExternalTask/ExternalTaskManagerTest.php @@ -41,4 +41,10 @@ class ExternalTaskManagerTest extends Base $expected = array('MyProvider1' => 'MyProvider1', 'MyProvider2' => 'MyProvider2'); $this->assertEquals($expected, $providers); } + + public function testGetProviderListWithNoProviders() + { + $manager = new ExternalTaskManager(); + $this->assertSame(array(), $manager->getProvidersList()); + } } diff --git a/tests/units/ServiceProvider/ClassProviderTest.php b/tests/units/ServiceProvider/ClassProviderTest.php new file mode 100644 index 00000000..f6528918 --- /dev/null +++ b/tests/units/ServiceProvider/ClassProviderTest.php @@ -0,0 +1,21 @@ +<?php + +use Kanboard\ServiceProvider\ClassProvider; +use Pimple\Container; + +require_once __DIR__.'/../Base.php'; + +class ModelProviderTest extends Base +{ + public function testServiceInstance() + { + $container = new Container(); + $serviceProvider = new ClassProvider($container); + $serviceProvider->register($container); + + $instance1 = $container['userModel']; + $instance2 = $container['userModel']; + + $this->assertSame($instance1, $instance2); + } +} diff --git a/tests/units/ServiceProvider/FormatterProviderTest.php b/tests/units/ServiceProvider/FormatterProviderTest.php new file mode 100644 index 00000000..7984a12b --- /dev/null +++ b/tests/units/ServiceProvider/FormatterProviderTest.php @@ -0,0 +1,21 @@ +<?php + +use Kanboard\ServiceProvider\FormatterProvider; +use Pimple\Container; + +require_once __DIR__.'/../Base.php'; + +class FormatterProviderTest extends Base +{ + public function testServiceInstance() + { + $container = new Container(); + $serviceProvider = new FormatterProvider($container); + $serviceProvider->register($container); + + $instance1 = $container['userAutoCompleteFormatter']; + $instance2 = $container['userAutoCompleteFormatter']; + + $this->assertNotSame($instance1, $instance2); + } +} |