summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorJLGC @monolinux <monolinux@junglacode.org>2016-08-15 23:13:16 -0500
committerJLGC @monolinux <monolinux@junglacode.org>2016-08-15 23:13:16 -0500
commit683c0464093f6a7976236c68653c2a2cc5dae280 (patch)
treebf176ecd82415cc4952eea071b7d264dd5fd68b4 /app
parentb1e795fc5b45369f7b9b565b1e106d2673361977 (diff)
parent5f82a942c0011bf91947b2c1d627c0907bda0c92 (diff)
Merge https://github.com/kanboard/kanboard
Diffstat (limited to 'app')
-rw-r--r--app/Action/Base.php15
-rw-r--r--app/Action/CommentCreation.php2
-rw-r--r--app/Action/CommentCreationMoveTaskColumn.php14
-rw-r--r--app/Action/TaskAssignCategoryColor.php9
-rw-r--r--app/Action/TaskAssignCategoryLabel.php2
-rw-r--r--app/Action/TaskAssignCategoryLink.php15
-rw-r--r--app/Action/TaskAssignColorCategory.php9
-rw-r--r--app/Action/TaskAssignColorColumn.php9
-rw-r--r--app/Action/TaskAssignColorLink.php12
-rw-r--r--app/Action/TaskAssignColorPriority.php9
-rw-r--r--app/Action/TaskAssignColorUser.php9
-rw-r--r--app/Action/TaskAssignCurrentUser.php2
-rw-r--r--app/Action/TaskAssignCurrentUserColumn.php9
-rw-r--r--app/Action/TaskAssignDueDateOnCreation.php96
-rw-r--r--app/Action/TaskAssignSpecificUser.php9
-rw-r--r--app/Action/TaskAssignUser.php2
-rw-r--r--app/Action/TaskClose.php2
-rw-r--r--app/Action/TaskCloseColumn.php12
-rw-r--r--app/Action/TaskCloseNoActivity.php2
-rw-r--r--app/Action/TaskCloseNoActivityColumn.php96
-rw-r--r--app/Action/TaskCreation.php3
-rw-r--r--app/Action/TaskDuplicateAnotherProject.php16
-rw-r--r--app/Action/TaskEmail.php16
-rw-r--r--app/Action/TaskEmailNoActivity.php2
-rw-r--r--app/Action/TaskMoveAnotherProject.php12
-rw-r--r--app/Action/TaskMoveColumnAssigned.php21
-rw-r--r--app/Action/TaskMoveColumnCategoryChange.php21
-rw-r--r--app/Action/TaskMoveColumnClosed.php102
-rw-r--r--app/Action/TaskMoveColumnNotMovedPeriod.php104
-rw-r--r--app/Action/TaskMoveColumnUnAssigned.php21
-rw-r--r--app/Action/TaskOpen.php2
-rw-r--r--app/Action/TaskUpdateStartDate.php9
-rw-r--r--app/Api/Authorization/ActionAuthorization.php19
-rw-r--r--app/Api/Authorization/CategoryAuthorization.php19
-rw-r--r--app/Api/Authorization/ColumnAuthorization.php19
-rw-r--r--app/Api/Authorization/CommentAuthorization.php19
-rw-r--r--app/Api/Authorization/ProcedureAuthorization.php32
-rw-r--r--app/Api/Authorization/ProjectAuthorization.php35
-rw-r--r--app/Api/Authorization/SubtaskAuthorization.php19
-rw-r--r--app/Api/Authorization/TaskAuthorization.php19
-rw-r--r--app/Api/Authorization/TaskFileAuthorization.php19
-rw-r--r--app/Api/Authorization/TaskLinkAuthorization.php19
-rw-r--r--app/Api/Authorization/UserAuthorization.php22
-rw-r--r--app/Api/BoardApi.php18
-rw-r--r--app/Api/Middleware/AuthenticationMiddleware.php (renamed from app/Api/Middleware/AuthenticationApiMiddleware.php)59
-rw-r--r--app/Api/Procedure/ActionProcedure.php (renamed from app/Api/ActionApi.php)12
-rw-r--r--app/Api/Procedure/AppProcedure.php (renamed from app/Api/AppApi.php)8
-rw-r--r--app/Api/Procedure/BaseProcedure.php (renamed from app/Api/BaseApi.php)41
-rw-r--r--app/Api/Procedure/BoardProcedure.php25
-rw-r--r--app/Api/Procedure/CategoryProcedure.php (renamed from app/Api/CategoryApi.php)16
-rw-r--r--app/Api/Procedure/ColumnProcedure.php (renamed from app/Api/ColumnApi.php)15
-rw-r--r--app/Api/Procedure/CommentProcedure.php (renamed from app/Api/CommentApi.php)16
-rw-r--r--app/Api/Procedure/GroupMemberProcedure.php (renamed from app/Api/GroupMemberApi.php)8
-rw-r--r--app/Api/Procedure/GroupProcedure.php (renamed from app/Api/GroupApi.php)8
-rw-r--r--app/Api/Procedure/LinkProcedure.php (renamed from app/Api/LinkApi.php)8
-rw-r--r--app/Api/Procedure/MeProcedure.php (renamed from app/Api/MeApi.php)6
-rw-r--r--app/Api/Procedure/ProjectFileProcedure.php68
-rw-r--r--app/Api/Procedure/ProjectPermissionProcedure.php (renamed from app/Api/ProjectPermissionApi.php)34
-rw-r--r--app/Api/Procedure/ProjectProcedure.php113
-rw-r--r--app/Api/Procedure/SubtaskProcedure.php (renamed from app/Api/SubtaskApi.php)16
-rw-r--r--app/Api/Procedure/SubtaskTimeTrackingProcedure.php39
-rw-r--r--app/Api/Procedure/SwimlaneProcedure.php (renamed from app/Api/SwimlaneApi.php)21
-rw-r--r--app/Api/Procedure/TaskExternalLinkProcedure.php106
-rw-r--r--app/Api/Procedure/TaskFileProcedure.php (renamed from app/Api/FileApi.php)55
-rw-r--r--app/Api/Procedure/TaskLinkProcedure.php (renamed from app/Api/TaskLinkApi.php)14
-rw-r--r--app/Api/Procedure/TaskMetadataProcedure.php38
-rw-r--r--app/Api/Procedure/TaskProcedure.php (renamed from app/Api/TaskApi.php)46
-rw-r--r--app/Api/Procedure/UserProcedure.php (renamed from app/Api/UserApi.php)17
-rw-r--r--app/Api/ProjectApi.php87
-rw-r--r--app/Api/SubtaskTimeTrackingApi.php34
-rw-r--r--app/Auth/ReverseProxyAuth.php3
-rw-r--r--app/Auth/TotpAuth.php6
-rw-r--r--app/Console/TaskOverdueNotificationCommand.php2
-rw-r--r--app/Controller/ActionCreationController.php2
-rw-r--r--app/Controller/ActivityController.php1
-rw-r--r--app/Controller/AnalyticController.php16
-rw-r--r--app/Controller/BoardAjaxController.php2
-rw-r--r--app/Controller/BoardTooltipController.php6
-rw-r--r--app/Controller/BoardViewController.php8
-rw-r--r--app/Controller/ColumnController.php20
-rw-r--r--app/Controller/DashboardController.php90
-rw-r--r--app/Controller/DocumentationController.php70
-rw-r--r--app/Controller/ExportController.php33
-rw-r--r--app/Controller/FeedController.php60
-rw-r--r--app/Controller/ProjectListController.php2
-rw-r--r--app/Controller/ProjectPermissionController.php2
-rw-r--r--app/Controller/ProjectTagController.php134
-rw-r--r--app/Controller/SubtaskController.php2
-rw-r--r--app/Controller/SubtaskConverterController.php2
-rw-r--r--app/Controller/SubtaskRestrictionController.php2
-rw-r--r--app/Controller/SubtaskStatusController.php2
-rw-r--r--app/Controller/TagController.php121
-rw-r--r--app/Controller/TaskCreationController.php53
-rw-r--r--app/Controller/TaskDuplicationController.php4
-rw-r--r--app/Controller/TaskGanttCreationController.php19
-rw-r--r--app/Controller/TaskModificationController.php74
-rw-r--r--app/Controller/TaskPopoverController.php74
-rw-r--r--app/Controller/TaskRecurrenceController.php8
-rw-r--r--app/Controller/TaskViewController.php16
-rw-r--r--app/Controller/UserListController.php7
-rw-r--r--app/Controller/WebNotificationController.php4
-rw-r--r--app/Core/Action/ActionManager.php16
-rw-r--r--app/Core/Base.php37
-rw-r--r--app/Core/ExternalLink/ExternalLinkManager.php24
-rw-r--r--app/Core/Filter/Lexer.php2
-rw-r--r--app/Core/Filter/LexerBuilder.php4
-rw-r--r--app/Core/Http/Request.php1
-rw-r--r--app/Core/Ldap/User.php8
-rw-r--r--app/Core/Mail/Transport/Smtp.php9
-rw-r--r--app/Core/Plugin/Hook.php17
-rw-r--r--app/Core/Queue/JobHandler.php35
-rw-r--r--app/Core/Queue/QueueManager.php6
-rw-r--r--app/Event/FileEvent.php7
-rw-r--r--app/Event/ProjectFileEvent.php7
-rw-r--r--app/Event/TaskFileEvent.php7
-rw-r--r--app/EventBuilder/BaseEventBuilder.php44
-rw-r--r--app/EventBuilder/CommentEventBuilder.php98
-rw-r--r--app/EventBuilder/EventIteratorBuilder.php52
-rw-r--r--app/EventBuilder/ProjectFileEventBuilder.php77
-rw-r--r--app/EventBuilder/SubtaskEventBuilder.php125
-rw-r--r--app/EventBuilder/TaskEventBuilder.php223
-rw-r--r--app/EventBuilder/TaskFileEventBuilder.php86
-rw-r--r--app/EventBuilder/TaskLinkEventBuilder.php89
-rw-r--r--app/Filter/BaseFilter.php3
-rw-r--r--app/Filter/TaskMovedDateFilter.php38
-rw-r--r--app/Filter/TaskPriorityFilter.php38
-rw-r--r--app/Filter/TaskTagFilter.php74
-rw-r--r--app/Formatter/BaseFormatter.php17
-rw-r--r--app/Formatter/BoardColumnFormatter.php94
-rw-r--r--app/Formatter/BoardFormatter.php25
-rw-r--r--app/Formatter/BoardSwimlaneFormatter.php120
-rw-r--r--app/Formatter/BoardTaskFormatter.php96
-rw-r--r--app/Formatter/TaskAutoCompleteFormatter.php9
-rw-r--r--app/Formatter/TaskICalFormatter.php17
-rw-r--r--app/Helper/FormHelper.php42
-rw-r--r--app/Helper/HookHelper.php55
-rw-r--r--app/Helper/LayoutHelper.php4
-rw-r--r--app/Helper/SubtaskHelper.php5
-rw-r--r--app/Helper/TaskHelper.php104
-rw-r--r--app/Helper/UserHelper.php11
-rw-r--r--app/Job/CommentEventJob.php50
-rw-r--r--app/Job/NotificationJob.php60
-rw-r--r--app/Job/ProjectFileEventJob.php45
-rw-r--r--app/Job/SubtaskEventJob.php48
-rw-r--r--app/Job/TaskEventJob.php75
-rw-r--r--app/Job/TaskFileEventJob.php45
-rw-r--r--app/Job/TaskLinkEventJob.php45
-rw-r--r--app/Locale/bs_BA/translations.php90
-rw-r--r--app/Locale/cs_CZ/translations.php208
-rw-r--r--app/Locale/da_DK/translations.php90
-rw-r--r--app/Locale/de_DE/translations.php90
-rw-r--r--app/Locale/el_GR/translations.php90
-rw-r--r--app/Locale/es_ES/translations.php474
-rw-r--r--app/Locale/fi_FI/translations.php90
-rw-r--r--app/Locale/fr_FR/translations.php92
-rw-r--r--app/Locale/hu_HU/translations.php90
-rw-r--r--app/Locale/id_ID/translations.php406
-rw-r--r--app/Locale/it_IT/translations.php90
-rw-r--r--app/Locale/ja_JP/translations.php90
-rw-r--r--app/Locale/ko_KR/translations.php180
-rw-r--r--app/Locale/my_MY/translations.php90
-rw-r--r--app/Locale/nb_NO/translations.php90
-rw-r--r--app/Locale/nl_NL/translations.php90
-rw-r--r--app/Locale/pl_PL/translations.php90
-rw-r--r--app/Locale/pt_BR/translations.php90
-rw-r--r--app/Locale/pt_PT/translations.php106
-rw-r--r--app/Locale/ru_RU/translations.php178
-rw-r--r--app/Locale/sr_Latn_RS/translations.php90
-rw-r--r--app/Locale/sv_SE/translations.php90
-rw-r--r--app/Locale/th_TH/translations.php90
-rw-r--r--app/Locale/tr_TR/translations.php90
-rw-r--r--app/Locale/zh_CN/translations.php92
-rw-r--r--app/Model/ActionModel.php12
-rw-r--r--app/Model/BoardModel.php60
-rw-r--r--app/Model/CategoryModel.php12
-rw-r--r--app/Model/ColumnModel.php30
-rw-r--r--app/Model/CommentModel.php25
-rw-r--r--app/Model/FileModel.php22
-rw-r--r--app/Model/GroupModel.php19
-rw-r--r--app/Model/NotificationModel.php168
-rw-r--r--app/Model/ProjectDuplicationModel.php26
-rw-r--r--app/Model/ProjectFileModel.php9
-rw-r--r--app/Model/ProjectModel.php15
-rw-r--r--app/Model/ProjectPermissionModel.php12
-rw-r--r--app/Model/ProjectTaskDuplicationModel.php35
-rw-r--r--app/Model/ProjectTaskPriorityModel.php74
-rw-r--r--app/Model/SubtaskModel.php272
-rw-r--r--app/Model/SubtaskPositionModel.php47
-rw-r--r--app/Model/SubtaskStatusModel.php88
-rw-r--r--app/Model/SubtaskTaskConversionModel.php41
-rw-r--r--app/Model/SubtaskTimeTrackingModel.php23
-rw-r--r--app/Model/SwimlaneModel.php24
-rw-r--r--app/Model/TagDuplicationModel.php87
-rw-r--r--app/Model/TagModel.php180
-rw-r--r--app/Model/TaskCreationModel.php48
-rw-r--r--app/Model/TaskDuplicationModel.php165
-rw-r--r--app/Model/TaskFileModel.php27
-rw-r--r--app/Model/TaskFinderModel.php192
-rw-r--r--app/Model/TaskLinkModel.php189
-rw-r--r--app/Model/TaskModel.php143
-rw-r--r--app/Model/TaskModificationModel.php84
-rw-r--r--app/Model/TaskPositionModel.php47
-rw-r--r--app/Model/TaskProjectDuplicationModel.php60
-rw-r--r--app/Model/TaskProjectMoveModel.php65
-rw-r--r--app/Model/TaskRecurrenceModel.php147
-rw-r--r--app/Model/TaskStatusModel.php10
-rw-r--r--app/Model/TaskTagModel.php184
-rw-r--r--app/Model/UserModel.php13
-rw-r--r--app/Notification/MailNotification.php80
-rw-r--r--app/Pagination/ProjectPagination.php35
-rw-r--r--app/Pagination/SubtaskPagination.php39
-rw-r--r--app/Pagination/TaskPagination.php38
-rw-r--r--app/Pagination/UserPagination.php32
-rw-r--r--app/Schema/Mysql.php30
-rw-r--r--app/Schema/Postgres.php29
-rw-r--r--app/Schema/Sql/mysql.sql28
-rw-r--r--app/Schema/Sql/postgres.sql96
-rw-r--r--app/Schema/Sqlite.php29
-rw-r--r--app/ServiceProvider/ActionProvider.php8
-rw-r--r--app/ServiceProvider/ApiProvider.php89
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php61
-rw-r--r--app/ServiceProvider/ClassProvider.php20
-rw-r--r--app/ServiceProvider/EventDispatcherProvider.php2
-rw-r--r--app/ServiceProvider/FilterProvider.php10
-rw-r--r--app/ServiceProvider/JobProvider.php67
-rw-r--r--app/ServiceProvider/QueueProvider.php6
-rw-r--r--app/ServiceProvider/RouteProvider.php2
-rw-r--r--app/Subscriber/BaseSubscriber.php24
-rw-r--r--app/Subscriber/BootstrapSubscriber.php2
-rw-r--r--app/Subscriber/NotificationSubscriber.php15
-rw-r--r--app/Subscriber/ProjectDailySummarySubscriber.php7
-rw-r--r--app/Subscriber/ProjectModificationDateSubscriber.php6
-rw-r--r--app/Subscriber/RecurringTaskSubscriber.php16
-rw-r--r--app/Subscriber/SubtaskTimeTrackingSubscriber.php48
-rw-r--r--app/Template/action/index.php2
-rw-r--r--app/Template/activity/task.php11
-rw-r--r--app/Template/analytic/avg_time_columns.php2
-rw-r--r--app/Template/analytic/compare_hours.php4
-rw-r--r--app/Template/analytic/sidebar.php1
-rw-r--r--app/Template/analytic/tasks.php2
-rw-r--r--app/Template/analytic/users.php2
-rw-r--r--app/Template/board/table_column.php9
-rw-r--r--app/Template/board/table_tasks.php3
-rw-r--r--app/Template/board/task_avatar.php2
-rw-r--r--app/Template/board/task_footer.php16
-rw-r--r--app/Template/board/tooltip_external_links.php2
-rw-r--r--app/Template/board/tooltip_files.php2
-rw-r--r--app/Template/board/tooltip_subtasks.php2
-rw-r--r--app/Template/board/tooltip_tasklinks.php2
-rw-r--r--app/Template/category/index.php2
-rw-r--r--app/Template/column/create.php2
-rw-r--r--app/Template/column/edit.php2
-rw-r--r--app/Template/column/index.php2
-rw-r--r--app/Template/comment/show.php4
-rw-r--r--app/Template/comments/show.php6
-rw-r--r--app/Template/config/about.php2
-rw-r--r--app/Template/config/keyboard_shortcuts.php3
-rw-r--r--app/Template/config/sidebar.php4
-rw-r--r--app/Template/currency/index.php2
-rw-r--r--app/Template/custom_filter/index.php2
-rw-r--r--app/Template/dashboard/notifications.php8
-rw-r--r--app/Template/dashboard/projects.php8
-rw-r--r--app/Template/dashboard/show.php11
-rw-r--r--app/Template/dashboard/sidebar.php3
-rw-r--r--app/Template/dashboard/subtasks.php6
-rw-r--r--app/Template/dashboard/tasks.php10
-rw-r--r--app/Template/event/comment_create.php2
-rw-r--r--app/Template/event/comment_delete.php11
-rw-r--r--app/Template/event/comment_update.php5
-rw-r--r--app/Template/event/subtask_create.php2
-rw-r--r--app/Template/event/subtask_delete.php15
-rw-r--r--app/Template/event/subtask_update.php2
-rw-r--r--app/Template/event/task_assignee_change.php4
-rw-r--r--app/Template/event/task_close.php2
-rw-r--r--app/Template/event/task_create.php2
-rw-r--r--app/Template/event/task_file_create.php2
-rw-r--r--app/Template/event/task_internal_link_create_update.php16
-rw-r--r--app/Template/event/task_internal_link_delete.php16
-rw-r--r--app/Template/event/task_move_column.php2
-rw-r--r--app/Template/event/task_move_position.php2
-rw-r--r--app/Template/event/task_move_swimlane.php2
-rw-r--r--app/Template/event/task_open.php2
-rw-r--r--app/Template/event/task_update.php2
-rw-r--r--app/Template/export/sidebar.php1
-rw-r--r--app/Template/export/subtasks.php4
-rw-r--r--app/Template/export/summary.php4
-rw-r--r--app/Template/export/tasks.php4
-rw-r--r--app/Template/feed/project.php27
-rw-r--r--app/Template/feed/user.php27
-rw-r--r--app/Template/group/index.php2
-rw-r--r--app/Template/group/users.php2
-rw-r--r--app/Template/header.php138
-rw-r--r--app/Template/header/board_selector.php13
-rw-r--r--app/Template/header/creation_dropdown.php22
-rw-r--r--app/Template/header/title.php17
-rw-r--r--app/Template/header/user_dropdown.php49
-rw-r--r--app/Template/header/user_notifications.php5
-rw-r--r--app/Template/layout.php1
-rw-r--r--app/Template/link/index.php2
-rw-r--r--app/Template/notification/comment_delete.php7
-rw-r--r--app/Template/notification/subtask_delete.php11
-rw-r--r--app/Template/notification/task_file_create.php2
-rw-r--r--app/Template/notification/task_internal_link_create_update.php11
-rw-r--r--app/Template/notification/task_internal_link_delete.php11
-rw-r--r--app/Template/plugin/sidebar.php1
-rw-r--r--app/Template/project/sidebar.php4
-rw-r--r--app/Template/project_creation/create.php5
-rw-r--r--app/Template/project_header/dropdown.php8
-rw-r--r--app/Template/project_header/header.php26
-rw-r--r--app/Template/project_header/search.php77
-rw-r--r--app/Template/project_list/show.php12
-rw-r--r--app/Template/project_overview/columns.php4
-rw-r--r--app/Template/project_overview/files.php2
-rw-r--r--app/Template/project_permission/index.php4
-rw-r--r--app/Template/project_tag/create.php16
-rw-r--r--app/Template/project_tag/edit.php17
-rw-r--r--app/Template/project_tag/index.php31
-rw-r--r--app/Template/project_tag/remove.php15
-rw-r--r--app/Template/project_user_overview/roles.php2
-rw-r--r--app/Template/project_user_overview/sidebar.php2
-rw-r--r--app/Template/project_user_overview/tasks.php2
-rw-r--r--app/Template/project_user_overview/tooltip_users.php2
-rw-r--r--app/Template/project_view/duplicate.php5
-rw-r--r--app/Template/project_view/show.php12
-rw-r--r--app/Template/search/activity.php9
-rw-r--r--app/Template/search/index.php9
-rw-r--r--app/Template/search/results.php2
-rw-r--r--app/Template/subtask/table.php2
-rw-r--r--app/Template/swimlane/table.php2
-rw-r--r--app/Template/tag/create.php16
-rw-r--r--app/Template/tag/edit.php17
-rw-r--r--app/Template/tag/index.php31
-rw-r--r--app/Template/tag/remove.php15
-rw-r--r--app/Template/task/analytics.php12
-rw-r--r--app/Template/task/color_picker.php11
-rw-r--r--app/Template/task/details.php262
-rw-r--r--app/Template/task/dropdown.php12
-rw-r--r--app/Template/task/layout.php1
-rw-r--r--app/Template/task/public.php7
-rw-r--r--app/Template/task/show.php87
-rw-r--r--app/Template/task/sidebar.php12
-rw-r--r--app/Template/task/time_tracking_details.php11
-rw-r--r--app/Template/task/transitions.php11
-rw-r--r--app/Template/task_bulk/show.php5
-rw-r--r--app/Template/task_creation/show.php76
-rw-r--r--app/Template/task_external_link/table.php2
-rw-r--r--app/Template/task_file/files.php2
-rw-r--r--app/Template/task_gantt_creation/show.php45
-rw-r--r--app/Template/task_import/sidebar.php1
-rw-r--r--app/Template/task_internal_link/table.php2
-rw-r--r--app/Template/task_list/show.php2
-rw-r--r--app/Template/task_modification/edit_description.php27
-rw-r--r--app/Template/task_modification/edit_task.php38
-rw-r--r--app/Template/task_modification/show.php44
-rw-r--r--app/Template/task_popover/change_assignee.php20
-rw-r--r--app/Template/task_popover/change_category.php20
-rw-r--r--app/Template/user_creation/local.php46
-rw-r--r--app/Template/user_creation/remote.php45
-rw-r--r--app/Template/user_list/show.php2
-rw-r--r--app/Template/user_modification/show.php8
-rw-r--r--app/Template/user_view/last.php4
-rw-r--r--app/Template/user_view/password_reset.php4
-rw-r--r--app/Template/user_view/sessions.php2
-rw-r--r--app/Template/user_view/sidebar.php76
-rw-r--r--app/Template/user_view/timesheet.php2
-rw-r--r--app/User/Avatar/LetterAvatarProvider.php2
-rw-r--r--app/User/OAuthUserProvider.php14
-rw-r--r--app/User/ReverseProxyUserProvider.php21
-rw-r--r--app/Validator/ProjectValidator.php8
-rw-r--r--app/Validator/TagValidator.php76
-rw-r--r--app/Validator/TaskValidator.php47
-rw-r--r--app/common.php1
-rw-r--r--app/constants.php4
-rw-r--r--app/functions.php76
374 files changed, 9157 insertions, 4441 deletions
diff --git a/app/Action/Base.php b/app/Action/Base.php
index e5c65a17..9a502a08 100644
--- a/app/Action/Base.php
+++ b/app/Action/Base.php
@@ -7,7 +7,7 @@ use Kanboard\Event\GenericEvent;
/**
* Base class for automatic actions
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
abstract class Base extends \Kanboard\Core\Base
@@ -216,7 +216,8 @@ abstract class Base extends \Kanboard\Core\Base
*/
public function hasRequiredProject(array $data)
{
- return isset($data['project_id']) && $data['project_id'] == $this->getProjectId();
+ return (isset($data['project_id']) && $data['project_id'] == $this->getProjectId()) ||
+ (isset($data['task']['project_id']) && $data['task']['project_id'] == $this->getProjectId());
}
/**
@@ -226,10 +227,14 @@ abstract class Base extends \Kanboard\Core\Base
* @param array $data Event data dictionary
* @return bool True if all keys are there
*/
- public function hasRequiredParameters(array $data)
+ public function hasRequiredParameters(array $data, array $parameters = array())
{
- foreach ($this->getEventRequiredParameters() as $parameter) {
- if (! isset($data[$parameter])) {
+ $parameters = $parameters ?: $this->getEventRequiredParameters();
+
+ foreach ($parameters as $key => $value) {
+ if (is_array($value)) {
+ return isset($data[$key]) && $this->hasRequiredParameters($data[$key], $value);
+ } else if (! isset($data[$value])) {
return false;
}
}
diff --git a/app/Action/CommentCreation.php b/app/Action/CommentCreation.php
index 60ca24f7..301d2cf9 100644
--- a/app/Action/CommentCreation.php
+++ b/app/Action/CommentCreation.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Create automatically a comment from a webhook
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class CommentCreation extends Base
diff --git a/app/Action/CommentCreationMoveTaskColumn.php b/app/Action/CommentCreationMoveTaskColumn.php
index 1b16f481..d5bdd807 100644
--- a/app/Action/CommentCreationMoveTaskColumn.php
+++ b/app/Action/CommentCreationMoveTaskColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Add a comment of the triggering event to the task description.
*
- * @package action
+ * @package Kanboard\Action
* @author Oren Ben-Kiki
*/
class CommentCreationMoveTaskColumn extends Base
@@ -55,7 +55,13 @@ class CommentCreationMoveTaskColumn extends Base
*/
public function getEventRequiredParameters()
{
- return array('task_id', 'column_id');
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'column_id',
+ 'project_id',
+ ),
+ );
}
/**
@@ -71,7 +77,7 @@ class CommentCreationMoveTaskColumn extends Base
return false;
}
- $column = $this->columnModel->getById($data['column_id']);
+ $column = $this->columnModel->getById($data['task']['column_id']);
return (bool) $this->commentModel->create(array(
'comment' => t('Moved to column %s', $column['title']),
@@ -89,6 +95,6 @@ class CommentCreationMoveTaskColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id');
+ return $data['task']['column_id'] == $this->getParam('column_id');
}
}
diff --git a/app/Action/TaskAssignCategoryColor.php b/app/Action/TaskAssignCategoryColor.php
index fc486870..9228e1ff 100644
--- a/app/Action/TaskAssignCategoryColor.php
+++ b/app/Action/TaskAssignCategoryColor.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Set a category automatically according to the color
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryColor extends Base
@@ -60,7 +60,10 @@ class TaskAssignCategoryColor extends Base
{
return array(
'task_id',
- 'color_id',
+ 'task' => array(
+ 'project_id',
+ 'color_id',
+ ),
);
}
@@ -90,6 +93,6 @@ class TaskAssignCategoryColor extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['color_id'] == $this->getParam('color_id');
+ return $data['task']['color_id'] == $this->getParam('color_id');
}
}
diff --git a/app/Action/TaskAssignCategoryLabel.php b/app/Action/TaskAssignCategoryLabel.php
index 48299010..c390414e 100644
--- a/app/Action/TaskAssignCategoryLabel.php
+++ b/app/Action/TaskAssignCategoryLabel.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Set a category automatically according to a label
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryLabel extends Base
diff --git a/app/Action/TaskAssignCategoryLink.php b/app/Action/TaskAssignCategoryLink.php
index 6937edd1..6c4b6c96 100644
--- a/app/Action/TaskAssignCategoryLink.php
+++ b/app/Action/TaskAssignCategoryLink.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskLinkModel;
/**
* Set a category automatically according to a task link
*
- * @package action
+ * @package Kanboard\Action
* @author Olivier Maridat
* @author Frederic Guillot
*/
@@ -60,8 +60,10 @@ class TaskAssignCategoryLink extends Base
public function getEventRequiredParameters()
{
return array(
- 'task_id',
- 'link_id',
+ 'task_link' => array(
+ 'task_id',
+ 'link_id',
+ )
);
}
@@ -75,7 +77,7 @@ class TaskAssignCategoryLink extends Base
public function doAction(array $data)
{
$values = array(
- 'id' => $data['task_id'],
+ 'id' => $data['task_link']['task_id'],
'category_id' => $this->getParam('category_id'),
);
@@ -91,9 +93,8 @@ class TaskAssignCategoryLink extends Base
*/
public function hasRequiredCondition(array $data)
{
- if ($data['link_id'] == $this->getParam('link_id')) {
- $task = $this->taskFinderModel->getById($data['task_id']);
- return empty($task['category_id']);
+ if ($data['task_link']['link_id'] == $this->getParam('link_id')) {
+ return empty($data['task']['category_id']);
}
return false;
diff --git a/app/Action/TaskAssignColorCategory.php b/app/Action/TaskAssignColorCategory.php
index 284b8f40..a136ffd2 100644
--- a/app/Action/TaskAssignColorCategory.php
+++ b/app/Action/TaskAssignColorCategory.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific category
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorCategory extends Base
@@ -60,7 +60,10 @@ class TaskAssignColorCategory extends Base
{
return array(
'task_id',
- 'category_id',
+ 'task' => array(
+ 'project_id',
+ 'category_id',
+ ),
);
}
@@ -90,6 +93,6 @@ class TaskAssignColorCategory extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['category_id'] == $this->getParam('category_id');
+ return $data['task']['category_id'] == $this->getParam('category_id');
}
}
diff --git a/app/Action/TaskAssignColorColumn.php b/app/Action/TaskAssignColorColumn.php
index 57fd6f44..da6e3aed 100644
--- a/app/Action/TaskAssignColorColumn.php
+++ b/app/Action/TaskAssignColorColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorColumn extends Base
@@ -61,7 +61,10 @@ class TaskAssignColorColumn extends Base
{
return array(
'task_id',
- 'column_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ ),
);
}
@@ -91,6 +94,6 @@ class TaskAssignColorColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id');
+ return $data['task']['column_id'] == $this->getParam('column_id');
}
}
diff --git a/app/Action/TaskAssignColorLink.php b/app/Action/TaskAssignColorLink.php
index 9ab5458b..19c37afe 100644
--- a/app/Action/TaskAssignColorLink.php
+++ b/app/Action/TaskAssignColorLink.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskLinkModel;
/**
* Assign a color to a specific task link
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorLink extends Base
@@ -59,8 +59,10 @@ class TaskAssignColorLink extends Base
public function getEventRequiredParameters()
{
return array(
- 'task_id',
- 'link_id',
+ 'task_link' => array(
+ 'task_id',
+ 'link_id',
+ )
);
}
@@ -74,7 +76,7 @@ class TaskAssignColorLink extends Base
public function doAction(array $data)
{
$values = array(
- 'id' => $data['task_id'],
+ 'id' => $data['task_link']['task_id'],
'color_id' => $this->getParam('color_id'),
);
@@ -90,6 +92,6 @@ class TaskAssignColorLink extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['link_id'] == $this->getParam('link_id');
+ return $data['task_link']['link_id'] == $this->getParam('link_id');
}
}
diff --git a/app/Action/TaskAssignColorPriority.php b/app/Action/TaskAssignColorPriority.php
index eae1b771..37f7ffed 100644
--- a/app/Action/TaskAssignColorPriority.php
+++ b/app/Action/TaskAssignColorPriority.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a priority
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorPriority extends Base
@@ -60,7 +60,10 @@ class TaskAssignColorPriority extends Base
{
return array(
'task_id',
- 'priority',
+ 'task' => array(
+ 'project_id',
+ 'priority',
+ ),
);
}
@@ -90,6 +93,6 @@ class TaskAssignColorPriority extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['priority'] == $this->getParam('priority');
+ return $data['task']['priority'] == $this->getParam('priority');
}
}
diff --git a/app/Action/TaskAssignColorUser.php b/app/Action/TaskAssignColorUser.php
index 4bcf7a5c..468d0198 100644
--- a/app/Action/TaskAssignColorUser.php
+++ b/app/Action/TaskAssignColorUser.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific user
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorUser extends Base
@@ -61,7 +61,10 @@ class TaskAssignColorUser extends Base
{
return array(
'task_id',
- 'owner_id',
+ 'task' => array(
+ 'project_id',
+ 'owner_id',
+ ),
);
}
@@ -91,6 +94,6 @@ class TaskAssignColorUser extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['owner_id'] == $this->getParam('user_id');
+ return $data['task']['owner_id'] == $this->getParam('user_id');
}
}
diff --git a/app/Action/TaskAssignCurrentUser.php b/app/Action/TaskAssignCurrentUser.php
index 997aa98f..dee5e7db 100644
--- a/app/Action/TaskAssignCurrentUser.php
+++ b/app/Action/TaskAssignCurrentUser.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUser extends Base
diff --git a/app/Action/TaskAssignCurrentUserColumn.php b/app/Action/TaskAssignCurrentUserColumn.php
index bc28a90b..60ada7ef 100644
--- a/app/Action/TaskAssignCurrentUserColumn.php
+++ b/app/Action/TaskAssignCurrentUserColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user on column change
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUserColumn extends Base
@@ -59,7 +59,10 @@ class TaskAssignCurrentUserColumn extends Base
{
return array(
'task_id',
- 'column_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ ),
);
}
@@ -93,6 +96,6 @@ class TaskAssignCurrentUserColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id');
+ return $data['task']['column_id'] == $this->getParam('column_id');
}
}
diff --git a/app/Action/TaskAssignDueDateOnCreation.php b/app/Action/TaskAssignDueDateOnCreation.php
new file mode 100644
index 00000000..5c6e2b61
--- /dev/null
+++ b/app/Action/TaskAssignDueDateOnCreation.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+/**
+ * Set the due date of task
+ *
+ * @package Kanboard\Action
+ * @author Frederic Guillot
+ */
+class TaskAssignDueDateOnCreation extends Base
+{
+ /**
+ * Get automatic action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Automatically set the due date on task creation');
+ }
+
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(
+ TaskModel::EVENT_CREATE,
+ );
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'duration' => t('Duration in days')
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'project_id',
+ ),
+ );
+ }
+
+ /**
+ * Execute the action (set the task color)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ $values = array(
+ 'id' => $data['task_id'],
+ 'date_due' => strtotime('+'.$this->getParam('duration').'days'),
+ );
+
+ return $this->taskModificationModel->update($values, false);
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return true;
+ }
+}
diff --git a/app/Action/TaskAssignSpecificUser.php b/app/Action/TaskAssignSpecificUser.php
index 50a2b2ae..daf9e1df 100644
--- a/app/Action/TaskAssignSpecificUser.php
+++ b/app/Action/TaskAssignSpecificUser.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Assign a task to a specific user
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignSpecificUser extends Base
@@ -61,7 +61,10 @@ class TaskAssignSpecificUser extends Base
{
return array(
'task_id',
- 'column_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ ),
);
}
@@ -91,6 +94,6 @@ class TaskAssignSpecificUser extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id');
+ return $data['task']['column_id'] == $this->getParam('column_id');
}
}
diff --git a/app/Action/TaskAssignUser.php b/app/Action/TaskAssignUser.php
index 9ea22986..8727b672 100644
--- a/app/Action/TaskAssignUser.php
+++ b/app/Action/TaskAssignUser.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Assign a task to someone
*
- * @package action
+ * @package Kanboard\Actionv
* @author Frederic Guillot
*/
class TaskAssignUser extends Base
diff --git a/app/Action/TaskClose.php b/app/Action/TaskClose.php
index 91e8cf43..e476e9ba 100644
--- a/app/Action/TaskClose.php
+++ b/app/Action/TaskClose.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Close automatically a task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskClose extends Base
diff --git a/app/Action/TaskCloseColumn.php b/app/Action/TaskCloseColumn.php
index 1edce8fa..523996f4 100644
--- a/app/Action/TaskCloseColumn.php
+++ b/app/Action/TaskCloseColumn.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Close automatically a task in a specific column
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseColumn extends Base
@@ -55,7 +55,13 @@ class TaskCloseColumn extends Base
*/
public function getEventRequiredParameters()
{
- return array('task_id', 'column_id');
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ )
+ );
}
/**
@@ -79,6 +85,6 @@ class TaskCloseColumn extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id');
+ return $data['task']['column_id'] == $this->getParam('column_id');
}
}
diff --git a/app/Action/TaskCloseNoActivity.php b/app/Action/TaskCloseNoActivity.php
index 5a10510f..ea724d8c 100644
--- a/app/Action/TaskCloseNoActivity.php
+++ b/app/Action/TaskCloseNoActivity.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Close automatically a task after when inactive
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNoActivity extends Base
diff --git a/app/Action/TaskCloseNoActivityColumn.php b/app/Action/TaskCloseNoActivityColumn.php
new file mode 100644
index 00000000..b2ee5224
--- /dev/null
+++ b/app/Action/TaskCloseNoActivityColumn.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+/**
+ * Close automatically a task after inactive and in an defined column
+ *
+ * @package Kanboard\Action
+ * @author Frederic Guillot
+ */
+class TaskCloseNoActivityColumn extends Base
+{
+ /**
+ * Get automatic action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Close a task when there is no activity in an specific column');
+ }
+
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(TaskModel::EVENT_DAILY_CRONJOB);
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'duration' => t('Duration in days'),
+ 'column_id' => t('Column')
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array('tasks');
+ }
+
+ /**
+ * Execute the action (close the task)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ $results = array();
+ $max = $this->getParam('duration') * 86400;
+
+ foreach ($data['tasks'] as $task) {
+ $duration = time() - $task['date_modification'];
+
+ if ($duration > $max && $task['column_id'] == $this->getParam('column_id')) {
+ $results[] = $this->taskStatusModel->close($task['id']);
+ }
+ }
+
+ return in_array(true, $results, true);
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return count($data['tasks']) > 0;
+ }
+}
diff --git a/app/Action/TaskCreation.php b/app/Action/TaskCreation.php
index e9e5c5f3..01d91228 100644
--- a/app/Action/TaskCreation.php
+++ b/app/Action/TaskCreation.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Create automatically a task from a webhook
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCreation extends Base
@@ -52,6 +52,7 @@ class TaskCreation extends Base
public function getEventRequiredParameters()
{
return array(
+ 'project_id',
'reference',
'title',
);
diff --git a/app/Action/TaskDuplicateAnotherProject.php b/app/Action/TaskDuplicateAnotherProject.php
index 1d4a2f13..0ad7713c 100644
--- a/app/Action/TaskDuplicateAnotherProject.php
+++ b/app/Action/TaskDuplicateAnotherProject.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Duplicate a task to another project
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskDuplicateAnotherProject extends Base
@@ -62,7 +62,10 @@ class TaskDuplicateAnotherProject extends Base
{
return array(
'task_id',
- 'column_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ )
);
}
@@ -76,7 +79,12 @@ class TaskDuplicateAnotherProject extends Base
public function doAction(array $data)
{
$destination_column_id = $this->columnModel->getFirstColumnId($this->getParam('project_id'));
- return (bool) $this->taskDuplicationModel->duplicateToProject($data['task_id'], $this->getParam('project_id'), null, $destination_column_id);
+ return (bool) $this->taskProjectDuplicationModel->duplicateToProject(
+ $data['task_id'],
+ $this->getParam('project_id'),
+ null,
+ $destination_column_id
+ );
}
/**
@@ -88,6 +96,6 @@ class TaskDuplicateAnotherProject extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id');
+ return $data['task']['column_id'] == $this->getParam('column_id') && $data['task']['project_id'] != $this->getParam('project_id');
}
}
diff --git a/app/Action/TaskEmail.php b/app/Action/TaskEmail.php
index 7f9ba416..fdfe7987 100644
--- a/app/Action/TaskEmail.php
+++ b/app/Action/TaskEmail.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Email a task to someone
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmail extends Base
@@ -62,7 +62,10 @@ class TaskEmail extends Base
{
return array(
'task_id',
- 'column_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ ),
);
}
@@ -78,13 +81,14 @@ class TaskEmail extends Base
$user = $this->userModel->getById($this->getParam('user_id'));
if (! empty($user['email'])) {
- $task = $this->taskFinderModel->getDetails($data['task_id']);
-
$this->emailClient->send(
$user['email'],
$user['name'] ?: $user['username'],
$this->getParam('subject'),
- $this->template->render('notification/task_create', array('task' => $task, 'application_url' => $this->configModel->get('application_url')))
+ $this->template->render('notification/task_create', array(
+ 'task' => $data['task'],
+ 'application_url' => $this->configModel->get('application_url'),
+ ))
);
return true;
@@ -102,6 +106,6 @@ class TaskEmail extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id');
+ return $data['task']['column_id'] == $this->getParam('column_id');
}
}
diff --git a/app/Action/TaskEmailNoActivity.php b/app/Action/TaskEmailNoActivity.php
index c60702fb..cac4281e 100644
--- a/app/Action/TaskEmailNoActivity.php
+++ b/app/Action/TaskEmailNoActivity.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Email a task with no activity
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmailNoActivity extends Base
diff --git a/app/Action/TaskMoveAnotherProject.php b/app/Action/TaskMoveAnotherProject.php
index 73ad4b69..0fa22b1b 100644
--- a/app/Action/TaskMoveAnotherProject.php
+++ b/app/Action/TaskMoveAnotherProject.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another project
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveAnotherProject extends Base
@@ -61,8 +61,10 @@ class TaskMoveAnotherProject extends Base
{
return array(
'task_id',
- 'column_id',
- 'project_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ )
);
}
@@ -75,7 +77,7 @@ class TaskMoveAnotherProject extends Base
*/
public function doAction(array $data)
{
- return $this->taskDuplicationModel->moveToProject($data['task_id'], $this->getParam('project_id'));
+ return $this->taskProjectMoveModel->moveToProject($data['task_id'], $this->getParam('project_id'));
}
/**
@@ -87,6 +89,6 @@ class TaskMoveAnotherProject extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id');
+ return $data['task']['column_id'] == $this->getParam('column_id') && $data['task']['project_id'] != $this->getParam('project_id');
}
}
diff --git a/app/Action/TaskMoveColumnAssigned.php b/app/Action/TaskMoveColumnAssigned.php
index 7e3db9c5..1cfe6743 100644
--- a/app/Action/TaskMoveColumnAssigned.php
+++ b/app/Action/TaskMoveColumnAssigned.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is set
*
- * @package action
+ * @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnAssigned extends Base
@@ -61,8 +61,13 @@ class TaskMoveColumnAssigned extends Base
{
return array(
'task_id',
- 'column_id',
- 'owner_id'
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ 'owner_id',
+ 'position',
+ 'swimlane_id',
+ )
);
}
@@ -75,14 +80,12 @@ class TaskMoveColumnAssigned extends Base
*/
public function doAction(array $data)
{
- $original_task = $this->taskFinderModel->getById($data['task_id']);
-
return $this->taskPositionModel->movePosition(
- $data['project_id'],
+ $data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
- $original_task['position'],
- $original_task['swimlane_id'],
+ $data['task']['position'],
+ $data['task']['swimlane_id'],
false
);
}
@@ -96,6 +99,6 @@ class TaskMoveColumnAssigned extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('src_column_id') && $data['owner_id'] > 0;
+ return $data['task']['column_id'] == $this->getParam('src_column_id') && $data['task']['owner_id'] > 0;
}
}
diff --git a/app/Action/TaskMoveColumnCategoryChange.php b/app/Action/TaskMoveColumnCategoryChange.php
index e4f88760..13d6ee4f 100644
--- a/app/Action/TaskMoveColumnCategoryChange.php
+++ b/app/Action/TaskMoveColumnCategoryChange.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when the category is changed
*
- * @package action
+ * @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnCategoryChange extends Base
@@ -60,8 +60,13 @@ class TaskMoveColumnCategoryChange extends Base
{
return array(
'task_id',
- 'column_id',
- 'category_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ 'category_id',
+ 'position',
+ 'swimlane_id',
+ )
);
}
@@ -74,14 +79,12 @@ class TaskMoveColumnCategoryChange extends Base
*/
public function doAction(array $data)
{
- $original_task = $this->taskFinderModel->getById($data['task_id']);
-
return $this->taskPositionModel->movePosition(
- $data['project_id'],
+ $data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
- $original_task['position'],
- $original_task['swimlane_id'],
+ $data['task']['position'],
+ $data['task']['swimlane_id'],
false
);
}
@@ -95,6 +98,6 @@ class TaskMoveColumnCategoryChange extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] != $this->getParam('dest_column_id') && $data['category_id'] == $this->getParam('category_id');
+ return $data['task']['column_id'] != $this->getParam('dest_column_id') && $data['task']['category_id'] == $this->getParam('category_id');
}
}
diff --git a/app/Action/TaskMoveColumnClosed.php b/app/Action/TaskMoveColumnClosed.php
new file mode 100644
index 00000000..3f3e2124
--- /dev/null
+++ b/app/Action/TaskMoveColumnClosed.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+/**
+ * Move a task to another column when the task is closed
+ *
+ * @package Kanboard\Action
+ * @author Frederic Guillot
+ */
+class TaskMoveColumnClosed extends Base
+{
+ /**
+ * Get automatic action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Move the task to another column when closed');
+ }
+
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(
+ TaskModel::EVENT_CLOSE,
+ );
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'dest_column_id' => t('Destination column'),
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ 'swimlane_id',
+ 'is_active',
+ )
+ );
+ }
+
+ /**
+ * Execute the action (move the task to another column)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ return $this->taskPositionModel->movePosition(
+ $data['task']['project_id'],
+ $data['task']['id'],
+ $this->getParam('dest_column_id'),
+ 1,
+ $data['task']['swimlane_id'],
+ false,
+ false
+ );
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return $data['task']['column_id'] != $this->getParam('dest_column_id') && $data['task']['is_active'] == 0;
+ }
+}
diff --git a/app/Action/TaskMoveColumnNotMovedPeriod.php b/app/Action/TaskMoveColumnNotMovedPeriod.php
new file mode 100644
index 00000000..87e7e405
--- /dev/null
+++ b/app/Action/TaskMoveColumnNotMovedPeriod.php
@@ -0,0 +1,104 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+/**
+ * Move a task to another column when not moved during a given period
+ *
+ * @package Kanboard\Action
+ * @author Frederic Guillot
+ */
+class TaskMoveColumnNotMovedPeriod extends Base
+{
+ /**
+ * Get automatic action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Move the task to another column when not moved during a given period');
+ }
+
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(TaskModel::EVENT_DAILY_CRONJOB);
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'duration' => t('Duration in days'),
+ 'src_column_id' => t('Source column'),
+ 'dest_column_id' => t('Destination column'),
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array('tasks');
+ }
+
+ /**
+ * Execute the action (close the task)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ $results = array();
+ $max = $this->getParam('duration') * 86400;
+
+ foreach ($data['tasks'] as $task) {
+ $duration = time() - $task['date_moved'];
+
+ if ($duration > $max && $task['column_id'] == $this->getParam('src_column_id')) {
+ $results[] = $this->taskPositionModel->movePosition(
+ $task['project_id'],
+ $task['id'],
+ $this->getParam('dest_column_id'),
+ 1,
+ $task['swimlane_id'],
+ false
+ );
+ }
+ }
+
+ return in_array(true, $results, true);
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return count($data['tasks']) > 0;
+ }
+}
diff --git a/app/Action/TaskMoveColumnUnAssigned.php b/app/Action/TaskMoveColumnUnAssigned.php
index c3ae9e1d..ab63d624 100644
--- a/app/Action/TaskMoveColumnUnAssigned.php
+++ b/app/Action/TaskMoveColumnUnAssigned.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is cleared
*
- * @package action
+ * @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnUnAssigned extends Base
@@ -61,8 +61,13 @@ class TaskMoveColumnUnAssigned extends Base
{
return array(
'task_id',
- 'column_id',
- 'owner_id'
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ 'owner_id',
+ 'position',
+ 'swimlane_id',
+ )
);
}
@@ -75,14 +80,12 @@ class TaskMoveColumnUnAssigned extends Base
*/
public function doAction(array $data)
{
- $original_task = $this->taskFinderModel->getById($data['task_id']);
-
return $this->taskPositionModel->movePosition(
- $data['project_id'],
+ $data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
- $original_task['position'],
- $original_task['swimlane_id'],
+ $data['task']['position'],
+ $data['task']['swimlane_id'],
false
);
}
@@ -96,6 +99,6 @@ class TaskMoveColumnUnAssigned extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('src_column_id') && $data['owner_id'] == 0;
+ return $data['task']['column_id'] == $this->getParam('src_column_id') && $data['task']['owner_id'] == 0;
}
}
diff --git a/app/Action/TaskOpen.php b/app/Action/TaskOpen.php
index 8e847b8e..49017831 100644
--- a/app/Action/TaskOpen.php
+++ b/app/Action/TaskOpen.php
@@ -5,7 +5,7 @@ namespace Kanboard\Action;
/**
* Open automatically a task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskOpen extends Base
diff --git a/app/Action/TaskUpdateStartDate.php b/app/Action/TaskUpdateStartDate.php
index e5410a87..160f6ee5 100644
--- a/app/Action/TaskUpdateStartDate.php
+++ b/app/Action/TaskUpdateStartDate.php
@@ -7,7 +7,7 @@ use Kanboard\Model\TaskModel;
/**
* Set the start date of task
*
- * @package action
+ * @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskUpdateStartDate extends Base
@@ -59,7 +59,10 @@ class TaskUpdateStartDate extends Base
{
return array(
'task_id',
- 'column_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ ),
);
}
@@ -89,6 +92,6 @@ class TaskUpdateStartDate extends Base
*/
public function hasRequiredCondition(array $data)
{
- return $data['column_id'] == $this->getParam('column_id');
+ return $data['task']['column_id'] == $this->getParam('column_id');
}
}
diff --git a/app/Api/Authorization/ActionAuthorization.php b/app/Api/Authorization/ActionAuthorization.php
new file mode 100644
index 00000000..4b41ad82
--- /dev/null
+++ b/app/Api/Authorization/ActionAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class ActionAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class ActionAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $action_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->actionModel->getProjectId($action_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/CategoryAuthorization.php b/app/Api/Authorization/CategoryAuthorization.php
new file mode 100644
index 00000000..f17265a2
--- /dev/null
+++ b/app/Api/Authorization/CategoryAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class CategoryAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class CategoryAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $category_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->categoryModel->getProjectId($category_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/ColumnAuthorization.php b/app/Api/Authorization/ColumnAuthorization.php
new file mode 100644
index 00000000..37aecda2
--- /dev/null
+++ b/app/Api/Authorization/ColumnAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class ColumnAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class ColumnAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $column_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->columnModel->getProjectId($column_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/CommentAuthorization.php b/app/Api/Authorization/CommentAuthorization.php
new file mode 100644
index 00000000..ed15512e
--- /dev/null
+++ b/app/Api/Authorization/CommentAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class CommentAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class CommentAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $comment_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->commentModel->getProjectId($comment_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/ProcedureAuthorization.php b/app/Api/Authorization/ProcedureAuthorization.php
new file mode 100644
index 00000000..070a6371
--- /dev/null
+++ b/app/Api/Authorization/ProcedureAuthorization.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+use JsonRPC\Exception\AccessDeniedException;
+use Kanboard\Core\Base;
+
+/**
+ * Class ProcedureAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class ProcedureAuthorization extends Base
+{
+ private $userSpecificProcedures = array(
+ 'getMe',
+ 'getMyDashboard',
+ 'getMyActivityStream',
+ 'createMyPrivateProject',
+ 'getMyProjectsList',
+ 'getMyProjects',
+ 'getMyOverdueTasks',
+ );
+
+ public function check($procedure)
+ {
+ if (! $this->userSession->isLogged() && in_array($procedure, $this->userSpecificProcedures)) {
+ throw new AccessDeniedException('This procedure is not available with the API credentials');
+ }
+ }
+}
diff --git a/app/Api/Authorization/ProjectAuthorization.php b/app/Api/Authorization/ProjectAuthorization.php
new file mode 100644
index 00000000..21ecf311
--- /dev/null
+++ b/app/Api/Authorization/ProjectAuthorization.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+use JsonRPC\Exception\AccessDeniedException;
+use Kanboard\Core\Base;
+
+/**
+ * Class ProjectAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class ProjectAuthorization extends Base
+{
+ public function check($class, $method, $project_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $project_id);
+ }
+ }
+
+ protected function checkProjectPermission($class, $method, $project_id)
+ {
+ if (empty($project_id)) {
+ throw new AccessDeniedException('Project not found');
+ }
+
+ $role = $this->projectUserRoleModel->getUserRole($project_id, $this->userSession->getId());
+
+ if (! $this->apiProjectAuthorization->isAllowed($class, $method, $role)) {
+ throw new AccessDeniedException('Project access denied');
+ }
+ }
+}
diff --git a/app/Api/Authorization/SubtaskAuthorization.php b/app/Api/Authorization/SubtaskAuthorization.php
new file mode 100644
index 00000000..fcb57929
--- /dev/null
+++ b/app/Api/Authorization/SubtaskAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class SubtaskAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class SubtaskAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $subtask_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->subtaskModel->getProjectId($subtask_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskAuthorization.php b/app/Api/Authorization/TaskAuthorization.php
new file mode 100644
index 00000000..db93b76b
--- /dev/null
+++ b/app/Api/Authorization/TaskAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class TaskAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class TaskAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $category_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($category_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskFileAuthorization.php b/app/Api/Authorization/TaskFileAuthorization.php
new file mode 100644
index 00000000..e40783eb
--- /dev/null
+++ b/app/Api/Authorization/TaskFileAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class TaskFileAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class TaskFileAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $file_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->taskFileModel->getProjectId($file_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskLinkAuthorization.php b/app/Api/Authorization/TaskLinkAuthorization.php
new file mode 100644
index 00000000..2f5fc8d5
--- /dev/null
+++ b/app/Api/Authorization/TaskLinkAuthorization.php
@@ -0,0 +1,19 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class TaskLinkAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class TaskLinkAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $task_link_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $this->checkProjectPermission($class, $method, $this->taskLinkModel->getProjectId($task_link_id));
+ }
+ }
+}
diff --git a/app/Api/Authorization/UserAuthorization.php b/app/Api/Authorization/UserAuthorization.php
new file mode 100644
index 00000000..3fd6865c
--- /dev/null
+++ b/app/Api/Authorization/UserAuthorization.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+use JsonRPC\Exception\AccessDeniedException;
+use Kanboard\Core\Base;
+
+/**
+ * Class UserAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class UserAuthorization extends Base
+{
+ public function check($class, $method)
+ {
+ if ($this->userSession->isLogged() && ! $this->apiAuthorization->isAllowed($class, $method, $this->userSession->getRole())) {
+ throw new AccessDeniedException('You are not allowed to access to this resource');
+ }
+ }
+}
diff --git a/app/Api/BoardApi.php b/app/Api/BoardApi.php
deleted file mode 100644
index aa5942af..00000000
--- a/app/Api/BoardApi.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-namespace Kanboard\Api;
-
-/**
- * Board API controller
- *
- * @package Kanboard\Api
- * @author Frederic Guillot
- */
-class BoardApi extends BaseApi
-{
- public function getBoard($project_id)
- {
- $this->checkProjectPermission($project_id);
- return $this->boardModel->getBoard($project_id);
- }
-}
diff --git a/app/Api/Middleware/AuthenticationApiMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php
index b16e10b8..8e309593 100644
--- a/app/Api/Middleware/AuthenticationApiMiddleware.php
+++ b/app/Api/Middleware/AuthenticationMiddleware.php
@@ -13,46 +13,8 @@ use Kanboard\Core\Base;
* @package Kanboard\Api\Middleware
* @author Frederic Guillot
*/
-class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
+class AuthenticationMiddleware extends Base implements MiddlewareInterface
{
- private $user_allowed_procedures = array(
- 'getMe',
- 'getMyDashboard',
- 'getMyActivityStream',
- 'createMyPrivateProject',
- 'getMyProjectsList',
- 'getMyProjects',
- 'getMyOverdueTasks',
- );
-
- private $both_allowed_procedures = array(
- 'getTimezone',
- 'getVersion',
- 'getDefaultTaskColor',
- 'getDefaultTaskColors',
- 'getColorList',
- 'getProjectById',
- 'getSubTask',
- 'getTask',
- 'getTaskByReference',
- 'getTimeSpent',
- 'getAllTasks',
- 'getAllSubTasks',
- 'hasTimer',
- 'logStartTime',
- 'logEndTime',
- 'openTask',
- 'closeTask',
- 'moveTaskPosition',
- 'createTask',
- 'createSubtask',
- 'updateTask',
- 'getBoard',
- 'getProjectActivity',
- 'getOverdueTasksByProject',
- 'searchTasks',
- );
-
/**
* Execute Middleware
*
@@ -68,11 +30,8 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
$this->dispatcher->dispatch('app.bootstrap');
if ($this->isUserAuthenticated($username, $password)) {
- $this->checkProcedurePermission(true, $procedureName);
$this->userSession->initialize($this->userModel->getByUsername($username));
- } elseif ($this->isAppAuthenticated($username, $password)) {
- $this->checkProcedurePermission(false, $procedureName);
- } else {
+ } elseif (! $this->isAppAuthenticated($username, $password)) {
$this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailureException('Wrong credentials');
}
@@ -120,18 +79,4 @@ class AuthenticationApiMiddleware extends Base implements MiddlewareInterface
return $this->configModel->get('api_token');
}
-
- public function checkProcedurePermission($is_user, $procedure)
- {
- $is_both_procedure = in_array($procedure, $this->both_allowed_procedures);
- $is_user_procedure = in_array($procedure, $this->user_allowed_procedures);
-
- if ($is_user && ! $is_both_procedure && ! $is_user_procedure) {
- throw new AccessDeniedException('Permission denied');
- } elseif (! $is_user && ! $is_both_procedure && $is_user_procedure) {
- throw new AccessDeniedException('Permission denied');
- }
-
- $this->logger->debug('API call: '.$procedure);
- }
}
diff --git a/app/Api/ActionApi.php b/app/Api/Procedure/ActionProcedure.php
index 116742d8..4043dbb9 100644
--- a/app/Api/ActionApi.php
+++ b/app/Api/Procedure/ActionProcedure.php
@@ -1,16 +1,17 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\ActionAuthorization;
+use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Action API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class ActionApi extends Base
+class ActionProcedure extends BaseProcedure
{
public function getAvailableActions()
{
@@ -29,16 +30,19 @@ class ActionApi extends Base
public function removeAction($action_id)
{
+ ActionAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAction', $action_id);
return $this->actionModel->remove($action_id);
}
public function getActions($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActions', $project_id);
return $this->actionModel->getAllByProject($project_id);
}
public function createAction($project_id, $event_name, $action_name, array $params)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createAction', $project_id);
$values = array(
'project_id' => $project_id,
'event_name' => $event_name,
diff --git a/app/Api/AppApi.php b/app/Api/Procedure/AppProcedure.php
index 637de5c5..60af4a60 100644
--- a/app/Api/AppApi.php
+++ b/app/Api/Procedure/AppProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* App API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class AppApi extends Base
+class AppProcedure extends BaseProcedure
{
public function getTimezone()
{
diff --git a/app/Api/BaseApi.php b/app/Api/Procedure/BaseProcedure.php
index 9f69aa65..e31b3027 100644
--- a/app/Api/BaseApi.php
+++ b/app/Api/Procedure/BaseProcedure.php
@@ -1,30 +1,24 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use JsonRPC\Exception\AccessDeniedException;
+use Kanboard\Api\Authorization\ProcedureAuthorization;
+use Kanboard\Api\Authorization\UserAuthorization;
use Kanboard\Core\Base;
+use ReflectionClass;
/**
* Base class
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-abstract class BaseApi extends Base
+abstract class BaseProcedure extends Base
{
- public function checkProjectPermission($project_id)
+ public function beforeProcedure($procedure)
{
- if ($this->userSession->isLogged() && ! $this->projectPermissionModel->isUserAllowed($project_id, $this->userSession->getId())) {
- throw new AccessDeniedException('Permission denied');
- }
- }
-
- public function checkTaskPermission($task_id)
- {
- if ($this->userSession->isLogged()) {
- $this->checkProjectPermission($this->taskFinderModel->getProjectId($task_id));
- }
+ ProcedureAuthorization::getInstance($this->container)->check($procedure);
+ UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure);
}
protected function formatTask($task)
@@ -71,4 +65,21 @@ abstract class BaseApi extends Base
return $projects;
}
+
+ protected function filterValues(array $values)
+ {
+ foreach ($values as $key => $value) {
+ if (is_null($value)) {
+ unset($values[$key]);
+ }
+ }
+
+ return $values;
+ }
+
+ protected function getClassName()
+ {
+ $reflection = new ReflectionClass(get_called_class());
+ return $reflection->getShortName();
+ }
}
diff --git a/app/Api/Procedure/BoardProcedure.php b/app/Api/Procedure/BoardProcedure.php
new file mode 100644
index 00000000..674b5466
--- /dev/null
+++ b/app/Api/Procedure/BoardProcedure.php
@@ -0,0 +1,25 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Formatter\BoardFormatter;
+
+/**
+ * Board API controller
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class BoardProcedure extends BaseProcedure
+{
+ public function getBoard($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id);
+
+ return BoardFormatter::getInstance($this->container)
+ ->withProjectId($project_id)
+ ->withQuery($this->taskFinderModel->getExtendedQuery())
+ ->format();
+ }
+}
diff --git a/app/Api/CategoryApi.php b/app/Api/Procedure/CategoryProcedure.php
index c56cfb35..3ebbd908 100644
--- a/app/Api/CategoryApi.php
+++ b/app/Api/Procedure/CategoryProcedure.php
@@ -1,34 +1,40 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\CategoryAuthorization;
+use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Category API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class CategoryApi extends Base
+class CategoryProcedure extends BaseProcedure
{
public function getCategory($category_id)
{
+ CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'getCategory', $category_id);
return $this->categoryModel->getById($category_id);
}
public function getAllCategories($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllCategories', $project_id);
return $this->categoryModel->getAll($project_id);
}
public function removeCategory($category_id)
{
+ CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeCategory', $category_id);
return $this->categoryModel->remove($category_id);
}
public function createCategory($project_id, $name)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createCategory', $project_id);
+
$values = array(
'project_id' => $project_id,
'name' => $name,
@@ -40,6 +46,8 @@ class CategoryApi extends Base
public function updateCategory($id, $name)
{
+ CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id);
+
$values = array(
'id' => $id,
'name' => $name,
diff --git a/app/Api/ColumnApi.php b/app/Api/Procedure/ColumnProcedure.php
index aa4026f6..ab9d173b 100644
--- a/app/Api/ColumnApi.php
+++ b/app/Api/Procedure/ColumnProcedure.php
@@ -1,42 +1,51 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ColumnAuthorization;
+use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Column API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class ColumnApi extends BaseApi
+class ColumnProcedure extends BaseProcedure
{
public function getColumns($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumns', $project_id);
return $this->columnModel->getAll($project_id);
}
public function getColumn($column_id)
{
+ ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumn', $column_id);
return $this->columnModel->getById($column_id);
}
public function updateColumn($column_id, $title, $task_limit = 0, $description = '')
{
+ ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateColumn', $column_id);
return $this->columnModel->update($column_id, $title, $task_limit, $description);
}
public function addColumn($project_id, $title, $task_limit = 0, $description = '')
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addColumn', $project_id);
return $this->columnModel->create($project_id, $title, $task_limit, $description);
}
public function removeColumn($column_id)
{
+ ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeColumn', $column_id);
return $this->columnModel->remove($column_id);
}
public function changeColumnPosition($project_id, $column_id, $position)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeColumnPosition', $project_id);
return $this->columnModel->changePosition($project_id, $column_id, $position);
}
}
diff --git a/app/Api/CommentApi.php b/app/Api/Procedure/CommentProcedure.php
index 8358efee..019a49bb 100644
--- a/app/Api/CommentApi.php
+++ b/app/Api/Procedure/CommentProcedure.php
@@ -1,34 +1,40 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\CommentAuthorization;
+use Kanboard\Api\Authorization\TaskAuthorization;
/**
* Comment API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class CommentApi extends Base
+class CommentProcedure extends BaseProcedure
{
public function getComment($comment_id)
{
+ CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'getComment', $comment_id);
return $this->commentModel->getById($comment_id);
}
public function getAllComments($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllComments', $task_id);
return $this->commentModel->getAll($task_id);
}
public function removeComment($comment_id)
{
+ CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeComment', $comment_id);
return $this->commentModel->remove($comment_id);
}
public function createComment($task_id, $user_id, $content, $reference = '')
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createComment', $task_id);
+
$values = array(
'task_id' => $task_id,
'user_id' => $user_id,
@@ -43,6 +49,8 @@ class CommentApi extends Base
public function updateComment($id, $content)
{
+ CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id);
+
$values = array(
'id' => $id,
'comment' => $content,
diff --git a/app/Api/GroupMemberApi.php b/app/Api/Procedure/GroupMemberProcedure.php
index e09f6975..081d6ac8 100644
--- a/app/Api/GroupMemberApi.php
+++ b/app/Api/Procedure/GroupMemberProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* Group Member API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class GroupMemberApi extends Base
+class GroupMemberProcedure extends BaseProcedure
{
public function getMemberGroups($user_id)
{
diff --git a/app/Api/GroupApi.php b/app/Api/Procedure/GroupProcedure.php
index 1701edc3..804940a2 100644
--- a/app/Api/GroupApi.php
+++ b/app/Api/Procedure/GroupProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* Group API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class GroupApi extends Base
+class GroupProcedure extends BaseProcedure
{
public function createGroup($name, $external_id = '')
{
diff --git a/app/Api/LinkApi.php b/app/Api/Procedure/LinkProcedure.php
index d8e525e4..b4cecf3a 100644
--- a/app/Api/LinkApi.php
+++ b/app/Api/Procedure/LinkProcedure.php
@@ -1,16 +1,14 @@
<?php
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
+namespace Kanboard\Api\Procedure;
/**
* Link API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class LinkApi extends Base
+class LinkProcedure extends BaseProcedure
{
/**
* Get a link by id
diff --git a/app/Api/MeApi.php b/app/Api/Procedure/MeProcedure.php
index 497749b6..e59e6522 100644
--- a/app/Api/MeApi.php
+++ b/app/Api/Procedure/MeProcedure.php
@@ -1,16 +1,16 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
use Kanboard\Model\SubtaskModel;
/**
* Me API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class MeApi extends BaseApi
+class MeProcedure extends BaseProcedure
{
public function getMe()
{
diff --git a/app/Api/Procedure/ProjectFileProcedure.php b/app/Api/Procedure/ProjectFileProcedure.php
new file mode 100644
index 00000000..48466ce3
--- /dev/null
+++ b/app/Api/Procedure/ProjectFileProcedure.php
@@ -0,0 +1,68 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Core\ObjectStorage\ObjectStorageException;
+
+/**
+ * Project File API controller
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class ProjectFileProcedure extends BaseProcedure
+{
+ public function getProjectFile($project_id, $file_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectFile', $project_id);
+ return $this->projectFileModel->getById($file_id);
+ }
+
+ public function getAllProjectFiles($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllProjectFiles', $project_id);
+ return $this->projectFileModel->getAll($project_id);
+ }
+
+ public function downloadProjectFile($project_id, $file_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadProjectFile', $project_id);
+
+ try {
+ $file = $this->projectFileModel->getById($file_id);
+
+ if (! empty($file)) {
+ return base64_encode($this->objectStorage->get($file['path']));
+ }
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
+
+ return '';
+ }
+
+ public function createProjectFile($project_id, $filename, $blob)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createProjectFile', $project_id);
+
+ try {
+ return $this->projectFileModel->uploadContent($project_id, $filename, $blob);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error(__METHOD__.': '.$e->getMessage());
+ return false;
+ }
+ }
+
+ public function removeProjectFile($project_id, $file_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectFile', $project_id);
+ return $this->projectFileModel->remove($file_id);
+ }
+
+ public function removeAllProjectFiles($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllProjectFiles', $project_id);
+ return $this->projectFileModel->removeAll($project_id);
+ }
+}
diff --git a/app/Api/ProjectPermissionApi.php b/app/Api/Procedure/ProjectPermissionProcedure.php
index 703cd0f3..e22e1d62 100644
--- a/app/Api/ProjectPermissionApi.php
+++ b/app/Api/Procedure/ProjectPermissionProcedure.php
@@ -1,73 +1,69 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Core\Security\Role;
/**
* Project Permission API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class ProjectPermissionApi extends Base
+class ProjectPermissionProcedure extends BaseProcedure
{
public function getProjectUsers($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUsers', $project_id);
return $this->projectUserRoleModel->getAllUsers($project_id);
}
public function getAssignableUsers($project_id, $prepend_unassigned = false)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAssignableUsers', $project_id);
return $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned);
}
public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectUser', $project_id);
return $this->projectUserRoleModel->addUser($project_id, $user_id, $role);
}
public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectGroup', $project_id);
return $this->projectGroupRoleModel->addGroup($project_id, $group_id, $role);
}
public function removeProjectUser($project_id, $user_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectUser', $project_id);
return $this->projectUserRoleModel->removeUser($project_id, $user_id);
}
public function removeProjectGroup($project_id, $group_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectGroup', $project_id);
return $this->projectGroupRoleModel->removeGroup($project_id, $group_id);
}
public function changeProjectUserRole($project_id, $user_id, $role)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectUserRole', $project_id);
return $this->projectUserRoleModel->changeUserRole($project_id, $user_id, $role);
}
public function changeProjectGroupRole($project_id, $group_id, $role)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectGroupRole', $project_id);
return $this->projectGroupRoleModel->changeGroupRole($project_id, $group_id, $role);
}
- // Deprecated
- public function getMembers($project_id)
+ public function getProjectUserRole($project_id, $user_id)
{
- return $this->getProjectUsers($project_id);
- }
-
- // Deprecated
- public function revokeUser($project_id, $user_id)
- {
- return $this->removeProjectUser($project_id, $user_id);
- }
-
- // Deprecated
- public function allowUser($project_id, $user_id)
- {
- return $this->addProjectUser($project_id, $user_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUserRole', $project_id);
+ return $this->projectUserRoleModel->getUserRole($project_id, $user_id);
}
}
diff --git a/app/Api/Procedure/ProjectProcedure.php b/app/Api/Procedure/ProjectProcedure.php
new file mode 100644
index 00000000..a580c8d9
--- /dev/null
+++ b/app/Api/Procedure/ProjectProcedure.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ProjectAuthorization;
+
+/**
+ * Project API controller
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class ProjectProcedure extends BaseProcedure
+{
+ public function getProjectById($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectById', $project_id);
+ return $this->formatProject($this->projectModel->getById($project_id));
+ }
+
+ public function getProjectByName($name)
+ {
+ $project = $this->projectModel->getByName($name);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByName', $project['id']);
+ return $this->formatProject($project);
+ }
+
+ public function getProjectByIdentifier($identifier)
+ {
+ $project = $this->formatProject($this->projectModel->getByIdentifier($identifier));
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByIdentifier', $project['id']);
+ return $this->formatProject($project);
+ }
+
+ public function getAllProjects()
+ {
+ return $this->formatProjects($this->projectModel->getAll());
+ }
+
+ public function removeProject($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProject', $project_id);
+ return $this->projectModel->remove($project_id);
+ }
+
+ public function enableProject($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProject', $project_id);
+ return $this->projectModel->enable($project_id);
+ }
+
+ public function disableProject($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProject', $project_id);
+ return $this->projectModel->disable($project_id);
+ }
+
+ public function enableProjectPublicAccess($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProjectPublicAccess', $project_id);
+ return $this->projectModel->enablePublicAccess($project_id);
+ }
+
+ public function disableProjectPublicAccess($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProjectPublicAccess', $project_id);
+ return $this->projectModel->disablePublicAccess($project_id);
+ }
+
+ public function getProjectActivities(array $project_ids)
+ {
+ foreach ($project_ids as $project_id) {
+ ProjectAuthorization::getInstance($this->container)
+ ->check($this->getClassName(), 'getProjectActivities', $project_id);
+ }
+
+ return $this->helper->projectActivity->getProjectsEvents($project_ids);
+ }
+
+ public function getProjectActivity($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectActivity', $project_id);
+ return $this->helper->projectActivity->getProjectEvents($project_id);
+ }
+
+ public function createProject($name, $description = null, $owner_id = 0, $identifier = null)
+ {
+ $values = $this->filterValues(array(
+ 'name' => $name,
+ 'description' => $description,
+ 'identifier' => $identifier,
+ ));
+
+ list($valid, ) = $this->projectValidator->validateCreation($values);
+ return $valid ? $this->projectModel->create($values, $owner_id, $this->userSession->isLogged()) : false;
+ }
+
+ public function updateProject($project_id, $name = null, $description = null, $owner_id = null, $identifier = null)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateProject', $project_id);
+
+ $values = $this->filterValues(array(
+ 'id' => $project_id,
+ 'name' => $name,
+ 'description' => $description,
+ 'owner_id' => $owner_id,
+ 'identifier' => $identifier,
+ ));
+
+ list($valid, ) = $this->projectValidator->validateModification($values);
+ return $valid && $this->projectModel->update($values);
+ }
+}
diff --git a/app/Api/SubtaskApi.php b/app/Api/Procedure/SubtaskProcedure.php
index 5764ff7d..e2400912 100644
--- a/app/Api/SubtaskApi.php
+++ b/app/Api/Procedure/SubtaskProcedure.php
@@ -1,34 +1,40 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\SubtaskAuthorization;
+use Kanboard\Api\Authorization\TaskAuthorization;
/**
* Subtask API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class SubtaskApi extends Base
+class SubtaskProcedure extends BaseProcedure
{
public function getSubtask($subtask_id)
{
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtask', $subtask_id);
return $this->subtaskModel->getById($subtask_id);
}
public function getAllSubtasks($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSubtasks', $task_id);
return $this->subtaskModel->getAll($task_id);
}
public function removeSubtask($subtask_id)
{
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSubtask', $subtask_id);
return $this->subtaskModel->remove($subtask_id);
}
public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createSubtask', $task_id);
+
$values = array(
'title' => $title,
'task_id' => $task_id,
@@ -44,6 +50,8 @@ class SubtaskApi extends Base
public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSubtask', $task_id);
+
$values = array(
'id' => $id,
'task_id' => $task_id,
diff --git a/app/Api/Procedure/SubtaskTimeTrackingProcedure.php b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php
new file mode 100644
index 00000000..5ceaa08d
--- /dev/null
+++ b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\SubtaskAuthorization;
+
+/**
+ * Subtask Time Tracking API controller
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ * @author Nikolaos Georgakis
+ */
+class SubtaskTimeTrackingProcedure extends BaseProcedure
+{
+ public function hasSubtaskTimer($subtask_id, $user_id)
+ {
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'hasSubtaskTimer', $subtask_id);
+ return $this->subtaskTimeTrackingModel->hasTimer($subtask_id, $user_id);
+ }
+
+ public function setSubtaskStartTime($subtask_id, $user_id)
+ {
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskStartTime', $subtask_id);
+ return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id);
+ }
+
+ public function setSubtaskEndTime($subtask_id, $user_id)
+ {
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskEndTime', $subtask_id);
+ return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id);
+ }
+
+ public function getSubtaskTimeSpent($subtask_id, $user_id)
+ {
+ SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id);
+ return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id);
+ }
+}
diff --git a/app/Api/SwimlaneApi.php b/app/Api/Procedure/SwimlaneProcedure.php
index c3c56a71..9b7d181d 100644
--- a/app/Api/SwimlaneApi.php
+++ b/app/Api/Procedure/SwimlaneProcedure.php
@@ -1,34 +1,39 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Swimlane API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class SwimlaneApi extends Base
+class SwimlaneProcedure extends BaseProcedure
{
public function getActiveSwimlanes($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActiveSwimlanes', $project_id);
return $this->swimlaneModel->getSwimlanes($project_id);
}
public function getAllSwimlanes($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSwimlanes', $project_id);
return $this->swimlaneModel->getAll($project_id);
}
public function getSwimlaneById($swimlane_id)
{
- return $this->swimlaneModel->getById($swimlane_id);
+ $swimlane = $this->swimlaneModel->getById($swimlane_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneById', $swimlane['project_id']);
+ return $swimlane;
}
public function getSwimlaneByName($project_id, $name)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneByName', $project_id);
return $this->swimlaneModel->getByName($project_id, $name);
}
@@ -39,11 +44,13 @@ class SwimlaneApi extends Base
public function getDefaultSwimlane($project_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getDefaultSwimlane', $project_id);
return $this->swimlaneModel->getDefault($project_id);
}
public function addSwimlane($project_id, $name, $description = '')
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addSwimlane', $project_id);
return $this->swimlaneModel->create(array('project_id' => $project_id, 'name' => $name, 'description' => $description));
}
@@ -60,21 +67,25 @@ class SwimlaneApi extends Base
public function removeSwimlane($project_id, $swimlane_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSwimlane', $project_id);
return $this->swimlaneModel->remove($project_id, $swimlane_id);
}
public function disableSwimlane($project_id, $swimlane_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableSwimlane', $project_id);
return $this->swimlaneModel->disable($project_id, $swimlane_id);
}
public function enableSwimlane($project_id, $swimlane_id)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableSwimlane', $project_id);
return $this->swimlaneModel->enable($project_id, $swimlane_id);
}
public function changeSwimlanePosition($project_id, $swimlane_id, $position)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeSwimlanePosition', $project_id);
return $this->swimlaneModel->changePosition($project_id, $swimlane_id, $position);
}
}
diff --git a/app/Api/Procedure/TaskExternalLinkProcedure.php b/app/Api/Procedure/TaskExternalLinkProcedure.php
new file mode 100644
index 00000000..05ec6906
--- /dev/null
+++ b/app/Api/Procedure/TaskExternalLinkProcedure.php
@@ -0,0 +1,106 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\TaskAuthorization;
+use Kanboard\Core\ExternalLink\ExternalLinkManager;
+use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
+
+/**
+ * Task External Link API controller
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class TaskExternalLinkProcedure extends BaseProcedure
+{
+ public function getExternalTaskLinkTypes()
+ {
+ return $this->externalLinkManager->getTypes();
+ }
+
+ public function getExternalTaskLinkProviderDependencies($providerName)
+ {
+ try {
+ return $this->externalLinkManager->getProvider($providerName)->getDependencies();
+ } catch (ExternalLinkProviderNotFound $e) {
+ $this->logger->error(__METHOD__.': '.$e->getMessage());
+ return false;
+ }
+ }
+
+ public function getExternalTaskLinkById($task_id, $link_id)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLink', $task_id);
+ return $this->taskExternalLinkModel->getById($link_id);
+ }
+
+ public function getAllExternalTaskLinks($task_id)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLinks', $task_id);
+ return $this->taskExternalLinkModel->getAll($task_id);
+ }
+
+ public function createExternalTaskLink($task_id, $url, $dependency, $type = ExternalLinkManager::TYPE_AUTO, $title = '')
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createExternalTaskLink', $task_id);
+
+ try {
+ $provider = $this->externalLinkManager
+ ->setUserInputText($url)
+ ->setUserInputType($type)
+ ->find();
+
+ $link = $provider->getLink();
+
+ $values = array(
+ 'task_id' => $task_id,
+ 'title' => $title ?: $link->getTitle(),
+ 'url' => $link->getUrl(),
+ 'link_type' => $provider->getType(),
+ 'dependency' => $dependency,
+ );
+
+ list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
+
+ if (! $valid) {
+ $this->logger->error(__METHOD__.': '.var_export($errors));
+ return false;
+ }
+
+ return $this->taskExternalLinkModel->create($values);
+ } catch (ExternalLinkProviderNotFound $e) {
+ $this->logger->error(__METHOD__.': '.$e->getMessage());
+ }
+
+ return false;
+ }
+
+ public function updateExternalTaskLink($task_id, $link_id, $title = null, $url = null, $dependency = null)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateExternalTaskLink', $task_id);
+
+ $link = $this->taskExternalLinkModel->getById($link_id);
+ $values = $this->filterValues(array(
+ 'title' => $title,
+ 'url' => $url,
+ 'dependency' => $dependency,
+ ));
+
+ $values = array_merge($link, $values);
+ list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
+
+ if (! $valid) {
+ $this->logger->error(__METHOD__.': '.var_export($errors));
+ return false;
+ }
+
+ return $this->taskExternalLinkModel->update($values);
+ }
+
+ public function removeExternalTaskLink($task_id, $link_id)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeExternalTaskLink', $task_id);
+ return $this->taskExternalLinkModel->remove($link_id);
+ }
+}
diff --git a/app/Api/FileApi.php b/app/Api/Procedure/TaskFileProcedure.php
index 1ed3aeb9..bd006578 100644
--- a/app/Api/FileApi.php
+++ b/app/Api/Procedure/TaskFileProcedure.php
@@ -1,29 +1,36 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Api\Authorization\TaskAuthorization;
+use Kanboard\Api\Authorization\TaskFileAuthorization;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
- * File API controller
+ * Task File API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class FileApi extends BaseApi
+class TaskFileProcedure extends BaseProcedure
{
public function getTaskFile($file_id)
{
+ TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskFile', $file_id);
return $this->taskFileModel->getById($file_id);
}
public function getAllTaskFiles($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskFiles', $task_id);
return $this->taskFileModel->getAll($task_id);
}
public function downloadTaskFile($file_id)
{
+ TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id);
+
try {
$file = $this->taskFileModel->getById($file_id);
@@ -33,59 +40,31 @@ class FileApi extends BaseApi
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
-
+
return '';
}
public function createTaskFile($project_id, $task_id, $filename, $blob)
{
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $project_id);
+
try {
return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
} catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
+ $this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
public function removeTaskFile($file_id)
{
+ TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskFile', $file_id);
return $this->taskFileModel->remove($file_id);
}
public function removeAllTaskFiles($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllTaskFiles', $task_id);
return $this->taskFileModel->removeAll($task_id);
}
-
- // Deprecated procedures
-
- public function getFile($file_id)
- {
- return $this->getTaskFile($file_id);
- }
-
- public function getAllFiles($task_id)
- {
- return $this->getAllTaskFiles($task_id);
- }
-
- public function downloadFile($file_id)
- {
- return $this->downloadTaskFile($file_id);
- }
-
- public function createFile($project_id, $task_id, $filename, $blob)
- {
- return $this->createTaskFile($project_id, $task_id, $filename, $blob);
- }
-
- public function removeFile($file_id)
- {
- return $this->removeTaskFile($file_id);
- }
-
- public function removeAllFiles($task_id)
- {
- return $this->removeAllTaskFiles($task_id);
- }
}
diff --git a/app/Api/TaskLinkApi.php b/app/Api/Procedure/TaskLinkProcedure.php
index bb809133..375266fb 100644
--- a/app/Api/TaskLinkApi.php
+++ b/app/Api/Procedure/TaskLinkProcedure.php
@@ -1,16 +1,17 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
+use Kanboard\Api\Authorization\TaskAuthorization;
+use Kanboard\Api\Authorization\TaskLinkAuthorization;
/**
* TaskLink API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class TaskLinkApi extends Base
+class TaskLinkProcedure extends BaseProcedure
{
/**
* Get a task link
@@ -21,6 +22,7 @@ class TaskLinkApi extends Base
*/
public function getTaskLinkById($task_link_id)
{
+ TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id);
return $this->taskLinkModel->getById($task_link_id);
}
@@ -33,6 +35,7 @@ class TaskLinkApi extends Base
*/
public function getAllTaskLinks($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $task_id);
return $this->taskLinkModel->getAll($task_id);
}
@@ -47,6 +50,7 @@ class TaskLinkApi extends Base
*/
public function createTaskLink($task_id, $opposite_task_id, $link_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskLink', $task_id);
return $this->taskLinkModel->create($task_id, $opposite_task_id, $link_id);
}
@@ -62,6 +66,7 @@ class TaskLinkApi extends Base
*/
public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTaskLink', $task_id);
return $this->taskLinkModel->update($task_link_id, $task_id, $opposite_task_id, $link_id);
}
@@ -74,6 +79,7 @@ class TaskLinkApi extends Base
*/
public function removeTaskLink($task_link_id)
{
+ TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id);
return $this->taskLinkModel->remove($task_link_id);
}
}
diff --git a/app/Api/Procedure/TaskMetadataProcedure.php b/app/Api/Procedure/TaskMetadataProcedure.php
new file mode 100644
index 00000000..169482f5
--- /dev/null
+++ b/app/Api/Procedure/TaskMetadataProcedure.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\TaskAuthorization;
+
+/**
+ * Class TaskMetadataProcedure
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class TaskMetadataProcedure extends BaseProcedure
+{
+ public function getTaskMetadata($task_id)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id);
+ return $this->taskMetadataModel->getAll($task_id);
+ }
+
+ public function getTaskMetadataByName($task_id, $name)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id);
+ return $this->taskMetadataModel->get($task_id, $name);
+ }
+
+ public function saveTaskMetadata($task_id, array $values)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $task_id);
+ return $this->taskMetadataModel->save($task_id, $values);
+ }
+
+ public function removeTaskMetadata($task_id, $name)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $task_id);
+ return $this->taskMetadataModel->remove($task_id, $name);
+ }
+}
diff --git a/app/Api/TaskApi.php b/app/Api/Procedure/TaskProcedure.php
index ddb3ac54..ee9242d1 100644
--- a/app/Api/TaskApi.php
+++ b/app/Api/Procedure/TaskProcedure.php
@@ -1,39 +1,41 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\TaskModel;
/**
* Task API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class TaskApi extends BaseApi
+class TaskProcedure extends BaseProcedure
{
public function searchTasks($project_id, $query)
{
- $this->checkProjectPermission($project_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'searchTasks', $project_id);
return $this->taskLexer->build($query)->withFilter(new TaskProjectFilter($project_id))->toArray();
}
public function getTask($task_id)
{
- $this->checkTaskPermission($task_id);
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id);
return $this->formatTask($this->taskFinderModel->getById($task_id));
}
public function getTaskByReference($project_id, $reference)
{
- $this->checkProjectPermission($project_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskByReference', $project_id);
return $this->formatTask($this->taskFinderModel->getByReference($project_id, $reference));
}
public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN)
{
- $this->checkProjectPermission($project_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id);
return $this->formatTasks($this->taskFinderModel->getAll($project_id, $status_id));
}
@@ -44,41 +46,44 @@ class TaskApi extends BaseApi
public function getOverdueTasksByProject($project_id)
{
- $this->checkProjectPermission($project_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getOverdueTasksByProject', $project_id);
return $this->taskFinderModel->getOverdueTasksByProject($project_id);
}
public function openTask($task_id)
{
- $this->checkTaskPermission($task_id);
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'openTask', $task_id);
return $this->taskStatusModel->open($task_id);
}
public function closeTask($task_id)
{
- $this->checkTaskPermission($task_id);
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'closeTask', $task_id);
return $this->taskStatusModel->close($task_id);
}
public function removeTask($task_id)
{
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTask', $task_id);
return $this->taskModel->remove($task_id);
}
public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0)
{
- $this->checkProjectPermission($project_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $project_id);
return $this->taskPositionModel->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id);
}
public function moveTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{
- return $this->taskDuplicationModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $project_id);
+ return $this->taskProjectMoveModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
}
public function duplicateTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{
- return $this->taskDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $project_id);
+ return $this->taskProjectDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
}
public function createTask($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0,
@@ -86,7 +91,7 @@ class TaskApi extends BaseApi
$recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
$recurrence_basedate = 0, $reference = '')
{
- $this->checkProjectPermission($project_id);
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id);
if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
return false;
@@ -127,8 +132,7 @@ class TaskApi extends BaseApi
$recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
$recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
{
- $this->checkTaskPermission($id);
-
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id);
$project_id = $this->taskFinderModel->getProjectId($id);
if ($project_id === 0) {
@@ -139,7 +143,7 @@ class TaskApi extends BaseApi
return false;
}
- $values = array(
+ $values = $this->filterValues(array(
'id' => $id,
'title' => $title,
'color_id' => $color_id,
@@ -155,13 +159,7 @@ class TaskApi extends BaseApi
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
- );
-
- foreach ($values as $key => $value) {
- if (is_null($value)) {
- unset($values[$key]);
- }
- }
+ ));
list($valid) = $this->taskValidator->validateApiModification($values);
return $valid && $this->taskModificationModel->update($values);
diff --git a/app/Api/UserApi.php b/app/Api/Procedure/UserProcedure.php
index 88d75527..145f85bf 100644
--- a/app/Api/UserApi.php
+++ b/app/Api/Procedure/UserProcedure.php
@@ -1,8 +1,7 @@
<?php
-namespace Kanboard\Api;
+namespace Kanboard\Api\Procedure;
-use Kanboard\Core\Base;
use LogicException;
use Kanboard\Core\Security\Role;
use Kanboard\Core\Ldap\Client as LdapClient;
@@ -12,10 +11,10 @@ use Kanboard\Core\Ldap\User as LdapUser;
/**
* User API controller
*
- * @package Kanboard\Api
+ * @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
-class UserApi extends Base
+class UserProcedure extends BaseProcedure
{
public function getUser($user_id)
{
@@ -118,19 +117,13 @@ class UserApi extends Base
public function updateUser($id, $username = null, $name = null, $email = null, $role = null)
{
- $values = array(
+ $values = $this->filterValues(array(
'id' => $id,
'username' => $username,
'name' => $name,
'email' => $email,
'role' => $role,
- );
-
- foreach ($values as $key => $value) {
- if (is_null($value)) {
- unset($values[$key]);
- }
- }
+ ));
list($valid, ) = $this->userValidator->validateApiModification($values);
return $valid && $this->userModel->update($values);
diff --git a/app/Api/ProjectApi.php b/app/Api/ProjectApi.php
deleted file mode 100644
index 29a9cd79..00000000
--- a/app/Api/ProjectApi.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-namespace Kanboard\Api;
-
-/**
- * Project API controller
- *
- * @package Kanboard\Api
- * @author Frederic Guillot
- */
-class ProjectApi extends BaseApi
-{
- public function getProjectById($project_id)
- {
- $this->checkProjectPermission($project_id);
- return $this->formatProject($this->projectModel->getById($project_id));
- }
-
- public function getProjectByName($name)
- {
- return $this->formatProject($this->projectModel->getByName($name));
- }
-
- public function getAllProjects()
- {
- return $this->formatProjects($this->projectModel->getAll());
- }
-
- public function removeProject($project_id)
- {
- return $this->projectModel->remove($project_id);
- }
-
- public function enableProject($project_id)
- {
- return $this->projectModel->enable($project_id);
- }
-
- public function disableProject($project_id)
- {
- return $this->projectModel->disable($project_id);
- }
-
- public function enableProjectPublicAccess($project_id)
- {
- return $this->projectModel->enablePublicAccess($project_id);
- }
-
- public function disableProjectPublicAccess($project_id)
- {
- return $this->projectModel->disablePublicAccess($project_id);
- }
-
- public function getProjectActivities(array $project_ids)
- {
- return $this->helper->projectActivity->getProjectsEvents($project_ids);
- }
-
- public function getProjectActivity($project_id)
- {
- $this->checkProjectPermission($project_id);
- return $this->helper->projectActivity->getProjectEvents($project_id);
- }
-
- public function createProject($name, $description = null)
- {
- $values = array(
- 'name' => $name,
- 'description' => $description
- );
-
- list($valid, ) = $this->projectValidator->validateCreation($values);
- return $valid ? $this->projectModel->create($values) : false;
- }
-
- public function updateProject($id, $name, $description = null)
- {
- $values = array(
- 'id' => $id,
- 'name' => $name,
- 'description' => $description
- );
-
- list($valid, ) = $this->projectValidator->validateModification($values);
- return $valid && $this->projectModel->update($values);
- }
-}
diff --git a/app/Api/SubtaskTimeTrackingApi.php b/app/Api/SubtaskTimeTrackingApi.php
deleted file mode 100644
index 0e700b31..00000000
--- a/app/Api/SubtaskTimeTrackingApi.php
+++ /dev/null
@@ -1,34 +0,0 @@
-<?php
-
-namespace Kanboard\Api;
-
-use Kanboard\Core\Base;
-
-/**
- * Subtask Time Tracking API controller
- *
- * @package api
- * @author Nikolaos Georgakis
- */
-class SubtaskTimeTrackingApi extends Base
-{
- public function hasTimer($subtask_id,$user_id)
- {
- return $this->subtaskTimeTrackingModel->hasTimer($subtask_id,$user_id);
- }
-
- public function logStartTime($subtask_id,$user_id)
- {
- return $this->subtaskTimeTrackingModel->logStartTime($subtask_id,$user_id);
- }
-
- public function logEndTime($subtask_id,$user_id)
- {
- return $this->subtaskTimeTrackingModel->logEndTime($subtask_id,$user_id);
- }
-
- public function getTimeSpent($subtask_id,$user_id)
- {
- return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id,$user_id);
- }
-}
diff --git a/app/Auth/ReverseProxyAuth.php b/app/Auth/ReverseProxyAuth.php
index b9730c5c..fdf936b1 100644
--- a/app/Auth/ReverseProxyAuth.php
+++ b/app/Auth/ReverseProxyAuth.php
@@ -45,7 +45,8 @@ class ReverseProxyAuth extends Base implements PreAuthenticationProviderInterfac
$username = $this->request->getRemoteUser();
if (! empty($username)) {
- $this->userInfo = new ReverseProxyUserProvider($username);
+ $userProfile = $this->userModel->getByUsername($username);
+ $this->userInfo = new ReverseProxyUserProvider($username, $userProfile ?: array());
return true;
}
diff --git a/app/Auth/TotpAuth.php b/app/Auth/TotpAuth.php
index f4304930..8e1ebe35 100644
--- a/app/Auth/TotpAuth.php
+++ b/app/Auth/TotpAuth.php
@@ -123,7 +123,8 @@ class TotpAuth extends Base implements PostAuthenticationProviderInterface
return '';
}
- return GoogleAuthenticator::getQrCodeUrl('totp', $label, $this->secret);
+ $options = array('issuer' => TOTP_ISSUER);
+ return GoogleAuthenticator::getQrCodeUrl('totp', $label, $this->secret, null, $options);
}
/**
@@ -139,6 +140,7 @@ class TotpAuth extends Base implements PostAuthenticationProviderInterface
return '';
}
- return GoogleAuthenticator::getKeyUri('totp', $label, $this->secret);
+ $options = array('issuer' => TOTP_ISSUER);
+ return GoogleAuthenticator::getKeyUri('totp', $label, $this->secret, null, $options);
}
}
diff --git a/app/Console/TaskOverdueNotificationCommand.php b/app/Console/TaskOverdueNotificationCommand.php
index 225a6a1a..36276615 100644
--- a/app/Console/TaskOverdueNotificationCommand.php
+++ b/app/Console/TaskOverdueNotificationCommand.php
@@ -149,7 +149,7 @@ class TaskOverdueNotificationCommand extends BaseCommand
$this->userNotificationModel->sendUserNotification(
$user,
TaskModel::EVENT_OVERDUE,
- array('tasks' => $user_tasks, 'project_name' => implode(", ", $project_names))
+ array('tasks' => $user_tasks, 'project_name' => implode(', ', $project_names))
);
}
}
diff --git a/app/Controller/ActionCreationController.php b/app/Controller/ActionCreationController.php
index abd8abd3..9b228f28 100644
--- a/app/Controller/ActionCreationController.php
+++ b/app/Controller/ActionCreationController.php
@@ -81,7 +81,7 @@ class ActionCreationController extends BaseController
'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($project['id']),
'links_list' => $this->linkModel->getList(0, false),
- 'priorities_list' => $this->projectModel->getPriorities($project),
+ 'priorities_list' => $this->projectTaskPriorityModel->getPriorities($project),
'project' => $project,
'available_actions' => $this->actionManager->getAvailableActions(),
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
diff --git a/app/Controller/ActivityController.php b/app/Controller/ActivityController.php
index 9f9841af..476e4aac 100644
--- a/app/Controller/ActivityController.php
+++ b/app/Controller/ActivityController.php
@@ -40,6 +40,7 @@ class ActivityController extends BaseController
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'events' => $this->helper->projectActivity->getTaskEvents($task['id']),
+ 'tags' => $this->taskTagModel->getList($task['id']),
)));
}
}
diff --git a/app/Controller/AnalyticController.php b/app/Controller/AnalyticController.php
index cf3ba034..ab0646a2 100644
--- a/app/Controller/AnalyticController.php
+++ b/app/Controller/AnalyticController.php
@@ -33,7 +33,7 @@ class AnalyticController extends BaseController
'metrics' => $this->projectDailyStatsModel->getRawMetrics($project['id'], $from, $to),
'date_format' => $this->configModel->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
- 'title' => t('Lead and Cycle time for "%s"', $project['name']),
+ 'title' => t('Lead and cycle time'),
)));
}
@@ -60,7 +60,7 @@ class AnalyticController extends BaseController
'project' => $project,
'paginator' => $paginator,
'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']),
- 'title' => t('Compare hours for "%s"', $project['name']),
+ 'title' => t('Estimated vs actual time'),
)));
}
@@ -76,7 +76,7 @@ class AnalyticController extends BaseController
$this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array(
'project' => $project,
'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']),
- 'title' => t('Average time spent into each column for "%s"', $project['name']),
+ 'title' => t('Average time into each column'),
)));
}
@@ -92,7 +92,7 @@ class AnalyticController extends BaseController
$this->response->html($this->helper->layout->analytic('analytic/tasks', array(
'project' => $project,
'metrics' => $this->taskDistributionAnalytic->build($project['id']),
- 'title' => t('Task repartition for "%s"', $project['name']),
+ 'title' => t('Task distribution'),
)));
}
@@ -108,7 +108,7 @@ class AnalyticController extends BaseController
$this->response->html($this->helper->layout->analytic('analytic/users', array(
'project' => $project,
'metrics' => $this->userDistributionAnalytic->build($project['id']),
- 'title' => t('User repartition for "%s"', $project['name']),
+ 'title' => t('User repartition'),
)));
}
@@ -119,7 +119,7 @@ class AnalyticController extends BaseController
*/
public function cfd()
{
- $this->commonAggregateMetrics('analytic/cfd', 'total', 'Cumulative flow diagram for "%s"');
+ $this->commonAggregateMetrics('analytic/cfd', 'total', t('Cumulative flow diagram'));
}
/**
@@ -129,7 +129,7 @@ class AnalyticController extends BaseController
*/
public function burndown()
{
- $this->commonAggregateMetrics('analytic/burndown', 'score', 'Burndown chart for "%s"');
+ $this->commonAggregateMetrics('analytic/burndown', 'score', t('Burndown chart'));
}
/**
@@ -157,7 +157,7 @@ class AnalyticController extends BaseController
'project' => $project,
'date_format' => $this->configModel->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
- 'title' => t($title, $project['name']),
+ 'title' => $title,
)));
}
diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php
index 24914671..9b721f06 100644
--- a/app/Controller/BoardAjaxController.php
+++ b/app/Controller/BoardAjaxController.php
@@ -134,7 +134,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)->setProjectId($project_id))
+ ->format(BoardFormatter::getInstance($this->container)->withProjectId($project_id))
));
}
}
diff --git a/app/Controller/BoardTooltipController.php b/app/Controller/BoardTooltipController.php
index 2a947027..134d728e 100644
--- a/app/Controller/BoardTooltipController.php
+++ b/app/Controller/BoardTooltipController.php
@@ -107,9 +107,9 @@ class BoardTooltipController extends BaseController
$this->response->html($this->template->render('task_recurrence/info', array(
'task' => $task,
- 'recurrence_trigger_list' => $this->taskModel->getRecurrenceTriggerList(),
- 'recurrence_timeframe_list' => $this->taskModel->getRecurrenceTimeframeList(),
- 'recurrence_basedate_list' => $this->taskModel->getRecurrenceBasedateList(),
+ 'recurrence_trigger_list' => $this->taskRecurrenceModel->getRecurrenceTriggerList(),
+ 'recurrence_timeframe_list' => $this->taskRecurrenceModel->getRecurrenceTimeframeList(),
+ 'recurrence_basedate_list' => $this->taskRecurrenceModel->getRecurrenceBasedateList(),
)));
}
diff --git a/app/Controller/BoardViewController.php b/app/Controller/BoardViewController.php
index 496fa995..97c99d11 100644
--- a/app/Controller/BoardViewController.php
+++ b/app/Controller/BoardViewController.php
@@ -30,7 +30,11 @@ class BoardViewController extends BaseController
$this->response->html($this->helper->layout->app('board/view_public', array(
'project' => $project,
- 'swimlanes' => $this->boardModel->getBoard($project['id']),
+ 'swimlanes' => BoardFormatter::getInstance($this->container)
+ ->withProjectId($project['id'])
+ ->withQuery($this->taskFinderModel->getExtendedQuery())
+ ->format()
+ ,
'title' => $project['name'],
'description' => $project['description'],
'no_layout' => true,
@@ -59,7 +63,7 @@ class BoardViewController extends BaseController
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($search)
- ->format(BoardFormatter::getInstance($this->container)->setProjectId($project['id']))
+ ->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id']))
)));
}
}
diff --git a/app/Controller/ColumnController.php b/app/Controller/ColumnController.php
index 95fbcaaa..d3f0e36e 100644
--- a/app/Controller/ColumnController.php
+++ b/app/Controller/ColumnController.php
@@ -66,7 +66,15 @@ class ColumnController extends BaseController
list($valid, $errors) = $this->columnValidator->validateCreation($values);
if ($valid) {
- if ($this->columnModel->create($project['id'], $values['title'], $values['task_limit'], $values['description']) !== false) {
+ $result = $this->columnModel->create(
+ $project['id'],
+ $values['title'],
+ $values['task_limit'],
+ $values['description'],
+ isset($values['hide_in_dashboard']) ? $values['hide_in_dashboard'] : 0
+ );
+
+ if ($result !== false) {
$this->flash->success(t('Column created successfully.'));
return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true);
} else {
@@ -111,7 +119,15 @@ class ColumnController extends BaseController
list($valid, $errors) = $this->columnValidator->validateModification($values);
if ($valid) {
- if ($this->columnModel->update($values['id'], $values['title'], $values['task_limit'], $values['description'])) {
+ $result = $this->columnModel->update(
+ $values['id'],
+ $values['title'],
+ $values['task_limit'],
+ $values['description'],
+ isset($values['hide_in_dashboard']) ? $values['hide_in_dashboard'] : 0
+ );
+
+ if ($result) {
$this->flash->success(t('Board updated successfully.'));
return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])));
} else {
diff --git a/app/Controller/DashboardController.php b/app/Controller/DashboardController.php
index 44874546..f32f8552 100644
--- a/app/Controller/DashboardController.php
+++ b/app/Controller/DashboardController.php
@@ -2,9 +2,6 @@
namespace Kanboard\Controller;
-use Kanboard\Model\ProjectModel;
-use Kanboard\Model\SubtaskModel;
-
/**
* Dashboard Controller
*
@@ -14,63 +11,6 @@ use Kanboard\Model\SubtaskModel;
class DashboardController extends BaseController
{
/**
- * Get project pagination
- *
- * @access private
- * @param integer $user_id
- * @param string $action
- * @param integer $max
- * @return \Kanboard\Core\Paginator
- */
- private function getProjectPaginator($user_id, $action, $max)
- {
- return $this->paginator
- ->setUrl('DashboardController', $action, array('pagination' => 'projects', 'user_id' => $user_id))
- ->setMax($max)
- ->setOrder(ProjectModel::TABLE.'.name')
- ->setQuery($this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id)))
- ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
- }
-
- /**
- * Get task pagination
- *
- * @access private
- * @param integer $user_id
- * @param string $action
- * @param integer $max
- * @return \Kanboard\Core\Paginator
- */
- private function getTaskPaginator($user_id, $action, $max)
- {
- return $this->paginator
- ->setUrl('DashboardController', $action, array('pagination' => 'tasks', 'user_id' => $user_id))
- ->setMax($max)
- ->setOrder('tasks.id')
- ->setQuery($this->taskFinderModel->getUserQuery($user_id))
- ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
- }
-
- /**
- * Get subtask pagination
- *
- * @access private
- * @param integer $user_id
- * @param string $action
- * @param integer $max
- * @return \Kanboard\Core\Paginator
- */
- private function getSubtaskPaginator($user_id, $action, $max)
- {
- return $this->paginator
- ->setUrl('DashboardController', $action, array('pagination' => 'subtasks', 'user_id' => $user_id))
- ->setMax($max)
- ->setOrder('tasks.id')
- ->setQuery($this->subtaskModel->getUserQuery($user_id, array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS)))
- ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
- }
-
- /**
* Dashboard overview
*
* @access public
@@ -80,10 +20,10 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/show', array(
- 'title' => t('Dashboard'),
- 'project_paginator' => $this->getProjectPaginator($user['id'], 'show', 10),
- 'task_paginator' => $this->getTaskPaginator($user['id'], 'show', 10),
- 'subtask_paginator' => $this->getSubtaskPaginator($user['id'], 'show', 10),
+ 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)),
+ 'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', 10),
+ 'task_paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'show', 10),
+ 'subtask_paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'show', 10),
'user' => $user,
)));
}
@@ -98,8 +38,8 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/tasks', array(
- 'title' => t('My tasks'),
- 'paginator' => $this->getTaskPaginator($user['id'], 'tasks', 50),
+ 'title' => t('Tasks overview for %s', $this->helper->user->getFullname($user)),
+ 'paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'tasks', 50),
'user' => $user,
)));
}
@@ -114,8 +54,8 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array(
- 'title' => t('My subtasks'),
- 'paginator' => $this->getSubtaskPaginator($user['id'], 'subtasks', 50),
+ 'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)),
+ 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id'], 'subtasks', 50),
'user' => $user,
)));
}
@@ -130,8 +70,8 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/projects', array(
- 'title' => t('My projects'),
- 'paginator' => $this->getProjectPaginator($user['id'], 'projects', 25),
+ 'title' => t('Projects overview for %s', $this->helper->user->getFullname($user)),
+ 'paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'projects', 25),
'user' => $user,
)));
}
@@ -146,7 +86,7 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/activity', array(
- 'title' => t('My activity stream'),
+ 'title' => t('Activity stream for %s', $this->helper->user->getFullname($user)),
'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']), 100),
'user' => $user,
)));
@@ -159,9 +99,11 @@ class DashboardController extends BaseController
*/
public function calendar()
{
+ $user = $this->getUser();
+
$this->response->html($this->helper->layout->dashboard('dashboard/calendar', array(
- 'title' => t('My calendar'),
- 'user' => $this->getUser(),
+ 'title' => t('Calendar for %s', $this->helper->user->getFullname($user)),
+ 'user' => $user,
)));
}
@@ -175,7 +117,7 @@ class DashboardController extends BaseController
$user = $this->getUser();
$this->response->html($this->helper->layout->dashboard('dashboard/notifications', array(
- 'title' => t('My notifications'),
+ 'title' => t('Notifications for %s', $this->helper->user->getFullname($user)),
'notifications' => $this->userUnreadNotificationModel->getAll($user['id']),
'user' => $user,
)));
diff --git a/app/Controller/DocumentationController.php b/app/Controller/DocumentationController.php
index d86fb3c8..0d02ebda 100644
--- a/app/Controller/DocumentationController.php
+++ b/app/Controller/DocumentationController.php
@@ -20,16 +20,7 @@ class DocumentationController extends BaseController
$page = 'index';
}
- if ($this->languageModel->getCurrentLanguage() === 'fr_FR') {
- $filename = __DIR__.'/../../doc/fr/' . $page . '.markdown';
- } else {
- $filename = __DIR__ . '/../../doc/' . $page . '.markdown';
- }
-
- if (!file_exists($filename)) {
- $filename = __DIR__.'/../../doc/index.markdown';
- }
-
+ $filename = $this->getPageFilename($page);
$this->response->html($this->helper->layout->app('doc/show', $this->render($filename)));
}
@@ -83,10 +74,63 @@ class DocumentationController extends BaseController
*/
public function replaceImageUrl(array $matches)
{
- if ($this->languageModel->getCurrentLanguage() === 'fr_FR') {
- return '('.$this->helper->url->base().'doc/fr/'.$matches[1].')';
+ return '('.$this->getFileBaseUrl($matches[1]).')';
+ }
+
+ /**
+ * Get Markdown file according to the current language
+ *
+ * @access private
+ * @param string $page
+ * @return string
+ */
+ private function getPageFilename($page)
+ {
+ return $this->getFileLocation($page . '.markdown') ?:
+ implode(DIRECTORY_SEPARATOR, array(ROOT_DIR, 'doc', 'index.markdown'));
+ }
+
+ /**
+ * Get base URL for Markdown links
+ *
+ * @access private
+ * @param string $filename
+ * @return string
+ */
+ private function getFileBaseUrl($filename)
+ {
+ $language = $this->languageModel->getCurrentLanguage();
+ $path = $this->getFileLocation($filename);
+
+ if (strpos($path, $language) !== false) {
+ $url = implode('/', array('doc', $language, $filename));
+ } else {
+ $url = implode('/', array('doc', $filename));
+ }
+
+ return $this->helper->url->base().$url;
+ }
+
+ /**
+ * Get file location according to the current language
+ *
+ * @access private
+ * @param string $filename
+ * @return string
+ */
+ private function getFileLocation($filename)
+ {
+ $files = array(
+ implode(DIRECTORY_SEPARATOR, array(ROOT_DIR, 'doc', $this->languageModel->getCurrentLanguage(), $filename)),
+ implode(DIRECTORY_SEPARATOR, array(ROOT_DIR, 'doc', $filename)),
+ );
+
+ foreach ($files as $filename) {
+ if (file_exists($filename)) {
+ return $filename;
+ }
}
- return '('.$this->helper->url->base().'doc/'.$matches[1].')';
+ return '';
}
}
diff --git a/app/Controller/ExportController.php b/app/Controller/ExportController.php
index b2fe0ebd..27046c76 100644
--- a/app/Controller/ExportController.php
+++ b/app/Controller/ExportController.php
@@ -31,22 +31,23 @@ class ExportController extends BaseController
$data = $this->$model->$method($project['id'], $from, $to);
$this->response->withFileDownload($filename.'.csv');
$this->response->csv($data);
- }
+ } else {
- $this->response->html($this->helper->layout->project('export/'.$action, array(
- 'values' => array(
- 'controller' => 'ExportController',
- 'action' => $action,
- 'project_id' => $project['id'],
- 'from' => $from,
- 'to' => $to,
- ),
- 'errors' => array(),
- 'date_format' => $this->configModel->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
- 'project' => $project,
- 'title' => $page_title,
- ), 'export/sidebar'));
+ $this->response->html($this->helper->layout->project('export/'.$action, array(
+ 'values' => array(
+ 'controller' => 'ExportController',
+ 'action' => $action,
+ 'project_id' => $project['id'],
+ 'from' => $from,
+ 'to' => $to,
+ ),
+ 'errors' => array(),
+ 'date_format' => $this->configModel->get('application_date_format'),
+ 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
+ 'project' => $project,
+ 'title' => $page_title,
+ ), 'export/sidebar'));
+ }
}
/**
@@ -76,7 +77,7 @@ class ExportController extends BaseController
*/
public function summary()
{
- $this->common('projectDailyColumnStats', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
+ $this->common('projectDailyColumnStatsModel', 'getAggregatedMetrics', t('Summary'), 'summary', t('Daily project summary export'));
}
/**
diff --git a/app/Controller/FeedController.php b/app/Controller/FeedController.php
index cf2b1088..f9b0ed7c 100644
--- a/app/Controller/FeedController.php
+++ b/app/Controller/FeedController.php
@@ -2,7 +2,11 @@
namespace Kanboard\Controller;
+use DateTime;
use Kanboard\Core\Controller\AccessForbiddenException;
+use PicoFeed\Syndication\AtomFeedBuilder;
+use PicoFeed\Syndication\AtomItemBuilder;
+use PicoFeed\Syndication\FeedBuilder;
/**
* Atom/RSS Feed controller
@@ -27,10 +31,15 @@ class FeedController extends BaseController
throw AccessForbiddenException::getInstance()->withoutLayout();
}
- $this->response->xml($this->template->render('feed/user', array(
- 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id'])),
- 'user' => $user,
- )));
+ $events = $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']));
+
+ $feedBuilder = AtomFeedBuilder::create()
+ ->withTitle(e('Project activities for %s', $this->helper->user->getFullname($user)))
+ ->withFeedUrl($this->helper->url->to('FeedController', 'user', array('token' => $user['token']), '', true))
+ ->withSiteUrl($this->helper->url->base())
+ ->withDate(new DateTime());
+
+ $this->response->xml($this->buildFeedItems($events, $feedBuilder)->build());
}
/**
@@ -47,9 +56,44 @@ class FeedController extends BaseController
throw AccessForbiddenException::getInstance()->withoutLayout();
}
- $this->response->xml($this->template->render('feed/project', array(
- 'events' => $this->helper->projectActivity->getProjectEvents($project['id']),
- 'project' => $project,
- )));
+ $events = $this->helper->projectActivity->getProjectEvents($project['id']);
+
+ $feedBuilder = AtomFeedBuilder::create()
+ ->withTitle(e('%s\'s activity', $project['name']))
+ ->withFeedUrl($this->helper->url->to('FeedController', 'project', array('token' => $project['token']), '', true))
+ ->withSiteUrl($this->helper->url->base())
+ ->withDate(new DateTime());
+
+ $this->response->xml($this->buildFeedItems($events, $feedBuilder)->build());
+ }
+
+ /**
+ * Build feed items
+ *
+ * @access protected
+ * @param array $events
+ * @param FeedBuilder $feedBuilder
+ * @return FeedBuilder
+ */
+ protected function buildFeedItems(array $events, FeedBuilder $feedBuilder)
+ {
+ foreach ($events as $event) {
+ $itemDate = new DateTime();
+ $itemDate->setTimestamp($event['date_creation']);
+
+ $itemUrl = $this->helper->url->to('TaskViewController', 'show', array('task_id' => $event['task_id']), '', true);
+
+ $feedBuilder
+ ->withItem(AtomItemBuilder::create($feedBuilder)
+ ->withTitle($event['event_title'])
+ ->withUrl($itemUrl.'#event-'.$event['id'])
+ ->withAuthor($event['author'])
+ ->withPublishedDate($itemDate)
+ ->withUpdatedDate($itemDate)
+ ->withContent($event['event_content'])
+ );
+ }
+
+ return $feedBuilder;
}
}
diff --git a/app/Controller/ProjectListController.php b/app/Controller/ProjectListController.php
index e1172400..4de73c97 100644
--- a/app/Controller/ProjectListController.php
+++ b/app/Controller/ProjectListController.php
@@ -20,7 +20,7 @@ class ProjectListController extends BaseController
if ($this->userSession->isAdmin()) {
$project_ids = $this->projectModel->getAllIds();
} else {
- $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
+ $project_ids = $this->projectPermissionModel->getProjectIds($this->userSession->getId());
}
$nb_projects = count($project_ids);
diff --git a/app/Controller/ProjectPermissionController.php b/app/Controller/ProjectPermissionController.php
index f3ca6ed9..99f556e8 100644
--- a/app/Controller/ProjectPermissionController.php
+++ b/app/Controller/ProjectPermissionController.php
@@ -147,7 +147,7 @@ class ProjectPermissionController extends BaseController
$values = $this->request->getValues();
if (empty($values['group_id']) && ! empty($values['external_id'])) {
- $values['group_id'] = $this->groupModel->create($values['name'], $values['external_id']);
+ $values['group_id'] = $this->groupModel->getOrCreateExternalGroupId($values['name'], $values['external_id']);
}
if ($this->projectGroupRoleModel->addGroup($project['id'], $values['group_id'], $values['role'])) {
diff --git a/app/Controller/ProjectTagController.php b/app/Controller/ProjectTagController.php
new file mode 100644
index 00000000..acf514d3
--- /dev/null
+++ b/app/Controller/ProjectTagController.php
@@ -0,0 +1,134 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+
+/**
+ * Class ProjectTagController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class ProjectTagController extends BaseController
+{
+ public function index()
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->helper->layout->project('project_tag/index', array(
+ 'project' => $project,
+ 'tags' => $this->tagModel->getAllByProject($project['id']),
+ 'title' => t('Project tags management'),
+ )));
+ }
+
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+
+ if (empty($values)) {
+ $values['project_id'] = $project['id'];
+ }
+
+ $this->response->html($this->template->render('project_tag/create', array(
+ 'project' => $project,
+ 'values' => $values,
+ 'errors' => $errors,
+ )));
+ }
+
+ public function save()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->tagValidator->validateCreation($values);
+
+ if ($valid) {
+ if ($this->tagModel->create($project['id'], $values['name']) > 0) {
+ $this->flash->success(t('Tag created successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to create this tag.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
+ } else {
+ $this->create($values, $errors);
+ }
+ }
+
+ public function edit(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+
+ if (empty($values)) {
+ $values = $tag;
+ }
+
+ $this->response->html($this->template->render('project_tag/edit', array(
+ 'project' => $project,
+ 'tag' => $tag,
+ 'values' => $values,
+ 'errors' => $errors,
+ )));
+ }
+
+ public function update()
+ {
+ $project = $this->getProject();
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->tagValidator->validateModification($values);
+
+ if ($tag['project_id'] != $project['id']) {
+ throw new AccessForbiddenException();
+ }
+
+ if ($valid) {
+ if ($this->tagModel->update($values['id'], $values['name'])) {
+ $this->flash->success(t('Tag updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this tag.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
+ } else {
+ $this->edit($values, $errors);
+ }
+ }
+
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+
+ $this->response->html($this->template->render('project_tag/remove', array(
+ 'tag' => $tag,
+ 'project' => $project,
+ )));
+ }
+
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $project = $this->getProject();
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+
+ if ($tag['project_id'] != $project['id']) {
+ throw new AccessForbiddenException();
+ }
+
+ if ($this->tagModel->remove($tag_id)) {
+ $this->flash->success(t('Tag removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this tag.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id'])));
+ }
+}
diff --git a/app/Controller/SubtaskController.php b/app/Controller/SubtaskController.php
index 93dab5cd..7502d84f 100644
--- a/app/Controller/SubtaskController.php
+++ b/app/Controller/SubtaskController.php
@@ -168,7 +168,7 @@ class SubtaskController extends BaseController
$values = $this->request->getJson();
if (! empty($values) && $this->helper->user->hasProjectAccess('SubtaskController', 'movePosition', $project_id)) {
- $result = $this->subtaskModel->changePosition($task_id, $values['subtask_id'], $values['position']);
+ $result = $this->subtaskPositionModel->changePosition($task_id, $values['subtask_id'], $values['position']);
$this->response->json(array('result' => $result));
} else {
throw new AccessForbiddenException();
diff --git a/app/Controller/SubtaskConverterController.php b/app/Controller/SubtaskConverterController.php
index 65bcd2da..404c50d0 100644
--- a/app/Controller/SubtaskConverterController.php
+++ b/app/Controller/SubtaskConverterController.php
@@ -26,7 +26,7 @@ class SubtaskConverterController extends BaseController
$project = $this->getProject();
$subtask = $this->getSubtask();
- $task_id = $this->subtaskModel->convertToTask($project['id'], $subtask['id']);
+ $task_id = $this->subtaskTaskConversionModel->convertToTask($project['id'], $subtask['id']);
if ($task_id !== false) {
$this->flash->success(t('Subtask converted to task successfully.'));
diff --git a/app/Controller/SubtaskRestrictionController.php b/app/Controller/SubtaskRestrictionController.php
index 084fc0d9..cb642e1c 100644
--- a/app/Controller/SubtaskRestrictionController.php
+++ b/app/Controller/SubtaskRestrictionController.php
@@ -27,7 +27,7 @@ class SubtaskRestrictionController extends BaseController
SubtaskModel::STATUS_TODO => t('Todo'),
SubtaskModel::STATUS_DONE => t('Done'),
),
- 'subtask_inprogress' => $this->subtaskModel->getSubtaskInProgress($this->userSession->getId()),
+ 'subtask_inprogress' => $this->subtaskStatusModel->getSubtaskInProgress($this->userSession->getId()),
'subtask' => $subtask,
'task' => $task,
)));
diff --git a/app/Controller/SubtaskStatusController.php b/app/Controller/SubtaskStatusController.php
index 699951fe..d4d356c3 100644
--- a/app/Controller/SubtaskStatusController.php
+++ b/app/Controller/SubtaskStatusController.php
@@ -20,7 +20,7 @@ class SubtaskStatusController extends BaseController
$task = $this->getTask();
$subtask = $this->getSubtask();
- $status = $this->subtaskModel->toggleStatus($subtask['id']);
+ $status = $this->subtaskStatusModel->toggleStatus($subtask['id']);
if ($this->request->getIntegerParam('refresh-table') === 0) {
$subtask['status'] = $status;
diff --git a/app/Controller/TagController.php b/app/Controller/TagController.php
new file mode 100644
index 00000000..b8389910
--- /dev/null
+++ b/app/Controller/TagController.php
@@ -0,0 +1,121 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+
+/**
+ * Class TagController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class TagController extends BaseController
+{
+ public function index()
+ {
+ $this->response->html($this->helper->layout->config('tag/index', array(
+ 'tags' => $this->tagModel->getAllByProject(0),
+ 'title' => t('Settings').' &gt; '.t('Global tags management'),
+ )));
+ }
+
+ public function create(array $values = array(), array $errors = array())
+ {
+ if (empty($values)) {
+ $values['project_id'] = 0;
+ }
+
+ $this->response->html($this->template->render('tag/create', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ )));
+ }
+
+ public function save()
+ {
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->tagValidator->validateCreation($values);
+
+ if ($valid) {
+ if ($this->tagModel->create(0, $values['name']) > 0) {
+ $this->flash->success(t('Tag created successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to create this tag.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('TagController', 'index'));
+ } else {
+ $this->create($values, $errors);
+ }
+ }
+
+ public function edit(array $values = array(), array $errors = array())
+ {
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+
+ if (empty($values)) {
+ $values = $tag;
+ }
+
+ $this->response->html($this->template->render('tag/edit', array(
+ 'tag' => $tag,
+ 'values' => $values,
+ 'errors' => $errors,
+ )));
+ }
+
+ public function update()
+ {
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->tagValidator->validateModification($values);
+
+ if ($tag['project_id'] != 0) {
+ throw new AccessForbiddenException();
+ }
+
+ if ($valid) {
+ if ($this->tagModel->update($values['id'], $values['name'])) {
+ $this->flash->success(t('Tag updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update this tag.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('TagController', 'index'));
+ } else {
+ $this->edit($values, $errors);
+ }
+ }
+
+ public function confirm()
+ {
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+
+ $this->response->html($this->template->render('tag/remove', array(
+ 'tag' => $tag,
+ )));
+ }
+
+ public function remove()
+ {
+ $this->checkCSRFParam();
+ $tag_id = $this->request->getIntegerParam('tag_id');
+ $tag = $this->tagModel->getById($tag_id);
+
+ if ($tag['project_id'] != 0) {
+ throw new AccessForbiddenException();
+ }
+
+ if ($this->tagModel->remove($tag_id)) {
+ $this->flash->success(t('Tag removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this tag.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('TagController', 'index'));
+ }
+}
diff --git a/app/Controller/TaskCreationController.php b/app/Controller/TaskCreationController.php
index 819de96e..5f1337e5 100644
--- a/app/Controller/TaskCreationController.php
+++ b/app/Controller/TaskCreationController.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\PageNotFoundException;
+
/**
* Task Creation Controller
*
@@ -14,9 +16,9 @@ class TaskCreationController extends BaseController
* Display a form to create a new task
*
* @access public
- * @param array $values
- * @param array $errors
- * @throws \Kanboard\Core\Controller\PageNotFoundException
+ * @param array $values
+ * @param array $errors
+ * @throws PageNotFoundException
*/
public function show(array $values = array(), array $errors = array())
{
@@ -24,15 +26,7 @@ class TaskCreationController extends BaseController
$swimlanes_list = $this->swimlaneModel->getList($project['id'], false, true);
if (empty($values)) {
- $values = array(
- 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)),
- 'column_id' => $this->request->getIntegerParam('column_id'),
- 'color_id' => $this->colorModel->getDefaultColor(),
- 'owner_id' => $this->userSession->getId(),
- );
-
- $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
- $values = $this->hook->merge('controller:task-creation:form:default', $values, array('default_values' => $values));
+ $values = $this->prepareValues($swimlanes_list);
}
$this->response->html($this->template->render('task_creation/show', array(
@@ -41,10 +35,8 @@ class TaskCreationController extends BaseController
'values' => $values + array('project_id' => $project['id']),
'columns_list' => $this->columnModel->getList($project['id']),
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, true),
- 'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($project['id']),
'swimlanes_list' => $swimlanes_list,
- 'title' => $project['name'].' &gt; '.t('New task')
)));
}
@@ -62,17 +54,17 @@ class TaskCreationController extends BaseController
if ($valid && $this->taskCreationModel->create($values)) {
$this->flash->success(t('Task created successfully.'));
- return $this->afterSave($project, $values);
+ $this->afterSave($project, $values);
+ } else {
+ $this->flash->failure(t('Unable to create your task.'));
+ $this->show($values, $errors);
}
-
- $this->flash->failure(t('Unable to create your task.'));
- return $this->show($values, $errors);
}
private function afterSave(array $project, array &$values)
{
if (isset($values['another_task']) && $values['another_task'] == 1) {
- return $this->show(array(
+ $this->show(array(
'owner_id' => $values['owner_id'],
'color_id' => $values['color_id'],
'category_id' => isset($values['category_id']) ? $values['category_id'] : 0,
@@ -80,8 +72,29 @@ class TaskCreationController extends BaseController
'swimlane_id' => isset($values['swimlane_id']) ? $values['swimlane_id'] : 0,
'another_task' => 1,
));
+ } else {
+ $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
}
+ }
+
+ /**
+ * Prepare form values
+ *
+ * @access protected
+ * @param array $swimlanes_list
+ * @return array
+ */
+ protected function prepareValues(array $swimlanes_list)
+ {
+ $values = array(
+ 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanes_list)),
+ 'column_id' => $this->request->getIntegerParam('column_id'),
+ 'color_id' => $this->colorModel->getDefaultColor(),
+ 'owner_id' => $this->userSession->getId(),
+ );
- return $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
+ $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
+ $values = $this->hook->merge('controller:task-creation:form:default', $values, array('default_values' => $values));
+ return $values;
}
}
diff --git a/app/Controller/TaskDuplicationController.php b/app/Controller/TaskDuplicationController.php
index 6a475374..915bf8f8 100644
--- a/app/Controller/TaskDuplicationController.php
+++ b/app/Controller/TaskDuplicationController.php
@@ -50,7 +50,7 @@ class TaskDuplicationController extends BaseController
$values = $this->request->getValues();
list($valid, ) = $this->taskValidator->validateProjectModification($values);
- if ($valid && $this->taskDuplicationModel->moveToProject($task['id'],
+ if ($valid && $this->taskProjectMoveModel->moveToProject($task['id'],
$values['project_id'],
$values['swimlane_id'],
$values['column_id'],
@@ -80,7 +80,7 @@ class TaskDuplicationController extends BaseController
list($valid, ) = $this->taskValidator->validateProjectModification($values);
if ($valid) {
- $task_id = $this->taskDuplicationModel->duplicateToProject(
+ $task_id = $this->taskProjectDuplicationModel->duplicateToProject(
$task['id'], $values['project_id'], $values['swimlane_id'],
$values['column_id'], $values['category_id'], $values['owner_id']
);
diff --git a/app/Controller/TaskGanttCreationController.php b/app/Controller/TaskGanttCreationController.php
index c2998a3e..0fbac8bb 100644
--- a/app/Controller/TaskGanttCreationController.php
+++ b/app/Controller/TaskGanttCreationController.php
@@ -36,10 +36,8 @@ class TaskGanttCreationController extends BaseController
'errors' => $errors,
'values' => $values,
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, true),
- 'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($project['id']),
'swimlanes_list' => $this->swimlaneModel->getList($project['id'], false, true),
- 'title' => $project['name'].' &gt; '.t('New task')
)));
}
@@ -55,17 +53,12 @@ class TaskGanttCreationController extends BaseController
list($valid, $errors) = $this->taskValidator->validateCreation($values);
- if ($valid) {
- $task_id = $this->taskCreationModel->create($values);
-
- if ($task_id !== false) {
- $this->flash->success(t('Task created successfully.'));
- return $this->response->redirect($this->helper->url->to('TaskGanttController', 'show', array('project_id' => $project['id'])));
- } else {
- $this->flash->failure(t('Unable to create your task.'));
- }
+ if ($valid && $this->taskCreationModel->create($values)) {
+ $this->flash->success(t('Task created successfully.'));
+ $this->response->redirect($this->helper->url->to('TaskGanttController', 'show', array('project_id' => $project['id'])));
+ } else {
+ $this->flash->failure(t('Unable to create your task.'));
+ $this->show($values, $errors);
}
-
- return $this->show($values, $errors);
}
}
diff --git a/app/Controller/TaskModificationController.php b/app/Controller/TaskModificationController.php
index f9c63c12..cbc3777a 100644
--- a/app/Controller/TaskModificationController.php
+++ b/app/Controller/TaskModificationController.php
@@ -23,55 +23,6 @@ class TaskModificationController extends BaseController
}
/**
- * Edit description form
- *
- * @access public
- * @param array $values
- * @param array $errors
- * @throws \Kanboard\Core\Controller\AccessForbiddenException
- * @throws \Kanboard\Core\Controller\PageNotFoundException
- */
- public function description(array $values = array(), array $errors = array())
- {
- $task = $this->getTask();
-
- if (empty($values)) {
- $values = array('id' => $task['id'], 'description' => $task['description']);
- }
-
- $this->response->html($this->template->render('task_modification/edit_description', array(
- 'values' => $values,
- 'errors' => $errors,
- 'task' => $task,
- )));
- }
-
- /**
- * Update description
- *
- * @access public
- */
- public function updateDescription()
- {
- $task = $this->getTask();
- $values = $this->request->getValues();
-
- list($valid, $errors) = $this->taskValidator->validateDescriptionCreation($values);
-
- if ($valid) {
- if ($this->taskModificationModel->update($values)) {
- $this->flash->success(t('Task updated successfully.'));
- } else {
- $this->flash->failure(t('Unable to update your task.'));
- }
-
- return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
- }
-
- return $this->description($values, $errors);
- }
-
- /**
* Display a form to edit a task
*
* @access public
@@ -86,20 +37,16 @@ class TaskModificationController extends BaseController
$project = $this->projectModel->getById($task['project_id']);
if (empty($values)) {
- $values = $task;
- $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
- $values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values));
- $values = $this->dateParser->format($values, array('date_due'), $this->dateParser->getUserDateFormat());
- $values = $this->dateParser->format($values, array('date_started'), $this->dateParser->getUserDateTimeFormat());
+ $values = $this->prepareValues($task);
}
- $this->response->html($this->template->render('task_modification/edit_task', array(
+ $this->response->html($this->template->render('task_modification/show', array(
'project' => $project,
'values' => $values,
'errors' => $errors,
'task' => $task,
+ 'tags' => $this->taskTagModel->getList($task['id']),
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']),
- 'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($task['project_id']),
)));
}
@@ -124,4 +71,19 @@ class TaskModificationController extends BaseController
$this->edit($values, $errors);
}
}
+
+ /**
+ * Prepare form values
+ *
+ * @access protected
+ * @param array $task
+ * @return array
+ */
+ protected function prepareValues(array $task)
+ {
+ $values = $task;
+ $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
+ $values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values));
+ return $values;
+ }
}
diff --git a/app/Controller/TaskPopoverController.php b/app/Controller/TaskPopoverController.php
index bf4e23d5..4e3c11a3 100644
--- a/app/Controller/TaskPopoverController.php
+++ b/app/Controller/TaskPopoverController.php
@@ -11,80 +11,6 @@ namespace Kanboard\Controller;
class TaskPopoverController extends BaseController
{
/**
- * Change a task assignee directly from the board
- *
- * @access public
- */
- public function changeAssignee()
- {
- $task = $this->getTask();
- $project = $this->projectModel->getById($task['project_id']);
-
- $this->response->html($this->template->render('task_popover/change_assignee', array(
- 'values' => $task,
- 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']),
- 'project' => $project,
- )));
- }
-
- /**
- * Validate an assignee modification
- *
- * @access public
- */
- public function updateAssignee()
- {
- $values = $this->request->getValues();
-
- list($valid,) = $this->taskValidator->validateAssigneeModification($values);
-
- if ($valid && $this->taskModificationModel->update($values)) {
- $this->flash->success(t('Task updated successfully.'));
- } else {
- $this->flash->failure(t('Unable to update your task.'));
- }
-
- $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $values['project_id'])), true);
- }
-
- /**
- * Change a task category directly from the board
- *
- * @access public
- */
- public function changeCategory()
- {
- $task = $this->getTask();
- $project = $this->projectModel->getById($task['project_id']);
-
- $this->response->html($this->template->render('task_popover/change_category', array(
- 'values' => $task,
- 'categories_list' => $this->categoryModel->getList($project['id']),
- 'project' => $project,
- )));
- }
-
- /**
- * Validate a category modification
- *
- * @access public
- */
- public function updateCategory()
- {
- $values = $this->request->getValues();
-
- list($valid,) = $this->taskValidator->validateCategoryModification($values);
-
- if ($valid && $this->taskModificationModel->update($values)) {
- $this->flash->success(t('Task updated successfully.'));
- } else {
- $this->flash->failure(t('Unable to update your task.'));
- }
-
- $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $values['project_id'])), true);
- }
-
- /**
* Screenshot popover
*
* @access public
diff --git a/app/Controller/TaskRecurrenceController.php b/app/Controller/TaskRecurrenceController.php
index dc7a0e1b..c6fdfa37 100644
--- a/app/Controller/TaskRecurrenceController.php
+++ b/app/Controller/TaskRecurrenceController.php
@@ -31,10 +31,10 @@ class TaskRecurrenceController extends BaseController
'values' => $values,
'errors' => $errors,
'task' => $task,
- 'recurrence_status_list' => $this->taskModel->getRecurrenceStatusList(),
- 'recurrence_trigger_list' => $this->taskModel->getRecurrenceTriggerList(),
- 'recurrence_timeframe_list' => $this->taskModel->getRecurrenceTimeframeList(),
- 'recurrence_basedate_list' => $this->taskModel->getRecurrenceBasedateList(),
+ 'recurrence_status_list' => $this->taskRecurrenceModel->getRecurrenceStatusList(),
+ 'recurrence_trigger_list' => $this->taskRecurrenceModel->getRecurrenceTriggerList(),
+ 'recurrence_timeframe_list' => $this->taskRecurrenceModel->getRecurrenceTimeframeList(),
+ 'recurrence_basedate_list' => $this->taskRecurrenceModel->getRecurrenceBasedateList(),
)));
}
diff --git a/app/Controller/TaskViewController.php b/app/Controller/TaskViewController.php
index bd1e86ae..36597457 100644
--- a/app/Controller/TaskViewController.php
+++ b/app/Controller/TaskViewController.php
@@ -22,7 +22,6 @@ class TaskViewController extends BaseController
{
$project = $this->projectModel->getByToken($this->request->getStringParam('token'));
- // Token verification
if (empty($project)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
@@ -45,6 +44,7 @@ class TaskViewController extends BaseController
'task' => $task,
'columns_list' => $this->columnModel->getList($task['project_id']),
'colors_list' => $this->colorModel->getList(),
+ 'tags' => $this->taskTagModel->getList($task['id']),
'title' => $task['title'],
'no_layout' => true,
'auto_refresh' => true,
@@ -62,19 +62,9 @@ class TaskViewController extends BaseController
$task = $this->getTask();
$subtasks = $this->subtaskModel->getAll($task['id']);
- $values = array(
- 'id' => $task['id'],
- 'date_started' => $task['date_started'],
- 'time_estimated' => $task['time_estimated'] ?: '',
- 'time_spent' => $task['time_spent'] ?: '',
- );
-
- $values = $this->dateParser->format($values, array('date_started'), $this->dateParser->getUserDateTimeFormat());
-
$this->response->html($this->helper->layout->task('task/show', array(
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
- 'values' => $values,
'files' => $this->taskFileModel->getAllDocuments($task['id']),
'images' => $this->taskFileModel->getAllImages($task['id']),
'comments' => $this->commentModel->getAll($task['id'], $this->userSession->getCommentSorting()),
@@ -82,6 +72,7 @@ class TaskViewController extends BaseController
'internal_links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']),
'external_links' => $this->taskExternalLinkModel->getAll($task['id']),
'link_label_list' => $this->linkModel->getList(0, false),
+ 'tags' => $this->taskTagModel->getList($task['id']),
)));
}
@@ -100,6 +91,7 @@ class TaskViewController extends BaseController
'lead_time' => $this->taskAnalyticModel->getLeadTime($task),
'cycle_time' => $this->taskAnalyticModel->getCycleTime($task),
'time_spent_columns' => $this->taskAnalyticModel->getTimeSpentByColumn($task),
+ 'tags' => $this->taskTagModel->getList($task['id']),
)));
}
@@ -124,6 +116,7 @@ class TaskViewController extends BaseController
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'subtask_paginator' => $subtask_paginator,
+ 'tags' => $this->taskTagModel->getList($task['id']),
)));
}
@@ -140,6 +133,7 @@ class TaskViewController extends BaseController
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'transitions' => $this->transitionModel->getAllByTask($task['id']),
+ 'tags' => $this->taskTagModel->getList($task['id']),
)));
}
}
diff --git a/app/Controller/UserListController.php b/app/Controller/UserListController.php
index 31fcdd44..888583fa 100644
--- a/app/Controller/UserListController.php
+++ b/app/Controller/UserListController.php
@@ -17,12 +17,7 @@ class UserListController extends BaseController
*/
public function show()
{
- $paginator = $this->paginator
- ->setUrl('UserListController', 'show')
- ->setMax(30)
- ->setOrder('username')
- ->setQuery($this->userModel->getQuery())
- ->calculate();
+ $paginator = $this->userPagination->getListingPaginator();
$this->response->html($this->helper->layout->app('user_list/show', array(
'title' => t('Users').' ('.$paginator->getTotal().')',
diff --git a/app/Controller/WebNotificationController.php b/app/Controller/WebNotificationController.php
index 46a42063..30e317f8 100644
--- a/app/Controller/WebNotificationController.php
+++ b/app/Controller/WebNotificationController.php
@@ -54,14 +54,14 @@ class WebNotificationController extends BaseController
$this->response->redirect($this->helper->url->to(
'TaskViewController',
'show',
- array('task_id' => $notification['event_data']['task']['id'], 'project_id' => $notification['event_data']['task']['project_id']),
+ array('task_id' => $this->notificationModel->getTaskIdFromEvent($notification['event_name'], $notification['event_data'])),
'comment-'.$notification['event_data']['comment']['id']
));
} else {
$this->response->redirect($this->helper->url->to(
'TaskViewController',
'show',
- array('task_id' => $notification['event_data']['task']['id'], 'project_id' => $notification['event_data']['task']['project_id'])
+ array('task_id' => $this->notificationModel->getTaskIdFromEvent($notification['event_name'], $notification['event_data']))
));
}
}
diff --git a/app/Core/Action/ActionManager.php b/app/Core/Action/ActionManager.php
index 1dfd820c..aec9ef02 100644
--- a/app/Core/Action/ActionManager.php
+++ b/app/Core/Action/ActionManager.php
@@ -139,4 +139,20 @@ class ActionManager extends Base
return $this;
}
+
+ /**
+ * Remove all listeners for automated actions
+ *
+ * @access public
+ */
+ public function removeEvents()
+ {
+ foreach ($this->dispatcher->getListeners() as $eventName => $listeners) {
+ foreach ($listeners as $listener) {
+ if (is_array($listener) && $listener[0] instanceof ActionBase) {
+ $this->dispatcher->removeListener($eventName, $listener);
+ }
+ }
+ }
+ }
}
diff --git a/app/Core/Base.php b/app/Core/Base.php
index 7b4462e2..68604785 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -35,8 +35,12 @@ use Pimple\Container;
* @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
@@ -82,20 +86,31 @@ use Pimple\Container;
* @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
@@ -107,6 +122,10 @@ use Pimple\Container;
* @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
@@ -114,14 +133,15 @@ use Pimple\Container;
* @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\ExternalLinkValidator $externalLinkValidator
* @property \Kanboard\Validator\TaskValidator $taskValidator
* @property \Kanboard\Validator\UserValidator $userValidator
* @property \Kanboard\Import\TaskImport $taskImport
@@ -137,6 +157,14 @@ use Pimple\Container;
* @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
@@ -165,10 +193,10 @@ abstract class Base
}
/**
- * Load automatically models
+ * Load automatically dependencies
*
* @access public
- * @param string $name Model name
+ * @param string $name Class name
* @return mixed
*/
public function __get($name)
@@ -186,7 +214,6 @@ abstract class Base
*/
public static function getInstance(Container $container)
{
- $self = new static($container);
- return $self;
+ return new static($container);
}
}
diff --git a/app/Core/ExternalLink/ExternalLinkManager.php b/app/Core/ExternalLink/ExternalLinkManager.php
index 804e6b34..5a037999 100644
--- a/app/Core/ExternalLink/ExternalLinkManager.php
+++ b/app/Core/ExternalLink/ExternalLinkManager.php
@@ -153,6 +153,30 @@ class ExternalLinkManager extends Base
}
/**
+ * Set provider type
+ *
+ * @access public
+ * @param string $userInputType
+ * @return ExternalLinkManager
+ */
+ public function setUserInputType($userInputType)
+ {
+ $this->userInputType = $userInputType;
+ return $this;
+ }
+
+ /**
+ * Set external link
+ * @param string $userInputText
+ * @return ExternalLinkManager
+ */
+ public function setUserInputText($userInputText)
+ {
+ $this->userInputText = $userInputText;
+ return $this;
+ }
+
+ /**
* Find a provider that user input
*
* @access private
diff --git a/app/Core/Filter/Lexer.php b/app/Core/Filter/Lexer.php
index fa5b8d2d..3ff57641 100644
--- a/app/Core/Filter/Lexer.php
+++ b/app/Core/Filter/Lexer.php
@@ -30,7 +30,7 @@ class Lexer
'/^([<=>]{1,2}\w+)/u' => 'T_STRING',
'/^([<=>]{1,2}".+")/' => 'T_STRING',
'/^("(.+)")/' => 'T_STRING',
- '/^(\w+)/u' => 'T_STRING',
+ '/^(\S+)/u' => 'T_STRING',
'/^(#\d+)/' => 'T_STRING',
);
diff --git a/app/Core/Filter/LexerBuilder.php b/app/Core/Filter/LexerBuilder.php
index 7a9a714f..e3ab725b 100644
--- a/app/Core/Filter/LexerBuilder.php
+++ b/app/Core/Filter/LexerBuilder.php
@@ -51,7 +51,7 @@ class LexerBuilder
*/
public function __construct()
{
- $this->lexer = new Lexer;
+ $this->lexer = new Lexer();
$this->queryBuilder = new QueryBuilder();
}
@@ -69,7 +69,7 @@ class LexerBuilder
foreach ($attributes as $attribute) {
$this->filters[$attribute] = $filter;
- $this->lexer->addToken(sprintf("/^(%s:)/", $attribute), $attribute);
+ $this->lexer->addToken(sprintf("/^(%s:)/i", $attribute), $attribute);
if ($default) {
$this->lexer->setDefaultToken($attribute);
diff --git a/app/Core/Http/Request.php b/app/Core/Http/Request.php
index e0df2d3c..2e84958d 100644
--- a/app/Core/Http/Request.php
+++ b/app/Core/Http/Request.php
@@ -301,6 +301,7 @@ class Request extends Base
public function getIpAddress()
{
$keys = array(
+ 'HTTP_X_REAL_IP',
'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED',
diff --git a/app/Core/Ldap/User.php b/app/Core/Ldap/User.php
index 91b48530..4bc1f5f9 100644
--- a/app/Core/Ldap/User.php
+++ b/app/Core/Ldap/User.php
@@ -116,7 +116,7 @@ class User
*/
protected function getRole(array $groupIds)
{
- if ($this->hasGroupsNotConfigured()) {
+ if (! $this->hasGroupsConfigured()) {
return null;
}
@@ -278,14 +278,14 @@ class User
}
/**
- * Return true if LDAP Group mapping is not configured
+ * Return true if LDAP Group mapping are configured
*
* @access public
* @return boolean
*/
- public function hasGroupsNotConfigured()
+ public function hasGroupsConfigured()
{
- return !$this->getGroupAdminDn() && !$this->getGroupManagerDn();
+ return $this->getGroupAdminDn() || $this->getGroupManagerDn();
}
/**
diff --git a/app/Core/Mail/Transport/Smtp.php b/app/Core/Mail/Transport/Smtp.php
index 66f0a3aa..1f4e54ce 100644
--- a/app/Core/Mail/Transport/Smtp.php
+++ b/app/Core/Mail/Transport/Smtp.php
@@ -24,6 +24,15 @@ class Smtp extends Mail
$transport->setUsername(MAIL_SMTP_USERNAME);
$transport->setPassword(MAIL_SMTP_PASSWORD);
$transport->setEncryption(MAIL_SMTP_ENCRYPTION);
+ if (HTTP_VERIFY_SSL_CERTIFICATE === false) {
+ $transport->setStreamOptions(array(
+ 'ssl' => array(
+ 'allow_self_signed' => true,
+ 'verify_peer' => false,
+ 'verify_peer_name' => false,
+ )
+ ));
+ }
return $transport;
}
diff --git a/app/Core/Plugin/Hook.php b/app/Core/Plugin/Hook.php
index ade69150..ca197937 100644
--- a/app/Core/Plugin/Hook.php
+++ b/app/Core/Plugin/Hook.php
@@ -96,4 +96,21 @@ class Hook
return null;
}
+
+ /**
+ * Hook with reference
+ *
+ * @access public
+ * @param string $hook
+ * @param mixed $param
+ * @return mixed
+ */
+ public function reference($hook, &$param)
+ {
+ foreach ($this->getListeners($hook) as $listener) {
+ $listener($param);
+ }
+
+ return $param;
+ }
}
diff --git a/app/Core/Queue/JobHandler.php b/app/Core/Queue/JobHandler.php
index f8736cce..11c1fb69 100644
--- a/app/Core/Queue/JobHandler.php
+++ b/app/Core/Queue/JobHandler.php
@@ -2,6 +2,7 @@
namespace Kanboard\Core\Queue;
+use Exception;
use Kanboard\Core\Base;
use Kanboard\Job\BaseJob;
use SimpleQueue\Job;
@@ -39,15 +40,23 @@ class JobHandler extends Base
public function executeJob(Job $job)
{
$payload = $job->getBody();
- $className = $payload['class'];
- $this->prepareJobSession($payload['user_id']);
- if (DEBUG) {
- $this->logger->debug(__METHOD__.' Received job => '.$className.' ('.getmypid().')');
- }
+ try {
+ $className = $payload['class'];
+ $this->prepareJobSession($payload['user_id']);
+ $this->prepareJobEnvironment();
+
+ if (DEBUG) {
+ $this->logger->debug(__METHOD__.' Received job => '.$className.' ('.getmypid().')');
+ $this->logger->debug(__METHOD__.' => '.json_encode($payload));
+ }
- $worker = new $className($this->container);
- call_user_func_array(array($worker, 'execute'), $payload['params']);
+ $worker = new $className($this->container);
+ call_user_func_array(array($worker, 'execute'), $payload['params']);
+ } catch (Exception $e) {
+ $this->logger->error(__METHOD__.': Error during job execution: '.$e->getMessage());
+ $this->logger->error(__METHOD__ .' => '.json_encode($payload));
+ }
}
/**
@@ -66,4 +75,16 @@ class JobHandler extends Base
$this->userSession->initialize($user);
}
}
+
+ /**
+ * Flush in-memory caching and specific events
+ *
+ * @access protected
+ */
+ protected function prepareJobEnvironment()
+ {
+ $this->memoryCache->flush();
+ $this->actionManager->removeEvents();
+ $this->dispatcher->dispatch('app.bootstrap');
+ }
}
diff --git a/app/Core/Queue/QueueManager.php b/app/Core/Queue/QueueManager.php
index f34cb220..dcf0ebf5 100644
--- a/app/Core/Queue/QueueManager.php
+++ b/app/Core/Queue/QueueManager.php
@@ -42,9 +42,13 @@ class QueueManager extends Base
*/
public function push(BaseJob $job)
{
+ $jobClassName = get_class($job);
+
if ($this->queue !== null) {
+ $this->logger->debug(__METHOD__.': Job pushed in queue: '.$jobClassName);
$this->queue->push(JobHandler::getInstance($this->container)->serializeJob($job));
} else {
+ $this->logger->debug(__METHOD__.': Job executed synchronously: '.$jobClassName);
call_user_func_array(array($job, 'execute'), $job->getJobParams());
}
@@ -60,7 +64,7 @@ class QueueManager extends Base
public function listen()
{
if ($this->queue === null) {
- throw new LogicException('No Queue Driver defined!');
+ throw new LogicException('No queue driver defined!');
}
while ($job = $this->queue->pull()) {
diff --git a/app/Event/FileEvent.php b/app/Event/FileEvent.php
deleted file mode 100644
index 482a4eab..00000000
--- a/app/Event/FileEvent.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Kanboard\Event;
-
-class FileEvent extends GenericEvent
-{
-}
diff --git a/app/Event/ProjectFileEvent.php b/app/Event/ProjectFileEvent.php
new file mode 100644
index 00000000..5d57e463
--- /dev/null
+++ b/app/Event/ProjectFileEvent.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Kanboard\Event;
+
+class ProjectFileEvent extends GenericEvent
+{
+}
diff --git a/app/Event/TaskFileEvent.php b/app/Event/TaskFileEvent.php
new file mode 100644
index 00000000..fa3bdde9
--- /dev/null
+++ b/app/Event/TaskFileEvent.php
@@ -0,0 +1,7 @@
+<?php
+
+namespace Kanboard\Event;
+
+class TaskFileEvent extends GenericEvent
+{
+}
diff --git a/app/EventBuilder/BaseEventBuilder.php b/app/EventBuilder/BaseEventBuilder.php
new file mode 100644
index 00000000..5aa777a0
--- /dev/null
+++ b/app/EventBuilder/BaseEventBuilder.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Kanboard\Core\Base;
+use Kanboard\Event\GenericEvent;
+
+/**
+ * Class BaseEventBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+abstract class BaseEventBuilder extends Base
+{
+ /**
+ * Build event data
+ *
+ * @access public
+ * @return GenericEvent|null
+ */
+ abstract public function buildEvent();
+
+ /**
+ * Get event title with author
+ *
+ * @access public
+ * @param string $author
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ abstract public function buildTitleWithAuthor($author, $eventName, array $eventData);
+
+ /**
+ * Get event title without author
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ abstract public function buildTitleWithoutAuthor($eventName, array $eventData);
+}
diff --git a/app/EventBuilder/CommentEventBuilder.php b/app/EventBuilder/CommentEventBuilder.php
new file mode 100644
index 00000000..ba5842a4
--- /dev/null
+++ b/app/EventBuilder/CommentEventBuilder.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Kanboard\Event\CommentEvent;
+use Kanboard\Model\CommentModel;
+
+/**
+ * Class CommentEventBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+class CommentEventBuilder extends BaseEventBuilder
+{
+ protected $commentId = 0;
+
+ /**
+ * Set commentId
+ *
+ * @param int $commentId
+ * @return $this
+ */
+ public function withCommentId($commentId)
+ {
+ $this->commentId = $commentId;
+ return $this;
+ }
+
+ /**
+ * Build event data
+ *
+ * @access public
+ * @return CommentEvent|null
+ */
+ public function buildEvent()
+ {
+ $comment = $this->commentModel->getById($this->commentId);
+
+ if (empty($comment)) {
+ return null;
+ }
+
+ return new CommentEvent(array(
+ 'comment' => $comment,
+ 'task' => $this->taskFinderModel->getDetails($comment['task_id']),
+ ));
+ }
+
+ /**
+ * Get event title with author
+ *
+ * @access public
+ * @param string $author
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithAuthor($author, $eventName, array $eventData)
+ {
+ switch ($eventName) {
+ case CommentModel::EVENT_UPDATE:
+ return e('%s updated a comment on the task #%d', $author, $eventData['task']['id']);
+ case CommentModel::EVENT_CREATE:
+ return e('%s commented on the task #%d', $author, $eventData['task']['id']);
+ case CommentModel::EVENT_DELETE:
+ return e('%s removed a comment on the task #%d', $author, $eventData['task']['id']);
+ case CommentModel::EVENT_USER_MENTION:
+ return e('%s mentioned you in a comment on the task #%d', $author, $eventData['task']['id']);
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Get event title without author
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithoutAuthor($eventName, array $eventData)
+ {
+ switch ($eventName) {
+ case CommentModel::EVENT_CREATE:
+ return e('New comment on task #%d', $eventData['comment']['task_id']);
+ case CommentModel::EVENT_UPDATE:
+ return e('Comment updated on task #%d', $eventData['comment']['task_id']);
+ case CommentModel::EVENT_DELETE:
+ return e('Comment removed on task #%d', $eventData['comment']['task_id']);
+ case CommentModel::EVENT_USER_MENTION:
+ return e('You were mentioned in a comment on the task #%d', $eventData['task']['id']);
+ default:
+ return '';
+ }
+ }
+}
diff --git a/app/EventBuilder/EventIteratorBuilder.php b/app/EventBuilder/EventIteratorBuilder.php
new file mode 100644
index 00000000..ba821753
--- /dev/null
+++ b/app/EventBuilder/EventIteratorBuilder.php
@@ -0,0 +1,52 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Iterator;
+
+/**
+ * Class EventIteratorBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+class EventIteratorBuilder implements Iterator {
+ private $position = 0;
+ private $builders = array();
+
+ /**
+ * Set builder
+ *
+ * @access public
+ * @param BaseEventBuilder $builder
+ * @return $this
+ */
+ public function withBuilder(BaseEventBuilder $builder)
+ {
+ $this->builders[] = $builder;
+ return $this;
+ }
+
+ public function rewind() {
+ $this->position = 0;
+ }
+
+ /**
+ * @return BaseEventBuilder
+ */
+ public function current() {
+ return $this->builders[$this->position];
+ }
+
+ public function key() {
+ return $this->position;
+ }
+
+ public function next() {
+ ++$this->position;
+ }
+
+ public function valid() {
+ return isset($this->builders[$this->position]);
+ }
+}
diff --git a/app/EventBuilder/ProjectFileEventBuilder.php b/app/EventBuilder/ProjectFileEventBuilder.php
new file mode 100644
index 00000000..6698f78a
--- /dev/null
+++ b/app/EventBuilder/ProjectFileEventBuilder.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Kanboard\Event\ProjectFileEvent;
+use Kanboard\Event\GenericEvent;
+
+/**
+ * Class ProjectFileEventBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+class ProjectFileEventBuilder extends BaseEventBuilder
+{
+ protected $fileId = 0;
+
+ /**
+ * Set fileId
+ *
+ * @param int $fileId
+ * @return $this
+ */
+ public function withFileId($fileId)
+ {
+ $this->fileId = $fileId;
+ return $this;
+ }
+
+ /**
+ * Build event data
+ *
+ * @access public
+ * @return GenericEvent|null
+ */
+ public function buildEvent()
+ {
+ $file = $this->projectFileModel->getById($this->fileId);
+
+ if (empty($file)) {
+ $this->logger->debug(__METHOD__.': File not found');
+ return null;
+ }
+
+ return new ProjectFileEvent(array(
+ 'file' => $file,
+ 'project' => $this->projectModel->getById($file['project_id']),
+ ));
+ }
+
+ /**
+ * Get event title with author
+ *
+ * @access public
+ * @param string $author
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithAuthor($author, $eventName, array $eventData)
+ {
+ return '';
+ }
+
+ /**
+ * Get event title without author
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithoutAuthor($eventName, array $eventData)
+ {
+ return '';
+ }
+}
diff --git a/app/EventBuilder/SubtaskEventBuilder.php b/app/EventBuilder/SubtaskEventBuilder.php
new file mode 100644
index 00000000..5f7e831d
--- /dev/null
+++ b/app/EventBuilder/SubtaskEventBuilder.php
@@ -0,0 +1,125 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Kanboard\Event\SubtaskEvent;
+use Kanboard\Event\GenericEvent;
+use Kanboard\Model\SubtaskModel;
+
+/**
+ * Class SubtaskEventBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+class SubtaskEventBuilder extends BaseEventBuilder
+{
+ /**
+ * SubtaskId
+ *
+ * @access protected
+ * @var int
+ */
+ protected $subtaskId = 0;
+
+ /**
+ * Changed values
+ *
+ * @access protected
+ * @var array
+ */
+ protected $values = array();
+
+ /**
+ * Set SubtaskId
+ *
+ * @param int $subtaskId
+ * @return $this
+ */
+ public function withSubtaskId($subtaskId)
+ {
+ $this->subtaskId = $subtaskId;
+ return $this;
+ }
+
+ /**
+ * Set values
+ *
+ * @param array $values
+ * @return $this
+ */
+ public function withValues(array $values)
+ {
+ $this->values = $values;
+ return $this;
+ }
+
+ /**
+ * Build event data
+ *
+ * @access public
+ * @return GenericEvent|null
+ */
+ public function buildEvent()
+ {
+ $eventData = array();
+ $eventData['subtask'] = $this->subtaskModel->getById($this->subtaskId, true);
+
+ if (empty($eventData['subtask'])) {
+ $this->logger->debug(__METHOD__.': Subtask not found');
+ return null;
+ }
+
+ if (! empty($this->values)) {
+ $eventData['changes'] = array_diff_assoc($this->values, $eventData['subtask']);
+ }
+
+ $eventData['task'] = $this->taskFinderModel->getDetails($eventData['subtask']['task_id']);
+ return new SubtaskEvent($eventData);
+ }
+
+ /**
+ * Get event title with author
+ *
+ * @access public
+ * @param string $author
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithAuthor($author, $eventName, array $eventData)
+ {
+ switch ($eventName) {
+ case SubtaskModel::EVENT_UPDATE:
+ return e('%s updated a subtask for the task #%d', $author, $eventData['task']['id']);
+ case SubtaskModel::EVENT_CREATE:
+ return e('%s created a subtask for the task #%d', $author, $eventData['task']['id']);
+ case SubtaskModel::EVENT_DELETE:
+ return e('%s removed a subtask for the task #%d', $author, $eventData['task']['id']);
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Get event title without author
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithoutAuthor($eventName, array $eventData)
+ {
+ switch ($eventName) {
+ case SubtaskModel::EVENT_CREATE:
+ return e('New subtask on task #%d', $eventData['subtask']['task_id']);
+ case SubtaskModel::EVENT_UPDATE:
+ return e('Subtask updated on task #%d', $eventData['subtask']['task_id']);
+ case SubtaskModel::EVENT_DELETE:
+ return e('Subtask removed on task #%d', $eventData['subtask']['task_id']);
+ default:
+ return '';
+ }
+ }
+}
diff --git a/app/EventBuilder/TaskEventBuilder.php b/app/EventBuilder/TaskEventBuilder.php
new file mode 100644
index 00000000..aa897632
--- /dev/null
+++ b/app/EventBuilder/TaskEventBuilder.php
@@ -0,0 +1,223 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Kanboard\Event\TaskEvent;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskEventBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+class TaskEventBuilder extends BaseEventBuilder
+{
+ /**
+ * TaskId
+ *
+ * @access protected
+ * @var int
+ */
+ protected $taskId = 0;
+
+ /**
+ * Task
+ *
+ * @access protected
+ * @var array
+ */
+ protected $task = array();
+
+ /**
+ * Extra values
+ *
+ * @access protected
+ * @var array
+ */
+ protected $values = array();
+
+ /**
+ * Changed values
+ *
+ * @access protected
+ * @var array
+ */
+ protected $changes = array();
+
+ /**
+ * Set TaskId
+ *
+ * @param int $taskId
+ * @return $this
+ */
+ public function withTaskId($taskId)
+ {
+ $this->taskId = $taskId;
+ return $this;
+ }
+
+ /**
+ * Set task
+ *
+ * @param array $task
+ * @return $this
+ */
+ public function withTask(array $task)
+ {
+ $this->task = $task;
+ return $this;
+ }
+
+ /**
+ * Set values
+ *
+ * @param array $values
+ * @return $this
+ */
+ public function withValues(array $values)
+ {
+ $this->values = $values;
+ return $this;
+ }
+
+ /**
+ * Set changes
+ *
+ * @param array $changes
+ * @return $this
+ */
+ public function withChanges(array $changes)
+ {
+ $this->changes = $changes;
+ return $this;
+ }
+
+ /**
+ * Build event data
+ *
+ * @access public
+ * @return TaskEvent|null
+ */
+ public function buildEvent()
+ {
+ $eventData = array();
+ $eventData['task_id'] = $this->taskId;
+ $eventData['task'] = $this->taskFinderModel->getDetails($this->taskId);
+
+ if (empty($eventData['task'])) {
+ $this->logger->debug(__METHOD__.': Task not found');
+ return null;
+ }
+
+ if (! empty($this->changes)) {
+ if (empty($this->task)) {
+ $this->task = $eventData['task'];
+ }
+
+ $eventData['changes'] = array_diff_assoc($this->changes, $this->task);
+ unset($eventData['changes']['date_modification']);
+ }
+
+ return new TaskEvent(array_merge($eventData, $this->values));
+ }
+
+ /**
+ * Get event title with author
+ *
+ * @access public
+ * @param string $author
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithAuthor($author, $eventName, array $eventData)
+ {
+ switch ($eventName) {
+ case TaskModel::EVENT_ASSIGNEE_CHANGE:
+ $assignee = $eventData['task']['assignee_name'] ?: $eventData['task']['assignee_username'];
+
+ if (! empty($assignee)) {
+ return e('%s changed the assignee of the task #%d to %s', $author, $eventData['task']['id'], $assignee);
+ }
+
+ return e('%s removed the assignee of the task %s', $author, e('#%d', $eventData['task']['id']));
+ case TaskModel::EVENT_UPDATE:
+ return e('%s updated the task #%d', $author, $eventData['task']['id']);
+ case TaskModel::EVENT_CREATE:
+ return e('%s created the task #%d', $author, $eventData['task']['id']);
+ case TaskModel::EVENT_CLOSE:
+ return e('%s closed the task #%d', $author, $eventData['task']['id']);
+ case TaskModel::EVENT_OPEN:
+ return e('%s opened the task #%d', $author, $eventData['task']['id']);
+ case TaskModel::EVENT_MOVE_COLUMN:
+ return e(
+ '%s moved the task #%d to the column "%s"',
+ $author,
+ $eventData['task']['id'],
+ $eventData['task']['column_title']
+ );
+ case TaskModel::EVENT_MOVE_POSITION:
+ return e(
+ '%s moved the task #%d to the position %d in the column "%s"',
+ $author,
+ $eventData['task']['id'],
+ $eventData['task']['position'],
+ $eventData['task']['column_title']
+ );
+ case TaskModel::EVENT_MOVE_SWIMLANE:
+ if ($eventData['task']['swimlane_id'] == 0) {
+ return e('%s moved the task #%d to the first swimlane', $author, $eventData['task']['id']);
+ }
+
+ return e(
+ '%s moved the task #%d to the swimlane "%s"',
+ $author,
+ $eventData['task']['id'],
+ $eventData['task']['swimlane_name']
+ );
+
+ case TaskModel::EVENT_USER_MENTION:
+ return e('%s mentioned you in the task #%d', $author, $eventData['task']['id']);
+ default:
+ return '';
+ }
+ }
+
+ /**
+ * Get event title without author
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithoutAuthor($eventName, array $eventData)
+ {
+ switch ($eventName) {
+ case TaskModel::EVENT_CREATE:
+ return e('New task #%d: %s', $eventData['task']['id'], $eventData['task']['title']);
+ case TaskModel::EVENT_UPDATE:
+ return e('Task updated #%d', $eventData['task']['id']);
+ case TaskModel::EVENT_CLOSE:
+ return e('Task #%d closed', $eventData['task']['id']);
+ case TaskModel::EVENT_OPEN:
+ return e('Task #%d opened', $eventData['task']['id']);
+ case TaskModel::EVENT_MOVE_COLUMN:
+ return e('Column changed for task #%d', $eventData['task']['id']);
+ case TaskModel::EVENT_MOVE_POSITION:
+ return e('New position for task #%d', $eventData['task']['id']);
+ case TaskModel::EVENT_MOVE_SWIMLANE:
+ return e('Swimlane changed for task #%d', $eventData['task']['id']);
+ case TaskModel::EVENT_ASSIGNEE_CHANGE:
+ return e('Assignee changed on task #%d', $eventData['task']['id']);
+ case TaskModel::EVENT_OVERDUE:
+ $nb = count($eventData['tasks']);
+ return $nb > 1 ? e('%d overdue tasks', $nb) : e('Task #%d is overdue', $eventData['tasks'][0]['id']);
+ case TaskModel::EVENT_USER_MENTION:
+ return e('You were mentioned in the task #%d', $eventData['task']['id']);
+ default:
+ return '';
+ }
+ }
+}
diff --git a/app/EventBuilder/TaskFileEventBuilder.php b/app/EventBuilder/TaskFileEventBuilder.php
new file mode 100644
index 00000000..8c985cc0
--- /dev/null
+++ b/app/EventBuilder/TaskFileEventBuilder.php
@@ -0,0 +1,86 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Kanboard\Event\TaskFileEvent;
+use Kanboard\Event\GenericEvent;
+use Kanboard\Model\TaskFileModel;
+
+/**
+ * Class TaskFileEventBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+class TaskFileEventBuilder extends BaseEventBuilder
+{
+ protected $fileId = 0;
+
+ /**
+ * Set fileId
+ *
+ * @param int $fileId
+ * @return $this
+ */
+ public function withFileId($fileId)
+ {
+ $this->fileId = $fileId;
+ return $this;
+ }
+
+ /**
+ * Build event data
+ *
+ * @access public
+ * @return GenericEvent|null
+ */
+ public function buildEvent()
+ {
+ $file = $this->taskFileModel->getById($this->fileId);
+
+ if (empty($file)) {
+ $this->logger->debug(__METHOD__.': File not found');
+ return null;
+ }
+
+ return new TaskFileEvent(array(
+ 'file' => $file,
+ 'task' => $this->taskFinderModel->getDetails($file['task_id']),
+ ));
+ }
+
+ /**
+ * Get event title with author
+ *
+ * @access public
+ * @param string $author
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithAuthor($author, $eventName, array $eventData)
+ {
+ if ($eventName === TaskFileModel::EVENT_CREATE) {
+ return e('%s attached a file to the task #%d', $author, $eventData['task']['id']);
+ }
+
+ return '';
+ }
+
+ /**
+ * Get event title without author
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithoutAuthor($eventName, array $eventData)
+ {
+ if ($eventName === TaskFileModel::EVENT_CREATE) {
+ return e('New attachment on task #%d: %s', $eventData['file']['task_id'], $eventData['file']['name']);
+ }
+
+ return '';
+ }
+}
diff --git a/app/EventBuilder/TaskLinkEventBuilder.php b/app/EventBuilder/TaskLinkEventBuilder.php
new file mode 100644
index 00000000..f1a3fba2
--- /dev/null
+++ b/app/EventBuilder/TaskLinkEventBuilder.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Kanboard\EventBuilder;
+
+use Kanboard\Event\TaskLinkEvent;
+use Kanboard\Model\TaskLinkModel;
+
+/**
+ * Class TaskLinkEventBuilder
+ *
+ * @package Kanboard\EventBuilder
+ * @author Frederic Guillot
+ */
+class TaskLinkEventBuilder extends BaseEventBuilder
+{
+ protected $taskLinkId = 0;
+
+ /**
+ * Set taskLinkId
+ *
+ * @param int $taskLinkId
+ * @return $this
+ */
+ public function withTaskLinkId($taskLinkId)
+ {
+ $this->taskLinkId = $taskLinkId;
+ return $this;
+ }
+
+ /**
+ * Build event data
+ *
+ * @access public
+ * @return TaskLinkEvent|null
+ */
+ public function buildEvent()
+ {
+ $taskLink = $this->taskLinkModel->getById($this->taskLinkId);
+
+ if (empty($taskLink)) {
+ $this->logger->debug(__METHOD__.': TaskLink not found');
+ return null;
+ }
+
+ return new TaskLinkEvent(array(
+ 'task_link' => $taskLink,
+ 'task' => $this->taskFinderModel->getDetails($taskLink['task_id']),
+ ));
+ }
+
+ /**
+ * Get event title with author
+ *
+ * @access public
+ * @param string $author
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithAuthor($author, $eventName, array $eventData)
+ {
+ if ($eventName === TaskLinkModel::EVENT_CREATE_UPDATE) {
+ return e('%s set a new internal link for the task #%d', $author, $eventData['task']['id']);
+ } elseif ($eventName === TaskLinkModel::EVENT_DELETE) {
+ return e('%s removed an internal link for the task #%d', $author, $eventData['task']['id']);
+ }
+
+ return '';
+ }
+
+ /**
+ * Get event title without author
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return string
+ */
+ public function buildTitleWithoutAuthor($eventName, array $eventData)
+ {
+ if ($eventName === TaskLinkModel::EVENT_CREATE_UPDATE) {
+ return e('A new internal link for the task #%d have been defined', $eventData['task']['id']);
+ } elseif ($eventName === TaskLinkModel::EVENT_DELETE) {
+ return e('Internal link removed for the task #%d', $eventData['task']['id']);
+ }
+
+ return '';
+ }
+}
diff --git a/app/Filter/BaseFilter.php b/app/Filter/BaseFilter.php
index 79a664be..e029f4e1 100644
--- a/app/Filter/BaseFilter.php
+++ b/app/Filter/BaseFilter.php
@@ -43,8 +43,7 @@ abstract class BaseFilter
*/
public static function getInstance($value = null)
{
- $self = new static($value);
- return $self;
+ return new static($value);
}
/**
diff --git a/app/Filter/TaskMovedDateFilter.php b/app/Filter/TaskMovedDateFilter.php
new file mode 100644
index 00000000..d57b7d23
--- /dev/null
+++ b/app/Filter/TaskMovedDateFilter.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Kanboard\Filter;
+
+use Kanboard\Core\Filter\FilterInterface;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Filter tasks by modification date
+ *
+ * @package filter
+ * @author Frederic Guillot
+ */
+class TaskMovedDateFilter extends BaseDateFilter implements FilterInterface
+{
+ /**
+ * Get search attribute
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getAttributes()
+ {
+ return array('moved');
+ }
+
+ /**
+ * Apply filter
+ *
+ * @access public
+ * @return FilterInterface
+ */
+ public function apply()
+ {
+ $this->applyDateFilter(TaskModel::TABLE.'.date_moved');
+ return $this;
+ }
+}
diff --git a/app/Filter/TaskPriorityFilter.php b/app/Filter/TaskPriorityFilter.php
new file mode 100644
index 00000000..75f6ae3d
--- /dev/null
+++ b/app/Filter/TaskPriorityFilter.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Kanboard\Filter;
+
+use Kanboard\Core\Filter\FilterInterface;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskPriorityFilter
+ *
+ * @package Kanboard\Filter
+ * @author Frederic Guillot
+ */
+class TaskPriorityFilter extends BaseFilter implements FilterInterface
+{
+ /**
+ * Get search attribute
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getAttributes()
+ {
+ return array('priority');
+ }
+
+ /**
+ * Apply filter
+ *
+ * @access public
+ * @return FilterInterface
+ */
+ public function apply()
+ {
+ $this->query->eq(TaskModel::TABLE.'.priority', $this->value);
+ return $this;
+ }
+}
diff --git a/app/Filter/TaskTagFilter.php b/app/Filter/TaskTagFilter.php
new file mode 100644
index 00000000..01b6f625
--- /dev/null
+++ b/app/Filter/TaskTagFilter.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Kanboard\Filter;
+
+use Kanboard\Core\Filter\FilterInterface;
+use Kanboard\Model\TagModel;
+use Kanboard\Model\TaskModel;
+use Kanboard\Model\TaskTagModel;
+use PicoDb\Database;
+
+/**
+ * Class TaskTagFilter
+ *
+ * @package Kanboard\Filter
+ * @author Frederic Guillot
+ */
+class TaskTagFilter extends BaseFilter implements FilterInterface
+{
+ /**
+ * Database object
+ *
+ * @access private
+ * @var Database
+ */
+ private $db;
+
+ /**
+ * Get search attribute
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getAttributes()
+ {
+ return array('tag');
+ }
+
+ /**
+ * Set database object
+ *
+ * @access public
+ * @param Database $db
+ * @return $this
+ */
+ public function setDatabase(Database $db)
+ {
+ $this->db = $db;
+ return $this;
+ }
+
+ /**
+ * Apply filter
+ *
+ * @access public
+ * @return FilterInterface
+ */
+ public function apply()
+ {
+ $task_ids = $this->db
+ ->table(TagModel::TABLE)
+ ->ilike(TagModel::TABLE.'.name', $this->value)
+ ->asc(TagModel::TABLE.'.project_id')
+ ->join(TaskTagModel::TABLE, 'tag_id', 'id')
+ ->findAllByColumn(TaskTagModel::TABLE.'.task_id');
+
+ if (empty($task_ids)) {
+ $task_ids = array(-1);
+ }
+
+ $this->query->in(TaskModel::TABLE.'.id', $task_ids);
+
+ return $this;
+ }
+}
diff --git a/app/Formatter/BaseFormatter.php b/app/Formatter/BaseFormatter.php
index a9f0ad15..89c48437 100644
--- a/app/Formatter/BaseFormatter.php
+++ b/app/Formatter/BaseFormatter.php
@@ -3,8 +3,8 @@
namespace Kanboard\Formatter;
use Kanboard\Core\Base;
-use Kanboard\Core\Filter\FormatterInterface;
use PicoDb\Table;
+use Pimple\Container;
/**
* Class BaseFormatter
@@ -23,11 +23,24 @@ abstract class BaseFormatter extends Base
protected $query;
/**
+ * Get object instance
+ *
+ * @static
+ * @access public
+ * @param Container $container
+ * @return static
+ */
+ public static function getInstance(Container $container)
+ {
+ return new static($container);
+ }
+
+ /**
* Set query
*
* @access public
* @param Table $query
- * @return FormatterInterface
+ * @return $this
*/
public function withQuery(Table $query)
{
diff --git a/app/Formatter/BoardColumnFormatter.php b/app/Formatter/BoardColumnFormatter.php
new file mode 100644
index 00000000..d49a577a
--- /dev/null
+++ b/app/Formatter/BoardColumnFormatter.php
@@ -0,0 +1,94 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+
+/**
+ * Board Column Formatter
+ *
+ * @package formatter
+ * @author Frederic Guillot
+ */
+class BoardColumnFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $swimlaneId = 0;
+ protected $columns = array();
+ protected $tasks = array();
+ protected $tags = array();
+
+ /**
+ * Set swimlaneId
+ *
+ * @access public
+ * @param integer $swimlaneId
+ * @return $this
+ */
+ public function withSwimlaneId($swimlaneId)
+ {
+ $this->swimlaneId = $swimlaneId;
+ return $this;
+ }
+
+ /**
+ * Set columns
+ *
+ * @access public
+ * @param array $columns
+ * @return $this
+ */
+ public function withColumns(array $columns)
+ {
+ $this->columns = $columns;
+ return $this;
+ }
+
+ /**
+ * Set tasks
+ *
+ * @access public
+ * @param array $tasks
+ * @return $this
+ */
+ public function withTasks(array $tasks)
+ {
+ $this->tasks = $tasks;
+ return $this;
+ }
+
+ /**
+ * Set tags
+ *
+ * @access public
+ * @param array $tags
+ * @return $this
+ */
+ public function withTags(array $tags)
+ {
+ $this->tags = $tags;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ foreach ($this->columns as &$column) {
+ $column['tasks'] = BoardTaskFormatter::getInstance($this->container)
+ ->withTasks($this->tasks)
+ ->withTags($this->tags)
+ ->withSwimlaneId($this->swimlaneId)
+ ->withColumnId($column['id'])
+ ->format();
+
+ $column['nb_tasks'] = count($column['tasks']);
+ $column['score'] = (int) array_column_sum($column['tasks'], 'score');
+ }
+
+ return $this->columns;
+ }
+}
diff --git a/app/Formatter/BoardFormatter.php b/app/Formatter/BoardFormatter.php
index dbc7cf21..df443a52 100644
--- a/app/Formatter/BoardFormatter.php
+++ b/app/Formatter/BoardFormatter.php
@@ -28,7 +28,7 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
* @param integer $projectId
* @return $this
*/
- public function setProjectId($projectId)
+ public function withProjectId($projectId)
{
$this->projectId = $projectId;
return $this;
@@ -42,15 +42,28 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
*/
public function format()
{
+ $swimlanes = $this->swimlaneModel->getSwimlanes($this->projectId);
+ $columns = $this->columnModel->getAll($this->projectId);
+
+ if (empty($swimlanes) || empty($columns)) {
+ return array();
+ }
+
+ $this->hook->reference('formatter:board:query', $this->query);
+
$tasks = $this->query
->eq(TaskModel::TABLE.'.project_id', $this->projectId)
->asc(TaskModel::TABLE.'.position')
->findAll();
- return $this->boardModel->getBoard($this->projectId, function ($project_id, $column_id, $swimlane_id) use ($tasks) {
- return array_filter($tasks, function (array $task) use ($column_id, $swimlane_id) {
- return $task['column_id'] == $column_id && $task['swimlane_id'] == $swimlane_id;
- });
- });
+ $task_ids = array_column($tasks, 'id');
+ $tags = $this->taskTagModel->getTagsByTasks($task_ids);
+
+ return BoardSwimlaneFormatter::getInstance($this->container)
+ ->withSwimlanes($swimlanes)
+ ->withColumns($columns)
+ ->withTasks($tasks)
+ ->withTags($tags)
+ ->format();
}
}
diff --git a/app/Formatter/BoardSwimlaneFormatter.php b/app/Formatter/BoardSwimlaneFormatter.php
new file mode 100644
index 00000000..c2abb444
--- /dev/null
+++ b/app/Formatter/BoardSwimlaneFormatter.php
@@ -0,0 +1,120 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+
+/**
+ * Board Swimlane Formatter
+ *
+ * @package formatter
+ * @author Frederic Guillot
+ */
+class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $swimlanes = array();
+ protected $columns = array();
+ protected $tasks = array();
+ protected $tags = array();
+
+ /**
+ * Set swimlanes
+ *
+ * @access public
+ * @param array $swimlanes
+ * @return $this
+ */
+ public function withSwimlanes($swimlanes)
+ {
+ $this->swimlanes = $swimlanes;
+ return $this;
+ }
+
+ /**
+ * Set columns
+ *
+ * @access public
+ * @param array $columns
+ * @return $this
+ */
+ public function withColumns($columns)
+ {
+ $this->columns = $columns;
+ return $this;
+ }
+
+ /**
+ * Set tasks
+ *
+ * @access public
+ * @param array $tasks
+ * @return $this
+ */
+ public function withTasks(array $tasks)
+ {
+ $this->tasks = $tasks;
+ return $this;
+ }
+
+ /**
+ * Set tags
+ *
+ * @access public
+ * @param array $tags
+ * @return $this
+ */
+ public function withTags(array $tags)
+ {
+ $this->tags = $tags;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ $nb_swimlanes = count($this->swimlanes);
+ $nb_columns = count($this->columns);
+
+ foreach ($this->swimlanes as &$swimlane) {
+ $swimlane['columns'] = BoardColumnFormatter::getInstance($this->container)
+ ->withSwimlaneId($swimlane['id'])
+ ->withColumns($this->columns)
+ ->withTasks($this->tasks)
+ ->withTags($this->tags)
+ ->format();
+
+ $swimlane['nb_swimlanes'] = $nb_swimlanes;
+ $swimlane['nb_columns'] = $nb_columns;
+ $swimlane['nb_tasks'] = array_column_sum($swimlane['columns'], 'nb_tasks');
+ $swimlane['score'] = array_column_sum($swimlane['columns'], 'score');
+
+ $this->calculateStatsByColumnAcrossSwimlanes($swimlane['columns']);
+ }
+
+ return $this->swimlanes;
+ }
+
+ /**
+ * Calculate stats for each column acrosss all swimlanes
+ *
+ * @access protected
+ * @param array $columns
+ */
+ protected function calculateStatsByColumnAcrossSwimlanes(array $columns)
+ {
+ foreach ($columns as $columnIndex => $column) {
+ if (! isset($this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'])) {
+ $this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'] = 0;
+ $this->swimlanes[0]['columns'][$columnIndex]['column_score'] = 0;
+ }
+
+ $this->swimlanes[0]['columns'][$columnIndex]['column_nb_tasks'] += $column['nb_tasks'];
+ $this->swimlanes[0]['columns'][$columnIndex]['column_score'] += $column['score'];
+ }
+ }
+}
diff --git a/app/Formatter/BoardTaskFormatter.php b/app/Formatter/BoardTaskFormatter.php
new file mode 100644
index 00000000..3bf171b1
--- /dev/null
+++ b/app/Formatter/BoardTaskFormatter.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+
+/**
+ * Board Task Formatter
+ *
+ * @package formatter
+ * @author Frederic Guillot
+ */
+class BoardTaskFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $tasks = array();
+ protected $tags = array();
+ protected $columnId = 0;
+ protected $swimlaneId = 0;
+
+ /**
+ * Set tags
+ *
+ * @access public
+ * @param array $tags
+ * @return $this
+ */
+ public function withTags(array $tags)
+ {
+ $this->tags = $tags;
+ return $this;
+ }
+
+ /**
+ * Set tasks
+ *
+ * @access public
+ * @param array $tasks
+ * @return $this
+ */
+ public function withTasks(array $tasks)
+ {
+ $this->tasks = $tasks;
+ return $this;
+ }
+
+ /**
+ * Set columnId
+ *
+ * @access public
+ * @param integer $columnId
+ * @return $this
+ */
+ public function withColumnId($columnId)
+ {
+ $this->columnId = $columnId;
+ return $this;
+ }
+
+ /**
+ * Set swimlaneId
+ *
+ * @access public
+ * @param integer $swimlaneId
+ * @return $this
+ */
+ public function withSwimlaneId($swimlaneId)
+ {
+ $this->swimlaneId = $swimlaneId;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ $tasks = array_values(array_filter($this->tasks, array($this, 'filterTasks')));
+ array_merge_relation($tasks, $this->tags, 'tags', 'id');
+ return $tasks;
+ }
+
+ /**
+ * Keep only tasks of the given column and swimlane
+ *
+ * @access protected
+ * @param array $task
+ * @return bool
+ */
+ protected function filterTasks(array $task)
+ {
+ return $task['column_id'] == $this->columnId && $task['swimlane_id'] == $this->swimlaneId;
+ }
+}
diff --git a/app/Formatter/TaskAutoCompleteFormatter.php b/app/Formatter/TaskAutoCompleteFormatter.php
index 4f1c4c69..2d9f7341 100644
--- a/app/Formatter/TaskAutoCompleteFormatter.php
+++ b/app/Formatter/TaskAutoCompleteFormatter.php
@@ -3,6 +3,7 @@
namespace Kanboard\Formatter;
use Kanboard\Core\Filter\FormatterInterface;
+use Kanboard\Model\ProjectModel;
use Kanboard\Model\TaskModel;
/**
@@ -21,11 +22,15 @@ class TaskAutoCompleteFormatter extends BaseFormatter implements FormatterInterf
*/
public function format()
{
- $tasks = $this->query->columns(TaskModel::TABLE.'.id', TaskModel::TABLE.'.title')->findAll();
+ $tasks = $this->query->columns(
+ TaskModel::TABLE.'.id',
+ TaskModel::TABLE.'.title',
+ ProjectModel::TABLE.'.name AS project_name'
+ )->asc(TaskModel::TABLE.'.id')->findAll();
foreach ($tasks as &$task) {
$task['value'] = $task['title'];
- $task['label'] = '#'.$task['id'].' - '.$task['title'];
+ $task['label'] = $task['project_name'].' > #'.$task['id'].' '.$task['title'];
}
return $tasks;
diff --git a/app/Formatter/TaskICalFormatter.php b/app/Formatter/TaskICalFormatter.php
index 890674c7..ad2a4449 100644
--- a/app/Formatter/TaskICalFormatter.php
+++ b/app/Formatter/TaskICalFormatter.php
@@ -6,6 +6,7 @@ use DateTime;
use Eluceo\iCal\Component\Calendar;
use Eluceo\iCal\Component\Event;
use Eluceo\iCal\Property\Event\Attendees;
+use Eluceo\iCal\Property\Event\Organizer;
use Kanboard\Core\Filter\FormatterInterface;
/**
@@ -117,16 +118,24 @@ class TaskICalFormatter extends BaseTaskCalendarFormatter implements FormatterIn
$vEvent->setModified($dateModif);
$vEvent->setUseTimezone(true);
$vEvent->setSummary(t('#%d', $task['id']).' '.$task['title']);
+ $vEvent->setDescription($task['description']);
+ $vEvent->setDescriptionHTML($this->helper->text->markdown($task['description']));
$vEvent->setUrl($this->helper->url->base().$this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
if (! empty($task['owner_id'])) {
- $vEvent->setOrganizer($task['assignee_name'] ?: $task['assignee_username'], $task['assignee_email']);
+ $attendees = new Attendees;
+ $attendees->add(
+ 'MAILTO:'.($task['assignee_email'] ?: $task['assignee_username'].'@kanboard.local'),
+ array('CN' => $task['assignee_name'] ?: $task['assignee_username'])
+ );
+ $vEvent->setAttendees($attendees);
}
if (! empty($task['creator_id'])) {
- $attendees = new Attendees;
- $attendees->add('MAILTO:'.($task['creator_email'] ?: $task['creator_username'].'@kanboard.local'));
- $vEvent->setAttendees($attendees);
+ $vEvent->setOrganizer(new Organizer(
+ 'MAILTO:' . $task['creator_email'] ?: $task['creator_username'].'@kanboard.local',
+ array('CN' => $task['creator_name'] ?: $task['creator_username'])
+ ));
}
return $vEvent;
diff --git a/app/Helper/FormHelper.php b/app/Helper/FormHelper.php
index c2ea1d72..0bb94d39 100644
--- a/app/Helper/FormHelper.php
+++ b/app/Helper/FormHelper.php
@@ -307,6 +307,48 @@ class FormHelper extends Base
}
/**
+ * Date field
+ *
+ * @access public
+ * @param string $label
+ * @param string $name
+ * @param array $values
+ * @param array $errors
+ * @param array $attributes
+ * @return string
+ */
+ public function date($label, $name, array $values, array $errors = array(), array $attributes = array())
+ {
+ $userFormat = $this->dateParser->getUserDateFormat();
+ $values = $this->dateParser->format($values, array($name), $userFormat);
+ $attributes = array_merge(array('placeholder="'.date($userFormat).'"'), $attributes);
+
+ return $this->helper->form->label($label, $name) .
+ $this->helper->form->text($name, $values, $errors, $attributes, 'form-date');
+ }
+
+ /**
+ * Datetime field
+ *
+ * @access public
+ * @param string $label
+ * @param string $name
+ * @param array $values
+ * @param array $errors
+ * @param array $attributes
+ * @return string
+ */
+ public function datetime($label, $name, array $values, array $errors = array(), array $attributes = array())
+ {
+ $userFormat = $this->dateParser->getUserDateTimeFormat();
+ $values = $this->dateParser->format($values, array($name), $userFormat);
+ $attributes = array_merge(array('placeholder="'.date($userFormat).'"'), $attributes);
+
+ return $this->helper->form->label($label, $name) .
+ $this->helper->form->text($name, $values, $errors, $attributes, 'form-datetime');
+ }
+
+ /**
* Display the form error class
*
* @access private
diff --git a/app/Helper/HookHelper.php b/app/Helper/HookHelper.php
index 2d13ebcc..24b7d00a 100644
--- a/app/Helper/HookHelper.php
+++ b/app/Helper/HookHelper.php
@@ -2,6 +2,7 @@
namespace Kanboard\Helper;
+use Closure;
use Kanboard\Core\Base;
/**
@@ -24,8 +25,8 @@ class HookHelper extends Base
{
$buffer = '';
- foreach ($this->hook->getListeners($hook) as $file) {
- $buffer .= $this->helper->asset->$type($file);
+ foreach ($this->hook->getListeners($hook) as $params) {
+ $buffer .= $this->helper->asset->$type($params['template']);
}
return $buffer;
@@ -43,8 +44,18 @@ class HookHelper extends Base
{
$buffer = '';
- foreach ($this->hook->getListeners($hook) as $template) {
- $buffer .= $this->template->render($template, $variables);
+ foreach ($this->hook->getListeners($hook) as $params) {
+ if (! empty($params['variables'])) {
+ $variables = array_merge($variables, $params['variables']);
+ } elseif (! empty($params['callable'])) {
+ $result = call_user_func_array($params['callable'], $variables);
+
+ if (is_array($result)) {
+ $variables = array_merge($variables, $result);
+ }
+ }
+
+ $buffer .= $this->template->render($params['template'], $variables);
}
return $buffer;
@@ -54,13 +65,39 @@ class HookHelper extends Base
* Attach a template to a hook
*
* @access public
- * @param string $hook
- * @param string $template
- * @return \Kanboard\Helper\Hook
+ * @param string $hook
+ * @param string $template
+ * @param array $variables
+ * @return $this
+ */
+ public function attach($hook, $template, array $variables = array())
+ {
+ $this->hook->on($hook, array(
+ 'template' => $template,
+ 'variables' => $variables,
+ ));
+
+ return $this;
+ }
+
+ /**
+ * Attach a template to a hook with a callable
+ *
+ * Arguments passed to the callback are the one passed to the hook
+ *
+ * @access public
+ * @param string $hook
+ * @param string $template
+ * @param Closure $callable
+ * @return $this
*/
- public function attach($hook, $template)
+ public function attachCallable($hook, $template, Closure $callable)
{
- $this->hook->on($hook, $template);
+ $this->hook->on($hook, array(
+ 'template' => $template,
+ 'callable' => $callable,
+ ));
+
return $this;
}
}
diff --git a/app/Helper/LayoutHelper.php b/app/Helper/LayoutHelper.php
index 8ebb05d4..8d2e7e00 100644
--- a/app/Helper/LayoutHelper.php
+++ b/app/Helper/LayoutHelper.php
@@ -156,6 +156,10 @@ class LayoutHelper extends Base
*/
public function analytic($template, array $params)
{
+ if (isset($params['project']['name'])) {
+ $params['title'] = $params['project']['name'].' &gt; '.$params['title'];
+ }
+
return $this->subLayout('analytic/layout', 'analytic/sidebar', $template, $params);
}
diff --git a/app/Helper/SubtaskHelper.php b/app/Helper/SubtaskHelper.php
index dac71203..833544a7 100644
--- a/app/Helper/SubtaskHelper.php
+++ b/app/Helper/SubtaskHelper.php
@@ -66,7 +66,10 @@ class SubtaskHelper extends Base
$html = $this->helper->form->label(t('Assignee'), 'user_id');
$html .= $this->helper->form->select('user_id', $users, $values, $errors, $attributes);
- $html .= '&nbsp;<a href="#" class="assign-me" data-target-id="form-user_id" data-current-id="'.$this->userSession->getId().'" title="'.t('Assign to me').'">'.t('Me').'</a>';
+ $html .= '&nbsp;';
+ $html .= '<small>';
+ $html .= '<a href="#" class="assign-me" data-target-id="form-user_id" data-current-id="'.$this->userSession->getId().'" title="'.t('Assign to me').'">'.t('Me').'</a>';
+ $html .= '</small>';
return $html;
}
diff --git a/app/Helper/TaskHelper.php b/app/Helper/TaskHelper.php
index e33438d6..678b4bed 100644
--- a/app/Helper/TaskHelper.php
+++ b/app/Helper/TaskHelper.php
@@ -27,17 +27,74 @@ class TaskHelper extends Base
public function recurrenceTriggers()
{
- return $this->taskModel->getRecurrenceTriggerList();
+ return $this->taskRecurrenceModel->getRecurrenceTriggerList();
}
public function recurrenceTimeframes()
{
- return $this->taskModel->getRecurrenceTimeframeList();
+ return $this->taskRecurrenceModel->getRecurrenceTimeframeList();
}
public function recurrenceBasedates()
{
- return $this->taskModel->getRecurrenceBasedateList();
+ return $this->taskRecurrenceModel->getRecurrenceBasedateList();
+ }
+
+ public function selectTitle(array $values, array $errors)
+ {
+ $html = $this->helper->form->label(t('Title'), 'title');
+ $html .= $this->helper->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="200"', 'tabindex="1"'), 'form-input-large');
+ return $html;
+ }
+
+ public function selectDescription(array $values, array $errors)
+ {
+ $html = $this->helper->form->label(t('Description'), 'description');
+ $html .= '<div class="markdown-editor-container">';
+ $html .= $this->helper->form->textarea(
+ 'description',
+ $values,
+ $errors,
+ array(
+ 'placeholder="'.t('Leave a description').'"',
+ 'tabindex="2"',
+ 'data-mention-search-url="'.$this->helper->url->href('UserAjaxController', 'mention', array('project_id' => $values['project_id'])).'"'
+ ),
+ 'markdown-editor'
+ );
+
+ $html .= '</div>';
+ return $html;
+ }
+
+ public function selectTags(array $project, array $tags = array())
+ {
+ $options = $this->tagModel->getAssignableList($project['id']);
+
+ $html = $this->helper->form->label(t('Tags'), 'tags[]');
+ $html .= '<input type="hidden" name="tags[]" value="">';
+ $html .= '<select name="tags[]" id="form-tags" class="tag-autocomplete" multiple>';
+
+ foreach ($options as $tag) {
+ $html .= sprintf(
+ '<option value="%s" %s>%s</option>',
+ $this->helper->text->e($tag),
+ in_array($tag, $tags) ? 'selected="selected"' : '',
+ $this->helper->text->e($tag)
+ );
+ }
+
+ $html .= '</select>';
+
+ return $html;
+ }
+
+ public function selectColor(array $values)
+ {
+ $colors = $this->colorModel->getList();
+ $html = $this->helper->form->label(t('Color'), 'color_id');
+ $html .= $this->helper->form->select('color_id', $colors, $values, array(), array(), 'color-picker');
+ return $html;
}
public function selectAssignee(array $users, array $values, array $errors = array(), array $attributes = array())
@@ -46,7 +103,10 @@ class TaskHelper extends Base
$html = $this->helper->form->label(t('Assignee'), 'owner_id');
$html .= $this->helper->form->select('owner_id', $users, $values, $errors, $attributes);
- $html .= '&nbsp;<a href="#" class="assign-me" data-target-id="form-owner_id" data-current-id="'.$this->userSession->getId().'" title="'.t('Assign to me').'">'.t('Me').'</a>';
+ $html .= '&nbsp;';
+ $html .= '<small>';
+ $html .= '<a href="#" class="assign-me" data-target-id="form-owner_id" data-current-id="'.$this->userSession->getId().'" title="'.t('Assign to me').'">'.t('Me').'</a>';
+ $html .= '</small>';
return $html;
}
@@ -91,7 +151,7 @@ class TaskHelper extends Base
{
$html = '';
- if ($project['priority_end'] > $project['priority_start']) {
+ if ($project['priority_end'] != $project['priority_start']) {
$range = range($project['priority_start'], $project['priority_end']);
$options = array_combine($range, $range);
$values += array('priority' => $project['priority_default']);
@@ -113,10 +173,20 @@ class TaskHelper extends Base
return $html;
}
- public function selectTimeEstimated(array $values, array $errors = array(), array $attributes = array())
+ public function selectReference(array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="9"'), $attributes);
+ $html = $this->helper->form->label(t('Reference'), 'reference');
+ $html .= $this->helper->form->text('reference', $values, $errors, $attributes, 'form-input-small');
+
+ return $html;
+ }
+
+ public function selectTimeEstimated(array $values, array $errors = array(), array $attributes = array())
+ {
+ $attributes = array_merge(array('tabindex="10"'), $attributes);
+
$html = $this->helper->form->label(t('Original estimate'), 'time_estimated');
$html .= $this->helper->form->numeric('time_estimated', $values, $errors, $attributes);
$html .= ' '.t('hours');
@@ -126,7 +196,7 @@ class TaskHelper extends Base
public function selectTimeSpent(array $values, array $errors = array(), array $attributes = array())
{
- $attributes = array_merge(array('tabindex="10"'), $attributes);
+ $attributes = array_merge(array('tabindex="11"'), $attributes);
$html = $this->helper->form->label(t('Time spent'), 'time_spent');
$html .= $this->helper->form->numeric('time_spent', $values, $errors, $attributes);
@@ -137,31 +207,21 @@ class TaskHelper extends Base
public function selectStartDate(array $values, array $errors = array(), array $attributes = array())
{
- $placeholder = date($this->configModel->get('application_date_format', 'm/d/Y H:i'));
- $attributes = array_merge(array('tabindex="11"', 'placeholder="'.$placeholder.'"'), $attributes);
-
- $html = $this->helper->form->label(t('Start Date'), 'date_started');
- $html .= $this->helper->form->text('date_started', $values, $errors, $attributes, 'form-datetime');
-
- return $html;
+ $attributes = array_merge(array('tabindex="12"'), $attributes);
+ return $this->helper->form->datetime(t('Start Date'), 'date_started', $values, $errors, $attributes);
}
public function selectDueDate(array $values, array $errors = array(), array $attributes = array())
{
- $placeholder = date($this->configModel->get('application_date_format', 'm/d/Y'));
- $attributes = array_merge(array('tabindex="12"', 'placeholder="'.$placeholder.'"'), $attributes);
-
- $html = $this->helper->form->label(t('Due Date'), 'date_due');
- $html .= $this->helper->form->text('date_due', $values, $errors, $attributes, 'form-date');
-
- return $html;
+ $attributes = array_merge(array('tabindex="13"'), $attributes);
+ return $this->helper->form->date(t('Due Date'), 'date_due', $values, $errors, $attributes);
}
public function formatPriority(array $project, array $task)
{
$html = '';
- if ($project['priority_end'] > $project['priority_start']) {
+ if ($project['priority_end'] != $project['priority_start']) {
$html .= '<span class="task-board-priority" title="'.t('Task priority').'">';
$html .= $task['priority'] >= 0 ? 'P'.$task['priority'] : '-P'.abs($task['priority']);
$html .= '</span>';
diff --git a/app/Helper/UserHelper.php b/app/Helper/UserHelper.php
index ae3efe1d..e42bafe4 100644
--- a/app/Helper/UserHelper.php
+++ b/app/Helper/UserHelper.php
@@ -50,7 +50,8 @@ class UserHelper extends Base
*/
public function getFullname(array $user = array())
{
- return $this->userModel->getFullname(empty($user) ? $this->userSession->getAll() : $user);
+ $user = empty($user) ? $this->userSession->getAll() : $user;
+ return $user['name'] ?: $user['username'];
}
/**
@@ -107,6 +108,10 @@ class UserHelper extends Base
*/
public function hasAccess($controller, $action)
{
+ if (! $this->userSession->isLogged()) {
+ return false;
+ }
+
$key = 'app_access:'.$controller.$action;
$result = $this->memoryCache->get($key);
@@ -128,6 +133,10 @@ class UserHelper extends Base
*/
public function hasProjectAccess($controller, $action, $project_id)
{
+ if (! $this->userSession->isLogged()) {
+ return false;
+ }
+
if ($this->userSession->isAdmin()) {
return true;
}
diff --git a/app/Job/CommentEventJob.php b/app/Job/CommentEventJob.php
new file mode 100644
index 00000000..47cf8020
--- /dev/null
+++ b/app/Job/CommentEventJob.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\EventBuilder\CommentEventBuilder;
+use Kanboard\Model\CommentModel;
+
+/**
+ * Class CommentEventJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class CommentEventJob extends BaseJob
+{
+ /**
+ * Set job params
+ *
+ * @param int $commentId
+ * @param string $eventName
+ * @return $this
+ */
+ public function withParams($commentId, $eventName)
+ {
+ $this->jobParams = array($commentId, $eventName);
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param int $commentId
+ * @param string $eventName
+ * @return $this
+ */
+ public function execute($commentId, $eventName)
+ {
+ $event = CommentEventBuilder::getInstance($this->container)
+ ->withCommentId($commentId)
+ ->buildEvent();
+
+ if ($event !== null) {
+ $this->dispatcher->dispatch($eventName, $event);
+
+ if ($eventName === CommentModel::EVENT_CREATE) {
+ $this->userMentionModel->fireEvents($event['comment']['comment'], CommentModel::EVENT_USER_MENTION, $event);
+ }
+ }
+ }
+}
diff --git a/app/Job/NotificationJob.php b/app/Job/NotificationJob.php
index 904a9273..8fb260e8 100644
--- a/app/Job/NotificationJob.php
+++ b/app/Job/NotificationJob.php
@@ -17,69 +17,27 @@ class NotificationJob extends BaseJob
*
* @param GenericEvent $event
* @param string $eventName
- * @param string $eventObjectName
* @return $this
*/
- public function withParams(GenericEvent $event, $eventName, $eventObjectName)
+ public function withParams(GenericEvent $event, $eventName)
{
- $this->jobParams = array($event->getAll(), $eventName, $eventObjectName);
+ $this->jobParams = array($event->getAll(), $eventName);
return $this;
}
/**
* Execute job
*
- * @param array $event
+ * @param array $eventData
* @param string $eventName
- * @param string $eventObjectName
*/
- public function execute(array $event, $eventName, $eventObjectName)
+ public function execute(array $eventData, $eventName)
{
- $eventData = $this->getEventData($event, $eventObjectName);
-
- if (! empty($eventData)) {
- if (! empty($event['mention'])) {
- $this->userNotificationModel->sendUserNotification($event['mention'], $eventName, $eventData);
- } else {
- $this->userNotificationModel->sendNotifications($eventName, $eventData);
- $this->projectNotificationModel->sendNotifications($eventData['task']['project_id'], $eventName, $eventData);
- }
- }
- }
-
- /**
- * Get event data
- *
- * @param array $event
- * @param string $eventObjectName
- * @return array
- */
- public function getEventData(array $event, $eventObjectName)
- {
- $values = array();
-
- if (! empty($event['changes'])) {
- $values['changes'] = $event['changes'];
+ if (! empty($eventData['mention'])) {
+ $this->userNotificationModel->sendUserNotification($eventData['mention'], $eventName, $eventData);
+ } else {
+ $this->userNotificationModel->sendNotifications($eventName, $eventData);
+ $this->projectNotificationModel->sendNotifications($eventData['task']['project_id'], $eventName, $eventData);
}
-
- switch ($eventObjectName) {
- case 'Kanboard\Event\TaskEvent':
- $values['task'] = $this->taskFinderModel->getDetails($event['task_id']);
- break;
- case 'Kanboard\Event\SubtaskEvent':
- $values['subtask'] = $this->subtaskModel->getById($event['id'], true);
- $values['task'] = $this->taskFinderModel->getDetails($values['subtask']['task_id']);
- break;
- case 'Kanboard\Event\FileEvent':
- $values['file'] = $event;
- $values['task'] = $this->taskFinderModel->getDetails($values['file']['task_id']);
- break;
- case 'Kanboard\Event\CommentEvent':
- $values['comment'] = $this->commentModel->getById($event['id']);
- $values['task'] = $this->taskFinderModel->getDetails($values['comment']['task_id']);
- break;
- }
-
- return $values;
}
}
diff --git a/app/Job/ProjectFileEventJob.php b/app/Job/ProjectFileEventJob.php
new file mode 100644
index 00000000..45e6ece3
--- /dev/null
+++ b/app/Job/ProjectFileEventJob.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\EventBuilder\ProjectFileEventBuilder;
+
+/**
+ * Class ProjectFileEventJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class ProjectFileEventJob extends BaseJob
+{
+ /**
+ * Set job params
+ *
+ * @param int $fileId
+ * @param string $eventName
+ * @return $this
+ */
+ public function withParams($fileId, $eventName)
+ {
+ $this->jobParams = array($fileId, $eventName);
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param int $fileId
+ * @param string $eventName
+ * @return $this
+ */
+ public function execute($fileId, $eventName)
+ {
+ $event = ProjectFileEventBuilder::getInstance($this->container)
+ ->withFileId($fileId)
+ ->buildEvent();
+
+ if ($event !== null) {
+ $this->dispatcher->dispatch($eventName, $event);
+ }
+ }
+}
diff --git a/app/Job/SubtaskEventJob.php b/app/Job/SubtaskEventJob.php
new file mode 100644
index 00000000..85c4d73e
--- /dev/null
+++ b/app/Job/SubtaskEventJob.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\EventBuilder\SubtaskEventBuilder;
+
+/**
+ * Class SubtaskEventJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class SubtaskEventJob extends BaseJob
+{
+ /**
+ * Set job params
+ *
+ * @param int $subtaskId
+ * @param string $eventName
+ * @param array $values
+ * @return $this
+ */
+ public function withParams($subtaskId, $eventName, array $values = array())
+ {
+ $this->jobParams = array($subtaskId, $eventName, $values);
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param int $subtaskId
+ * @param string $eventName
+ * @param array $values
+ * @return $this
+ */
+ public function execute($subtaskId, $eventName, array $values = array())
+ {
+ $event = SubtaskEventBuilder::getInstance($this->container)
+ ->withSubtaskId($subtaskId)
+ ->withValues($values)
+ ->buildEvent();
+
+ if ($event !== null) {
+ $this->dispatcher->dispatch($eventName, $event);
+ }
+ }
+}
diff --git a/app/Job/TaskEventJob.php b/app/Job/TaskEventJob.php
new file mode 100644
index 00000000..7d026a68
--- /dev/null
+++ b/app/Job/TaskEventJob.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\Event\TaskEvent;
+use Kanboard\EventBuilder\TaskEventBuilder;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskEventJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class TaskEventJob extends BaseJob
+{
+ /**
+ * Set job params
+ *
+ * @param int $taskId
+ * @param array $eventNames
+ * @param array $changes
+ * @param array $values
+ * @param array $task
+ * @return $this
+ */
+ public function withParams($taskId, array $eventNames, array $changes = array(), array $values = array(), array $task = array())
+ {
+ $this->jobParams = array($taskId, $eventNames, $changes, $values, $task);
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param int $taskId
+ * @param array $eventNames
+ * @param array $changes
+ * @param array $values
+ * @param array $task
+ * @return $this
+ */
+ public function execute($taskId, array $eventNames, array $changes = array(), array $values = array(), array $task = array())
+ {
+ $event = TaskEventBuilder::getInstance($this->container)
+ ->withTaskId($taskId)
+ ->withChanges($changes)
+ ->withValues($values)
+ ->withTask($task)
+ ->buildEvent();
+
+ if ($event !== null) {
+ foreach ($eventNames as $eventName) {
+ $this->fireEvent($eventName, $event);
+ }
+ }
+ }
+
+ /**
+ * Trigger event
+ *
+ * @access protected
+ * @param string $eventName
+ * @param TaskEvent $event
+ */
+ protected function fireEvent($eventName, TaskEvent $event)
+ {
+ $this->logger->debug(__METHOD__.' Event fired: '.$eventName);
+ $this->dispatcher->dispatch($eventName, $event);
+
+ if ($eventName === TaskModel::EVENT_CREATE) {
+ $this->userMentionModel->fireEvents($event['task']['description'], TaskModel::EVENT_USER_MENTION, $event);
+ }
+ }
+}
diff --git a/app/Job/TaskFileEventJob.php b/app/Job/TaskFileEventJob.php
new file mode 100644
index 00000000..293dbf27
--- /dev/null
+++ b/app/Job/TaskFileEventJob.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\EventBuilder\TaskFileEventBuilder;
+
+/**
+ * Class TaskFileEventJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class TaskFileEventJob extends BaseJob
+{
+ /**
+ * Set job params
+ *
+ * @param int $fileId
+ * @param string $eventName
+ * @return $this
+ */
+ public function withParams($fileId, $eventName)
+ {
+ $this->jobParams = array($fileId, $eventName);
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param int $fileId
+ * @param string $eventName
+ * @return $this
+ */
+ public function execute($fileId, $eventName)
+ {
+ $event = TaskFileEventBuilder::getInstance($this->container)
+ ->withFileId($fileId)
+ ->buildEvent();
+
+ if ($event !== null) {
+ $this->dispatcher->dispatch($eventName, $event);
+ }
+ }
+}
diff --git a/app/Job/TaskLinkEventJob.php b/app/Job/TaskLinkEventJob.php
new file mode 100644
index 00000000..31f62f07
--- /dev/null
+++ b/app/Job/TaskLinkEventJob.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\EventBuilder\TaskLinkEventBuilder;
+
+/**
+ * Class TaskLinkEventJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class TaskLinkEventJob extends BaseJob
+{
+ /**
+ * Set job params
+ *
+ * @param int $taskLinkId
+ * @param string $eventName
+ * @return $this
+ */
+ public function withParams($taskLinkId, $eventName)
+ {
+ $this->jobParams = array($taskLinkId, $eventName);
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param int $taskLinkId
+ * @param string $eventName
+ * @return $this
+ */
+ public function execute($taskLinkId, $eventName)
+ {
+ $event = TaskLinkEventBuilder::getInstance($this->container)
+ ->withTaskLinkId($taskLinkId)
+ ->buildEvent();
+
+ if ($event !== null) {
+ $this->dispatcher->dispatch($eventName, $event);
+ }
+ }
+}
diff --git a/app/Locale/bs_BA/translations.php b/app/Locale/bs_BA/translations.php
index b6b0f534..898e4d66 100644
--- a/app/Locale/bs_BA/translations.php
+++ b/app/Locale/bs_BA/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d zatvorenih zadataka',
'No task for this project' => 'Nema dodijeljenih zadataka ovom projektu',
'Public link' => 'Javni link',
- 'Change assignee' => 'Promjena izvršioca',
- 'Change assignee for the task "%s"' => 'Promjena izvršioca za zadatak "%s"',
'Timezone' => 'Vremenska zona',
'Sorry, I didn\'t find this information in my database!' => 'Na žalost, nije pronađena informacija u bazi',
'Page not found' => 'Strana nije pronađena',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategorija',
'Category:' => 'Kategorija:',
'Categories' => 'Kategorije',
- 'Category not found.' => 'Kategorija nije pronađena',
'Your category have been created successfully.' => 'Uspješno kreirana kategorija.',
'Unable to create your category.' => 'Nije moguće kreirati kategoriju.',
'Your category have been updated successfully.' => 'Kategorija je uspješno ažurirana',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Da li da uklonim fajl: "%s"?',
'Attachments' => 'Prilozi',
'Edit the task' => 'Uredi zadatak',
- 'Edit the description' => 'Uredi opis zadatka',
'Add a comment' => 'Dodaj komentar',
'Edit a comment' => 'Izmijeni komentar',
'Summary' => 'Pregled',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Prikaži drugi projekat',
'Created by %s' => 'Kreirao %s',
'Tasks Export' => 'Izvoz zadataka',
- 'Tasks exportation for "%s"' => 'Izvoz zadataka za "%s"',
'Start Date' => 'Početni datum',
'End Date' => 'Datum završetka',
'Execute' => 'Izvrši',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Novi pod-zadatak',
'New attachment added "%s"' => 'Ubačen novi prilog "%s"',
'New comment posted by %s' => '%s ostavio novi komentar',
- 'New attachment' => 'Novi prilog',
'New comment' => 'Novi komentar',
'Comment updated' => 'Komentar ažuriran',
'New subtask' => 'Novi pod-zadatak',
- 'Subtask updated' => 'Pod-zadatak ažuriran',
- 'Task updated' => 'Zadatak ažuriran',
- 'Task closed' => 'Zadatak je zatvoren',
- 'Task opened' => 'Zadatak je otvoren',
'I want to receive notifications only for those projects:' => 'Želim obavještenja samo za ove projekte:',
'view the task on Kanboard' => 'Pregledaj zadatke',
'Public access' => 'Javni pristup',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Bez omogućenih vanjskih autentikacija.',
'Password modified successfully.' => 'Uspješna izmjena šifre.',
'Unable to change the password.' => 'Nije moguće izmijeniti šifru.',
- 'Change category for the task "%s"' => 'Izijmeni kategoriju zadatka "%s"',
'Change category' => 'Izmijeni kategoriju',
'%s updated the task %s' => '%s izmijenio zadatak %s',
'%s opened the task %s' => '%s otvorio zadatak %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s ažurirao zadatak #%d',
'%s created the task #%d' => '%s kreirao zadatak #%d',
'%s closed the task #%d' => '%s zatvorio zadatak #%d',
- '%s open the task #%d' => '%s otvorio zadatak #%d',
- '%s moved the task #%d to the column "%s"' => '%s premjestio zadatak #%d u kolonu "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s premjestio zadatak #%d na poziciju %d u koloni "%s"',
+ '%s opened the task #%d' => '%s otvorio zadatak #%d',
'Activity' => 'Aktivnosti',
'Default values are "%s"' => 'Podrazumijevane vrijednosti su: "%s"',
'Default columns for new projects (Comma-separated)' => 'Podrazumijevane kolone za novi projekat (Odvojene zarezom)',
'Task assignee change' => 'Promijena izvršioca zadatka',
- '%s change the assignee of the task #%d to %s' => '%s zamijeni izvršioca za zadatak #%d u %s',
+ '%s changed the assignee of the task #%d to %s' => '%s zamijeni izvršioca za zadatak #%d u %s',
'%s changed the assignee of the task %s to %s' => '%s promijenio izvršioca za zadatak %s u %s',
'New password for the user "%s"' => 'Nova šifra korisnika "%s"',
'Choose an event' => 'Izaberi događaj',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Procenat',
'Number of tasks' => 'Broj zadataka',
'Task distribution' => 'Podjela zadataka',
- 'Reportings' => 'Izveštaji',
- 'Task repartition for "%s"' => 'Zaduženja zadataka za "%s"',
'Analytics' => 'Analiza',
'Subtask' => 'Pod-zadatak',
'My subtasks' => 'Moji pod-zadaci',
'User repartition' => 'Zaduženja korisnika',
- 'User repartition for "%s"' => 'Zaduženja korisnika za "%s"',
'Clone this project' => 'Kloniraj ovaj projekat',
'Column removed successfully.' => 'Kolona uspješno uklonjena.',
'Not enough data to show the graph.' => 'Nedovoljno podataka za prikaz na grafikonu.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Vrijednost mora biti broj',
'Unable to create this task.' => 'Nije moguće kreirati zadatak.',
'Cumulative flow diagram' => 'Zbirni dijagram toka',
- 'Cumulative flow diagram for "%s"' => 'Zbirni dijagram toka za "%s"',
'Daily project summary' => 'Zbirni pregled po danima',
'Daily project summary export' => 'Izvoz zbirnog pregleda po danima',
- 'Daily project summary export for "%s"' => 'Izvoz zbirnog pregleda po danima za "%s"',
'Exports' => 'Izvozi',
'This export contains the number of tasks per column grouped per day.' => 'Ovaj izvoz sadržava broj zadataka po koloni grupisanih po danima.',
'Active swimlanes' => 'Aktivne swimline trake',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Ukloni swimline traku',
'Show default swimlane' => 'Prikaži podrazumijevanu swimline traku',
'Swimlane modification for the project "%s"' => 'Izmjene swimline trake za projekat "%s"',
- 'Swimlane not found.' => 'Swimline traka nije pronađena.',
'Swimlane removed successfully.' => 'Swimline traka uspješno uklonjena.',
'Swimlanes' => 'Swimline trake',
'Swimlane updated successfully.' => 'Swimline traka uspjeno ažurirana.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'ID pod-zadatka',
'Subtasks' => 'Pod-zadaci',
'Subtasks Export' => 'Izvoz pod-zadataka',
- 'Subtasks exportation for "%s"' => 'Izvoz pod-zadataka za "%s"',
'Task Title' => 'Naslov zadatka',
'Untitled' => 'Bez naslova',
'Application default' => 'Podrazumijevano od aplikacije',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Stopa valute je uspješno dodana.',
'Unable to add this currency rate.' => 'Nemoguće dodati stopu valute.',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s je uklonio izvršioca zadatka %s',
+ '%s removed the assignee of the task %s' => '%s je uklonio izvršioca zadatka %s',
'Enable Gravatar images' => 'Omogući Gravatar slike',
'Information' => 'Informacije',
'Check two factor authentication code' => 'Provjera faktor-dva autentifikacionog koda',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Testiraj svoj uređaj',
'Assign a color when the task is moved to a specific column' => 'Dodijeli boju kada je zadatak pomjeren u odabranu kolonu',
'%s via Kanboard' => '%s uz pomoć Kanboard-a',
- 'Burndown chart for "%s"' => 'Grafikon izgaranja za "%s"',
'Burndown chart' => 'Grafikon izgaranja',
'This chart show the task complexity over the time (Work Remaining).' => 'Ovaj grafikon pokazuje kompleksnost zadatka u vremenu (Preostalo vremena)',
'Screenshot taken %s' => 'Slika ekrana uzeta %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Pomjeri zadatak u drugu kolonu kada je kategorija promijenjena',
'Send a task by email to someone' => 'Pošalji zadatak nekome emailom',
'Reopen a task' => 'Ponovo otvori zadatak',
- 'Column change' => 'Promijena kolone',
- 'Position change' => 'Promjena pozicije',
- 'Swimlane change' => 'Promjena swimline trake',
- 'Assignee change' => 'Promijenjen izvršilac',
- '[%s] Overdue tasks' => '[%s] Zaostali zadaci',
'Notification' => 'Obavještenja',
'%s moved the task #%d to the first swimlane' => '%s je premjestio zadatak #%d u prvu swimline traku',
- '%s moved the task #%d to the swimlane "%s"' => '%s je premjestio zadatak #%d u swimline traku "%s"',
'Swimlane' => 'Swimline traka',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s je premjestio zadatak %s u prvi swimline traku',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Pretraga po kategoriji: ',
'Search by description: ' => 'Pretraga po opisu: ',
'Search by due date: ' => 'Pretraga po datumu završetka: ',
- 'Lead and Cycle time for "%s"' => 'Vrijeme upravljanje i vremenski ciklus za "%s"',
- 'Average time spent into each column for "%s"' => 'Prosjek utrošenog vremena u svakoj koloni za "%s"',
'Average time spent into each column' => 'Prosjek utrošenog vrmena u svakoj koloni',
'Average time spent' => 'Prosjek utrošenog vremena',
'This chart show the average time spent into each column for the last %d tasks.' => 'Ovaj grafik pokazuje prosjek utrošenog vremena u svakoj koloni za posljednjih %d zadataka.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Licenca:',
'License' => 'Licenca',
'Enter the text below' => 'Unesi tekst ispod',
- 'Gantt chart for %s' => 'Gantogram za %s',
'Sort by position' => 'Sortiraj po poziciji',
'Sort by date' => 'Sortiraj po datumu',
'Add task' => 'Dodaj zadatak',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Verzija',
'Plugins' => 'Dodaci',
'There is no plugin loaded.' => 'Nema učitanih dodataka.',
- 'Set maximum column height' => 'Postavi maksimalnu visinu kolone',
- 'Remove maximum column height' => 'Ukloni maksimalnu visinu kolone',
'My notifications' => 'Moja obavještenja',
'Custom filters' => 'Prilagođeni filteri',
'Your custom filter have been created successfully.' => 'Tvoj prilagođeni filter je uspješno napravljen.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Pogrešna captcha',
'The name must be unique' => 'Ime mora biti jedinstveno',
'View all groups' => 'Pregledaj sve grupe',
- 'View group members' => 'Pregledaj članove grupe',
'There is no user available.' => 'Trenutno nema dostupnih korisnika.',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Da li zaista želiš ukloniti korisnika "%s" iz grupe "%s"?',
'There is no group.' => 'Trenutno nema grupa.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Unesi ime grupe...',
'Role:' => 'Uloga:',
'Project members' => 'Članovi projekta',
- 'Compare hours for "%s"' => 'Poredi sate za "%s"',
'%s mentioned you in the task #%d' => '%s te spomenuo u zadatku #%d',
'%s mentioned you in a comment on the task #%d' => '%s te spomenuo u komentaru zadatka #%d',
'You were mentioned in the task #%d' => 'Spomenut si u zadatku #%d',
'You were mentioned in a comment on the task #%d' => 'Spomenut si u komentaru zadatka #%d',
- 'Mentioned' => 'Spominjanja',
- 'Compare Estimated Time vs Actual Time' => 'Poređenje očekivanog i aktuelnog vremena',
'Estimated hours: ' => 'Očekivani sati:',
'Actual hours: ' => 'Aktuelni sati:',
'Hours Spent' => 'Utrošeni sati:',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php
index f24c79ab..565f92f5 100644
--- a/app/Locale/cs_CZ/translations.php
+++ b/app/Locale/cs_CZ/translations.php
@@ -100,7 +100,7 @@ return array(
'There is nobody assigned' => 'Není přiřazeno žádnému uživateli',
'Column on the board:' => 'Sloupec:',
'Close this task' => 'Uzavřít úkol',
- 'Open this task' => 'Aufgabe wieder öffnen',
+ 'Open this task' => 'Otevřít tento úkol',
'There is no description.' => 'Bez popisu',
'Add a new task' => 'Přidat nový úkol',
'The username is required' => 'Uživatelské jméno je vyžadováno',
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d dokončených úkolů',
'No task for this project' => 'Tento projekt nemá žádné úkoly',
'Public link' => 'Veřejný odkaz',
- 'Change assignee' => 'Změna přiřazení k uživatelům',
- 'Change assignee for the task "%s"' => 'Změna přiřazení uživatele pro úkol "%s"',
'Timezone' => 'Časová zóna',
'Sorry, I didn\'t find this information in my database!' => 'Omlouváme se, tuto informaci nelze nalézt!',
'Page not found' => 'Stránka nenalezena',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategorie',
'Category:' => 'Kategorie:',
'Categories' => 'Kategorie',
- 'Category not found.' => 'Kategorie není nalezena.',
'Your category have been created successfully.' => 'Kategorie byla úspěšně vytvořena.',
'Unable to create your category.' => 'Kategorii nelze vytvořit.',
'Your category have been updated successfully.' => 'Kategorie byla úspěšně aktualizována.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Skutečně chcete odebrat soubor: "%s"?',
'Attachments' => 'Přílohy',
'Edit the task' => 'Upravit úkol',
- 'Edit the description' => 'Upravit popis',
'Add a comment' => 'Přidat komentář',
'Edit a comment' => 'Upravit komentář',
'Summary' => 'Souhrn',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Zobrazit jiný projekt',
'Created by %s' => 'Vytvořeno uživatelem %s',
'Tasks Export' => 'Export úkolů',
- 'Tasks exportation for "%s"' => 'Export úkolů pro "%s"',
'Start Date' => 'Počáteční datum',
'End Date' => 'Konečné datum',
'Execute' => 'Spustit',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nový dílčí úkol',
'New attachment added "%s"' => 'Byla přidána nová příloha "%s".',
'New comment posted by %s' => 'Nový komentář publikovaný uživatelem %s',
- 'New attachment' => 'Nová příloha',
'New comment' => 'Nový komentář',
'Comment updated' => 'Komentář byl aktualizován.',
'New subtask' => 'Nový dílčí úkol',
- 'Subtask updated' => 'Dílčí úkol byl aktualizován',
- 'Task updated' => 'Úkol byl aktualizován',
- 'Task closed' => 'Úkol byl uzavřen',
- 'Task opened' => 'Úkol byl otevřen',
'I want to receive notifications only for those projects:' => 'Přeji si dostávat upozornění pouze pro následující projekty:',
'view the task on Kanboard' => 'Zobrazit úkol na Kanboard',
'Public access' => 'Veřejný přístup',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Není povolena žádná vzdálená autorizace.',
'Password modified successfully.' => 'Heslo bylo úspěšně změněno.',
'Unable to change the password.' => 'Nelze změnit heslo.',
- 'Change category for the task "%s"' => 'Změna kategorie pro úkol "%s" ',
'Change category' => 'Změna kategorie',
'%s updated the task %s' => '%s aktualizoval úkol %s ',
'%s opened the task %s' => '%s znovu otevřel úkol %s ',
@@ -391,15 +380,13 @@ return array(
'%s updated the task #%d' => '%s aktualizoval úkol #%d ',
'%s created the task #%d' => '%s vytvořil úkol #%d ',
'%s closed the task #%d' => '%s uzavřel úkol #%d ',
- '%s open the task #%d' => '%s znovu otevřel úkol #%d ',
- '%s moved the task #%d to the column "%s"' => '%s přesunul úkol #%d do sloupce "%s" ',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s přesunul úkol #%d na pozici %d ve sloupci "%s" ',
+ '%s opened the task #%d' => '%s znovu otevřel úkol #%d ',
'Activity' => 'Aktivity',
'Default values are "%s"' => 'Standardní hodnoty jsou: "%s"',
'Default columns for new projects (Comma-separated)' => 'Výchozí sloupce pro nové projekty (odděleny čárkou)',
'Task assignee change' => 'Změna přiřazení uživatelů',
- '%s change the assignee of the task #%d to %s' => '%s hat die Zusständigkeit der Aufgabe #%d geändert um %s',
- '%s changed the assignee of the task %s to %s' => '%s změnil řešitele úkolu %s na uživatele %s',
+ '%s changed the assignee of the task #%d to %s' => '%s změnil přidělení úkolu #%d na uživatele %s',
+ '%s changed the assignee of the task %s to %s' => '%s změnil přidělení úkolu %s na uživatele %s',
'New password for the user "%s"' => 'Nové heslo pro uživatele "%s"',
'Choose an event' => 'Vybrat událost',
'Create a task from an external provider' => 'Vytvořit úkol externím poskytovatelem',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Procenta',
'Number of tasks' => 'Počet úkolů',
'Task distribution' => 'Rozdělení úkolů',
- 'Reportings' => 'Reporty',
- 'Task repartition for "%s"' => 'Rozdělení úkolů pro "%s"',
'Analytics' => 'Analýza',
'Subtask' => 'Dílčí úkoly',
'My subtasks' => 'Moje dílčí úkoly',
'User repartition' => 'Rozdělení podle uživatelů',
- 'User repartition for "%s"' => 'Rozdělení podle uživatelů pro "%s"',
'Clone this project' => 'Duplokovat projekt',
'Column removed successfully.' => 'Sloupec byl odstraněn.',
'Not enough data to show the graph.' => 'Pro zobrazení grafu není dostatek dat.',
@@ -462,45 +446,41 @@ return array(
'The project id must be an integer' => 'ID projektu musí být celé číslo',
'The status must be an integer' => 'Status musí být celé číslo',
'The subtask id is required' => 'Je požadováno id dílčího úkolu',
- 'The subtask id must be an integer' => 'Die Teilaufgabenid muss eine ganze Zahl sein',
- 'The task id is required' => 'Die Aufgabenid ist benötigt',
- 'The task id must be an integer' => 'Die Aufgabenid muss eine ganze Zahl sein',
- 'The user id must be an integer' => 'Die Userid muss eine ganze Zahl sein',
- 'This value is required' => 'Dieser Wert ist erforderlich',
- 'This value must be numeric' => 'Dieser Wert muss numerisch sein',
- 'Unable to create this task.' => 'Diese Aufgabe kann nicht erstellt werden',
+ 'The subtask id must be an integer' => 'ID dílčího úkolu musí být číslo',
+ 'The task id is required' => 'ID úkolu je povinné',
+ 'The task id must be an integer' => 'ID úkolu musí být číslo',
+ 'The user id must be an integer' => 'ID uživatele musí být číslo',
+ 'This value is required' => 'Hodnota je povinná',
+ 'This value must be numeric' => 'Hodnota musí být číselná',
+ 'Unable to create this task.' => 'Nelze vytvořit tento úkol',
'Cumulative flow diagram' => 'Kumulativní diagram',
- 'Cumulative flow diagram for "%s"' => 'Kumulativní diagram pro "%s"',
'Daily project summary' => 'Denní přehledy',
'Daily project summary export' => 'Export denních přehledů',
- 'Daily project summary export for "%s"' => 'Export denních přehledů pro "%s"',
'Exports' => 'Exporty',
'This export contains the number of tasks per column grouped per day.' => 'Tento export obsahuje počet úkolů pro jednotlivé sloupce seskupených podle dní.',
- 'Active swimlanes' => 'Aktive Swimlane',
- 'Add a new swimlane' => 'Přidat nový řádek',
- 'Change default swimlane' => 'Standard Swimlane ändern',
- 'Default swimlane' => 'Výchozí Swimlane',
- 'Do you really want to remove this swimlane: "%s"?' => 'Diese Swimlane wirklich ändern: "%s"?',
- 'Inactive swimlanes' => 'Inaktive Swimlane',
- 'Remove a swimlane' => 'Odstranit swimlane',
- 'Show default swimlane' => 'Standard Swimlane anzeigen',
- 'Swimlane modification for the project "%s"' => 'Swimlane Änderung für das Projekt "%s"',
- 'Swimlane not found.' => 'Swimlane nicht gefunden',
- 'Swimlane removed successfully.' => 'Swimlane erfolgreich entfernt.',
- 'Swimlanes' => 'Swimlanes',
- 'Swimlane updated successfully.' => 'Swimlane erfolgreich geändert.',
- 'The default swimlane have been updated successfully.' => 'Die standard Swimlane wurden erfolgreich aktualisiert. Die standard Swimlane wurden erfolgreich aktualisiert.',
- 'Unable to remove this swimlane.' => 'Es ist nicht möglich die Swimlane zu entfernen.',
- 'Unable to update this swimlane.' => 'Es ist nicht möglich die Swimöane zu ändern.',
- 'Your swimlane have been created successfully.' => 'Die Swimlane wurde erfolgreich angelegt.',
- 'Example: "Bug, Feature Request, Improvement"' => 'Beispiel: "Bug, Funktionswünsche, Verbesserung"',
+ 'Active swimlanes' => 'Aktivní dráhy',
+ 'Add a new swimlane' => 'Přidat novou dráhu',
+ 'Change default swimlane' => 'Změnit výchozí dráhu',
+ 'Default swimlane' => 'Výchozí dráha',
+ 'Do you really want to remove this swimlane: "%s"?' => 'Opravdu si přejete odstranit tuto dráhu: "%s"?',
+ 'Inactive swimlanes' => 'Neaktivní dráha',
+ 'Remove a swimlane' => 'Odstranit dráhu',
+ 'Show default swimlane' => 'Zobrazit výchozí dráhu',
+ 'Swimlane modification for the project "%s"' => 'Změny dráhy pro projekt "%s"',
+ 'Swimlane removed successfully.' => 'Dráha byla odstraněna.',
+ 'Swimlanes' => 'Dráhy',
+ 'Swimlane updated successfully.' => 'Dráha byla upravena.',
+ 'The default swimlane have been updated successfully.' => 'Výchozí dráha byla upravena',
+ 'Unable to remove this swimlane.' => 'Tuto dráhu nelze odstranit.',
+ 'Unable to update this swimlane.' => 'Tuto dráhu nelze upravit.',
+ 'Your swimlane have been created successfully.' => 'Dráha byla vytvořena.',
+ 'Example: "Bug, Feature Request, Improvement"' => 'Například: "Chyba", "Nápad", "Požadavek"...',
'Default categories for new projects (Comma-separated)' => 'Výchozí kategorie pro nové projekty (oddělené čárkou)',
'Integrations' => 'Integrace',
- 'Integration with third-party services' => 'Integration von Fremdleistungen',
+ 'Integration with third-party services' => 'Integrace se službami třetích stran',
'Subtask Id' => 'Dílčí úkol Id',
'Subtasks' => 'Dílčí úkoly',
'Subtasks Export' => 'Export dílčích úkolů',
- 'Subtasks exportation for "%s"' => 'Export dílčích úkolů pro "%s"',
'Task Title' => 'Název úkolu',
'Untitled' => 'bez názvu',
'Application default' => 'Standardní hodnoty',
@@ -516,7 +496,7 @@ return array(
'User dashboard' => 'Nástěnka uživatele',
'Allow only one subtask in progress at the same time for a user' => 'Umožnit uživateli práci pouze na jednom dílčím úkolu ve stejném čase',
'Edit column "%s"' => 'Upravit sloupec "%s" ',
- 'Select the new status of the subtask: "%s"' => 'Wähle einen neuen Status für Teilaufgabe: "%s"',
+ 'Select the new status of the subtask: "%s"' => 'Vyberte nový stav pro podúkol: "%s"',
'Subtask timesheet' => 'Časový rozvrh dílčích úkolů',
'There is nothing to show.' => 'Žádná položka k zobrazení',
'Time Tracking' => 'Sledování času',
@@ -529,9 +509,9 @@ return array(
'Days in this column' => 'Dní v tomto sloupci',
'%dd' => '%d d',
'Add a new link' => 'Přidat nový odkaz',
- 'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?',
- 'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?',
- 'Field required' => 'Feld erforderlich',
+ 'Do you really want to remove this link: "%s"?' => 'Opravdu chcete odstranit odkaz "%s"?',
+ 'Do you really want to remove this link with task #%d?' => 'Opravdu chcete odstranit odkaz na úkol #%d ?',
+ 'Field required' => 'Povinné pole',
'Link added successfully.' => 'Propojení bylo úspěšně přidáno.',
'Link updated successfully.' => 'Propojení bylo úspěšně aktualizováno.',
'Link removed successfully.' => 'Propojení bylo úspěšně odebráno.',
@@ -555,8 +535,8 @@ return array(
'is duplicated by' => 'je duplikován',
'is a child of' => 'je podřízený',
'is a parent of' => 'je nadřízený',
- 'targets milestone' => 'targets milestone',
- 'is a milestone of' => 'is a milestone of',
+ 'targets milestone' => 'patří k milníku',
+ 'is a milestone of' => 'je milníkem',
'fixes' => 'nahrazuje',
'is fixed by' => 'je nahrazen',
'This task' => 'Tento úkol',
@@ -598,7 +578,7 @@ return array(
'Time spent in the column' => 'Trvání jednotlivých etap',
'Task transitions' => 'Přesuny úkolů',
'Task transitions export' => 'Export přesunů mezi sloupci',
- 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Diese Auswertung enthält alle Spaltenbewegungen für jede Aufgabe mit Datum, Benutzer und Zeit vor jedem Wechsel.',
+ 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Tento seznam obsahuje všechny pohyby úkolů s daty, uživateli a časy strávenými na úkolu.',
'Currency rates' => 'Aktuální kurzy',
'Rate' => 'Kurz',
'Change reference currency' => 'Změnit referenční měnu',
@@ -607,31 +587,30 @@ return array(
'The currency rate have been added successfully.' => 'Směnný kurz byl úspěšně přidán.',
'Unable to add this currency rate.' => 'Nelze přidat tento směnný kurz',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s odstranil přiřazení úkolu %s ',
+ '%s removed the assignee of the task %s' => '%s odstranil přiřazení úkolu %s ',
'Enable Gravatar images' => 'Aktiviere Gravatar Bilder',
'Information' => 'Informace',
- 'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode',
- 'The two factor authentication code is not valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist ungültig.',
- 'The two factor authentication code is valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist gültig.',
- 'Code' => 'Code',
+ 'Check two factor authentication code' => 'Zkontrolujte dvouúrovňový autentifikační klíč',
+ 'The two factor authentication code is not valid.' => 'Dvouúrovňový autentifikační klíč není platný.',
+ 'The two factor authentication code is valid.' => 'Dvouúrovňový autentifikační klíč je platný.',
+ 'Code' => 'Klíč',
'Two factor authentication' => 'Dvouúrovňová autorizace',
- 'This QR code contains the key URI: ' => 'Dieser QR-Code beinhaltet die Schlüssel-URI',
+ 'This QR code contains the key URI: ' => 'Tento QR kód obsahuje adresu s klíčem',
'Check my code' => 'Kontrola mého kódu',
'Secret key: ' => 'Tajný klíč',
'Test your device' => 'Test Vašeho zařízení',
'Assign a color when the task is moved to a specific column' => 'Přiřadit barvu, když je úkol přesunut do konkrétního sloupce',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Burndown-Chart für "%s"',
'Burndown chart' => 'Burndown-Chart',
'This chart show the task complexity over the time (Work Remaining).' => 'Graf zobrazuje složitost úkolů v čase (Zbývající práce).',
'Screenshot taken %s' => 'Screenshot aufgenommen %s ',
'Add a screenshot' => 'Přidat snímek obrazovky',
- 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Nimm einen Screenshot auf und drücke STRG+V oder ⌘+V um ihn hier einzufügen.',
- 'Screenshot uploaded successfully.' => 'Screenshot erfolgreich hochgeladen.',
+ 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Pořiďte snímek obrazovky a v tomto poli stiskněte Ctrl+V nebo ⌘+V ',
+ 'Screenshot uploaded successfully.' => 'Snímek obrazovky byl úspěšně nahrán.',
'SEK - Swedish Krona' => 'SEK - Schwedische Kronen',
- 'Identifier' => 'Identifikator',
- 'Disable two factor authentication' => 'Zrušit dvou stupňovou autorizaci',
- 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Willst du wirklich für folgenden Nutzer die Zwei-Faktor-Authentifizierung deaktivieren: "%s"?',
+ 'Identifier' => 'Identifikátor',
+ 'Disable two factor authentication' => 'Zrušit dvouúrovňovou autorizaci',
+ 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Opravdu chcete vypnout dvouúrovňovou autentifikaci pro uživatele: "%s"?',
// 'Edit link' => '',
// 'Start to type task title...' => '',
// 'A task cannot be linked to itself' => '',
@@ -686,27 +665,21 @@ return array(
'Move the task to another column when the category is changed' => 'Přesun úkolu do jiného sloupce když je změněna kategorie',
'Send a task by email to someone' => 'Poslat někomu úkol poštou',
'Reopen a task' => 'Znovu otevřít úkol',
- 'Column change' => 'Spalte geändert',
- 'Position change' => 'Position geändert',
- 'Swimlane change' => 'Swimlane geändert',
- 'Assignee change' => 'Zuordnung geändert',
- '[%s] Overdue tasks' => '[%s] überfallige Aufgaben',
- 'Notification' => 'Benachrichtigungen',
- '%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben',
- '%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben',
+ 'Notification' => 'Upozornění',
+ '%s moved the task #%d to the first swimlane' => '%s přesunul úkol #%d do první dráhy',
// 'Swimlane' => '',
// 'Gravatar' => '',
- '%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben',
- '%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben',
+ '%s moved the task %s to the first swimlane' => '%s přesunul úkol %s do první dráhy',
+ '%s moved the task %s to the swimlane "%s"' => '%s přesunul úkol %s do dráhy "%s"',
'This report contains all subtasks information for the given date range.' => 'Report obsahuje všechny informace o dílčích úkolech pro daný časový úsek',
'This report contains all tasks information for the given date range.' => 'Report obsahuje informace o všech úkolech pro daný časový úsek.',
'Project activities for %s' => 'Aktivity projektu %s',
- 'view the board on Kanboard' => 'Pinnwand in Kanboard anzeigen',
- 'The task have been moved to the first swimlane' => 'Die Aufgabe wurde in die erste Swimlane verschoben',
- 'The task have been moved to another swimlane:' => 'Die Aufgaben wurde in ene andere Swimlane verschoben',
- 'New title: %s' => 'Neuer Titel: %s',
- 'The task is not assigned anymore' => 'Die Aufgabe ist nicht mehr zugewiesen',
- 'New assignee: %s' => 'Neue Zuordnung: %s',
+ 'view the board on Kanboard' => 'Zobrazit nástěnku',
+ 'The task have been moved to the first swimlane' => 'Úkol byl přesunut do první dráhy',
+ 'The task have been moved to another swimlane:' => 'Úkol byl přesunut do další dráhy',
+ 'New title: %s' => 'Nový název: %s',
+ 'The task is not assigned anymore' => 'Úkol již není přidělen',
+ 'New assignee: %s' => 'přidělení: %s',
'There is no category now' => 'Nyní neexistuje žádná kategorie',
'New category: %s' => 'Nová kategorie: %s',
'New color: %s' => 'Nová barva: %s',
@@ -714,11 +687,11 @@ return array(
'The due date have been removed' => 'Datum dokončení byl odstraněn',
'There is no description anymore' => 'Ještě neexistuje žádný popis',
'Recurrence settings have been modified' => 'Nastavení opakování bylo změněno',
- 'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh',
- 'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh',
- 'The field "%s" have been updated' => 'Das Feld "%s" wurde verändert',
- 'The description has been modified:' => 'Die Beschreibung wurde geändert:',
- 'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)',
+ 'Time spent changed: %sh' => 'Strávený čas se změnil: %sh',
+ 'Time estimated changed: %sh' => 'Odhadovaný čas se změnil: %sh',
+ 'The field "%s" have been updated' => 'Sloupec "%s" byl upraven',
+ 'The description has been modified:' => 'Popis byl upraven:',
+ 'Do you really want to close the task "%s" as well as all subtasks?' => 'Opravdu si přejete úkol "%s" uzavřít? (včetně podúkolů)',
'I want to receive notifications for:' => 'Chci dostávat upozornění na:',
'All tasks' => 'Všechny úkoly',
'Only for tasks assigned to me' => 'pouze pro moje úkoly',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Hledat podle kategorie: ',
'Search by description: ' => 'Hledat podle popisu: ',
'Search by due date: ' => 'Hledat podle termínu: ',
- 'Lead and Cycle time for "%s"' => 'Dodací lhůta a doba cyklu pro "%s"',
- 'Average time spent into each column for "%s"' => 'Průměrná doba strávená v každé fázi pro "%s"',
'Average time spent into each column' => 'Průměrná doba strávená v každé fázi',
'Average time spent' => 'Průměrná strávená doba',
// 'This chart show the average time spent into each column for the last %d tasks.' => '',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Licence:',
'License' => 'Licence',
'Enter the text below' => 'Zadejte text níže',
- 'Gantt chart for %s' => 'Gantt graf pro %s',
'Sort by position' => 'Třídit podle pozice',
'Sort by date' => 'Třídit podle datumu',
'Add task' => 'Přidat úkol',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
// 'Project members' => '',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index 280ee997..0ff4253e 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d lukket opgavet',
'No task for this project' => 'Ingen opgaver i dette projekt',
'Public link' => 'Offentligt link',
- 'Change assignee' => 'Ændre ansvarlig',
- 'Change assignee for the task "%s"' => 'Ændre ansvarlig for opgaven: "%s"',
'Timezone' => 'Tidszone',
'Sorry, I didn\'t find this information in my database!' => 'Denne information kunne ikke findes i databasen!',
'Page not found' => 'Siden er ikke fundet',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategori',
'Category:' => 'Kategori:',
'Categories' => 'Kategorier',
- 'Category not found.' => 'Kategori ikke fundet.',
'Your category have been created successfully.' => 'Kategorien er oprettet.',
'Unable to create your category.' => 'Kategorien kunne ikke oprettes.',
'Your category have been updated successfully.' => 'Kategorien er opdateret.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Vil du virkelig fjerne denne fil: "%s"?',
'Attachments' => 'Vedhæftninger',
'Edit the task' => 'Rediger opgaven',
- 'Edit the description' => 'Rediger beskrivelsen',
'Add a comment' => 'Tilføj en kommentar',
'Edit a comment' => 'Rediger en kommentar',
'Summary' => 'Resumé',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Vis et andet projekt...',
'Created by %s' => 'Oprettet af %s',
'Tasks Export' => 'Opgave eksport',
- 'Tasks exportation for "%s"' => 'Opgave eksport for "%s"',
'Start Date' => 'Start-dato',
'End Date' => 'Slut-dato',
'Execute' => 'Udfør',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Ny under-opgave',
'New attachment added "%s"' => 'Ny vedhæftning tilføjet "%s"',
'New comment posted by %s' => 'Ny kommentar af %s',
- // 'New attachment' => '',
// 'New comment' => '',
'Comment updated' => 'Kommentar opdateret',
// 'New subtask' => '',
- // 'Subtask updated' => '',
- // 'Task updated' => '',
- // 'Task closed' => '',
- // 'Task opened' => '',
'I want to receive notifications only for those projects:' => 'Jeg vil kun have notifikationer for disse projekter:',
'view the task on Kanboard' => 'se opgaven på Kanboard',
'Public access' => 'Offentlig adgang',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Ingen eksterne autentificering aktiveret.',
'Password modified successfully.' => 'Adgangskode ændret.',
'Unable to change the password.' => 'Adgangskoden kunne ikke ændres.',
- 'Change category for the task "%s"' => 'Skift kategori for opgaven "%s"',
'Change category' => 'Skift kategori',
'%s updated the task %s' => '%s opdatert opgaven %s',
'%s opened the task %s' => '%s åben opgaven %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s opdaterede opgaven #%d',
'%s created the task #%d' => '%s oprettede opgaven #%d',
'%s closed the task #%d' => '%s lukkede opgaven #%d',
- '%s open the task #%d' => '%s åbnede opgaven #%d',
- '%s moved the task #%d to the column "%s"' => '%s flyttede opgaven #%d til kolonnen "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s flyttede opgaven #%d til position %d i kolonnen "%s"',
+ '%s opened the task #%d' => '%s åbnede opgaven #%d',
'Activity' => 'Aktivitet',
'Default values are "%s"' => 'Standard værdier er "%s"',
'Default columns for new projects (Comma-separated)' => 'Standard kolonne for nye projekter (kommasepareret)',
'Task assignee change' => 'Opgaven ansvarlig ændring',
- '%s change the assignee of the task #%d to %s' => '%s skrift ansvarlig for opgaven #%d til %s',
+ '%s changed the assignee of the task #%d to %s' => '%s skrift ansvarlig for opgaven #%d til %s',
'%s changed the assignee of the task %s to %s' => '%s skift ansvarlig for opgaven %s til %s',
'New password for the user "%s"' => 'Ny adgangskode for brugeren "%s"',
'Choose an event' => 'Vælg et event',
@@ -447,13 +434,10 @@ return array(
// 'Percentage' => '',
// 'Number of tasks' => '',
// 'Task distribution' => '',
- // 'Reportings' => '',
- // 'Task repartition for "%s"' => '',
// 'Analytics' => '',
// 'Subtask' => '',
// 'My subtasks' => '',
// 'User repartition' => '',
- // 'User repartition for "%s"' => '',
// 'Clone this project' => '',
// 'Column removed successfully.' => '',
// 'Not enough data to show the graph.' => '',
@@ -470,10 +454,8 @@ return array(
// 'This value must be numeric' => '',
// 'Unable to create this task.' => '',
// 'Cumulative flow diagram' => '',
- // 'Cumulative flow diagram for "%s"' => '',
// 'Daily project summary' => '',
// 'Daily project summary export' => '',
- // 'Daily project summary export for "%s"' => '',
// 'Exports' => '',
// 'This export contains the number of tasks per column grouped per day.' => '',
// 'Active swimlanes' => '',
@@ -485,7 +467,6 @@ return array(
// 'Remove a swimlane' => '',
// 'Show default swimlane' => '',
// 'Swimlane modification for the project "%s"' => '',
- // 'Swimlane not found.' => '',
// 'Swimlane removed successfully.' => '',
// 'Swimlanes' => '',
// 'Swimlane updated successfully.' => '',
@@ -500,7 +481,6 @@ return array(
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
- // 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
// 'Application default' => '',
@@ -607,7 +587,7 @@ return array(
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Webhook URL' => '',
- // '%s remove the assignee of the task %s' => '',
+ // '%s removed the assignee of the task %s' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@@ -621,7 +601,6 @@ return array(
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
// '%s via Kanboard' => '',
- // 'Burndown chart for "%s"' => '',
// 'Burndown chart' => '',
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
@@ -686,14 +665,8 @@ return array(
// 'Move the task to another column when the category is changed' => '',
// 'Send a task by email to someone' => '',
// 'Reopen a task' => '',
- // 'Column change' => '',
- // 'Position change' => '',
- // 'Swimlane change' => '',
- // 'Assignee change' => '',
- // '[%s] Overdue tasks' => '',
// 'Notification' => '',
// '%s moved the task #%d to the first swimlane' => '',
- // '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// '%s moved the task %s to the first swimlane' => '',
@@ -764,8 +737,6 @@ return array(
// 'Search by category: ' => '',
// 'Search by description: ' => '',
// 'Search by due date: ' => '',
- // 'Lead and Cycle time for "%s"' => '',
- // 'Average time spent into each column for "%s"' => '',
// 'Average time spent into each column' => '',
// 'Average time spent' => '',
// 'This chart show the average time spent into each column for the last %d tasks.' => '',
@@ -806,7 +777,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
// 'Project members' => '',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index e76100c1..59de2956 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d abgeschlossene Aufgaben',
'No task for this project' => 'Keine Aufgaben in diesem Projekt',
'Public link' => 'Öffentlicher Link',
- 'Change assignee' => 'Zuständigkeit ändern',
- 'Change assignee for the task "%s"' => 'Zuständigkeit für diese Aufgabe ändern: "%s"',
'Timezone' => 'Zeitzone',
'Sorry, I didn\'t find this information in my database!' => 'Diese Information wurde in der Datenbank nicht gefunden!',
'Page not found' => 'Seite nicht gefunden',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategorie',
'Category:' => 'Kategorie:',
'Categories' => 'Kategorien',
- 'Category not found.' => 'Kategorie nicht gefunden.',
'Your category have been created successfully.' => 'Kategorie erfolgreich erstellt.',
'Unable to create your category.' => 'Erstellung der Kategorie nicht möglich.',
'Your category have been updated successfully.' => 'Kategorie erfolgreich aktualisiert.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Soll diese Datei wirklich gelöscht werden: "%s"?',
'Attachments' => 'Anhänge',
'Edit the task' => 'Aufgabe bearbeiten',
- 'Edit the description' => 'Beschreibung bearbeiten',
'Add a comment' => 'Kommentar hinzufügen',
'Edit a comment' => 'Kommentar bearbeiten',
'Summary' => 'Zusammenfassung',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Zu Projekt wechseln',
'Created by %s' => 'Erstellt durch %s',
'Tasks Export' => 'Aufgaben exportieren',
- 'Tasks exportation for "%s"' => 'Aufgaben exportieren für "%s"',
'Start Date' => 'Anfangsdatum',
'End Date' => 'Enddatum',
'Execute' => 'Ausführen',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Neue Teilaufgabe',
'New attachment added "%s"' => 'Neuer Anhang "%s" wurde hinzugefügt.',
'New comment posted by %s' => 'Neuer Kommentar verfasst durch %s',
- 'New attachment' => 'Neuer Anhang',
'New comment' => 'Neuer Kommentar',
'Comment updated' => 'Kommentar wurde aktualisiert',
'New subtask' => 'Neue Teilaufgabe',
- 'Subtask updated' => 'Teilaufgabe aktualisiert',
- 'Task updated' => 'Aufgabe aktualisiert',
- 'Task closed' => 'Aufgabe geschlossen',
- 'Task opened' => 'Aufgabe geöffnet',
'I want to receive notifications only for those projects:' => 'Ich möchte nur für diese Projekte Benachrichtigungen erhalten:',
'view the task on Kanboard' => 'diese Aufgabe auf dem Kanboard zeigen',
'Public access' => 'Öffentlicher Zugriff',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Es sind keine externen Authentisierungsmethoden aktiv.',
'Password modified successfully.' => 'Passwort wurde erfolgreich geändert.',
'Unable to change the password.' => 'Passwort konnte nicht geändert werden.',
- 'Change category for the task "%s"' => 'Kategorie der Aufgabe "%s" ändern',
'Change category' => 'Kategorie ändern',
'%s updated the task %s' => '%s hat die Aufgabe %s aktualisiert',
'%s opened the task %s' => '%s hat die Aufgabe %s geöffnet',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s hat die Aufgabe #%d aktualisiert',
'%s created the task #%d' => '%s hat die Aufgabe #%d angelegt',
'%s closed the task #%d' => '%s hat die Aufgabe #%d geschlossen',
- '%s open the task #%d' => '%s hat die Aufgabe #%d geöffnet',
- '%s moved the task #%d to the column "%s"' => '%s hat die Aufgabe #%d in die Spalte "%s" verschoben',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s hat die Aufgabe #%d an die Position %d in der Spalte "%s" verschoben',
+ '%s opened the task #%d' => '%s hat die Aufgabe #%d geöffnet',
'Activity' => 'Aktivität',
'Default values are "%s"' => 'Die Standardwerte sind "%s"',
'Default columns for new projects (Comma-separated)' => 'Standardspalten für neue Projekte (komma-getrennt)',
'Task assignee change' => 'Zuständigkeit geändert',
- '%s change the assignee of the task #%d to %s' => '%s hat die Zusständigkeit der Aufgabe #%d geändert um %s',
+ '%s changed the assignee of the task #%d to %s' => '%s hat die Zusständigkeit der Aufgabe #%d geändert um %s',
'%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert um %s',
'New password for the user "%s"' => 'Neues Passwort des Benutzers "%s"',
'Choose an event' => 'Aktion wählen',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Prozentsatz',
'Number of tasks' => 'Anzahl an Aufgaben',
'Task distribution' => 'Aufgabenverteilung',
- 'Reportings' => 'Berichte',
- 'Task repartition for "%s"' => 'Aufgabenzuweisung für "%s"',
'Analytics' => 'Analyse',
'Subtask' => 'Teilaufgabe',
'My subtasks' => 'Meine Teilaufgaben',
'User repartition' => 'Benutzerverteilung',
- 'User repartition for "%s"' => 'Benutzerverteilung für "%s"',
'Clone this project' => 'Projekt kopieren',
'Column removed successfully.' => 'Spalte erfolgreich entfernt.',
'Not enough data to show the graph.' => 'Nicht genügend Daten, um die Grafik zu zeigen.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Dieser Wert muss nummerisch sein',
'Unable to create this task.' => 'Diese Aufgabe kann nicht erstellt werden',
'Cumulative flow diagram' => 'Kumulatives Flussdiagramm',
- 'Cumulative flow diagram for "%s"' => 'Kumulatives Flussdiagramm für "%s"',
'Daily project summary' => 'Tägliche Projektzusammenfassung',
'Daily project summary export' => 'Export der täglichen Projektzusammenfassung',
- 'Daily project summary export for "%s"' => 'Export der täglichen Projektzusammenfassung für "%s"',
'Exports' => 'Exporte',
'This export contains the number of tasks per column grouped per day.' => 'Dieser Export enthält die Anzahl der Aufgaben pro Spalte nach Tagen gruppiert.',
'Active swimlanes' => 'Aktive Swimlane',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Swimlane entfernen',
'Show default swimlane' => 'Standard-Swimlane anzeigen',
'Swimlane modification for the project "%s"' => 'Swimlane-Änderung für das Projekt "%s"',
- 'Swimlane not found.' => 'Swimlane nicht gefunden',
'Swimlane removed successfully.' => 'Swimlane erfolgreich entfernt.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane erfolgreich geändert.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Teilaufgaben-ID',
'Subtasks' => 'Teilaufgaben',
'Subtasks Export' => 'Export von Teilaufgaben',
- 'Subtasks exportation for "%s"' => 'Export von Teilaufgaben für "%s"',
'Task Title' => 'Aufgaben-Titel',
'Untitled' => 'unbetitelt',
'Application default' => 'Anwendungsstandard',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Der Währungskurs wurde erfolgreich hinzugefügt.',
'Unable to add this currency rate.' => 'Währungskurs konnte nicht hinzugefügt werden',
'Webhook URL' => 'Webhook-URL',
- '%s remove the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen',
+ '%s removed the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen',
'Enable Gravatar images' => 'Aktiviere Gravatar-Bilder',
'Information' => 'Information',
'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Teste dein Gerät',
'Assign a color when the task is moved to a specific column' => 'Weise eine Farbe zu, wenn die Aufgabe zu einer bestimmten Spalte bewegt wird',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Burndown-Diagramm für "%s"',
'Burndown chart' => 'Burndown-Diagramm',
'This chart show the task complexity over the time (Work Remaining).' => 'Dieses Diagramm zeigt die Aufgabenkomplexität über den Faktor Zeit (Verbleibende Arbeit).',
'Screenshot taken %s' => 'Screenshot aufgenommen %s ',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Aufgabe in andere Spalte verschieben, wenn Kategorie geändert wird',
'Send a task by email to someone' => 'Aufgabe per E-Mail versenden',
'Reopen a task' => 'Aufgabe wieder öffnen',
- 'Column change' => 'Spalte geändert',
- 'Position change' => 'Position geändert',
- 'Swimlane change' => 'Swimlane geändert',
- 'Assignee change' => 'Zuordnung geändert',
- '[%s] Overdue tasks' => '[%s] überfallige Aufgaben',
'Notification' => 'Benachrichtigungen',
'%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben',
- '%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Suche nach Kategorie: ',
'Search by description: ' => 'Suche nach Beschreibung: ',
'Search by due date: ' => 'Suche nach Fälligkeitsdatum: ',
- 'Lead and Cycle time for "%s"' => 'Durchlauf und Zykluszeit für "%s"',
- 'Average time spent into each column for "%s"' => 'Durchschnittliche Zeit in jeder Spalte für "%s"',
'Average time spent into each column' => 'Durchschnittszeit in jeder Spalte',
'Average time spent' => 'Durchschnittlicher Zeitverbrauch',
'This chart show the average time spent into each column for the last %d tasks.' => 'Dieses Diagramm zeigt die durchschnittliche Zeit in jeder Spalte der letzten %d Aufgaben.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Lizenz:',
'License' => 'Lizenz',
'Enter the text below' => 'Text unten eingeben',
- 'Gantt chart for %s' => 'Gantt Diagramm für %s',
'Sort by position' => 'Nach Position sortieren',
'Sort by date' => 'Nach Datum sortieren',
'Add task' => 'Aufgabe hinzufügen',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Version',
'Plugins' => 'Plugins',
'There is no plugin loaded.' => 'Es ist kein Plugin geladen.',
- 'Set maximum column height' => 'Setze maximale Spaltenhöhe',
- 'Remove maximum column height' => 'Entferne maximale Spaltenhöhe',
'My notifications' => 'Meine Benachrichtigungen',
'Custom filters' => 'Benutzerdefinierte Filter',
'Your custom filter have been created successfully.' => 'Benutzerdefinierten Filter erfolgreich erstellt.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Ungültiges Captcha',
'The name must be unique' => 'Der Name muss eindeutig sein',
'View all groups' => 'Alle Gruppen anzeigen',
- 'View group members' => 'Gruppenmitglieder anzeigen',
'There is no user available.' => 'Es ist kein Benutzer verfügbar.',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Wollen Sie den Benutzer "%s" wirklich aus der Gruppe "%s" löschen?',
'There is no group.' => 'Es gibt keine Gruppe.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Geben Sie den Gruppennamen ein...',
'Role:' => 'Rolle:',
'Project members' => 'Projektmitglieder',
- 'Compare hours for "%s"' => 'Vergleich der Stunden für %s',
'%s mentioned you in the task #%d' => '%s erwähnte Sie in Aufgabe #%d',
'%s mentioned you in a comment on the task #%d' => '%s erwähnte Sie in einem Kommentar zur Aufgabe #%d',
'You were mentioned in the task #%d' => 'Sie wurden in der Aufgabe #%d erwähnt',
'You were mentioned in a comment on the task #%d' => 'Sie wurden in einem Kommentar zur Aufgabe #%d erwähnt',
- 'Mentioned' => 'Erwähnt',
- 'Compare Estimated Time vs Actual Time' => 'Vergleich zwischen erwartetem und tatsächlichem Zeitaufwand',
'Estimated hours: ' => 'Erwarteter Zeitaufwand (Stunden): ',
'Actual hours: ' => 'Tatsächlich aufgewändete Stunden: ',
'Hours Spent' => 'Stunden aufgewändet',
@@ -1202,4 +1166,52 @@ return array(
'Email transport' => 'E-Mail Verkehr',
'Webhook token' => 'Webhook Token',
'Imports' => 'Importe',
+ 'Project tags management' => 'Projektbezogenes Schlagwort-Management',
+ 'Tag created successfully.' => 'Schlagwort erfolgreich erstellt.',
+ 'Unable to create this tag.' => 'Das Schlagwort kann nicht erstellt werden.',
+ 'Tag updated successfully.' => 'Schlagwort erfolgreich aktualisiert.',
+ 'Unable to update this tag.' => 'Das Schlagwort kann nicht aktualisiert werden.',
+ 'Tag removed successfully.' => 'Schlagwort erfolgreich entfernt.',
+ 'Unable to remove this tag.' => 'Das Schlagwort kann nicht entfernt werden.',
+ 'Global tags management' => 'Globales Schlagwort-Management',
+ 'Tags' => 'Schlagworte',
+ 'Tags management' => 'Schlagwort-Management',
+ 'Add new tag' => 'Neues Schlagwort hinzufügen',
+ 'Edit a tag' => 'Schlagwort bearbeiten',
+ 'Project tags' => 'Projektbezogene Schlagwörter',
+ 'There is no specific tag for this project at the moment.' => 'Es gibt zur Zeit kein spezifisches Schlagwort.',
+ 'Tag' => 'Schlagwort',
+ 'Remove a tag' => 'Schlagwort entfernen',
+ 'Do you really want to remove this tag: "%s"?' => 'Soll dieses Schlagwort wirklich entfernt werden: "%s"?',
+ 'Global tags' => 'Globale Schlagwörter',
+ 'There is no global tag at the moment.' => 'Es gibt zur Zeit kein globales Schlagwort',
+ 'This field cannot be empty' => 'Dieses Feld kann nicht leer sein',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ 'Hide tasks in this column in the dashboard' => 'Aufgaben in dieser Spalte im Dashboard ausblenden',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/el_GR/translations.php b/app/Locale/el_GR/translations.php
index 59d75a96..cf3bb588 100644
--- a/app/Locale/el_GR/translations.php
+++ b/app/Locale/el_GR/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d κλειστές εργασίες',
'No task for this project' => 'Αριθμός εργασιών για το έργο',
'Public link' => 'Δημόσιος σύνδεσμος',
- 'Change assignee' => 'Αλλαγή ανάθεσης',
- 'Change assignee for the task "%s"' => 'Αλλαγή ανάθεσης για την εργασία « %s »',
'Timezone' => 'Timezone',
'Sorry, I didn\'t find this information in my database!' => 'Δυστυχώς δεν βρέθηκε αυτή η πληροφορία στη βάση δεδομένων',
'Page not found' => 'Η σελίδα δεν βρέθηκε',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Κατηγορία',
'Category:' => 'Κατηγορία:',
'Categories' => 'Κατηγορίες',
- 'Category not found.' => 'Η κατηγορία δεν βρέθηκε',
'Your category have been created successfully.' => 'Η κατηγορία δημιουργήθηκε.',
'Unable to create your category.' => 'Δεν είναι δυνατή η δημιουργία της κατηγορίας.',
'Your category have been updated successfully.' => 'Η ενημέρωση της κατηγορίας καταχωρήθηκε με επιτυχία.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Αφαίρεση του αρχείου: « %s » ?',
'Attachments' => 'Συνημμένα',
'Edit the task' => 'Διόρθωση εργασίας',
- 'Edit the description' => 'Διόρθωση περιγραφής',
'Add a comment' => 'Προσθήκη σχολίου',
'Edit a comment' => 'Διόρθωση σχολίου',
'Summary' => 'Περίληψη',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Εμφάνιση άλλου έργου',
'Created by %s' => 'Δημιουργήθηκε από %s',
'Tasks Export' => 'Εξαγωγή εργασιών',
- 'Tasks exportation for "%s"' => 'Εξαγωγή εργασιών για το έργο « %s »',
'Start Date' => 'Ημερομηνία έναρξης',
'End Date' => 'ημερομηνία λήξης',
'Execute' => 'Εκτέλεση',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Νέα υπο-εργασία',
'New attachment added "%s"' => 'Νέα επικόλληση προστέθηκε « %s »',
'New comment posted by %s' => 'Νέο σχόλιο από τον χρήστη « %s »',
- 'New attachment' => 'New attachment',
'New comment' => 'Νέο σχόλιο',
'Comment updated' => 'Το σχόλιο ενημερώθηκε',
'New subtask' => 'Νέα υπο-εργασία',
- 'Subtask updated' => 'Η Υπο-Εργασία ενημερώθηκε',
- 'Task updated' => 'Η εργασία ενημερώθηκε',
- 'Task closed' => 'Η εργασία έκλεισε',
- 'Task opened' => 'Η εργασία άνοιξε',
'I want to receive notifications only for those projects:' => 'Θέλω να ενημερώνομαι αποκλειστικά για:',
'view the task on Kanboard' => 'Προβολή της εργασίας στο Kanboard',
'Public access' => 'Ανοιχτή πρόσβαση',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Καμία εξωτερική πιστοποιήση ενεργοποιημένη.',
'Password modified successfully.' => 'Το password τροποποιήθηκε με επιτυχία.',
'Unable to change the password.' => 'Αδύνατο να αλλάξει το password.',
- 'Change category for the task "%s"' => 'Αλλαγή κατηγορίας της εργασίας « %s »',
'Change category' => 'Αλλαγή κατηγορίας',
'%s updated the task %s' => '%s ενημέρωσε την εργασία %s',
'%s opened the task %s' => '%s άνοιξε την εργασία %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s ενημέρωσε την εργασία n°%d',
'%s created the task #%d' => '%s δημιούργησε την εργασία n°%d',
'%s closed the task #%d' => '%s έκλεισε την εργασία n°%d',
- '%s open the task #%d' => '%s άνοιξε την εργασία n°%d',
- '%s moved the task #%d to the column "%s"' => '%s μετακίνησε την εργασία n°%d στη στήλη « %s »',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s μετακίνησε την εργασία n°%d στη θέση n°%d της στήλης « %s »',
+ '%s opened the task #%d' => '%s άνοιξε την εργασία n°%d',
'Activity' => 'Δραστηριότητα',
'Default values are "%s"' => 'Οι προεπιλεγμένες τιμές είναι « %s »',
'Default columns for new projects (Comma-separated)' => 'Προεπιλεγμένες στήλες για νέα έργα (Comma-separated)',
'Task assignee change' => 'Αλλαγή εκδοχέα εργασίας',
- '%s change the assignee of the task #%d to %s' => '%s άλλαξε τον εκδοχέα της εργασίας n˚%d σε %s',
+ '%s changed the assignee of the task #%d to %s' => '%s άλλαξε τον εκδοχέα της εργασίας n˚%d σε %s',
'%s changed the assignee of the task %s to %s' => '%s ενημέρωσε τον εκδοχέα της εργασίας %s σε %s',
'New password for the user "%s"' => 'Νέο password του χρήστη « %s »',
'Choose an event' => 'Επιλογή event',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Ποσοστό',
'Number of tasks' => 'Αριθμός εργασιών',
'Task distribution' => 'Κατανομή εργασιών',
- 'Reportings' => 'Αναφορές',
- 'Task repartition for "%s"' => 'Επανάληψη εργασιών για « %s »',
'Analytics' => 'Αναλύσεις',
'Subtask' => 'Υπο-Εργασία',
'My subtasks' => 'Οι υπο-εργασίες μου',
'User repartition' => 'Επαναλήψεις χρηστών',
- 'User repartition for "%s"' => 'Επαναλήψεις χρηστών για « %s »',
'Clone this project' => 'Κλωνοποίηση έργου',
'Column removed successfully.' => 'Η στήλη αφαιρέθηκε με επιτυχία.',
'Not enough data to show the graph.' => 'Ελλειπή δεδομένα για να εμφανιστεί το γράφημα.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Η τιμή πρέπει να είναι αριθμός',
'Unable to create this task.' => 'Αδύνατο να δημιουργηθεί αυτή η εργασία.',
'Cumulative flow diagram' => 'Συγκεντρωτικό διάγραμμα ροής',
- 'Cumulative flow diagram for "%s"' => 'Συγκεντρωτικό διάγραμμα ροής για « %s »',
'Daily project summary' => 'Καθημερινή περίληψη του έργου',
'Daily project summary export' => 'Εξαγωγή της καθημερινής περίληψης του έργου',
- 'Daily project summary export for "%s"' => 'Εξαγωγή της καθημερινής περίληψης του έργου « %s »',
'Exports' => 'Εξαγωγές',
'This export contains the number of tasks per column grouped per day.' => 'Αυτή η κατάσταση περιέχει τον αριθμό των εργασιών ανά στήλη ομαδοποιημένα ανά ημέρα.',
'Active swimlanes' => 'Ενεργές λωρίδες',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Αφαίρεση λωρίδας',
'Show default swimlane' => 'Εμφάνιση προεπιλεγμένων λωρίδων',
'Swimlane modification for the project "%s"' => 'Τροποποίηση λωρίδας για το έργο « %s »',
- 'Swimlane not found.' => 'Η λωρίδα δεν βρέθηκε.',
'Swimlane removed successfully.' => 'Η λωρίδα αφαιρέθηκε με επιτυχία.',
'Swimlanes' => 'Λωρίδες',
'Swimlane updated successfully.' => 'Η λωρίδα ενημερώθηκε με επιτυχία.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Id υπο-εργασίας',
'Subtasks' => 'Υπο-Εργασίες',
'Subtasks Export' => 'Εξαγωγή υπο-εργασίων',
- 'Subtasks exportation for "%s"' => 'Εξαγωγή υπο-εργασίων για το έργο « %s »',
'Task Title' => 'Τίτλος εργασίας',
'Untitled' => 'Χωρίς τίτλο',
'Application default' => 'Προεπιλογή από την εφαρμογή',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Η ισοτιμία προστέθηκε με επιτυχία.',
'Unable to add this currency rate.' => 'Αδύνατο να προστεθεί αυτή η ισοτιμία.',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s αφαίρεσε τον εκδοχέα της εργασίας %s',
+ '%s removed the assignee of the task %s' => '%s αφαίρεσε τον εκδοχέα της εργασίας %s',
'Enable Gravatar images' => 'Ενεργοποίηση εικόνων Gravatar',
'Information' => 'Πληροφορίες',
'Check two factor authentication code' => 'Ελέγξτε δύο παράγοντες ελέγχου ταυτότητας κωδικού',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Ελέγξτε τη συσκευή σας',
'Assign a color when the task is moved to a specific column' => 'Αντιστοίχιση χρώματος όταν η εργασία κινείται σε μια συγκεκριμένη στήλη',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Δημιουργία διαγράμματος για « %s »',
'Burndown chart' => 'Δημιουργία διαγράμματος',
'This chart show the task complexity over the time (Work Remaining).' => 'Αυτό το γράφημα δείχνει την πολυπλοκότητα του έργου κατά την πάροδο του χρόνου (Εργασία που παραμένει).',
'Screenshot taken %s' => 'Το screenshot αποθηκεύτηκε από %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Μετακινήστε την εργασία σε άλλη στήλη, όταν η κατηγορία έχει αλλάξει',
'Send a task by email to someone' => 'Στείλτε μια εργασία μέσω ηλεκτρονικού ταχυδρομείου σε κάποιον',
'Reopen a task' => 'Ξανα-ανοίξτε μια εργασία',
- 'Column change' => 'Αλλαγή στήλης',
- 'Position change' => 'Αλλαγή θέσης',
- 'Swimlane change' => 'Αλλαγή λωρίδας',
- 'Assignee change' => 'Αλλαγή εκδοχέα',
- '[%s] Overdue tasks' => '[%s] Εκπρόθεσμες εργασίες',
'Notification' => 'Κοινοποίηση',
'%s moved the task #%d to the first swimlane' => '%s μετέφερε την εργασία n°%d στην 1η λωρίδα',
- '%s moved the task #%d to the swimlane "%s"' => '%s μετέφερε την εργασία n°%d στη λωρίδα « %s »',
'Swimlane' => 'Λωρίδα',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s μετέφερε την εργασία %s στην 1η λωρίδα',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Αναζήτηση βάση κατηγορίας: ',
'Search by description: ' => 'Αναζήτηση βάση περιγραφής: ',
'Search by due date: ' => 'Αναζήτηση βάση ημέρας λήξης: ',
- 'Lead and Cycle time for "%s"' => 'Lead & cycle time για « %s »',
- 'Average time spent into each column for "%s"' => 'Μέσος χρόνος παραμονής σε κάθε στήλη για « %s »',
'Average time spent into each column' => 'Μέσος χρόνος παραμονής σε κάθε στήλη',
'Average time spent' => 'Μέσος χρόνος που δαπανήθηκε',
'This chart show the average time spent into each column for the last %d tasks.' => 'Αυτό το γράφημα δείχνει ότι ο μέσος χρόνος που δαπανάται σε κάθε στήλη για τις τελευταίες %d εργασίες',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Άδεια:',
'License' => 'Άδεια',
'Enter the text below' => 'Πληκτρολογήστε το παρακάτω κείμενο',
- 'Gantt chart for %s' => 'Gantt διάγραμμα για %s',
'Sort by position' => 'Ταξινόμηση κατά Θέση',
'Sort by date' => 'Ταξινόμηση κατά ημέρα',
'Add task' => 'Προσθήκη εργασίας',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Έκδοση',
'Plugins' => 'Πρόσθετα',
'There is no plugin loaded.' => 'Δεν έχει φορτωθεί plugin',
- 'Set maximum column height' => 'Ορισμός μέγιστου ύψους στήλης',
- 'Remove maximum column height' => 'Αφαίρεση μέγιστου ύψους στήλης',
'My notifications' => 'Οι ειδοποιήσεις μου',
'Custom filters' => 'Φίλτρα ορισμένα από τον χρήστη',
'Your custom filter have been created successfully.' => 'Το παρεμετροποιημένο από τον χρήστη φίλτρο δημιουργήθηκε με επιτυχία',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Μη αποδεκτό Captcha',
'The name must be unique' => 'Το όνομα πρέπει να είναι μοναδικό',
'View all groups' => 'Προβολή όλων των ομάδων',
- 'View group members' => 'Προβολή των μελών της ομάδας',
'There is no user available.' => 'Δεν υπάρχει διαθέσιμος χρήστης',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Αφαίρεση του χρήστη « %s » από την ομάδα « %s » ?',
'There is no group.' => 'Δεν υπάρχει ομάδα.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Εισαγωγή ονομασίας ομάδας...',
'Role:' => 'Ρόλος:',
'Project members' => 'Μέλη έργου',
- 'Compare hours for "%s"' => 'Σύγκριση ωρών για « %s »',
'%s mentioned you in the task #%d' => '%s αναφέρονται σε εσάς, στη εργασία n°%d',
'%s mentioned you in a comment on the task #%d' => '%s αναφέρονται σε εσάς σε σχόλιο, στη εργασίας n°%d',
'You were mentioned in the task #%d' => 'Αναφέρεστε στην εργασία n°%d',
'You were mentioned in a comment on the task #%d' => 'Αναφέρεστε σε σχόλιο, στην εργασία n°%d',
- 'Mentioned' => 'Αναφέρεται',
- 'Compare Estimated Time vs Actual Time' => 'Σύγκριση προβλεπόμενου χρόνου vs πραγματικού χρόνου',
'Estimated hours: ' => 'Προβλεπόμενες ώρες: ',
'Actual hours: ' => 'Πραγματικές ώρες: ',
'Hours Spent' => 'Δαπανόμενες ώρες',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index a646e4ef..5a2c16c0 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -20,16 +20,16 @@ return array(
'Orange' => 'Naranja',
'Grey' => 'Gris',
'Brown' => 'Marrón',
- 'Deep Orange' => 'Naranja Oscuro',
- 'Dark Grey' => 'Gris Oscuro',
+ 'Deep Orange' => 'Naranja oscuro',
+ 'Dark Grey' => 'Gris oscuro',
'Pink' => 'Rosa',
- 'Teal' => 'Verde Azulado',
- 'Cyan' => 'Cián',
+ 'Teal' => 'Verde azulado',
+ 'Cyan' => 'Cian',
'Lime' => 'Lima',
- 'Light Green' => 'Verde Claro',
+ 'Light Green' => 'Verde claro',
'Amber' => 'Ámbar',
'Save' => 'Guardar',
- 'Login' => 'Iniciar sesión (Ingresar)',
+ 'Login' => 'Iniciar sesión (ingresar)',
'Official website:' => 'Página web oficial:',
'Unassigned' => 'No asignado',
'View this task' => 'Ver esta tarea',
@@ -80,7 +80,7 @@ return array(
'Settings' => 'Preferencias',
'Application settings' => 'Preferencias de la aplicación',
'Language' => 'Idioma',
- 'Webhook token:' => 'Token de los disparadores Web (webhooks):',
+ 'Webhook token:' => 'Token de los disparadores web (webhooks):',
'API token:' => 'Token de la API:',
'Database size:' => 'Tamaño de la base de datos:',
'Download the database' => 'Descargar la base de datos',
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d tareas completadas',
'No task for this project' => 'Ninguna tarea para este proyecto',
'Public link' => 'Enlace público',
- 'Change assignee' => 'Cambiar responsable',
- 'Change assignee for the task "%s"' => 'Cambiar el responsable para la tarea «%s»',
'Timezone' => 'Zona horaria',
'Sorry, I didn\'t find this information in my database!' => '¡Lo siento, no he encontrado esta información en mi base de datos!',
'Page not found' => 'Página no encontrada',
@@ -175,7 +173,7 @@ return array(
'Your automatic action have been created successfully.' => 'La acción automatizada ha sido creada correctamente.',
'Unable to create your automatic action.' => 'No se puede crear esta acción automatizada.',
'Remove an action' => 'Eliminar una acción',
- 'Unable to remove this action.' => 'No se puede eliminar esta accción.',
+ 'Unable to remove this action.' => 'No se puede eliminar esta acción.',
'Action removed successfully.' => 'La acción ha sido eliminada correctamente.',
'Automatic actions for the project "%s"' => 'Acciones automatizadas para el proyecto «%s»',
'Add an action' => 'Añadir una acción',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Categoría',
'Category:' => 'Categoría:',
'Categories' => 'Categorías',
- 'Category not found.' => 'Categoría no hallada.',
'Your category have been created successfully.' => 'Se ha creado su categoría correctamente.',
'Unable to create your category.' => 'No se puede crear su categoría.',
'Your category have been updated successfully.' => 'Se ha actualizado su categoría correctamente.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => '¿Realmente desea eliminar este fichero: «%s»?',
'Attachments' => 'Adjuntos',
'Edit the task' => 'Modificar la tarea',
- 'Edit the description' => 'Modificar la descripción',
'Add a comment' => 'Añadir un comentario',
'Edit a comment' => 'Modificar un comentario',
'Summary' => 'Resumen',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Mostrar otro proyecto',
'Created by %s' => 'Creado por %s',
'Tasks Export' => 'Exportar tareas',
- 'Tasks exportation for "%s"' => 'Exportación de tareas para «%s»',
'Start Date' => 'Fecha de inicio',
'End Date' => 'Fecha final',
'Execute' => 'Ejecutar',
@@ -315,9 +310,9 @@ return array(
'Project cloned successfully.' => 'Proyecto clonado correctamente.',
'Unable to clone this project.' => 'No se puede clonar este proyecto.',
'Enable email notifications' => 'Habilitar notificaciones por correo electrónico',
- 'Task position:' => 'Posición de la tarea',
- 'The task #%d have been opened.' => 'La tarea #%d ha sido abierta',
- 'The task #%d have been closed.' => 'La tarea #%d ha sido cerrada',
+ 'Task position:' => 'Posición de la tarea:',
+ 'The task #%d have been opened.' => 'La tarea #%d ha sido abierta.',
+ 'The task #%d have been closed.' => 'La tarea #%d ha sido cerrada.',
'Sub-task updated' => 'Subtarea actualizada',
'Title:' => 'Título:',
'Status:' => 'Estado:',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nueva subtarea',
'New attachment added "%s"' => 'Nuevo adjunto añadido «%s»',
'New comment posted by %s' => 'Nuevo comentario añadido por %s',
- 'New attachment' => 'Nuevo adjunto',
'New comment' => 'Nuevo comentario',
'Comment updated' => 'Comentario actualizado',
'New subtask' => 'Nueva subtarea',
- 'Subtask updated' => 'Subtarea actualizada',
- 'Task updated' => 'Tarea actualizada',
- 'Task closed' => 'Tarea cerrada',
- 'Task opened' => 'Tarea abierta',
'I want to receive notifications only for those projects:' => 'Quiero recibir notificaciones sólo de estos proyectos:',
'view the task on Kanboard' => 'ver la tarea en Kanboard',
'Public access' => 'Acceso público',
@@ -362,13 +352,12 @@ return array(
'Account type:' => 'Tipo de cuenta:',
'Edit profile' => 'Modificar perfil',
'Change password' => 'Cambiar contraseña',
- 'Password modification' => 'Modificacion de contraseña',
+ 'Password modification' => 'Modificación de contraseña',
'External authentications' => 'Autenticación externa',
'Never connected.' => 'Nunca se ha conectado.',
'No external authentication enabled.' => 'Sin autenticación externa activa.',
'Password modified successfully.' => 'Contraseña cambiada correctamente.',
'Unable to change the password.' => 'No se puede cambiar la contraseña.',
- 'Change category for the task "%s"' => 'Cambiar la categoría de la tarea «%s»',
'Change category' => 'Cambiar categoría',
'%s updated the task %s' => '%s actualizó la tarea %s',
'%s opened the task %s' => '%s abrió la tarea %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s actualizó la tarea #%d',
'%s created the task #%d' => '%s creó la tarea #%d',
'%s closed the task #%d' => '%s cerró la tarea #%d',
- '%s open the task #%d' => '%s abrió la tarea #%d',
- '%s moved the task #%d to the column "%s"' => '%s movió la tarea #%d a la columna «%s»',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s movió la tarea #%d a la posición %d de la columna «%s»',
+ '%s opened the task #%d' => '%s abrió la tarea #%d',
'Activity' => 'Actividad',
'Default values are "%s"' => 'Los valores por defecto son «%s»',
'Default columns for new projects (Comma-separated)' => 'Columnas por defecto para los nuevos proyectos (separadas mediante comas)',
'Task assignee change' => 'Cambiar responsable de la tarea',
- '%s change the assignee of the task #%d to %s' => '%s cambió el responsable de la tarea #%d por %s',
+ '%s changed the assignee of the task #%d to %s' => '%s cambió el responsable de la tarea #%d por %s',
'%s changed the assignee of the task %s to %s' => '%s cambió el responsable de la tarea %s por %s',
'New password for the user "%s"' => 'Nueva contraseña para el usuario «%s»',
'Choose an event' => 'Seleccione un evento',
@@ -411,13 +398,13 @@ return array(
'About' => 'Acerca de',
'Database driver:' => 'Controlador de la base de datos (driver):',
'Board settings' => 'Preferencias del tablero',
- 'Webhook settings' => 'Preferencias del disparador web (Webhook)',
+ 'Webhook settings' => 'Preferencias del disparador web (webhook)',
'Reset token' => 'Limpiar token',
'API endpoint:' => 'Endpoint del API:',
'Refresh interval for private board' => 'Intervalo de refresco del tablero privado',
'Refresh interval for public board' => 'Intervalo de refresco del tablero público',
'Task highlight period' => 'Periodo de realce de la tarea',
- 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (en segundos) para considerar que una tarea fué modificada recientemente (0 para deshabilitar, 2 días por defecto)',
+ 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (en segundos) para considerar que una tarea fue modificada recientemente (0 para deshabilitar, 2 días por defecto)',
'Frequency in second (60 seconds by default)' => 'Frecuencia en segundos (60 segundos por defecto)',
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frecuencia en segundos (0 para deshabilitar esta característica, 10 segundos por defecto)',
'Application URL' => 'URL de la aplicación',
@@ -435,7 +422,7 @@ return array(
'Dashboard' => 'Tablero',
'Confirmation' => 'Confirmación',
'Allow everybody to access to this project' => 'Permitir a cualquiera acceder a este proyecto',
- 'Everybody have access to this project.' => 'Cualquiera tiene acceso a este proyecto',
+ 'Everybody have access to this project.' => 'Cualquiera tiene acceso a este proyecto.',
'Webhooks' => 'Disparadores web (webhooks)',
'API' => 'API',
'Create a comment from an external provider' => 'Crear un comentario a partir de un proveedor externo',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Porcentaje',
'Number of tasks' => 'Número de tareas',
'Task distribution' => 'Distribución de tareas',
- 'Reportings' => 'Informes',
- 'Task repartition for "%s"' => 'Repartición de tareas para «%s»',
'Analytics' => 'Analítica',
'Subtask' => 'Subtarea',
'My subtasks' => 'Mis subtareas',
'User repartition' => 'Repartición de usuarios',
- 'User repartition for "%s"' => 'Repartición de usuarios para «%s»',
'Clone this project' => 'Clonar este proyecto',
'Column removed successfully.' => 'Columna eliminada correctamente.',
'Not enough data to show the graph.' => 'No hay suficiente información para mostrar el gráfico.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Este valor debe ser numérico',
'Unable to create this task.' => 'No se puede crear esta tarea.',
'Cumulative flow diagram' => 'Diagrama de flujo acumulativo',
- 'Cumulative flow diagram for "%s"' => 'Diagrama de flujo acumulativo para «%s»',
'Daily project summary' => 'Resumen diario del proyecto',
'Daily project summary export' => 'Exportar resumen diario del proyecto',
- 'Daily project summary export for "%s"' => 'Exportar resumen diario del proyecto para «%s»',
'Exports' => 'Exportaciones',
'This export contains the number of tasks per column grouped per day.' => 'Esta exportación contiene el número de tareas por columna agrupadas por día.',
'Active swimlanes' => 'Calles activas',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Eliminar una calle',
'Show default swimlane' => 'Mostrar calle por defecto',
'Swimlane modification for the project "%s"' => 'Modificación de la calle para el proyecto «%s»',
- 'Swimlane not found.' => 'Calle no encontrada.',
'Swimlane removed successfully.' => 'Calle eliminada correctamente.',
'Swimlanes' => 'Calles',
'Swimlane updated successfully.' => 'Calle actualizada correctamente.',
@@ -500,12 +481,11 @@ return array(
'Subtask Id' => 'Identificador de subtarea',
'Subtasks' => 'Subtareas',
'Subtasks Export' => 'Exportación de subtareas',
- 'Subtasks exportation for "%s"' => 'Exportación de subtareas para «%s»',
'Task Title' => 'Título de la tarea',
'Untitled' => 'Sin título',
'Application default' => 'Predefinido por la aplicación',
- 'Language:' => 'Idioma',
- 'Timezone:' => 'Zona horaria',
+ 'Language:' => 'Idioma:',
+ 'Timezone:' => 'Zona horaria:',
'All columns' => 'Todas las columnas',
'Calendar' => 'Calendario',
'Next' => 'Siguiente',
@@ -564,7 +544,7 @@ return array(
'%dh' => '%dh',
'Expand tasks' => 'Expandir tareas',
'Collapse tasks' => 'Colapsar tareas',
- 'Expand/collapse tasks' => 'Expande/colapasa tareas',
+ 'Expand/collapse tasks' => 'Expande/colapsa tareas',
'Close dialog box' => 'Cerrar caja de diálogo',
'Submit a form' => 'Enviar formulario',
'Board view' => 'Vista de tablero',
@@ -597,17 +577,17 @@ return array(
'Executer' => 'Ejecutor',
'Time spent in the column' => 'Tiempo transcurrido en la columna',
'Task transitions' => 'Transiciones de tarea',
- 'Task transitions export' => 'Eportar transiciones de tarea',
- 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este informe contiene todos los movimientos de columna para cada tarea con la fecha, el usuario y el tiempo transcurrido en cada trasición.',
+ 'Task transitions export' => 'Exportar transiciones de tarea',
+ 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este informe contiene todos los movimientos de columna para cada tarea con la fecha, el usuario y el tiempo transcurrido en cada transición.',
'Currency rates' => 'Cambio de monedas',
'Rate' => 'Cambio',
'Change reference currency' => 'Cambiar moneda de referencia',
'Add a new currency rate' => 'Añadir nuevo cambio de moneda',
'Reference currency' => 'Moneda de referencia',
- 'The currency rate have been added successfully.' => 'Se ha añadido el cambio de moneda correctamente.',
+ 'The currency rate have been added successfully.' => 'El cambio de moneda se ha añadido correctamente.',
'Unable to add this currency rate.' => 'No se puede añadir este cambio de moneda.',
'Webhook URL' => 'URL del disparador web (webhook)',
- '%s remove the assignee of the task %s' => '%s quita el responsable de la tarea %s',
+ '%s removed the assignee of the task %s' => '%s quita el responsable de la tarea %s',
'Enable Gravatar images' => 'Activar imágenes Gravatar',
'Information' => 'Información',
'Check two factor authentication code' => 'Revisar código de autenticación en dos pasos',
@@ -621,89 +601,82 @@ return array(
'Test your device' => 'Probar su dispositivo',
'Assign a color when the task is moved to a specific column' => 'Asignar un color al mover la tarea a una columna específica',
'%s via Kanboard' => '%s vía Kanboard',
- 'Burndown chart for "%s"' => 'Trabajo pendiente para «%s»',
'Burndown chart' => 'Trabajo pendiente',
- 'This chart show the task complexity over the time (Work Remaining).' => 'Este diagrama mestra la complejidad de la tarea a lo largo del tiempo (trabajo restante).',
+ 'This chart show the task complexity over the time (Work Remaining).' => 'Este diagrama muestra la complejidad de la tarea a lo largo del tiempo (trabajo restante).',
'Screenshot taken %s' => 'Pantallazo tomado el %s',
'Add a screenshot' => 'Añadir un pantallazo',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Capture un patallazo y pulse CTRL+V o ⌘+V para pegar aquí.',
'Screenshot uploaded successfully.' => 'Pantallazo cargado correctamente.',
'SEK - Swedish Krona' => 'SEK - Corona sueca',
'Identifier' => 'Identificador',
- 'Disable two factor authentication' => 'Desactivar la autenticación de dos factores',
- 'Do you really want to disable the two factor authentication for this user: "%s"?' => '¿Realmentes quiere desactuvar la autenticación de dos factores para este usuario: "%s?"',
+ 'Disable two factor authentication' => 'Desactivar la autenticación en dos pasos',
+ 'Do you really want to disable the two factor authentication for this user: "%s"?' => '¿Realmente desea desactivar la autenticación en dos pasos para este usuario: «%s»?',
'Edit link' => 'Modificar enlace',
'Start to type task title...' => 'Empiece a escribir el título de la tarea...',
- 'A task cannot be linked to itself' => 'Una tarea no puede se enlazada con sigo misma',
+ 'A task cannot be linked to itself' => 'Una tarea no se puede enlazar con sigo misma',
'The exact same link already exists' => 'El mismo enlace ya existe',
'Recurrent task is scheduled to be generated' => 'Tarea recurrente programada para ser generada',
'Score' => 'Puntuación',
'The identifier must be unique' => 'El identificador debe ser único',
- 'This linked task id doesn\'t exists' => 'El id de tarea no existe',
+ 'This linked task id doesn\'t exists' => 'El identificador de tarea no existe',
'This value must be alphanumeric' => 'Este valor debe ser alfanumérico',
'Edit recurrence' => 'Modificar repetición',
- 'Generate recurrent task' => 'Generar tarea recurrente',
- 'Trigger to generate recurrent task' => 'Disparador para generar tarea recurrente',
+ 'Generate recurrent task' => 'Generar una tarea recurrente',
+ 'Trigger to generate recurrent task' => 'Disparador para generar una tarea recurrente',
'Factor to calculate new due date' => 'Factor para calcular la nueva fecha de entrega',
'Timeframe to calculate new due date' => 'Calendario para calcular la nueva fecha de entrega',
'Base date to calculate new due date' => 'Fecha base para calcular la nueva fecha de entrega',
'Action date' => 'Fecha de la acción',
- 'Base date to calculate new due date: ' => 'Fecha base para calcular la nueva fecha de entrega:',
- 'This task has created this child task: ' => 'Esta tarea ha cerado esta tarea hija:',
+ 'Base date to calculate new due date: ' => 'Fecha base para calcular la nueva fecha de entrega: ',
+ 'This task has created this child task: ' => 'Esta tarea ha creado esta tarea hija: ',
'Day(s)' => 'Día(s)',
'Existing due date' => 'Fecha de entrega existente',
- 'Factor to calculate new due date: ' => 'Factor para calcular la nueva fecha de entrega:',
+ 'Factor to calculate new due date: ' => 'Factor para calcular la nueva fecha de entrega: ',
'Month(s)' => 'Mes(es)',
'Recurrence' => 'Repetición',
'This task has been created by: ' => 'Esta tarea ha sido creada por: ',
'Recurrent task has been generated:' => 'Tarea recurrente generada:',
- 'Timeframe to calculate new due date: ' => 'Calendario para calcular la nueva fecha de entrega:',
- 'Trigger to generate recurrent task: ' => 'Disparador para generar tarea recurrente',
+ 'Timeframe to calculate new due date: ' => 'Calendario para calcular la nueva fecha de entrega: ',
+ 'Trigger to generate recurrent task: ' => 'Disparador para generar una tarea recurrente: ',
'When task is closed' => 'Cuando la tarea es cerrada',
'When task is moved from first column' => 'Cuando la tarea es movida desde la primera columna',
'When task is moved to last column' => 'Cuando la tarea es movida a la última columna',
'Year(s)' => 'Año(s)',
- 'Calendar settings' => 'Parámetros del Calendario',
- 'Project calendar view' => 'Vista de Calendario para el Proyecto',
- 'Project settings' => 'Parámetros del Proyecto',
- 'Show subtasks based on the time tracking' => 'Mostrar subtareas en base al seguimiento de tiempo',
+ 'Calendar settings' => 'Preferencias del calendario',
+ 'Project calendar view' => 'Vista de calendario del proyecto',
+ 'Project settings' => 'Preferencias del proyecto',
+ 'Show subtasks based on the time tracking' => 'Mostrar subtareas en base al seguimiento temporal',
'Show tasks based on the creation date' => 'Mostrar tareas en base a la fecha de creación',
- 'Show tasks based on the start date' => 'Mostrar tareas en base a la fecha de comienzo',
+ 'Show tasks based on the start date' => 'Mostrar tareas en base a la fecha de inicio',
'Subtasks time tracking' => 'Seguimiento de tiempo en subtareas',
- 'User calendar view' => 'Vista de Calendario para el Usuario',
- 'Automatically update the start date' => 'Actualizar automáticamente la fecha de comienzo',
+ 'User calendar view' => 'Vista de calendario del usuario',
+ 'Automatically update the start date' => 'Actualizar automáticamente la fecha de inicio',
'iCal feed' => 'Fuente iCal',
'Preferences' => 'Preferencias',
'Security' => 'Seguridad',
- 'Two factor authentication disabled' => 'Autenticación de dos factores deshabilitada',
- 'Two factor authentication enabled' => 'Autenticación de dos factores habilitada',
+ 'Two factor authentication disabled' => 'Autenticación en dos pasos deshabilitada',
+ 'Two factor authentication enabled' => 'Autenticación en dos pasos habilitada',
'Unable to update this user.' => 'No se puede actualizar este usuario.',
'There is no user management for private projects.' => 'No hay gestión de usuarios para proyectos privados.',
'User that will receive the email' => 'Usuario que recibirá el correo',
'Email subject' => 'Asunto del correo',
'Date' => 'Fecha',
'Add a comment log when moving the task between columns' => 'Añadir un comentario al mover la tarea entre columnas',
- 'Move the task to another column when the category is changed' => 'Mover la tarea a otra columna cuando cambia la categoría',
+ 'Move the task to another column when the category is changed' => 'Mover la tarea a otra columna cuando cambie la categoría',
'Send a task by email to someone' => 'Enviar una tarea a alguien por correo',
'Reopen a task' => 'Reabrir tarea',
- 'Column change' => 'Cambio de columna',
- 'Position change' => 'Cambio de posición',
- 'Swimlane change' => 'Cambio de calle',
- 'Assignee change' => 'Cambio de responsable',
- '[%s] Overdue tasks' => '[%s] Tareas vencidas',
'Notification' => 'Notificación',
'%s moved the task #%d to the first swimlane' => '%s movió la tarea #%d a la primera calle',
- '%s moved the task #%d to the swimlane "%s"' => '%s movió la tarea #%d a la calle «%s»',
'Swimlane' => 'Calle',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s movió la tarea %s a la primera calle',
'%s moved the task %s to the swimlane "%s"' => '%s movió la tarea %s a la calle «%s»',
- 'This report contains all subtasks information for the given date range.' => 'Este informe contiene todas la información de las subtareas para el rango proporcionado de fechas.',
- 'This report contains all tasks information for the given date range.' => 'Este informe contiene todas la información de las tareas para el rango proporcionado de fechas.',
+ 'This report contains all subtasks information for the given date range.' => 'Este informe contiene toda la información de las subtareas para el rango de fechas proporcionado.',
+ 'This report contains all tasks information for the given date range.' => 'Este informe contiene toda la información de las tareas para el rango de fechas proporcionado.',
'Project activities for %s' => 'Actividades del proyecto para %s',
'view the board on Kanboard' => 'ver el tablero en Kanboard',
'The task have been moved to the first swimlane' => 'Se ha movido la tarea a la primera calle',
- 'The task have been moved to another swimlane:' => 'Se ha movido la tarea a otra calle',
+ 'The task have been moved to another swimlane:' => 'Se ha movido la tarea a otra calle:',
'New title: %s' => 'Nuevo título: %s',
'The task is not assigned anymore' => 'La tarea ya no está asignada',
'New assignee: %s' => 'Nuevo responsable: %s',
@@ -717,46 +690,46 @@ return array(
'Time spent changed: %sh' => 'Se ha cambiado el tiempo empleado: %sh',
'Time estimated changed: %sh' => 'Se ha cambiado el tiempo estimado: %sh',
'The field "%s" have been updated' => 'Se ha actualizado el campo «%s»',
- 'The description has been modified:' => 'Se ha modificado la descripción',
+ 'The description has been modified:' => 'Se ha modificado la descripción:',
'Do you really want to close the task "%s" as well as all subtasks?' => '¿Realmente desea cerrar la tarea «%s» así como todas las subtareas?',
'I want to receive notifications for:' => 'Deseo recibir notificaciones para:',
'All tasks' => 'Todas las tareas',
'Only for tasks assigned to me' => 'Sólo para las tareas que me han sido asignadas',
- 'Only for tasks created by me' => 'Sólo para las taread creadas por mí',
- 'Only for tasks created by me and assigned to me' => 'Sólo para las tareas credas por mí y que me han sido asignadas',
+ 'Only for tasks created by me' => 'Sólo para las tareas creadas por mí',
+ 'Only for tasks created by me and assigned to me' => 'Sólo para las tareas creadas por mí y que me han sido asignadas',
'%%Y-%%m-%%d' => '%%d/%%M/%%Y',
'Total for all columns' => 'Total para todas las columnas',
'You need at least 2 days of data to show the chart.' => 'Necesitas al menos 2 días de datos para mostrar el gráfico.',
'<15m' => '<15m',
'<30m' => '<30m',
'Stop timer' => 'Parar temporizador',
- 'Start timer' => 'Arrancar temporizador',
+ 'Start timer' => 'Iniciar temporizador',
'Add project member' => 'Añadir miembro al proyecto',
'My activity stream' => 'Mi flujo de actividad',
'My calendar' => 'Mi calendario',
'Search tasks' => 'Buscar tareas',
'Reset filters' => 'Limpiar filtros',
'My tasks due tomorrow' => 'Mis tareas a entregar mañana',
- 'Tasks due today' => 'Tareas a antregar hoy',
- 'Tasks due tomorrow' => 'Taraes a entregar mañana',
+ 'Tasks due today' => 'Tareas a entregar hoy',
+ 'Tasks due tomorrow' => 'Tareas a entregar mañana',
'Tasks due yesterday' => 'Tareas a entregar ayer',
'Closed tasks' => 'Tareas cerradas',
'Open tasks' => 'Tareas abiertas',
'Not assigned' => 'No asignada',
- 'View advanced search syntax' => 'Ver sintáxis avanzada de búsqueda',
+ 'View advanced search syntax' => 'Ver sintaxis de búsqueda avanzada',
'Overview' => 'Resumen',
- 'Board/Calendar/List view' => 'Vista de Tablero/Calendario/Lista',
+ 'Board/Calendar/List view' => 'Vista de tablero/calendario/lista',
'Switch to the board view' => 'Cambiar a vista de tablero',
'Switch to the calendar view' => 'Cambiar a vista de calendario',
'Switch to the list view' => 'Cambiar a vista de lista',
'Go to the search/filter box' => 'Ir a caja de buscar/filtrar',
'There is no activity yet.' => 'Aún no hay actividades.',
- 'No tasks found.' => 'No se ha hallado tarea alguna.',
+ 'No tasks found.' => 'No se ha encontrado ninguna tarea.',
'Keyboard shortcut: "%s"' => 'Atajo de teclado: %s',
'List' => 'Lista',
'Filter' => 'Filtro',
'Advanced search' => 'Búsqueda avanzada',
- 'Example of query: ' => 'Ejemplo de query: ',
+ 'Example of query: ' => 'Ejemplo de consulta: ',
'Search by project: ' => 'Buscar por proyecto: ',
'Search by column: ' => 'Buscar por columna: ',
'Search by assignee: ' => 'Buscar por responsable: ',
@@ -764,36 +737,34 @@ return array(
'Search by category: ' => 'Buscar por categoría: ',
'Search by description: ' => 'Buscar por descripción: ',
'Search by due date: ' => 'Buscar por fecha de entrega: ',
- 'Lead and Cycle time for "%s"' => 'Plazo de Entrega y Ciclo para «%s»',
- 'Average time spent into each column for "%s"' => 'Tiempo medio empleado en cada columna para «%s»',
'Average time spent into each column' => 'Tiempo medio empleado en cada columna',
'Average time spent' => 'Tiempo medio empleado',
'This chart show the average time spent into each column for the last %d tasks.' => 'Esta gráfica muestra el tiempo medio empleado en cada columna para las últimas %d tareas.',
- 'Average Lead and Cycle time' => 'Plazo Medio de Entrega y de Ciclo',
- 'Average lead time: ' => 'Plazo Medio de entrega: ',
- 'Average cycle time: ' => 'Tiempo Medio de Ciclo: ',
- 'Cycle Time' => 'Tiempo de Ciclo',
- 'Lead Time' => 'Plazo de Entrega',
- 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Esta gráfica muestra el plazo medio de entrega y de ciclo para las %d últimas tareas transcurridas.',
+ 'Average Lead and Cycle time' => 'Plazo medio de entrega y de ciclo',
+ 'Average lead time: ' => 'Plazo medio de entrega: ',
+ 'Average cycle time: ' => 'Tiempo medio de ciclo: ',
+ 'Cycle Time' => 'Tiempo de ciclo',
+ 'Lead Time' => 'Plazo de entrega',
+ 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Esta gráfica muestra el plazo medio de entrega y de ciclo para las %d últimas tareas.',
'Average time into each column' => 'Tiempo medio en cada columna',
'Lead and cycle time' => 'Plazo de entrega y de ciclo',
'Lead time: ' => 'Plazo de entrega: ',
- 'Cycle time: ' => 'Tiempo de Ciclo: ',
+ 'Cycle time: ' => 'Tiempo de ciclo: ',
'Time spent into each column' => 'Tiempo empleado en cada columna',
- 'The lead time is the duration between the task creation and the completion.' => 'El plazo de entrega es la duración entre la creación de la tarea su terminación.',
- 'The cycle time is the duration between the start date and the completion.' => 'El tiempo de ciclo es la duración entre la fecha de inicio y su terminación.',
- 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tarea no se cierra, se usa la fecha actual en lugar de la de terminación.',
+ 'The lead time is the duration between the task creation and the completion.' => 'El plazo de entrega es la duración entre la creación de la tarea su finalización.',
+ 'The cycle time is the duration between the start date and the completion.' => 'El tiempo de ciclo es la duración entre la fecha de inicio y su finalización.',
+ 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tarea no se cierra, se usa la fecha actual en lugar de la de finalización.',
'Set automatically the start date' => 'Poner la fecha de inicio de forma automática',
'Edit Authentication' => 'Modificar autenticación',
'Remote user' => 'Usuario remoto',
- 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Los usuarios remotos no almacenan sus contraseñas en la base de datos Kanboard, por ejemplo: cuentas de LDAP, Google y Github',
- 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marcas la caja de edición "Desactivar formulario de ingreso", se ignoran las credenciales entradas en el formulario de ingreso.',
+ 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Los usuarios remotos no almacenan sus contraseñas en la base de datos Kanboard, por ejemplo: cuentas de LDAP, Google y Github.',
+ 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marcas la opción "Desactivar formulario de ingreso", se ignoran las credenciales introducidas en el formulario de ingreso.',
'New remote user' => 'Nuevo usuario remoto',
'New local user' => 'Nuevo usuario local',
- 'Default task color' => 'Color por defecto de tarea',
- 'This feature does not work with all browsers.' => 'Esta característica no funciona con todos los navegadores',
- 'There is no destination project available.' => 'No está disponible proyecto destino',
- 'Trigger automatically subtask time tracking' => 'Disparar de forma automática seguimiento temporal de subtarea',
+ 'Default task color' => 'Color de la tarea por defecto',
+ 'This feature does not work with all browsers.' => 'Esta característica no funciona en todos los navegadores.',
+ 'There is no destination project available.' => 'No está disponible proyecto de destino.',
+ 'Trigger automatically subtask time tracking' => 'Disparar de forma automática el seguimiento temporal de subtarea',
'Include closed tasks in the cumulative flow diagram' => 'Incluir tareas cerradas en el diagrama de flujo acumulado',
'Current swimlane: %s' => 'Calle en curso: %s',
'Current column: %s' => 'Columna en curso: %s',
@@ -805,20 +776,19 @@ return array(
'contributors' => 'contribuyentes',
'License:' => 'Licencia:',
'License' => 'Licencia',
- 'Enter the text below' => 'Digita el texto de abajo',
- 'Gantt chart for %s' => 'Diagrama de Gantt para %s',
- 'Sort by position' => 'Clasificado mediante posición',
- 'Sort by date' => 'Clasificado mediante fecha',
+ 'Enter the text below' => 'Introduzca el texto a continuación',
+ 'Sort by position' => 'Ordenar por posición',
+ 'Sort by date' => 'Ordenar por fecha',
'Add task' => 'Añadir tarea',
'Start date:' => 'Fecha de inicio:',
'Due date:' => 'Fecha de entrega:',
'There is no start date or due date for this task.' => 'No hay fecha de inicio o de entrega para esta tarea.',
- 'Moving or resizing a task will change the start and due date of the task.' => 'El mover o redimensionar una tarea cambiará la fecha inicio y de entrega de la misma.',
+ 'Moving or resizing a task will change the start and due date of the task.' => 'Mover o redimensionar una tarea cambiará la fecha inicio y de entrega de la misma.',
'There is no task in your project.' => 'No hay tareas en su proyecto.',
- 'Gantt chart' => 'Digrama de Gantt',
- 'People who are project managers' => 'Usuarios que son administradores de proyecto',
- 'People who are project members' => 'Usuarios que son miembros de proyecto',
- 'NOK - Norwegian Krone' => 'NOK - Coronoa Noruega',
+ 'Gantt chart' => 'Diagrama de Gantt',
+ 'People who are project managers' => 'Usuarios que son administradores del proyecto',
+ 'People who are project members' => 'Usuarios que son miembros del proyecto',
+ 'NOK - Norwegian Krone' => 'NOK - Corona Noruega',
'Show this column' => 'Mostrar esta columna',
'Hide this column' => 'Ocultar esta columna',
'open file' => 'abrir fichero',
@@ -826,80 +796,78 @@ return array(
'Users overview' => 'Resumen de usuarios',
'Members' => 'Miembros',
'Shared project' => 'Proyecto compartido',
- 'Project managers' => 'Administradores de proyecto',
+ 'Project managers' => 'Administradores del proyecto',
'Gantt chart for all projects' => 'Diagrama de Gantt para todos los proyectos',
'Projects list' => 'Lista de proyectos',
'Gantt chart for this project' => 'Diagrama de Gantt para este proyecto',
'Project board' => 'Tablero del proyecto',
- 'End date:' => 'Fecha final',
+ 'End date:' => 'Fecha de fin:',
'There is no start date or end date for this project.' => 'No existe fecha de inicio o de fin para este proyecto.',
- 'Projects Gantt chart' => 'Diagramas de Gantt de los proyectos',
- 'Change task color when using a specific task link' => 'Cambiar colo de la tarea al usar un enlace específico a tarea',
- 'Task link creation or modification' => 'Creación o modificación de enlace a tarea',
+ 'Projects Gantt chart' => 'Diagrama de Gantt de los proyectos',
+ 'Change task color when using a specific task link' => 'Cambiar el color de la tarea al usar un enlace específico a tarea',
+ 'Task link creation or modification' => 'Creación o modificación del enlace a tarea',
'Milestone' => 'Hito',
'Documentation: %s' => 'Documentación: %s',
- 'Switch to the Gantt chart view' => 'Conmutar a vista de diagrama de Gantt',
- 'Reset the search/filter box' => 'Limpiar la caja del filtro de búsqueda',
+ 'Switch to the Gantt chart view' => 'Cambiar a vista de diagrama de Gantt',
+ 'Reset the search/filter box' => 'Limpiar el filtro de búsqueda',
'Documentation' => 'Documentación',
'Table of contents' => 'Tabla de contenido',
'Gantt' => 'Gantt',
'Author' => 'Autor',
'Version' => 'Versión',
'Plugins' => 'Plugins',
- 'There is no plugin loaded.' => 'No hay ningún plugin cargado',
- 'Set maximum column height' => 'Establecer altura máxima de la columna',
- 'Remove maximum column height' => 'Eliminar altura máxima de la columna',
+ 'There is no plugin loaded.' => 'No hay ningún plugin cargado.',
'My notifications' => 'Mis notificaciones',
'Custom filters' => 'Filtros personalizados',
'Your custom filter have been created successfully.' => 'Tus filtros personalizados han sido creados correctamente.',
'Unable to create your custom filter.' => 'No se ha podido crear tu filtro personalizado.',
- 'Custom filter removed successfully.' => 'Filtro personalizado ha sido eliminado correctamente.',
- 'Unable to remove this custom filter.' => 'No se ha podido eliminar tu filtro personalizado',
+ 'Custom filter removed successfully.' => 'El filtro personalizado ha sido eliminado correctamente.',
+ 'Unable to remove this custom filter.' => 'No se ha podido eliminar tu filtro personalizado.',
'Edit custom filter' => 'Modificar filtro personalizado',
'Your custom filter have been updated successfully.' => 'Tu filtro personalizado ha sido actualizado correctamente.',
- 'Unable to update custom filter.' => 'No se ha podido actualizar tu filtro personalizado',
+ 'Unable to update custom filter.' => 'No se ha podido actualizar tu filtro personalizado.',
'Web' => 'Web',
'New attachment on task #%d: %s' => 'Nuevo adjunto en la tarea #%d: %s',
'New comment on task #%d' => 'Nuevo comentario en la tarea #%d',
'Comment updated on task #%d' => 'Comentario actualizado en la tarea #%d',
'New subtask on task #%d' => 'Nueva subtarea en la tarea #%d',
- 'Subtask updated on task #%d' => 'La subtarea en la tarea #%d ha sido actualizada',
+ 'Subtask updated on task #%d' => 'Subtarea actualizada en la tarea #%d',
'New task #%d: %s' => 'Nueva tarea #%d: %s',
'Task updated #%d' => 'Tarea actualizada #%d',
- 'Task #%d closed' => 'Tarea #%d ha sido cerrada',
- 'Task #%d opened' => 'Tarea #%d ha sido abierta',
- 'Column changed for task #%d' => 'Columna para tarea #%d ha sido cambiada',
+ 'Task #%d closed' => 'Tarea #%d cerrada',
+ 'Task #%d opened' => 'Tarea #%d abierta',
+ 'Column changed for task #%d' => 'Columna cambiada para la tarea #%d',
'New position for task #%d' => 'Nueva posición para tarea #%d',
- 'Swimlane changed for task #%d' => 'Se cambió el swimlane de la tarea #%d',
- 'Assignee changed on task #%d' => 'Se cambió el asignado de la tarea #%d',
+ 'Swimlane changed for task #%d' => 'Se cambió la calle de la tarea #%d',
+ 'Assignee changed on task #%d' => 'Se cambió el responsable de la tarea #%d',
'%d overdue tasks' => '%d tareas atrasadas',
'Task #%d is overdue' => 'La tarea #%d está atrasada',
- 'No new notifications.' => 'No hay nuevas notificaciones',
+ 'No new notifications.' => 'No hay nuevas notificaciones.',
'Mark all as read' => 'Marcar todo como leído',
'Mark as read' => 'Marcar como leído',
- 'Total number of tasks in this column across all swimlanes' => 'Número total de tareas en esta columna por todas las swimlanes',
- 'Collapse swimlane' => 'Contraer swimlane',
- 'Expand swimlane' => 'Ampliar swimlane',
+ 'Total number of tasks in this column across all swimlanes' => 'Número total de tareas en esta columna a través de todas las calles',
+ 'Collapse swimlane' => 'Contraer calle',
+ 'Expand swimlane' => 'Ampliar calle',
'Add a new filter' => 'Añadir nuevo filtro',
'Share with all project members' => 'Compartir con todos los miembros del proyecto',
'Shared' => 'Compartido',
- 'Owner' => 'Dueño',
+ 'Owner' => 'Propietario',
'Unread notifications' => 'Notificaciones sin leer',
- 'Notification methods:' => 'Métodos de notificación',
+ 'Notification methods:' => 'Métodos de notificación:',
'Import tasks from CSV file' => 'Importar tareas desde archivo CSV',
'Unable to read your file' => 'No es posible leer el archivo',
- '%d task(s) have been imported successfully.' => '%d tarea(s) han sido importadas correctamente',
- 'Nothing have been imported!' => 'No se ha importado nada!',
+ '%d task(s) have been imported successfully.' => '%d tarea(s) han sido importadas correctamente.',
+ 'Nothing have been imported!' => '¡No se ha importado nada!',
'Import users from CSV file' => 'Importar usuarios desde archivo CSV',
- '%d user(s) have been imported successfully.' => '%d usuario(s) se han importado correctamente',
+ '%d user(s) have been imported successfully.' => '%d usuario(s) se han importado correctamente.',
'Comma' => 'Coma',
'Semi-colon' => 'Punto y coma',
'Tab' => 'Tabulación',
- 'Vertical bar' => 'Pleca',
+ 'Vertical bar' => 'Barra vertical',
'Double Quote' => 'Comilla doble',
- 'Single Quote' => 'Comilla sencilla',
- '%s attached a file to the task #%d' => '%s adjuntó un archivo a la tarea #%d',
- 'There is no column or swimlane activated in your project!' => 'No hay ninguna columna o swimlane activada en su proyecto!',
+ 'Single Quote' => 'Comilla simple',
+ '%s attached a file to the task #%d' => '%s adjuntó un archivo en la tarea #%d',
+ 'There is no column or swimlane activated in your project!' => '¡No hay ninguna columna o calle activada en su proyecto!',
'Append filter (instead of replacement)' => 'Añadir filtro (en vez de reemplazar)',
'Append/Replace' => 'Añadir/Reemplazar',
'Append' => 'Añadir',
@@ -912,92 +880,88 @@ return array(
'CSV File' => 'Archivo CSV',
'Instructions' => 'Indicaciones',
'Your file must use the predefined CSV format' => 'Su archivo debe utilizar el formato CSV predeterminado',
- 'Your file must be encoded in UTF-8' => 'Su archivo debe ser codificado en UTF-8',
+ 'Your file must be encoded in UTF-8' => 'Su archivo debe estar codificado en UTF-8',
'The first row must be the header' => 'La primera fila debe ser el encabezado',
'Duplicates are not verified for you' => 'Los duplicados no serán verificados',
'The due date must use the ISO format: YYYY-MM-DD' => 'La fecha de entrega debe utilizar el formato ISO: AAAA-MM-DD',
'Download CSV template' => 'Descargar plantilla CSV',
- 'No external integration registered.' => 'No se ha registrado integración externa',
+ 'No external integration registered.' => 'No se ha registrado integración externa.',
'Duplicates are not imported' => 'Los duplicados no son importados',
- 'Usernames must be lowercase and unique' => 'Los nombres de usuario deben ser únicos y contener sólo minúsculas',
+ 'Usernames must be lowercase and unique' => 'Los nombres de usuario deben ser únicos y en minúsculas',
'Passwords will be encrypted if present' => 'Las contraseñas serán cifradas si es que existen',
- '%s attached a new file to the task %s' => '%s adjuntó un nuevo archivo a la tarea %s',
+ '%s attached a new file to the task %s' => '%s adjuntó un nuevo archivo en la tarea %s',
'Link type' => 'Tipo de enlace',
'Assign automatically a category based on a link' => 'Asignar una categoría automáticamente basado en un enlace',
- 'BAM - Konvertible Mark' => 'BAM - marco convertible',
+ 'BAM - Konvertible Mark' => 'BAM - Marco convertible',
'Assignee Username' => 'Nombre de usuario del responsable',
'Assignee Name' => 'Nombre del responsable',
'Groups' => 'Grupos',
'Members of %s' => 'Miembros de %s',
'New group' => 'Nuevo grupo',
- 'Group created successfully.' => 'Grupo creado correctamente',
- 'Unable to create your group.' => 'No es posible crear el grupo',
+ 'Group created successfully.' => 'Grupo creado correctamente.',
+ 'Unable to create your group.' => 'No es posible crear el grupo.',
'Edit group' => 'Modificar grupo',
- 'Group updated successfully.' => 'Grupo actualizado correctamente',
- 'Unable to update your group.' => 'No es posible actualizar el grupo',
+ 'Group updated successfully.' => 'Grupo actualizado correctamente.',
+ 'Unable to update your group.' => 'No es posible actualizar el grupo.',
'Add group member to "%s"' => 'Añadir un miembro del grupo a «%s»',
- 'Group member added successfully.' => 'Miembro del grupo añadido correctamente',
- 'Unable to add group member.' => 'No es posible añadir miembro del grupo',
+ 'Group member added successfully.' => 'Miembro del grupo añadido correctamente.',
+ 'Unable to add group member.' => 'No es posible añadir el miembro del grupo.',
'Remove user from group "%s"' => 'Eliminar usuario del grupo «%s»',
- 'User removed successfully from this group.' => 'Usuario eliminado correctamente del grupo',
- 'Unable to remove this user from the group.' => 'No es posible eliminar este usuario del grupo',
+ 'User removed successfully from this group.' => 'Usuario eliminado correctamente del grupo.',
+ 'Unable to remove this user from the group.' => 'No es posible eliminar este usuario del grupo.',
'Remove group' => 'Eliminar grupo',
- 'Group removed successfully.' => 'Grupo eliminado correctamente',
- 'Unable to remove this group.' => 'No es posible eliminar este grupo',
+ 'Group removed successfully.' => 'Grupo eliminado correctamente.',
+ 'Unable to remove this group.' => 'No es posible eliminar este grupo.',
'Project Permissions' => 'Permisos del proyecto',
'Manager' => 'Administrador',
- 'Project Manager' => 'Administrador de proyecto',
+ 'Project Manager' => 'Administrador del proyecto',
'Project Member' => 'Miembro del proyecto',
- 'Project Viewer' => 'Visor de proyectos',
- 'Your account is locked for %d minutes' => 'Tu cuenta ha sido bloqueada por %d minuto(s)',
+ 'Project Viewer' => 'Observador del proyecto',
+ 'Your account is locked for %d minutes' => 'Tu cuenta ha sido bloqueada durante %d minutos',
'Invalid captcha' => 'CAPTCHA inválido',
'The name must be unique' => 'El nombre debe ser único',
'View all groups' => 'Ver todos los grupos',
- 'View group members' => 'Ver miembros del grupo',
- 'There is no user available.' => 'No hay usuario disponible',
- 'Do you really want to remove the user "%s" from the group "%s"?' => '¿Realmente desea eliminar el usuario "%s" del grupo «%s»?',
- 'There is no group.' => 'No hay grupo',
- 'External Id' => 'ID externo',
- 'Add group member' => 'Añadir un miembro de grupo',
+ 'There is no user available.' => 'No hay usuario disponible.',
+ 'Do you really want to remove the user "%s" from the group "%s"?' => '¿Realmente desea eliminar el usuario «%s» del grupo «%s»?',
+ 'There is no group.' => 'No hay grupo.',
+ 'External Id' => 'Identificador externo',
+ 'Add group member' => 'Añadir un miembro al grupo',
'Do you really want to remove this group: "%s"?' => '¿Realmente desea eliminar este grupo: «%s»?',
- 'There is no user in this group.' => 'No hay usuario en este grupo',
+ 'There is no user in this group.' => 'No hay usuario en este grupo.',
'Remove this user' => 'Eliminar este usuario',
'Permissions' => 'Permisos',
'Allowed Users' => 'Usuarios permitidos',
- 'No user have been allowed specifically.' => 'Ningun usuario ha sido explícitamente permitido',
+ 'No user have been allowed specifically.' => 'Ningún usuario ha sido explícitamente permitido.',
'Role' => 'Rol',
'Enter user name...' => 'Ingresa nombre de usuario...',
'Allowed Groups' => 'Grupos permitidos',
- 'No group have been allowed specifically.' => 'Ningun grupo ha sido explícitamente permitido',
+ 'No group have been allowed specifically.' => 'Ningún grupo ha sido explícitamente permitido.',
'Group' => 'Grupo',
'Group Name' => 'Nombre del grupo',
'Enter group name...' => 'Ingresa el nombre del grupo...',
'Role:' => 'Rol:',
'Project members' => 'Miembros del proyecto',
- 'Compare hours for "%s"' => 'Compara horas con «%s»',
'%s mentioned you in the task #%d' => '%s te mencionó en la tarea #%d',
'%s mentioned you in a comment on the task #%d' => '%s te mencionó en un comentario en la tarea #%d',
- 'You were mentioned in the task #%d' => 'Te mencionaron en la tarea #%d',
- 'You were mentioned in a comment on the task #%d' => 'Te mencionaron en un comentario en la tarea #%d',
- 'Mentioned' => 'Mencionado',
- 'Compare Estimated Time vs Actual Time' => 'Comparar Tiempo Estimado vs Tiempo Actual',
+ 'You were mentioned in the task #%d' => 'Fuiste mencionado en la tarea #%d',
+ 'You were mentioned in a comment on the task #%d' => 'Fuiste mencionado en un comentario de la tarea #%d',
'Estimated hours: ' => 'Horas estimadas: ',
'Actual hours: ' => 'Horas actuales: ',
'Hours Spent' => 'Horas gastadas',
- 'Hours Estimated' => 'Hora Estimada',
- 'Estimated Time' => 'Tiempo Estimado',
- 'Actual Time' => 'Tiempo Actual',
+ 'Hours Estimated' => 'Horas estimadas',
+ 'Estimated Time' => 'Tiempo estimado',
+ 'Actual Time' => 'Tiempo actual',
'Estimated vs actual time' => 'Tiempo estimado vs real',
- 'RUB - Russian Ruble' => 'RUB - rublo ruso',
- 'Assign the task to the person who does the action when the column is changed' => 'Asignar la tarea a la persona que haga la acción al cambiar de columna',
+ 'RUB - Russian Ruble' => 'RUB - Rublo ruso',
+ 'Assign the task to the person who does the action when the column is changed' => 'Asignar la tarea a la persona que hace la acción al cambiar de columna',
'Close a task in a specific column' => 'Cerrar tarea en una columna especifica',
'Time-based One-time Password Algorithm' => 'Algoritmo basado en tiempo de un solo uso',
- 'Two-Factor Provider: ' => 'Proveedor de autenticación de dos factores',
- 'Disable two-factor authentication' => 'Deshabilitar autenticación de dos factores',
- 'Enable two-factor authentication' => 'Habilitar autenticación de dos factorse',
- 'There is no integration registered at the moment.' => 'No hay ninguna integración registrada por el momento',
+ 'Two-Factor Provider: ' => 'Proveedor de autenticación en dos pasos: ',
+ 'Disable two-factor authentication' => 'Deshabilitar autenticación en dos pasos',
+ 'Enable two-factor authentication' => 'Habilitar autenticación en dos pasos',
+ 'There is no integration registered at the moment.' => 'No hay ninguna integración registrada por el momento.',
'Password Reset for Kanboard' => 'Restablecimiento de contraseña para Kanboard',
- 'Forgot password?' => '¿Olvidó contraseña?',
+ 'Forgot password?' => '¿Olvidó la contraseña?',
'Enable "Forget Password"' => 'Habilitar "olvidar contraseña"',
'Password Reset' => 'Restablecer contraseña',
'New password' => 'Nueva contraseña',
@@ -1008,19 +972,19 @@ return array(
'Creation' => 'Creación',
'Expiration' => 'Vencimiento',
'Password reset history' => 'Historial de restablecimiento de contraseña',
- 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas las tareas de la columna "%s" y el swimlane «%s» se han cerrado correctamente',
+ 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas las tareas de la columna "%s" y la calle «%s» se han cerrado correctamente.',
'Do you really want to close all tasks of this column?' => '¿Realmente desea cerrar todas las tareas de esta columna?',
- '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarea(s) en la columna "%s" y el swimlane «%s» será(n) cerrada(s)',
+ '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarea(s) en la columna "%s" y en la calle «%s» será(n) cerrada(s).',
'Close all tasks of this column' => 'Cerrar todas las tareas de esta columna',
- 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Ningún plugin ha registrado un método de notificación para el proyecto. Aún puedes configurar notificaciones individuales en tu perfil de usuario',
+ 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Ningún plugin ha registrado un método de notificación para el proyecto. Aún puedes configurar notificaciones individuales en tu perfil de usuario.',
'My dashboard' => 'Mi tablero',
'My profile' => 'Mi perfil',
- 'Project owner: ' => 'Dueño del proyecto',
- 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'El identificador de proyecto es opcional y debe ser alfanumérico. Ejemplo: MIPROYECTO',
- 'Project owner' => 'Dueño del proyecto',
- 'Those dates are useful for the project Gantt chart.' => 'Esas fechas son útiles para el diagrama de Gantt',
- 'Private projects do not have users and groups management.' => 'Proyectos privados no cuentan con gestión de usuarios y grupos',
- 'There is no project member.' => 'No existe miembro del proyecto',
+ 'Project owner: ' => 'Propietario del proyecto: ',
+ 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'El identificador del proyecto es opcional y debe ser alfanumérico. Ejemplo: MIPROYECTO',
+ 'Project owner' => 'Propietario del proyecto',
+ 'Those dates are useful for the project Gantt chart.' => 'Esas fechas son útiles para el diagrama de Gantt.',
+ 'Private projects do not have users and groups management.' => 'Los proyectos privados no cuentan con gestión de usuarios y grupos.',
+ 'There is no project member.' => 'No existe miembro del proyecto.',
'Priority' => 'Prioridad',
'Task priority' => 'Prioridad de la tarea',
'General' => 'General',
@@ -1028,16 +992,16 @@ return array(
'Default priority' => 'Prioridad predeterminada',
'Lowest priority' => 'Prioridad más baja',
'Highest priority' => 'Prioridad más alta',
- 'If you put zero to the low and high priority, this feature will be disabled.' => 'Si estableces la prioridad más baja y alta como cero esta función será deshabilitada',
+ 'If you put zero to the low and high priority, this feature will be disabled.' => 'Si estableces la prioridad más baja y más alta a cero esta función será deshabilitada.',
'Close a task when there is no activity' => 'Cerrar tarea cuando no haya actividad',
'Duration in days' => 'Duración en días',
'Send email when there is no activity on a task' => 'Enviar correo cuando no haya actividad en una tarea',
- 'Unable to fetch link information.' => 'No es posible obtener información sobre el enlace',
- 'Daily background job for tasks' => 'Tarea de fondo diaria para las tareas',
+ 'Unable to fetch link information.' => 'No es posible obtener información del enlace.',
+ 'Daily background job for tasks' => 'Trabajo en segundo plano diario para las tareas',
'Auto' => 'Automático',
'Related' => 'Relacionado',
'Attachment' => 'Adjunto',
- 'Title not found' => 'Título no ha sido encontrado',
+ 'Title not found' => 'No se ha encontrado el título',
'Web Link' => 'Enlace web',
'External links' => 'Enlaces externos',
'Add external link' => 'Añadir enlace externo',
@@ -1050,50 +1014,50 @@ return array(
'Copy and paste your link here...' => 'Copia y pega tu enlace aquí...',
'URL' => 'URL',
'Internal links' => 'Enlaces internos',
- 'Assign to me' => 'Asignar a mí',
+ 'Assign to me' => 'Asignarme a mí',
'Me' => 'Yo',
'Do not duplicate anything' => 'No duplicar nada',
'Projects management' => 'Administración de proyectos',
'Users management' => 'Administración de usuarios',
'Groups management' => 'Administración de grupos',
- 'Create from another project' => 'Crear de otro proyecto',
+ 'Create from another project' => 'Crear a partir de otro proyecto',
'open' => 'abierto',
'closed' => 'cerrado',
- 'Priority:' => 'Prioridad',
- 'Reference:' => 'Referencia',
+ 'Priority:' => 'Prioridad:',
+ 'Reference:' => 'Referencia:',
'Complexity:' => 'Complejidad:',
- 'Swimlane:' => 'Swimlane:',
+ 'Swimlane:' => 'Calle:',
'Column:' => 'Columna:',
'Position:' => 'Posición:',
'Creator:' => 'Creador:',
'Time estimated:' => 'Tiempo estimado:',
'%s hours' => '%s horas',
'Time spent:' => 'Tiempo gastado:',
- 'Created:' => 'Creado',
- 'Modified:' => 'Modificado',
- 'Completed:' => 'Terminado',
- 'Started:' => 'Iniciado',
- 'Moved:' => 'Movido',
+ 'Created:' => 'Creado:',
+ 'Modified:' => 'Modificado:',
+ 'Completed:' => 'Finalizado:',
+ 'Started:' => 'Iniciado:',
+ 'Moved:' => 'Movido:',
'Task #%d' => 'Tarea #%d',
- 'Date and time format' => 'Formato de hora y fecha',
+ 'Date and time format' => 'Formato de fecha y hora',
'Time format' => 'Formato de hora',
- 'Start date: ' => 'Fecha de inicio',
- 'End date: ' => 'Fecha de terminación',
- 'New due date: ' => 'Nueva fecha de entrega',
- 'Start date changed: ' => 'Fecha de inicio cambiada',
+ 'Start date: ' => 'Fecha de inicio: ',
+ 'End date: ' => 'Fecha de finalización: ',
+ 'New due date: ' => 'Nueva fecha de entrega: ',
+ 'Start date changed: ' => 'Fecha de inicio cambiada: ',
'Disable private projects' => 'Deshabilitar proyectos privados',
'Do you really want to remove this custom filter: "%s"?' => '¿Realmente desea eliminar este filtro personalizado: «%s»?',
- 'Remove a custom filter' => 'Eliminar filtro personalizado',
- 'User activated successfully.' => 'Usuario activado correctamente',
- 'Unable to enable this user.' => 'No es posible habilitar este usuario',
- 'User disabled successfully.' => 'Usuario deshabilitado correctamente',
- 'Unable to disable this user.' => 'No es posible deshabilitar este usuario',
- 'All files have been uploaded successfully.' => 'Todos los archivos han sido subidos correctamente',
- 'View uploaded files' => 'Ver archivos subidos',
- 'The maximum allowed file size is %sB.' => 'El límite de tamaño de archivo permitido para subir es %sB.',
- 'Choose files again' => 'Eligir archivos de nuevo',
+ 'Remove a custom filter' => 'Eliminar el filtro personalizado',
+ 'User activated successfully.' => 'Usuario activado correctamente.',
+ 'Unable to enable this user.' => 'No es posible habilitar este usuario.',
+ 'User disabled successfully.' => 'Usuario deshabilitado correctamente.',
+ 'Unable to disable this user.' => 'No es posible deshabilitar este usuario.',
+ 'All files have been uploaded successfully.' => 'Todos los archivos han sido cargados correctamente.',
+ 'View uploaded files' => 'Ver archivos cargados',
+ 'The maximum allowed file size is %sB.' => 'El tamaño máximo de archivo es %sB.',
+ 'Choose files again' => 'Elegir archivos de nuevo',
'Drag and drop your files here' => 'Arrastra y suelta tus archivos aquí',
- 'choose files' => 'Elegir archivos',
+ 'choose files' => 'elegir archivos',
'View profile' => 'Ver perfil',
'Two Factor' => 'Dos factores',
'Disable user' => 'Deshabilitar usuario',
@@ -1113,16 +1077,16 @@ return array(
'Change column position' => 'Cambiar posición de la columna',
'Switch to the project overview' => 'Cambiar a vista general del proyecto',
'User filters' => 'Usar filtros',
- 'Category filters' => 'Categoría y filtros',
- 'Upload a file' => 'Subir archivo',
+ 'Category filters' => 'Filtros de categoría',
+ 'Upload a file' => 'Cargar archivo',
'View file' => 'Ver archivo',
'Last activity' => 'Última actividad',
'Change subtask position' => 'Cambiar posición de la subtarea',
'This value must be greater than %d' => 'Este valor debe ser mayor que %d',
- 'Another swimlane with the same name exists in the project' => 'Ya existe otro swimlane con el mismo nombre en el proyecto',
- 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Ejemplo: http://ejemplo.kanboard.net/ (Usado para generar URLs absolutas)',
- 'Actions duplicated successfully.' => 'Acción duplicada con exito.',
- 'Unable to duplicate actions.' => 'No se ha podido duplicar la acción.',
+ 'Another swimlane with the same name exists in the project' => 'Ya existe otra calle con el mismo nombre en el proyecto',
+ 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Ejemplo: http://ejemplo.kanboard.net/ (usado para generar URLs absolutas)',
+ 'Actions duplicated successfully.' => 'Acciones duplicadas con éxito.',
+ 'Unable to duplicate actions.' => 'No se han podido duplicar las acciones.',
'Add a new action' => 'Añadir una nueva acción',
'Import from another project' => 'Importar de otro proyecto',
'There is no action at the moment.' => 'No hay ninguna acción en este momento.',
@@ -1202,4 +1166,52 @@ return array(
'Email transport' => 'Transporte de correo electrónico',
'Webhook token' => 'Token del disparador web (webhook)',
'Imports' => 'Importaciones',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index 8fbb0ba9..ed38fb56 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d suljettua tehtävää',
'No task for this project' => 'Ei tehtävää tälle projektille',
'Public link' => 'Julkinen linkki',
- 'Change assignee' => 'Vaihda suorittajaa',
- 'Change assignee for the task "%s"' => 'Vaihda suorittajaa tehtävälle %s',
'Timezone' => 'Aikavyöhyke',
'Sorry, I didn\'t find this information in my database!' => 'Anteeksi, en löytänyt tätä tietoa tietokannastani',
'Page not found' => 'Sivua ei löydy',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategoria',
'Category:' => 'Kategoria:',
'Categories' => 'Kategoriat',
- 'Category not found.' => 'Kategoriaa ei löytynyt.',
'Your category have been created successfully.' => 'Kategoria luotiin onnistuneesti.',
'Unable to create your category.' => 'Kategorian luonti epäonnistui.',
'Your category have been updated successfully.' => 'Kategoriaa muokattiin onnistuneesti.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Haluatko varmasti poistaa tiedoston: "%s"?',
'Attachments' => 'Liitteet',
'Edit the task' => 'Muokkaa tehtävää',
- 'Edit the description' => 'Muokkaa kuvausta',
'Add a comment' => 'Lisää kommentti',
'Edit a comment' => 'Muokkaa kommenttia',
'Summary' => 'Yhteenveto',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Näytä toinen projekti',
'Created by %s' => 'Luonut: %s',
'Tasks Export' => 'Tehtävien vienti',
- 'Tasks exportation for "%s"' => 'Tehtävien vienti projektilta "%s"',
'Start Date' => 'Aloituspäivä',
'End Date' => 'Lopetuspäivä',
'Execute' => 'Suorita',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Uusi alitehtävä',
'New attachment added "%s"' => 'Uusi liite lisätty "%s"',
'New comment posted by %s' => '%s lisäsi uuden kommentin',
- // 'New attachment' => '',
// 'New comment' => '',
'Comment updated' => 'Kommentti päivitetty',
// 'New subtask' => '',
- // 'Subtask updated' => '',
- // 'Task updated' => '',
- // 'Task closed' => '',
- // 'Task opened' => '',
'I want to receive notifications only for those projects:' => 'Haluan vastaanottaa ilmoituksia ainoastaan näistä projekteista:',
'view the task on Kanboard' => 'katso tehtävää Kanboardissa',
'Public access' => 'Julkinen käyttöoikeus',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Muita tunnistautumistapoja ei ole otettu käyttöön.',
'Password modified successfully.' => 'Salasana vaihdettu onnistuneesti.',
'Unable to change the password.' => 'Salasanan vaihto epäonnistui.',
- 'Change category for the task "%s"' => 'Vaihda tehtävän "%s" kategoria',
'Change category' => 'Vaihda kategoria',
'%s updated the task %s' => '%s päivitti tehtävän %s',
'%s opened the task %s' => '%s avasi tehtävän %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s päivitti tehtävää #%d',
'%s created the task #%d' => '%s loi tehtävän #%d',
'%s closed the task #%d' => '%s sulki tehtävän #%d',
- '%s open the task #%d' => '%s avasi tehtävän #%d',
- '%s moved the task #%d to the column "%s"' => '%s siirsi tehtävän #%d sarakkeeseen "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s siirsi tehtävän #%d %d. sarakkeessa %s',
+ '%s opened the task #%d' => '%s avasi tehtävän #%d',
'Activity' => 'Toiminta',
'Default values are "%s"' => 'Oletusarvot ovat "%s"',
'Default columns for new projects (Comma-separated)' => 'Oletussarakkeet uusille projekteille',
'Task assignee change' => 'Tehtävän saajan vaihto',
- '%s change the assignee of the task #%d to %s' => '%s vaihtoi tehtävän #%d saajaksi %s',
+ '%s changed the assignee of the task #%d to %s' => '%s vaihtoi tehtävän #%d saajaksi %s',
'%s changed the assignee of the task %s to %s' => '%s vaihtoi tehtävän %s saajaksi %s',
'New password for the user "%s"' => 'Uusi salasana käyttäjälle "%s"',
'Choose an event' => 'Valitse toiminta',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Prosentti',
'Number of tasks' => 'Tehtävien määrä',
'Task distribution' => 'Tehtävien jakauma',
- 'Reportings' => 'Raportoinnit',
- // 'Task repartition for "%s"' => '',
'Analytics' => 'Analytiikka',
'Subtask' => 'Alitehtävä',
'My subtasks' => 'Minun alitehtäväni',
// 'User repartition' => '',
- // 'User repartition for "%s"' => '',
'Clone this project' => 'Kahdenna projekti',
'Column removed successfully.' => 'Sarake poistettu onnstuneesti.',
'Not enough data to show the graph.' => 'Ei riittävästi dataa graafin näyttämiseksi.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Tämän arvon tulee olla numeerinen',
'Unable to create this task.' => 'Tehtävän luonti epäonnistui',
'Cumulative flow diagram' => 'Kumulatiivinen vuokaavio',
- 'Cumulative flow diagram for "%s"' => 'Kumulatiivinen vuokaavio kohteelle "%s"',
'Daily project summary' => 'Päivittäinen yhteenveto',
'Daily project summary export' => 'Päivittäisen yhteenvedon vienti',
- 'Daily project summary export for "%s"' => 'Päivittäisen yhteenvedon vienti kohteeseen "%s"',
'Exports' => 'Viennit',
'This export contains the number of tasks per column grouped per day.' => 'Tämä tiedosto sisältää tehtäviä sarakkeisiin päiväkohtaisesti ryhmilteltyinä',
'Active swimlanes' => 'Aktiiviset kaistat',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Poista kaista',
'Show default swimlane' => 'Näytä oletuskaista',
'Swimlane modification for the project "%s"' => 'Kaistamuutos projektille "%s"',
- 'Swimlane not found.' => 'Kaistaa ei löydy',
'Swimlane removed successfully.' => 'Kaista poistettu onnistuneesti.',
'Swimlanes' => 'Kaistat',
'Swimlane updated successfully.' => 'Kaista päivitetty onnistuneesti.',
@@ -500,7 +481,6 @@ return array(
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
- // 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
// 'Application default' => '',
@@ -607,7 +587,7 @@ return array(
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Webhook URL' => '',
- // '%s remove the assignee of the task %s' => '',
+ // '%s removed the assignee of the task %s' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@@ -621,7 +601,6 @@ return array(
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
// '%s via Kanboard' => '',
- // 'Burndown chart for "%s"' => '',
// 'Burndown chart' => '',
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
@@ -686,14 +665,8 @@ return array(
// 'Move the task to another column when the category is changed' => '',
// 'Send a task by email to someone' => '',
// 'Reopen a task' => '',
- // 'Column change' => '',
- // 'Position change' => '',
- // 'Swimlane change' => '',
- // 'Assignee change' => '',
- // '[%s] Overdue tasks' => '',
// 'Notification' => '',
// '%s moved the task #%d to the first swimlane' => '',
- // '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// '%s moved the task %s to the first swimlane' => '',
@@ -764,8 +737,6 @@ return array(
// 'Search by category: ' => '',
// 'Search by description: ' => '',
// 'Search by due date: ' => '',
- // 'Lead and Cycle time for "%s"' => '',
- // 'Average time spent into each column for "%s"' => '',
// 'Average time spent into each column' => '',
// 'Average time spent' => '',
// 'This chart show the average time spent into each column for the last %d tasks.' => '',
@@ -806,7 +777,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
// 'Project members' => '',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index 19ac2b48..e7184949 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d tâches terminées',
'No task for this project' => 'Aucune tâche pour ce projet',
'Public link' => 'Lien public',
- 'Change assignee' => 'Changer la personne assignée',
- 'Change assignee for the task "%s"' => 'Changer la personne assignée pour la tâche « %s »',
'Timezone' => 'Fuseau horaire',
'Sorry, I didn\'t find this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !',
'Page not found' => 'Page introuvable',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Catégorie',
'Category:' => 'Catégorie :',
'Categories' => 'Catégories',
- 'Category not found.' => 'Catégorie introuvable',
'Your category have been created successfully.' => 'Votre catégorie a été créée avec succès.',
'Unable to create your category.' => 'Impossible de créer votre catégorie.',
'Your category have been updated successfully.' => 'Votre catégorie a été mise à jour avec succès.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Voulez-vous vraiment supprimer ce fichier « %s » ?',
'Attachments' => 'Pièces-jointes',
'Edit the task' => 'Modifier la tâche',
- 'Edit the description' => 'Modifier la description',
'Add a comment' => 'Ajouter un commentaire',
'Edit a comment' => 'Modifier un commentaire',
'Summary' => 'Résumé',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Afficher un autre projet',
'Created by %s' => 'Créé par %s',
'Tasks Export' => 'Exportation des tâches',
- 'Tasks exportation for "%s"' => 'Exportation des tâches pour « %s »',
'Start Date' => 'Date de début',
'End Date' => 'Date de fin',
'Execute' => 'Exécuter',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nouvelle sous-tâche',
'New attachment added "%s"' => 'Nouvelle pièce-jointe ajoutée « %s »',
'New comment posted by %s' => 'Nouveau commentaire ajouté par « %s »',
- 'New attachment' => 'Nouveau document',
'New comment' => 'Nouveau commentaire',
'Comment updated' => 'Commentaire mis à jour',
'New subtask' => 'Nouvelle sous-tâche',
- 'Subtask updated' => 'Sous-tâche mise à jour',
- 'Task updated' => 'Tâche mise à jour',
- 'Task closed' => 'Tâche fermée',
- 'Task opened' => 'Tâche ouverte',
'I want to receive notifications only for those projects:' => 'Je souhaite reçevoir les notifications uniquement pour les projets sélectionnés :',
'view the task on Kanboard' => 'voir la tâche sur Kanboard',
'Public access' => 'Accès public',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Aucune authentification externe activée.',
'Password modified successfully.' => 'Mot de passe changé avec succès.',
'Unable to change the password.' => 'Impossible de changer le mot de passe.',
- 'Change category for the task "%s"' => 'Changer la catégorie pour la tâche « %s »',
'Change category' => 'Changer de catégorie',
'%s updated the task %s' => '%s a mis à jour la tâche %s',
'%s opened the task %s' => '%s a ouvert la tâche %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s a mis à jour la tâche n°%d',
'%s created the task #%d' => '%s a créé la tâche n°%d',
'%s closed the task #%d' => '%s a fermé la tâche n°%d',
- '%s open the task #%d' => '%s a ouvert la tâche n°%d',
- '%s moved the task #%d to the column "%s"' => '%s a déplacé la tâche n°%d dans la colonne « %s »',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s a déplacé la tâche n°%d à la position n°%d dans la colonne « %s »',
+ '%s opened the task #%d' => '%s a ouvert la tâche n°%d',
'Activity' => 'Activité',
'Default values are "%s"' => 'Les valeurs par défaut sont « %s »',
'Default columns for new projects (Comma-separated)' => 'Colonnes par défaut pour les nouveaux projets (séparation par des virgules)',
'Task assignee change' => 'Modification de la personne assignée à une tâche',
- '%s change the assignee of the task #%d to %s' => '%s a changé la personne assignée à la tâche n˚%d pour %s',
+ '%s changed the assignee of the task #%d to %s' => '%s a changé la personne assignée à la tâche n˚%d pour %s',
'%s changed the assignee of the task %s to %s' => '%s a changé la personne assignée à la tâche %s pour %s',
'New password for the user "%s"' => 'Nouveau mot de passe pour l\'utilisateur « %s »',
'Choose an event' => 'Choisir un événement',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Pourcentage',
'Number of tasks' => 'Nombre de tâches',
'Task distribution' => 'Répartition des tâches',
- 'Reportings' => 'Rapports',
- 'Task repartition for "%s"' => 'Répartition des tâches pour « %s »',
'Analytics' => 'Analytique',
'Subtask' => 'Sous-tâche',
'My subtasks' => 'Mes sous-tâches',
'User repartition' => 'Répartition des utilisateurs',
- 'User repartition for "%s"' => 'Répartition des utilisateurs pour « %s »',
'Clone this project' => 'Cloner ce projet',
'Column removed successfully.' => 'Colonne supprimée avec succès.',
'Not enough data to show the graph.' => 'Pas assez de données pour afficher le graphique.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Cette valeur doit être numérique',
'Unable to create this task.' => 'Impossible de créer cette tâche',
'Cumulative flow diagram' => 'Diagramme de flux cumulé',
- 'Cumulative flow diagram for "%s"' => 'Diagramme de flux cumulé pour « %s »',
'Daily project summary' => 'Résumé journalier du projet',
'Daily project summary export' => 'Export du résumé journalier du projet',
- 'Daily project summary export for "%s"' => 'Export du résumé quotidien du projet pour « %s »',
'Exports' => 'Exports',
'This export contains the number of tasks per column grouped per day.' => 'Cet export contient le nombre de tâches par colonne groupé par jour.',
'Active swimlanes' => 'Swimlanes actives',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Supprimer une swimlane',
'Show default swimlane' => 'Afficher la swimlane par défaut',
'Swimlane modification for the project "%s"' => 'Modification d\'une swimlane pour le projet « %s »',
- 'Swimlane not found.' => 'Cette swimlane est introuvable.',
'Swimlane removed successfully.' => 'Swimlane supprimée avec succès.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane mise à jour avec succès.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Identifiant de la sous-tâche',
'Subtasks' => 'Sous-tâches',
'Subtasks Export' => 'Exportation des sous-tâches',
- 'Subtasks exportation for "%s"' => 'Exportation des sous-tâches pour le projet « %s »',
'Task Title' => 'Titre de la tâche',
'Untitled' => 'Sans nom',
'Application default' => 'Valeur par défaut de l\'application',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Le taux de change a été ajouté avec succès.',
'Unable to add this currency rate.' => 'Impossible d\'ajouter ce taux de change',
'Webhook URL' => 'URL du webhook',
- '%s remove the assignee of the task %s' => '%s a enlevé la personne assignée à la tâche %s',
+ '%s removed the assignee of the task %s' => '%s a enlevé la personne assignée à la tâche %s',
'Enable Gravatar images' => 'Activer les images Gravatar',
'Information' => 'Informations',
'Check two factor authentication code' => 'Vérification du code pour l\'authentification à deux-facteurs',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Testez votre appareil',
'Assign a color when the task is moved to a specific column' => 'Assigner une couleur lorsque la tâche est déplacée dans une colonne spécifique',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Graphique d\'avancement pour « %s »',
'Burndown chart' => 'Graphique d\'avancement',
'This chart show the task complexity over the time (Work Remaining).' => 'Ce graphique représente la complexité des tâches en fonction du temps (travail restant).',
'Screenshot taken %s' => 'Capture d\'écran prise le %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Déplacer une tâche vers une autre colonne lorsque la catégorie a changé',
'Send a task by email to someone' => 'Envoyer une tâche par email à quelqu\'un',
'Reopen a task' => 'Rouvrir une tâche',
- 'Column change' => 'Changement de colonne',
- 'Position change' => 'Changement de position',
- 'Swimlane change' => 'Changement de swimlane',
- 'Assignee change' => 'Changement d\'assigné',
- '[%s] Overdue tasks' => '[%s] Tâches en retard',
'Notification' => 'Notification',
'%s moved the task #%d to the first swimlane' => '%s a déplacé la tâche n°%d dans la première swimlane',
- '%s moved the task #%d to the swimlane "%s"' => '%s a déplacé la tâche n°%d dans la swimlane « %s »',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s a déplacé la tâche %s dans la première swimlane',
@@ -765,8 +738,6 @@ return array(
'Search by category: ' => 'Rechercher par catégorie : ',
'Search by description: ' => 'Rechercher par description : ',
'Search by due date: ' => 'Rechercher par date d\'échéance : ',
- 'Lead and Cycle time for "%s"' => 'Lead et cycle time pour « %s »',
- 'Average time spent into each column for "%s"' => 'Temps passé moyen dans chaque colonne pour « %s »',
'Average time spent into each column' => 'Temps moyen passé dans chaque colonne',
'Average time spent' => 'Temps moyen passé',
'This chart show the average time spent into each column for the last %d tasks.' => 'Ce graphique montre le temps passé moyen dans chaque colonne pour les %d dernières tâches.',
@@ -807,7 +778,6 @@ return array(
'License:' => 'Licence :',
'License' => 'Licence',
'Enter the text below' => 'Entrez le texte ci-dessous',
- 'Gantt chart for %s' => 'Diagramme de Gantt pour %s',
'Sort by position' => 'Trier par position',
'Sort by date' => 'Trier par date',
'Add task' => 'Ajouter une tâche',
@@ -848,8 +818,6 @@ return array(
'Version' => 'Version',
'Plugins' => 'Extensions',
'There is no plugin loaded.' => 'Il n\'y a aucune extension chargée.',
- 'Set maximum column height' => 'Définir la hauteur max. des colonnes',
- 'Remove maximum column height' => 'Enlever la hauteur max. des colonnes',
'My notifications' => 'Mes notifications',
'Custom filters' => 'Filtres personalisés',
'Your custom filter have been created successfully.' => 'Votre filter personalisé a été créé avec succès.',
@@ -950,11 +918,10 @@ return array(
'Project Manager' => 'Chef de projet',
'Project Member' => 'Membre du projet',
'Project Viewer' => 'Visualiseur de projet',
- 'Your account is locked for %d minutes' => 'Votre compte est vérouillé pour %d minutes',
+ 'Your account is locked for %d minutes' => 'Votre compte est verrouillé pour %d minutes',
'Invalid captcha' => 'Captcha invalid',
'The name must be unique' => 'Le nom doit être unique',
'View all groups' => 'Voir tous les groupes',
- 'View group members' => 'Voir les membres du groupe',
'There is no user available.' => 'Il n\'y a aucun utilisateur disponible',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Voulez-vous vraiment supprimer l\'utilisateur « %s » du groupe « %s » ?',
'There is no group.' => 'Il n\'y a aucun groupe.',
@@ -975,13 +942,10 @@ return array(
'Enter group name...' => 'Entrez le nom du groupe...',
'Role:' => 'Rôle :',
'Project members' => 'Membres du projet',
- 'Compare hours for "%s"' => 'Comparer les heures pour « %s »',
'%s mentioned you in the task #%d' => '%s vous a mentionné dans la tâche n°%d',
'%s mentioned you in a comment on the task #%d' => '%s vous a mentionné dans un commentaire de la tâche n°%d',
'You were mentioned in the task #%d' => 'Vous avez été mentionné dans la tâche n°%d',
'You were mentioned in a comment on the task #%d' => 'Vous avez été mentionné dans un commentaire de la tâche n°%d',
- 'Mentioned' => 'Mentionné',
- 'Compare Estimated Time vs Actual Time' => 'Comparer le temps estimé et le temps actuel',
'Estimated hours: ' => 'Heures estimées : ',
'Actual hours: ' => 'Heures actuelles : ',
'Hours Spent' => 'Heures passées',
@@ -1203,4 +1167,52 @@ return array(
'Email transport' => 'Transport des emails',
'Webhook token' => 'Jeton de sécurité des webhooks',
'Imports' => 'Importations',
+ 'Project tags management' => 'Gestion des libellés pour le projet',
+ 'Tag created successfully.' => 'Libellé créé avec succès.',
+ 'Unable to create this tag.' => 'Imposssible de créer ce libellé.',
+ 'Tag updated successfully.' => 'Libellé mis à jour avec succès.',
+ 'Unable to update this tag.' => 'Impossible de mettre à jour ce libellé.',
+ 'Tag removed successfully.' => 'Libellé supprimé avec succès.',
+ 'Unable to remove this tag.' => 'Impossible de supprimer ce libellé.',
+ 'Global tags management' => 'Gestion des libellés globaux',
+ 'Tags' => 'Libellés',
+ 'Tags management' => 'Gestion des libellés',
+ 'Add new tag' => 'Ajouter un nouveau libellé',
+ 'Edit a tag' => 'Mettre à jour un libellé',
+ 'Project tags' => 'Libellés du projet',
+ 'There is no specific tag for this project at the moment.' => 'Il n\'y a aucun libellé spécifique pour ce projet pour le moment.',
+ 'Tag' => 'Libellé',
+ 'Remove a tag' => 'Supprimer un libellé',
+ 'Do you really want to remove this tag: "%s"?' => 'Voulez-vous vraiment supprimer ce libellé : « %s » ?',
+ 'Global tags' => 'Libellés globaux',
+ 'There is no global tag at the moment.' => 'Il n\'y a aucun libellé global pour le moment.',
+ 'This field cannot be empty' => 'Ce champ ne peut être vide',
+ 'Close a task when there is no activity in an specific column' => 'Fermer une tâche lorsqu\'il n\'y a aucune activité dans une colonne spécifique',
+ '%s removed a subtask for the task #%d' => '%s a supprimé une sous-tâche de la tâche n°%d',
+ '%s removed a comment on the task #%d' => '%s a supprimé un commentaire de la tâche n°%d',
+ 'Comment removed on task #%d' => 'Commentaire supprimé sur la tâche n°%d',
+ 'Subtask removed on task #%d' => 'Sous-tâche supprimée sur la tâche n°%d',
+ 'Hide tasks in this column in the dashboard' => 'Cacher les tâches de cette colonne dans le tableau de bord',
+ '%s removed a comment on the task %s' => '%s a supprimé un commentaire de la tâche %s',
+ '%s removed a subtask for the task %s' => '%s a supprimé une sous-tâche de la tâche %s',
+ 'Comment removed' => 'Commentaire supprimé',
+ 'Subtask removed' => 'Sous-tâche supprimée',
+ '%s set a new internal link for the task #%d' => '%s a défini un nouveau lien interne pour la tâche n°%d',
+ '%s removed an internal link for the task #%d' => '%s a supprimé un lien interne pour la tâche n°%d',
+ 'A new internal link for the task #%d have been defined' => 'Un nouveau lien interne pour la tâche n°%d a été défini',
+ 'Internal link removed for the task #%d' => 'Lien interne supprimé pour la tâche n°%d',
+ '%s set a new internal link for the task %s' => '%s a défini un nouveau lien interne pour la tâche %s',
+ '%s removed an internal link for the task %s' => '%s a supprimé un lien interne pour la tâche %s',
+ 'Automatically set the due date on task creation' => 'Définir automatiquement la date d\'échéance lors de la création de la tâche',
+ 'Move the task to another column when closed' => 'Déplacer la tâche vers une autre colonne lorsque celle-ci est fermée',
+ 'Move the task to another column when not moved during a given period' => 'Déplacer la tâche vers une autre colonne lorsque celle-ci n\'a pas été bougée pendant une certaine période',
+ 'Dashboard for %s' => 'Tableau de bord pour %s',
+ 'Tasks overview for %s' => 'Aperçu des tâches pour %s',
+ 'Subtasks overview for %s' => 'Aperçu des sous-tâches pour %s',
+ 'Projects overview for %s' => 'Aperçu des projets pour %s',
+ 'Activity stream for %s' => 'Flux d\'activité pour %s',
+ 'Calendar for %s' => 'Calendrier pour %s',
+ 'Notifications for %s' => 'Notifications pour %s',
+ 'Subtasks export' => 'Export des sous-tâches',
+ 'Tasks exportation' => 'Export des tâches',
);
diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php
index 3b5d6036..32e34857 100644
--- a/app/Locale/hu_HU/translations.php
+++ b/app/Locale/hu_HU/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d lezárt feladat',
'No task for this project' => 'Nincs feladat ebben a projektben',
'Public link' => 'Nyilvános hivatkozás',
- 'Change assignee' => 'Felelős módosítása',
- 'Change assignee for the task "%s"' => 'Feladat felelősének módosítása: "%s"',
'Timezone' => 'Időzóna',
'Sorry, I didn\'t find this information in my database!' => 'Ez az információ nem található az adatbázisban!',
'Page not found' => 'Az oldal nem található',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategória',
'Category:' => 'Kategória:',
'Categories' => 'Kategóriák',
- 'Category not found.' => 'Kategória nem található.',
'Your category have been created successfully.' => 'Kategória sikeresen létrehozva.',
'Unable to create your category.' => 'A kategória létrehozása nem lehetséges.',
'Your category have been updated successfully.' => 'Kategória sikeresen frissítve.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Valóban törölni akarja ezt a fájlt: "%s"?',
'Attachments' => 'Mellékletek',
'Edit the task' => 'Feladat módosítása',
- 'Edit the description' => 'Leírás szerkesztése',
'Add a comment' => 'Új megjegyzés',
'Edit a comment' => 'Megjegyzés szerkesztése',
'Summary' => 'Összegzés',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Másik projekt megjelenítése',
'Created by %s' => 'Készítette: %s',
'Tasks Export' => 'Feladatok exportálása',
- 'Tasks exportation for "%s"' => 'Feladatok exportálása: "%s"',
'Start Date' => 'Kezdés dátuma',
'End Date' => 'Befejezés dátuma',
'Execute' => 'Végrehajt',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Új részfeladat',
'New attachment added "%s"' => 'Új melléklet "%s" hozzáadva.',
'New comment posted by %s' => 'Új megjegyzés %s',
- 'New attachment' => 'Új melléklet',
'New comment' => 'Új megjegyzés',
'Comment updated' => 'Megjegyzés frissítve',
'New subtask' => 'Új részfeladat',
- 'Subtask updated' => 'Részfeladat frissítve',
- 'Task updated' => 'Feladat frissítve',
- 'Task closed' => 'Feladat lezárva',
- 'Task opened' => 'Feladat megnyitva',
'I want to receive notifications only for those projects:' => 'Csak ezekről a projektekről kérek értesítést:',
'view the task on Kanboard' => 'feladat megtekintése a Kanboardon',
'Public access' => 'Nyilvános hozzáférés',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'A külső azonosítás nincs engedélyezve.',
'Password modified successfully.' => 'A jelszó sikeresen módosítva.',
'Unable to change the password.' => 'A jelszó módosítása sikertelen.',
- 'Change category for the task "%s"' => 'Feladat kategória módosítása "%s"',
'Change category' => 'Kategória módosítása',
'%s updated the task %s' => '%s frissítette a feladatot %s',
'%s opened the task %s' => '%s megnyitott a feladatot %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s frissítette a feladatot #%d',
'%s created the task #%d' => '%s létrehozta a feladatot #%d',
'%s closed the task #%d' => '%s lezárta a feladatot #%d',
- '%s open the task #%d' => '%s megnyitotta a feladatot #%d',
- '%s moved the task #%d to the column "%s"' => '%s átmozgatta a feladatot #%d a "%s" oszlopba',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s átmozgatta a feladatot #%d a %d pozícióba a "%s" oszlopban',
+ '%s opened the task #%d' => '%s megnyitotta a feladatot #%d',
'Activity' => 'Tevékenységek',
'Default values are "%s"' => 'Az alapértelmezett értékek: %s',
'Default columns for new projects (Comma-separated)' => 'Alapértelmezett oszlopok az új projektekben (vesszővel elválasztva)',
'Task assignee change' => 'Felelős módosítása',
- '%s change the assignee of the task #%d to %s' => '%s a felelőst módosította #%d %s',
+ '%s changed the assignee of the task #%d to %s' => '%s a felelőst módosította #%d %s',
'%s changed the assignee of the task %s to %s' => '%s a felelőst %s módosította: %s',
'New password for the user "%s"' => 'Felhasználó új jelszava: %s',
'Choose an event' => 'Válasszon eseményt',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Százalék',
'Number of tasks' => 'A feladatok száma',
'Task distribution' => 'Feladatelosztás',
- 'Reportings' => 'Jelentések',
- 'Task repartition for "%s"' => 'Feladat újraosztása: %s',
'Analytics' => 'Analitika',
'Subtask' => 'Részfeladat',
'My subtasks' => 'Részfeladataim',
'User repartition' => 'Felhasználó újrafelosztás',
- 'User repartition for "%s"' => 'Felhasználó újrafelosztás: %s',
'Clone this project' => 'Projekt másolása',
'Column removed successfully.' => 'Oszlop sikeresen törölve.',
'Not enough data to show the graph.' => 'Nincs elég adat a grafikonhoz.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Ez a mező csak szám lehet',
'Unable to create this task.' => 'A feladat nem hozható létre,',
'Cumulative flow diagram' => 'Kumulatív folyamatábra',
- 'Cumulative flow diagram for "%s"' => 'Kumulatív folyamatábra: %s',
'Daily project summary' => 'Napi projektösszefoglaló',
'Daily project summary export' => 'Napi projektösszefoglaló exportálása',
- 'Daily project summary export for "%s"' => 'Napi projektösszefoglaló exportálása: %s',
'Exports' => 'Exportálások',
'This export contains the number of tasks per column grouped per day.' => 'Ez az export tartalmazza a feladatok számát oszloponként összesítve, napokra lebontva.',
'Active swimlanes' => 'Aktív sávok',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Sáv törlés',
'Show default swimlane' => 'Alapértelmezett sáv megjelenítése',
'Swimlane modification for the project "%s"' => '%s projekt sávjainak módosítása',
- 'Swimlane not found.' => 'Sáv nem található',
'Swimlane removed successfully.' => 'Sáv sikeresen törölve.',
'Swimlanes' => 'Sávok',
'Swimlane updated successfully.' => 'Sáv sikeresen frissítve',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Részfeladat id',
'Subtasks' => 'Részfeladatok',
'Subtasks Export' => 'Részfeladat exportálás',
- 'Subtasks exportation for "%s"' => 'Részfeladatok exportálása: %s',
'Task Title' => 'Feladat címe',
'Untitled' => 'Névtelen',
'Application default' => 'Alkalmazás alapértelmezett',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Az átváltási árfolyammal történő bővítés sikerült',
'Unable to add this currency rate.' => 'Nem sikerült az átváltási árfolyam felvétele',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s eltávolította a %s feladathoz rendelt személyt',
+ '%s removed the assignee of the task %s' => '%s eltávolította a %s feladathoz rendelt személyt',
'Enable Gravatar images' => 'Gravatár képek engedélyezése',
'Information' => 'Információ',
'Check two factor authentication code' => 'Két fázisú beléptető kód ellenőrzése',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Az eszköz ellenőrzése',
'Assign a color when the task is moved to a specific column' => 'Szín hozzárendelése, ha a feladatot egy adott oszlopba mozgatták',
'%s via Kanboard' => '%s a Kanboard-on keresztül',
- // 'Burndown chart for "%s"' => '',
// 'Burndown chart' => '',
'This chart show the task complexity over the time (Work Remaining).' => 'Ez a diagram a feladat időbeli bonyolultságát ábrázolja (mennyi munka van hátra)',
'Screenshot taken %s' => 'A képernyőmentés megtörtént, %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'A feladat átmozgatása egy másik oszlopba, ha megváltozik a kategória',
'Send a task by email to someone' => 'Email-en egy feladat küldése valakinek',
'Reopen a task' => 'Egy feladat újbóli megnyitása',
- 'Column change' => 'Oszlop módosítás',
- 'Position change' => 'Helyzet módosítás',
- 'Swimlane change' => 'Sáv módosítás',
- 'Assignee change' => 'Felelős módosítása',
- '[%s] Overdue tasks' => '[%s] késésben lévő feladat',
'Notification' => 'Értesítés',
'%s moved the task #%d to the first swimlane' => '%s a #%d feladatot az első sávba mozgatta',
- '%s moved the task #%d to the swimlane "%s"' => '%s a #%d feladatot a "%s" sávba mozgatta',
'Swimlane' => 'Sáv',
'Gravatar' => 'Gravatár',
'%s moved the task %s to the first swimlane' => '%s a %s feladatot az első sávba mozgatta',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Keresés kategória alapján: ',
'Search by description: ' => 'Keresés leírás alapján: ',
'Search by due date: ' => 'Keresés határidő alapján: ',
- 'Lead and Cycle time for "%s"' => 'A "%s" átfutási ideje és ciklusideje',
- 'Average time spent into each column for "%s"' => 'A "%s" során az egyes oszlopokban töltött átlagos idő',
'Average time spent into each column' => 'Az egyes oszlopokban töltött átlagos idő',
'Average time spent' => 'Az eltöltött átlagos idő',
'This chart show the average time spent into each column for the last %d tasks.' => 'Ez az ábra az utolsó %d feladatra vonatkozóan mutatja az egyes oszlopkban eltöltött átlagos időt.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Engedély:',
'License' => 'Engedély',
'Enter the text below' => 'Adja be a lenti szöveget',
- 'Gantt chart for %s' => 'Gantt diagram a %s számára',
'Sort by position' => 'Rendezés hely szerint',
'Sort by date' => 'Rendezés idő szerint',
'Add task' => 'Feladat hozzáadása',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Verzió',
'Plugins' => 'Plugin-ek',
'There is no plugin loaded.' => 'Nincs betöltött plugin.',
- 'Set maximum column height' => 'Max. oszlopmagasság beállítása',
- 'Remove maximum column height' => 'Max. oszlopmagasság törlése',
'My notifications' => 'Emlékeztetőim',
'Custom filters' => 'Egyedi szűrők',
'Your custom filter have been created successfully.' => 'Az ön egyedi szűrője sikeresen létrejött.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Érvénytelen captcha',
'The name must be unique' => 'A névnek egyedinek kell lennie',
'View all groups' => 'Az összes csoport megtekintése',
- 'View group members' => 'A csoporttagok megtekintése',
'There is no user available.' => 'Nincs ilyen felhasználó.',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Valóban el kívánja távolítani a "%s" felhasználót a "%s" csoportból?',
'There is no group.' => 'Nincs ilyen csoport.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Adja meg a csoport nevét...',
'Role:' => 'Szerepkör:',
'Project members' => 'Projekt tagok',
- 'Compare hours for "%s"' => 'Az órák összehasonlítása "%s" számára',
'%s mentioned you in the task #%d' => '%s megemlítette önt a #%d feladatban',
'%s mentioned you in a comment on the task #%d' => '%s megemlítette önt a #%d feladathoz fűzött megjegyzésben',
'You were mentioned in the task #%d' => 'Ön meg lett említve a #%d feladatban',
'You were mentioned in a comment on the task #%d' => 'Ön meg lett említve a #%d feladathoz fűzött megjegyzésben',
- 'Mentioned' => 'Meg lett említve',
- 'Compare Estimated Time vs Actual Time' => 'A becsült és a tényleges idő összehasonlítása',
'Estimated hours: ' => 'Becsült órák: ',
'Actual hours: ' => 'Tényleges órák: ',
'Hours Spent' => 'Ráfordítás órákban',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/id_ID/translations.php b/app/Locale/id_ID/translations.php
index 45e32c87..9ecb0121 100644
--- a/app/Locale/id_ID/translations.php
+++ b/app/Locale/id_ID/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d tugas yang ditutup',
'No task for this project' => 'Tidak ada tugas dalam proyek ini',
'Public link' => 'Tautan publik',
- 'Change assignee' => 'Mengubah orang yand ditugaskan',
- 'Change assignee for the task "%s"' => 'Mengubah orang yang ditugaskan untuk tugas « %s »',
'Timezone' => 'Zona waktu',
'Sorry, I didn\'t find this information in my database!' => 'Maaf, saya tidak menemukan informasi ini dalam basis data saya !',
'Page not found' => 'Halaman tidak ditemukan',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategori',
'Category:' => 'Kategori :',
'Categories' => 'Kategori',
- 'Category not found.' => 'Kategori tidak ditemukan',
'Your category have been created successfully.' => 'Kategori anda berhasil dibuat.',
'Unable to create your category.' => 'Tidak dapat membuat kategori anda.',
'Your category have been updated successfully.' => 'Kategori anda berhasil diperbaharui.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Apakah anda yakin akan menghapus berkas ini « %s » ?',
'Attachments' => 'Lampiran',
'Edit the task' => 'Modifikasi tugas',
- 'Edit the description' => 'Modifikasi deskripsi',
'Add a comment' => 'Tambahkan komentar',
'Edit a comment' => 'Modifikasi komentar',
'Summary' => 'Ringkasan',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Lihat proyek lain',
'Created by %s' => 'Dibuat oleh %s',
'Tasks Export' => 'Ekspor Tugas',
- 'Tasks exportation for "%s"' => 'Tugas di ekspor untuk « %s »',
'Start Date' => 'Tanggal Mulai',
'End Date' => 'Tanggal Berakhir',
'Execute' => 'Eksekusi',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Sub-tugas baru',
'New attachment added "%s"' => 'Lampiran baru ditambahkan « %s »',
'New comment posted by %s' => 'Komentar baru ditambahkan oleh « %s »',
- 'New attachment' => 'Lampirkan baru',
'New comment' => 'Komentar baru',
'Comment updated' => 'Komentar diperbaharui',
'New subtask' => 'Sub-tugas baru',
- 'Subtask updated' => 'Sub-tugas diperbaharui',
- 'Task updated' => 'Tugas diperbaharui',
- 'Task closed' => 'Tugas ditutup',
- 'Task opened' => 'Tugas dibuka',
'I want to receive notifications only for those projects:' => 'Saya ingin menerima pemberitahuan hanya untuk proyek-proyek yang dipilih :',
'view the task on Kanboard' => 'lihat tugas di Kanboard',
'Public access' => 'Akses publik',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Tidak ada otentifikasi eksternal yang aktif.',
'Password modified successfully.' => 'Kata sandi berhasil dimodifikasi.',
'Unable to change the password.' => 'Tidak dapat merubah kata sandir.',
- 'Change category for the task "%s"' => 'Rubah kategori untuk tugas « %s »',
'Change category' => 'Rubah kategori',
'%s updated the task %s' => '%s memperbaharui tugas %s',
'%s opened the task %s' => '%s membuka tugas %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s memperbaharui tugas n°%d',
'%s created the task #%d' => '%s membuat tugas n°%d',
'%s closed the task #%d' => '%s menutup tugas n°%d',
- '%s open the task #%d' => '%s membuka tugas n°%d',
- '%s moved the task #%d to the column "%s"' => '%s memindahkan tugas n°%d ke kolom « %s »',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s memindahkan tugas n°%d ke posisi n°%d dalam kolom « %s »',
+ '%s opened the task #%d' => '%s membuka tugas n°%d',
'Activity' => 'Aktifitas',
'Default values are "%s"' => 'Standar nilai adalah« %s »',
'Default columns for new projects (Comma-separated)' => 'Kolom default untuk proyek baru (dipisahkan dengan koma)',
'Task assignee change' => 'Mengubah orang ditugaskan untuk tugas',
- '%s change the assignee of the task #%d to %s' => '%s rubah orang yang ditugaskan dari tugas n%d ke %s',
+ '%s changed the assignee of the task #%d to %s' => '%s rubah orang yang ditugaskan dari tugas n%d ke %s',
'%s changed the assignee of the task %s to %s' => '%s mengubah orang yang ditugaskan dari tugas %s ke %s',
'New password for the user "%s"' => 'Kata sandi baru untuk pengguna « %s »',
'Choose an event' => 'Pilih acara',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Persentasi',
'Number of tasks' => 'Jumlah dari tugas',
'Task distribution' => 'Pembagian tugas',
- 'Reportings' => 'Pelaporan',
- 'Task repartition for "%s"' => 'Pembagian tugas untuk « %s »',
'Analytics' => 'Analitis',
'Subtask' => 'Subtugas',
'My subtasks' => 'Subtugas saya',
'User repartition' => 'Partisi ulang pengguna',
- 'User repartition for "%s"' => 'Partisi ulang pengguna untuk « %s »',
'Clone this project' => 'Gandakan proyek ini',
'Column removed successfully.' => 'Kolom berhasil dihapus.',
'Not enough data to show the graph.' => 'Tidak cukup data untuk menampilkan grafik.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Nilai ini harus angka',
'Unable to create this task.' => 'Tidak dapat membuat tugas ini',
'Cumulative flow diagram' => 'Diagram alir kumulatif',
- 'Cumulative flow diagram for "%s"' => 'Diagram alir kumulatif untuk « %s »',
'Daily project summary' => 'Ringkasan proyek harian',
'Daily project summary export' => 'Ekspor ringkasan proyek harian',
- 'Daily project summary export for "%s"' => 'Ekspor ringkasan proyek harian untuk « %s »',
'Exports' => 'Ekspor',
'This export contains the number of tasks per column grouped per day.' => 'Ekspor ini berisi jumlah dari tugas per kolom dikelompokan perhari.',
'Active swimlanes' => 'Swimlanes aktif',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Supprimer une swimlane',
'Show default swimlane' => 'Perlihatkan standar swimlane',
'Swimlane modification for the project "%s"' => 'Modifikasi swimlane untuk proyek « %s »',
- 'Swimlane not found.' => 'Swimlane tidak ditemukan.',
'Swimlane removed successfully.' => 'Swimlane berhasil dihapus.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane berhasil diperbaharui.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Id Subtugas',
'Subtasks' => 'Subtugas',
'Subtasks Export' => 'Ekspor Subtugas',
- 'Subtasks exportation for "%s"' => 'Ekspor subtugas untuk « %s »',
'Task Title' => 'Judul Tugas',
'Untitled' => 'Tanpa nama',
'Application default' => 'Aplikasi standar',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Nilai tukar mata uang berhasil ditambahkan.',
'Unable to add this currency rate.' => 'Tidak dapat menambahkan nilai tukar mata uang',
'Webhook URL' => 'URL webhook',
- '%s remove the assignee of the task %s' => '%s menghapus penugasan dari tugas %s',
+ '%s removed the assignee of the task %s' => '%s menghapus penugasan dari tugas %s',
'Enable Gravatar images' => 'Mengaktifkan gambar Gravatar',
'Information' => 'Informasi',
'Check two factor authentication code' => 'Cek dua faktor kode otentifikasi',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Menguji perangkat anda',
'Assign a color when the task is moved to a specific column' => 'Menetapkan warna ketika tugas tersebut dipindahkan ke kolom tertentu',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Grafik Burndown untku « %s »',
'Burndown chart' => 'Grafik Burndown',
'This chart show the task complexity over the time (Work Remaining).' => 'Grafik ini menunjukkan kompleksitas tugas dari waktu ke waktu (Sisa Pekerjaan).',
'Screenshot taken %s' => 'Screenshot diambil %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Pindahkan tugas ke kolom lain ketika kategori berubah',
'Send a task by email to someone' => 'Kirim tugas melalui email ke seseorang',
'Reopen a task' => 'Membuka kembali tugas',
- 'Column change' => 'Kolom berubah',
- 'Position change' => 'Posisi berubah',
- 'Swimlane change' => 'Swimlane berubah',
- 'Assignee change' => 'Penerima berubah',
- '[%s] Overdue tasks' => '[%s] Tugas terlambat',
'Notification' => 'Pemberitahuan',
'%s moved the task #%d to the first swimlane' => '%s memindahkan tugas n°%d ke swimlane pertama',
- '%s moved the task #%d to the swimlane "%s"' => '%s memindahkan tugas n°%d ke swimlane « %s »',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s memindahkan tugas %s ke swimlane pertama',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Pencarian berdasarkan kategori : ',
'Search by description: ' => 'Pencarian berdasarkan deskripsi : ',
'Search by due date: ' => 'Pencarian berdasarkan tanggal jatuh tempo : ',
- 'Lead and Cycle time for "%s"' => 'Memimpin dan Siklus waktu untuk « %s »',
- 'Average time spent into each column for "%s"' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom untuk « %s »',
'Average time spent into each column' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom',
'Average time spent' => 'Rata-rata waktu yang dihabiskan',
'This chart show the average time spent into each column for the last %d tasks.' => 'Grafik ini menunjukkan rata-rata waktu yang dihabiskan dalam setiap kolom untuk %d tugas.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Lisensi :',
'License' => 'Lisensi',
'Enter the text below' => 'Masukkan teks di bawah',
- 'Gantt chart for %s' => 'Grafik Gantt untuk %s',
'Sort by position' => 'Urutkan berdasarkan posisi',
'Sort by date' => 'Urutkan berdasarkan tanggal',
'Add task' => 'Tambah tugas',
@@ -843,172 +813,166 @@ return array(
'Documentation' => 'Dokumentasi',
'Table of contents' => 'Daftar isi',
'Gantt' => 'Gantt',
- // 'Author' => '',
- // 'Version' => '',
- // 'Plugins' => '',
- // 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
- // 'My notifications' => '',
- // 'Custom filters' => '',
- // 'Your custom filter have been created successfully.' => '',
- // 'Unable to create your custom filter.' => '',
- // 'Custom filter removed successfully.' => '',
- // 'Unable to remove this custom filter.' => '',
- // 'Edit custom filter' => '',
- // 'Your custom filter have been updated successfully.' => '',
- // 'Unable to update custom filter.' => '',
- // 'Web' => '',
- // 'New attachment on task #%d: %s' => '',
- // 'New comment on task #%d' => '',
- // 'Comment updated on task #%d' => '',
- // 'New subtask on task #%d' => '',
- // 'Subtask updated on task #%d' => '',
- // 'New task #%d: %s' => '',
- // 'Task updated #%d' => '',
- // 'Task #%d closed' => '',
- // 'Task #%d opened' => '',
- // 'Column changed for task #%d' => '',
- // 'New position for task #%d' => '',
- // 'Swimlane changed for task #%d' => '',
- // 'Assignee changed on task #%d' => '',
- // '%d overdue tasks' => '',
- // 'Task #%d is overdue' => '',
- // 'No new notifications.' => '',
- // 'Mark all as read' => '',
- // 'Mark as read' => '',
- // 'Total number of tasks in this column across all swimlanes' => '',
- // 'Collapse swimlane' => '',
- // 'Expand swimlane' => '',
- // 'Add a new filter' => '',
- // 'Share with all project members' => '',
- // 'Shared' => '',
- // 'Owner' => '',
- // 'Unread notifications' => '',
- // 'Notification methods:' => '',
- // 'Import tasks from CSV file' => '',
- // 'Unable to read your file' => '',
- // '%d task(s) have been imported successfully.' => '',
- // 'Nothing have been imported!' => '',
- // 'Import users from CSV file' => '',
- // '%d user(s) have been imported successfully.' => '',
- // 'Comma' => '',
- // 'Semi-colon' => '',
- // 'Tab' => '',
- // 'Vertical bar' => '',
- // 'Double Quote' => '',
- // 'Single Quote' => '',
- // '%s attached a file to the task #%d' => '',
- // 'There is no column or swimlane activated in your project!' => '',
- // 'Append filter (instead of replacement)' => '',
- // 'Append/Replace' => '',
- // 'Append' => '',
- // 'Replace' => '',
- // 'Import' => '',
- // 'change sorting' => '',
- // 'Tasks Importation' => '',
- // 'Delimiter' => '',
- // 'Enclosure' => '',
- // 'CSV File' => '',
- // 'Instructions' => '',
- // 'Your file must use the predefined CSV format' => '',
- // 'Your file must be encoded in UTF-8' => '',
- // 'The first row must be the header' => '',
- // 'Duplicates are not verified for you' => '',
- // 'The due date must use the ISO format: YYYY-MM-DD' => '',
- // 'Download CSV template' => '',
- // 'No external integration registered.' => '',
- // 'Duplicates are not imported' => '',
- // 'Usernames must be lowercase and unique' => '',
- // 'Passwords will be encrypted if present' => '',
- // '%s attached a new file to the task %s' => '',
+ 'Author' => 'Penulis',
+ 'Version' => 'Versi',
+ 'Plugins' => 'Plugin',
+ 'There is no plugin loaded.' => 'Tidak ada plugin yang dimuat',
+ 'My notifications' => 'Notifikasi saya',
+ 'Custom filters' => 'Filter kustom',
+ 'Your custom filter have been created successfully.' => 'Filter kustom anda telah berhasil dibuat',
+ 'Unable to create your custom filter.' => 'Tidak dapat membuat filter kustom',
+ 'Custom filter removed successfully.' => 'Filter kustom berhail dihapus',
+ 'Unable to remove this custom filter.' => 'Tidak dapat menghapus filter kustom',
+ 'Edit custom filter' => 'Modifikasi filter kustom',
+ 'Your custom filter have been updated successfully.' => 'Filter kustom anda telah berhasil diperbaharui',
+ 'Unable to update custom filter.' => 'Tidak dapat memperbaharui filter kustom',
+ 'Web' => 'Web',
+ 'New attachment on task #%d: %s' => 'Lampiran baru pada tugas #%d: %s',
+ 'New comment on task #%d' => 'Komentar baru pada tugas #%d',
+ 'Comment updated on task #%d' => 'Komentar diperbaharui pada tugas #%d',
+ 'New subtask on task #%d' => 'Subtask baru pada tugas #%d',
+ 'Subtask updated on task #%d' => 'Subtask diperbaharui pada tugas #%d',
+ 'New task #%d: %s' => 'Tugas baru #%d: %s',
+ 'Task updated #%d' => 'Tugas diperbaharui #%d',
+ 'Task #%d closed' => 'Tugas #%d ditutup',
+ 'Task #%d opened' => 'Tugas #%d dibuka',
+ 'Column changed for task #%d' => 'Kolom berubah untuk tugas #%d',
+ 'New position for task #%d' => 'Posisi baru untuk tugas #%d',
+ 'Swimlane changed for task #%d' => 'Swimlane berubah untuk tugas #%d',
+ 'Assignee changed on task #%d' => 'Orang yang ditugaskan berubah pada tugas #%d',
+ '%d overdue tasks' => '%d tugas terlambat',
+ 'Task #%d is overdue' => 'Tugas #%d terlambat',
+ 'No new notifications.' => 'Tidak ada notifikasi baru',
+ 'Mark all as read' => 'Tandai semua sebagai sudah dibaca',
+ 'Mark as read' => 'Tandai sebagai sudah dibaca',
+ 'Total number of tasks in this column across all swimlanes' => 'Total jumlah tugas di kolom ini di semua swimlanes',
+ 'Collapse swimlane' => 'Lipat swimlane',
+ 'Expand swimlane' => 'Perluas swimlane',
+ 'Add a new filter' => 'Tambah filter baru',
+ 'Share with all project members' => 'Bagikan dengan semua member proyek',
+ 'Shared' => 'Dibagikan',
+ 'Owner' => 'Pemilik',
+ 'Unread notifications' => 'Notifikasi belum terbaca',
+ 'Notification methods:' => 'Metode pemberitahuan',
+ 'Import tasks from CSV file' => 'Impor tugas dari berkas CSV',
+ 'Unable to read your file' => 'Tidak dapat membaca berkas anda',
+ '%d task(s) have been imported successfully.' => '%d tugas telah berhasil di impor',
+ 'Nothing have been imported!' => 'Tidak ada yang dapat di impor',
+ 'Import users from CSV file' => 'Impor pengguna dari berkas CSV',
+ '%d user(s) have been imported successfully.' => '%d pengguna telah berhasil di impor',
+ 'Comma' => 'Koma',
+ 'Semi-colon' => 'Titik Koma',
+ 'Tab' => 'Tab',
+ 'Vertical bar' => 'Bar vertikal',
+ 'Double Quote' => 'Kutip Ganda',
+ 'Single Quote' => 'Kutip Satu',
+ '%s attached a file to the task #%d' => '%s berkas dilampirkan untuk tugas #%d',
+ 'There is no column or swimlane activated in your project!' => 'Tidak ada kolom atau swimlane aktif untuk proyek anda',
+ 'Append filter (instead of replacement)' => 'Tambahkan filter (bukan pengganti)',
+ 'Append/Replace' => 'Tambah/Ganti',
+ 'Append' => 'Tambahkan',
+ 'Replace' => 'Ganti',
+ 'Import' => 'Impor',
+ 'change sorting' => 'rubah sortir',
+ 'Tasks Importation' => 'Tugas Impor',
+ 'Delimiter' => 'Pembatas',
+ 'Enclosure' => 'Lampiran',
+ 'CSV File' => 'Berkas CSV',
+ 'Instructions' => 'Intruksi',
+ 'Your file must use the predefined CSV format' => 'Berkas Anda harus menggunakan format CSV yang telah ditetapkan',
+ 'Your file must be encoded in UTF-8' => 'Berkas anda harus di kodekan dalam bentuk UTF-8',
+ 'The first row must be the header' => 'Baris pertama harus header',
+ 'Duplicates are not verified for you' => 'Duplikasi tidak diverifikasi untuk anda',
+ 'The due date must use the ISO format: YYYY-MM-DD' => 'Tanggal jatuh tempo harus menggunakan format ISO: YYYY-MM-DD',
+ 'Download CSV template' => 'Unduh template CSV',
+ 'No external integration registered.' => 'Tidak ada integrasi eksternal terdaftar',
+ 'Duplicates are not imported' => 'Duplikasi tidak diimpor',
+ 'Usernames must be lowercase and unique' => 'Username harus huruf kecil dan unik',
+ 'Passwords will be encrypted if present' => 'Kata sandi akan di enkripsi jika ada',
+ '%s attached a new file to the task %s' => '%s berkas baru dilampirkan untuk tugas %s',
'Link type' => 'Tipe tautan',
- // 'Assign automatically a category based on a link' => '',
- // 'BAM - Konvertible Mark' => '',
- // 'Assignee Username' => '',
- // 'Assignee Name' => '',
- // 'Groups' => '',
- // 'Members of %s' => '',
- // 'New group' => '',
- // 'Group created successfully.' => '',
- // 'Unable to create your group.' => '',
- // 'Edit group' => '',
- // 'Group updated successfully.' => '',
- // 'Unable to update your group.' => '',
- // 'Add group member to "%s"' => '',
- // 'Group member added successfully.' => '',
- // 'Unable to add group member.' => '',
- // 'Remove user from group "%s"' => '',
- // 'User removed successfully from this group.' => '',
- // 'Unable to remove this user from the group.' => '',
- // 'Remove group' => '',
- // 'Group removed successfully.' => '',
- // 'Unable to remove this group.' => '',
- // 'Project Permissions' => '',
- // 'Manager' => '',
- // 'Project Manager' => '',
- // 'Project Member' => '',
- // 'Project Viewer' => '',
- // 'Your account is locked for %d minutes' => '',
- // 'Invalid captcha' => '',
- // 'The name must be unique' => '',
- // 'View all groups' => '',
- // 'View group members' => '',
- // 'There is no user available.' => '',
- // 'Do you really want to remove the user "%s" from the group "%s"?' => '',
- // 'There is no group.' => '',
- // 'External Id' => '',
- // 'Add group member' => '',
- // 'Do you really want to remove this group: "%s"?' => '',
- // 'There is no user in this group.' => '',
- // 'Remove this user' => '',
- // 'Permissions' => '',
- // 'Allowed Users' => '',
- // 'No user have been allowed specifically.' => '',
- // 'Role' => '',
- // 'Enter user name...' => '',
- // 'Allowed Groups' => '',
- // 'No group have been allowed specifically.' => '',
- // 'Group' => '',
- // 'Group Name' => '',
- // 'Enter group name...' => '',
- // 'Role:' => '',
+ 'Assign automatically a category based on a link' => 'Menetapkan otomatis kategori berdasarkan tautan',
+ 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark',
+ 'Assignee Username' => 'Username yang ditugaskan',
+ 'Assignee Name' => 'Nama yang ditugaskan',
+ 'Groups' => 'Grup',
+ 'Members of %s' => 'Anggota dari %s',
+ 'New group' => 'Grup baru',
+ 'Group created successfully.' => 'Grup berhasil dibuat',
+ 'Unable to create your group.' => 'Tidak dapat membuat grup anda',
+ 'Edit group' => 'Rubah grup',
+ 'Group updated successfully.' => 'Grup berhasil diperbaharui',
+ 'Unable to update your group.' => 'Tidak dapat memperbaharui grup anda',
+ 'Add group member to "%s"' => 'Tambahkan anggota grup ke "%s"',
+ 'Group member added successfully.' => 'Anggota grup berhasil ditambahkan',
+ 'Unable to add group member.' => 'Tidak dapat menambahkan anggota grup',
+ 'Remove user from group "%s"' => 'hapus pengguna dari grup "%s"',
+ 'User removed successfully from this group.' => 'Pengguna berhasil dihapus dari grup ini',
+ 'Unable to remove this user from the group.' => 'Tidak dapat menghapus pengguna dari grup',
+ 'Remove group' => 'Hapus grup',
+ 'Group removed successfully.' => 'Grup berhasil dihapus',
+ 'Unable to remove this group.' => 'Tidak dapat menghapus grup ini',
+ 'Project Permissions' => 'Izin Proyek',
+ 'Manager' => 'Manajer',
+ 'Project Manager' => 'Manajer Proyek',
+ 'Project Member' => 'Anggota Proyek',
+ 'Project Viewer' => 'Penonton Proyek',
+ 'Your account is locked for %d minutes' => 'Akun anda dikunci untuk %d menit',
+ 'Invalid captcha' => 'Captcha tidak valid',
+ 'The name must be unique' => 'Nama harus unik',
+ 'View all groups' => 'Lihat semua grup',
+ 'There is no user available.' => 'Tidak ada pengguna yang tersedia',
+ 'Do you really want to remove the user "%s" from the group "%s"?' => 'Anda yakin akan menghapus pengguna "%s" dari grup "%s"?',
+ 'There is no group.' => 'Tidak ada grup',
+ 'External Id' => 'Id Eksternal',
+ 'Add group member' => 'Tambah anggota grup',
+ 'Do you really want to remove this group: "%s"?' => 'Anda yakin akan menghapus grup ini: "%s"?',
+ 'There is no user in this group.' => 'Tidak ada pengguna dalam grup ini',
+ 'Remove this user' => 'Hapus pengguna ini',
+ 'Permissions' => 'Izin',
+ 'Allowed Users' => 'Pengguna Yang Diperbolehkan',
+ 'No user have been allowed specifically.' => 'Tidak ada user yang diperbolehkan secara khusus',
+ 'Role' => 'Peran',
+ 'Enter user name...' => 'Masukkan nama pengguna...',
+ 'Allowed Groups' => 'Grup Yang Diperbolehkan',
+ 'No group have been allowed specifically.' => 'Tidak ada grup yang diperbolehkan secara khusus',
+ 'Group' => 'Grup',
+ 'Group Name' => 'Nama Grup',
+ 'Enter group name...' => 'Masukkan nama grup...',
+ 'Role:' => 'Peran:',
'Project members' => 'Anggota proyek',
- // 'Compare hours for "%s"' => '',
- // '%s mentioned you in the task #%d' => '',
- // '%s mentioned you in a comment on the task #%d' => '',
- // 'You were mentioned in the task #%d' => '',
- // 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
- // 'Estimated hours: ' => '',
- // 'Actual hours: ' => '',
- // 'Hours Spent' => '',
- // 'Hours Estimated' => '',
- // 'Estimated Time' => '',
- // 'Actual Time' => '',
- // 'Estimated vs actual time' => '',
- // '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' => '',
- // 'There is no integration registered at the moment.' => '',
- // 'Password Reset for Kanboard' => '',
- // 'Forgot password?' => '',
- // 'Enable "Forget Password"' => '',
- // 'Password Reset' => '',
- // 'New password' => '',
- // 'Change Password' => '',
- // 'To reset your password click on this link:' => '',
- // 'Last Password Reset' => '',
- // 'The password has never been reinitialized.' => '',
- // 'Creation' => '',
- // 'Expiration' => '',
- // 'Password reset history' => '',
- // 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '',
+ '%s mentioned you in the task #%d' => '%s menyebut anda dalam tugas #%d',
+ '%s mentioned you in a comment on the task #%d' => '%s menyebut anda dalam komentar pada tugas #%d',
+ 'You were mentioned in the task #%d' => 'Anda disebutkan dalam tugas #%d',
+ 'You were mentioned in a comment on the task #%d' => 'Anda disebutkan dalam komentar pada tugas #%d',
+ 'Estimated hours: ' => 'Estimasi jam:',
+ 'Actual hours: ' => 'Aktual jam',
+ 'Hours Spent' => 'Jam dihabiskan',
+ 'Hours Estimated' => 'Jam diperkirakan',
+ 'Estimated Time' => 'Waktu Estimasi',
+ 'Actual Time' => 'Waktu Aktual',
+ 'Estimated vs actual time' => 'Estimasi vs waktu aktual',
+ 'RUB - Russian Ruble' => 'RUB - Rusia rubel',
+ 'Assign the task to the person who does the action when the column is changed' => 'Menetapkan tugas kepada orang yang melakukan tindakan ketika kolom berubah',
+ 'Close a task in a specific column' => 'Tutup tugas di kolom tertentu',
+ 'Time-based One-time Password Algorithm' => 'Waktu berbasis Satu-waktu Algoritma Kata sandi',
+ 'Two-Factor Provider: ' => 'Provider Dua-Faktor',
+ 'Disable two-factor authentication' => 'Nonaktifkan otentikasi dua-faktor',
+ 'Enable two-factor authentication' => 'Aktifkan otentikasi dua-faktor',
+ 'There is no integration registered at the moment.' => 'Tidak ada integrasi yang diregristasi untuk saat ini',
+ 'Password Reset for Kanboard' => 'Setel ulang Kata sandi untuk Kanboard',
+ 'Forgot password?' => 'Lupa kata sandi',
+ 'Enable "Forget Password"' => 'Aktifkan "Lupa Kata Sandi"',
+ 'Password Reset' => 'Setel ulang Kata sandi',
+ 'New password' => 'Kata sandi baru',
+ 'Change Password' => 'Rubah kata sandi',
+ 'To reset your password click on this link:' => 'Untuk menyetel ulang kata sandi anda klik tautan ini:',
+ 'Last Password Reset' => 'Terakhir Setel Ulang Kata Sandi',
+ 'The password has never been reinitialized.' => 'Kata sandi tidak pernah di reinisialisasi',
+ 'Creation' => 'Kreasi',
+ 'Expiration' => 'Waktu berakhir',
+ 'Password reset history' => 'Sejarah setel ulang kata sandi',
+ 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Semua tugas dalam kolom "%s" dan swimlane "%s" telah berhasil ditutup',
// 'Do you really want to close all tasks of this column?' => '',
// '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '',
// 'Close all tasks of this column' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index 261cd82c..b72ca181 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d task chiusi',
'No task for this project' => 'Nessun task per questo progetto',
'Public link' => 'Link pubblico',
- 'Change assignee' => 'Cambia l\'assegnatario',
- 'Change assignee for the task "%s"' => 'Cambia l\'assegnatario per il task "%s"',
'Timezone' => 'Fuso orario',
'Sorry, I didn\'t find this information in my database!' => 'Spiacente, non ho trovato questa informazione sul database!',
'Page not found' => 'Pagina non trovata',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Categoria',
'Category:' => 'Categoria:',
'Categories' => 'Categorie',
- 'Category not found.' => 'Categoria non trovata.',
'Your category have been created successfully.' => 'La tua categoria è stata creata con successo.',
'Unable to create your category.' => 'Impossibile creare la tua categoria.',
'Your category have been updated successfully.' => 'La tua categoria è stata aggiornata con successo.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Vuoi davvero cancellare questo file: "%s"?',
'Attachments' => 'Allegati',
'Edit the task' => 'Modifica il task',
- 'Edit the description' => 'Modifica la descrizione',
'Add a comment' => 'Aggiungi un commento',
'Edit a comment' => 'Modifica un commento',
'Summary' => 'Sommario',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Mostra un altro progetto',
'Created by %s' => 'Creato da %s',
'Tasks Export' => 'Export dei task',
- 'Tasks exportation for "%s"' => 'Export dei task per "%s"',
'Start Date' => 'Data d\'inizio',
'End Date' => 'Data di fine',
'Execute' => 'Esegui',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nuovo sotto-task',
'New attachment added "%s"' => 'Nuovo allegato aggiunto "%s"',
'New comment posted by %s' => 'Nuovo commento aggiunto da "%s"',
- 'New attachment' => 'Nuovo allegato',
'New comment' => 'Nuovo commento',
'Comment updated' => 'Commento aggiornato',
'New subtask' => 'Nuovo sotto-task',
- 'Subtask updated' => 'Sotto-task aggiornato',
- 'Task updated' => 'Task aggiornato',
- 'Task closed' => 'Task chiuso',
- 'Task opened' => 'Task aperto',
'I want to receive notifications only for those projects:' => 'Vorrei ricevere le notifiche solo da questi progetti:',
'view the task on Kanboard' => 'visualizza il task su Kanboard',
'Public access' => 'Accesso pubblico',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Nessuna autenticazione esterna abilitata.',
'Password modified successfully.' => 'Password modificata con successo.',
'Unable to change the password.' => 'Impossibile cambiare la password.',
- 'Change category for the task "%s"' => 'Cambia categoria per il task "%s"',
'Change category' => 'Cambia categoria',
'%s updated the task %s' => '%s ha aggiornato il task %s',
'%s opened the task %s' => '%s ha aperto il task %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s ha aggiornato il task #%d',
'%s created the task #%d' => '%s ha creato il task #%d',
'%s closed the task #%d' => '%s ha chiuso il task #%d',
- '%s open the task #%d' => '%s ha aperto il task #%d',
- '%s moved the task #%d to the column "%s"' => '%s ha spostato il task #%d nella colonna "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s ha spostato il task #%d nella posizione %d della colonna "%s"',
+ '%s opened the task #%d' => '%s ha aperto il task #%d',
'Activity' => 'Attività',
'Default values are "%s"' => 'Valori di default "%s"',
'Default columns for new projects (Comma-separated)' => 'Colonne di default per i nuovi progetti (Separati da virgola)',
'Task assignee change' => 'Cambia l\'assegnatario del task',
- '%s change the assignee of the task #%d to %s' => '%s dai l\'assegnazione del task #%d a %s',
+ '%s changed the assignee of the task #%d to %s' => '%s dai l\'assegnazione del task #%d a %s',
'%s changed the assignee of the task %s to %s' => '%s ha cambiato l\'assegnatario del task %s a %s',
'New password for the user "%s"' => 'Nuova password per l\'utente "%s"',
'Choose an event' => 'Scegli un evento',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Percentuale',
'Number of tasks' => 'Numero di task',
'Task distribution' => 'Distribuzione dei task',
- 'Reportings' => 'Rapporti',
- 'Task repartition for "%s"' => 'Ripartizione task per "%s"',
// 'Analytics' => '',
'Subtask' => 'Sotto-task',
'My subtasks' => 'I miei sotto-task',
'User repartition' => 'Ripartizione per utente',
- 'User repartition for "%s"' => 'Ripartizione utente per "%s"',
'Clone this project' => 'Clona questo progetto',
'Column removed successfully.' => 'Colonna rimossa con successo',
'Not enough data to show the graph.' => 'Non ci sono abbastanza dati per visualizzare il grafico.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Questo valore deve essere numerico',
'Unable to create this task.' => 'Impossibile creare questo task',
'Cumulative flow diagram' => 'Diagramma di flusso cumulativo',
- 'Cumulative flow diagram for "%s"' => 'Diagramma di flusso comulativo per "%s"',
'Daily project summary' => 'Sommario giornaliero del progetto',
'Daily project summary export' => 'Export del sommario giornaliero del progetto',
- 'Daily project summary export for "%s"' => 'Export del sommario giornaliero del progetto per "%s"',
'Exports' => 'Esporta',
'This export contains the number of tasks per column grouped per day.' => 'Questo export contiene il numero di task per colonna raggruppati per giorno',
'Active swimlanes' => 'Corsie attive',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Rimuovi una corsia',
'Show default swimlane' => 'Mostra la corsia predefinita',
'Swimlane modification for the project "%s"' => 'Modifica corsia per il progetto "%s"',
- 'Swimlane not found.' => 'Corsia non trovata.',
'Swimlane removed successfully.' => 'Corsia rimossa con successo.',
'Swimlanes' => 'Corsie',
'Swimlane updated successfully.' => 'Corsia aggiornata con successo.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Id del sotto-task',
'Subtasks' => 'Sotto-task',
'Subtasks Export' => 'Esporta i sotto-task',
- 'Subtasks exportation for "%s"' => 'Export dei sotto-task per "%s"',
'Task Title' => 'Titolo del task',
'Untitled' => 'Senza titolo',
'Application default' => 'Default dell\'applicazione',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Il tasso di cambio è stato aggiunto con successo.',
'Unable to add this currency rate.' => 'Impossibile aggiungere questo tasso di cambio.',
'Webhook URL' => 'URL Webhook',
- '%s remove the assignee of the task %s' => '%s rimuove l\'assegnatario del task %s',
+ '%s removed the assignee of the task %s' => '%s rimuove l\'assegnatario del task %s',
'Enable Gravatar images' => 'Abilita immagini Gravatar',
'Information' => 'Informazioni',
'Check two factor authentication code' => 'Controlla il codice di autenticazione "two-factor"',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Testa il tuo dispositivo',
'Assign a color when the task is moved to a specific column' => 'Assegna un colore quando il task viene spostato in una colonna specifica',
'%s via Kanboard' => '%s tramite Kanboard',
- 'Burndown chart for "%s"' => 'Grafico Burndown per "%s"',
'Burndown chart' => 'Grafico Burndown',
'This chart show the task complexity over the time (Work Remaining).' => 'Questo grafico mostra la complessità dei task nel tempo (Lavoro residuo).',
'Screenshot taken %s' => 'Schermata catturata %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Sposta il task in un\'altra colonna quando la categoria viene modificata',
'Send a task by email to someone' => 'Invia un task via email a qualcuno',
'Reopen a task' => 'Riapri un task',
- 'Column change' => 'Cambio di colonna',
- 'Position change' => 'Cambio di posizione',
- 'Swimlane change' => 'Cambio di corsia',
- 'Assignee change' => 'Cambio assegnatario',
- '[%s] Overdue tasks' => '[%s] Task scaduti',
'Notification' => 'Notifica',
'%s moved the task #%d to the first swimlane' => '%s ha spostato il task #%d nella prima corsia',
- '%s moved the task #%d to the swimlane "%s"' => '%s ha spostato il task #%d nella corsia "%s"',
'Swimlane' => 'Corsia',
// 'Gravatar' => '',
'%s moved the task %s to the first swimlane' => '%s ha spostato il task %s nella prima corsia',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Ricerca per categoria: ',
'Search by description: ' => 'Ricerca per descrizione: ',
'Search by due date: ' => 'Ricerca per data di scadenza: ',
- 'Lead and Cycle time for "%s"' => 'Tempo di consegna (Lead Time) e lavorazione (Cycle Time) per "%s"',
- 'Average time spent into each column for "%s"' => 'Tempo medio trascorso in ogni colonna per "%s"',
'Average time spent into each column' => 'Tempo medio trascorso in ogni colonna',
'Average time spent' => 'Tempo medio trascorso',
'This chart show the average time spent into each column for the last %d tasks.' => 'Questo grafico mostra il tempo medio trascorso in ogni colonna per gli ultimi %d task.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Licenza:',
'License' => 'Licenza',
'Enter the text below' => 'Inserisci il testo qui sotto',
- 'Gantt chart for %s' => 'Grafico Gantt per %s',
'Sort by position' => 'Ordina per posizione',
'Sort by date' => 'Ordina per data',
'Add task' => 'Aggiungi task',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Versione',
'Plugins' => 'Plugin',
'There is no plugin loaded.' => 'Nessun plugin è stato caricato.',
- 'Set maximum column height' => 'Imposta l\'altezza massima della colonna',
- 'Remove maximum column height' => 'Rimuovi l\'altezza massima della colonna',
'My notifications' => 'Le mie notifiche',
'Custom filters' => 'Filtri personalizzati',
'Your custom filter have been created successfully.' => 'Il filtro personalizzato è stato creato con successo.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Captcha non valido',
'The name must be unique' => 'Il nome deve essere univoco',
'View all groups' => 'Visualiza tutti i gruppi',
- 'View group members' => 'Visualizza i membri del gruppo',
'There is no user available.' => 'Nessun utente disponibile.',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Vuoi davvero rimuovere l\'utente "%s" dal gruppo "%s"?',
'There is no group.' => 'Nessun gruppo presente',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Inserisci il nome del gruppo...',
'Role:' => 'Ruolo:',
'Project members' => 'Membri di progetto',
- 'Compare hours for "%s"' => 'Confronta le ore per "%s"',
'%s mentioned you in the task #%d' => '%s ti ha menzionato nel task #%d',
'%s mentioned you in a comment on the task #%d' => '%s ti ha menzionato in un commento del task #%d',
'You were mentioned in the task #%d' => 'Sei stato menzionato nel task #%d',
'You were mentioned in a comment on the task #%d' => 'Sei stato menzionato in un commento del task #%d',
- 'Mentioned' => 'Menzionato',
- 'Compare Estimated Time vs Actual Time' => 'Confronta il Tempo Stimato vs Tempo Effettivo',
'Estimated hours: ' => 'Ore stimate: ',
'Actual hours: ' => 'Ore effettive: ',
'Hours Spent' => 'Ore impiegate',
@@ -1202,4 +1166,52 @@ return array(
'Email transport' => 'Trasporto Email',
// 'Webhook token' => '',
'Imports' => 'Importa',
+ 'Project tags management' => 'Gestione tag di progetto',
+ 'Tag created successfully.' => 'Tag creato con successo.',
+ 'Unable to create this tag.' => 'Impossibile creare questo tag.',
+ 'Tag updated successfully.' => 'Tag aggiornato con successo.',
+ 'Unable to update this tag.' => 'Impossibile aggiornare questo tag.',
+ 'Tag removed successfully.' => 'Tag rimosso con successo.',
+ 'Unable to remove this tag.' => 'Impossibile rimuovere questo tag.',
+ 'Global tags management' => 'Gestione dei tag globali',
+ 'Tags' => 'Tag',
+ 'Tags management' => 'Gestione dei tag',
+ 'Add new tag' => 'Aggiungi un nuovo tag',
+ 'Edit a tag' => 'Modifica un tag',
+ 'Project tags' => 'Tag di progetto',
+ 'There is no specific tag for this project at the moment.' => 'Non è definito nessun tag specifico per questo progetto al momento.',
+ // 'Tag' => '',
+ 'Remove a tag' => 'Rimuovi un tag',
+ 'Do you really want to remove this tag: "%s"?' => 'Vuoi davvero rimuovere questo tag: "%s"?',
+ 'Global tags' => 'Tag globali',
+ 'There is no global tag at the moment.' => 'Non sono definiti tag globali al momento.',
+ 'This field cannot be empty' => 'Questo campo non può essere vuoto',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index 061a2069..d3a11fc8 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d 個のクローズしたタスク',
'No task for this project' => 'このプロジェクトにタスクがありません',
'Public link' => '公開アクセス用リンク',
- 'Change assignee' => '担当を変更する',
- 'Change assignee for the task "%s"' => 'タスク「%s」の担当を変更する',
'Timezone' => 'タイムゾーン',
'Sorry, I didn\'t find this information in my database!' => 'データベース上で情報が見つかりませんでした!',
'Page not found' => 'ページが見つかりません',
@@ -248,7 +246,6 @@ return array(
'Category' => 'カテゴリ',
'Category:' => 'カテゴリ:',
'Categories' => 'カテゴリ',
- 'Category not found.' => 'カテゴリが見つかりません',
'Your category have been created successfully.' => 'カテゴリを作成しました。',
'Unable to create your category.' => 'カテゴリの作成に失敗しました。',
'Your category have been updated successfully.' => 'カテゴリを更新しました。',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'ファイル「%s」を削除しますか?',
'Attachments' => '添付',
'Edit the task' => 'タスクを変更する',
- 'Edit the description' => '説明を変更する',
'Add a comment' => 'コメントの追加',
'Edit a comment' => 'コメントを変更する',
'Summary' => '概要',
@@ -303,7 +299,6 @@ return array(
'Display another project' => '別のプロジェクトを表示',
'Created by %s' => '%s が作成',
'Tasks Export' => 'タスクの出力',
- 'Tasks exportation for "%s"' => '「%s」のタスク出力',
'Start Date' => '開始日',
'End Date' => '終了日',
'Execute' => '実行',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => '新しいサブタスク',
'New attachment added "%s"' => '添付ファイル「%s」が追加されました',
'New comment posted by %s' => '「%s」の新しいコメントが追加されました',
- 'New attachment' => '新しい添付ファイル',
'New comment' => '新しいコメント',
'Comment updated' => 'コメントが更新されました',
'New subtask' => '新しいサブタスク',
- 'Subtask updated' => 'サブタスクの更新',
- 'Task updated' => 'タスクの更新',
- 'Task closed' => 'タスクのクローズ',
- 'Task opened' => 'タスクのオープン',
'I want to receive notifications only for those projects:' => '以下のプロジェクトにのみ通知を受け取る:',
'view the task on Kanboard' => 'Kanboard でタスクを見る',
'Public access' => '公開アクセス設定',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => '外部認証が設定されていません。',
'Password modified successfully.' => 'パスワードを変更しました。',
'Unable to change the password.' => 'パスワードが変更できませんでした。',
- 'Change category for the task "%s"' => 'タスク「%s」のカテゴリの変更',
'Change category' => 'カテゴリの変更',
'%s updated the task %s' => '%s がタスク %s をアップデートしました',
'%s opened the task %s' => '%s がタスク %s をオープンしました',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s がタスク #%d を更新しました',
'%s created the task #%d' => '%s がタスク #%d を追加しました',
'%s closed the task #%d' => '%s がタスク #%d をクローズしました',
- '%s open the task #%d' => '%s がタスク #%d をオープンしました',
- '%s moved the task #%d to the column "%s"' => '%s がタスク #%d をカラム「%s」に移動しました',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s がタスク #%d を位置 %d カラム「%s」移動しました',
+ '%s opened the task #%d' => '%s がタスク #%d をオープンしました',
'Activity' => 'アクティビティ',
'Default values are "%s"' => 'デフォルト値は「%s」',
'Default columns for new projects (Comma-separated)' => '新規プロジェクトのデフォルトカラム (コンマで区切って入力)',
'Task assignee change' => '担当者の変更',
- '%s change the assignee of the task #%d to %s' => '%s がタスク #%d の担当を %s に変更しました',
+ '%s changed the assignee of the task #%d to %s' => '%s がタスク #%d の担当を %s に変更しました',
'%s changed the assignee of the task %s to %s' => '%s がタスク %s の担当を %s に変更しました',
'New password for the user "%s"' => 'ユーザ「%s」の新しいパスワード',
'Choose an event' => 'イベントの選択',
@@ -447,13 +434,10 @@ return array(
'Percentage' => '割合',
'Number of tasks' => 'タスク数',
'Task distribution' => 'タスク分布',
- 'Reportings' => 'レポート',
- 'Task repartition for "%s"' => '「%s」のタスク分布',
'Analytics' => '分析',
'Subtask' => 'サブタスク',
'My subtasks' => '自分のサブタスク',
'User repartition' => '担当者分布',
- 'User repartition for "%s"' => '「%s」の担当者分布',
'Clone this project' => 'このプロジェクトを複製する',
'Column removed successfully.' => 'カラムを削除しました',
'Not enough data to show the graph.' => 'グラフを描画するには出たが足りません',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'この値は数字でなければなりません',
'Unable to create this task.' => 'このタスクを作成できませんでした',
'Cumulative flow diagram' => '蓄積フロー図',
- 'Cumulative flow diagram for "%s"' => '「%s」の蓄積フロー図',
'Daily project summary' => '日時プロジェクトサマリー',
'Daily project summary export' => '日時プロジェクトサマリーの出力',
- 'Daily project summary export for "%s"' => '「%s」の日時プロジェクトサマリーの出力',
'Exports' => '出力',
'This export contains the number of tasks per column grouped per day.' => 'この出力は日時のカラムごとのタスク数を集計したものです',
'Active swimlanes' => 'アクティブなスイムレーン',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'スイムレーンの削除',
'Show default swimlane' => 'デフォルトスイムレーンの表示',
'Swimlane modification for the project "%s"' => '「%s」に対するスイムレーン変更',
- 'Swimlane not found.' => 'スイムレーンが見つかりません。',
'Swimlane removed successfully.' => 'スイムレーンを削除しました。',
'Swimlanes' => 'スイムレーン',
'Swimlane updated successfully.' => 'スイムレーンを更新しました。',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'サブタスク Id',
'Subtasks' => 'サブタスク',
'Subtasks Export' => 'サブタスクの出力',
- 'Subtasks exportation for "%s"' => '「%s」のサブタスク出力',
'Task Title' => 'タスクタイトル',
'Untitled' => 'タイトル無し',
'Application default' => 'アプリケーションデフォルト',
@@ -607,7 +587,7 @@ return array(
// 'The currency rate have been added successfully.' => '',
'Unable to add this currency rate.' => 'この通貨レートを追加できません。',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s がタスク「%s」の担当を解除しました。',
+ '%s removed the assignee of the task %s' => '%s がタスク「%s」の担当を解除しました。',
'Enable Gravatar images' => 'Gravatar イメージを有効化',
'Information' => '情報 ',
'Check two factor authentication code' => '2 段認証をチェックする',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'デバイスをテストする',
// 'Assign a color when the task is moved to a specific column' => '',
// '%s via Kanboard' => '',
- // 'Burndown chart for "%s"' => '',
// 'Burndown chart' => '',
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
@@ -686,14 +665,8 @@ return array(
// 'Move the task to another column when the category is changed' => '',
// 'Send a task by email to someone' => '',
// 'Reopen a task' => '',
- // 'Column change' => '',
- // 'Position change' => '',
- // 'Swimlane change' => '',
- // 'Assignee change' => '',
- // '[%s] Overdue tasks' => '',
// 'Notification' => '',
// '%s moved the task #%d to the first swimlane' => '',
- // '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// '%s moved the task %s to the first swimlane' => '',
@@ -764,8 +737,6 @@ return array(
// 'Search by category: ' => '',
// 'Search by description: ' => '',
// 'Search by due date: ' => '',
- // 'Lead and Cycle time for "%s"' => '',
- // 'Average time spent into each column for "%s"' => '',
// 'Average time spent into each column' => '',
// 'Average time spent' => '',
// 'This chart show the average time spent into each column for the last %d tasks.' => '',
@@ -806,7 +777,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
// 'Project members' => '',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/ko_KR/translations.php b/app/Locale/ko_KR/translations.php
index ea13419b..b8ec0c81 100644
--- a/app/Locale/ko_KR/translations.php
+++ b/app/Locale/ko_KR/translations.php
@@ -100,9 +100,9 @@ return array(
'There is nobody assigned' => '담당자가 없습니다',
'Column on the board:' => '칼럼:',
'Close this task' => '할일 마치기',
- 'Open this task' => '할일 열기',
- 'There is no description.' => '설명이 없습니다',
- 'Add a new task' => '할일 추가 ',
+ 'Open this task' => '할일을 열다',
+ 'There is no description.' => '설명이 없다',
+ 'Add a new task' => '할일을 추가하는 ',
'The username is required' => '사용자 이름이 필요합니다',
'The maximum length is %d characters' => '최대 길이는 "%d" 글자 입니다',
'The minimum length is %d characters' => '최소 길이는 "%d" 글자 입니다',
@@ -145,7 +145,7 @@ return array(
'User removed successfully.' => '사용자를 삭제했습니다.',
'Unable to remove this user.' => '사용자 삭제에 실패했습니다.',
'Board updated successfully.' => '보드를 갱신했습니다.',
- 'Ready' => '준비완료',
+ 'Ready' => '준비중',
'Backlog' => '요구사항',
'Work in progress' => '진행중',
'Done' => '완료',
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d개의 마친 할일',
'No task for this project' => '이 프로젝트에 할일이 없습니다',
'Public link' => '공개 접속 링크',
- 'Change assignee' => '담당자 변경',
- 'Change assignee for the task "%s"' => '할일 "%s"의 담당자를 변경',
'Timezone' => '시간대',
'Sorry, I didn\'t find this information in my database!' => '데이터베이스에서 정보가 발견되지 않았습니다!',
'Page not found' => '페이지가 발견되지 않는다',
@@ -191,15 +189,15 @@ return array(
'Remove an automatic action' => '자동 액션의 삭제',
'Assign the task to a specific user' => '할일 담당자를 할당',
'Assign the task to the person who does the action' => '액션을 일으킨 사용자를 담당자이자',
- 'Duplicate the task to another project' => '할일을 다른 프로젝트로 복사',
- 'Move a task to another column' => '할일을 다른 칼럼으로 이동',
+ 'Duplicate the task to another project' => ' 다른 프로젝트에 할일을 복제하는 ',
+ 'Move a task to another column' => '할일을 다른 칼럼에 이동하는 ',
'Task modification' => '할일 변경',
- 'Task creation' => '할일 만들기',
- 'Closing a task' => '할일 종료',
- 'Assign a color to a specific user' => '사용자 색 할당',
+ 'Task creation' => '할일을 만들',
+ 'Closing a task' => '할일을 닫혔다',
+ 'Assign a color to a specific user' => '색을 사용자에 할당',
'Column title' => '칼럼의 제목',
'Position' => '위치',
- 'Duplicate to another project' => '다른 프로젝트로 복사',
+ 'Duplicate to another project' => '다른 프로젝트에 복사',
'Duplicate' => '복사',
'link' => '링크',
'Comment updated successfully.' => '댓글을 갱신했습니다.',
@@ -248,7 +246,6 @@ return array(
'Category' => '카테고리',
'Category:' => '카테고리:',
'Categories' => '카테고리',
- 'Category not found.' => '카테고리가 발견되지 않습니다',
'Your category have been created successfully.' => '카테고리를 작성했습니다.',
'Unable to create your category.' => '카테고리의 작성에 실패했습니다.',
'Your category have been updated successfully.' => '카테고리를 갱신했습니다.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => '파일 "%s" 을 삭제할까요?',
'Attachments' => '첨부',
'Edit the task' => '할일 수정',
- 'Edit the description' => '설명 수정',
'Add a comment' => '댓글 추가',
'Edit a comment' => '댓글 수정',
'Summary' => '개요',
@@ -303,7 +299,6 @@ return array(
'Display another project' => '프로젝트 보기',
'Created by %s' => '작성자 %s',
'Tasks Export' => '할일 내보내기',
- 'Tasks exportation for "%s"' => '"%s" 할일 내보내기',
'Start Date' => '시작일',
'End Date' => '종료일',
'Execute' => '실행',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => '새로운 서브 할일',
'New attachment added "%s"' => '"%s"의 새로운 첨부 파일',
'New comment posted by %s' => '"%s"님이 댓글을 추가하였습니다',
- 'New attachment' => ' 새로운 첨부 파일',
'New comment' => ' 새로운 댓글',
'Comment updated' => '댓글가 갱신되었습니다',
'New subtask' => ' 새로운 서브 할일',
- 'Subtask updated' => '서브 할일 갱신',
- 'Task updated' => '할일 업데이트',
- 'Task closed' => '할일 마침',
- 'Task opened' => '할일 시작',
'I want to receive notifications only for those projects:' => '다음 프로젝트의 알림만 받겠습니다:',
'view the task on Kanboard' => 'Kanboard에서 할일을 본다',
'Public access' => '공개 접속 설정',
@@ -368,9 +358,8 @@ return array(
'No external authentication enabled.' => '외부 인증이 설정되어 있지 않습니다.',
'Password modified successfully.' => '패스워드를 변경했습니다.',
'Unable to change the password.' => '비밀 번호가 변경할 수 없었습니다.',
- 'Change category for the task "%s"' => '할일 "%s"의 카테고리의 변경',
'Change category' => '카테고리 수정',
- '%s updated the task %s' => '%s이 할일 %s을 업데이트했습니다',
+ '%s updated the task %s' => '%s이 할일 %s을 갱신 하였습니다',
'%s opened the task %s' => '%s이 할일 %s을 시작시켰습니다',
'%s moved the task %s to the position #%d in the column "%s"' => '%s이 할일%s을 위치#%d컬럼%s로 옮겼습니다',
'%s moved the task %s to the column "%s"' => '%s이 할일 %s을 칼럼 "%s" 로 옮겼습니다',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s이 할일#%d을 갱신했습니다',
'%s created the task #%d' => '%s이 할일#%d을 추가했습니다',
'%s closed the task #%d' => '%s이 할일#%d을 닫혔습니다',
- '%s open the task #%d' => '%s이 할일#%d를 오픈했습니다',
- '%s moved the task #%d to the column "%s"' => '%s이 할일#%d을 칼럼"%s"로 옮겼습니다',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s이 할일#%d을 칼럼 "%s"의 %d 위치로 이동시켰습니다',
+ '%s opened the task #%d' => '%s이 할일#%d를 오픈했습니다',
'Activity' => '활동',
'Default values are "%s"' => '기본 값은 "%s" 입니다',
'Default columns for new projects (Comma-separated)' => '새로운 프로젝트의 기본 칼럼 (콤마(,)로 분리됨)',
'Task assignee change' => '담당자의 변경',
- '%s change the assignee of the task #%d to %s' => '%s이 할일 #%d의 담당을 %s로 변경합니다',
+ '%s changed the assignee of the task #%d to %s' => '%s이 할일 #%d의 담당을 %s로 변경합니다',
'%s changed the assignee of the task %s to %s' => '%s이 할일 %s의 담당을 %s로 변경했습니다',
'New password for the user "%s"' => '사용자 "%s"의 새로운 패스워드',
'Choose an event' => '행사의 선택',
@@ -447,13 +434,10 @@ return array(
'Percentage' => '비중',
'Number of tasks' => '할일 수',
'Task distribution' => '할일 분포',
- 'Reportings' => '리포트',
- 'Task repartition for "%s"' => '"%s"의 할일 분포',
'Analytics' => '분석',
'Subtask' => '서브 할일',
'My subtasks' => '내 서브 할일',
'User repartition' => '담당자 분포',
- 'User repartition for "%s"' => '"%s"의 담당자 분포',
'Clone this project' => '이 프로젝트를 복제하는 ',
'Column removed successfully.' => '(※)컬럼을 삭제했습니다',
'Not enough data to show the graph.' => '그래프를 선묘화하려면 나왔지만 부족합니다',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => '이 값은 숫자가 아니면 안 됩니다',
'Unable to create this task.' => '이 할일을 작성할 수 없었습니다',
'Cumulative flow diagram' => '축적 흐름',
- 'Cumulative flow diagram for "%s"' => '"%s"의 축적 흐름',
'Daily project summary' => '하루 프로젝트 개요',
'Daily project summary export' => '하루 프로젝트 개요의 출력',
- 'Daily project summary export for "%s"' => '"%s" 하루 프로젝트 개요의 출력',
'Exports' => '출력',
'This export contains the number of tasks per column grouped per day.' => '이 출력은 날짜의 칼람별 할일 수를 집계한 것입니다',
'Active swimlanes' => '액티브한 스윔레인',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => '스윔레인의 삭제',
'Show default swimlane' => '기본 스윔레인의 표시',
'Swimlane modification for the project "%s"' => '"%s" 프로젝트의 스웜레인 수정',
- 'Swimlane not found.' => '스윔레인이 발견되지 않습니다.',
'Swimlane removed successfully.' => '스윔레인을 삭제했습니다.',
'Swimlanes' => '스윔레인',
'Swimlane updated successfully.' => '스윔레인을 갱신했습니다.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => '서브 할일 Id',
'Subtasks' => '서브 할일',
'Subtasks Export' => '서브 할일 출력',
- 'Subtasks exportation for "%s"' => '"%s"의 서브 할일 출력',
'Task Title' => '할일 제목',
'Untitled' => '제목 없음',
'Application default' => '애플리케이션 기본',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => '통화가 성공적으로 추가되었습니다',
'Unable to add this currency rate.' => '이 통화 환율을 추가할 수 없습니다.',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s이 할일 %s의 담당을 삭제했습니다',
+ '%s removed the assignee of the task %s' => '%s이 할일 %s의 담당을 삭제했습니다',
'Enable Gravatar images' => 'Gravatar이미지를 활성화',
'Information' => '정보',
'Check two factor authentication code' => '2단 인증을 체크한다',
@@ -621,7 +601,6 @@ return array(
'Test your device' => '디바이스 테스트',
'Assign a color when the task is moved to a specific column' => '상세 칼럼으로 이동할 할일의 색깔을 지정하세요',
'%s via Kanboard' => '%s via E-board',
- 'Burndown chart for "%s"' => '"%s" 번다운 차트',
'Burndown chart' => '번다운 차트',
// 'This chart show the task complexity over the time (Work Remaining).' => '',
'Screenshot taken %s' => '스크린샷_%s',
@@ -671,13 +650,13 @@ return array(
'Show tasks based on the start date' => '시작 날짜로 할일 보기',
'Subtasks time tracking' => '서브 할일 시간 트래킹',
'User calendar view' => '담당자 달력 보기',
- 'Automatically update the start date' => '시작일에 자동 업데이트',
+ 'Automatically update the start date' => '시작일에 자동 갱신',
// 'iCal feed' => '',
'Preferences' => '우선권',
'Security' => '보안',
'Two factor authentication disabled' => '이중 인증이 비활성화 되었습니다',
'Two factor authentication enabled' => '이중 인증이 활성화 되었습니다',
- 'Unable to update this user.' => '담당자의 업데이트가 가능합니다',
+ 'Unable to update this user.' => '담당자 갱신이 가능합니다',
'There is no user management for private projects.' => '비밀 프로젝트의 관리 담당자가 없습니다',
'User that will receive the email' => '그 담당자가 이메일을 수신할 것입니다',
'Email subject' => '이메일 제목',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => '카테고리 변경시 할일을 다른 칼럼으로 이동',
'Send a task by email to someone' => '할일을 이메일로 보내기',
'Reopen a task' => '할일 다시 시작',
- 'Column change' => '칼럼 이동',
- 'Position change' => '위치 이동',
- 'Swimlane change' => '스윔레인 변경',
- 'Assignee change' => '담당자 변경',
- '[%s] Overdue tasks' => '[%s] 마감시간 지남',
'Notification' => '알림',
'%s moved the task #%d to the first swimlane' => '%s가 할일 #%d를 첫번째 스웜레인으로 이동시켰습니다',
- '%s moved the task #%d to the swimlane "%s"' => '%s가 할일 #%d를 "%s" 스웜레인으로 이동시켰습니다',
'Swimlane' => '스윔레인',
// 'Gravatar' => '',
'%s moved the task %s to the first swimlane' => '%s가 할일 %s를 첫번째 스웜레인으로 이동시켰습니다',
@@ -716,7 +689,7 @@ return array(
'Recurrence settings have been modified' => '반복할일 설정 수정',
'Time spent changed: %sh' => '경과시간 변경: %s시간',
'Time estimated changed: %sh' => '%s시간으로 예상시간 변경',
- 'The field "%s" have been updated' => '%s 필드가 업데이트 되어있습니다',
+ 'The field "%s" have been updated' => '%s 필드가 갱신되어 있습니다',
'The description has been modified:' => '설명이 수정되어 있습니다: ',
'Do you really want to close the task "%s" as well as all subtasks?' => '할일 "%s"과 서브 할일을 모두 마치시겠습니까?',
'I want to receive notifications for:' => '다음의 알림을 받기를 원합니다:',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => '카테고리로 찾기 ',
'Search by description: ' => '설명으로 찾기 ',
'Search by due date: ' => '마감날짜로 찾기 ',
- 'Lead and Cycle time for "%s"' => '"%s"의 리드와 사이클 시간',
- 'Average time spent into each column for "%s"' => '"%s"의 각 칼럼 평균 소요시간',
'Average time spent into each column' => '각 칼럼의 평균 소요시간',
'Average time spent' => '평균 소요시간',
'This chart show the average time spent into each column for the last %d tasks.' => '마지막 %d 할일의 칼럼 평균 소요시간을 차트에 표시합니다',
@@ -806,7 +777,6 @@ return array(
'License:' => '라이센스:',
'License' => '라이센스',
'Enter the text below' => '아랫글로 들어가기',
- 'Gantt chart for %s' => '%s의 간트 차트',
'Sort by position' => '위치별 정렬',
'Sort by date' => '날짜별 정렬',
'Add task' => '할일 추가',
@@ -847,8 +817,6 @@ return array(
'Version' => '버전',
'Plugins' => '플러그인',
'There is no plugin loaded.' => '플러그인이 로드되지 않았습니다',
- 'Set maximum column height' => '최대 칼럼 높이 제한하기',
- 'Remove maximum column height' => '최대 칼럼 높이 없애기',
'My notifications' => '내 알림',
'Custom filters' => '사용자 정의 필터',
'Your custom filter have been created successfully.' => '사용자 정의 필터가 성공적으로 생성되었습니다',
@@ -861,11 +829,11 @@ return array(
'Web' => '웹',
// 'New attachment on task #%d: %s' => '',
'New comment on task #%d' => '할일 #%d에 새로운 댓글이 달렸습니다',
- 'Comment updated on task #%d' => '할일 #%d에 댓글이 업데이트되었습니다',
- 'New subtask on task #%d' => '서브 할일 #%d이 업데이트되었습니다',
- 'Subtask updated on task #%d' => '서브 할일 #%d가 업데이트되었습니다',
+ 'Comment updated on task #%d' => '할일 #%d에 댓글이 갱신 되었습니다',
+ 'New subtask on task #%d' => '서브 할일 #%d이 갱신 되었습니다',
+ 'Subtask updated on task #%d' => '서브 할일 #%d가 갱신 되었습니다',
'New task #%d: %s' => '할일 #%d: %s이 추가되었습니다',
- 'Task updated #%d' => '할일 #%d이 업데이트되었습니다',
+ 'Task updated #%d' => '할일 #%d이 갱신 되었습니다',
'Task #%d closed' => '할일 #%d를 마쳤습니다',
'Task #%d opened' => '할일 #%d가 시작되었습니다',
'Column changed for task #%d' => '할일 #%d의 칼럼이 변경되었습니다',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
'The name must be unique' => '이름은 유일해야 합니다',
'View all groups' => '모든그룹보기',
- 'View group members' => '그룹맴버 보기',
'There is no user available.' => '가능한 사용자가 없습니다',
'Do you really want to remove the user "%s" from the group "%s"?' => '"%s" 사용자를 "%s" 에서 삭제하시겠습니까?',
'There is no group.' => '그룹이 없습니다',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => '그룹명을 입력합니다...',
'Role:' => '역할: ',
'Project members' => '프로젝트 멤버',
- 'Compare hours for "%s"' => '"%s" 시간동안 비교',
'%s mentioned you in the task #%d' => '#%d 할일에서 %s가 당신을 언급하였습니다',
'%s mentioned you in a comment on the task #%d' => '#%d 할일에서 %s가 당신의 댓글을 언급하였습니다',
'You were mentioned in the task #%d' => '#%d 할일에서 당신이 언급되었습니다',
'You were mentioned in a comment on the task #%d' => '할일 #%d의 댓글에서 언급되었습니다',
- 'Mentioned' => '언급된',
- 'Compare Estimated Time vs Actual Time' => '예상 시간과 실제 시간 비교',
'Estimated hours: ' => '예상 시간: ',
'Actual hours: ' => '실제 시간: ',
'Hours Spent' => '소요 시간',
@@ -1165,41 +1129,89 @@ return array(
'Upload files' => '파일 올리기',
'Installed Plugins' => '설치된 플러그인',
'Plugin Directory' => '플러그인 폴더',
- 'Plugin installed successfully.' => '플러그인이 성공적으로 설치 되었습니다',
- 'Plugin updated successfully.' => '플러그인이 성공적으로 업데이트 되었습니다',
- 'Plugin removed successfully.' => '플러그인이 성공적으로 삭제 되었습니다',
- 'Subtask converted to task successfully.' => '서브 할일이 성공적으로 할일로 변경 되었습니다',
- 'Unable to convert the subtask.' => '서브할일 변경 비활성화',
- 'Unable to extract plugin archive.' => '플러그인 보관소 비활성화',
- 'Plugin not found.' => '플러그인을 찾을 수 없습니다',
- 'You don\'t have the permission to remove this plugin.' => '플러그인 삭제 권한이 없습니다',
- 'Unable to download plugin archive.' => '플러그인 보관소 다운로드 비활성화',
- 'Unable to write temporary file for plugin.' => '플러그인의 임시 파일 기록 비활성화',
- 'Unable to open plugin archive.' => '플러그인 보관소 열기 비활성화',
- 'There is no file in the plugin archive.' => '플러그인 보관소에 파일이 없습니다',
- 'Create tasks in bulk' => '벌크에 할일 생성',
+ 'Plugin installed successfully.' => '플러그인이 성공적으로 설치 되었습니다.',
+ 'Plugin updated successfully.' => '플러그인이 성공적으로 갱신 되었습니다.',
+ 'Plugin removed successfully.' => '플러그인이 성공적으로 삭제 되었습니다.',
+ 'Subtask converted to task successfully.' => '서브 할일이 할일로 성공적으로 변경 되었습니다.',
+ 'Unable to convert the subtask.' => '서브 할일로 변경할 수 없습니다.',
+ 'Unable to extract plugin archive.' => '플러그인 아카이브를 추출할 수 없습니다.',
+ 'Plugin not found.' => '플러그인을 찾을 수 없습니다.',
+ 'You don\'t have the permission to remove this plugin.' => '이 플러그인의 삭제 권한이 없습니다.',
+ 'Unable to download plugin archive.' => '플러그인 아카이브를 다운로드할 수 없습니다.',
+ 'Unable to write temporary file for plugin.' => '플러그인의 임시 파일을 기록할 수 없습니다.',
+ 'Unable to open plugin archive.' => '플러그인 아카이브를 열수 없습니다.',
+ 'There is no file in the plugin archive.' => '플러그인 아카이브에 파일이 존재하지 않습니다.',
+ 'Create tasks in bulk' => '대량의 할일 만들기',
// 'Your Kanboard instance is not configured to install plugins from the user interface.' => '',
- 'There is no plugin available.' => '사용할 수 있는 플러그인이 없습니다',
+ 'There is no plugin available.' => '사용 가능한 플러그인이 없습니다.',
'Install' => '설치',
- 'Update' => '업데이트',
- 'Up to date' => '최신의',
+ 'Update' => '갱신',
+ 'Up to date' => '날짜 갱신',
'Not available' => '사용 불가능',
'Remove plugin' => '플러그인 삭제',
- 'Do you really want to remove this plugin: "%s"?' => '플러그인을 삭제 하시겠습니까: "%s"?',
- 'Uninstall' => '제거',
+ 'Do you really want to remove this plugin: "%s"?' => '정말로 플러그인을 삭제하시겠습니까: "%s"?',
+ 'Uninstall' => '삭제',
'Listing' => '목록',
- 'Metadata' => '메타 데이터',
+ 'Metadata' => '메타데이터',
'Manage projects' => '프로젝트 관리',
- 'Convert to task' => '할일로 변경',
- 'Convert sub-task to task' => '서브 할일을 할일로 변경',
- 'Do you really want to convert this sub-task to a task?' => '서브 할일을 일로 변경하시겠습니까?',
- 'My task title' => '할일 제목',
- // 'Enter one task by line.' => '',
- 'Number of failed login:' => '로그인 실패 횟수',
- 'Account locked until:' => '계정이 잠겼습니다:',
+ 'Convert to task' => '할일로 변경하기',
+ 'Convert sub-task to task' => '서브 할일을 할일로 변경하기',
+ 'Do you really want to convert this sub-task to a task?' => '정말로 서브 할일을 할일로 변경하시겠습니까?',
+ 'My task title' => '나의 할일 제목',
+ 'Enter one task by line.' => '정확히 하나의 할일로 진입',
+ 'Number of failed login:' => '로그인 실패 횟수:',
+ 'Account locked until:' => '다음동안 계정이 잠겼습니다:',
'Email settings' => '이메일 설정',
- 'Email sender address' => '이메일 송신자 주소',
+ 'Email sender address' => '이메일 보낸이 주소',
'Email transport' => '이메일 전송',
// 'Webhook token' => '',
'Imports' => '가져오기',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php
index 913b690a..79baadf7 100644
--- a/app/Locale/my_MY/translations.php
+++ b/app/Locale/my_MY/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d tugas yang ditutup',
'No task for this project' => 'Tidak ada tugas dalam projek ini',
'Public link' => 'Pautan publik',
- 'Change assignee' => 'Mengubah orang yand ditugaskan',
- 'Change assignee for the task "%s"' => 'Mengubah orang yang ditugaskan untuk tugas « %s »',
'Timezone' => 'Zona waktu',
'Sorry, I didn\'t find this information in my database!' => 'Maaf, saya tidak menemukan informasi ini dalam basis data saya !',
'Page not found' => 'Halaman tidak ditemukan',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategori',
'Category:' => 'Kategori :',
'Categories' => 'Kategori',
- 'Category not found.' => 'Kategori tidak ditemukan',
'Your category have been created successfully.' => 'Kategori anda berhasil dibuat.',
'Unable to create your category.' => 'Tidak dapat membuat kategori anda.',
'Your category have been updated successfully.' => 'Kategori anda berhasil diperbaharui.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Apakah anda yakin akan menghapus berkas ini « %s » ?',
'Attachments' => 'Lampiran',
'Edit the task' => 'Modifikasi tugas',
- 'Edit the description' => 'Modifikasi deskripsi',
'Add a comment' => 'Tambahkan komentar',
'Edit a comment' => 'Modifikasi komentar',
'Summary' => 'Ringkasan',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Lihat projek lain',
'Created by %s' => 'Dibuat oleh %s',
'Tasks Export' => 'Ekspor Tugas',
- 'Tasks exportation for "%s"' => 'Tugas di ekspor untuk « %s »',
'Start Date' => 'Tanggal Mulai',
'End Date' => 'Tanggal Berakhir',
'Execute' => 'Eksekusi',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Sub-tugas baru',
'New attachment added "%s"' => 'Lampiran baru ditambahkan « %s »',
'New comment posted by %s' => 'Komentar baru ditambahkan oleh « %s »',
- 'New attachment' => 'Lampirkan baru',
'New comment' => 'Komentar baru',
'Comment updated' => 'Komentar diperbaharui',
'New subtask' => 'Sub-tugas baru',
- 'Subtask updated' => 'Sub-tugas diperbaharui',
- 'Task updated' => 'Tugas diperbaharui',
- 'Task closed' => 'Tugas ditutup',
- 'Task opened' => 'Tugas dibuka',
'I want to receive notifications only for those projects:' => 'Saya ingin menerima pemberitahuan hanya untuk projek-projek yang dipilih :',
'view the task on Kanboard' => 'lihat tugas di Kanboard',
'Public access' => 'Akses awam',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Tidak ada otentifikasi eksternal yang aktif.',
'Password modified successfully.' => 'Kata laluan telah berjaya ditukar.',
'Unable to change the password.' => 'Tidak dapat merubah kata laluanr.',
- 'Change category for the task "%s"' => 'Rubah kategori untuk tugas « %s »',
'Change category' => 'Tukar kategori',
'%s updated the task %s' => '%s memperbaharui tugas %s',
'%s opened the task %s' => '%s membuka tugas %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s memperbaharui tugas n°%d',
'%s created the task #%d' => '%s membuat tugas n°%d',
'%s closed the task #%d' => '%s menutup tugas n°%d',
- '%s open the task #%d' => '%s membuka tugas n°%d',
- '%s moved the task #%d to the column "%s"' => '%s memindahkan tugas n°%d ke kolom « %s »',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s memindahkan tugas n°%d ke posisi n°%d dalam kolom « %s »',
+ '%s opened the task #%d' => '%s membuka tugas n°%d',
'Activity' => 'Aktifitas',
'Default values are "%s"' => 'Standar nilai adalah« %s »',
'Default columns for new projects (Comma-separated)' => 'Kolom default untuk projek baru (dipisahkan dengan koma)',
'Task assignee change' => 'Mengubah orang ditugaskan untuk tugas',
- '%s change the assignee of the task #%d to %s' => '%s rubah orang yang ditugaskan dari tugas n%d ke %s',
+ '%s changed the assignee of the task #%d to %s' => '%s rubah orang yang ditugaskan dari tugas n%d ke %s',
'%s changed the assignee of the task %s to %s' => '%s mengubah orang yang ditugaskan dari tugas %s ke %s',
'New password for the user "%s"' => 'Kata laluan baru untuk pengguna « %s »',
'Choose an event' => 'Pilih sebuah acara',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Persentasi',
'Number of tasks' => 'Jumlah dari tugas',
'Task distribution' => 'Pembagian tugas',
- 'Reportings' => 'Pelaporan',
- 'Task repartition for "%s"' => 'Pembagian tugas untuk « %s »',
'Analytics' => 'Analitis',
'Subtask' => 'Subtugas',
'My subtasks' => 'Subtugas saya',
'User repartition' => 'Partisi ulang pengguna',
- 'User repartition for "%s"' => 'Partisi ulang pengguna untuk « %s »',
'Clone this project' => 'Gandakan projek ini',
'Column removed successfully.' => 'Kolom berhasil dihapus.',
'Not enough data to show the graph.' => 'Tidak cukup data untuk menampilkan grafik.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Nilai ini harus angka',
'Unable to create this task.' => 'Tidak dapat membuat tugas ini',
'Cumulative flow diagram' => 'Diagram alir kumulatif',
- 'Cumulative flow diagram for "%s"' => 'Diagram alir kumulatif untuk « %s »',
'Daily project summary' => 'Ringkasan projek harian',
'Daily project summary export' => 'Ekspot ringkasan projek harian',
- 'Daily project summary export for "%s"' => 'Ekspor ringkasan projek harian untuk « %s »',
'Exports' => 'Ekspor',
'This export contains the number of tasks per column grouped per day.' => 'Ekspor ini berisi jumlah dari tugas per kolom dikelompokan perhari.',
'Active swimlanes' => 'Swimlanes aktif',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Padam swimlane',
'Show default swimlane' => 'Tampilkan piawai swimlane',
'Swimlane modification for the project "%s"' => 'Modifikasi swimlane untuk projek « %s »',
- 'Swimlane not found.' => 'Swimlane tidak ditemui.',
'Swimlane removed successfully.' => 'Swimlane telah dipadamkan.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane telah dikemaskini.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Id Subtugas',
'Subtasks' => 'Subtugas',
'Subtasks Export' => 'Ekspot Subtugas',
- 'Subtasks exportation for "%s"' => 'Ekspor subtugas untuk « %s »',
'Task Title' => 'Judul Tugas',
'Untitled' => 'Tanpa nama',
'Application default' => 'Aplikasi Piawaian',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Nilai tukar mata uang berhasil ditambahkan.',
'Unable to add this currency rate.' => 'Tidak dapat menambahkan nilai tukar mata uang',
'Webhook URL' => 'URL webhook',
- '%s remove the assignee of the task %s' => '%s menghapus penugasan dari tugas %s',
+ '%s removed the assignee of the task %s' => '%s menghapus penugasan dari tugas %s',
'Enable Gravatar images' => 'Mengaktifkan gambar Gravatar',
'Information' => 'Informasi',
'Check two factor authentication code' => 'Cek dua faktor kode otentifikasi',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Menguji perangkat anda',
'Assign a color when the task is moved to a specific column' => 'Menetapkan warna ketika tugas tersebut dipindahkan ke kolom tertentu',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Grafik Burndown untku « %s »',
'Burndown chart' => 'Grafik Burndown',
'This chart show the task complexity over the time (Work Remaining).' => 'Grafik ini menunjukkan kompleksitas tugas dari waktu ke waktu (Sisa Pekerjaan).',
'Screenshot taken %s' => 'Screenshot diambil %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Pindahkan tugas ke kolom lain ketika kategori berubah',
'Send a task by email to someone' => 'Kirim tugas melalui email ke seseorang',
'Reopen a task' => 'Membuka kembali tugas',
- 'Column change' => 'Kolom berubah',
- 'Position change' => 'Posisi berubah',
- 'Swimlane change' => 'Swimlane berubah',
- 'Assignee change' => 'Penerima berubah',
- '[%s] Overdue tasks' => '[%s] Tugas terlambat',
'Notification' => 'Pemberitahuan',
'%s moved the task #%d to the first swimlane' => '%s memindahkan tugas n°%d ke swimlane pertama',
- '%s moved the task #%d to the swimlane "%s"' => '%s memindahkan tugas n°%d ke swimlane « %s »',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s memindahkan tugas %s ke swimlane pertama',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Pencarian berdasarkan kategori : ',
'Search by description: ' => 'Pencarian berdasarkan deskripsi : ',
'Search by due date: ' => 'Pencarian berdasarkan tanggal jatuh tempo : ',
- 'Lead and Cycle time for "%s"' => 'Memimpin dan Siklus waktu untuk « %s »',
- 'Average time spent into each column for "%s"' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom untuk « %s »',
'Average time spent into each column' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom',
'Average time spent' => 'Rata-rata waktu yang dihabiskan',
'This chart show the average time spent into each column for the last %d tasks.' => 'Grafik ini menunjukkan rata-rata waktu yang dihabiskan dalam setiap kolom untuk %d tugas.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Lesen:',
'License' => 'Lesen',
'Enter the text below' => 'Masukkan teks di bawah',
- 'Gantt chart for %s' => 'Carta Gantt untuk %s',
'Sort by position' => 'Urutkan berdasarkan posisi',
'Sort by date' => 'Urutkan berdasarkan tanggal',
'Add task' => 'Tambah tugas',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
'Role:' => 'Peranan',
'Project members' => 'Anggota projek',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php
index c2e5b62f..3a0c3353 100644
--- a/app/Locale/nb_NO/translations.php
+++ b/app/Locale/nb_NO/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d lukkede oppgaver',
'No task for this project' => 'Ingen oppgaver i dette prosjektet',
'Public link' => 'Offentligt lenke',
- 'Change assignee' => 'Tildel oppgaven til andre',
- 'Change assignee for the task "%s"' => 'Endre tildeling av oppgaven: "%s"',
'Timezone' => 'Tidssone',
'Sorry, I didn\'t find this information in my database!' => 'Denne informasjonen kunne ikke finnes i databasen!',
'Page not found' => 'Siden er ikke funnet',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategori',
'Category:' => 'Kategori:',
'Categories' => 'Kategorier',
- 'Category not found.' => 'Kategori ikke funnet.',
'Your category have been created successfully.' => 'Kategorien er opprettet.',
'Unable to create your category.' => 'Kategorien kunne ikke opprettes.',
'Your category have been updated successfully.' => 'Kategorien er oppdatert.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Vil du fjerne filen: "%s"?',
'Attachments' => 'Vedlegg',
'Edit the task' => 'Rediger oppgaven',
- 'Edit the description' => 'Rediger beskrivelsen',
'Add a comment' => 'Legg til en kommentar',
'Edit a comment' => 'Rediger en kommentar',
'Summary' => 'Sammendrag',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Vis annet prosjekt...',
'Created by %s' => 'Opprettet av %s',
'Tasks Export' => 'Oppgave eksport',
- 'Tasks exportation for "%s"' => 'Oppgaveeksportering for "%s"',
'Start Date' => 'Start-dato',
'End Date' => 'Slutt-dato',
'Execute' => 'KKjør',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Ny deloppgave',
'New attachment added "%s"' => 'Nytt vedlegg er lagt tilet "%s"',
'New comment posted by %s' => 'Ny kommentar fra %s',
- 'New attachment' => 'Nytt vedlegg',
'New comment' => 'Ny kommentar',
'Comment updated' => 'Kommentar oppdatert',
'New subtask' => 'Ny deloppgave',
- 'Subtask updated' => 'Deloppgave oppdatert',
- 'Task updated' => 'Oppgave oppdatert',
- 'Task closed' => 'Oppgave lukket',
- 'Task opened' => 'Oppgave åpnet',
'I want to receive notifications only for those projects:' => 'Jeg vil kun ha varslinger for disse prosjekter:',
'view the task on Kanboard' => 'se oppgaven påhovedsiden',
'Public access' => 'Offentlig tilgang',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Ingen eksterne godkjenninger aktiveret.',
'Password modified successfully.' => 'Passord er endret.',
'Unable to change the password.' => 'Passordet kuenne ikke endres.',
- 'Change category for the task "%s"' => 'Endre kategori for oppgaven "%s"',
'Change category' => 'Endre kategori',
'%s updated the task %s' => '%s oppdaterte oppgaven %s',
'%s opened the task %s' => '%s åpnet oppgaven %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s oppdaterte oppgaven #%d',
'%s created the task #%d' => '%s opprettet oppgaven #%d',
'%s closed the task #%d' => '%s lukket oppgaven #%d',
- '%s open the task #%d' => '%s åpnet oppgaven #%d',
- '%s moved the task #%d to the column "%s"' => '%s flyttet oppgaven #%d til kolonnen "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s flyttet oppgaven #%d til posisjonen %d i kolonnen "%s"',
+ '%s opened the task #%d' => '%s åpnet oppgaven #%d',
'Activity' => 'Aktivitetslogg',
'Default values are "%s"' => 'Standardverdier er "%s"',
'Default columns for new projects (Comma-separated)' => 'Standard kolonne for nye prosjekter (komma-separert)',
'Task assignee change' => 'Endring av oppgaveansvarlig',
- '%s change the assignee of the task #%d to %s' => '%s endre ansvarlig for oppgaven #%d til %s',
+ '%s changed the assignee of the task #%d to %s' => '%s endre ansvarlig for oppgaven #%d til %s',
'%s changed the assignee of the task %s to %s' => '%s endret ansvarlig for oppgaven %s til %s',
'New password for the user "%s"' => 'Nytt passord for brukeren "%s"',
'Choose an event' => 'Velg en hendelse',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Prosent',
'Number of tasks' => 'Antall oppgaver',
'Task distribution' => 'Kolonnefordeling',
- 'Reportings' => 'Rapportering',
- // 'Task repartition for "%s"' => '',
'Analytics' => 'Analyser',
'Subtask' => 'Deloppgave',
'My subtasks' => 'Mine deloppgaver',
'User repartition' => 'Brukerfordeling',
- // 'User repartition for "%s"' => '',
'Clone this project' => 'Kopier dette prosjektet',
'Column removed successfully.' => 'Kolonne flyttet',
// 'Not enough data to show the graph.' => '',
@@ -470,10 +454,8 @@ return array(
// 'This value must be numeric' => '',
// 'Unable to create this task.' => '',
'Cumulative flow diagram' => 'Kumulativt flytdiagram',
- // 'Cumulative flow diagram for "%s"' => '',
'Daily project summary' => 'Daglig prosjektsammendrag',
// 'Daily project summary export' => '',
- // 'Daily project summary export for "%s"' => '',
'Exports' => 'Eksporter',
// 'This export contains the number of tasks per column grouped per day.' => '',
'Active swimlanes' => 'Aktive svømmebaner',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Fjern en svømmebane',
'Show default swimlane' => 'Vis standard svømmebane',
// 'Swimlane modification for the project "%s"' => '',
- 'Swimlane not found.' => 'Svømmebane ikke funnet',
'Swimlane removed successfully.' => 'Svømmebane fjernet',
'Swimlanes' => 'Svømmebaner',
'Swimlane updated successfully.' => 'Svømmebane oppdatert',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Deloppgave ID',
'Subtasks' => 'Deloppgaver',
'Subtasks Export' => 'Eksporter deloppgaver',
- // 'Subtasks exportation for "%s"' => '',
'Task Title' => 'Oppgavetittel',
// 'Untitled' => '',
'Application default' => 'Standardinstilling',
@@ -607,7 +587,7 @@ return array(
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Webhook URL' => '',
- // '%s remove the assignee of the task %s' => '',
+ // '%s removed the assignee of the task %s' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@@ -621,7 +601,6 @@ return array(
// 'Test your device' => '',
'Assign a color when the task is moved to a specific column' => 'Endre til en valgt farge hvis en oppgave flyttes til en spesifikk kolonne',
// '%s via Kanboard' => '',
- // 'Burndown chart for "%s"' => '',
// 'Burndown chart' => '',
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Flytt oppgaven til en annen kolonne når kategorien endres',
'Send a task by email to someone' => 'Send en oppgave på epost til noen',
// 'Reopen a task' => '',
- 'Column change' => 'Endret kolonne',
- 'Position change' => 'Posisjonsendring',
- 'Swimlane change' => 'Endret svømmebane',
- 'Assignee change' => 'Endret eier',
- // '[%s] Overdue tasks' => '',
'Notification' => 'Varsel',
// '%s moved the task #%d to the first swimlane' => '',
- // '%s moved the task #%d to the swimlane "%s"' => '',
'Swimlane' => 'Svømmebane',
// 'Gravatar' => '',
// '%s moved the task %s to the first swimlane' => '',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Søk etter kategori',
'Search by description: ' => 'Søk etter beskrivelse',
'Search by due date: ' => 'Søk etter frist',
- // 'Lead and Cycle time for "%s"' => '',
- // 'Average time spent into each column for "%s"' => '',
// 'Average time spent into each column' => '',
// 'Average time spent' => '',
// 'This chart show the average time spent into each column for the last %d tasks.' => '',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Lisens:',
'License' => 'Lisens',
'Enter the text below' => 'Legg inn teksten nedenfor',
- 'Gantt chart for %s' => 'Gantt skjema for %s',
// 'Sort by position' => '',
'Sort by date' => 'Sorter etter dato',
'Add task' => 'Legg til oppgave',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
'Project members' => 'Prosjektmedlemmer',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php
index 9d295129..5a026092 100644
--- a/app/Locale/nl_NL/translations.php
+++ b/app/Locale/nl_NL/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d gesloten taken',
'No task for this project' => 'Geen taken voor dit project',
'Public link' => 'Publieke link',
- 'Change assignee' => 'Toegewezene aanpassen',
- 'Change assignee for the task "%s"' => 'Toegewezene aanpassen voor taak « %s »',
'Timezone' => 'Tijdzone',
'Sorry, I didn\'t find this information in my database!' => 'Sorry deze informatie kon niet worden gevonden in de database !',
'Page not found' => 'Pagina niet gevonden',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Categorie',
'Category:' => 'Categorie :',
'Categories' => 'Categorieën',
- 'Category not found.' => 'Categorie niet gevonden',
'Your category have been created successfully.' => 'Categorie succesvol aangemaakt.',
'Unable to create your category.' => 'Categorie aanmaken niet gelukt.',
'Your category have been updated successfully.' => 'Categorie succesvol aangepast.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Weet u zeker dat u dit bestand wil verwijderen: « %s » ?',
'Attachments' => 'Bijlages',
'Edit the task' => 'Taak aanpassen',
- 'Edit the description' => 'Omschrijving aanpassen',
'Add a comment' => 'Commentaar toevoegen',
'Edit a comment' => 'Commentaar aanpassen',
'Summary' => 'Samenvatting',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Een ander project weergeven',
'Created by %s' => 'Aangemaakt door %s',
'Tasks Export' => 'Taken exporteren',
- 'Tasks exportation for "%s"' => 'Taken exporteren voor « %s »',
'Start Date' => 'Startdatum',
'End Date' => 'Einddatum',
'Execute' => 'Uitvoeren',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nieuwe subtaak',
'New attachment added "%s"' => 'Nieuwe bijlage toegevoegd « %s »',
'New comment posted by %s' => 'Nieuw commentaar geplaatst door « %s »',
- 'New attachment' => 'Nieuwe bijlage',
'New comment' => 'Nieuw commentaar',
'Comment updated' => 'Commentaar aangepast',
'New subtask' => 'Nieuwe subtaak',
- 'Subtask updated' => 'Subtaak aangepast',
- 'Task updated' => 'Taak aangepast',
- 'Task closed' => 'Taak gesloten',
- 'Task opened' => 'Taak geopend',
'I want to receive notifications only for those projects:' => 'Ik wil notificaties ontvangen van de volgende projecten :',
'view the task on Kanboard' => 'taak bekijken op Kanboard',
'Public access' => 'Publieke toegang',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Geen externe authenticatie aangezet.',
'Password modified successfully.' => 'Wachtwoord succesvol aangepast.',
'Unable to change the password.' => 'Aanpassen van wachtwoord niet gelukt.',
- 'Change category for the task "%s"' => 'Pas categorie aan voor taak « %s »',
'Change category' => 'Categorie aanpassen',
'%s updated the task %s' => '%s heeft taak %s aangepast',
'%s opened the task %s' => '%s heeft taak %s geopend',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s heeft taak %d aangepast',
'%s created the task #%d' => '%s heeft taak %d aangemaakt',
'%s closed the task #%d' => '%s heeft taak %d gesloten',
- '%s open the task #%d' => '%s a heeft taak %d geopend',
- '%s moved the task #%d to the column "%s"' => '%s heeft taak %d verplaatst naar kolom « %s »',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s heeft taak %d verplaatst naar positie %d in kolom « %s »',
+ '%s opened the task #%d' => '%s a heeft taak %d geopend',
'Activity' => 'Activiteit',
'Default values are "%s"' => 'Standaardwaarden zijn « %s »',
'Default columns for new projects (Comma-separated)' => 'Standaard kolommen voor nieuw projecten (komma gescheiden)',
'Task assignee change' => 'Taak toegewezene verandering',
- '%s change the assignee of the task #%d to %s' => '%s heeft de toegewezene voor taak %d veranderd in %s',
+ '%s changed the assignee of the task #%d to %s' => '%s heeft de toegewezene voor taak %d veranderd in %s',
'%s changed the assignee of the task %s to %s' => '%s heeft de toegewezene voor taak %s veranderd in %s',
'New password for the user "%s"' => 'Nieuw wachtwoord voor gebruiker « %s »',
'Choose an event' => 'Kies een gebeurtenis',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Percentage',
'Number of tasks' => 'Aantal taken',
'Task distribution' => 'Distributie van taken',
- 'Reportings' => 'Rapporten',
- 'Task repartition for "%s"' => 'Taakverdeling voor « %s »',
'Analytics' => 'Analytics',
'Subtask' => 'Subtaak',
'My subtasks' => 'Mijn subtaken',
'User repartition' => 'Gebruikerverdeling',
- 'User repartition for "%s"' => 'Gebruikerverdeling voor « %s »',
'Clone this project' => 'Kloon dit project',
'Column removed successfully.' => 'Kolom succesvol verwijderd.',
'Not enough data to show the graph.' => 'Niet genoeg data om de grafiek te laten zien.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Deze waarde moet numeriek zijn',
'Unable to create this task.' => 'Aanmaken van de taak mislukt',
'Cumulative flow diagram' => 'Cummulatief stroomdiagram',
- 'Cumulative flow diagram for "%s"' => 'Cummulatief stroomdiagram voor « %s »',
'Daily project summary' => 'Dagelijkse project samenvatting',
'Daily project summary export' => 'Dagelijkse project samenvatting export',
- 'Daily project summary export for "%s"' => 'Dagelijkse project samenvatting voor « %s »',
'Exports' => 'Exports',
'This export contains the number of tasks per column grouped per day.' => 'Dit rapport bevat het aantal taken per kolom gegroupeerd per dag.',
'Active swimlanes' => 'Actieve swinlanes',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Verwijder swinlane',
'Show default swimlane' => 'Standaard swimlane tonen',
'Swimlane modification for the project "%s"' => 'Swinlane aanpassing voor project « %s »',
- 'Swimlane not found.' => 'Swimlane niet gevonden.',
'Swimlane removed successfully.' => 'Swimlane succesvol verwijderd.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane succesvol aangepast.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Subtaak id',
'Subtasks' => 'Subtaken',
'Subtasks Export' => 'Subtaken exporteren',
- 'Subtasks exportation for "%s"' => 'Subtaken exporteren voor project « %s »',
'Task Title' => 'Taak title',
'Untitled' => 'Geen titel',
'Application default' => 'Standaard taal voor applicatie',
@@ -607,7 +587,7 @@ return array(
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
'Webhook URL' => 'Webhook URL',
- // '%s remove the assignee of the task %s' => '',
+ // '%s removed the assignee of the task %s' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@@ -621,7 +601,6 @@ return array(
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
// '%s via Kanboard' => '',
- // 'Burndown chart for "%s"' => '',
// 'Burndown chart' => '',
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
@@ -686,14 +665,8 @@ return array(
// 'Move the task to another column when the category is changed' => '',
// 'Send a task by email to someone' => '',
'Reopen a task' => 'Heropen een taak',
- // 'Column change' => '',
- // 'Position change' => '',
- // 'Swimlane change' => '',
- // 'Assignee change' => '',
- // '[%s] Overdue tasks' => '',
// 'Notification' => '',
// '%s moved the task #%d to the first swimlane' => '',
- // '%s moved the task #%d to the swimlane "%s"' => '',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s heeft de taak %s naar de eerste swimlane verplaatst',
@@ -764,8 +737,6 @@ return array(
// 'Search by category: ' => '',
// 'Search by description: ' => '',
// 'Search by due date: ' => '',
- // 'Lead and Cycle time for "%s"' => '',
- // 'Average time spent into each column for "%s"' => '',
// 'Average time spent into each column' => '',
// 'Average time spent' => '',
// 'This chart show the average time spent into each column for the last %d tasks.' => '',
@@ -806,7 +777,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
// 'Project members' => '',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index 34429305..af3bcd4e 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d zamkniętych zadań',
'No task for this project' => 'Brak zadań dla tego projektu',
'Public link' => 'Link publiczny',
- 'Change assignee' => 'Zmień odpowiedzialną osobę',
- 'Change assignee for the task "%s"' => 'Zmień odpowiedzialną osobę dla zadania "%s"',
'Timezone' => 'Strefa czasowa',
'Sorry, I didn\'t find this information in my database!' => 'Niestety nie znaleziono tej informacji w bazie danych',
'Page not found' => 'Strona nie istnieje',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategoria',
'Category:' => 'Kategoria:',
'Categories' => 'Kategorie',
- 'Category not found.' => 'Kategoria nie istnieje',
'Your category have been created successfully.' => 'Pomyślnie utworzono kategorię.',
'Unable to create your category.' => 'Nie można tworzyć kategorii.',
'Your category have been updated successfully.' => 'Pomyślnie zaktualizowano kategorię',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Czy na pewno chcesz usunąć plik: "%s"?',
'Attachments' => 'Załączniki',
'Edit the task' => 'Edytuj zadanie',
- 'Edit the description' => 'Edytuj opis',
'Add a comment' => 'Dodaj komentarz',
'Edit a comment' => 'Edytuj komentarz',
'Summary' => 'Podsumowanie',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Wyświetl inny projekt',
'Created by %s' => 'Utworzone przez %s',
'Tasks Export' => 'Eksport zadań',
- 'Tasks exportation for "%s"' => 'Eksport zadań dla "%s"',
'Start Date' => 'Data początkowa',
'End Date' => 'Data Końcowa',
'Execute' => 'Wykonaj',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nowe Pod-zadanie',
'New attachment added "%s"' => 'Nowy załącznik dodany "%s"',
'New comment posted by %s' => 'Nowy komentarz dodany przez %s',
- 'New attachment' => 'Nowy załącznik',
'New comment' => 'Nowy Komentarz',
'Comment updated' => 'Komentarz zaktualizowany',
'New subtask' => 'Nowe pod-zadanie',
- 'Subtask updated' => 'Zaktualizowane pod-zadanie',
- 'Task updated' => 'Zaktualizowane zadanie',
- 'Task closed' => 'Zadanie zamknięte',
- 'Task opened' => 'Zadanie otwarte',
'I want to receive notifications only for those projects:' => 'Chcę otrzymywać powiadomienia tylko dla poniższych projektów:',
'view the task on Kanboard' => 'Zobacz zadanie',
'Public access' => 'Dostęp publiczny',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Brak włączonych uwierzytelnień zewnętrznych.',
'Password modified successfully.' => 'Hasło zmienione pomyślne.',
'Unable to change the password.' => 'Nie można zmienić hasła.',
- 'Change category for the task "%s"' => 'Zmień kategorię dla zadania "%s"',
'Change category' => 'Zmień kategorię',
'%s updated the task %s' => '%s zaktualizował zadanie %s',
'%s opened the task %s' => '%s otworzył zadanie %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s zaktualizował zadanie #%d',
'%s created the task #%d' => '%s utworzył zadanie #%d',
'%s closed the task #%d' => '%s zamknął zadanie #%d',
- '%s open the task #%d' => '%s otworzył zadanie #%d',
- '%s moved the task #%d to the column "%s"' => '%s przeniósł zadanie #%d do kolumny "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s przeniósł zadanie #%d na pozycję %d w kolmnie "%s"',
+ '%s opened the task #%d' => '%s otworzył zadanie #%d',
'Activity' => 'Aktywność',
'Default values are "%s"' => 'Domyślne wartości: "%s"',
'Default columns for new projects (Comma-separated)' => 'Domyślne kolumny dla nowych projektów (oddzielone przecinkiem)',
'Task assignee change' => 'Zmień osobę odpowiedzialną',
- '%s change the assignee of the task #%d to %s' => '%s zmienił osobę odpowiedzialną za zadanie #%d na %s',
+ '%s changed the assignee of the task #%d to %s' => '%s zmienił osobę odpowiedzialną za zadanie #%d na %s',
'%s changed the assignee of the task %s to %s' => '%s zmienił osobę odpowiedzialną za zadanie %s na %s',
'New password for the user "%s"' => 'Nowe hasło użytkownika "%s"',
'Choose an event' => 'Wybierz zdarzenie',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Procent',
'Number of tasks' => 'Liczba zadań',
'Task distribution' => 'Rozmieszczenie zadań',
- 'Reportings' => 'Raporty',
- 'Task repartition for "%s"' => 'Przydział zadań dla "%s"',
'Analytics' => 'Analizy',
'Subtask' => 'Pod-zadanie',
'My subtasks' => 'Moje pod-zadania',
'User repartition' => 'Przydział użytkownika',
- 'User repartition for "%s"' => 'Przydział użytkownika dla "%s"',
'Clone this project' => 'Sklonuj ten projekt',
'Column removed successfully.' => 'Kolumna usunięta pomyślnie.',
'Not enough data to show the graph.' => 'Za mało danych do utworzenia wykresu.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Wartość musi być liczbą',
'Unable to create this task.' => 'Nie można tworzyć zadania.',
'Cumulative flow diagram' => 'Zbiorowy diagram przepływu',
- 'Cumulative flow diagram for "%s"' => 'Zbiorowy diagram przepływu dla "%s"',
'Daily project summary' => 'Dzienne raport z projektu',
'Daily project summary export' => 'Eksport dziennego podsumowania projektu',
- 'Daily project summary export for "%s"' => 'Wygeneruj dzienny raport dla projektu: "%s"',
'Exports' => 'Eksporty',
'This export contains the number of tasks per column grouped per day.' => 'Ten eksport zawiera ilość zadań zgrupowanych w kolumnach na dzień',
'Active swimlanes' => 'Aktywne tory',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Usuń tor',
'Show default swimlane' => 'Pokaż domyślny tor',
'Swimlane modification for the project "%s"' => 'Edycja torów dla projektu "%s"',
- 'Swimlane not found.' => 'Nie znaleziono toru.',
'Swimlane removed successfully.' => 'Tor usunięty pomyślnie.',
'Swimlanes' => 'Tory',
'Swimlane updated successfully.' => 'Zaktualizowano tor.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'ID pod-zadania',
'Subtasks' => 'Pod-zadania',
'Subtasks Export' => 'Eksport pod-zadań',
- 'Subtasks exportation for "%s"' => 'Wygeneruj raport pod-zadań dla projektu "%s"',
'Task Title' => 'Nazwa zadania',
'Untitled' => 'Bez nazwy',
'Application default' => 'Domyślne dla aplikacji',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Dodano kurs waluty',
'Unable to add this currency rate.' => 'Nie można dodać kursu waluty',
'Webhook URL' => 'Adres webhooka',
- '%s remove the assignee of the task %s' => '%s usunął osobę przypisaną do zadania %s',
+ '%s removed the assignee of the task %s' => '%s usunął osobę przypisaną do zadania %s',
'Enable Gravatar images' => 'Włącz Gravatar',
'Information' => 'Informacje',
'Check two factor authentication code' => 'Sprawdź kod weryfikujący',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Przetestuj urządzenie',
'Assign a color when the task is moved to a specific column' => 'Przypisz kolor gdy zadanie jest przeniesione do danej kolumny',
'%s via Kanboard' => '%s poprzez Kanboard',
- 'Burndown chart for "%s"' => 'Wykres Burndown dla "%s"',
'Burndown chart' => 'Wykres Burndown',
'This chart show the task complexity over the time (Work Remaining).' => 'Ten wykres pokazuje złożoność zadania na przestrzeni czasu (pozostała praca).',
'Screenshot taken %s' => 'Zrzut ekranu zapisany %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Przenieś zadanie do innej kolumny gdy kategoria ulegnie zmianie',
'Send a task by email to someone' => 'Wyślij zadanie emailem do kogoś',
'Reopen a task' => 'Otwórz ponownie zadanie',
- 'Column change' => 'Zmiana kolumny',
- 'Position change' => 'Zmiana pozycji',
- 'Swimlane change' => 'Zmiana toru',
- 'Assignee change' => 'Zmiana przypisanego użytkownika',
- '[%s] Overdue tasks' => '[%s] zaległych zadań',
'Notification' => 'Powiadomienie',
'%s moved the task #%d to the first swimlane' => '%s przeniosł zadanie #%d na pierwszy tor',
- '%s moved the task #%d to the swimlane "%s"' => '%s przeniosł zadanie #%d na tor "%s"',
'Swimlane' => 'Tor',
// 'Gravatar' => '',
'%s moved the task %s to the first swimlane' => '%s przeniosł zadanie %s na pierwszy tor',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Szukaj wg kategorii:',
'Search by description: ' => 'Szukaj wg opisu:',
'Search by due date: ' => 'Szukaj wg terminu:',
- 'Lead and Cycle time for "%s"' => 'Czas cyklu i realizacji dla "%s"',
- 'Average time spent into each column for "%s"' => 'Średni czas spędzony w każdej z kolumn dla "%s"',
'Average time spent into each column' => 'Średni czas spędzony w każdej z kolumn',
'Average time spent' => 'Średni spędzony czas',
'This chart show the average time spent into each column for the last %d tasks.' => 'Niniejszy wykres pokazuje średni czas spędzony w każdej z kolumn dla ostatnich %d zadań.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Licencja:',
'License' => 'Licencja',
'Enter the text below' => 'Wpisz tekst poniżej',
- 'Gantt chart for %s' => 'Wykres Gantt dla %s',
'Sort by position' => 'Sortuj wg pozycji',
'Sort by date' => 'Sortuj wg daty',
'Add task' => 'Dodaj zadanie',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Wersja',
'Plugins' => 'Wtyczki',
'There is no plugin loaded.' => 'Nie wykryto żadnych wtyczek.',
- 'Set maximum column height' => 'Rozwiń kolumny',
- 'Remove maximum column height' => 'Zwiń kolumny',
'My notifications' => 'Powiadomienia',
'Custom filters' => 'Dostosuj filtry',
'Your custom filter have been created successfully.' => 'Niestandardowy filtr został utworzony.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Błędny kod z obrazka (captcha)',
'The name must be unique' => 'Nazwa musi być unikatowa',
'View all groups' => 'Wyświetl wszystkie grupy',
- 'View group members' => 'Wyświetl wszystkich członków grupy',
'There is no user available.' => 'Żaden użytkownik nie jest dostępny.',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Czy na pewno chcesz usunąć użytkownika "%s" z grupy "%s"?',
'There is no group.' => 'Nie utworzono jeszcze żadnej grupy.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Wprowadź nazwę grupy...',
'Role:' => 'Rola:',
'Project members' => 'Uczestnicy projektu',
- 'Compare hours for "%s"' => 'Porównaj godziny dla "%s"',
'%s mentioned you in the task #%d' => '%s wspomiał o Tobie w zadaniu #%d',
'%s mentioned you in a comment on the task #%d' => '%s wspomiał o Tobie w komentarzu do zadania #%d',
'You were mentioned in the task #%d' => 'Wspomiano o Tobie w zadaniu #%d',
'You were mentioned in a comment on the task #%d' => 'Wspomiano o Tobie w komentarzu do zadania #%d',
- 'Mentioned' => 'Wspomiano',
- 'Compare Estimated Time vs Actual Time' => 'Porównaj szacowany czas z rzeczywistym',
'Estimated hours: ' => 'Szacowane godziny: ',
'Actual hours: ' => 'Rzeczywiste godziny: ',
'Hours Spent' => 'Spędzone godziny',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index 04b9feaf..2772d8bb 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d tarefas finalizadas',
'No task for this project' => 'Não há tarefa para este projeto',
'Public link' => 'Link público',
- 'Change assignee' => 'Alterar designação',
- 'Change assignee for the task "%s"' => 'Alterar designação para a tarefa "%s"',
'Timezone' => 'Fuso horário',
'Sorry, I didn\'t find this information in my database!' => 'Desculpe, não encontrei esta informação no meu banco de dados!',
'Page not found' => 'Página não encontrada',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Categoria',
'Category:' => 'Categoria:',
'Categories' => 'Categorias',
- 'Category not found.' => 'Categoria não encontrada.',
'Your category have been created successfully.' => 'Sua categoria foi criada com sucesso.',
'Unable to create your category.' => 'Não foi possível criar a sua categoria.',
'Your category have been updated successfully.' => 'A sua categoria foi atualizada com sucesso.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Você realmente deseja remover este arquivo: "%s"?',
'Attachments' => 'Anexos',
'Edit the task' => 'Editar a tarefa',
- 'Edit the description' => 'Editar a descrição',
'Add a comment' => 'Adicionar um comentário',
'Edit a comment' => 'Editar um comentário',
'Summary' => 'Resumo',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Exibir outro projeto',
'Created by %s' => 'Criado por %s',
'Tasks Export' => 'Exportar Tarefas',
- 'Tasks exportation for "%s"' => 'As tarefas foram exportadas para "%s"',
'Start Date' => 'Data inicial',
'End Date' => 'Data final',
'Execute' => 'Executar',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nova subtarefa',
'New attachment added "%s"' => 'Novo anexo adicionado "%s"',
'New comment posted by %s' => 'Novo comentário postado por %s',
- 'New attachment' => 'Novo anexo',
'New comment' => 'Novo comentário',
'Comment updated' => 'Comentário atualizado',
'New subtask' => 'Nova subtarefa',
- 'Subtask updated' => 'Subtarefa alterada',
- 'Task updated' => 'Tarefa alterada',
- 'Task closed' => 'Tarefa finalizada',
- 'Task opened' => 'Tarefa aberta',
'I want to receive notifications only for those projects:' => 'Quero receber notificações apenas destes projetos:',
'view the task on Kanboard' => 'ver a tarefa no Kanboard',
'Public access' => 'Acesso público',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Nenhuma autenticação externa habilitada.',
'Password modified successfully.' => 'Senha alterada com sucesso.',
'Unable to change the password.' => 'Não foi possível alterar a senha.',
- 'Change category for the task "%s"' => 'Mudar categoria da tarefa "%s"',
'Change category' => 'Mudar categoria',
'%s updated the task %s' => '%s atualizou a tarefa %s',
'%s opened the task %s' => '%s abriu a tarefa %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s atualizou a tarefa #%d',
'%s created the task #%d' => '%s criou a tarefa #%d',
'%s closed the task #%d' => '%s finalizou a tarefa #%d',
- '%s open the task #%d' => '%s abriu a tarefa #%d',
- '%s moved the task #%d to the column "%s"' => '%s moveu a tarefa #%d para a coluna "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s moveu a tarefa #%d para a posição %d na coluna "%s"',
+ '%s opened the task #%d' => '%s abriu a tarefa #%d',
'Activity' => 'Atividade',
'Default values are "%s"' => 'Os valores padrão são "%s"',
'Default columns for new projects (Comma-separated)' => 'Colunas padrão para novos projetos (Separado por vírgula)',
'Task assignee change' => 'Mudar designação da tarefa',
- '%s change the assignee of the task #%d to %s' => '%s mudou a designação da tarefa #%d para %s',
+ '%s changed the assignee of the task #%d to %s' => '%s mudou a designação da tarefa #%d para %s',
'%s changed the assignee of the task %s to %s' => '%s mudou a designação da tarefa %s para %s',
'New password for the user "%s"' => 'Nova senha para o usuário "%s"',
'Choose an event' => 'Escolher um evento',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Porcentagem',
'Number of tasks' => 'Número de tarefas',
'Task distribution' => 'Distribuição de tarefas',
- 'Reportings' => 'Relatórios',
- 'Task repartition for "%s"' => 'Redistribuição da tarefa para "%s"',
'Analytics' => 'Estatísticas',
'Subtask' => 'Subtarefa',
'My subtasks' => 'Minhas subtarefas',
'User repartition' => 'Redistribuição de usuário',
- 'User repartition for "%s"' => 'Redistribuição de usuário para "%s"',
'Clone this project' => 'Clonar este projeto',
'Column removed successfully.' => 'Coluna removida com sucesso.',
'Not enough data to show the graph.' => 'Não há dados suficientes para mostrar o gráfico.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Este valor deve ser numérico',
'Unable to create this task.' => 'Não foi possível criar esta tarefa.',
'Cumulative flow diagram' => 'Fluxograma cumulativo',
- 'Cumulative flow diagram for "%s"' => 'Fluxograma cumulativo para "%s"',
'Daily project summary' => 'Resumo diário do projeto',
'Daily project summary export' => 'Exportação diária do resumo do projeto',
- 'Daily project summary export for "%s"' => 'Exportação diária do resumo do projeto para "%s"',
'Exports' => 'Exportar',
'This export contains the number of tasks per column grouped per day.' => 'Esta exportação contém o número de tarefas por coluna agrupada por dia.',
'Active swimlanes' => 'Ativar swimlanes',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Remover uma swimlane',
'Show default swimlane' => 'Exibir swimlane padrão',
'Swimlane modification for the project "%s"' => 'Modificação de swimlane para o projeto "%s"',
- 'Swimlane not found.' => 'Swimlane não encontrada.',
'Swimlane removed successfully.' => 'Swimlane removida com sucesso.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane atualizada com sucesso.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'ID da subtarefa',
'Subtasks' => 'Subtarefas',
'Subtasks Export' => 'Exportar subtarefas',
- 'Subtasks exportation for "%s"' => 'Subtarefas exportadas para "%s"',
'Task Title' => 'Título da Tarefa',
'Untitled' => 'Sem título',
'Application default' => 'Padrão da aplicação',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'A taxa de câmbio foi adicionada com sucesso.',
'Unable to add this currency rate.' => 'Impossível de adicionar essa taxa de câmbio.',
'Webhook URL' => 'URL do webhook',
- '%s remove the assignee of the task %s' => '%s removeu a pessoa designada para a tarefa %s',
+ '%s removed the assignee of the task %s' => '%s removeu a pessoa designada para a tarefa %s',
'Enable Gravatar images' => 'Ativar imagens do Gravatar',
'Information' => 'Informações',
'Check two factor authentication code' => 'Verifique o código de autenticação em duas etapas',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Teste o seu dispositivo',
'Assign a color when the task is moved to a specific column' => 'Atribuir uma cor quando a tarefa é movida em uma coluna específica',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Gráfico de Burndown para "%s"',
'Burndown chart' => 'Gráfico de Burndown',
'This chart show the task complexity over the time (Work Remaining).' => 'Este gráfico mostra a complexidade da tarefa ao longo do tempo (Trabalho Restante).',
'Screenshot taken %s' => 'Captura de tela tirada em %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Mover uma tarefa para outra coluna quando a categoria mudou',
'Send a task by email to someone' => 'Enviar uma tarefa por e-mail a alguém',
'Reopen a task' => 'Reabrir uma tarefa',
- 'Column change' => 'Mudança de coluna',
- 'Position change' => 'Mudança de posição',
- 'Swimlane change' => 'Mudança de swimlane',
- 'Assignee change' => 'Mudança de designação',
- '[%s] Overdue tasks' => '[%s] Tarefas atrasadas',
'Notification' => 'Notificação',
'%s moved the task #%d to the first swimlane' => '%s moveu a tarefa #%d para a primeira swimlane',
- '%s moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa #%d para a swimlane "%s"',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s para a primeira swimlane',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Pesquisar por categoria: ',
'Search by description: ' => 'Pesquisar por descrição: ',
'Search by due date: ' => 'Pesquisar por data de expiração: ',
- 'Lead and Cycle time for "%s"' => 'Lead and Cycle time para "%s"',
- 'Average time spent into each column for "%s"' => 'Tempo médio gasto em cada coluna para "%s"',
'Average time spent into each column' => 'Tempo médio gasto em cada coluna',
'Average time spent' => 'Tempo médio gasto',
'This chart show the average time spent into each column for the last %d tasks.' => 'Este gráfico mostra o tempo médio gasto em cada coluna para as %d últimas tarefas.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Licença:',
'License' => 'Licença',
'Enter the text below' => 'Entre o texto abaixo',
- 'Gantt chart for %s' => 'Gráfico de Gantt para %s',
'Sort by position' => 'Ordenar por posição',
'Sort by date' => 'Ordenar por data',
'Add task' => 'Adicionar uma tarefa',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Versão',
'Plugins' => 'Extensões',
'There is no plugin loaded.' => 'Não há extensões carregadas.',
- 'Set maximum column height' => 'Definir a altura máxima das colunas',
- 'Remove maximum column height' => 'Retirar a altura máxima das colunas',
'My notifications' => 'Minhas notificações',
'Custom filters' => 'Filtros personalizados',
'Your custom filter have been created successfully.' => 'Seu filtro personalizado foi criado com sucesso.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Captcha inválido',
'The name must be unique' => 'O nome deve ser único',
'View all groups' => 'Ver todos os grupos',
- 'View group members' => 'Ver os membros do grupo',
'There is no user available.' => 'Não há nenhum usuário disponível',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Você realmente deseja remover o usuário "%s" do grupo "%s"?',
'There is no group.' => 'Não há nenhum grupo.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Digite o nome do grupo',
'Role:' => 'Função:',
'Project members' => 'Membros de projeto',
- 'Compare hours for "%s"' => 'Comparar as horas para "%s"',
'%s mentioned you in the task #%d' => '%s mencionou você na tarefa #%d',
'%s mentioned you in a comment on the task #%d' => '%s mencionou você num comentário da tarefa #%d',
'You were mentioned in the task #%d' => 'Você foi mencionado na tarefa #%d',
'You were mentioned in a comment on the task #%d' => 'Você foi mencionado num comentário da tarefa #%d',
- 'Mentioned' => 'Mencionado',
- 'Compare Estimated Time vs Actual Time' => 'Comparar o tempo estimado e o tempo atual',
'Estimated hours: ' => 'Horas estimadas: ',
'Actual hours: ' => 'Horas reais: ',
'Hours Spent' => 'Horas gastas',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php
index d2b1e62d..ebc26cd7 100644
--- a/app/Locale/pt_PT/translations.php
+++ b/app/Locale/pt_PT/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d tarefas finalizadas',
'No task for this project' => 'Não há tarefa para este projecto',
'Public link' => 'Link público',
- 'Change assignee' => 'Mudar a assignação',
- 'Change assignee for the task "%s"' => 'Modificar assignação para a tarefa "%s"',
'Timezone' => 'Fuso horário',
'Sorry, I didn\'t find this information in my database!' => 'Desculpe, não encontrei esta informação na minha base de dados!',
'Page not found' => 'Página não encontrada',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Categoria',
'Category:' => 'Categoria:',
'Categories' => 'Categorias',
- 'Category not found.' => 'Categoria não encontrada.',
'Your category have been created successfully.' => 'A sua categoria foi criada com sucesso.',
'Unable to create your category.' => 'Não foi possível criar a sua categoria.',
'Your category have been updated successfully.' => 'A sua categoria foi actualizada com sucesso.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Tem a certeza que quer remover este arquivo: "%s"',
'Attachments' => 'Anexos',
'Edit the task' => 'Editar a tarefa',
- 'Edit the description' => 'Editar a descrição',
'Add a comment' => 'Adicionar um comentário',
'Edit a comment' => 'Editar um comentário',
'Summary' => 'Resumo',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Mostrar outro projecto',
'Created by %s' => 'Criado por %s',
'Tasks Export' => 'Exportar Tarefas',
- 'Tasks exportation for "%s"' => 'As tarefas foram exportadas para "%s"',
'Start Date' => 'Data inicial',
'End Date' => 'Data final',
'Execute' => 'Executar',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Nova subtarefa',
'New attachment added "%s"' => 'Novo anexo adicionado "%s"',
'New comment posted by %s' => 'Novo comentário por %s',
- 'New attachment' => 'Novo anexo',
'New comment' => 'Novo comentário',
'Comment updated' => 'Comentário actualizado',
'New subtask' => 'Nova subtarefa',
- 'Subtask updated' => 'Subtarefa alterada',
- 'Task updated' => 'Tarefa alterada',
- 'Task closed' => 'Tarefa finalizada',
- 'Task opened' => 'Tarefa aberta',
'I want to receive notifications only for those projects:' => 'Quero receber notificações apenas destes projectos:',
'view the task on Kanboard' => 'ver a tarefa no Kanboard',
'Public access' => 'Acesso público',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Nenhuma autenticação externa activa.',
'Password modified successfully.' => 'Senha alterada com sucesso.',
'Unable to change the password.' => 'Não foi possível alterar a senha.',
- 'Change category for the task "%s"' => 'Mudar categoria da tarefa "%s"',
'Change category' => 'Mudar categoria',
'%s updated the task %s' => '%s actualizou a tarefa %s',
'%s opened the task %s' => '%s abriu a tarefa %s',
@@ -382,7 +371,7 @@ return array(
'Not assigned, estimate of %sh' => 'Não assignado, estimado em %sh',
'%s updated a comment on the task %s' => '%s actualizou o comentário na tarefa %s',
'%s commented the task %s' => '%s comentou a tarefa %s',
- '%s\'s activity' => 'Atividades de%s',
+ '%s\'s activity' => 'Atividades %s',
'RSS feed' => 'Feed RSS',
'%s updated a comment on the task #%d' => '%s actualizou um comentário sobre a tarefa #%d',
'%s commented on the task #%d' => '%s comentou sobre a tarefa #%d',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s actualizou a tarefa #%d',
'%s created the task #%d' => '%s criou a tarefa #%d',
'%s closed the task #%d' => '%s finalizou a tarefa #%d',
- '%s open the task #%d' => '%s abriu a tarefa #%d',
- '%s moved the task #%d to the column "%s"' => '%s moveu a tarefa #%d para a coluna "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s moveu a tarefa #%d para a posição %d na coluna "%s"',
+ '%s opened the task #%d' => '%s abriu a tarefa #%d',
'Activity' => 'Actividade',
'Default values are "%s"' => 'Os valores padrão são "%s"',
'Default columns for new projects (Comma-separated)' => 'Colunas padrão para novos projectos (Separado por vírgula)',
'Task assignee change' => 'Mudar assignação da tarefa',
- '%s change the assignee of the task #%d to %s' => '%s mudou a assignação da tarefa #%d para %s',
+ '%s changed the assignee of the task #%d to %s' => '%s mudou a assignação da tarefa #%d para %s',
'%s changed the assignee of the task %s to %s' => '%s mudou a assignação da tarefa %s para %s',
'New password for the user "%s"' => 'Nova senha para o utilizador "%s"',
'Choose an event' => 'Escolher um evento',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Percentagem',
'Number of tasks' => 'Número de tarefas',
'Task distribution' => 'Distribuição de tarefas',
- 'Reportings' => 'Relatórios',
- 'Task repartition for "%s"' => 'Redistribuição da tarefa para "%s"',
'Analytics' => 'Estatísticas',
'Subtask' => 'Subtarefa',
'My subtasks' => 'As minhas subtarefas',
'User repartition' => 'Redistribuição de utilizador',
- 'User repartition for "%s"' => 'Redistribuição de utilizador para "%s"',
'Clone this project' => 'Clonar este projecto',
'Column removed successfully.' => 'Coluna removida com sucesso.',
'Not enough data to show the graph.' => 'Não há dados suficientes para mostrar o gráfico.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Este valor deve ser numérico',
'Unable to create this task.' => 'Não foi possível criar esta tarefa.',
'Cumulative flow diagram' => 'Fluxograma cumulativo',
- 'Cumulative flow diagram for "%s"' => 'Fluxograma cumulativo para "%s"',
'Daily project summary' => 'Resumo diário do projecto',
'Daily project summary export' => 'Exportação diária do resumo do projecto',
- 'Daily project summary export for "%s"' => 'Exportação diária do resumo do projecto para "%s"',
'Exports' => 'Exportar',
'This export contains the number of tasks per column grouped per day.' => 'Esta exportação contém o número de tarefas por coluna agrupada por dia.',
'Active swimlanes' => 'Activar swimlanes',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Remover um swimlane',
'Show default swimlane' => 'Mostrar swimlane padrão',
'Swimlane modification for the project "%s"' => 'Modificação de swimlane para o projecto "%s"',
- 'Swimlane not found.' => 'Swimlane não encontrado.',
'Swimlane removed successfully.' => 'Swimlane removido com sucesso.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane atualizado com sucesso.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'ID da subtarefa',
'Subtasks' => 'Subtarefas',
'Subtasks Export' => 'Exportar subtarefas',
- 'Subtasks exportation for "%s"' => 'Subtarefas exportadas para "%s"',
'Task Title' => 'Título da Tarefa',
'Untitled' => 'Sem título',
'Application default' => 'Aplicação padrão',
@@ -548,17 +528,17 @@ return array(
'Unable to create your link.' => 'Impossível de adicionar sua associação.',
'Unable to update your link.' => 'Impossível de atualizar sua associação.',
'Unable to remove this link.' => 'Impossível de remover sua associação.',
- 'relates to' => 'é associado com',
- 'blocks' => 'blocos',
- 'is blocked by' => 'está bloqueado por',
+ 'relates to' => 'é associada a',
+ 'blocks' => 'bloqueia',
+ 'is blocked by' => 'está bloqueada por',
'duplicates' => 'duplica',
- 'is duplicated by' => 'é duplicado por',
- 'is a child of' => 'é um filho de',
+ 'is duplicated by' => 'é duplicada por',
+ 'is a child of' => 'é um filha de',
'is a parent of' => 'é um parente do',
'targets milestone' => 'visa um objectivo',
'is a milestone of' => 'é um objectivo de',
'fixes' => 'corrige',
- 'is fixed by' => 'foi corrigido por',
+ 'is fixed by' => 'foi corrigida por',
'This task' => 'Esta tarefa',
'<1h' => '<1h',
'%dh' => '%dh',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'A taxa de câmbio foi adicionada com sucesso.',
'Unable to add this currency rate.' => 'Impossível adicionar essa taxa de câmbio.',
'Webhook URL' => 'URL do webhook',
- '%s remove the assignee of the task %s' => '%s removeu a pessoa assignada à tarefa %s',
+ '%s removed the assignee of the task %s' => '%s removeu a pessoa assignada à tarefa %s',
'Enable Gravatar images' => 'Activar imagem Gravatar',
'Information' => 'Informações',
'Check two factor authentication code' => 'Verificação do código de autenticação com factor duplo',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Teste o seu dispositivo',
'Assign a color when the task is moved to a specific column' => 'Atribuir uma cor quando a tarefa é movida em uma coluna específica',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Gráfico de Burndown para "%s"',
'Burndown chart' => 'Gráfico de Burndown',
'This chart show the task complexity over the time (Work Remaining).' => 'Este gráfico mostra a complexidade da tarefa ao longo do tempo (Trabalho Restante).',
'Screenshot taken %s' => 'Screenshot tirada a %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Mover uma tarefa para outra coluna quando a categoria mudar',
'Send a task by email to someone' => 'Enviar uma tarefa por e-mail a alguém',
'Reopen a task' => 'Reabrir uma tarefa',
- 'Column change' => 'Mudança de coluna',
- 'Position change' => 'Mudança de posição',
- 'Swimlane change' => 'Mudança de swimlane',
- 'Assignee change' => 'Mudança de assignação',
- '[%s] Overdue tasks' => '[%s] Tarefas atrasadas',
'Notification' => 'Notificação',
'%s moved the task #%d to the first swimlane' => '%s moveu a tarefa n° %d no primeiro swimlane',
- '%s moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa n° %d no swimlane "%s"',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s no primeiro swimlane',
@@ -764,9 +737,7 @@ return array(
'Search by category: ' => 'Pesquisar por categoria: ',
'Search by description: ' => 'Pesquisar por descrição: ',
'Search by due date: ' => 'Pesquisar por data de vencimento: ',
- 'Lead and Cycle time for "%s"' => 'Tempo de Espera e Ciclo para "%s"',
- 'Average time spent into each column for "%s"' => 'Tempo médio gasto em cada coluna para "%s"',
- 'Average time spent into each column' => 'Tempo médio gasto em cada coluna',
+ 'Average time spent into each column' => 'Tempo médio gasto por coluna',
'Average time spent' => 'Tempo médio gasto',
'This chart show the average time spent into each column for the last %d tasks.' => 'Este gráfico mostra o tempo médio gasto em cada coluna nas últimas %d tarefas.',
'Average Lead and Cycle time' => 'Tempo de Espera e Ciclo médio',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Licença:',
'License' => 'Licença',
'Enter the text below' => 'Escreva o texto em baixo',
- 'Gantt chart for %s' => 'Gráfico de Gantt para %s',
'Sort by position' => 'Ordenar por posição',
'Sort by date' => 'Ordenar por data',
'Add task' => 'Adicionar tarefa',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Versão',
'Plugins' => 'Plugins',
'There is no plugin loaded.' => 'Não existem extras carregados',
- 'Set maximum column height' => 'Definir altura máxima da coluna',
- 'Remove maximum column height' => 'Remover altura máxima da coluna',
'My notifications' => 'As minhas notificações',
'Custom filters' => 'Filtros personalizados',
'Your custom filter have been created successfully.' => 'O seu filtro personalizado foi criado com sucesso.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Captcha inválido',
'The name must be unique' => 'O nome deve ser único',
'View all groups' => 'Ver todos os grupos',
- 'View group members' => 'Ver membros do grupo',
'There is no user available.' => 'Não existe utilizador disponivel.',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Tem a certeza que quer remover o utilizador "%s" do grupo "%s"?',
'There is no group.' => 'Não existe grupo.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Escreva o nome do Grupo',
'Role:' => 'Função:',
'Project members' => 'Membros do projecto',
- 'Compare hours for "%s"' => 'Comparar horas para "%s"',
'%s mentioned you in the task #%d' => '%s mencionou-te na tarefa #%d',
'%s mentioned you in a comment on the task #%d' => '%s mencionou-te num comentário na tarefa #%d',
'You were mentioned in the task #%d' => 'Foi mencionado na tarefa #%d',
'You were mentioned in a comment on the task #%d' => 'Foi mencionado num comentário na tarefa #%d',
- 'Mentioned' => 'Mencionado',
- 'Compare Estimated Time vs Actual Time' => 'Comparar Tempo Estimado vs Tempo Real',
'Estimated hours: ' => 'Horas estimadas: ',
'Actual hours: ' => 'Horas reais: ',
'Hours Spent' => 'Horas Gastas',
@@ -1202,4 +1166,52 @@ return array(
'Email transport' => 'Transportador de Email',
'Webhook token' => 'Token do Webhook',
'Imports' => 'Importados',
+ 'Project tags management' => 'Gestão de etiquetas do Projecto',
+ 'Tag created successfully.' => 'Etiqueta criada com sucesso.',
+ 'Unable to create this tag.' => 'Não foi possivel criar esta etiqueta.',
+ 'Tag updated successfully.' => 'Etiqueta actualizada com sucesso.',
+ 'Unable to update this tag.' => 'Não foi possivel actualizar esta etiqueta.',
+ 'Tag removed successfully.' => 'Etiqueta removida com sucesso.',
+ 'Unable to remove this tag.' => 'Não foi possivel remover esta etiqueta.',
+ 'Global tags management' => 'Gestão de etiquetas globais',
+ 'Tags' => 'Etiquetas',
+ 'Tags management' => 'Gestão de Etiquetas',
+ 'Add new tag' => 'Adicionar etiqueta nova',
+ 'Edit a tag' => 'Editar a etiqueta',
+ 'Project tags' => 'Etiquetas do Projecto',
+ 'There is no specific tag for this project at the moment.' => 'De momento não existe nenhuma etiqueta para este projecto.',
+ 'Tag' => 'Etiqueta',
+ 'Remove a tag' => 'Remover etiqueta',
+ 'Do you really want to remove this tag: "%s"?' => 'Tem a certeza que pretende remover esta etiqueta: "%s"?',
+ 'Global tags' => 'Etiquetas globais',
+ 'There is no global tag at the moment.' => 'De momento não existe nenhuma etiqueta global.',
+ 'This field cannot be empty' => 'Este campo não pode ficar vazio',
+ 'Close a task when there is no activity in an specific column' => 'Fechar tarefa quando não houver actividade numa coluna especifica',
+ '%s removed a subtask for the task #%d' => '%s removeu uma sub-tarefa da tarefa #%d',
+ '%s removed a comment on the task #%d' => '%s removeu um comentário da tarefa #%d ',
+ 'Comment removed on task #%d' => 'Comentário removido da tarefa #%d',
+ 'Subtask removed on task #%d' => 'Sub-tarefa removida da tarefa #%d',
+ 'Hide tasks in this column in the dashboard' => 'Esconder do meu painel tarefas nesta coluna',
+ '%s removed a comment on the task %s' => '%s removeu um comentário da tarefa %s',
+ '%s removed a subtask for the task %s' => '%s removeu uma sub-tarefa da tarefa %s',
+ 'Comment removed' => 'Comentário removido',
+ 'Subtask removed' => 'Sub-tarefa removida',
+ '%s set a new internal link for the task #%d' => '%s definiu uma nova ligação interna para a tarefa #%d',
+ '%s removed an internal link for the task #%d' => '%s removeu uma ligação interna da tarefa #%d',
+ 'A new internal link for the task #%d have been defined' => 'Uma nova ligação para a tarea #%d foi definida',
+ 'Internal link removed for the task #%d' => 'Ligação interna removida da tarefa #%d',
+ '%s set a new internal link for the task %s' => '%s definiu uma nova ligação interna para a tarefa %s',
+ '%s removed an internal link for the task %s' => '%s removeu uma ligação interna da tarefa %s',
+ 'Automatically set the due date on task creation' => 'Definir data de vencimento automáticamente ao criar uma tarefa',
+ 'Move the task to another column when closed' => 'Mover a tarefa para outra coluna quando fechada',
+ 'Move the task to another column when not moved during a given period' => 'Mover a tarefa para outra coluna quando não movida dentro de determinado periodo',
+ 'Dashboard for %s' => 'Painel de %s',
+ 'Tasks overview for %s' => 'Vista geral das tarefas de %s',
+ 'Subtasks overview for %s' => 'Vista geral das sub-tarefas de %s',
+ 'Projects overview for %s' => 'Vista geral dos projectos de %s',
+ 'Activity stream for %s' => 'Fluxo de actividade de %s',
+ 'Calendar for %s' => 'Calendário de %s',
+ 'Notifications for %s' => 'Notificações de %s',
+ 'Subtasks export' => 'Exportar sub-tarefas',
+ 'Tasks exportation' => 'Exportação de tarefas',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index f500db20..f3ec5af7 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d завершенных задач',
'No task for this project' => 'Нет задач для этого проекта',
'Public link' => 'Ссылка для просмотра',
- 'Change assignee' => 'Сменить назначенного',
- 'Change assignee for the task "%s"' => 'Сменить назначенного для задачи « %s »',
'Timezone' => 'Часовой пояс',
'Sorry, I didn\'t find this information in my database!' => 'К сожалению, информация в базе данных не найдена !',
'Page not found' => 'Страница не найдена',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Категория',
'Category:' => 'Категория:',
'Categories' => 'Категории',
- 'Category not found.' => 'Категория не найдена',
'Your category have been created successfully.' => 'Категория создана.',
'Unable to create your category.' => 'Не удалось создать категорию.',
'Your category have been updated successfully.' => 'Категория обновлена.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Вы точно хотите удалить этот файл « %s » ?',
'Attachments' => 'Вложения',
'Edit the task' => 'Изменить задачу',
- 'Edit the description' => 'Изменить описание',
'Add a comment' => 'Добавить комментарий',
'Edit a comment' => 'Изменить комментарий',
'Summary' => 'Сводка',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Показать другой проект',
'Created by %s' => 'Создано %s',
'Tasks Export' => 'Экспорт задач',
- 'Tasks exportation for "%s"' => 'Задача экспортирована для « %s »',
'Start Date' => 'Дата начала',
'End Date' => 'Дата завершения',
'Execute' => 'Выполнить',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Новая подзадача',
'New attachment added "%s"' => 'Добавлено вложение « %s »',
'New comment posted by %s' => 'Новый комментарий написан « %s »',
- 'New attachment' => 'Новое вложение',
'New comment' => 'Новый комментарий',
'Comment updated' => 'Комментарий обновлен',
'New subtask' => 'Новая подзадача',
- 'Subtask updated' => 'Подзадача обновлена',
- 'Task updated' => 'Задача обновлена',
- 'Task closed' => 'Задача закрыта',
- 'Task opened' => 'Задача открыта',
'I want to receive notifications only for those projects:' => 'Я хочу получать уведомления только по этим проектам:',
'view the task on Kanboard' => 'посмотреть задачу на Kanboard',
'Public access' => 'Общий доступ',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Нет активной внешней аутентификации.',
'Password modified successfully.' => 'Пароль изменен.',
'Unable to change the password.' => 'Не удалось сменить пароль.',
- 'Change category for the task "%s"' => 'Сменить категорию для задачи "%s"',
'Change category' => 'Смена категории',
'%s updated the task %s' => '%s обновил задачу %s',
'%s opened the task %s' => '%s открыл задачу %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s обновил задачу #%d',
'%s created the task #%d' => '%s создал задачу #%d',
'%s closed the task #%d' => '%s закрыл задачу #%d',
- '%s open the task #%d' => '%s открыл задачу #%d',
- '%s moved the task #%d to the column "%s"' => '%s переместил задачу #%d в колонку "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s переместил задачу #%d на позицию %d в колонке "%s"',
+ '%s opened the task #%d' => '%s открыл задачу #%d',
'Activity' => 'Активность',
'Default values are "%s"' => 'Колонки по умолчанию: "%s"',
'Default columns for new projects (Comma-separated)' => 'Колонки по умолчанию для новых проектов (разделять запятой)',
'Task assignee change' => 'Изменен назначенный',
- '%s change the assignee of the task #%d to %s' => '%s сменил назначенного для задачи #%d на %s',
+ '%s changed the assignee of the task #%d to %s' => '%s сменил назначенного для задачи #%d на %s',
'%s changed the assignee of the task %s to %s' => '%s сменил назначенного для задачи %s на %s',
'New password for the user "%s"' => 'Новый пароль для пользователя "%s"',
'Choose an event' => 'Выберите событие',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Процент',
'Number of tasks' => 'Количество задач',
'Task distribution' => 'Распределение задач',
- 'Reportings' => 'Отчетность',
- 'Task repartition for "%s"' => 'Распределение задач для "%s"',
'Analytics' => 'Аналитика',
'Subtask' => 'Подзадача',
'My subtasks' => 'Мои подзадачи',
'User repartition' => 'Перераспределение пользователей',
- 'User repartition for "%s"' => 'Перераспределение пользователей для "%s"',
'Clone this project' => 'Клонировать проект',
'Column removed successfully.' => 'Колонка успешно удалена.',
'Not enough data to show the graph.' => 'Недостаточно данных, чтобы показать график.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Это значение должно быть цифровым',
'Unable to create this task.' => 'Невозможно создать задачу.',
'Cumulative flow diagram' => 'Накопительная диаграма',
- 'Cumulative flow diagram for "%s"' => 'Накопительная диаграма для "%s"',
'Daily project summary' => 'Ежедневное состояние проекта',
'Daily project summary export' => 'Экспорт ежедневного резюме проекта',
- 'Daily project summary export for "%s"' => 'Экспорт ежедневного резюме проекта "%s"',
'Exports' => 'Экспорт',
'This export contains the number of tasks per column grouped per day.' => 'Этот экспорт содержит ряд задач в колонках, сгруппированные по дням.',
'Active swimlanes' => 'Активные дорожки',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Удалить дорожку',
'Show default swimlane' => 'Показать стандартную дорожку',
'Swimlane modification for the project "%s"' => 'Редактирование дорожки для проекта "%s"',
- 'Swimlane not found.' => 'Дорожка не найдена.',
'Swimlane removed successfully.' => 'Дорожка успешно удалена',
'Swimlanes' => 'Дорожки',
'Swimlane updated successfully.' => 'Дорожка успешно обновлена.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Id подзадачи',
'Subtasks' => 'Подзадачи',
'Subtasks Export' => 'Экспортировать подзадачи',
- 'Subtasks exportation for "%s"' => 'Экспорт подзадач для "%s"',
'Task Title' => 'Загловок задачи',
'Untitled' => 'Заголовок отсутствует',
'Application default' => 'Приложение по умолчанию',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Курс валюты был успешно добавлен.',
'Unable to add this currency rate.' => 'Невозможно добавить этот курс валюты.',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s удалить назначенную задачу %s',
+ '%s removed the assignee of the task %s' => '%s удалить назначенную задачу %s',
'Enable Gravatar images' => 'Включить Gravatar изображения',
'Information' => 'Информация',
'Check two factor authentication code' => 'Проверка кода двухфакторной авторизации',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Проверьте свое устройство',
'Assign a color when the task is moved to a specific column' => 'Назначить цвет, когда задача перемещается в определенную колонку',
'%s via Kanboard' => '%s через Канборд',
- 'Burndown chart for "%s"' => 'Диаграмма сгорания для « %s »',
'Burndown chart' => 'Диаграмма сгорания',
'This chart show the task complexity over the time (Work Remaining).' => 'Эта диаграмма показывают сложность задачи по времени (оставшейся работы).',
'Screenshot taken %s' => 'Принято скриншотов %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Переносить задачи в другую колонку при изменении категории',
'Send a task by email to someone' => 'Отправить задачу по email',
'Reopen a task' => 'Переоткрыть задачу',
- 'Column change' => 'Изменение колонки',
- 'Position change' => 'Позиция изменена',
- 'Swimlane change' => 'Дорожка изменена',
- 'Assignee change' => 'Назначенный пользователь изменен',
- '[%s] Overdue tasks' => '[%s] просроченные задачи',
'Notification' => 'Уведомления',
'%s moved the task #%d to the first swimlane' => '%s задач перемещено #%d в первой дорожке',
- '%s moved the task #%d to the swimlane "%s"' => '%s задач перемещено #%d в дорожке "%s"',
'Swimlane' => 'Дорожки',
'Gravatar' => 'Граватар',
'%s moved the task %s to the first swimlane' => '%s переместил задачу %s на первую дорожку',
@@ -721,9 +694,9 @@ return array(
'Do you really want to close the task "%s" as well as all subtasks?' => 'Вы действительно хотите закрыть задачу "%s", а также все подзадачи?',
'I want to receive notifications for:' => 'Я хочу получать уведомления для:',
'All tasks' => 'Все задачи',
- 'Only for tasks assigned to me' => 'Только для задач, назначенных на меня',
+ 'Only for tasks assigned to me' => 'Только для задач, назначенных мне',
'Only for tasks created by me' => 'Только для задач, созданных мной',
- 'Only for tasks created by me and assigned to me' => 'Только для задач, созданных мной и назначенных мной',
+ 'Only for tasks created by me and assigned to me' => 'Только для задач, созданных мной и назначенных мне',
'%%Y-%%m-%%d' => '%%Y-%%m-%%d',
'Total for all columns' => 'Суммарно для всех колонок',
'You need at least 2 days of data to show the chart.' => 'Для отображения диаграммы нужно по крайней мере 2 дня.',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Поиск по категориям: ',
'Search by description: ' => 'Поиск по описанию: ',
'Search by due date: ' => 'Поиск по дате завершения: ',
- 'Lead and Cycle time for "%s"' => 'Затраченное время и время цикла для "%s"',
- 'Average time spent into each column for "%s"' => 'Затрачено времени в среднем в каждой колонке для "%s"',
'Average time spent into each column' => 'Затрачено времени в среднем в каждой колонке',
'Average time spent' => 'Затрачено времени в среднем',
'This chart show the average time spent into each column for the last %d tasks.' => 'Эта диаграмма показывает среднее время, проведенное задачами в каждой колонке за последний %d.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Лицензия:',
'License' => 'Лицензия',
'Enter the text below' => 'Введите текст ниже',
- 'Gantt chart for %s' => 'Диаграмма Ганта для %s',
'Sort by position' => 'Сортировать по позиции',
'Sort by date' => 'Сортировать по дате',
'Add task' => 'Добавить задачу',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Версия',
'Plugins' => 'Плагины',
'There is no plugin loaded.' => 'Нет установленных плагинов.',
- 'Set maximum column height' => 'Установить максимальную высоту колонки',
- 'Remove maximum column height' => 'Сбросить максимальную высоту колонки',
'My notifications' => 'Мои уведомления',
'Custom filters' => 'Пользовательские фильтры',
'Your custom filter have been created successfully.' => 'Фильтр был успешно создан.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Неверный код подтверждения',
'The name must be unique' => 'Имя должно быть уникальным',
'View all groups' => 'Просмотр всех групп',
- 'View group members' => 'Просмотр участников группы',
'There is no user available.' => 'Нет доступных пользователей.',
'Do you really want to remove the user "%s" from the group "%s"?' => 'Вы действительно хотите удалить пользователя "%s" из группы "%s"?',
'There is no group.' => 'Нет созданных групп.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Введите имя группы...',
'Role:' => 'Роль:',
'Project members' => 'Участники проекта',
- 'Compare hours for "%s"' => 'Сравнить часы для "%s"',
'%s mentioned you in the task #%d' => '%s упомянул вас в задаче #%d',
'%s mentioned you in a comment on the task #%d' => '%s упомянул вас в комментарии к задаче #%d',
'You were mentioned in the task #%d' => 'Вы упомянуты в задаче #%d',
'You were mentioned in a comment on the task #%d' => 'Вы упомянуты в комментарии к задаче #%d',
- 'Mentioned' => 'Упоминания',
- 'Compare Estimated Time vs Actual Time' => 'Сравнить запланированное время и реальное',
'Estimated hours: ' => 'Запланировано часов: ',
'Actual hours: ' => 'Реально затрачено часов: ',
'Hours Spent' => 'Затрачено часов',
@@ -1160,46 +1124,94 @@ return array(
'Projects where "%s" is member' => 'Проекты, где членом является "%s"',
'Open tasks assigned to "%s"' => 'Открытые задачи, назначенные на "%s"',
'Closed tasks assigned to "%s"' => 'Закрытые задачи, назначенные на "%s"',
- // 'Assign automatically a color based on a priority' => '',
- // 'Overdue tasks for the project(s) "%s"' => '',
- // 'Upload files' => '',
- // 'Installed Plugins' => '',
- // 'Plugin Directory' => '',
- // 'Plugin installed successfully.' => '',
- // 'Plugin updated successfully.' => '',
- // 'Plugin removed successfully.' => '',
- // 'Subtask converted to task successfully.' => '',
- // 'Unable to convert the subtask.' => '',
- // 'Unable to extract plugin archive.' => '',
- // 'Plugin not found.' => '',
- // 'You don\'t have the permission to remove this plugin.' => '',
- // 'Unable to download plugin archive.' => '',
- // 'Unable to write temporary file for plugin.' => '',
- // 'Unable to open plugin archive.' => '',
- // 'There is no file in the plugin archive.' => '',
- // 'Create tasks in bulk' => '',
- // 'Your Kanboard instance is not configured to install plugins from the user interface.' => '',
- // 'There is no plugin available.' => '',
- // 'Install' => '',
- // 'Update' => '',
- // 'Up to date' => '',
- // 'Not available' => '',
- // 'Remove plugin' => '',
- // 'Do you really want to remove this plugin: "%s"?' => '',
- // 'Uninstall' => '',
- // 'Listing' => '',
- // 'Metadata' => '',
- // 'Manage projects' => '',
- // 'Convert to task' => '',
- // 'Convert sub-task to task' => '',
- // 'Do you really want to convert this sub-task to a task?' => '',
- // 'My task title' => '',
- // 'Enter one task by line.' => '',
- // 'Number of failed login:' => '',
- // 'Account locked until:' => '',
- // 'Email settings' => '',
- // 'Email sender address' => '',
- // 'Email transport' => '',
- // 'Webhook token' => '',
- // 'Imports' => '',
+ 'Assign automatically a color based on a priority' => 'Автоматически назначить цвет в зависимости от категории',
+ 'Overdue tasks for the project(s) "%s"' => 'Просроченные задачи для проекта(ов) "%s"',
+ 'Upload files' => 'Загрузить файлы',
+ 'Installed Plugins' => 'Установленные плагины',
+ 'Plugin Directory' => 'Доступные плагины',
+ 'Plugin installed successfully.' => 'Плагин успешно установлен.',
+ 'Plugin updated successfully.' => 'Плагин успешно обновлен.',
+ 'Plugin removed successfully.' => 'Плагин успешно удален.',
+ 'Subtask converted to task successfully.' => 'Подзадача успешно преобразована в задачу.',
+ 'Unable to convert the subtask.' => 'Невозможно преобразовать подзадачу.',
+ 'Unable to extract plugin archive.' => 'Невозможно распаковать архив с плагином.',
+ 'Plugin not found.' => 'Плагин не найден.',
+ 'You don\'t have the permission to remove this plugin.' => 'У Вас нет прав на удаление этого плагина.',
+ 'Unable to download plugin archive.' => 'Невозможно загрузить архив с плагином.',
+ 'Unable to write temporary file for plugin.' => 'Невозможно записать временный файл для плагина.',
+ 'Unable to open plugin archive.' => 'Невозможно открыть архив плагина.',
+ 'There is no file in the plugin archive.' => 'В арзиве плагина нет файлов.',
+ 'Create tasks in bulk' => 'Массовое создание задач',
+ 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Ваш Kanboard не сконфигурирова для установки плагинов через пользовательский интерфейс.',
+ 'There is no plugin available.' => 'Нет доступных плагинов.',
+ 'Install' => 'Установить',
+ 'Update' => 'Обновить',
+ 'Up to date' => 'Самый новый',
+ 'Not available' => 'Недоступен',
+ 'Remove plugin' => 'Удалить плагин',
+ 'Do you really want to remove this plugin: "%s"?' => 'Вы действительно хотите удалить плагин: "%s"?',
+ 'Uninstall' => 'Деинсталлировать',
+ 'Listing' => 'Список',
+ 'Metadata' => 'Метаданные',
+ 'Manage projects' => 'Управление проектами',
+ 'Convert to task' => 'Преобразовать в задачу',
+ 'Convert sub-task to task' => 'Преобразовать подзадачу в задачу',
+ 'Do you really want to convert this sub-task to a task?' => 'Вы действительно хотите преобразовать эту подзадачу в задачу?',
+ 'My task title' => 'Заголовок задачи',
+ 'Enter one task by line.' => 'Указывайте одну задачу на строке',
+ 'Number of failed login:' => 'Число неудачных попыток входа:',
+ 'Account locked until:' => 'Аккаунт заблокирован до:',
+ 'Email settings' => 'Настройки почты',
+ 'Email sender address' => 'Адрес отправителя',
+ 'Email transport' => 'Почтовый транспорт',
+ 'Webhook token' => 'Webhook токены',
+ 'Imports' => 'Импорт',
+ 'Project tags management' => 'Управление метками проекта',
+ 'Tag created successfully.' => 'Метка успешно создана.',
+ 'Unable to create this tag.' => 'Невозможно создать эту метку.',
+ 'Tag updated successfully.' => 'Метак успешно обновлена.',
+ 'Unable to update this tag.' => 'Невозможно обновить эту метку.',
+ 'Tag removed successfully.' => 'Метка успешно удалена.',
+ 'Unable to remove this tag.' => 'Невозможно удалить эту метку.',
+ 'Global tags management' => 'Управление глоабльными метками',
+ 'Tags' => 'Метки',
+ 'Tags management' => 'Управление метками',
+ 'Add new tag' => 'Добавить новую метку',
+ 'Edit a tag' => 'Редактировать метку',
+ 'Project tags' => 'Метки проекта',
+ 'There is no specific tag for this project at the moment.' => 'Нет меток для этого проекта.',
+ 'Tag' => 'Метка',
+ 'Remove a tag' => 'Удалить метку',
+ 'Do you really want to remove this tag: "%s"?' => 'Вы действительно хотите удалить метку: "%s"?',
+ 'Global tags' => 'Глобальные метка',
+ 'There is no global tag at the moment.' => 'Нет глобальных меток.',
+ 'This field cannot be empty' => 'Это поле не может быть пустым',
+ 'Close a task when there is no activity in an specific column' => 'Закрыть задачу при отсутствии активности в определенной колонке',
+ '%s removed a subtask for the task #%d' => '%s удалил подзадачу для #%d',
+ '%s removed a comment on the task #%d' => '%s удалил комментарий к задаче #%d',
+ 'Comment removed on task #%d' => 'Комментарий удален в задаче #%d',
+ 'Subtask removed on task #%d' => 'Подзадача удалена в задаче #%d',
+ 'Hide tasks in this column in the dashboard' => 'Не показывать задачи из этой колонки в кабинете',
+ '%s removed a comment on the task %s' => '%s удалил комментарии к задаче %s',
+ '%s removed a subtask for the task %s' => '%s удалил подзадачу для %s',
+ 'Comment removed' => 'Комментарий удален',
+ 'Subtask removed' => 'Подзадача удалена',
+ '%s set a new internal link for the task #%d' => '%s добавил внутреннюю ссылку для задачи #%d',
+ '%s removed an internal link for the task #%d' => '%s удалил внутреннюю ссылку для задачи #%d',
+ 'A new internal link for the task #%d have been defined' => 'Внешняя ссылка для задачи #%d была установлена',
+ 'Internal link removed for the task #%d' => 'Внутренняя ссылка удалена для задачи #%d',
+ '%s set a new internal link for the task %s' => '%s добавил внутреннюю ссылку для задачи %s',
+ '%s removed an internal link for the task %s' => '%s удалил внутреннюю ссылку для задачи %s',
+ 'Automatically set the due date on task creation' => 'Автоматическая установка срока задачи при создании',
+ 'Move the task to another column when closed' => 'Переместить задачу в другую колонку при закрытии',
+ 'Move the task to another column when not moved during a given period' => 'Переместить задачу в другую колонку если она не был перемещен в указанный период',
+ 'Dashboard for %s' => 'Панель управления для %s',
+ 'Tasks overview for %s' => 'Обзор задач для %s',
+ 'Subtasks overview for %s' => 'Обзор подзадач для %s',
+ 'Projects overview for %s' => 'Обзор проектов для %s',
+ 'Activity stream for %s' => 'Лента активности для %s',
+ 'Calendar for %s' => 'Календарь для %s',
+ 'Notifications for %s' => 'Уведомления для %s',
+ 'Subtasks export' => 'Экспорт подзадач',
+ 'Tasks exportation' => 'Экспортирование задач',
);
diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php
index 98f72c80..7e28e9a9 100644
--- a/app/Locale/sr_Latn_RS/translations.php
+++ b/app/Locale/sr_Latn_RS/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d zatvorenih zadataka',
'No task for this project' => 'Nema dodeljenih zadataka ovom projektu',
'Public link' => 'Javni link',
- 'Change assignee' => 'Izmeni dodelu',
- 'Change assignee for the task "%s"' => 'Izmeni dodelu za ovaj zadatak "%s"',
'Timezone' => 'Vremenska zona',
'Sorry, I didn\'t find this information in my database!' => 'Na žalost, nije pronađena informacija u bazi',
'Page not found' => 'Strana nije pronađena',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategorija',
'Category:' => 'Kategorija:',
'Categories' => 'Kategorije',
- 'Category not found.' => 'Kategorija nije pronađena',
'Your category have been created successfully.' => 'Uspešno kreirana kategorija.',
'Unable to create your category.' => 'Nije moguće kreirati kategoriju.',
'Your category have been updated successfully.' => 'Kategorija je uspešno izmenjena',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Da li da uklonim fajl: "%s"?',
'Attachments' => 'Prilozi',
'Edit the task' => 'Izmena Zadatka',
- 'Edit the description' => 'Izmena opisa',
'Add a comment' => 'Dodaj komentar',
'Edit a comment' => 'Izmeni komentar',
'Summary' => 'Pregled',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Prikaži drugi projekat',
'Created by %s' => 'Kreirao %s',
'Tasks Export' => 'Izvoz zadataka',
- 'Tasks exportation for "%s"' => 'Izvoz zadataka za "%s"',
'Start Date' => 'Početni datum',
'End Date' => 'Krajni datum',
'Execute' => 'Izvrši',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Novi Pod-zadatak',
'New attachment added "%s"' => 'Novi prilog ubačen "%s"',
'New comment posted by %s' => 'Novi komentar ostavio %s',
- // 'New attachment' => '',
// 'New comment' => '',
'Comment updated' => 'Komentar izmenjen',
// 'New subtask' => '',
- // 'Subtask updated' => '',
- // 'Task updated' => '',
- 'Task closed' => 'Zadatak je zatvoren',
- 'Task opened' => 'Zadatak je otvoren',
'I want to receive notifications only for those projects:' => 'Želim obaveštenja samo za ovaj projekat:',
'view the task on Kanboard' => 'Pregledaj zadatke',
'Public access' => 'Javni pristup',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Bez omogućenih spoljnih autentikacija.',
'Password modified successfully.' => 'Uspešna izmena lozinke.',
'Unable to change the password.' => 'Nije moguće izmeniti lozinku.',
- 'Change category for the task "%s"' => 'Izmeni kategoriju zadatka "%s"',
'Change category' => 'Izmeni kategoriju',
'%s updated the task %s' => '%s izmeni zadatak %s',
'%s opened the task %s' => '%s aktivni zadaci %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s izmenjen zadatak #%d',
'%s created the task #%d' => '%s kreirao zadatak #%d',
'%s closed the task #%d' => '%s zatvorio zadatak #%d',
- '%s open the task #%d' => '%s otvorio zadatak #%d',
- '%s moved the task #%d to the column "%s"' => '%s premestio zadatak #%d u kolonu "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s premestio zadatak #%d na pozycję %d w kolmnie "%s"',
+ '%s opened the task #%d' => '%s otvorio zadatak #%d',
'Activity' => 'Aktivnosti',
'Default values are "%s"' => 'Osnovne vrednosti su: "%s"',
'Default columns for new projects (Comma-separated)' => 'Osnovne kolone za novi projekat (Odvojeni zarezom)',
'Task assignee change' => 'Zmień osobę odpowiedzialną',
- '%s change the assignee of the task #%d to %s' => '%s zamena dodele za zadatak #%d na %s',
+ '%s changed the assignee of the task #%d to %s' => '%s zamena dodele za zadatak #%d na %s',
'%s changed the assignee of the task %s to %s' => '%s zamena dodele za zadatak %s na %s',
'New password for the user "%s"' => 'Nova lozinka za korisnika "%s"',
'Choose an event' => 'Izaberi događaj',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Procenat',
'Number of tasks' => 'Broj zadataka',
'Task distribution' => 'Podela zadataka',
- 'Reportings' => 'Izveštaji',
- 'Task repartition for "%s"' => 'Zaduženja zadataka za "%s"',
'Analytics' => 'Analiza',
'Subtask' => 'Pod-zadatak',
'My subtasks' => 'Moji pod-zadaci',
'User repartition' => 'Zaduženja korisnika',
- 'User repartition for "%s"' => 'Zaduženja korisnika za "%s"',
'Clone this project' => 'Kopiraj projekat',
'Column removed successfully.' => 'Kolumna usunięta pomyslnie.',
'Not enough data to show the graph.' => 'Nedovoljno podataka za grafikon.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Vrednost mora biti broj',
'Unable to create this task.' => 'Nije moguće kreirati zadatak.',
'Cumulative flow diagram' => 'Zbirni dijagram toka',
- 'Cumulative flow diagram for "%s"' => 'Zbirni dijagram toka za "%s"',
'Daily project summary' => 'Zbirni pregled po danima',
'Daily project summary export' => 'Izvoz zbirnog pregleda po danima',
- 'Daily project summary export for "%s"' => 'Izvoz zbirnig pregleda po danima za "%s"',
'Exports' => 'Izvoz',
// 'This export contains the number of tasks per column grouped per day.' => '',
'Active swimlanes' => 'Aktivni razdelnik',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Ukloni razdelnik',
'Show default swimlane' => 'Prikaži osnovni razdelnik',
'Swimlane modification for the project "%s"' => 'Izmena razdelnika za projekat "%s"',
- 'Swimlane not found.' => 'Razdelnik nije pronađen.',
'Swimlane removed successfully.' => 'Razdelnik uspešno uklonjen.',
'Swimlanes' => 'Razdelnici',
'Swimlane updated successfully.' => 'Razdelnik zaktualizowany pomyślnie.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'ID pod-zadania',
'Subtasks' => 'Pod-zadataka',
'Subtasks Export' => 'Eksport pod-zadań',
- 'Subtasks exportation for "%s"' => 'Izvoz pod-zadań dla "%s"',
'Task Title' => 'Naslov zadatka',
'Untitled' => 'Bez naslova',
'Application default' => 'Postavke aplikacje',
@@ -607,7 +587,7 @@ return array(
// 'The currency rate have been added successfully.' => '',
// 'Unable to add this currency rate.' => '',
// 'Webhook URL' => '',
- // '%s remove the assignee of the task %s' => '',
+ // '%s removed the assignee of the task %s' => '',
// 'Enable Gravatar images' => '',
// 'Information' => '',
// 'Check two factor authentication code' => '',
@@ -621,7 +601,6 @@ return array(
// 'Test your device' => '',
// 'Assign a color when the task is moved to a specific column' => '',
// '%s via Kanboard' => '',
- // 'Burndown chart for "%s"' => '',
// 'Burndown chart' => '',
// 'This chart show the task complexity over the time (Work Remaining).' => '',
// 'Screenshot taken %s' => '',
@@ -686,14 +665,8 @@ return array(
// 'Move the task to another column when the category is changed' => '',
// 'Send a task by email to someone' => '',
// 'Reopen a task' => '',
- // 'Column change' => '',
- // 'Position change' => '',
- // 'Swimlane change' => '',
- // 'Assignee change' => '',
- // '[%s] Overdue tasks' => '',
// 'Notification' => '',
// '%s moved the task #%d to the first swimlane' => '',
- // '%s moved the task #%d to the swimlane "%s"' => '',
// 'Swimlane' => '',
// 'Gravatar' => '',
// '%s moved the task %s to the first swimlane' => '',
@@ -764,8 +737,6 @@ return array(
// 'Search by category: ' => '',
// 'Search by description: ' => '',
// 'Search by due date: ' => '',
- // 'Lead and Cycle time for "%s"' => '',
- // 'Average time spent into each column for "%s"' => '',
// 'Average time spent into each column' => '',
// 'Average time spent' => '',
// 'This chart show the average time spent into each column for the last %d tasks.' => '',
@@ -806,7 +777,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
// 'Project members' => '',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index dcc4dab0..5ad2938c 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d stängda uppgifter',
'No task for this project' => 'Inga uppgifter i detta projekt',
'Public link' => 'Publik länk',
- 'Change assignee' => 'Ändra uppdragsinnehavare',
- 'Change assignee for the task "%s"' => 'Ändra uppdragsinnehavare för uppgiften "%s"',
'Timezone' => 'Tidszon',
'Sorry, I didn\'t find this information in my database!' => 'Informationen kunde inte hittas i databasen.',
'Page not found' => 'Sidan hittas inte',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategori',
'Category:' => 'Kategori:',
'Categories' => 'Ange kategorier',
- 'Category not found.' => 'Kategorin hittades inte',
'Your category have been created successfully.' => 'Din kategori har skapats',
'Unable to create your category.' => 'Kunde inte skapa din kategori',
'Your category have been updated successfully.' => 'Din kategori har uppdaterats',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Vill du verkligen ta bort denna fil:"%s"?',
'Attachments' => 'Bifogade filer',
'Edit the task' => 'Ändra uppgiften',
- 'Edit the description' => 'Ändra beskrivningen',
'Add a comment' => 'Lägg till kommentar',
'Edit a comment' => 'Ändra en kommentar',
'Summary' => 'Sammanfattning',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Visa ett annat projekt',
'Created by %s' => 'Skapad av %s',
'Tasks Export' => 'Exportera uppgifter',
- 'Tasks exportation for "%s"' => 'Exportera uppgifter för "%s"',
'Start Date' => 'Startdatum',
'End Date' => 'Slutdatum',
'Execute' => 'Utför',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Ny deluppgift',
'New attachment added "%s"' => 'Ny bifogning tillagd "%s"',
'New comment posted by %s' => 'Ny kommentar postad av %s',
- 'New attachment' => 'Ny bifogning',
'New comment' => 'Ny kommentar',
'Comment updated' => 'Kommentaren har uppdaterats',
'New subtask' => 'Ny deluppgift',
- 'Subtask updated' => 'Deluppgiften har uppdaterats',
- 'Task updated' => 'Uppgiften har uppdaterats',
- 'Task closed' => 'Uppgiften har stängts',
- 'Task opened' => 'Uppgiften har öppnats',
'I want to receive notifications only for those projects:' => 'Jag vill endast få notiser för dessa projekt:',
'view the task on Kanboard' => 'Visa uppgiften på Kanboard',
'Public access' => 'Publik åtkomst',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Ingen extern autentisering aktiverad.',
'Password modified successfully.' => 'Lösenordet har ändrats.',
'Unable to change the password.' => 'Kunde inte byta lösenord.',
- 'Change category for the task "%s"' => 'Byt kategori för uppgiften "%s"',
'Change category' => 'Byt kategori',
'%s updated the task %s' => '%s uppdaterade uppgiften %s',
'%s opened the task %s' => '%s öppna uppgiften %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s uppdaterade uppgiften #%d',
'%s created the task #%d' => '%s skapade uppgiften #%d',
'%s closed the task #%d' => '%s stängde uppgiften #%d',
- '%s open the task #%d' => '%s öppnade uppgiften #%d',
- '%s moved the task #%d to the column "%s"' => '%s flyttade uppgiften #%d till kolumnen "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s flyttade uppgiften #%d till positionen %d i kolumnen "%s"',
+ '%s opened the task #%d' => '%s öppnade uppgiften #%d',
'Activity' => 'Aktivitet',
'Default values are "%s"' => 'Standardvärden är "%s"',
'Default columns for new projects (Comma-separated)' => 'Standardkolumner för nya projekt (kommaseparerade)',
'Task assignee change' => 'Ändra tilldelning av uppgiften',
- '%s change the assignee of the task #%d to %s' => '%s byt tilldelning av uppgiften #%d till %s',
+ '%s changed the assignee of the task #%d to %s' => '%s byt tilldelning av uppgiften #%d till %s',
'%s changed the assignee of the task %s to %s' => '%s byt tilldelning av uppgiften %s till %s',
'New password for the user "%s"' => 'Nytt lösenord för användaren "%s"',
'Choose an event' => 'Välj en händelse',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Procent',
'Number of tasks' => 'Antal uppgifter',
'Task distribution' => 'Uppgiftsfördelning',
- 'Reportings' => 'Rapportering',
- 'Task repartition for "%s"' => 'Uppgiftsdeltagande för "%s"',
'Analytics' => 'Analyser',
'Subtask' => 'Deluppgift',
'My subtasks' => 'Mina deluppgifter',
'User repartition' => 'Användardeltagande',
- 'User repartition for "%s"' => 'Användardeltagande för "%s"',
'Clone this project' => 'Klona projektet',
'Column removed successfully.' => 'Kolumnen togs bort',
'Not enough data to show the graph.' => 'Inte tillräckligt med data för att visa graf',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Värdet måste vara numeriskt',
'Unable to create this task.' => 'Kunde inte skapa uppgiften.',
'Cumulative flow diagram' => 'Diagram med kumulativt flöde',
- 'Cumulative flow diagram for "%s"' => 'Diagram med kumulativt flöde för "%s"',
'Daily project summary' => 'Daglig projektsummering',
'Daily project summary export' => 'Export av daglig projektsummering',
- 'Daily project summary export for "%s"' => 'Export av daglig projektsummering för "%s"',
'Exports' => 'Exporter',
'This export contains the number of tasks per column grouped per day.' => 'Denna export innehåller antalet uppgifter per kolumn grupperade per dag.',
'Active swimlanes' => 'Aktiva swimlanes',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Ta bort en swimlane',
'Show default swimlane' => 'Visa standard swimlane',
'Swimlane modification for the project "%s"' => 'Ändra swimlane för projektet "%s"',
- 'Swimlane not found.' => 'Swimlane kunde inte hittas',
'Swimlane removed successfully.' => 'Swimlane togs bort',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane uppdaterad',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Deluppgifts-ID',
'Subtasks' => 'Deluppgift',
'Subtasks Export' => 'Export av deluppgifter',
- 'Subtasks exportation for "%s"' => 'Export av deluppgifter för "%s"',
'Task Title' => 'Uppgiftstitel',
'Untitled' => 'Titel saknas',
'Application default' => 'Applikationsstandard',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Valutakursen har lagts till.',
'Unable to add this currency rate.' => 'Kunde inte lägga till valutakursen.',
'Webhook URL' => 'Webhook URL',
- '%s remove the assignee of the task %s' => '%s ta bort tilldelningen av uppgiften %s',
+ '%s removed the assignee of the task %s' => '%s ta bort tilldelningen av uppgiften %s',
'Enable Gravatar images' => 'Aktivera Gravatar bilder',
'Information' => 'Information',
'Check two factor authentication code' => 'Kolla tvåfaktorsverifieringskod',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Testa din enhet',
'Assign a color when the task is moved to a specific column' => 'Tilldela en färg när uppgiften flyttas till en specifik kolumn',
'%s via Kanboard' => '%s via Kanboard',
- 'Burndown chart for "%s"' => 'Burndown diagram för "%s"',
'Burndown chart' => 'Burndown diagram',
'This chart show the task complexity over the time (Work Remaining).' => 'Diagrammet visar uppgiftens svårighet över tid (återstående arbete).',
'Screenshot taken %s' => 'Skärmdump tagen %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Flyttas uppgiften till en annan kolumn när kategorin ändras',
'Send a task by email to someone' => 'Skicka en uppgift med e-post till någon',
'Reopen a task' => 'Återöppna en uppgift',
- 'Column change' => 'Kolumnändring',
- 'Position change' => 'Positionsändring',
- 'Swimlane change' => 'Swimlaneändring',
- 'Assignee change' => 'Tilldelningsändring',
- '[%s] Overdue tasks' => '[%s] Försenade uppgifter',
'Notification' => 'Notis',
'%s moved the task #%d to the first swimlane' => '%s flyttade uppgiften #%d till första swimlane',
- '%s moved the task #%d to the swimlane "%s"' => '%s flyttade uppgiften #%d till swimlane "%s"',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s flyttade uppgiften %s till första swimlane',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Sök efter kategori:',
'Search by description: ' => 'Sök efter beskrivning',
'Search by due date: ' => 'Sök efter förfallodatum',
- 'Lead and Cycle time for "%s"' => 'Led- och cykeltid för "%s"',
- 'Average time spent into each column for "%s"' => 'Medeltidsåtgång i varje kolumn för "%s"',
'Average time spent into each column' => 'Medeltidsåtgång i varje kolumn',
'Average time spent' => 'Medeltidsåtgång',
'This chart show the average time spent into each column for the last %d tasks.' => 'Diagramet visar medeltidsåtgång i varje kolumn för de senaste %d uppgifterna.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Licens:',
'License' => 'Licens',
'Enter the text below' => 'Fyll i texten nedan',
- 'Gantt chart for %s' => 'Gantt-schema för %s',
'Sort by position' => 'Sortera efter position',
'Sort by date' => 'Sortera efter datum',
'Add task' => 'Lägg till uppgift',
@@ -847,8 +817,6 @@ return array(
// 'Version' => '',
// 'Plugins' => '',
// 'There is no plugin loaded.' => '',
- // 'Set maximum column height' => '',
- // 'Remove maximum column height' => '',
// 'My notifications' => '',
// 'Custom filters' => '',
// 'Your custom filter have been created successfully.' => '',
@@ -953,7 +921,6 @@ return array(
// 'Invalid captcha' => '',
// 'The name must be unique' => '',
// 'View all groups' => '',
- // 'View group members' => '',
// 'There is no user available.' => '',
// 'Do you really want to remove the user "%s" from the group "%s"?' => '',
// 'There is no group.' => '',
@@ -974,13 +941,10 @@ return array(
// 'Enter group name...' => '',
// 'Role:' => '',
// 'Project members' => '',
- // 'Compare hours for "%s"' => '',
// '%s mentioned you in the task #%d' => '',
// '%s mentioned you in a comment on the task #%d' => '',
// 'You were mentioned in the task #%d' => '',
// 'You were mentioned in a comment on the task #%d' => '',
- // 'Mentioned' => '',
- // 'Compare Estimated Time vs Actual Time' => '',
// 'Estimated hours: ' => '',
// 'Actual hours: ' => '',
// 'Hours Spent' => '',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 607f62ca..2aee696b 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d งานที่ปิด',
'No task for this project' => 'ไม่มีงานสำหรับโปรเจคนี้',
'Public link' => 'ลิงค์สาธารณะ',
- 'Change assignee' => 'เปลี่ยนการกำหนด',
- 'Change assignee for the task "%s"' => 'เปลี่ยนการกำหนดสำหรับงาน « %s »',
'Timezone' => 'เขตเวลา',
'Sorry, I didn\'t find this information in my database!' => 'เสียใจด้วย ไม่สามารถหาข้อมูลในฐานข้อมูลได้',
'Page not found' => 'ไม่พบหน้า',
@@ -248,7 +246,6 @@ return array(
'Category' => 'หมวด',
'Category:' => 'หมวด:',
'Categories' => 'หมวด',
- 'Category not found.' => 'ไม่พบหมวด',
'Your category have been created successfully.' => 'สร้างหมวดเรียบร้อยแล้ว',
'Unable to create your category.' => 'ไม่สามารถสร้างหมวดได้',
'Your category have been updated successfully.' => 'ปรับปรุงหมวดเรียบร้อยแล้ว',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'คุณต้องการลบไฟล์ "%s" ใช่หรือไม่?',
'Attachments' => 'แนบ',
'Edit the task' => 'แก้ไขงาน',
- 'Edit the description' => 'แก้ไขคำอธิบาย',
'Add a comment' => 'เพิ่มความคิดเห็น',
'Edit a comment' => 'แก้ไขความคิดเห็น',
'Summary' => 'สรุป',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'แสดงโปรเจคอื่น',
'Created by %s' => 'สร้างโดย %s',
'Tasks Export' => 'ส่งออกงาน',
- 'Tasks exportation for "%s"' => 'ส่งออกงานสำหรับ "%s"',
'Start Date' => 'เริ่มวันที่',
'End Date' => 'สิ้นสุดวันที่',
'Execute' => 'ประมวลผล',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'งานย่อยใหม่',
'New attachment added "%s"' => 'เพิ่มการแนบใหม่ "%s"',
'New comment posted by %s' => 'ความคิดเห็นใหม่จาก %s',
- 'New attachment' => 'การแนบใหม่',
'New comment' => 'ความคิดเห็นใหม่',
'Comment updated' => 'ปรับปรุงความคิดเห็น',
'New subtask' => 'งานย่อยใหม่',
- 'Subtask updated' => 'ปรับปรุงงานย่อยแล้ว',
- 'Task updated' => 'ปรับปรุงงานแล้ว',
- 'Task closed' => 'ปิดงาน',
- 'Task opened' => 'เปิดงาน',
'I want to receive notifications only for those projects:' => 'ฉันต้องการรับการแจ้งเตือนสำหรับโปรเจค:',
'view the task on Kanboard' => 'แสดงงานบน Kanboard',
'Public access' => 'การเข้าถึงสาธารณะ',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'ไม่เปิดการใช้งานการยืนยันภายนอก',
'Password modified successfully.' => 'แก้ไขรหัสผ่านเรียบร้อยแล้ว',
'Unable to change the password.' => 'ไม่สามารถเปลี่ยนรหัสผ่านได้',
- 'Change category for the task "%s"' => 'เปลี่ยนหมวดสำหรับงาน "%s"',
'Change category' => 'เปลี่ยนหมวด',
'%s updated the task %s' => '%s ปรับปรุงงานแล้ว %s',
'%s opened the task %s' => '%s เปิดงานแล้ว %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s ปรับปรุงงานแล้ว #%d',
'%s created the task #%d' => '%s สร้างงานแล้ว #%d',
'%s closed the task #%d' => '%s ปิดงานแล้ว #%d',
- '%s open the task #%d' => '%s เปิดงานแล้ว #%d',
- '%s moved the task #%d to the column "%s"' => '%s ย้ายงานแล้ว #%d ไปที่คอลัมน์ "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s ย้ายงานแล้ว #%d ไปตำแหน่ง %d ในคอลัมน์ที่ "%s"',
+ '%s opened the task #%d' => '%s เปิดงานแล้ว #%d',
'Activity' => 'กิจกรรม',
'Default values are "%s"' => 'ค่าเริ่มต้น "%s"',
'Default columns for new projects (Comma-separated)' => 'คอลัมน์เริ่มต้นสำหรับโปรเจคใหม่ (Comma-separated)',
'Task assignee change' => 'เปลี่ยนการกำหนดบุคคลของงาน',
- '%s change the assignee of the task #%d to %s' => '%s เปลี่ยนผู้รับผิดชอบของงาน #%d เป็น %s',
+ '%s changed the assignee of the task #%d to %s' => '%s เปลี่ยนผู้รับผิดชอบของงาน #%d เป็น %s',
'%s changed the assignee of the task %s to %s' => '%s เปลี่ยนผู้รับผิดชอบของงาน %s เป็น %s',
'New password for the user "%s"' => 'รหัสผ่านใหม่สำหรับผู้ใช้ "%s"',
'Choose an event' => 'เลือกเหตุการณ์',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'เปอร์เซ็นต์',
'Number of tasks' => 'จำนวนงาน',
'Task distribution' => 'การกระจายงาน',
- 'Reportings' => 'รายงาน',
- // 'Task repartition for "%s"' => '',
'Analytics' => 'การวิเคราะห์',
'Subtask' => 'งานย่อย',
'My subtasks' => 'งานย่อยของฉัน',
'User repartition' => 'การแบ่งงานของผู้ใช้',
- 'User repartition for "%s"' => 'การแบ่งงานของผู้ใช้ "%s"',
'Clone this project' => 'เลียนแบบโปรเจคนี้',
'Column removed successfully.' => 'ลบคอลัมน์สำเร็จ',
'Not enough data to show the graph.' => 'ไม่มีข้อมูลแสดงเป็นกราฟ',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'ค่านี้ต้องเป็นตัวเลข',
'Unable to create this task.' => 'ไม่สามารถสร้างงานนี้',
'Cumulative flow diagram' => 'แผนภาพงานสะสม',
- 'Cumulative flow diagram for "%s"' => 'แผนภาพงานสะสม "%s"',
'Daily project summary' => 'สรุปโปรเจครายวัน',
'Daily project summary export' => 'ส่งออกสรุปโปรเจครายวัน',
- 'Daily project summary export for "%s"' => 'ส่งออกสรุปโปรเจครายวันสำหรับ "%s"',
'Exports' => 'ส่งออก',
'This export contains the number of tasks per column grouped per day.' => 'การส่งออกนี้เป็นการนับจำนวนงานในแต่ละคอลัมน์ในแต่ละวัน',
'Active swimlanes' => 'สวิมเลนพร้อมใช้งาน',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'ลบสวิมเลน',
'Show default swimlane' => 'แสดงสวิมเลนเริ่มต้น',
'Swimlane modification for the project "%s"' => 'แก้ไขสวิมเลนสำหรับโปรเจค "%s"',
- 'Swimlane not found.' => 'หาสวิมเลนไม่พบ',
'Swimlane removed successfully.' => 'ลบสวิมเลนเรียบร้อยแล้ว',
'Swimlanes' => 'สวิมเลน',
'Swimlane updated successfully.' => 'ปรับปรุงสวิมเลนเรียบร้อยแล้ว',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'รหัสงานย่อย',
'Subtasks' => 'งานย่อย',
'Subtasks Export' => 'ส่งออก งานย่อย',
- 'Subtasks exportation for "%s"' => 'ส่งออกงานย่อยสำหรับ "%s"',
'Task Title' => 'ชื่องาน',
'Untitled' => 'ไม่มีชื่อ',
'Application default' => 'แอพพลิเคชันเริ่มต้น',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'เพิ่มอัตราค่าเงินเรียบร้อย',
'Unable to add this currency rate.' => 'ไม่สามารถเพิ่มค่าเงินนี้',
// 'Webhook URL' => '',
- '%s remove the assignee of the task %s' => '%s เอาผู้รับผิดชอบออกจากงาน %s',
+ '%s removed the assignee of the task %s' => '%s เอาผู้รับผิดชอบออกจากงาน %s',
'Enable Gravatar images' => 'สามารถใช้งานภาพ Gravatar',
'Information' => 'ข้อมูลสารสนเทศ',
// 'Check two factor authentication code' => '',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'ทดสอบอุปกรณ์ของคุณ',
'Assign a color when the task is moved to a specific column' => 'กำหนดสีเมื่องานถูกย้ายไปคอลัมน์ที่กำหนดไว้',
// '%s via Kanboard' => '',
- 'Burndown chart for "%s"' => 'แผนภูมิงานกับเวลา "%s"',
'Burndown chart' => 'แผนภูมิงานกับเวลา',
'This chart show the task complexity over the time (Work Remaining).' => 'แผนภูมิแสดงความซับซ้อนของงานตามเวลา (งานที่เหลือ)',
'Screenshot taken %s' => 'จับภาพหน้าจอ %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'ย้ายงานไปคอลัมน์อื่นเมื่อหมวดถูกเปลี่ยน',
'Send a task by email to someone' => 'ส่งงานโดยถึงบางคน',
'Reopen a task' => 'เปิดงานอีกครั้ง',
- 'Column change' => 'เปลี่ยนคอลัมน์',
- 'Position change' => 'เปลี่ยนตำแหน่ง',
- 'Swimlane change' => 'เปลี่ยนสวิมเลน',
- 'Assignee change' => 'เปลี่ยนการผู้รับผิดชอบ',
- '[%s] Overdue tasks' => '[%s] งานที่เกินกำหนด',
'Notification' => 'แจ้งเตือน',
'%s moved the task #%d to the first swimlane' => '%s ย้ายงาน #%d ไปสวินเลนแรก',
- '%s moved the task #%d to the swimlane "%s"' => '%s ย้ายงาน #%d ไปสวินเลน "%s"',
'Swimlane' => 'สวิมเลน',
'Gravatar' => 'รูปแทนตัว',
'%s moved the task %s to the first swimlane' => '%s ย้ายงาน %s ไปสวินเลนแรก',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'ค้นหาตามหมวด: ',
'Search by description: ' => 'ค้นหาตามคำอธิบาย: ',
'Search by due date: ' => 'ค้นหาตามวันครบกำหนด: ',
- 'Lead and Cycle time for "%s"' => 'เวลานำและรอบเวลาสำหรับ "%s"',
- 'Average time spent into each column for "%s"' => 'ค่าเฉลี่ยเวลาที่ใช้แต่ละคอลัมน์สำหรับ "%s"',
'Average time spent into each column' => 'ค่าเฉลี่ยเวลาที่ใช้แต่ละคอลัมน์',
'Average time spent' => 'ค่าเฉลี่ยเวลาที่ใช้',
'This chart show the average time spent into each column for the last %d tasks.' => 'แผนภูมิแสดงค่าเฉลี่ยเวลาที่ใช้แต่ละคอลัมน์สำหรับ %d งานล่าสุด',
@@ -806,7 +777,6 @@ return array(
'License:' => 'สัญญาอนุญาต:',
'License' => 'สัญญาอนุญาต',
'Enter the text below' => 'พิมพ์ข้อความด้านล่าง',
- 'Gantt chart for %s' => 'แผนภูมิแกรนท์สำหรับ %s',
'Sort by position' => 'เรียงตามตำแหน่ง',
'Sort by date' => 'เรียงตามวัน',
'Add task' => 'เพิ่มงาน',
@@ -847,8 +817,6 @@ return array(
'Version' => 'เวอร์ชัน',
'Plugins' => 'ปลั๊กอิน',
'There is no plugin loaded.' => 'ไม่มีปลั๊กอินถูกโหลดไว้',
- 'Set maximum column height' => 'กำหนดความสูงสูงสุดของคอลัมน์',
- 'Remove maximum column height' => 'เอาความสูงสูงสุดของคอลัมน์ออก',
'My notifications' => 'การแจ้งเตือนของฉัน',
'Custom filters' => 'ตัวกรองกำหนดเอง',
'Your custom filter have been created successfully.' => 'ตัวกรองกำหนดเองของคุณสร้างเรียบร้อย',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'captcha ไม่ถูกต้อง',
'The name must be unique' => 'ชื่อต้องไม่ซ้ำ',
'View all groups' => 'แสดงกลุ่มทั้งหมด',
- 'View group members' => 'แสดงสมาชิกกลุ่ม',
// 'There is no user available.' => '',
'Do you really want to remove the user "%s" from the group "%s"?' => 'คุณต้องการลบผู้ใช้ "%s" ออกจากกลุ่ม "%s"?',
'There is no group.' => 'ไม่มีกลุ่ม',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'พิมพ์ชื่อกลุ่ม...',
'Role:' => 'บทบาท:',
'Project members' => 'สมาชิกโปรเจค',
- 'Compare hours for "%s"' => 'เปรียบเทียบรายชั่วโมงสำหรับ %s',
'%s mentioned you in the task #%d' => '%s กล่าวถึงคุณในงาน #%d',
'%s mentioned you in a comment on the task #%d' => '%s กล่าวถึงคุณในความคิดเห็นของงาน #%d',
'You were mentioned in the task #%d' => 'คุณได้รับการกล่าวถึงในงาน #%d',
'You were mentioned in a comment on the task #%d' => 'คุณได้รับการกล่าวถึงในความคิดเห็นของงาน #%d',
- 'Mentioned' => 'กล่าวถึง',
- 'Compare Estimated Time vs Actual Time' => 'เปรียบเทียบเวลาโดยประมาณกับเวลาที่เกิดขึ้นจริง',
'Estimated hours: ' => 'เวลาโดยประมาณ:',
'Actual hours: ' => 'เวลาที่เกิดขึ้นจริง:',
'Hours Spent' => 'เวลาที่ใช้',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php
index 229d766c..69642b58 100644
--- a/app/Locale/tr_TR/translations.php
+++ b/app/Locale/tr_TR/translations.php
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d kapatılmış görevler',
'No task for this project' => 'Bu proje için görev yok',
'Public link' => 'Dışa açık link',
- 'Change assignee' => 'Atanmış Kullanıcıyı değiştir',
- 'Change assignee for the task "%s"' => '"%s" görevi için atanmış kullanıcıyı değiştir',
'Timezone' => 'Saat dilimi',
'Sorry, I didn\'t find this information in my database!' => 'Üzgünüm, bu bilgiyi veri tabanımda bulamadım.',
'Page not found' => 'Sayfa bulunamadı',
@@ -248,7 +246,6 @@ return array(
'Category' => 'Kategori',
'Category:' => 'Kategori:',
'Categories' => 'Kategoriler',
- 'Category not found.' => 'Kategori bulunamadı.',
'Your category have been created successfully.' => 'Kategori başarıyla oluşturuldu.',
'Unable to create your category.' => 'Kategori oluşturulamadı.',
'Your category have been updated successfully.' => 'Kategori başarıyla güncellendi.',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => 'Bu dosyayı silmek istediğinize emin misiniz: "%s"?',
'Attachments' => 'Ekler',
'Edit the task' => 'Görevi değiştir',
- 'Edit the description' => 'Açıklamayı değiştir',
'Add a comment' => 'Yorum ekle',
'Edit a comment' => 'Yorum değiştir',
'Summary' => 'Özet',
@@ -303,7 +299,6 @@ return array(
'Display another project' => 'Başka bir proje göster',
'Created by %s' => '%s tarafından oluşturuldu',
'Tasks Export' => 'Görevleri dışa aktar',
- 'Tasks exportation for "%s"' => '"%s" için görevleri dışa aktar',
'Start Date' => 'Başlangıç tarihi',
'End Date' => 'Bitiş tarihi',
'Execute' => 'Gerçekleştir',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => 'Yeni alt görev',
'New attachment added "%s"' => 'Yeni dosya "%s" eklendi.',
'New comment posted by %s' => '%s tarafından yeni yorum eklendi',
- 'New attachment' => 'Yeni dosya eki',
'New comment' => 'Yeni yorum',
'Comment updated' => 'Yorum güncellendi',
'New subtask' => 'Yeni alt görev',
- 'Subtask updated' => 'Alt görev güncellendi',
- 'Task updated' => 'Görev güncellendi',
- 'Task closed' => 'Görev kapatıldı',
- 'Task opened' => 'Görev açıldı',
'I want to receive notifications only for those projects:' => 'Yalnızca bu projelerle ilgili bildirim almak istiyorum:',
'view the task on Kanboard' => 'bu görevi Kanboard üzerinde göster',
'Public access' => 'Dışa açık erişim',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => 'Dış kimlik doğrulama kapalı.',
'Password modified successfully.' => 'Şifre başarıyla değiştirildi.',
'Unable to change the password.' => 'Şifre değiştirilemiyor.',
- 'Change category for the task "%s"' => '"%s" görevi için kategori değiştirme',
'Change category' => 'Kategori değiştirme',
'%s updated the task %s' => '%s kullanıcısı %s görevini güncelledi',
'%s opened the task %s' => '%s kullanıcısı %s görevini açtı',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s kullanıcısı #%d nolu görevi güncelledi',
'%s created the task #%d' => '%s kullanıcısı #%d nolu görevi oluşturdu',
'%s closed the task #%d' => '%s kullanıcısı #%d nolu görevi kapattı',
- '%s open the task #%d' => '%s kullanıcısı #%d nolu görevi açtı',
- '%s moved the task #%d to the column "%s"' => '%s kullanıcısı #%d nolu görevi "%s" sütununa taşıdı',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s kullanıcısı #%d nolu görevi %d pozisyonu "%s" sütununa taşıdı',
+ '%s opened the task #%d' => '%s kullanıcısı #%d nolu görevi açtı',
'Activity' => 'Aktivite',
'Default values are "%s"' => 'Varsayılan değerler "%s"',
'Default columns for new projects (Comma-separated)' => 'Yeni projeler için varsayılan sütunlar (virgül ile ayrılmış)',
'Task assignee change' => 'Göreve atanan kullanıcı değişikliği',
- '%s change the assignee of the task #%d to %s' => '%s kullanıcısı #%d nolu görevin sorumlusunu %s olarak değiştirdi',
+ '%s changed the assignee of the task #%d to %s' => '%s kullanıcısı #%d nolu görevin sorumlusunu %s olarak değiştirdi',
'%s changed the assignee of the task %s to %s' => '%s kullanıcısı %s görevinin sorumlusunu %s olarak değiştirdi',
'New password for the user "%s"' => '"%s" kullanıcısı için yeni şifre',
'Choose an event' => 'Bir durum seçin',
@@ -447,13 +434,10 @@ return array(
'Percentage' => 'Yüzde',
'Number of tasks' => 'Görev sayısı',
'Task distribution' => 'Görev dağılımı',
- 'Reportings' => 'Raporlar',
- 'Task repartition for "%s"' => '"%s" için görev dağılımı',
'Analytics' => 'Analiz',
'Subtask' => 'Alt görev',
'My subtasks' => 'Alt görevlerim',
'User repartition' => 'Kullanıcı dağılımı',
- 'User repartition for "%s"' => '"%s" için kullanıcı dağılımı',
'Clone this project' => 'Projenin kopyasını oluştur',
'Column removed successfully.' => 'Sütun başarıyla kaldırıldı.',
'Not enough data to show the graph.' => 'Grafik gösterimi için yeterli veri yok.',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => 'Bu değer sayı olmalı',
'Unable to create this task.' => 'Bu görev oluşturulamıyor.',
'Cumulative flow diagram' => 'Kümülatif akış diyagramı',
- 'Cumulative flow diagram for "%s"' => '"%s" için kümülatif akış diyagramı',
'Daily project summary' => 'Günlük proje özeti',
'Daily project summary export' => 'Günlük proje özetini dışa aktar',
- 'Daily project summary export for "%s"' => '"%s" için günlük proje özetinin dışa',
'Exports' => 'Dışa aktarımlar',
'This export contains the number of tasks per column grouped per day.' => 'Bu dışa aktarım günlük gruplanmış olarak her sütundaki görev sayısını içerir.',
'Active swimlanes' => 'Aktif Kulvar',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => 'Kulvarı sil',
'Show default swimlane' => 'Varsayılan Kulvarı göster',
'Swimlane modification for the project "%s"' => '"%s" Projesi için Kulvar değişikliği',
- 'Swimlane not found.' => 'Kulvar bulunamadı',
'Swimlane removed successfully.' => 'Kulvar başarıyla kaldırıldı.',
'Swimlanes' => 'Kulvarlar',
'Swimlane updated successfully.' => 'Kulvar başarıyla güncellendi.',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => 'Alt görev No:',
'Subtasks' => 'Alt görevler',
'Subtasks Export' => 'Alt görevleri dışa aktar',
- 'Subtasks exportation for "%s"' => '"%s" için alt görevleri dışa aktarımı',
'Task Title' => 'Görev Başlığı',
'Untitled' => 'Başlıksız',
'Application default' => 'Uygulama varsayılanları',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => 'Kur başarıyla eklendi',
'Unable to add this currency rate.' => 'Bu kur eklenemedi',
// 'Webhook URL' => '',
- '%s remove the assignee of the task %s' => '%s, %s görevinin atanan bilgisini kaldırdı',
+ '%s removed the assignee of the task %s' => '%s, %s görevinin atanan bilgisini kaldırdı',
'Enable Gravatar images' => 'Gravatar resimlerini kullanıma aç',
'Information' => 'Bilgi',
'Check two factor authentication code' => 'İki kademeli doğrulama kodunu kontrol et',
@@ -621,7 +601,6 @@ return array(
'Test your device' => 'Cihazınızı test edin',
'Assign a color when the task is moved to a specific column' => 'Görev belirli bir sütuna taşındığında rengini değiştir',
'%s via Kanboard' => '%s Kanboard ile',
- 'Burndown chart for "%s"' => '%s icin kalan iş grafiği',
'Burndown chart' => 'Kalan iş grafiği',
'This chart show the task complexity over the time (Work Remaining).' => 'Bu grafik zorluk seviyesini zamana göre gösterir (kalan iş)',
'Screenshot taken %s' => 'Ekran görüntüsü alındı %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => 'Kategori değiştirildiğinde görevi başka sütuna taşı',
'Send a task by email to someone' => 'Bir görevi email ile birine gönder',
'Reopen a task' => 'Bir görevi tekrar aç',
- 'Column change' => 'Sütun değişikliği',
- 'Position change' => 'Konum değişikliği',
- 'Swimlane change' => 'Kulvar değişikliği',
- 'Assignee change' => 'Atanan değişikliği',
- '[%s] Overdue tasks' => '[%s] Gecikmiş görevler',
'Notification' => 'Uyarılar',
'%s moved the task #%d to the first swimlane' => '%s, #%d görevini birinci kulvara taşıdı',
- '%s moved the task #%d to the swimlane "%s"' => '%s, #%d görevini "%s" kulvarına taşıdı',
'Swimlane' => 'Kulvar',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s, %s görevini ilk kulvara taşıdı',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => 'Kategoriye göre ara',
'Search by description: ' => 'Açıklamaya göre ara',
'Search by due date: ' => 'Tamamlanma tarihine göre ara',
- 'Lead and Cycle time for "%s"' => '"%s" için teslim ve çevrim süresi',
- 'Average time spent into each column for "%s"' => '"%s" için her bir sütunda geçirilen ortalama zaman',
'Average time spent into each column' => 'Her bir sütunda geçirilen ortalama zaman',
'Average time spent' => 'Harcanan ortalama zaman',
'This chart show the average time spent into each column for the last %d tasks.' => 'Bu grafik son %d görev için her bir sütunda geçirilen ortalama zamanı gösterir.',
@@ -806,7 +777,6 @@ return array(
'License:' => 'Lisans:',
'License' => 'Lisans',
'Enter the text below' => 'Aşağıdaki metni girin',
- 'Gantt chart for %s' => '%s için Gantt diyagramı',
'Sort by position' => 'Pozisyona göre sırala',
'Sort by date' => 'Tarihe göre sırala',
'Add task' => 'Görev ekle',
@@ -847,8 +817,6 @@ return array(
'Version' => 'Versiyon',
'Plugins' => 'Eklentiler',
'There is no plugin loaded.' => 'Yüklenmiş bir eklendi yok',
- 'Set maximum column height' => 'Maksimum sütun yüksekliğini belirle',
- 'Remove maximum column height' => 'Maksimum sütun yüksekliğini iptal et',
'My notifications' => 'Bildirimlerim',
'Custom filters' => 'Özel filtreler',
'Your custom filter have been created successfully.' => 'Özel filtreleriniz başarıyla oluşturuldu.',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => 'Geçersiz captcha',
'The name must be unique' => 'İsim tekil olmalı',
'View all groups' => 'Tüm grupları görüntüle',
- 'View group members' => 'Grup üyelerini görüntüle',
'There is no user available.' => 'Uygun üye yok',
'Do you really want to remove the user "%s" from the group "%s"?' => '"%s" kullanıcısını "%s" grubundan çıkarmak istediğinize emin misiniz?',
'There is no group.' => 'Hiç grup yok.',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => 'Grup adını girin...',
'Role:' => 'Rol:',
'Project members' => 'Proje üyeleri',
- 'Compare hours for "%s"' => '"%s" için saatleri karşılaştır',
'%s mentioned you in the task #%d' => '%s sizden #%d görevinde bahsetti',
'%s mentioned you in a comment on the task #%d' => '%s sizden #%d görevindeki bir yorumda bahsetti',
'You were mentioned in the task #%d' => '#%d görevinde sizden bahsedildi',
'You were mentioned in a comment on the task #%d' => '#%d görevindeki bir yorumda sizden bahsedildi',
- 'Mentioned' => 'Bahsedilmiş',
- 'Compare Estimated Time vs Actual Time' => 'Tahmini süre ile gerçekleşen süreyi karşılaştır',
'Estimated hours: ' => 'Tahmini saat:',
'Actual hours: ' => 'Gerçekleşen saat:',
'Hours Spent' => 'Harcanan saat',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index 2f3a9f74..b4e9c063 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -105,7 +105,7 @@ return array(
'Add a new task' => '添加新任务',
'The username is required' => '需要用户名',
'The maximum length is %d characters' => '最长%d个英文字符',
- 'The minimum length is %d characters' => '最短%d个英文自负',
+ 'The minimum length is %d characters' => '最短%d个英文字符',
'The password is required' => '需要密码',
'This value must be an integer' => '该值必须为整数',
'The username must be unique' => '用户名必须唯一',
@@ -154,8 +154,6 @@ return array(
'%d closed tasks' => '%d个已关闭任务',
'No task for this project' => '该项目尚无任务',
'Public link' => '公开链接',
- 'Change assignee' => '变更负责人',
- 'Change assignee for the task "%s"' => '更改任务"%s"的负责人',
'Timezone' => '时区',
'Sorry, I didn\'t find this information in my database!' => '抱歉,无法在数据库中找到该信息!',
'Page not found' => '页面未找到',
@@ -248,7 +246,6 @@ return array(
'Category' => '分类',
'Category:' => '分类:',
'Categories' => '分类',
- 'Category not found.' => '未找到分类。',
'Your category have been created successfully.' => '成功为您创建分类。',
'Unable to create your category.' => '无法为您创建分类。',
'Your category have been updated successfully.' => '成功为您更新分类。',
@@ -270,7 +267,6 @@ return array(
'Do you really want to remove this file: "%s"?' => '确定要移除文件"%s"吗?',
'Attachments' => '附件',
'Edit the task' => '修改任务',
- 'Edit the description' => '修改描述',
'Add a comment' => '添加评论',
'Edit a comment' => '编辑评论',
'Summary' => '概要',
@@ -303,7 +299,6 @@ return array(
'Display another project' => '显示其它项目',
'Created by %s' => '创建者:%s',
'Tasks Export' => '任务导出',
- 'Tasks exportation for "%s"' => '导出"%s"的任务',
'Start Date' => '开始时间',
'End Date' => '结束时间',
'Execute' => '执行',
@@ -326,14 +321,9 @@ return array(
'New sub-task' => '新建子任务',
'New attachment added "%s"' => '新附件已添加"%s"',
'New comment posted by %s' => '%s 的新评论',
- 'New attachment' => '新建附件',
'New comment' => '新建评论',
'Comment updated' => '更新了评论',
'New subtask' => '新建子任务',
- 'Subtask updated' => '子任务更新',
- 'Task updated' => '任务更新',
- 'Task closed' => '任务关闭',
- 'Task opened' => '任务开启',
'I want to receive notifications only for those projects:' => '我仅需要收到下面项目的通知:',
'view the task on Kanboard' => '在看板中查看此任务',
'Public access' => '公开访问',
@@ -368,7 +358,6 @@ return array(
'No external authentication enabled.' => '未启用外部认证。',
'Password modified successfully.' => '已经成功修改密码。',
'Unable to change the password.' => '无法修改密码。',
- 'Change category for the task "%s"' => '变更任务 "%s" 的分类',
'Change category' => '变更分类',
'%s updated the task %s' => '%s 更新了任务 %s',
'%s opened the task %s' => '%s 开启了任务 %s',
@@ -391,14 +380,12 @@ return array(
'%s updated the task #%d' => '%s 更新了任务 #%d',
'%s created the task #%d' => '%s 创建了任务 #%d',
'%s closed the task #%d' => '%s 关闭了任务 #%d',
- '%s open the task #%d' => '%s 开启了任务 #%d',
- '%s moved the task #%d to the column "%s"' => '%s 将任务 #%d 移动到栏目 "%s"',
- '%s moved the task #%d to the position %d in the column "%s"' => '%s将任务#%d移动到"%s"的第 %d 列',
+ '%s opened the task #%d' => '%s 开启了任务 #%d',
'Activity' => '动态',
'Default values are "%s"' => '默认值为 "%s"',
'Default columns for new projects (Comma-separated)' => '新建项目的默认栏目(用逗号分开)',
'Task assignee change' => '任务分配变更',
- '%s change the assignee of the task #%d to %s' => '%s 将任务 #%d 分配给了 %s',
+ '%s changed the assignee of the task #%d to %s' => '%s 将任务 #%d 分配给了 %s',
'%s changed the assignee of the task %s to %s' => '%s 将任务 %s 分配给 %s',
'New password for the user "%s"' => '用户"%s"的新密码',
'Choose an event' => '选择一个事件',
@@ -447,13 +434,10 @@ return array(
'Percentage' => '百分比',
'Number of tasks' => '任务数',
'Task distribution' => '任务分布',
- 'Reportings' => '报告',
- 'Task repartition for "%s"' => '"%s"的任务分析',
'Analytics' => '分析',
'Subtask' => '子任务',
'My subtasks' => '我的子任务',
'User repartition' => '用户分析',
- 'User repartition for "%s"' => '"%s"的用户分析',
'Clone this project' => '复制此项目',
'Column removed successfully.' => '成功删除了栏目。',
'Not enough data to show the graph.' => '数据不足,无法绘图。',
@@ -470,10 +454,8 @@ return array(
'This value must be numeric' => '这个值必须为数字',
'Unable to create this task.' => '无法创建此任务。',
'Cumulative flow diagram' => '累积流图表',
- 'Cumulative flow diagram for "%s"' => '"%s"的累积流图表',
'Daily project summary' => '每日项目汇总',
'Daily project summary export' => '导出每日项目汇总',
- 'Daily project summary export for "%s"' => '导出项目"%s"的每日汇总',
'Exports' => '导出',
'This export contains the number of tasks per column grouped per day.' => '此导出包含每列的任务数,按天分组',
'Active swimlanes' => '活动里程碑',
@@ -485,7 +467,6 @@ return array(
'Remove a swimlane' => '删除里程碑',
'Show default swimlane' => '显示默认里程碑',
'Swimlane modification for the project "%s"' => '项目"%s"的里程碑变更',
- 'Swimlane not found.' => '未找到里程碑。',
'Swimlane removed successfully.' => '成功删除里程碑',
'Swimlanes' => '里程碑',
'Swimlane updated successfully.' => '成功更新了里程碑。',
@@ -500,7 +481,6 @@ return array(
'Subtask Id' => '子任务 Id',
'Subtasks' => '子任务',
'Subtasks Export' => '子任务导出',
- 'Subtasks exportation for "%s"' => '导出"%s"的子任务',
'Task Title' => '任务标题',
'Untitled' => '无标题',
'Application default' => '程序默认',
@@ -607,7 +587,7 @@ return array(
'The currency rate have been added successfully.' => '成功添加汇率。',
'Unable to add this currency rate.' => '无法添加此汇率',
'Webhook URL' => '网络钩子 URL',
- '%s remove the assignee of the task %s' => '%s删除了任务%s的负责人',
+ '%s removed the assignee of the task %s' => '%s删除了任务%s的负责人',
'Enable Gravatar images' => '启用 Gravatar 图像',
'Information' => '信息',
'Check two factor authentication code' => '检查双重认证码',
@@ -621,7 +601,6 @@ return array(
'Test your device' => '测试设备',
'Assign a color when the task is moved to a specific column' => '任务移动到指定栏目时设置颜色',
'%s via Kanboard' => '%s 通过KanBoard',
- 'Burndown chart for "%s"' => '燃尽图:"%s"',
'Burndown chart' => '燃尽图',
'This chart show the task complexity over the time (Work Remaining).' => '图表显示任务随时间(剩余时间)的复杂度',
'Screenshot taken %s' => '已截图 %s',
@@ -686,14 +665,8 @@ return array(
'Move the task to another column when the category is changed' => '当任务分类改变时移动到任务栏',
'Send a task by email to someone' => '发送任务邮件到用户',
'Reopen a task' => '重新开始一个任务',
- 'Column change' => '任务栏改变',
- 'Position change' => '位置改变',
- 'Swimlane change' => '里程碑改变',
- 'Assignee change' => '指派人改变',
- '[%s] Overdue tasks' => '[%s] 超期任务',
'Notification' => '通知',
'%s moved the task #%d to the first swimlane' => '%s将任务#%d移动到了首个里程碑',
- '%s moved the task #%d to the swimlane "%s"' => '%s将任务#%d移动到了里程碑"%s"下',
'Swimlane' => '里程碑',
'Gravatar' => 'Gravatar头像',
'%s moved the task %s to the first swimlane' => '%s将任务%s移动到了首个里程碑',
@@ -764,8 +737,6 @@ return array(
'Search by category: ' => '按分类:',
'Search by description: ' => '按描述:',
'Search by due date: ' => '按超期时间:',
- // 'Lead and Cycle time for "%s"' => '',
- 'Average time spent into each column for "%s"' => '"%s"在每个任务栏下平均花费时间',
'Average time spent into each column' => '每个任务栏平均花费时间',
'Average time spent' => '平均花费时间',
'This chart show the average time spent into each column for the last %d tasks.' => '当前柱状图表示最新%d条任务在每个任务栏下的平均花费时间',
@@ -806,7 +777,6 @@ return array(
'License:' => '授权许可:',
'License' => '授权许可',
'Enter the text below' => '输入下方的文本',
- 'Gantt chart for %s' => '%s的甘特图',
'Sort by position' => '按位置排序',
'Sort by date' => '按日期排序',
'Add task' => '添加任务',
@@ -847,8 +817,6 @@ return array(
'Version' => '版本',
'Plugins' => '插件',
'There is no plugin loaded.' => '当前没有插件载入',
- 'Set maximum column height' => '设置任务栏最大高度',
- 'Remove maximum column height' => '移除任务栏最大高度',
'My notifications' => '我的通知',
'Custom filters' => '自定义过滤器',
'Your custom filter have been created successfully.' => '成功创建过滤器',
@@ -953,7 +921,6 @@ return array(
'Invalid captcha' => '验证码无效',
'The name must be unique' => '请确保用户名唯一',
'View all groups' => '查看所有用户组',
- 'View group members' => '查看该组所有用户',
'There is no user available.' => '当前没有有效用户',
'Do you really want to remove the user "%s" from the group "%s"?' => '你确定把用户"%s"从"%s"中移除?',
'There is no group.' => '当前没有用户组',
@@ -974,13 +941,10 @@ return array(
'Enter group name...' => '输入用户组名称...',
'Role:' => '角色:',
'Project members' => '项目成员',
- 'Compare hours for "%s"' => '比较"%s"的时间',
'%s mentioned you in the task #%d' => '%s在任务#%d里提及你',
'%s mentioned you in a comment on the task #%d' => '%s在任务#%d的评论里提及你',
'You were mentioned in the task #%d' => '你在任务#%d里被提及',
'You were mentioned in a comment on the task #%d' => '你在任务#%d的评论里被提及',
- 'Mentioned' => '被提及',
- 'Compare Estimated Time vs Actual Time' => '对比预估时间VS实际时间',
'Estimated hours: ' => '预估小时数',
'Actual hours: ' => '实际小时数',
'Hours Spent' => '花费小时数',
@@ -1202,4 +1166,52 @@ return array(
// 'Email transport' => '',
// 'Webhook token' => '',
// 'Imports' => '',
+ // 'Project tags management' => '',
+ // 'Tag created successfully.' => '',
+ // 'Unable to create this tag.' => '',
+ // 'Tag updated successfully.' => '',
+ // 'Unable to update this tag.' => '',
+ // 'Tag removed successfully.' => '',
+ // 'Unable to remove this tag.' => '',
+ // 'Global tags management' => '',
+ // 'Tags' => '',
+ // 'Tags management' => '',
+ // 'Add new tag' => '',
+ // 'Edit a tag' => '',
+ // 'Project tags' => '',
+ // 'There is no specific tag for this project at the moment.' => '',
+ // 'Tag' => '',
+ // 'Remove a tag' => '',
+ // 'Do you really want to remove this tag: "%s"?' => '',
+ // 'Global tags' => '',
+ // 'There is no global tag at the moment.' => '',
+ // 'This field cannot be empty' => '',
+ // 'Close a task when there is no activity in an specific column' => '',
+ // '%s removed a subtask for the task #%d' => '',
+ // '%s removed a comment on the task #%d' => '',
+ // 'Comment removed on task #%d' => '',
+ // 'Subtask removed on task #%d' => '',
+ // 'Hide tasks in this column in the dashboard' => '',
+ // '%s removed a comment on the task %s' => '',
+ // '%s removed a subtask for the task %s' => '',
+ // 'Comment removed' => '',
+ // 'Subtask removed' => '',
+ // '%s set a new internal link for the task #%d' => '',
+ // '%s removed an internal link for the task #%d' => '',
+ // 'A new internal link for the task #%d have been defined' => '',
+ // 'Internal link removed for the task #%d' => '',
+ // '%s set a new internal link for the task %s' => '',
+ // '%s removed an internal link for the task %s' => '',
+ // 'Automatically set the due date on task creation' => '',
+ // 'Move the task to another column when closed' => '',
+ // 'Move the task to another column when not moved during a given period' => '',
+ // 'Dashboard for %s' => '',
+ // 'Tasks overview for %s' => '',
+ // 'Subtasks overview for %s' => '',
+ // 'Projects overview for %s' => '',
+ // 'Activity stream for %s' => '',
+ // 'Calendar for %s' => '',
+ // 'Notifications for %s' => '',
+ // 'Subtasks export' => '',
+ // 'Tasks exportation' => '',
);
diff --git a/app/Model/ActionModel.php b/app/Model/ActionModel.php
index 53393ed5..b5d2bd06 100644
--- a/app/Model/ActionModel.php
+++ b/app/Model/ActionModel.php
@@ -86,6 +86,18 @@ class ActionModel extends Base
}
/**
+ * Get the projectId by the actionId
+ *
+ * @access public
+ * @param integer $action_id
+ * @return integer
+ */
+ public function getProjectId($action_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $action_id)->findOneColumn('project_id') ?: 0;
+ }
+
+ /**
* Attach parameters to actions
*
* @access private
diff --git a/app/Model/BoardModel.php b/app/Model/BoardModel.php
index d2718b47..4d559936 100644
--- a/app/Model/BoardModel.php
+++ b/app/Model/BoardModel.php
@@ -94,66 +94,6 @@ class BoardModel extends Base
}
/**
- * Get all tasks sorted by columns and swimlanes
- *
- * @access public
- * @param integer $project_id
- * @param callable $callback
- * @return array
- */
- public function getBoard($project_id, $callback = null)
- {
- $swimlanes = $this->swimlaneModel->getSwimlanes($project_id);
- $columns = $this->columnModel->getAll($project_id);
- $nb_columns = count($columns);
-
- for ($i = 0, $ilen = count($swimlanes); $i < $ilen; $i++) {
- $swimlanes[$i]['columns'] = $columns;
- $swimlanes[$i]['nb_columns'] = $nb_columns;
- $swimlanes[$i]['nb_tasks'] = 0;
- $swimlanes[$i]['nb_swimlanes'] = $ilen;
-
- for ($j = 0; $j < $nb_columns; $j++) {
- $column_id = $columns[$j]['id'];
- $swimlane_id = $swimlanes[$i]['id'];
-
- if (! isset($swimlanes[0]['columns'][$j]['nb_column_tasks'])) {
- $swimlanes[0]['columns'][$j]['nb_column_tasks'] = 0;
- $swimlanes[0]['columns'][$j]['total_score'] = 0;
- }
-
- $swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinderModel->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id);
- $swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']);
- $swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score');
- $swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];
- $swimlanes[0]['columns'][$j]['nb_column_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks'];
- $swimlanes[0]['columns'][$j]['total_score'] += $swimlanes[$i]['columns'][$j]['score'];
- }
- }
-
- return $swimlanes;
- }
-
- /**
- * Calculate the sum of the defined field for a list of tasks
- *
- * @access public
- * @param array $tasks
- * @param string $field
- * @return integer
- */
- public function getColumnSum(array &$tasks, $field)
- {
- $sum = 0;
-
- foreach ($tasks as $task) {
- $sum += $task[$field];
- }
-
- return $sum;
- }
-
- /**
* Get the total of tasks per column
*
* @access public
diff --git a/app/Model/CategoryModel.php b/app/Model/CategoryModel.php
index 62fb5611..024d0026 100644
--- a/app/Model/CategoryModel.php
+++ b/app/Model/CategoryModel.php
@@ -56,6 +56,18 @@ class CategoryModel extends Base
}
/**
+ * Get the projectId by the category id
+ *
+ * @access public
+ * @param integer $category_id Category id
+ * @return integer
+ */
+ public function getProjectId($category_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('project_id') ?: 0;
+ }
+
+ /**
* Get a category id by the category name and project id
*
* @access public
diff --git a/app/Model/ColumnModel.php b/app/Model/ColumnModel.php
index 1adac0f2..5498ef54 100644
--- a/app/Model/ColumnModel.php
+++ b/app/Model/ColumnModel.php
@@ -32,6 +32,18 @@ class ColumnModel extends Base
}
/**
+ * Get projectId by the columnId
+ *
+ * @access public
+ * @param integer $column_id Column id
+ * @return integer
+ */
+ public function getProjectId($column_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('project_id');
+ }
+
+ /**
* Get the first column id for a given project
*
* @access public
@@ -126,19 +138,21 @@ class ColumnModel extends Base
* Add a new column to the board
*
* @access public
- * @param integer $project_id Project id
- * @param string $title Column title
- * @param integer $task_limit Task limit
- * @param string $description Column description
- * @return boolean|integer
+ * @param integer $project_id Project id
+ * @param string $title Column title
+ * @param integer $task_limit Task limit
+ * @param string $description Column description
+ * @param integer $hide_in_dashboard
+ * @return bool|int
*/
- public function create($project_id, $title, $task_limit = 0, $description = '')
+ public function create($project_id, $title, $task_limit = 0, $description = '', $hide_in_dashboard = 0)
{
$values = array(
'project_id' => $project_id,
'title' => $title,
'task_limit' => intval($task_limit),
'position' => $this->getLastColumnPosition($project_id) + 1,
+ 'hide_in_dashboard' => $hide_in_dashboard,
'description' => $description,
);
@@ -153,13 +167,15 @@ class ColumnModel extends Base
* @param string $title Column title
* @param integer $task_limit Task limit
* @param string $description Optional description
+ * @param integer $hide_in_dashboard
* @return boolean
*/
- public function update($column_id, $title, $task_limit = 0, $description = '')
+ public function update($column_id, $title, $task_limit = 0, $description = '', $hide_in_dashboard = 0)
{
return $this->db->table(self::TABLE)->eq('id', $column_id)->update(array(
'title' => $title,
'task_limit' => intval($task_limit),
+ 'hide_in_dashboard' => $hide_in_dashboard,
'description' => $description,
));
}
diff --git a/app/Model/CommentModel.php b/app/Model/CommentModel.php
index 36e1fc48..a9e48bd3 100644
--- a/app/Model/CommentModel.php
+++ b/app/Model/CommentModel.php
@@ -2,7 +2,6 @@
namespace Kanboard\Model;
-use Kanboard\Event\CommentEvent;
use Kanboard\Core\Base;
/**
@@ -27,9 +26,26 @@ class CommentModel extends Base
*/
const EVENT_UPDATE = 'comment.update';
const EVENT_CREATE = 'comment.create';
+ const EVENT_DELETE = 'comment.delete';
const EVENT_USER_MENTION = 'comment.user.mention';
/**
+ * Get projectId from commentId
+ *
+ * @access public
+ * @param integer $comment_id
+ * @return integer
+ */
+ public function getProjectId($comment_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->eq(self::TABLE.'.id', $comment_id)
+ ->join(TaskModel::TABLE, 'id', 'task_id')
+ ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
+ }
+
+ /**
* Get all comments for a given task
*
* @access public
@@ -114,9 +130,7 @@ class CommentModel extends Base
$comment_id = $this->db->table(self::TABLE)->persist($values);
if ($comment_id !== false) {
- $event = new CommentEvent(array('id' => $comment_id) + $values);
- $this->dispatcher->dispatch(self::EVENT_CREATE, $event);
- $this->userMentionModel->fireEvents($values['comment'], self::EVENT_USER_MENTION, $event);
+ $this->queueManager->push($this->commentEventJob->withParams($comment_id, self::EVENT_CREATE));
}
return $comment_id;
@@ -137,7 +151,7 @@ class CommentModel extends Base
->update(array('comment' => $values['comment']));
if ($result) {
- $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new CommentEvent($values));
+ $this->queueManager->push($this->commentEventJob->withParams($values['id'], self::EVENT_UPDATE));
}
return $result;
@@ -152,6 +166,7 @@ class CommentModel extends Base
*/
public function remove($comment_id)
{
+ $this->commentEventJob->execute($comment_id, self::EVENT_DELETE);
return $this->db->table(self::TABLE)->eq('id', $comment_id)->remove();
}
}
diff --git a/app/Model/FileModel.php b/app/Model/FileModel.php
index 8cdea9a0..98032f9d 100644
--- a/app/Model/FileModel.php
+++ b/app/Model/FileModel.php
@@ -5,7 +5,6 @@ namespace Kanboard\Model;
use Exception;
use Kanboard\Core\Base;
use Kanboard\Core\Thumbnail;
-use Kanboard\Event\FileEvent;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
@@ -44,13 +43,13 @@ abstract class FileModel extends Base
abstract protected function getPathPrefix();
/**
- * Get event name
+ * Fire file creation event
*
* @abstract
* @access protected
- * @return string
+ * @param integer $file_id
*/
- abstract protected function getEventName();
+ abstract protected function fireCreationEvent($file_id);
/**
* Get PicoDb query to get all files
@@ -130,16 +129,16 @@ abstract class FileModel extends Base
* Create a file entry in the database
*
* @access public
- * @param integer $id Foreign key
- * @param string $name Filename
- * @param string $path Path on the disk
- * @param integer $size File size
+ * @param integer $foreign_key_id Foreign key
+ * @param string $name Filename
+ * @param string $path Path on the disk
+ * @param integer $size File size
* @return bool|integer
*/
- public function create($id, $name, $path, $size)
+ public function create($foreign_key_id, $name, $path, $size)
{
$values = array(
- $this->getForeignKey() => $id,
+ $this->getForeignKey() => $foreign_key_id,
'name' => substr($name, 0, 255),
'path' => $path,
'is_image' => $this->isImage($name) ? 1 : 0,
@@ -152,8 +151,7 @@ abstract class FileModel extends Base
if ($result) {
$file_id = (int) $this->db->getLastId();
- $event = new FileEvent($values + array('file_id' => $file_id));
- $this->dispatcher->dispatch($this->getEventName(), $event);
+ $this->fireCreationEvent($file_id);
return $file_id;
}
diff --git a/app/Model/GroupModel.php b/app/Model/GroupModel.php
index 0a975570..b43423b3 100644
--- a/app/Model/GroupModel.php
+++ b/app/Model/GroupModel.php
@@ -116,4 +116,23 @@ class GroupModel extends Base
{
return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
}
+
+ /**
+ * Get groupId from externalGroupId and create the group if not found
+ *
+ * @access public
+ * @param string $name
+ * @param string $external_id
+ * @return bool|integer
+ */
+ public function getOrCreateExternalGroupId($name, $external_id)
+ {
+ $group_id = $this->db->table(self::TABLE)->eq('external_id', $external_id)->findOneColumn('id');
+
+ if (empty($group_id)) {
+ $group_id = $this->create($name, $external_id);
+ }
+
+ return $group_id;
+ }
}
diff --git a/app/Model/NotificationModel.php b/app/Model/NotificationModel.php
index 8937b77e..803d4f18 100644
--- a/app/Model/NotificationModel.php
+++ b/app/Model/NotificationModel.php
@@ -3,9 +3,15 @@
namespace Kanboard\Model;
use Kanboard\Core\Base;
+use Kanboard\EventBuilder\CommentEventBuilder;
+use Kanboard\EventBuilder\EventIteratorBuilder;
+use Kanboard\EventBuilder\SubtaskEventBuilder;
+use Kanboard\EventBuilder\TaskEventBuilder;
+use Kanboard\EventBuilder\TaskFileEventBuilder;
+use Kanboard\EventBuilder\TaskLinkEventBuilder;
/**
- * Notification
+ * Notification Model
*
* @package Kanboard\Model
* @author Frederic Guillot
@@ -16,121 +22,79 @@ class NotificationModel extends Base
* Get the event title with author
*
* @access public
- * @param string $event_author
- * @param string $event_name
- * @param array $event_data
+ * @param string $eventAuthor
+ * @param string $eventName
+ * @param array $eventData
* @return string
*/
- public function getTitleWithAuthor($event_author, $event_name, array $event_data)
+ public function getTitleWithAuthor($eventAuthor, $eventName, array $eventData)
{
- switch ($event_name) {
- case TaskModel::EVENT_ASSIGNEE_CHANGE:
- $assignee = $event_data['task']['assignee_name'] ?: $event_data['task']['assignee_username'];
+ foreach ($this->getIteratorBuilder() as $builder) {
+ $title = $builder->buildTitleWithAuthor($eventAuthor, $eventName, $eventData);
- if (! empty($assignee)) {
- return e('%s change the assignee of the task #%d to %s', $event_author, $event_data['task']['id'], $assignee);
- }
-
- return e('%s remove the assignee of the task %s', $event_author, e('#%d', $event_data['task']['id']));
- case TaskModel::EVENT_UPDATE:
- return e('%s updated the task #%d', $event_author, $event_data['task']['id']);
- case TaskModel::EVENT_CREATE:
- return e('%s created the task #%d', $event_author, $event_data['task']['id']);
- case TaskModel::EVENT_CLOSE:
- return e('%s closed the task #%d', $event_author, $event_data['task']['id']);
- case TaskModel::EVENT_OPEN:
- return e('%s open the task #%d', $event_author, $event_data['task']['id']);
- case TaskModel::EVENT_MOVE_COLUMN:
- return e(
- '%s moved the task #%d to the column "%s"',
- $event_author,
- $event_data['task']['id'],
- $event_data['task']['column_title']
- );
- case TaskModel::EVENT_MOVE_POSITION:
- return e(
- '%s moved the task #%d to the position %d in the column "%s"',
- $event_author,
- $event_data['task']['id'],
- $event_data['task']['position'],
- $event_data['task']['column_title']
- );
- case TaskModel::EVENT_MOVE_SWIMLANE:
- if ($event_data['task']['swimlane_id'] == 0) {
- return e('%s moved the task #%d to the first swimlane', $event_author, $event_data['task']['id']);
- }
-
- return e(
- '%s moved the task #%d to the swimlane "%s"',
- $event_author,
- $event_data['task']['id'],
- $event_data['task']['swimlane_name']
- );
- case SubtaskModel::EVENT_UPDATE:
- return e('%s updated a subtask for the task #%d', $event_author, $event_data['task']['id']);
- case SubtaskModel::EVENT_CREATE:
- return e('%s created a subtask for the task #%d', $event_author, $event_data['task']['id']);
- case CommentModel::EVENT_UPDATE:
- return e('%s updated a comment on the task #%d', $event_author, $event_data['task']['id']);
- case CommentModel::EVENT_CREATE:
- return e('%s commented on the task #%d', $event_author, $event_data['task']['id']);
- case TaskFileModel::EVENT_CREATE:
- return e('%s attached a file to the task #%d', $event_author, $event_data['task']['id']);
- case TaskModel::EVENT_USER_MENTION:
- return e('%s mentioned you in the task #%d', $event_author, $event_data['task']['id']);
- case CommentModel::EVENT_USER_MENTION:
- return e('%s mentioned you in a comment on the task #%d', $event_author, $event_data['task']['id']);
- default:
- return e('Notification');
+ if ($title !== '') {
+ return $title;
+ }
}
+
+ return e('Notification');
}
/**
* Get the event title without author
*
* @access public
- * @param string $event_name
- * @param array $event_data
+ * @param string $eventName
+ * @param array $eventData
* @return string
*/
- public function getTitleWithoutAuthor($event_name, array $event_data)
+ public function getTitleWithoutAuthor($eventName, array $eventData)
{
- switch ($event_name) {
- case TaskFileModel::EVENT_CREATE:
- return e('New attachment on task #%d: %s', $event_data['file']['task_id'], $event_data['file']['name']);
- case CommentModel::EVENT_CREATE:
- return e('New comment on task #%d', $event_data['comment']['task_id']);
- case CommentModel::EVENT_UPDATE:
- return e('Comment updated on task #%d', $event_data['comment']['task_id']);
- case SubtaskModel::EVENT_CREATE:
- return e('New subtask on task #%d', $event_data['subtask']['task_id']);
- case SubtaskModel::EVENT_UPDATE:
- return e('Subtask updated on task #%d', $event_data['subtask']['task_id']);
- case TaskModel::EVENT_CREATE:
- return e('New task #%d: %s', $event_data['task']['id'], $event_data['task']['title']);
- case TaskModel::EVENT_UPDATE:
- return e('Task updated #%d', $event_data['task']['id']);
- case TaskModel::EVENT_CLOSE:
- return e('Task #%d closed', $event_data['task']['id']);
- case TaskModel::EVENT_OPEN:
- return e('Task #%d opened', $event_data['task']['id']);
- case TaskModel::EVENT_MOVE_COLUMN:
- return e('Column changed for task #%d', $event_data['task']['id']);
- case TaskModel::EVENT_MOVE_POSITION:
- return e('New position for task #%d', $event_data['task']['id']);
- case TaskModel::EVENT_MOVE_SWIMLANE:
- return e('Swimlane changed for task #%d', $event_data['task']['id']);
- case TaskModel::EVENT_ASSIGNEE_CHANGE:
- return e('Assignee changed on task #%d', $event_data['task']['id']);
- case TaskModel::EVENT_OVERDUE:
- $nb = count($event_data['tasks']);
- return $nb > 1 ? e('%d overdue tasks', $nb) : e('Task #%d is overdue', $event_data['tasks'][0]['id']);
- case TaskModel::EVENT_USER_MENTION:
- return e('You were mentioned in the task #%d', $event_data['task']['id']);
- case CommentModel::EVENT_USER_MENTION:
- return e('You were mentioned in a comment on the task #%d', $event_data['task']['id']);
- default:
- return e('Notification');
+ foreach ($this->getIteratorBuilder() as $builder) {
+ $title = $builder->buildTitleWithoutAuthor($eventName, $eventData);
+
+ if ($title !== '') {
+ return $title;
+ }
}
+
+ return e('Notification');
+ }
+
+ /**
+ * Get task id from event
+ *
+ * @access public
+ * @param string $eventName
+ * @param array $eventData
+ * @return integer
+ */
+ public function getTaskIdFromEvent($eventName, array $eventData)
+ {
+ if ($eventName === TaskModel::EVENT_OVERDUE) {
+ return $eventData['tasks'][0]['id'];
+ }
+
+ return isset($eventData['task']['id']) ? $eventData['task']['id'] : 0;
+ }
+
+ /**
+ * Get iterator builder
+ *
+ * @access protected
+ * @return EventIteratorBuilder
+ */
+ protected function getIteratorBuilder()
+ {
+ $iterator = new EventIteratorBuilder();
+ $iterator
+ ->withBuilder(TaskEventBuilder::getInstance($this->container))
+ ->withBuilder(CommentEventBuilder::getInstance($this->container))
+ ->withBuilder(SubtaskEventBuilder::getInstance($this->container))
+ ->withBuilder(TaskFileEventBuilder::getInstance($this->container))
+ ->withBuilder(TaskLinkEventBuilder::getInstance($this->container))
+ ;
+
+ return $iterator;
}
}
diff --git a/app/Model/ProjectDuplicationModel.php b/app/Model/ProjectDuplicationModel.php
index b67f8302..d32fa367 100644
--- a/app/Model/ProjectDuplicationModel.php
+++ b/app/Model/ProjectDuplicationModel.php
@@ -22,7 +22,15 @@ class ProjectDuplicationModel extends Base
*/
public function getOptionalSelection()
{
- return array('categoryModel', 'projectPermissionModel', 'actionModel', 'swimlaneModel', 'taskModel', 'projectMetadataModel');
+ return array(
+ 'categoryModel',
+ 'projectPermissionModel',
+ 'actionModel',
+ 'swimlaneModel',
+ 'tagDuplicationModel',
+ 'projectMetadataModel',
+ 'projectTaskDuplicationModel',
+ );
}
/**
@@ -33,7 +41,16 @@ class ProjectDuplicationModel extends Base
*/
public function getPossibleSelection()
{
- return array('boardModel', 'categoryModel', 'projectPermissionModel', 'actionModel', 'swimlaneModel', 'taskModel', 'projectMetadataModel');
+ return array(
+ 'boardModel',
+ 'categoryModel',
+ 'projectPermissionModel',
+ 'actionModel',
+ 'swimlaneModel',
+ 'tagDuplicationModel',
+ 'projectMetadataModel',
+ 'projectTaskDuplicationModel',
+ );
}
/**
@@ -129,6 +146,9 @@ class ProjectDuplicationModel extends Base
'is_public' => 0,
'is_private' => $private ? 1 : $is_private,
'owner_id' => $owner_id,
+ 'priority_default' => $project['priority_default'],
+ 'priority_start' => $project['priority_start'],
+ 'priority_end' => $project['priority_end'],
);
if (! $this->db->table(ProjectModel::TABLE)->save($values)) {
@@ -139,7 +159,7 @@ class ProjectDuplicationModel extends Base
}
/**
- * Make sure that the creator of the duplicated project is alsp owner
+ * Make sure that the creator of the duplicated project is also owner
*
* @access private
* @param integer $dst_project_id
diff --git a/app/Model/ProjectFileModel.php b/app/Model/ProjectFileModel.php
index b464bb2a..4de4d66d 100644
--- a/app/Model/ProjectFileModel.php
+++ b/app/Model/ProjectFileModel.php
@@ -61,14 +61,13 @@ class ProjectFileModel extends FileModel
}
/**
- * Get event name
+ * Fire file creation event
*
- * @abstract
* @access protected
- * @return string
+ * @param integer $file_id
*/
- protected function getEventName()
+ protected function fireCreationEvent($file_id)
{
- return self::EVENT_CREATE;
+ $this->queueManager->push($this->projectFileEventJob->withParams($file_id, self::EVENT_CREATE));
}
}
diff --git a/app/Model/ProjectModel.php b/app/Model/ProjectModel.php
index 7382537e..d2019b72 100644
--- a/app/Model/ProjectModel.php
+++ b/app/Model/ProjectModel.php
@@ -246,19 +246,6 @@ class ProjectModel extends Base
}
/**
- * Get Priority range from a project
- *
- * @access public
- * @param array $project
- * @return array
- */
- public function getPriorities(array $project)
- {
- $range = range($project['priority_start'], $project['priority_end']);
- return array_combine($range, $range);
- }
-
- /**
* Gather some task metrics for a given project
*
* @access public
@@ -331,7 +318,7 @@ class ProjectModel extends Base
public function getQueryColumnStats(array $project_ids)
{
if (empty($project_ids)) {
- return $this->db->table(ProjectModel::TABLE)->limit(0);
+ return $this->db->table(ProjectModel::TABLE)->eq(ProjectModel::TABLE.'.id', 0);
}
return $this->db
diff --git a/app/Model/ProjectPermissionModel.php b/app/Model/ProjectPermissionModel.php
index a7c1857c..4882343d 100644
--- a/app/Model/ProjectPermissionModel.php
+++ b/app/Model/ProjectPermissionModel.php
@@ -152,6 +152,18 @@ class ProjectPermissionModel extends Base
}
/**
+ * Get all project ids by user
+ *
+ * @access public
+ * @param integer $user_id
+ * @return array
+ */
+ public function getProjectIds($user_id)
+ {
+ return array_keys($this->projectUserRoleModel->getProjectsByUser($user_id));
+ }
+
+ /**
* Copy permissions to another project
*
* @param integer $project_src_id Project Template
diff --git a/app/Model/ProjectTaskDuplicationModel.php b/app/Model/ProjectTaskDuplicationModel.php
new file mode 100644
index 00000000..5d2e1322
--- /dev/null
+++ b/app/Model/ProjectTaskDuplicationModel.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Project Task Duplication Model
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class ProjectTaskDuplicationModel extends Base
+{
+ /**
+ * Duplicate all tasks to another project
+ *
+ * @access public
+ * @param integer $src_project_id
+ * @param integer $dst_project_id
+ * @return boolean
+ */
+ public function duplicate($src_project_id, $dst_project_id)
+ {
+ $task_ids = $this->taskFinderModel->getAllIds($src_project_id, array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED));
+
+ foreach ($task_ids as $task_id) {
+ if (! $this->taskProjectDuplicationModel->duplicateToProject($task_id, $dst_project_id)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/app/Model/ProjectTaskPriorityModel.php b/app/Model/ProjectTaskPriorityModel.php
new file mode 100644
index 00000000..c1a0257a
--- /dev/null
+++ b/app/Model/ProjectTaskPriorityModel.php
@@ -0,0 +1,74 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Project Task Priority Model
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class ProjectTaskPriorityModel extends Base
+{
+ /**
+ * Get Priority range from a project
+ *
+ * @access public
+ * @param array $project
+ * @return array
+ */
+ public function getPriorities(array $project)
+ {
+ $range = range($project['priority_start'], $project['priority_end']);
+ return array_combine($range, $range);
+ }
+
+ /**
+ * Get task priority settings
+ *
+ * @access public
+ * @param int $project_id
+ * @return array|null
+ */
+ public function getPrioritySettings($project_id)
+ {
+ return $this->db
+ ->table(ProjectModel::TABLE)
+ ->columns('priority_default', 'priority_start', 'priority_end')
+ ->eq('id', $project_id)
+ ->findOne();
+ }
+
+ /**
+ * Get default task priority
+ *
+ * @access public
+ * @param int $project_id
+ * @return int
+ */
+ public function getDefaultPriority($project_id)
+ {
+ return $this->db->table(ProjectModel::TABLE)->eq('id', $project_id)->findOneColumn('priority_default') ?: 0;
+ }
+
+ /**
+ * Get priority for a destination project
+ *
+ * @access public
+ * @param integer $dst_project_id
+ * @param integer $priority
+ * @return integer
+ */
+ public function getPriorityForProject($dst_project_id, $priority)
+ {
+ $settings = $this->getPrioritySettings($dst_project_id);
+
+ if ($priority >= $settings['priority_start'] && $priority <= $settings['priority_end']) {
+ return $priority;
+ }
+
+ return $settings['priority_default'];
+ }
+}
diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php
index 019064ad..608ffce7 100644
--- a/app/Model/SubtaskModel.php
+++ b/app/Model/SubtaskModel.php
@@ -4,7 +4,6 @@ namespace Kanboard\Model;
use PicoDb\Database;
use Kanboard\Core\Base;
-use Kanboard\Event\SubtaskEvent;
/**
* Subtask Model
@@ -22,25 +21,13 @@ class SubtaskModel extends Base
const TABLE = 'subtasks';
/**
- * Task "done" status
- *
- * @var integer
- */
- const STATUS_DONE = 2;
-
- /**
- * Task "in progress" status
- *
- * @var integer
- */
- const STATUS_INPROGRESS = 1;
-
- /**
- * Task "todo" status
+ * Subtask status
*
* @var integer
*/
const STATUS_TODO = 0;
+ const STATUS_INPROGRESS = 1;
+ const STATUS_DONE = 2;
/**
* Events
@@ -52,6 +39,22 @@ class SubtaskModel extends Base
const EVENT_DELETE = 'subtask.delete';
/**
+ * Get projectId from subtaskId
+ *
+ * @access public
+ * @param integer $subtask_id
+ * @return integer
+ */
+ public function getProjectId($subtask_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->eq(self::TABLE.'.id', $subtask_id)
+ ->join(TaskModel::TABLE, 'id', 'task_id')
+ ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
+ }
+
+ /**
* Get available status
*
* @access public
@@ -67,26 +70,6 @@ class SubtaskModel extends Base
}
/**
- * Add subtask status status to the resultset
- *
- * @access public
- * @param array $subtasks Subtasks
- * @return array
- */
- public function addStatusName(array $subtasks)
- {
- $status = $this->getStatusList();
-
- foreach ($subtasks as &$subtask) {
- $subtask['status_name'] = $status[$subtask['status']];
- $subtask['timer_start_date'] = isset($subtask['timer_start_date']) ? $subtask['timer_start_date'] : 0;
- $subtask['is_timer_started'] = ! empty($subtask['timer_start_date']);
- }
-
- return $subtasks;
- }
-
- /**
* Get the query to fetch subtasks assigned to a user
*
* @access public
@@ -162,35 +145,6 @@ class SubtaskModel extends Base
}
/**
- * Prepare data before insert/update
- *
- * @access public
- * @param array $values Form values
- */
- public function prepare(array &$values)
- {
- $this->helper->model->removeFields($values, array('another_subtask'));
- $this->helper->model->resetFields($values, array('time_estimated', 'time_spent'));
- }
-
- /**
- * Prepare data before insert
- *
- * @access public
- * @param array $values Form values
- */
- public function prepareCreation(array &$values)
- {
- $this->prepare($values);
-
- $values['position'] = $this->getLastPosition($values['task_id']) + 1;
- $values['status'] = isset($values['status']) ? $values['status'] : self::STATUS_TODO;
- $values['time_estimated'] = isset($values['time_estimated']) ? $values['time_estimated'] : 0;
- $values['time_spent'] = isset($values['time_spent']) ? $values['time_spent'] : 0;
- $values['user_id'] = isset($values['user_id']) ? $values['user_id'] : 0;
- }
-
- /**
* Get the position of the last column for a given project
*
* @access public
@@ -219,10 +173,8 @@ class SubtaskModel extends Base
$subtask_id = $this->db->table(self::TABLE)->persist($values);
if ($subtask_id !== false) {
- $this->container['dispatcher']->dispatch(
- self::EVENT_CREATE,
- new SubtaskEvent(array('id' => $subtask_id) + $values)
- );
+ $this->subtaskTimeTrackingModel->updateTaskTimeTracking($values['task_id']);
+ $this->queueManager->push($this->subtaskEventJob->withParams($subtask_id, self::EVENT_CREATE));
}
return $subtask_id;
@@ -232,124 +184,24 @@ class SubtaskModel extends Base
* Update
*
* @access public
- * @param array $values Form values
- * @param bool $fire_events If true, will be called an event
+ * @param array $values
+ * @param bool $fire_event
* @return bool
*/
- public function update(array $values, $fire_events = true)
+ public function update(array $values, $fire_event = true)
{
$this->prepare($values);
- $subtask = $this->getById($values['id']);
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
- if ($result && $fire_events) {
- $event = $subtask;
- $event['changes'] = array_diff_assoc($values, $subtask);
- $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new SubtaskEvent($event));
- }
-
- return $result;
- }
-
- /**
- * Close all subtasks of a task
- *
- * @access public
- * @param integer $task_id
- * @return boolean
- */
- public function closeAll($task_id)
- {
- return $this->db->table(self::TABLE)->eq('task_id', $task_id)->update(array('status' => self::STATUS_DONE));
- }
-
- /**
- * Save subtask position
- *
- * @access public
- * @param integer $task_id
- * @param integer $subtask_id
- * @param integer $position
- * @return boolean
- */
- public function changePosition($task_id, $subtask_id, $position)
- {
- if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('task_id', $task_id)->count()) {
- return false;
- }
-
- $subtask_ids = $this->db->table(self::TABLE)->eq('task_id', $task_id)->neq('id', $subtask_id)->asc('position')->findAllByColumn('id');
- $offset = 1;
- $results = array();
+ if ($result) {
+ $this->subtaskTimeTrackingModel->updateTaskTimeTracking($values['task_id']);
- foreach ($subtask_ids as $current_subtask_id) {
- if ($offset == $position) {
- $offset++;
+ if ($fire_event) {
+ $this->queueManager->push($this->subtaskEventJob->withParams($values['id'], self::EVENT_UPDATE, $values));
}
-
- $results[] = $this->db->table(self::TABLE)->eq('id', $current_subtask_id)->update(array('position' => $offset));
- $offset++;
- }
-
- $results[] = $this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
-
- return !in_array(false, $results, true);
- }
-
- /**
- * Change the status of subtask
- *
- * @access public
- * @param integer $subtask_id
- * @return boolean|integer
- */
- public function toggleStatus($subtask_id)
- {
- $subtask = $this->getById($subtask_id);
- $status = ($subtask['status'] + 1) % 3;
-
- $values = array(
- 'id' => $subtask['id'],
- 'status' => $status,
- 'task_id' => $subtask['task_id'],
- );
-
- if (empty($subtask['user_id']) && $this->userSession->isLogged()) {
- $values['user_id'] = $this->userSession->getId();
}
- return $this->update($values) ? $status : false;
- }
-
- /**
- * Get the subtask in progress for this user
- *
- * @access public
- * @param integer $user_id
- * @return array
- */
- public function getSubtaskInProgress($user_id)
- {
- return $this->db->table(self::TABLE)
- ->eq('status', self::STATUS_INPROGRESS)
- ->eq('user_id', $user_id)
- ->findOne();
- }
-
- /**
- * Return true if the user have a subtask in progress
- *
- * @access public
- * @param integer $user_id
- * @return boolean
- */
- public function hasSubtaskInProgress($user_id)
- {
- return $this->configModel->get('subtask_restriction') == 1 &&
- $this->db->table(self::TABLE)
- ->eq('status', self::STATUS_INPROGRESS)
- ->eq('user_id', $user_id)
- ->exists();
+ return $result;
}
/**
@@ -361,14 +213,8 @@ class SubtaskModel extends Base
*/
public function remove($subtask_id)
{
- $subtask = $this->getById($subtask_id);
- $result = $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove();
-
- if ($result) {
- $this->container['dispatcher']->dispatch(self::EVENT_DELETE, new SubtaskEvent($subtask));
- }
-
- return $result;
+ $this->subtaskEventJob->execute($subtask_id, self::EVENT_DELETE);
+ return $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove();
}
/**
@@ -400,29 +246,51 @@ class SubtaskModel extends Base
}
/**
- * Convert a subtask to a task
+ * Prepare data before insert/update
*
- * @access public
- * @param integer $project_id
- * @param integer $subtask_id
- * @return integer
+ * @access protected
+ * @param array $values Form values
*/
- public function convertToTask($project_id, $subtask_id)
+ protected function prepare(array &$values)
{
- $subtask = $this->getById($subtask_id);
+ $this->helper->model->removeFields($values, array('another_subtask'));
+ $this->helper->model->resetFields($values, array('time_estimated', 'time_spent'));
+ }
- $task_id = $this->taskCreationModel->create(array(
- 'project_id' => $project_id,
- 'title' => $subtask['title'],
- 'time_estimated' => $subtask['time_estimated'],
- 'time_spent' => $subtask['time_spent'],
- 'owner_id' => $subtask['user_id'],
- ));
+ /**
+ * Prepare data before insert
+ *
+ * @access protected
+ * @param array $values Form values
+ */
+ protected function prepareCreation(array &$values)
+ {
+ $this->prepare($values);
- if ($task_id !== false) {
- $this->remove($subtask_id);
+ $values['position'] = $this->getLastPosition($values['task_id']) + 1;
+ $values['status'] = isset($values['status']) ? $values['status'] : self::STATUS_TODO;
+ $values['time_estimated'] = isset($values['time_estimated']) ? $values['time_estimated'] : 0;
+ $values['time_spent'] = isset($values['time_spent']) ? $values['time_spent'] : 0;
+ $values['user_id'] = isset($values['user_id']) ? $values['user_id'] : 0;
+ }
+
+ /**
+ * Add subtask status status to the resultset
+ *
+ * @access public
+ * @param array $subtasks Subtasks
+ * @return array
+ */
+ public function addStatusName(array $subtasks)
+ {
+ $status = $this->getStatusList();
+
+ foreach ($subtasks as &$subtask) {
+ $subtask['status_name'] = $status[$subtask['status']];
+ $subtask['timer_start_date'] = isset($subtask['timer_start_date']) ? $subtask['timer_start_date'] : 0;
+ $subtask['is_timer_started'] = ! empty($subtask['timer_start_date']);
}
- return $task_id;
+ return $subtasks;
}
}
diff --git a/app/Model/SubtaskPositionModel.php b/app/Model/SubtaskPositionModel.php
new file mode 100644
index 00000000..3c26465d
--- /dev/null
+++ b/app/Model/SubtaskPositionModel.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class SubtaskPositionModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class SubtaskPositionModel extends Base
+{
+ /**
+ * Change subtask position
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $subtask_id
+ * @param integer $position
+ * @return boolean
+ */
+ public function changePosition($task_id, $subtask_id, $position)
+ {
+ if ($position < 1 || $position > $this->db->table(SubtaskModel::TABLE)->eq('task_id', $task_id)->count()) {
+ return false;
+ }
+
+ $subtask_ids = $this->db->table(SubtaskModel::TABLE)->eq('task_id', $task_id)->neq('id', $subtask_id)->asc('position')->findAllByColumn('id');
+ $offset = 1;
+ $results = array();
+
+ foreach ($subtask_ids as $current_subtask_id) {
+ if ($offset == $position) {
+ $offset++;
+ }
+
+ $results[] = $this->db->table(SubtaskModel::TABLE)->eq('id', $current_subtask_id)->update(array('position' => $offset));
+ $offset++;
+ }
+
+ $results[] = $this->db->table(SubtaskModel::TABLE)->eq('id', $subtask_id)->update(array('position' => $position));
+
+ return !in_array(false, $results, true);
+ }
+}
diff --git a/app/Model/SubtaskStatusModel.php b/app/Model/SubtaskStatusModel.php
new file mode 100644
index 00000000..c99d6055
--- /dev/null
+++ b/app/Model/SubtaskStatusModel.php
@@ -0,0 +1,88 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class SubtaskStatusModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class SubtaskStatusModel extends Base
+{
+ /**
+ * Get the subtask in progress for this user
+ *
+ * @access public
+ * @param integer $user_id
+ * @return array
+ */
+ public function getSubtaskInProgress($user_id)
+ {
+ return $this->db->table(SubtaskModel::TABLE)
+ ->eq('status', SubtaskModel::STATUS_INPROGRESS)
+ ->eq('user_id', $user_id)
+ ->findOne();
+ }
+
+ /**
+ * Return true if the user have a subtask in progress
+ *
+ * @access public
+ * @param integer $user_id
+ * @return boolean
+ */
+ public function hasSubtaskInProgress($user_id)
+ {
+ return $this->configModel->get('subtask_restriction') == 1 &&
+ $this->db->table(SubtaskModel::TABLE)
+ ->eq('status', SubtaskModel::STATUS_INPROGRESS)
+ ->eq('user_id', $user_id)
+ ->exists();
+ }
+
+ /**
+ * Change the status of subtask
+ *
+ * @access public
+ * @param integer $subtask_id
+ * @return boolean|integer
+ */
+ public function toggleStatus($subtask_id)
+ {
+ $subtask = $this->subtaskModel->getById($subtask_id);
+ $status = ($subtask['status'] + 1) % 3;
+
+ $values = array(
+ 'id' => $subtask['id'],
+ 'status' => $status,
+ 'task_id' => $subtask['task_id'],
+ );
+
+ if (empty($subtask['user_id']) && $this->userSession->isLogged()) {
+ $values['user_id'] = $this->userSession->getId();
+ $subtask['user_id'] = $values['user_id'];
+ }
+
+ $this->subtaskTimeTrackingModel->toggleTimer($subtask_id, $subtask['user_id'], $status);
+
+ return $this->subtaskModel->update($values) ? $status : false;
+ }
+
+ /**
+ * Close all subtasks of a task
+ *
+ * @access public
+ * @param integer $task_id
+ * @return boolean
+ */
+ public function closeAll($task_id)
+ {
+ return $this->db
+ ->table(SubtaskModel::TABLE)
+ ->eq('task_id', $task_id)
+ ->update(array('status' => SubtaskModel::STATUS_DONE));
+ }
+}
diff --git a/app/Model/SubtaskTaskConversionModel.php b/app/Model/SubtaskTaskConversionModel.php
new file mode 100644
index 00000000..8bf83d76
--- /dev/null
+++ b/app/Model/SubtaskTaskConversionModel.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class SubtaskTaskConversionModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class SubtaskTaskConversionModel extends Base
+{
+ /**
+ * Convert a subtask to a task
+ *
+ * @access public
+ * @param integer $project_id
+ * @param integer $subtask_id
+ * @return integer
+ */
+ public function convertToTask($project_id, $subtask_id)
+ {
+ $subtask = $this->subtaskModel->getById($subtask_id);
+
+ $task_id = $this->taskCreationModel->create(array(
+ 'project_id' => $project_id,
+ 'title' => $subtask['title'],
+ 'time_estimated' => $subtask['time_estimated'],
+ 'time_spent' => $subtask['time_spent'],
+ 'owner_id' => $subtask['user_id'],
+ ));
+
+ if ($task_id !== false) {
+ $this->subtaskModel->remove($subtask_id);
+ }
+
+ return $task_id;
+ }
+}
diff --git a/app/Model/SubtaskTimeTrackingModel.php b/app/Model/SubtaskTimeTrackingModel.php
index 062e594a..3b1b97e4 100644
--- a/app/Model/SubtaskTimeTrackingModel.php
+++ b/app/Model/SubtaskTimeTrackingModel.php
@@ -160,6 +160,28 @@ class SubtaskTimeTrackingModel extends Base
}
/**
+ * Start or stop timer according to subtask status
+ *
+ * @access public
+ * @param integer $subtask_id
+ * @param integer $user_id
+ * @param integer $status
+ * @return boolean
+ */
+ public function toggleTimer($subtask_id, $user_id, $status)
+ {
+ if ($this->configModel->get('subtask_time_tracking') == 1) {
+ if ($status == SubtaskModel::STATUS_INPROGRESS) {
+ return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id);
+ } elseif ($status == SubtaskModel::STATUS_DONE) {
+ return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id);
+ }
+ }
+
+ return false;
+ }
+
+ /**
* Log start time
*
* @access public
@@ -252,7 +274,6 @@ class SubtaskTimeTrackingModel extends Base
{
$subtask = $this->subtaskModel->getById($subtask_id);
- // Fire the event subtask.update
return $this->subtaskModel->update(array(
'id' => $subtask['id'],
'time_spent' => $subtask['time_spent'] + $time_spent,
diff --git a/app/Model/SwimlaneModel.php b/app/Model/SwimlaneModel.php
index 35e39879..f20bfa2f 100644
--- a/app/Model/SwimlaneModel.php
+++ b/app/Model/SwimlaneModel.php
@@ -94,15 +94,17 @@ class SwimlaneModel extends Base
*
* @access public
* @param integer $project_id
- * @return array
+ * @return array|null
*/
public function getFirstActiveSwimlane($project_id)
{
- return $this->db->table(self::TABLE)
- ->eq('is_active', self::ACTIVE)
- ->eq('project_id', $project_id)
- ->orderBy('position', 'asc')
- ->findOne();
+ $swimlanes = $this->getSwimlanes($project_id);
+
+ if (empty($swimlanes)) {
+ return null;
+ }
+
+ return $swimlanes[0];
}
/**
@@ -184,18 +186,18 @@ class SwimlaneModel extends Base
->orderBy('position', 'asc')
->findAll();
- $default_swimlane = $this->db
+ $defaultSwimlane = $this->db
->table(ProjectModel::TABLE)
->eq('id', $project_id)
->eq('show_default_swimlane', 1)
->findOneColumn('default_swimlane');
- if ($default_swimlane) {
- if ($default_swimlane === 'Default swimlane') {
- $default_swimlane = t($default_swimlane);
+ if ($defaultSwimlane) {
+ if ($defaultSwimlane === 'Default swimlane') {
+ $defaultSwimlane = t($defaultSwimlane);
}
- array_unshift($swimlanes, array('id' => 0, 'name' => $default_swimlane));
+ array_unshift($swimlanes, array('id' => 0, 'name' => $defaultSwimlane));
}
return $swimlanes;
diff --git a/app/Model/TagDuplicationModel.php b/app/Model/TagDuplicationModel.php
new file mode 100644
index 00000000..fb0d8170
--- /dev/null
+++ b/app/Model/TagDuplicationModel.php
@@ -0,0 +1,87 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Tag Duplication
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class TagDuplicationModel extends Base
+{
+ /**
+ * Duplicate project tags to another project
+ *
+ * @access public
+ * @param integer $src_project_id
+ * @param integer $dst_project_id
+ * @return bool
+ */
+ public function duplicate($src_project_id, $dst_project_id)
+ {
+ $tags = $this->tagModel->getAllByProject($src_project_id);
+ $results = array();
+
+ foreach ($tags as $tag) {
+ $results[] = $this->tagModel->create($dst_project_id, $tag['name']);
+ }
+
+ return ! in_array(false, $results, true);
+ }
+
+ /**
+ * Link tags to the new tasks
+ *
+ * @access public
+ * @param integer $src_task_id
+ * @param integer $dst_task_id
+ * @param integer $dst_project_id
+ */
+ public function duplicateTaskTagsToAnotherProject($src_task_id, $dst_task_id, $dst_project_id)
+ {
+ $tags = $this->taskTagModel->getTagsByTask($src_task_id);
+
+ foreach ($tags as $tag) {
+ $tag_id = $this->tagModel->getIdByName($dst_project_id, $tag['name']);
+
+ if ($tag_id) {
+ $this->taskTagModel->associateTag($dst_task_id, $tag_id);
+ }
+ }
+ }
+
+ /**
+ * Duplicate tags to the new task
+ *
+ * @access public
+ * @param integer $src_task_id
+ * @param integer $dst_task_id
+ */
+ public function duplicateTaskTags($src_task_id, $dst_task_id)
+ {
+ $tags = $this->taskTagModel->getTagsByTask($src_task_id);
+
+ foreach ($tags as $tag) {
+ $this->taskTagModel->associateTag($dst_task_id, $tag['id']);
+ }
+ }
+
+ /**
+ * Remove tags that are not available in destination project
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $dst_project_id
+ */
+ public function syncTaskTagsToAnotherProject($task_id, $dst_project_id)
+ {
+ $tag_ids = $this->taskTagModel->getTagIdsByTaskNotAvailableInProject($task_id, $dst_project_id);
+
+ foreach ($tag_ids as $tag_id) {
+ $this->taskTagModel->dissociateTag($task_id, $tag_id);
+ }
+ }
+}
diff --git a/app/Model/TagModel.php b/app/Model/TagModel.php
new file mode 100644
index 00000000..e85c5a87
--- /dev/null
+++ b/app/Model/TagModel.php
@@ -0,0 +1,180 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class TagModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class TagModel extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'tags';
+
+ /**
+ * Get all tags
+ *
+ * @access public
+ * @return array
+ */
+ public function getAll()
+ {
+ return $this->db->table(self::TABLE)->asc('name')->findAll();
+ }
+
+ /**
+ * Get all tags by project
+ *
+ * @access public
+ * @param integer $project_id
+ * @return array
+ */
+ public function getAllByProject($project_id)
+ {
+ return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('name')->findAll();
+ }
+
+ /**
+ * Get assignable tags for a project
+ *
+ * @access public
+ * @param integer $project_id
+ * @return array
+ */
+ public function getAssignableList($project_id)
+ {
+ return $this->db->hashtable(self::TABLE)
+ ->beginOr()
+ ->eq('project_id', $project_id)
+ ->eq('project_id', 0)
+ ->closeOr()
+ ->asc('name')
+ ->getAll('id', 'name');
+ }
+
+ /**
+ * Get one tag
+ *
+ * @access public
+ * @param integer $tag_id
+ * @return array|null
+ */
+ public function getById($tag_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $tag_id)->findOne();
+ }
+
+ /**
+ * Get tag id from tag name
+ *
+ * @access public
+ * @param int $project_id
+ * @param string $tag
+ * @return integer
+ */
+ public function getIdByName($project_id, $tag)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->beginOr()
+ ->eq('project_id', 0)
+ ->eq('project_id', $project_id)
+ ->closeOr()
+ ->ilike('name', $tag)
+ ->asc('project_id')
+ ->findOneColumn('id');
+ }
+
+ /**
+ * Return true if the tag exists
+ *
+ * @access public
+ * @param integer $project_id
+ * @param string $tag
+ * @param integer $tag_id
+ * @return boolean
+ */
+ public function exists($project_id, $tag, $tag_id = 0)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->neq('id', $tag_id)
+ ->beginOr()
+ ->eq('project_id', 0)
+ ->eq('project_id', $project_id)
+ ->closeOr()
+ ->ilike('name', $tag)
+ ->asc('project_id')
+ ->exists();
+ }
+
+ /**
+ * Return tag id and create a new tag if necessary
+ *
+ * @access public
+ * @param int $project_id
+ * @param string $tag
+ * @return bool|int
+ */
+ public function findOrCreateTag($project_id, $tag)
+ {
+ $tag_id = $this->getIdByName($project_id, $tag);
+
+ if (empty($tag_id)) {
+ $tag_id = $this->create($project_id, $tag);
+ }
+
+ return $tag_id;
+ }
+
+ /**
+ * Add a new tag
+ *
+ * @access public
+ * @param int $project_id
+ * @param string $tag
+ * @return bool|int
+ */
+ public function create($project_id, $tag)
+ {
+ return $this->db->table(self::TABLE)->persist(array(
+ 'project_id' => $project_id,
+ 'name' => $tag,
+ ));
+ }
+
+ /**
+ * Update a tag
+ *
+ * @access public
+ * @param integer $tag_id
+ * @param string $tag
+ * @return bool
+ */
+ public function update($tag_id, $tag)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $tag_id)->update(array(
+ 'name' => $tag,
+ ));
+ }
+
+ /**
+ * Remove a tag
+ *
+ * @access public
+ * @param integer $tag_id
+ * @return bool
+ */
+ public function remove($tag_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $tag_id)->remove();
+ }
+}
diff --git a/app/Model/TaskCreationModel.php b/app/Model/TaskCreationModel.php
index 3800f831..b9b07d5e 100644
--- a/app/Model/TaskCreationModel.php
+++ b/app/Model/TaskCreationModel.php
@@ -3,7 +3,6 @@
namespace Kanboard\Model;
use Kanboard\Core\Base;
-use Kanboard\Event\TaskEvent;
/**
* Task Creation
@@ -22,11 +21,13 @@ class TaskCreationModel extends Base
*/
public function create(array $values)
{
- if (! $this->projectModel->exists($values['project_id'])) {
- return 0;
- }
-
$position = empty($values['position']) ? 0 : $values['position'];
+ $tags = array();
+
+ if (isset($values['tags'])) {
+ $tags = $values['tags'];
+ unset($values['tags']);
+ }
$this->prepare($values);
$task_id = $this->db->table(TaskModel::TABLE)->persist($values);
@@ -36,7 +37,14 @@ class TaskCreationModel extends Base
$this->taskPositionModel->movePosition($values['project_id'], $task_id, $values['column_id'], $position, $values['swimlane_id'], false);
}
- $this->fireEvents($task_id, $values);
+ if (! empty($tags)) {
+ $this->taskTagModel->save($values['project_id'], $task_id, $tags);
+ }
+
+ $this->queueManager->push($this->taskEventJob->withParams(
+ $task_id,
+ array(TaskModel::EVENT_CREATE_UPDATE, TaskModel::EVENT_CREATE)
+ ));
}
return (int) $task_id;
@@ -45,16 +53,16 @@ class TaskCreationModel extends Base
/**
* Prepare data
*
- * @access public
+ * @access protected
* @param array $values Form values
*/
- public function prepare(array &$values)
+ protected function prepare(array &$values)
{
$values = $this->dateParser->convert($values, array('date_due'));
$values = $this->dateParser->convert($values, array('date_started'), true);
$this->helper->model->removeFields($values, array('another_task'));
- $this->helper->model->resetFields($values, array('date_started', 'creator_id', 'owner_id', 'swimlane_id', 'date_due', 'score', 'category_id', 'time_estimated'));
+ $this->helper->model->resetFields($values, array('creator_id', 'owner_id', 'swimlane_id', 'date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent'));
if (empty($values['column_id'])) {
$values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']);
@@ -77,27 +85,7 @@ class TaskCreationModel extends Base
$values['date_modification'] = $values['date_creation'];
$values['date_moved'] = $values['date_creation'];
$values['position'] = $this->taskFinderModel->countByColumnAndSwimlaneId($values['project_id'], $values['column_id'], $values['swimlane_id']) + 1;
- }
- /**
- * Fire events
- *
- * @access private
- * @param integer $task_id Task id
- * @param array $values Form values
- */
- private function fireEvents($task_id, array $values)
- {
- $event = new TaskEvent(array('task_id' => $task_id) + $values);
-
- $this->logger->debug('Event fired: '.TaskModel::EVENT_CREATE_UPDATE);
- $this->logger->debug('Event fired: '.TaskModel::EVENT_CREATE);
-
- $this->dispatcher->dispatch(TaskModel::EVENT_CREATE_UPDATE, $event);
- $this->dispatcher->dispatch(TaskModel::EVENT_CREATE, $event);
-
- if (! empty($values['description'])) {
- $this->userMentionModel->fireEvents($values['description'], TaskModel::EVENT_USER_MENTION, $event);
- }
+ $this->hook->reference('model:task:creation:prepare', $values);
}
}
diff --git a/app/Model/TaskDuplicationModel.php b/app/Model/TaskDuplicationModel.php
index 9a4613e2..c9079653 100644
--- a/app/Model/TaskDuplicationModel.php
+++ b/app/Model/TaskDuplicationModel.php
@@ -2,10 +2,7 @@
namespace Kanboard\Model;
-use DateTime;
-use DateInterval;
use Kanboard\Core\Base;
-use Kanboard\Event\TaskEvent;
/**
* Task Duplication
@@ -18,10 +15,10 @@ class TaskDuplicationModel extends Base
/**
* Fields to copy when duplicating a task
*
- * @access private
- * @var array
+ * @access protected
+ * @var string[]
*/
- private $fields_to_duplicate = array(
+ protected $fieldsToDuplicate = array(
'title',
'description',
'date_due',
@@ -30,6 +27,7 @@ class TaskDuplicationModel extends Base
'column_id',
'owner_id',
'score',
+ 'priority',
'category_id',
'time_estimated',
'swimlane_id',
@@ -49,106 +47,13 @@ class TaskDuplicationModel extends Base
*/
public function duplicate($task_id)
{
- return $this->save($task_id, $this->copyFields($task_id));
- }
+ $new_task_id = $this->save($task_id, $this->copyFields($task_id));
- /**
- * Duplicate recurring task
- *
- * @access public
- * @param integer $task_id Task id
- * @return boolean|integer Recurrence task id
- */
- public function duplicateRecurringTask($task_id)
- {
- $values = $this->copyFields($task_id);
-
- if ($values['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) {
- $values['recurrence_parent'] = $task_id;
- $values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']);
- $this->calculateRecurringTaskDueDate($values);
-
- $recurring_task_id = $this->save($task_id, $values);
-
- if ($recurring_task_id > 0) {
- $parent_update = $this->db
- ->table(TaskModel::TABLE)
- ->eq('id', $task_id)
- ->update(array(
- 'recurrence_status' => TaskModel::RECURRING_STATUS_PROCESSED,
- 'recurrence_child' => $recurring_task_id,
- ));
-
- if ($parent_update) {
- return $recurring_task_id;
- }
- }
+ if ($new_task_id !== false) {
+ $this->tagDuplicationModel->duplicateTaskTags($task_id, $new_task_id);
}
- return false;
- }
-
- /**
- * Duplicate a task to another project
- *
- * @access public
- * @param integer $task_id
- * @param integer $project_id
- * @param integer $swimlane_id
- * @param integer $column_id
- * @param integer $category_id
- * @param integer $owner_id
- * @return boolean|integer
- */
- public function duplicateToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
- {
- $values = $this->copyFields($task_id);
- $values['project_id'] = $project_id;
- $values['column_id'] = $column_id !== null ? $column_id : $values['column_id'];
- $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $values['swimlane_id'];
- $values['category_id'] = $category_id !== null ? $category_id : $values['category_id'];
- $values['owner_id'] = $owner_id !== null ? $owner_id : $values['owner_id'];
-
- $this->checkDestinationProjectValues($values);
-
- return $this->save($task_id, $values);
- }
-
- /**
- * Move a task to another project
- *
- * @access public
- * @param integer $task_id
- * @param integer $project_id
- * @param integer $swimlane_id
- * @param integer $column_id
- * @param integer $category_id
- * @param integer $owner_id
- * @return boolean
- */
- public function moveToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
- {
- $task = $this->taskFinderModel->getById($task_id);
-
- $values = array();
- $values['is_active'] = 1;
- $values['project_id'] = $project_id;
- $values['column_id'] = $column_id !== null ? $column_id : $task['column_id'];
- $values['position'] = $this->taskFinderModel->countByColumnId($project_id, $values['column_id']) + 1;
- $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $task['swimlane_id'];
- $values['category_id'] = $category_id !== null ? $category_id : $task['category_id'];
- $values['owner_id'] = $owner_id !== null ? $owner_id : $task['owner_id'];
-
- $this->checkDestinationProjectValues($values);
-
- if ($this->db->table(TaskModel::TABLE)->eq('id', $task['id'])->update($values)) {
- $this->container['dispatcher']->dispatch(
- TaskModel::EVENT_MOVE_PROJECT,
- new TaskEvent(array_merge($task, $values, array('task_id' => $task['id'])))
- );
- }
-
- return true;
+ return $new_task_id;
}
/**
@@ -191,58 +96,28 @@ class TaskDuplicationModel extends Base
$values['column_id'] = $values['column_id'] ?: $this->columnModel->getFirstColumnId($values['project_id']);
}
- return $values;
- }
+ // Check if priority exists for destination project
+ $values['priority'] = $this->projectTaskPriorityModel->getPriorityForProject(
+ $values['project_id'],
+ empty($values['priority']) ? 0 : $values['priority']
+ );
- /**
- * Calculate new due date for new recurrence task
- *
- * @access public
- * @param array $values Task fields
- */
- public function calculateRecurringTaskDueDate(array &$values)
- {
- if (! empty($values['date_due']) && $values['recurrence_factor'] != 0) {
- if ($values['recurrence_basedate'] == TaskModel::RECURRING_BASEDATE_TRIGGERDATE) {
- $values['date_due'] = time();
- }
-
- $factor = abs($values['recurrence_factor']);
- $subtract = $values['recurrence_factor'] < 0;
-
- switch ($values['recurrence_timeframe']) {
- case TaskModel::RECURRING_TIMEFRAME_MONTHS:
- $interval = 'P' . $factor . 'M';
- break;
- case TaskModel::RECURRING_TIMEFRAME_YEARS:
- $interval = 'P' . $factor . 'Y';
- break;
- default:
- $interval = 'P' . $factor . 'D';
- }
-
- $date_due = new DateTime();
- $date_due->setTimestamp($values['date_due']);
-
- $subtract ? $date_due->sub(new DateInterval($interval)) : $date_due->add(new DateInterval($interval));
-
- $values['date_due'] = $date_due->getTimestamp();
- }
+ return $values;
}
/**
* Duplicate fields for the new task
*
- * @access private
+ * @access protected
* @param integer $task_id Task id
* @return array
*/
- private function copyFields($task_id)
+ protected function copyFields($task_id)
{
$task = $this->taskFinderModel->getById($task_id);
$values = array();
- foreach ($this->fields_to_duplicate as $field) {
+ foreach ($this->fieldsToDuplicate as $field) {
$values[$field] = $task[$field];
}
@@ -252,16 +127,16 @@ class TaskDuplicationModel extends Base
/**
* Create the new task and duplicate subtasks
*
- * @access private
+ * @access protected
* @param integer $task_id Task id
* @param array $values Form values
* @return boolean|integer
*/
- private function save($task_id, array $values)
+ protected function save($task_id, array $values)
{
$new_task_id = $this->taskCreationModel->create($values);
- if ($new_task_id) {
+ if ($new_task_id !== false) {
$this->subtaskModel->duplicate($task_id, $new_task_id);
}
diff --git a/app/Model/TaskFileModel.php b/app/Model/TaskFileModel.php
index 24c1ad4b..0163da28 100644
--- a/app/Model/TaskFileModel.php
+++ b/app/Model/TaskFileModel.php
@@ -61,15 +61,19 @@ class TaskFileModel extends FileModel
}
/**
- * Get event name
+ * Get projectId from fileId
*
- * @abstract
- * @access protected
- * @return string
+ * @access public
+ * @param integer $file_id
+ * @return integer
*/
- protected function getEventName()
+ public function getProjectId($file_id)
{
- return self::EVENT_CREATE;
+ return $this->db
+ ->table(self::TABLE)
+ ->eq(self::TABLE.'.id', $file_id)
+ ->join(TaskModel::TABLE, 'id', 'task_id')
+ ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
}
/**
@@ -85,4 +89,15 @@ class TaskFileModel extends FileModel
$original_filename = e('Screenshot taken %s', $this->helper->dt->datetime(time())).'.png';
return $this->uploadContent($task_id, $original_filename, $blob);
}
+
+ /**
+ * Fire file creation event
+ *
+ * @access protected
+ * @param integer $file_id
+ */
+ protected function fireCreationEvent($file_id)
+ {
+ $this->queueManager->push($this->taskFileEventJob->withParams($file_id, self::EVENT_CREATE));
+ }
}
diff --git a/app/Model/TaskFinderModel.php b/app/Model/TaskFinderModel.php
index 8b636e28..3c32e140 100644
--- a/app/Model/TaskFinderModel.php
+++ b/app/Model/TaskFinderModel.php
@@ -2,7 +2,6 @@
namespace Kanboard\Model;
-use PDO;
use Kanboard\Core\Base;
/**
@@ -63,25 +62,26 @@ class TaskFinderModel extends Base
return $this->db
->table(TaskModel::TABLE)
->columns(
- 'tasks.id',
- 'tasks.title',
- 'tasks.date_due',
- 'tasks.date_creation',
- 'tasks.project_id',
- 'tasks.color_id',
- 'tasks.priority',
- 'tasks.time_spent',
- 'tasks.time_estimated',
- 'tasks.is_active',
- 'tasks.creator_id',
- 'projects.name AS project_name',
- 'columns.title AS column_title'
+ TaskModel::TABLE.'.id',
+ TaskModel::TABLE.'.title',
+ TaskModel::TABLE.'.date_due',
+ TaskModel::TABLE.'.date_creation',
+ TaskModel::TABLE.'.project_id',
+ TaskModel::TABLE.'.color_id',
+ TaskModel::TABLE.'.priority',
+ TaskModel::TABLE.'.time_spent',
+ TaskModel::TABLE.'.time_estimated',
+ TaskModel::TABLE.'.is_active',
+ TaskModel::TABLE.'.creator_id',
+ ProjectModel::TABLE.'.name AS project_name',
+ ColumnModel::TABLE.'.title AS column_title'
)
->join(ProjectModel::TABLE, 'id', 'project_id')
->join(ColumnModel::TABLE, 'id', 'column_id')
->eq(TaskModel::TABLE.'.owner_id', $user_id)
->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN)
- ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE);
+ ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE)
+ ->eq(ColumnModel::TABLE.'.hide_in_dashboard', 0);
}
/**
@@ -102,36 +102,36 @@ class TaskFinderModel extends Base
'(SELECT COUNT(*) FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id) AS nb_links',
'(SELECT COUNT(*) FROM '.TaskExternalLinkModel::TABLE.' WHERE '.TaskExternalLinkModel::TABLE.'.task_id = tasks.id) AS nb_external_links',
'(SELECT DISTINCT 1 FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id AND '.TaskLinkModel::TABLE.'.link_id = 9) AS is_milestone',
- 'tasks.id',
- 'tasks.reference',
- 'tasks.title',
- 'tasks.description',
- 'tasks.date_creation',
- 'tasks.date_modification',
- 'tasks.date_completed',
- 'tasks.date_started',
- 'tasks.date_due',
- 'tasks.color_id',
- 'tasks.project_id',
- 'tasks.column_id',
- 'tasks.swimlane_id',
- 'tasks.owner_id',
- 'tasks.creator_id',
- 'tasks.position',
- 'tasks.is_active',
- 'tasks.score',
- 'tasks.category_id',
- 'tasks.priority',
- 'tasks.date_moved',
- 'tasks.recurrence_status',
- 'tasks.recurrence_trigger',
- 'tasks.recurrence_factor',
- 'tasks.recurrence_timeframe',
- 'tasks.recurrence_basedate',
- 'tasks.recurrence_parent',
- 'tasks.recurrence_child',
- 'tasks.time_estimated',
- 'tasks.time_spent',
+ TaskModel::TABLE.'.id',
+ TaskModel::TABLE.'.reference',
+ TaskModel::TABLE.'.title',
+ TaskModel::TABLE.'.description',
+ TaskModel::TABLE.'.date_creation',
+ TaskModel::TABLE.'.date_modification',
+ TaskModel::TABLE.'.date_completed',
+ TaskModel::TABLE.'.date_started',
+ TaskModel::TABLE.'.date_due',
+ TaskModel::TABLE.'.color_id',
+ TaskModel::TABLE.'.project_id',
+ TaskModel::TABLE.'.column_id',
+ TaskModel::TABLE.'.swimlane_id',
+ TaskModel::TABLE.'.owner_id',
+ TaskModel::TABLE.'.creator_id',
+ TaskModel::TABLE.'.position',
+ TaskModel::TABLE.'.is_active',
+ TaskModel::TABLE.'.score',
+ TaskModel::TABLE.'.category_id',
+ TaskModel::TABLE.'.priority',
+ TaskModel::TABLE.'.date_moved',
+ TaskModel::TABLE.'.recurrence_status',
+ TaskModel::TABLE.'.recurrence_trigger',
+ TaskModel::TABLE.'.recurrence_factor',
+ TaskModel::TABLE.'.recurrence_timeframe',
+ TaskModel::TABLE.'.recurrence_basedate',
+ TaskModel::TABLE.'.recurrence_parent',
+ TaskModel::TABLE.'.recurrence_child',
+ TaskModel::TABLE.'.time_estimated',
+ TaskModel::TABLE.'.time_spent',
UserModel::TABLE.'.username AS assignee_username',
UserModel::TABLE.'.name AS assignee_name',
UserModel::TABLE.'.email AS assignee_email',
@@ -153,26 +153,6 @@ class TaskFinderModel extends Base
}
/**
- * Get all tasks shown on the board (sorted by position)
- *
- * @access public
- * @param integer $project_id Project id
- * @param integer $column_id Column id
- * @param integer $swimlane_id Swimlane id
- * @return array
- */
- public function getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id = 0)
- {
- return $this->getExtendedQuery()
- ->eq(TaskModel::TABLE.'.project_id', $project_id)
- ->eq(TaskModel::TABLE.'.column_id', $column_id)
- ->eq(TaskModel::TABLE.'.swimlane_id', $swimlane_id)
- ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN)
- ->asc(TaskModel::TABLE.'.position')
- ->findAll();
- }
-
- /**
* Get all tasks for a given project and status
*
* @access public
@@ -186,6 +166,7 @@ class TaskFinderModel extends Base
->table(TaskModel::TABLE)
->eq(TaskModel::TABLE.'.project_id', $project_id)
->eq(TaskModel::TABLE.'.is_active', $status_id)
+ ->asc(TaskModel::TABLE.'.id')
->findAll();
}
@@ -203,7 +184,8 @@ class TaskFinderModel extends Base
->table(TaskModel::TABLE)
->eq(TaskModel::TABLE.'.project_id', $project_id)
->in(TaskModel::TABLE.'.is_active', $status)
- ->findAllByColumn('id');
+ ->asc(TaskModel::TABLE.'.id')
+ ->findAllByColumn(TaskModel::TABLE.'.id');
}
/**
@@ -315,59 +297,30 @@ class TaskFinderModel extends Base
*/
public function getDetails($task_id)
{
- $sql = '
- SELECT
- tasks.id,
- tasks.reference,
- tasks.title,
- tasks.description,
- tasks.date_creation,
- tasks.date_completed,
- tasks.date_modification,
- tasks.date_due,
- tasks.date_started,
- tasks.time_estimated,
- tasks.time_spent,
- tasks.color_id,
- tasks.project_id,
- tasks.column_id,
- tasks.owner_id,
- tasks.creator_id,
- tasks.position,
- tasks.is_active,
- tasks.score,
- tasks.category_id,
- tasks.priority,
- tasks.swimlane_id,
- tasks.date_moved,
- tasks.recurrence_status,
- tasks.recurrence_trigger,
- tasks.recurrence_factor,
- tasks.recurrence_timeframe,
- tasks.recurrence_basedate,
- tasks.recurrence_parent,
- tasks.recurrence_child,
- project_has_categories.name AS category_name,
- swimlanes.name AS swimlane_name,
- projects.name AS project_name,
- projects.default_swimlane,
- columns.title AS column_title,
- users.username AS assignee_username,
- users.name AS assignee_name,
- creators.username AS creator_username,
- creators.name AS creator_name
- FROM tasks
- LEFT JOIN users ON users.id = tasks.owner_id
- LEFT JOIN users AS creators ON creators.id = tasks.creator_id
- LEFT JOIN project_has_categories ON project_has_categories.id = tasks.category_id
- LEFT JOIN projects ON projects.id = tasks.project_id
- LEFT JOIN columns ON columns.id = tasks.column_id
- LEFT JOIN swimlanes ON swimlanes.id = tasks.swimlane_id
- WHERE tasks.id = ?
- ';
-
- $rq = $this->db->execute($sql, array($task_id));
- return $rq->fetch(PDO::FETCH_ASSOC);
+ return $this->db->table(TaskModel::TABLE)
+ ->columns(
+ TaskModel::TABLE.'.*',
+ CategoryModel::TABLE.'.name AS category_name',
+ SwimlaneModel::TABLE.'.name AS swimlane_name',
+ ProjectModel::TABLE.'.name AS project_name',
+ ProjectModel::TABLE.'.default_swimlane',
+ ColumnModel::TABLE.'.title AS column_title',
+ UserModel::TABLE.'.username AS assignee_username',
+ UserModel::TABLE.'.name AS assignee_name',
+ 'uc.username AS creator_username',
+ 'uc.name AS creator_name',
+ CategoryModel::TABLE.'.description AS category_description',
+ ColumnModel::TABLE.'.position AS column_position',
+ ProjectModel::TABLE.'.default_swimlane'
+ )
+ ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE)
+ ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id')
+ ->join(CategoryModel::TABLE, 'id', 'category_id', TaskModel::TABLE)
+ ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE)
+ ->join(SwimlaneModel::TABLE, 'id', 'swimlane_id', TaskModel::TABLE)
+ ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE)
+ ->eq(TaskModel::TABLE.'.id', $task_id)
+ ->findOne();
}
/**
@@ -387,6 +340,7 @@ class TaskFinderModel extends Base
'ua.name AS assignee_name',
'ua.username AS assignee_username',
'uc.email AS creator_email',
+ 'uc.name AS creator_name',
'uc.username AS creator_username'
);
}
diff --git a/app/Model/TaskLinkModel.php b/app/Model/TaskLinkModel.php
index 45225e35..e8d3c5df 100644
--- a/app/Model/TaskLinkModel.php
+++ b/app/Model/TaskLinkModel.php
@@ -3,7 +3,6 @@
namespace Kanboard\Model;
use Kanboard\Core\Base;
-use Kanboard\Event\TaskLinkEvent;
/**
* TaskLink model
@@ -26,7 +25,24 @@ class TaskLinkModel extends Base
*
* @var string
*/
- const EVENT_CREATE_UPDATE = 'tasklink.create_update';
+ const EVENT_CREATE_UPDATE = 'task_internal_link.create_update';
+ const EVENT_DELETE = 'task_internal_link.delete';
+
+ /**
+ * Get projectId from $task_link_id
+ *
+ * @access public
+ * @param integer $task_link_id
+ * @return integer
+ */
+ public function getProjectId($task_link_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->eq(self::TABLE.'.id', $task_link_id)
+ ->join(TaskModel::TABLE, 'id', 'task_id')
+ ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
+ }
/**
* Get a task link
@@ -37,7 +53,19 @@ class TaskLinkModel extends Base
*/
public function getById($task_link_id)
{
- return $this->db->table(self::TABLE)->eq('id', $task_link_id)->findOne();
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.id',
+ self::TABLE.'.opposite_task_id',
+ self::TABLE.'.task_id',
+ self::TABLE.'.link_id',
+ LinkModel::TABLE.'.label',
+ LinkModel::TABLE.'.opposite_id AS opposite_link_id'
+ )
+ ->eq(self::TABLE.'.id', $task_link_id)
+ ->join(LinkModel::TABLE, 'id', 'link_id')
+ ->findOne();
}
/**
@@ -124,62 +152,31 @@ class TaskLinkModel extends Base
}
/**
- * Publish events
- *
- * @access private
- * @param array $events
- */
- private function fireEvents(array $events)
- {
- foreach ($events as $event) {
- $event['project_id'] = $this->taskFinderModel->getProjectId($event['task_id']);
- $this->container['dispatcher']->dispatch(self::EVENT_CREATE_UPDATE, new TaskLinkEvent($event));
- }
- }
-
- /**
* Create a new link
*
* @access public
* @param integer $task_id Task id
* @param integer $opposite_task_id Opposite task id
* @param integer $link_id Link id
- * @return integer Task link id
+ * @return integer|boolean
*/
public function create($task_id, $opposite_task_id, $link_id)
{
- $events = array();
$this->db->startTransaction();
- // Get opposite link
$opposite_link_id = $this->linkModel->getOppositeLinkId($link_id);
+ $task_link_id1 = $this->createTaskLink($task_id, $opposite_task_id, $link_id);
+ $task_link_id2 = $this->createTaskLink($opposite_task_id, $task_id, $opposite_link_id);
- $values = array(
- 'task_id' => $task_id,
- 'opposite_task_id' => $opposite_task_id,
- 'link_id' => $link_id,
- );
-
- // Create the original task link
- $this->db->table(self::TABLE)->insert($values);
- $task_link_id = $this->db->getLastId();
- $events[] = $values;
-
- // Create the opposite task link
- $values = array(
- 'task_id' => $opposite_task_id,
- 'opposite_task_id' => $task_id,
- 'link_id' => $opposite_link_id,
- );
-
- $this->db->table(self::TABLE)->insert($values);
- $events[] = $values;
+ if ($task_link_id1 === false || $task_link_id2 === false) {
+ $this->db->cancelTransaction();
+ return false;
+ }
$this->db->closeTransaction();
+ $this->fireEvents(array($task_link_id1, $task_link_id2), self::EVENT_CREATE_UPDATE);
- $this->fireEvents($events);
-
- return (int) $task_link_id;
+ return $task_link_id1;
}
/**
@@ -194,46 +191,24 @@ class TaskLinkModel extends Base
*/
public function update($task_link_id, $task_id, $opposite_task_id, $link_id)
{
- $events = array();
$this->db->startTransaction();
- // Get original task link
$task_link = $this->getById($task_link_id);
-
- // Find opposite task link
$opposite_task_link = $this->getOppositeTaskLink($task_link);
-
- // Get opposite link
$opposite_link_id = $this->linkModel->getOppositeLinkId($link_id);
- // Update the original task link
- $values = array(
- 'task_id' => $task_id,
- 'opposite_task_id' => $opposite_task_id,
- 'link_id' => $link_id,
- );
-
- $rs1 = $this->db->table(self::TABLE)->eq('id', $task_link_id)->update($values);
- $events[] = $values;
-
- // Update the opposite link
- $values = array(
- 'task_id' => $opposite_task_id,
- 'opposite_task_id' => $task_id,
- 'link_id' => $opposite_link_id,
- );
+ $result1 = $this->updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id);
+ $result2 = $this->updateTaskLink($opposite_task_link['id'], $opposite_task_id, $task_id, $opposite_link_id);
- $rs2 = $this->db->table(self::TABLE)->eq('id', $opposite_task_link['id'])->update($values);
- $events[] = $values;
+ if ($result1 === false || $result2 === false) {
+ $this->db->cancelTransaction();
+ return false;
+ }
$this->db->closeTransaction();
+ $this->fireEvents(array($task_link_id, $opposite_task_link['id']), self::EVENT_CREATE_UPDATE);
- if ($rs1 && $rs2) {
- $this->fireEvents($events);
- return true;
- }
-
- return false;
+ return true;
}
/**
@@ -245,21 +220,83 @@ class TaskLinkModel extends Base
*/
public function remove($task_link_id)
{
+ $this->taskLinkEventJob->execute($task_link_id, self::EVENT_DELETE);
+
$this->db->startTransaction();
$link = $this->getById($task_link_id);
$link_id = $this->linkModel->getOppositeLinkId($link['link_id']);
- $this->db->table(self::TABLE)->eq('id', $task_link_id)->remove();
+ $result1 = $this->db
+ ->table(self::TABLE)
+ ->eq('id', $task_link_id)
+ ->remove();
- $this->db
+ $result2 = $this->db
->table(self::TABLE)
->eq('opposite_task_id', $link['task_id'])
->eq('task_id', $link['opposite_task_id'])
- ->eq('link_id', $link_id)->remove();
+ ->eq('link_id', $link_id)
+ ->remove();
+
+ if ($result1 === false || $result2 === false) {
+ $this->db->cancelTransaction();
+ return false;
+ }
$this->db->closeTransaction();
return true;
}
+
+ /**
+ * Publish events
+ *
+ * @access protected
+ * @param integer[] $task_link_ids
+ * @param string $eventName
+ */
+ protected function fireEvents(array $task_link_ids, $eventName)
+ {
+ foreach ($task_link_ids as $task_link_id) {
+ $this->queueManager->push($this->taskLinkEventJob->withParams($task_link_id, $eventName));
+ }
+ }
+
+ /**
+ * Create task link
+ *
+ * @access protected
+ * @param integer $task_id
+ * @param integer $opposite_task_id
+ * @param integer $link_id
+ * @return integer|boolean
+ */
+ protected function createTaskLink($task_id, $opposite_task_id, $link_id)
+ {
+ return $this->db->table(self::TABLE)->persist(array(
+ 'task_id' => $task_id,
+ 'opposite_task_id' => $opposite_task_id,
+ 'link_id' => $link_id,
+ ));
+ }
+
+ /**
+ * Update task link
+ *
+ * @access protected
+ * @param integer $task_link_id
+ * @param integer $task_id
+ * @param integer $opposite_task_id
+ * @param integer $link_id
+ * @return boolean
+ */
+ protected function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $task_link_id)->update(array(
+ 'task_id' => $task_id,
+ 'opposite_task_id' => $opposite_task_id,
+ 'link_id' => $link_id,
+ ));
+ }
}
diff --git a/app/Model/TaskModel.php b/app/Model/TaskModel.php
index b0e7772a..5cddb509 100644
--- a/app/Model/TaskModel.php
+++ b/app/Model/TaskModel.php
@@ -5,7 +5,7 @@ namespace Kanboard\Model;
use Kanboard\Core\Base;
/**
- * Task model
+ * Task Model
*
* @package Kanboard\Model
* @author Frederic Guillot
@@ -17,80 +17,80 @@ class TaskModel extends Base
*
* @var string
*/
- const TABLE = 'tasks';
+ const TABLE = 'tasks';
/**
* Task status
*
* @var integer
*/
- const STATUS_OPEN = 1;
- const STATUS_CLOSED = 0;
+ const STATUS_OPEN = 1;
+ const STATUS_CLOSED = 0;
/**
* Events
*
* @var string
*/
- const EVENT_MOVE_PROJECT = 'task.move.project';
- const EVENT_MOVE_COLUMN = 'task.move.column';
- const EVENT_MOVE_POSITION = 'task.move.position';
- const EVENT_MOVE_SWIMLANE = 'task.move.swimlane';
- const EVENT_UPDATE = 'task.update';
- const EVENT_CREATE = 'task.create';
- const EVENT_CLOSE = 'task.close';
- const EVENT_OPEN = 'task.open';
- const EVENT_CREATE_UPDATE = 'task.create_update';
+ const EVENT_MOVE_PROJECT = 'task.move.project';
+ const EVENT_MOVE_COLUMN = 'task.move.column';
+ const EVENT_MOVE_POSITION = 'task.move.position';
+ const EVENT_MOVE_SWIMLANE = 'task.move.swimlane';
+ const EVENT_UPDATE = 'task.update';
+ const EVENT_CREATE = 'task.create';
+ const EVENT_CLOSE = 'task.close';
+ const EVENT_OPEN = 'task.open';
+ const EVENT_CREATE_UPDATE = 'task.create_update';
const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change';
- const EVENT_OVERDUE = 'task.overdue';
- const EVENT_USER_MENTION = 'task.user.mention';
- const EVENT_DAILY_CRONJOB = 'task.cronjob.daily';
+ const EVENT_OVERDUE = 'task.overdue';
+ const EVENT_USER_MENTION = 'task.user.mention';
+ const EVENT_DAILY_CRONJOB = 'task.cronjob.daily';
/**
* Recurrence: status
*
* @var integer
*/
- const RECURRING_STATUS_NONE = 0;
- const RECURRING_STATUS_PENDING = 1;
- const RECURRING_STATUS_PROCESSED = 2;
+ const RECURRING_STATUS_NONE = 0;
+ const RECURRING_STATUS_PENDING = 1;
+ const RECURRING_STATUS_PROCESSED = 2;
/**
* Recurrence: trigger
*
* @var integer
*/
- const RECURRING_TRIGGER_FIRST_COLUMN = 0;
- const RECURRING_TRIGGER_LAST_COLUMN = 1;
- const RECURRING_TRIGGER_CLOSE = 2;
+ const RECURRING_TRIGGER_FIRST_COLUMN = 0;
+ const RECURRING_TRIGGER_LAST_COLUMN = 1;
+ const RECURRING_TRIGGER_CLOSE = 2;
/**
* Recurrence: timeframe
*
* @var integer
*/
- const RECURRING_TIMEFRAME_DAYS = 0;
- const RECURRING_TIMEFRAME_MONTHS = 1;
- const RECURRING_TIMEFRAME_YEARS = 2;
+ const RECURRING_TIMEFRAME_DAYS = 0;
+ const RECURRING_TIMEFRAME_MONTHS = 1;
+ const RECURRING_TIMEFRAME_YEARS = 2;
/**
* Recurrence: base date used to calculate new due date
*
* @var integer
*/
- const RECURRING_BASEDATE_DUEDATE = 0;
- const RECURRING_BASEDATE_TRIGGERDATE = 1;
+ const RECURRING_BASEDATE_DUEDATE = 0;
+ const RECURRING_BASEDATE_TRIGGERDATE = 1;
/**
* Remove a task
*
* @access public
- * @param integer $task_id Task id
+ * @param integer $task_id Task id
* @return boolean
*/
public function remove($task_id)
{
- if (! $this->taskFinderModel->exists($task_id)) {
+ if (!$this->taskFinderModel->exists($task_id)) {
return false;
}
@@ -105,7 +105,7 @@ class TaskModel extends Base
* Example: "Fix bug #1234" will return 1234
*
* @access public
- * @param string $message Text
+ * @param string $message Text
* @return integer
*/
public function getTaskIdFromText($message)
@@ -118,69 +118,11 @@ class TaskModel extends Base
}
/**
- * Return the list user selectable recurrence status
- *
- * @access public
- * @return array
- */
- public function getRecurrenceStatusList()
- {
- return array(
- TaskModel::RECURRING_STATUS_NONE => t('No'),
- TaskModel::RECURRING_STATUS_PENDING => t('Yes'),
- );
- }
-
- /**
- * Return the list recurrence triggers
- *
- * @access public
- * @return array
- */
- public function getRecurrenceTriggerList()
- {
- return array(
- TaskModel::RECURRING_TRIGGER_FIRST_COLUMN => t('When task is moved from first column'),
- TaskModel::RECURRING_TRIGGER_LAST_COLUMN => t('When task is moved to last column'),
- TaskModel::RECURRING_TRIGGER_CLOSE => t('When task is closed'),
- );
- }
-
- /**
- * Return the list options to calculate recurrence due date
- *
- * @access public
- * @return array
- */
- public function getRecurrenceBasedateList()
- {
- return array(
- TaskModel::RECURRING_BASEDATE_DUEDATE => t('Existing due date'),
- TaskModel::RECURRING_BASEDATE_TRIGGERDATE => t('Action date'),
- );
- }
-
- /**
- * Return the list recurrence timeframes
- *
- * @access public
- * @return array
- */
- public function getRecurrenceTimeframeList()
- {
- return array(
- TaskModel::RECURRING_TIMEFRAME_DAYS => t('Day(s)'),
- TaskModel::RECURRING_TIMEFRAME_MONTHS => t('Month(s)'),
- TaskModel::RECURRING_TIMEFRAME_YEARS => t('Year(s)'),
- );
- }
-
- /**
* Get task progress based on the column position
*
* @access public
- * @param array $task
- * @param array $columns
+ * @param array $task
+ * @param array $columns
* @return integer
*/
public function getProgress(array $task, array $columns)
@@ -201,25 +143,4 @@ class TaskModel extends Base
return round(($position * 100) / count($columns), 1);
}
-
- /**
- * Helper method to duplicate all tasks to another project
- *
- * @access public
- * @param integer $src_project_id
- * @param integer $dst_project_id
- * @return boolean
- */
- public function duplicate($src_project_id, $dst_project_id)
- {
- $task_ids = $this->taskFinderModel->getAllIds($src_project_id, array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED));
-
- foreach ($task_ids as $task_id) {
- if (! $this->taskDuplicationModel->duplicateToProject($task_id, $dst_project_id)) {
- return false;
- }
- }
-
- return true;
- }
}
diff --git a/app/Model/TaskModificationModel.php b/app/Model/TaskModificationModel.php
index 762af2c5..6e16fbec 100644
--- a/app/Model/TaskModificationModel.php
+++ b/app/Model/TaskModificationModel.php
@@ -3,7 +3,6 @@
namespace Kanboard\Model;
use Kanboard\Core\Base;
-use Kanboard\Event\TaskEvent;
/**
* Task Modification
@@ -23,13 +22,14 @@ class TaskModificationModel extends Base
*/
public function update(array $values, $fire_events = true)
{
- $original_task = $this->taskFinderModel->getById($values['id']);
+ $task = $this->taskFinderModel->getById($values['id']);
+ $this->updateTags($values, $task);
$this->prepare($values);
- $result = $this->db->table(TaskModel::TABLE)->eq('id', $original_task['id'])->update($values);
+ $result = $this->db->table(TaskModel::TABLE)->eq('id', $task['id'])->update($values);
if ($fire_events && $result) {
- $this->fireEvents($original_task, $values);
+ $this->fireEvents($task, $values);
}
return $result;
@@ -38,52 +38,65 @@ class TaskModificationModel extends Base
/**
* Fire events
*
- * @access public
- * @param array $task
- * @param array $new_values
+ * @access protected
+ * @param array $task
+ * @param array $changes
*/
- public function fireEvents(array $task, array $new_values)
+ protected function fireEvents(array $task, array $changes)
{
$events = array();
- $event_data = array_merge($task, $new_values, array('task_id' => $task['id']));
-
- // Values changed
- $event_data['changes'] = array_diff_assoc($new_values, $task);
- unset($event_data['changes']['date_modification']);
- if ($this->isFieldModified('owner_id', $event_data['changes'])) {
+ if ($this->isAssigneeChanged($task, $changes)) {
$events[] = TaskModel::EVENT_ASSIGNEE_CHANGE;
- } elseif (! empty($event_data['changes'])) {
+ } elseif ($this->isModified($task, $changes)) {
$events[] = TaskModel::EVENT_CREATE_UPDATE;
$events[] = TaskModel::EVENT_UPDATE;
}
- foreach ($events as $event) {
- $this->logger->debug('Event fired: '.$event);
- $this->dispatcher->dispatch($event, new TaskEvent($event_data));
+ if (! empty($events)) {
+ $this->queueManager->push($this->taskEventJob
+ ->withParams($task['id'], $events, $changes, array(), $task)
+ );
}
}
/**
+ * Return true if the task have been modified
+ *
+ * @access protected
+ * @param array $task
+ * @param array $changes
+ * @return bool
+ */
+ protected function isModified(array $task, array $changes)
+ {
+ $diff = array_diff_assoc($changes, $task);
+ unset($diff['date_modification']);
+ return count($diff) > 0;
+ }
+
+ /**
* Return true if the field is the only modified value
*
- * @access public
- * @param string $field
- * @param array $changes
- * @return boolean
+ * @access protected
+ * @param array $task
+ * @param array $changes
+ * @return bool
*/
- public function isFieldModified($field, array $changes)
+ protected function isAssigneeChanged(array $task, array $changes)
{
- return isset($changes[$field]) && count($changes) === 1;
+ $diff = array_diff_assoc($changes, $task);
+ unset($diff['date_modification']);
+ return isset($changes['owner_id']) && $task['owner_id'] != $changes['owner_id'] && count($diff) === 1;
}
/**
* Prepare data before task modification
*
- * @access public
- * @param array $values Form values
+ * @access protected
+ * @param array $values
*/
- public function prepare(array &$values)
+ protected function prepare(array &$values)
{
$values = $this->dateParser->convert($values, array('date_due'));
$values = $this->dateParser->convert($values, array('date_started'), true);
@@ -93,5 +106,22 @@ class TaskModificationModel extends Base
$this->helper->model->convertIntegerFields($values, array('priority', 'is_active', 'recurrence_status', 'recurrence_trigger', 'recurrence_factor', 'recurrence_timeframe', 'recurrence_basedate'));
$values['date_modification'] = time();
+
+ $this->hook->reference('model:task:modification:prepare', $values);
+ }
+
+ /**
+ * Update tags
+ *
+ * @access protected
+ * @param array $values
+ * @param array $original_task
+ */
+ protected function updateTags(array &$values, array $original_task)
+ {
+ if (isset($values['tags'])) {
+ $this->taskTagModel->save($original_task['project_id'], $values['id'], $values['tags']);
+ unset($values['tags']);
+ }
}
}
diff --git a/app/Model/TaskPositionModel.php b/app/Model/TaskPositionModel.php
index 9fdb8f7d..3d95a763 100644
--- a/app/Model/TaskPositionModel.php
+++ b/app/Model/TaskPositionModel.php
@@ -3,7 +3,6 @@
namespace Kanboard\Model;
use Kanboard\Core\Base;
-use Kanboard\Event\TaskEvent;
/**
* Task Position
@@ -17,15 +16,16 @@ class TaskPositionModel extends Base
* Move a task to another column or to another position
*
* @access public
- * @param integer $project_id Project id
- * @param integer $task_id Task id
- * @param integer $column_id Column id
- * @param integer $position Position (must be >= 1)
- * @param integer $swimlane_id Swimlane id
- * @param boolean $fire_events Fire events
- * @return boolean
+ * @param integer $project_id Project id
+ * @param integer $task_id Task id
+ * @param integer $column_id Column id
+ * @param integer $position Position (must be >= 1)
+ * @param integer $swimlane_id Swimlane id
+ * @param boolean $fire_events Fire events
+ * @param bool $onlyOpen Do not move closed tasks
+ * @return bool
*/
- public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true)
+ public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true, $onlyOpen = true)
{
if ($position < 1) {
return false;
@@ -33,7 +33,7 @@ class TaskPositionModel extends Base
$task = $this->taskFinderModel->getById($task_id);
- if ($task['is_active'] == TaskModel::STATUS_CLOSED) {
+ if ($onlyOpen && $task['is_active'] == TaskModel::STATUS_CLOSED) {
return true;
}
@@ -212,8 +212,7 @@ class TaskPositionModel extends Base
*/
private function fireEvents(array $task, $new_column_id, $new_position, $new_swimlane_id)
{
- $event_data = array(
- 'task_id' => $task['id'],
+ $changes = array(
'project_id' => $task['project_id'],
'position' => $new_position,
'column_id' => $new_column_id,
@@ -226,14 +225,26 @@ class TaskPositionModel extends Base
);
if ($task['swimlane_id'] != $new_swimlane_id) {
- $this->logger->debug('Event fired: '.TaskModel::EVENT_MOVE_SWIMLANE);
- $this->dispatcher->dispatch(TaskModel::EVENT_MOVE_SWIMLANE, new TaskEvent($event_data));
+ $this->queueManager->push($this->taskEventJob->withParams(
+ $task['id'],
+ array(TaskModel::EVENT_MOVE_SWIMLANE),
+ $changes,
+ $changes
+ ));
} elseif ($task['column_id'] != $new_column_id) {
- $this->logger->debug('Event fired: '.TaskModel::EVENT_MOVE_COLUMN);
- $this->dispatcher->dispatch(TaskModel::EVENT_MOVE_COLUMN, new TaskEvent($event_data));
+ $this->queueManager->push($this->taskEventJob->withParams(
+ $task['id'],
+ array(TaskModel::EVENT_MOVE_COLUMN),
+ $changes,
+ $changes
+ ));
} elseif ($task['position'] != $new_position) {
- $this->logger->debug('Event fired: '.TaskModel::EVENT_MOVE_POSITION);
- $this->dispatcher->dispatch(TaskModel::EVENT_MOVE_POSITION, new TaskEvent($event_data));
+ $this->queueManager->push($this->taskEventJob->withParams(
+ $task['id'],
+ array(TaskModel::EVENT_MOVE_POSITION),
+ $changes,
+ $changes
+ ));
}
}
}
diff --git a/app/Model/TaskProjectDuplicationModel.php b/app/Model/TaskProjectDuplicationModel.php
new file mode 100644
index 00000000..8ebed255
--- /dev/null
+++ b/app/Model/TaskProjectDuplicationModel.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Kanboard\Model;
+
+/**
+ * Task Project Duplication
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class TaskProjectDuplicationModel extends TaskDuplicationModel
+{
+ /**
+ * Duplicate a task to another project
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $project_id
+ * @param integer $swimlane_id
+ * @param integer $column_id
+ * @param integer $category_id
+ * @param integer $owner_id
+ * @return boolean|integer
+ */
+ public function duplicateToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
+ {
+ $values = $this->prepare($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
+ $this->checkDestinationProjectValues($values);
+ $new_task_id = $this->save($task_id, $values);
+
+ if ($new_task_id !== false) {
+ $this->tagDuplicationModel->duplicateTaskTagsToAnotherProject($task_id, $new_task_id, $project_id);
+ }
+
+ return $new_task_id;
+ }
+
+ /**
+ * Prepare values before duplication
+ *
+ * @access protected
+ * @param integer $task_id
+ * @param integer $project_id
+ * @param integer $swimlane_id
+ * @param integer $column_id
+ * @param integer $category_id
+ * @param integer $owner_id
+ * @return array
+ */
+ protected function prepare($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id)
+ {
+ $values = $this->copyFields($task_id);
+ $values['project_id'] = $project_id;
+ $values['column_id'] = $column_id !== null ? $column_id : $values['column_id'];
+ $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $values['swimlane_id'];
+ $values['category_id'] = $category_id !== null ? $category_id : $values['category_id'];
+ $values['owner_id'] = $owner_id !== null ? $owner_id : $values['owner_id'];
+ return $values;
+ }
+}
diff --git a/app/Model/TaskProjectMoveModel.php b/app/Model/TaskProjectMoveModel.php
new file mode 100644
index 00000000..ae3ae084
--- /dev/null
+++ b/app/Model/TaskProjectMoveModel.php
@@ -0,0 +1,65 @@
+<?php
+
+namespace Kanboard\Model;
+
+/**
+ * Task Project Move
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class TaskProjectMoveModel extends TaskDuplicationModel
+{
+ /**
+ * Move a task to another project
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $project_id
+ * @param integer $swimlane_id
+ * @param integer $column_id
+ * @param integer $category_id
+ * @param integer $owner_id
+ * @return boolean
+ */
+ public function moveToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
+ {
+ $task = $this->taskFinderModel->getById($task_id);
+ $values = $this->prepare($project_id, $swimlane_id, $column_id, $category_id, $owner_id, $task);
+
+ $this->checkDestinationProjectValues($values);
+ $this->tagDuplicationModel->syncTaskTagsToAnotherProject($task_id, $project_id);
+
+ if ($this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update($values)) {
+ $this->queueManager->push($this->taskEventJob->withParams($task_id, array(TaskModel::EVENT_MOVE_PROJECT), $values));
+ }
+
+ return true;
+ }
+
+ /**
+ * Prepare new task values
+ *
+ * @access protected
+ * @param integer $project_id
+ * @param integer $swimlane_id
+ * @param integer $column_id
+ * @param integer $category_id
+ * @param integer $owner_id
+ * @param array $task
+ * @return array
+ */
+ protected function prepare($project_id, $swimlane_id, $column_id, $category_id, $owner_id, array $task)
+ {
+ $values = array();
+ $values['is_active'] = 1;
+ $values['project_id'] = $project_id;
+ $values['column_id'] = $column_id !== null ? $column_id : $task['column_id'];
+ $values['position'] = $this->taskFinderModel->countByColumnId($project_id, $values['column_id']) + 1;
+ $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $task['swimlane_id'];
+ $values['category_id'] = $category_id !== null ? $category_id : $task['category_id'];
+ $values['owner_id'] = $owner_id !== null ? $owner_id : $task['owner_id'];
+ $values['priority'] = $task['priority'];
+ return $values;
+ }
+}
diff --git a/app/Model/TaskRecurrenceModel.php b/app/Model/TaskRecurrenceModel.php
new file mode 100644
index 00000000..ffe43f8c
--- /dev/null
+++ b/app/Model/TaskRecurrenceModel.php
@@ -0,0 +1,147 @@
+<?php
+
+namespace Kanboard\Model;
+
+use DateInterval;
+use DateTime;
+
+/**
+ * Task Recurrence
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class TaskRecurrenceModel extends TaskDuplicationModel
+{
+ /**
+ * Return the list user selectable recurrence status
+ *
+ * @access public
+ * @return array
+ */
+ public function getRecurrenceStatusList()
+ {
+ return array(
+ TaskModel::RECURRING_STATUS_NONE => t('No'),
+ TaskModel::RECURRING_STATUS_PENDING => t('Yes'),
+ );
+ }
+
+ /**
+ * Return the list recurrence triggers
+ *
+ * @access public
+ * @return array
+ */
+ public function getRecurrenceTriggerList()
+ {
+ return array(
+ TaskModel::RECURRING_TRIGGER_FIRST_COLUMN => t('When task is moved from first column'),
+ TaskModel::RECURRING_TRIGGER_LAST_COLUMN => t('When task is moved to last column'),
+ TaskModel::RECURRING_TRIGGER_CLOSE => t('When task is closed'),
+ );
+ }
+
+ /**
+ * Return the list options to calculate recurrence due date
+ *
+ * @access public
+ * @return array
+ */
+ public function getRecurrenceBasedateList()
+ {
+ return array(
+ TaskModel::RECURRING_BASEDATE_DUEDATE => t('Existing due date'),
+ TaskModel::RECURRING_BASEDATE_TRIGGERDATE => t('Action date'),
+ );
+ }
+
+ /**
+ * Return the list recurrence timeframes
+ *
+ * @access public
+ * @return array
+ */
+ public function getRecurrenceTimeframeList()
+ {
+ return array(
+ TaskModel::RECURRING_TIMEFRAME_DAYS => t('Day(s)'),
+ TaskModel::RECURRING_TIMEFRAME_MONTHS => t('Month(s)'),
+ TaskModel::RECURRING_TIMEFRAME_YEARS => t('Year(s)'),
+ );
+ }
+
+ /**
+ * Duplicate recurring task
+ *
+ * @access public
+ * @param integer $task_id Task id
+ * @return boolean|integer Recurrence task id
+ */
+ public function duplicateRecurringTask($task_id)
+ {
+ $values = $this->copyFields($task_id);
+
+ if ($values['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) {
+ $values['recurrence_parent'] = $task_id;
+ $values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']);
+ $this->calculateRecurringTaskDueDate($values);
+
+ $recurring_task_id = $this->save($task_id, $values);
+
+ if ($recurring_task_id !== false) {
+ $this->tagDuplicationModel->duplicateTaskTags($task_id, $recurring_task_id);
+
+ $parent_update = $this->db
+ ->table(TaskModel::TABLE)
+ ->eq('id', $task_id)
+ ->update(array(
+ 'recurrence_status' => TaskModel::RECURRING_STATUS_PROCESSED,
+ 'recurrence_child' => $recurring_task_id,
+ ));
+
+ if ($parent_update) {
+ return $recurring_task_id;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Calculate new due date for new recurrence task
+ *
+ * @access public
+ * @param array $values Task fields
+ */
+ public function calculateRecurringTaskDueDate(array &$values)
+ {
+ if (! empty($values['date_due']) && $values['recurrence_factor'] != 0) {
+ if ($values['recurrence_basedate'] == TaskModel::RECURRING_BASEDATE_TRIGGERDATE) {
+ $values['date_due'] = time();
+ }
+
+ $factor = abs($values['recurrence_factor']);
+ $subtract = $values['recurrence_factor'] < 0;
+
+ switch ($values['recurrence_timeframe']) {
+ case TaskModel::RECURRING_TIMEFRAME_MONTHS:
+ $interval = 'P' . $factor . 'M';
+ break;
+ case TaskModel::RECURRING_TIMEFRAME_YEARS:
+ $interval = 'P' . $factor . 'Y';
+ break;
+ default:
+ $interval = 'P' . $factor . 'D';
+ }
+
+ $date_due = new DateTime();
+ $date_due->setTimestamp($values['date_due']);
+
+ $subtract ? $date_due->sub(new DateInterval($interval)) : $date_due->add(new DateInterval($interval));
+
+ $values['date_due'] = $date_due->getTimestamp();
+ }
+ }
+}
diff --git a/app/Model/TaskStatusModel.php b/app/Model/TaskStatusModel.php
index 4d573f0e..dc114698 100644
--- a/app/Model/TaskStatusModel.php
+++ b/app/Model/TaskStatusModel.php
@@ -3,7 +3,6 @@
namespace Kanboard\Model;
use Kanboard\Core\Base;
-use Kanboard\Event\TaskEvent;
/**
* Task Status
@@ -46,7 +45,7 @@ class TaskStatusModel extends Base
*/
public function close($task_id)
{
- $this->subtaskModel->closeAll($task_id);
+ $this->subtaskStatusModel->closeAll($task_id);
return $this->changeStatus($task_id, TaskModel::STATUS_CLOSED, time(), TaskModel::EVENT_CLOSE);
}
@@ -101,10 +100,10 @@ class TaskStatusModel extends Base
* @param integer $task_id Task id
* @param integer $status Task status
* @param integer $date_completed Timestamp
- * @param string $event Event name
+ * @param string $event_name Event name
* @return boolean
*/
- private function changeStatus($task_id, $status, $date_completed, $event)
+ private function changeStatus($task_id, $status, $date_completed, $event_name)
{
if (! $this->taskFinderModel->exists($task_id)) {
return false;
@@ -120,8 +119,7 @@ class TaskStatusModel extends Base
));
if ($result) {
- $this->logger->debug('Event fired: '.$event);
- $this->dispatcher->dispatch($event, new TaskEvent(array('task_id' => $task_id) + $this->taskFinderModel->getById($task_id)));
+ $this->queueManager->push($this->taskEventJob->withParams($task_id, array($event_name)));
}
return $result;
diff --git a/app/Model/TaskTagModel.php b/app/Model/TaskTagModel.php
new file mode 100644
index 00000000..0553cc6c
--- /dev/null
+++ b/app/Model/TaskTagModel.php
@@ -0,0 +1,184 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class TaskTagModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class TaskTagModel extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'task_has_tags';
+
+ /**
+ * Get all tags not available in a project
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $project_id
+ * @return array
+ */
+ public function getTagIdsByTaskNotAvailableInProject($task_id, $project_id)
+ {
+ return $this->db->table(TagModel::TABLE)
+ ->eq(self::TABLE.'.task_id', $task_id)
+ ->notIn(TagModel::TABLE.'.project_id', array(0, $project_id))
+ ->join(self::TABLE, 'tag_id', 'id')
+ ->findAllByColumn(TagModel::TABLE.'.id');
+ }
+
+ /**
+ * Get all tags associated to a task
+ *
+ * @access public
+ * @param integer $task_id
+ * @return array
+ */
+ public function getTagsByTask($task_id)
+ {
+ return $this->db->table(TagModel::TABLE)
+ ->columns(TagModel::TABLE.'.id', TagModel::TABLE.'.name')
+ ->eq(self::TABLE.'.task_id', $task_id)
+ ->join(self::TABLE, 'tag_id', 'id')
+ ->findAll();
+ }
+
+ /**
+ * Get all tags associated to a list of tasks
+ *
+ * @access public
+ * @param integer[] $task_ids
+ * @return array
+ */
+ public function getTagsByTasks($task_ids)
+ {
+ if (empty($task_ids)) {
+ return array();
+ }
+
+ $tags = $this->db->table(TagModel::TABLE)
+ ->columns(TagModel::TABLE.'.id', TagModel::TABLE.'.name', self::TABLE.'.task_id')
+ ->in(self::TABLE.'.task_id', $task_ids)
+ ->join(self::TABLE, 'tag_id', 'id')
+ ->findAll();
+
+ return array_column_index($tags, 'task_id');
+ }
+
+ /**
+ * Get dictionary of tags
+ *
+ * @access public
+ * @param integer $task_id
+ * @return array
+ */
+ public function getList($task_id)
+ {
+ $tags = $this->getTagsByTask($task_id);
+ return array_column($tags, 'name', 'id');
+ }
+
+ /**
+ * Add or update a list of tags to a task
+ *
+ * @access public
+ * @param integer $project_id
+ * @param integer $task_id
+ * @param string[] $tags
+ * @return boolean
+ */
+ public function save($project_id, $task_id, array $tags)
+ {
+ $task_tags = $this->getList($task_id);
+ $tags = array_filter($tags);
+
+ return $this->associateTags($project_id, $task_id, $task_tags, $tags) &&
+ $this->dissociateTags($task_id, $task_tags, $tags);
+ }
+
+ /**
+ * Associate a tag to a task
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $tag_id
+ * @return boolean
+ */
+ public function associateTag($task_id, $tag_id)
+ {
+ return $this->db->table(self::TABLE)->insert(array(
+ 'task_id' => $task_id,
+ 'tag_id' => $tag_id,
+ ));
+ }
+
+ /**
+ * Dissociate a tag from a task
+ *
+ * @access public
+ * @param integer $task_id
+ * @param integer $tag_id
+ * @return boolean
+ */
+ public function dissociateTag($task_id, $tag_id)
+ {
+ return $this->db->table(self::TABLE)
+ ->eq('task_id', $task_id)
+ ->eq('tag_id', $tag_id)
+ ->remove();
+ }
+
+ /**
+ * Associate missing tags
+ *
+ * @access protected
+ * @param integer $project_id
+ * @param integer $task_id
+ * @param array $task_tags
+ * @param string[] $tags
+ * @return bool
+ */
+ protected function associateTags($project_id, $task_id, $task_tags, $tags)
+ {
+ foreach ($tags as $tag) {
+ $tag_id = $this->tagModel->findOrCreateTag($project_id, $tag);
+
+ if (! isset($task_tags[$tag_id]) && ! $this->associateTag($task_id, $tag_id)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Dissociate removed tags
+ *
+ * @access protected
+ * @param integer $task_id
+ * @param array $task_tags
+ * @param string[] $tags
+ * @return bool
+ */
+ protected function dissociateTags($task_id, $task_tags, $tags)
+ {
+ foreach ($task_tags as $tag_id => $tag) {
+ if (! in_array($tag, $tags)) {
+ if (! $this->dissociateTag($task_id, $tag_id)) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/app/Model/UserModel.php b/app/Model/UserModel.php
index f7a051c5..56b1a960 100644
--- a/app/Model/UserModel.php
+++ b/app/Model/UserModel.php
@@ -65,17 +65,6 @@ class UserModel extends Base
}
/**
- * Return the full name
- *
- * @param array $user User properties
- * @return string
- */
- public function getFullname(array $user)
- {
- return $user['name'] ?: $user['username'];
- }
-
- /**
* Return true is the given user id is administrator
*
* @access public
@@ -230,7 +219,7 @@ class UserModel extends Base
$result = array();
foreach ($users as $user) {
- $result[$user['id']] = $this->getFullname($user);
+ $result[$user['id']] = $this->helper->user->getFullname($user);
}
asort($result);
diff --git a/app/Notification/MailNotification.php b/app/Notification/MailNotification.php
index 2d27179c..9e042820 100644
--- a/app/Notification/MailNotification.php
+++ b/app/Notification/MailNotification.php
@@ -4,10 +4,6 @@ namespace Kanboard\Notification;
use Kanboard\Core\Base;
use Kanboard\Core\Notification\NotificationInterface;
-use Kanboard\Model\TaskModel;
-use Kanboard\Model\TaskFileModel;
-use Kanboard\Model\CommentModel;
-use Kanboard\Model\SubtaskModel;
/**
* Email Notification
@@ -76,76 +72,16 @@ class MailNotification extends Base implements NotificationInterface
* Get the mail subject for a given template name
*
* @access public
- * @param string $event_name Event name
- * @param array $event_data Event data
- * @return string
- */
- public function getMailSubject($event_name, array $event_data)
- {
- switch ($event_name) {
- case TaskFileModel::EVENT_CREATE:
- $subject = $this->getStandardMailSubject(e('New attachment'), $event_data);
- break;
- case CommentModel::EVENT_CREATE:
- $subject = $this->getStandardMailSubject(e('New comment'), $event_data);
- break;
- case CommentModel::EVENT_UPDATE:
- $subject = $this->getStandardMailSubject(e('Comment updated'), $event_data);
- break;
- case SubtaskModel::EVENT_CREATE:
- $subject = $this->getStandardMailSubject(e('New subtask'), $event_data);
- break;
- case SubtaskModel::EVENT_UPDATE:
- $subject = $this->getStandardMailSubject(e('Subtask updated'), $event_data);
- break;
- case TaskModel::EVENT_CREATE:
- $subject = $this->getStandardMailSubject(e('New task'), $event_data);
- break;
- case TaskModel::EVENT_UPDATE:
- $subject = $this->getStandardMailSubject(e('Task updated'), $event_data);
- break;
- case TaskModel::EVENT_CLOSE:
- $subject = $this->getStandardMailSubject(e('Task closed'), $event_data);
- break;
- case TaskModel::EVENT_OPEN:
- $subject = $this->getStandardMailSubject(e('Task opened'), $event_data);
- break;
- case TaskModel::EVENT_MOVE_COLUMN:
- $subject = $this->getStandardMailSubject(e('Column change'), $event_data);
- break;
- case TaskModel::EVENT_MOVE_POSITION:
- $subject = $this->getStandardMailSubject(e('Position change'), $event_data);
- break;
- case TaskModel::EVENT_MOVE_SWIMLANE:
- $subject = $this->getStandardMailSubject(e('Swimlane change'), $event_data);
- break;
- case TaskModel::EVENT_ASSIGNEE_CHANGE:
- $subject = $this->getStandardMailSubject(e('Assignee change'), $event_data);
- break;
- case TaskModel::EVENT_USER_MENTION:
- case CommentModel::EVENT_USER_MENTION:
- $subject = $this->getStandardMailSubject(e('Mentioned'), $event_data);
- break;
- case TaskModel::EVENT_OVERDUE:
- $subject = e('[%s] Overdue tasks', $event_data['project_name']);
- break;
- default:
- $subject = e('Notification');
- }
-
- return $subject;
- }
-
- /**
- * Get the mail subject for a given label
- *
- * @access private
- * @param string $label Label
- * @param array $data Template data
+ * @param string $eventName Event name
+ * @param array $eventData Event data
* @return string
*/
- private function getStandardMailSubject($label, array $data)
+ public function getMailSubject($eventName, array $eventData)
{
- return sprintf('[%s][%s] %s (#%d)', $data['task']['project_name'], $label, $data['task']['title'], $data['task']['id']);
+ return sprintf(
+ '[%s] %s',
+ isset($eventData['project_name']) ? $eventData['project_name'] : $eventData['task']['project_name'],
+ $this->notificationModel->getTitleWithoutAuthor($eventName, $eventData)
+ );
}
}
diff --git a/app/Pagination/ProjectPagination.php b/app/Pagination/ProjectPagination.php
new file mode 100644
index 00000000..8f1fa87c
--- /dev/null
+++ b/app/Pagination/ProjectPagination.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Kanboard\Pagination;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Paginator;
+use Kanboard\Model\ProjectModel;
+
+/**
+ * Class ProjectPagination
+ *
+ * @package Kanboard\Pagination
+ * @author Frederic Guillot
+ */
+class ProjectPagination extends Base
+{
+ /**
+ * Get dashboard pagination
+ *
+ * @access public
+ * @param integer $user_id
+ * @param string $method
+ * @param integer $max
+ * @return Paginator
+ */
+ public function getDashboardPaginator($user_id, $method, $max)
+ {
+ return $this->paginator
+ ->setUrl('DashboardController', $method, array('pagination' => 'projects', 'user_id' => $user_id))
+ ->setMax($max)
+ ->setOrder(ProjectModel::TABLE.'.name')
+ ->setQuery($this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id)))
+ ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects');
+ }
+}
diff --git a/app/Pagination/SubtaskPagination.php b/app/Pagination/SubtaskPagination.php
new file mode 100644
index 00000000..c55d0fb4
--- /dev/null
+++ b/app/Pagination/SubtaskPagination.php
@@ -0,0 +1,39 @@
+<?php
+
+namespace Kanboard\Pagination;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Paginator;
+use Kanboard\Model\SubtaskModel;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class SubtaskPagination
+ *
+ * @package Kanboard\Pagination
+ * @author Frederic Guillot
+ */
+class SubtaskPagination extends Base
+{
+ /**
+ * Get dashboard pagination
+ *
+ * @access public
+ * @param integer $user_id
+ * @param string $method
+ * @param integer $max
+ * @return Paginator
+ */
+ public function getDashboardPaginator($user_id, $method, $max)
+ {
+ $query = $this->subtaskModel->getUserQuery($user_id, array(SubtaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS));
+ $this->hook->reference('pagination:dashboard:subtask:query', $query);
+
+ return $this->paginator
+ ->setUrl('DashboardController', $method, array('pagination' => 'subtasks', 'user_id' => $user_id))
+ ->setMax($max)
+ ->setOrder(TaskModel::TABLE.'.id')
+ ->setQuery($query)
+ ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks');
+ }
+}
diff --git a/app/Pagination/TaskPagination.php b/app/Pagination/TaskPagination.php
new file mode 100644
index 00000000..5fe986e7
--- /dev/null
+++ b/app/Pagination/TaskPagination.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Kanboard\Pagination;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Paginator;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskPagination
+ *
+ * @package Kanboard\Pagination
+ * @author Frederic Guillot
+ */
+class TaskPagination extends Base
+{
+ /**
+ * Get dashboard pagination
+ *
+ * @access public
+ * @param integer $user_id
+ * @param string $method
+ * @param integer $max
+ * @return Paginator
+ */
+ public function getDashboardPaginator($user_id, $method, $max)
+ {
+ $query = $this->taskFinderModel->getUserQuery($user_id);
+ $this->hook->reference('pagination:dashboard:task:query', $query);
+
+ return $this->paginator
+ ->setUrl('DashboardController', $method, array('pagination' => 'tasks', 'user_id' => $user_id))
+ ->setMax($max)
+ ->setOrder(TaskModel::TABLE.'.id')
+ ->setQuery($query)
+ ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks');
+ }
+}
diff --git a/app/Pagination/UserPagination.php b/app/Pagination/UserPagination.php
new file mode 100644
index 00000000..430b7d2f
--- /dev/null
+++ b/app/Pagination/UserPagination.php
@@ -0,0 +1,32 @@
+<?php
+
+namespace Kanboard\Pagination;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Paginator;
+use Kanboard\Model\UserModel;
+
+/**
+ * Class UserPagination
+ *
+ * @package Kanboard\Pagination
+ * @author Frederic Guillot
+ */
+class UserPagination extends Base
+{
+ /**
+ * Get user listing paginator
+ *
+ * @access public
+ * @return Paginator
+ */
+ public function getListingPaginator()
+ {
+ return $this->paginator
+ ->setUrl('UserListController', 'show')
+ ->setMax(30)
+ ->setOrder(UserModel::TABLE.'.username')
+ ->setQuery($this->userModel->getQuery())
+ ->calculate();
+ }
+}
diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php
index 934b063f..99fed66f 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -6,7 +6,35 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
-const VERSION = 110;
+const VERSION = 112;
+
+function version_112(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE columns ADD COLUMN hide_in_dashboard INT DEFAULT 0 NOT NULL');
+}
+
+function version_111(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE tags (
+ id INT NOT NULL AUTO_INCREMENT,
+ name VARCHAR(255) NOT NULL,
+ project_id INT NOT NULL,
+ UNIQUE(project_id, name),
+ PRIMARY KEY(id)
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+
+ $pdo->exec("
+ CREATE TABLE task_has_tags (
+ task_id INT NOT NULL,
+ tag_id INT NOT NULL,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE,
+ UNIQUE(tag_id, task_id)
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+}
function version_110(PDO $pdo)
{
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
index 3ef49498..b982bcae 100644
--- a/app/Schema/Postgres.php
+++ b/app/Schema/Postgres.php
@@ -6,7 +6,34 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
-const VERSION = 89;
+const VERSION = 91;
+
+function version_91(PDO $pdo)
+{
+ $pdo->exec("ALTER TABLE columns ADD COLUMN hide_in_dashboard BOOLEAN DEFAULT '0'");
+}
+
+function version_90(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE tags (
+ id SERIAL PRIMARY KEY,
+ name VARCHAR(255) NOT NULL,
+ project_id INTEGER NOT NULL,
+ UNIQUE(project_id, name)
+ )
+ ");
+
+ $pdo->exec("
+ CREATE TABLE task_has_tags (
+ task_id INTEGER NOT NULL,
+ tag_id INTEGER NOT NULL,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE,
+ UNIQUE(tag_id, task_id)
+ )
+ ");
+}
function version_89(PDO $pdo)
{
diff --git a/app/Schema/Sql/mysql.sql b/app/Schema/Sql/mysql.sql
index 92ca3686..8d494dcf 100644
--- a/app/Schema/Sql/mysql.sql
+++ b/app/Schema/Sql/mysql.sql
@@ -45,6 +45,7 @@ CREATE TABLE `columns` (
`project_id` int(11) NOT NULL,
`task_limit` int(11) DEFAULT '0',
`description` text,
+ `hide_in_dashboard` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `idx_title_project` (`title`,`project_id`),
KEY `columns_project_idx` (`project_id`),
@@ -414,6 +415,17 @@ CREATE TABLE `swimlanes` (
CONSTRAINT `swimlanes_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `tags`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `tags` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `name` varchar(255) NOT NULL,
+ `project_id` int(11) NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `project_id` (`project_id`,`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `task_has_external_links`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -479,6 +491,18 @@ CREATE TABLE `task_has_metadata` (
CONSTRAINT `task_has_metadata_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `task_has_tags`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `task_has_tags` (
+ `task_id` int(11) NOT NULL,
+ `tag_id` int(11) NOT NULL,
+ UNIQUE KEY `tag_id` (`tag_id`,`task_id`),
+ KEY `task_id` (`task_id`),
+ CONSTRAINT `task_has_tags_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `task_has_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `tasks`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -647,7 +671,7 @@ CREATE TABLE `users` (
LOCK TABLES `settings` WRITE;
/*!40000 ALTER TABLE `settings` DISABLE KEYS */;
-INSERT INTO `settings` VALUES ('api_token','e8a7a983f25efa80e203d44a832c9570a5083d3fefa91366989c00e931d0',0,0),('application_currency','USD',0,0),('application_date_format','m/d/Y',0,0),('application_language','en_US',0,0),('application_stylesheet','',0,0),('application_timezone','UTC',0,0),('application_url','',0,0),('board_columns','',0,0),('board_highlight_period','172800',0,0),('board_private_refresh_interval','10',0,0),('board_public_refresh_interval','60',0,0),('calendar_project_tasks','date_started',0,0),('calendar_user_subtasks_time_tracking','0',0,0),('calendar_user_tasks','date_started',0,0),('cfd_include_closed_tasks','1',0,0),('default_color','yellow',0,0),('integration_gravatar','0',0,0),('password_reset','1',0,0),('project_categories','',0,0),('subtask_restriction','0',0,0),('subtask_time_tracking','1',0,0),('webhook_token','296892f9c821909a92df539b028fdb384e47c9f7a34a8f9cad598e0edbba',0,0),('webhook_url','',0,0);
+INSERT INTO `settings` VALUES ('api_token','4064ef3d26efa9a0ff78fa7067d8bb9d99323455128edd89e9dc7c53ed76',0,0),('application_currency','USD',0,0),('application_date_format','m/d/Y',0,0),('application_language','en_US',0,0),('application_stylesheet','',0,0),('application_timezone','UTC',0,0),('application_url','',0,0),('board_columns','',0,0),('board_highlight_period','172800',0,0),('board_private_refresh_interval','10',0,0),('board_public_refresh_interval','60',0,0),('calendar_project_tasks','date_started',0,0),('calendar_user_subtasks_time_tracking','0',0,0),('calendar_user_tasks','date_started',0,0),('cfd_include_closed_tasks','1',0,0),('default_color','yellow',0,0),('integration_gravatar','0',0,0),('password_reset','1',0,0),('project_categories','',0,0),('subtask_restriction','0',0,0),('subtask_time_tracking','1',0,0),('webhook_token','c8f53c0bcd8aead902ad04f180ffafd7889b9c0062c2d510e2297ef543b8',0,0),('webhook_url','',0,0);
/*!40000 ALTER TABLE `settings` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -676,4 +700,4 @@ UNLOCK TABLES;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$kliMGeKgDYtx9Igek9jGDu0eZM.KXivgzvqtnMuWMkjvZiIc.8p8S', 'app-admin');INSERT INTO schema_version VALUES ('110');
+INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$yUJ9QnhG.f47yO.YvWKo3eMAHULukpluDNTOF9.Z7QQg0vOfFRB6u', 'app-admin');INSERT INTO schema_version VALUES ('112');
diff --git a/app/Schema/Sql/postgres.sql b/app/Schema/Sql/postgres.sql
index 6c17c1b1..0add9c91 100644
--- a/app/Schema/Sql/postgres.sql
+++ b/app/Schema/Sql/postgres.sql
@@ -98,7 +98,8 @@ CREATE TABLE "columns" (
"position" integer,
"project_id" integer NOT NULL,
"task_limit" integer DEFAULT 0,
- "description" "text"
+ "description" "text",
+ "hide_in_dashboard" boolean DEFAULT false
);
@@ -740,6 +741,36 @@ ALTER SEQUENCE "swimlanes_id_seq" OWNED BY "swimlanes"."id";
--
+-- Name: tags; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "tags" (
+ "id" integer NOT NULL,
+ "name" character varying(255) NOT NULL,
+ "project_id" integer NOT NULL
+);
+
+
+--
+-- Name: tags_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "tags_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: tags_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "tags_id_seq" OWNED BY "tags"."id";
+
+
+--
-- Name: task_has_external_links; Type: TABLE; Schema: public; Owner: -
--
@@ -874,6 +905,16 @@ ALTER SEQUENCE "task_has_subtasks_id_seq" OWNED BY "subtasks"."id";
--
+-- Name: task_has_tags; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "task_has_tags" (
+ "task_id" integer NOT NULL,
+ "tag_id" integer NOT NULL
+);
+
+
+--
-- Name: tasks; Type: TABLE; Schema: public; Owner: -
--
@@ -1236,6 +1277,13 @@ ALTER TABLE ONLY "swimlanes" ALTER COLUMN "id" SET DEFAULT "nextval"('"swimlanes
-- Name: id; Type: DEFAULT; Schema: public; Owner: -
--
+ALTER TABLE ONLY "tags" ALTER COLUMN "id" SET DEFAULT "nextval"('"tags_id_seq"'::"regclass");
+
+
+--
+-- Name: id; Type: DEFAULT; Schema: public; Owner: -
+--
+
ALTER TABLE ONLY "task_has_external_links" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_external_links_id_seq"'::"regclass");
@@ -1545,6 +1593,22 @@ ALTER TABLE ONLY "swimlanes"
--
+-- Name: tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "tags"
+ ADD CONSTRAINT "tags_pkey" PRIMARY KEY ("id");
+
+
+--
+-- Name: tags_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "tags"
+ ADD CONSTRAINT "tags_project_id_name_key" UNIQUE ("project_id", "name");
+
+
+--
-- Name: task_has_external_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -1585,6 +1649,14 @@ ALTER TABLE ONLY "subtasks"
--
+-- Name: task_has_tags_tag_id_task_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "task_has_tags"
+ ADD CONSTRAINT "task_has_tags_tag_id_task_id_key" UNIQUE ("tag_id", "task_id");
+
+
+--
-- Name: tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -2031,6 +2103,22 @@ ALTER TABLE ONLY "subtasks"
--
+-- Name: task_has_tags_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "task_has_tags"
+ ADD CONSTRAINT "task_has_tags_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "tags"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: task_has_tags_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "task_has_tags"
+ ADD CONSTRAINT "task_has_tags_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "tasks"("id") ON DELETE CASCADE;
+
+
+--
-- Name: tasks_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
@@ -2155,8 +2243,8 @@ INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_high
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_public_refresh_interval', '60', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_private_refresh_interval', '10', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('board_columns', '', 0, 0);
-INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('webhook_token', '85b9f242e49f4c50176591a2f9b812c626384b89ff985a02068455a5be07', 0, 0);
-INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', '207d1aaeb9d6d5c01f9ef1e6d61baca86c4c66fdd0b95e76b5c5953681e4', 0, 0);
+INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('webhook_token', 'c9a7c2a4523f1724b2ca047c5685f8e2b26bba47eb69baf4f22d5d50d837', 0, 0);
+INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', 'c57a6cb1789269547b616454e4e2f06d3de0514f83baf8fa5b5a8af44a08', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_language', 'en_US', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_timezone', 'UTC', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('application_url', '', 0, 0);
@@ -2225,4 +2313,4 @@ SELECT pg_catalog.setval('links_id_seq', 11, true);
-- PostgreSQL database dump complete
--
-INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$kliMGeKgDYtx9Igek9jGDu0eZM.KXivgzvqtnMuWMkjvZiIc.8p8S', 'app-admin');INSERT INTO schema_version VALUES ('89');
+INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$yUJ9QnhG.f47yO.YvWKo3eMAHULukpluDNTOF9.Z7QQg0vOfFRB6u', 'app-admin');INSERT INTO schema_version VALUES ('91');
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index 9ded7ed9..2a7735ee 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -6,7 +6,34 @@ use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
use PDO;
-const VERSION = 101;
+const VERSION = 103;
+
+function version_103(PDO $pdo)
+{
+ $pdo->exec("ALTER TABLE columns ADD COLUMN hide_in_dashboard INTEGER DEFAULT 0 NOT NULL");
+}
+
+function version_102(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE tags (
+ id INTEGER PRIMARY KEY,
+ name TEXT NOT NULL,
+ project_id INTEGER NOT NULL,
+ UNIQUE(project_id, name)
+ )
+ ");
+
+ $pdo->exec("
+ CREATE TABLE task_has_tags (
+ task_id INTEGER NOT NULL,
+ tag_id INTEGER NOT NULL,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE,
+ UNIQUE(tag_id, task_id)
+ )
+ ");
+}
function version_101(PDO $pdo)
{
diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php
index 34202052..946fbf41 100644
--- a/app/ServiceProvider/ActionProvider.php
+++ b/app/ServiceProvider/ActionProvider.php
@@ -3,6 +3,9 @@
namespace Kanboard\ServiceProvider;
use Kanboard\Action\TaskAssignColorPriority;
+use Kanboard\Action\TaskAssignDueDateOnCreation;
+use Kanboard\Action\TaskMoveColumnClosed;
+use Kanboard\Action\TaskMoveColumnNotMovedPeriod;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Kanboard\Core\Action\ActionManager;
@@ -32,6 +35,7 @@ use Kanboard\Action\TaskMoveColumnUnAssigned;
use Kanboard\Action\TaskOpen;
use Kanboard\Action\TaskUpdateStartDate;
use Kanboard\Action\TaskCloseNoActivity;
+use Kanboard\Action\TaskCloseNoActivityColumn;
/**
* Action Provider
@@ -68,6 +72,7 @@ class ActionProvider implements ServiceProviderInterface
$container['actionManager']->register(new TaskClose($container));
$container['actionManager']->register(new TaskCloseColumn($container));
$container['actionManager']->register(new TaskCloseNoActivity($container));
+ $container['actionManager']->register(new TaskCloseNoActivityColumn($container));
$container['actionManager']->register(new TaskCreation($container));
$container['actionManager']->register(new TaskDuplicateAnotherProject($container));
$container['actionManager']->register(new TaskEmail($container));
@@ -75,9 +80,12 @@ class ActionProvider implements ServiceProviderInterface
$container['actionManager']->register(new TaskMoveAnotherProject($container));
$container['actionManager']->register(new TaskMoveColumnAssigned($container));
$container['actionManager']->register(new TaskMoveColumnCategoryChange($container));
+ $container['actionManager']->register(new TaskMoveColumnClosed($container));
+ $container['actionManager']->register(new TaskMoveColumnNotMovedPeriod($container));
$container['actionManager']->register(new TaskMoveColumnUnAssigned($container));
$container['actionManager']->register(new TaskOpen($container));
$container['actionManager']->register(new TaskUpdateStartDate($container));
+ $container['actionManager']->register(new TaskAssignDueDateOnCreation($container));
return $container;
}
diff --git a/app/ServiceProvider/ApiProvider.php b/app/ServiceProvider/ApiProvider.php
index 93b3c7f5..d5d1f260 100644
--- a/app/ServiceProvider/ApiProvider.php
+++ b/app/ServiceProvider/ApiProvider.php
@@ -3,26 +3,29 @@
namespace Kanboard\ServiceProvider;
use JsonRPC\Server;
-use Kanboard\Api\ActionApi;
-use Kanboard\Api\AppApi;
-use Kanboard\Api\BoardApi;
-use Kanboard\Api\CategoryApi;
-use Kanboard\Api\ColumnApi;
-use Kanboard\Api\CommentApi;
-use Kanboard\Api\FileApi;
-use Kanboard\Api\GroupApi;
-use Kanboard\Api\GroupMemberApi;
-use Kanboard\Api\LinkApi;
-use Kanboard\Api\MeApi;
-use Kanboard\Api\Middleware\AuthenticationApiMiddleware;
-use Kanboard\Api\ProjectApi;
-use Kanboard\Api\ProjectPermissionApi;
-use Kanboard\Api\SubtaskApi;
-use Kanboard\Api\SubtaskTimeTrackingApi;
-use Kanboard\Api\SwimlaneApi;
-use Kanboard\Api\TaskApi;
-use Kanboard\Api\TaskLinkApi;
-use Kanboard\Api\UserApi;
+use Kanboard\Api\Procedure\ActionProcedure;
+use Kanboard\Api\Procedure\AppProcedure;
+use Kanboard\Api\Procedure\BoardProcedure;
+use Kanboard\Api\Procedure\CategoryProcedure;
+use Kanboard\Api\Procedure\ColumnProcedure;
+use Kanboard\Api\Procedure\CommentProcedure;
+use Kanboard\Api\Procedure\ProjectFileProcedure;
+use Kanboard\Api\Procedure\TaskExternalLinkProcedure;
+use Kanboard\Api\Procedure\TaskFileProcedure;
+use Kanboard\Api\Procedure\GroupProcedure;
+use Kanboard\Api\Procedure\GroupMemberProcedure;
+use Kanboard\Api\Procedure\LinkProcedure;
+use Kanboard\Api\Procedure\MeProcedure;
+use Kanboard\Api\Middleware\AuthenticationMiddleware;
+use Kanboard\Api\Procedure\ProjectProcedure;
+use Kanboard\Api\Procedure\ProjectPermissionProcedure;
+use Kanboard\Api\Procedure\SubtaskProcedure;
+use Kanboard\Api\Procedure\SubtaskTimeTrackingProcedure;
+use Kanboard\Api\Procedure\SwimlaneProcedure;
+use Kanboard\Api\Procedure\TaskMetadataProcedure;
+use Kanboard\Api\Procedure\TaskProcedure;
+use Kanboard\Api\Procedure\TaskLinkProcedure;
+use Kanboard\Api\Procedure\UserProcedure;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
@@ -45,31 +48,35 @@ class ApiProvider implements ServiceProviderInterface
$server = new Server();
$server->setAuthenticationHeader(API_AUTHENTICATION_HEADER);
$server->getMiddlewareHandler()
- ->withMiddleware(new AuthenticationApiMiddleware($container))
+ ->withMiddleware(new AuthenticationMiddleware($container))
;
$server->getProcedureHandler()
- ->withObject(new MeApi($container))
- ->withObject(new ActionApi($container))
- ->withObject(new AppApi($container))
- ->withObject(new BoardApi($container))
- ->withObject(new ColumnApi($container))
- ->withObject(new CategoryApi($container))
- ->withObject(new CommentApi($container))
- ->withObject(new FileApi($container))
- ->withObject(new LinkApi($container))
- ->withObject(new ProjectApi($container))
- ->withObject(new ProjectPermissionApi($container))
- ->withObject(new SubtaskApi($container))
- ->withObject(new SubtaskTimeTrackingApi($container))
- ->withObject(new SwimlaneApi($container))
- ->withObject(new TaskApi($container))
- ->withObject(new TaskLinkApi($container))
- ->withObject(new UserApi($container))
- ->withObject(new GroupApi($container))
- ->withObject(new GroupMemberApi($container))
+ ->withObject(new MeProcedure($container))
+ ->withObject(new ActionProcedure($container))
+ ->withObject(new AppProcedure($container))
+ ->withObject(new BoardProcedure($container))
+ ->withObject(new ColumnProcedure($container))
+ ->withObject(new CategoryProcedure($container))
+ ->withObject(new CommentProcedure($container))
+ ->withObject(new TaskFileProcedure($container))
+ ->withObject(new ProjectFileProcedure($container))
+ ->withObject(new LinkProcedure($container))
+ ->withObject(new ProjectProcedure($container))
+ ->withObject(new ProjectPermissionProcedure($container))
+ ->withObject(new SubtaskProcedure($container))
+ ->withObject(new SubtaskTimeTrackingProcedure($container))
+ ->withObject(new SwimlaneProcedure($container))
+ ->withObject(new TaskProcedure($container))
+ ->withObject(new TaskLinkProcedure($container))
+ ->withObject(new TaskExternalLinkProcedure($container))
+ ->withObject(new TaskMetadataProcedure($container))
+ ->withObject(new UserProcedure($container))
+ ->withObject(new GroupProcedure($container))
+ ->withObject(new GroupMemberProcedure($container))
+ ->withBeforeMethod('beforeProcedure')
;
-
+
$container['api'] = $server;
return $container;
}
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index 2fad8a3a..978bc05b 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -46,9 +46,13 @@ class AuthenticationProvider implements ServiceProviderInterface
$container['projectAccessMap'] = $this->getProjectAccessMap();
$container['applicationAccessMap'] = $this->getApplicationAccessMap();
+ $container['apiAccessMap'] = $this->getApiAccessMap();
+ $container['apiProjectAccessMap'] = $this->getApiProjectAccessMap();
$container['projectAuthorization'] = new Authorization($container['projectAccessMap']);
$container['applicationAuthorization'] = new Authorization($container['applicationAccessMap']);
+ $container['apiAuthorization'] = new Authorization($container['apiAccessMap']);
+ $container['apiProjectAuthorization'] = new Authorization($container['apiProjectAccessMap']);
return $container;
}
@@ -88,6 +92,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('ProjectFileController', '*', Role::PROJECT_MEMBER);
$acl->add('ProjectUserOverviewController', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectStatusController', '*', Role::PROJECT_MANAGER);
+ $acl->add('ProjectTagController', '*', Role::PROJECT_MANAGER);
$acl->add('SubtaskController', '*', Role::PROJECT_MEMBER);
$acl->add('SubtaskRestrictionController', '*', Role::PROJECT_MEMBER);
$acl->add('SubtaskStatusController', '*', Role::PROJECT_MEMBER);
@@ -131,6 +136,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('AvatarFileController', 'show', Role::APP_PUBLIC);
$acl->add('ConfigController', '*', Role::APP_ADMIN);
+ $acl->add('TagController', '*', Role::APP_ADMIN);
$acl->add('PluginController', '*', Role::APP_ADMIN);
$acl->add('CurrencyController', '*', Role::APP_ADMIN);
$acl->add('ProjectGanttController', '*', Role::APP_MANAGER);
@@ -149,4 +155,59 @@ class AuthenticationProvider implements ServiceProviderInterface
return $acl;
}
+
+ /**
+ * Get ACL for the API
+ *
+ * @access public
+ * @return AccessMap
+ */
+ public function getApiAccessMap()
+ {
+ $acl = new AccessMap;
+ $acl->setDefaultRole(Role::APP_USER);
+ $acl->setRoleHierarchy(Role::APP_ADMIN, array(Role::APP_MANAGER, Role::APP_USER, Role::APP_PUBLIC));
+ $acl->setRoleHierarchy(Role::APP_MANAGER, array(Role::APP_USER, Role::APP_PUBLIC));
+
+ $acl->add('UserProcedure', '*', Role::APP_ADMIN);
+ $acl->add('GroupMemberProcedure', '*', Role::APP_ADMIN);
+ $acl->add('GroupProcedure', '*', Role::APP_ADMIN);
+ $acl->add('LinkProcedure', '*', Role::APP_ADMIN);
+ $acl->add('TaskProcedure', array('getOverdueTasks'), Role::APP_ADMIN);
+ $acl->add('ProjectProcedure', array('getAllProjects'), Role::APP_ADMIN);
+ $acl->add('ProjectProcedure', array('createProject'), Role::APP_MANAGER);
+
+ return $acl;
+ }
+
+ /**
+ * Get ACL for the API
+ *
+ * @access public
+ * @return AccessMap
+ */
+ public function getApiProjectAccessMap()
+ {
+ $acl = new AccessMap;
+ $acl->setDefaultRole(Role::PROJECT_VIEWER);
+ $acl->setRoleHierarchy(Role::PROJECT_MANAGER, array(Role::PROJECT_MEMBER, Role::PROJECT_VIEWER));
+ $acl->setRoleHierarchy(Role::PROJECT_MEMBER, array(Role::PROJECT_VIEWER));
+
+ $acl->add('ActionProcedure', array('removeAction', 'getActions', 'createAction'), Role::PROJECT_MANAGER);
+ $acl->add('CategoryProcedure', '*', Role::PROJECT_MANAGER);
+ $acl->add('ColumnProcedure', '*', Role::PROJECT_MANAGER);
+ $acl->add('CommentProcedure', array('removeComment', 'createComment', 'updateComment'), Role::PROJECT_MEMBER);
+ $acl->add('ProjectPermissionProcedure', '*', Role::PROJECT_MANAGER);
+ $acl->add('ProjectProcedure', array('updateProject', 'removeProject', 'enableProject', 'disableProject', 'enableProjectPublicAccess', 'disableProjectPublicAccess'), Role::PROJECT_MANAGER);
+ $acl->add('SubtaskProcedure', '*', Role::PROJECT_MEMBER);
+ $acl->add('SubtaskTimeTrackingProcedure', '*', Role::PROJECT_MEMBER);
+ $acl->add('SwimlaneProcedure', '*', Role::PROJECT_MANAGER);
+ $acl->add('ProjectFileProcedure', '*', Role::PROJECT_MEMBER);
+ $acl->add('TaskFileProcedure', '*', Role::PROJECT_MEMBER);
+ $acl->add('TaskLinkProcedure', '*', Role::PROJECT_MEMBER);
+ $acl->add('TaskExternalLinkProcedure', array('createExternalTaskLink', 'updateExternalTaskLink', 'removeExternalTaskLink'), Role::PROJECT_MEMBER);
+ $acl->add('TaskProcedure', '*', Role::PROJECT_MEMBER);
+
+ return $acl;
+ }
}
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index 3e6efb02..aab41c74 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -55,15 +55,25 @@ class ClassProvider implements ServiceProviderInterface
'ProjectNotificationModel',
'ProjectMetadataModel',
'ProjectGroupRoleModel',
+ 'ProjectTaskDuplicationModel',
+ 'ProjectTaskPriorityModel',
'ProjectUserRoleModel',
'RememberMeSessionModel',
'SubtaskModel',
+ 'SubtaskPositionModel',
+ 'SubtaskStatusModel',
+ 'SubtaskTaskConversionModel',
'SubtaskTimeTrackingModel',
'SwimlaneModel',
+ 'TagDuplicationModel',
+ 'TagModel',
'TaskModel',
'TaskAnalyticModel',
'TaskCreationModel',
'TaskDuplicationModel',
+ 'TaskProjectDuplicationModel',
+ 'TaskProjectMoveModel',
+ 'TaskRecurrenceModel',
'TaskExternalLinkModel',
'TaskFinderModel',
'TaskFileModel',
@@ -71,6 +81,7 @@ class ClassProvider implements ServiceProviderInterface
'TaskModificationModel',
'TaskPositionModel',
'TaskStatusModel',
+ 'TaskTagModel',
'TaskMetadataModel',
'TimezoneModel',
'TransitionModel',
@@ -97,8 +108,9 @@ class ClassProvider implements ServiceProviderInterface
'ProjectValidator',
'SubtaskValidator',
'SwimlaneValidator',
- 'TaskValidator',
+ 'TagValidator',
'TaskLinkValidator',
+ 'TaskValidator',
'UserValidator',
),
'Import' => array(
@@ -110,6 +122,12 @@ class ClassProvider implements ServiceProviderInterface
'TaskExport',
'TransitionExport',
),
+ 'Pagination' => array(
+ 'TaskPagination',
+ 'SubtaskPagination',
+ 'ProjectPagination',
+ 'UserPagination',
+ ),
'Core' => array(
'DateParser',
'Lexer',
diff --git a/app/ServiceProvider/EventDispatcherProvider.php b/app/ServiceProvider/EventDispatcherProvider.php
index 57543fe4..ebf42cbf 100644
--- a/app/ServiceProvider/EventDispatcherProvider.php
+++ b/app/ServiceProvider/EventDispatcherProvider.php
@@ -11,7 +11,6 @@ use Kanboard\Subscriber\BootstrapSubscriber;
use Kanboard\Subscriber\NotificationSubscriber;
use Kanboard\Subscriber\ProjectDailySummarySubscriber;
use Kanboard\Subscriber\ProjectModificationDateSubscriber;
-use Kanboard\Subscriber\SubtaskTimeTrackingSubscriber;
use Kanboard\Subscriber\TransitionSubscriber;
use Kanboard\Subscriber\RecurringTaskSubscriber;
@@ -31,7 +30,6 @@ class EventDispatcherProvider implements ServiceProviderInterface
$container['dispatcher']->addSubscriber(new ProjectDailySummarySubscriber($container));
$container['dispatcher']->addSubscriber(new ProjectModificationDateSubscriber($container));
$container['dispatcher']->addSubscriber(new NotificationSubscriber($container));
- $container['dispatcher']->addSubscriber(new SubtaskTimeTrackingSubscriber($container));
$container['dispatcher']->addSubscriber(new TransitionSubscriber($container));
$container['dispatcher']->addSubscriber(new RecurringTaskSubscriber($container));
diff --git a/app/ServiceProvider/FilterProvider.php b/app/ServiceProvider/FilterProvider.php
index cdef9ed8..443d3588 100644
--- a/app/ServiceProvider/FilterProvider.php
+++ b/app/ServiceProvider/FilterProvider.php
@@ -21,11 +21,14 @@ use Kanboard\Filter\TaskDueDateFilter;
use Kanboard\Filter\TaskIdFilter;
use Kanboard\Filter\TaskLinkFilter;
use Kanboard\Filter\TaskModificationDateFilter;
+use Kanboard\Filter\TaskMovedDateFilter;
+use Kanboard\Filter\TaskPriorityFilter;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Filter\TaskReferenceFilter;
use Kanboard\Filter\TaskStatusFilter;
use Kanboard\Filter\TaskSubtaskAssigneeFilter;
use Kanboard\Filter\TaskSwimlaneFilter;
+use Kanboard\Filter\TaskTagFilter;
use Kanboard\Filter\TaskTitleFilter;
use Kanboard\Model\ProjectModel;
use Kanboard\Model\ProjectGroupRoleModel;
@@ -136,6 +139,7 @@ class FilterProvider implements ServiceProviderInterface
->withFilter(TaskColorFilter::getInstance()
->setColorModel($c['colorModel'])
)
+ ->withFilter(new TaskPriorityFilter())
->withFilter(new TaskColumnFilter())
->withFilter(new TaskCommentFilter())
->withFilter(TaskCreationDateFilter::getInstance()
@@ -155,6 +159,9 @@ class FilterProvider implements ServiceProviderInterface
->withFilter(TaskModificationDateFilter::getInstance()
->setDateParser($c['dateParser'])
)
+ ->withFilter(TaskMovedDateFilter::getInstance()
+ ->setDateParser($c['dateParser'])
+ )
->withFilter(new TaskProjectFilter())
->withFilter(new TaskReferenceFilter())
->withFilter(new TaskStatusFilter())
@@ -163,6 +170,9 @@ class FilterProvider implements ServiceProviderInterface
->setDatabase($c['db'])
)
->withFilter(new TaskSwimlaneFilter())
+ ->withFilter(TaskTagFilter::getInstance()
+ ->setDatabase($c['db'])
+ )
->withFilter(new TaskTitleFilter(), true)
;
diff --git a/app/ServiceProvider/JobProvider.php b/app/ServiceProvider/JobProvider.php
new file mode 100644
index 00000000..2194b11c
--- /dev/null
+++ b/app/ServiceProvider/JobProvider.php
@@ -0,0 +1,67 @@
+<?php
+
+namespace Kanboard\ServiceProvider;
+
+use Kanboard\Job\CommentEventJob;
+use Kanboard\Job\NotificationJob;
+use Kanboard\Job\ProjectFileEventJob;
+use Kanboard\Job\ProjectMetricJob;
+use Kanboard\Job\SubtaskEventJob;
+use Kanboard\Job\TaskEventJob;
+use Kanboard\Job\TaskFileEventJob;
+use Kanboard\Job\TaskLinkEventJob;
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+
+/**
+ * Class JobProvider
+ *
+ * @package Kanboard\ServiceProvider
+ * @author Frederic Guillot
+ */
+class JobProvider implements ServiceProviderInterface
+{
+ /**
+ * Register providers
+ *
+ * @access public
+ * @param \Pimple\Container $container
+ * @return \Pimple\Container
+ */
+ public function register(Container $container)
+ {
+ $container['commentEventJob'] = $container->factory(function ($c) {
+ return new CommentEventJob($c);
+ });
+
+ $container['subtaskEventJob'] = $container->factory(function ($c) {
+ return new SubtaskEventJob($c);
+ });
+
+ $container['taskEventJob'] = $container->factory(function ($c) {
+ return new TaskEventJob($c);
+ });
+
+ $container['taskFileEventJob'] = $container->factory(function ($c) {
+ return new TaskFileEventJob($c);
+ });
+
+ $container['taskLinkEventJob'] = $container->factory(function ($c) {
+ return new TaskLinkEventJob($c);
+ });
+
+ $container['projectFileEventJob'] = $container->factory(function ($c) {
+ return new ProjectFileEventJob($c);
+ });
+
+ $container['notificationJob'] = $container->factory(function ($c) {
+ return new NotificationJob($c);
+ });
+
+ $container['projectMetricJob'] = $container->factory(function ($c) {
+ return new ProjectMetricJob($c);
+ });
+
+ return $container;
+ }
+}
diff --git a/app/ServiceProvider/QueueProvider.php b/app/ServiceProvider/QueueProvider.php
index 946b436a..570f2e77 100644
--- a/app/ServiceProvider/QueueProvider.php
+++ b/app/ServiceProvider/QueueProvider.php
@@ -15,9 +15,11 @@ use Pimple\ServiceProviderInterface;
class QueueProvider implements ServiceProviderInterface
{
/**
- * Registers services on the given container.
+ * Register providers
*
- * @param Container $container
+ * @access public
+ * @param \Pimple\Container $container
+ * @return \Pimple\Container
*/
public function register(Container $container)
{
diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php
index 3d1391df..8801e3d0 100644
--- a/app/ServiceProvider/RouteProvider.php
+++ b/app/ServiceProvider/RouteProvider.php
@@ -59,6 +59,7 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('project/:project_id/duplicate', 'ProjectViewController', 'duplicate');
$container['route']->addRoute('project/:project_id/permissions', 'ProjectPermissionController', 'index');
$container['route']->addRoute('project/:project_id/activity', 'ActivityController', 'project');
+ $container['route']->addRoute('project/:project_id/tags', 'ProjectTagController', 'index');
// Project Overview
$container['route']->addRoute('project/:project_id/overview', 'ProjectOverviewController', 'show');
@@ -174,6 +175,7 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('settings/api', 'ConfigController', 'api');
$container['route']->addRoute('settings/links', 'LinkController', 'index');
$container['route']->addRoute('settings/currencies', 'CurrencyController', 'index');
+ $container['route']->addRoute('settings/tags', 'TagController', 'index');
// Plugins
$container['route']->addRoute('extensions', 'PluginController', 'show');
diff --git a/app/Subscriber/BaseSubscriber.php b/app/Subscriber/BaseSubscriber.php
index fdea29f6..92441962 100644
--- a/app/Subscriber/BaseSubscriber.php
+++ b/app/Subscriber/BaseSubscriber.php
@@ -12,28 +12,4 @@ use Kanboard\Core\Base;
*/
class BaseSubscriber extends Base
{
- /**
- * Method called
- *
- * @access private
- * @var array
- */
- private $called = array();
-
- /**
- * Check if a listener has been executed
- *
- * @access public
- * @param string $key
- * @return boolean
- */
- public function isExecuted($key = '')
- {
- if (isset($this->called[$key])) {
- return true;
- }
-
- $this->called[$key] = true;
- return false;
- }
}
diff --git a/app/Subscriber/BootstrapSubscriber.php b/app/Subscriber/BootstrapSubscriber.php
index 7d12e9ae..3618f30f 100644
--- a/app/Subscriber/BootstrapSubscriber.php
+++ b/app/Subscriber/BootstrapSubscriber.php
@@ -21,7 +21,7 @@ class BootstrapSubscriber extends BaseSubscriber implements EventSubscriberInter
$this->actionManager->attachEvents();
if ($this->userSession->isLogged()) {
- $this->sessionStorage->hasSubtaskInProgress = $this->subtaskModel->hasSubtaskInProgress($this->userSession->getId());
+ $this->sessionStorage->hasSubtaskInProgress = $this->subtaskStatusModel->hasSubtaskInProgress($this->userSession->getId());
}
}
diff --git a/app/Subscriber/NotificationSubscriber.php b/app/Subscriber/NotificationSubscriber.php
index db11e585..7cc68b26 100644
--- a/app/Subscriber/NotificationSubscriber.php
+++ b/app/Subscriber/NotificationSubscriber.php
@@ -3,7 +3,7 @@
namespace Kanboard\Subscriber;
use Kanboard\Event\GenericEvent;
-use Kanboard\Job\NotificationJob;
+use Kanboard\Model\TaskLinkModel;
use Kanboard\Model\TaskModel;
use Kanboard\Model\CommentModel;
use Kanboard\Model\SubtaskModel;
@@ -26,21 +26,20 @@ class NotificationSubscriber extends BaseSubscriber implements EventSubscriberIn
TaskModel::EVENT_ASSIGNEE_CHANGE => 'handleEvent',
SubtaskModel::EVENT_CREATE => 'handleEvent',
SubtaskModel::EVENT_UPDATE => 'handleEvent',
+ SubtaskModel::EVENT_DELETE => 'handleEvent',
CommentModel::EVENT_CREATE => 'handleEvent',
CommentModel::EVENT_UPDATE => 'handleEvent',
+ CommentModel::EVENT_DELETE => 'handleEvent',
CommentModel::EVENT_USER_MENTION => 'handleEvent',
TaskFileModel::EVENT_CREATE => 'handleEvent',
+ TaskLinkModel::EVENT_CREATE_UPDATE => 'handleEvent',
+ TaskLinkModel::EVENT_DELETE => 'handleEvent',
);
}
public function handleEvent(GenericEvent $event, $eventName)
{
- if (!$this->isExecuted($eventName)) {
- $this->logger->debug('Subscriber executed: ' . __METHOD__);
-
- $this->queueManager->push(NotificationJob::getInstance($this->container)
- ->withParams($event, $eventName, get_class($event))
- );
- }
+ $this->logger->debug('Subscriber executed: ' . __METHOD__);
+ $this->queueManager->push($this->notificationJob->withParams($event, $eventName));
}
}
diff --git a/app/Subscriber/ProjectDailySummarySubscriber.php b/app/Subscriber/ProjectDailySummarySubscriber.php
index 6971a121..eaa9d468 100644
--- a/app/Subscriber/ProjectDailySummarySubscriber.php
+++ b/app/Subscriber/ProjectDailySummarySubscriber.php
@@ -3,7 +3,6 @@
namespace Kanboard\Subscriber;
use Kanboard\Event\TaskEvent;
-use Kanboard\Job\ProjectMetricJob;
use Kanboard\Model\TaskModel;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -22,9 +21,7 @@ class ProjectDailySummarySubscriber extends BaseSubscriber implements EventSubsc
public function execute(TaskEvent $event)
{
- if (isset($event['project_id']) && !$this->isExecuted()) {
- $this->logger->debug('Subscriber executed: '.__METHOD__);
- $this->queueManager->push(ProjectMetricJob::getInstance($this->container)->withParams($event['project_id']));
- }
+ $this->logger->debug('Subscriber executed: '.__METHOD__);
+ $this->queueManager->push($this->projectMetricJob->withParams($event['task']['project_id']));
}
}
diff --git a/app/Subscriber/ProjectModificationDateSubscriber.php b/app/Subscriber/ProjectModificationDateSubscriber.php
index fee04eaa..1ffe0248 100644
--- a/app/Subscriber/ProjectModificationDateSubscriber.php
+++ b/app/Subscriber/ProjectModificationDateSubscriber.php
@@ -24,9 +24,7 @@ class ProjectModificationDateSubscriber extends BaseSubscriber implements EventS
public function execute(GenericEvent $event)
{
- if (isset($event['project_id']) && !$this->isExecuted()) {
- $this->logger->debug('Subscriber executed: '.__METHOD__);
- $this->projectModel->updateModificationDate($event['project_id']);
- }
+ $this->logger->debug('Subscriber executed: '.__METHOD__);
+ $this->projectModel->updateModificationDate($event['task']['project_id']);
}
}
diff --git a/app/Subscriber/RecurringTaskSubscriber.php b/app/Subscriber/RecurringTaskSubscriber.php
index 75b7ff76..3e2848f8 100644
--- a/app/Subscriber/RecurringTaskSubscriber.php
+++ b/app/Subscriber/RecurringTaskSubscriber.php
@@ -19,12 +19,13 @@ class RecurringTaskSubscriber extends BaseSubscriber implements EventSubscriberI
public function onMove(TaskEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
+ $task = $event['task'];
- if ($event['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) {
- if ($event['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_FIRST_COLUMN && $this->columnModel->getFirstColumnId($event['project_id']) == $event['src_column_id']) {
- $this->taskDuplicationModel->duplicateRecurringTask($event['task_id']);
- } elseif ($event['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_LAST_COLUMN && $this->columnModel->getLastColumnId($event['project_id']) == $event['dst_column_id']) {
- $this->taskDuplicationModel->duplicateRecurringTask($event['task_id']);
+ if ($task['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) {
+ if ($task['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_FIRST_COLUMN && $this->columnModel->getFirstColumnId($task['project_id']) == $event['src_column_id']) {
+ $this->taskRecurrenceModel->duplicateRecurringTask($task['id']);
+ } elseif ($task['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_LAST_COLUMN && $this->columnModel->getLastColumnId($task['project_id']) == $event['dst_column_id']) {
+ $this->taskRecurrenceModel->duplicateRecurringTask($task['id']);
}
}
}
@@ -32,9 +33,10 @@ class RecurringTaskSubscriber extends BaseSubscriber implements EventSubscriberI
public function onClose(TaskEvent $event)
{
$this->logger->debug('Subscriber executed: '.__METHOD__);
+ $task = $event['task'];
- if ($event['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING && $event['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_CLOSE) {
- $this->taskDuplicationModel->duplicateRecurringTask($event['task_id']);
+ if ($task['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING && $task['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_CLOSE) {
+ $this->taskRecurrenceModel->duplicateRecurringTask($event['task_id']);
}
}
}
diff --git a/app/Subscriber/SubtaskTimeTrackingSubscriber.php b/app/Subscriber/SubtaskTimeTrackingSubscriber.php
deleted file mode 100644
index 7e39c126..00000000
--- a/app/Subscriber/SubtaskTimeTrackingSubscriber.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-namespace Kanboard\Subscriber;
-
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Kanboard\Model\SubtaskModel;
-use Kanboard\Event\SubtaskEvent;
-
-class SubtaskTimeTrackingSubscriber extends BaseSubscriber implements EventSubscriberInterface
-{
- public static function getSubscribedEvents()
- {
- return array(
- SubtaskModel::EVENT_CREATE => 'updateTaskTime',
- SubtaskModel::EVENT_DELETE => 'updateTaskTime',
- SubtaskModel::EVENT_UPDATE => array(
- array('logStartEnd', 10),
- array('updateTaskTime', 0),
- )
- );
- }
-
- public function updateTaskTime(SubtaskEvent $event)
- {
- if (isset($event['task_id'])) {
- $this->logger->debug('Subscriber executed: '.__METHOD__);
- $this->subtaskTimeTrackingModel->updateTaskTimeTracking($event['task_id']);
- }
- }
-
- public function logStartEnd(SubtaskEvent $event)
- {
- if (isset($event['status']) && $this->configModel->get('subtask_time_tracking') == 1) {
- $this->logger->debug('Subscriber executed: '.__METHOD__);
- $subtask = $this->subtaskModel->getById($event['id']);
-
- if (empty($subtask['user_id'])) {
- return false;
- }
-
- if ($subtask['status'] == SubtaskModel::STATUS_INPROGRESS) {
- return $this->subtaskTimeTrackingModel->logStartTime($subtask['id'], $subtask['user_id']);
- } else {
- return $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $subtask['user_id']);
- }
- }
- }
-}
diff --git a/app/Template/action/index.php b/app/Template/action/index.php
index 0a94e4f0..085ea3ad 100644
--- a/app/Template/action/index.php
+++ b/app/Template/action/index.php
@@ -15,7 +15,7 @@
<?php if (empty($actions)): ?>
<p class="alert"><?= t('There is no action at the moment.') ?></p>
<?php else: ?>
- <table>
+ <table class="table-scrolling">
<tr>
<th><?= t('Automatic actions') ?></th>
<th><?= t('Action parameters') ?></th>
diff --git a/app/Template/activity/task.php b/app/Template/activity/task.php
index 04c64c63..39953d1a 100644
--- a/app/Template/activity/task.php
+++ b/app/Template/activity/task.php
@@ -1,9 +1,12 @@
-<div class="task-show-title color-<?= $task['color_id'] ?>">
- <h2><?= $this->text->e($task['title']) ?></h2>
-</div>
+<?= $this->render('task/details', array(
+ 'task' => $task,
+ 'tags' => $tags,
+ 'project' => $project,
+ 'editable' => false,
+)) ?>
<div class="page-header">
<h2><?= t('Activity stream') ?></h2>
</div>
-<?= $this->render('event/events', array('events' => $events)) ?> \ No newline at end of file
+<?= $this->render('event/events', array('events' => $events)) ?>
diff --git a/app/Template/analytic/avg_time_columns.php b/app/Template/analytic/avg_time_columns.php
index 5f6c6b35..91c269fc 100644
--- a/app/Template/analytic/avg_time_columns.php
+++ b/app/Template/analytic/avg_time_columns.php
@@ -9,7 +9,7 @@
<div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>' data-label="<?= t('Average time spent') ?>"></div>
- <table class="table-stripped">
+ <table class="table-striped">
<tr>
<th><?= t('Column') ?></th>
<th><?= t('Average time spent') ?></th>
diff --git a/app/Template/analytic/compare_hours.php b/app/Template/analytic/compare_hours.php
index 70d8d02b..c0b9cfc3 100644
--- a/app/Template/analytic/compare_hours.php
+++ b/app/Template/analytic/compare_hours.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= t('Compare Estimated Time vs Actual Time') ?></h2>
+ <h2><?= t('Estimated vs actual time') ?></h2>
</div>
<div class="listing">
@@ -23,7 +23,7 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No tasks found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
- <table class="table-fixed table-small">
+ <table class="table-fixed table-small table-scrolling">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
<th><?= $paginator->order(t('Title'), 'tasks.title') ?></th>
diff --git a/app/Template/analytic/sidebar.php b/app/Template/analytic/sidebar.php
index de3dccf8..0f5ee101 100644
--- a/app/Template/analytic/sidebar.php
+++ b/app/Template/analytic/sidebar.php
@@ -1,5 +1,4 @@
<div class="sidebar">
- <h2><?= t('Reportings') ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('AnalyticController', 'tasks') ?>>
<?= $this->url->link(t('Task distribution'), 'AnalyticController', 'tasks', array('project_id' => $project['id'])) ?>
diff --git a/app/Template/analytic/tasks.php b/app/Template/analytic/tasks.php
index 9e7b1fd7..4bc19784 100644
--- a/app/Template/analytic/tasks.php
+++ b/app/Template/analytic/tasks.php
@@ -9,7 +9,7 @@
<div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>'></div>
- <table>
+ <table class="table-striped">
<tr>
<th><?= t('Column') ?></th>
<th><?= t('Number of tasks') ?></th>
diff --git a/app/Template/analytic/users.php b/app/Template/analytic/users.php
index 9d1d3a1e..91bec11b 100644
--- a/app/Template/analytic/users.php
+++ b/app/Template/analytic/users.php
@@ -9,7 +9,7 @@
<div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>'></div>
- <table>
+ <table class="table-striped">
<tr>
<th><?= t('User') ?></th>
<th><?= t('Number of tasks') ?></th>
diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php
index f7a9f6ad..c0b71eab 100644
--- a/app/Template/board/table_column.php
+++ b/app/Template/board/table_column.php
@@ -5,9 +5,9 @@
<!-- column in collapsed mode -->
<div class="board-column-collapsed">
- <span class="board-column-header-task-count" title="<?= t('Show this column') ?>">
+ <small class="board-column-header-task-count" title="<?= t('Show this column') ?>">
<span id="task-number-column-<?= $column['id'] ?>"><?= $column['nb_tasks'] ?></span>
- </span>
+ </small>
</div>
<!-- column in expanded mode -->
@@ -18,9 +18,9 @@
</div>
<?php endif ?>
- <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['nb_column_tasks'])): ?>
+ <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['column_nb_tasks'])): ?>
<span title="<?= t('Total number of tasks in this column across all swimlanes') ?>" class="board-column-header-task-count">
- (<span><?= $column['nb_column_tasks'] ?></span>)
+ (<span><?= $column['column_nb_tasks'] ?></span>)
</span>
<?php endif ?>
@@ -47,6 +47,7 @@
</li>
<?php endif ?>
<?php endif ?>
+ <?= $this->hook->render('template:board:column:dropdown', array('swimlane' => $swimlane, 'column' => $column)) ?>
</ul>
</span>
<?php endif ?>
diff --git a/app/Template/board/table_tasks.php b/app/Template/board/table_tasks.php
index fd9ce5e7..1651f5d6 100644
--- a/app/Template/board/table_tasks.php
+++ b/app/Template/board/table_tasks.php
@@ -4,7 +4,8 @@
<td class="
board-column-<?= $column['id'] ?>
<?= $column['task_limit'] > 0 && $column['nb_tasks'] > $column['task_limit'] ? 'board-task-list-limit' : '' ?>
- ">
+ "
+ >
<!-- tasks list -->
<div class="board-task-list board-column-expanded" data-column-id="<?= $column['id'] ?>" data-swimlane-id="<?= $swimlane['id'] ?>" data-task-limit="<?= $column['task_limit'] ?>">
diff --git a/app/Template/board/task_avatar.php b/app/Template/board/task_avatar.php
index 14b55476..28e0813e 100644
--- a/app/Template/board/task_avatar.php
+++ b/app/Template/board/task_avatar.php
@@ -3,7 +3,7 @@
<span
<?php if ($this->user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?>
class="task-board-assignee task-board-change-assignee"
- data-url="<?= $this->url->href('TaskPopoverController', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
+ data-url="<?= $this->url->href('TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
<?php else: ?>
class="task-board-assignee">
<?php endif ?>
diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php
index f6cbff70..bc34363c 100644
--- a/app/Template/board/task_footer.php
+++ b/app/Template/board/task_footer.php
@@ -6,8 +6,8 @@
<?php else: ?>
<?= $this->url->link(
$this->text->e($task['category_name']),
- 'TaskPopoverController',
- 'changeCategory',
+ 'TaskModificationController',
+ 'edit',
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
false,
'popover' . (! empty($task['category_description']) ? ' tooltip' : ''),
@@ -18,6 +18,16 @@
</div>
<?php endif ?>
+<?php if (! empty($task['tags'])): ?>
+ <div class="task-tags">
+ <ul>
+ <?php foreach ($task['tags'] as $tag): ?>
+ <li><?= $this->text->e($tag['name']) ?></li>
+ <?php endforeach ?>
+ </ul>
+ </div>
+<?php endif ?>
+
<div class="task-board-icons">
<?php if ($task['score']): ?>
<span class="task-score" title="<?= t('Complexity') ?>">
@@ -27,7 +37,7 @@
<?php endif ?>
<?php if (! empty($task['date_due'])): ?>
- <?php if (date('d') == date('d', $task['date_due'])): ?>
+ <?php if (date('Y-m-d') == date('Y-m-d', $task['date_due'])): ?>
<span class="task-board-date task-board-date-today">
<?php elseif (time() > $task['date_due']): ?>
<span class="task-board-date task-board-date-overdue">
diff --git a/app/Template/board/tooltip_external_links.php b/app/Template/board/tooltip_external_links.php
index 65331864..a9f1fc7f 100644
--- a/app/Template/board/tooltip_external_links.php
+++ b/app/Template/board/tooltip_external_links.php
@@ -1,5 +1,5 @@
<div class="tooltip-large">
- <table>
+ <table class="table-small">
<tr>
<th class="column-20"><?= t('Type') ?></th>
<th class="column-70"><?= t('Title') ?></th>
diff --git a/app/Template/board/tooltip_files.php b/app/Template/board/tooltip_files.php
index 6f9e2640..4e704dac 100644
--- a/app/Template/board/tooltip_files.php
+++ b/app/Template/board/tooltip_files.php
@@ -1,5 +1,5 @@
<div class="tooltip-large">
- <table>
+ <table class="table-small">
<?php foreach ($files as $file): ?>
<tr>
<th>
diff --git a/app/Template/board/tooltip_subtasks.php b/app/Template/board/tooltip_subtasks.php
index 0322d373..8d5bc059 100644
--- a/app/Template/board/tooltip_subtasks.php
+++ b/app/Template/board/tooltip_subtasks.php
@@ -1,5 +1,5 @@
<div class="tooltip-large">
- <table>
+ <table class="table-small">
<tr>
<th class="column-80"><?= t('Subtask') ?></th>
<th><?= t('Assignee') ?></th>
diff --git a/app/Template/board/tooltip_tasklinks.php b/app/Template/board/tooltip_tasklinks.php
index d1156cbe..08432e71 100644
--- a/app/Template/board/tooltip_tasklinks.php
+++ b/app/Template/board/tooltip_tasklinks.php
@@ -1,5 +1,5 @@
<div class="tooltip-large">
- <table>
+ <table class="table-small">
<?php foreach ($links as $label => $grouped_links): ?>
<tr>
<th colspan="4"><?= t($label) ?></th>
diff --git a/app/Template/category/index.php b/app/Template/category/index.php
index a103d89f..ac60d9a8 100644
--- a/app/Template/category/index.php
+++ b/app/Template/category/index.php
@@ -2,7 +2,7 @@
<div class="page-header">
<h2><?= t('Categories') ?></h2>
</div>
-<table>
+<table class="table-striped">
<tr>
<th><?= t('Category Name') ?></th>
<th class="column-8"><?= t('Actions') ?></th>
diff --git a/app/Template/column/create.php b/app/Template/column/create.php
index 023de525..812e9139 100644
--- a/app/Template/column/create.php
+++ b/app/Template/column/create.php
@@ -13,6 +13,8 @@
<?= $this->form->label(t('Task limit'), 'task_limit') ?>
<?= $this->form->number('task_limit', $values, $errors) ?>
+ <?= $this->form->checkbox('hide_in_dashboard', t('Hide tasks in this column in the dashboard'), 1) ?>
+
<?= $this->form->label(t('Description'), 'description') ?>
<?= $this->form->textarea('description', $values, $errors, array(), 'markdown-editor') ?>
diff --git a/app/Template/column/edit.php b/app/Template/column/edit.php
index a742e4b9..89487298 100644
--- a/app/Template/column/edit.php
+++ b/app/Template/column/edit.php
@@ -15,6 +15,8 @@
<?= $this->form->label(t('Task limit'), 'task_limit') ?>
<?= $this->form->number('task_limit', $values, $errors) ?>
+ <?= $this->form->checkbox('hide_in_dashboard', t('Hide tasks in this column in the dashboard'), 1, $values['hide_in_dashboard'] == 1) ?>
+
<?= $this->form->label(t('Description'), 'description') ?>
<?= $this->form->textarea('description', $values, $errors, array(), 'markdown-editor') ?>
diff --git a/app/Template/column/index.php b/app/Template/column/index.php
index 04760a16..66890ba5 100644
--- a/app/Template/column/index.php
+++ b/app/Template/column/index.php
@@ -12,7 +12,7 @@
<p class="alert alert-error"><?= t('Your board doesn\'t have any columns!') ?></p>
<?php else: ?>
<table
- class="columns-table table-stripped"
+ class="columns-table table-striped"
data-save-position-url="<?= $this->url->href('ColumnController', 'move', array('project_id' => $project['id'])) ?>">
<thead>
<tr>
diff --git a/app/Template/comment/show.php b/app/Template/comment/show.php
index 8419a14e..16a807bc 100644
--- a/app/Template/comment/show.php
+++ b/app/Template/comment/show.php
@@ -4,10 +4,10 @@
<div class="comment-title">
<?php if (! empty($comment['username'])): ?>
- <span class="comment-username"><?= $this->text->e($comment['name'] ?: $comment['username']) ?></span>
+ <strong class="comment-username"><?= $this->text->e($comment['name'] ?: $comment['username']) ?></strong>
<?php endif ?>
- <span class="comment-date"><?= $this->dt->datetime($comment['date_creation']) ?></span>
+ <small class="comment-date"><?= $this->dt->datetime($comment['date_creation']) ?></small>
</div>
<div class="comment-content">
diff --git a/app/Template/comments/show.php b/app/Template/comments/show.php
index 43f6b2c2..5c6d8e20 100644
--- a/app/Template/comments/show.php
+++ b/app/Template/comments/show.php
@@ -5,8 +5,10 @@
<div class="accordion-content" id="comments">
<?php if (!isset($is_public) || !$is_public): ?>
<div class="comment-sorting">
- <i class="fa fa-sort"></i>
- <?= $this->url->link(t('change sorting'), 'CommentController', 'toggleSorting', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <small>
+ <i class="fa fa-sort"></i>
+ <?= $this->url->link(t('change sorting'), 'CommentController', 'toggleSorting', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </small>
</div>
<?php endif ?>
<?php foreach ($comments as $comment): ?>
diff --git a/app/Template/config/about.php b/app/Template/config/about.php
index 8e2d1325..8d5a575d 100644
--- a/app/Template/config/about.php
+++ b/app/Template/config/about.php
@@ -9,7 +9,7 @@
</li>
<li>
<?= t('Author:') ?>
- <strong>Frédéric Guillot</strong> (<a href="https://github.com/fguillot/kanboard/blob/master/CONTRIBUTORS.md" target="_blank"><?= t('contributors') ?></a>)
+ <strong>Frédéric Guillot</strong> (<a href="https://github.com/kanboard/kanboard/blob/master/CONTRIBUTORS.md" target="_blank"><?= t('contributors') ?></a>)
</li>
<li>
<?= t('License:') ?>
diff --git a/app/Template/config/keyboard_shortcuts.php b/app/Template/config/keyboard_shortcuts.php
index da532661..1b1a9477 100644
--- a/app/Template/config/keyboard_shortcuts.php
+++ b/app/Template/config/keyboard_shortcuts.php
@@ -19,7 +19,6 @@
<h3><?= t('Task view') ?></h3>
<ul>
<li><?= t('Edit task') ?> = <strong>e</strong></li>
- <li><?= t('Edit description') ?> = <strong>d</strong></li>
<li><?= t('New subtask') ?> = <strong>s</strong></li>
<li><?= t('New comment') ?> = <strong>c</strong></li>
<li><?= t('New internal link') ?> = <strong>l</strong></li>
@@ -33,4 +32,4 @@
<li><?= t('Close dialog box') ?> = <strong>ESC</strong></li>
<li><?= t('Submit a form') ?> = <strong>CTRL+ENTER</strong> <?= t('or') ?> <strong>⌘+ENTER</strong></li>
</ul>
-</div> \ No newline at end of file
+</div>
diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php
index 29caa0ef..239edc19 100644
--- a/app/Template/config/sidebar.php
+++ b/app/Template/config/sidebar.php
@@ -1,5 +1,4 @@
<div class="sidebar">
- <h2><?= t('Actions') ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('ConfigController', 'index') ?>>
<?= $this->url->link(t('About'), 'ConfigController', 'index') ?>
@@ -19,6 +18,9 @@
<li <?= $this->app->checkMenuSelection('ConfigController', 'calendar') ?>>
<?= $this->url->link(t('Calendar settings'), 'ConfigController', 'calendar') ?>
</li>
+ <li <?= $this->app->checkMenuSelection('TagController', 'index') ?>>
+ <?= $this->url->link(t('Tags management'), 'TagController', 'index') ?>
+ </li>
<li <?= $this->app->checkMenuSelection('LinkController') ?>>
<?= $this->url->link(t('Link settings'), 'LinkController', 'index') ?>
</li>
diff --git a/app/Template/currency/index.php b/app/Template/currency/index.php
index 9881cee5..db9b21af 100644
--- a/app/Template/currency/index.php
+++ b/app/Template/currency/index.php
@@ -4,7 +4,7 @@
<?php if (! empty($rates)): ?>
-<table class="table-stripped">
+<table class="table-striped">
<tr>
<th class="column-35"><?= t('Currency') ?></th>
<th><?= t('Rate') ?></th>
diff --git a/app/Template/custom_filter/index.php b/app/Template/custom_filter/index.php
index 08c8040c..dcab891b 100644
--- a/app/Template/custom_filter/index.php
+++ b/app/Template/custom_filter/index.php
@@ -3,7 +3,7 @@
<h2><?= t('Custom filters') ?></h2>
</div>
<div>
- <table>
+ <table class="table-striped table-scrolling">
<tr>
<th class="column-15"><?= t('Name') ?></th>
<th class="column-30"><?= t('Filter') ?></th>
diff --git a/app/Template/dashboard/notifications.php b/app/Template/dashboard/notifications.php
index e0e9b878..a189d74f 100644
--- a/app/Template/dashboard/notifications.php
+++ b/app/Template/dashboard/notifications.php
@@ -13,7 +13,7 @@
</ul>
</div>
- <table class="table-fixed table-small">
+ <table class="table-striped table-scrolling table-small">
<tr>
<th><?= t('Notification') ?></th>
<th class="column-20"><?= t('Date') ?></th>
@@ -36,10 +36,8 @@
<i class="fa fa-file-o fa-fw"></i>
<?php endif ?>
- <?php if ($this->text->contains($notification['event_name'], 'task.overdue')): ?>
- <?php if (count($notification['event_data']['tasks']) > 1): ?>
- <?= $notification['title'] ?>
- <?php endif ?>
+ <?php if ($this->text->contains($notification['event_name'], 'task.overdue') && count($notification['event_data']['tasks']) > 1): ?>
+ <?= $notification['title'] ?>
<?php else: ?>
<?= $this->url->link($notification['title'], 'WebNotificationController', 'redirect', array('notification_id' => $notification['id'], 'user_id' => $user['id'])) ?>
<?php endif ?>
diff --git a/app/Template/dashboard/projects.php b/app/Template/dashboard/projects.php
index 962e4d83..f8806c01 100644
--- a/app/Template/dashboard/projects.php
+++ b/app/Template/dashboard/projects.php
@@ -4,10 +4,10 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('Your are not member of any project.') ?></p>
<?php else: ?>
- <table class="table-fixed table-small">
+ <table class="table-striped table-small table-scrolling">
<tr>
- <th class="column-5"><?= $paginator->order('Id', 'id') ?></th>
- <th class="column-3"><?= $paginator->order('<i class="fa fa-lock fa-fw" title="'.t('Private project').'"></i>', 'is_private') ?></th>
+ <th class="column-5"><?= $paginator->order('Id', \Kanboard\Model\ProjectModel::TABLE.'.id') ?></th>
+ <th class="column-3"><?= $paginator->order('<i class="fa fa-lock fa-fw" title="'.t('Private project').'"></i>', \Kanboard\Model\ProjectModel::TABLE.'.is_private') ?></th>
<th class="column-25"><?= $paginator->order(t('Project'), \Kanboard\Model\ProjectModel::TABLE.'.name') ?></th>
<th class="column-10"><?= t('Tasks') ?></th>
<th><?= t('Columns') ?></th>
@@ -43,7 +43,7 @@
<td class="dashboard-project-stats">
<?php foreach ($project['columns'] as $column): ?>
<strong title="<?= t('Task count') ?>"><?= $column['nb_tasks'] ?></strong>
- <span><?= $this->text->e($column['title']) ?></span>
+ <small><?= $this->text->e($column['title']) ?></small>
<?php endforeach ?>
</td>
diff --git a/app/Template/dashboard/show.php b/app/Template/dashboard/show.php
index 637b60f8..aec6f591 100644
--- a/app/Template/dashboard/show.php
+++ b/app/Template/dashboard/show.php
@@ -2,11 +2,18 @@
<form method="get" action="<?= $this->url->dir() ?>" class="search">
<?= $this->form->hidden('controller', array('controller' => 'SearchController')) ?>
<?= $this->form->hidden('action', array('action' => 'index')) ?>
- <?= $this->form->text('search', array(), array(), array('placeholder="'.t('Search').'"'), 'form-input-large') ?>
- <?= $this->render('app/filters_helper') ?>
+
+ <div class="input-addon">
+ <?= $this->form->text('search', array(), array(), array('placeholder="'.t('Search').'"'), 'input-addon-field') ?>
+ <div class="input-addon-item">
+ <?= $this->render('app/filters_helper') ?>
+ </div>
+ </div>
</form>
</div>
<?= $this->render('dashboard/projects', array('paginator' => $project_paginator, 'user' => $user)) ?>
<?= $this->render('dashboard/tasks', array('paginator' => $task_paginator, 'user' => $user)) ?>
<?= $this->render('dashboard/subtasks', array('paginator' => $subtask_paginator, 'user' => $user)) ?>
+
+<?= $this->hook->render('template:dashboard:show', array('user' => $user)) ?>
diff --git a/app/Template/dashboard/sidebar.php b/app/Template/dashboard/sidebar.php
index 86cc20f8..108c028a 100644
--- a/app/Template/dashboard/sidebar.php
+++ b/app/Template/dashboard/sidebar.php
@@ -1,5 +1,4 @@
<div class="sidebar">
- <h2><?= $this->text->e($user['name'] ?: $user['username']) ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('DashboardController', 'show') ?>>
<?= $this->url->link(t('Overview'), 'DashboardController', 'show', array('user_id' => $user['id'])) ?>
@@ -22,6 +21,6 @@
<li <?= $this->app->checkMenuSelection('DashboardController', 'notifications') ?>>
<?= $this->url->link(t('My notifications'), 'DashboardController', 'notifications', array('user_id' => $user['id'])) ?>
</li>
- <?= $this->hook->render('template:dashboard:sidebar') ?>
+ <?= $this->hook->render('template:dashboard:sidebar', array('user' => $user)) ?>
</ul>
</div>
diff --git a/app/Template/dashboard/subtasks.php b/app/Template/dashboard/subtasks.php
index 8e0aa3ce..b71deeb9 100644
--- a/app/Template/dashboard/subtasks.php
+++ b/app/Template/dashboard/subtasks.php
@@ -4,12 +4,12 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
<?php else: ?>
- <table class="table-fixed table-small">
+ <table class="table-striped table-small table-scrolling">
<tr>
- <th class="column-5"><?= $paginator->order('Id', 'tasks.id') ?></th>
+ <th class="column-5"><?= $paginator->order('Id', \Kanboard\Model\TaskModel::TABLE.'.id') ?></th>
<th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th>
<th><?= $paginator->order(t('Task'), 'task_name') ?></th>
- <th><?= $paginator->order(t('Subtask'), 'title') ?></th>
+ <th><?= $paginator->order(t('Subtask'), \Kanboard\Model\SubtaskModel::TABLE.'.title') ?></th>
<th class="column-20"><?= t('Time tracking') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $subtask): ?>
diff --git a/app/Template/dashboard/tasks.php b/app/Template/dashboard/tasks.php
index 4b83a96a..427b903d 100644
--- a/app/Template/dashboard/tasks.php
+++ b/app/Template/dashboard/tasks.php
@@ -4,14 +4,14 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing assigned to you.') ?></p>
<?php else: ?>
- <table class="table-fixed table-small">
+ <table class="table-striped table-small table-scrolling">
<tr>
- <th class="column-5"><?= $paginator->order('Id', 'tasks.id') ?></th>
+ <th class="column-5"><?= $paginator->order('Id', \Kanboard\Model\TaskModel::TABLE.'.id') ?></th>
<th class="column-20"><?= $paginator->order(t('Project'), 'project_name') ?></th>
- <th><?= $paginator->order(t('Task'), 'title') ?></th>
- <th class="column-5"><?= $paginator->order('Priority', 'tasks.priority') ?></th>
+ <th><?= $paginator->order(t('Task'), \Kanboard\Model\TaskModel::TABLE.'.title') ?></th>
+ <th class="column-8"><?= $paginator->order(t('Priority'), \Kanboard\Model\TaskModel::TABLE.'.priority') ?></th>
<th class="column-20"><?= t('Time tracking') ?></th>
- <th class="column-10"><?= $paginator->order(t('Due date'), 'date_due') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Due date'), \Kanboard\Model\TaskModel::TABLE.'.date_due') ?></th>
<th class="column-10"><?= $paginator->order(t('Column'), 'column_title') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $task): ?>
diff --git a/app/Template/event/comment_create.php b/app/Template/event/comment_create.php
index 45132e6d..780bba93 100644
--- a/app/Template/event/comment_create.php
+++ b/app/Template/event/comment_create.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/comment_delete.php b/app/Template/event/comment_delete.php
new file mode 100644
index 00000000..e3a2f9fa
--- /dev/null
+++ b/app/Template/event/comment_delete.php
@@ -0,0 +1,11 @@
+<p class="activity-title">
+ <?= e('%s removed a comment on the task %s',
+ $this->text->e($author),
+ $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
+ ) ?>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
+</p>
+<div class="activity-description">
+ <p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
+ <div class="markdown"><?= $this->text->markdown($comment['comment']) ?></div>
+</div>
diff --git a/app/Template/event/comment_update.php b/app/Template/event/comment_update.php
index 5a0821bd..9e25ec2d 100644
--- a/app/Template/event/comment_update.php
+++ b/app/Template/event/comment_update.php
@@ -3,8 +3,11 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
+ <?php if (! empty($comment['comment'])): ?>
+ <div class="markdown"><?= $this->text->markdown($comment['comment']) ?></div>
+ <?php endif ?>
</div>
diff --git a/app/Template/event/subtask_create.php b/app/Template/event/subtask_create.php
index 1bf36c05..9a115c73 100644
--- a/app/Template/event/subtask_create.php
+++ b/app/Template/event/subtask_create.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/subtask_delete.php b/app/Template/event/subtask_delete.php
new file mode 100644
index 00000000..7f0d6d58
--- /dev/null
+++ b/app/Template/event/subtask_delete.php
@@ -0,0 +1,15 @@
+<p class="activity-title">
+ <?= e('%s removed a subtask for the task %s',
+ $this->text->e($author),
+ $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
+ ) ?>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
+</p>
+<div class="activity-description">
+ <p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
+ <ul>
+ <li>
+ <?= $this->text->e($subtask['title']) ?> (<strong><?= $this->text->e($subtask['status_name']) ?></strong>)
+ </li>
+ </ul>
+</div>
diff --git a/app/Template/event/subtask_update.php b/app/Template/event/subtask_update.php
index 201402f6..e566022e 100644
--- a/app/Template/event/subtask_update.php
+++ b/app/Template/event/subtask_update.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_assignee_change.php b/app/Template/event/task_assignee_change.php
index 7c962223..405f8ac1 100644
--- a/app/Template/event/task_assignee_change.php
+++ b/app/Template/event/task_assignee_change.php
@@ -8,9 +8,9 @@
$this->text->e($assignee)
) ?>
<?php else: ?>
- <?= e('%s remove the assignee of the task %s', $this->text->e($author), $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))) ?>
+ <?= e('%s removed the assignee of the task %s', $this->text->e($author), $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))) ?>
<?php endif ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_close.php b/app/Template/event/task_close.php
index 90ff9207..1ac81ea9 100644
--- a/app/Template/event/task_close.php
+++ b/app/Template/event/task_close.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_create.php b/app/Template/event/task_create.php
index 017a5ada..9d0ff358 100644
--- a/app/Template/event/task_create.php
+++ b/app/Template/event/task_create.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_file_create.php b/app/Template/event/task_file_create.php
index d329529a..7e58fdc1 100644
--- a/app/Template/event/task_file_create.php
+++ b/app/Template/event/task_file_create.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($file['name']) ?></p>
diff --git a/app/Template/event/task_internal_link_create_update.php b/app/Template/event/task_internal_link_create_update.php
new file mode 100644
index 00000000..4bc6ae9a
--- /dev/null
+++ b/app/Template/event/task_internal_link_create_update.php
@@ -0,0 +1,16 @@
+<p class="activity-title">
+ <?= e('%s set a new internal link for the task %s',
+ $this->text->e($author),
+ $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
+ ) ?>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
+</p>
+<div class="activity-description">
+ <p class="activity-task-title">
+ <?= e(
+ 'This task is now linked to the task %s with the relation "%s"',
+ $this->url->link(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id'])),
+ $this->text->e($task_link['label'])
+ ) ?>
+ </p>
+</div>
diff --git a/app/Template/event/task_internal_link_delete.php b/app/Template/event/task_internal_link_delete.php
new file mode 100644
index 00000000..3465fa57
--- /dev/null
+++ b/app/Template/event/task_internal_link_delete.php
@@ -0,0 +1,16 @@
+<p class="activity-title">
+ <?= e('%s removed an internal link for the task %s',
+ $this->text->e($author),
+ $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
+ ) ?>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
+</p>
+<div class="activity-description">
+ <p class="activity-task-title">
+ <?= e(
+ 'The link with the relation "%s" to the task %s have been removed',
+ $this->text->e($task_link['label']),
+ $this->url->link(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id']))
+ ) ?>
+ </p>
+</div>
diff --git a/app/Template/event/task_move_column.php b/app/Template/event/task_move_column.php
index f3155e47..e7e5ec28 100644
--- a/app/Template/event/task_move_column.php
+++ b/app/Template/event/task_move_column.php
@@ -4,7 +4,7 @@
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
$this->text->e($task['column_title'])
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_move_position.php b/app/Template/event/task_move_position.php
index ecdd02b6..48fbbb1e 100644
--- a/app/Template/event/task_move_position.php
+++ b/app/Template/event/task_move_position.php
@@ -5,7 +5,7 @@
$task['position'],
$this->text->e($task['column_title'])
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_move_swimlane.php b/app/Template/event/task_move_swimlane.php
index fe9bfb55..a467875b 100644
--- a/app/Template/event/task_move_swimlane.php
+++ b/app/Template/event/task_move_swimlane.php
@@ -11,7 +11,7 @@
$this->text->e($task['swimlane_name'])
) ?>
<?php endif ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_open.php b/app/Template/event/task_open.php
index 548aa98f..6d5252a1 100644
--- a/app/Template/event/task_open.php
+++ b/app/Template/event/task_open.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/event/task_update.php b/app/Template/event/task_update.php
index 7c7507c0..2608f623 100644
--- a/app/Template/event/task_update.php
+++ b/app/Template/event/task_update.php
@@ -3,7 +3,7 @@
$this->text->e($author),
$this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']))
) ?>
- <span class="activity-date"><?= $this->dt->datetime($date_creation) ?></span>
+ <small class="activity-date"><?= $this->dt->datetime($date_creation) ?></small>
</p>
<div class="activity-description">
<p class="activity-task-title"><?= $this->text->e($task['title']) ?></p>
diff --git a/app/Template/export/sidebar.php b/app/Template/export/sidebar.php
index 55fbaeef..463c0cee 100644
--- a/app/Template/export/sidebar.php
+++ b/app/Template/export/sidebar.php
@@ -1,5 +1,4 @@
<div class="sidebar">
- <h2><?= t('Exports') ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('ExportController', 'tasks') ?>>
<?= $this->url->link(t('Tasks'), 'ExportController', 'tasks', array('project_id' => $project['id'])) ?>
diff --git a/app/Template/export/subtasks.php b/app/Template/export/subtasks.php
index a82cb3d1..878a7132 100644
--- a/app/Template/export/subtasks.php
+++ b/app/Template/export/subtasks.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= t('Subtasks exportation for "%s"', $project['name']) ?></h2>
+ <h2><?= t('Subtasks export') ?></h2>
</div>
<p class="alert alert-info"><?= t('This report contains all subtasks information for the given date range.') ?></p>
@@ -21,4 +21,4 @@
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
</div>
-</form> \ No newline at end of file
+</form>
diff --git a/app/Template/export/summary.php b/app/Template/export/summary.php
index 60aa306f..d9362a9b 100644
--- a/app/Template/export/summary.php
+++ b/app/Template/export/summary.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= t('Daily project summary export for "%s"', $project['name']) ?></h2>
+ <h2><?= t('Daily project summary export') ?></h2>
</div>
<p class="alert alert-info"><?= t('This export contains the number of tasks per column grouped per day.') ?></p>
@@ -21,4 +21,4 @@
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
</div>
-</form> \ No newline at end of file
+</form>
diff --git a/app/Template/export/tasks.php b/app/Template/export/tasks.php
index bed8ab90..ae411326 100644
--- a/app/Template/export/tasks.php
+++ b/app/Template/export/tasks.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= t('Tasks exportation for "%s"', $project['name']) ?></h2>
+ <h2><?= t('Tasks exportation') ?></h2>
</div>
<p class="alert alert-info"><?= t('This report contains all tasks information for the given date range.') ?></p>
@@ -21,4 +21,4 @@
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
</div>
-</form> \ No newline at end of file
+</form>
diff --git a/app/Template/feed/project.php b/app/Template/feed/project.php
deleted file mode 100644
index 213a04d4..00000000
--- a/app/Template/feed/project.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?= '<?xml version="1.0" encoding="utf-8"?>' ?>
-<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
- <title><?= t('%s\'s activity', $project['name']) ?></title>
- <link rel="alternate" type="text/html" href="<?= $this->url->base() ?>"/>
- <link rel="self" type="application/atom+xml" href="<?= $this->url->href('FeedController', 'project', array('token' => $project['token']), false, '', true) ?>"/>
- <updated><?= date(DATE_ATOM) ?></updated>
- <id><?= $this->url->href('FeedController', 'project', array('token' => $project['token']), false, '', true) ?></id>
- <icon><?= $this->url->base() ?>assets/img/favicon.png</icon>
-
- <?php foreach ($events as $e): ?>
- <entry>
- <title type="text"><?= $e['event_title'] ?></title>
- <link rel="alternate" href="<?= $this->url->href('TaskViewController', 'show', array('task_id' => $e['task_id']), false, '', true) ?>"/>
- <id><?= $e['id'].'-'.$e['event_name'].'-'.$e['task_id'].'-'.$e['date_creation'] ?></id>
- <published><?= date(DATE_ATOM, $e['date_creation']) ?></published>
- <updated><?= date(DATE_ATOM, $e['date_creation']) ?></updated>
- <author>
- <name><?= $this->text->e($e['author']) ?></name>
- </author>
- <content type="html">
- <![CDATA[
- <?= $e['event_content'] ?>
- ]]>
- </content>
- </entry>
- <?php endforeach ?>
-</feed>
diff --git a/app/Template/feed/user.php b/app/Template/feed/user.php
deleted file mode 100644
index 0c45f03c..00000000
--- a/app/Template/feed/user.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?= '<?xml version="1.0" encoding="utf-8"?>' ?>
-<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
- <title><?= t('Project activities for %s', $user['name'] ?: $user['username']) ?></title>
- <link rel="alternate" type="text/html" href="<?= $this->url->base() ?>"/>
- <link rel="self" type="application/atom+xml" href="<?= $this->url->href('FeedController', 'user', array('token' => $user['token']), false, '', true) ?>"/>
- <updated><?= date(DATE_ATOM) ?></updated>
- <id><?= $this->url->href('FeedController', 'user', array('token' => $user['token']), false, '', true) ?></id>
- <icon><?= $this->url->base() ?>assets/img/favicon.png</icon>
-
- <?php foreach ($events as $e): ?>
- <entry>
- <title type="text"><?= $e['event_title'] ?></title>
- <link rel="alternate" href="<?= $this->url->href('TaskViewController', 'show', array('task_id' => $e['task_id']), false, '', true) ?>"/>
- <id><?= $e['id'].'-'.$e['event_name'].'-'.$e['task_id'].'-'.$e['date_creation'] ?></id>
- <published><?= date(DATE_ATOM, $e['date_creation']) ?></published>
- <updated><?= date(DATE_ATOM, $e['date_creation']) ?></updated>
- <author>
- <name><?= $this->text->e($e['author']) ?></name>
- </author>
- <content type="html">
- <![CDATA[
- <?= $e['event_content'] ?>
- ]]>
- </content>
- </entry>
- <?php endforeach ?>
-</feed>
diff --git a/app/Template/group/index.php b/app/Template/group/index.php
index 1062e18c..fe8a07e7 100644
--- a/app/Template/group/index.php
+++ b/app/Template/group/index.php
@@ -8,7 +8,7 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is no group.') ?></p>
<?php else: ?>
- <table class="table-small table-fixed">
+ <table class="table-small table-fixed table-scrolling">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'id') ?></th>
<th class="column-20"><?= $paginator->order(t('External Id'), 'external_id') ?></th>
diff --git a/app/Template/group/users.php b/app/Template/group/users.php
index a4895ab7..73597b39 100644
--- a/app/Template/group/users.php
+++ b/app/Template/group/users.php
@@ -8,7 +8,7 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is no user in this group.') ?></p>
<?php else: ?>
- <table>
+ <table class="table-striped table-scrolling">
<tr>
<th><?= $paginator->order(t('Id'), 'id') ?></th>
<th><?= $paginator->order(t('Username'), 'username') ?></th>
diff --git a/app/Template/header.php b/app/Template/header.php
index 13521ae7..2fe68ebe 100644
--- a/app/Template/header.php
+++ b/app/Template/header.php
@@ -1,118 +1,26 @@
-<header>
- <nav>
- <h1>
- <span class="logo">
- <?= $this->url->link('K<span>B</span>', 'DashboardController', 'show', array(), false, '', t('Dashboard')) ?>
- </span>
- <span class="title">
- <?php if (isset($project) && ! empty($project)): ?>
- <?= $this->url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?>
- <?php else: ?>
- <?= $this->text->e($title) ?>
- <?php endif ?>
- </span>
- <?php if (! empty($description)): ?>
- <span class="tooltip" title="<?= $this->text->markdownAttribute($description) ?>">
- <i class="fa fa-info-circle"></i>
- </span>
- <?php endif ?>
- </h1>
- <ul>
- <?php if (isset($board_selector) && ! empty($board_selector)): ?>
- <li>
- <select id="board-selector"
- class="chosen-select select-auto-redirect"
- tabindex="-1"
- data-search-threshold="0"
- data-notfound="<?= t('No results match:') ?>"
- data-placeholder="<?= t('Display another project') ?>"
- data-redirect-regex="PROJECT_ID"
- data-redirect-url="<?= $this->url->href('BoardViewController', 'show', array('project_id' => 'PROJECT_ID')) ?>">
- <option value=""></option>
- <?php foreach ($board_selector as $board_id => $board_name): ?>
- <option value="<?= $board_id ?>"><?= $this->text->e($board_name) ?></option>
- <?php endforeach ?>
- </select>
- </li>
- <?php endif ?>
- <li class="user-links">
- <?php if ($this->user->hasNotifications()): ?>
- <span class="notification">
- <?= $this->url->link('<i class="fa fa-bell web-notification-icon"></i>', 'DashboardController', 'notifications', array('user_id' => $this->user->getId()), false, '', t('Unread notifications')) ?>
- </span>
- <?php endif ?>
-
- <?php $has_project_creation_access = $this->user->hasAccess('ProjectCreationController', 'create'); ?>
- <?php $is_private_project_enabled = $this->app->config('disable_private_project', 0) == 0; ?>
-
- <?php if ($has_project_creation_access || (!$has_project_creation_access && $is_private_project_enabled)): ?>
- <div class="dropdown">
- <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-plus fa-fw"></i><i class="fa fa-caret-down"></i></a>
- <ul>
- <?php if ($has_project_creation_access): ?>
- <li><i class="fa fa-plus fa-fw"></i>
- <?= $this->url->link(t('New project'), 'ProjectCreationController', 'create', array(), false, 'popover') ?></li>
- <?php endif ?>
- <?php if ($is_private_project_enabled): ?>
- <li>
- <i class="fa fa-lock fa-fw"></i>
- <?= $this->url->link(t('New private project'), 'ProjectCreationController', 'createPrivate', array(), false, 'popover') ?>
- </li>
- <?php endif ?>
- </ul>
- </div>
- <?php endif ?>
+<?php $_title = $this->render('header/title', array(
+ 'project' => isset($project) ? $project : null,
+ 'task' => isset($task) ? $task : null,
+ 'description' => isset($description) ? $description : null,
+ 'title' => $title,
+)) ?>
- <div class="dropdown">
- <a href="#" class="dropdown-menu dropdown-menu-link-icon"><?= $this->avatar->currentUserSmall('avatar-inline') ?><i class="fa fa-caret-down"></i></a>
- <ul>
- <li class="no-hover"><strong><?= $this->text->e($this->user->getFullname()) ?></strong></li>
- <li>
- <i class="fa fa-tachometer fa-fw"></i>
- <?= $this->url->link(t('My dashboard'), 'DashboardController', 'show', array('user_id' => $this->user->getId())) ?>
- </li>
- <li>
- <i class="fa fa-home fa-fw"></i>
- <?= $this->url->link(t('My profile'), 'UserViewController', 'show', array('user_id' => $this->user->getId())) ?>
- </li>
- <li>
- <i class="fa fa-folder fa-fw"></i>
- <?= $this->url->link(t('Projects management'), 'ProjectListController', 'show') ?>
- </li>
- <?php if ($this->user->hasAccess('UserListController', 'show')): ?>
- <li>
- <i class="fa fa-user fa-fw"></i>
- <?= $this->url->link(t('Users management'), 'UserListController', 'show') ?>
- </li>
- <li>
- <i class="fa fa-group fa-fw"></i>
- <?= $this->url->link(t('Groups management'), 'GroupListController', 'index') ?>
- </li>
- <li>
- <i class="fa fa-cubes" aria-hidden="true"></i>
- <?= $this->url->link(t('Plugins'), 'PluginController', 'show') ?>
- </li>
- <li>
- <i class="fa fa-cog fa-fw"></i>
- <?= $this->url->link(t('Settings'), 'ConfigController', 'index') ?>
- </li>
- <?php endif ?>
+<?php $_top_right_corner = implode('&nbsp;', array(
+ $this->render('header/user_notifications'),
+ $this->render('header/creation_dropdown'),
+ $this->render('header/user_dropdown')
+ )) ?>
- <?= $this->hook->render('template:header:dropdown') ?>
-
- <li>
- <i class="fa fa-life-ring fa-fw"></i>
- <?= $this->url->link(t('Documentation'), 'DocumentationController', 'show') ?>
- </li>
- <?php if (! DISABLE_LOGOUT): ?>
- <li>
- <i class="fa fa-sign-out fa-fw"></i>
- <?= $this->url->link(t('Logout'), 'AuthController', 'logout') ?>
- </li>
- <?php endif ?>
- </ul>
- </div>
- </li>
- </ul>
- </nav>
+<header>
+ <div class="title-container">
+ <?= $_title ?>
+ </div>
+ <?php if (! empty($board_selector)): ?>
+ <div class="board-selector-container">
+ <?= $this->render('header/board_selector', array('board_selector' => $board_selector)) ?>
+ </div>
+ <?php endif ?>
+ <div class="menus-container pull-right">
+ <?= $_top_right_corner ?>
+ </div>
</header>
diff --git a/app/Template/header/board_selector.php b/app/Template/header/board_selector.php
new file mode 100644
index 00000000..b42d47f9
--- /dev/null
+++ b/app/Template/header/board_selector.php
@@ -0,0 +1,13 @@
+<select id="board-selector"
+ class="chosen-select select-auto-redirect"
+ tabindex="-1"
+ data-search-threshold="0"
+ data-notfound="<?= t('No results match:') ?>"
+ data-placeholder="<?= t('Display another project') ?>"
+ data-redirect-regex="PROJECT_ID"
+ data-redirect-url="<?= $this->url->href('BoardViewController', 'show', array('project_id' => 'PROJECT_ID')) ?>">
+ <option value=""></option>
+ <?php foreach ($board_selector as $board_id => $board_name): ?>
+ <option value="<?= $board_id ?>"><?= $this->text->e($board_name) ?></option>
+ <?php endforeach ?>
+</select>
diff --git a/app/Template/header/creation_dropdown.php b/app/Template/header/creation_dropdown.php
new file mode 100644
index 00000000..d3b9e7cb
--- /dev/null
+++ b/app/Template/header/creation_dropdown.php
@@ -0,0 +1,22 @@
+<?php $has_project_creation_access = $this->user->hasAccess('ProjectCreationController', 'create'); ?>
+<?php $is_private_project_enabled = $this->app->config('disable_private_project', 0) == 0; ?>
+
+<?php if ($has_project_creation_access || (!$has_project_creation_access && $is_private_project_enabled)): ?>
+ <div class="dropdown">
+ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-plus fa-fw"></i><i class="fa fa-caret-down"></i></a>
+ <ul>
+ <?php if ($has_project_creation_access): ?>
+ <li><i class="fa fa-plus fa-fw"></i>
+ <?= $this->url->link(t('New project'), 'ProjectCreationController', 'create', array(), false, 'popover') ?>
+ </li>
+ <?php endif ?>
+ <?php if ($is_private_project_enabled): ?>
+ <li>
+ <i class="fa fa-lock fa-fw"></i>
+ <?= $this->url->link(t('New private project'), 'ProjectCreationController', 'createPrivate', array(), false, 'popover') ?>
+ </li>
+ <?php endif ?>
+ <?= $this->hook->render('template:header:creation-dropdown') ?>
+ </ul>
+ </div>
+<?php endif ?>
diff --git a/app/Template/header/title.php b/app/Template/header/title.php
new file mode 100644
index 00000000..61c6ee9a
--- /dev/null
+++ b/app/Template/header/title.php
@@ -0,0 +1,17 @@
+<h1>
+ <span class="logo">
+ <?= $this->url->link('K<span>B</span>', 'DashboardController', 'show', array(), false, '', t('Dashboard')) ?>
+ </span>
+ <span class="title">
+ <?php if (! empty($project) && ! empty($task)): ?>
+ <?= $this->url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?>
+ <?php else: ?>
+ <?= $this->text->e($title) ?>
+ <?php endif ?>
+ </span>
+ <?php if (! empty($description)): ?>
+ <small class="tooltip" title="<?= $this->text->markdownAttribute($description) ?>">
+ <i class="fa fa-info-circle"></i>
+ </small>
+ <?php endif ?>
+</h1>
diff --git a/app/Template/header/user_dropdown.php b/app/Template/header/user_dropdown.php
new file mode 100644
index 00000000..49d08213
--- /dev/null
+++ b/app/Template/header/user_dropdown.php
@@ -0,0 +1,49 @@
+<div class="dropdown">
+ <a href="#" class="dropdown-menu dropdown-menu-link-icon"><?= $this->avatar->currentUserSmall('avatar-inline') ?><i class="fa fa-caret-down"></i></a>
+ <ul>
+ <li class="no-hover"><strong><?= $this->text->e($this->user->getFullname()) ?></strong></li>
+ <li>
+ <i class="fa fa-tachometer fa-fw"></i>
+ <?= $this->url->link(t('My dashboard'), 'DashboardController', 'show', array('user_id' => $this->user->getId())) ?>
+ </li>
+ <li>
+ <i class="fa fa-home fa-fw"></i>
+ <?= $this->url->link(t('My profile'), 'UserViewController', 'show', array('user_id' => $this->user->getId())) ?>
+ </li>
+ <li>
+ <i class="fa fa-folder fa-fw"></i>
+ <?= $this->url->link(t('Projects management'), 'ProjectListController', 'show') ?>
+ </li>
+ <?php if ($this->user->hasAccess('UserListController', 'show')): ?>
+ <li>
+ <i class="fa fa-user fa-fw"></i>
+ <?= $this->url->link(t('Users management'), 'UserListController', 'show') ?>
+ </li>
+ <li>
+ <i class="fa fa-group fa-fw"></i>
+ <?= $this->url->link(t('Groups management'), 'GroupListController', 'index') ?>
+ </li>
+ <li>
+ <i class="fa fa-cubes" aria-hidden="true"></i>
+ <?= $this->url->link(t('Plugins'), 'PluginController', 'show') ?>
+ </li>
+ <li>
+ <i class="fa fa-cog fa-fw"></i>
+ <?= $this->url->link(t('Settings'), 'ConfigController', 'index') ?>
+ </li>
+ <?php endif ?>
+
+ <?= $this->hook->render('template:header:dropdown') ?>
+
+ <li>
+ <i class="fa fa-life-ring fa-fw"></i>
+ <?= $this->url->link(t('Documentation'), 'DocumentationController', 'show') ?>
+ </li>
+ <?php if (! DISABLE_LOGOUT): ?>
+ <li>
+ <i class="fa fa-sign-out fa-fw"></i>
+ <?= $this->url->link(t('Logout'), 'AuthController', 'logout') ?>
+ </li>
+ <?php endif ?>
+ </ul>
+</div>
diff --git a/app/Template/header/user_notifications.php b/app/Template/header/user_notifications.php
new file mode 100644
index 00000000..83c545d2
--- /dev/null
+++ b/app/Template/header/user_notifications.php
@@ -0,0 +1,5 @@
+<?php if ($this->user->hasNotifications()): ?>
+ <span class="notification">
+ <?= $this->url->link('<i class="fa fa-bell web-notification-icon"></i>', 'DashboardController', 'notifications', array('user_id' => $this->user->getId()), false, '', t('Unread notifications')) ?>
+ </span>
+<?php endif ?>
diff --git a/app/Template/layout.php b/app/Template/layout.php
index 411237cb..8c85ffc6 100644
--- a/app/Template/layout.php
+++ b/app/Template/layout.php
@@ -15,7 +15,6 @@
<?= $this->asset->colorCss() ?>
<?= $this->asset->css('assets/css/vendor.min.css') ?>
<?= $this->asset->css('assets/css/app.min.css') ?>
- <?= $this->asset->css('assets/css/print.min.css', true, 'print') ?>
<?= $this->asset->customCss() ?>
<?php if (! isset($not_editable)): ?>
diff --git a/app/Template/link/index.php b/app/Template/link/index.php
index 7e32069a..70ead4a6 100644
--- a/app/Template/link/index.php
+++ b/app/Template/link/index.php
@@ -2,7 +2,7 @@
<h2><?= t('Link labels') ?></h2>
</div>
<?php if (! empty($links)): ?>
-<table>
+<table class="table-striped table-scrolling">
<tr>
<th class="column-70"><?= t('Link labels') ?></th>
<th><?= t('Actions') ?></th>
diff --git a/app/Template/notification/comment_delete.php b/app/Template/notification/comment_delete.php
new file mode 100644
index 00000000..928623ec
--- /dev/null
+++ b/app/Template/notification/comment_delete.php
@@ -0,0 +1,7 @@
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<h3><?= t('Comment removed') ?></h3>
+
+<?= $this->text->markdown($comment['comment']) ?>
+
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/app/Template/notification/subtask_delete.php b/app/Template/notification/subtask_delete.php
new file mode 100644
index 00000000..8c5f262c
--- /dev/null
+++ b/app/Template/notification/subtask_delete.php
@@ -0,0 +1,11 @@
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<h3><?= t('Subtask removed') ?></h3>
+
+<ul>
+ <li><?= t('Title:') ?> <?= $this->text->e($subtask['title']) ?></li>
+ <li><?= t('Status:') ?> <?= $this->text->e($subtask['status_name']) ?></li>
+ <li><?= t('Assignee:') ?> <?= $this->text->e($subtask['name'] ?: $subtask['username'] ?: '?') ?></li>
+</ul>
+
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/app/Template/notification/task_file_create.php b/app/Template/notification/task_file_create.php
index feab8dd2..c19f7279 100644
--- a/app/Template/notification/task_file_create.php
+++ b/app/Template/notification/task_file_create.php
@@ -2,4 +2,4 @@
<p><?= t('New attachment added "%s"', $file['name']) ?></p>
-<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/app/Template/notification/task_internal_link_create_update.php b/app/Template/notification/task_internal_link_create_update.php
new file mode 100644
index 00000000..73cad84d
--- /dev/null
+++ b/app/Template/notification/task_internal_link_create_update.php
@@ -0,0 +1,11 @@
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p>
+ <?= e(
+ 'This task is now linked to the task %s with the relation "%s"',
+ $this->url->link(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id'])),
+ $this->text->e($task_link['label'])
+ ) ?>
+</p>
+
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/app/Template/notification/task_internal_link_delete.php b/app/Template/notification/task_internal_link_delete.php
new file mode 100644
index 00000000..bb54e0a7
--- /dev/null
+++ b/app/Template/notification/task_internal_link_delete.php
@@ -0,0 +1,11 @@
+<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
+
+<p>
+ <?= e(
+ 'The link with the relation "%s" to the task %s have been removed',
+ $this->text->e($task_link['label']),
+ $this->url->link(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id']))
+ ) ?>
+</p>
+
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/app/Template/plugin/sidebar.php b/app/Template/plugin/sidebar.php
index e1b47632..dd1a2a6b 100644
--- a/app/Template/plugin/sidebar.php
+++ b/app/Template/plugin/sidebar.php
@@ -1,5 +1,4 @@
<div class="sidebar">
- <h2><?= t('Actions') ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('PluginController', 'show') ?>>
<?= $this->url->link(t('Installed Plugins'), 'PluginController', 'show') ?>
diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php
index 9bc0c9c4..3be0da48 100644
--- a/app/Template/project/sidebar.php
+++ b/app/Template/project/sidebar.php
@@ -1,5 +1,4 @@
<div class="sidebar">
- <h2><?= t('Actions') ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('ProjectViewController', 'show') ?>>
<?= $this->url->link(t('Summary'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?>
@@ -32,6 +31,9 @@
<li <?= $this->app->checkMenuSelection('CategoryController') ?>>
<?= $this->url->link(t('Categories'), 'CategoryController', 'index', array('project_id' => $project['id'])) ?>
</li>
+ <li <?= $this->app->checkMenuSelection('ProjectTagController') ?>>
+ <?= $this->url->link(t('Tags'), 'ProjectTagController', 'index', array('project_id' => $project['id'])) ?>
+ </li>
<?php if ($project['is_private'] == 0): ?>
<li <?= $this->app->checkMenuSelection('ProjectPermissionController') ?>>
<?= $this->url->link(t('Permissions'), 'ProjectPermissionController', 'index', array('project_id' => $project['id'])) ?>
diff --git a/app/Template/project_creation/create.php b/app/Template/project_creation/create.php
index 01d06bab..b90b15c4 100644
--- a/app/Template/project_creation/create.php
+++ b/app/Template/project_creation/create.php
@@ -19,13 +19,14 @@
<p class="alert"><?= t('Which parts of the project do you want to duplicate?') ?></p>
<?php if (! $is_private): ?>
- <?= $this->form->checkbox('projectPermission', t('Permissions'), 1, true) ?>
+ <?= $this->form->checkbox('projectPermissionModel', t('Permissions'), 1, true) ?>
<?php endif ?>
<?= $this->form->checkbox('categoryModel', t('Categories'), 1, true) ?>
+ <?= $this->form->checkbox('tagDuplicationModel', t('Tags'), 1, true) ?>
<?= $this->form->checkbox('actionModel', t('Actions'), 1, true) ?>
<?= $this->form->checkbox('swimlaneModel', t('Swimlanes'), 1, true) ?>
- <?= $this->form->checkbox('taskModel', t('Tasks'), 1, false) ?>
+ <?= $this->form->checkbox('projectTaskDuplicationModel', t('Tasks'), 1, false) ?>
</div>
<div class="form-actions">
diff --git a/app/Template/project_header/dropdown.php b/app/Template/project_header/dropdown.php
index 79a1b389..f8901289 100644
--- a/app/Template/project_header/dropdown.php
+++ b/app/Template/project_header/dropdown.php
@@ -20,14 +20,6 @@
<i class="fa fa-arrows-h fa-fw"></i> <a href="#" class="filter-toggle-scrolling" title="<?= t('Keyboard shortcut: "%s"', 'c') ?>"><?= t('Horizontal scrolling') ?></a>
</span>
</li>
- <li>
- <span class="filter-max-height" style="display: none">
- <i class="fa fa-arrows-v fa-fw"></i> <a href="#" class="filter-toggle-height"><?= t('Set maximum column height') ?></a>
- </span>
- <span class="filter-min-height">
- <i class="fa fa-arrows-v fa-fw"></i> <a href="#" class="filter-toggle-height"><?= t('Remove maximum column height') ?></a>
- </span>
- </li>
<?php endif ?>
<?php if ($this->user->hasProjectAccess('TaskCreationController', 'show', $project['id'])): ?>
diff --git a/app/Template/project_header/header.php b/app/Template/project_header/header.php
index aaa8137b..6c91e38b 100644
--- a/app/Template/project_header/header.php
+++ b/app/Template/project_header/header.php
@@ -1,15 +1,21 @@
<div class="project-header">
<?= $this->hook->render('template:project:header:before', array('project' => $project)) ?>
- <?= $this->render('project_header/dropdown', array('project' => $project, 'board_view' => $board_view)) ?>
- <?= $this->render('project_header/views', array('project' => $project, 'filters' => $filters)) ?>
- <?= $this->render('project_header/search', array(
- 'project' => $project,
- 'filters' => $filters,
- 'custom_filters_list' => isset($custom_filters_list) ? $custom_filters_list : array(),
- 'users_list' => isset($users_list) ? $users_list : array(),
- 'categories_list' => isset($categories_list) ? $categories_list : array(),
- )) ?>
+ <div class="dropdown-component">
+ <?= $this->render('project_header/dropdown', array('project' => $project, 'board_view' => $board_view)) ?>
+ </div>
+ <div class="views-switcher-component">
+ <?= $this->render('project_header/views', array('project' => $project, 'filters' => $filters)) ?>
+ </div>
+ <div class="filter-box-component">
+ <?= $this->render('project_header/search', array(
+ 'project' => $project,
+ 'filters' => $filters,
+ 'custom_filters_list' => isset($custom_filters_list) ? $custom_filters_list : array(),
+ 'users_list' => isset($users_list) ? $users_list : array(),
+ 'categories_list' => isset($categories_list) ? $categories_list : array(),
+ )) ?>
+ </div>
<?= $this->hook->render('template:project:header:after', array('project' => $project)) ?>
-</div> \ No newline at end of file
+</div>
diff --git a/app/Template/project_header/search.php b/app/Template/project_header/search.php
index 8885d9c9..512e88d7 100644
--- a/app/Template/project_header/search.php
+++ b/app/Template/project_header/search.php
@@ -3,43 +3,54 @@
<?= $this->form->hidden('controller', $filters) ?>
<?= $this->form->hidden('action', $filters) ?>
<?= $this->form->hidden('project_id', $filters) ?>
- <?= $this->form->text('search', $filters, array(), array('placeholder="'.t('Filter').'"')) ?>
- <?= $this->render('app/filters_helper', array('reset' => 'status:open', 'project' => $project)) ?>
+ <div class="input-addon">
+ <?= $this->form->text('search', $filters, array(), array('placeholder="'.t('Filter').'"'), 'input-addon-field') ?>
+ <div class="input-addon-item">
+ <?= $this->render('app/filters_helper', array('reset' => 'status:open', 'project' => $project)) ?>
+ </div>
- <?php if (isset($custom_filters_list) && ! empty($custom_filters_list)): ?>
- <div class="dropdown">
- <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('Custom filters') ?>"><i class="fa fa-bookmark fa-fw"></i><i class="fa fa-caret-down"></i></a>
- <ul>
- <?php foreach ($custom_filters_list as $filter): ?>
- <li><a href="#" class="filter-helper" data-<?php if ($filter['append']): ?><?= 'append-' ?><?php endif ?>filter='<?= $this->text->e($filter['filter']) ?>'><?= $this->text->e($filter['name']) ?></a></li>
- <?php endforeach ?>
- </ul>
- </div>
- <?php endif ?>
+ <?php if (isset($custom_filters_list) && ! empty($custom_filters_list)): ?>
+ <div class="input-addon-item">
+ <div class="dropdown">
+ <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('Custom filters') ?>"><i class="fa fa-bookmark fa-fw"></i><i class="fa fa-caret-down"></i></a>
+ <ul>
+ <?php foreach ($custom_filters_list as $filter): ?>
+ <li><a href="#" class="filter-helper" data-<?php if ($filter['append']): ?><?= 'append-' ?><?php endif ?>filter='<?= $this->text->e($filter['filter']) ?>'><?= $this->text->e($filter['name']) ?></a></li>
+ <?php endforeach ?>
+ </ul>
+ </div>
+ </div>
+ <?php endif ?>
- <?php if (isset($users_list)): ?>
- <div class="dropdown">
- <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('User filters') ?>"><i class="fa fa-users fa-fw"></i> <i class="fa fa-caret-down"></i></a>
- <ul>
- <li><a href="#" class="filter-helper" data-unique-filter="assignee:nobody"><?= t('Not assigned') ?></a></li>
- <?php foreach ($users_list as $user): ?>
- <li><a href="#" class="filter-helper" data-unique-filter='assignee:"<?= $this->text->e($user) ?>"'><?= $this->text->e($user) ?></a></li>
- <?php endforeach ?>
- </ul>
- </div>
- <?php endif ?>
+ <?php if (isset($users_list)): ?>
+ <div class="input-addon-item">
+ <div class="dropdown">
+ <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('User filters') ?>"><i class="fa fa-users fa-fw"></i> <i class="fa fa-caret-down"></i></a>
+ <ul>
+ <li><a href="#" class="filter-helper" data-unique-filter="assignee:nobody"><?= t('Not assigned') ?></a></li>
+ <?php foreach ($users_list as $user): ?>
+ <li><a href="#" class="filter-helper" data-unique-filter='assignee:"<?= $this->text->e($user) ?>"'><?= $this->text->e($user) ?></a></li>
+ <?php endforeach ?>
+ </ul>
+ </div>
+ </div>
+ <?php endif ?>
- <?php if (isset($categories_list) && ! empty($categories_list)): ?>
- <div class="dropdown">
- <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('Category filters') ?>"><i class="fa fa-tags fa-fw"></i><i class="fa fa-caret-down"></i></a>
- <ul>
- <li><a href="#" class="filter-helper" data-unique-filter="category:none"><?= t('No category') ?></a></li>
- <?php foreach ($categories_list as $category): ?>
- <li><a href="#" class="filter-helper" data-unique-filter='category:"<?= $this->text->e($category) ?>"'><?= $this->text->e($category) ?></a></li>
- <?php endforeach ?>
- </ul>
+ <?php if (isset($categories_list) && ! empty($categories_list)): ?>
+ <div class="input-addon-item">
+ <div class="dropdown">
+ <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('Category filters') ?>"><i class="fa fa-tags fa-fw"></i><i class="fa fa-caret-down"></i></a>
+ <ul>
+ <li><a href="#" class="filter-helper" data-unique-filter="category:none"><?= t('No category') ?></a></li>
+ <?php foreach ($categories_list as $category): ?>
+ <li><a href="#" class="filter-helper" data-unique-filter='category:"<?= $this->text->e($category) ?>"'><?= $this->text->e($category) ?></a></li>
+ <?php endforeach ?>
+ </ul>
+ </div>
+ </div>
+ <?php endif ?>
</div>
- <?php endif ?>
+
</form>
</div>
diff --git a/app/Template/project_list/show.php b/app/Template/project_list/show.php
index 8b9f1396..8e4c3e6a 100644
--- a/app/Template/project_list/show.php
+++ b/app/Template/project_list/show.php
@@ -12,13 +12,13 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No project') ?></p>
<?php else: ?>
- <table class="table-stripped table-small">
+ <table class="table-striped table-scrolling">
<tr>
- <th class="column-3"><?= $paginator->order(t('Id'), 'id') ?></th>
- <th class="column-5"><?= $paginator->order(t('Status'), 'is_active') ?></th>
+ <th class="column-5"><?= $paginator->order(t('Id'), 'id') ?></th>
+ <th class="column-8"><?= $paginator->order(t('Status'), 'is_active') ?></th>
<th class="column-15"><?= $paginator->order(t('Project'), 'name') ?></th>
- <th class="column-8"><?= $paginator->order(t('Start date'), 'start_date') ?></th>
- <th class="column-8"><?= $paginator->order(t('End date'), 'end_date') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Start date'), 'start_date') ?></th>
+ <th class="column-10"><?= $paginator->order(t('End date'), 'end_date') ?></th>
<th class="column-15"><?= $paginator->order(t('Owner'), 'owner_id') ?></th>
<?php if ($this->user->hasAccess('ProjectUserOverviewController', 'managers')): ?>
<th class="column-10"><?= t('Users') ?></th>
@@ -73,7 +73,7 @@
<td class="dashboard-project-stats">
<?php foreach ($project['columns'] as $column): ?>
<strong title="<?= t('Task count') ?>"><?= $column['nb_tasks'] ?></strong>
- <span><?= $this->text->e($column['title']) ?></span>
+ <small><?= $this->text->e($column['title']) ?></small>
<?php endforeach ?>
</td>
</tr>
diff --git a/app/Template/project_overview/columns.php b/app/Template/project_overview/columns.php
index cc5782bd..daae9ca7 100644
--- a/app/Template/project_overview/columns.php
+++ b/app/Template/project_overview/columns.php
@@ -1,8 +1,8 @@
<div class="project-overview-columns">
<?php foreach ($project['columns'] as $column): ?>
<div class="project-overview-column">
- <strong title="<?= t('Task count') ?>"><?= $column['nb_tasks'] ?></strong><br>
- <span><?= $this->text->e($column['title']) ?></span>
+ <strong title="<?= t('Task count') ?>"><?= $column['nb_tasks'] ?></strong>
+ <small><?= $this->text->e($column['title']) ?></small>
</div>
<?php endforeach ?>
</div>
diff --git a/app/Template/project_overview/files.php b/app/Template/project_overview/files.php
index fa870938..826e6325 100644
--- a/app/Template/project_overview/files.php
+++ b/app/Template/project_overview/files.php
@@ -1,5 +1,5 @@
<?php if (! empty($files)): ?>
- <table class="table-stripped">
+ <table class="table-striped table-scrolling">
<tr>
<th><?= t('Filename') ?></th>
<th><?= t('Creator') ?></th>
diff --git a/app/Template/project_permission/index.php b/app/Template/project_permission/index.php
index d850ec50..c7b17782 100644
--- a/app/Template/project_permission/index.php
+++ b/app/Template/project_permission/index.php
@@ -9,7 +9,7 @@
<?php if (empty($users)): ?>
<div class="alert"><?= t('No user have been allowed specifically.') ?></div>
<?php else: ?>
- <table>
+ <table class="table-scrolling">
<tr>
<th class="column-50"><?= t('User') ?></th>
<th><?= t('Role') ?></th>
@@ -69,7 +69,7 @@
<?php if (empty($groups)): ?>
<div class="alert"><?= t('No group have been allowed specifically.') ?></div>
<?php else: ?>
- <table>
+ <table class="table-scrolling">
<tr>
<th class="column-50"><?= t('Group') ?></th>
<th><?= t('Role') ?></th>
diff --git a/app/Template/project_tag/create.php b/app/Template/project_tag/create.php
new file mode 100644
index 00000000..bfd1084a
--- /dev/null
+++ b/app/Template/project_tag/create.php
@@ -0,0 +1,16 @@
+<div class="page-header">
+ <h2><?= t('Add new tag') ?></h2>
+</div>
+<form method="post" class="popover-form" action="<?= $this->url->href('ProjectTagController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'ProjectTagController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
+ </div>
+</form>
diff --git a/app/Template/project_tag/edit.php b/app/Template/project_tag/edit.php
new file mode 100644
index 00000000..9bf261bd
--- /dev/null
+++ b/app/Template/project_tag/edit.php
@@ -0,0 +1,17 @@
+<div class="page-header">
+ <h2><?= t('Edit a tag') ?></h2>
+</div>
+<form method="post" class="popover-form" action="<?= $this->url->href('ProjectTagController', 'update', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'ProjectTagController', 'index', array(), false, 'close-popover') ?>
+ </div>
+</form>
diff --git a/app/Template/project_tag/index.php b/app/Template/project_tag/index.php
new file mode 100644
index 00000000..f77e21ee
--- /dev/null
+++ b/app/Template/project_tag/index.php
@@ -0,0 +1,31 @@
+<div class="page-header">
+ <h2><?= t('Project tags') ?></h2>
+ <ul>
+ <li>
+ <i class="fa fa-plus" aria-hidden="true"></i>
+ <?= $this->url->link(t('Add new tag'), 'ProjectTagController', 'create', array('project_id' => $project['id']), false, 'popover') ?>
+ </li>
+ </ul>
+</div>
+
+<?php if (empty($tags)): ?>
+ <p class="alert"><?= t('There is no specific tag for this project at the moment.') ?></p>
+<?php else: ?>
+ <table class="table-striped table-scrolling">
+ <tr>
+ <th class="column-80"><?= t('Tag') ?></th>
+ <th><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($tags as $tag): ?>
+ <tr>
+ <td><?= $this->text->e($tag['name']) ?></td>
+ <td>
+ <i class="fa fa-times" aria-hidden="true"></i>
+ <?= $this->url->link(t('Remove'), 'ProjectTagController', 'confirm', array('tag_id' => $tag['id'], 'project_id' => $project['id']), false, 'popover') ?>
+ <i class="fa fa-pencil-square-o" aria-hidden="true"></i>
+ <?= $this->url->link(t('Edit'), 'ProjectTagController', 'edit', array('tag_id' => $tag['id'], 'project_id' => $project['id']), false, 'popover') ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?>
diff --git a/app/Template/project_tag/remove.php b/app/Template/project_tag/remove.php
new file mode 100644
index 00000000..f4aadab1
--- /dev/null
+++ b/app/Template/project_tag/remove.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Remove a tag') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this tag: "%s"?', $tag['name']) ?>
+ </p>
+
+ <div class="form-actions">
+ <?= $this->url->link(t('Yes'), 'ProjectTagController', 'remove', array('tag_id' => $tag['id'], 'project_id' => $project['id']), true, 'btn btn-red popover-link') ?>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'ProjectTagController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
+ </div>
+</div>
diff --git a/app/Template/project_user_overview/roles.php b/app/Template/project_user_overview/roles.php
index 87c8df85..011714d4 100644
--- a/app/Template/project_user_overview/roles.php
+++ b/app/Template/project_user_overview/roles.php
@@ -1,7 +1,7 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No project') ?></p>
<?php else: ?>
- <table class="table-fixed">
+ <table class="table-fixed table-scrolling">
<tr>
<th class="column-20"><?= $paginator->order(t('User'), 'users.username') ?></th>
<th class="column-25"><?= $paginator->order(t('Project'), 'projects.name') ?></th>
diff --git a/app/Template/project_user_overview/sidebar.php b/app/Template/project_user_overview/sidebar.php
index 9a87d4eb..ccbf9cab 100644
--- a/app/Template/project_user_overview/sidebar.php
+++ b/app/Template/project_user_overview/sidebar.php
@@ -1,6 +1,4 @@
<div class="sidebar">
- <h2><?= t('Actions') ?></h2>
-
<?= $this->form->select(
'user_id',
$users,
diff --git a/app/Template/project_user_overview/tasks.php b/app/Template/project_user_overview/tasks.php
index af0a3d97..8d682170 100644
--- a/app/Template/project_user_overview/tasks.php
+++ b/app/Template/project_user_overview/tasks.php
@@ -1,7 +1,7 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No tasks found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
- <table class="table-small">
+ <table class="table-small table-striped table-scrolling">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
<th class="column-10"><?= $paginator->order(t('Project'), 'projects.name') ?></th>
diff --git a/app/Template/project_user_overview/tooltip_users.php b/app/Template/project_user_overview/tooltip_users.php
index 7117a87f..99b00030 100644
--- a/app/Template/project_user_overview/tooltip_users.php
+++ b/app/Template/project_user_overview/tooltip_users.php
@@ -1,7 +1,7 @@
<?php if (empty($users)): ?>
<p><?= t('There is no project member.') ?></p>
<?php else: ?>
- <table>
+ <table class="table-small">
<?php foreach ($roles as $role => $role_name): ?>
<?php if (isset($users[$role])): ?>
<tr><th><?= $role_name ?></th></tr>
diff --git a/app/Template/project_view/duplicate.php b/app/Template/project_view/duplicate.php
index d2cd127a..561378d1 100644
--- a/app/Template/project_view/duplicate.php
+++ b/app/Template/project_view/duplicate.php
@@ -11,14 +11,15 @@
<?= $this->form->csrf() ?>
<?php if ($project['is_private'] == 0): ?>
- <?= $this->form->checkbox('projectPermission', t('Permissions'), 1, true) ?>
+ <?= $this->form->checkbox('projectPermissionModel', t('Permissions'), 1, true) ?>
<?php endif ?>
<?= $this->form->checkbox('categoryModel', t('Categories'), 1, true) ?>
+ <?= $this->form->checkbox('tagDuplicationModel', t('Tags'), 1, true) ?>
<?= $this->form->checkbox('actionModel', t('Actions'), 1, true) ?>
<?= $this->form->checkbox('swimlaneModel', t('Swimlanes'), 1, false) ?>
- <?= $this->form->checkbox('taskModel', t('Tasks'), 1, false) ?>
<?= $this->form->checkbox('projectMetadataModel', t('Metadata'), 1, false) ?>
+ <?= $this->form->checkbox('projectTaskDuplicationModel', t('Tasks'), 1, false) ?>
<div class="form-actions">
<button type="submit" class="btn btn-red"><?= t('Duplicate') ?></button>
diff --git a/app/Template/project_view/show.php b/app/Template/project_view/show.php
index 5efe8ce6..afe60384 100644
--- a/app/Template/project_view/show.php
+++ b/app/Template/project_view/show.php
@@ -52,11 +52,12 @@
<div class="page-header">
<h2><?= t('Board') ?></h2>
</div>
-<table class="table-stripped">
+<table class="table-striped table-scrolling">
<tr>
- <th class="column-60"><?= t('Column') ?></th>
+ <th class="column-40"><?= t('Column') ?></th>
<th class="column-20"><?= t('Task limit') ?></th>
<th class="column-20"><?= t('Active tasks') ?></th>
+ <th class="column-20"><?= t('Hide tasks in this column in the dashboard') ?></th>
</tr>
<?php foreach ($stats['columns'] as $column): ?>
<tr>
@@ -70,6 +71,13 @@
</td>
<td><?= $column['task_limit'] ?: '∞' ?></td>
<td><?= $column['nb_active_tasks'] ?></td>
+ <td>
+ <?php if ($column['hide_in_dashboard'] == 1): ?>
+ <?= t('Yes') ?>
+ <?php else: ?>
+ <?= t('No') ?>
+ <?php endif ?>
+ </td>
</tr>
<?php endforeach ?>
</table>
diff --git a/app/Template/search/activity.php b/app/Template/search/activity.php
index 9abc7d7e..1dfd9234 100644
--- a/app/Template/search/activity.php
+++ b/app/Template/search/activity.php
@@ -12,8 +12,13 @@
<form method="get" action="<?= $this->url->dir() ?>" class="search">
<?= $this->form->hidden('controller', $values) ?>
<?= $this->form->hidden('action', $values) ?>
- <?= $this->form->text('search', $values, array(), array(empty($values['search']) ? 'autofocus' : '', 'placeholder="'.t('Search').'"'), 'form-input-large') ?>
- <?= $this->render('activity/filter_dropdown') ?>
+
+ <div class="input-addon">
+ <?= $this->form->text('search', $values, array(), array(empty($values['search']) ? 'autofocus' : '', 'placeholder="'.t('Search').'"'), 'input-addon-field') ?>
+ <div class="input-addon-item">
+ <?= $this->render('app/filters_helper') ?>
+ </div>
+ </div>
</form>
</div>
diff --git a/app/Template/search/index.php b/app/Template/search/index.php
index bc528af7..c59a5c99 100644
--- a/app/Template/search/index.php
+++ b/app/Template/search/index.php
@@ -12,8 +12,13 @@
<form method="get" action="<?= $this->url->dir() ?>" class="search">
<?= $this->form->hidden('controller', $values) ?>
<?= $this->form->hidden('action', $values) ?>
- <?= $this->form->text('search', $values, array(), array(empty($values['search']) ? 'autofocus' : '', 'placeholder="'.t('Search').'"'), 'form-input-large') ?>
- <?= $this->render('app/filters_helper') ?>
+
+ <div class="input-addon">
+ <?= $this->form->text('search', $values, array(), array(empty($values['search']) ? 'autofocus' : '', 'placeholder="'.t('Search').'"'), 'input-addon-field') ?>
+ <div class="input-addon-item">
+ <?= $this->render('app/filters_helper') ?>
+ </div>
+ </div>
</form>
</div>
diff --git a/app/Template/search/results.php b/app/Template/search/results.php
index 8376b9e8..8c439a8a 100644
--- a/app/Template/search/results.php
+++ b/app/Template/search/results.php
@@ -1,4 +1,4 @@
-<table class="table-fixed table-small">
+<table class="table-small table-scrolling">
<tr>
<th class="column-8"><?= $paginator->order(t('Project'), 'tasks.project_id') ?></th>
<th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
diff --git a/app/Template/subtask/table.php b/app/Template/subtask/table.php
index 4c6484ef..5c60df44 100644
--- a/app/Template/subtask/table.php
+++ b/app/Template/subtask/table.php
@@ -1,6 +1,6 @@
<?php if (! empty($subtasks)): ?>
<table
- class="subtasks-table table-stripped"
+ class="subtasks-table table-striped table-scrolling"
data-save-position-url="<?= $this->url->href('SubtaskController', 'movePosition', array('project_id' => $task['project_id'], 'task_id' => $task['id'])) ?>"
>
<thead>
diff --git a/app/Template/swimlane/table.php b/app/Template/swimlane/table.php
index be123b08..cefef9de 100644
--- a/app/Template/swimlane/table.php
+++ b/app/Template/swimlane/table.php
@@ -1,5 +1,5 @@
<table
- class="swimlanes-table table-stripped"
+ class="swimlanes-table table-striped table-scrolling"
data-save-position-url="<?= $this->url->href('SwimlaneController', 'move', array('project_id' => $project['id'])) ?>">
<thead>
<tr>
diff --git a/app/Template/tag/create.php b/app/Template/tag/create.php
new file mode 100644
index 00000000..9b32bc46
--- /dev/null
+++ b/app/Template/tag/create.php
@@ -0,0 +1,16 @@
+<div class="page-header">
+ <h2><?= t('Add new tag') ?></h2>
+</div>
+<form method="post" class="popover-form" action="<?= $this->url->href('TagController', 'save') ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'TagController', 'index', array(), false, 'close-popover') ?>
+ </div>
+</form>
diff --git a/app/Template/tag/edit.php b/app/Template/tag/edit.php
new file mode 100644
index 00000000..f751ff49
--- /dev/null
+++ b/app/Template/tag/edit.php
@@ -0,0 +1,17 @@
+<div class="page-header">
+ <h2><?= t('Edit a tag') ?></h2>
+</div>
+<form method="post" class="popover-form" action="<?= $this->url->href('TagController', 'update', array('tag_id' => $tag['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="255"')) ?>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'TagController', 'index', array(), false, 'close-popover') ?>
+ </div>
+</form>
diff --git a/app/Template/tag/index.php b/app/Template/tag/index.php
new file mode 100644
index 00000000..8e0c9a06
--- /dev/null
+++ b/app/Template/tag/index.php
@@ -0,0 +1,31 @@
+<div class="page-header">
+ <h2><?= t('Global tags') ?></h2>
+ <ul>
+ <li>
+ <i class="fa fa-plus" aria-hidden="true"></i>
+ <?= $this->url->link(t('Add new tag'), 'TagController', 'create', array(), false, 'popover') ?>
+ </li>
+ </ul>
+</div>
+
+<?php if (empty($tags)): ?>
+ <p class="alert"><?= t('There is no global tag at the moment.') ?></p>
+<?php else: ?>
+ <table class="table-striped table-scrolling">
+ <tr>
+ <th class="column-80"><?= t('Tag') ?></th>
+ <th><?= t('Action') ?></th>
+ </tr>
+ <?php foreach ($tags as $tag): ?>
+ <tr>
+ <td><?= $this->text->e($tag['name']) ?></td>
+ <td>
+ <i class="fa fa-times" aria-hidden="true"></i>
+ <?= $this->url->link(t('Remove'), 'TagController', 'confirm', array('tag_id' => $tag['id']), false, 'popover') ?>
+ <i class="fa fa-pencil-square-o" aria-hidden="true"></i>
+ <?= $this->url->link(t('Edit'), 'TagController', 'edit', array('tag_id' => $tag['id']), false, 'popover') ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?>
diff --git a/app/Template/tag/remove.php b/app/Template/tag/remove.php
new file mode 100644
index 00000000..46ea3f99
--- /dev/null
+++ b/app/Template/tag/remove.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Remove a tag') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this tag: "%s"?', $tag['name']) ?>
+ </p>
+
+ <div class="form-actions">
+ <?= $this->url->link(t('Yes'), 'TagController', 'remove', array('tag_id' => $tag['id']), true, 'btn btn-red popover-link') ?>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'TagController', 'index', array(), false, 'close-popover') ?>
+ </div>
+</div>
diff --git a/app/Template/task/analytics.php b/app/Template/task/analytics.php
index db2d0cef..071f24a7 100644
--- a/app/Template/task/analytics.php
+++ b/app/Template/task/analytics.php
@@ -1,6 +1,10 @@
-<div class="task-show-title color-<?= $task['color_id'] ?>">
- <h2><?= $this->text->e($task['title']) ?></h2>
-</div>
+<?= $this->render('task/details', array(
+ 'task' => $task,
+ 'tags' => $tags,
+ 'project' => $project,
+ 'editable' => false,
+)) ?>
+
<div class="page-header">
<h2><?= t('Analytics') ?></h2>
</div>
@@ -14,7 +18,7 @@
<h3 id="analytic-task-time-column"><?= t('Time spent into each column') ?></h3>
<div id="chart" data-metrics='<?= json_encode($time_spent_columns, JSON_HEX_APOS) ?>' data-label="<?= t('Time spent') ?>"></div>
-<table class="table-stripped">
+<table class="table-striped">
<tr>
<th><?= t('Column') ?></th>
<th><?= t('Time spent') ?></th>
diff --git a/app/Template/task/color_picker.php b/app/Template/task/color_picker.php
deleted file mode 100644
index 0c62fa70..00000000
--- a/app/Template/task/color_picker.php
+++ /dev/null
@@ -1,11 +0,0 @@
-<div class="color-picker">
-<?php foreach ($colors_list as $color_id => $color_name): ?>
- <div
- data-color-id="<?= $color_id ?>"
- class="color-square color-<?= $color_id ?> <?= isset($values['color_id']) && $values['color_id'] === $color_id ? 'color-square-selected' : '' ?>"
- title="<?= $this->text->e($color_name) ?>">
- </div>
-<?php endforeach ?>
-</div>
-
-<?= $this->form->hidden('color_id', $values) ?> \ No newline at end of file
diff --git a/app/Template/task/details.php b/app/Template/task/details.php
index fe2bba67..a39c1bab 100644
--- a/app/Template/task/details.php
+++ b/app/Template/task/details.php
@@ -4,150 +4,164 @@
<?= $this->hook->render('template:task:details:top', array('task' => $task)) ?>
<div class="task-summary-container color-<?= $task['color_id'] ?>">
- <div class="task-summary-column">
- <ul class="no-bullet">
- <li>
- <strong><?= t('Status:') ?></strong>
- <span>
- <?php if ($task['is_active'] == 1): ?>
- <?= t('open') ?>
- <?php else: ?>
- <?= t('closed') ?>
+ <div class="task-summary-columns">
+ <div class="task-summary-column">
+ <ul class="no-bullet">
+ <li>
+ <strong><?= t('Status:') ?></strong>
+ <span>
+ <?php if ($task['is_active'] == 1): ?>
+ <?= t('open') ?>
+ <?php else: ?>
+ <?= t('closed') ?>
+ <?php endif ?>
+ </span>
+ </li>
+ <li>
+ <strong><?= t('Priority:') ?></strong> <span><?= $task['priority'] ?></span>
+ </li>
+ <?php if (! empty($task['reference'])): ?>
+ <li>
+ <strong><?= t('Reference:') ?></strong> <span><?= $this->text->e($task['reference']) ?></span>
+ </li>
+ <?php endif ?>
+ <?php if (! empty($task['score'])): ?>
+ <li>
+ <strong><?= t('Complexity:') ?></strong> <span><?= $this->text->e($task['score']) ?></span>
+ </li>
<?php endif ?>
- </span>
- </li>
- <li>
- <strong><?= t('Priority:') ?></strong> <span><?= $task['priority'] ?></span>
- </li>
- <?php if (! empty($task['reference'])): ?>
+ <?php if ($project['is_public']): ?>
<li>
- <strong><?= t('Reference:') ?></strong> <span><?= $this->text->e($task['reference']) ?></span>
+ <small>
+ <i class="fa fa-external-link fa-fw"></i>
+ <?= $this->url->link(t('Public link'), 'TaskViewController', 'readonly', array('task_id' => $task['id'], 'token' => $project['token']), false, '', '', true) ?>
+ </small>
</li>
- <?php endif ?>
- <?php if (! empty($task['score'])): ?>
+ <?php endif ?>
+ <?php if ($project['is_public'] && !$editable): ?>
<li>
- <strong><?= t('Complexity:') ?></strong> <span><?= $this->text->e($task['score']) ?></span>
+ <small>
+ <i class="fa fa-th fa-fw"></i>
+ <?= $this->url->link(t('Back to the board'), 'BoardViewController', 'readonly', array('token' => $project['token'])) ?>
+ </small>
</li>
- <?php endif ?>
- <?php if ($project['is_public']): ?>
- <li class="smaller">
- <i class="fa fa-external-link fa-fw"></i>
- <?= $this->url->link(t('Public link'), 'TaskViewController', 'readonly', array('task_id' => $task['id'], 'token' => $project['token']), false, '', '', true) ?>
- </li>
- <?php endif ?>
- <?php if ($project['is_public'] && !$editable): ?>
- <li class="smaller">
- <i class="fa fa-th fa-fw"></i>
- <?= $this->url->link(t('Back to the board'), 'BoardViewController', 'readonly', array('token' => $project['token'])) ?>
- </li>
- <?php endif ?>
- <li class="smaller">
+ <?php endif ?>
- <?= $this->hook->render('template:task:details:first-column', array('task' => $task)) ?>
- </ul>
- </div>
- <div class="task-summary-column">
- <ul class="no-bullet">
- <?php if (! empty($task['category_name'])): ?>
+ <?= $this->hook->render('template:task:details:first-column', array('task' => $task)) ?>
+ </ul>
+ </div>
+ <div class="task-summary-column">
+ <ul class="no-bullet">
+ <?php if (! empty($task['category_name'])): ?>
+ <li>
+ <strong><?= t('Category:') ?></strong>
+ <span><?= $this->text->e($task['category_name']) ?></span>
+ </li>
+ <?php endif ?>
+ <?php if (! empty($task['swimlane_name'])): ?>
+ <li>
+ <strong><?= t('Swimlane:') ?></strong>
+ <span><?= $this->text->e($task['swimlane_name']) ?></span>
+ </li>
+ <?php endif ?>
<li>
- <strong><?= t('Category:') ?></strong>
- <span><?= $this->text->e($task['category_name']) ?></span>
+ <strong><?= t('Column:') ?></strong>
+ <span><?= $this->text->e($task['column_title']) ?></span>
</li>
- <?php endif ?>
- <?php if (! empty($task['swimlane_name'])): ?>
<li>
- <strong><?= t('Swimlane:') ?></strong>
- <span><?= $this->text->e($task['swimlane_name']) ?></span>
+ <strong><?= t('Position:') ?></strong>
+ <span><?= $task['position'] ?></span>
</li>
- <?php endif ?>
- <li>
- <strong><?= t('Column:') ?></strong>
- <span><?= $this->text->e($task['column_title']) ?></span>
- </li>
- <li>
- <strong><?= t('Position:') ?></strong>
- <span><?= $task['position'] ?></span>
- </li>
- <?= $this->hook->render('template:task:details:second-column', array('task' => $task)) ?>
- </ul>
- </div>
- <div class="task-summary-column">
- <ul class="no-bullet">
- <li>
- <strong><?= t('Assignee:') ?></strong>
- <span>
- <?php if ($task['assignee_username']): ?>
- <?= $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?>
- <?php else: ?>
- <?= t('not assigned') ?>
+ <?= $this->hook->render('template:task:details:second-column', array('task' => $task)) ?>
+ </ul>
+ </div>
+ <div class="task-summary-column">
+ <ul class="no-bullet">
+ <li>
+ <strong><?= t('Assignee:') ?></strong>
+ <span>
+ <?php if ($task['assignee_username']): ?>
+ <?= $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?>
+ <?php else: ?>
+ <?= t('not assigned') ?>
+ <?php endif ?>
+ </span>
+ </li>
+ <?php if ($task['creator_username']): ?>
+ <li>
+ <strong><?= t('Creator:') ?></strong>
+ <span><?= $this->text->e($task['creator_name'] ?: $task['creator_username']) ?></span>
+ </li>
+ <?php endif ?>
+ <?php if ($task['date_due']): ?>
+ <li>
+ <strong><?= t('Due date:') ?></strong>
+ <span><?= $this->dt->date($task['date_due']) ?></span>
+ </li>
+ <?php endif ?>
+ <?php if ($task['time_estimated']): ?>
+ <li>
+ <strong><?= t('Time estimated:') ?></strong>
+ <span><?= t('%s hours', $task['time_estimated']) ?></span>
+ </li>
<?php endif ?>
- </span>
- </li>
- <?php if ($task['creator_username']): ?>
+ <?php if ($task['time_spent']): ?>
<li>
- <strong><?= t('Creator:') ?></strong>
- <span><?= $this->text->e($task['creator_name'] ?: $task['creator_username']) ?></span>
+ <strong><?= t('Time spent:') ?></strong>
+ <span><?= t('%s hours', $task['time_spent']) ?></span>
</li>
- <?php endif ?>
- <?php if ($task['date_due']): ?>
- <li>
- <strong><?= t('Due date:') ?></strong>
- <span><?= $this->dt->date($task['date_due']) ?></span>
- </li>
- <?php endif ?>
- <?php if ($task['time_estimated']): ?>
- <li>
- <strong><?= t('Time estimated:') ?></strong>
- <span><?= t('%s hours', $task['time_estimated']) ?></span>
- </li>
- <?php endif ?>
- <?php if ($task['time_spent']): ?>
- <li>
- <strong><?= t('Time spent:') ?></strong>
- <span><?= t('%s hours', $task['time_spent']) ?></span>
- </li>
- <?php endif ?>
+ <?php endif ?>
- <?= $this->hook->render('template:task:details:third-column', array('task' => $task)) ?>
- </ul>
- </div>
- <div class="task-summary-column">
- <ul class="no-bullet">
- <li>
- <strong><?= t('Created:') ?></strong>
- <span><?= $this->dt->datetime($task['date_creation']) ?></span>
- </li>
- <li>
- <strong><?= t('Modified:') ?></strong>
- <span><?= $this->dt->datetime($task['date_modification']) ?></span>
- </li>
- <?php if ($task['date_completed']): ?>
- <li>
- <strong><?= t('Completed:') ?></strong>
- <span><?= $this->dt->datetime($task['date_completed']) ?></span>
- </li>
- <?php endif ?>
- <?php if ($task['date_started']): ?>
- <li>
- <strong><?= t('Started:') ?></strong>
- <span><?= $this->dt->datetime($task['date_started']) ?></span>
- </li>
- <?php endif ?>
- <?php if ($task['date_moved']): ?>
- <li>
- <strong><?= t('Moved:') ?></strong>
- <span><?= $this->dt->datetime($task['date_moved']) ?></span>
- </li>
- <?php endif ?>
+ <?= $this->hook->render('template:task:details:third-column', array('task' => $task)) ?>
+ </ul>
+ </div>
+ <div class="task-summary-column">
+ <ul class="no-bullet">
+ <li>
+ <strong><?= t('Created:') ?></strong>
+ <span><?= $this->dt->datetime($task['date_creation']) ?></span>
+ </li>
+ <li>
+ <strong><?= t('Modified:') ?></strong>
+ <span><?= $this->dt->datetime($task['date_modification']) ?></span>
+ </li>
+ <?php if ($task['date_completed']): ?>
+ <li>
+ <strong><?= t('Completed:') ?></strong>
+ <span><?= $this->dt->datetime($task['date_completed']) ?></span>
+ </li>
+ <?php endif ?>
+ <?php if ($task['date_started']): ?>
+ <li>
+ <strong><?= t('Started:') ?></strong>
+ <span><?= $this->dt->datetime($task['date_started']) ?></span>
+ </li>
+ <?php endif ?>
+ <?php if ($task['date_moved']): ?>
+ <li>
+ <strong><?= t('Moved:') ?></strong>
+ <span><?= $this->dt->datetime($task['date_moved']) ?></span>
+ </li>
+ <?php endif ?>
- <?= $this->hook->render('template:task:details:fourth-column', array('task' => $task)) ?>
- </ul>
+ <?= $this->hook->render('template:task:details:fourth-column', array('task' => $task)) ?>
+ </ul>
+ </div>
</div>
+ <?php if (! empty($tags)): ?>
+ <div class="task-tags">
+ <ul>
+ <?php foreach ($tags as $tag): ?>
+ <li><?= $this->text->e($tag) ?></li>
+ <?php endforeach ?>
+ </ul>
+ </div>
+ <?php endif ?>
</div>
<?php if ($editable && empty($task['date_started'])): ?>
- <div class="task-summary-buttons">
+ <div class="buttons-header">
<?= $this->url->button('fa-play', t('Set start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</div>
<?php endif ?>
diff --git a/app/Template/task/dropdown.php b/app/Template/task/dropdown.php
index b6b9c789..95c7a88c 100644
--- a/app/Template/task/dropdown.php
+++ b/app/Template/task/dropdown.php
@@ -8,22 +8,10 @@
</li>
<?php endif ?>
<li>
- <i class="fa fa-user fa-fw"></i>
- <?= $this->url->link(t('Change assignee'), 'TaskPopoverController', 'changeAssignee', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
- </li>
- <li>
- <i class="fa fa-tag fa-fw"></i>
- <?= $this->url->link(t('Change category'), 'TaskPopoverController', 'changeCategory', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
- </li>
- <li>
<i class="fa fa-pencil-square-o fa-fw"></i>
<?= $this->url->link(t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
</li>
<li>
- <i class="fa fa-align-left fa-fw"></i>
- <?= $this->url->link(t('Edit the description'), 'TaskModificationController', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
- </li>
- <li>
<i class="fa fa-plus fa-fw"></i>
<?= $this->url->link(t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
</li>
diff --git a/app/Template/task/layout.php b/app/Template/task/layout.php
index 00e0e9ae..7f6c2913 100644
--- a/app/Template/task/layout.php
+++ b/app/Template/task/layout.php
@@ -4,7 +4,6 @@
<section
class="sidebar-container" id="task-view"
data-edit-url="<?= $this->url->href('TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"
- data-description-url="<?= $this->url->href('TaskModificationController', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"
data-subtask-url="<?= $this->url->href('SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"
data-internal-link-url="<?= $this->url->href('TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"
data-comment-url="<?= $this->url->href('CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
diff --git a/app/Template/task/public.php b/app/Template/task/public.php
index 94782163..b8405ff7 100644
--- a/app/Template/task/public.php
+++ b/app/Template/task/public.php
@@ -1,5 +1,10 @@
<section id="main" class="public-task">
- <?= $this->render('task/details', array('task' => $task, 'project' => $project, 'editable' => false)) ?>
+ <?= $this->render('task/details', array(
+ 'task' => $task,
+ 'tags' => $tags,
+ 'project' => $project,
+ 'editable' => false,
+ )) ?>
<?= $this->render('task/description', array(
'task' => $task,
diff --git a/app/Template/task/show.php b/app/Template/task/show.php
index 2b54eea8..a5c2d5a7 100644
--- a/app/Template/task/show.php
+++ b/app/Template/task/show.php
@@ -2,51 +2,64 @@
<?= $this->render('task/details', array(
'task' => $task,
+ 'tags' => $tags,
'project' => $project,
'editable' => $this->user->hasProjectAccess('TaskModificationController', 'edit', $project['id']),
)) ?>
-<?= $this->hook->render('template:task:show:before-description', array('task' => $task, 'project' => $project)) ?>
-<?= $this->render('task/description', array('task' => $task)) ?>
+<?php if (!empty($task['description'])): ?>
+ <?= $this->hook->render('template:task:show:before-description', array('task' => $task, 'project' => $project)) ?>
+ <?= $this->render('task/description', array('task' => $task)) ?>
+<?php endif ?>
-<?= $this->hook->render('template:task:show:before-subtasks', array('task' => $task, 'project' => $project)) ?>
-<?= $this->render('subtask/show', array(
- 'task' => $task,
- 'subtasks' => $subtasks,
- 'project' => $project,
- 'editable' => true,
-)) ?>
+<?php if(!empty($subtasks)): ?>
+ <?= $this->hook->render('template:task:show:before-subtasks', array('task' => $task, 'project' => $project)) ?>
+ <?= $this->render('subtask/show', array(
+ 'task' => $task,
+ 'subtasks' => $subtasks,
+ 'project' => $project,
+ 'editable' => true,
+ )) ?>
+<?php endif ?>
-<?= $this->hook->render('template:task:show:before-internal-links', array('task' => $task, 'project' => $project)) ?>
-<?= $this->render('task_internal_link/show', array(
- 'task' => $task,
- 'links' => $internal_links,
- 'project' => $project,
- 'link_label_list' => $link_label_list,
- 'editable' => true,
- 'is_public' => false,
-)) ?>
+<?php if (!empty($internal_links)): ?>
+ <?= $this->hook->render('template:task:show:before-internal-links', array('task' => $task, 'project' => $project)) ?>
+ <?= $this->render('task_internal_link/show', array(
+ 'task' => $task,
+ 'links' => $internal_links,
+ 'project' => $project,
+ 'link_label_list' => $link_label_list,
+ 'editable' => true,
+ 'is_public' => false,
+ )) ?>
+<?php endif ?>
-<?= $this->hook->render('template:task:show:before-external-links', array('task' => $task, 'project' => $project)) ?>
-<?= $this->render('task_external_link/show', array(
- 'task' => $task,
- 'links' => $external_links,
- 'project' => $project,
-)) ?>
+<?php if (!empty($external_links)): ?>
+ <?= $this->hook->render('template:task:show:before-external-links', array('task' => $task, 'project' => $project)) ?>
+ <?= $this->render('task_external_link/show', array(
+ 'task' => $task,
+ 'links' => $external_links,
+ 'project' => $project,
+ )) ?>
+<?php endif ?>
-<?= $this->hook->render('template:task:show:before-attachments', array('task' => $task, 'project' => $project)) ?>
-<?= $this->render('task_file/show', array(
- 'task' => $task,
- 'files' => $files,
- 'images' => $images
-)) ?>
+<?php if (!empty($files) || !empty($images)): ?>
+ <?= $this->hook->render('template:task:show:before-attachments', array('task' => $task, 'project' => $project)) ?>
+ <?= $this->render('task_file/show', array(
+ 'task' => $task,
+ 'files' => $files,
+ 'images' => $images
+ )) ?>
+<?php endif ?>
-<?= $this->hook->render('template:task:show:before-comments', array('task' => $task, 'project' => $project)) ?>
-<?= $this->render('comments/show', array(
- 'task' => $task,
- 'comments' => $comments,
- 'project' => $project,
- 'editable' => $this->user->hasProjectAccess('CommentController', 'edit', $project['id']),
-)) ?>
+<?php if (!empty($comments)): ?>
+ <?= $this->hook->render('template:task:show:before-comments', array('task' => $task, 'project' => $project)) ?>
+ <?= $this->render('comments/show', array(
+ 'task' => $task,
+ 'comments' => $comments,
+ 'project' => $project,
+ 'editable' => $this->user->hasProjectAccess('CommentController', 'edit', $project['id']),
+ )) ?>
+<?php endif ?>
<?= $this->hook->render('template:task:show:bottom', array('task' => $task, 'project' => $project)) ?>
diff --git a/app/Template/task/sidebar.php b/app/Template/task/sidebar.php
index e77ec18a..728741ba 100644
--- a/app/Template/task/sidebar.php
+++ b/app/Template/task/sidebar.php
@@ -1,5 +1,7 @@
<div class="sidebar sidebar-icons">
- <h2><?= t('Task #%d', $task['id']) ?></h2>
+ <div class="sidebar-title">
+ <h2><?= t('Task #%d', $task['id']) ?></h2>
+ </div>
<ul>
<li <?= $this->app->checkMenuSelection('TaskViewController', 'show') ?>>
<i class="fa fa-newspaper-o fa-fw"></i>
@@ -28,17 +30,15 @@
</ul>
<?php if ($this->user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?>
- <h2><?= t('Actions') ?></h2>
+ <div class="sidebar-title">
+ <h2><?= t('Actions') ?></h2>
+ </div>
<ul>
<li>
<i class="fa fa-pencil-square-o fa-fw"></i>
<?= $this->url->link(t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
</li>
<li>
- <i class="fa fa-align-left fa-fw"></i>
- <?= $this->url->link(t('Edit the description'), 'TaskModificationController', 'description', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
- </li>
- <li>
<i class="fa fa-refresh fa-rotate-90 fa-fw"></i>
<?= $this->url->link(t('Edit recurrence'), 'TaskRecurrenceController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
</li>
diff --git a/app/Template/task/time_tracking_details.php b/app/Template/task/time_tracking_details.php
index 1a179522..7cb419e0 100644
--- a/app/Template/task/time_tracking_details.php
+++ b/app/Template/task/time_tracking_details.php
@@ -1,6 +1,9 @@
-<div class="task-show-title color-<?= $task['color_id'] ?>">
- <h2><?= $this->text->e($task['title']) ?></h2>
-</div>
+<?= $this->render('task/details', array(
+ 'task' => $task,
+ 'tags' => $tags,
+ 'project' => $project,
+ 'editable' => false,
+)) ?>
<?= $this->render('task/time_tracking_summary', array('task' => $task)) ?>
@@ -8,7 +11,7 @@
<?php if ($subtask_paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing to show.') ?></p>
<?php else: ?>
- <table class="table-fixed">
+ <table class="table-fixed table-scrolling">
<tr>
<th class="column-15"><?= $subtask_paginator->order(t('User'), 'username') ?></th>
<th><?= $subtask_paginator->order(t('Subtask'), 'subtask_title') ?></th>
diff --git a/app/Template/task/transitions.php b/app/Template/task/transitions.php
index 9e04c4e1..4a9f22ce 100644
--- a/app/Template/task/transitions.php
+++ b/app/Template/task/transitions.php
@@ -1,6 +1,9 @@
-<div class="task-show-title color-<?= $task['color_id'] ?>">
- <h2><?= $this->text->e($task['title']) ?></h2>
-</div>
+<?= $this->render('task/details', array(
+ 'task' => $task,
+ 'tags' => $tags,
+ 'project' => $project,
+ 'editable' => false,
+)) ?>
<div class="page-header">
<h2><?= t('Transitions') ?></h2>
@@ -9,7 +12,7 @@
<?php if (empty($transitions)): ?>
<p class="alert"><?= t('There is nothing to show.') ?></p>
<?php else: ?>
- <table class="table-stripped">
+ <table class="table-striped table-scrolling">
<tr>
<th><?= t('Date') ?></th>
<th><?= t('Source column') ?></th>
diff --git a/app/Template/task_bulk/show.php b/app/Template/task_bulk/show.php
index 1391c2c1..11ddea31 100644
--- a/app/Template/task_bulk/show.php
+++ b/app/Template/task_bulk/show.php
@@ -1,5 +1,5 @@
<div class="page-header">
- <h2><?= t('Create tasks in bulk') ?></h2>
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= t('Create tasks in bulk') ?></h2>
</div>
<form class="popover-form" method="post" action="<?= $this->url->href('TaskBulkController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
@@ -8,6 +8,7 @@
<?= $this->form->hidden('swimlane_id', $values) ?>
<?= $this->form->hidden('project_id', $values) ?>
+ <?= $this->task->selectColor($values) ?>
<?= $this->task->selectAssignee($users_list, $values, $errors) ?>
<?= $this->task->selectCategory($categories_list, $values, $errors) ?>
@@ -15,8 +16,6 @@
<?= $this->form->textarea('tasks', $values, $errors, array('placeholder="'.t('My task title').'"')) ?>
<p class="form-help"><?= t('Enter one task by line.') ?></p>
- <?= $this->render('task/color_picker', array('colors_list' => $colors_list, 'values' => $values)) ?>
-
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
<?= t('or') ?> <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
diff --git a/app/Template/task_creation/show.php b/app/Template/task_creation/show.php
index 7bebbfe9..1a8a17d5 100644
--- a/app/Template/task_creation/show.php
+++ b/app/Template/task_creation/show.php
@@ -1,49 +1,45 @@
<div class="page-header">
- <h2><?= t('New task') ?></h2>
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= t('New task') ?></h2>
</div>
<form class="popover-form" method="post" action="<?= $this->url->href('TaskCreationController', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
- <div class="form-column">
- <?= $this->form->label(t('Title'), 'title') ?>
- <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="200"', 'tabindex="1"'), 'form-input-large') ?>
-
- <?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textarea(
- 'description',
- $values,
- $errors,
- array(
- 'placeholder="'.t('Leave a description').'"',
- 'tabindex="2"',
- 'data-mention-search-url="'.$this->url->href('UserAjaxController', 'mention', array('project_id' => $values['project_id'])).'"'
- ),
- 'markdown-editor'
- ) ?>
-
- <?= $this->render('task/color_picker', array('colors_list' => $colors_list, 'values' => $values)) ?>
-
- <?php if (! isset($duplicate)): ?>
- <?= $this->form->checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?>
- <?php endif ?>
-
- <?= $this->hook->render('template:task:form:left-column', array('values' => $values, 'errors' => $errors)) ?>
- </div>
-
- <div class="form-column">
- <?= $this->form->hidden('project_id', $values) ?>
- <?= $this->task->selectAssignee($users_list, $values, $errors) ?>
- <?= $this->task->selectCategory($categories_list, $values, $errors) ?>
- <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?>
- <?= $this->task->selectColumn($columns_list, $values, $errors) ?>
- <?= $this->task->selectPriority($project, $values) ?>
- <?= $this->task->selectScore($values, $errors) ?>
- <?= $this->task->selectTimeEstimated($values, $errors) ?>
- <?= $this->task->selectDueDate($values, $errors) ?>
-
- <?= $this->hook->render('template:task:form:right-column', array('values' => $values, 'errors' => $errors)) ?>
+ <div class="form-columns">
+ <div class="form-column">
+ <?= $this->task->selectTitle($values, $errors) ?>
+ <?= $this->task->selectDescription($values, $errors) ?>
+ <?= $this->task->selectTags($project) ?>
+
+ <?php if (! isset($duplicate)): ?>
+ <?= $this->form->checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?>
+ <?php endif ?>
+
+ <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
+
+ <div class="form-column">
+ <?= $this->form->hidden('project_id', $values) ?>
+ <?= $this->task->selectColor($values) ?>
+ <?= $this->task->selectAssignee($users_list, $values, $errors) ?>
+ <?= $this->task->selectCategory($categories_list, $values, $errors) ?>
+ <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?>
+ <?= $this->task->selectColumn($columns_list, $values, $errors) ?>
+ <?= $this->task->selectPriority($project, $values) ?>
+ <?= $this->task->selectScore($values, $errors) ?>
+ <?= $this->task->selectReference($values, $errors) ?>
+
+ <?= $this->hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
+
+ <div class="form-column">
+ <?= $this->task->selectTimeEstimated($values, $errors) ?>
+ <?= $this->task->selectTimeSpent($values, $errors) ?>
+ <?= $this->task->selectStartDate($values, $errors) ?>
+ <?= $this->task->selectDueDate($values, $errors) ?>
+
+ <?= $this->hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
</div>
<div class="form-actions">
diff --git a/app/Template/task_external_link/table.php b/app/Template/task_external_link/table.php
index 56ef0363..cdfe0028 100644
--- a/app/Template/task_external_link/table.php
+++ b/app/Template/task_external_link/table.php
@@ -1,5 +1,5 @@
<?php if (! empty($links)): ?>
-<table class="table-stripped table-small">
+<table class="table-striped table-scrolling">
<tr>
<th class="column-10"><?= t('Type') ?></th>
<th><?= t('Title') ?></th>
diff --git a/app/Template/task_file/files.php b/app/Template/task_file/files.php
index 7ca59b1c..94c26f73 100644
--- a/app/Template/task_file/files.php
+++ b/app/Template/task_file/files.php
@@ -1,5 +1,5 @@
<?php if (! empty($files)): ?>
- <table class="table-stripped table-small">
+ <table class="table-striped table-scrolling">
<tr>
<th><?= t('Filename') ?></th>
<th><?= t('Creator') ?></th>
diff --git a/app/Template/task_gantt_creation/show.php b/app/Template/task_gantt_creation/show.php
index 683bc8c8..7906c39a 100644
--- a/app/Template/task_gantt_creation/show.php
+++ b/app/Template/task_gantt_creation/show.php
@@ -1,30 +1,41 @@
<div class="page-header">
- <h2><?= t('New task') ?></h2>
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= t('New task') ?></h2>
</div>
-<form method="post" action="<?= $this->url->href('TaskGanttCreationController', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off">
+<form class="popover-form" method="post" action="<?= $this->url->href('TaskGanttCreationController', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->hidden('column_id', $values) ?>
<?= $this->form->hidden('position', $values) ?>
- <div class="form-column">
- <?= $this->form->label(t('Title'), 'title') ?>
- <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="200"', 'tabindex="1"'), 'form-input-large') ?>
+ <div class="form-columns">
+ <div class="form-column">
+ <?= $this->task->selectTitle($values, $errors) ?>
+ <?= $this->task->selectDescription($values, $errors) ?>
+ <?= $this->task->selectTags($project) ?>
- <?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textarea('description', $values, $errors, array('placeholder="'.t('Leave a description').'"', 'tabindex="2"'), 'markdown-editor') ?>
+ <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
- <?= $this->render('task/color_picker', array('colors_list' => $colors_list, 'values' => $values)) ?>
- </div>
+ <div class="form-column">
+ <?= $this->task->selectColor($values) ?>
+ <?= $this->task->selectAssignee($users_list, $values, $errors) ?>
+ <?= $this->task->selectCategory($categories_list, $values, $errors) ?>
+ <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?>
+ <?= $this->task->selectPriority($project, $values) ?>
+ <?= $this->task->selectScore($values, $errors) ?>
+ <?= $this->task->selectReference($values, $errors) ?>
+
+ <?= $this->hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
+
+ <div class="form-column">
+ <?= $this->task->selectTimeEstimated($values, $errors) ?>
+ <?= $this->task->selectTimeSpent($values, $errors) ?>
+ <?= $this->task->selectStartDate($values, $errors) ?>
+ <?= $this->task->selectDueDate($values, $errors) ?>
- <div class="form-column">
- <?= $this->task->selectAssignee($users_list, $values, $errors) ?>
- <?= $this->task->selectCategory($categories_list, $values, $errors) ?>
- <?= $this->task->selectSwimlane($swimlanes_list, $values, $errors) ?>
- <?= $this->task->selectPriority($project, $values) ?>
- <?= $this->task->selectScore($values, $errors) ?>
- <?= $this->task->selectStartDate($values, $errors) ?>
- <?= $this->task->selectDueDate($values, $errors) ?>
+ <?= $this->hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
</div>
<div class="form-actions">
diff --git a/app/Template/task_import/sidebar.php b/app/Template/task_import/sidebar.php
index 4cd92af8..04896948 100644
--- a/app/Template/task_import/sidebar.php
+++ b/app/Template/task_import/sidebar.php
@@ -1,5 +1,4 @@
<div class="sidebar">
- <h2><?= t('Imports') ?></h2>
<ul>
<li <?= $this->app->checkMenuSelection('TaskImportController', 'show') ?>>
<?= $this->url->link(t('Tasks').' (CSV)', 'TaskImportController', 'show', array('project_id' => $project['id'])) ?>
diff --git a/app/Template/task_internal_link/table.php b/app/Template/task_internal_link/table.php
index 424d4791..6584a33a 100644
--- a/app/Template/task_internal_link/table.php
+++ b/app/Template/task_internal_link/table.php
@@ -1,5 +1,5 @@
<?php if (! empty($links)): ?>
-<table class="task-links-table table-stripped">
+<table class="task-links-table table-striped table-scrolling">
<?php foreach ($links as $label => $grouped_links): ?>
<?php $hide_td = false ?>
<?php foreach ($grouped_links as $link): ?>
diff --git a/app/Template/task_list/show.php b/app/Template/task_list/show.php
index bb95b6a3..0518e4c3 100644
--- a/app/Template/task_list/show.php
+++ b/app/Template/task_list/show.php
@@ -4,7 +4,7 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No tasks found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
- <table class="table-fixed table-small">
+ <table class="table-striped table-scrolling table-small">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'tasks.id') ?></th>
<th class="column-10"><?= $paginator->order(t('Swimlane'), 'tasks.swimlane_id') ?></th>
diff --git a/app/Template/task_modification/edit_description.php b/app/Template/task_modification/edit_description.php
deleted file mode 100644
index 339ed036..00000000
--- a/app/Template/task_modification/edit_description.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<div class="page-header">
- <h2><?= t('Edit the description') ?></h2>
-</div>
-
-<form class="popover-form" method="post" action="<?= $this->url->href('TaskModificationController', 'updateDescription', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
-
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('id', $values) ?>
-
- <?= $this->form->textarea(
- 'description',
- $values,
- $errors,
- array(
- 'autofocus',
- 'placeholder="'.t('Leave a description').'"',
- 'data-mention-search-url="'.$this->url->href('UserAjaxController', 'mention', array('project_id' => $task['project_id'])).'"'
- ),
- 'markdown-editor'
- ) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
-</form>
diff --git a/app/Template/task_modification/edit_task.php b/app/Template/task_modification/edit_task.php
deleted file mode 100644
index 0707fd9a..00000000
--- a/app/Template/task_modification/edit_task.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<div class="page-header">
- <h2><?= t('Edit a task') ?></h2>
-</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('id', $values) ?>
- <?= $this->form->hidden('project_id', $values) ?>
-
- <div class="form-column">
- <?= $this->form->label(t('Title'), 'title') ?>
- <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="200"', 'tabindex="1"')) ?>
- <?= $this->task->selectAssignee($users_list, $values, $errors) ?>
- <?= $this->task->selectCategory($categories_list, $values, $errors) ?>
- <?= $this->task->selectPriority($project, $values) ?>
- <?= $this->task->selectScore($values, $errors) ?>
-
- <?= $this->hook->render('template:task:form:left-column', array('values' => $values, 'errors' => $errors)) ?>
- </div>
-
- <div class="form-column">
- <?= $this->task->selectTimeEstimated($values, $errors) ?>
- <?= $this->task->selectTimeSpent($values, $errors) ?>
- <?= $this->task->selectStartDate($values, $errors) ?>
- <?= $this->task->selectDueDate($values, $errors) ?>
-
- <?= $this->hook->render('template:task:form:right-column', array('values' => $values, 'errors' => $errors)) ?>
- </div>
-
- <div class="form-clear">
- <?= $this->render('task/color_picker', array('colors_list' => $colors_list, 'values' => $values)) ?>
- </div>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue" tabindex="15"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
-</form>
diff --git a/app/Template/task_modification/show.php b/app/Template/task_modification/show.php
new file mode 100644
index 00000000..734b247a
--- /dev/null
+++ b/app/Template/task_modification/show.php
@@ -0,0 +1,44 @@
+<div class="page-header">
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= $this->text->e($task['title']) ?></h2>
+</div>
+<form class="popover-form" method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <div class="form-columns">
+ <div class="form-column">
+ <?= $this->task->selectTitle($values, $errors) ?>
+ <?= $this->task->selectDescription($values, $errors) ?>
+ <?= $this->task->selectTags($project, $tags) ?>
+
+ <?= $this->hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
+
+ <div class="form-column">
+ <?= $this->task->selectColor($values) ?>
+ <?= $this->task->selectAssignee($users_list, $values, $errors) ?>
+ <?= $this->task->selectCategory($categories_list, $values, $errors) ?>
+ <?= $this->task->selectPriority($project, $values) ?>
+ <?= $this->task->selectScore($values, $errors) ?>
+ <?= $this->task->selectReference($values, $errors) ?>
+
+ <?= $this->hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
+
+ <div class="form-column">
+ <?= $this->task->selectTimeEstimated($values, $errors) ?>
+ <?= $this->task->selectTimeSpent($values, $errors) ?>
+ <?= $this->task->selectStartDate($values, $errors) ?>
+ <?= $this->task->selectDueDate($values, $errors) ?>
+
+ <?= $this->hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
+ </div>
+
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue" tabindex="15"><?= t('Save') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
+ </div>
+</form>
diff --git a/app/Template/task_popover/change_assignee.php b/app/Template/task_popover/change_assignee.php
deleted file mode 100644
index 02f3e198..00000000
--- a/app/Template/task_popover/change_assignee.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<section id="main">
- <div class="page-header">
- <h2><?= t('Change assignee for the task "%s"', $values['title']) ?></h2>
- </div>
- <form class="popover-form" method="post" action="<?= $this->url->href('TaskPopoverController', 'updateAssignee', array('task_id' => $values['id'], 'project_id' => $project['id'])) ?>">
-
- <?= $this->form->csrf() ?>
-
- <?= $this->form->hidden('id', $values) ?>
- <?= $this->form->hidden('project_id', $values) ?>
-
- <?= $this->task->selectAssignee($users_list, $values, array(), array('autofocus')) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
- </form>
-</section>
diff --git a/app/Template/task_popover/change_category.php b/app/Template/task_popover/change_category.php
deleted file mode 100644
index eb6a373d..00000000
--- a/app/Template/task_popover/change_category.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<section id="main">
- <div class="page-header">
- <h2><?= t('Change category for the task "%s"', $values['title']) ?></h2>
- </div>
- <form class="popover-form" method="post" action="<?= $this->url->href('TaskPopoverController', 'updateCategory', array('task_id' => $values['id'], 'project_id' => $project['id'])) ?>">
-
- <?= $this->form->csrf() ?>
-
- <?= $this->form->hidden('id', $values) ?>
- <?= $this->form->hidden('project_id', $values) ?>
-
- <?= $this->task->selectCategory($categories_list, $values, array(), array('autofocus'), true) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
- </form>
-</section>
diff --git a/app/Template/user_creation/local.php b/app/Template/user_creation/local.php
index 4c224cec..059a0114 100644
--- a/app/Template/user_creation/local.php
+++ b/app/Template/user_creation/local.php
@@ -4,37 +4,39 @@
<form class="popover-form" method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off">
<?= $this->form->csrf() ?>
- <div class="form-column">
- <?= $this->form->label(t('Username'), 'username') ?>
- <?= $this->form->text('username', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <div class="form-columns">
+ <div class="form-column">
+ <?= $this->form->label(t('Username'), 'username') ?>
+ <?= $this->form->text('username', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
- <?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors) ?>
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors) ?>
- <?= $this->form->label(t('Email'), 'email') ?>
- <?= $this->form->email('email', $values, $errors) ?>
+ <?= $this->form->label(t('Email'), 'email') ?>
+ <?= $this->form->email('email', $values, $errors) ?>
- <?= $this->form->label(t('Password'), 'password') ?>
- <?= $this->form->password('password', $values, $errors, array('required')) ?>
+ <?= $this->form->label(t('Password'), 'password') ?>
+ <?= $this->form->password('password', $values, $errors, array('required')) ?>
- <?= $this->form->label(t('Confirmation'), 'confirmation') ?>
- <?= $this->form->password('confirmation', $values, $errors, array('required')) ?>
- </div>
+ <?= $this->form->label(t('Confirmation'), 'confirmation') ?>
+ <?= $this->form->password('confirmation', $values, $errors, array('required')) ?>
+ </div>
- <div class="form-column">
- <?= $this->form->label(t('Add project member'), 'project_id') ?>
- <?= $this->form->select('project_id', $projects, $values, $errors) ?>
+ <div class="form-column">
+ <?= $this->form->label(t('Add project member'), 'project_id') ?>
+ <?= $this->form->select('project_id', $projects, $values, $errors) ?>
- <?= $this->form->label(t('Timezone'), 'timezone') ?>
- <?= $this->form->select('timezone', $timezones, $values, $errors) ?>
+ <?= $this->form->label(t('Timezone'), 'timezone') ?>
+ <?= $this->form->select('timezone', $timezones, $values, $errors) ?>
- <?= $this->form->label(t('Language'), 'language') ?>
- <?= $this->form->select('language', $languages, $values, $errors) ?>
+ <?= $this->form->label(t('Language'), 'language') ?>
+ <?= $this->form->select('language', $languages, $values, $errors) ?>
- <?= $this->form->label(t('Role'), 'role') ?>
- <?= $this->form->select('role', $roles, $values, $errors) ?>
+ <?= $this->form->label(t('Role'), 'role') ?>
+ <?= $this->form->select('role', $roles, $values, $errors) ?>
- <?= $this->form->checkbox('notifications_enabled', t('Enable email notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?>
+ <?= $this->form->checkbox('notifications_enabled', t('Enable email notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?>
+ </div>
</div>
<div class="form-actions">
diff --git a/app/Template/user_creation/remote.php b/app/Template/user_creation/remote.php
index dc4981a4..41d0d3c7 100644
--- a/app/Template/user_creation/remote.php
+++ b/app/Template/user_creation/remote.php
@@ -2,38 +2,39 @@
<h2><?= t('New remote user') ?></h2>
</div>
<form class="popover-form" method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
<?= $this->form->hidden('is_ldap_user', array('is_ldap_user' => 1)) ?>
- <div class="form-column">
- <?= $this->form->label(t('Username'), 'username') ?>
- <?= $this->form->text('username', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
-
- <?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors) ?>
+ <div class="form-columns">
+ <div class="form-column">
+ <?= $this->form->label(t('Username'), 'username') ?>
+ <?= $this->form->text('username', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors) ?>
- <?= $this->form->label(t('Email'), 'email') ?>
- <?= $this->form->email('email', $values, $errors) ?>
+ <?= $this->form->label(t('Email'), 'email') ?>
+ <?= $this->form->email('email', $values, $errors) ?>
- <?= $this->hook->render('template:user:create-remote:form', array('values' => $values, 'errors' => $errors)) ?>
- </div>
+ <?= $this->hook->render('template:user:create-remote:form', array('values' => $values, 'errors' => $errors)) ?>
+ </div>
- <div class="form-column">
- <?= $this->form->label(t('Add project member'), 'project_id') ?>
- <?= $this->form->select('project_id', $projects, $values, $errors) ?>
+ <div class="form-column">
+ <?= $this->form->label(t('Add project member'), 'project_id') ?>
+ <?= $this->form->select('project_id', $projects, $values, $errors) ?>
- <?= $this->form->label(t('Timezone'), 'timezone') ?>
- <?= $this->form->select('timezone', $timezones, $values, $errors) ?>
+ <?= $this->form->label(t('Timezone'), 'timezone') ?>
+ <?= $this->form->select('timezone', $timezones, $values, $errors) ?>
- <?= $this->form->label(t('Language'), 'language') ?>
- <?= $this->form->select('language', $languages, $values, $errors) ?>
+ <?= $this->form->label(t('Language'), 'language') ?>
+ <?= $this->form->select('language', $languages, $values, $errors) ?>
- <?= $this->form->label(t('Role'), 'role') ?>
- <?= $this->form->select('role', $roles, $values, $errors) ?>
+ <?= $this->form->label(t('Role'), 'role') ?>
+ <?= $this->form->select('role', $roles, $values, $errors) ?>
- <?= $this->form->checkbox('notifications_enabled', t('Enable email notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?>
- <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?>
+ <?= $this->form->checkbox('notifications_enabled', t('Enable email notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?>
+ <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?>
+ </div>
</div>
<div class="form-actions">
diff --git a/app/Template/user_list/show.php b/app/Template/user_list/show.php
index b2bd9377..5e285c89 100644
--- a/app/Template/user_list/show.php
+++ b/app/Template/user_list/show.php
@@ -12,7 +12,7 @@
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No user') ?></p>
<?php else: ?>
- <table class="table-stripped">
+ <table class="table-scrolling table-striped">
<tr>
<th class="column-5"><?= $paginator->order(t('Id'), 'id') ?></th>
<th class="column-18"><?= $paginator->order(t('Username'), 'username') ?></th>
diff --git a/app/Template/user_modification/show.php b/app/Template/user_modification/show.php
index 396d550d..506c9161 100644
--- a/app/Template/user_modification/show.php
+++ b/app/Template/user_modification/show.php
@@ -11,16 +11,16 @@
<?= $this->form->text('username', $values, $errors, array('required', isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1 ? 'readonly' : '', 'maxlength="50"')) ?>
<?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors) ?>
+ <?= $this->form->text('name', $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_name') ? '' : 'readonly')) ?>
<?= $this->form->label(t('Email'), 'email') ?>
- <?= $this->form->email('email', $values, $errors) ?>
+ <?= $this->form->email('email', $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_email') ? '' : 'readonly')) ?>
<?= $this->form->label(t('Timezone'), 'timezone') ?>
- <?= $this->form->select('timezone', $timezones, $values, $errors) ?>
+ <?= $this->form->select('timezone', $timezones, $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_timezone') ? '' : 'disabled')) ?>
<?= $this->form->label(t('Language'), 'language') ?>
- <?= $this->form->select('language', $languages, $values, $errors) ?>
+ <?= $this->form->select('language', $languages, $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_language') ? '' : 'disabled')) ?>
<?php if ($this->user->isAdmin()): ?>
<?= $this->form->label(t('Role'), 'role') ?>
diff --git a/app/Template/user_view/last.php b/app/Template/user_view/last.php
index 3de4d5e2..72f59bf6 100644
--- a/app/Template/user_view/last.php
+++ b/app/Template/user_view/last.php
@@ -5,7 +5,7 @@
<?php if (empty($last_logins)): ?>
<p class="alert"><?= t('Never connected.') ?></p>
<?php else: ?>
- <table class="table-small table-fixed">
+ <table class="table-small table-fixed table-scrolling table-striped">
<tr>
<th class="column-20"><?= t('Login date') ?></th>
<th class="column-15"><?= t('Authentication method') ?></th>
@@ -21,4 +21,4 @@
</tr>
<?php endforeach ?>
</table>
-<?php endif ?> \ No newline at end of file
+<?php endif ?>
diff --git a/app/Template/user_view/password_reset.php b/app/Template/user_view/password_reset.php
index 1371ce11..de7047e0 100644
--- a/app/Template/user_view/password_reset.php
+++ b/app/Template/user_view/password_reset.php
@@ -5,7 +5,7 @@
<?php if (empty($tokens)): ?>
<p class="alert"><?= t('The password has never been reinitialized.') ?></p>
<?php else: ?>
- <table class="table-small table-fixed">
+ <table class="table-small table-fixed table-scrolling table-striped">
<tr>
<th class="column-20"><?= t('Creation') ?></th>
<th class="column-20"><?= t('Expiration') ?></th>
@@ -23,4 +23,4 @@
</tr>
<?php endforeach ?>
</table>
-<?php endif ?> \ No newline at end of file
+<?php endif ?>
diff --git a/app/Template/user_view/sessions.php b/app/Template/user_view/sessions.php
index eda3ef7f..10497e4f 100644
--- a/app/Template/user_view/sessions.php
+++ b/app/Template/user_view/sessions.php
@@ -5,7 +5,7 @@
<?php if (empty($sessions)): ?>
<p class="alert"><?= t('No session.') ?></p>
<?php else: ?>
- <table class="table-small table-fixed">
+ <table class="table-small table-fixed table-scrolling table-striped">
<tr>
<th class="column-20"><?= t('Creation date') ?></th>
<th class="column-20"><?= t('Expiration date') ?></th>
diff --git a/app/Template/user_view/sidebar.php b/app/Template/user_view/sidebar.php
index d200a7f5..a80daefa 100644
--- a/app/Template/user_view/sidebar.php
+++ b/app/Template/user_view/sidebar.php
@@ -1,5 +1,7 @@
<div class="sidebar">
- <h2><?= t('Information') ?></h2>
+ <div class="sidebar-title">
+ <h2><?= t('Information') ?></h2>
+ </div>
<ul>
<?php if ($this->user->hasAccess('UserViewController', 'show')): ?>
<li <?= $this->app->checkMenuSelection('UserViewController', 'show') ?>>
@@ -12,24 +14,34 @@
</li>
<?php endif ?>
<?php if ($this->user->isAdmin() || $this->user->isCurrentUser($user['id'])): ?>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'timesheet') ?>>
- <?= $this->url->link(t('Time tracking'), 'UserViewController', 'timesheet', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'lastLogin') ?>>
- <?= $this->url->link(t('Last logins'), 'UserViewController', 'lastLogin', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'sessions') ?>>
- <?= $this->url->link(t('Persistent connections'), 'UserViewController', 'sessions', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'passwordReset') ?>>
- <?= $this->url->link(t('Password reset history'), 'UserViewController', 'passwordReset', array('user_id' => $user['id'])) ?>
- </li>
+ <?php if ($this->user->hasAccess('UserViewController', 'timesheet')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'timesheet') ?>>
+ <?= $this->url->link(t('Time tracking'), 'UserViewController', 'timesheet', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->hasAccess('UserViewController', 'lastLogin')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'lastLogin') ?>>
+ <?= $this->url->link(t('Last logins'), 'UserViewController', 'lastLogin', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->hasAccess('UserViewController', 'sessions')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'sessions') ?>>
+ <?= $this->url->link(t('Persistent connections'), 'UserViewController', 'sessions', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->hasAccess('UserViewController', 'passwordReset')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'passwordReset') ?>>
+ <?= $this->url->link(t('Password reset history'), 'UserViewController', 'passwordReset', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
<?php endif ?>
<?= $this->hook->render('template:user:sidebar:information', array('user' => $user)) ?>
</ul>
- <h2><?= t('Actions') ?></h2>
+ <div class="sidebar-title">
+ <h2><?= t('Actions') ?></h2>
+ </div>
<ul>
<?php if ($this->user->isAdmin() || $this->user->isCurrentUser($user['id'])): ?>
@@ -42,13 +54,13 @@
</li>
<?php endif ?>
- <?php if ($user['is_ldap_user'] == 0): ?>
+ <?php if ($user['is_ldap_user'] == 0 && $this->user->hasAccess('UserCredentialController', 'changePassword')): ?>
<li <?= $this->app->checkMenuSelection('UserCredentialController', 'changePassword') ?>>
<?= $this->url->link(t('Change password'), 'UserCredentialController', 'changePassword', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
- <?php if ($this->user->isCurrentUser($user['id'])): ?>
+ <?php if ($this->user->isCurrentUser($user['id']) && $this->user->hasAccess('TwoFactorController', 'index')): ?>
<li <?= $this->app->checkMenuSelection('TwoFactorController', 'index') ?>>
<?= $this->url->link(t('Two factor authentication'), 'TwoFactorController', 'index', array('user_id' => $user['id'])) ?>
</li>
@@ -58,18 +70,26 @@
</li>
<?php endif ?>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'share') ?>>
- <?= $this->url->link(t('Public access'), 'UserViewController', 'share', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'notifications') ?>>
- <?= $this->url->link(t('Notifications'), 'UserViewController', 'notifications', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'external') ?>>
- <?= $this->url->link(t('External accounts'), 'UserViewController', 'external', array('user_id' => $user['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('UserViewController', 'integrations') ?>>
- <?= $this->url->link(t('Integrations'), 'UserViewController', 'integrations', array('user_id' => $user['id'])) ?>
- </li>
+ <?php if ($this->user->hasAccess('UserViewController', 'share')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'share') ?>>
+ <?= $this->url->link(t('Public access'), 'UserViewController', 'share', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->hasAccess('UserViewController', 'notifications')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'notifications') ?>>
+ <?= $this->url->link(t('Notifications'), 'UserViewController', 'notifications', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->hasAccess('UserViewController', 'external')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'external') ?>>
+ <?= $this->url->link(t('External accounts'), 'UserViewController', 'external', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->user->hasAccess('UserViewController', 'integrations')): ?>
+ <li <?= $this->app->checkMenuSelection('UserViewController', 'integrations') ?>>
+ <?= $this->url->link(t('Integrations'), 'UserViewController', 'integrations', array('user_id' => $user['id'])) ?>
+ </li>
+ <?php endif ?>
<?php endif ?>
<?php if ($this->user->hasAccess('UserCredentialController', 'changeAuthentication')): ?>
diff --git a/app/Template/user_view/timesheet.php b/app/Template/user_view/timesheet.php
index 3df57492..aeffd2f4 100644
--- a/app/Template/user_view/timesheet.php
+++ b/app/Template/user_view/timesheet.php
@@ -6,7 +6,7 @@
<?php if ($subtask_paginator->isEmpty()): ?>
<p class="alert"><?= t('There is nothing to show.') ?></p>
<?php else: ?>
- <table class="table-fixed">
+ <table class="table-fixed table-scrolling table-striped">
<tr>
<th class="column-25"><?= $subtask_paginator->order(t('Task'), 'task_title') ?></th>
<th class="column-25"><?= $subtask_paginator->order(t('Subtask'), 'subtask_title') ?></th>
diff --git a/app/User/Avatar/LetterAvatarProvider.php b/app/User/Avatar/LetterAvatarProvider.php
index b7a95f41..727f9109 100644
--- a/app/User/Avatar/LetterAvatarProvider.php
+++ b/app/User/Avatar/LetterAvatarProvider.php
@@ -142,7 +142,7 @@ class LetterAvatarProvider extends Base implements AvatarProviderInterface
// Make hash more sensitive for short string like 'a', 'b', 'c'
$str .= 'x';
- $max = intval(9007199254740991 / $seed2);
+ $max = intval(PHP_INT_MAX / $seed2);
for ($i = 0, $ilen = mb_strlen($str, 'UTF-8'); $i < $ilen; $i++) {
if ($hash > $max) {
diff --git a/app/User/OAuthUserProvider.php b/app/User/OAuthUserProvider.php
index dec26250..e5fedcca 100644
--- a/app/User/OAuthUserProvider.php
+++ b/app/User/OAuthUserProvider.php
@@ -13,14 +13,6 @@ use Kanboard\Core\User\UserProviderInterface;
abstract class OAuthUserProvider implements UserProviderInterface
{
/**
- * Get external id column name
- *
- * @access public
- * @return string
- */
- abstract public function getExternalIdColumn();
-
- /**
* User properties
*
* @access protected
@@ -69,7 +61,7 @@ abstract class OAuthUserProvider implements UserProviderInterface
*/
public function getExternalId()
{
- return $this->user['id'];
+ return isset($this->user['id']) ? $this->user['id'] : '';
}
/**
@@ -102,7 +94,7 @@ abstract class OAuthUserProvider implements UserProviderInterface
*/
public function getName()
{
- return $this->user['name'];
+ return isset($this->user['name']) ? $this->user['name'] : '';
}
/**
@@ -113,7 +105,7 @@ abstract class OAuthUserProvider implements UserProviderInterface
*/
public function getEmail()
{
- return $this->user['email'];
+ return isset($this->user['email']) ? $this->user['email'] : '';
}
/**
diff --git a/app/User/ReverseProxyUserProvider.php b/app/User/ReverseProxyUserProvider.php
index 723b8155..34d2187d 100644
--- a/app/User/ReverseProxyUserProvider.php
+++ b/app/User/ReverseProxyUserProvider.php
@@ -22,14 +22,23 @@ class ReverseProxyUserProvider implements UserProviderInterface
protected $username = '';
/**
+ * User profile if the user already exists
+ *
+ * @access protected
+ * @var array
+ */
+ private $userProfile = array();
+
+ /**
* Constructor
*
* @access public
* @param string $username
*/
- public function __construct($username)
+ public function __construct($username, array $userProfile = array())
{
$this->username = $username;
+ $this->userProfile = $userProfile;
}
/**
@@ -84,7 +93,15 @@ class ReverseProxyUserProvider implements UserProviderInterface
*/
public function getRole()
{
- return REVERSE_PROXY_DEFAULT_ADMIN === $this->username ? Role::APP_ADMIN : Role::APP_USER;
+ if (REVERSE_PROXY_DEFAULT_ADMIN === $this->username) {
+ return Role::APP_ADMIN;
+ }
+
+ if (isset($this->userProfile['role'])) {
+ return $this->userProfile['role'];
+ }
+
+ return Role::APP_USER;
}
/**
diff --git a/app/Validator/ProjectValidator.php b/app/Validator/ProjectValidator.php
index 9ef59111..8c6117a4 100644
--- a/app/Validator/ProjectValidator.php
+++ b/app/Validator/ProjectValidator.php
@@ -28,7 +28,7 @@ class ProjectValidator extends BaseValidator
new Validators\Integer('priority_start', t('This value must be an integer')),
new Validators\Integer('priority_end', t('This value must be an integer')),
new Validators\Integer('is_active', t('This value must be an integer')),
- new Validators\Required('name', t('The project name is required')),
+ new Validators\NotEmpty('name', t('This field cannot be empty')),
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50),
new Validators\MaxLength('identifier', t('The maximum length is %d characters', 50), 50),
new Validators\MaxLength('start_date', t('The maximum length is %d characters', 10), 10),
@@ -47,11 +47,15 @@ class ProjectValidator extends BaseValidator
*/
public function validateCreation(array $values)
{
+ $rules = array(
+ new Validators\Required('name', t('The project name is required')),
+ );
+
if (! empty($values['identifier'])) {
$values['identifier'] = strtoupper($values['identifier']);
}
- $v = new Validator($values, $this->commonValidationRules());
+ $v = new Validator($values, array_merge($this->commonValidationRules(), $rules));
return array(
$v->execute(),
diff --git a/app/Validator/TagValidator.php b/app/Validator/TagValidator.php
new file mode 100644
index 00000000..0567cf3e
--- /dev/null
+++ b/app/Validator/TagValidator.php
@@ -0,0 +1,76 @@
+<?php
+
+namespace Kanboard\Validator;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Tag Validator
+ *
+ * @package Kanboard\Validator
+ * @author Frederic Guillot
+ */
+class TagValidator extends BaseValidator
+{
+ /**
+ * Validate creation
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateCreation(array $values)
+ {
+ $v = new Validator($values, $this->commonValidationRules());
+ $result = $v->execute();
+ $errors = $v->getErrors();
+
+ if ($result && $this->tagModel->exists($values['project_id'], $values['name'])) {
+ $result = false;
+ $errors = array('name' => array(t('The name must be unique')));
+ }
+
+ return array($result, $errors);
+ }
+
+ /**
+ * Validate modification
+ *
+ * @access public
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateModification(array $values)
+ {
+ $rules = array(
+ new Validators\Required('id', t('Field required')),
+ );
+
+ $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
+ $result = $v->execute();
+ $errors = $v->getErrors();
+
+ if ($result && $this->tagModel->exists($values['project_id'], $values['name'], $values['id'])) {
+ $result = false;
+ $errors = array('name' => array(t('The name must be unique')));
+ }
+
+ return array($result, $errors);
+ }
+
+ /**
+ * Common validation rules
+ *
+ * @access protected
+ * @return array
+ */
+ protected function commonValidationRules()
+ {
+ return array(
+ new Validators\Required('project_id', t('Field required')),
+ new Validators\Required('name', t('Field required')),
+ new Validators\MaxLength('name', t('The maximum length is %d characters', 255), 255),
+ );
+ }
+}
diff --git a/app/Validator/TaskValidator.php b/app/Validator/TaskValidator.php
index 90bda6f3..8aa5c440 100644
--- a/app/Validator/TaskValidator.php
+++ b/app/Validator/TaskValidator.php
@@ -182,53 +182,6 @@ class TaskValidator extends BaseValidator
}
/**
- * Validate assignee change
- *
- * @access public
- * @param array $values Form values
- * @return array $valid, $errors [0] = Success or not, [1] = List of errors
- */
- public function validateAssigneeModification(array $values)
- {
- $rules = array(
- new Validators\Required('id', t('The id is required')),
- new Validators\Required('project_id', t('The project is required')),
- new Validators\Required('owner_id', t('This value is required')),
- );
-
- $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
-
- return array(
- $v->execute(),
- $v->getErrors()
- );
- }
-
- /**
- * Validate category change
- *
- * @access public
- * @param array $values Form values
- * @return array $valid, $errors [0] = Success or not, [1] = List of errors
- */
- public function validateCategoryModification(array $values)
- {
- $rules = array(
- new Validators\Required('id', t('The id is required')),
- new Validators\Required('project_id', t('The project is required')),
- new Validators\Required('category_id', t('This value is required')),
-
- );
-
- $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
-
- return array(
- $v->execute(),
- $v->getErrors()
- );
- }
-
- /**
* Validate project modification
*
* @access public
diff --git a/app/common.php b/app/common.php
index 72be3603..15fd7a75 100644
--- a/app/common.php
+++ b/app/common.php
@@ -46,6 +46,7 @@ $container->register(new Kanboard\ServiceProvider\ActionProvider());
$container->register(new Kanboard\ServiceProvider\ExternalLinkProvider());
$container->register(new Kanboard\ServiceProvider\AvatarProvider());
$container->register(new Kanboard\ServiceProvider\FilterProvider());
+$container->register(new Kanboard\ServiceProvider\JobProvider());
$container->register(new Kanboard\ServiceProvider\QueueProvider());
$container->register(new Kanboard\ServiceProvider\ApiProvider());
$container->register(new Kanboard\ServiceProvider\CommandProvider());
diff --git a/app/constants.php b/app/constants.php
index 604f6acd..40b88fe9 100644
--- a/app/constants.php
+++ b/app/constants.php
@@ -21,7 +21,7 @@ defined('PLUGIN_INSTALLER') or define('PLUGIN_INSTALLER', true);
defined('DEBUG') or define('DEBUG', strtolower(getenv('DEBUG')) === 'true');
// Logging drivers: syslog, stdout, stderr or file
-defined('LOG_DRIVER') or define('LOG_DRIVER', getenv('LOG_DRIVER'));
+defined('LOG_DRIVER') or define('LOG_DRIVER', '');
// Logging file
defined('LOG_FILE') or define('LOG_FILE', DATA_DIR.DIRECTORY_SEPARATOR.'debug.log');
@@ -134,3 +134,5 @@ defined('HTTP_PROXY_PORT') or define('HTTP_PROXY_PORT', '3128');
defined('HTTP_PROXY_USERNAME') or define('HTTP_PROXY_USERNAME', '');
defined('HTTP_PROXY_PASSWORD') or define('HTTP_PROXY_PASSWORD', '');
defined('HTTP_VERIFY_SSL_CERTIFICATE') or define('HTTP_VERIFY_SSL_CERTIFICATE', true);
+
+defined('TOTP_ISSUER') or define('TOTP_ISSUER', 'Kanboard');
diff --git a/app/functions.php b/app/functions.php
index b759763f..eaf33a52 100644
--- a/app/functions.php
+++ b/app/functions.php
@@ -3,6 +3,82 @@
use Kanboard\Core\Translator;
/**
+ * Associate another dict to a dict based on a common key
+ *
+ * @param array $input
+ * @param array $relations
+ * @param string $relation
+ * @param string $column
+ */
+function array_merge_relation(array &$input, array &$relations, $relation, $column)
+{
+ foreach ($input as &$row) {
+ if (isset($row[$column]) && isset($relations[$row[$column]])) {
+ $row[$relation] = $relations[$row[$column]];
+ } else {
+ $row[$relation] = array();
+ }
+ }
+}
+
+/**
+ * Create indexed array from a list of dict
+ *
+ * $input = [
+ * ['k1' => 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 2, 'k2' => 5]
+ * ]
+ *
+ * array_column_index($input, 'k1') will returns:
+ *
+ * [
+ * 1 => [['k1' => 1, 'k2' => 2], ['k1' => 2, 'k2' => 5]],
+ * 3 => [['k1' => 3, 'k2' => 4]],
+ * ]
+ *
+ * @param array $input
+ * @param string $column
+ * @return array
+ */
+function array_column_index(array &$input, $column)
+{
+ $result = array();
+
+ foreach ($input as &$row) {
+ if (isset($row[$column])) {
+ $result[$row[$column]][] = $row;
+ }
+ }
+
+ return $result;
+}
+
+/**
+ * Sum all values from a single column in the input array
+ *
+ * $input = [
+ * ['column' => 2], ['column' => 3]
+ * ]
+ *
+ * array_column_sum($input, 'column') returns 5
+ *
+ * @param array $input
+ * @param string $column
+ * @return double
+ */
+function array_column_sum(array &$input, $column)
+{
+ $sum = 0.0;
+
+ foreach ($input as &$row) {
+ if (isset($row[$column])) {
+ $sum += (float) $row[$column];
+ }
+ }
+
+ return $sum;
+}
+
+/**
* Build version number from git-archive output
*
* @param string $ref