summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
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/TaskAssignColorSwimlane.php99
-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/TaskAssignPrioritySwimlane.php99
-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/TaskCloseNotMovedColumn.php96
-rw-r--r--app/Action/TaskCreation.php3
-rw-r--r--app/Action/TaskDuplicateAnotherProject.php16
-rw-r--r--app/Action/TaskEmail.php17
-rw-r--r--app/Action/TaskEmailNoActivity.php2
-rw-r--r--app/Action/TaskMoveAnotherProject.php10
-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/Analytic/EstimatedTimeComparisonAnalytic.php4
-rw-r--r--app/Api/Authorization/ProjectAuthorization.php4
-rw-r--r--app/Api/Authorization/TagAuthorization.php23
-rw-r--r--app/Api/Authorization/TaskAuthorization.php4
-rw-r--r--app/Api/Middleware/AuthenticationMiddleware.php3
-rw-r--r--app/Api/Procedure/ActionProcedure.php6
-rw-r--r--app/Api/Procedure/BoardProcedure.php3
-rw-r--r--app/Api/Procedure/MeProcedure.php2
-rw-r--r--app/Api/Procedure/ProjectPermissionProcedure.php4
-rw-r--r--app/Api/Procedure/TagProcedure.php44
-rw-r--r--app/Api/Procedure/TaskMetadataProcedure.php38
-rw-r--r--app/Api/Procedure/TaskProcedure.php16
-rw-r--r--app/Api/Procedure/TaskTagProcedure.php26
-rw-r--r--app/Auth/ApiAccessTokenAuth.php119
-rw-r--r--app/Auth/DatabaseAuth.php2
-rw-r--r--app/Auth/LdapAuth.php2
-rw-r--r--app/Auth/RememberMeAuth.php4
-rw-r--r--app/Auth/ReverseProxyAuth.php4
-rw-r--r--app/Auth/TotpAuth.php8
-rw-r--r--app/Console/DatabaseMigrationCommand.php23
-rw-r--r--app/Console/DatabaseVersionCommand.php23
-rw-r--r--app/Console/JobCommand.php35
-rw-r--r--app/Console/ResetTwoFactorCommand.php6
-rw-r--r--app/Controller/ActionController.php1
-rw-r--r--app/Controller/ActionCreationController.php1
-rw-r--r--app/Controller/ActivityController.php1
-rw-r--r--app/Controller/AnalyticController.php38
-rw-r--r--app/Controller/AppController.php17
-rw-r--r--app/Controller/BoardAjaxController.php18
-rw-r--r--app/Controller/BoardTooltipController.php5
-rw-r--r--app/Controller/BoardViewController.php12
-rw-r--r--app/Controller/CalendarController.php1
-rw-r--r--app/Controller/ColumnController.php39
-rw-r--r--app/Controller/ColumnMoveRestrictionController.php102
-rw-r--r--app/Controller/ColumnRestrictionController.php103
-rw-r--r--app/Controller/CommentController.php34
-rw-r--r--app/Controller/ConfigController.php1
-rw-r--r--app/Controller/CurrencyController.php59
-rw-r--r--app/Controller/CustomFilterController.php34
-rw-r--r--app/Controller/DashboardController.php90
-rw-r--r--app/Controller/ExportController.php36
-rw-r--r--app/Controller/ExternalTaskCreationController.php97
-rw-r--r--app/Controller/ExternalTaskViewController.php30
-rw-r--r--app/Controller/FeedController.php60
-rw-r--r--app/Controller/FileViewerController.php53
-rw-r--r--app/Controller/GroupAjaxController.php6
-rw-r--r--app/Controller/ICalendarController.php5
-rw-r--r--app/Controller/LinkController.php50
-rw-r--r--app/Controller/PasswordResetController.php2
-rw-r--r--app/Controller/PluginController.php1
-rw-r--r--app/Controller/ProjectEditController.php95
-rw-r--r--app/Controller/ProjectFileController.php2
-rw-r--r--app/Controller/ProjectGanttController.php3
-rw-r--r--app/Controller/ProjectListController.php2
-rw-r--r--app/Controller/ProjectOverviewController.php2
-rw-r--r--app/Controller/ProjectPermissionController.php6
-rw-r--r--app/Controller/ProjectRoleController.php162
-rw-r--r--app/Controller/ProjectRoleRestrictionController.php96
-rw-r--r--app/Controller/ProjectUserOverviewController.php4
-rw-r--r--app/Controller/SearchController.php2
-rw-r--r--app/Controller/SubtaskController.php25
-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/TaskAjaxController.php27
-rw-r--r--app/Controller/TaskBulkController.php9
-rw-r--r--app/Controller/TaskCreationController.php110
-rw-r--r--app/Controller/TaskExternalLinkController.php21
-rw-r--r--app/Controller/TaskFileController.php2
-rw-r--r--app/Controller/TaskGanttController.php3
-rw-r--r--app/Controller/TaskGanttCreationController.php70
-rw-r--r--app/Controller/TaskImportController.php9
-rw-r--r--app/Controller/TaskModificationController.php58
-rw-r--r--app/Controller/TaskMovePositionController.php51
-rw-r--r--app/Controller/TaskStatusController.php10
-rw-r--r--app/Controller/TaskSuppressionController.php4
-rw-r--r--app/Controller/TaskViewController.php18
-rw-r--r--app/Controller/UserAjaxController.php8
-rw-r--r--app/Controller/UserApiAccessController.php50
-rw-r--r--app/Controller/UserCreationController.php7
-rw-r--r--app/Controller/UserCredentialController.php17
-rw-r--r--app/Controller/UserImportController.php2
-rw-r--r--app/Controller/UserInviteController.php107
-rw-r--r--app/Controller/UserListController.php7
-rw-r--r--app/Core/Action/ActionManager.php16
-rw-r--r--app/Core/Base.php342
-rw-r--r--app/Core/Cache/BaseCache.php (renamed from app/Core/Cache/Base.php)6
-rw-r--r--app/Core/Cache/CacheInterface.php16
-rw-r--r--app/Core/Cache/FileCache.php98
-rw-r--r--app/Core/Cache/MemoryCache.php10
-rw-r--r--app/Core/Controller/Runner.php2
-rw-r--r--app/Core/DateParser.php21
-rw-r--r--app/Core/Event/EventManager.php1
-rw-r--r--app/Core/ExternalTask/AccessForbiddenException.php13
-rw-r--r--app/Core/ExternalTask/ExternalTaskException.php15
-rw-r--r--app/Core/ExternalTask/ExternalTaskInterface.php26
-rw-r--r--app/Core/ExternalTask/ExternalTaskManager.php58
-rw-r--r--app/Core/ExternalTask/ExternalTaskProviderInterface.php77
-rw-r--r--app/Core/ExternalTask/NotFoundException.php13
-rw-r--r--app/Core/ExternalTask/ProviderNotFoundException.php13
-rw-r--r--app/Core/Filter/FormatterInterface.php2
-rw-r--r--app/Core/Filter/LexerBuilder.php4
-rw-r--r--app/Core/Helper.php4
-rw-r--r--app/Core/Http/Request.php16
-rw-r--r--app/Core/Http/Response.php12
-rw-r--r--app/Core/Mail/Transport/Mail.php4
-rw-r--r--app/Core/Mail/Transport/Smtp.php10
-rw-r--r--app/Core/Markdown.php26
-rw-r--r--app/Core/Paginator.php45
-rw-r--r--app/Core/Plugin/Base.php13
-rw-r--r--app/Core/Plugin/Directory.php6
-rw-r--r--app/Core/Plugin/Hook.php17
-rw-r--r--app/Core/Plugin/Installer.php27
-rw-r--r--app/Core/Plugin/Loader.php48
-rw-r--r--app/Core/Plugin/PluginException.php15
-rw-r--r--app/Core/Plugin/PluginInstallerException.php4
-rw-r--r--app/Core/Plugin/Version.php38
-rw-r--r--app/Core/Queue/JobHandler.php36
-rw-r--r--app/Core/Queue/QueueManager.php6
-rw-r--r--app/Core/Security/Role.php12
-rw-r--r--app/Core/Session/SessionStorage.php1
-rw-r--r--app/Core/Tool.php53
-rw-r--r--app/Core/Translator.php26
-rw-r--r--app/Core/User/UserSession.php46
-rw-r--r--app/Decorator/ColumnMoveRestrictionCacheDecorator.php59
-rw-r--r--app/Decorator/ColumnRestrictionCacheDecorator.php59
-rw-r--r--app/Decorator/MetadataCacheDecorator.php96
-rw-r--r--app/Decorator/ProjectRoleRestrictionCacheDecorator.php59
-rw-r--r--app/Decorator/UserCacheDecorator.php59
-rw-r--r--app/Event/FileEvent.php7
-rw-r--r--app/Event/GenericEvent.php22
-rw-r--r--app/Event/ProjectFileEvent.php15
-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/Export/TaskExport.php128
-rw-r--r--app/ExternalLink/FileLinkProvider.php17
-rw-r--r--app/Filter/TaskMovedDateFilter.php38
-rw-r--r--app/Filter/TaskPriorityFilter.php38
-rw-r--r--app/Filter/TaskStartsWithIdFilter.php38
-rw-r--r--app/Filter/TaskStatusFilter.php2
-rw-r--r--app/Filter/TaskTagFilter.php31
-rw-r--r--app/Formatter/BaseFormatter.php14
-rw-r--r--app/Formatter/BaseTaskCalendarFormatter.php4
-rw-r--r--app/Formatter/BoardColumnFormatter.php3
-rw-r--r--app/Formatter/BoardFormatter.php13
-rw-r--r--app/Formatter/BoardSwimlaneFormatter.php7
-rw-r--r--app/Formatter/BoardTaskFormatter.php5
-rw-r--r--app/Formatter/GroupAutoCompleteFormatter.php22
-rw-r--r--app/Formatter/TaskAutoCompleteFormatter.php27
-rw-r--r--app/Formatter/TaskSuggestMenuFormatter.php63
-rw-r--r--app/Formatter/UserAutoCompleteFormatter.php6
-rw-r--r--app/Formatter/UserMentionFormatter.php60
-rw-r--r--app/Helper/AppHelper.php18
-rw-r--r--app/Helper/BoardHelper.php3
-rw-r--r--app/Helper/CalendarHelper.php26
-rw-r--r--app/Helper/DateHelper.php2
-rw-r--r--app/Helper/FileHelper.php29
-rw-r--r--app/Helper/FormHelper.php115
-rw-r--r--app/Helper/HookHelper.php55
-rw-r--r--app/Helper/ICalHelper.php11
-rw-r--r--app/Helper/LayoutHelper.php18
-rw-r--r--app/Helper/ModalHelper.php83
-rw-r--r--app/Helper/ProjectActivityHelper.php11
-rw-r--r--app/Helper/ProjectRoleHelper.php286
-rw-r--r--app/Helper/SubtaskHelper.php13
-rw-r--r--app/Helper/TaskHelper.php119
-rw-r--r--app/Helper/UrlHelper.php68
-rw-r--r--app/Helper/UserHelper.php53
-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.php76
-rw-r--r--app/Job/TaskFileEventJob.php45
-rw-r--r--app/Job/TaskLinkEventJob.php45
-rw-r--r--app/Job/UserMentionJob.php73
-rw-r--r--app/Locale/bs_BA/translations.php204
-rw-r--r--app/Locale/cs_CZ/translations.php204
-rw-r--r--app/Locale/da_DK/translations.php204
-rw-r--r--app/Locale/de_DE/translations.php210
-rw-r--r--app/Locale/el_GR/translations.php204
-rw-r--r--app/Locale/es_ES/translations.php204
-rw-r--r--app/Locale/fi_FI/translations.php204
-rw-r--r--app/Locale/fr_FR/translations.php209
-rw-r--r--app/Locale/hu_HU/translations.php204
-rw-r--r--app/Locale/id_ID/translations.php1548
-rw-r--r--app/Locale/it_IT/translations.php224
-rw-r--r--app/Locale/ja_JP/translations.php204
-rw-r--r--app/Locale/ko_KR/translations.php512
-rw-r--r--app/Locale/my_MY/translations.php204
-rw-r--r--app/Locale/nb_NO/translations.php204
-rw-r--r--app/Locale/nl_NL/translations.php204
-rw-r--r--app/Locale/pl_PL/translations.php204
-rw-r--r--app/Locale/pt_BR/translations.php422
-rw-r--r--app/Locale/pt_PT/translations.php436
-rw-r--r--app/Locale/ru_RU/translations.php328
-rw-r--r--app/Locale/sr_Latn_RS/translations.php204
-rw-r--r--app/Locale/sv_SE/translations.php204
-rw-r--r--app/Locale/th_TH/translations.php324
-rw-r--r--app/Locale/tr_TR/translations.php734
-rw-r--r--app/Locale/zh_CN/translations.php386
-rw-r--r--app/Middleware/BootstrapMiddleware.php1
-rw-r--r--app/Middleware/PostAuthenticationMiddleware.php4
-rw-r--r--app/Model/ActionParameterModel.php3
-rw-r--r--app/Model/ColorModel.php2
-rw-r--r--app/Model/ColumnModel.php12
-rw-r--r--app/Model/ColumnMoveRestrictionModel.php122
-rw-r--r--app/Model/ColumnRestrictionModel.php152
-rw-r--r--app/Model/CommentModel.php16
-rw-r--r--app/Model/CurrencyModel.php2
-rw-r--r--app/Model/FileModel.php26
-rw-r--r--app/Model/GroupModel.php19
-rw-r--r--app/Model/InviteModel.php73
-rw-r--r--app/Model/MetadataModel.php3
-rw-r--r--app/Model/NotificationModel.php185
-rw-r--r--app/Model/ProjectDuplicationModel.php2
-rw-r--r--app/Model/ProjectFileModel.php9
-rw-r--r--app/Model/ProjectModel.php10
-rw-r--r--app/Model/ProjectPermissionModel.php45
-rw-r--r--app/Model/ProjectRoleModel.php196
-rw-r--r--app/Model/ProjectRoleRestrictionModel.php130
-rw-r--r--app/Model/ProjectUserRoleModel.php2
-rw-r--r--app/Model/SubtaskModel.php260
-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/TaskCreationModel.php34
-rw-r--r--app/Model/TaskFileModel.php23
-rw-r--r--app/Model/TaskFinderModel.php165
-rw-r--r--app/Model/TaskLinkModel.php173
-rw-r--r--app/Model/TaskModificationModel.php66
-rw-r--r--app/Model/TaskPositionModel.php65
-rw-r--r--app/Model/TaskProjectMoveModel.php7
-rw-r--r--app/Model/TaskStatusModel.php10
-rw-r--r--app/Model/UserMentionModel.php62
-rw-r--r--app/Model/UserMetadataModel.php3
-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.php105
-rw-r--r--app/Schema/Postgres.php101
-rw-r--r--app/Schema/Sql/mysql.sql91
-rw-r--r--app/Schema/Sql/postgres.sql598
-rw-r--r--app/Schema/Sqlite.php93
-rw-r--r--app/ServiceProvider/ActionProvider.php16
-rw-r--r--app/ServiceProvider/ApiProvider.php6
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php31
-rw-r--r--app/ServiceProvider/CacheProvider.php83
-rw-r--r--app/ServiceProvider/ClassProvider.php21
-rw-r--r--app/ServiceProvider/CommandProvider.php6
-rw-r--r--app/ServiceProvider/DatabaseProvider.php41
-rw-r--r--app/ServiceProvider/EventDispatcherProvider.php2
-rw-r--r--app/ServiceProvider/ExternalTaskProvider.php29
-rw-r--r--app/ServiceProvider/FilterProvider.php14
-rw-r--r--app/ServiceProvider/FormatterProvider.php48
-rw-r--r--app/ServiceProvider/HelperProvider.php2
-rw-r--r--app/ServiceProvider/JobProvider.php72
-rw-r--r--app/ServiceProvider/QueueProvider.php6
-rw-r--r--app/ServiceProvider/RouteProvider.php10
-rw-r--r--app/Subscriber/BaseSubscriber.php24
-rw-r--r--app/Subscriber/BootstrapSubscriber.php2
-rw-r--r--app/Subscriber/NotificationSubscriber.php45
-rw-r--r--app/Subscriber/ProjectDailySummarySubscriber.php7
-rw-r--r--app/Subscriber/ProjectModificationDateSubscriber.php6
-rw-r--r--app/Subscriber/RecurringTaskSubscriber.php14
-rw-r--r--app/Subscriber/SubtaskTimeTrackingSubscriber.php48
-rw-r--r--app/Template/action/index.php16
-rw-r--r--app/Template/action/remove.php10
-rw-r--r--app/Template/action_creation/create.php10
-rw-r--r--app/Template/action_creation/event.php11
-rw-r--r--app/Template/action_creation/params.php12
-rw-r--r--app/Template/activity/project.php14
-rw-r--r--app/Template/activity/task.php11
-rw-r--r--app/Template/analytic/avg_time_columns.php34
-rw-r--r--app/Template/analytic/burndown.php34
-rw-r--r--app/Template/analytic/cfd.php33
-rw-r--r--app/Template/analytic/layout.php18
-rw-r--r--app/Template/analytic/lead_cycle_time.php53
-rw-r--r--app/Template/analytic/sidebar.php22
-rw-r--r--app/Template/analytic/task_distribution.php (renamed from app/Template/analytic/tasks.php)18
-rw-r--r--app/Template/analytic/time_comparison.php (renamed from app/Template/analytic/compare_hours.php)35
-rw-r--r--app/Template/analytic/user_distribution.php (renamed from app/Template/analytic/users.php)18
-rw-r--r--app/Template/board/table_column.php28
-rw-r--r--app/Template/board/table_tasks.php10
-rw-r--r--app/Template/board/task_footer.php14
-rw-r--r--app/Template/board/task_private.php3
-rw-r--r--app/Template/board/tooltip_external_links.php2
-rw-r--r--app/Template/board/tooltip_files.php6
-rw-r--r--app/Template/board/tooltip_subtasks.php6
-rw-r--r--app/Template/board/tooltip_tasklinks.php2
-rw-r--r--app/Template/board_popover/close_all_tasks_column.php29
-rw-r--r--app/Template/calendar/show.php12
-rw-r--r--app/Template/category/edit.php13
-rw-r--r--app/Template/category/index.php6
-rw-r--r--app/Template/category/remove.php28
-rw-r--r--app/Template/column/create.php17
-rw-r--r--app/Template/column/edit.php13
-rw-r--r--app/Template/column/index.php15
-rw-r--r--app/Template/column/remove.php9
-rw-r--r--app/Template/column_move_restriction/create.php18
-rw-r--r--app/Template/column_move_restriction/remove.php15
-rw-r--r--app/Template/column_restriction/create.php16
-rw-r--r--app/Template/column_restriction/remove.php15
-rw-r--r--app/Template/comment/create.php23
-rw-r--r--app/Template/comment/edit.php19
-rw-r--r--app/Template/comment/remove.php10
-rw-r--r--app/Template/comment/show.php12
-rw-r--r--app/Template/comments/create.php15
-rw-r--r--app/Template/comments/show.php6
-rw-r--r--app/Template/config/about.php10
-rw-r--r--app/Template/config/api.php12
-rw-r--r--app/Template/config/application.php40
-rw-r--r--app/Template/config/board.php21
-rw-r--r--app/Template/config/calendar.php28
-rw-r--r--app/Template/config/email.php11
-rw-r--r--app/Template/config/integrations.php8
-rw-r--r--app/Template/config/keyboard_shortcuts.php2
-rw-r--r--app/Template/config/project.php29
-rw-r--r--app/Template/config/sidebar.php7
-rw-r--r--app/Template/config/webhook.php22
-rw-r--r--app/Template/currency/change.php9
-rw-r--r--app/Template/currency/create.php11
-rw-r--r--app/Template/currency/index.php54
-rw-r--r--app/Template/currency/show.php34
-rw-r--r--app/Template/custom_filter/create.php (renamed from app/Template/custom_filter/add.php)9
-rw-r--r--app/Template/custom_filter/edit.php11
-rw-r--r--app/Template/custom_filter/index.php18
-rw-r--r--app/Template/custom_filter/remove.php28
-rw-r--r--app/Template/dashboard/calendar.php9
-rw-r--r--app/Template/dashboard/layout.php12
-rw-r--r--app/Template/dashboard/notifications.php24
-rw-r--r--app/Template/dashboard/projects.php8
-rw-r--r--app/Template/dashboard/show.php13
-rw-r--r--app/Template/dashboard/sidebar.php3
-rw-r--r--app/Template/dashboard/subtasks.php8
-rw-r--r--app/Template/dashboard/tasks.php10
-rw-r--r--app/Template/doc/show.php3
-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/header.php17
-rw-r--r--app/Template/export/sidebar.php18
-rw-r--r--app/Template/export/subtasks.php26
-rw-r--r--app/Template/export/summary.php26
-rw-r--r--app/Template/export/tasks.php26
-rw-r--r--app/Template/export/transitions.php26
-rw-r--r--app/Template/external_task_creation/step1.php16
-rw-r--r--app/Template/external_task_creation/step2.php22
-rw-r--r--app/Template/external_task_modification/show.php22
-rw-r--r--app/Template/feed/project.php27
-rw-r--r--app/Template/feed/user.php27
-rw-r--r--app/Template/group/associate.php17
-rw-r--r--app/Template/group/dissociate.php10
-rw-r--r--app/Template/group/index.php14
-rw-r--r--app/Template/group/remove.php10
-rw-r--r--app/Template/group/users.php9
-rw-r--r--app/Template/group_creation/show.php8
-rw-r--r--app/Template/group_modification/show.php8
-rw-r--r--app/Template/header.php138
-rw-r--r--app/Template/header/board_selector.php13
-rw-r--r--app/Template/header/creation_dropdown.php21
-rw-r--r--app/Template/header/title.php17
-rw-r--r--app/Template/header/user_dropdown.php40
-rw-r--r--app/Template/header/user_notifications.php5
-rw-r--r--app/Template/layout.php2
-rw-r--r--app/Template/link/create.php12
-rw-r--r--app/Template/link/edit.php6
-rw-r--r--app/Template/link/index.php33
-rw-r--r--app/Template/link/remove.php10
-rw-r--r--app/Template/link/show.php36
-rw-r--r--app/Template/notification/comment_create.php2
-rw-r--r--app/Template/notification/comment_delete.php7
-rw-r--r--app/Template/notification/comment_update.php2
-rw-r--r--app/Template/notification/comment_user_mention.php2
-rw-r--r--app/Template/notification/footer.php4
-rw-r--r--app/Template/notification/subtask_delete.php11
-rw-r--r--app/Template/notification/task_assignee_change.php2
-rw-r--r--app/Template/notification/task_create.php2
-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/notification/task_open.php2
-rw-r--r--app/Template/notification/task_overdue.php8
-rw-r--r--app/Template/notification/task_update.php2
-rw-r--r--app/Template/notification/task_user_mention.php2
-rw-r--r--app/Template/plugin/directory.php6
-rw-r--r--app/Template/plugin/remove.php10
-rw-r--r--app/Template/plugin/show.php43
-rw-r--r--app/Template/plugin/sidebar.php1
-rw-r--r--app/Template/project/dropdown.php25
-rw-r--r--app/Template/project/sidebar.php14
-rw-r--r--app/Template/project_action_duplication/show.php8
-rw-r--r--app/Template/project_creation/create.php14
-rw-r--r--app/Template/project_edit/dates.php26
-rw-r--r--app/Template/project_edit/description.php19
-rw-r--r--app/Template/project_edit/general.php36
-rw-r--r--app/Template/project_edit/show.php58
-rw-r--r--app/Template/project_edit/task_priority.php29
-rw-r--r--app/Template/project_file/create.php43
-rw-r--r--app/Template/project_file/remove.php10
-rw-r--r--app/Template/project_gantt/show.php16
-rw-r--r--app/Template/project_header/dropdown.php45
-rw-r--r--app/Template/project_header/header.php26
-rw-r--r--app/Template/project_header/search.php77
-rw-r--r--app/Template/project_header/views.php17
-rw-r--r--app/Template/project_list/show.php32
-rw-r--r--app/Template/project_overview/attachments.php2
-rw-r--r--app/Template/project_overview/columns.php4
-rw-r--r--app/Template/project_overview/description.php4
-rw-r--r--app/Template/project_overview/files.php15
-rw-r--r--app/Template/project_overview/images.php18
-rw-r--r--app/Template/project_overview/information.php8
-rw-r--r--app/Template/project_overview/show.php1
-rw-r--r--app/Template/project_permission/groups.php60
-rw-r--r--app/Template/project_permission/index.php134
-rw-r--r--app/Template/project_permission/users.php53
-rw-r--r--app/Template/project_role/create.php12
-rw-r--r--app/Template/project_role/edit.php13
-rw-r--r--app/Template/project_role/remove.php15
-rw-r--r--app/Template/project_role/show.php93
-rw-r--r--app/Template/project_role_restriction/create.php15
-rw-r--r--app/Template/project_role_restriction/remove.php15
-rw-r--r--app/Template/project_status/disable.php9
-rw-r--r--app/Template/project_status/enable.php9
-rw-r--r--app/Template/project_status/remove.php9
-rw-r--r--app/Template/project_tag/create.php8
-rw-r--r--app/Template/project_tag/edit.php8
-rw-r--r--app/Template/project_tag/index.php11
-rw-r--r--app/Template/project_tag/remove.php10
-rw-r--r--app/Template/project_user_overview/layout.php17
-rw-r--r--app/Template/project_user_overview/roles.php2
-rw-r--r--app/Template/project_user_overview/sidebar.php21
-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.php2
-rw-r--r--app/Template/project_view/share.php8
-rw-r--r--app/Template/project_view/show.php12
-rw-r--r--app/Template/search/activity.php14
-rw-r--r--app/Template/search/index.php14
-rw-r--r--app/Template/search/results.php2
-rw-r--r--app/Template/subtask/create.php16
-rw-r--r--app/Template/subtask/edit.php20
-rw-r--r--app/Template/subtask/menu.php9
-rw-r--r--app/Template/subtask/remove.php10
-rw-r--r--app/Template/subtask/table.php10
-rw-r--r--app/Template/subtask_converter/show.php10
-rw-r--r--app/Template/subtask_restriction/show.php8
-rw-r--r--app/Template/swimlane/create.php12
-rw-r--r--app/Template/swimlane/edit.php12
-rw-r--r--app/Template/swimlane/edit_default.php10
-rw-r--r--app/Template/swimlane/index.php3
-rw-r--r--app/Template/swimlane/remove.php28
-rw-r--r--app/Template/swimlane/table.php16
-rw-r--r--app/Template/tag/create.php8
-rw-r--r--app/Template/tag/edit.php8
-rw-r--r--app/Template/tag/index.php11
-rw-r--r--app/Template/tag/remove.php10
-rw-r--r--app/Template/task/analytics.php23
-rw-r--r--app/Template/task/changes.php6
-rw-r--r--app/Template/task/details.php25
-rw-r--r--app/Template/task/dropdown.php45
-rw-r--r--app/Template/task/show.php86
-rw-r--r--app/Template/task/sidebar.php80
-rw-r--r--app/Template/task/time_tracking_details.php11
-rw-r--r--app/Template/task/time_tracking_summary.php20
-rw-r--r--app/Template/task/transitions.php11
-rw-r--r--app/Template/task_bulk/show.php15
-rw-r--r--app/Template/task_creation/duplicate_projects.php25
-rw-r--r--app/Template/task_creation/show.php61
-rw-r--r--app/Template/task_duplication/copy.php32
-rw-r--r--app/Template/task_duplication/duplicate.php10
-rw-r--r--app/Template/task_duplication/move.php30
-rw-r--r--app/Template/task_external_link/create.php9
-rw-r--r--app/Template/task_external_link/edit.php9
-rw-r--r--app/Template/task_external_link/find.php8
-rw-r--r--app/Template/task_external_link/remove.php10
-rw-r--r--app/Template/task_external_link/table.php10
-rw-r--r--app/Template/task_file/create.php43
-rw-r--r--app/Template/task_file/files.php14
-rw-r--r--app/Template/task_file/images.php20
-rw-r--r--app/Template/task_file/remove.php10
-rw-r--r--app/Template/task_file/screenshot.php10
-rw-r--r--app/Template/task_gantt/show.php9
-rw-r--r--app/Template/task_gantt_creation/show.php46
-rw-r--r--app/Template/task_import/show.php15
-rw-r--r--app/Template/task_import/sidebar.php9
-rw-r--r--app/Template/task_internal_link/create.php8
-rw-r--r--app/Template/task_internal_link/edit.php6
-rw-r--r--app/Template/task_internal_link/remove.php10
-rw-r--r--app/Template/task_internal_link/table.php10
-rw-r--r--app/Template/task_list/show.php2
-rw-r--r--app/Template/task_modification/show.php46
-rw-r--r--app/Template/task_move_position/show.php19
-rw-r--r--app/Template/task_recurrence/edit.php10
-rw-r--r--app/Template/task_status/close.php10
-rw-r--r--app/Template/task_status/open.php10
-rw-r--r--app/Template/task_suppression/remove.php10
-rw-r--r--app/Template/twofactor/disable.php9
-rw-r--r--app/Template/twofactor/show.php2
-rw-r--r--app/Template/user_api_access/show.php17
-rw-r--r--app/Template/user_creation/remote.php51
-rw-r--r--app/Template/user_creation/show.php67
-rw-r--r--app/Template/user_credential/authentication.php12
-rw-r--r--app/Template/user_credential/password.php14
-rw-r--r--app/Template/user_import/show.php9
-rw-r--r--app/Template/user_invite/email.php12
-rw-r--r--app/Template/user_invite/show.php15
-rw-r--r--app/Template/user_invite/signup.php (renamed from app/Template/user_creation/local.php)47
-rw-r--r--app/Template/user_list/dropdown.php12
-rw-r--r--app/Template/user_list/show.php18
-rw-r--r--app/Template/user_modification/show.php33
-rw-r--r--app/Template/user_status/disable.php10
-rw-r--r--app/Template/user_status/enable.php10
-rw-r--r--app/Template/user_status/remove.php10
-rw-r--r--app/Template/user_view/last.php4
-rw-r--r--app/Template/user_view/layout.php17
-rw-r--r--app/Template/user_view/password_reset.php4
-rw-r--r--app/Template/user_view/profile.php14
-rw-r--r--app/Template/user_view/sessions.php2
-rw-r--r--app/Template/user_view/share.php6
-rw-r--r--app/Template/user_view/show.php21
-rw-r--r--app/Template/user_view/sidebar.php81
-rw-r--r--app/Template/user_view/timesheet.php2
-rw-r--r--app/User/OAuthUserProvider.php14
-rw-r--r--app/Validator/ColumnMoveRestrictionValidator.php41
-rw-r--r--app/Validator/ColumnRestrictionValidator.php40
-rw-r--r--app/Validator/ProjectRoleValidator.php70
-rw-r--r--app/Validator/ProjectValidator.php12
-rw-r--r--app/Validator/TaskValidator.php24
-rw-r--r--app/Validator/UserValidator.php2
-rw-r--r--app/common.php4
-rw-r--r--app/constants.php11
-rw-r--r--app/functions.php56
593 files changed, 16931 insertions, 7571 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/TaskAssignColorSwimlane.php b/app/Action/TaskAssignColorSwimlane.php
new file mode 100644
index 00000000..31f2d25a
--- /dev/null
+++ b/app/Action/TaskAssignColorSwimlane.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+/**
+ * Assign a color to a task based on Swimlane
+ *
+ * @package Kanboard\Action
+ * @author Dave Almond
+ */
+class TaskAssignColorSwimlane extends Base
+{
+ /**
+ * Get automatic action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Assign a color when the task is moved to a specific swimlane');
+ }
+
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(
+ TaskModel::EVENT_CREATE,
+ TaskModel::EVENT_MOVE_SWIMLANE,
+ );
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'swimlane_id' => t('Swimlane'),
+ 'color_id' => t('Color'),
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'project_id',
+ 'swimlane_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'],
+ 'color_id' => $this->getParam('color_id'),
+ );
+
+ 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 $data['task']['swimlane_id'] == $this->getParam('swimlane_id');
+ }
+}
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/TaskAssignPrioritySwimlane.php b/app/Action/TaskAssignPrioritySwimlane.php
new file mode 100644
index 00000000..7eaca7c8
--- /dev/null
+++ b/app/Action/TaskAssignPrioritySwimlane.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+/**
+ * Set a priority automatically according to the Swimlane
+ *
+ * @package Kanboard\Action
+ * @author Dave Almond
+ */
+class TaskAssignPrioritySwimlane extends Base
+{
+ /**
+ * Get automatic action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Assign a priority when the task is moved to a specific swimlane');
+ }
+
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(
+ TaskModel::EVENT_CREATE,
+ TaskModel::EVENT_MOVE_SWIMLANE,
+ );
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'swimlane_id' => t('Swimlane'),
+ 'priority' => t('Priority'),
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'task_id',
+ 'task' => array(
+ 'project_id',
+ 'swimlane_id',
+ ),
+ );
+ }
+
+ /**
+ * Execute the action (set the priority)
+ *
+ * @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'],
+ 'priority' => $this->getParam('priority'),
+ );
+
+ return $this->taskModificationModel->update($values);
+ }
+
+ /**
+ * 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']['swimlane_id'] == $this->getParam('swimlane_id');
+ }
+}
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..e97aaac6
--- /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 a 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/TaskCloseNotMovedColumn.php b/app/Action/TaskCloseNotMovedColumn.php
new file mode 100644
index 00000000..cee5256b
--- /dev/null
+++ b/app/Action/TaskCloseNotMovedColumn.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+/**
+ * Close automatically in a defined column after a certain amount of time
+ *
+ * @package Kanboard\Action
+ * @author Frederic Guillot
+ */
+class TaskCloseNotMovedColumn extends Base
+{
+ /**
+ * Get automatic action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Close a task in a specific 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'),
+ '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_moved'];
+
+ 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 d70d2ee8..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->taskProjectDuplicationModel->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..559c2c0b 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
@@ -34,6 +34,7 @@ class TaskEmail extends Base
return array(
TaskModel::EVENT_MOVE_COLUMN,
TaskModel::EVENT_CLOSE,
+ TaskModel::EVENT_CREATE,
);
}
@@ -62,7 +63,10 @@ class TaskEmail extends Base
{
return array(
'task_id',
- 'column_id',
+ 'task' => array(
+ 'project_id',
+ 'column_id',
+ ),
);
}
@@ -78,13 +82,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 +107,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 66635a63..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',
+ )
);
}
@@ -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/Analytic/EstimatedTimeComparisonAnalytic.php b/app/Analytic/EstimatedTimeComparisonAnalytic.php
index d9aea32d..020478f0 100644
--- a/app/Analytic/EstimatedTimeComparisonAnalytic.php
+++ b/app/Analytic/EstimatedTimeComparisonAnalytic.php
@@ -41,8 +41,8 @@ class EstimatedTimeComparisonAnalytic extends Base
foreach ($rows as $row) {
$key = $row['is_active'] == TaskModel::STATUS_OPEN ? 'open' : 'closed';
- $metrics[$key]['time_spent'] = $row['time_spent'];
- $metrics[$key]['time_estimated'] = $row['time_estimated'];
+ $metrics[$key]['time_spent'] = (float) $row['time_spent'];
+ $metrics[$key]['time_estimated'] = (float) $row['time_estimated'];
}
return $metrics;
diff --git a/app/Api/Authorization/ProjectAuthorization.php b/app/Api/Authorization/ProjectAuthorization.php
index 21ecf311..7dcdc445 100644
--- a/app/Api/Authorization/ProjectAuthorization.php
+++ b/app/Api/Authorization/ProjectAuthorization.php
@@ -23,13 +23,13 @@ class ProjectAuthorization extends Base
protected function checkProjectPermission($class, $method, $project_id)
{
if (empty($project_id)) {
- throw new AccessDeniedException('Project not found');
+ 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');
+ throw new AccessDeniedException('Project Access Denied');
}
}
}
diff --git a/app/Api/Authorization/TagAuthorization.php b/app/Api/Authorization/TagAuthorization.php
new file mode 100644
index 00000000..247f57db
--- /dev/null
+++ b/app/Api/Authorization/TagAuthorization.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Kanboard\Api\Authorization;
+
+/**
+ * Class TagAuthorization
+ *
+ * @package Kanboard\Api\Authorization
+ * @author Frederic Guillot
+ */
+class TagAuthorization extends ProjectAuthorization
+{
+ public function check($class, $method, $tag_id)
+ {
+ if ($this->userSession->isLogged()) {
+ $tag = $this->tagModel->getById($tag_id);
+
+ if (! empty($tag)) {
+ $this->checkProjectPermission($class, $method, $tag['project_id']);
+ }
+ }
+ }
+}
diff --git a/app/Api/Authorization/TaskAuthorization.php b/app/Api/Authorization/TaskAuthorization.php
index db93b76b..6e044211 100644
--- a/app/Api/Authorization/TaskAuthorization.php
+++ b/app/Api/Authorization/TaskAuthorization.php
@@ -10,10 +10,10 @@ namespace Kanboard\Api\Authorization;
*/
class TaskAuthorization extends ProjectAuthorization
{
- public function check($class, $method, $category_id)
+ public function check($class, $method, $task_id)
{
if ($this->userSession->isLogged()) {
- $this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($category_id));
+ $this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($task_id));
}
}
}
diff --git a/app/Api/Middleware/AuthenticationMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php
index 8e309593..174dc467 100644
--- a/app/Api/Middleware/AuthenticationMiddleware.php
+++ b/app/Api/Middleware/AuthenticationMiddleware.php
@@ -28,9 +28,10 @@ class AuthenticationMiddleware extends Base implements MiddlewareInterface
public function execute($username, $password, $procedureName)
{
$this->dispatcher->dispatch('app.bootstrap');
+ $this->sessionStorage->scope = 'API';
if ($this->isUserAuthenticated($username, $password)) {
- $this->userSession->initialize($this->userModel->getByUsername($username));
+ $this->userSession->initialize($this->userCacheDecorator->getByUsername($username));
} elseif (! $this->isAppAuthenticated($username, $password)) {
$this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailureException('Wrong credentials');
diff --git a/app/Api/Procedure/ActionProcedure.php b/app/Api/Procedure/ActionProcedure.php
index 4043dbb9..72fb9bbe 100644
--- a/app/Api/Procedure/ActionProcedure.php
+++ b/app/Api/Procedure/ActionProcedure.php
@@ -15,17 +15,17 @@ class ActionProcedure extends BaseProcedure
{
public function getAvailableActions()
{
- return $this->actionManager->getAvailableActions();
+ return (object) $this->actionManager->getAvailableActions();
}
public function getAvailableActionEvents()
{
- return $this->eventManager->getAll();
+ return (object) $this->eventManager->getAll();
}
public function getCompatibleActionEvents($action_name)
{
- return $this->actionManager->getCompatibleEvents($action_name);
+ return (object) $this->actionManager->getCompatibleEvents($action_name);
}
public function removeAction($action_id)
diff --git a/app/Api/Procedure/BoardProcedure.php b/app/Api/Procedure/BoardProcedure.php
index 674b5466..69daaf09 100644
--- a/app/Api/Procedure/BoardProcedure.php
+++ b/app/Api/Procedure/BoardProcedure.php
@@ -3,7 +3,6 @@
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
-use Kanboard\Formatter\BoardFormatter;
/**
* Board API controller
@@ -17,7 +16,7 @@ class BoardProcedure extends BaseProcedure
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id);
- return BoardFormatter::getInstance($this->container)
+ return $this->boardFormatter
->withProjectId($project_id)
->withQuery($this->taskFinderModel->getExtendedQuery())
->format();
diff --git a/app/Api/Procedure/MeProcedure.php b/app/Api/Procedure/MeProcedure.php
index e59e6522..71d5555b 100644
--- a/app/Api/Procedure/MeProcedure.php
+++ b/app/Api/Procedure/MeProcedure.php
@@ -54,7 +54,7 @@ class MeProcedure extends BaseProcedure
public function getMyProjectsList()
{
- return $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId());
+ return (object) $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId());
}
public function getMyOverdueTasks()
diff --git a/app/Api/Procedure/ProjectPermissionProcedure.php b/app/Api/Procedure/ProjectPermissionProcedure.php
index e22e1d62..1938a067 100644
--- a/app/Api/Procedure/ProjectPermissionProcedure.php
+++ b/app/Api/Procedure/ProjectPermissionProcedure.php
@@ -16,13 +16,13 @@ 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);
+ return (object) $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);
+ return (object) $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned);
}
public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER)
diff --git a/app/Api/Procedure/TagProcedure.php b/app/Api/Procedure/TagProcedure.php
new file mode 100644
index 00000000..f1c06d01
--- /dev/null
+++ b/app/Api/Procedure/TagProcedure.php
@@ -0,0 +1,44 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\ProjectAuthorization;
+use Kanboard\Api\Authorization\TagAuthorization;
+
+/**
+ * Class TagProcedure
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class TagProcedure extends BaseProcedure
+{
+ public function getAllTags()
+ {
+ return $this->tagModel->getAll();
+ }
+
+ public function getTagsByProject($project_id)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTagsByProject', $project_id);
+ return $this->tagModel->getAllByProject($project_id);
+ }
+
+ public function createTag($project_id, $tag)
+ {
+ ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTag', $project_id);
+ return $this->tagModel->findOrCreateTag($project_id, $tag);
+ }
+
+ public function updateTag($tag_id, $tag)
+ {
+ TagAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTag', $tag_id);
+ return $this->tagModel->update($tag_id, $tag);
+ }
+
+ public function removeTag($tag_id)
+ {
+ TagAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTag', $tag_id);
+ return $this->tagModel->remove($tag_id);
+ }
+}
diff --git a/app/Api/Procedure/TaskMetadataProcedure.php b/app/Api/Procedure/TaskMetadataProcedure.php
new file mode 100644
index 00000000..ab6c32d0
--- /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 (object) $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/Procedure/TaskProcedure.php b/app/Api/Procedure/TaskProcedure.php
index 8661deef..af67f3de 100644
--- a/app/Api/Procedure/TaskProcedure.php
+++ b/app/Api/Procedure/TaskProcedure.php
@@ -87,12 +87,12 @@ class TaskProcedure extends BaseProcedure
}
public function createTask($title, $project_id, $color_id = '', $column_id = 0, $owner_id = 0, $creator_id = 0,
- $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, $priority = 0,
- $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
- $recurrence_basedate = 0, $reference = '')
+ $date_due = '', $description = '', $category_id = 0, $score = 0, $swimlane_id = 0, $priority = 0,
+ $recurrence_status = 0, $recurrence_trigger = 0, $recurrence_factor = 0, $recurrence_timeframe = 0,
+ $recurrence_basedate = 0, $reference = '', array $tags = array())
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id);
-
+
if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
return false;
}
@@ -120,6 +120,7 @@ class TaskProcedure extends BaseProcedure
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
+ 'tags' => $tags,
);
list($valid, ) = $this->taskValidator->validateCreation($values);
@@ -128,9 +129,9 @@ class TaskProcedure extends BaseProcedure
}
public function updateTask($id, $title = null, $color_id = null, $owner_id = null,
- $date_due = null, $description = null, $category_id = null, $score = null, $priority = null,
- $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
- $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null)
+ $date_due = null, $description = null, $category_id = null, $score = null, $priority = null,
+ $recurrence_status = null, $recurrence_trigger = null, $recurrence_factor = null,
+ $recurrence_timeframe = null, $recurrence_basedate = null, $reference = null, $tags = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id);
$project_id = $this->taskFinderModel->getProjectId($id);
@@ -159,6 +160,7 @@ class TaskProcedure extends BaseProcedure
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
+ 'tags' => $tags,
));
list($valid) = $this->taskValidator->validateApiModification($values);
diff --git a/app/Api/Procedure/TaskTagProcedure.php b/app/Api/Procedure/TaskTagProcedure.php
new file mode 100644
index 00000000..55dac8d4
--- /dev/null
+++ b/app/Api/Procedure/TaskTagProcedure.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Kanboard\Api\Procedure;
+
+use Kanboard\Api\Authorization\TaskAuthorization;
+
+/**
+ * Class TaskTagProcedure
+ *
+ * @package Kanboard\Api\Procedure
+ * @author Frederic Guillot
+ */
+class TaskTagProcedure extends BaseProcedure
+{
+ public function setTaskTags($project_id, $task_id, array $tags)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setTaskTags', $task_id);
+ return $this->taskTagModel->save($project_id, $task_id, $tags);
+ }
+
+ public function getTaskTags($task_id)
+ {
+ TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskTags', $task_id);
+ return (object) $this->taskTagModel->getList($task_id);
+ }
+}
diff --git a/app/Auth/ApiAccessTokenAuth.php b/app/Auth/ApiAccessTokenAuth.php
new file mode 100644
index 00000000..12ab21a7
--- /dev/null
+++ b/app/Auth/ApiAccessTokenAuth.php
@@ -0,0 +1,119 @@
+<?php
+
+namespace Kanboard\Auth;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
+use Kanboard\Model\UserModel;
+use Kanboard\User\DatabaseUserProvider;
+
+/**
+ * API Access Token Authentication Provider
+ *
+ * @package Kanboard\Auth
+ * @author Frederic Guillot
+ */
+class ApiAccessTokenAuth extends Base implements PasswordAuthenticationProviderInterface
+{
+ /**
+ * User properties
+ *
+ * @access protected
+ * @var array
+ */
+ protected $userInfo = array();
+
+ /**
+ * Username
+ *
+ * @access protected
+ * @var string
+ */
+ protected $username = '';
+
+ /**
+ * Password
+ *
+ * @access protected
+ * @var string
+ */
+ protected $password = '';
+
+ /**
+ * Get authentication provider name
+ *
+ * @access public
+ * @return string
+ */
+ public function getName()
+ {
+ return 'API Access Token';
+ }
+
+ /**
+ * Authenticate the user
+ *
+ * @access public
+ * @return boolean
+ */
+ public function authenticate()
+ {
+ if (! isset($this->sessionStorage->scope) || $this->sessionStorage->scope !== 'API') {
+ $this->logger->debug(__METHOD__.': Authentication provider skipped because invalid scope');
+ return false;
+ }
+
+ $user = $this->db
+ ->table(UserModel::TABLE)
+ ->columns('id', 'password')
+ ->eq('username', $this->username)
+ ->eq('api_access_token', $this->password)
+ ->notNull('api_access_token')
+ ->eq('is_active', 1)
+ ->findOne();
+
+ if (! empty($user)) {
+ $this->userInfo = $user;
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get user object
+ *
+ * @access public
+ * @return \Kanboard\User\DatabaseUserProvider
+ */
+ public function getUser()
+ {
+ if (empty($this->userInfo)) {
+ return null;
+ }
+
+ return new DatabaseUserProvider($this->userInfo);
+ }
+
+ /**
+ * Set username
+ *
+ * @access public
+ * @param string $username
+ */
+ public function setUsername($username)
+ {
+ $this->username = $username;
+ }
+
+ /**
+ * Set password
+ *
+ * @access public
+ * @param string $password
+ */
+ public function setPassword($password)
+ {
+ $this->password = $password;
+ }
+}
diff --git a/app/Auth/DatabaseAuth.php b/app/Auth/DatabaseAuth.php
index ecb42c17..84a1e019 100644
--- a/app/Auth/DatabaseAuth.php
+++ b/app/Auth/DatabaseAuth.php
@@ -11,7 +11,7 @@ use Kanboard\User\DatabaseUserProvider;
/**
* Database Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterface, SessionCheckProviderInterface
diff --git a/app/Auth/LdapAuth.php b/app/Auth/LdapAuth.php
index a8dcfcb6..05ffbebf 100644
--- a/app/Auth/LdapAuth.php
+++ b/app/Auth/LdapAuth.php
@@ -12,7 +12,7 @@ use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
/**
* LDAP Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class LdapAuth extends Base implements PasswordAuthenticationProviderInterface
diff --git a/app/Auth/RememberMeAuth.php b/app/Auth/RememberMeAuth.php
index 5d0a8b2e..e0f4ceb6 100644
--- a/app/Auth/RememberMeAuth.php
+++ b/app/Auth/RememberMeAuth.php
@@ -7,9 +7,9 @@ use Kanboard\Core\Security\PreAuthenticationProviderInterface;
use Kanboard\User\DatabaseUserProvider;
/**
- * Rember Me Cookie Authentication Provider
+ * RememberMe Cookie Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class RememberMeAuth extends Base implements PreAuthenticationProviderInterface
diff --git a/app/Auth/ReverseProxyAuth.php b/app/Auth/ReverseProxyAuth.php
index fdf936b1..bf71e71e 100644
--- a/app/Auth/ReverseProxyAuth.php
+++ b/app/Auth/ReverseProxyAuth.php
@@ -10,7 +10,7 @@ use Kanboard\User\ReverseProxyUserProvider;
/**
* Reverse-Proxy Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class ReverseProxyAuth extends Base implements PreAuthenticationProviderInterface, SessionCheckProviderInterface
@@ -45,7 +45,7 @@ class ReverseProxyAuth extends Base implements PreAuthenticationProviderInterfac
$username = $this->request->getRemoteUser();
if (! empty($username)) {
- $userProfile = $this->userModel->getByUsername($username);
+ $userProfile = $this->userCacheDecorator->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..abfb2168 100644
--- a/app/Auth/TotpAuth.php
+++ b/app/Auth/TotpAuth.php
@@ -11,7 +11,7 @@ use Kanboard\Core\Security\PostAuthenticationProviderInterface;
/**
* TOTP Authentication Provider
*
- * @package auth
+ * @package Kanboard\Auth
* @author Frederic Guillot
*/
class TotpAuth extends Base implements PostAuthenticationProviderInterface
@@ -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/DatabaseMigrationCommand.php b/app/Console/DatabaseMigrationCommand.php
new file mode 100644
index 00000000..252d4369
--- /dev/null
+++ b/app/Console/DatabaseMigrationCommand.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Kanboard\Console;
+
+use Kanboard\ServiceProvider\DatabaseProvider;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DatabaseMigrationCommand extends DatabaseVersionCommand
+{
+ protected function configure()
+ {
+ $this
+ ->setName('db:migrate')
+ ->setDescription('Execute SQL migrations');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ parent::execute($input, $output);
+ DatabaseProvider::runMigrations($this->container['db']);
+ }
+}
diff --git a/app/Console/DatabaseVersionCommand.php b/app/Console/DatabaseVersionCommand.php
new file mode 100644
index 00000000..5b1f1ed1
--- /dev/null
+++ b/app/Console/DatabaseVersionCommand.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Kanboard\Console;
+
+use Kanboard\ServiceProvider\DatabaseProvider;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class DatabaseVersionCommand extends BaseCommand
+{
+ protected function configure()
+ {
+ $this
+ ->setName('db:version')
+ ->setDescription('Show database schema version');
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $output->writeln('<info>Current version: '.DatabaseProvider::getSchemaVersion($this->container['db']).'</info>');
+ $output->writeln('<info>Last version: '.\Schema\VERSION.'</info>');
+ }
+}
diff --git a/app/Console/JobCommand.php b/app/Console/JobCommand.php
new file mode 100644
index 00000000..bdaae32f
--- /dev/null
+++ b/app/Console/JobCommand.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Kanboard\Console;
+
+use Kanboard\Core\Queue\JobHandler;
+use SimpleQueue\Job;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+/**
+ * Class JobCommand
+ *
+ * @package Kanboard\Console
+ * @author Frederic Guillot
+ */
+class JobCommand extends BaseCommand
+{
+ protected function configure()
+ {
+ $this
+ ->setName('job')
+ ->setDescription('Execute individual job (read payload from stdin)')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $payload = fgets(STDIN);
+
+ $job = new Job();
+ $job->unserialize($payload);
+
+ JobHandler::getInstance($this->container)->executeJob($job);
+ }
+}
diff --git a/app/Console/ResetTwoFactorCommand.php b/app/Console/ResetTwoFactorCommand.php
index a64206b6..4a99db66 100644
--- a/app/Console/ResetTwoFactorCommand.php
+++ b/app/Console/ResetTwoFactorCommand.php
@@ -23,16 +23,14 @@ class ResetTwoFactorCommand extends BaseCommand
if (empty($userId)) {
$output->writeln('<error>User not found</error>');
- return false;
+ return 1;
}
if (!$this->userModel->update(array('id' => $userId, 'twofactor_activated' => 0, 'twofactor_secret' => ''))) {
$output->writeln('<error>Unable to update user profile</error>');
- return false;
+ return 1;
}
$output->writeln('<info>Two-factor authentication disabled</info>');
-
- return true;
}
}
diff --git a/app/Controller/ActionController.php b/app/Controller/ActionController.php
index 097640f6..c935125a 100644
--- a/app/Controller/ActionController.php
+++ b/app/Controller/ActionController.php
@@ -33,6 +33,7 @@ class ActionController extends BaseController
'colors_list' => $this->colorModel->getList(),
'categories_list' => $this->categoryModel->getList($project['id']),
'links_list' => $this->linkModel->getList(0, false),
+ 'swimlane_list' => $this->swimlaneModel->getList($project['id']),
'title' => t('Automatic actions')
)));
}
diff --git a/app/Controller/ActionCreationController.php b/app/Controller/ActionCreationController.php
index 9b228f28..1629e68f 100644
--- a/app/Controller/ActionCreationController.php
+++ b/app/Controller/ActionCreationController.php
@@ -84,6 +84,7 @@ class ActionCreationController extends BaseController
'priorities_list' => $this->projectTaskPriorityModel->getPriorities($project),
'project' => $project,
'available_actions' => $this->actionManager->getAvailableActions(),
+ 'swimlane_list' => $this->swimlaneModel->getList($project['id']),
'events' => $this->actionManager->getCompatibleEvents($values['action_name']),
)));
}
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..38169fd7 100644
--- a/app/Controller/AnalyticController.php
+++ b/app/Controller/AnalyticController.php
@@ -31,9 +31,7 @@ class AnalyticController extends BaseController
'project' => $project,
'average' => $this->averageLeadCycleTimeAnalytic->build($project['id']),
'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'),
)));
}
@@ -42,12 +40,12 @@ class AnalyticController extends BaseController
*
* @access public
*/
- public function compareHours()
+ public function timeComparison()
{
$project = $this->getProject();
$paginator = $this->paginator
- ->setUrl('AnalyticController', 'compareHours', array('project_id' => $project['id']))
+ ->setUrl('AnalyticController', 'timeComparison', array('project_id' => $project['id']))
->setMax(30)
->setOrder(TaskModel::TABLE.'.id')
->setQuery($this->taskQuery
@@ -56,11 +54,11 @@ class AnalyticController extends BaseController
)
->calculate();
- $this->response->html($this->helper->layout->analytic('analytic/compare_hours', array(
+ $this->response->html($this->helper->layout->analytic('analytic/time_comparison', array(
'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 +74,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'),
)));
}
@@ -85,14 +83,14 @@ class AnalyticController extends BaseController
*
* @access public
*/
- public function tasks()
+ public function taskDistribution()
{
$project = $this->getProject();
- $this->response->html($this->helper->layout->analytic('analytic/tasks', array(
+ $this->response->html($this->helper->layout->analytic('analytic/task_distribution', array(
'project' => $project,
'metrics' => $this->taskDistributionAnalytic->build($project['id']),
- 'title' => t('Task repartition for "%s"', $project['name']),
+ 'title' => t('Task distribution'),
)));
}
@@ -101,14 +99,14 @@ class AnalyticController extends BaseController
*
* @access public
*/
- public function users()
+ public function userDistribution()
{
$project = $this->getProject();
- $this->response->html($this->helper->layout->analytic('analytic/users', array(
+ $this->response->html($this->helper->layout->analytic('analytic/user_distribution', array(
'project' => $project,
'metrics' => $this->userDistributionAnalytic->build($project['id']),
- 'title' => t('User repartition for "%s"', $project['name']),
+ 'title' => t('User repartition'),
)));
}
@@ -119,7 +117,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 +127,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'));
}
/**
@@ -155,9 +153,7 @@ class AnalyticController extends BaseController
'display_graph' => $display_graph,
'metrics' => $display_graph ? $this->projectDailyColumnStatsModel->getAggregatedMetrics($project['id'], $from, $to, $column) : array(),
'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,
)));
}
@@ -169,8 +165,8 @@ class AnalyticController extends BaseController
$to = $this->request->getStringParam('to', date('Y-m-d'));
if (! empty($values)) {
- $from = $values['from'];
- $to = $values['to'];
+ $from = $this->dateParser->getIsoDate($values['from']);
+ $to = $this->dateParser->getIsoDate($values['to']);
}
return array($from, $to);
diff --git a/app/Controller/AppController.php b/app/Controller/AppController.php
index 45cf39a5..34b9c8cc 100644
--- a/app/Controller/AppController.php
+++ b/app/Controller/AppController.php
@@ -16,18 +16,19 @@ class AppController extends Base
* Forbidden page
*
* @access public
- * @param bool $withoutLayout
+ * @param bool $withoutLayout
+ * @param string $message
*/
- public function accessForbidden($withoutLayout = false)
+ public function accessForbidden($withoutLayout = false, $message = '')
{
if ($this->request->isAjax()) {
- $this->response->json(array('message' => 'Access Forbidden'), 403);
+ $this->response->json(array('message' => $message ?: t('Access Forbidden')), 403);
+ } else {
+ $this->response->html($this->helper->layout->app('app/forbidden', array(
+ 'title' => t('Access Forbidden'),
+ 'no_layout' => $withoutLayout,
+ )));
}
-
- $this->response->html($this->helper->layout->app('app/forbidden', array(
- 'title' => t('Access Forbidden'),
- 'no_layout' => $withoutLayout,
- )));
}
/**
diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php
index 9b721f06..ecb76e9c 100644
--- a/app/Controller/BoardAjaxController.php
+++ b/app/Controller/BoardAjaxController.php
@@ -3,7 +3,7 @@
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
-use Kanboard\Formatter\BoardFormatter;
+use Kanboard\Model\UserMetadataModel;
/**
* Class BoardAjaxController
@@ -28,10 +28,14 @@ class BoardAjaxController extends BaseController
$values = $this->request->getJson();
+ if (! $this->helper->projectRole->canMoveTask($project_id, $values['src_column_id'], $values['dst_column_id'])) {
+ throw new AccessForbiddenException(e("You don't have the permission to move this task"));
+ }
+
$result =$this->taskPositionModel->movePosition(
$project_id,
$values['task_id'],
- $values['column_id'],
+ $values['dst_column_id'],
$values['position'],
$values['swimlane_id']
);
@@ -88,7 +92,7 @@ class BoardAjaxController extends BaseController
*/
public function collapse()
{
- $this->changeDisplayMode(true);
+ $this->changeDisplayMode(1);
}
/**
@@ -98,19 +102,19 @@ class BoardAjaxController extends BaseController
*/
public function expand()
{
- $this->changeDisplayMode(false);
+ $this->changeDisplayMode(0);
}
/**
* Change display mode
*
* @access private
- * @param boolean $mode
+ * @param int $mode
*/
private function changeDisplayMode($mode)
{
$project_id = $this->request->getIntegerParam('project_id');
- $this->userSession->setBoardDisplayMode($project_id, $mode);
+ $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, $mode);
if ($this->request->isAjax()) {
$this->response->html($this->renderBoard($project_id));
@@ -134,7 +138,7 @@ class BoardAjaxController extends BaseController
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($this->userSession->getFilters($project_id))
- ->format(BoardFormatter::getInstance($this->container)->withProjectId($project_id))
+ ->format($this->boardFormatter->withProjectId($project_id))
));
}
}
diff --git a/app/Controller/BoardTooltipController.php b/app/Controller/BoardTooltipController.php
index 134d728e..79b9b509 100644
--- a/app/Controller/BoardTooltipController.php
+++ b/app/Controller/BoardTooltipController.php
@@ -2,6 +2,8 @@
namespace Kanboard\Controller;
+use Kanboard\Model\UserMetadataModel;
+
/**
* Board Tooltip
*
@@ -75,10 +77,11 @@ class BoardTooltipController extends BaseController
public function comments()
{
$task = $this->getTask();
+ $commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
$this->response->html($this->template->render('board/tooltip_comments', array(
'task' => $task,
- 'comments' => $this->commentModel->getAll($task['id'], $this->userSession->getCommentSorting())
+ 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection)
)));
}
diff --git a/app/Controller/BoardViewController.php b/app/Controller/BoardViewController.php
index 97c99d11..9ef77e54 100644
--- a/app/Controller/BoardViewController.php
+++ b/app/Controller/BoardViewController.php
@@ -3,7 +3,7 @@
namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
-use Kanboard\Formatter\BoardFormatter;
+use Kanboard\Model\TaskModel;
/**
* Board controller
@@ -28,11 +28,15 @@ class BoardViewController extends BaseController
throw AccessForbiddenException::getInstance()->withoutLayout();
}
+ $query = $this->taskFinderModel
+ ->getExtendedQuery()
+ ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN);
+
$this->response->html($this->helper->layout->app('board/view_public', array(
'project' => $project,
- 'swimlanes' => BoardFormatter::getInstance($this->container)
+ 'swimlanes' => $this->boardFormatter
->withProjectId($project['id'])
- ->withQuery($this->taskFinderModel->getExtendedQuery())
+ ->withQuery($query)
->format()
,
'title' => $project['name'],
@@ -63,7 +67,7 @@ class BoardViewController extends BaseController
'board_highlight_period' => $this->configModel->get('board_highlight_period'),
'swimlanes' => $this->taskLexer
->build($search)
- ->format(BoardFormatter::getInstance($this->container)->withProjectId($project['id']))
+ ->format($this->boardFormatter->withProjectId($project['id']))
)));
}
}
diff --git a/app/Controller/CalendarController.php b/app/Controller/CalendarController.php
index e5114f02..5ad253e1 100644
--- a/app/Controller/CalendarController.php
+++ b/app/Controller/CalendarController.php
@@ -29,7 +29,6 @@ class CalendarController extends BaseController
'project' => $project,
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
- 'check_interval' => $this->configModel->get('board_private_refresh_interval'),
)));
}
diff --git a/app/Controller/ColumnController.php b/app/Controller/ColumnController.php
index e3f9bfff..69167976 100644
--- a/app/Controller/ColumnController.php
+++ b/app/Controller/ColumnController.php
@@ -25,7 +25,7 @@ class ColumnController extends BaseController
$this->response->html($this->helper->layout->project('column/index', array(
'columns' => $columns,
'project' => $project,
- 'title' => t('Edit board')
+ 'title' => t('Edit columns')
)));
}
@@ -49,7 +49,6 @@ class ColumnController extends BaseController
'values' => $values,
'errors' => $errors,
'project' => $project,
- 'title' => t('Add a new column')
)));
}
@@ -61,20 +60,29 @@ class ColumnController extends BaseController
public function save()
{
$project = $this->getProject();
- $values = $this->request->getValues();
+ $values = $this->request->getValues() + array('hide_in_dashboard' => 0);
list($valid, $errors) = $this->columnValidator->validateCreation($values);
if ($valid) {
- if ($this->columnModel->create($project['id'], $values['title'], $values['task_limit'], $values['description'], $values['hide_in_dashboard']) !== false) {
+ $result = $this->columnModel->create(
+ $project['id'],
+ $values['title'],
+ $values['task_limit'],
+ $values['description'],
+ $values['hide_in_dashboard']
+ );
+
+ if ($result !== false) {
$this->flash->success(t('Column created successfully.'));
- return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true);
+ $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true);
+ return;
} else {
$errors['title'] = array(t('Another column with the same name exists in the project'));
}
}
- return $this->create($values, $errors);
+ $this->create($values, $errors);
}
/**
@@ -94,7 +102,6 @@ class ColumnController extends BaseController
'values' => $values ?: $column,
'project' => $project,
'column' => $column,
- 'title' => t('Edit column "%s"', $column['title'])
)));
}
@@ -106,20 +113,29 @@ class ColumnController extends BaseController
public function update()
{
$project = $this->getProject();
- $values = $this->request->getValues();
+ $values = $this->request->getValues() + array('hide_in_dashboard' => 0);
list($valid, $errors) = $this->columnValidator->validateModification($values);
if ($valid) {
- if ($this->columnModel->update($values['id'], $values['title'], $values['task_limit'], $values['description'], $values['hide_in_dashboard']) !== false) {
+ $result = $this->columnModel->update(
+ $values['id'],
+ $values['title'],
+ $values['task_limit'],
+ $values['description'],
+ $values['hide_in_dashboard']
+ );
+
+ if ($result) {
$this->flash->success(t('Board updated successfully.'));
- return $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])));
+ $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id'])), true);
+ return;
} else {
$this->flash->failure(t('Unable to update this board.'));
}
}
- return $this->edit($values, $errors);
+ $this->edit($values, $errors);
}
/**
@@ -152,7 +168,6 @@ class ColumnController extends BaseController
$this->response->html($this->helper->layout->project('column/remove', array(
'column' => $this->columnModel->getById($this->request->getIntegerParam('column_id')),
'project' => $project,
- 'title' => t('Remove a column from a board')
)));
}
diff --git a/app/Controller/ColumnMoveRestrictionController.php b/app/Controller/ColumnMoveRestrictionController.php
new file mode 100644
index 00000000..b12f6b77
--- /dev/null
+++ b/app/Controller/ColumnMoveRestrictionController.php
@@ -0,0 +1,102 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+
+/**
+ * Class ColumnMoveRestrictionController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class ColumnMoveRestrictionController extends BaseController
+{
+ /**
+ * Show form to create a new column restriction
+ *
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $role_id = $this->request->getIntegerParam('role_id');
+ $role = $this->projectRoleModel->getById($project['id'], $role_id);
+
+ $this->response->html($this->template->render('column_move_restriction/create', array(
+ 'project' => $project,
+ 'role' => $role,
+ 'columns' => $this->columnModel->getList($project['id']),
+ 'values' => $values + array('project_id' => $project['id'], 'role_id' => $role['role_id']),
+ 'errors' => $errors,
+ )));
+ }
+
+ /**
+ * Save new column restriction
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->columnMoveRestrictionValidator->validateCreation($values);
+
+ if ($valid) {
+ $restriction_id = $this->columnMoveRestrictionModel->create(
+ $project['id'],
+ $values['role_id'],
+ $values['src_column_id'],
+ $values['dst_column_id']
+ );
+
+ if ($restriction_id !== false) {
+ $this->flash->success(t('The column restriction has been created successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to create this column restriction.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ } else {
+ $this->create($values, $errors);
+ }
+ }
+
+ /**
+ * Confirm suppression
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $restriction_id = $this->request->getIntegerParam('restriction_id');
+
+ $this->response->html($this->helper->layout->project('column_move_restriction/remove', array(
+ 'project' => $project,
+ 'restriction' => $this->columnMoveRestrictionModel->getById($project['id'], $restriction_id),
+ )));
+ }
+
+ /**
+ * Remove a restriction
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $project = $this->getProject();
+ $this->checkCSRFParam();
+ $restriction_id = $this->request->getIntegerParam('restriction_id');
+
+ if ($this->columnMoveRestrictionModel->remove($restriction_id)) {
+ $this->flash->success(t('Column restriction removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this restriction.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ }
+}
diff --git a/app/Controller/ColumnRestrictionController.php b/app/Controller/ColumnRestrictionController.php
new file mode 100644
index 00000000..ce2a1ca8
--- /dev/null
+++ b/app/Controller/ColumnRestrictionController.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+
+/**
+ * Class ColumnMoveRestrictionController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class ColumnRestrictionController extends BaseController
+{
+ /**
+ * Show form to create a new column restriction
+ *
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $role_id = $this->request->getIntegerParam('role_id');
+ $role = $this->projectRoleModel->getById($project['id'], $role_id);
+
+ $this->response->html($this->template->render('column_restriction/create', array(
+ 'project' => $project,
+ 'role' => $role,
+ 'rules' => $this->columnRestrictionModel->getRules(),
+ 'columns' => $this->columnModel->getList($project['id']),
+ 'values' => $values + array('project_id' => $project['id'], 'role_id' => $role['role_id']),
+ 'errors' => $errors,
+ )));
+ }
+
+ /**
+ * Save new column restriction
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->columnRestrictionValidator->validateCreation($values);
+
+ if ($valid) {
+ $restriction_id = $this->columnRestrictionModel->create(
+ $project['id'],
+ $values['role_id'],
+ $values['column_id'],
+ $values['rule']
+ );
+
+ if ($restriction_id !== false) {
+ $this->flash->success(t('The column restriction has been created successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to create this column restriction.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ } else {
+ $this->create($values, $errors);
+ }
+ }
+
+ /**
+ * Confirm suppression
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $restriction_id = $this->request->getIntegerParam('restriction_id');
+
+ $this->response->html($this->helper->layout->project('column_restriction/remove', array(
+ 'project' => $project,
+ 'restriction' => $this->columnRestrictionModel->getById($project['id'], $restriction_id),
+ )));
+ }
+
+ /**
+ * Remove a restriction
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $project = $this->getProject();
+ $this->checkCSRFParam();
+ $restriction_id = $this->request->getIntegerParam('restriction_id');
+
+ if ($this->columnRestrictionModel->remove($restriction_id)) {
+ $this->flash->success(t('Column restriction removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this restriction.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ }
+}
diff --git a/app/Controller/CommentController.php b/app/Controller/CommentController.php
index 2a8c258a..526bd2bf 100644
--- a/app/Controller/CommentController.php
+++ b/app/Controller/CommentController.php
@@ -4,6 +4,7 @@ namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
+use Kanboard\Model\UserMetadataModel;
/**
* Comment Controller
@@ -47,6 +48,7 @@ class CommentController extends BaseController
*/
public function create(array $values = array(), array $errors = array())
{
+ $project = $this->getProject();
$task = $this->getTask();
if (empty($values)) {
@@ -56,10 +58,13 @@ class CommentController extends BaseController
);
}
- $this->response->html($this->template->render('comment/create', array(
+ $values['project_id'] = $task['project_id'];
+
+ $this->response->html($this->helper->layout->task('comment/create', array(
'values' => $values,
'errors' => $errors,
'task' => $task,
+ 'project' => $project,
)));
}
@@ -82,10 +87,10 @@ class CommentController extends BaseController
$this->flash->failure(t('Unable to create your comment.'));
}
- return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
+ $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'), true);
+ } else {
+ $this->create($values, $errors);
}
-
- return $this->create($values, $errors);
}
/**
@@ -102,8 +107,14 @@ class CommentController extends BaseController
$task = $this->getTask();
$comment = $this->getComment();
+ if (empty($values)) {
+ $values = $comment;
+ }
+
+ $values['project_id'] = $task['project_id'];
+
$this->response->html($this->template->render('comment/edit', array(
- 'values' => empty($values) ? $comment : $values,
+ 'values' => $values,
'errors' => $errors,
'comment' => $comment,
'task' => $task,
@@ -183,9 +194,16 @@ class CommentController extends BaseController
{
$task = $this->getTask();
- $order = $this->userSession->getCommentSorting() === 'ASC' ? 'DESC' : 'ASC';
- $this->userSession->setCommentSorting($order);
+ $oldDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
+ $newDirection = $oldDirection === 'ASC' ? 'DESC' : 'ASC';
- $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), 'comments'));
+ $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, $newDirection);
+
+ $this->response->redirect($this->helper->url->to(
+ 'TaskViewController',
+ 'show',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id']),
+ 'comments'
+ ));
}
}
diff --git a/app/Controller/ConfigController.php b/app/Controller/ConfigController.php
index 8285ee13..8572316e 100644
--- a/app/Controller/ConfigController.php
+++ b/app/Controller/ConfigController.php
@@ -76,7 +76,6 @@ class ConfigController extends BaseController
'languages' => $this->languageModel->getLanguages(),
'timezones' => $this->timezoneModel->getTimezones(),
'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
- 'datetime_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateTimeFormats()),
'time_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getTimeFormats()),
'title' => t('Settings').' &gt; '.t('Application settings'),
)));
diff --git a/app/Controller/CurrencyController.php b/app/Controller/CurrencyController.php
index ad590035..155e229e 100644
--- a/app/Controller/CurrencyController.php
+++ b/app/Controller/CurrencyController.php
@@ -11,21 +11,33 @@ namespace Kanboard\Controller;
class CurrencyController extends BaseController
{
/**
- * Display all currency rates and form
+ * Display all currency rates
+ *
+ * @access public
+ */
+ public function show()
+ {
+ $this->response->html($this->helper->layout->config('currency/show', array(
+ 'application_currency' => $this->configModel->get('application_currency'),
+ 'rates' => $this->currencyModel->getAll(),
+ 'currencies' => $this->currencyModel->getCurrencies(),
+ 'title' => t('Settings') . ' &gt; ' . t('Currency rates'),
+ )));
+ }
+
+ /**
+ * Add or change currency rate
*
* @access public
* @param array $values
* @param array $errors
*/
- public function index(array $values = array(), array $errors = array())
+ public function create(array $values = array(), array $errors = array())
{
- $this->response->html($this->helper->layout->config('currency/index', array(
- 'config_values' => array('application_currency' => $this->configModel->get('application_currency')),
- 'values' => $values,
- 'errors' => $errors,
- 'rates' => $this->currencyModel->getAll(),
+ $this->response->html($this->template->render('currency/create', array(
+ 'values' => $values,
+ 'errors' => $errors,
'currencies' => $this->currencyModel->getCurrencies(),
- 'title' => t('Settings').' &gt; '.t('Currency rates'),
)));
}
@@ -34,7 +46,7 @@ class CurrencyController extends BaseController
*
* @access public
*/
- public function create()
+ public function save()
{
$values = $this->request->getValues();
list($valid, $errors) = $this->currencyValidator->validateCreation($values);
@@ -42,13 +54,34 @@ class CurrencyController extends BaseController
if ($valid) {
if ($this->currencyModel->create($values['currency'], $values['rate'])) {
$this->flash->success(t('The currency rate have been added successfully.'));
- return $this->response->redirect($this->helper->url->to('CurrencyController', 'index'));
+ $this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true);
+ return;
} else {
$this->flash->failure(t('Unable to add this currency rate.'));
}
}
- return $this->index($values, $errors);
+ $this->create($values, $errors);
+ }
+
+ /**
+ * Change reference currency
+ *
+ * @access public
+ * @param array $values
+ * @param array $errors
+ */
+ public function change(array $values = array(), array $errors = array())
+ {
+ if (empty($values)) {
+ $values['application_currency'] = $this->configModel->get('application_currency');
+ }
+
+ $this->response->html($this->template->render('currency/change', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'currencies' => $this->currencyModel->getCurrencies(),
+ )));
}
/**
@@ -56,7 +89,7 @@ class CurrencyController extends BaseController
*
* @access public
*/
- public function reference()
+ public function update()
{
$values = $this->request->getValues();
@@ -66,6 +99,6 @@ class CurrencyController extends BaseController
$this->flash->failure(t('Unable to save your settings.'));
}
- $this->response->redirect($this->helper->url->to('CurrencyController', 'index'));
+ $this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true);
}
}
diff --git a/app/Controller/CustomFilterController.php b/app/Controller/CustomFilterController.php
index e5f674cd..dfe1ffc4 100644
--- a/app/Controller/CustomFilterController.php
+++ b/app/Controller/CustomFilterController.php
@@ -18,17 +18,13 @@ class CustomFilterController extends BaseController
* Display list of filters
*
* @access public
- * @param array $values
- * @param array $errors
* @throws \Kanboard\Core\Controller\PageNotFoundException
*/
- public function index(array $values = array(), array $errors = array())
+ public function index()
{
$project = $this->getProject();
$this->response->html($this->helper->layout->project('custom_filter/index', array(
- 'values' => $values + array('project_id' => $project['id']),
- 'errors' => $errors,
'project' => $project,
'custom_filters' => $this->customFilterModel->getAll($project['id'], $this->userSession->getId()),
'title' => t('Custom filters'),
@@ -36,6 +32,24 @@ class CustomFilterController extends BaseController
}
/**
+ * Show creation form for custom filters
+ *
+ * @access public
+ * @param array $values
+ * @param array $errors
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->template->render('custom_filter/create', array(
+ 'values' => $values + array('project_id' => $project['id']),
+ 'errors' => $errors,
+ 'project' => $project,
+ )));
+ }
+
+ /**
* Save a new custom filter
*
* @access public
@@ -52,13 +66,14 @@ class CustomFilterController extends BaseController
if ($valid) {
if ($this->customFilterModel->create($values) !== false) {
$this->flash->success(t('Your custom filter have been created successfully.'));
- return $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id'])));
+ $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id'])), true);
+ return;
} else {
$this->flash->failure(t('Unable to create your custom filter.'));
}
}
- return $this->index($values, $errors);
+ $this->create($values, $errors);
}
/**
@@ -152,13 +167,14 @@ class CustomFilterController extends BaseController
if ($valid) {
if ($this->customFilterModel->update($values)) {
$this->flash->success(t('Your custom filter have been updated successfully.'));
- return $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id'])));
+ $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id'])), true);
+ return;
} else {
$this->flash->failure(t('Unable to update custom filter.'));
}
}
- return $this->edit($values, $errors);
+ $this->edit($values, $errors);
}
private function checkPermission(array $project, array $filter)
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/ExportController.php b/app/Controller/ExportController.php
index 27046c76..19f73a7c 100644
--- a/app/Controller/ExportController.php
+++ b/app/Controller/ExportController.php
@@ -24,29 +24,29 @@ class ExportController extends BaseController
private function common($model, $method, $filename, $action, $page_title)
{
$project = $this->getProject();
- $from = $this->request->getStringParam('from');
- $to = $this->request->getStringParam('to');
- if ($from && $to) {
- $data = $this->$model->$method($project['id'], $from, $to);
- $this->response->withFileDownload($filename.'.csv');
- $this->response->csv($data);
- } else {
+ if ($this->request->isPost()) {
+ $values = $this->request->getValues();
+ $from = empty($values['from']) ? '' : $values['from'];
+ $to = empty($values['to']) ? '' : $values['to'];
- $this->response->html($this->helper->layout->project('export/'.$action, array(
- 'values' => array(
- 'controller' => 'ExportController',
- 'action' => $action,
+ if ($from && $to) {
+ $data = $this->$model->$method($project['id'], $from, $to);
+ $this->response->withFileDownload($filename.'.csv');
+ $this->response->csv($data);
+ return;
+ }
+ } else {
+ $this->response->html($this->template->render('export/'.$action, array(
+ 'values' => array(
'project_id' => $project['id'],
- 'from' => $from,
- 'to' => $to,
+ 'from' => '',
+ 'to' => '',
),
- 'errors' => array(),
- 'date_format' => $this->configModel->get('application_date_format'),
- 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats()),
+ 'errors' => array(),
'project' => $project,
- 'title' => $page_title,
- ), 'export/sidebar'));
+ 'title' => $page_title,
+ )));
}
}
diff --git a/app/Controller/ExternalTaskCreationController.php b/app/Controller/ExternalTaskCreationController.php
new file mode 100644
index 00000000..a1985adb
--- /dev/null
+++ b/app/Controller/ExternalTaskCreationController.php
@@ -0,0 +1,97 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\ExternalTask\ExternalTaskException;
+
+/**
+ * External Task Creation Controller
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class ExternalTaskCreationController extends BaseController
+{
+ public function step1(array $values = array(), $errorMessage = '')
+ {
+ $project = $this->getProject();
+ $providerName = $this->request->getStringParam('provider_name');
+ $taskProvider = $this->externalTaskManager->getProvider($providerName);
+
+ if (empty($values)) {
+ $values = array(
+ 'swimlane_id' => $this->request->getIntegerParam('swimlane_id'),
+ 'column_id' => $this->request->getIntegerParam('column_id'),
+ );
+ }
+
+ $this->response->html($this->template->render('external_task_creation/step1', array(
+ 'project' => $project,
+ 'values' => $values,
+ 'error_message' => $errorMessage,
+ 'provider_name' => $providerName,
+ 'template' => $taskProvider->getImportFormTemplate(),
+ )));
+ }
+
+ public function step2(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $providerName = $this->request->getStringParam('provider_name');
+
+ try {
+ $taskProvider = $this->externalTaskManager->getProvider($providerName);
+
+ if (empty($values)) {
+ $values = $this->request->getValues();
+ $externalTask = $taskProvider->fetch($taskProvider->buildTaskUri($values));
+
+ $values = $externalTask->getFormValues() + array(
+ 'external_uri' => $externalTask->getUri(),
+ 'external_provider' => $providerName,
+ 'project_id' => $project['id'],
+ 'swimlane_id' => $values['swimlane_id'],
+ 'column_id' => $values['column_id'],
+ 'color_id' => $this->colorModel->getDefaultColor(),
+ 'owner_id' => $this->userSession->getId(),
+ );
+ } else {
+ $externalTask = $taskProvider->fetch($values['external_uri']);
+ }
+
+ $this->response->html($this->template->render('external_task_creation/step2', array(
+ 'project' => $project,
+ 'external_task' => $externalTask,
+ 'provider_name' => $providerName,
+ 'values' => $values,
+ 'errors' => $errors,
+ 'template' => $taskProvider->getCreationFormTemplate(),
+ 'columns_list' => $this->columnModel->getList($project['id']),
+ 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, true),
+ 'categories_list' => $this->categoryModel->getList($project['id']),
+ 'swimlanes_list' => $this->swimlaneModel->getList($project['id'], false, true),
+ )));
+ } catch (ExternalTaskException $e) {
+ $this->step1($values, $e->getMessage());
+ }
+ }
+
+ public function step3()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->taskValidator->validateCreation($values);
+
+ if (! $valid) {
+ $this->step2($values, $errors);
+ } else if (! $this->helper->projectRole->canCreateTaskInColumn($project['id'], $values['column_id'])) {
+ $this->flash->failure(t('You cannot create tasks in this column.'));
+ $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
+ } else {
+ $taskId = $this->taskCreationModel->create($values);
+ $this->flash->success(t('Task created successfully.'));
+ $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $project['id'], 'task_id' => $taskId)), true);
+ }
+ }
+}
diff --git a/app/Controller/ExternalTaskViewController.php b/app/Controller/ExternalTaskViewController.php
new file mode 100644
index 00000000..18bc15c1
--- /dev/null
+++ b/app/Controller/ExternalTaskViewController.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\ExternalTask\ExternalTaskException;
+
+/**
+ * Class ExternalTaskViewController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class ExternalTaskViewController extends BaseController
+{
+ public function show()
+ {
+ try {
+ $task = $this->getTask();
+ $taskProvider = $this->externalTaskManager->getProvider($task['external_provider']);
+ $externalTask = $taskProvider->fetch($task['external_uri']);
+
+ $this->response->html($this->template->render($taskProvider->getViewTemplate(), array(
+ 'task' => $task,
+ 'external_task' => $externalTask,
+ )));
+ } catch (ExternalTaskException $e) {
+ $this->response->html('<div class="alert alert-error">'.$e->getMessage().'</div>');
+ }
+ }
+}
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/FileViewerController.php b/app/Controller/FileViewerController.php
index 518f5b0b..49568912 100644
--- a/app/Controller/FileViewerController.php
+++ b/app/Controller/FileViewerController.php
@@ -15,11 +15,11 @@ class FileViewerController extends BaseController
/**
* Get file content from object storage
*
- * @access private
+ * @access protected
* @param array $file
* @return string
*/
- private function getFileContent(array $file)
+ protected function getFileContent(array $file)
{
$content = '';
@@ -35,6 +35,30 @@ class FileViewerController extends BaseController
}
/**
+ * Output file with cache
+ *
+ * @param array $file
+ * @param $mimetype
+ */
+ protected function renderFileWithCache(array $file, $mimetype)
+ {
+ $etag = md5($file['path']);
+
+ if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
+ $this->response->status(304);
+ } else {
+ try {
+ $this->response->withContentType($mimetype);
+ $this->response->withCache(5 * 86400, $etag);
+ $this->response->send();
+ $this->objectStorage->output($file['path']);
+ } catch (ObjectStorageException $e) {
+ $this->logger->error($e->getMessage());
+ }
+ }
+ }
+
+ /**
* Show file content in a popover
*
* @access public
@@ -65,21 +89,18 @@ class FileViewerController extends BaseController
public function image()
{
$file = $this->getFile();
- $etag = md5($file['path']);
- $this->response->withContentType($this->helper->file->getImageMimeType($file['name']));
- $this->response->withCache(5 * 86400, $etag);
-
- if ($this->request->getHeader('If-None-Match') === '"'.$etag.'"') {
- $this->response->status(304);
- } else {
+ $this->renderFileWithCache($file, $this->helper->file->getImageMimeType($file['name']));
+ }
- try {
- $this->response->send();
- $this->objectStorage->output($file['path']);
- } catch (ObjectStorageException $e) {
- $this->logger->error($e->getMessage());
- }
- }
+ /**
+ * Display file in browser
+ *
+ * @access public
+ */
+ public function browser()
+ {
+ $file = $this->getFile();
+ $this->renderFileWithCache($file, $this->helper->file->getBrowserViewType($file['name']));
}
/**
diff --git a/app/Controller/GroupAjaxController.php b/app/Controller/GroupAjaxController.php
index 496e9ef2..308bba9e 100644
--- a/app/Controller/GroupAjaxController.php
+++ b/app/Controller/GroupAjaxController.php
@@ -2,8 +2,6 @@
namespace Kanboard\Controller;
-use Kanboard\Formatter\GroupAutoCompleteFormatter;
-
/**
* Group Ajax Controller
*
@@ -20,7 +18,7 @@ class GroupAjaxController extends BaseController
public function autocomplete()
{
$search = $this->request->getStringParam('term');
- $formatter = new GroupAutoCompleteFormatter($this->groupManager->find($search));
- $this->response->json($formatter->format());
+ $groups = $this->groupManager->find($search);
+ $this->response->json($this->groupAutoCompleteFormatter->withGroups($groups)->format());
}
}
diff --git a/app/Controller/ICalendarController.php b/app/Controller/ICalendarController.php
index e354c6f1..4fe8b78a 100644
--- a/app/Controller/ICalendarController.php
+++ b/app/Controller/ICalendarController.php
@@ -7,7 +7,6 @@ use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\TaskAssigneeFilter;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Filter\TaskStatusFilter;
-use Kanboard\Formatter\TaskICalFormatter;
use Kanboard\Model\TaskModel;
use Eluceo\iCal\Component\Calendar as iCalendar;
@@ -94,8 +93,6 @@ class ICalendarController extends BaseController
$end = $this->request->getStringParam('end', strtotime('+6 months'));
$this->helper->ical->addTaskDateDueEvents($queryBuilder, $calendar, $start, $end);
-
- $formatter = new TaskICalFormatter($this->container);
- $this->response->ical($formatter->setCalendar($calendar)->format());
+ $this->response->ical($this->taskICalFormatter->setCalendar($calendar)->format());
}
}
diff --git a/app/Controller/LinkController.php b/app/Controller/LinkController.php
index 477b25a4..2ad8a2b5 100644
--- a/app/Controller/LinkController.php
+++ b/app/Controller/LinkController.php
@@ -16,11 +16,11 @@ class LinkController extends BaseController
/**
* Get the current link
*
- * @access private
+ * @access protected
* @return array
* @throws PageNotFoundException
*/
- private function getLink()
+ protected function getLink()
{
$link = $this->linkModel->getById($this->request->getIntegerParam('link_id'));
@@ -32,19 +32,31 @@ class LinkController extends BaseController
}
/**
- * List of links
+ * List of labels
+ *
+ * @access public
+ */
+ public function show()
+ {
+ $this->response->html($this->helper->layout->config('link/show', array(
+ 'links' => $this->linkModel->getMergedList(),
+ 'title' => t('Settings').' &gt; '.t('Link labels'),
+ )));
+ }
+
+ /**
+ * Add new link label
*
* @access public
* @param array $values
* @param array $errors
*/
- public function index(array $values = array(), array $errors = array())
+ public function create(array $values = array(), array $errors = array())
{
- $this->response->html($this->helper->layout->config('link/index', array(
- 'links' => $this->linkModel->getMergedList(),
+ $this->response->html($this->template->render('link/create', array(
+ 'links' => $this->linkModel->getMergedList(),
'values' => $values,
'errors' => $errors,
- 'title' => t('Settings').' &gt; '.t('Task\'s links'),
)));
}
@@ -61,21 +73,22 @@ class LinkController extends BaseController
if ($valid) {
if ($this->linkModel->create($values['label'], $values['opposite_label']) !== false) {
$this->flash->success(t('Link added successfully.'));
- return $this->response->redirect($this->helper->url->to('LinkController', 'index'));
+ $this->response->redirect($this->helper->url->to('LinkController', 'show'), true);
+ return;
} else {
$this->flash->failure(t('Unable to create your link.'));
}
}
- return $this->index($values, $errors);
+ $this->create($values, $errors);
}
/**
* Edit form
*
* @access public
- * @param array $values
- * @param array $errors
+ * @param array $values
+ * @param array $errors
* @throws PageNotFoundException
*/
public function edit(array $values = array(), array $errors = array())
@@ -83,12 +96,11 @@ class LinkController extends BaseController
$link = $this->getLink();
$link['label'] = t($link['label']);
- $this->response->html($this->helper->layout->config('link/edit', array(
+ $this->response->html($this->template->render('link/edit', array(
'values' => $values ?: $link,
'errors' => $errors,
'labels' => $this->linkModel->getList($link['id']),
- 'link' => $link,
- 'title' => t('Link modification')
+ 'link' => $link,
)));
}
@@ -105,13 +117,14 @@ class LinkController extends BaseController
if ($valid) {
if ($this->linkModel->update($values)) {
$this->flash->success(t('Link updated successfully.'));
- return $this->response->redirect($this->helper->url->to('LinkController', 'index'));
+ $this->response->redirect($this->helper->url->to('LinkController', 'show'), true);
+ return;
} else {
$this->flash->failure(t('Unable to update your link.'));
}
}
- return $this->edit($values, $errors);
+ $this->edit($values, $errors);
}
/**
@@ -123,9 +136,8 @@ class LinkController extends BaseController
{
$link = $this->getLink();
- $this->response->html($this->helper->layout->config('link/remove', array(
+ $this->response->html($this->template->render('link/remove', array(
'link' => $link,
- 'title' => t('Remove a link')
)));
}
@@ -145,6 +157,6 @@ class LinkController extends BaseController
$this->flash->failure(t('Unable to remove this link.'));
}
- $this->response->redirect($this->helper->url->to('LinkController', 'index'));
+ $this->response->redirect($this->helper->url->to('LinkController', 'show'), true);
}
}
diff --git a/app/Controller/PasswordResetController.php b/app/Controller/PasswordResetController.php
index a1780ed9..6189f946 100644
--- a/app/Controller/PasswordResetController.php
+++ b/app/Controller/PasswordResetController.php
@@ -109,7 +109,7 @@ class PasswordResetController extends BaseController
$token = $this->passwordResetModel->create($username);
if ($token !== false) {
- $user = $this->userModel->getByUsername($username);
+ $user = $this->userCacheDecorator->getByUsername($username);
$this->emailClient->send(
$user['email'],
diff --git a/app/Controller/PluginController.php b/app/Controller/PluginController.php
index 7b9d64d9..dbb739d6 100644
--- a/app/Controller/PluginController.php
+++ b/app/Controller/PluginController.php
@@ -23,6 +23,7 @@ class PluginController extends BaseController
{
$this->response->html($this->helper->layout->plugin('plugin/show', array(
'plugins' => $this->pluginLoader->getPlugins(),
+ 'incompatible_plugins' => $this->pluginLoader->getIncompatiblePlugins(),
'title' => t('Installed Plugins'),
'is_configured' => Installer::isConfigured(),
)));
diff --git a/app/Controller/ProjectEditController.php b/app/Controller/ProjectEditController.php
index 228d681c..ae39fdf3 100644
--- a/app/Controller/ProjectEditController.php
+++ b/app/Controller/ProjectEditController.php
@@ -11,51 +11,23 @@ namespace Kanboard\Controller;
class ProjectEditController extends BaseController
{
/**
- * General edition (most common operations)
+ * Edit project
*
* @access public
* @param array $values
* @param array $errors
*/
- public function edit(array $values = array(), array $errors = array())
+ public function show(array $values = array(), array $errors = array())
{
- $this->renderView('project_edit/general', $values, $errors);
- }
-
- /**
- * Change start and end dates
- *
- * @access public
- * @param array $values
- * @param array $errors
- */
- public function dates(array $values = array(), array $errors = array())
- {
- $this->renderView('project_edit/dates', $values, $errors);
- }
-
- /**
- * Change project description
- *
- * @access public
- * @param array $values
- * @param array $errors
- */
- public function description(array $values = array(), array $errors = array())
- {
- $this->renderView('project_edit/description', $values, $errors);
- }
+ $project = $this->getProject();
- /**
- * Change task priority
- *
- * @access public
- * @param array $values
- * @param array $errors
- */
- public function priority(array $values = array(), array $errors = array())
- {
- $this->renderView('project_edit/task_priority', $values, $errors);
+ $this->response->html($this->helper->layout->project('project_edit/show', array(
+ 'owners' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true),
+ 'values' => empty($values) ? $project : $values,
+ 'errors' => $errors,
+ 'project' => $project,
+ 'title' => t('Edit project')
+ )));
}
/**
@@ -67,67 +39,42 @@ class ProjectEditController extends BaseController
{
$project = $this->getProject();
$values = $this->request->getValues();
- $redirect = $this->request->getStringParam('redirect', 'edit');
- $values = $this->prepareValues($redirect, $project, $values);
+ $values = $this->prepareValues($project, $values);
list($valid, $errors) = $this->projectValidator->validateModification($values);
if ($valid) {
if ($this->projectModel->update($values)) {
$this->flash->success(t('Project updated successfully.'));
- return $this->response->redirect($this->helper->url->to('ProjectEditController', $redirect, array('project_id' => $project['id'])), true);
+ return $this->response->redirect($this->helper->url->to('ProjectEditController', 'show', array('project_id' => $project['id'])), true);
} else {
$this->flash->failure(t('Unable to update this project.'));
}
}
- return $this->$redirect($values, $errors);
+ return $this->show($values, $errors);
}
/**
* Prepare form values
*
* @access private
- * @param string $redirect
* @param array $project
* @param array $values
* @return array
*/
- private function prepareValues($redirect, array $project, array $values)
+ private function prepareValues(array $project, array $values)
{
- if ($redirect === 'edit') {
- if (isset($values['is_private'])) {
- if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) {
- unset($values['is_private']);
- }
- } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) {
- if ($this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) {
- $values += array('is_private' => 0);
- }
+ if (isset($values['is_private'])) {
+ if (! $this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) {
+ unset($values['is_private']);
+ }
+ } elseif ($project['is_private'] == 1 && ! isset($values['is_private'])) {
+ if ($this->helper->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])) {
+ $values += array('is_private' => 0);
}
}
return $values;
}
-
- /**
- * Common method to render different views
- *
- * @access private
- * @param string $template
- * @param array $values
- * @param array $errors
- */
- private function renderView($template, array $values, array $errors)
- {
- $project = $this->getProject();
-
- $this->response->html($this->helper->layout->project($template, array(
- 'owners' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true),
- 'values' => empty($values) ? $project : $values,
- 'errors' => $errors,
- 'project' => $project,
- 'title' => t('Edit project')
- )));
- }
}
diff --git a/app/Controller/ProjectFileController.php b/app/Controller/ProjectFileController.php
index cbe48679..9c38f684 100644
--- a/app/Controller/ProjectFileController.php
+++ b/app/Controller/ProjectFileController.php
@@ -21,7 +21,7 @@ class ProjectFileController extends BaseController
$this->response->html($this->template->render('project_file/create', array(
'project' => $project,
- 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
+ 'max_size' => $this->helper->text->phpToBytes(get_upload_max_size()),
)));
}
diff --git a/app/Controller/ProjectGanttController.php b/app/Controller/ProjectGanttController.php
index a70d9eee..8239005e 100644
--- a/app/Controller/ProjectGanttController.php
+++ b/app/Controller/ProjectGanttController.php
@@ -5,7 +5,6 @@ namespace Kanboard\Controller;
use Kanboard\Filter\ProjectIdsFilter;
use Kanboard\Filter\ProjectStatusFilter;
use Kanboard\Filter\ProjectTypeFilter;
-use Kanboard\Formatter\ProjectGanttFormatter;
use Kanboard\Model\ProjectModel;
/**
@@ -30,7 +29,7 @@ class ProjectGanttController extends BaseController
$filter->getQuery()->asc(ProjectModel::TABLE.'.start_date');
$this->response->html($this->helper->layout->app('project_gantt/show', array(
- 'projects' => $filter->format(new ProjectGanttFormatter($this->container)),
+ 'projects' => $filter->format($this->projectGanttFormatter),
'title' => t('Gantt chart for all projects'),
)));
}
diff --git a/app/Controller/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/ProjectOverviewController.php b/app/Controller/ProjectOverviewController.php
index abdff657..eb002936 100644
--- a/app/Controller/ProjectOverviewController.php
+++ b/app/Controller/ProjectOverviewController.php
@@ -23,7 +23,7 @@ class ProjectOverviewController extends BaseController
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']),
- 'roles' => $this->role->getProjectRoles(),
+ 'roles' => $this->projectRoleModel->getList($project['id']),
'events' => $this->helper->projectActivity->getProjectEvents($project['id'], 10),
'images' => $this->projectFileModel->getAllImages($project['id']),
'files' => $this->projectFileModel->getAllDocuments($project['id']),
diff --git a/app/Controller/ProjectPermissionController.php b/app/Controller/ProjectPermissionController.php
index f3ca6ed9..56777b25 100644
--- a/app/Controller/ProjectPermissionController.php
+++ b/app/Controller/ProjectPermissionController.php
@@ -52,7 +52,7 @@ class ProjectPermissionController extends BaseController
'project' => $project,
'users' => $this->projectUserRoleModel->getUsers($project['id']),
'groups' => $this->projectGroupRoleModel->getGroups($project['id']),
- 'roles' => $this->role->getProjectRoles(),
+ 'roles' => $this->projectRoleModel->getList($project['id']),
'values' => $values,
'errors' => $errors,
'title' => t('Project Permissions'),
@@ -132,7 +132,7 @@ class ProjectPermissionController extends BaseController
if (! empty($project) && ! empty($values) && $this->projectUserRoleModel->changeUserRole($project['id'], $values['id'], $values['role'])) {
$this->response->json(array('status' => 'ok'));
} else {
- $this->response->json(array('status' => 'error'));
+ $this->response->json(array('status' => 'error'), 500);
}
}
@@ -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/ProjectRoleController.php b/app/Controller/ProjectRoleController.php
new file mode 100644
index 00000000..95503750
--- /dev/null
+++ b/app/Controller/ProjectRoleController.php
@@ -0,0 +1,162 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+
+/**
+ * Class ProjectRoleController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class ProjectRoleController extends BaseController
+{
+ /**
+ * Show roles and permissions
+ */
+ public function show()
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->helper->layout->project('project_role/show', array(
+ 'project' => $project,
+ 'roles' => $this->projectRoleModel->getAllWithRestrictions($project['id']),
+ 'title' => t('Custom Project Roles'),
+ )));
+ }
+
+ /**
+ * Show form to create new role
+ *
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->template->render('project_role/create', array(
+ 'project' => $project,
+ 'values' => $values + array('project_id' => $project['id']),
+ 'errors' => $errors,
+ )));
+ }
+
+ /**
+ * Save new role
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->projectRoleValidator->validateCreation($values);
+
+ if ($valid) {
+ $role_id = $this->projectRoleModel->create($project['id'], $values['role']);
+
+ if ($role_id !== false) {
+ $this->flash->success(t('Your custom project role has been created successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to create custom project role.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ } else {
+ $this->create($values, $errors);
+ }
+ }
+
+ /**
+ * Show form to change existing role
+ *
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ */
+ public function edit(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $role = $this->getRole($project['id']);
+
+ if (empty($values)) {
+ $values = $role;
+ }
+
+ $this->response->html($this->template->render('project_role/edit', array(
+ 'role' => $role,
+ 'project' => $project,
+ 'values' => $values,
+ 'errors' => $errors,
+ )));
+ }
+
+ /**
+ * Update role
+ */
+ public function update()
+ {
+ $project = $this->getProject();
+ $role = $this->getRole($project['id']);
+
+ $values = $this->request->getValues();
+
+ list($valid, $errors) = $this->projectRoleValidator->validateModification($values);
+
+ if ($valid) {
+ if ($this->projectRoleModel->update($role['role_id'], $project['id'], $values['role'])) {
+ $this->flash->success(t('Your custom project role has been updated successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to update custom project role.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ } else {
+ $this->edit($values, $errors);
+ }
+ }
+
+ /**
+ * Confirm suppression
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $role = $this->getRole($project['id']);
+
+ $this->response->html($this->helper->layout->project('project_role/remove', array(
+ 'project' => $project,
+ 'role' => $role,
+ )));
+ }
+
+ /**
+ * Remove a custom role
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $project = $this->getProject();
+ $this->checkCSRFParam();
+ $role_id = $this->request->getIntegerParam('role_id');
+
+ if ($this->projectRoleModel->remove($project['id'], $role_id)) {
+ $this->flash->success(t('Custom project role removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this project role.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ }
+
+ protected function getRole($project_id)
+ {
+ $role_id = $this->request->getIntegerParam('role_id');
+ return $this->projectRoleModel->getById($project_id, $role_id);
+ }
+}
diff --git a/app/Controller/ProjectRoleRestrictionController.php b/app/Controller/ProjectRoleRestrictionController.php
new file mode 100644
index 00000000..4fa9b13b
--- /dev/null
+++ b/app/Controller/ProjectRoleRestrictionController.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+
+/**
+ * Class ProjectRoleRestrictionController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class ProjectRoleRestrictionController extends BaseController
+{
+ /**
+ * Show form to create a new project restriction
+ *
+ * @param array $values
+ * @param array $errors
+ * @throws AccessForbiddenException
+ */
+ public function create(array $values = array(), array $errors = array())
+ {
+ $project = $this->getProject();
+ $role_id = $this->request->getIntegerParam('role_id');
+ $role = $this->projectRoleModel->getById($project['id'], $role_id);
+
+ $this->response->html($this->template->render('project_role_restriction/create', array(
+ 'project' => $project,
+ 'role' => $role,
+ 'values' => $values + array('project_id' => $project['id'], 'role_id' => $role['role_id']),
+ 'errors' => $errors,
+ 'restrictions' => $this->projectRoleRestrictionModel->getRules(),
+ )));
+ }
+
+ /**
+ * Save new restriction
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ $restriction_id = $this->projectRoleRestrictionModel->create(
+ $project['id'],
+ $values['role_id'],
+ $values['rule']
+ );
+
+ if ($restriction_id !== false) {
+ $this->flash->success(t('The project restriction has been created successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to create this project restriction.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ }
+
+ /**
+ * Confirm suppression
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $restriction_id = $this->request->getIntegerParam('restriction_id');
+
+ $this->response->html($this->helper->layout->project('project_role_restriction/remove', array(
+ 'project' => $project,
+ 'restriction' => $this->projectRoleRestrictionModel->getById($project['id'], $restriction_id),
+ 'restrictions' => $this->projectRoleRestrictionModel->getRules(),
+ )));
+ }
+
+ /**
+ * Remove a restriction
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $project = $this->getProject();
+ $this->checkCSRFParam();
+ $restriction_id = $this->request->getIntegerParam('restriction_id');
+
+ if ($this->projectRoleRestrictionModel->remove($restriction_id)) {
+ $this->flash->success(t('Project restriction removed successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to remove this restriction.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('ProjectRoleController', 'show', array('project_id' => $project['id'])));
+ }
+}
diff --git a/app/Controller/ProjectUserOverviewController.php b/app/Controller/ProjectUserOverviewController.php
index 686de830..5faf5790 100644
--- a/app/Controller/ProjectUserOverviewController.php
+++ b/app/Controller/ProjectUserOverviewController.php
@@ -122,9 +122,9 @@ class ProjectUserOverviewController extends BaseController
{
$project = $this->getProject();
- return $this->response->html($this->template->render('project_user_overview/tooltip_users', array(
+ $this->response->html($this->template->render('project_user_overview/tooltip_users', array(
'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']),
- 'roles' => $this->role->getProjectRoles(),
+ 'roles' => $this->projectRoleModel->getList($project['id']),
)));
}
}
diff --git a/app/Controller/SearchController.php b/app/Controller/SearchController.php
index 8557b182..c9213085 100644
--- a/app/Controller/SearchController.php
+++ b/app/Controller/SearchController.php
@@ -14,7 +14,7 @@ class SearchController extends BaseController
{
public function index()
{
- $projects = $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId());
+ $projects = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
$search = urldecode($this->request->getStringParam('search'));
$nb_tasks = 0;
diff --git a/app/Controller/SubtaskController.php b/app/Controller/SubtaskController.php
index 93dab5cd..134b057e 100644
--- a/app/Controller/SubtaskController.php
+++ b/app/Controller/SubtaskController.php
@@ -27,10 +27,7 @@ class SubtaskController extends BaseController
$task = $this->getTask();
if (empty($values)) {
- $values = array(
- 'task_id' => $task['id'],
- 'another_subtask' => $this->request->getIntegerParam('another_subtask', 0)
- );
+ $values = $this->prepareValues($task);
}
$this->response->html($this->template->render('subtask/create', array(
@@ -40,6 +37,24 @@ class SubtaskController extends BaseController
'task' => $task,
)));
}
+
+ /**
+ * Prepare form values
+ *
+ * @access protected
+ * @param array $task
+ * @return array
+ */
+ protected function prepareValues(array $task)
+ {
+ $values = array(
+ 'task_id' => $task['id'],
+ 'another_subtask' => $this->request->getIntegerParam('another_subtask', 0)
+ );
+
+ $values = $this->hook->merge('controller:subtask:form:default', $values, array('default_values' => $values));
+ return $values;
+ }
/**
* Validation and creation
@@ -168,7 +183,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/TaskAjaxController.php b/app/Controller/TaskAjaxController.php
index f9feff15..6d0b3fc2 100644
--- a/app/Controller/TaskAjaxController.php
+++ b/app/Controller/TaskAjaxController.php
@@ -5,8 +5,10 @@ namespace Kanboard\Controller;
use Kanboard\Filter\TaskIdExclusionFilter;
use Kanboard\Filter\TaskIdFilter;
use Kanboard\Filter\TaskProjectsFilter;
+use Kanboard\Filter\TaskStartsWithIdFilter;
+use Kanboard\Filter\TaskStatusFilter;
use Kanboard\Filter\TaskTitleFilter;
-use Kanboard\Formatter\TaskAutoCompleteFormatter;
+use Kanboard\Model\TaskModel;
/**
* Task Ajax Controller
@@ -19,7 +21,6 @@ class TaskAjaxController extends BaseController
/**
* Task auto-completion (Ajax)
*
- * @access public
*/
public function autocomplete()
{
@@ -43,7 +44,27 @@ class TaskAjaxController extends BaseController
$filter->withFilter(new TaskTitleFilter($search));
}
- $this->response->json($filter->format(new TaskAutoCompleteFormatter($this->container)));
+ $this->response->json($filter->format($this->taskAutoCompleteFormatter));
+ }
+ }
+
+ /**
+ * Task ID suggest menu
+ */
+ public function suggest()
+ {
+ $taskId = $this->request->getIntegerParam('search');
+ $projectIds = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
+
+ if (empty($projectIds)) {
+ $this->response->json(array());
+ } else {
+ $filter = $this->taskQuery
+ ->withFilter(new TaskProjectsFilter($projectIds))
+ ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN))
+ ->withFilter(new TaskStartsWithIdFilter($taskId));
+
+ $this->response->json($filter->format($this->taskSuggestMenuFormatter));
}
}
}
diff --git a/app/Controller/TaskBulkController.php b/app/Controller/TaskBulkController.php
index 80ff8f4f..51447f32 100644
--- a/app/Controller/TaskBulkController.php
+++ b/app/Controller/TaskBulkController.php
@@ -47,7 +47,12 @@ class TaskBulkController extends BaseController
$values = $this->request->getValues();
list($valid, $errors) = $this->taskValidator->validateBulkCreation($values);
- if ($valid) {
+ if (! $valid) {
+ $this->show($values, $errors);
+ } else if (! $this->helper->projectRole->canCreateTaskInColumn($project['id'], $values['column_id'])) {
+ $this->flash->failure(t('You cannot create tasks in this column.'));
+ $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
+ } else {
$this->createTasks($project, $values);
$this->response->redirect($this->helper->url->to(
'BoardViewController',
@@ -55,8 +60,6 @@ class TaskBulkController extends BaseController
array('project_id' => $project['id']),
'swimlane-'. $values['swimlane_id']
), true);
- } else {
- $this->show($values, $errors);
}
}
diff --git a/app/Controller/TaskCreationController.php b/app/Controller/TaskCreationController.php
index 8636d02a..cafd7e06 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,26 +16,18 @@ 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())
{
$project = $this->getProject();
$swimlanes_list = $this->swimlaneModel->getList($project['id'], false, true);
+ $values += $this->prepareValues($swimlanes_list);
- 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->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
+ $values = $this->hook->merge('controller:task-creation:form:default', $values, array('default_values' => $values));
$this->response->html($this->template->render('task_creation/show', array(
'project' => $project,
@@ -43,7 +37,6 @@ class TaskCreationController extends BaseController
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private']),
'categories_list' => $this->categoryModel->getList($project['id']),
'swimlanes_list' => $swimlanes_list,
- 'title' => $project['name'].' &gt; '.t('New task')
)));
}
@@ -59,19 +52,51 @@ class TaskCreationController extends BaseController
list($valid, $errors) = $this->taskValidator->validateCreation($values);
- if ($valid && $this->taskCreationModel->create($values)) {
+ if (! $valid) {
+ $this->flash->failure(t('Unable to create your task.'));
+ $this->show($values, $errors);
+ } else if (! $this->helper->projectRole->canCreateTaskInColumn($project['id'], $values['column_id'])) {
+ $this->flash->failure(t('You cannot create tasks in this column.'));
+ $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
+ } else {
+ $task_id = $this->taskCreationModel->create($values);
$this->flash->success(t('Task created successfully.'));
- return $this->afterSave($project, $values);
+ $this->afterSave($project, $values, $task_id);
+ }
+ }
+
+ /**
+ * Duplicate created tasks to multiple projects
+ *
+ * @throws PageNotFoundException
+ */
+ public function duplicateProjects()
+ {
+ $project = $this->getProject();
+ $values = $this->request->getValues();
+
+ if (isset($values['project_ids'])) {
+ foreach ($values['project_ids'] as $project_id) {
+ $this->taskProjectDuplicationModel->duplicateToProject($values['task_id'], $project_id);
+ }
}
- $this->flash->failure(t('Unable to create your task.'));
- return $this->show($values, $errors);
+ $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
}
- private function afterSave(array $project, array &$values)
+ /**
+ * Executed after the task is saved
+ *
+ * @param array $project
+ * @param array $values
+ * @param integer $task_id
+ */
+ protected function afterSave(array $project, array &$values, $task_id)
{
- if (isset($values['another_task']) && $values['another_task'] == 1) {
- return $this->show(array(
+ if (isset($values['duplicate_multiple_projects']) && $values['duplicate_multiple_projects'] == 1) {
+ $this->chooseProjects($project, $task_id);
+ } elseif (isset($values['another_task']) && $values['another_task'] == 1) {
+ $this->show(array(
'owner_id' => $values['owner_id'],
'color_id' => $values['color_id'],
'category_id' => isset($values['category_id']) ? $values['category_id'] : 0,
@@ -79,8 +104,47 @@ 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 $values;
+ }
- return $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true);
+ /**
+ * Choose projects
+ *
+ * @param array $project
+ * @param integer $task_id
+ */
+ protected function chooseProjects(array $project, $task_id)
+ {
+ $task = $this->taskFinderModel->getById($task_id);
+ $projects = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
+ unset($projects[$project['id']]);
+
+ $this->response->html($this->template->render('task_creation/duplicate_projects', array(
+ 'project' => $project,
+ 'task' => $task,
+ 'projects_list' => $projects,
+ 'values' => array('task_id' => $task['id'])
+ )));
}
}
diff --git a/app/Controller/TaskExternalLinkController.php b/app/Controller/TaskExternalLinkController.php
index 9c04eb00..df23f87b 100644
--- a/app/Controller/TaskExternalLinkController.php
+++ b/app/Controller/TaskExternalLinkController.php
@@ -76,12 +76,23 @@ class TaskExternalLinkController extends BaseController
$values = $this->request->getValues();
list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
- if ($valid && $this->taskExternalLinkModel->create($values) !== false) {
- $this->flash->success(t('Link added successfully.'));
- return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ if ($valid) {
+ if ($this->taskExternalLinkModel->create($values) !== false) {
+ $this->flash->success(t('Link added successfully.'));
+ } else {
+ $this->flash->success(t('Unable to create your link.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ } else {
+ $provider = $this->externalLinkManager->getProvider($values['link_type']);
+ $this->response->html($this->template->render('task_external_link/create', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'dependencies' => $provider->getDependencies(),
+ 'task' => $task,
+ )));
}
-
- return $this->edit($values, $errors);
}
/**
diff --git a/app/Controller/TaskFileController.php b/app/Controller/TaskFileController.php
index 77c0c026..8a0971e4 100644
--- a/app/Controller/TaskFileController.php
+++ b/app/Controller/TaskFileController.php
@@ -40,7 +40,7 @@ class TaskFileController extends BaseController
$this->response->html($this->template->render('task_file/create', array(
'task' => $task,
- 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')),
+ 'max_size' => $this->helper->text->phpToBytes(get_upload_max_size()),
)));
}
diff --git a/app/Controller/TaskGanttController.php b/app/Controller/TaskGanttController.php
index 868368e1..b03b9d00 100644
--- a/app/Controller/TaskGanttController.php
+++ b/app/Controller/TaskGanttController.php
@@ -3,7 +3,6 @@
namespace Kanboard\Controller;
use Kanboard\Filter\TaskProjectFilter;
-use Kanboard\Formatter\TaskGanttFormatter;
use Kanboard\Model\TaskModel;
/**
@@ -35,7 +34,7 @@ class TaskGanttController extends BaseController
'title' => $project['name'],
'description' => $this->helper->projectHeader->getDescription($project),
'sorting' => $sorting,
- 'tasks' => $filter->format(new TaskGanttFormatter($this->container)),
+ 'tasks' => $filter->format($this->taskGanttFormatter),
)));
}
diff --git a/app/Controller/TaskGanttCreationController.php b/app/Controller/TaskGanttCreationController.php
deleted file mode 100644
index 4361ede3..00000000
--- a/app/Controller/TaskGanttCreationController.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Kanboard\Controller;
-
-/**
- * Class TaskGanttCreationController
- *
- * @package Kanboard\Controller
- * @author Frederic Guillot
- */
-class TaskGanttCreationController extends BaseController
-{
- /**
- * Simplified form to create a new task
- *
- * @access public
- * @param array $values
- * @param array $errors
- * @throws \Kanboard\Core\Controller\PageNotFoundException
- */
- public function show(array $values = array(), array $errors = array())
- {
- $project = $this->getProject();
-
- $values = $values + array(
- 'project_id' => $project['id'],
- 'column_id' => $this->columnModel->getFirstColumnId($project['id']),
- 'position' => 1
- );
-
- $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
- $values = $this->hook->merge('controller:gantt:task:form:default', $values, array('default_values' => $values));
-
- $this->response->html($this->template->render('task_gantt_creation/show', array(
- 'project' => $project,
- 'errors' => $errors,
- 'values' => $values,
- 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private']),
- 'categories_list' => $this->categoryModel->getList($project['id']),
- 'swimlanes_list' => $this->swimlaneModel->getList($project['id'], false, true),
- 'title' => $project['name'].' &gt; '.t('New task')
- )));
- }
-
- /**
- * Validate and save a new task
- *
- * @access public
- */
- public function save()
- {
- $project = $this->getProject();
- $values = $this->request->getValues();
-
- 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.'));
- }
- }
-
- return $this->show($values, $errors);
- }
-}
diff --git a/app/Controller/TaskImportController.php b/app/Controller/TaskImportController.php
index aff2d390..2e323979 100644
--- a/app/Controller/TaskImportController.php
+++ b/app/Controller/TaskImportController.php
@@ -23,15 +23,14 @@ class TaskImportController extends BaseController
{
$project = $this->getProject();
- $this->response->html($this->helper->layout->project('task_import/show', array(
+ $this->response->html($this->template->render('task_import/show', array(
'project' => $project,
'values' => $values,
'errors' => $errors,
- 'max_size' => ini_get('upload_max_filesize'),
+ 'max_size' => get_upload_max_size(),
'delimiters' => Csv::getDelimiters(),
'enclosures' => Csv::getEnclosures(),
- 'title' => t('Import tasks from CSV file'),
- ), 'task_import/sidebar'));
+ )));
}
/**
@@ -58,7 +57,7 @@ class TaskImportController extends BaseController
$this->flash->failure(t('Nothing have been imported!'));
}
- $this->response->redirect($this->helper->url->to('TaskImportController', 'show', array('project_id' => $project['id'])));
+ $this->response->redirect($this->helper->url->to('TaskImportController', 'show', array('project_id' => $project['id'])), true);
}
}
diff --git a/app/Controller/TaskModificationController.php b/app/Controller/TaskModificationController.php
index b064123a..520bf70e 100644
--- a/app/Controller/TaskModificationController.php
+++ b/app/Controller/TaskModificationController.php
@@ -2,6 +2,10 @@
namespace Kanboard\Controller;
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Core\ExternalTask\AccessForbiddenException as ExternalTaskAccessForbiddenException;
+use Kanboard\Core\ExternalTask\ExternalTaskException;
+
/**
* Task Modification controller
*
@@ -38,13 +42,12 @@ class TaskModificationController extends BaseController
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());
}
- $this->response->html($this->template->render('task_modification/show', array(
+ $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values));
+ $values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values));
+
+ $params = array(
'project' => $project,
'values' => $values,
'errors' => $errors,
@@ -52,7 +55,29 @@ class TaskModificationController extends BaseController
'tags' => $this->taskTagModel->getList($task['id']),
'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']),
'categories_list' => $this->categoryModel->getList($task['project_id']),
- )));
+ );
+
+ $this->renderTemplate($task, $params);
+ }
+
+ protected function renderTemplate(array &$task, array &$params)
+ {
+ if (empty($task['external_uri'])) {
+ $this->response->html($this->template->render('task_modification/show', $params));
+ } else {
+
+ try {
+ $taskProvider = $this->externalTaskManager->getProvider($task['external_provider']);
+ $params['template'] = $taskProvider->getModificationFormTemplate();
+ $params['external_task'] = $taskProvider->fetch($task['external_uri']);
+ } catch (ExternalTaskAccessForbiddenException $e) {
+ throw new AccessForbiddenException($e->getMessage());
+ } catch (ExternalTaskException $e) {
+ $params['error_message'] = $e->getMessage();
+ }
+
+ $this->response->html($this->template->render('external_task_modification/show', $params));
+ }
}
/**
@@ -67,7 +92,7 @@ class TaskModificationController extends BaseController
list($valid, $errors) = $this->taskValidator->validateModification($values);
- if ($valid && $this->taskModificationModel->update($values)) {
+ if ($valid && $this->updateTask($task, $values, $errors)) {
$this->flash->success(t('Task updated successfully.'));
$this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])), true);
} else {
@@ -75,4 +100,23 @@ class TaskModificationController extends BaseController
$this->edit($values, $errors);
}
}
+
+ protected function updateTask(array &$task, array &$values, array &$errors)
+ {
+ $result = $this->taskModificationModel->update($values);
+
+ if ($result && ! empty($task['external_uri'])) {
+ try {
+ $taskProvider = $this->externalTaskManager->getProvider($task['external_provider']);
+ $result = $taskProvider->save($task['external_uri'], $values, $errors);
+ } catch (ExternalTaskAccessForbiddenException $e) {
+ throw new AccessForbiddenException($e->getMessage());
+ } catch (ExternalTaskException $e) {
+ $this->logger->error($e->getMessage());
+ $result = false;
+ }
+ }
+
+ return $result;
+ }
}
diff --git a/app/Controller/TaskMovePositionController.php b/app/Controller/TaskMovePositionController.php
new file mode 100644
index 00000000..39687b79
--- /dev/null
+++ b/app/Controller/TaskMovePositionController.php
@@ -0,0 +1,51 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\AccessForbiddenException;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskMovePositionController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class TaskMovePositionController extends BaseController
+{
+ public function show()
+ {
+ $task = $this->getTask();
+
+ $this->response->html($this->template->render('task_move_position/show', array(
+ 'task' => $task,
+ 'board' => $this->boardFormatter
+ ->withProjectId($task['project_id'])
+ ->withQuery($this->taskFinderModel->getExtendedQuery()
+ ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN)
+ ->neq(TaskModel::TABLE.'.id', $task['id'])
+ )
+ ->format()
+ )));
+ }
+
+ public function save()
+ {
+ $task = $this->getTask();
+ $values = $this->request->getJson();
+
+ if (! $this->helper->projectRole->canMoveTask($task['project_id'], $task['column_id'], $values['column_id'])) {
+ throw new AccessForbiddenException(e('You are not allowed to move this task.'));
+ }
+
+ $this->taskPositionModel->movePosition(
+ $task['project_id'],
+ $task['id'],
+ $values['column_id'],
+ $values['position'],
+ $values['swimlane_id']
+ );
+
+ $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
+ }
+}
diff --git a/app/Controller/TaskStatusController.php b/app/Controller/TaskStatusController.php
index 82b4f9c4..56d38400 100644
--- a/app/Controller/TaskStatusController.php
+++ b/app/Controller/TaskStatusController.php
@@ -52,11 +52,11 @@ class TaskStatusController extends BaseController
$this->flash->failure($failure_message);
}
- return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])), true);
+ } else {
+ $this->response->html($this->template->render($template, array(
+ 'task' => $task,
+ )));
}
-
- return $this->response->html($this->template->render($template, array(
- 'task' => $task,
- )));
}
}
diff --git a/app/Controller/TaskSuppressionController.php b/app/Controller/TaskSuppressionController.php
index 600107c9..019bd97c 100644
--- a/app/Controller/TaskSuppressionController.php
+++ b/app/Controller/TaskSuppressionController.php
@@ -19,7 +19,7 @@ class TaskSuppressionController extends BaseController
{
$task = $this->getTask();
- if (! $this->helper->user->canRemoveTask($task)) {
+ if (! $this->helper->projectRole->canRemoveTask($task)) {
throw new AccessForbiddenException();
}
@@ -37,7 +37,7 @@ class TaskSuppressionController extends BaseController
$task = $this->getTask();
$this->checkCSRFParam();
- if (! $this->helper->user->canRemoveTask($task)) {
+ if (! $this->helper->projectRole->canRemoveTask($task)) {
throw new AccessForbiddenException();
}
diff --git a/app/Controller/TaskViewController.php b/app/Controller/TaskViewController.php
index f40f8bea..31b9de11 100644
--- a/app/Controller/TaskViewController.php
+++ b/app/Controller/TaskViewController.php
@@ -4,6 +4,7 @@ namespace Kanboard\Controller;
use Kanboard\Core\Controller\AccessForbiddenException;
use Kanboard\Core\Controller\PageNotFoundException;
+use Kanboard\Model\UserMetadataModel;
/**
* Task Controller
@@ -22,7 +23,6 @@ class TaskViewController extends BaseController
{
$project = $this->projectModel->getByToken($this->request->getStringParam('token'));
- // Token verification
if (empty($project)) {
throw AccessForbiddenException::getInstance()->withoutLayout();
}
@@ -62,23 +62,14 @@ 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());
+ $commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC');
$this->response->html($this->helper->layout->task('task/show', array(
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
- 'values' => $values,
'files' => $this->taskFileModel->getAllDocuments($task['id']),
'images' => $this->taskFileModel->getAllImages($task['id']),
- 'comments' => $this->commentModel->getAll($task['id'], $this->userSession->getCommentSorting()),
+ 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection),
'subtasks' => $subtasks,
'internal_links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']),
'external_links' => $this->taskExternalLinkModel->getAll($task['id']),
@@ -102,6 +93,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']),
)));
}
@@ -126,6 +118,7 @@ class TaskViewController extends BaseController
'task' => $task,
'project' => $this->projectModel->getById($task['project_id']),
'subtask_paginator' => $subtask_paginator,
+ 'tags' => $this->taskTagModel->getList($task['id']),
)));
}
@@ -142,6 +135,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/UserAjaxController.php b/app/Controller/UserAjaxController.php
index ed180471..17567a00 100644
--- a/app/Controller/UserAjaxController.php
+++ b/app/Controller/UserAjaxController.php
@@ -3,7 +3,6 @@
namespace Kanboard\Controller;
use Kanboard\Filter\UserNameFilter;
-use Kanboard\Formatter\UserAutoCompleteFormatter;
use Kanboard\Model\UserModel;
/**
@@ -24,7 +23,7 @@ class UserAjaxController extends BaseController
$search = $this->request->getStringParam('term');
$filter = $this->userQuery->withFilter(new UserNameFilter($search));
$filter->getQuery()->asc(UserModel::TABLE.'.name')->asc(UserModel::TABLE.'.username');
- $this->response->json($filter->format(new UserAutoCompleteFormatter($this->container)));
+ $this->response->json($filter->format($this->userAutoCompleteFormatter));
}
/**
@@ -35,9 +34,10 @@ class UserAjaxController extends BaseController
public function mention()
{
$project_id = $this->request->getStringParam('project_id');
- $query = $this->request->getStringParam('q');
+ $query = $this->request->getStringParam('search');
$users = $this->projectPermissionModel->findUsernames($project_id, $query);
- $this->response->json($users);
+
+ $this->response->json($this->userMentionFormatter->withUsers($users)->format());
}
/**
diff --git a/app/Controller/UserApiAccessController.php b/app/Controller/UserApiAccessController.php
new file mode 100644
index 00000000..e03514d5
--- /dev/null
+++ b/app/Controller/UserApiAccessController.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Security\Token;
+
+/**
+ * Class UserApiAccessController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class UserApiAccessController extends BaseController
+{
+ public function show()
+ {
+ $user = $this->getUser();
+
+ return $this->response->html($this->helper->layout->user('user_api_access/show', array(
+ 'user' => $user,
+ 'title' => t('API User Access'),
+ )));
+ }
+
+ public function generate()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ $this->userModel->update(array(
+ 'id' => $user['id'],
+ 'api_access_token' => Token::getToken(),
+ ));
+
+ $this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id'])));
+ }
+
+ public function remove()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ $this->userModel->update(array(
+ 'id' => $user['id'],
+ 'api_access_token' => null,
+ ));
+
+ $this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id'])));
+ }
+} \ No newline at end of file
diff --git a/app/Controller/UserCreationController.php b/app/Controller/UserCreationController.php
index 9c873f85..27f1687b 100644
--- a/app/Controller/UserCreationController.php
+++ b/app/Controller/UserCreationController.php
@@ -22,10 +22,7 @@ class UserCreationController extends BaseController
*/
public function show(array $values = array(), array $errors = array())
{
- $isRemote = $this->request->getIntegerParam('remote') == 1 || (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1);
- $template = $isRemote ? 'user_creation/remote' : 'user_creation/local';
-
- $this->response->html($this->template->render($template, array(
+ $this->response->html($this->template->render('user_creation/show', array(
'timezones' => $this->timezoneModel->getTimezones(true),
'languages' => $this->languageModel->getLanguages(true),
'roles' => $this->role->getApplicationRoles(),
@@ -57,7 +54,7 @@ class UserCreationController extends BaseController
*
* @param array $values
*/
- private function createUser(array $values)
+ protected function createUser(array $values)
{
$project_id = empty($values['project_id']) ? 0 : $values['project_id'];
unset($values['project_id']);
diff --git a/app/Controller/UserCredentialController.php b/app/Controller/UserCredentialController.php
index 4021dc37..98fe967d 100644
--- a/app/Controller/UserCredentialController.php
+++ b/app/Controller/UserCredentialController.php
@@ -106,4 +106,21 @@ class UserCredentialController extends BaseController
return $this->changeAuthentication($values, $errors);
}
+
+ /**
+ * Unlock user
+ */
+ public function unlock()
+ {
+ $user = $this->getUser();
+ $this->checkCSRFParam();
+
+ if ($this->userLockingModel->resetFailedLogin($user['username'])) {
+ $this->flash->success(t('User unlocked successfully.'));
+ } else {
+ $this->flash->failure(t('Unable to unlock the user.'));
+ }
+
+ $this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])));
+ }
}
diff --git a/app/Controller/UserImportController.php b/app/Controller/UserImportController.php
index fec9a31d..6a9d5992 100644
--- a/app/Controller/UserImportController.php
+++ b/app/Controller/UserImportController.php
@@ -23,7 +23,7 @@ class UserImportController extends BaseController
$this->response->html($this->template->render('user_import/show', array(
'values' => $values,
'errors' => $errors,
- 'max_size' => ini_get('upload_max_filesize'),
+ 'max_size' => get_upload_max_size(),
'delimiters' => Csv::getDelimiters(),
'enclosures' => Csv::getEnclosures(),
)));
diff --git a/app/Controller/UserInviteController.php b/app/Controller/UserInviteController.php
new file mode 100644
index 00000000..8c77940c
--- /dev/null
+++ b/app/Controller/UserInviteController.php
@@ -0,0 +1,107 @@
+<?php
+
+namespace Kanboard\Controller;
+
+use Kanboard\Core\Controller\PageNotFoundException;
+use Kanboard\Core\Security\Role;
+use Kanboard\Notification\MailNotification;
+
+/**
+ * Class UserInviteController
+ *
+ * @package Kanboard\Controller
+ * @author Frederic Guillot
+ */
+class UserInviteController extends BaseController
+{
+ public function show(array $values = array(), array $errors = array())
+ {
+ $this->response->html($this->template->render('user_invite/show', array(
+ 'projects' => $this->projectModel->getList(),
+ 'errors' => $errors,
+ 'values' => $values,
+ )));
+ }
+
+ public function save()
+ {
+ $values = $this->request->getValues();
+
+ if (! empty($values['emails']) && isset($values['project_id'])) {
+ $emails = explode("\r\n", trim($values['emails']));
+ $nb = $this->inviteModel->createInvites($emails, $values['project_id']);
+ $this->flash->success($nb > 1 ? t('%d invitations were sent.', $nb) : t('%d invitation was sent.', $nb));
+ }
+
+ $this->response->redirect($this->helper->url->to('UserListController', 'show'));
+ }
+
+ public function signup(array $values = array(), array $errors = array())
+ {
+ $invite = $this->getInvite();
+
+ $this->response->html($this->helper->layout->app('user_invite/signup', array(
+ 'no_layout' => true,
+ 'not_editable' => true,
+ 'token' => $invite['token'],
+ 'errors' => $errors,
+ 'values' => $values + array('email' => $invite['email']),
+ 'timezones' => $this->timezoneModel->getTimezones(true),
+ 'languages' => $this->languageModel->getLanguages(true),
+ )));
+ }
+
+ public function register()
+ {
+ $invite = $this->getInvite();
+
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->userValidator->validateCreation($values);
+
+ if ($valid) {
+ $this->createUser($invite, $values);
+ } else {
+ $this->signup($values, $errors);
+ }
+ }
+
+ protected function getInvite()
+ {
+ $token = $this->request->getStringParam('token');
+
+ if (empty($token)) {
+ throw PageNotFoundException::getInstance()->withoutLayout();
+ }
+
+ $invite = $this->inviteModel->getByToken($token);
+
+ if (empty($invite)) {
+ throw PageNotFoundException::getInstance()->withoutLayout();
+ }
+
+ return $invite;
+ }
+
+ protected function createUser(array $invite, array $values)
+ {
+ $user_id = $this->userModel->create($values);
+
+ if ($user_id !== false) {
+ if ($invite['project_id'] != 0) {
+ $this->projectUserRoleModel->addUser($invite['project_id'], $user_id, Role::PROJECT_MEMBER);
+ }
+
+ if (! empty($values['notifications_enabled'])) {
+ $this->userNotificationTypeModel->saveSelectedTypes($user_id, array(MailNotification::TYPE));
+ }
+
+ $this->inviteModel->remove($invite['email']);
+
+ $this->flash->success(t('User created successfully.'));
+ $this->response->redirect($this->helper->url->to('AuthController', 'login'));
+ } else {
+ $this->flash->failure(t('Unable to create this user.'));
+ $this->response->redirect($this->helper->url->to('UserInviteController', 'signup'));
+ }
+ }
+}
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/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 8103ec14..17ed5b33 100644
--- a/app/Core/Base.php
+++ b/app/Core/Base.php
@@ -10,151 +10,196 @@ use Pimple\Container;
* @package core
* @author Frederic Guillot
*
- * @property \Kanboard\Analytic\TaskDistributionAnalytic $taskDistributionAnalytic
- * @property \Kanboard\Analytic\UserDistributionAnalytic $userDistributionAnalytic
- * @property \Kanboard\Analytic\EstimatedTimeComparisonAnalytic $estimatedTimeComparisonAnalytic
- * @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic
- * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic
- * @property \Kanboard\Core\Action\ActionManager $actionManager
- * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager
- * @property \Kanboard\Core\Cache\MemoryCache $memoryCache
- * @property \Kanboard\Core\Event\EventManager $eventManager
- * @property \Kanboard\Core\Group\GroupManager $groupManager
- * @property \Kanboard\Core\Http\Client $httpClient
- * @property \Kanboard\Core\Http\OAuth2 $oauth
- * @property \Kanboard\Core\Http\RememberMeCookie $rememberMeCookie
- * @property \Kanboard\Core\Http\Request $request
- * @property \Kanboard\Core\Http\Response $response
- * @property \Kanboard\Core\Http\Router $router
- * @property \Kanboard\Core\Http\Route $route
- * @property \Kanboard\Core\Queue\QueueManager $queueManager
- * @property \Kanboard\Core\Mail\Client $emailClient
- * @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage
- * @property \Kanboard\Core\Plugin\Hook $hook
- * @property \Kanboard\Core\Plugin\Loader $pluginLoader
- * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager
- * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap
- * @property \Kanboard\Core\Security\AccessMap $projectAccessMap
- * @property \Kanboard\Core\Security\AccessMap $apiAccessMap
- * @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap
- * @property \Kanboard\Core\Security\Authorization $applicationAuthorization
- * @property \Kanboard\Core\Security\Authorization $projectAuthorization
- * @property \Kanboard\Core\Security\Authorization $apiAuthorization
- * @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization
- * @property \Kanboard\Core\Security\Role $role
- * @property \Kanboard\Core\Security\Token $token
- * @property \Kanboard\Core\Session\FlashMessage $flash
- * @property \Kanboard\Core\Session\SessionManager $sessionManager
- * @property \Kanboard\Core\Session\SessionStorage $sessionStorage
- * @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager
- * @property \Kanboard\Core\User\GroupSync $groupSync
- * @property \Kanboard\Core\User\UserProfile $userProfile
- * @property \Kanboard\Core\User\UserSync $userSync
- * @property \Kanboard\Core\User\UserSession $userSession
- * @property \Kanboard\Core\DateParser $dateParser
- * @property \Kanboard\Core\Helper $helper
- * @property \Kanboard\Core\Paginator $paginator
- * @property \Kanboard\Core\Template $template
- * @property \Kanboard\Model\ActionModel $actionModel
- * @property \Kanboard\Model\ActionParameterModel $actionParameterModel
- * @property \Kanboard\Model\AvatarFileModel $avatarFileModel
- * @property \Kanboard\Model\BoardModel $boardModel
- * @property \Kanboard\Model\CategoryModel $categoryModel
- * @property \Kanboard\Model\ColorModel $colorModel
- * @property \Kanboard\Model\ColumnModel $columnModel
- * @property \Kanboard\Model\CommentModel $commentModel
- * @property \Kanboard\Model\ConfigModel $configModel
- * @property \Kanboard\Model\CurrencyModel $currencyModel
- * @property \Kanboard\Model\CustomFilterModel $customFilterModel
- * @property \Kanboard\Model\TaskFileModel $taskFileModel
- * @property \Kanboard\Model\ProjectFileModel $projectFileModel
- * @property \Kanboard\Model\GroupModel $groupModel
- * @property \Kanboard\Model\GroupMemberModel $groupMemberModel
- * @property \Kanboard\Model\LanguageModel $languageModel
- * @property \Kanboard\Model\LastLoginModel $lastLoginModel
- * @property \Kanboard\Model\LinkModel $linkModel
- * @property \Kanboard\Model\NotificationModel $notificationModel
- * @property \Kanboard\Model\PasswordResetModel $passwordResetModel
- * @property \Kanboard\Model\ProjectModel $projectModel
- * @property \Kanboard\Model\ProjectActivityModel $projectActivityModel
- * @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel
- * @property \Kanboard\Model\ProjectDailyColumnStatsModel $projectDailyColumnStatsModel
- * @property \Kanboard\Model\ProjectDailyStatsModel $projectDailyStatsModel
- * @property \Kanboard\Model\ProjectMetadataModel $projectMetadataModel
- * @property \Kanboard\Model\ProjectPermissionModel $projectPermissionModel
- * @property \Kanboard\Model\ProjectUserRoleModel $projectUserRoleModel
- * @property \Kanboard\Model\ProjectGroupRoleModel $projectGroupRoleModel
- * @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel
- * @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel
- * @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel
- * @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel
- * @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel
- * @property \Kanboard\Model\SubtaskModel $subtaskModel
- * @property \Kanboard\Model\SubtaskTimeTrackingModel $subtaskTimeTrackingModel
- * @property \Kanboard\Model\SwimlaneModel $swimlaneModel
- * @property \Kanboard\Model\TagDuplicationModel $tagDuplicationModel
- * @property \Kanboard\Model\TagModel $tagModel
- * @property \Kanboard\Model\TaskModel $taskModel
- * @property \Kanboard\Model\TaskAnalyticModel $taskAnalyticModel
- * @property \Kanboard\Model\TaskCreationModel $taskCreationModel
- * @property \Kanboard\Model\TaskDuplicationModel $taskDuplicationModel
- * @property \Kanboard\Model\TaskProjectDuplicationModel $taskProjectDuplicationModel
- * @property \Kanboard\Model\TaskProjectMoveModel $taskProjectMoveModel
- * @property \Kanboard\Model\TaskRecurrenceModel $taskRecurrenceModel
- * @property \Kanboard\Model\TaskExternalLinkModel $taskExternalLinkModel
- * @property \Kanboard\Model\TaskFinderModel $taskFinderModel
- * @property \Kanboard\Model\TaskLinkModel $taskLinkModel
- * @property \Kanboard\Model\TaskModificationModel $taskModificationModel
- * @property \Kanboard\Model\TaskPositionModel $taskPositionModel
- * @property \Kanboard\Model\TaskStatusModel $taskStatusModel
- * @property \Kanboard\Model\TaskTagModel $taskTagModel
- * @property \Kanboard\Model\TaskMetadataModel $taskMetadataModel
- * @property \Kanboard\Model\TimezoneModel $timezoneModel
- * @property \Kanboard\Model\TransitionModel $transitionModel
- * @property \Kanboard\Model\UserModel $userModel
- * @property \Kanboard\Model\UserLockingModel $userLockingModel
- * @property \Kanboard\Model\UserMentionModel $userMentionModel
- * @property \Kanboard\Model\UserNotificationModel $userNotificationModel
- * @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel
- * @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel
- * @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel
- * @property \Kanboard\Model\UserMetadataModel $userMetadataModel
- * @property \Kanboard\Validator\ActionValidator $actionValidator
- * @property \Kanboard\Validator\AuthValidator $authValidator
- * @property \Kanboard\Validator\ColumnValidator $columnValidator
- * @property \Kanboard\Validator\CategoryValidator $categoryValidator
- * @property \Kanboard\Validator\CommentValidator $commentValidator
- * @property \Kanboard\Validator\CurrencyValidator $currencyValidator
- * @property \Kanboard\Validator\CustomFilterValidator $customFilterValidator
- * @property \Kanboard\Validator\ExternalLinkValidator $externalLinkValidator
- * @property \Kanboard\Validator\GroupValidator $groupValidator
- * @property \Kanboard\Validator\LinkValidator $linkValidator
- * @property \Kanboard\Validator\PasswordResetValidator $passwordResetValidator
- * @property \Kanboard\Validator\ProjectValidator $projectValidator
- * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator
- * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator
- * @property \Kanboard\Validator\TagValidator $tagValidator
- * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator
- * @property \Kanboard\Validator\TaskValidator $taskValidator
- * @property \Kanboard\Validator\UserValidator $userValidator
- * @property \Kanboard\Import\TaskImport $taskImport
- * @property \Kanboard\Import\UserImport $userImport
- * @property \Kanboard\Export\SubtaskExport $subtaskExport
- * @property \Kanboard\Export\TaskExport $taskExport
- * @property \Kanboard\Export\TransitionExport $transitionExport
- * @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery
- * @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery
- * @property \Kanboard\Core\Filter\QueryBuilder $projectActivityQuery
- * @property \Kanboard\Core\Filter\QueryBuilder $userQuery
- * @property \Kanboard\Core\Filter\QueryBuilder $projectQuery
- * @property \Kanboard\Core\Filter\QueryBuilder $taskQuery
- * @property \Kanboard\Core\Filter\LexerBuilder $taskLexer
- * @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer
- * @property \Psr\Log\LoggerInterface $logger
- * @property \PicoDb\Database $db
- * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
- * @property \Symfony\Component\Console\Application $cli
- * @property \JsonRPC\Server $api
+ * @property \Kanboard\Analytic\TaskDistributionAnalytic $taskDistributionAnalytic
+ * @property \Kanboard\Analytic\UserDistributionAnalytic $userDistributionAnalytic
+ * @property \Kanboard\Analytic\EstimatedTimeComparisonAnalytic $estimatedTimeComparisonAnalytic
+ * @property \Kanboard\Analytic\AverageLeadCycleTimeAnalytic $averageLeadCycleTimeAnalytic
+ * @property \Kanboard\Analytic\AverageTimeSpentColumnAnalytic $averageTimeSpentColumnAnalytic
+ * @property \Kanboard\Core\Action\ActionManager $actionManager
+ * @property \Kanboard\Core\ExternalLink\ExternalLinkManager $externalLinkManager
+ * @property \Kanboard\Core\ExternalTask\ExternalTaskManager $externalTaskManager
+ * @property \Kanboard\Core\Cache\MemoryCache $memoryCache
+ * @property \Kanboard\Core\Cache\BaseCache $cacheDriver
+ * @property \Kanboard\Core\Event\EventManager $eventManager
+ * @property \Kanboard\Core\Group\GroupManager $groupManager
+ * @property \Kanboard\Core\Http\Client $httpClient
+ * @property \Kanboard\Core\Http\OAuth2 $oauth
+ * @property \Kanboard\Core\Http\RememberMeCookie $rememberMeCookie
+ * @property \Kanboard\Core\Http\Request $request
+ * @property \Kanboard\Core\Http\Response $response
+ * @property \Kanboard\Core\Http\Router $router
+ * @property \Kanboard\Core\Http\Route $route
+ * @property \Kanboard\Core\Queue\QueueManager $queueManager
+ * @property \Kanboard\Core\Mail\Client $emailClient
+ * @property \Kanboard\Core\ObjectStorage\ObjectStorageInterface $objectStorage
+ * @property \Kanboard\Core\Plugin\Hook $hook
+ * @property \Kanboard\Core\Plugin\Loader $pluginLoader
+ * @property \Kanboard\Core\Security\AuthenticationManager $authenticationManager
+ * @property \Kanboard\Core\Security\AccessMap $applicationAccessMap
+ * @property \Kanboard\Core\Security\AccessMap $projectAccessMap
+ * @property \Kanboard\Core\Security\AccessMap $apiAccessMap
+ * @property \Kanboard\Core\Security\AccessMap $apiProjectAccessMap
+ * @property \Kanboard\Core\Security\Authorization $applicationAuthorization
+ * @property \Kanboard\Core\Security\Authorization $projectAuthorization
+ * @property \Kanboard\Core\Security\Authorization $apiAuthorization
+ * @property \Kanboard\Core\Security\Authorization $apiProjectAuthorization
+ * @property \Kanboard\Core\Security\Role $role
+ * @property \Kanboard\Core\Security\Token $token
+ * @property \Kanboard\Core\Session\FlashMessage $flash
+ * @property \Kanboard\Core\Session\SessionManager $sessionManager
+ * @property \Kanboard\Core\Session\SessionStorage $sessionStorage
+ * @property \Kanboard\Core\User\Avatar\AvatarManager $avatarManager
+ * @property \Kanboard\Core\User\GroupSync $groupSync
+ * @property \Kanboard\Core\User\UserProfile $userProfile
+ * @property \Kanboard\Core\User\UserSync $userSync
+ * @property \Kanboard\Core\User\UserSession $userSession
+ * @property \Kanboard\Core\DateParser $dateParser
+ * @property \Kanboard\Core\Helper $helper
+ * @property \Kanboard\Core\Paginator $paginator
+ * @property \Kanboard\Core\Template $template
+ * @property \Kanboard\Decorator\MetadataCacheDecorator $userMetadataCacheDecorator
+ * @property \Kanboard\Decorator\UserCacheDecorator $userCacheDecorator
+ * @property \Kanboard\Decorator\ColumnRestrictionCacheDecorator $columnRestrictionCacheDecorator
+ * @property \Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator $columnMoveRestrictionCacheDecorator
+ * @property \Kanboard\Decorator\ProjectRoleRestrictionCacheDecorator $projectRoleRestrictionCacheDecorator
+ * @property \Kanboard\Formatter\BoardColumnFormatter $boardColumnFormatter
+ * @property \Kanboard\Formatter\BoardFormatter $boardFormatter
+ * @property \Kanboard\Formatter\BoardSwimlaneFormatter $boardSwimlaneFormatter
+ * @property \Kanboard\Formatter\BoardTaskFormatter $boardTaskFormatter
+ * @property \Kanboard\Formatter\GroupAutoCompleteFormatter $groupAutoCompleteFormatter
+ * @property \Kanboard\Formatter\ProjectActivityEventFormatter $projectActivityEventFormatter
+ * @property \Kanboard\Formatter\ProjectGanttFormatter $projectGanttFormatter
+ * @property \Kanboard\Formatter\SubtaskTimeTrackingCalendarFormatter $subtaskTimeTrackingCalendarFormatter
+ * @property \Kanboard\Formatter\TaskAutoCompleteFormatter $taskAutoCompleteFormatter
+ * @property \Kanboard\Formatter\TaskCalendarFormatter $taskCalendarFormatter
+ * @property \Kanboard\Formatter\TaskGanttFormatter $taskGanttFormatter
+ * @property \Kanboard\Formatter\TaskICalFormatter $taskICalFormatter
+ * @property \Kanboard\Formatter\TaskSuggestMenuFormatter $taskSuggestMenuFormatter
+ * @property \Kanboard\Formatter\UserAutoCompleteFormatter $userAutoCompleteFormatter
+ * @property \Kanboard\Formatter\UserMentionFormatter $userMentionFormatter
+ * @property \Kanboard\Model\ActionModel $actionModel
+ * @property \Kanboard\Model\ActionParameterModel $actionParameterModel
+ * @property \Kanboard\Model\AvatarFileModel $avatarFileModel
+ * @property \Kanboard\Model\BoardModel $boardModel
+ * @property \Kanboard\Model\CategoryModel $categoryModel
+ * @property \Kanboard\Model\ColorModel $colorModel
+ * @property \Kanboard\Model\ColumnModel $columnModel
+ * @property \Kanboard\Model\ColumnRestrictionModel $columnRestrictionModel
+ * @property \Kanboard\Model\ColumnMoveRestrictionModel $columnMoveRestrictionModel
+ * @property \Kanboard\Model\CommentModel $commentModel
+ * @property \Kanboard\Model\ConfigModel $configModel
+ * @property \Kanboard\Model\CurrencyModel $currencyModel
+ * @property \Kanboard\Model\CustomFilterModel $customFilterModel
+ * @property \Kanboard\Model\TaskFileModel $taskFileModel
+ * @property \Kanboard\Model\ProjectFileModel $projectFileModel
+ * @property \Kanboard\Model\GroupModel $groupModel
+ * @property \Kanboard\Model\GroupMemberModel $groupMemberModel
+ * @property \Kanboard\Model\InviteModel $inviteModel
+ * @property \Kanboard\Model\LanguageModel $languageModel
+ * @property \Kanboard\Model\LastLoginModel $lastLoginModel
+ * @property \Kanboard\Model\LinkModel $linkModel
+ * @property \Kanboard\Model\NotificationModel $notificationModel
+ * @property \Kanboard\Model\PasswordResetModel $passwordResetModel
+ * @property \Kanboard\Model\ProjectModel $projectModel
+ * @property \Kanboard\Model\ProjectActivityModel $projectActivityModel
+ * @property \Kanboard\Model\ProjectDuplicationModel $projectDuplicationModel
+ * @property \Kanboard\Model\ProjectDailyColumnStatsModel $projectDailyColumnStatsModel
+ * @property \Kanboard\Model\ProjectDailyStatsModel $projectDailyStatsModel
+ * @property \Kanboard\Model\ProjectMetadataModel $projectMetadataModel
+ * @property \Kanboard\Model\ProjectPermissionModel $projectPermissionModel
+ * @property \Kanboard\Model\ProjectUserRoleModel $projectUserRoleModel
+ * @property \Kanboard\Model\ProjectGroupRoleModel $projectGroupRoleModel
+ * @property \Kanboard\Model\ProjectNotificationModel $projectNotificationModel
+ * @property \Kanboard\Model\ProjectNotificationTypeModel $projectNotificationTypeModel
+ * @property \Kanboard\Model\ProjectRoleModel $projectRoleModel
+ * @property \Kanboard\Model\ProjectRoleRestrictionModel $projectRoleRestrictionModel
+ * @property \Kanboard\Model\ProjectTaskDuplicationModel $projectTaskDuplicationModel
+ * @property \Kanboard\Model\ProjectTaskPriorityModel $projectTaskPriorityModel
+ * @property \Kanboard\Model\RememberMeSessionModel $rememberMeSessionModel
+ * @property \Kanboard\Model\SubtaskModel $subtaskModel
+ * @property \Kanboard\Model\SubtaskPositionModel $subtaskPositionModel
+ * @property \Kanboard\Model\SubtaskStatusModel $subtaskStatusModel
+ * @property \Kanboard\Model\SubtaskTaskConversionModel $subtaskTaskConversionModel
+ * @property \Kanboard\Model\SubtaskTimeTrackingModel $subtaskTimeTrackingModel
+ * @property \Kanboard\Model\SwimlaneModel $swimlaneModel
+ * @property \Kanboard\Model\TagDuplicationModel $tagDuplicationModel
+ * @property \Kanboard\Model\TagModel $tagModel
+ * @property \Kanboard\Model\TaskModel $taskModel
+ * @property \Kanboard\Model\TaskAnalyticModel $taskAnalyticModel
+ * @property \Kanboard\Model\TaskCreationModel $taskCreationModel
+ * @property \Kanboard\Model\TaskDuplicationModel $taskDuplicationModel
+ * @property \Kanboard\Model\TaskProjectDuplicationModel $taskProjectDuplicationModel
+ * @property \Kanboard\Model\TaskProjectMoveModel $taskProjectMoveModel
+ * @property \Kanboard\Model\TaskRecurrenceModel $taskRecurrenceModel
+ * @property \Kanboard\Model\TaskExternalLinkModel $taskExternalLinkModel
+ * @property \Kanboard\Model\TaskFinderModel $taskFinderModel
+ * @property \Kanboard\Model\TaskLinkModel $taskLinkModel
+ * @property \Kanboard\Model\TaskModificationModel $taskModificationModel
+ * @property \Kanboard\Model\TaskPositionModel $taskPositionModel
+ * @property \Kanboard\Model\TaskStatusModel $taskStatusModel
+ * @property \Kanboard\Model\TaskTagModel $taskTagModel
+ * @property \Kanboard\Model\TaskMetadataModel $taskMetadataModel
+ * @property \Kanboard\Model\TimezoneModel $timezoneModel
+ * @property \Kanboard\Model\TransitionModel $transitionModel
+ * @property \Kanboard\Model\UserModel $userModel
+ * @property \Kanboard\Model\UserLockingModel $userLockingModel
+ * @property \Kanboard\Model\UserNotificationModel $userNotificationModel
+ * @property \Kanboard\Model\UserNotificationTypeModel $userNotificationTypeModel
+ * @property \Kanboard\Model\UserNotificationFilterModel $userNotificationFilterModel
+ * @property \Kanboard\Model\UserUnreadNotificationModel $userUnreadNotificationModel
+ * @property \Kanboard\Model\UserMetadataModel $userMetadataModel
+ * @property \Kanboard\Pagination\TaskPagination $taskPagination
+ * @property \Kanboard\Pagination\SubtaskPagination $subtaskPagination
+ * @property \Kanboard\Pagination\ProjectPagination $projectPagination
+ * @property \Kanboard\Pagination\UserPagination $userPagination
+ * @property \Kanboard\Validator\ActionValidator $actionValidator
+ * @property \Kanboard\Validator\AuthValidator $authValidator
+ * @property \Kanboard\Validator\ColumnValidator $columnValidator
+ * @property \Kanboard\Validator\CategoryValidator $categoryValidator
+ * @property \Kanboard\Validator\ColumnRestrictionValidator $columnRestrictionValidator
+ * @property \Kanboard\Validator\ColumnMoveRestrictionValidator $columnMoveRestrictionValidator
+ * @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\ProjectRoleValidator $projectRoleValidator
+ * @property \Kanboard\Validator\SubtaskValidator $subtaskValidator
+ * @property \Kanboard\Validator\SwimlaneValidator $swimlaneValidator
+ * @property \Kanboard\Validator\TagValidator $tagValidator
+ * @property \Kanboard\Validator\TaskLinkValidator $taskLinkValidator
+ * @property \Kanboard\Validator\TaskValidator $taskValidator
+ * @property \Kanboard\Validator\UserValidator $userValidator
+ * @property \Kanboard\Import\TaskImport $taskImport
+ * @property \Kanboard\Import\UserImport $userImport
+ * @property \Kanboard\Export\SubtaskExport $subtaskExport
+ * @property \Kanboard\Export\TaskExport $taskExport
+ * @property \Kanboard\Export\TransitionExport $transitionExport
+ * @property \Kanboard\Core\Filter\QueryBuilder $projectGroupRoleQuery
+ * @property \Kanboard\Core\Filter\QueryBuilder $projectUserRoleQuery
+ * @property \Kanboard\Core\Filter\QueryBuilder $projectActivityQuery
+ * @property \Kanboard\Core\Filter\QueryBuilder $userQuery
+ * @property \Kanboard\Core\Filter\QueryBuilder $projectQuery
+ * @property \Kanboard\Core\Filter\QueryBuilder $taskQuery
+ * @property \Kanboard\Core\Filter\LexerBuilder $taskLexer
+ * @property \Kanboard\Core\Filter\LexerBuilder $projectActivityLexer
+ * @property \Kanboard\Job\CommentEventJob $commentEventJob
+ * @property \Kanboard\Job\SubtaskEventJob $subtaskEventJob
+ * @property \Kanboard\Job\TaskEventJob $taskEventJob
+ * @property \Kanboard\Job\TaskFileEventJob $taskFileEventJob
+ * @property \Kanboard\Job\TaskLinkEventJob $taskLinkEventJob
+ * @property \Kanboard\Job\ProjectFileEventJob $projectFileEventJob
+ * @property \Kanboard\Job\NotificationJob $notificationJob
+ * @property \Kanboard\Job\ProjectMetricJob $projectMetricJob
+ * @property \Kanboard\Job\UserMentionJob $userMentionJob
+ * @property \Psr\Log\LoggerInterface $logger
+ * @property \PicoDb\Database $db
+ * @property \Symfony\Component\EventDispatcher\EventDispatcher $dispatcher
+ * @property \Symfony\Component\Console\Application $cli
+ * @property \JsonRPC\Server $api
*/
abstract class Base
{
@@ -178,10 +223,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)
@@ -199,7 +244,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/Cache/Base.php b/app/Core/Cache/BaseCache.php
index d62b8507..b51c4c0c 100644
--- a/app/Core/Cache/Base.php
+++ b/app/Core/Cache/BaseCache.php
@@ -3,12 +3,12 @@
namespace Kanboard\Core\Cache;
/**
- * Base class for cache drivers
+ * Base Class for Cache Drivers
*
- * @package cache
+ * @package Kanboard\Core\Cache
* @author Frederic Guillot
*/
-abstract class Base
+abstract class BaseCache implements CacheInterface
{
/**
* Proxy cache
diff --git a/app/Core/Cache/CacheInterface.php b/app/Core/Cache/CacheInterface.php
index d9e9747a..033732cf 100644
--- a/app/Core/Cache/CacheInterface.php
+++ b/app/Core/Cache/CacheInterface.php
@@ -3,24 +3,24 @@
namespace Kanboard\Core\Cache;
/**
- * Cache Interface
+ * Interface CacheInterface
*
- * @package cache
- * @author Frederic Guillot
+ * @package Kanboard\Core\Cache
+ * @author Frederic Guillot
*/
interface CacheInterface
{
/**
- * Save a new value in the cache
+ * Store an item in the cache
*
* @access public
* @param string $key
- * @param string $value
+ * @param mixed $value
*/
public function set($key, $value);
/**
- * Fetch value from cache
+ * Retrieve an item from the cache by key
*
* @access public
* @param string $key
@@ -29,14 +29,14 @@ interface CacheInterface
public function get($key);
/**
- * Clear all cache
+ * Remove all items from the cache
*
* @access public
*/
public function flush();
/**
- * Remove cached value
+ * Remove an item from the cache
*
* @access public
* @param string $key
diff --git a/app/Core/Cache/FileCache.php b/app/Core/Cache/FileCache.php
new file mode 100644
index 00000000..1e0a60ef
--- /dev/null
+++ b/app/Core/Cache/FileCache.php
@@ -0,0 +1,98 @@
+<?php
+
+namespace Kanboard\Core\Cache;
+
+use Kanboard\Core\Tool;
+use LogicException;
+
+/**
+ * Class FileCache
+ *
+ * @package Kanboard\Core\Cache
+ */
+class FileCache extends BaseCache
+{
+ /**
+ * Store an item in the cache
+ *
+ * @access public
+ * @param string $key
+ * @param mixed $value
+ */
+ public function set($key, $value)
+ {
+ $this->createCacheFolder();
+ file_put_contents($this->getFilenameFromKey($key), serialize($value));
+ }
+
+ /**
+ * Retrieve an item from the cache by key
+ *
+ * @access public
+ * @param string $key
+ * @return mixed Null when not found, cached value otherwise
+ */
+ public function get($key)
+ {
+ $filename = $this->getFilenameFromKey($key);
+
+ if (file_exists($filename)) {
+ return unserialize(file_get_contents($filename));
+ }
+
+ return null;
+ }
+
+ /**
+ * Remove all items from the cache
+ *
+ * @access public
+ */
+ public function flush()
+ {
+ $this->createCacheFolder();
+ Tool::removeAllFiles(CACHE_DIR, false);
+ }
+
+ /**
+ * Remove an item from the cache
+ *
+ * @access public
+ * @param string $key
+ */
+ public function remove($key)
+ {
+ $filename = $this->getFilenameFromKey($key);
+
+ if (file_exists($filename)) {
+ unlink($filename);
+ }
+ }
+
+ /**
+ * Get absolute filename from the key
+ *
+ * @access protected
+ * @param string $key
+ * @return string
+ */
+ protected function getFilenameFromKey($key)
+ {
+ return CACHE_DIR.DIRECTORY_SEPARATOR.$key;
+ }
+
+ /**
+ * Create cache folder if missing
+ *
+ * @access protected
+ * @throws LogicException
+ */
+ protected function createCacheFolder()
+ {
+ if (! is_dir(CACHE_DIR)) {
+ if (! mkdir(CACHE_DIR, 0755)) {
+ throw new LogicException('Unable to create cache directory: '.CACHE_DIR);
+ }
+ }
+ }
+}
diff --git a/app/Core/Cache/MemoryCache.php b/app/Core/Cache/MemoryCache.php
index 39e3947b..4fb94728 100644
--- a/app/Core/Cache/MemoryCache.php
+++ b/app/Core/Cache/MemoryCache.php
@@ -3,12 +3,12 @@
namespace Kanboard\Core\Cache;
/**
- * Memory Cache
+ * Memory Cache Driver
*
- * @package cache
+ * @package Kanboard\Core\Cache
* @author Frederic Guillot
*/
-class MemoryCache extends Base implements CacheInterface
+class MemoryCache extends BaseCache
{
/**
* Container
@@ -19,7 +19,7 @@ class MemoryCache extends Base implements CacheInterface
private $storage = array();
/**
- * Save a new value in the cache
+ * Store an item in the cache
*
* @access public
* @param string $key
@@ -31,7 +31,7 @@ class MemoryCache extends Base implements CacheInterface
}
/**
- * Fetch value from cache
+ * Retrieve an item from the cache by key
*
* @access public
* @param string $key
diff --git a/app/Core/Controller/Runner.php b/app/Core/Controller/Runner.php
index 8353cf69..48346390 100644
--- a/app/Core/Controller/Runner.php
+++ b/app/Core/Controller/Runner.php
@@ -35,7 +35,7 @@ class Runner extends Base
$controllerObject->notFound($e->hasLayout());
} catch (AccessForbiddenException $e) {
$controllerObject = new AppController($this->container);
- $controllerObject->accessForbidden($e->hasLayout());
+ $controllerObject->accessForbidden($e->hasLayout(), $e->getMessage());
}
}
diff --git a/app/Core/DateParser.php b/app/Core/DateParser.php
index a7b10a7a..9d012d12 100644
--- a/app/Core/DateParser.php
+++ b/app/Core/DateParser.php
@@ -13,7 +13,6 @@ use DateTime;
class DateParser extends Base
{
const DATE_FORMAT = 'm/d/Y';
- const DATE_TIME_FORMAT = 'm/d/Y H:i';
const TIME_FORMAT = 'H:i';
/**
@@ -35,7 +34,7 @@ class DateParser extends Base
*/
public function getUserDateTimeFormat()
{
- return $this->configModel->get('application_datetime_format', DateParser::DATE_TIME_FORMAT);
+ return $this->getUserDateFormat().' '.$this->getUserTimeFormat();
}
/**
@@ -239,23 +238,11 @@ class DateParser extends Base
*/
public function getHours(DateTime $d1, DateTime $d2)
{
- $seconds = $this->getRoundedSeconds(abs($d1->getTimestamp() - $d2->getTimestamp()));
+ $seconds = abs($d1->getTimestamp() - $d2->getTimestamp());
return round($seconds / 3600, 2);
}
/**
- * Round the timestamp to the nearest quarter
- *
- * @access public
- * @param integer $seconds Timestamp
- * @return integer
- */
- public function getRoundedSeconds($seconds)
- {
- return (int) round($seconds / (15 * 60)) * (15 * 60);
- }
-
- /**
* Get ISO-8601 date from user input
*
* @access public
@@ -304,7 +291,9 @@ class DateParser extends Base
{
foreach ($fields as $field) {
if (! empty($values[$field])) {
- $values[$field] = date($format, $values[$field]);
+ if (ctype_digit($values[$field])) {
+ $values[$field] = date($format, $values[$field]);
+ }
} else {
$values[$field] = '';
}
diff --git a/app/Core/Event/EventManager.php b/app/Core/Event/EventManager.php
index 9ae43170..48a9d299 100644
--- a/app/Core/Event/EventManager.php
+++ b/app/Core/Event/EventManager.php
@@ -53,6 +53,7 @@ class EventManager
TaskModel::EVENT_CREATE_UPDATE => t('Task creation or modification'),
TaskModel::EVENT_ASSIGNEE_CHANGE => t('Task assignee change'),
TaskModel::EVENT_DAILY_CRONJOB => t('Daily background job for tasks'),
+ TaskModel::EVENT_MOVE_SWIMLANE => t('Move a task to another swimlane'),
);
$events = array_merge($events, $this->events);
diff --git a/app/Core/ExternalTask/AccessForbiddenException.php b/app/Core/ExternalTask/AccessForbiddenException.php
new file mode 100644
index 00000000..2b5ebd33
--- /dev/null
+++ b/app/Core/ExternalTask/AccessForbiddenException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Kanboard\Core\ExternalTask;
+
+/**
+ * Class AccessForbiddenException
+ *
+ * @package Kanboard\Core\ExternalTask
+ * @author Frederic Guillot
+ */
+class AccessForbiddenException extends ExternalTaskException
+{
+}
diff --git a/app/Core/ExternalTask/ExternalTaskException.php b/app/Core/ExternalTask/ExternalTaskException.php
new file mode 100644
index 00000000..07e5665d
--- /dev/null
+++ b/app/Core/ExternalTask/ExternalTaskException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Kanboard\Core\ExternalTask;
+
+use Exception;
+
+/**
+ * Class NotFoundException
+ *
+ * @package Kanboard\Core\ExternalTask
+ * @author Frederic Guillot
+ */
+class ExternalTaskException extends Exception
+{
+}
diff --git a/app/Core/ExternalTask/ExternalTaskInterface.php b/app/Core/ExternalTask/ExternalTaskInterface.php
new file mode 100644
index 00000000..084af509
--- /dev/null
+++ b/app/Core/ExternalTask/ExternalTaskInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+namespace Kanboard\Core\ExternalTask;
+
+/**
+ * Interface ExternalTaskInterface
+ *
+ * @package Kanboard\Core\ExternalTask
+ * @author Frederic Guillot
+ */
+interface ExternalTaskInterface
+{
+ /**
+ * Return Uniform Resource Identifier for the task
+ *
+ * @return string
+ */
+ public function getUri();
+
+ /**
+ * Return a dict to populate the task form
+ *
+ * @return array
+ */
+ public function getFormValues();
+}
diff --git a/app/Core/ExternalTask/ExternalTaskManager.php b/app/Core/ExternalTask/ExternalTaskManager.php
new file mode 100644
index 00000000..102ec459
--- /dev/null
+++ b/app/Core/ExternalTask/ExternalTaskManager.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Kanboard\Core\ExternalTask;
+
+/**
+ * Class ExternalTaskManager
+ *
+ * @package Kanboard\Core\ExternalTask
+ * @author Frederic Guillot
+ */
+class ExternalTaskManager
+{
+ protected $providers = array();
+
+ /**
+ * Register a new task provider
+ *
+ * @param ExternalTaskProviderInterface $externalTaskProvider
+ * @return $this
+ */
+ public function register(ExternalTaskProviderInterface $externalTaskProvider)
+ {
+ $this->providers[$externalTaskProvider->getName()] = $externalTaskProvider;
+ return $this;
+ }
+
+ /**
+ * Get task provider
+ *
+ * @param string $name
+ * @return ExternalTaskProviderInterface|null
+ * @throws ProviderNotFoundException
+ */
+ public function getProvider($name)
+ {
+ if (isset($this->providers[$name])) {
+ return $this->providers[$name];
+ }
+
+ throw new ProviderNotFoundException('Unable to load this provider: '.$name);
+ }
+
+ /**
+ * Get list of task providers
+ *
+ * @return array
+ */
+ public function getProvidersList()
+ {
+ $providers = array_keys($this->providers);
+
+ if (count($providers)) {
+ return array_combine($providers, $providers);
+ }
+
+ return array();
+ }
+}
diff --git a/app/Core/ExternalTask/ExternalTaskProviderInterface.php b/app/Core/ExternalTask/ExternalTaskProviderInterface.php
new file mode 100644
index 00000000..f67f7552
--- /dev/null
+++ b/app/Core/ExternalTask/ExternalTaskProviderInterface.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Kanboard\Core\ExternalTask;
+
+/**
+ * Interface ExternalTaskProviderInterface
+ *
+ * @package Kanboard\Core\ExternalTask
+ * @author Frederic Guillot
+ */
+interface ExternalTaskProviderInterface
+{
+ /**
+ * Get provider name (visible in the user interface)
+ *
+ * @access public
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Retrieve task from external system or cache
+ *
+ * @access public
+ * @throws \Kanboard\Core\ExternalTask\ExternalTaskException
+ * @param string $uri
+ * @return ExternalTaskInterface
+ */
+ public function fetch($uri);
+
+ /**
+ * Save external task to another system
+ *
+ * @throws \Kanboard\Core\ExternalTask\ExternalTaskException
+ * @param string $uri
+ * @param array $formValues
+ * @param array $formErrors
+ * @return bool
+ */
+ public function save($uri, array $formValues, array &$formErrors);
+
+ /**
+ * Get task import template name
+ *
+ * @return string
+ */
+ public function getImportFormTemplate();
+
+ /**
+ * Get creation form template
+ *
+ * @return string
+ */
+ public function getCreationFormTemplate();
+
+ /**
+ * Get modification form template
+ *
+ * @return string
+ */
+ public function getModificationFormTemplate();
+
+ /**
+ * Get task view template name
+ *
+ * @return string
+ */
+ public function getViewTemplate();
+
+ /**
+ * Build external task URI based on import form values
+ *
+ * @param array $formValues
+ * @return string
+ */
+ public function buildTaskUri(array $formValues);
+}
diff --git a/app/Core/ExternalTask/NotFoundException.php b/app/Core/ExternalTask/NotFoundException.php
new file mode 100644
index 00000000..34eff8ea
--- /dev/null
+++ b/app/Core/ExternalTask/NotFoundException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Kanboard\Core\ExternalTask;
+
+/**
+ * Class NotFoundException
+ *
+ * @package Kanboard\Core\ExternalTask
+ * @author Frederic Guillot
+ */
+class NotFoundException extends ExternalTaskException
+{
+}
diff --git a/app/Core/ExternalTask/ProviderNotFoundException.php b/app/Core/ExternalTask/ProviderNotFoundException.php
new file mode 100644
index 00000000..6ad1fae1
--- /dev/null
+++ b/app/Core/ExternalTask/ProviderNotFoundException.php
@@ -0,0 +1,13 @@
+<?php
+
+namespace Kanboard\Core\ExternalTask;
+
+/**
+ * Class ProviderNotFoundException
+ *
+ * @package Kanboard\Core\ExternalTask
+ * @author Frederic Guillot
+ */
+class ProviderNotFoundException extends ExternalTaskException
+{
+}
diff --git a/app/Core/Filter/FormatterInterface.php b/app/Core/Filter/FormatterInterface.php
index b7c04c51..0ff84976 100644
--- a/app/Core/Filter/FormatterInterface.php
+++ b/app/Core/Filter/FormatterInterface.php
@@ -17,7 +17,7 @@ interface FormatterInterface
*
* @access public
* @param Table $query
- * @return FormatterInterface
+ * @return $this
*/
public function withQuery(Table $query);
diff --git a/app/Core/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/Helper.php b/app/Core/Helper.php
index 43151be8..ab7c3b7b 100644
--- a/app/Core/Helper.php
+++ b/app/Core/Helper.php
@@ -12,12 +12,15 @@ use Pimple\Container;
*
* @property \Kanboard\Helper\AppHelper $app
* @property \Kanboard\Helper\AssetHelper $asset
+ * @property \Kanboard\Helper\AvatarHelper $avatar
+ * @property \Kanboard\Helper\BoardHelper $board
* @property \Kanboard\Helper\CalendarHelper $calendar
* @property \Kanboard\Helper\DateHelper $dt
* @property \Kanboard\Helper\FileHelper $file
* @property \Kanboard\Helper\FormHelper $form
* @property \Kanboard\Helper\HookHelper $hook
* @property \Kanboard\Helper\ICalHelper $ical
+ * @property \Kanboard\Helper\ModalHelper $modal
* @property \Kanboard\Helper\ModelHelper $model
* @property \Kanboard\Helper\SubtaskHelper $subtask
* @property \Kanboard\Helper\TaskHelper $task
@@ -25,6 +28,7 @@ use Pimple\Container;
* @property \Kanboard\Helper\UrlHelper $url
* @property \Kanboard\Helper\UserHelper $user
* @property \Kanboard\Helper\LayoutHelper $layout
+ * @property \Kanboard\Helper\ProjectRoleHelper $projectRole
* @property \Kanboard\Helper\ProjectHeaderHelper $projectHeader
* @property \Kanboard\Helper\ProjectActivityHelper $projectActivity
* @property \Kanboard\Helper\MailHelper $mail
diff --git a/app/Core/Http/Request.php b/app/Core/Http/Request.php
index e0df2d3c..44bfdbe6 100644
--- a/app/Core/Http/Request.php
+++ b/app/Core/Http/Request.php
@@ -105,7 +105,7 @@ class Request extends Base
{
if (! empty($this->post) && ! empty($this->post['csrf_token']) && $this->token->validateCSRFToken($this->post['csrf_token'])) {
unset($this->post['csrf_token']);
- return $this->post;
+ return $this->filterValues($this->post);
}
return array();
@@ -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',
@@ -343,4 +344,17 @@ class Request extends Base
{
return isset($this->server[$variable]) ? $this->server[$variable] : '';
}
+
+ protected function filterValues(array $values)
+ {
+ foreach ($values as $key => $value) {
+
+ // IE11 Workaround when submitting multipart/form-data
+ if (strpos($key, '-----------------------------') === 0) {
+ unset($values[$key]);
+ }
+ }
+
+ return $values;
+ }
}
diff --git a/app/Core/Http/Response.php b/app/Core/Http/Response.php
index 0f16fb65..0af763a6 100644
--- a/app/Core/Http/Response.php
+++ b/app/Core/Http/Response.php
@@ -129,6 +129,18 @@ class Response extends Base
}
/**
+ * Add P3P headers for Internet Explorer
+ *
+ * @access public
+ * @return $this
+ */
+ public function withP3P()
+ {
+ $this->withHeader('P3P', 'CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');
+ return $this;
+ }
+
+ /**
* Set HTTP response body
*
* @access public
diff --git a/app/Core/Mail/Transport/Mail.php b/app/Core/Mail/Transport/Mail.php
index d27925f0..c99cc8ba 100644
--- a/app/Core/Mail/Transport/Mail.php
+++ b/app/Core/Mail/Transport/Mail.php
@@ -33,8 +33,8 @@ class Mail extends Base implements ClientInterface
$message = Swift_Message::newInstance()
->setSubject($subject)
->setFrom(array($this->helper->mail->getMailSenderAddress() => $author))
- ->setBody($html, 'text/html')
- ->setTo(array($email => $name));
+ ->setTo(array($email => $name))
+ ->setBody($html, 'text/html');
Swift_Mailer::newInstance($this->getTransport())->send($message);
} catch (Swift_TransportException $e) {
diff --git a/app/Core/Mail/Transport/Smtp.php b/app/Core/Mail/Transport/Smtp.php
index 66f0a3aa..815dde5d 100644
--- a/app/Core/Mail/Transport/Smtp.php
+++ b/app/Core/Mail/Transport/Smtp.php
@@ -25,6 +25,16 @@ class Smtp extends Mail
$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/Markdown.php b/app/Core/Markdown.php
index b5abe5ed..4487bf2a 100644
--- a/app/Core/Markdown.php
+++ b/app/Core/Markdown.php
@@ -86,18 +86,23 @@ class Markdown extends Parsedown
*/
protected function inlineUserLink(array $Excerpt)
{
- if (! $this->isPublicLink && preg_match('/^@([^\s]+)/', $Excerpt['text'], $matches)) {
- $user_id = $this->container['userModel']->getIdByUsername($matches[1]);
+ if (! $this->isPublicLink && preg_match('/^@([^\s,!:?]+)/', $Excerpt['text'], $matches)) {
+ $username = rtrim($matches[1], '.');
+ $user = $this->container['userCacheDecorator']->getByUsername($username);
- if (! empty($user_id)) {
- $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user_id));
+ if (! empty($user)) {
+ $url = $this->container['helper']->url->href('UserViewController', 'profile', array('user_id' => $user['id']));
return array(
- 'extent' => strlen($matches[0]),
+ 'extent' => strlen($username) + 1,
'element' => array(
- 'name' => 'a',
- 'text' => $matches[0],
- 'attributes' => array('href' => $url, 'class' => 'user-mention-link'),
+ 'name' => 'a',
+ 'text' => '@' . $username,
+ 'attributes' => array(
+ 'href' => $url,
+ 'class' => 'user-mention-link',
+ 'title' => $user['name'] ?: $user['username'],
+ ),
),
);
}
@@ -125,7 +130,10 @@ class Markdown extends Parsedown
array(
'token' => $token,
'task_id' => $task_id,
- )
+ ),
+ false,
+ '',
+ true
);
}
diff --git a/app/Core/Paginator.php b/app/Core/Paginator.php
index cfe89938..9075a713 100644
--- a/app/Core/Paginator.php
+++ b/app/Core/Paginator.php
@@ -232,6 +232,17 @@ class Paginator
}
/**
+ * Get the number of current page
+ *
+ * @access public
+ * @return integer
+ */
+ public function getPage()
+ {
+ return $this->page;
+ }
+
+ /**
* Set the default column order
*
* @access public
@@ -271,6 +282,16 @@ class Paginator
}
/**
+ * Get the maximum number of items per page.
+ *
+ * @return int
+ */
+ public function getMax()
+ {
+ return $this->limit;
+ }
+
+ /**
* Return true if the collection is empty
*
* @access public
@@ -353,7 +374,9 @@ class Paginator
'&larr; '.t('Previous'),
$this->controller,
$this->action,
- $this->getUrlParams($this->page - 1, $this->order, $this->direction)
+ $this->getUrlParams($this->page - 1, $this->order, $this->direction),
+ false,
+ 'js-modal-replace'
);
} else {
$html .= '&larr; '.t('Previous');
@@ -379,7 +402,9 @@ class Paginator
t('Next').' &rarr;',
$this->controller,
$this->action,
- $this->getUrlParams($this->page + 1, $this->order, $this->direction)
+ $this->getUrlParams($this->page + 1, $this->order, $this->direction),
+ false,
+ 'js-modal-replace'
);
} else {
$html .= t('Next').' &rarr;';
@@ -391,6 +416,17 @@ class Paginator
}
/**
+ * Generate the page showing.
+ *
+ * @access public
+ * @return string
+ */
+ public function generatPageShowing()
+ {
+ return '<span class="pagination-showing">'.t('Showing %d-%d of %d', (($this->getPage() - 1) * $this->getMax() + 1), min($this->getTotal(), $this->getPage() * $this->getMax()), $this->getTotal()).'</span>';
+ }
+
+ /**
* Return true if there is no pagination to show
*
* @access public
@@ -413,6 +449,7 @@ class Paginator
if (! $this->hasNothingtoShow()) {
$html .= '<div class="pagination">';
+ $html .= $this->generatPageShowing();
$html .= $this->generatePreviousLink();
$html .= $this->generateNextLink();
$html .= '</div>';
@@ -453,7 +490,9 @@ class Paginator
$label,
$this->controller,
$this->action,
- $this->getUrlParams($this->page, $column, $direction)
+ $this->getUrlParams($this->page, $column, $direction),
+ false,
+ 'js-modal-replace'
);
}
}
diff --git a/app/Core/Plugin/Base.php b/app/Core/Plugin/Base.php
index 9d8167a9..e0b5954a 100644
--- a/app/Core/Plugin/Base.php
+++ b/app/Core/Plugin/Base.php
@@ -131,4 +131,17 @@ abstract class Base extends \Kanboard\Core\Base
{
return '';
}
+
+ /**
+ * Get application compatibility version
+ *
+ * Examples: >=1.0.36, 1.0.37, APP_VERSION
+ *
+ * @access public
+ * @return string
+ */
+ public function getCompatibleVersion()
+ {
+ return APP_VERSION;
+ }
}
diff --git a/app/Core/Plugin/Directory.php b/app/Core/Plugin/Directory.php
index 21f11ca9..dc32e655 100644
--- a/app/Core/Plugin/Directory.php
+++ b/app/Core/Plugin/Directory.php
@@ -36,11 +36,7 @@ class Directory extends BaseCore
*/
public function isCompatible(array $plugin, $appVersion = APP_VERSION)
{
- if (strpos($appVersion, 'master') !== false) {
- return true;
- }
-
- return $plugin['compatible_version'] === $appVersion;
+ return Version::isCompatible($plugin['compatible_version'], $appVersion);
}
/**
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/Plugin/Installer.php b/app/Core/Plugin/Installer.php
index 48c4d978..b3618aeb 100644
--- a/app/Core/Plugin/Installer.php
+++ b/app/Core/Plugin/Installer.php
@@ -2,9 +2,8 @@
namespace Kanboard\Core\Plugin;
-use RecursiveDirectoryIterator;
-use RecursiveIteratorIterator;
use ZipArchive;
+use Kanboard\Core\Tool;
/**
* Class Installer
@@ -64,7 +63,7 @@ class Installer extends \Kanboard\Core\Base
throw new PluginInstallerException(e('You don\'t have the permission to remove this plugin.'));
}
- $this->removeAllDirectories($pluginFolder);
+ Tool::removeAllFiles($pluginFolder);
}
/**
@@ -137,26 +136,4 @@ class Installer extends \Kanboard\Core\Base
unlink($zip->filename);
$zip->close();
}
-
- /**
- * Remove recursively a directory
- *
- * @access protected
- * @param string $directory
- */
- protected function removeAllDirectories($directory)
- {
- $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS);
- $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
-
- foreach ($files as $file) {
- if ($file->isDir()) {
- rmdir($file->getRealPath());
- } else {
- unlink($file->getRealPath());
- }
- }
-
- rmdir($directory);
- }
}
diff --git a/app/Core/Plugin/Loader.php b/app/Core/Plugin/Loader.php
index f2f6add7..38f41d39 100644
--- a/app/Core/Plugin/Loader.php
+++ b/app/Core/Plugin/Loader.php
@@ -4,6 +4,7 @@ namespace Kanboard\Core\Plugin;
use Composer\Autoload\ClassLoader;
use DirectoryIterator;
+use Exception;
use LogicException;
use Kanboard\Core\Tool;
@@ -22,6 +23,7 @@ class Loader extends \Kanboard\Core\Base
* @var array
*/
protected $plugins = array();
+ protected $incompatiblePlugins = array();
/**
* Get list of loaded plugins
@@ -35,6 +37,17 @@ class Loader extends \Kanboard\Core\Base
}
/**
+ * Get list of not compatible plugins
+ *
+ * @access public
+ * @return Base[]
+ */
+ public function getIncompatiblePlugins()
+ {
+ return $this->incompatiblePlugins;
+ }
+
+ /**
* Scan plugin folder and load plugins
*
* @access public
@@ -51,8 +64,7 @@ class Loader extends \Kanboard\Core\Base
foreach ($dir as $fileInfo) {
if ($fileInfo->isDir() && substr($fileInfo->getFilename(), 0, 1) !== '.') {
$pluginName = $fileInfo->getFilename();
- $this->loadSchema($pluginName);
- $this->initializePlugin($pluginName, $this->loadPlugin($pluginName));
+ $this->initializePlugin($pluginName);
}
}
}
@@ -85,7 +97,7 @@ class Loader extends \Kanboard\Core\Base
$className = '\Kanboard\Plugin\\'.$pluginName.'\\Plugin';
if (! class_exists($className)) {
- throw new LogicException('Unable to load this plugin class '.$className);
+ throw new LogicException('Unable to load this plugin class: '.$className);
}
return new $className($this->container);
@@ -96,18 +108,30 @@ class Loader extends \Kanboard\Core\Base
*
* @access public
* @param string $pluginName
- * @param Base $plugin
*/
- public function initializePlugin($pluginName, Base $plugin)
+ public function initializePlugin($pluginName)
{
- if (method_exists($plugin, 'onStartup')) {
- $this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup'));
- }
+ try {
+ $plugin = $this->loadPlugin($pluginName);
- Tool::buildDIC($this->container, $plugin->getClasses());
- Tool::buildDICHelpers($this->container, $plugin->getHelpers());
+ if (Version::isCompatible($plugin->getCompatibleVersion(), APP_VERSION)) {
+ $this->loadSchema($pluginName);
+
+ if (method_exists($plugin, 'onStartup')) {
+ $this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup'));
+ }
- $plugin->initialize();
- $this->plugins[$pluginName] = $plugin;
+ Tool::buildDIC($this->container, $plugin->getClasses());
+ Tool::buildDICHelpers($this->container, $plugin->getHelpers());
+
+ $plugin->initialize();
+ $this->plugins[$pluginName] = $plugin;
+ } else {
+ $this->incompatiblePlugins[$pluginName] = $plugin;
+ $this->logger->error($pluginName.' is not compatible with this version');
+ }
+ } catch (Exception $e) {
+ $this->logger->critical($pluginName.': '.$e->getMessage());
+ }
}
}
diff --git a/app/Core/Plugin/PluginException.php b/app/Core/Plugin/PluginException.php
new file mode 100644
index 00000000..fae7de35
--- /dev/null
+++ b/app/Core/Plugin/PluginException.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Kanboard\Core\Plugin;
+
+use Exception;
+
+/**
+ * Class PluginException
+ *
+ * @package Kanboard\Core\Plugin
+ * @author Frederic Guillot
+ */
+class PluginException extends Exception
+{
+}
diff --git a/app/Core/Plugin/PluginInstallerException.php b/app/Core/Plugin/PluginInstallerException.php
index 7d356c9b..31745f22 100644
--- a/app/Core/Plugin/PluginInstallerException.php
+++ b/app/Core/Plugin/PluginInstallerException.php
@@ -2,14 +2,12 @@
namespace Kanboard\Core\Plugin;
-use Exception;
-
/**
* Class PluginInstallerException
*
* @package Kanboard\Core\Plugin
* @author Frederic Guillot
*/
-class PluginInstallerException extends Exception
+class PluginInstallerException extends PluginException
{
}
diff --git a/app/Core/Plugin/Version.php b/app/Core/Plugin/Version.php
new file mode 100644
index 00000000..ba5e0443
--- /dev/null
+++ b/app/Core/Plugin/Version.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Kanboard\Core\Plugin;
+
+/**
+ * Class Version
+ *
+ * @package Kanboard\Core\Plugin
+ * @author Frederic Guillot
+ */
+class Version
+{
+ /**
+ * Check plugin version compatibility with application version
+ *
+ * @param string $pluginCompatibleVersion
+ * @param string $appVersion
+ * @return bool
+ */
+ public static function isCompatible($pluginCompatibleVersion, $appVersion = APP_VERSION)
+ {
+ if (strpos($appVersion, 'master') !== false) {
+ return true;
+ }
+
+ $appVersion = str_replace('v', '', $appVersion);
+ $pluginCompatibleVersion = str_replace('v', '', $pluginCompatibleVersion);
+
+ foreach (array('>=', '>', '<=', '<') as $operator) {
+ if (strpos($pluginCompatibleVersion, $operator) === 0) {
+ $pluginVersion = substr($pluginCompatibleVersion, strlen($operator));
+ return version_compare($appVersion, $pluginVersion, $operator);
+ }
+ }
+
+ return $pluginCompatibleVersion === $appVersion;
+ }
+}
diff --git a/app/Core/Queue/JobHandler.php b/app/Core/Queue/JobHandler.php
index 7ca36328..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,16 +40,23 @@ class JobHandler extends Base
public function executeJob(Job $job)
{
$payload = $job->getBody();
- $className = $payload['class'];
- $this->memoryCache->flush();
- $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));
+ }
}
/**
@@ -67,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..1d7c2d1e 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 or unable to connect to broker!');
}
while ($job = $this->queue->pull()) {
diff --git a/app/Core/Security/Role.php b/app/Core/Security/Role.php
index cb45a8af..c16d4094 100644
--- a/app/Core/Security/Role.php
+++ b/app/Core/Security/Role.php
@@ -50,6 +50,18 @@ class Role
}
/**
+ * Check if the given role is custom or not
+ *
+ * @access public
+ * @param string $role
+ * @return bool
+ */
+ public function isCustomProjectRole($role)
+ {
+ return ! empty($role) && $role !== self::PROJECT_MANAGER && $role !== self::PROJECT_MEMBER && $role !== self::PROJECT_VIEWER;
+ }
+
+ /**
* Get role name
*
* @access public
diff --git a/app/Core/Session/SessionStorage.php b/app/Core/Session/SessionStorage.php
index 9e93602c..e6478d8d 100644
--- a/app/Core/Session/SessionStorage.php
+++ b/app/Core/Session/SessionStorage.php
@@ -19,6 +19,7 @@ namespace Kanboard\Core\Session;
* @property bool $hasSubtaskInProgress
* @property bool $hasRememberMe
* @property bool $boardCollapsed
+ * @property string $scope
* @property bool $twoFactorBeforeCodeCalled
* @property string $twoFactorSecret
* @property string $oauthState
diff --git a/app/Core/Tool.php b/app/Core/Tool.php
index bfa6c955..6e457641 100644
--- a/app/Core/Tool.php
+++ b/app/Core/Tool.php
@@ -3,6 +3,8 @@
namespace Kanboard\Core;
use Pimple\Container;
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
/**
* Tool class
@@ -13,7 +15,33 @@ use Pimple\Container;
class Tool
{
/**
- * Build dependency injection container from an array
+ * Remove recursively a directory
+ *
+ * @static
+ * @access public
+ * @param string $directory
+ * @param bool $removeDirectory
+ */
+ public static function removeAllFiles($directory, $removeDirectory = true)
+ {
+ $it = new RecursiveDirectoryIterator($directory, RecursiveDirectoryIterator::SKIP_DOTS);
+ $files = new RecursiveIteratorIterator($it, RecursiveIteratorIterator::CHILD_FIRST);
+
+ foreach ($files as $file) {
+ if ($file->isDir()) {
+ rmdir($file->getRealPath());
+ } else {
+ unlink($file->getRealPath());
+ }
+ }
+
+ if ($removeDirectory) {
+ rmdir($directory);
+ }
+ }
+
+ /**
+ * Build dependency injection containers from an array
*
* @static
* @access public
@@ -36,6 +64,29 @@ class Tool
}
/**
+ * Build dependency injection container from an array
+ *
+ * @static
+ * @access public
+ * @param Container $container
+ * @param array $namespaces
+ * @return Container
+ */
+ public static function buildFactories(Container $container, array $namespaces)
+ {
+ foreach ($namespaces as $namespace => $classes) {
+ foreach ($classes as $name) {
+ $class = '\\Kanboard\\'.$namespace.'\\'.$name;
+ $container[lcfirst($name)] = $container->factory(function ($c) use ($class) {
+ return new $class($c);
+ });
+ }
+ }
+
+ return $container;
+ }
+
+ /**
* Build dependency injection container for custom helpers from an array
*
* @static
diff --git a/app/Core/Translator.php b/app/Core/Translator.php
index 113c0dc6..ac2e2aae 100644
--- a/app/Core/Translator.php
+++ b/app/Core/Translator.php
@@ -11,13 +11,6 @@ namespace Kanboard\Core;
class Translator
{
/**
- * Locale path
- *
- * @var string
- */
- const PATH = 'app/Locale';
-
- /**
* Locale
*
* @static
@@ -171,9 +164,13 @@ class Translator
* @param string $language Locale code: fr_FR
* @param string $path Locale folder
*/
- public static function load($language, $path = self::PATH)
+ public static function load($language, $path = '')
{
- $filename = $path.DIRECTORY_SEPARATOR.$language.DIRECTORY_SEPARATOR.'translations.php';
+ if ($path === '') {
+ $path = self::getDefaultFolder();
+ }
+
+ $filename = implode(DIRECTORY_SEPARATOR, array($path, $language, 'translations.php'));
if (file_exists($filename)) {
self::$locales = array_merge(self::$locales, require($filename));
@@ -190,4 +187,15 @@ class Translator
{
self::$locales = array();
}
+
+ /**
+ * Get default locales folder
+ *
+ * @access public
+ * @return string
+ */
+ public static function getDefaultFolder()
+ {
+ return implode(DIRECTORY_SEPARATOR, array(__DIR__, '..', 'Locale'));
+ }
}
diff --git a/app/Core/User/UserSession.php b/app/Core/User/UserSession.php
index 9c63f07a..7917b223 100644
--- a/app/Core/User/UserSession.php
+++ b/app/Core/User/UserSession.php
@@ -179,50 +179,4 @@ class UserSession extends Base
{
$this->sessionStorage->filters[$project_id] = $filters;
}
-
- /**
- * Is board collapsed or expanded
- *
- * @access public
- * @param integer $project_id
- * @return boolean
- */
- public function isBoardCollapsed($project_id)
- {
- return ! empty($this->sessionStorage->boardCollapsed[$project_id]) ? $this->sessionStorage->boardCollapsed[$project_id] : false;
- }
-
- /**
- * Set board display mode
- *
- * @access public
- * @param integer $project_id
- * @param boolean $is_collapsed
- */
- public function setBoardDisplayMode($project_id, $is_collapsed)
- {
- $this->sessionStorage->boardCollapsed[$project_id] = $is_collapsed;
- }
-
- /**
- * Set comments sorting
- *
- * @access public
- * @param string $order
- */
- public function setCommentSorting($order)
- {
- $this->sessionStorage->commentSorting = $order;
- }
-
- /**
- * Get comments sorting direction
- *
- * @access public
- * @return string
- */
- public function getCommentSorting()
- {
- return empty($this->sessionStorage->commentSorting) ? 'ASC' : $this->sessionStorage->commentSorting;
- }
}
diff --git a/app/Decorator/ColumnMoveRestrictionCacheDecorator.php b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php
new file mode 100644
index 00000000..82140d16
--- /dev/null
+++ b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Kanboard\Decorator;
+
+use Kanboard\Core\Cache\CacheInterface;
+use Kanboard\Model\ColumnMoveRestrictionModel;
+
+/**
+ * Class ColumnMoveRestrictionCacheDecorator
+ *
+ * @package Kanboard\Decorator
+ * @author Frederic Guillot
+ */
+class ColumnMoveRestrictionCacheDecorator
+{
+ protected $cachePrefix = 'column_move_restriction:';
+
+ /**
+ * @var CacheInterface
+ */
+ protected $cache;
+
+ /**
+ * @var ColumnMoveRestrictionModel
+ */
+ protected $columnMoveRestrictionModel;
+
+ /**
+ * ColumnMoveRestrictionDecorator constructor.
+ *
+ * @param CacheInterface $cache
+ * @param ColumnMoveRestrictionModel $columnMoveRestrictionModel
+ */
+ public function __construct(CacheInterface $cache, ColumnMoveRestrictionModel $columnMoveRestrictionModel)
+ {
+ $this->cache = $cache;
+ $this->columnMoveRestrictionModel = $columnMoveRestrictionModel;
+ }
+
+ /**
+ * Proxy method to get sortable columns
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return array|mixed
+ */
+ public function getSortableColumns($project_id, $role)
+ {
+ $key = $this->cachePrefix.$project_id.$role;
+ $columnIds = $this->cache->get($key);
+
+ if ($columnIds === null) {
+ $columnIds = $this->columnMoveRestrictionModel->getSortableColumns($project_id, $role);
+ $this->cache->set($key, $columnIds);
+ }
+
+ return $columnIds;
+ }
+}
diff --git a/app/Decorator/ColumnRestrictionCacheDecorator.php b/app/Decorator/ColumnRestrictionCacheDecorator.php
new file mode 100644
index 00000000..a615030d
--- /dev/null
+++ b/app/Decorator/ColumnRestrictionCacheDecorator.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Kanboard\Decorator;
+
+use Kanboard\Core\Cache\CacheInterface;
+use Kanboard\Model\ColumnRestrictionModel;
+
+/**
+ * Class ColumnRestrictionCacheDecorator
+ *
+ * @package Kanboard\Decorator
+ * @author Frederic Guillot
+ */
+class ColumnRestrictionCacheDecorator
+{
+ protected $cachePrefix = 'column_restriction:';
+
+ /**
+ * @var CacheInterface
+ */
+ protected $cache;
+
+ /**
+ * @var ColumnRestrictionModel
+ */
+ protected $columnRestrictionModel;
+
+ /**
+ * ColumnMoveRestrictionDecorator constructor.
+ *
+ * @param CacheInterface $cache
+ * @param ColumnRestrictionModel $columnMoveRestrictionModel
+ */
+ public function __construct(CacheInterface $cache, ColumnRestrictionModel $columnMoveRestrictionModel)
+ {
+ $this->cache = $cache;
+ $this->columnRestrictionModel = $columnMoveRestrictionModel;
+ }
+
+ /**
+ * Proxy method to get sortable columns
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return array|mixed
+ */
+ public function getAllByRole($project_id, $role)
+ {
+ $key = $this->cachePrefix.$project_id.$role;
+ $columnRestrictions = $this->cache->get($key);
+
+ if ($columnRestrictions === null) {
+ $columnRestrictions = $this->columnRestrictionModel->getAllByRole($project_id, $role);
+ $this->cache->set($key, $columnRestrictions);
+ }
+
+ return $columnRestrictions;
+ }
+}
diff --git a/app/Decorator/MetadataCacheDecorator.php b/app/Decorator/MetadataCacheDecorator.php
new file mode 100644
index 00000000..0897b51c
--- /dev/null
+++ b/app/Decorator/MetadataCacheDecorator.php
@@ -0,0 +1,96 @@
+<?php
+
+namespace Kanboard\Decorator;
+
+use Kanboard\Core\Cache\CacheInterface;
+use Kanboard\Model\MetadataModel;
+
+/**
+ * Class MetadataCacheDecorator
+ *
+ * @package Kanboard\Decorator
+ * @author Frederic Guillot
+ */
+class MetadataCacheDecorator
+{
+ /**
+ * @var CacheInterface
+ */
+ protected $cache;
+
+ /**
+ * @var MetadataModel
+ */
+ protected $metadataModel;
+
+ /**
+ * @var string
+ */
+ protected $cachePrefix;
+
+ /**
+ * @var int
+ */
+ protected $entityId;
+
+ /**
+ * Constructor
+ *
+ * @param CacheInterface $cache
+ * @param MetadataModel $metadataModel
+ * @param string $cachePrefix
+ * @param integer $entityId
+ */
+ public function __construct(CacheInterface $cache, MetadataModel $metadataModel, $cachePrefix, $entityId)
+ {
+ $this->cache = $cache;
+ $this->metadataModel = $metadataModel;
+ $this->cachePrefix = $cachePrefix;
+ $this->entityId = $entityId;
+ }
+
+ /**
+ * Get metadata value by key
+ *
+ * @param string $key
+ * @param mixed $default
+ * @return mixed
+ */
+ public function get($key, $default)
+ {
+ $metadata = $this->cache->get($this->getCacheKey());
+
+ if ($metadata === null) {
+ $metadata = $this->metadataModel->getAll($this->entityId);
+ $this->cache->set($this->getCacheKey(), $metadata);
+ }
+
+ return isset($metadata[$key]) ? $metadata[$key] : $default;
+ }
+
+ /**
+ * Set new metadata value
+ *
+ * @param $key
+ * @param $value
+ */
+ public function set($key, $value)
+ {
+ $this->metadataModel->save($this->entityId, array(
+ $key => $value,
+ ));
+
+ $metadata = $this->metadataModel->getAll($this->entityId);
+ $this->cache->set($this->getCacheKey(), $metadata);
+ }
+
+ /**
+ * Get cache key
+ *
+ * @return string
+ */
+ protected function getCacheKey()
+ {
+ return $this->cachePrefix.$this->entityId;
+ }
+}
diff --git a/app/Decorator/ProjectRoleRestrictionCacheDecorator.php b/app/Decorator/ProjectRoleRestrictionCacheDecorator.php
new file mode 100644
index 00000000..a6e24048
--- /dev/null
+++ b/app/Decorator/ProjectRoleRestrictionCacheDecorator.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Kanboard\Decorator;
+
+use Kanboard\Core\Cache\CacheInterface;
+use Kanboard\Model\ProjectRoleRestrictionModel;
+
+/**
+ * Class ProjectRoleRestrictionCacheDecorator
+ *
+ * @package Kanboard\Decorator
+ * @author Frederic Guillot
+ */
+class ProjectRoleRestrictionCacheDecorator
+{
+ protected $cachePrefix = 'project_restriction:';
+
+ /**
+ * @var CacheInterface
+ */
+ protected $cache;
+
+ /**
+ * @var ProjectRoleRestrictionModel
+ */
+ protected $projectRoleRestrictionModel;
+
+ /**
+ * ColumnMoveRestrictionDecorator constructor.
+ *
+ * @param CacheInterface $cache
+ * @param ProjectRoleRestrictionModel $projectRoleRestrictionModel
+ */
+ public function __construct(CacheInterface $cache, ProjectRoleRestrictionModel $projectRoleRestrictionModel)
+ {
+ $this->cache = $cache;
+ $this->projectRoleRestrictionModel = $projectRoleRestrictionModel;
+ }
+
+ /**
+ * Proxy method to get sortable columns
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return array|mixed
+ */
+ public function getAllByRole($project_id, $role)
+ {
+ $key = $this->cachePrefix.$project_id.$role;
+ $projectRestrictions = $this->cache->get($key);
+
+ if ($projectRestrictions === null) {
+ $projectRestrictions = $this->projectRoleRestrictionModel->getAllByRole($project_id, $role);
+ $this->cache->set($key, $projectRestrictions);
+ }
+
+ return $projectRestrictions;
+ }
+}
diff --git a/app/Decorator/UserCacheDecorator.php b/app/Decorator/UserCacheDecorator.php
new file mode 100644
index 00000000..1cfe31c9
--- /dev/null
+++ b/app/Decorator/UserCacheDecorator.php
@@ -0,0 +1,59 @@
+<?php
+
+namespace Kanboard\Decorator;
+
+use Kanboard\Core\Cache\CacheInterface;
+use Kanboard\Model\UserModel;
+
+/**
+ * Class UserCacheDecorator
+ *
+ * @package Kanboard\Decorator
+ * @author Frederic Guillot
+ */
+class UserCacheDecorator
+{
+ protected $cachePrefix = 'user_model:';
+
+ /**
+ * @var CacheInterface
+ */
+ protected $cache;
+
+ /**
+ * @var UserModel
+ */
+ private $userModel;
+
+ /**
+ * UserCacheDecorator constructor.
+ *
+ * @param CacheInterface $cache
+ * @param UserModel $userModel
+ */
+ public function __construct(CacheInterface $cache, UserModel $userModel)
+ {
+ $this->cache = $cache;
+ $this->userModel = $userModel;
+ }
+
+ /**
+ * Get a specific user by the username
+ *
+ * @access public
+ * @param string $username Username
+ * @return array
+ */
+ public function getByUsername($username)
+ {
+ $key = $this->cachePrefix.$username;
+ $user = $this->cache->get($key);
+
+ if ($user === null) {
+ $user = $this->userModel->getByUsername($username);
+ $this->cache->set($key, $user);
+ }
+
+ return $user;
+ }
+}
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/GenericEvent.php b/app/Event/GenericEvent.php
index 94a51479..e87d9481 100644
--- a/app/Event/GenericEvent.php
+++ b/app/Event/GenericEvent.php
@@ -14,6 +14,28 @@ class GenericEvent extends BaseEvent implements ArrayAccess
$this->container = $values;
}
+ public function getTaskId()
+ {
+ if (isset($this->container['task']['id'])) {
+ return $this->container['task']['id'];
+ }
+
+ if (isset($this->container['task_id'])) {
+ return $this->container['task_id'];
+ }
+
+ return null;
+ }
+
+ public function getProjectId()
+ {
+ if (isset($this->container['task']['project_id'])) {
+ return $this->container['task']['project_id'];
+ }
+
+ return null;
+ }
+
public function getAll()
{
return $this->container;
diff --git a/app/Event/ProjectFileEvent.php b/app/Event/ProjectFileEvent.php
new file mode 100644
index 00000000..e1d29c48
--- /dev/null
+++ b/app/Event/ProjectFileEvent.php
@@ -0,0 +1,15 @@
+<?php
+
+namespace Kanboard\Event;
+
+class ProjectFileEvent extends GenericEvent
+{
+ public function getProjectId()
+ {
+ if (isset($this->container['file']['project_id'])) {
+ return $this->container['file']['project_id'];
+ }
+
+ return null;
+ }
+}
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/Export/TaskExport.php b/app/Export/TaskExport.php
index 0e576d33..9ca5f798 100644
--- a/app/Export/TaskExport.php
+++ b/app/Export/TaskExport.php
@@ -3,9 +3,12 @@
namespace Kanboard\Export;
use Kanboard\Core\Base;
-use Kanboard\Core\DateParser;
+use Kanboard\Model\CategoryModel;
+use Kanboard\Model\ColumnModel;
+use Kanboard\Model\ProjectModel;
+use Kanboard\Model\SwimlaneModel;
use Kanboard\Model\TaskModel;
-use PDO;
+use Kanboard\Model\UserModel;
/**
* Task Export
@@ -19,19 +22,21 @@ class TaskExport extends Base
* Fetch tasks and return the prepared CSV
*
* @access public
- * @param integer $project_id Project id
- * @param mixed $from Start date (timestamp or user formatted date)
- * @param mixed $to End date (timestamp or user formatted date)
+ * @param integer $project_id Project id
+ * @param mixed $from Start date (timestamp or user formatted date)
+ * @param mixed $to End date (timestamp or user formatted date)
* @return array
*/
public function export($project_id, $from, $to)
{
$tasks = $this->getTasks($project_id, $from, $to);
- $swimlanes = $this->swimlaneModel->getList($project_id);
+ $colors = $this->colorModel->getList();
+ $defaultSwimlane = $this->swimlaneModel->getDefault($project_id);
$results = array($this->getColumns());
foreach ($tasks as &$task) {
- $results[] = array_values($this->format($task, $swimlanes));
+ $task = $this->format($task, $defaultSwimlane['default_swimlane'], $colors);
+ $results[] = array_values($task);
}
return $results;
@@ -40,76 +45,81 @@ class TaskExport extends Base
/**
* Get the list of tasks for a given project and date range
*
- * @access public
- * @param integer $project_id Project id
- * @param mixed $from Start date (timestamp or user formatted date)
- * @param mixed $to End date (timestamp or user formatted date)
+ * @access protected
+ * @param integer $project_id Project id
+ * @param mixed $from Start date (timestamp or user formatted date)
+ * @param mixed $to End date (timestamp or user formatted date)
* @return array
*/
- public function getTasks($project_id, $from, $to)
+ protected function getTasks($project_id, $from, $to)
{
- $sql = '
- SELECT
- tasks.id,
- projects.name AS project_name,
- tasks.is_active,
- project_has_categories.name AS category_name,
- tasks.swimlane_id,
- columns.title AS column_title,
- tasks.position,
- tasks.color_id,
- tasks.date_due,
- creators.username AS creator_username,
- users.username AS assignee_username,
- users.name AS assignee_name,
- tasks.score,
- tasks.title,
- tasks.date_creation,
- tasks.date_modification,
- tasks.date_completed,
- tasks.date_started,
- tasks.time_estimated,
- tasks.time_spent
- 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 columns ON columns.id = tasks.column_id
- LEFT JOIN projects ON projects.id = tasks.project_id
- WHERE tasks.date_creation >= ? AND tasks.date_creation <= ? AND tasks.project_id = ?
- ORDER BY tasks.id ASC
- ';
-
- if (! is_numeric($from)) {
+ if (!is_numeric($from)) {
$from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from));
}
- if (! is_numeric($to)) {
+ if (!is_numeric($to)) {
$to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to)));
}
- $rq = $this->db->execute($sql, array($from, $to, $project_id));
- return $rq->fetchAll(PDO::FETCH_ASSOC);
+ return $this->db->table(TaskModel::TABLE)
+ ->columns(
+ TaskModel::TABLE . '.id',
+ TaskModel::TABLE . '.reference',
+ ProjectModel::TABLE . '.name AS project_name',
+ TaskModel::TABLE . '.is_active',
+ CategoryModel::TABLE . '.name AS category_name',
+ SwimlaneModel::TABLE . '.name AS swimlane_name',
+ ColumnModel::TABLE . '.title AS column_title',
+ TaskModel::TABLE . '.position',
+ TaskModel::TABLE . '.color_id',
+ TaskModel::TABLE . '.date_due',
+ 'uc.username AS creator_username',
+ 'uc.name AS creator_name',
+ UserModel::TABLE . '.username AS assignee_username',
+ UserModel::TABLE . '.name AS assignee_name',
+ TaskModel::TABLE . '.score',
+ TaskModel::TABLE . '.title',
+ TaskModel::TABLE . '.date_creation',
+ TaskModel::TABLE . '.date_modification',
+ TaskModel::TABLE . '.date_completed',
+ TaskModel::TABLE . '.date_started',
+ TaskModel::TABLE . '.time_estimated',
+ TaskModel::TABLE . '.time_spent'
+ )
+ ->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)
+ ->gte(TaskModel::TABLE . '.date_creation', $from)
+ ->lte(TaskModel::TABLE . '.date_creation', $to)
+ ->eq(TaskModel::TABLE . '.project_id', $project_id)
+ ->asc(TaskModel::TABLE.'.id')
+ ->findAll();
}
/**
* Format the output of a task array
*
- * @access public
- * @param array $task Task properties
- * @param array $swimlanes List of swimlanes
+ * @access protected
+ * @param array $task
+ * @param string $defaultSwimlaneName
+ * @param array $colors
* @return array
*/
- public function format(array &$task, array &$swimlanes)
+ protected function format(array &$task, $defaultSwimlaneName, array $colors)
{
- $colors = $this->colorModel->getList();
-
$task['is_active'] = $task['is_active'] == TaskModel::STATUS_OPEN ? e('Open') : e('Closed');
$task['color_id'] = $colors[$task['color_id']];
$task['score'] = $task['score'] ?: 0;
- $task['swimlane_id'] = isset($swimlanes[$task['swimlane_id']]) ? $swimlanes[$task['swimlane_id']] : '?';
+ $task['swimlane_name'] = $task['swimlane_name'] ?: $defaultSwimlaneName;
- $task = $this->dateParser->format($task, array('date_due', 'date_modification', 'date_creation', 'date_started', 'date_completed'), DateParser::DATE_FORMAT);
+ $task = $this->dateParser->format(
+ $task,
+ array('date_due', 'date_modification', 'date_creation', 'date_started', 'date_completed'),
+ $this->dateParser->getUserDateTimeFormat()
+ );
return $task;
}
@@ -117,13 +127,14 @@ class TaskExport extends Base
/**
* Get column titles
*
- * @access public
+ * @access protected
* @return string[]
*/
- public function getColumns()
+ protected function getColumns()
{
return array(
e('Task Id'),
+ e('Reference'),
e('Project'),
e('Status'),
e('Category'),
@@ -133,6 +144,7 @@ class TaskExport extends Base
e('Color'),
e('Due date'),
e('Creator'),
+ e('Creator Name'),
e('Assignee Username'),
e('Assignee Name'),
e('Complexity'),
diff --git a/app/ExternalLink/FileLinkProvider.php b/app/ExternalLink/FileLinkProvider.php
index 901f78f8..eb8c1084 100644
--- a/app/ExternalLink/FileLinkProvider.php
+++ b/app/ExternalLink/FileLinkProvider.php
@@ -12,6 +12,11 @@ use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface;
*/
class FileLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface
{
+ protected $excludedPrefixes= array(
+ 'http',
+ 'ftp',
+ );
+
/**
* Get provider name
*
@@ -55,7 +60,17 @@ class FileLinkProvider extends BaseLinkProvider implements ExternalLinkProviderI
*/
public function match()
{
- return strpos($this->userInput, 'file://') === 0;
+ if (strpos($this->userInput, '://') === false) {
+ return false;
+ }
+
+ foreach ($this->excludedPrefixes as $prefix) {
+ if (strpos($this->userInput, $prefix) === 0) {
+ return false;
+ }
+ }
+
+ return true;
}
/**
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/TaskStartsWithIdFilter.php b/app/Filter/TaskStartsWithIdFilter.php
new file mode 100644
index 00000000..8b7cc678
--- /dev/null
+++ b/app/Filter/TaskStartsWithIdFilter.php
@@ -0,0 +1,38 @@
+<?php
+
+namespace Kanboard\Filter;
+
+use Kanboard\Core\Filter\FilterInterface;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskIdSearchFilter
+ *
+ * @package Kanboard\Filter
+ * @author Frederic Guillot
+ */
+class TaskStartsWithIdFilter extends BaseFilter implements FilterInterface
+{
+ /**
+ * Get search attribute
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getAttributes()
+ {
+ return array('starts_with_id');
+ }
+
+ /**
+ * Apply filter
+ *
+ * @access public
+ * @return FilterInterface
+ */
+ public function apply()
+ {
+ $this->query->ilike('CAST('.TaskModel::TABLE.'.id AS CHAR(8))', $this->value.'%');
+ return $this;
+ }
+}
diff --git a/app/Filter/TaskStatusFilter.php b/app/Filter/TaskStatusFilter.php
index a55532cb..791ebce6 100644
--- a/app/Filter/TaskStatusFilter.php
+++ b/app/Filter/TaskStatusFilter.php
@@ -34,7 +34,7 @@ class TaskStatusFilter extends BaseFilter implements FilterInterface
{
if ($this->value === 'open' || $this->value === 'closed') {
$this->query->eq(TaskModel::TABLE.'.is_active', $this->value === 'open' ? TaskModel::STATUS_OPEN : TaskModel::STATUS_CLOSED);
- } else {
+ } elseif (is_int($this->value) || ctype_digit($this->value)) {
$this->query->eq(TaskModel::TABLE.'.is_active', $this->value);
}
diff --git a/app/Filter/TaskTagFilter.php b/app/Filter/TaskTagFilter.php
index 01b6f625..98be5554 100644
--- a/app/Filter/TaskTagFilter.php
+++ b/app/Filter/TaskTagFilter.php
@@ -56,12 +56,11 @@ class TaskTagFilter extends BaseFilter implements 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 ($this->value === 'none') {
+ $task_ids = $this->getTaskIdsWithoutTags();
+ } else {
+ $task_ids = $this->getTaskIdsWithGivenTag();
+ }
if (empty($task_ids)) {
$task_ids = array(-1);
@@ -71,4 +70,24 @@ class TaskTagFilter extends BaseFilter implements FilterInterface
return $this;
}
+
+ protected function getTaskIdsWithoutTags()
+ {
+ return $this->db
+ ->table(TaskModel::TABLE)
+ ->asc(TaskModel::TABLE . '.project_id')
+ ->left(TaskTagModel::TABLE, 'tg', 'task_id', TaskModel::TABLE, 'id')
+ ->isNull('tg.tag_id')
+ ->findAllByColumn(TaskModel::TABLE . '.id');
+ }
+
+ protected function getTaskIdsWithGivenTag()
+ {
+ return $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');
+ }
}
diff --git a/app/Formatter/BaseFormatter.php b/app/Formatter/BaseFormatter.php
index 89c48437..0d62628e 100644
--- a/app/Formatter/BaseFormatter.php
+++ b/app/Formatter/BaseFormatter.php
@@ -4,7 +4,6 @@ namespace Kanboard\Formatter;
use Kanboard\Core\Base;
use PicoDb\Table;
-use Pimple\Container;
/**
* Class BaseFormatter
@@ -23,19 +22,6 @@ 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
diff --git a/app/Formatter/BaseTaskCalendarFormatter.php b/app/Formatter/BaseTaskCalendarFormatter.php
index 8fab3e9a..3d9ead4d 100644
--- a/app/Formatter/BaseTaskCalendarFormatter.php
+++ b/app/Formatter/BaseTaskCalendarFormatter.php
@@ -2,8 +2,6 @@
namespace Kanboard\Formatter;
-use Kanboard\Core\Filter\FormatterInterface;
-
/**
* Common class to handle calendar events
*
@@ -34,7 +32,7 @@ abstract class BaseTaskCalendarFormatter extends BaseFormatter
* @access public
* @param string $start_column Column name for the start date
* @param string $end_column Column name for the end date
- * @return FormatterInterface
+ * @return $this
*/
public function setColumns($start_column, $end_column = '')
{
diff --git a/app/Formatter/BoardColumnFormatter.php b/app/Formatter/BoardColumnFormatter.php
index d49a577a..0d59f54e 100644
--- a/app/Formatter/BoardColumnFormatter.php
+++ b/app/Formatter/BoardColumnFormatter.php
@@ -78,7 +78,8 @@ class BoardColumnFormatter extends BaseFormatter implements FormatterInterface
public function format()
{
foreach ($this->columns as &$column) {
- $column['tasks'] = BoardTaskFormatter::getInstance($this->container)
+ $column['id'] = (int) $column['id'];
+ $column['tasks'] = $this->boardTaskFormatter
->withTasks($this->tasks)
->withTags($this->tags)
->withSwimlaneId($this->swimlaneId)
diff --git a/app/Formatter/BoardFormatter.php b/app/Formatter/BoardFormatter.php
index 350dde6c..3f47bfa9 100644
--- a/app/Formatter/BoardFormatter.php
+++ b/app/Formatter/BoardFormatter.php
@@ -44,6 +44,13 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
{
$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')
@@ -52,11 +59,7 @@ class BoardFormatter extends BaseFormatter implements FormatterInterface
$task_ids = array_column($tasks, 'id');
$tags = $this->taskTagModel->getTagsByTasks($task_ids);
- if (empty($swimlanes) || empty($columns)) {
- return array();
- }
-
- return BoardSwimlaneFormatter::getInstance($this->container)
+ return $this->boardSwimlaneFormatter
->withSwimlanes($swimlanes)
->withColumns($columns)
->withTasks($tasks)
diff --git a/app/Formatter/BoardSwimlaneFormatter.php b/app/Formatter/BoardSwimlaneFormatter.php
index c2abb444..18db259d 100644
--- a/app/Formatter/BoardSwimlaneFormatter.php
+++ b/app/Formatter/BoardSwimlaneFormatter.php
@@ -24,7 +24,7 @@ class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface
* @param array $swimlanes
* @return $this
*/
- public function withSwimlanes($swimlanes)
+ public function withSwimlanes(array $swimlanes)
{
$this->swimlanes = $swimlanes;
return $this;
@@ -37,7 +37,7 @@ class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface
* @param array $columns
* @return $this
*/
- public function withColumns($columns)
+ public function withColumns(array $columns)
{
$this->columns = $columns;
return $this;
@@ -81,7 +81,8 @@ class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface
$nb_columns = count($this->columns);
foreach ($this->swimlanes as &$swimlane) {
- $swimlane['columns'] = BoardColumnFormatter::getInstance($this->container)
+ $swimlane['id'] = (int) $swimlane['id'];
+ $swimlane['columns'] = $this->boardColumnFormatter
->withSwimlaneId($swimlane['id'])
->withColumns($this->columns)
->withTasks($this->tasks)
diff --git a/app/Formatter/BoardTaskFormatter.php b/app/Formatter/BoardTaskFormatter.php
index 3bf171b1..cd10f77a 100644
--- a/app/Formatter/BoardTaskFormatter.php
+++ b/app/Formatter/BoardTaskFormatter.php
@@ -79,6 +79,11 @@ class BoardTaskFormatter extends BaseFormatter implements FormatterInterface
{
$tasks = array_values(array_filter($this->tasks, array($this, 'filterTasks')));
array_merge_relation($tasks, $this->tags, 'tags', 'id');
+
+ foreach ($tasks as &$task) {
+ $task['is_draggable'] = $this->helper->projectRole->isDraggable($task);
+ }
+
return $tasks;
}
diff --git a/app/Formatter/GroupAutoCompleteFormatter.php b/app/Formatter/GroupAutoCompleteFormatter.php
index 4d552886..d811de7f 100644
--- a/app/Formatter/GroupAutoCompleteFormatter.php
+++ b/app/Formatter/GroupAutoCompleteFormatter.php
@@ -12,36 +12,26 @@ use PicoDb\Table;
* @package formatter
* @author Frederic Guillot
*/
-class GroupAutoCompleteFormatter implements FormatterInterface
+class GroupAutoCompleteFormatter extends BaseFormatter implements FormatterInterface
{
/**
* Groups found
*
- * @access private
+ * @access protected
* @var GroupProviderInterface[]
*/
- private $groups;
+ protected $groups;
/**
- * Format groups for the ajax auto-completion
+ * Set groups
*
* @access public
* @param GroupProviderInterface[] $groups
+ * @return $this
*/
- public function __construct(array $groups)
+ public function withGroups(array $groups)
{
$this->groups = $groups;
- }
-
- /**
- * Set query
- *
- * @access public
- * @param Table $query
- * @return FormatterInterface
- */
- public function withQuery(Table $query)
- {
return $this;
}
diff --git a/app/Formatter/TaskAutoCompleteFormatter.php b/app/Formatter/TaskAutoCompleteFormatter.php
index 4f1c4c69..3a4f1e1a 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;
/**
@@ -13,6 +14,20 @@ use Kanboard\Model\TaskModel;
*/
class TaskAutoCompleteFormatter extends BaseFormatter implements FormatterInterface
{
+ protected $limit = 25;
+
+ /**
+ * Limit number of results
+ *
+ * @param $limit
+ * @return $this
+ */
+ public function withLimit($limit)
+ {
+ $this->limit = $limit;
+ return $this;
+ }
+
/**
* Apply formatter
*
@@ -21,11 +36,19 @@ 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')
+ ->limit($this->limit)
+ ->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/TaskSuggestMenuFormatter.php b/app/Formatter/TaskSuggestMenuFormatter.php
new file mode 100644
index 00000000..518f99e6
--- /dev/null
+++ b/app/Formatter/TaskSuggestMenuFormatter.php
@@ -0,0 +1,63 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+use Kanboard\Core\Filter\FormatterInterface;
+use Kanboard\Model\ProjectModel;
+use Kanboard\Model\TaskModel;
+
+/**
+ * Class TaskSuggestMenuFormatter
+ *
+ * @package Kanboard\Formatter
+ * @author Frederic Guillot
+ */
+class TaskSuggestMenuFormatter extends BaseFormatter implements FormatterInterface
+{
+ protected $limit = 25;
+
+ /**
+ * Limit number of results
+ *
+ * @param $limit
+ * @return $this
+ */
+ public function withLimit($limit)
+ {
+ $this->limit = $limit;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return mixed
+ */
+ public function format()
+ {
+ $result = array();
+ $tasks = $this->query
+ ->columns(
+ TaskModel::TABLE.'.id',
+ TaskModel::TABLE.'.title',
+ ProjectModel::TABLE.'.name AS project_name'
+ )
+ ->asc(TaskModel::TABLE.'.id')
+ ->limit($this->limit)
+ ->findAll();
+
+ foreach ($tasks as $task) {
+ $html = '#'.$task['id'].' ';
+ $html .= $this->helper->text->e($task['title']).' ';
+ $html .= '<small>'.$this->helper->text->e($task['project_name']).'</small>';
+
+ $result[] = array(
+ 'value' => (string) $task['id'],
+ 'html' => $html,
+ );
+ }
+
+ return $result;
+ }
+}
diff --git a/app/Formatter/UserAutoCompleteFormatter.php b/app/Formatter/UserAutoCompleteFormatter.php
index cd23a2a4..c81af00a 100644
--- a/app/Formatter/UserAutoCompleteFormatter.php
+++ b/app/Formatter/UserAutoCompleteFormatter.php
@@ -14,7 +14,7 @@ use Kanboard\Core\Filter\FormatterInterface;
class UserAutoCompleteFormatter extends BaseFormatter implements FormatterInterface
{
/**
- * Format the tasks for the ajax autocompletion
+ * Format the tasks for the ajax auto-completion
*
* @access public
* @return array
@@ -24,11 +24,11 @@ class UserAutoCompleteFormatter extends BaseFormatter implements FormatterInterf
$users = $this->query->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name')->findAll();
foreach ($users as &$user) {
- $user['value'] = $user['username'].' (#'.$user['id'].')';
-
if (empty($user['name'])) {
+ $user['value'] = $user['username'].' (#'.$user['id'].')';
$user['label'] = $user['username'];
} else {
+ $user['value'] = $user['name'].' (#'.$user['id'].')';
$user['label'] = $user['name'].' ('.$user['username'].')';
}
}
diff --git a/app/Formatter/UserMentionFormatter.php b/app/Formatter/UserMentionFormatter.php
new file mode 100644
index 00000000..395fc463
--- /dev/null
+++ b/app/Formatter/UserMentionFormatter.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace Kanboard\Formatter;
+
+/**
+ * Class UserMentionFormatter
+ *
+ * @package Kanboard\Formatter
+ * @author Frederic Guillot
+ */
+class UserMentionFormatter extends BaseFormatter
+{
+ protected $users = array();
+
+ /**
+ * Set users
+ *
+ * @param array $users
+ * @return $this
+ */
+ public function withUsers(array $users) {
+ $this->users = $users;
+ return $this;
+ }
+
+ /**
+ * Apply formatter
+ *
+ * @access public
+ * @return array
+ */
+ public function format()
+ {
+ $result = array();
+
+ foreach ($this->users as $user) {
+ $html = $this->helper->avatar->small(
+ $user['id'],
+ $user['username'],
+ $user['name'],
+ $user['email'],
+ $user['avatar_path'],
+ 'avatar-inline'
+ );
+
+ $html .= ' '.$this->helper->text->e($user['username']);
+
+ if (! empty($user['name'])) {
+ $html .= ' <small>'.$this->helper->text->e($user['name']).'</small>';
+ }
+
+ $result[] = array(
+ 'value' => $user['username'],
+ 'html' => $html,
+ );
+ }
+
+ return $result;
+ }
+} \ No newline at end of file
diff --git a/app/Helper/AppHelper.php b/app/Helper/AppHelper.php
index 09f280cb..3b48d7d3 100644
--- a/app/Helper/AppHelper.php
+++ b/app/Helper/AppHelper.php
@@ -13,16 +13,28 @@ use Kanboard\Core\Base;
class AppHelper extends Base
{
/**
+ * Render Javascript component
+ *
+ * @param string $name
+ * @param array $params
+ * @return string
+ */
+ public function component($name, array $params = array())
+ {
+ return '<div class="js-'.$name.'" data-params=\''.json_encode($params, JSON_HEX_APOS).'\'></div>';
+ }
+
+ /**
* Get config variable
*
* @access public
* @param string $param
- * @param mixed $default_value
+ * @param mixed $default
* @return mixed
*/
- public function config($param, $default_value = '')
+ public function config($param, $default = '')
{
- return $this->configModel->get($param, $default_value);
+ return $this->configModel->get($param, $default);
}
/**
diff --git a/app/Helper/BoardHelper.php b/app/Helper/BoardHelper.php
index a86a6c18..f5df3db2 100644
--- a/app/Helper/BoardHelper.php
+++ b/app/Helper/BoardHelper.php
@@ -3,6 +3,7 @@
namespace Kanboard\Helper;
use Kanboard\Core\Base;
+use Kanboard\Model\UserMetadataModel;
/**
* Board Helper
@@ -21,6 +22,6 @@ class BoardHelper extends Base
*/
public function isCollapsed($project_id)
{
- return $this->userSession->isBoardCollapsed($project_id);
+ return $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, 0) == 1;
}
}
diff --git a/app/Helper/CalendarHelper.php b/app/Helper/CalendarHelper.php
index b35c40f7..0942177d 100644
--- a/app/Helper/CalendarHelper.php
+++ b/app/Helper/CalendarHelper.php
@@ -5,8 +5,6 @@ namespace Kanboard\Helper;
use Kanboard\Core\Base;
use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\TaskDueDateRangeFilter;
-use Kanboard\Formatter\SubtaskTimeTrackingCalendarFormatter;
-use Kanboard\Formatter\TaskCalendarFormatter;
/**
* Calendar Helper
@@ -17,6 +15,23 @@ use Kanboard\Formatter\TaskCalendarFormatter;
class CalendarHelper extends Base
{
/**
+ * Render calendar component
+ *
+ * @param string $checkUrl
+ * @param string $saveUrl
+ * @return string
+ */
+ public function render($checkUrl, $saveUrl)
+ {
+ $params = array(
+ 'checkUrl' => $checkUrl,
+ 'saveUrl' => $saveUrl,
+ );
+
+ return '<div class="js-calendar" data-params=\''.json_encode($params, JSON_HEX_APOS).'\'></div>';
+ }
+
+ /**
* Get formatted calendar task due events
*
* @access public
@@ -27,7 +42,7 @@ class CalendarHelper extends Base
*/
public function getTaskDateDueEvents(QueryBuilder $queryBuilder, $start, $end)
{
- $formatter = new TaskCalendarFormatter($this->container);
+ $formatter = $this->taskCalendarFormatter;
$formatter->setFullDay();
$formatter->setColumns('date_due');
@@ -56,7 +71,7 @@ class CalendarHelper extends Base
'date_due'
));
- $formatter = new TaskCalendarFormatter($this->container);
+ $formatter = $this->taskCalendarFormatter;
$formatter->setColumns($startColumn, 'date_due');
return $queryBuilder->format($formatter);
@@ -73,8 +88,7 @@ class CalendarHelper extends Base
*/
public function getSubtaskTimeTrackingEvents($user_id, $start, $end)
{
- $formatter = new SubtaskTimeTrackingCalendarFormatter($this->container);
- return $formatter
+ return $this->subtaskTimeTrackingCalendarFormatter
->withQuery($this->subtaskTimeTrackingModel->getUserQuery($user_id)
->addCondition($this->getCalendarCondition(
$this->dateParser->getTimestampFromIsoFormat($start),
diff --git a/app/Helper/DateHelper.php b/app/Helper/DateHelper.php
index 7e2ec79c..3bc85b76 100644
--- a/app/Helper/DateHelper.php
+++ b/app/Helper/DateHelper.php
@@ -54,7 +54,7 @@ class DateHelper extends Base
*/
public function datetime($value)
{
- return date($this->configModel->get('application_datetime_format', 'm/d/Y H:i'), $value);
+ return date($this->dateParser->getUserDateTimeFormat(), $value);
}
/**
diff --git a/app/Helper/FileHelper.php b/app/Helper/FileHelper.php
index cabf371c..06589124 100644
--- a/app/Helper/FileHelper.php
+++ b/app/Helper/FileHelper.php
@@ -21,9 +21,7 @@ class FileHelper extends Base
*/
public function icon($filename)
{
- $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
-
- switch ($extension) {
+ switch (get_file_extension($filename)) {
case 'jpeg':
case 'jpg':
case 'png':
@@ -70,9 +68,7 @@ class FileHelper extends Base
*/
public function getImageMimeType($filename)
{
- $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
-
- switch ($extension) {
+ switch (get_file_extension($filename)) {
case 'jpeg':
case 'jpg':
return 'image/jpeg';
@@ -94,9 +90,7 @@ class FileHelper extends Base
*/
public function getPreviewType($filename)
{
- $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
-
- switch ($extension) {
+ switch (get_file_extension($filename)) {
case 'md':
case 'markdown':
return 'markdown';
@@ -106,4 +100,21 @@ class FileHelper extends Base
return null;
}
+
+ /**
+ * Return the browser view mime-type based on the file extension.
+ *
+ * @access public
+ * @param $filename
+ * @return string
+ */
+ public function getBrowserViewType($filename)
+ {
+ switch (get_file_extension($filename)) {
+ case 'pdf':
+ return 'application/pdf';
+ }
+
+ return null;
+ }
}
diff --git a/app/Helper/FormHelper.php b/app/Helper/FormHelper.php
index c2ea1d72..9eabd724 100644
--- a/app/Helper/FormHelper.php
+++ b/app/Helper/FormHelper.php
@@ -131,16 +131,34 @@ class FormHelper extends Base
* Display a checkbox field
*
* @access public
- * @param string $name Field name
- * @param string $label Form label
- * @param string $value Form value
- * @param boolean $checked Field selected or not
- * @param string $class CSS class
+ * @param string $name Field name
+ * @param string $label Form label
+ * @param string $value Form value
+ * @param boolean $checked Field selected or not
+ * @param string $class CSS class
+ * @param array $attributes
* @return string
*/
- public function checkbox($name, $label, $value, $checked = false, $class = '')
+ public function checkbox($name, $label, $value, $checked = false, $class = '', array $attributes = array())
{
- return '<label><input type="checkbox" name="'.$name.'" class="'.$class.'" value="'.$this->helper->text->e($value).'" '.($checked ? 'checked="checked"' : '').'>&nbsp;'.$this->helper->text->e($label).'</label>';
+ $htmlAttributes = '';
+
+ if ($checked) {
+ $attributes['checked'] = 'checked';
+ }
+
+ foreach ($attributes as $attribute => $attributeValue) {
+ $htmlAttributes .= sprintf('%s="%s"', $attribute, $this->helper->text->e($attributeValue));
+ }
+
+ return sprintf(
+ '<label><input type="checkbox" name="%s" class="%s" value="%s" %s>&nbsp;%s</label>',
+ $name,
+ $class,
+ $this->helper->text->e($value),
+ $htmlAttributes,
+ $this->helper->text->e($label)
+ );
}
/**
@@ -174,7 +192,7 @@ class FormHelper extends Base
$html = '<textarea name="'.$name.'" id="form-'.$name.'" class="'.$class.'" ';
$html .= implode(' ', $attributes).'>';
- $html .= isset($values->$name) ? $this->helper->text->e($values->$name) : isset($values[$name]) ? $values[$name] : '';
+ $html .= isset($values[$name]) ? $this->helper->text->e($values[$name]) : '';
$html .= '</textarea>';
$html .= $this->errorList($errors, $name);
@@ -182,6 +200,45 @@ class FormHelper extends Base
}
/**
+ * Display a markdown editor
+ *
+ * @access public
+ * @param string $name Field name
+ * @param array $values Form values
+ * @param array $errors Form errors
+ * @param array $attributes
+ * @return string
+ */
+ public function textEditor($name, $values = array(), array $errors = array(), array $attributes = array())
+ {
+ $params = array(
+ 'name' => $name,
+ 'text' => isset($values[$name]) ? $values[$name] : '',
+ 'css' => $this->errorClass($errors, $name),
+ 'required' => isset($attributes['required']) && $attributes['required'],
+ 'tabindex' => isset($attributes['tabindex']) ? $attributes['tabindex'] : '-1',
+ 'labelPreview' => t('Preview'),
+ 'labelWrite' => t('Write'),
+ 'placeholder' => t('Write your text in Markdown'),
+ 'autofocus' => isset($attributes['autofocus']) && $attributes['autofocus'],
+ 'suggestOptions' => array(
+ 'triggers' => array(
+ '#' => $this->helper->url->to('TaskAjaxController', 'suggest', array('search' => 'SEARCH_TERM')),
+ )
+ ),
+ );
+
+ if (isset($values['project_id'])) {
+ $params['suggestOptions']['triggers']['@'] = $this->helper->url->to('UserAjaxController', 'mention', array('project_id' => $values['project_id'], 'search' => 'SEARCH_TERM'));
+ }
+
+ $html = '<div class="js-text-editor" data-params=\''.json_encode($params, JSON_HEX_APOS).'\'></div>';
+ $html .= $this->errorList($errors, $name);
+
+ return $html;
+ }
+
+ /**
* Display file field
*
* @access public
@@ -307,6 +364,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/ICalHelper.php b/app/Helper/ICalHelper.php
index dc399bf8..95723417 100644
--- a/app/Helper/ICalHelper.php
+++ b/app/Helper/ICalHelper.php
@@ -5,7 +5,6 @@ namespace Kanboard\Helper;
use Kanboard\Core\Base;
use Kanboard\Core\Filter\QueryBuilder;
use Kanboard\Filter\TaskDueDateRangeFilter;
-use Kanboard\Formatter\TaskICalFormatter;
use Eluceo\iCal\Component\Calendar as iCalendar;
/**
@@ -29,10 +28,10 @@ class ICalHelper extends Base
{
$queryBuilder->withFilter(new TaskDueDateRangeFilter(array($start, $end)));
- $formatter = new TaskICalFormatter($this->container);
- $formatter->setColumns('date_due');
- $formatter->setCalendar($calendar);
- $formatter->withQuery($queryBuilder->getQuery());
- $formatter->addFullDayEvents();
+ $this->taskICalFormatter
+ ->setColumns('date_due')
+ ->setCalendar($calendar)
+ ->withQuery($queryBuilder->getQuery())
+ ->addFullDayEvents();
}
}
diff --git a/app/Helper/LayoutHelper.php b/app/Helper/LayoutHelper.php
index 8ebb05d4..8be71757 100644
--- a/app/Helper/LayoutHelper.php
+++ b/app/Helper/LayoutHelper.php
@@ -22,7 +22,10 @@ class LayoutHelper extends Base
*/
public function app($template, array $params = array())
{
- if ($this->request->isAjax()) {
+ $isAjax = $this->request->isAjax();
+ $params['is_ajax'] = $isAjax;
+
+ if ($isAjax) {
return $this->template->render($template, $params);
}
@@ -156,7 +159,11 @@ class LayoutHelper extends Base
*/
public function analytic($template, array $params)
{
- return $this->subLayout('analytic/layout', 'analytic/sidebar', $template, $params);
+ if (isset($params['project']['name'])) {
+ $params['title'] = $params['project']['name'].' &gt; '.$params['title'];
+ }
+
+ return $this->subLayout('analytic/layout', 'analytic/sidebar', $template, $params, true);
}
/**
@@ -184,13 +191,16 @@ class LayoutHelper extends Base
* @param string $sidebar
* @param string $template
* @param array $params
+ * @param bool $ignoreAjax
* @return string
*/
- public function subLayout($sublayout, $sidebar, $template, array $params = array())
+ public function subLayout($sublayout, $sidebar, $template, array $params = array(), $ignoreAjax = false)
{
+ $isAjax = $this->request->isAjax();
+ $params['is_ajax'] = $isAjax;
$content = $this->template->render($template, $params);
- if ($this->request->isAjax()) {
+ if (!$ignoreAjax && $isAjax) {
return $content;
}
diff --git a/app/Helper/ModalHelper.php b/app/Helper/ModalHelper.php
new file mode 100644
index 00000000..efbe2c4d
--- /dev/null
+++ b/app/Helper/ModalHelper.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Kanboard\Helper;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class ModalHelper
+ *
+ * @package Kanboard\Helper
+ * @author Frederic Guillot
+ */
+class ModalHelper extends Base
+{
+ public function submitButtons(array $params = array())
+ {
+ return $this->helper->app->component('submit-buttons', array(
+ 'submitLabel' => isset($params['submitLabel']) ? $params['submitLabel'] : t('Save'),
+ 'orLabel' => t('or'),
+ 'cancelLabel' => t('cancel'),
+ 'color' => isset($params['color']) ? $params['color'] : 'blue',
+ 'tabindex' => isset($params['tabindex']) ? $params['tabindex'] : null,
+ 'disabled' => isset($params['disabled']) ? true : false,
+ ));
+ }
+
+ public function confirmButtons($controller, $action, array $params = array(), $submitLabel = '', $tabindex = null)
+ {
+ return $this->helper->app->component('confirm-buttons', array(
+ 'url' => $this->helper->url->href($controller, $action, $params, true),
+ 'submitLabel' => $submitLabel ?: t('Yes'),
+ 'orLabel' => t('or'),
+ 'cancelLabel' => t('cancel'),
+ 'tabindex' => $tabindex,
+ ));
+ }
+
+ public function largeIcon($icon, $label, $controller, $action, array $params = array())
+ {
+ $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-large" aria-hidden="true"></i>';
+ return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-large', $label);
+ }
+
+ public function large($icon, $label, $controller, $action, array $params = array())
+ {
+ $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-large" aria-hidden="true"></i>'.$label;
+ return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-large');
+ }
+
+ public function medium($icon, $label, $controller, $action, array $params = array())
+ {
+ $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-medium" aria-hidden="true"></i>'.$label;
+ return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-medium');
+ }
+
+ public function small($icon, $label, $controller, $action, array $params = array())
+ {
+ $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-small" aria-hidden="true"></i>'.$label;
+ return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-small');
+ }
+
+ public function mediumButton($icon, $label, $controller, $action, array $params = array())
+ {
+ $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-medium" aria-hidden="true"></i>'.$label;
+ return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-medium btn');
+ }
+
+ public function confirm($icon, $label, $controller, $action, array $params = array())
+ {
+ $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-confirm" aria-hidden="true"></i>'.$label;
+ return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-confirm');
+ }
+
+ public function confirmLink($label, $controller, $action, array $params = array())
+ {
+ return $this->helper->url->link($label, $controller, $action, $params, false, 'js-modal-confirm');
+ }
+
+ public function replaceLink($label, $controller, $action, array $params = array())
+ {
+ return $this->helper->url->link($label, $controller, $action, $params, false, 'js-modal-replace');
+ }
+}
diff --git a/app/Helper/ProjectActivityHelper.php b/app/Helper/ProjectActivityHelper.php
index 40f386db..480db3d5 100644
--- a/app/Helper/ProjectActivityHelper.php
+++ b/app/Helper/ProjectActivityHelper.php
@@ -6,7 +6,6 @@ use Kanboard\Core\Base;
use Kanboard\Filter\ProjectActivityProjectIdFilter;
use Kanboard\Filter\ProjectActivityProjectIdsFilter;
use Kanboard\Filter\ProjectActivityTaskIdFilter;
-use Kanboard\Formatter\ProjectActivityEventFormatter;
use Kanboard\Model\ProjectActivityModel;
/**
@@ -26,7 +25,7 @@ class ProjectActivityHelper extends Base
*/
public function searchEvents($search)
{
- $projects = $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId());
+ $projects = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId());
$events = array();
if ($search !== '') {
@@ -38,7 +37,7 @@ class ProjectActivityHelper extends Base
->limit(500)
;
- $events = $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
+ $events = $queryBuilder->format($this->projectActivityEventFormatter);
}
return $events;
@@ -62,7 +61,7 @@ class ProjectActivityHelper extends Base
->limit($limit)
;
- return $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
+ return $queryBuilder->format($this->projectActivityEventFormatter);
}
/**
@@ -83,7 +82,7 @@ class ProjectActivityHelper extends Base
->limit($limit)
;
- return $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
+ return $queryBuilder->format($this->projectActivityEventFormatter);
}
/**
@@ -100,6 +99,6 @@ class ProjectActivityHelper extends Base
$queryBuilder->getQuery()->desc(ProjectActivityModel::TABLE.'.id');
- return $queryBuilder->format(new ProjectActivityEventFormatter($this->container));
+ return $queryBuilder->format($this->projectActivityEventFormatter);
}
}
diff --git a/app/Helper/ProjectRoleHelper.php b/app/Helper/ProjectRoleHelper.php
new file mode 100644
index 00000000..6f9cf10c
--- /dev/null
+++ b/app/Helper/ProjectRoleHelper.php
@@ -0,0 +1,286 @@
+<?php
+
+namespace Kanboard\Helper;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Security\Role;
+use Kanboard\Model\ColumnRestrictionModel;
+use Kanboard\Model\ProjectRoleRestrictionModel;
+
+/**
+ * Class ProjectRoleHelper
+ *
+ * @package Kanboard\Helper
+ * @author Frederic Guillot
+ */
+class ProjectRoleHelper extends Base
+{
+ /**
+ * Get project role for the current user
+ *
+ * @access public
+ * @param integer $project_id
+ * @return string
+ */
+ public function getProjectUserRole($project_id)
+ {
+ return $this->memoryCache->proxy($this->projectUserRoleModel, 'getUserRole', $project_id, $this->userSession->getId());
+ }
+
+ /**
+ * Return true if the task can be moved by the logged user
+ *
+ * @param array $task
+ * @return bool
+ */
+ public function isDraggable(array &$task)
+ {
+ if ($task['is_active'] == 1 && $this->helper->user->hasProjectAccess('BoardAjaxController', 'save', $task['project_id'])) {
+ return $this->isSortableColumn($task['project_id'], $task['column_id']);
+ }
+
+ return false;
+ }
+
+ /**
+ * Return true is the column is sortable
+ *
+ * @param int $project_id
+ * @param int $column_id
+ * @return bool
+ */
+ public function isSortableColumn($project_id, $column_id)
+ {
+ $role = $this->getProjectUserRole($project_id);
+
+ if ($this->role->isCustomProjectRole($role)) {
+ $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($project_id, $role);
+
+ foreach ($sortableColumns as $column) {
+ if ($column['src_column_id'] == $column_id || $column['dst_column_id'] == $column_id) {
+ return true;
+ }
+ }
+
+ return empty($sortableColumns) && $this->isAllowedToMoveTask($project_id, $role);
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the user can move a task
+ *
+ * @param int $project_id
+ * @param int $src_column_id
+ * @param int $dst_column_id
+ * @return bool|int
+ */
+ public function canMoveTask($project_id, $src_column_id, $dst_column_id)
+ {
+ $role = $this->getProjectUserRole($project_id);
+
+ if ($this->role->isCustomProjectRole($role)) {
+ if ($src_column_id == $dst_column_id) {
+ return true;
+ }
+
+ $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($project_id, $role);
+
+ foreach ($sortableColumns as $column) {
+ if ($column['src_column_id'] == $src_column_id && $column['dst_column_id'] == $dst_column_id) {
+ return true;
+ }
+
+ if ($column['dst_column_id'] == $src_column_id && $column['src_column_id'] == $dst_column_id) {
+ return true;
+ }
+ }
+
+ return empty($sortableColumns) && $this->isAllowedToMoveTask($project_id, $role);
+ }
+
+ return true;
+ }
+
+ /**
+ * Return true if the user can create a task for the given column
+ *
+ * @param int $project_id
+ * @param int $column_id
+ * @return bool
+ */
+ public function canCreateTaskInColumn($project_id, $column_id)
+ {
+ $role = $this->getProjectUserRole($project_id);
+
+ if ($this->role->isCustomProjectRole($role)) {
+ if (! $this->isAllowedToCreateTask($project_id, $column_id, $role)) {
+ return false;
+ }
+ }
+
+ return $this->helper->user->hasProjectAccess('TaskCreationController', 'show', $project_id);
+ }
+
+ /**
+ * Return true if the user can create a task for the given column
+ *
+ * @param int $project_id
+ * @param int $column_id
+ * @return bool
+ */
+ public function canChangeTaskStatusInColumn($project_id, $column_id)
+ {
+ $role = $this->getProjectUserRole($project_id);
+
+ if ($this->role->isCustomProjectRole($role)) {
+ if (! $this->isAllowedToChangeTaskStatus($project_id, $column_id, $role)) {
+ return false;
+ }
+ }
+
+ return $this->helper->user->hasProjectAccess('TaskStatusController', 'close', $project_id);
+ }
+
+ /**
+ * Return true if the user can remove a task
+ *
+ * Regular users can't remove tasks from other people
+ *
+ * @public
+ * @param array $task
+ * @return bool
+ */
+ public function canRemoveTask(array $task)
+ {
+ if (isset($task['creator_id']) && $task['creator_id'] == $this->userSession->getId()) {
+ return true;
+ }
+
+ if ($this->userSession->isAdmin() || $this->getProjectUserRole($task['project_id']) === Role::PROJECT_MANAGER) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Check project access
+ *
+ * @param string $controller
+ * @param string $action
+ * @param integer $project_id
+ * @return bool
+ */
+ public function checkProjectAccess($controller, $action, $project_id)
+ {
+ if (! $this->userSession->isLogged()) {
+ return false;
+ }
+
+ if ($this->userSession->isAdmin()) {
+ return true;
+ }
+
+ if (! $this->helper->user->hasAccess($controller, $action)) {
+ return false;
+ }
+
+ $role = $this->getProjectUserRole($project_id);
+
+ if ($this->role->isCustomProjectRole($role)) {
+ $result = $this->projectAuthorization->isAllowed($controller, $action, Role::PROJECT_MEMBER);
+ } else {
+ $result = $this->projectAuthorization->isAllowed($controller, $action, $role);
+ }
+
+ return $result;
+ }
+
+ /**
+ * Check authorization for a custom project role to change the task status
+ *
+ * @param int $project_id
+ * @param int $column_id
+ * @param string $role
+ * @return bool
+ */
+ protected function isAllowedToChangeTaskStatus($project_id, $column_id, $role)
+ {
+ $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($project_id, $role);
+
+ foreach ($columnRestrictions as $restriction) {
+ if ($restriction['column_id'] == $column_id) {
+ if ($restriction['rule'] == ColumnRestrictionModel::RULE_ALLOW_TASK_OPEN_CLOSE) {
+ return true;
+ } else if ($restriction['rule'] == ColumnRestrictionModel::RULE_BLOCK_TASK_OPEN_CLOSE) {
+ return false;
+ }
+ }
+ }
+
+ $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($project_id, $role);
+
+ foreach ($projectRestrictions as $restriction) {
+ if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_OPEN_CLOSE) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check authorization for a custom project role to create a task
+ *
+ * @param int $project_id
+ * @param int $column_id
+ * @param string $role
+ * @return bool
+ */
+ protected function isAllowedToCreateTask($project_id, $column_id, $role)
+ {
+ $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($project_id, $role);
+
+ foreach ($columnRestrictions as $restriction) {
+ if ($restriction['column_id'] == $column_id) {
+ if ($restriction['rule'] == ColumnRestrictionModel::RULE_ALLOW_TASK_CREATION) {
+ return true;
+ } else if ($restriction['rule'] == ColumnRestrictionModel::RULE_BLOCK_TASK_CREATION) {
+ return false;
+ }
+ }
+ }
+
+ $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($project_id, $role);
+
+ foreach ($projectRestrictions as $restriction) {
+ if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_CREATION) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Check if the role can move task in the given project
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return bool
+ */
+ protected function isAllowedToMoveTask($project_id, $role)
+ {
+ $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($project_id, $role);
+
+ foreach ($projectRestrictions as $restriction) {
+ if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_MOVE) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/app/Helper/SubtaskHelper.php b/app/Helper/SubtaskHelper.php
index dac71203..8e090f17 100644
--- a/app/Helper/SubtaskHelper.php
+++ b/app/Helper/SubtaskHelper.php
@@ -50,7 +50,7 @@ class SubtaskHelper extends Base
return $this->helper->url->link($this->getTitle($subtask), 'SubtaskStatusController', 'change', $params, false, $class);
}
- public function selectTitle(array $values, array $errors = array(), array $attributes = array())
+ public function renderTitleField(array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="1"', 'required', 'maxlength="255"'), $attributes);
@@ -60,18 +60,21 @@ class SubtaskHelper extends Base
return $html;
}
- public function selectAssignee(array $users, array $values, array $errors = array(), array $attributes = array())
+ public function renderAssigneeField(array $users, array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="2"'), $attributes);
$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;
}
- public function selectTimeEstimated(array $values, array $errors = array(), array $attributes = array())
+ public function renderTimeEstimatedField(array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="3"'), $attributes);
@@ -82,7 +85,7 @@ class SubtaskHelper extends Base
return $html;
}
- public function selectTimeSpent(array $values, array $errors = array(), array $attributes = array())
+ public function renderTimeSpentField(array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="4"'), $attributes);
diff --git a/app/Helper/TaskHelper.php b/app/Helper/TaskHelper.php
index e1d65cca..71596b60 100644
--- a/app/Helper/TaskHelper.php
+++ b/app/Helper/TaskHelper.php
@@ -40,32 +40,28 @@ class TaskHelper extends Base
return $this->taskRecurrenceModel->getRecurrenceBasedateList();
}
- public function selectTitle(array $values, array $errors)
+ public function renderTitleField(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 .= $this->helper->form->textarea(
- 'description',
+ return $this->helper->form->text(
+ 'title',
$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'
+ 'autofocus',
+ 'required',
+ 'maxlength="200"',
+ 'tabindex="1"',
+ 'placeholder="'.t('Title').'"'
+ )
);
+ }
- return $html;
+ public function renderDescriptionField(array $values, array $errors)
+ {
+ return $this->helper->form->textEditor('description', $values, $errors, array('tabindex' => 2));
}
- public function selectTags(array $project, array $tags = array())
+ public function renderTagField(array $project, array $tags = array())
{
$options = $this->tagModel->getAssignableList($project['id']);
@@ -87,7 +83,7 @@ class TaskHelper extends Base
return $html;
}
- public function selectColor(array $values)
+ public function renderColorField(array $values)
{
$colors = $this->colorModel->getList();
$html = $this->helper->form->label(t('Color'), 'color_id');
@@ -95,18 +91,21 @@ class TaskHelper extends Base
return $html;
}
- public function selectAssignee(array $users, array $values, array $errors = array(), array $attributes = array())
+ public function renderAssigneeField(array $users, array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="3"'), $attributes);
$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;
}
- public function selectCategory(array $categories, array $values, array $errors = array(), array $attributes = array(), $allow_one_item = false)
+ public function renderCategoryField(array $categories, array $values, array $errors = array(), array $attributes = array(), $allow_one_item = false)
{
$attributes = array_merge(array('tabindex="4"'), $attributes);
$html = '';
@@ -119,7 +118,7 @@ class TaskHelper extends Base
return $html;
}
- public function selectSwimlane(array $swimlanes, array $values, array $errors = array(), array $attributes = array())
+ public function renderSwimlaneField(array $swimlanes, array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="5"'), $attributes);
$html = '';
@@ -132,7 +131,7 @@ class TaskHelper extends Base
return $html;
}
- public function selectColumn(array $columns, array $values, array $errors = array(), array $attributes = array())
+ public function renderColumnField(array $columns, array $values, array $errors = array(), array $attributes = array())
{
$attributes = array_merge(array('tabindex="6"'), $attributes);
@@ -142,11 +141,11 @@ class TaskHelper extends Base
return $html;
}
- public function selectPriority(array $project, array $values)
+ public function renderPriorityField(array $project, array $values)
{
$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']);
@@ -158,9 +157,9 @@ class TaskHelper extends Base
return $html;
}
- public function selectScore(array $values, array $errors = array(), array $attributes = array())
+ public function renderScoreField(array $values, array $errors = array(), array $attributes = array())
{
- $attributes = array_merge(array('tabindex="8"'), $attributes);
+ $attributes = array_merge(array('tabindex="13"'), $attributes);
$html = $this->helper->form->label(t('Complexity'), 'score');
$html .= $this->helper->form->number('score', $values, $errors, $attributes);
@@ -168,9 +167,9 @@ class TaskHelper extends Base
return $html;
}
- public function selectReference(array $values, array $errors = array(), array $attributes = array())
+ public function renderReferenceField(array $values, array $errors = array(), array $attributes = array())
{
- $attributes = array_merge(array('tabindex="9"'), $attributes);
+ $attributes = array_merge(array('tabindex="14"'), $attributes);
$html = $this->helper->form->label(t('Reference'), 'reference');
$html .= $this->helper->form->text('reference', $values, $errors, $attributes, 'form-input-small');
@@ -178,9 +177,9 @@ class TaskHelper extends Base
return $html;
}
- public function selectTimeEstimated(array $values, array $errors = array(), array $attributes = array())
+ public function renderTimeEstimatedField(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('Original estimate'), 'time_estimated');
$html .= $this->helper->form->numeric('time_estimated', $values, $errors, $attributes);
@@ -189,9 +188,9 @@ class TaskHelper extends Base
return $html;
}
- public function selectTimeSpent(array $values, array $errors = array(), array $attributes = array())
+ public function renderTimeSpentField(array $values, array $errors = array(), array $attributes = array())
{
- $attributes = array_merge(array('tabindex="11"'), $attributes);
+ $attributes = array_merge(array('tabindex="12"'), $attributes);
$html = $this->helper->form->label(t('Time spent'), 'time_spent');
$html .= $this->helper->form->numeric('time_spent', $values, $errors, $attributes);
@@ -200,33 +199,23 @@ class TaskHelper extends Base
return $html;
}
- public function selectStartDate(array $values, array $errors = array(), array $attributes = array())
+ public function renderStartDateField(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="12"', '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="10"'), $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())
+ public function renderDueDateField(array $values, array $errors = array(), array $attributes = array())
{
- $placeholder = date($this->configModel->get('application_date_format', 'm/d/Y'));
- $attributes = array_merge(array('tabindex="13"', '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="9"'), $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>';
@@ -243,4 +232,32 @@ class TaskHelper extends Base
return $this->taskModel->getProgress($task, $this->columns[$task['project_id']]);
}
+
+ public function getNewTaskDropdown($projectId, $swimlaneId, $columnId)
+ {
+ $providers = $this->externalTaskManager->getProvidersList();
+
+ if (empty($providers)) {
+ return '';
+ }
+
+ $html = '<small class="pull-right"><div class="dropdown">';
+ $html .= '<a href="#" class="dropdown-menu"><i class="fa fa-cloud-download" aria-hidden="true"></i> <i class="fa fa-caret-down"></i></a><ul>';
+
+ foreach ($providers as $providerName) {
+ $link = $this->helper->url->link(
+ t('New External Task: %s', $providerName),
+ 'ExternalTaskCreationController',
+ 'step1',
+ array('project_id' => $projectId, 'swimlane_id' => $swimlaneId, 'column_id' => $columnId, 'provider_name' => $providerName),
+ false,
+ 'js-modal-replace'
+ );
+
+ $html .= '<li><i class="fa fa-fw fa-plus-square" aria-hidden="true"></i> '.$link.'</li>';
+ }
+
+ $html .= '</ul></div></small>';
+ return $html;
+ }
}
diff --git a/app/Helper/UrlHelper.php b/app/Helper/UrlHelper.php
index 2127c69e..94412cf5 100644
--- a/app/Helper/UrlHelper.php
+++ b/app/Helper/UrlHelper.php
@@ -42,29 +42,67 @@ class UrlHelper extends Base
*/
public function button($icon, $label, $controller, $action, array $params = array(), $class = '')
{
- $icon = '<i class="fa '.$icon.' fa-fw"></i> ';
+ $html = '<i class="fa fa-'.$icon.' fa-fw"></i> '.$label;
$class = 'btn '.$class;
- return $this->link($icon.$label, $controller, $action, $params, false, $class);
+ return $this->link($html, $controller, $action, $params, false, $class);
+ }
+
+ /**
+ * Link element with icon
+ *
+ * @access public
+ * @param string $icon Icon name
+ * @param string $label Link label
+ * @param string $controller Controller name
+ * @param string $action Action name
+ * @param array $params Url parameters
+ * @param boolean $csrf Add a CSRF token
+ * @param string $class CSS class attribute
+ * @param string $title Link title
+ * @param boolean $newTab Open the link in a new tab
+ * @param string $anchor Link Anchor
+ * @param bool $absolute
+ * @return string
+ */
+ public function icon($icon, $label, $controller, $action, array $params = array(), $csrf = false, $class = '', $title = '', $newTab = false, $anchor = '', $absolute = false)
+ {
+ $html = '<i class="fa fa-fw fa-'.$icon.'" aria-hidden="true"></i>'.$label;
+ return $this->helper->url->link($html, $controller, $action, $params, $csrf, $class, $title, $newTab, $anchor, $absolute);
}
/**
* Link element
*
* @access public
- * @param string $label Link label
- * @param string $controller Controller name
- * @param string $action Action name
- * @param array $params Url parameters
- * @param boolean $csrf Add a CSRF token
- * @param string $class CSS class attribute
- * @param string $title
- * @param boolean $new_tab Open the link in a new tab
- * @param string $anchor Link Anchor
+ * @param string $label Link label
+ * @param string $controller Controller name
+ * @param string $action Action name
+ * @param array $params Url parameters
+ * @param boolean $csrf Add a CSRF token
+ * @param string $class CSS class attribute
+ * @param string $title Link title
+ * @param boolean $newTab Open the link in a new tab
+ * @param string $anchor Link Anchor
+ * @param bool $absolute
+ * @return string
+ */
+ public function link($label, $controller, $action, array $params = array(), $csrf = false, $class = '', $title = '', $newTab = false, $anchor = '', $absolute = false)
+ {
+ return '<a href="'.$this->href($controller, $action, $params, $csrf, $anchor, $absolute).'" class="'.$class.'" title=\''.$title.'\' '.($newTab ? 'target="_blank"' : '').'>'.$label.'</a>';
+ }
+
+ /**
+ * Absolute link
+ *
+ * @param string $label
+ * @param string $controller
+ * @param string $action
+ * @param array $params
* @return string
*/
- public function link($label, $controller, $action, array $params = array(), $csrf = false, $class = '', $title = '', $new_tab = false, $anchor = '')
+ public function absoluteLink($label, $controller, $action, array $params = array())
{
- return '<a href="'.$this->href($controller, $action, $params, $csrf, $anchor).'" class="'.$class.'" title=\''.$title.'\' '.($new_tab ? 'target="_blank"' : '').'>'.$label.'</a>';
+ return $this->link($label, $controller, $action, $params, false, '', '', true, '', true);
}
/**
@@ -155,7 +193,7 @@ class UrlHelper extends Base
/**
* Build relative url
*
- * @access private
+ * @access protected
* @param string $separator Querystring argument separator
* @param string $controller Controller name
* @param string $action Action name
@@ -165,7 +203,7 @@ class UrlHelper extends Base
* @param boolean $absolute Absolute or relative link
* @return string
*/
- private function build($separator, $controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false)
+ protected function build($separator, $controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false)
{
$path = $this->route->findUrl($controller, $action, $params);
$qs = array();
diff --git a/app/Helper/UserHelper.php b/app/Helper/UserHelper.php
index ab259a62..8c2567b9 100644
--- a/app/Helper/UserHelper.php
+++ b/app/Helper/UserHelper.php
@@ -3,7 +3,6 @@
namespace Kanboard\Helper;
use Kanboard\Core\Base;
-use Kanboard\Core\Security\Role;
/**
* User helpers
@@ -50,7 +49,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'];
}
/**
@@ -132,61 +132,14 @@ class UserHelper extends Base
*/
public function hasProjectAccess($controller, $action, $project_id)
{
- if (! $this->userSession->isLogged()) {
- return false;
- }
-
- if ($this->userSession->isAdmin()) {
- return true;
- }
-
- if (! $this->hasAccess($controller, $action)) {
- return false;
- }
-
$key = 'project_access:'.$controller.$action.$project_id;
$result = $this->memoryCache->get($key);
if ($result === null) {
- $role = $this->getProjectUserRole($project_id);
- $result = $this->projectAuthorization->isAllowed($controller, $action, $role);
+ $result = $this->helper->projectRole->checkProjectAccess($controller, $action, $project_id);
$this->memoryCache->set($key, $result);
}
return $result;
}
-
- /**
- * Get project role for the current user
- *
- * @access public
- * @param integer $project_id
- * @return string
- */
- public function getProjectUserRole($project_id)
- {
- return $this->memoryCache->proxy($this->projectUserRoleModel, 'getUserRole', $project_id, $this->userSession->getId());
- }
-
- /**
- * Return true if the user can remove a task
- *
- * Regular users can't remove tasks from other people
- *
- * @public
- * @param array $task
- * @return bool
- */
- public function canRemoveTask(array $task)
- {
- if (isset($task['creator_id']) && $task['creator_id'] == $this->userSession->getId()) {
- return true;
- }
-
- if ($this->userSession->isAdmin() || $this->getProjectUserRole($task['project_id']) === Role::PROJECT_MANAGER) {
- return true;
- }
-
- return false;
- }
}
diff --git a/app/Job/CommentEventJob.php b/app/Job/CommentEventJob.php
new file mode 100644
index 00000000..62fae40a
--- /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
+ */
+ 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) {
+ $userMentionJob = $this->userMentionJob->withParams($event['comment']['comment'], CommentModel::EVENT_USER_MENTION, $event);
+ $this->queueManager->push($userMentionJob);
+ }
+ }
+ }
+}
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..acc7fca3
--- /dev/null
+++ b/app/Job/TaskEventJob.php
@@ -0,0 +1,76 @@
+<?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) {
+ $userMentionJob = $this->userMentionJob->withParams($event['task']['description'], TaskModel::EVENT_USER_MENTION, $event);
+ $this->queueManager->push($userMentionJob);
+ }
+ }
+}
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/Job/UserMentionJob.php b/app/Job/UserMentionJob.php
new file mode 100644
index 00000000..355095bb
--- /dev/null
+++ b/app/Job/UserMentionJob.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Kanboard\Job;
+
+use Kanboard\Event\GenericEvent;
+use Kanboard\Model\UserModel;
+
+/**
+ * Class UserMentionJob
+ *
+ * @package Kanboard\Job
+ * @author Frederic Guillot
+ */
+class UserMentionJob extends BaseJob
+{
+ /**
+ * Set job parameters
+ *
+ * @param string $text
+ * @param string $eventName
+ * @param GenericEvent $event
+ * @return $this
+ */
+ public function withParams($text, $eventName, GenericEvent $event)
+ {
+ $this->jobParams = array($text, $eventName, $event->getAll());
+ return $this;
+ }
+
+ /**
+ * Execute job
+ *
+ * @param string $text
+ * @param string $eventName
+ * @param array $eventData
+ */
+ public function execute($text, $eventName, array $eventData)
+ {
+ $event = new GenericEvent($eventData);
+ $users = $this->getMentionedUsers($text);
+
+ foreach ($users as $user) {
+ if ($this->projectPermissionModel->isMember($event->getProjectId(), $user['id'])) {
+ $event['mention'] = $user;
+ $this->dispatcher->dispatch($eventName, $event);
+ }
+ }
+ }
+
+ /**
+ * Get list of mentioned users
+ *
+ * @access public
+ * @param string $text
+ * @return array
+ */
+ public function getMentionedUsers($text)
+ {
+ $users = array();
+
+ if (preg_match_all('/@([^\s,!:?]+)/', $text, $matches)) {
+ array_walk($matches[1], function (&$username) { $username = rtrim($username, '.'); });
+ $users = $this->db->table(UserModel::TABLE)
+ ->columns('id', 'username', 'name', 'email', 'language')
+ ->eq('notifications_enabled', 1)
+ ->neq('id', $this->userSession->getId())
+ ->in('username', array_unique($matches[1]))
+ ->findAll();
+ }
+
+ return $users;
+ }
+}
diff --git a/app/Locale/bs_BA/translations.php b/app/Locale/bs_BA/translations.php
index e3c29261..e689439c 100644
--- a/app/Locale/bs_BA/translations.php
+++ b/app/Locale/bs_BA/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d zadataka na tabli',
'%d tasks in total' => '%d zadataka ukupno',
'Unable to update this board.' => 'Nemogu da ažuriram ovu ploču.',
- 'Edit board' => 'Izmijeni ploču',
'Disable' => 'Onemogući',
'Enable' => 'Omogući',
'New project' => 'Novi projekat',
'Do you really want to remove this project: "%s"?' => 'Da li želiš da ukloniš projekat: "%s"?',
'Remove project' => 'Ukloni projekat',
'Edit the board for "%s"' => 'Uredi ploču za "%s"',
- 'All projects' => 'Svi projekti',
'Add a new column' => 'Dodaj novu kolonu',
'Title' => 'Naslov',
'Assigned to %s' => 'Dodijeljen korisniku %s',
'Remove a column' => 'Ukloni kolonu',
- 'Remove a column from a board' => 'Ukloni kolonu sa table',
'Unable to remove this column.' => 'Nemoguće uklanjanje kolone.',
'Do you really want to remove this column: "%s"?' => 'Da li zaista želiš da ukoniš ovu kolonu: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Ova akcija BRIŠE SVE ZADATKE vezane za ovu kolonu!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Naredba VACUUM)',
'(Gzip compressed Sqlite file)' => '(Sqlite baza spakovana Gzip-om)',
'Close a task' => 'Zatvori zadatak',
- 'Edit a task' => 'Uredi zadatak',
'Column' => 'Kolona',
'Color' => 'Boja',
'Assignee' => 'Izvršilac',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Broj zadataka',
'User' => 'Korisnik',
'Comments' => 'Komentari',
- 'Leave a comment' => 'Ostavi komentar',
'Comment is required' => 'Komentar je obavezan',
- 'Leave a description' => 'Dodaj opis',
'Comment added successfully.' => 'Komentar uspješno dodan',
'Unable to create your comment.' => 'Nemoguće kreiranje komentara',
'Due Date' => 'Treba biti gotovo do dana',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Pretraga',
'Nothing found.' => 'Ništa nije pronađeno',
'Due date' => 'Treba biti gotovo do dana',
- 'Others formats accepted: %s and %s' => 'Ostali podržani formati: %s i %s',
'Description' => 'Opis',
'%d comments' => '%d Komentara',
'%d comment' => '%d Komentar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Identifikator zadatka',
'Creator' => 'Autor',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Udaljeno',
'Enabled' => 'Omogućeno',
'Disabled' => 'Onemogućeno',
- 'Username:' => 'Korisničko ime:',
- 'Name:' => 'Ime i Prezime',
+ 'Login:' => 'Korisničko ime:',
+ 'Full Name:' => 'Ime i Prezime',
'Email:' => 'Email: ',
'Notifications:' => 'Obavještenja: ',
'Notifications' => 'Obavještenja',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Veza s etiketama',
'Link modification' => 'Veza modifikacija',
'Links' => 'Veze',
- 'Link settings' => 'Postavke veza',
'Opposite label' => 'Suprotna etiketa',
'Remove a link' => 'Ukloni vezu',
- 'Task\'s links' => 'Veze zadatka',
'The labels must be different' => 'Etikete moraju biti različite',
'There is no link.' => 'Ovdje nema veza',
'This label must be unique' => 'Ova etiketa mora biti jedinstvena',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Kompaktan pregled',
'Horizontal scrolling' => 'Horizontalno listanje',
'Compact/wide view' => 'Skupi/raširi pregled',
- 'No results match:' => 'Nema rezultata:',
'Currency' => 'Valuta',
'Private project' => 'Privatni projekat',
'AUD - Australian Dollar' => 'AUD - Australijski dolar',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Japanski jen',
'NZD - New Zealand Dollar' => 'NZD - Novozelandski dolar',
'RSD - Serbian dinar' => 'RSD - Srpski dinar',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Američki dolar',
'Destination column' => 'Odredišna kolona',
'Move the task to another column when assigned to a user' => 'Premjesti zadatak u neku drugu kolonu kada se dodijeli izvršiocu',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Stopa valute',
'Rate' => 'Stopa',
'Change reference currency' => 'Promijeni referencu valute',
- 'Add a new currency rate' => 'Dodaj novu stopu valute',
'Reference currency' => 'Referentna valuta',
'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',
@@ -615,7 +590,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',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Zaustavi tajmer',
'Start timer' => 'Pokreni tajmer',
- 'Add project member' => 'Dodaj člana projekta',
'My activity stream' => 'Tok mojih aktivnosti',
'My calendar' => 'Moj kalendar',
'Search tasks' => 'Pretraga zadataka',
@@ -758,8 +725,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.',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Vanjski korisnik',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Vanjski korisnik ne čuva šifru u Kanboard bazi, npr: LDAP, Google i Github korisnički računi.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ako ste označili kvadratić "Zabrani prijavnu formu", unos pristupnih podataka u prijavnoj formi će biti ignorisan.',
- 'New remote user' => 'Novi vanjski korisnik',
- 'New local user' => 'Novi lokalni korisnik',
'Default task color' => 'Podrazumijevana boja zadatka',
'This feature does not work with all browsers.' => 'Ovaj funkcionalnost ne radi na svim internet pretraživačima.',
'There is no destination project available.' => 'Nema definisanog odredišta za projekat.',
@@ -800,7 +763,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',
@@ -841,8 +803,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.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Vlasnik',
'Unread notifications' => 'Nepročitana obavještenja',
'Notification methods:' => 'Metode obavještenja:',
- 'Import tasks from CSV file' => 'Uvezi zadatke putem CSV fajla',
'Unable to read your file' => 'Nemoguće pročitati fajl',
'%d task(s) have been imported successfully.' => '%d zadataka uspješno uvezeno.',
'Nothing have been imported!' => 'Ništa nije uvezeno!',
@@ -947,7 +906,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.',
@@ -968,13 +926,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:',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'Vlasnik projekta:',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identifikator projekta je opcionalan i mora biti alfanumerički, na primjer: MOJPROJEKAT.',
'Project owner' => 'Vlasnik projekta',
- 'Those dates are useful for the project Gantt chart.' => 'Ovi datumi su korisni za pravljenje Gantt dijagrama za projekat.',
'Private projects do not have users and groups management.' => 'Privatni projekti ne mogu imati korisnike ili grupe korisnika.',
'There is no project member.' => 'Nema članova projekta.',
'Priority' => 'Prioritet',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Početo:',
'Moved:' => 'Pomjereno:',
'Task #%d' => 'Zadatak #%d',
- 'Date and time format' => 'Format za datum i vrijeme',
'Time format' => 'Format za vrijeme',
'Start date: ' => 'Početni datum:',
'End date: ' => 'Krajnji datum:',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Korisnik uspješno onemogućen.',
'Unable to disable this user.' => 'Nemoguće onemogućiti ovog korisnika.',
'All files have been uploaded successfully.' => 'Svi fajlovi su uspješno dodani.',
- 'View uploaded files' => 'Pregled dodanih fajlova',
'The maximum allowed file size is %sB.' => 'Maksimalna dozvoljena veličina fajla je %sB.',
- 'Choose files again' => 'Izaberi ponovo fajlove',
'Drag and drop your files here' => 'Povuci i spusti svoje fajlove ovdje',
'choose files' => 'izaberi fajlove',
'View profile' => 'Pregledaj profil',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php
index ce66eee5..6299f514 100644
--- a/app/Locale/cs_CZ/translations.php
+++ b/app/Locale/cs_CZ/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d úkolů na nástěnce',
'%d tasks in total' => '%d úkolů celkem',
'Unable to update this board.' => 'Nástěnku není možné aktualizovat',
- 'Edit board' => 'Editace nástěnky',
'Disable' => 'Zakázat projekt',
'Enable' => 'Povolit projekt',
'New project' => 'Nový projekt',
'Do you really want to remove this project: "%s"?' => 'Opravdu chcete vyjmout projekt: "%s"?',
'Remove project' => 'Vyjmout projekt',
'Edit the board for "%s"' => 'Editace nástěnky pro "%s" ',
- 'All projects' => 'Všechny projekty',
'Add a new column' => 'Přidat nový sloupec',
'Title' => 'Název',
'Assigned to %s' => 'Přiřazeno uživateli: %s',
'Remove a column' => 'Vyjmout sloupec',
- 'Remove a column from a board' => 'Vyjmout sloupec z nástěnky',
'Unable to remove this column.' => 'Tento sloupec nelze odstranit',
'Do you really want to remove this column: "%s"?' => 'Opravdu chcete vyjmout tento sloupec: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Tato akce vyjme všechny úkoly přiřazený k tomuto sloupci!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Vyčištění)',
'(Gzip compressed Sqlite file)' => '(Gzip )',
'Close a task' => 'Uzavřít úkol',
- 'Edit a task' => 'Editovat úkol',
'Column' => 'Sloupec',
'Color' => 'Barva',
'Assignee' => 'Přiřazeno uživateli',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Počet úkolů',
'User' => 'Uživatel',
'Comments' => 'Komentáře',
- 'Leave a comment' => 'Zanechte komentář',
'Comment is required' => 'Komentář je vyžadován',
- 'Leave a description' => 'Vložte popis',
'Comment added successfully.' => 'Komentář byl úspěšně přidán.',
'Unable to create your comment.' => 'Komentář nelze vytvořit.',
'Due Date' => 'Datum splnění',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Vyhledat',
'Nothing found.' => 'Nenalezena žádná položka.',
'Due date' => 'Plánovaný termín',
- 'Others formats accepted: %s and %s' => 'Akceptovány jiné formáty: %s und %s',
'Description' => 'Podrobný popis',
'%d comments' => '%d komentářů',
'%d comment' => '%d komentář',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Úkol ID',
'Creator' => 'Vlastník',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Vzdálený',
'Enabled' => 'Povoleno',
'Disabled' => 'Zakázáno',
- 'Username:' => 'Uživatelské jméno:',
- 'Name:' => 'Jméno:',
+ 'Login:' => 'Uživatelské jméno:',
+ 'Full Name:' => 'Jméno:',
'Email:' => 'e-mail',
'Notifications:' => 'Upozornění:',
'Notifications' => 'Upozornění',
@@ -386,14 +372,12 @@ 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 změnil přidělení úkolu #%d 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',
@@ -442,13 +426,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.',
@@ -465,10 +446,8 @@ return array(
'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' => 'Aktivní dráhy',
@@ -494,7 +473,6 @@ return array(
'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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Seznam odkazů',
'Link modification' => 'Úpravy odkazů',
'Links' => 'Odkazy',
- 'Link settings' => 'Nastavení odkazů',
'Opposite label' => 'Opačný text',
'Remove a link' => 'Odstranit odkaz',
- 'Task\'s links' => 'Související odkazy',
'The labels must be different' => 'názvy musí být odlišné',
'There is no link.' => 'Nejsou zde žádné odkazy',
'This label must be unique' => 'Tento název musí být jedinečný',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Kompaktní zobrazení',
'Horizontal scrolling' => 'Horizontální rolování',
'Compact/wide view' => 'Kompaktní/plné zobrazení',
- 'No results match:' => 'Žádná shoda:',
'Currency' => 'Měna',
'Private project' => 'Soukromý projekt',
// 'AUD - Australian Dollar' => '',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Japanischer Yen',
'NZD - New Zealand Dollar' => 'NZD - Neuseeland-Dollar',
'RSD - Serbian dinar' => 'RSD - Serbische Dinar',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - US Dollar',
'Destination column' => 'Cílový sloupec',
'Move the task to another column when assigned to a user' => 'Přesunout úkol do jiného sloupce, když je úkol přiřazen uživateli.',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Aktuální kurzy',
'Rate' => 'Kurz',
'Change reference currency' => 'Změnit referenční měnu',
- 'Add a new currency rate' => 'Přidat nový směnný kurz',
'Reference currency' => 'Referenční měna',
'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' => 'Zkontrolujte dvouúrovňový autentifikační klíč',
@@ -615,7 +590,6 @@ return array(
'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 pro "%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 ',
@@ -680,14 +654,8 @@ 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' => 'Změna sloupce',
- 'Position change' => 'Změna pozice',
- 'Swimlane change' => 'Změna dráhy',
- 'Assignee change' => 'Změna přidělení',
- '[%s] Overdue tasks' => '[%s] přetažených úkolů',
'Notification' => 'Upozornění',
'%s moved the task #%d to the first swimlane' => '%s přesunul úkol #%d do první dráhy',
- '%s moved the task #%d to the swimlane "%s"' => '%s přesunul úkol #%d do dráhy "%s"',
// 'Swimlane' => '',
// 'Gravatar' => '',
'%s moved the task %s to the first swimlane' => '%s přesunul úkol %s do první dráhy',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30min.',
'Stop timer' => 'Zastavit časovač',
'Start timer' => 'Spustit časovač',
- 'Add project member' => 'Přidat člena projektu',
'My activity stream' => 'Přehled mých aktivit',
'My calendar' => 'Můj kalendář',
'Search tasks' => 'Hledání úkolů',
@@ -758,8 +725,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.' => '',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Vzdálený uživatel',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Hesla vzdáleným uživatelům se neukládají do databáze Kanboard. Naříklad: LDAP, Google a Github účty.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Pokud zaškrtnete políčko "Zakázat přihlašovací formulář", budou pověření zadané do přihlašovacího formuláře ignorovány.',
- 'New remote user' => 'Nový vzdálený uživatel',
- 'New local user' => 'Nový lokální uživatel',
'Default task color' => 'Výchozí barva úkolu',
'This feature does not work with all browsers.' => 'Tato funkcionalita nefunguje ve všech prohlížečích.',
'There is no destination project available.' => 'Není dostupný žádný cílový projekt.',
@@ -800,7 +763,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',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index f9bc0031..87d65e87 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d Opgaver på boardet',
'%d tasks in total' => '%d Opgaver i alt',
'Unable to update this board.' => 'Ikke muligt at opdatere dette board',
- 'Edit board' => 'Rediger board',
'Disable' => 'Deaktiver',
'Enable' => 'Aktiver',
'New project' => 'Nyt projekt',
'Do you really want to remove this project: "%s"?' => 'Vil du virkelig fjerne dette projekt: "%s"?',
'Remove project' => 'Fjern projekt',
'Edit the board for "%s"' => 'Rediger boardet for "%s"',
- 'All projects' => 'Alle Projekter',
'Add a new column' => 'Tilføj en ny kolonne',
'Title' => 'Titel',
'Assigned to %s' => 'Ansvarlig: %s',
'Remove a column' => 'Fjern en kolonne',
- 'Remove a column from a board' => 'Fjern en kolonne fra et board',
'Unable to remove this column.' => 'Ikke muligt at fjerne denne kolonne',
'Do you really want to remove this column: "%s"?' => 'Vil du virkelig fjerne denne kolonne: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Denne handling vil SLETTE ALLE OPGAVER tilknyttet denne kolonne',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM kommando)',
'(Gzip compressed Sqlite file)' => '(Gzip-komprimeret Sqlite fil)',
'Close a task' => 'Luk en opgave',
- 'Edit a task' => 'Rediger en opgave',
'Column' => 'Kolonne',
'Color' => 'Farve',
'Assignee' => 'Ansvarlig',
@@ -162,9 +158,7 @@ return array(
// 'Task count' => '',
'User' => 'Bruger',
'Comments' => 'Kommentarer',
- 'Leave a comment' => 'Efterlad en kommentar',
'Comment is required' => 'Kommentar er krævet',
- 'Leave a description' => 'Efterlad en beskrivelse...',
'Comment added successfully.' => 'Kommentaren er tilføjet.',
'Unable to create your comment.' => 'Din kommentar kunne ikke oprettes.',
'Due Date' => 'Forfaldsdato',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Søg',
'Nothing found.' => 'Intet fundet.',
'Due date' => 'Forfaldsdato',
- 'Others formats accepted: %s and %s' => 'Andre acceptable formater: %s und %s',
'Description' => 'Beskrivelse',
'%d comments' => '%d kommentarer',
'%d comment' => '%d kommentar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Opgave ID',
'Creator' => 'Skaber',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Remote',
'Enabled' => 'Aktiv',
'Disabled' => 'Deaktiveret',
- 'Username:' => 'Brugernavn',
- 'Name:' => 'Navn:',
+ 'Login:' => 'Brugernavn',
+ 'Full Name:' => 'Navn:',
'Email:' => 'Email:',
'Notifications:' => 'Notifikationer:',
'Notifications' => 'Notifikationer',
@@ -386,14 +372,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',
@@ -442,13 +426,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.' => '',
@@ -465,10 +446,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' => '',
@@ -494,7 +473,6 @@ return array(
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
- // 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
// 'Application default' => '',
@@ -532,10 +510,8 @@ return array(
// 'Link labels' => '',
// 'Link modification' => '',
// 'Links' => '',
- // 'Link settings' => '',
// 'Opposite label' => '',
// 'Remove a link' => '',
- // 'Task\'s links' => '',
// 'The labels must be different' => '',
// 'There is no link.' => '',
// 'This label must be unique' => '',
@@ -568,7 +544,6 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
- // 'No results match:' => '',
// 'Currency' => '',
// 'Private project' => '',
// 'AUD - Australian Dollar' => '',
@@ -582,6 +557,7 @@ return array(
// 'JPY - Japanese Yen' => '',
// 'NZD - New Zealand Dollar' => '',
// 'RSD - Serbian dinar' => '',
+ // 'CNY - Chinese Yuan' => '',
// 'USD - US Dollar' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
@@ -596,12 +572,11 @@ return array(
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
- // 'Add a new currency rate' => '',
// 'Reference currency' => '',
// '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' => '',
@@ -615,7 +590,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' => '',
@@ -680,14 +654,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' => '',
@@ -725,7 +693,6 @@ return array(
// '<30m' => '',
// 'Stop timer' => '',
// 'Start timer' => '',
- // 'Add project member' => '',
// 'My activity stream' => '',
// 'My calendar' => '',
// 'Search tasks' => '',
@@ -758,8 +725,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.' => '',
@@ -782,8 +747,6 @@ return array(
// 'Remote user' => '',
// 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '',
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
- // 'New remote user' => '',
- // 'New local user' => '',
// 'Default task color' => '',
// 'This feature does not work with all browsers.' => '',
// 'There is no destination project available.' => '',
@@ -800,7 +763,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index d3192f46..adefbf13 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d Aufgaben auf dieser Pinnwand',
'%d tasks in total' => '%d Aufgaben insgesamt',
'Unable to update this board.' => 'Ändern dieser Pinnwand nicht möglich.',
- 'Edit board' => 'Pinnwand bearbeiten',
'Disable' => 'Deaktivieren',
'Enable' => 'Aktivieren',
'New project' => 'Neues Projekt',
'Do you really want to remove this project: "%s"?' => 'Soll dieses Projekt wirklich gelöscht werden: "%s"?',
'Remove project' => 'Projekt löschen',
'Edit the board for "%s"' => 'Pinnwand für "%s" bearbeiten',
- 'All projects' => 'Alle Projekte',
'Add a new column' => 'Neue Spalte hinzufügen',
'Title' => 'Titel',
'Assigned to %s' => 'Zuständig: %s',
'Remove a column' => 'Spalte löschen',
- 'Remove a column from a board' => 'Spalte einer Pinnwand löschen',
'Unable to remove this column.' => 'Löschen dieser Spalte nicht möglich.',
'Do you really want to remove this column: "%s"?' => 'Soll diese Spalte wirklich gelöscht werden: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'ALLE AUFGABEN dieser Spalte werden GELÖSCHT!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM Befehl)',
'(Gzip compressed Sqlite file)' => '(Gzip-komprimierte SQLite-Datei)',
'Close a task' => 'Aufgabe abschließen',
- 'Edit a task' => 'Aufgabe bearbeiten',
'Column' => 'Spalte',
'Color' => 'Farbe',
'Assignee' => 'Zuständiger',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Aufgabenanzahl',
'User' => 'Benutzer',
'Comments' => 'Kommentare',
- 'Leave a comment' => 'Kommentar eingeben',
'Comment is required' => 'Ein Kommentar wird benötigt',
- 'Leave a description' => 'Beschreibung eingeben',
'Comment added successfully.' => 'Kommentar erfolgreich hinzugefügt.',
'Unable to create your comment.' => 'Hinzufügen eines Kommentars nicht möglich.',
'Due Date' => 'Fällig am',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Suchen',
'Nothing found.' => 'Nichts gefunden.',
'Due date' => 'Fälligkeitsdatum',
- 'Others formats accepted: %s and %s' => 'Andere akzeptierte Formate: %s und %s',
'Description' => 'Beschreibung',
'%d comments' => '%d Kommentare',
'%d comment' => '%d Kommentar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Aufgaben ID',
'Creator' => 'Erstellt von',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Remote',
'Enabled' => 'angeschaltet',
'Disabled' => 'abgeschaltet',
- 'Username:' => 'Benutzername:',
- 'Name:' => 'Name:',
+ 'Login:' => 'Benutzername:',
+ 'Full Name:' => 'Vollständiger Name:',
'Email:' => 'E-Mail:',
'Notifications:' => 'Benachrichtigungen:',
'Notifications' => 'Benachrichtigungen',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Verbindungsbeschriftung',
'Link modification' => 'Verbindung ändern',
'Links' => 'Verbindungen',
- 'Link settings' => 'Verbindungseinstellungen',
'Opposite label' => 'Gegenteil',
'Remove a link' => 'Verbindung entfernen',
- 'Task\'s links' => 'Aufgaben-Verbindungen',
'The labels must be different' => 'Die Beschriftung muss unterschiedlich sein',
'There is no link.' => 'Es gibt keine Verbindung',
'This label must be unique' => 'Die Beschriftung muss einzigartig sein',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Kompaktansicht',
'Horizontal scrolling' => 'Horizontales Scrollen',
'Compact/wide view' => 'Kompakt/Breite-Ansicht',
- 'No results match:' => 'Keine Ergebnisse:',
'Currency' => 'Währung',
'Private project' => 'privates Projekt',
'AUD - Australian Dollar' => 'AUD - Australische Dollar',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Japanische Yen',
'NZD - New Zealand Dollar' => 'NZD - Neuseeland-Dollar',
'RSD - Serbian dinar' => 'RSD - Serbische Dinar',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - US-Dollar',
'Destination column' => 'Zielspalte',
'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein User zugeordnet wurde.',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Währungskurse',
'Rate' => 'Kurse',
'Change reference currency' => 'Referenzwährung ändern',
- 'Add a new currency rate' => 'Neuen Währungskurs hinzufügen',
'Reference currency' => 'Referenzwährung',
'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',
@@ -615,7 +590,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 ',
@@ -649,11 +623,11 @@ return array(
'Factor to calculate new due date: ' => 'Faktor zur Berechnung für neues Ablaufdatum: ',
'Month(s)' => 'Monat(e)',
'Recurrence' => 'Wiederholung',
- 'This task has been created by: ' => 'DIese Aufgabe wurde erstellt von: ',
+ 'This task has been created by: ' => 'Diese Aufgabe wurde erstellt von: ',
'Recurrent task has been generated:' => 'Wiederkehrende Aufgabe wurde erstellt:',
'Timeframe to calculate new due date: ' => 'Zeitfenster zur Berechnung für neues Ablaufdatum: ',
'Trigger to generate recurrent task: ' => 'Auslöser für wiederkehrende Aufgabe: ',
- 'When task is closed' => 'Wenn Aufgabe geshlossen wird',
+ 'When task is closed' => 'Wenn Aufgabe geschlossen wird',
'When task is moved from first column' => 'Wenn Aufgabe von erster Spalte verschoben wird',
'When task is moved to last column' => 'Wenn Aufgabe in letzte Spalte verschoben wird',
'Year(s)' => 'Jahr(e)',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30min',
'Stop timer' => 'Stoppe Timer',
'Start timer' => 'Starte Timer',
- 'Add project member' => 'Projektmitglied hinzufügen',
'My activity stream' => 'Aktivitätsstream',
'My calendar' => 'Mein Kalender',
'Search tasks' => 'Suche nach Aufgaben',
@@ -758,8 +725,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.',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Remote-Benutzer',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Remote-Benutzer haben kein Passwort in der Kanboard Datenbank, Beispiel: LDAP, Google und Github Accounts',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Wenn die Box "Verbiete Login-Formular" angeschaltet ist, werden Eingaben in das Login Formular ignoriert.',
- 'New remote user' => 'Neuer Remote-Benutzer',
- 'New local user' => 'Neuer lokaler Benutzer',
'Default task color' => 'Voreingestellte Aufgabenfarbe',
'This feature does not work with all browsers.' => 'Diese Funktion funktioniert nicht mit allen Browsern',
'There is no destination project available.' => 'Es ist kein Zielprojekt vorhanden.',
@@ -800,7 +763,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',
@@ -841,8 +803,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.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Eigentümer',
'Unread notifications' => 'Ungelesene Benachrichtigungen',
'Notification methods:' => 'Benachrichtigungs-Methoden:',
- 'Import tasks from CSV file' => 'Importiere Aufgaben aus CSV Datei',
'Unable to read your file' => 'Die Datei kann nicht gelesen werden',
'%d task(s) have been imported successfully.' => '%d Aufgabe(n) wurde(n) erfolgreich importiert',
'Nothing have been imported!' => 'Es wurde nichts importiert!',
@@ -947,7 +906,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.',
@@ -968,13 +926,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',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'Projekt-Besitzer: ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Die Projekt-Kennung ist optional und muss alphanumerisch sein, beispielsweise: MYPROJECT.',
'Project owner' => 'Projekt-Besitzer',
- 'Those dates are useful for the project Gantt chart.' => 'Diese Daten sind nützlich für das Gantt-Diagramm.',
'Private projects do not have users and groups management.' => 'Private Projekte haben kein Benutzer- und Gruppen-Management.',
'There is no project member.' => 'Es gibt kein Projekt-Mitglied.',
'Priority' => 'Priorität',
@@ -1041,7 +995,7 @@ return array(
'Add a new external link' => 'Füge eine neue externe Verbindung hinzu',
'Edit external link' => 'Externe Verbindung bearbeiten',
'External link' => 'Externe Verbindung',
- 'Copy and paste your link here...' => 'Kopieren Sie Ihren Link hier...',
+ 'Copy and paste your link here...' => 'Kopieren Sie Ihren Link hierher...',
'URL' => 'URL',
'Internal links' => 'Interne Verbindungen',
'Assign to me' => 'Mir zuweisen',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Gestarted:',
'Moved:' => 'Verschoben:',
'Task #%d' => 'Aufgabe #%d',
- 'Date and time format' => 'Datums- und Zeitformat',
'Time format' => 'Zeitformat',
'Start date: ' => 'Anfangsdatum: ',
'End date: ' => 'Enddatum: ',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Benutzer erfolgreich deaktiviert.',
'Unable to disable this user.' => 'Dieser Benutzer kann nicht deaktiviert werden.',
'All files have been uploaded successfully.' => 'Alle Dateien wurden erfolgreich hochgeladen.',
- 'View uploaded files' => 'Hochgeladene Dateien ansehen',
'The maximum allowed file size is %sB.' => 'Die maximal erlaubte Dateigröße ist %sB.',
- 'Choose files again' => 'Wählen Sie erneut Dateien aus',
'Drag and drop your files here' => 'Ziehen Sie Ihre Dateien hier hin',
'choose files' => 'Dateien auswählen',
'View profile' => 'Profil ansehen',
@@ -1195,7 +1146,6 @@ return array(
'Email sender address' => 'E-Mail Absender Adresse',
'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.',
@@ -1216,5 +1166,145 @@ return array(
'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',
- 'Hide tasks in this column in the Dashboard' => 'Aufgaben in dieser Spalte im Dashboard ausblenden',
+ 'Close a task when there is no activity in an specific column' => 'Aufgabe schließen wenn es keine Aktivität in einer bestimmten Spalte gibt',
+ '%s removed a subtask for the task #%d' => '%s hat eine Teilaufgabe der Aufgabe #%d entfernt',
+ '%s removed a comment on the task #%d' => '%s hat einen Kommentar der Aufgabe #%d entfernt',
+ 'Comment removed on task #%d' => 'Kommentar der Aufgabe #%d entfernt',
+ 'Subtask removed on task #%d' => 'Teilaufgabe der Aufgabe #%d entfernt',
+ 'Hide tasks in this column in the dashboard' => 'Aufgaben in dieser Spalte im Dashboard ausblenden',
+ '%s removed a comment on the task %s' => '%s hat einen Kommentar in der Aufgabe %s entfernt',
+ '%s removed a subtask for the task %s' => '%s hat eine Teilaufgabe in der Aufgabe %s entfernt',
+ 'Comment removed' => 'Kommentar entfernt',
+ 'Subtask removed' => 'Teilaufgabe entfernt',
+ '%s set a new internal link for the task #%d' => '%s hat eine neue interne Verbindung in der Aufgabe #%d erstellt',
+ '%s removed an internal link for the task #%d' => '%s hat eine interne Verbindung von der Aufgabe #%d entfernt',
+ 'A new internal link for the task #%d have been defined' => 'Eine neue interne Verbindung für die Aufgabe #%d wurde definiert',
+ 'Internal link removed for the task #%d' => 'Interne Verbindung in der Aufgabe #%d wurde entfernt',
+ '%s set a new internal link for the task %s' => '%s hat eine neue interne Verbindung in der Aufgabe %s erstellt',
+ '%s removed an internal link for the task %s' => '%s hat eine interne Verbindung von der Aufgabe %s entfernt',
+ 'Automatically set the due date on task creation' => 'Ablaufdatum automatisch bei Erstellung einer Aufgabe setzen',
+ 'Move the task to another column when closed' => 'Aufgabe in eine andere Spalte verschieben, wenn diese geschlossen wird',
+ 'Move the task to another column when not moved during a given period' => 'Aufgabe in eine andere Spalte verschieben, wenn diese in einer bestimmten Zeit nicht verschoben wurde',
+ 'Dashboard for %s' => 'Dashboard für %s',
+ 'Tasks overview for %s' => 'Aufgaben-Übersicht für %s',
+ 'Subtasks overview for %s' => 'Teilaufgaben-Übersicht für %s',
+ 'Projects overview for %s' => 'Projekt-Übersicht für %s',
+ 'Activity stream for %s' => 'Aktivitätenstrom für %s',
+ 'Calendar for %s' => 'Kalender für %s',
+ 'Notifications for %s' => 'Benachrichtigungen für %s',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Einer Aufgabe eine Farbe zuweisen, wenn diese in eine bestimmte Swimlane verschoben wird',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Einer Aufgabe eine Priorität zuweisen, wenn diese in eine bestimmte Swimlane verschoben wird',
+ 'User unlocked successfully.' => 'Benutzer erfolgreich entsperrt.',
+ 'Unable to unlock the user.' => 'Benutzer kann nicht entsperrt werden.',
+ 'Move a task to another swimlane' => 'Aufgabe in eine andere Swimlane verschieben',
+ 'Creator Name' => 'Name des Erstellers',
+ 'Time spent and estimated' => 'Aufgewendete und erwartete Zeit',
+ 'Move position' => 'Position verschieben',
+ 'Move task to another position on the board' => 'Aufgabe an eine andere Position im Board verschieben',
+ 'Insert before this task' => 'Vor dieser Aufgabe einfügen',
+ 'Insert after this task' => 'Nach dieser Aufgabe einfügen',
+ 'Unlock this user' => 'Diesen Benutzer entsperren',
+ 'Custom Project Roles' => 'Benutzerdefinierte Projekt Rollen',
+ 'Add a new custom role' => 'Neue benutzerdefinierte Rolle erstellen',
+ 'Restrictions for the role "%s"' => 'Einschränkungen für Rolle "%s"',
+ 'Add a new project restriction' => 'Neue projektbezogene Einschränkung erstellen',
+ 'Add a new drag and drop restriction' => 'Neue drag and drop Einschränkung erstellen',
+ 'Add a new column restriction' => 'Neue spaltenbezogene Einschränkung erstellen',
+ 'Edit this role' => 'Diese Rolle bearbeiten',
+ 'Remove this role' => 'Diese Rolle entfernen',
+ 'There is no restriction for this role.' => 'Für diese Rolle gibt es keine Einschränkungen.',
+ 'Only moving task between those columns is permitted' => 'Verschieben von Aufgaben ist nur zwischen diesen Spalten erlaubt',
+ 'Close a task in a specific column when not moved during a given period' => 'Aufgabe in einer bestimmten Spalte schliessen, wenn sie im angegebenen Zeitraum nicht verschoben wurde',
+ 'Edit columns' => 'Spalten bearbeiten',
+ 'The column restriction has been created successfully.' => 'Die spaltenbezogene Einschränkung wurde erfolgreich erstellt.',
+ 'Unable to create this column restriction.' => 'Erstellen der spaltenbezogenen Einschränkung fehlgeschlagen.',
+ 'Column restriction removed successfully.' => 'Spaltenbezogene Einschränkung erfolgreich entfernt.',
+ 'Unable to remove this restriction.' => 'Entfernen der spaltenbezogenen Einschränkung fehlgeschlagen.',
+ 'Your custom project role has been created successfully.' => 'Benutzerdefinierte Projekt Rolle erfolgreich erstellt.',
+ 'Unable to create custom project role.' => 'Erstellen der benutzerdefinierten Projekt Rolle fehlgeschlagen.',
+ 'Your custom project role has been updated successfully.' => 'Benutzerdefinierte Projekt Rolle wurde erfolgreich geändert.',
+ 'Unable to update custom project role.' => 'Ändern der benutzerdefinierten Projekt Rolle fehlgeschlagen.',
+ 'Custom project role removed successfully.' => 'Benutzerdefinierte Projekt Rolle erfolgreich entfernt.',
+ 'Unable to remove this project role.' => 'Entfernen der benutzerdefinierten Projekt Rolle fehlgeschlagen.',
+ 'The project restriction has been created successfully.' => 'Projektbezogene Einschränkung erfolgreich erstellt.',
+ 'Unable to create this project restriction.' => 'Erstellen der projektbezogenen Einschränkung fehlgeschlagen.',
+ 'Project restriction removed successfully.' => 'Projektbezogene Einschränkung erfolgreich entfernt.',
+ 'You cannot create tasks in this column.' => 'Sie können in dieser spalte keine Aufgaben erzeugen.',
+ 'Task creation is permitted for this column' => 'Erzeugen von Aufgaben ist für diese Spalte erlaubt.',
+ 'Closing or opening a task is permitted for this column' => 'Öffnen und Schliessen von Aufgaben ist für diese Spalte erlaubt.',
+ 'Task creation is blocked for this column' => 'Erzeugen von Aufgaben ist für diese Spalte blockiert.',
+ 'Closing or opening a task is blocked for this column' => 'Öffnen und Schliessen von Aufgaben ist für diese Spalte blockiert.',
+ 'Task creation is not permitted' => 'Erzeugen von Aufgaben ist nicht erlaubt.',
+ 'Closing or opening a task is not permitted' => 'Öffnen und Schliessen von Aufgaben ist nicht erlaubt.',
+ 'New drag and drop restriction for the role "%s"' => 'Neue drag and drop Einschränkung für Rolle "%s"',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Benutzer mit dieser Rolle können Aufgaben nur zwischen Quell- und Zielspalte verschieben.',
+ 'Remove a column restriction' => 'Spaltenbezogene Einschränkung entfernen',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Wollen Sie diese spaltenbezogene Einschränkung wirklich löschen: "%s" nach "%s"?',
+ 'New column restriction for the role "%s"' => 'Neue spaltenbezogene Einschränkung für Rolle "%s"',
+ 'Rule' => 'Regel',
+ 'Do you really want to remove this column restriction?' => 'Wollen Sie diese spaltenbezogene Einschränkung wirklich entfernen?',
+ 'Custom roles' => 'Benutzerdefinierte Rollen',
+ 'New custom project role' => 'Neue benutzerdefinierte Projekt Rolle',
+ 'Edit custom project role' => 'Benutzerdefinierte Projekt Rolle bearbeiten',
+ 'Remove a custom role' => 'Benutzerdefinierte Projekt Rolle entfernen',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Wollen sie diese benutzerdefinierte Rolle wirklich entfernen: "%s"? Alle Benutzer mit dieser Rolle werden zu Projekt-Mitgliedern.',
+ 'There is no custom role for this project.' => 'Für dieses Projekt gibt es keine benutzerdefinierten Rollen.',
+ 'New project restriction for the role "%s"' => 'Neue projektbezogene Einschränkung für Rolle "%s"',
+ 'Restriction' => 'Einschränkung',
+ 'Remove a project restriction' => 'Projektbezogene Einschränkung entfernen',
+ 'Do you really want to remove this project restriction: "%s"?' => 'Wollen Sie diese projektbezogene Einschränkung wirklich entfernen: "%s"?',
+ 'Duplicate to multiple projects' => 'In mehrere Projekte duplizieren',
+ 'This field is required' => 'Dies ist ein Pflichtfeld',
+ 'Moving a task is not permitted' => 'Verschieben einer Aufgabe ist nicht erlaubt',
+ 'This value must be in the range %d to %d' => 'Dieser Wert muss im Bereich %d bis %d sein',
+ 'You are not allowed to move this task.' => 'Sie haben nicht die Berechtigung, diese Aufgabe zu verschieben.',
+ 'API User Access' => 'API Benutzerzugriff',
+ 'Preview' => 'Vorschau',
+ 'Write' => 'Schreiben',
+ 'Write your text in Markdown' => 'Schreiben Sie iHren Text in Markdown',
+ 'New External Task: %s' => 'Neue externe Aufgabe: %s',
+ 'No personal API access token registered.' => 'Keine persönlichen API-Zugriffsinformationen registriert',
+ 'Your personal API access token is "%s"' => 'Ihre persönlichen API-Zugriffsinformationen: "%s"',
+ 'Remove your token' => 'Ihre Zugriffsinformationen entfernen',
+ 'Generate a new token' => 'Neue Zugriffsinformationen generieren',
+ 'Showing %d-%d of %d' => 'Zeige %d-%d of %d',
+ 'Outgoing Emails' => 'Ausgehende E-Mails',
+ 'Add or change currency rate' => 'Wechselkurs hinzufügen oder ändern',
+ 'Reference currency: %s' => 'Referenzwährung: %s',
+ 'Add custom filters' => 'Benutzerdefinierten Filter hinzufügen',
+ 'Export' => 'Exportieren',
+ 'Add link label' => 'Linkbeschreibung hinzufügen',
+ 'Incompatible Plugins' => 'Nicht-kompatible Plugins',
+ 'Compatibility' => 'Kompatibilität',
+ 'Permissions and ownership' => 'Berechtigungen und Besitz',
+ 'Priorities' => 'Prioritäten',
+ 'Close this window' => 'Dieses Fenster schließen',
+ 'Unable to upload this file.' => 'Diese Datei kann nicht hochgeladen werden',
+ 'Import tasks' => 'Aufgaben importieren',
+ 'Choose a project' => 'Wählen Sie ein Projekt',
+ 'Profile' => 'Profil',
+ 'Application role' => 'Anwendungsrolle',
+ '%d invitations were sent.' => '%d Einladungen wurden gesendet.',
+ '%d invitation was sent.' => '%d Einladung wurde gesendet.',
+ 'Unable to create this user.' => 'Dieser Benutzer kann nicht erstellt werden.',
+ 'Kanboard Invitation' => 'Kanboard Einladung',
+ 'Visible on dashboard' => 'Sichtbar auf dem Dashboard',
+ 'Created at:' => 'Erstellt am:',
+ 'Updated at:' => 'Aktualisiert am:',
+ 'There is no custom filter.' => 'Es gibt keinen benutzerdefinierten Filter.',
+ 'New User' => 'Neuer Benutzer',
+ 'Authentication' => 'Authentifizierung',
+ 'If checked, this user will use a third-party system for authentication.' => 'Wenn aktiviert, verwendet dieser Benutzer ein Drittanbieter-System für die Authentifizierung.',
+ 'The password is necessary only for local users.' => 'Das Passwort ist nur für lokale Benutzer erforderlich.',
+ 'You have been invited to register on Kanboard.' => 'Sie wurden eingeladen, sich auf Kanboard zu registrieren.',
+ 'Click here to join your team' => 'Klicken Sie hier, um Ihrem Team beizutreten',
+ 'Invite people' => 'Leute einladen',
+ 'Emails' => 'E-Mail',
+ 'Enter one email address by line.' => 'Geben Sie eine E-Mail-Adresse pro Zeile ein.',
+ 'Add these people to this project' => 'Füge diese Personen diesem Projekt hinzu',
+ 'Add this person to this project' => 'Füge diese Persone diesem Projekt hinzu',
+ 'Sign-up' => 'Anmelden',
+ 'Credentials' => 'Anmeldeinformationen',
+ 'New user' => 'Neuer Benutzer',
+ 'This username is already taken' => 'Dieser Benutzername ist bereits vergeben',
);
diff --git a/app/Locale/el_GR/translations.php b/app/Locale/el_GR/translations.php
index 745acaea..959b32d2 100644
--- a/app/Locale/el_GR/translations.php
+++ b/app/Locale/el_GR/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d εργασίες στον κεντρικό πίνακα έργου',
'%d tasks in total' => '%d εργασιών στο σύνολο',
'Unable to update this board.' => 'Αδύνατη η ενημέρωση αυτού του πίνακα',
- 'Edit board' => 'Ενημέρωση πίνακα',
'Disable' => 'Απενεργοποίηση',
'Enable' => 'Ενεργοποίηση',
'New project' => 'Νέο έργο',
'Do you really want to remove this project: "%s"?' => 'Αφαίρεση του έργου: « %s » ?',
'Remove project' => 'Αφαίρεση του έργου',
'Edit the board for "%s"' => 'Διόρθωση πίνακα από « %s »',
- 'All projects' => 'Όλα τα έργα',
'Add a new column' => 'Πρόσθήκη στήλης',
'Title' => 'Τίτλος',
'Assigned to %s' => 'Ανατιθεμένο στον %s',
'Remove a column' => 'Αφαίρεση στήλης',
- 'Remove a column from a board' => 'Αφαίρεση στήλης από τον πίνακα',
'Unable to remove this column.' => 'Αδύνατη η αφαίρεση της στήλης',
'Do you really want to remove this column: "%s"?' => 'Θέλετε να αφαιρέσετε τη στήλη: « %s » ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Αυτή η ενέργεια θα ΑΦΑΙΡΕΣΕΙ ΟΛΕΣ ΤΙΣ ΕΡΓΑΣΙΕΣ που είναι σχετικές με τη στήλη!!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM command)',
'(Gzip compressed Sqlite file)' => '(Gzip compressed Sqlite file)',
'Close a task' => 'Κλείσιμο εργασίας',
- 'Edit a task' => 'Διόρθωση εργασίας',
'Column' => 'Στήλη',
'Color' => 'Χρώμα',
'Assignee' => 'Ανατεθιμένο στον χρήστη',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Αρίθμηση εργασιών',
'User' => 'Χρήστης',
'Comments' => 'Σχόλια',
- 'Leave a comment' => 'Αφήστε ένα σχόλιο',
'Comment is required' => 'Το σχόλιο απαιτείται',
- 'Leave a description' => 'Αφήστε μια περιγραφή',
'Comment added successfully.' => 'Το σχόλιο σας προστέθηκε με επιτυχία.',
'Unable to create your comment.' => 'Δεν είναι δυνατή η προσθήκη του σχολίου σας.',
'Due Date' => 'Μέχρι την ημερομηνία',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Αναζήτηση',
'Nothing found.' => 'Δεν βρέθηκε.',
'Due date' => 'Μέχρι την ημερομηνία',
- 'Others formats accepted: %s and %s' => 'Άλλες δεκτές μορφοποιήσεις: %s και %s',
'Description' => 'Περιγραφή',
'%d comments' => '%d σχόλια',
'%d comment' => '%d σχόλιο',
@@ -299,9 +292,7 @@ return array(
'Display another project' => 'Εμφάνιση άλλου έργου',
'Created by %s' => 'Δημιουργήθηκε από %s',
'Tasks Export' => 'Εξαγωγή εργασιών',
- 'Tasks exportation for "%s"' => 'Εξαγωγή εργασιών για το έργο « %s »',
'Start Date' => 'Ημερομηνία έναρξης',
- 'End Date' => 'ημερομηνία λήξης',
'Execute' => 'Εκτέλεση',
'Task Id' => 'Task Id',
'Creator' => 'Δημιουργός',
@@ -322,14 +313,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' => 'Ανοιχτή πρόσβαση',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Απομακρυσμένη',
'Enabled' => 'Ενεργή',
'Disabled' => 'Απενεργοποιημένη',
- 'Username:' => 'Username:',
- 'Name:' => 'Όνομα:',
+ 'Login:' => 'Login:',
+ 'Full Name:' => 'Όνομα:',
'Email:' => 'Email:',
'Notifications:' => 'Ειδοποιήσεις:',
'Notifications' => 'Ειδοποιήσεις',
@@ -386,14 +372,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',
@@ -442,13 +426,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.' => 'Ελλειπή δεδομένα για να εμφανιστεί το γράφημα.',
@@ -465,10 +446,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' => 'Ενεργές λωρίδες',
@@ -494,7 +473,6 @@ return array(
'Subtask Id' => 'Id υπο-εργασίας',
'Subtasks' => 'Υπο-Εργασίες',
'Subtasks Export' => 'Εξαγωγή υπο-εργασίων',
- 'Subtasks exportation for "%s"' => 'Εξαγωγή υπο-εργασίων για το έργο « %s »',
'Task Title' => 'Τίτλος εργασίας',
'Untitled' => 'Χωρίς τίτλο',
'Application default' => 'Προεπιλογή από την εφαρμογή',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Link labels',
'Link modification' => 'Τροποποίηση Link ',
'Links' => 'Links',
- 'Link settings' => 'Ρυθμίσεις συνδέσμων',
'Opposite label' => 'Αντίθετο label',
'Remove a link' => 'Αφαίρεση ενός link',
- 'Task\'s links' => 'Σύνδεσμοι εργασιών',
'The labels must be different' => 'Τα label πρέπει να είναι διαφορετικά',
'There is no link.' => 'Δεν υπάρχει σύνδεσμος.',
'This label must be unique' => 'Το label πρέπει να είναι μοναδικό',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Συμπυκνωμένη προβολή',
'Horizontal scrolling' => 'Οριζόντια ολίσθηση',
'Compact/wide view' => 'Συμπυκνωμένη/Ευρεία Προβολή',
- 'No results match:' => 'Δεν ταιριάζει κανένα αποτέλεσμα:',
'Currency' => 'Νόμισμα',
'Private project' => 'Ιδιωτικό έργο',
'AUD - Australian Dollar' => 'AUD - Australian Dollar',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Japanese Yen',
'NZD - New Zealand Dollar' => 'NZD - New Zealand Dollar',
'RSD - Serbian dinar' => 'RSD - Serbian dinar',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - US Dollar',
'Destination column' => 'Στήλη προορισμού',
'Move the task to another column when assigned to a user' => 'Μετακινήστε την εργασία σε άλλη στήλη όταν ανατεθεί σε ένα χρήστη',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Ισοτιμίες',
'Rate' => 'Τιμή',
'Change reference currency' => 'Αλλαγή ισοτιμίας',
- 'Add a new currency rate' => 'Προσθήκη ισοτιμίας',
'Reference currency' => 'Αναφορά ισοτιμίας',
'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' => 'Ελέγξτε δύο παράγοντες ελέγχου ταυτότητας κωδικού',
@@ -615,7 +590,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',
@@ -680,14 +654,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η λωρίδα',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Διακοπή ρολογιού',
'Start timer' => 'Έναρξη ρολογιού',
- 'Add project member' => 'Προσθήκη νέου μέλους έργου',
'My activity stream' => 'Η ροή δραστηριοτήτων μου',
'My calendar' => 'Το ημερολόγιο μου',
'Search tasks' => 'Αναζήτηση εργασιών',
@@ -758,8 +725,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 εργασίες',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Απομακρυσμένος χρήστης',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Στους απομακρυσμένους χρήστες δεν αποθηκεύονται οι κωδικοί πρόσβασης εντός της βάσης δεδομένων της τρέχουσας εφαρμογής, Παραδείγματα: LDAP, Google and Github accounts.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Αν ενεργοποιήσετε την επιλογή "Απαγόρευση φόρμας σύνδεσης", τα στοιχεία που εισάγονται στη φόρμα σύνδεσης αγνοούνται.',
- 'New remote user' => 'Νέος απομακρυσμένος χρήστης',
- 'New local user' => 'Νέος τοπικός χρήστης',
'Default task color' => 'Προεπιλογή χρώματος εργασίας',
'This feature does not work with all browsers.' => 'Αυτή η δυνατότητα δεν δουλεύει σε όλους τους browsers',
'There is no destination project available.' => 'Δεν υπάρχει διαθέσιμο κανένα έργο προορισμού.',
@@ -800,7 +763,6 @@ return array(
'License:' => 'Άδεια:',
'License' => 'Άδεια',
'Enter the text below' => 'Πληκτρολογήστε το παρακάτω κείμενο',
- 'Gantt chart for %s' => 'Gantt διάγραμμα για %s',
'Sort by position' => 'Ταξινόμηση κατά Θέση',
'Sort by date' => 'Ταξινόμηση κατά ημέρα',
'Add task' => 'Προσθήκη εργασίας',
@@ -841,8 +803,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.' => 'Το παρεμετροποιημένο από τον χρήστη φίλτρο δημιουργήθηκε με επιτυχία',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Ιδιοκτήτης',
'Unread notifications' => 'Αδιάβαστες ειδοποιήσεις',
'Notification methods:' => 'Μέθοδοι ειδοποίησης:',
- 'Import tasks from CSV file' => 'Εισαγωγή εργασιών μέσω αρχείου CSV',
'Unable to read your file' => 'Δεν είναι δυνατή η ανάγνωση του αρχείου',
'%d task(s) have been imported successfully.' => '%d η(οι) εργασία(ες) εισήχθησαν με επιτυχία.',
'Nothing have been imported!' => 'Τίποτα δεν εισήχθη',
@@ -947,7 +906,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.' => 'Δεν υπάρχει ομάδα.',
@@ -968,13 +926,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' => 'Δαπανόμενες ώρες',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'Ιδιοκτήτης έργου: ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Το αναγνωριστικό έργου είναι προαιρετικό και πρέπει να είναι αλφαριθμητικό, για παράδειγμα: MYPROJECT',
'Project owner' => 'Ιδιοκτήτης έργου',
- 'Those dates are useful for the project Gantt chart.' => 'Οι ημερομηνίες αυτές είανι χρήσιμες για το διάγραμμα Gantt του έργου',
'Private projects do not have users and groups management.' => 'Τα ιδιωτικά έργα δεν έχουν χρήστες και διαχείριση ομάδων',
'There is no project member.' => 'Δεν υπάρχει μέλος στο έργο',
'Priority' => 'Προτεραιότητα',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Ξεκίνησε:',
'Moved:' => 'Μετακινήθηκε:',
'Task #%d' => 'Εργασία #%d',
- 'Date and time format' => 'Μορφή ημερομηνίας και ώρας',
'Time format' => 'Μορφή ώρας',
'Start date: ' => 'Ημερομηνία έναρξης: ',
'End date: ' => 'Ημερομηνία λήξης: ',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Η απενεργοποίηση του χρήστη έγινε με επιτυχία',
'Unable to disable this user.' => 'Δεν είναι δυνατή η απενεργοποίηση του χρήστη',
'All files have been uploaded successfully.' => 'Όλα τα αρχεία ανέβηκαν με επιτυχία',
- 'View uploaded files' => 'Προβολή ανεβασμένων αρχείων',
'The maximum allowed file size is %sB.' => 'Το μέγιστο μέγεθος αρχείου που επιτρέπεται είναι %sB.',
- 'Choose files again' => 'Επιλογή κι άλλων αρχείων',
'Drag and drop your files here' => 'Σύρετε τα αρχεία σας εδώ',
'choose files' => 'Επιλογή αρχείων',
'View profile' => 'Προβολή προφίλ',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index 604a5e3e..5f5d71e1 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d tareas en el tablero',
'%d tasks in total' => '%d tareas en total',
'Unable to update this board.' => 'No se puede actualizar este tablero.',
- 'Edit board' => 'Modificar este tablero',
'Disable' => 'Desactivar',
'Enable' => 'Activar',
'New project' => 'Nuevo proyecto',
'Do you really want to remove this project: "%s"?' => '¿Realmente desea eliminar este proyecto: «%s»?',
'Remove project' => 'Eliminar el proyecto',
'Edit the board for "%s"' => 'Modificar el tablero para «%s»',
- 'All projects' => 'Todos los proyectos',
'Add a new column' => 'Añadir una nueva columna',
'Title' => 'Título',
'Assigned to %s' => 'Asignada a %s',
'Remove a column' => 'Eliminar esta columna',
- 'Remove a column from a board' => 'Eliminar una columna de un tablero',
'Unable to remove this column.' => 'No se puede eliminar esta columna.',
'Do you really want to remove this column: "%s"?' => '¿De vedad que desea eliminar esta columna: «%s»?',
'This action will REMOVE ALL TASKS associated to this column!' => '¡Esta acción ELIMINARÁ TODAS LAS TAREAS asociadas a esta columna!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(comando VACUUM)',
'(Gzip compressed Sqlite file)' => '(archivo Sqlite comprimido en Gzip)',
'Close a task' => 'Cerrar una tarea',
- 'Edit a task' => 'Modificar una tarea',
'Column' => 'Columna',
'Color' => 'Color',
'Assignee' => 'Responsable',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Contador de tareas',
'User' => 'Usuario',
'Comments' => 'Comentarios',
- 'Leave a comment' => 'Dejar un comentario',
'Comment is required' => 'El comentario es obligatorio',
- 'Leave a description' => 'Dejar una descripción',
'Comment added successfully.' => 'El comentario ha sido añadido correctamente.',
'Unable to create your comment.' => 'No se puede crear este comentario.',
'Due Date' => 'Fecha límite',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Buscar',
'Nothing found.' => 'No se ha encontrado nada.',
'Due date' => 'Fecha límite',
- 'Others formats accepted: %s and %s' => 'Otros formatos aceptados: %s y %s',
'Description' => 'Descripción',
'%d comments' => '%d comentarios',
'%d comment' => '%d comentario',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Identificador de tarea',
'Creator' => 'Creador',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Remota',
'Enabled' => 'Activada',
'Disabled' => 'Desactivada',
- 'Username:' => 'Nombre de usuario:',
- 'Name:' => 'Nombre:',
+ 'Login:' => 'Nombre de usuario:',
+ 'Full Name:' => 'Nombre:',
'Email:' => 'Correo electrónico:',
'Notifications:' => 'Notificaciones:',
'Notifications' => 'Notificaciones',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,6 @@ 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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Etiquetas de enlace',
'Link modification' => 'Modificación del enlace',
'Links' => 'Enlaces',
- 'Link settings' => 'Preferencias de enlace',
'Opposite label' => 'Etiqueta opuesta',
'Remove a link' => 'Eliminar un enlace',
- 'Task\'s links' => 'Enlaces de tareas',
'The labels must be different' => 'Las etiquetas han de ser diferentes',
'There is no link.' => 'No hay enlace.',
'This label must be unique' => 'Esta etiqueta debe ser única',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Compactar vista',
'Horizontal scrolling' => 'Desplazamiento horizontal',
'Compact/wide view' => 'Vista compacta/amplia',
- 'No results match:' => 'No hay resultados coincidentes:',
'Currency' => 'Moneda',
'Private project' => 'Proyecto privado',
'AUD - Australian Dollar' => 'AUD - Dólar australiano',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Yen japonés',
'NZD - New Zealand Dollar' => 'NZD - Dóloar neocelandés',
'RSD - Serbian dinar' => 'RSD - Dinar serbio',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Dólar estadounidense',
'Destination column' => 'Columna destino',
'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna al asignarse a un usuario',
@@ -596,12 +572,11 @@ return array(
'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.' => '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',
@@ -615,7 +590,6 @@ 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 muestra la complejidad de la tarea a lo largo del tiempo (trabajo restante).',
'Screenshot taken %s' => 'Pantallazo tomado el %s',
@@ -680,14 +654,8 @@ return array(
'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',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Parar 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',
@@ -758,8 +725,6 @@ 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.',
@@ -782,8 +747,6 @@ return array(
'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 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 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.',
@@ -800,7 +763,6 @@ return array(
'License:' => 'Licencia:',
'License' => 'Licencia',
'Enter the text below' => 'Introduzca el texto a continuación',
- 'Gantt chart for %s' => 'Diagrama de Gantt para %s',
'Sort by position' => 'Ordenar por posición',
'Sort by date' => 'Ordenar por fecha',
'Add task' => 'Añadir tarea',
@@ -841,8 +803,6 @@ return array(
'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',
'My notifications' => 'Mis notificaciones',
'Custom filters' => 'Filtros personalizados',
'Your custom filter have been created successfully.' => 'Tus filtros personalizados han sido creados correctamente.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Propietario',
'Unread notifications' => 'Notificaciones sin leer',
'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!',
@@ -947,7 +906,6 @@ return array(
'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.',
@@ -968,13 +926,10 @@ return array(
'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' => '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',
- 'Mentioned' => 'Mencionado',
- 'Compare Estimated Time vs Actual Time' => 'Comparar tiempo estimado vs tiempo actual',
'Estimated hours: ' => 'Horas estimadas: ',
'Actual hours: ' => 'Horas actuales: ',
'Hours Spent' => 'Horas gastadas',
@@ -1012,7 +967,6 @@ return array(
'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',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Iniciado:',
'Moved:' => 'Movido:',
'Task #%d' => 'Tarea #%d',
- 'Date and time format' => 'Formato de fecha y hora',
'Time format' => 'Formato de hora',
'Start date: ' => 'Fecha de inicio: ',
'End date: ' => 'Fecha de finalización: ',
@@ -1083,9 +1036,7 @@ return array(
'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',
'View profile' => 'Ver perfil',
@@ -1195,7 +1146,6 @@ return array(
'Email sender address' => 'Dirección del remitente del correo electrónico',
'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.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index 73b65463..685858aa 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d tehtävää taululla',
'%d tasks in total' => '%d tehtävää yhteensä',
'Unable to update this board.' => 'Taulun muuttaminen ei onnistunut.',
- 'Edit board' => 'Muuta taulua',
'Disable' => 'Disabloi',
'Enable' => 'Aktivoi',
'New project' => 'Uusi projekti',
'Do you really want to remove this project: "%s"?' => 'Haluatko varmasti poistaa projektin: "%s"?',
'Remove project' => 'Poista projekti',
'Edit the board for "%s"' => 'Muokkaa taulua projektille "%s"',
- 'All projects' => 'Kaikki projektit',
'Add a new column' => 'Lisää uusi sarake',
'Title' => 'Nimi',
'Assigned to %s' => 'Tekijä: %s',
'Remove a column' => 'Poista sarake',
- 'Remove a column from a board' => 'Poista sarake taulusta',
'Unable to remove this column.' => 'Sarakkeen poistaminen ei onnistunut.',
'Do you really want to remove this column: "%s"?' => 'Haluatko varmasti poistaa sarakkeen "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Tämä toiminto POISTAA KAIKKI TEHTÄVÄT tästä sarakkeesta!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM-komento)',
'(Gzip compressed Sqlite file)' => '(Gzip-pakattu Sqlite-tiedosto)',
'Close a task' => 'Sulje tehtävä',
- 'Edit a task' => 'Muokkaa tehtävää',
'Column' => 'Sarake',
'Color' => 'Väri',
'Assignee' => 'Suorittaja',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Tehtävien määrä',
'User' => 'Käyttäjät',
'Comments' => 'Kommentit',
- 'Leave a comment' => 'Lisää kommentti',
'Comment is required' => 'Kommentti vaaditaan',
- 'Leave a description' => 'Lisää kuvaus',
'Comment added successfully.' => 'Kommentti lisättiin onnistuneesti.',
'Unable to create your comment.' => 'Kommentin lisäys epäonnistui.',
'Due Date' => 'Deadline',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Etsi',
'Nothing found.' => 'Ei löytynyt.',
'Due date' => 'Deadline',
- 'Others formats accepted: %s and %s' => 'Muut hyväksytyt muodot: %s ja %s',
'Description' => 'Kuvaus',
'%d comments' => '%d kommenttia',
'%d comment' => '%d kommentti',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Tehtävän ID',
'Creator' => 'Luonut',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Etä',
'Enabled' => 'Käytössä',
'Disabled' => 'Pois käytöstä',
- 'Username:' => 'Käyttäjänimi:',
- 'Name:' => 'Nimi:',
+ 'Login:' => 'Käyttäjänimi:',
+ 'Full Name:' => 'Nimi:',
'Email:' => 'Sähköpostiosoite:',
'Notifications:' => 'Ilmoitukset:',
'Notifications' => 'Ilmoitukset',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,6 @@ return array(
// 'Subtask Id' => '',
// 'Subtasks' => '',
// 'Subtasks Export' => '',
- // 'Subtasks exportation for "%s"' => '',
// 'Task Title' => '',
// 'Untitled' => '',
// 'Application default' => '',
@@ -532,10 +510,8 @@ return array(
// 'Link labels' => '',
// 'Link modification' => '',
// 'Links' => '',
- // 'Link settings' => '',
// 'Opposite label' => '',
// 'Remove a link' => '',
- // 'Task\'s links' => '',
// 'The labels must be different' => '',
// 'There is no link.' => '',
// 'This label must be unique' => '',
@@ -568,7 +544,6 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
- // 'No results match:' => '',
// 'Currency' => '',
// 'Private project' => '',
// 'AUD - Australian Dollar' => '',
@@ -582,6 +557,7 @@ return array(
// 'JPY - Japanese Yen' => '',
// 'NZD - New Zealand Dollar' => '',
// 'RSD - Serbian dinar' => '',
+ // 'CNY - Chinese Yuan' => '',
// 'USD - US Dollar' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
@@ -596,12 +572,11 @@ return array(
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
- // 'Add a new currency rate' => '',
// 'Reference currency' => '',
// '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' => '',
@@ -615,7 +590,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' => '',
@@ -680,14 +654,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' => '',
@@ -725,7 +693,6 @@ return array(
// '<30m' => '',
// 'Stop timer' => '',
// 'Start timer' => '',
- // 'Add project member' => '',
// 'My activity stream' => '',
// 'My calendar' => '',
// 'Search tasks' => '',
@@ -758,8 +725,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.' => '',
@@ -782,8 +747,6 @@ return array(
// 'Remote user' => '',
// 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '',
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
- // 'New remote user' => '',
- // 'New local user' => '',
// 'Default task color' => '',
// 'This feature does not work with all browsers.' => '',
// 'There is no destination project available.' => '',
@@ -800,7 +763,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index e3ee8a4b..824519f9 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -36,7 +36,7 @@ return array(
'Remove user' => 'Supprimer un utilisateur',
'Do you really want to remove this user: "%s"?' => 'Voulez-vous vraiment supprimer cet utilisateur : « %s » ?',
'All users' => 'Tous les utilisateurs',
- 'Username' => 'Nom d\'utilisateur',
+ 'Username' => 'Identifiant',
'Password' => 'Mot de passe',
'Administrator' => 'Administrateur',
'Sign in' => 'Connexion',
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d tâches sur le tableau',
'%d tasks in total' => '%d tâches au total',
'Unable to update this board.' => 'Impossible de mettre à jour ce tableau.',
- 'Edit board' => 'Modifier le tableau',
'Disable' => 'Désactiver',
'Enable' => 'Activer',
'New project' => 'Nouveau projet',
'Do you really want to remove this project: "%s"?' => 'Voulez-vous vraiment supprimer ce projet : « %s » ?',
'Remove project' => 'Supprimer le projet',
'Edit the board for "%s"' => 'Modifier le tableau pour « %s »',
- 'All projects' => 'Tous les projets',
'Add a new column' => 'Ajouter une nouvelle colonne',
'Title' => 'Titre',
'Assigned to %s' => 'Assigné à %s',
'Remove a column' => 'Supprimer une colonne',
- 'Remove a column from a board' => 'Supprimer une colonne d\'un tableau',
'Unable to remove this column.' => 'Impossible de supprimer cette colonne.',
'Do you really want to remove this column: "%s"?' => 'Voulez vraiment supprimer cette colonne : « %s » ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Cette action va supprimer toutes les tâches associées à cette colonne !',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Commande VACUUM)',
'(Gzip compressed Sqlite file)' => '(Fichier Sqlite compressé en Gzip)',
'Close a task' => 'Fermer une tâche',
- 'Edit a task' => 'Modifier une tâche',
'Column' => 'Colonne',
'Color' => 'Couleur',
'Assignee' => 'Personne assignée',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Nombre de tâches',
'User' => 'Utilisateur',
'Comments' => 'Commentaires',
- 'Leave a comment' => 'Laissez un commentaire',
'Comment is required' => 'Le commentaire est obligatoire',
- 'Leave a description' => 'Laissez une description',
'Comment added successfully.' => 'Commentaire ajouté avec succès.',
'Unable to create your comment.' => 'Impossible de sauvegarder votre commentaire.',
'Due Date' => 'Date d\'échéance',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Rechercher',
'Nothing found.' => 'Rien trouvé.',
'Due date' => 'Date d\'échéance',
- 'Others formats accepted: %s and %s' => 'Autres formats acceptés : %s et %s',
'Description' => 'Description',
'%d comments' => '%d commentaires',
'%d comment' => '%d commentaire',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Identifiant de la tâche',
'Creator' => 'Créateur',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Distant',
'Enabled' => 'Activé',
'Disabled' => 'Désactivé',
- 'Username:' => 'Nom d\'utilisateur :',
- 'Name:' => 'Nom :',
+ 'Login:' => 'Nom d\'utilisateur :',
+ 'Full Name:' => 'Nom :',
'Email:' => 'Email :',
'Notifications:' => 'Notifications :',
'Notifications' => 'Notifications',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Libellé des liens',
'Link modification' => 'Modification d\'un lien',
'Links' => 'Liens',
- 'Link settings' => 'Paramètres des liens',
'Opposite label' => 'Nom du libellé opposé',
'Remove a link' => 'Supprimer un lien',
- 'Task\'s links' => 'Liens des tâches',
'The labels must be different' => 'Les libellés doivent être différents',
'There is no link.' => 'Il n\'y a aucun lien.',
'This label must be unique' => 'Ce libellé doit être unique',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Vue compacte',
'Horizontal scrolling' => 'Défilement horizontal',
'Compact/wide view' => 'Basculer entre la vue compacte et étendue',
- 'No results match:' => 'Aucun résultat :',
'Currency' => 'Devise',
'Private project' => 'Projet privé',
'AUD - Australian Dollar' => 'AUD - Dollar australien',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Yen',
'NZD - New Zealand Dollar' => 'NZD - Dollar néo-zélandais',
'RSD - Serbian dinar' => 'RSD - Dinar serbe',
+ 'CNY - Chinese Yuan' => 'CNY - Yuan (Chine)',
'USD - US Dollar' => 'USD - Dollar américain',
'Destination column' => 'Colonne de destination',
'Move the task to another column when assigned to a user' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci est assignée à quelqu\'un',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Taux de change des devises',
'Rate' => 'Taux',
'Change reference currency' => 'Changer la monnaie de référence',
- 'Add a new currency rate' => 'Ajouter un nouveau taux pour une devise',
'Reference currency' => 'Devise de référence',
'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',
@@ -615,7 +590,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',
@@ -680,14 +654,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',
@@ -698,7 +666,6 @@ return array(
'view the board on Kanboard' => 'voir le tableau sur Kanboard',
'The task have been moved to the first swimlane' => 'La tâche a été déplacée dans la première swimlane',
'The task have been moved to another swimlane:' => 'La tâche a été déplacée dans une autre swimlane :',
- // 'Overdue tasks for the project(s) "%s"' => 'Tâches en retard pour le projet « %s »',
'New title: %s' => 'Nouveau titre : %s',
'The task is not assigned anymore' => 'La tâche n\'est plus assignée maintenant',
'New assignee: %s' => 'Nouvel assigné : %s',
@@ -726,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Stopper le chrono',
'Start timer' => 'Démarrer le chrono',
- 'Add project member' => 'Ajouter un membre au projet',
'My activity stream' => 'Mon flux d\'activité',
'My calendar' => 'Mon agenda',
'Search tasks' => 'Rechercher des tâches',
@@ -759,8 +725,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.',
@@ -783,8 +747,6 @@ return array(
'Remote user' => 'Utilisateur distant',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Les utilisateurs distants ne stockent pas leur mot de passe dans la base de données de Kanboard, exemples : comptes LDAP, Github ou Google.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si vous cochez la case « Interdire le formulaire d\'authentification », les identifiants entrés dans le formulaire d\'authentification seront ignorés.',
- 'New remote user' => 'Créer un utilisateur distant',
- 'New local user' => 'Créer un utilisateur local',
'Default task color' => 'Couleur par défaut des tâches',
'This feature does not work with all browsers.' => 'Cette fonctionnalité n\'est pas compatible avec tous les navigateurs',
'There is no destination project available.' => 'Il n\'y a pas de projet de destination disponible.',
@@ -801,7 +763,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',
@@ -842,8 +803,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.',
@@ -868,7 +827,7 @@ return array(
'Swimlane changed for task #%d' => 'Changement de swimlane pour la tâche n°%d',
'Assignee changed on task #%d' => 'Changement de l\'assigné pour la tâche n°%d',
'%d overdue tasks' => '%d tâches en retard',
- 'Task #%d is overdue' => 'La tâche n°%d est retard',
+ 'Task #%d is overdue' => 'La tâche n°%d est en retard',
'No new notifications.' => 'Aucune notification.',
'Mark all as read' => 'Tout marquer comme lu',
'Mark as read' => 'Marquer comme lu',
@@ -881,7 +840,6 @@ return array(
'Owner' => 'Propriétaire',
'Unread notifications' => 'Notifications non lus',
'Notification methods:' => 'Méthodes de notifications :',
- 'Import tasks from CSV file' => 'Importer les tâches depuis un fichier CSV',
'Unable to read your file' => 'Impossible de lire votre fichier',
'%d task(s) have been imported successfully.' => '%d tâche(s) ont été importées avec succès.',
'Nothing have been imported!' => 'Rien n\'a été importé',
@@ -948,7 +906,6 @@ return array(
'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.',
@@ -969,13 +926,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',
@@ -1013,7 +967,6 @@ return array(
'Project owner: ' => 'Responsable du projet : ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identifiant du projet est optionnel et doit être alphanumérique, example: MONPROJET.',
'Project owner' => 'Responsable du projet',
- 'Those dates are useful for the project Gantt chart.' => 'Ces dates sont utiles pour le diagramme de Gantt des projets.',
'Private projects do not have users and groups management.' => 'Les projets privés n\'ont pas de gestion d\'utilisateurs et de groupes.',
'There is no project member.' => 'Il n\'y a aucun membre du projet.',
'Priority' => 'Priorité',
@@ -1070,7 +1023,6 @@ return array(
'Started:' => 'Commençé le :',
'Moved:' => 'Déplacé le : ',
'Task #%d' => 'Tâche n°%d',
- 'Date and time format' => 'Format de la date et de l\'heure',
'Time format' => 'Format de l\'heure',
'Start date: ' => 'Date de début : ',
'End date: ' => 'Date de fin : ',
@@ -1084,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Utilisateur désactivé avec succès.',
'Unable to disable this user.' => 'Impossible de désactiver cet utilisateur.',
'All files have been uploaded successfully.' => 'Tous les fichiers ont été uploadés avec succès.',
- 'View uploaded files' => 'Voir les fichiers uploadés',
'The maximum allowed file size is %sB.' => 'La taille maximale autorisée pour les fichiers est de %so.',
- 'Choose files again' => 'Choisir de nouveau des fichiers',
'Drag and drop your files here' => 'Glissez-déposez vos fichiers ici',
'choose files' => 'choisissez des fichiers',
'View profile' => 'Voir le profil',
@@ -1196,7 +1146,6 @@ return array(
'Email sender address' => 'Adresse email de l\'expéditeur',
'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é.',
@@ -1217,5 +1166,145 @@ return array(
'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',
- // 'Hide tasks in this column in the Dashboard' => '',
+ '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',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Assigner une couleur lorsque une tâche est déplaçée dans une swimlane spécifique',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Assigner une priorité lorsque une tâche est déplaçée dans une swimlane spécifique',
+ 'User unlocked successfully.' => 'Utilisateur débloqué avec succès.',
+ 'Unable to unlock the user.' => 'Impossible de débloquer cet utilisateur.',
+ 'Move a task to another swimlane' => 'Déplaçer une tâche dans une autre swimlane',
+ 'Creator Name' => 'Nom du créateur',
+ 'Time spent and estimated' => 'Temps passé et estimé',
+ 'Move position' => 'Changer la position',
+ 'Move task to another position on the board' => 'Déplaçer la tâche vers une autre position sur le tableau',
+ 'Insert before this task' => 'Insérer avant cette tâche',
+ 'Insert after this task' => 'Insérer après cette tâche',
+ 'Unlock this user' => 'Débloquer cet utilisateur',
+ 'Custom Project Roles' => 'Rôles personnalisés du projet',
+ 'Add a new custom role' => 'Ajouter un nouveau rôle personnalisé',
+ 'Restrictions for the role "%s"' => 'Restrictions pour le rôle « %s »',
+ 'Add a new project restriction' => 'Ajouter une nouvelle restriction pour ce projet',
+ 'Add a new drag and drop restriction' => 'Ajouter une nouvelle restriction pour le glisser-déposer',
+ 'Add a new column restriction' => 'Ajouter une nouvelle restriction basé sur les colonnes',
+ 'Edit this role' => 'Modifier ce rôle',
+ 'Remove this role' => 'Supprimer ce rôle',
+ 'There is no restriction for this role.' => 'Il n\'y a aucune restriction pour ce rôle',
+ 'Only moving task between those columns is permitted' => 'La tâche ne peut être déplacée qu\'entre ces colonnes',
+ 'Close a task in a specific column when not moved during a given period' => 'Fermez une tâche dans une colonne spécifique lorsqu\'elle n\'est pas déplacée au cours d\'une période donnée',
+ 'Edit columns' => 'Modifier les colonnes',
+ 'The column restriction has been created successfully.' => 'La restriction sur les colonnes a été créée avec succès.',
+ 'Unable to create this column restriction.' => 'Impossible de créer cette restriction de colonne.',
+ 'Column restriction removed successfully.' => 'Restriction de colonne supprimée avec succès.',
+ 'Unable to remove this restriction.' => 'Impossible de supprimer cette restriction.',
+ 'Your custom project role has been created successfully.' => 'Votre rôle personnalisé a été créé avec succès.',
+ 'Unable to create custom project role.' => 'Impossible de créer ce rôle personnalisé.',
+ 'Your custom project role has been updated successfully.' => 'Votre rôle personnalisé a été mis à jour avec succès.',
+ 'Unable to update custom project role.' => 'Impossible de mettre à jour ce rôle personnalisé.',
+ 'Custom project role removed successfully.' => 'Rôle personnalisé supprimé avec succès.',
+ 'Unable to remove this project role.' => 'Impossible de supprimer ce rôle.',
+ 'The project restriction has been created successfully.' => 'La restriction de projet a été créée avec succès.',
+ 'Unable to create this project restriction.' => 'Impossible de créer cette restriction de projet.',
+ 'Project restriction removed successfully.' => 'Restriction de projet supprimée avec succès.',
+ 'You cannot create tasks in this column.' => 'Vous ne pouvez pas créer de tâche dans cette colonne.',
+ 'Task creation is permitted for this column' => 'La création de tâche est permise dans cette colonne',
+ 'Closing or opening a task is permitted for this column' => 'Fermer ou ouvrir une tâche est permis pour cette colonne',
+ 'Task creation is blocked for this column' => 'La création de tâche est bloquée pour cette colonne',
+ 'Closing or opening a task is blocked for this column' => 'Fermer ou ouvrir une tâche est interdit pour cette colonne',
+ 'Task creation is not permitted' => 'La création de tâche ne pas permise',
+ 'Closing or opening a task is not permitted' => 'Fermer ou ouvrir une tâche n\'est pas autorisé',
+ 'New drag and drop restriction for the role "%s"' => 'Nouvelle restriction de glisser-déposer pour le rôle « %s »',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Les gens qui font partis de ce rôle pourrons seulement déplaçer des tâches entre la colonne source et de destination.',
+ 'Remove a column restriction' => 'Supprimer une restriction de colonne',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Voulez-vous vraiment supprimer cette restriction de colonne : « %s » vers « %s » ?',
+ 'New column restriction for the role "%s"' => 'Nouvelle restriction de colonne pour le rôle « %s »',
+ 'Rule' => 'Règle',
+ 'Do you really want to remove this column restriction?' => 'Voulez-vous vraiment supprimer cette restriction de colonne ?',
+ 'Custom roles' => 'Rôles personnalisés',
+ 'New custom project role' => 'Nouveau rôle de projet personnalisé',
+ 'Edit custom project role' => 'Modifier un rôle de projet personnalisé',
+ 'Remove a custom role' => 'Supprimer un rôle personnalisé',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Voulez-vous vraiment supprimer ce rôle personnalisé « %s » ? Tous les gens assignés à ce rôle deviendrons membre du projet.',
+ 'There is no custom role for this project.' => 'Il n\'y a aucun rôle personnalisé pour ce projet.',
+ 'New project restriction for the role "%s"' => 'Nouvelle restriction de projet pour le rôle « %s »',
+ 'Restriction' => 'Restriction',
+ 'Remove a project restriction' => 'Supprimer une restriction de project',
+ 'Do you really want to remove this project restriction: "%s"?' => 'Voulez-vous vraiment supprimer cette restriction de projet : « %s » ?',
+ 'Duplicate to multiple projects' => 'Dupliquer vers plusieurs projets',
+ 'This field is required' => 'Ce champ est requis',
+ 'Moving a task is not permitted' => 'Déplaçer une tâche n\'est pas autorisé',
+ 'This value must be in the range %d to %d' => 'Cette valeur doit être définie entre %d et %d',
+ 'You are not allowed to move this task.' => 'Vous n\'êtes pas autorisé à déplacer cette tâche.',
+ 'API User Access' => 'Accès utilisateur de l\'API',
+ 'Preview' => 'Aperçu',
+ 'Write' => 'Écrire',
+ 'Write your text in Markdown' => 'Écrivez votre texte en Markdown',
+ 'New External Task: %s' => 'Nouvelle tâche externe : %s',
+ 'No personal API access token registered.' => 'Aucun jeton d\'accès personnel à l\'API enregistré.',
+ 'Your personal API access token is "%s"' => 'Votre jeton d\'accès personnel à l\'API est « %s »',
+ 'Remove your token' => 'Supprimer votre jeton',
+ 'Generate a new token' => 'Générer un nouveau jeton',
+ 'Showing %d-%d of %d' => 'Éléments %d à %d sur %d',
+ 'Outgoing Emails' => 'Emails sortants',
+ 'Add or change currency rate' => 'Ajouter ou changer le taux de change',
+ 'Reference currency: %s' => 'Monnaie de référence : %s',
+ 'Add custom filters' => 'Ajouter un filtre personnalisé',
+ 'Export' => 'Exporter',
+ 'Add link label' => 'Ajouter un libellé de lien',
+ 'Incompatible Plugins' => 'Extensions incompatibles',
+ 'Compatibility' => 'Compatibilité',
+ 'Permissions and ownership' => 'Permissions et propriétaire',
+ 'Priorities' => 'Priorités',
+ 'Close this window' => 'Fermer cette fenêtre',
+ 'Unable to upload this file.' => 'Impossible de téléverser ce fichier.',
+ 'Import tasks' => 'Importer des tâches',
+ 'Choose a project' => 'Choisir un projet',
+ 'Profile' => 'Profil',
+ 'Application role' => 'Rôle dans l\'application',
+ '%d invitations were sent.' => '%d invitations ont été envoyées.',
+ '%d invitation was sent.' => '%d invitation a été envoyée.',
+ 'Unable to create this user.' => 'Impossible de créer cet utilisateur.',
+ 'Kanboard Invitation' => 'Invitation pour Kanboard',
+ 'Visible on dashboard' => 'Visible sur le tableau de bord',
+ 'Created at:' => 'Créé le :',
+ 'Updated at:' => 'Mis à jour le :',
+ 'There is no custom filter.' => 'Il n\'y a aucun filtre personnalisé.',
+ 'New User' => 'Nouvel utilisateur',
+ 'Authentication' => 'Authentification',
+ 'If checked, this user will use a third-party system for authentication.' => 'Si coché, cet utilisateur va utiliser un système externe pour s\'authentifier.',
+ 'The password is necessary only for local users.' => 'Le mot de passe est nécessaire uniquement pour les utilisateurs locaux.',
+ 'You have been invited to register on Kanboard.' => 'Vous avez été invité à vous inscrire sur Kanboard.',
+ 'Click here to join your team' => 'Cliquez ici pour rejoindre votre équipe',
+ 'Invite people' => 'Inviter des gens',
+ 'Emails' => 'Emails',
+ 'Enter one email address by line.' => 'Entrez une adresse électronique par ligne.',
+ 'Add these people to this project' => 'Ajouter ces personnes à ce projet',
+ 'Add this person to this project' => 'Ajouter cet utilisateur à ce projet',
+ 'Sign-up' => 'Inscription',
+ 'Credentials' => 'Informations d\'identification',
+ 'New user' => 'Nouvel utilisateur',
+ 'This username is already taken' => 'Ce nom d\'utilisateur est déjà pris',
);
diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php
index 9acdbd1a..4b8dedbe 100644
--- a/app/Locale/hu_HU/translations.php
+++ b/app/Locale/hu_HU/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d feladat a táblán',
'%d tasks in total' => 'Összesen %d feladat',
'Unable to update this board.' => 'Nem lehet frissíteni a táblát.',
- 'Edit board' => 'Tábla szerkesztése',
'Disable' => 'Letiltás',
'Enable' => 'Engedélyezés',
'New project' => 'Új projekt',
'Do you really want to remove this project: "%s"?' => 'Valóban törölni akarja ezt a projektet: "%s"?',
'Remove project' => 'Projekt törlése',
'Edit the board for "%s"' => 'Tábla szerkesztése: "%s"',
- 'All projects' => 'Minden projekt',
'Add a new column' => 'Új oszlop',
'Title' => 'Cím',
'Assigned to %s' => 'Felelős: %s',
'Remove a column' => 'Oszlop törlése',
- 'Remove a column from a board' => 'Oszlop törlése a tábláról',
'Unable to remove this column.' => 'Az oszlop törlése nem lehetséges.',
'Do you really want to remove this column: "%s"?' => 'Valóban törölni akarja ezt az oszlopot: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Az oszlophoz rendelt ÖSSZES FELADAT TÖRLŐDNI FOG!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM parancs)',
'(Gzip compressed Sqlite file)' => '(Gzip tömörített SQLite fájl)',
'Close a task' => 'Feladat lezárása',
- 'Edit a task' => 'Feladat módosítása',
'Column' => 'Oszlop',
'Color' => 'Szín',
'Assignee' => 'Felelős',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Feladatok száma',
'User' => 'Felhasználó',
'Comments' => 'Hozzászólások',
- 'Leave a comment' => 'Írjon hozzászólást ...',
'Comment is required' => 'A hozzászólás mező kötelező',
- 'Leave a description' => 'Írjon leírást ...',
'Comment added successfully.' => 'Hozzászólás sikeresen elküldve.',
'Unable to create your comment.' => 'Hozzászólás létrehozása nem lehetséges.',
'Due Date' => 'Határidő',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Keresés',
'Nothing found.' => 'Nincs találat.',
'Due date' => 'Határidő',
- 'Others formats accepted: %s and %s' => 'Egyéb érvényes formátumok: "%s" és "%s"',
'Description' => 'Leírás',
'%d comments' => '%d megjegyzés',
'%d comment' => '%d megjegyzés',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Feladat ID',
'Creator' => 'Készítette',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Távoli',
'Enabled' => 'Engedélyezve',
'Disabled' => 'Letiltva',
- 'Username:' => 'Felhasználónév:',
- 'Name:' => 'Név:',
+ 'Login:' => 'Felhasználónév:',
+ 'Full Name:' => 'Név:',
'Email:' => 'E-mail:',
'Notifications:' => 'Értesítések:',
'Notifications' => 'Értesítések',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Hivatkozás címkék',
'Link modification' => 'Hivatkozás módosítás',
'Links' => 'Hivatkozások',
- 'Link settings' => 'Hivatkozás beállítasok',
'Opposite label' => 'Ellenkező címke',
'Remove a link' => 'Hivatkozás törlése',
- 'Task\'s links' => 'Feladat hivatkozások',
'The labels must be different' => 'A címkék nem lehetnek azonosak',
'There is no link.' => 'Nincs hivatkozás.',
'This label must be unique' => 'A címkének egyedinek kell lennie.',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Kompakt nézet',
'Horizontal scrolling' => 'Vízszintes görgetés',
'Compact/wide view' => 'Kompakt/széles nézet',
- 'No results match:' => 'Nincs találat:',
'Currency' => 'Pénznem',
'Private project' => 'Privát projekt',
'AUD - Australian Dollar' => 'AUD - Ausztrál dollár',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Japán Yen',
'NZD - New Zealand Dollar' => 'NZD - Új-Zélandi dollár',
'RSD - Serbian dinar' => 'RSD - Szerb dínár',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Amerikai dollár',
'Destination column' => 'Cél oszlop',
'Move the task to another column when assigned to a user' => 'Feladat másik oszlopba helyezése felhasználóhoz rendélés után',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Árfolyamok',
'Rate' => 'Árfolyam',
'Change reference currency' => 'A bázis pénznem megváltoztatása',
- 'Add a new currency rate' => 'Új átváltási árfolyam megadása',
'Reference currency' => 'Bázis pénznem',
'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',
@@ -615,7 +590,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',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
'<30m' => '30p',
'Stop timer' => 'Időmérő leállítása',
'Start timer' => 'Időmérő elindítása',
- 'Add project member' => 'Projekt tag hozzáadása',
'My activity stream' => 'Tevékenységem',
'My calendar' => 'Naptáram',
'Search tasks' => 'Feladatok közötti keresés',
@@ -758,8 +725,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.',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Távoli felhasználó',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'A távoli felhasználók jelszava nem a Kanboard adatbázisban van tárolva. Példák: LDAP, Google és GitHub számlák.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ha bekattintja a "Bejelentkezési ablak tiltása" jelölőnégyzetet, akkor a login ablakban megadott jelszó nem lesz figyelembe véve.',
- 'New remote user' => 'Új távoli felhasználó',
- 'New local user' => 'Új helyi felhasználó',
'Default task color' => 'A feladathoz rendelt alapszín',
'This feature does not work with all browsers.' => 'Ez a jellemző nem minden böngészőben működik.',
'There is no destination project available.' => 'Nincs ilyen cél projekt.',
@@ -800,7 +763,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',
@@ -841,8 +803,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.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Tulajdonos',
'Unread notifications' => 'Olvasatlan értesítések',
'Notification methods:' => 'Értesítési módszerek:',
- 'Import tasks from CSV file' => 'Feladatok beolvasása CSV fájlból',
'Unable to read your file' => 'A fájl nem olvasható',
'%d task(s) have been imported successfully.' => '%d feladat sikeresen feldolgozva.',
'Nothing have been imported!' => 'Nem történt beolvasás!',
@@ -947,7 +906,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.',
@@ -968,13 +926,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',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'A projekt tulajdonosa: ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'A projekt azonosító opcionális, és kötelezően alfanumerikus karakterekből áll, pl: MYPROJECT.',
'Project owner' => 'Projekt tulajdonos',
- 'Those dates are useful for the project Gantt chart.' => 'Ezek a dátumok a projekt Gantt diagramjához hasznosak.',
'Private projects do not have users and groups management.' => 'A privát projektekhez nem tartozik felhasználó kezelés és csoport kezelés.',
'There is no project member.' => 'A projektnek nincs tagja.',
'Priority' => 'Prioritás',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Elindult:',
'Moved:' => 'Elmozgatva:',
'Task #%d' => '#%d. feladat',
- 'Date and time format' => 'Dátum és idő formátum',
'Time format' => 'Idő formátum',
'Start date: ' => 'Kezdő datum: ',
'End date: ' => 'Vég dátum: ',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'A felhaszáló sikeresen le lett tiltva.',
'Unable to disable this user.' => 'Nem sikerült a felhasználó letiltása.',
'All files have been uploaded successfully.' => 'Az összes fájl sikeresen feltöltődött.',
- 'View uploaded files' => 'A feltöltött fájlok megtekintése',
'The maximum allowed file size is %sB.' => 'A fájl max. megengedett mérete %s bájt',
- 'Choose files again' => 'Válasszon újból fájlt',
'Drag and drop your files here' => 'Fogdd-és-vidd módszerrel dobja ide a fájlt',
'choose files' => 'válasszon fájlt',
'View profile' => 'Profil megtekintés',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/id_ID/translations.php b/app/Locale/id_ID/translations.php
index d8c42cf4..45c93b06 100644
--- a/app/Locale/id_ID/translations.php
+++ b/app/Locale/id_ID/translations.php
@@ -5,7 +5,7 @@ return array(
'number.thousands_separator' => ' ',
'None' => 'Tidak satupun',
'edit' => 'modifikasi',
- 'Edit' => 'Modifikasi',
+ 'Edit' => 'Edit',
'remove' => 'hapus',
'Remove' => 'Hapus',
'Yes' => 'Ya',
@@ -30,11 +30,11 @@ return array(
'Amber' => 'Amber',
'Save' => 'Simpan',
'Login' => 'Masuk',
- 'Official website:' => 'Situs resmi :',
+ 'Official website:' => 'Situs resmi:',
'Unassigned' => 'Belum ditugaskan',
'View this task' => 'Lihat tugas ini',
'Remove user' => 'Hapus pengguna',
- 'Do you really want to remove this user: "%s"?' => 'Anda yakin akan menghapus pengguna ini : « %s » ?',
+ 'Do you really want to remove this user: "%s"?' => 'Anda yakin mau menghapus pengguna ini: "%s"?',
'All users' => 'Semua pengguna',
'Username' => 'Nama pengguna',
'Password' => 'Kata sandi',
@@ -44,10 +44,10 @@ return array(
'No user' => 'Tidak ada pengguna',
'Forbidden' => 'Terlarang',
'Access Forbidden' => 'Akses Dilarang',
- 'Edit user' => 'Rubah Pengguna',
+ 'Edit user' => 'Edit pengguna',
'Logout' => 'Keluar',
- 'Bad username or password' => 'Nama pengguna atau kata sandri buruk',
- 'Edit project' => 'Rubah proyek',
+ 'Bad username or password' => 'Nama pengguna atau password salah',
+ 'Edit project' => 'Edit proyek',
'Name' => 'Nama',
'Projects' => 'Proyek',
'No project' => 'Tidak ada proyek',
@@ -60,73 +60,69 @@ return array(
'Active' => 'Aktif',
'%d tasks on the board' => '%d tugas di papan',
'%d tasks in total' => '%d tugas di total',
- 'Unable to update this board.' => 'Tidak dapat memperbaharui papan ini',
- 'Edit board' => 'Rubah papan',
+ 'Unable to update this board.' => 'Tidak dapat memperbarui papan ini',
'Disable' => 'Nonaktifkan',
'Enable' => 'Aktifkan',
- 'New project' => 'Proyek Baru',
- 'Do you really want to remove this project: "%s"?' => 'Apakah anda yakin akan menghapus proyek ini : « %s » ?',
+ 'New project' => 'Proyek baru',
+ 'Do you really want to remove this project: "%s"?' => 'Apakah Anda yakin mau menghapus proyek ini: "%s"?',
'Remove project' => 'Hapus proyek',
- 'Edit the board for "%s"' => 'Rubah papan untuk « %s »',
- 'All projects' => 'Semua proyek',
+ 'Edit the board for "%s"' => 'Edit papan untuk "%s"',
'Add a new column' => 'Tambah kolom baru',
'Title' => 'Judul',
- 'Assigned to %s' => 'Ditugaskan ke %s',
+ 'Assigned to %s' => 'Ditugaskan kepada %s',
'Remove a column' => 'Hapus kolom',
- 'Remove a column from a board' => 'Hapus kolom dari papan',
'Unable to remove this column.' => 'Tidak dapat menghapus kolom ini.',
- 'Do you really want to remove this column: "%s"?' => 'Apakah anda yakin akan menghapus kolom ini : « %s » ?',
- 'This action will REMOVE ALL TASKS associated to this column!' => 'tindakan ini akan MENGHAPUS SEMUA TUGAS yang terkait dengan kolom ini!',
+ 'Do you really want to remove this column: "%s"?' => 'Apakah Anda yakin mau menghapus kolom ini: "%s"?',
+ 'This action will REMOVE ALL TASKS associated to this column!' => 'Tindakan ini akan MENGHAPUS SEMUA TUGAS yang berkaitan dengan kolom ini!',
'Settings' => 'Pengaturan',
'Application settings' => 'Pengaturan aplikasi',
'Language' => 'Bahasa',
- 'Webhook token:' => 'Token webhook :',
- 'API token:' => 'Token API :',
- 'Database size:' => 'Ukuran basis data :',
- 'Download the database' => 'Unduh basis data',
- 'Optimize the database' => 'Optimasi basis data',
- '(VACUUM command)' => '(perintah VACUUM)',
+ 'Webhook token:' => 'Token Webhook:',
+ 'API token:' => 'Token API:',
+ 'Database size:' => 'Ukuran database:',
+ 'Download the database' => 'Unduh database',
+ 'Optimize the database' => 'Optimasi database',
+ '(VACUUM command)' => '(Perintah VACUUM)',
'(Gzip compressed Sqlite file)' => '(File Sqlite yang terkompress Gzip)',
'Close a task' => 'Tutup tugas',
- 'Edit a task' => 'Edit tugas',
'Column' => 'Kolom',
'Color' => 'Warna',
'Assignee' => 'Orang yang ditugaskan',
'Create another task' => 'Buat tugas lain',
'New task' => 'Tugas baru',
'Open a task' => 'Buka tugas',
- 'Do you really want to open this task: "%s"?' => 'Apakah anda yakin akan membuka tugas ini : « %s » ?',
+ 'Do you really want to open this task: "%s"?' => 'Apakah Anda yakin mau membuka tugas ini: "%s"?',
'Back to the board' => 'Kembali ke papan',
'There is nobody assigned' => 'Tidak ada orang yand ditugaskan',
- 'Column on the board:' => 'Kolom di dalam papan : ',
+ 'Column on the board:' => 'Kolom di dalam papan:',
'Close this task' => 'Tutup tugas ini',
'Open this task' => 'Buka tugas ini',
'There is no description.' => 'Tidak ada deskripsi.',
'Add a new task' => 'Tambah tugas baru',
- 'The username is required' => 'nama pengguna diperlukan',
+ 'The username is required' => 'Nama pengguna dibutuhkan',
'The maximum length is %d characters' => 'Panjang maksimum adalah %d karakter',
'The minimum length is %d characters' => 'Panjang minimum adalah %d karakter',
- 'The password is required' => 'Kata sandi diperlukan',
+ 'The password is required' => 'Password dibutuhkan',
'This value must be an integer' => 'Nilai ini harus integer',
'The username must be unique' => 'Nama pengguna harus unik',
- 'The user id is required' => 'Id Pengguna diperlukan',
- 'Passwords don\'t match' => 'Kata sandi tidak cocok',
+ 'The user id is required' => 'ID pengguna diperlukan',
+ 'Passwords don\'t match' => 'Password tidak cocok',
'The confirmation is required' => 'Konfirmasi diperlukan',
'The project is required' => 'Proyek diperlukan',
- 'The id is required' => 'Id diperlukan',
- 'The project id is required' => 'Id proyek diperlukan',
+ 'The id is required' => 'ID diperlukan',
+ 'The project id is required' => 'ID proyek diperlukan',
'The project name is required' => 'Nama proyek diperlukan',
'The title is required' => 'Judul diperlukan',
'Settings saved successfully.' => 'Pengaturan berhasil disimpan.',
'Unable to save your settings.' => 'Tidak dapat menyimpan pengaturan anda.',
- 'Database optimization done.' => 'Optimasi basis data selesai.',
+ 'Database optimization done.' => 'Optimasi database selesai.',
'Your project have been created successfully.' => 'Proyek anda berhasil dibuat.',
'Unable to create your project.' => 'Tidak dapat membuat proyek anda.',
- 'Project updated successfully.' => 'Proyek berhasil diperbaharui.',
- 'Unable to update this project.' => 'Tidak dapat memperbaharui proyek ini.',
+ 'Project updated successfully.' => 'Proyek berhasil diperbarui.',
+ 'Unable to update this project.' => 'Tidak dapat memperbarui proyek ini.',
'Unable to remove this project.' => 'Tidak dapat menghapus proyek ini.',
'Project removed successfully.' => 'Proyek berhasil dihapus.',
- 'Project activated successfully.' => 'Proyek berhasil diaktivasi.',
+ 'Project activated successfully.' => 'Proyek berhasil diaktifkan.',
'Unable to activate this project.' => 'Tidak dapat mengaktifkan proyek ini.',
'Project disabled successfully.' => 'Proyek berhasil dinonaktifkan.',
'Unable to disable this project.' => 'Tidak dapat menonaktifkan proyek ini.',
@@ -134,61 +130,59 @@ return array(
'Task opened successfully.' => 'Tugas berhasil dibuka.',
'Unable to close this task.' => 'Tidak dapat menutup tugas ini.',
'Task closed successfully.' => 'Tugas berhasil ditutup.',
- 'Unable to update your task.' => 'Tidak dapat memperbaharui tugas ini.',
- 'Task updated successfully.' => 'Tugas berhasil diperbaharui.',
+ 'Unable to update your task.' => 'Tidak dapat memperbarui tugas ini.',
+ 'Task updated successfully.' => 'Tugas berhasil diperbarui.',
'Unable to create your task.' => 'Tidak dapat membuat tugas anda.',
'Task created successfully.' => 'Tugas berhasil dibuat.',
'User created successfully.' => 'Pengguna berhasil dibuat.',
- 'Unable to create your user.' => 'Tidak dapat membuat pengguna anda.',
- 'User updated successfully.' => 'Pengguna berhasil diperbaharui.',
- 'Unable to update your user.' => 'Tidak dapat memperbaharui pengguna anda.',
- 'User removed successfully.' => 'pengguna berhasil dihapus.',
+ 'Unable to create your user.' => 'Tidak dapat membuat pengguna Anda.',
+ 'User updated successfully.' => 'Pengguna berhasil diperbarui.',
+ 'Unable to update your user.' => 'Tidak dapat memperbarui pengguna anda.',
+ 'User removed successfully.' => 'Pengguna berhasil dihapus.',
'Unable to remove this user.' => 'Tidak dapat menghapus pengguna ini.',
'Board updated successfully.' => 'Papan berhasil diperbaharui.',
'Ready' => 'Siap',
'Backlog' => 'Tertunda',
'Work in progress' => 'Sedang dalam pengerjaan',
'Done' => 'Selesai',
- 'Application version:' => 'Versi aplikasi :',
- 'Id' => 'Id.',
+ 'Application version:' => 'Versi aplikasi:',
+ 'Id' => 'ID',
'%d closed tasks' => '%d tugas yang ditutup',
'No task for this project' => 'Tidak ada tugas dalam proyek ini',
'Public link' => 'Tautan publik',
'Timezone' => 'Zona waktu',
- 'Sorry, I didn\'t find this information in my database!' => 'Maaf, saya tidak menemukan informasi ini dalam basis data saya !',
+ 'Sorry, I didn\'t find this information in my database!' => 'Maaf, saya tidak dapat menemukan informasi ini dalam database saya!',
'Page not found' => 'Halaman tidak ditemukan',
'Complexity' => 'Kompleksitas',
- 'Task limit' => 'Batas tugas.',
+ 'Task limit' => 'Batas tugas',
'Task count' => 'Jumlah tugas',
'User' => 'Pengguna',
'Comments' => 'Komentar',
- 'Leave a comment' => 'Tinggalkan komentar',
- 'Comment is required' => 'Komentar diperlukan',
- 'Leave a description' => 'Tinggalkan deskripsi',
+ 'Comment is required' => 'Komentar dibutuhkan',
'Comment added successfully.' => 'Komentar berhasil ditambahkan.',
- 'Unable to create your comment.' => 'Tidak dapat menambahkan komentar anda.',
+ 'Unable to create your comment.' => 'Tidak dapat menambahkan komentar Anda.',
'Due Date' => 'Batas Tanggal Terakhir',
- 'Invalid date' => 'Tanggal tidak valid',
+ 'Invalid date' => 'Tanggal tidak sesuai',
'Automatic actions' => 'Tindakan otomatis',
- 'Your automatic action have been created successfully.' => 'Tindakan otomatis anda berhasil dibuat.',
- 'Unable to create your automatic action.' => 'Tidak dapat membuat tindakan otomatis anda.',
+ 'Your automatic action have been created successfully.' => 'Tindakan otomatis Anda berhasil dibuat.',
+ 'Unable to create your automatic action.' => 'Tidak dapat membuat tindakan otomatis Anda.',
'Remove an action' => 'Hapus tindakan',
- 'Unable to remove this action.' => 'Tidak dapat menghapus tindakan ini',
+ 'Unable to remove this action.' => 'Tidak dapat menghapus tindakan ini.',
'Action removed successfully.' => 'Tindakan berhasil dihapus.',
- 'Automatic actions for the project "%s"' => 'Tindakan otomatis untuk proyek ini « %s »',
+ 'Automatic actions for the project "%s"' => 'Tindakan otomatis untuk proyek ini "%s"',
'Add an action' => 'Tambah tindakan',
'Event name' => 'Nama acara',
'Action name' => 'Nama tindakan',
'Action parameters' => 'Parameter tindakan',
'Action' => 'Tindakan',
'Event' => 'Acara',
- 'When the selected event occurs execute the corresponding action.' => 'Ketika acara yang dipilih terjadi, melakukan tindakan yang sesuai.',
+ 'When the selected event occurs execute the corresponding action.' => 'Ketika acara yang dipilih terjadi, tindakan yang berhubungan dengan acara akan dieksekusi.',
'Next step' => 'Langkah selanjutnya',
'Define action parameters' => 'Definisi parameter tindakan',
- 'Do you really want to remove this action: "%s"?' => 'Apakah anda yakin akan menghapus tindakan ini « %s » ?',
+ 'Do you really want to remove this action: "%s"?' => 'Apakah Anda yakin mau menghapus tindakan ini: "%s"?',
'Remove an automatic action' => 'Hapus tindakan otomatis',
- 'Assign the task to a specific user' => 'Menetapkan tugas untuk pengguna tertentu',
- 'Assign the task to the person who does the action' => 'Memberikan tugas untuk orang yang melakukan tindakan',
+ 'Assign the task to a specific user' => 'Berikan tugas pada pengguna tertentu',
+ 'Assign the task to the person who does the action' => 'Berikan tugas pada orang yang melakukan tindakan',
'Duplicate the task to another project' => 'Duplikasi tugas ke proyek lain',
'Move a task to another column' => 'Pindahkan tugas ke kolom lain',
'Task modification' => 'Modifikasi tugas',
@@ -198,65 +192,64 @@ return array(
'Column title' => 'Judul kolom',
'Position' => 'Posisi',
'Duplicate to another project' => 'Duplikasi ke proyek lain',
- 'Duplicate' => 'Duplikasi',
+ 'Duplicate' => 'Duplikat',
'link' => 'tautan',
- 'Comment updated successfully.' => 'Komentar berhasil diperbaharui.',
- 'Unable to update your comment.' => 'Tidak dapat memperbaharui komentar anda.',
+ 'Comment updated successfully.' => 'Komentar berhasil diperbarui.',
+ 'Unable to update your comment.' => 'Tidak dapat memperbarui komentar Anda.',
'Remove a comment' => 'Hapus komentar',
'Comment removed successfully.' => 'Komentar berhasil dihapus.',
'Unable to remove this comment.' => 'Tidak dapat menghapus komentar ini.',
- 'Do you really want to remove this comment?' => 'Apakah anda yakin akan menghapus komentar ini ?',
- 'Current password for the user "%s"' => 'Kata sandi saat ini untuk pengguna « %s »',
- 'The current password is required' => 'Kata sandi saat ini diperlukan',
- 'Wrong password' => 'Kata sandi salah',
+ 'Do you really want to remove this comment?' => 'Apakah Anda yakin mau menghapus komentar ini?',
+ 'Current password for the user "%s"' => 'Password saat ini untuk pengguna "%s"',
+ 'The current password is required' => 'Password saat ini diperlukan',
+ 'Wrong password' => 'Password salah',
'Unknown' => 'Tidak diketahui',
'Last logins' => 'Masuk terakhir',
'Login date' => 'Tanggal masuk',
'Authentication method' => 'Metode otentifikasi',
'IP address' => 'Alamat IP',
- 'User agent' => 'Agen Pengguna',
- 'Persistent connections' => 'Koneksi persisten',
+ 'User agent' => 'Agen pengguna',
+ 'Persistent connections' => 'Koneksi tetap',
'No session.' => 'Tidak ada sesi.',
'Expiration date' => 'Tanggal kadaluarsa',
'Remember Me' => 'Ingat Saya',
- 'Creation date' => 'Tanggal dibuat',
+ 'Creation date' => 'Tanggal pembuatan',
'Everybody' => 'Semua orang',
'Open' => 'Terbuka',
'Closed' => 'Ditutup',
'Search' => 'Cari',
'Nothing found.' => 'Tidak ditemukan.',
'Due date' => 'Batas tanggal terakhir',
- 'Others formats accepted: %s and %s' => 'Format lain yang didukung : %s et %s',
'Description' => 'Deskripsi',
'%d comments' => '%d komentar',
'%d comment' => '%d komentar',
- 'Email address invalid' => 'Alamat email tidak valid',
- 'Your external account is not linked anymore to your profile.' => 'Akun eksternal anda tidak lagi terhubung ke profil anda.',
- 'Unable to unlink your external account.' => 'Tidak dapat memutuskan akun eksternal anda.',
+ 'Email address invalid' => 'Alamat email tidak sesuai',
+ 'Your external account is not linked anymore to your profile.' => 'Akun eksternal Anda tidak lagi terhubung ke profil anda.',
+ 'Unable to unlink your external account.' => 'Tidak dapat memutuskan akun eksternal Anda.',
'External authentication failed' => 'Otentifikasi eksternal gagal',
- 'Your external account is linked to your profile successfully.' => 'Akun eksternal anda berhasil dihubungkan ke profil anda.',
+ 'Your external account is linked to your profile successfully.' => 'Akun eksternal Anda berhasil dihubungkan ke profil anda.',
'Email' => 'Email',
'Task removed successfully.' => 'Tugas berhasil dihapus.',
'Unable to remove this task.' => 'Tidak dapat menghapus tugas ini.',
'Remove a task' => 'Hapus tugas',
- 'Do you really want to remove this task: "%s"?' => 'Apakah anda yakin akan menghapus tugas ini « %s » ?',
+ 'Do you really want to remove this task: "%s"?' => 'Apakah Anda yakin mau menghapus tugas ini: "%s"?',
'Assign automatically a color based on a category' => 'Otomatis menetapkan warna berdasarkan kategori',
'Assign automatically a category based on a color' => 'Otomatis menetapkan kategori berdasarkan warna',
- 'Task creation or modification' => 'Tugas dibuat atau di mofifikasi',
+ 'Task creation or modification' => 'Tugas dibuat atau di modifikasi',
'Category' => 'Kategori',
- 'Category:' => 'Kategori :',
+ 'Category:' => 'Kategori:',
'Categories' => 'Kategori',
- '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.',
- 'Unable to update your category.' => 'Tidak dapat memperbaharui kategori anda.',
+ '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 diperbarui.',
+ 'Unable to update your category.' => 'Tidak dapat memperbarui kategori Anda.',
'Remove a category' => 'Hapus kategori',
'Category removed successfully.' => 'Kategori berhasil dihapus.',
'Unable to remove this category.' => 'Tidak dapat menghapus kategori ini.',
- 'Category modification for the project "%s"' => 'Modifikasi kategori untuk proyek « %s »',
+ 'Category modification for the project "%s"' => 'Modifikasi kategori untuk proyek "%s"',
'Category Name' => 'Nama Kategori',
'Add a new category' => 'Tambah kategori baru',
- 'Do you really want to remove this category: "%s"?' => 'Apakah anda yakin akan menghapus kategori ini « %s » ?',
+ 'Do you really want to remove this category: "%s"?' => 'Apakah Anda yakin mau menghapus kategori ini: "%s"?',
'All categories' => 'Semua kategori',
'No category' => 'Tidak ada kategori',
'The name is required' => 'Nama diperlukan',
@@ -264,46 +257,44 @@ return array(
'Unable to remove this file.' => 'Tidak dapat menghapus berkas ini.',
'File removed successfully.' => 'Berkas berhasil dihapus.',
'Attach a document' => 'Lampirkan dokumen',
- 'Do you really want to remove this file: "%s"?' => 'Apakah anda yakin akan menghapus berkas ini « %s » ?',
+ '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 task' => 'Edit tugas',
'Add a comment' => 'Tambahkan komentar',
- 'Edit a comment' => 'Modifikasi komentar',
+ 'Edit a comment' => 'Edit komentar',
'Summary' => 'Ringkasan',
'Time tracking' => 'Pelacakan waktu',
- 'Estimate:' => 'Estimasi :',
+ 'Estimate:' => 'Estimasi:',
'Spent:' => 'Menghabiskan:',
- 'Do you really want to remove this sub-task?' => 'Apakah anda yakin akan menghapus sub-tugas ini ?',
+ 'Do you really want to remove this sub-task?' => 'Apakah Anda yakin mau menghapus sub-tugas ini?',
'Remaining:' => 'Tersisa:',
'hours' => 'jam',
- 'spent' => 'menghabiskan',
+ 'spent' => 'dihabiskan',
'estimated' => 'perkiraan',
'Sub-Tasks' => 'Sub-tugas',
'Add a sub-task' => 'Tambahkan sub-tugas',
'Original estimate' => 'Perkiraan semula',
'Create another sub-task' => 'Tambahkan sub-tugas lainnya',
'Time spent' => 'Waktu yang dihabiskan',
- 'Edit a sub-task' => 'Modifikasi sub-tugas',
+ 'Edit a sub-task' => 'Edit sub-tugas',
'Remove a sub-task' => 'Hapus sub-tugas',
- 'The time must be a numeric value' => 'Waktu harus berisikan numerik',
+ 'The time must be a numeric value' => 'Waktu harus berupa angka',
'Todo' => 'Yang harus dilakukan',
- 'In progress' => 'Sedang proses',
+ 'In progress' => 'Dalam proses',
'Sub-task removed successfully.' => 'Sub-tugas berhasil dihapus.',
'Unable to remove this sub-task.' => 'Tidak dapat menghapus sub-tugas.',
- 'Sub-task updated successfully.' => 'Sub-tugas berhasil diperbaharui.',
- 'Unable to update your sub-task.' => 'Tidak dapat memperbaharui sub-tugas anda.',
- 'Unable to create your sub-task.' => 'Tidak dapat membuat sub-tugas anda.',
+ 'Sub-task updated successfully.' => 'Sub-tugas berhasil diperbarui.',
+ 'Unable to update your sub-task.' => 'Tidak dapat memperbarui sub-tugas Anda.',
+ 'Unable to create your sub-task.' => 'Tidak dapat membuat sub-tugas Anda.',
'Sub-task added successfully.' => 'Sub-tugas berhasil dibuat.',
'Maximum size: ' => 'Ukuran maksimum: ',
'Unable to upload the file.' => 'Tidak dapat mengunggah berkas.',
'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',
- 'Task Id' => 'Id Tugas',
+ 'Task Id' => 'ID Tugas',
'Creator' => 'Pembuat',
'Modification date' => 'Tanggal modifikasi',
'Completion date' => 'Tanggal penyelesaian',
@@ -311,25 +302,20 @@ return array(
'Project cloned successfully.' => 'Kloning proyek berhasil.',
'Unable to clone this project.' => 'Tidak dapat mengkloning proyek.',
'Enable email notifications' => 'Aktifkan pemberitahuan dari email',
- 'Task position:' => 'Posisi tugas :',
+ 'Task position:' => 'Posisi tugas:',
'The task #%d have been opened.' => 'Tugas #%d telah dibuka.',
'The task #%d have been closed.' => 'Tugas #%d telah ditutup.',
- 'Sub-task updated' => 'Sub-tugas diperbaharui',
- 'Title:' => 'Judul :',
- 'Status:' => 'Status :',
- 'Assignee:' => 'Ditugaskan ke :',
- 'Time tracking:' => 'Pelacakan waktu :',
+ 'Sub-task updated' => 'Sub-tugas diperbarui',
+ 'Title:' => 'Judul:',
+ 'Status:' => 'Status:',
+ 'Assignee:' => 'Ditugaskan ke:',
+ 'Time tracking:' => 'Pelacakan waktu:',
'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 attachment added "%s"' => 'Lampiran baru ditambahkan "%s"',
+ 'New comment posted by %s' => 'Komentar baru ditambahkan oleh %s',
'New comment' => 'Komentar baru',
- 'Comment updated' => 'Komentar diperbaharui',
+ 'Comment updated' => 'Komentar diperbarui',
'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',
@@ -337,255 +323,245 @@ return array(
'Disable public access' => 'Nonaktifkan akses publik',
'Enable public access' => 'Aktifkan akses publik',
'Public access disabled' => 'Akses publik dinonaktifkan',
- 'Do you really want to disable this project: "%s"?' => 'Apakah anda yakin akan menonaktifkan proyek ini : « %s » ?',
- 'Do you really want to enable this project: "%s"?' => 'Apakah anda yakin akan mengaktifkan proyek ini : « %s » ?',
+ 'Do you really want to disable this project: "%s"?' => 'Apakah Anda yakin mau menonaktifkan proyek ini: "%s"?',
+ 'Do you really want to enable this project: "%s"?' => 'Apakah Anda yakin mau mengaktifkan proyek ini: "%s"?',
'Project activation' => 'Aktivasi proyek',
'Move the task to another project' => 'Pindahkan tugas ke proyek lain',
'Move to another project' => 'Pindahkan ke proyek lain',
- 'Do you really want to duplicate this task?' => 'Apakah anda yakin akan menduplikasi tugas ini ?',
+ 'Do you really want to duplicate this task?' => 'Apakah Anda yakin mau menduplikasi tugas ini?',
'Duplicate a task' => 'Duplikasi tugas',
'External accounts' => 'Akun eksternal',
'Account type' => 'Tipe akun',
'Local' => 'Lokal',
- 'Remote' => 'Jauh',
+ 'Remote' => 'Jarak Jauh',
'Enabled' => 'Aktif',
'Disabled' => 'Nonaktif',
- 'Username:' => 'Nama pengguna :',
- 'Name:' => 'Nama :',
- 'Email:' => 'Email :',
- 'Notifications:' => 'Pemberitahuan :',
+ // 'Login:' => '',
+ // 'Full Name:' => '',
+ 'Email:' => 'Email:',
+ 'Notifications:' => 'Pemberitahuan:',
'Notifications' => 'Pemberitahuan',
- 'Account type:' => 'Tipe akun :',
- 'Edit profile' => 'Modifikasi profil',
- 'Change password' => 'Rubah kata sandri',
- 'Password modification' => 'Modifikasi kata sandi',
+ 'Account type:' => 'Tipe akun:',
+ 'Edit profile' => 'Edit profil',
+ 'Change password' => 'Ganti password',
+ 'Password modification' => 'Modifikasi password',
'External authentications' => 'Otentifikasi eksternal',
'Never connected.' => 'Tidak pernah terhubung.',
'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' => 'Rubah kategori',
- '%s updated the task %s' => '%s memperbaharui tugas %s',
+ 'Password modified successfully.' => 'Password berhasil dimodifikasi.',
+ 'Unable to change the password.' => 'Tidak dapat mengganti kata sandi.',
+ 'Change category' => 'Ganti kategori',
+ '%s updated the task %s' => '%s memperbarui tugas %s',
'%s opened the task %s' => '%s membuka tugas %s',
- '%s moved the task %s to the position #%d in the column "%s"' => '%s memindahkan tugas %s ke posisi n°%d dalam kolom « %s »',
- '%s moved the task %s to the column "%s"' => '%s memindahkan tugas %s ke kolom « %s »',
+ '%s moved the task %s to the position #%d in the column "%s"' => '%s memindahkan tugas %s ke posisi #%d dalam kolom "%s"',
+ '%s moved the task %s to the column "%s"' => '%s memindahkan tugas %s ke kolom "%s"',
'%s created the task %s' => '%s membuat tugas %s',
'%s closed the task %s' => '%s menutup tugas %s',
- '%s created a subtask for the task %s' => '%s membuat subtugas untuk tugas %s',
- '%s updated a subtask for the task %s' => '%s memperbaharui subtugas untuk tugas %s',
- 'Assigned to %s with an estimate of %s/%sh' => 'Ditugaskan untuk %s dengan perkiraan %s/%sh',
+ '%s created a subtask for the task %s' => '%s membuat sub-tugas untuk tugas %s',
+ '%s updated a subtask for the task %s' => '%s memperbarui sub-tugas untuk tugas %s',
+ 'Assigned to %s with an estimate of %s/%sh' => 'Ditugaskan pada %s dengan perkiraan %s/%sh',
'Not assigned, estimate of %sh' => 'Tidak ada yang ditugaskan, perkiraan %sh',
- '%s updated a comment on the task %s' => '%s memperbaharui komentar pada tugas %s',
+ '%s updated a comment on the task %s' => '%s memperbarui komentar pada tugas %s',
'%s commented the task %s' => '%s memberikan komentar pada tugas %s',
'%s\'s activity' => 'Aktifitas dari %s',
- 'RSS feed' => 'RSS feed',
- '%s updated a comment on the task #%d' => '%s memperbaharui komentar pada tugas n°%d',
- '%s commented on the task #%d' => '%s memberikan komentar pada tugas n°%d',
- '%s updated a subtask for the task #%d' => '%s memperbaharui subtugas untuk tugas n°%d',
- '%s created a subtask for the task #%d' => '%s membuat subtugas untuk tugas n°%d',
- '%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 »',
+ 'RSS feed' => 'Umpan RSS',
+ '%s updated a comment on the task #%d' => '%s memperbarui komentar pada tugas #%d',
+ '%s commented on the task #%d' => '%s memberikan komentar pada tugas #%d',
+ '%s updated a subtask for the task #%d' => '%s memperbarui sub-tugas untuk tugas #%d',
+ '%s created a subtask for the task #%d' => '%s membuat sub-tugas untuk tugas #%d',
+ '%s updated the task #%d' => '%s memperbarui tugas #%d',
+ '%s created the task #%d' => '%s membuat tugas #%d',
+ '%s closed the task #%d' => '%s menutup tugas #%d',
+ '%s opened the task #%d' => '%s membuka tugas #%d',
'Activity' => 'Aktifitas',
- 'Default values are "%s"' => 'Standar nilai adalah« %s »',
+ 'Default values are "%s"' => 'Nilai default 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 %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 »',
+ 'Task assignee change' => 'Ganti orang yang ditugaskan',
+ '%s changed the assignee of the task #%d to %s' => '%s mengganti orang yang ditugaskan dari tugas #%d ke %s',
+ '%s changed the assignee of the task %s to %s' => '%s mengganti orang yang ditugaskan dari tugas %s ke %s',
+ 'New password for the user "%s"' => 'Password baru untuk pengguna "%s"',
'Choose an event' => 'Pilih acara',
- 'Create a task from an external provider' => 'Buat tugas dari pemasok eksternal',
- 'Change the assignee based on an external username' => 'Rubah penugasan berdasarkan nama pengguna eksternal',
- 'Change the category based on an external label' => 'Rubah kategori berdasarkan label eksternal',
+ 'Create a task from an external provider' => 'Buat tugas dari penyedia eksternal',
+ 'Change the assignee based on an external username' => 'Ganti penugasan berdasarkan nama pengguna eksternal',
+ 'Change the category based on an external label' => 'Ganti kategori berdasarkan label eksternal',
'Reference' => 'Referensi',
'Label' => 'Label',
- 'Database' => 'Basis data',
+ 'Database' => 'Database',
'About' => 'Tentang',
- 'Database driver:' => 'Driver basis data :',
+ 'Database driver:' => 'Driver database:',
'Board settings' => 'Pengaturan papan',
- 'Webhook settings' => 'Pengaturan webhook',
- 'Reset token' => 'Mereset token',
+ 'Webhook settings' => 'Pengaturan Webhook',
+ 'Reset token' => 'Reset token',
'API endpoint:' => 'API endpoint :',
'Refresh interval for private board' => 'Interval pembaruan untuk papan pribadi',
'Refresh interval for public board' => 'Interval pembaruan untuk papan publik',
- 'Task highlight period' => 'Periode puncak tugas',
- 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode (dalam detik) untuk mempertimbangkan tugas yang baru dimodifikasi (0 untuk menonaktifkan, standar 2 hari)',
- 'Frequency in second (60 seconds by default)' => 'Frequensi dalam detik (standar 60 detik)',
- 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequensi dalam detik (0 untuk menonaktifkan fitur ini, standar 10 detik)',
+ 'Task highlight period' => 'Periode penyorotan tugas',
+ 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode (dalam detik) untuk mempertimbangkan tugas yang baru dimodifikasi (0 untuk menonaktifkan, default 2 hari)',
+ 'Frequency in second (60 seconds by default)' => 'Frekuensi dalam detik (default 60 detik)',
+ 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekuensi dalam detik (0 untuk menonaktifkan fitur ini, default 10 detik)',
'Application URL' => 'URL Aplikasi',
'Token regenerated.' => 'Token diregenerasi.',
'Date format' => 'Format tanggal',
- 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO selalu diterima, contoh : « %s » et « %s »',
+ 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO selalu diterima, contoh: "%s" dan "%s"',
'New private project' => 'Proyek pribadi baru',
- 'This project is private' => 'Proyek ini adalah pribadi',
+ 'This project is private' => 'Proyek ini pribadi',
'Add' => 'Tambah',
'Start date' => 'Tanggal mulai',
'Time estimated' => 'Perkiraan waktu',
- 'There is nothing assigned to you.' => 'Tidak ada yang diberikan kepada anda.',
+ 'There is nothing assigned to you.' => 'Tidak ada tugas yang diberikan pada Anda.',
'My tasks' => 'Tugas saya',
'Activity stream' => 'Arus aktifitas',
'Dashboard' => 'Dasbor',
'Confirmation' => 'Konfirmasi',
- 'Allow everybody to access to this project' => 'Memungkinkan semua orang untuk mengakses proyek ini',
+ 'Allow everybody to access to this project' => 'Izinkan semua orang untuk mengakses proyek ini',
'Everybody have access to this project.' => 'Semua orang mendapat akses untuk proyek ini.',
'Webhooks' => 'Webhooks',
'API' => 'API',
- 'Create a comment from an external provider' => 'Buat komentar dari pemasok eksternal',
+ 'Create a comment from an external provider' => 'Buat komentar dari penyedia eksternal',
'Project management' => 'Manajemen proyek',
'My projects' => 'Proyek saya',
'Columns' => 'Kolom',
'Task' => 'Tugas',
- 'Your are not member of any project.' => 'Anda bukan anggota dari setiap proyek.',
+ 'Your are not member of any project.' => 'Anda bukan anggota dari proyek apapun.',
'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',
+ 'Analytics' => 'Analitik',
+ 'Subtask' => 'Sub-tugas',
+ 'My subtasks' => 'Sub-tugas saya',
'User repartition' => 'Partisi ulang pengguna',
- 'User repartition for "%s"' => 'Partisi ulang pengguna untuk « %s »',
- 'Clone this project' => 'Gandakan proyek ini',
+ 'Clone this project' => 'Klon proyek ini',
'Column removed successfully.' => 'Kolom berhasil dihapus.',
'Not enough data to show the graph.' => 'Tidak cukup data untuk menampilkan grafik.',
'Previous' => 'Sebelumnya',
- 'The id must be an integer' => 'Id harus integer',
- 'The project id must be an integer' => 'Id proyek harus integer',
+ 'The id must be an integer' => 'ID harus integer',
+ 'The project id must be an integer' => 'ID proyek harus integer',
'The status must be an integer' => 'Status harus integer',
- 'The subtask id is required' => 'Id subtugas diperlukan',
- 'The subtask id must be an integer' => 'Id subtugas harus integer',
- 'The task id is required' => 'Id tugas diperlukan',
- 'The task id must be an integer' => 'Id tugas harus integer',
- 'The user id must be an integer' => 'Id user harus integer',
+ 'The subtask id is required' => 'ID sub-tugas diperlukan',
+ 'The subtask id must be an integer' => 'ID sub-tugas harus integer',
+ 'The task id is required' => 'ID tugas diperlukan',
+ 'The task id must be an integer' => 'ID tugas harus integer',
+ 'The user id must be an integer' => 'ID user harus integer',
'This value is required' => 'Nilai ini diperlukan',
'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.',
+ 'This export contains the number of tasks per column grouped per day.' => 'Ekspor ini berisi jumlah dari tugas per kolom yang dikelompokan per hari.',
'Active swimlanes' => 'Swimlanes aktif',
'Add a new swimlane' => 'Tambah swimlane baru',
- 'Change default swimlane' => 'Modifikasi standar swimlane',
- 'Default swimlane' => 'Standar swimlane',
- 'Do you really want to remove this swimlane: "%s"?' => 'Apakah anda yakin akan menghapus swimlane ini : « %s » ?',
+ 'Change default swimlane' => 'Ganti swimlane default',
+ 'Default swimlane' => 'Swimlane default',
+ 'Do you really want to remove this swimlane: "%s"?' => 'Apakah Anda yakin mau menghapus swimlane ini: "%s"?',
'Inactive swimlanes' => 'Swimlanes tidak aktif',
- 'Remove a swimlane' => 'Supprimer une swimlane',
- 'Show default swimlane' => 'Perlihatkan standar swimlane',
- 'Swimlane modification for the project "%s"' => 'Modifikasi swimlane untuk proyek « %s »',
+ 'Remove a swimlane' => 'Hapus swimlane',
+ 'Show default swimlane' => 'Lihat swimlane default',
+ 'Swimlane modification for the project "%s"' => 'Modifikasi swimlane untuk proyek "%s"',
'Swimlane removed successfully.' => 'Swimlane berhasil dihapus.',
'Swimlanes' => 'Swimlanes',
- 'Swimlane updated successfully.' => 'Swimlane berhasil diperbaharui.',
- 'The default swimlane have been updated successfully.' => 'Standar swimlane berhasil diperbaharui.',
+ 'Swimlane updated successfully.' => 'Swimlane berhasil diperbarui.',
+ 'The default swimlane have been updated successfully.' => 'Swimlane default berhasil diperbarui.',
'Unable to remove this swimlane.' => 'Tidak dapat menghapus swimlane ini.',
- 'Unable to update this swimlane.' => 'Tidak dapat memperbaharui swimlane ini.',
- 'Your swimlane have been created successfully.' => 'Swimlane anda berhasil dibuat.',
- 'Example: "Bug, Feature Request, Improvement"' => 'Contoh: « Insiden, Permintaan Fitur, Perbaikan »',
- 'Default categories for new projects (Comma-separated)' => 'Standar kategori untuk proyek baru (dipisahkan dengan koma)',
+ 'Unable to update this swimlane.' => 'Tidak dapat memperbarui swimlane ini.',
+ 'Your swimlane have been created successfully.' => 'Swimlane Anda berhasil dibuat.',
+ 'Example: "Bug, Feature Request, Improvement"' => 'Contoh: "Bug, Permintaan Fitur, Peningkatan"',
+ 'Default categories for new projects (Comma-separated)' => 'Kategori default untuk proyek baru (dipisahkan dengan koma)',
'Integrations' => 'Integrasi',
'Integration with third-party services' => 'Integrasi dengan layanan pihak ketiga',
- 'Subtask Id' => 'Id Subtugas',
- 'Subtasks' => 'Subtugas',
- 'Subtasks Export' => 'Ekspor Subtugas',
- 'Subtasks exportation for "%s"' => 'Ekspor subtugas untuk « %s »',
+ 'Subtask Id' => 'ID Sub-tugas',
+ 'Subtasks' => 'Sub-tugas',
+ 'Subtasks Export' => 'Ekspor Sub-tugas',
'Task Title' => 'Judul Tugas',
- 'Untitled' => 'Tanpa nama',
- 'Application default' => 'Aplikasi standar',
- 'Language:' => 'Bahasa :',
- 'Timezone:' => 'Zona waktu :',
+ 'Untitled' => 'Tanpa Nama',
+ 'Application default' => 'Default aplikasi',
+ 'Language:' => 'Bahasa:',
+ 'Timezone:' => 'Zona waktu:',
'All columns' => 'Semua kolom',
'Calendar' => 'Kalender',
'Next' => 'Selanjutnya',
- '#%d' => 'n˚%d',
+ '#%d' => '#%d',
'All swimlanes' => 'Semua swimlane',
'All colors' => 'Semua warna',
'Moved to column %s' => 'Pindah ke kolom %s',
'User dashboard' => 'Dasbor pengguna',
- 'Allow only one subtask in progress at the same time for a user' => 'Izinkan hanya satu subtugas dalam proses secara bersamaan untuk satu pengguna',
- 'Edit column "%s"' => 'Modifikasi kolom « %s »',
- 'Select the new status of the subtask: "%s"' => 'Pilih status baru untuk subtugas : « %s »',
- 'Subtask timesheet' => 'Subtugas absen',
- 'There is nothing to show.' => 'Tidak ada yang dapat diperlihatkan.',
- 'Time Tracking' => 'Pelacakan waktu',
- 'You already have one subtask in progress' => 'Anda sudah ada satu subtugas dalam proses',
- 'Which parts of the project do you want to duplicate?' => 'Bagian dalam proyek mana yang ingin anda duplikasi?',
+ 'Allow only one subtask in progress at the same time for a user' => 'Izinkan hanya satu sub-tugas dalam proses secara bersamaan untuk satu pengguna',
+ 'Edit column "%s"' => 'Edit kolom "%s"',
+ 'Select the new status of the subtask: "%s"' => 'Pilih status baru untuk sub-tugas: "%s"',
+ 'Subtask timesheet' => 'Absen sub-tugas',
+ 'There is nothing to show.' => 'Tidak ada yang bisa diperlihatkan.',
+ 'Time Tracking' => 'Pelacakan Waktu',
+ 'You already have one subtask in progress' => 'Anda sudah memiliki satu sub-tugas dalam proses',
+ 'Which parts of the project do you want to duplicate?' => 'Bagian proyek mana yang ingin Anda duplikasi?',
'Disallow login form' => 'Larang formulir masuk',
'Start' => 'Mulai',
'End' => 'Selesai',
'Task age in days' => 'Usia tugas dalam hari',
'Days in this column' => 'Hari dalam kolom ini',
- '%dd' => '%dj',
+ '%dd' => '%dd',
'Add a new link' => 'Tambah tautan baru',
- 'Do you really want to remove this link: "%s"?' => 'Apakah anda yakin akan menghapus tautan ini : « %s » ?',
- 'Do you really want to remove this link with task #%d?' => 'Apakah anda yakin akan menghapus tautan ini dengan tugas n°%d ?',
- 'Field required' => 'Field diperlukan',
+ 'Do you really want to remove this link: "%s"?' => 'Apakah Anda yakin mau menghapus tautan ini: "%s"?',
+ 'Do you really want to remove this link with task #%d?' => 'Apakah Anda yakin mau menghapus tautan ini dengan tugas #%d ?',
+ 'Field required' => 'Bidang dibutuhkan',
'Link added successfully.' => 'Tautan berhasil ditambahkan.',
- 'Link updated successfully.' => 'Tautan berhasil diperbaharui.',
+ 'Link updated successfully.' => 'Tautan berhasil diperbarui.',
'Link removed successfully.' => 'Tautan berhasil dihapus.',
'Link labels' => 'Label tautan',
'Link modification' => 'Modifikasi tautan',
'Links' => 'Tautan',
- 'Link settings' => 'Pengaturan tautan',
'Opposite label' => 'Label berlawanan',
'Remove a link' => 'Hapus tautan',
- 'Task\'s links' => 'Tautan tugas',
'The labels must be different' => 'Label harus berbeda',
'There is no link.' => 'Tidak ada tautan.',
'This label must be unique' => 'Label ini harus unik',
- 'Unable to create your link.' => 'Tidak dapat membuat tautan anda.',
- 'Unable to update your link.' => 'Tidak dapat memperbaharui tautan anda.',
+ 'Unable to create your link.' => 'Tidak dapat membuat tautan Anda.',
+ 'Unable to update your link.' => 'Tidak dapat memperbarui tautan Anda.',
'Unable to remove this link.' => 'Tidak dapat menghapus tautan ini.',
'relates to' => 'berhubungan dengan',
- 'blocks' => 'blok',
+ 'blocks' => 'blokir',
'is blocked by' => 'diblokir oleh',
'duplicates' => 'duplikat',
'is duplicated by' => 'diduplikasi oleh',
'is a child of' => 'anak dari',
- 'is a parent of' => 'orant tua dari',
- 'targets milestone' => 'milestone target',
- 'is a milestone of' => 'adalah milestone dari',
+ 'is a parent of' => 'induk dari',
+ 'targets milestone' => 'target batu pijakan',
+ 'is a milestone of' => 'adalah batu pijakan dari',
'fixes' => 'perbaikan',
'is fixed by' => 'diperbaiki oleh',
'This task' => 'Tugas ini',
'<1h' => '<1h',
'%dh' => '%dh',
'Expand tasks' => 'Perluas tugas',
- 'Collapse tasks' => 'Lipat tugas',
- 'Expand/collapse tasks' => 'Perluas/lipat tugas',
+ 'Collapse tasks' => 'Tutup tugas',
+ 'Expand/collapse tasks' => 'Perluas/tutup tugas',
'Close dialog box' => 'Tutup kotak dialog',
- 'Submit a form' => 'Submit formulir',
- 'Board view' => 'Table halaman',
- 'Keyboard shortcuts' => 'pintas keyboard',
- 'Open board switcher' => 'Buka table switcher',
+ 'Submit a form' => 'Kirim formulir',
+ 'Board view' => 'Tampilan papan',
+ 'Keyboard shortcuts' => 'Pintasan keyboard',
+ 'Open board switcher' => 'Buka switcher papan',
'Application' => 'Aplikasi',
'Compact view' => 'Tampilan kompak',
- 'Horizontal scrolling' => 'Horisontal bergulir',
- 'Compact/wide view' => 'Beralih antara tampilan kompak dan diperluas',
- 'No results match:' => 'Tidak ada hasil :',
+ 'Horizontal scrolling' => 'Gulir horizontal',
+ 'Compact/wide view' => 'Tampilan kompak/lebar',
'Currency' => 'Mata uang',
'Private project' => 'Proyek pribadi',
- 'AUD - Australian Dollar' => 'AUD - Dollar Australia',
- 'CAD - Canadian Dollar' => 'CAD - Dollar Kanada',
- 'CHF - Swiss Francs' => 'CHF - Swiss Prancis',
- 'Custom Stylesheet' => 'Kustomisasi Stylesheet',
+ 'AUD - Australian Dollar' => 'AUD - Dolar Australia',
+ 'CAD - Canadian Dollar' => 'CAD - Dolar Kanada',
+ 'CHF - Swiss Francs' => 'CHF - Francs Swiss',
+ 'Custom Stylesheet' => 'Kustomisasi CSS',
'download' => 'unduh',
'EUR - Euro' => 'EUR - Euro',
- 'GBP - British Pound' => 'GBP - Poundsterling inggris',
+ 'GBP - British Pound' => 'GBP - Poundsterling Inggris',
'INR - Indian Rupee' => 'INR - Rupe India',
'JPY - Japanese Yen' => 'JPY - Yen Jepang',
- 'NZD - New Zealand Dollar' => 'NZD - Dollar Selandia baru',
+ 'NZD - New Zealand Dollar' => 'NZD - Dolar Selandia baru',
'RSD - Serbian dinar' => 'RSD - Dinar Serbia',
- 'USD - US Dollar' => 'USD - Dollar Amerika',
+ // 'CNY - Chinese Yuan' => '',
+ 'USD - US Dollar' => 'USD - Dolar Amerika',
'Destination column' => 'Kolom tujuan',
- 'Move the task to another column when assigned to a user' => 'Pindahkan tugas ke kolom lain ketika ditugaskan ke pengguna',
- 'Move the task to another column when assignee is cleared' => 'Pindahkan tugas ke kolom lain ketika orang yang ditugaskan dibersihkan',
+ 'Move the task to another column when assigned to a user' => 'Pindahkan tugas ke kolom lain saat ditugaskan ke pengguna',
+ 'Move the task to another column when assignee is cleared' => 'Pindahkan tugas ke kolom lain saat orang yang ditugaskan kosong',
'Source column' => 'Sumber kolom',
'Transitions' => 'Transisi',
'Executer' => 'Eksekusi',
@@ -595,78 +571,76 @@ return array(
'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Laporan ini berisi semua kolom yang pindah untuk setiap tugas dengan tanggal, pengguna dan waktu yang dihabiskan untuk setiap transisi.',
'Currency rates' => 'Nilai tukar mata uang',
'Rate' => 'Tarif',
- 'Change reference currency' => 'Mengubah referensi mata uang',
- 'Add a new currency rate' => 'Tambahkan nilai tukar mata uang baru',
+ 'Change reference currency' => 'Ganti referensi mata uang',
'Reference currency' => 'Referensi mata uang',
'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',
- 'Enable Gravatar images' => 'Mengaktifkan gambar Gravatar',
+ 'Webhook URL' => 'URL Webhook',
+ '%s removed the assignee of the task %s' => '%s menghapus penugasan dari tugas %s',
+ 'Enable Gravatar images' => 'Aktifkan gambar Gravatar',
'Information' => 'Informasi',
'Check two factor authentication code' => 'Cek dua faktor kode otentifikasi',
- 'The two factor authentication code is not valid.' => 'Kode dua faktor kode otentifikasi tidak valid.',
- 'The two factor authentication code is valid.' => 'Kode dua faktor kode otentifikasi valid.',
+ 'The two factor authentication code is not valid.' => 'Kode dua faktor kode otentifikasi tidak sesuai.',
+ 'The two factor authentication code is valid.' => 'Kode dua faktor kode otentifikasi sesuai.',
'Code' => 'Kode',
'Two factor authentication' => 'Dua faktor otentifikasi',
- 'This QR code contains the key URI: ' => 'kode QR ini mengandung kunci URI : ',
- 'Check my code' => 'Memeriksa kode saya',
- 'Secret key: ' => 'Kunci rahasia : ',
- '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',
+ 'This QR code contains the key URI: ' => 'Kode QR ini mengandung kunci URI: ',
+ 'Check my code' => 'Periksa kode saya',
+ 'Secret key: ' => 'Kunci rahasia: ',
+ 'Test your device' => 'Uji perangkat Anda',
+ 'Assign a color when the task is moved to a specific column' => 'Tetapkan 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',
'Add a screenshot' => 'Tambah screenshot',
- 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Mengambil screenshot dan tekan CTRL + V atau ⌘ + V untuk paste di sini.',
+ 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Ambil screenshot dan tekan CTRL+V atau ⌘+V untuk ditempel di sini.',
'Screenshot uploaded successfully.' => 'Screenshot berhasil diunggah.',
'SEK - Swedish Krona' => 'SEK - Krona Swedia',
'Identifier' => 'Identifier',
'Disable two factor authentication' => 'Matikan dua faktor otentifikasi',
- 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Apakah anda yakin akan mematikan dua faktor otentifikasi untuk pengguna ini : « %s » ?',
- 'Edit link' => 'Modifikasi tautan',
+ 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Apakah Anda yakin mau mematikan dua faktor otentifikasi untuk pengguna ini: "%s"?',
+ 'Edit link' => 'Edit tautan',
'Start to type task title...' => 'Mulai mengetik judul tugas...',
- 'A task cannot be linked to itself' => 'Sebuah tugas tidak dapat dikaitkan dengan dirinya sendiri',
+ 'A task cannot be linked to itself' => 'Tugas tidak dapat dikaitkan dengan dirinya sendiri',
'The exact same link already exists' => 'Tautan yang sama persis sudah ada',
- 'Recurrent task is scheduled to be generated' => 'Tugas berulang dijadwalkan akan dihasilkan',
+ 'Recurrent task is scheduled to be generated' => 'Tugas berulang dijadwalkan untuk di generate',
'Score' => 'Skor',
'The identifier must be unique' => 'Identifier harus unik',
- 'This linked task id doesn\'t exists' => 'Id tugas terkait tidak ada',
+ 'This linked task id doesn\'t exists' => 'ID tugas terkait tidak ada',
'This value must be alphanumeric' => 'Nilai harus alfanumerik',
- 'Edit recurrence' => 'Modifikasi pengulangan',
- 'Generate recurrent task' => 'Menghasilkan tugas berulang',
- 'Trigger to generate recurrent task' => 'Memicu untuk menghasilkan tugas berulang',
+ 'Edit recurrence' => 'Edit pengulangan',
+ 'Generate recurrent task' => 'Generate tugas berulang',
+ 'Trigger to generate recurrent task' => 'Pemicu untuk menghasilkan tugas berulang',
'Factor to calculate new due date' => 'Faktor untuk menghitung tanggal jatuh tempo baru',
'Timeframe to calculate new due date' => 'Jangka waktu untuk menghitung tanggal jatuh tempo baru',
'Base date to calculate new due date' => 'Tanggal dasar untuk menghitung tanggal jatuh tempo baru',
'Action date' => 'Tanggal aksi',
'Base date to calculate new due date: ' => 'Tanggal dasar untuk menghitung tanggal jatuh tempo baru: ',
- 'This task has created this child task: ' => 'Tugas ini telah menciptakan tugas anak ini: ',
+ 'This task has created this child task: ' => 'Tugas ini telah membuat tugas anak ini: ',
'Day(s)' => 'Hari',
'Existing due date' => 'Batas waktu yang ada',
'Factor to calculate new due date: ' => 'Faktor untuk menghitung tanggal jatuh tempo baru: ',
'Month(s)' => 'Bulan',
'Recurrence' => 'Pengulangan',
- 'This task has been created by: ' => 'Tugas ini telah dibuat oleh:',
- 'Recurrent task has been generated:' => 'Tugas berulang telah dihasilkan:',
+ 'This task has been created by: ' => 'Tugas ini telah dibuat oleh: ',
+ 'Recurrent task has been generated:' => 'Tugas berulang telah di generate:',
'Timeframe to calculate new due date: ' => 'Jangka waktu untuk menghitung tanggal jatuh tempo baru: ',
'Trigger to generate recurrent task: ' => 'Pemicu untuk menghasilkan tugas berulang: ',
- 'When task is closed' => 'Ketika tugas ditutup',
- 'When task is moved from first column' => 'Ketika tugas dipindahkan dari kolom pertama',
- 'When task is moved to last column' => 'Ketika tugas dipindahkan ke kolom terakhir',
+ 'When task is closed' => 'Saat tugas ditutup',
+ 'When task is moved from first column' => 'Saat tugas dipindahkan dari kolom pertama',
+ 'When task is moved to last column' => 'Saat tugas dipindahkan ke kolom terakhir',
'Year(s)' => 'Tahun',
'Calendar settings' => 'Pengaturan kalender',
'Project calendar view' => 'Tampilan kalender proyek',
'Project settings' => 'Pengaturan proyek',
- 'Show subtasks based on the time tracking' => 'Tampilkan subtugas berdasarkan pelacakan waktu',
+ 'Show subtasks based on the time tracking' => 'Tampilkan sub-tugas berdasarkan pelacakan waktu',
'Show tasks based on the creation date' => 'Tampilkan tugas berdasarkan tanggal pembuatan',
'Show tasks based on the start date' => 'Tampilkan tugas berdasarkan tanggal mulai',
- 'Subtasks time tracking' => 'Pelacakan waktu subtgas',
- 'User calendar view' => 'Pengguna tampilan kalender',
- 'Automatically update the start date' => 'Memperbarui tanggal mulai otomatis',
- 'iCal feed' => 'iCal feed',
+ 'Subtasks time tracking' => 'Pelacakan waktu sub-tugas',
+ 'User calendar view' => 'Tampilan kalender pengguna',
+ 'Automatically update the start date' => 'Otomatis memperbarui tanggal permulaan',
+ 'iCal feed' => 'Umpan iCal',
'Preferences' => 'Preferensi',
'Security' => 'Keamanan',
'Two factor authentication disabled' => 'Otentifikasi dua faktor dimatikan',
@@ -674,46 +648,40 @@ return array(
'Unable to update this user.' => 'Tidak dapat memperbarui pengguna ini.',
'There is no user management for private projects.' => 'Tidak ada manajemen pengguna untuk proyek-proyek pribadi.',
'User that will receive the email' => 'Pengguna yang akan menerima email',
- 'Email subject' => 'Subjek Email',
+ 'Email subject' => 'Subyek Email',
'Date' => 'Tanggal',
'Add a comment log when moving the task between columns' => 'Menambahkan log komentar ketika memindahkan tugas antara kolom',
'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',
+ 'Reopen a task' => 'Buka kembali tugas',
'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 »',
+ '%s moved the task #%d to the first swimlane' => '%s memindahkan tugas #%d ke swimlane pertama',
'Swimlane' => 'Swimlane',
'Gravatar' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s memindahkan tugas %s ke swimlane pertama',
- '%s moved the task %s to the swimlane "%s"' => '%s memindahkan tugas %s ke swimlane « %s »',
- 'This report contains all subtasks information for the given date range.' => 'Laporan ini berisi semua informasi subtugas untuk rentang tanggal tertentu.',
+ '%s moved the task %s to the swimlane "%s"' => '%s memindahkan tugas %s ke swimlane "%s"',
+ 'This report contains all subtasks information for the given date range.' => 'Laporan ini berisi semua informasi sub-tugas untuk rentang tanggal tertentu.',
'This report contains all tasks information for the given date range.' => 'Laporan ini berisi semua informasi tugas untuk rentang tanggal tertentu.',
- 'Project activities for %s' => 'Aktifitas proyek untuk « %s »',
+ 'Project activities for %s' => 'Aktifitas proyek untuk "%s"',
'view the board on Kanboard' => 'lihat papan di Kanboard',
'The task have been moved to the first swimlane' => 'Tugas telah dipindahkan ke swimlane pertama',
'The task have been moved to another swimlane:' => 'Tugas telah dipindahkan ke swimlane lain:',
- 'New title: %s' => 'Judul baru : %s',
+ 'New title: %s' => 'Judul baru: %s',
'The task is not assigned anymore' => 'Tugas tidak ditugaskan lagi',
- 'New assignee: %s' => 'Penerima baru : %s',
- 'There is no category now' => 'Tidak ada kategori untuk sekarang',
- 'New category: %s' => 'Kategori baru : %s',
- 'New color: %s' => 'Warna baru : %s',
- 'New complexity: %d' => 'Kompleksitas baru : %d',
+ 'New assignee: %s' => 'Penerima baru: %s',
+ 'There is no category now' => 'Tidak ada kategori untuk saat ini',
+ 'New category: %s' => 'Kategori baru: %s',
+ 'New color: %s' => 'Warna baru: %s',
+ 'New complexity: %d' => 'Kompleksitas baru: %d',
'The due date have been removed' => 'Tanggal jatuh tempo telah dihapus',
'There is no description anymore' => 'Tidak ada deskripsi lagi',
'Recurrence settings have been modified' => 'Pengaturan pengulangan telah dimodifikasi',
- 'Time spent changed: %sh' => 'Waktu yang dihabiskan berubah : %sh',
- 'Time estimated changed: %sh' => 'Perkiraan waktu berubah : %sh',
- 'The field "%s" have been updated' => 'Field « %s » telah diperbaharui',
+ 'Time spent changed: %sh' => 'Waktu yang dihabiskan telah diganti: %sh',
+ 'Time estimated changed: %sh' => 'Perkiraan waktu telah diganti: %sh',
+ 'The field "%s" have been updated' => 'Bidang "%s" telah diperbarui',
'The description has been modified:' => 'Deskripsi telah dimodifikasi',
- 'Do you really want to close the task "%s" as well as all subtasks?' => 'Apakah anda yakin akan menutup tugas « %s » beserta semua sub-tugasnya ?',
- 'I want to receive notifications for:' => 'Saya ingin menerima pemberitahuan untuk :',
+ 'Do you really want to close the task "%s" as well as all subtasks?' => 'Apakah Anda yakin mau menutup tugas "%s" beserta semua sub-tugasnya?',
+ 'I want to receive notifications for:' => 'Saya ingin menerima pemberitahuan untuk:',
'All tasks' => 'Semua tugas',
'Only for tasks assigned to me' => 'Hanya untuk tugas yang ditugaskan ke saya',
'Only for tasks created by me' => 'Hanya untuk tugas yang dibuat oleh saya',
@@ -725,87 +693,81 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Hentikan timer',
'Start timer' => 'Mulai timer',
- 'Add project member' => 'Tambahkan anggota proyek',
'My activity stream' => 'Aliran kegiatan saya',
'My calendar' => 'Kalender saya',
'Search tasks' => 'Cari tugas',
- 'Reset filters' => 'Reset ulang filter',
+ 'Reset filters' => 'Reset saringan',
'My tasks due tomorrow' => 'Tugas saya yang berakhir besok',
'Tasks due today' => 'Tugas yang berakhir hari ini',
'Tasks due tomorrow' => 'Tugas yang berakhir besok',
'Tasks due yesterday' => 'Tugas yang berakhir kemarin',
'Closed tasks' => 'Tugas yang ditutup',
- 'Open tasks' => 'Buka Tugas',
+ 'Open tasks' => 'Tugas terbuka',
'Not assigned' => 'Tidak ditugaskan',
'View advanced search syntax' => 'Lihat sintaks pencarian lanjutan',
- 'Overview' => 'Ikhtisar',
+ 'Overview' => 'Ringkasan',
'Board/Calendar/List view' => 'Tampilan Papan/Kalender/Daftar',
'Switch to the board view' => 'Beralih ke tampilan papan',
'Switch to the calendar view' => 'Beralih ke tampilan kalender',
'Switch to the list view' => 'Beralih ke tampilan daftar',
- 'Go to the search/filter box' => 'Pergi ke kotak pencarian/filter',
- 'There is no activity yet.' => 'Tidak ada aktifitas saat ini.',
+ 'Go to the search/filter box' => 'Pergi ke kotak pencarian/saringan',
+ 'There is no activity yet.' => 'Belum ada aktifitas.',
'No tasks found.' => 'Tidak ada tugas yang ditemukan.',
- 'Keyboard shortcut: "%s"' => 'Keyboard shortcut : « %s »',
+ 'Keyboard shortcut: "%s"' => 'Pintasan keyboard: "%s"',
'List' => 'Daftar',
- 'Filter' => 'Filter',
+ 'Filter' => 'Saringan',
'Advanced search' => 'Pencarian lanjutan',
'Example of query: ' => 'Contoh dari query : ',
- 'Search by project: ' => 'Pencarian berdasarkan proyek : ',
- 'Search by column: ' => 'Pencarian berdasarkan kolom : ',
- 'Search by assignee: ' => 'Pencarian berdasarkan penerima : ',
- 'Search by color: ' => 'Pencarian berdasarkan warna : ',
- '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 »',
+ 'Search by project: ' => 'Cari berdasarkan proyek: ',
+ 'Search by column: ' => 'Cari berdasarkan kolom: ',
+ 'Search by assignee: ' => 'Cari berdasarkan penerima tugas: ',
+ 'Search by color: ' => 'Cari berdasarkan warna: ',
+ 'Search by category: ' => 'Cari berdasarkan kategori: ',
+ 'Search by description: ' => 'Cari berdasarkan deskripsi: ',
+ 'Search by due date: ' => 'Cari berdasarkan tanggal jatuh tempo: ',
'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.',
- 'Average Lead and Cycle time' => 'Rata-rata Memimpin dan Siklus waktu',
- 'Average lead time: ' => 'Rata-rata waktu pimpinan : ',
- 'Average cycle time: ' => 'Rata-rata siklus waktu : ',
- 'Cycle Time' => 'Siklus Waktu',
+ '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 terakhir.',
+ 'Average Lead and Cycle time' => 'Rata-rata Lead dan Cycle time',
+ 'Average lead time: ' => 'Rata-rata lead time: ',
+ 'Average cycle time: ' => 'Rata-rata cycle time: ',
+ 'Cycle Time' => 'Cycle Time',
'Lead Time' => 'Lead Time',
- 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Grafik ini menunjukkan memimpin rata-rata dan waktu siklus untuk %d tugas terakhir dari waktu ke waktu.',
+ 'This chart show the average lead and cycle time for the last %d tasks over the time.' => 'Grafik ini menunjukkan rata-rata waktu lead dan cycle time untuk %d tugas terakhir dari waktu ke waktu.',
'Average time into each column' => 'Rata-rata waktu ke setiap kolom',
- 'Lead and cycle time' => 'Lead dan siklus waktu',
- 'Lead time: ' => 'Lead time : ',
- 'Cycle time: ' => 'Siklus waktu : ',
+ 'Lead and cycle time' => 'Lead dan cycle time',
+ 'Lead time: ' => 'Lead time: ',
+ 'Cycle time: ' => 'Cycle time: ',
'Time spent into each column' => 'Waktu yang dihabiskan di setiap kolom',
'The lead time is the duration between the task creation and the completion.' => 'Lead time adalah durasi antara pembuatan tugas dan penyelesaian.',
- 'The cycle time is the duration between the start date and the completion.' => 'Siklus waktu adalah durasi antara tanggal mulai dan tanggal penyelesaian.',
- 'If the task is not closed the current time is used instead of the completion date.' => 'Jika tugas tidak ditutup waktu saat ini yang digunakan sebagai pengganti tanggal penyelesaian.',
+ 'The cycle time is the duration between the start date and the completion.' => 'Cycle time adalah durasi antara tanggal mulai dan tanggal penyelesaian.',
+ 'If the task is not closed the current time is used instead of the completion date.' => 'Jika tugas tidak ditutup, waktu saat ini akan digunakan sebagai pengganti tanggal penyelesaian.',
'Set automatically the start date' => 'Secara otomatis mengatur tanggal mulai',
- 'Edit Authentication' => 'Modifikasi Otentifikasi',
+ 'Edit Authentication' => 'Edit Otentifikasi',
'Remote user' => 'Pengguna jauh',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Pengguna jauh tidak menyimpan kata sandi mereka dalam basis data Kanboard, contoh: akun LDAP, Google dan Github.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Jika anda mencentang kotak "Larang formulir login", kredensial masuk ke formulis login akan diabaikan.',
- 'New remote user' => 'Pengguna baru jauh',
- 'New local user' => 'Pengguna baru lokal',
- 'Default task color' => 'Standar warna tugas',
- 'This feature does not work with all browsers.' => 'Fitur ini tidak dapat digunakan di semua browsers',
- 'There is no destination project available.' => 'Tidak ada destinasi proyek yang tersedia.',
- 'Trigger automatically subtask time tracking' => 'Otomatis memicu pelacakan untuk subtugas',
- 'Include closed tasks in the cumulative flow diagram' => 'Termasuk tugas yang ditutup pada diagram aliran kumulatif',
- 'Current swimlane: %s' => 'Swimlane saat ini : %s',
- 'Current column: %s' => 'Kolom saat ini : %s',
- 'Current category: %s' => 'Kategori saat ini : %s',
+ 'Default task color' => 'Warna tugas default',
+ 'This feature does not work with all browsers.' => 'Fitur ini tidak dapat digunakan di semua peramban',
+ 'There is no destination project available.' => 'Tidak ada tujuan proyek yang tersedia.',
+ 'Trigger automatically subtask time tracking' => 'Otomatis memicu pelacakan waktu untuk sub-tugas',
+ 'Include closed tasks in the cumulative flow diagram' => 'Sertakan tugas yang ditutup dalam diagram alir kumulatif',
+ 'Current swimlane: %s' => 'Swimlane saat ini: %s',
+ 'Current column: %s' => 'Kolom saat ini: %s',
+ 'Current category: %s' => 'Kategori saat ini: %s',
'no category' => 'tidak ada kategori',
- 'Current assignee: %s' => 'Saat ini ditugaskan : %s',
- 'not assigned' => 'Belum ditugaskan',
- 'Author:' => 'Penulis :',
+ 'Current assignee: %s' => 'Orang yang ditugaskan saat ini: %s',
+ 'not assigned' => 'belum ditugaskan',
+ 'Author:' => 'Penulis:',
'contributors' => 'kontributor',
- 'License:' => 'Lisensi :',
+ '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',
- 'Start date:' => 'Tanggal mulai :',
- 'Due date:' => 'Batas waktu :',
+ 'Start date:' => 'Tanggal mulai:',
+ 'Due date:' => 'Batas waktu:',
'There is no start date or due date for this task.' => 'Tidak ada tanggal mulai dan batas waktu untuk tugas ini.',
'Moving or resizing a task will change the start and due date of the task.' => 'Memindahkan atau mengubah ukuran tugas anda akan mengubah tanggal mulai dan batas waktu dari tugas ini.',
'There is no task in your project.' => 'Tidak ada tugas didalam proyek anda.',
@@ -817,7 +779,7 @@ return array(
'Hide this column' => 'Sembunyikan kolom ini',
'open file' => 'buka berkas',
'End date' => 'Waktu berakhir',
- 'Users overview' => 'Ikhtisar pengguna',
+ 'Users overview' => 'Ringkasan pengguna',
'Members' => 'Anggota',
'Shared project' => 'Proyek bersama',
'Project managers' => 'Manajer proyek',
@@ -825,396 +787,524 @@ return array(
'Projects list' => 'Daftar proyek',
'Gantt chart for this project' => 'Grafik Gantt untuk proyek ini',
'Project board' => 'Papan proyek',
- 'End date:' => 'Waktu berakhir :',
+ 'End date:' => 'Waktu berakhir:',
'There is no start date or end date for this project.' => 'Tidak ada waktu mulai atau waktu berakhir untuk proyek ini',
- 'Projects Gantt chart' => 'Proyek grafik Gantt',
- 'Change task color when using a specific task link' => 'Rubah warna tugas ketika menggunakan tautan tugas yang spesifik',
+ 'Projects Gantt chart' => 'Grafik Gantt proyek',
+ 'Change task color when using a specific task link' => 'Ganti warna tugas ketika menggunakan tautan tugas yang spesifik',
'Task link creation or modification' => 'Tautan pembuatan atau modifikasi tugas ',
'Milestone' => 'Milestone',
- 'Documentation: %s' => 'Dokumentasi : %s',
+ 'Documentation: %s' => 'Dokumentasi: %s',
'Switch to the Gantt chart view' => 'Beralih ke tampilan grafik Gantt',
- 'Reset the search/filter box' => 'Atur ulang pencarian/kotak filter',
+ 'Reset the search/filter box' => 'Reset kotak pencarian/saringan',
'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' => 'Saringan kustom',
+ 'Your custom filter have been created successfully.' => 'Saringan kustom Anda berhasil dibuat',
+ 'Unable to create your custom filter.' => 'Tidak dapat membuat saringan kustom',
+ 'Custom filter removed successfully.' => 'Saringan kustom berhasil dihapus',
+ 'Unable to remove this custom filter.' => 'Tidak dapat menghapus saringan kustom',
+ 'Edit custom filter' => 'Edit saringan kustom',
+ 'Your custom filter have been updated successfully.' => 'Saringan kustom Anda berhasil diperbarui',
+ 'Unable to update custom filter.' => 'Tidak dapat memperbarui saringan 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 diperbarui pada tugas #%d',
+ 'New subtask on task #%d' => 'Sub-tugas baru pada tugas #%d',
+ 'Subtask updated on task #%d' => 'Sub-tugas diperbarui pada tugas #%d',
+ 'New task #%d: %s' => 'Tugas baru #%d: %s',
+ 'Task updated #%d' => 'Tugas diperbarui #%d',
+ 'Task #%d closed' => 'Tugas #%d ditutup',
+ 'Task #%d opened' => 'Tugas #%d dibuka',
+ 'Column changed for task #%d' => 'Kolom diganti untuk tugas #%d',
+ 'New position for task #%d' => 'Posisi baru untuk tugas #%d',
+ 'Swimlane changed for task #%d' => 'Swimlane diganti untuk tugas #%d',
+ 'Assignee changed on task #%d' => 'Orang yang ditugaskan diganti pada tugas #%d',
+ '%d overdue tasks' => '%d tugas kadaluarsa',
+ 'Task #%d is overdue' => 'Tugas #%d sudah kadaluarsa',
+ '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 tugas di kolom ini untuk semua swimlane',
+ 'Collapse swimlane' => 'Tutup swimlane',
+ 'Expand swimlane' => 'Perluas swimlane',
+ 'Add a new filter' => 'Tambah saringan baru',
+ 'Share with all project members' => 'Bagikan dengan semua anggota proyek',
+ 'Shared' => 'Dibagikan',
+ 'Owner' => 'Pemilik',
+ 'Unread notifications' => 'Notifikasi belum terbaca',
+ 'Notification methods:' => 'Metode pemberitahuan',
+ '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 saringan (ketimbang pengganti)',
+ 'Append/Replace' => 'Tambah/Ganti',
+ 'Append' => 'Tambahkan',
+ 'Replace' => 'Ganti',
+ 'Import' => 'Impor',
+ 'change sorting' => 'ubah pengurutan',
+ 'Tasks Importation' => 'Importasi Tugas',
+ '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 diverifikasikan 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 yang terdaftar',
+ 'Duplicates are not imported' => 'Duplikasi tidak diimpor',
+ 'Usernames must be lowercase and unique' => 'Nama pengguna 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 melampirkan berkas baru 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' => 'Otomatis menetapkan kategori berdasarkan tautan',
+ 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark',
+ 'Assignee Username' => 'Nama pengguna orang yang ditugaskan',
+ 'Assignee Name' => 'Nama orang 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' => 'Edit grup',
+ 'Group updated successfully.' => 'Grup berhasil diperbarui',
+ 'Unable to update your group.' => 'Tidak dapat memperbarui 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 ini 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 sesuai',
+ '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 mau 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 mau 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 Diizinkan',
+ '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 Diizinkan',
+ '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.' => '',
- // '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' => '',
- // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '',
- // 'My dashboard' => '',
- // 'My profile' => '',
- // 'Project owner: ' => '',
- // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
- // 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
- // 'Private projects do not have users and groups management.' => '',
- // 'There is no project member.' => '',
- // 'Priority' => '',
- // 'Task priority' => '',
- // 'General' => '',
- // 'Dates' => '',
- // 'Default priority' => '',
- // 'Lowest priority' => '',
- // 'Highest priority' => '',
- // 'If you put zero to the low and high priority, this feature will be disabled.' => '',
- // 'Close a task when there is no activity' => '',
- // 'Duration in days' => '',
- // 'Send email when there is no activity on a task' => '',
- // 'Unable to fetch link information.' => '',
- // 'Daily background job for tasks' => '',
- // 'Auto' => '',
- // 'Related' => '',
- // 'Attachment' => '',
- // 'Title not found' => '',
- // 'Web Link' => '',
- // 'External links' => '',
- // 'Add external link' => '',
- // 'Type' => '',
- // 'Dependency' => '',
- // 'Add internal link' => '',
- // 'Add a new external link' => '',
- // 'Edit external link' => '',
- // 'External link' => '',
- // 'Copy and paste your link here...' => '',
- // 'URL' => '',
- // 'Internal links' => '',
- // 'Assign to me' => '',
- // 'Me' => '',
- // 'Do not duplicate anything' => '',
- // 'Projects management' => '',
- // 'Users management' => '',
- // 'Groups management' => '',
- // 'Create from another project' => '',
- // 'open' => '',
- // 'closed' => '',
- // 'Priority:' => '',
- // 'Reference:' => '',
- // 'Complexity:' => '',
- // 'Swimlane:' => '',
- // 'Column:' => '',
- // 'Position:' => '',
- // 'Creator:' => '',
- // 'Time estimated:' => '',
- // '%s hours' => '',
- // 'Time spent:' => '',
- // 'Created:' => '',
- // 'Modified:' => '',
- // 'Completed:' => '',
- // 'Started:' => '',
- // 'Moved:' => '',
- // 'Task #%d' => '',
- // 'Date and time format' => '',
- // 'Time format' => '',
- // 'Start date: ' => '',
- // 'End date: ' => '',
- // 'New due date: ' => '',
- // 'Start date changed: ' => '',
- // 'Disable private projects' => '',
- // 'Do you really want to remove this custom filter: "%s"?' => '',
- // 'Remove a custom filter' => '',
- // 'User activated successfully.' => '',
- // 'Unable to enable this user.' => '',
- // 'User disabled successfully.' => '',
- // 'Unable to disable this user.' => '',
- // 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
- // 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
- // 'Drag and drop your files here' => '',
- // 'choose files' => '',
- // 'View profile' => '',
- // 'Two Factor' => '',
- // 'Disable user' => '',
- // 'Do you really want to disable this user: "%s"?' => '',
- // 'Enable user' => '',
- // 'Do you really want to enable this user: "%s"?' => '',
- // 'Download' => '',
- // 'Uploaded: %s' => '',
- // 'Size: %s' => '',
- // 'Uploaded by %s' => '',
- // 'Filename' => '',
- // 'Size' => '',
- // 'Column created successfully.' => '',
- // 'Another column with the same name exists in the project' => '',
- // 'Default filters' => '',
- // 'Your board doesn\'t have any columns!' => '',
- // 'Change column position' => '',
- // 'Switch to the project overview' => '',
- // 'User filters' => '',
- // 'Category filters' => '',
- // 'Upload a file' => '',
- // 'View file' => '',
- // 'Last activity' => '',
- // 'Change subtask position' => '',
- // 'This value must be greater than %d' => '',
- // 'Another swimlane with the same name exists in the project' => '',
- // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '',
- // 'Actions duplicated successfully.' => '',
- // 'Unable to duplicate actions.' => '',
- // 'Add a new action' => '',
- // 'Import from another project' => '',
- // 'There is no action at the moment.' => '',
- // 'Import actions from another project' => '',
- // 'There is no available project.' => '',
- // 'Local File' => '',
- // 'Configuration' => '',
- // 'PHP version:' => '',
- // 'PHP SAPI:' => '',
- // 'OS version:' => '',
- // 'Database version:' => '',
- // 'Browser:' => '',
- // 'Task view' => '',
- // 'Edit task' => '',
- // 'Edit description' => '',
- // 'New internal link' => '',
- // 'Display list of keyboard shortcuts' => '',
- // 'Menu' => '',
- // 'Set start date' => '',
- // 'Avatar' => '',
- // 'Upload my avatar image' => '',
- // 'Remove my image' => '',
- // 'The OAuth2 state parameter is invalid' => '',
- // 'User not found.' => '',
- // 'Search in activity stream' => '',
- // 'My activities' => '',
- // 'Activity until yesterday' => '',
- // 'Activity until today' => '',
- // 'Search by creator: ' => '',
- // 'Search by creation date: ' => '',
- // 'Search by task status: ' => '',
- // 'Search by task title: ' => '',
- // 'Activity stream search' => '',
- // 'Projects where "%s" is manager' => '',
- // 'Projects where "%s" is member' => '',
- // 'Open tasks assigned to "%s"' => '',
- // 'Closed tasks assigned to "%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' => '',
- // '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' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ '%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: ' => 'Jam sebenarnya: ',
+ 'Hours Spent' => 'Jam dihabiskan',
+ 'Hours Estimated' => 'Jam diperkirakan',
+ 'Estimated Time' => 'Waktu Estimasi',
+ 'Actual Time' => 'Waktu Sebenarnya',
+ 'Estimated vs actual time' => 'Estimasi vs waktu sebenarnya',
+ 'RUB - Russian Ruble' => 'RUB - Rubel Rusia',
+ 'Assign the task to the person who does the action when the column is changed' => 'Berikan tugas pada orang yang melakukan tindakan saat kolom diganti',
+ 'Close a task in a specific column' => 'Tutup tugas di kolom tertentu',
+ 'Time-based One-time Password Algorithm' => 'Algoritma Password Satu-Kali Berbasis-Waktu',
+ 'Two-Factor Provider: ' => 'Penyedia 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 didaftarkan untuk saat ini',
+ 'Password Reset for Kanboard' => 'Reset Password untuk Kanboard',
+ 'Forgot password?' => 'Lupa password?',
+ 'Enable "Forget Password"' => 'Aktifkan "Lupa Password"',
+ 'Password Reset' => 'Reset Password',
+ 'New password' => 'Password baru',
+ 'Change Password' => 'Ganti Password',
+ 'To reset your password click on this link:' => 'Untuk reset password Anda klik tautan ini:',
+ 'Last Password Reset' => 'Reset Password Terakhir',
+ 'The password has never been reinitialized.' => 'Password tidak pernah di inisialisasi ulang',
+ 'Creation' => 'Pembuatan',
+ 'Expiration' => 'Kadaluarsa',
+ 'Password reset history' => 'Sejarah reset password',
+ '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?' => 'Apakah Anda yakin mau menutup semua tugas dalam kolom ini?',
+ '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tugas dalam kolom "%s" dan swimlane "%s" akan ditutup.',
+ 'Close all tasks of this column' => 'Tutup semua tugas dalam kolom ini',
+ 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Tidak ada plugin yang mendaftarkan metode pemberitahuan proyek. Anda masih bisa mengatur pemberitahuan individu di dalam profil pengguna Anda.',
+ 'My dashboard' => 'Dasbor saya',
+ 'My profile' => 'Profil saya',
+ 'Project owner: ' => 'Pemilik proyek',
+ 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identifier proyek adalah opsional dan harus alfanumerik, contoh: MYPROJECT.',
+ 'Project owner' => 'Pemilik proyek',
+ 'Private projects do not have users and groups management.' => 'Proyek pribadi tidak memiliki manajemen pengguna dan grup',
+ 'There is no project member.' => 'Tidak ada anggota proyek',
+ 'Priority' => 'Prioritas',
+ 'Task priority' => 'Prioritas tugas',
+ 'General' => 'Umum',
+ 'Dates' => 'Tanggal',
+ 'Default priority' => 'Prioritas default',
+ 'Lowest priority' => 'Prioritas terendah',
+ 'Highest priority' => 'Prioritas tertinggi',
+ 'If you put zero to the low and high priority, this feature will be disabled.' => 'Jika anda meletakkan nol untuk prioritas rendah dan tinggi, fitur ini akan dinonaktifkan.',
+ 'Close a task when there is no activity' => 'Tutup tugas jika tidak ada aktifitas',
+ 'Duration in days' => 'Durasi dalam hari',
+ 'Send email when there is no activity on a task' => 'Kirim email jika tidak ada aktifitas dalam tugas',
+ 'Unable to fetch link information.' => 'Tidak dapat mengambil informasi tautan',
+ 'Daily background job for tasks' => 'Tugas latar belakang harian untuk tugas',
+ 'Auto' => 'Otomatis',
+ 'Related' => 'Terkait',
+ 'Attachment' => 'Lampiran',
+ 'Title not found' => 'Judul tidak ditemukan',
+ 'Web Link' => 'Tautan web',
+ 'External links' => 'Tautan eksternal',
+ 'Add external link' => 'Tambah tautan eksternal',
+ 'Type' => 'Tipe',
+ 'Dependency' => 'Ketergantungan',
+ 'Add internal link' => 'Tambah tautan internal',
+ 'Add a new external link' => 'Tambah tautan eksternal baru',
+ 'Edit external link' => 'Rubah tautan eksternal',
+ 'External link' => 'Tautan eksternal',
+ 'Copy and paste your link here...' => 'Copy dan paste tautan anda di sini...',
+ 'URL' => 'URL',
+ 'Internal links' => 'Tautan internal',
+ 'Assign to me' => 'Tugaskan ke saya',
+ 'Me' => 'Saya',
+ 'Do not duplicate anything' => 'Jangan menduplikasi apapun',
+ 'Projects management' => 'Manajemen proyek',
+ 'Users management' => 'Manajemen pengguna',
+ 'Groups management' => 'Manajemen grup',
+ 'Create from another project' => 'Buat dari proyek lain',
+ 'open' => 'buka',
+ 'closed' => 'tutup',
+ 'Priority:' => 'Prioritas',
+ 'Reference:' => 'Referensi',
+ 'Complexity:' => 'Kompleksitas',
+ 'Swimlane:' => 'Swimlane',
+ 'Column:' => 'Kolom:',
+ 'Position:' => 'Posisi:',
+ 'Creator:' => 'Pembuat:',
+ 'Time estimated:' => 'Estimasi waktu:',
+ '%s hours' => '%s jam',
+ 'Time spent:' => 'Waktu yang dihabiskan',
+ 'Created:' => 'Dibuat:',
+ 'Modified:' => 'Dimodifikasi:',
+ 'Completed:' => 'Selesai:',
+ 'Started:' => 'Dimulai:',
+ 'Moved:' => 'Dipindahkan:',
+ 'Task #%d' => 'Tugas #%d',
+ 'Time format' => 'Format waktu',
+ 'Start date: ' => 'Tanggal mulai: ',
+ 'End date: ' => 'Tanggal berakhir: ',
+ 'New due date: ' => 'Tanggal jatuh tempo baru: ',
+ 'Start date changed: ' => 'Tanggal mulai diganti: ',
+ 'Disable private projects' => 'Nonaktifkan proyek pribadi',
+ 'Do you really want to remove this custom filter: "%s"?' => 'Apakah Anda yakin mau menghapus saringan kustom: "%s"?',
+ 'Remove a custom filter' => 'Hapus saringan kustom',
+ 'User activated successfully.' => 'Pengguna berhasil diaktifkan.',
+ 'Unable to enable this user.' => 'Tidak dapat mengaktifkan pengguna ini.',
+ 'User disabled successfully.' => 'Pengguna berhasil dinonaktifkan.',
+ 'Unable to disable this user.' => 'Tidak dapat menonaktifkan pengguna ini.',
+ 'All files have been uploaded successfully.' => 'Semua berkas berhasil di unggah.',
+ 'The maximum allowed file size is %sB.' => 'Maksimum ukuran berkas yang diiziinkan adalah %sB.',
+ 'Drag and drop your files here' => 'Drag and drop file Anda di sini',
+ 'choose files' => 'pilih berkas',
+ 'View profile' => 'Lihat profil',
+ 'Two Factor' => 'Dua Faktor',
+ 'Disable user' => 'Nonaktifkan pengguna',
+ 'Do you really want to disable this user: "%s"?' => 'Anda yakin mau menonaktifkan pengguna ini: "%s"?',
+ 'Enable user' => 'Aktifkan pengguna',
+ 'Do you really want to enable this user: "%s"?' => 'Anda yakin mau mengaktifkan pengguna ini: "%s"?',
+ 'Download' => 'Unduh',
+ 'Uploaded: %s' => 'Diunggah: %s',
+ 'Size: %s' => 'Ukuran: %s',
+ 'Uploaded by %s' => 'Diunggah oleh %s',
+ 'Filename' => 'Nama berkas',
+ 'Size' => 'Ukuran',
+ 'Column created successfully.' => 'Kolom berhasil dibuat.',
+ 'Another column with the same name exists in the project' => 'Ada kolom lain dengan nama yang sama di proyek ini',
+ 'Default filters' => 'Saringan default',
+ 'Your board doesn\'t have any columns!' => 'Papan Anda tidak memiliki kolom!',
+ 'Change column position' => 'Ganti posisi kolom',
+ 'Switch to the project overview' => 'Pindah ke ringkasan proyek',
+ 'User filters' => 'Saringan pengguna',
+ 'Category filters' => 'Saringan kategori',
+ 'Upload a file' => 'Unggah berkas',
+ 'View file' => 'Lihat berkas',
+ 'Last activity' => 'Aktivitas terakhir',
+ 'Change subtask position' => 'Ganti posisi sub-tugas',
+ 'This value must be greater than %d' => 'Nilai ini harus lebih besar dari %d',
+ 'Another swimlane with the same name exists in the project' => 'Swimlane lain dengan nama yang sama sudah ada di proyek ini',
+ 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Contoh: http://contoh.kanboard.net/ (digunakan untuk menghasilkan URL yang absolut',
+ 'Actions duplicated successfully.' => 'Tindakan berhasil di duplikasi.',
+ 'Unable to duplicate actions.' => 'Tidak bisa menduplikasi tindakan.',
+ 'Add a new action' => 'Tambahkan tindakan baru',
+ 'Import from another project' => 'Impor dari proyek lain',
+ 'There is no action at the moment.' => 'Belum ada tindakan pada saat ini.',
+ 'Import actions from another project' => 'Impor tindakan dari proyek lain',
+ 'There is no available project.' => 'Tidak ada proyek yang tersedia',
+ 'Local File' => 'Berkas Lokal',
+ 'Configuration' => 'Konfigurasi',
+ 'PHP version:' => 'Versi PHP:',
+ 'PHP SAPI:' => 'PHP SAPI:',
+ 'OS version:' => 'Versi sistem operasi:',
+ 'Database version:' => 'Versi database:',
+ 'Browser:' => 'Peramban:',
+ 'Task view' => 'Tampilan Tugas',
+ 'Edit task' => 'Edit tugas',
+ 'Edit description' => 'Edit deskripsi',
+ 'New internal link' => 'Tautan internal baru',
+ 'Display list of keyboard shortcuts' => 'Tampilkan daftar pintasan keyboard',
+ 'Menu' => 'Menu',
+ 'Set start date' => 'Atur tanggal mulai',
+ 'Avatar' => 'Avatar',
+ 'Upload my avatar image' => 'Unggah foto avatar saya',
+ 'Remove my image' => 'Hapus foto saya',
+ 'The OAuth2 state parameter is invalid' => 'Status parameter OAuth2 tidak sesuai',
+ 'User not found.' => 'Pengguna tidak ditemukan.',
+ 'Search in activity stream' => 'Cari di saluran aktivitas',
+ 'My activities' => 'Aktivitas saya',
+ 'Activity until yesterday' => 'Aktivitas sampai kemarin',
+ 'Activity until today' => 'Aktivitas sampai hari ini',
+ 'Search by creator: ' => 'Cari berdasarkan pembuat: ',
+ 'Search by creation date: ' => 'Cari berdasarkan tanggal pembuatan: ',
+ 'Search by task status: ' => 'Cari berdasarkanstatus tugas: ',
+ 'Search by task title: ' => 'Cari berdasarkan judul tugas: ',
+ 'Activity stream search' => 'Pencarian saluran aktivitas',
+ 'Projects where "%s" is manager' => 'Proyek dimana "%s" adalah manajernya',
+ 'Projects where "%s" is member' => 'Proyek dimana "%s" adalah anggotanya',
+ 'Open tasks assigned to "%s"' => 'Tugas terbuka ditugaskan pada "%s"',
+ 'Closed tasks assigned to "%s"' => 'Tugas tertutup ditugaskan pada "%s"',
+ 'Assign automatically a color based on a priority' => 'Berikan warna otomatis berdasarkan prioritas',
+ 'Overdue tasks for the project(s) "%s"' => 'Tugas yang kadaluarsa untuk proyek "%s"',
+ 'Upload files' => 'Unggah berkas',
+ 'Installed Plugins' => 'Plugin terpasang',
+ 'Plugin Directory' => 'Direktori Plugin',
+ 'Plugin installed successfully.' => 'Plugin berhasil dipasang.',
+ 'Plugin updated successfully.' => 'Plugin berhasil diperbarui.',
+ 'Plugin removed successfully.' => 'Plugin berhasil dihapus.',
+ 'Subtask converted to task successfully.' => 'Sub-tugas berhasil diubah menjadi tugas.',
+ 'Unable to convert the subtask.' => 'Tidak dapat mengubah sub-tugas.',
+ 'Unable to extract plugin archive.' => 'Tidak bisa mengekstrak arsip plugin.',
+ 'Plugin not found.' => 'Plugin tidak ditemukan.',
+ 'You don\'t have the permission to remove this plugin.' => 'Anda tidak memiliki izin untuk menghapus plugin ini.',
+ 'Unable to download plugin archive.' => 'Tidak dapat mengunduh arsip plugin.',
+ 'Unable to write temporary file for plugin.' => 'Tidak dapat menulis berkas sementara untuk plugin.',
+ 'Unable to open plugin archive.' => 'Tidak dapat membuka arsip plugin.',
+ 'There is no file in the plugin archive.' => 'Tidak ada berkas di dalam arsip plugin.',
+ 'Create tasks in bulk' => 'Buat tugas sekaligus',
+ 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Instalasi Kanboard Anda tidak diatur untuk memasang plugin dari antar muka pengguna.',
+ 'There is no plugin available.' => 'Tidak ada plugin yang tersedia.',
+ 'Install' => 'Pasang',
+ 'Update' => 'Perbarui',
+ 'Up to date' => 'Terbaru',
+ 'Not available' => 'Tidak tersedia',
+ 'Remove plugin' => 'Hapus plugin',
+ 'Do you really want to remove this plugin: "%s"?' => 'Anda yakin ingin menghapus plugin ini: "%s"?',
+ 'Uninstall' => 'Lepaskan',
+ 'Listing' => 'Daftar',
+ 'Metadata' => 'Metadata',
+ 'Manage projects' => 'Atur proyek',
+ 'Convert to task' => 'Ubah menjadi tugas',
+ 'Convert sub-task to task' => 'Ubah sub-tugas menjadi tugas',
+ 'Do you really want to convert this sub-task to a task?' => 'Anda yakin ingin mengubah sub-tugas ini menjadi tugas?',
+ 'My task title' => 'Judul tugas saya',
+ 'Enter one task by line.' => 'Masukkan satu tugas berdasarkan baris.',
+ 'Number of failed login:' => 'Jumlah login yang gagal:',
+ 'Account locked until:' => 'Akun terkunci hingga:',
+ 'Email settings' => 'Pengaturan email',
+ 'Email sender address' => 'Alamat pengirim email',
+ 'Email transport' => 'Transportasi email',
+ 'Webhook token' => 'Token Webhook',
+ 'Project tags management' => 'Manajemen tag proyek',
+ 'Tag created successfully.' => 'Tag berhasil dibuat.',
+ 'Unable to create this tag.' => 'Tidak dapat membuat tag ini.',
+ 'Tag updated successfully.' => 'Tag berhasil diperbarui',
+ 'Unable to update this tag.' => 'Tidak dapat memperbarui tag ini.',
+ 'Tag removed successfully.' => 'Tag berhasil dihapus.',
+ 'Unable to remove this tag.' => 'Tidak dapat menghapus tag ini.',
+ 'Global tags management' => 'Manajemen tag global',
+ 'Tags' => 'Tag',
+ 'Tags management' => 'Manajemen tag',
+ 'Add new tag' => 'Tambah tag baru',
+ 'Edit a tag' => 'Edit tag',
+ 'Project tags' => 'Tag proyek',
+ 'There is no specific tag for this project at the moment.' => 'Saat ini tidak ada tag yang spesifik pada proyek ini.',
+ 'Tag' => 'Tag',
+ 'Remove a tag' => 'Hapus tag',
+ 'Do you really want to remove this tag: "%s"?' => 'Anda yakin ingin menghapus tag ini: "%s"?',
+ 'Global tags' => 'Tag global',
+ 'There is no global tag at the moment.' => 'Saat ini tidak ada tag global.',
+ 'This field cannot be empty' => 'Bidang ini tidak boleh kosong',
+ 'Close a task when there is no activity in an specific column' => 'Tutup tugas saat tidak ada aktivitas di kolom tertentu',
+ '%s removed a subtask for the task #%d' => '%s menghapus sub-tugas untuk tugas #%d',
+ '%s removed a comment on the task #%d' => '%s menghapus komentar pada tugas #%d',
+ 'Comment removed on task #%d' => 'Komentar dihapus pada tugas #%d',
+ 'Subtask removed on task #%d' => 'Sub-tugas dihapus pada tugas #%d',
+ 'Hide tasks in this column in the dashboard' => 'Sembunyikan tugas-tugas di kolom ini di dasbor',
+ '%s removed a comment on the task %s' => '%s menghapus komentar pada tugas %s',
+ '%s removed a subtask for the task %s' => '%s menghapus sub-tugas untuk tugas %s',
+ 'Comment removed' => 'Komentar dihapus',
+ 'Subtask removed' => 'Sub-tugas dihapus',
+ '%s set a new internal link for the task #%d' => '%s memasang tautan internal baru untuk tugas #%d',
+ '%s removed an internal link for the task #%d' => '%s menghapus tautan internal untuk tugas #%d',
+ 'A new internal link for the task #%d have been defined' => 'Tautan internal baru untuk tugas #%d telah ditentukan',
+ 'Internal link removed for the task #%d' => 'Tautan internal untuk tugas #%d telah dihapus',
+ '%s set a new internal link for the task %s' => '%s memasang tautan internal baru untuk tugas %s',
+ '%s removed an internal link for the task %s' => '%s menghapus tautan internal untuk tugas %s',
+ 'Automatically set the due date on task creation' => 'Otomatis memasang tanggal kadaluarsa saat pembuatan tugas',
+ 'Move the task to another column when closed' => 'Pindahkan tugas ke kolom lain saat ditutup',
+ 'Move the task to another column when not moved during a given period' => 'Pindahkan tugas ke kolom lain saat tidak dipindahkan selama periode yang diberikan',
+ 'Dashboard for %s' => 'Dasbor untuk %s',
+ 'Tasks overview for %s' => 'Ringkasan tugas-tugas untuk %s',
+ 'Subtasks overview for %s' => 'Ringkasan sub-tugas untuk %s',
+ 'Projects overview for %s' => 'Ringkasan proyek untuk %s',
+ 'Activity stream for %s' => 'Arus aktivitas untuk %s',
+ 'Calendar for %s' => 'Kalender untuk %s',
+ 'Notifications for %s' => 'Notifikasi untuk %s',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Berikan warna saat tugas dipindahkan ke swimlane tertentu',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Berikan prioritas saat tugas dipindahkan ke swimlane tertentu',
+ 'User unlocked successfully.' => 'Berhasil membuka blokir pengguna.',
+ 'Unable to unlock the user.' => 'Tidak bisa membuka blokir pengguna.',
+ 'Move a task to another swimlane' => 'Pindahkan tugas ke swimlane lain',
+ 'Creator Name' => 'Nama Pembuat',
+ 'Time spent and estimated' => 'Waktu yang dihabiskan dan diperkirakan',
+ 'Move position' => 'Pindahkan posisi',
+ 'Move task to another position on the board' => 'Pindahkan tugas ke posisi lain di dalam papan',
+ 'Insert before this task' => 'Masukkan sebelum tugas ini',
+ 'Insert after this task' => 'Masukkan setelah tugas ini',
+ 'Unlock this user' => 'Buka pengguna ini',
+ 'Custom Project Roles' => 'Peran Proyek Kustom',
+ 'Add a new custom role' => 'Tambahkan peran kustom baru',
+ 'Restrictions for the role "%s"' => 'Batasan untuk peran "%s"',
+ 'Add a new project restriction' => 'Tambahkan batasan proyek baru',
+ 'Add a new drag and drop restriction' => 'Tambahkan batasan drag and drop baru',
+ 'Add a new column restriction' => 'Tambahkan batasan kolom baru',
+ 'Edit this role' => 'Edit peran ini',
+ 'Remove this role' => 'Hapus peran ini',
+ 'There is no restriction for this role.' => 'Tidak ada batasan untuk peran ini.',
+ 'Only moving task between those columns is permitted' => 'Hanya diizinkan untuk memindahkan tugas diantara kolom-kolom tersebut',
+ 'Close a task in a specific column when not moved during a given period' => 'Tutup tugas pada kolom tertentu saat tidak dipindahkan pada periode yang diberikan',
+ 'Edit columns' => 'Edit kolom',
+ 'The column restriction has been created successfully.' => 'Batasan kolom berhasil dibuat.',
+ 'Unable to create this column restriction.' => 'Tidak dapat membuat batasan kolom ini.',
+ 'Column restriction removed successfully.' => 'Batasan kolom berhasil dihapus.',
+ 'Unable to remove this restriction.' => 'Gagal menghapus batasan ini.',
+ 'Your custom project role has been created successfully.' => 'Peran kustom proyek Anda berhasil dibuat.',
+ 'Unable to create custom project role.' => 'Tidak dapat membuat peran proyek kustom.',
+ 'Your custom project role has been updated successfully.' => 'Peran proyek kustom Anda berhasil diperbarui.',
+ 'Unable to update custom project role.' => 'Tidak dapat memperbarui peran kustom proyek',
+ 'Custom project role removed successfully.' => 'Peran kustom proyek berhasil dihapus.',
+ 'Unable to remove this project role.' => 'Tidak dapat menghapus peran proyek ini.',
+ 'The project restriction has been created successfully.' => 'Batasan proyek ini berhasil dibuat.',
+ 'Unable to create this project restriction.' => 'Tidak dapat membuat batasan proyek ini.',
+ 'Project restriction removed successfully.' => 'Batasan proyek berhasil dihapus.',
+ 'You cannot create tasks in this column.' => 'Anda tidak dapat membuat tugas di kolom ini.',
+ 'Task creation is permitted for this column' => 'Pembuatan tugas diizinkan untuk kolom ini',
+ 'Closing or opening a task is permitted for this column' => 'Penutupan atau pembukaan tugas diizinkan untuk kolom ini',
+ 'Task creation is blocked for this column' => 'Pembuatan tugas diblokir dari kolom ini',
+ 'Closing or opening a task is blocked for this column' => 'Penutupan atau pembukaan tugas diblokir di kolom ini',
+ 'Task creation is not permitted' => 'Oembuatan tugas tidak diizinkan',
+ 'Closing or opening a task is not permitted' => 'Penutupan atau pembukaan tugas tidak diizinkan',
+ 'New drag and drop restriction for the role "%s"' => 'Larangan drag and drop baru untuk peran "%s"',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Orang-orang dengan peran ini dapat memindahkan tugas diantara kolom sumber dan destinasi.',
+ 'Remove a column restriction' => 'Hapus pembatasan kolom',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Anda yakin ingin menghapus pembatasan kolom ini: "%s" ke "%s"?',
+ 'New column restriction for the role "%s"' => 'Kolom pembatasan baru untuk peran "%s"',
+ 'Rule' => 'Aturan',
+ 'Do you really want to remove this column restriction?' => 'Anda yakin ingin menghapus pembatasan kolom ini?',
+ 'Custom roles' => 'Peran kustom',
+ 'New custom project role' => 'Peran kustom proyek baru',
+ 'Edit custom project role' => 'Edit peran kustom proyek',
+ 'Remove a custom role' => 'Hapus peran kustom',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Anda yakin ingin menghapus peran kustom ini: "%s"? Semua orang yang memiliki peran ini akan berubah menjadi anggota proyek.',
+ 'There is no custom role for this project.' => 'Tidak ada peran kustom untuk proyek ini.',
+ 'New project restriction for the role "%s"' => 'Batasan proyek baru untuk peran "%s"',
+ 'Restriction' => 'Pembatasan',
+ 'Remove a project restriction' => 'Hapus batasan proyek',
+ 'Do you really want to remove this project restriction: "%s"?' => 'Anda yakin ingin menghapus pembatasan proyek ini: "%s"?',
+ 'Duplicate to multiple projects' => 'Duplikasikan ke banyak proyek',
+ 'This field is required' => 'Bidang ini dibutuhkan',
+ 'Moving a task is not permitted' => 'Memindahkan tugas tidak diizinkan',
+ 'This value must be in the range %d to %d' => 'Nilai ini harus berkisar antara %d hingga %d',
+ 'You are not allowed to move this task.' => 'Anda tidak diizinkan untuk memindahkan tugas ini.',
+ 'API User Access' => 'API Akses Pengguna',
+ 'Preview' => 'Pratinjau',
+ 'Write' => 'Tulis',
+ 'Write your text in Markdown' => 'Tuliskan teks Anda di Markdown',
+ 'New External Task: %s' => 'Tugas Eksternal Baru: %s',
+ 'No personal API access token registered.' => 'Tidak ada token akses API personal yang terdaftar.',
+ 'Your personal API access token is "%s"' => 'Token akses API personal Anda adalah "%s"',
+ 'Remove your token' => 'Hapus token Anda',
+ 'Generate a new token' => 'Generate token baru',
+ 'Showing %d-%d of %d' => 'Menampilkan %d-%d of %d',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index 9d2af814..f7c6c5ee 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d task sulla bacheca',
'%d tasks in total' => '%d task in totale',
'Unable to update this board.' => 'Impossibile aggiornare questa bacheca.',
- 'Edit board' => 'Modifica questa bacheca',
'Disable' => 'Disattiva',
'Enable' => 'Attiva',
'New project' => 'Nuovo progetto',
'Do you really want to remove this project: "%s"?' => 'Vuoi davvero eliminare il seguente progetto: "%s" ?',
'Remove project' => 'Cancella il progetto',
'Edit the board for "%s"' => 'Modifica la bacheca per "%s"',
- 'All projects' => 'Tutti i progetti',
'Add a new column' => 'Aggiungi una nuova colonna',
'Title' => 'Titolo',
'Assigned to %s' => 'Assegnato a %s',
'Remove a column' => 'Cancella questa colonna',
- 'Remove a column from a board' => 'Cancella una colonna da una bacheca',
'Unable to remove this column.' => 'Impossibile cancellare questa colonna.',
'Do you really want to remove this column: "%s"?' => 'Desideri davvero cancellare questa colonna: "%s" ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Questa azione cancellerà TUTTI I TASK legati a questa colonna!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Comando VACUUM)',
'(Gzip compressed Sqlite file)' => '(File Sqlite compresso in Gzip)',
'Close a task' => 'Chiudi un task',
- 'Edit a task' => 'Modifica un task',
'Column' => 'Colonna',
'Color' => 'Colore',
'Assignee' => 'Assegnatario',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Numero di task',
'User' => 'Utente',
'Comments' => 'Commenti',
- 'Leave a comment' => 'Lascia un commento',
'Comment is required' => 'Si richiede un commento',
- 'Leave a description' => 'Lascia una descrizione',
'Comment added successfully.' => 'Commenti aggiunti con successo.',
'Unable to create your comment.' => 'Impossibile creare questo commento.',
'Due Date' => 'Data di scadenza',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Cerca',
'Nothing found.' => 'Non si è trovato nulla.',
'Due date' => 'Data di scadenza',
- 'Others formats accepted: %s and %s' => 'Altri formati accettati: %s e %s',
'Description' => 'Descrizione',
'%d comments' => '%d commenti',
'%d comment' => '%d commento',
@@ -299,9 +292,7 @@ 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',
+ 'Start Date' => 'Data di inizio',
'Execute' => 'Esegui',
'Task Id' => 'Id del task',
'Creator' => 'Creatore',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Remoto',
'Enabled' => 'Abilitato',
'Disabled' => 'Disabilitato',
- 'Username:' => 'Nome utente:',
- 'Name:' => 'Nome:',
+ 'Login:' => 'Nome utente:',
+ 'Full Name:' => 'Nome:',
// 'Email:' => '',
'Notifications:' => 'Notifiche:',
'Notifications' => 'Notifiche',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Etichette delle relazioni',
'Link modification' => 'Modifica relazione',
'Links' => 'Relazioni',
- 'Link settings' => 'Impostazioni relazioni',
'Opposite label' => 'Etichetta contraria',
'Remove a link' => 'Rimuovi una relazione',
- 'Task\'s links' => 'Relazioni del task',
'The labels must be different' => 'Le etichette devono essere diverse',
'There is no link.' => 'Nessuna relazione presente.',
'This label must be unique' => 'Questa etichetta deve essere univoca',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Vista compatta',
'Horizontal scrolling' => 'Scrolling orizzontale',
'Compact/wide view' => 'Vista compatta/estesa',
- 'No results match:' => 'Nessun risultato trovato:',
'Currency' => 'Valuta',
'Private project' => 'Progetto privato',
'AUD - Australian Dollar' => 'AUD - Dollari Australiani',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Yen Giapponesi',
'NZD - New Zealand Dollar' => 'NZD - Dollari della Nuova Zelanda',
'RSD - Serbian dinar' => 'RSD - Dinar Serbi',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Dollari Americani',
'Destination column' => 'Colonna destinazione',
'Move the task to another column when assigned to a user' => 'Sposta il task in un\'altra colonna quando viene assegnato ad un utente',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Tassi di cambio',
'Rate' => 'Cambio',
'Change reference currency' => 'Cambia la valuta di riferimento',
- 'Add a new currency rate' => 'Aggiungi un nuovo tasso di cambio',
'Reference currency' => 'Valuta di riferimento',
'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"',
@@ -615,7 +590,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',
@@ -662,10 +636,10 @@ return array(
'Project settings' => 'Impostazioni di progetto',
'Show subtasks based on the time tracking' => 'Mostra i sotto-task in base al time tracking',
'Show tasks based on the creation date' => 'Mostra i task in base alla data di creazione',
- 'Show tasks based on the start date' => 'Mostra i task in base alla data di inzio',
+ 'Show tasks based on the start date' => 'Mostra i task in base alla data di inizio',
'Subtasks time tracking' => 'Time tracking per i sotto-task',
'User calendar view' => 'Vista utente a calendario',
- 'Automatically update the start date' => 'Aggiorna automaticamente la data di inzio',
+ 'Automatically update the start date' => 'Aggiorna automaticamente la data di inizio',
'iCal feed' => 'feed iCal',
'Preferences' => 'Preferenze',
'Security' => 'Sicurezza',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
// '<30m' => '',
'Stop timer' => 'Ferma il timer',
'Start timer' => 'Avvia il timer',
- 'Add project member' => 'Aggiungi un membro di progetto',
'My activity stream' => 'Il mio flusso attività',
'My calendar' => 'Il mio calendario',
'Search tasks' => 'Ricerca task',
@@ -758,8 +725,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.',
@@ -775,15 +740,13 @@ return array(
'Cycle time: ' => 'Tempo di lavorazione (Cycle Time): ',
'Time spent into each column' => 'Tempo trascorso in ogni colonna',
'The lead time is the duration between the task creation and the completion.' => 'Il tempo di consegna (Lead Time) è la durata tra la creazione di un task ed il suo completamento.',
- 'The cycle time is the duration between the start date and the completion.' => 'Il tempo di lavorazione (Cycle Time) è la durata tra la data di inzio della lavorazione di un task ed il suo completamento.',
+ 'The cycle time is the duration between the start date and the completion.' => 'Il tempo di lavorazione (Cycle Time) è la durata tra la data di inizio della lavorazione di un task ed il suo completamento.',
'If the task is not closed the current time is used instead of the completion date.' => 'Se il task non è chiuso sarà usata la data attuale invece della data di completamento.',
- 'Set automatically the start date' => 'Imposta automaticamente la data di inzio',
+ 'Set automatically the start date' => 'Imposta automaticamente la data di inizio',
'Edit Authentication' => 'Modifica Autenticazione',
'Remote user' => 'Utente remoto',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'La password degli utenti remoti (ad esempio: LDAP, account Google e Github) non è salvata nel database di Kanboard',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se imposti l\'opzione "Disabilita il form di login", le credenzali inserite nella saranno ignorate.',
- 'New remote user' => 'Nuovo utente remoto',
- 'New local user' => 'Nuovo utente locale',
'Default task color' => 'Colore predefinito dei task',
'This feature does not work with all browsers.' => 'Questa feature non funziona con tutti i browser.',
'There is no destination project available.' => 'Non ci sono progetti disponbili come destinazione.',
@@ -800,14 +763,13 @@ 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',
'Start date:' => 'Data di inizio:',
'Due date:' => 'Data di completamento:',
- 'There is no start date or due date for this task.' => 'Nessuna data di inzio o di scadenza per questo task.',
- 'Moving or resizing a task will change the start and due date of the task.' => 'Spostando o ridimensionado un task ne modifca la data di inzio e di scadenza.',
+ 'There is no start date or due date for this task.' => 'Nessuna data di inizio o di scadenza per questo task.',
+ 'Moving or resizing a task will change the start and due date of the task.' => 'Spostando o ridimensionado un task ne modifca la data di inizio e di scadenza.',
'There is no task in your project.' => 'Non ci sono task nel tuo progetto.',
'Gantt chart' => 'Grafici Gantt',
'People who are project managers' => 'Persone che sono manager di progetto',
@@ -826,7 +788,7 @@ return array(
'Gantt chart for this project' => 'Grafico Gantt per questo progetto',
'Project board' => 'Bacheca del progetto',
'End date:' => 'Data di fine:',
- 'There is no start date or end date for this project.' => 'Non è prevista una data di inzio o fine per questo progetto.',
+ 'There is no start date or end date for this project.' => 'Non è prevista una data di inizio o fine per questo progetto.',
'Projects Gantt chart' => 'Grafico Gantt dei progetti',
'Change task color when using a specific task link' => 'Cambia colore del task quando si un utilizza una determinata relazione di task',
'Task link creation or modification' => 'Creazione o modifica di relazione di task',
@@ -841,8 +803,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.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Proprietario',
'Unread notifications' => 'Notifiche non lette',
'Notification methods:' => 'Metodi di notifica',
- 'Import tasks from CSV file' => 'Importa task da file CSV',
'Unable to read your file' => 'Impossibile leggere il file',
'%d task(s) have been imported successfully.' => '%d task sono stati importati con successo.',
'Nothing have been imported!' => 'Non è stato importato nulla!',
@@ -947,7 +906,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',
@@ -968,13 +926,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',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'Proprietario del progetto: ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identificativo del progetto è opzionale e deve essere alfanumerico, ad esempio: MIOPROGETTO.',
'Project owner' => 'Proprietario del progetto',
- 'Those dates are useful for the project Gantt chart.' => 'Le seguenti date sono utilizzate per i grafici Gantt di progetto.',
'Private projects do not have users and groups management.' => 'Per i progetti privati non è prevista la gestione di utenti e gruppi.',
'There is no project member.' => 'Non è impostato un membro del progetto.',
'Priority' => 'Priorità',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Iniziato:',
'Moved:' => 'Spostato:',
// 'Task #%d' => '',
- 'Date and time format' => 'Formato data e ora',
'Time format' => 'Formato ora',
'Start date: ' => 'Data di inizio: ',
'End date: ' => 'Data di fine: ',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Utente disabilitato con successo.',
'Unable to disable this user.' => 'Impossibile disabilitare questo utente.',
'All files have been uploaded successfully.' => 'Tutti i file sono stati caricati con successo.',
- 'View uploaded files' => 'Visualizza i file caricati',
'The maximum allowed file size is %sB.' => 'La dimensione massima consentita del file è %sB.',
- 'Choose files again' => 'Seleziona nuovamente i file',
'Drag and drop your files here' => 'Trascina i tuoi file qui',
'choose files' => 'seleziona i file',
'View profile' => 'Guarda il profilo',
@@ -1135,7 +1086,7 @@ return array(
'New internal link' => 'Nuovo link interno',
'Display list of keyboard shortcuts' => 'Mostra una lista di scorciatoie da tastiera',
// 'Menu' => '',
- 'Set start date' => 'Imposta la data di inzio',
+ 'Set start date' => 'Imposta la data di inizio',
// 'Avatar' => '',
'Upload my avatar image' => 'Carica l\'immagine del mio avatar',
'Remove my image' => 'Rimuovi la mia immagine',
@@ -1194,8 +1145,7 @@ return array(
'Email settings' => 'Impostazioni Email',
'Email sender address' => 'Indirizzo Email mittente',
'Email transport' => 'Trasporto Email',
- // 'Webhook token' => '',
- 'Imports' => 'Importa',
+ 'Webhook token' => 'Token Webhook',
'Project tags management' => 'Gestione tag di progetto',
'Tag created successfully.' => 'Tag creato con successo.',
'Unable to create this tag.' => 'Impossibile creare questo tag.',
@@ -1216,5 +1166,145 @@ return array(
'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',
- // 'Hide tasks in this column in the Dashboard' => '',
+ 'Close a task when there is no activity in an specific column' => 'Chiudi un task quando non vi è alcuna attività in una specifica colonna',
+ '%s removed a subtask for the task #%d' => '%s rimosso un subtask per il task #%d',
+ '%s removed a comment on the task #%d' => '%s rimosso un commento nel task #%d',
+ 'Comment removed on task #%d' => 'Commento rimosso nel task #%d',
+ 'Subtask removed on task #%d' => 'Subtask rimosso nel task #%d',
+ 'Hide tasks in this column in the dashboard' => 'Nascondi tasks in questa colonna nella dashboard',
+ '%s removed a comment on the task %s' => '%s rimosso un commento nel task %s',
+ '%s removed a subtask for the task %s' => '%s rimosso un subtask per il task %s',
+ 'Comment removed' => 'Commento rimosso',
+ 'Subtask removed' => 'Subtask rimosso',
+ '%s set a new internal link for the task #%d' => '%s imposta un nuovo link interno per il task #%d',
+ '%s removed an internal link for the task #%d' => '%s rimosso un link interno per il task #%d',
+ 'A new internal link for the task #%d have been defined' => 'E\' stato definito un nuovo link interno per il task #%d',
+ 'Internal link removed for the task #%d' => 'Link interno rimosso per il task #%d',
+ '%s set a new internal link for the task %s' => '%s imposta un nuovo link interno per il task %s',
+ '%s removed an internal link for the task %s' => '%s rimosso un link interno per il task %s',
+ 'Automatically set the due date on task creation' => 'Imposta automaticamente la data di scadenza alla creazione del task',
+ 'Move the task to another column when closed' => 'Sposta il task su un\'altra colonna quando viene chiuso',
+ 'Move the task to another column when not moved during a given period' => 'Sposta il task su un\'altra colonna quando non viene spostato entro un certo periodo',
+ 'Dashboard for %s' => 'Dashboard per %s',
+ 'Tasks overview for %s' => 'Panoramica tasks per %s',
+ 'Subtasks overview for %s' => 'Panoramica tasks per %s',
+ 'Projects overview for %s' => 'Panoramica progetti per %s',
+ 'Activity stream for %s' => 'Flusso attività per %s',
+ 'Calendar for %s' => 'Calendario per %s',
+ 'Notifications for %s' => 'Notifiche per %s',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Assegna un colore quando il task viene spostato su una specifica swimlane',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Assegna una priorità quando il task viene spostato su una specifica swimlane',
+ 'User unlocked successfully.' => 'Utente sbloccato con successo.',
+ 'Unable to unlock the user.' => 'Impossibile sbloccare l\'utente.',
+ 'Move a task to another swimlane' => 'Sposta un task su un\'altra swimlane',
+ 'Creator Name' => 'Nome del creatore',
+ 'Time spent and estimated' => 'Tempo trascorso e stima',
+ 'Move position' => 'Cambia posizione',
+ 'Move task to another position on the board' => 'Sposta task su un\'altra posizione nella board',
+ 'Insert before this task' => 'Inserisci prima di questo task',
+ 'Insert after this task' => 'Inserisci dopo questo task',
+ 'Unlock this user' => 'Sblocca quest\'utente',
+ 'Custom Project Roles' => 'Personalizza Ruoli del Progetto',
+ 'Add a new custom role' => 'Aggiungi un nuovo ruolo personalizzato',
+ 'Restrictions for the role "%s"' => 'Restrizioni per il ruolo "%s"',
+ 'Add a new project restriction' => 'Aggiungi una nuova restrizione per il progetto',
+ 'Add a new drag and drop restriction' => 'Aggiungi una nuova restrizione per il trascinamento',
+ 'Add a new column restriction' => 'Aggiungi una nuova restrizione per la colonna',
+ 'Edit this role' => 'Modifica questo ruolo',
+ 'Remove this role' => 'Cancella questo ruolo',
+ 'There is no restriction for this role.' => 'Non ci sono restrizioni per questo ruolo.',
+ 'Only moving task between those columns is permitted' => 'È permesso solo muovere i tast tra queste colonne',
+ 'Close a task in a specific column when not moved during a given period' => 'Chiudi un task in una colonna specifica quando non viene mosso per un determinato periodo',
+ 'Edit columns' => 'Modifica colonne',
+ 'The column restriction has been created successfully.' => 'La restrizione per le colonne è stata creata correttamente.',
+ 'Unable to create this column restriction.' => 'Impossibile creare questa restrizione per colonne.',
+ 'Column restriction removed successfully.' => 'Restrizione per colonne rimossa correttamente.',
+ 'Unable to remove this restriction.' => 'Impossibile rimuovere questa restrizione.',
+ 'Your custom project role has been created successfully.' => 'La regola personalizzata per il progetto è stata creata correttamente.',
+ 'Unable to create custom project role.' => 'Impossibile creare la regola personalizzata per il progetto.',
+ 'Your custom project role has been updated successfully.' => 'La regola personalizzata per il progetto è stata modificata correttamente.',
+ 'Unable to update custom project role.' => 'Impossibile modificare correttamente la regola personalizzata per il progetto.',
+ 'Custom project role removed successfully.' => 'Regola personalizzata per il progetto rimossa correttamente.',
+ 'Unable to remove this project role.' => 'Impossibile rimuovere questa regola per il progetto.',
+ 'The project restriction has been created successfully.' => 'La restrizione di progetto è stata creata con successo.',
+ 'Unable to create this project restriction.' => 'Impossibile creare la restrizione di progetto.',
+ 'Project restriction removed successfully.' => 'Restrizione di progetto rimossa correttamente.',
+ 'You cannot create tasks in this column.' => 'Non puoi creare dei task in questa colonna.',
+ 'Task creation is permitted for this column' => 'La creazione di task è permessa per questa colonna',
+ 'Closing or opening a task is permitted for this column' => 'Chiudere o aprire task è permesso per questa colonna',
+ 'Task creation is blocked for this column' => 'La creazione di task è bloccata per questa colonna',
+ 'Closing or opening a task is blocked for this column' => 'Chiudere o aprire task è bloccato per questa colonna',
+ 'Task creation is not permitted' => 'Creazione task non permessa',
+ 'Closing or opening a task is not permitted' => 'Chiudere o aprire task non è permesso',
+ 'New drag and drop restriction for the role "%s"' => 'Nuova restrizione "drag and drop" per la regola "%s"',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Gli utenti che appartengono a questa regola saranno in grado di spostare i task solo tra la colonna di origine e quella di destinazione',
+ 'Remove a column restriction' => 'Rimuovere una restrizione di colonna',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Vuoi davvero rimuovere questa restrizione di colonna: "%s" a "%s"?',
+ 'New column restriction for the role "%s"' => 'Nuova restrizione di colonna per la regola "%s"',
+ 'Rule' => 'Regola',
+ 'Do you really want to remove this column restriction?' => 'Vuoi davvero rimuovere questa restrizione di colonna?',
+ 'Custom roles' => 'Regole personalizzate',
+ 'New custom project role' => 'Nuova regola per progetto personalizzato',
+ 'Edit custom project role' => 'Modifica regola per progetto personalizzato',
+ 'Remove a custom role' => 'Rimuovi regola personalizzata',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Vuoi davvero rimuovere questa regola personalizzata: "%s"? Tutti gli utenti assegnati a questa regola diventeranno membri del progetto',
+ 'There is no custom role for this project.' => 'Non c\'è regola personalizzata per questo progetto.',
+ 'New project restriction for the role "%s"' => 'Nuova restrizione di progetto per la regola "%s"',
+ 'Restriction' => 'Restrizione',
+ 'Remove a project restriction' => 'Rimuovi restrizione di progetto',
+ 'Do you really want to remove this project restriction: "%s"?' => 'Vuoi davvero rimuovere questa restrizione di progetto: "%s"?',
+ 'Duplicate to multiple projects' => 'Duplica su più progetti',
+ 'This field is required' => 'Questo campo è obbligatorio',
+ 'Moving a task is not permitted' => 'Spostare task non è permesso',
+ 'This value must be in the range %d to %d' => 'Questo valore deve essere compreso tra %d e %d',
+ 'You are not allowed to move this task.' => 'Non ti è permesso spostare questo task.',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index 9f6ab88f..e747c320 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d 個のタスク',
'%d tasks in total' => '合計 %d 個のタスク',
'Unable to update this board.' => 'ボードを更新できませんでした',
- 'Edit board' => 'ボードを変更する',
'Disable' => '無効にする',
'Enable' => '有効にする',
'New project' => 'プロジェクトを作る',
'Do you really want to remove this project: "%s"?' => 'プロジェクト「%s」を本当に削除しますか?',
'Remove project' => 'プロジェクトの削除',
'Edit the board for "%s"' => 'ボード「%s」を変更する',
- 'All projects' => 'すべてのプロジェクト',
'Add a new column' => 'カラムの追加',
'Title' => 'タイトル',
'Assigned to %s' => '%sが担当',
'Remove a column' => 'カラムの削除',
- 'Remove a column from a board' => 'ボードからカラムの削除',
'Unable to remove this column.' => 'カラムを削除できませんでした。',
'Do you really want to remove this column: "%s"?' => 'カラム「%s」を削除しますか?',
'This action will REMOVE ALL TASKS associated to this column!' => 'この操作はこのカラムに割当てられた『全てのタスクを削除』します!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM コマンド)',
'(Gzip compressed Sqlite file)' => '(GZip コマンドで圧縮された Sqlite ファイル)',
'Close a task' => 'タスクをクロースする',
- 'Edit a task' => 'タスクを変更する',
'Column' => 'カラム',
'Color' => '色',
'Assignee' => '担当',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'タスク数',
'User' => 'ユーザ',
'Comments' => 'コメント',
- 'Leave a comment' => 'コメントを書く',
'Comment is required' => 'コメントを入力してください',
- 'Leave a description' => '説明を書く',
'Comment added successfully.' => 'コメントを追加しました。',
'Unable to create your comment.' => 'コメントの追加に失敗しました。',
'Due Date' => '期限',
@@ -226,7 +220,6 @@ return array(
'Search' => '検索',
'Nothing found.' => '結果なし。',
'Due date' => '期限',
- 'Others formats accepted: %s and %s' => '他の書式: %s または %s',
'Description' => '説明',
'%d comments' => '%d 個のコメント',
'%d comment' => '%d 個のコメント',
@@ -299,9 +292,7 @@ return array(
'Display another project' => '別のプロジェクトを表示',
'Created by %s' => '%s が作成',
'Tasks Export' => 'タスクの出力',
- 'Tasks exportation for "%s"' => '「%s」のタスク出力',
'Start Date' => '開始日',
- 'End Date' => '終了日',
'Execute' => '実行',
'Task Id' => 'タスク ID',
'Creator' => '作成者',
@@ -322,14 +313,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' => '公開アクセス設定',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'リモート',
'Enabled' => '有効',
'Disabled' => '無効',
- 'Username:' => 'ユーザ名:',
- 'Name:' => '名前:',
+ 'Login:' => 'ユーザ名:',
+ 'Full Name:' => '名前:',
'Email:' => 'Email:',
'Notifications:' => '通知:',
'Notifications' => '通知',
@@ -386,14 +372,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' => 'イベントの選択',
@@ -442,13 +426,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.' => 'グラフを描画するには出たが足りません',
@@ -465,10 +446,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' => 'アクティブなスイムレーン',
@@ -494,7 +473,6 @@ return array(
'Subtask Id' => 'サブタスク Id',
'Subtasks' => 'サブタスク',
'Subtasks Export' => 'サブタスクの出力',
- 'Subtasks exportation for "%s"' => '「%s」のサブタスク出力',
'Task Title' => 'タスクタイトル',
'Untitled' => 'タイトル無し',
'Application default' => 'アプリケーションデフォルト',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'リンクラベル',
'Link modification' => 'リンクの変更',
'Links' => 'リンク',
- 'Link settings' => 'リンク設定',
'Opposite label' => '反対のラベル',
'Remove a link' => 'ラベルの削除',
- 'Task\'s links' => 'タスクのラベル',
'The labels must be different' => '異なるラベルを指定してください',
'There is no link.' => 'リンクがありません',
'This label must be unique' => 'ラベルはユニークである必要があります',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'コンパクトビュー',
'Horizontal scrolling' => '縦スクロール',
'Compact/wide view' => 'コンパクト/ワイドビュー',
- 'No results match:' => '結果が一致しませんでした',
'Currency' => '通貨',
'Private project' => 'プライベートプロジェクト',
'AUD - Australian Dollar' => 'AUD - 豪ドル',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - 日本円',
'NZD - New Zealand Dollar' => 'NZD - NZ ドル',
'RSD - Serbian dinar' => 'RSD - セルビアデナール',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - 米ドル',
'Destination column' => '移動先のカラム',
'Move the task to another column when assigned to a user' => 'ユーザの割り当てをしたらタスクを他のカラムに移動',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => '為替レート',
'Rate' => 'レート',
'Change reference currency' => '現在の基軸通貨',
- 'Add a new currency rate' => '新しい通貨レートを追加',
'Reference currency' => '基軸通貨',
// '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 段認証をチェックする',
@@ -615,7 +590,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' => '',
@@ -680,14 +654,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' => '',
@@ -725,7 +693,6 @@ return array(
// '<30m' => '',
// 'Stop timer' => '',
// 'Start timer' => '',
- // 'Add project member' => '',
// 'My activity stream' => '',
// 'My calendar' => '',
// 'Search tasks' => '',
@@ -758,8 +725,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.' => '',
@@ -782,8 +747,6 @@ return array(
// 'Remote user' => '',
// 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '',
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
- // 'New remote user' => '',
- // 'New local user' => '',
// 'Default task color' => '',
// 'This feature does not work with all browsers.' => '',
// 'There is no destination project available.' => '',
@@ -800,7 +763,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/ko_KR/translations.php b/app/Locale/ko_KR/translations.php
index f48b7486..e1825ad7 100644
--- a/app/Locale/ko_KR/translations.php
+++ b/app/Locale/ko_KR/translations.php
@@ -61,21 +61,18 @@ return array(
'%d tasks on the board' => '%d개의 할일',
'%d tasks in total' => '총 %d개의 할일',
'Unable to update this board.' => '보드를 갱신할 수 없었습니다',
- 'Edit board' => '보드를 변경하는 ',
'Disable' => '비활성화',
'Enable' => '유효하게 한다',
'New project' => '새 프로젝트',
'Do you really want to remove this project: "%s"?' => '프로젝트를 삭제하시겠습니까: "%s"?',
'Remove project' => '프로젝트의 삭제',
'Edit the board for "%s"' => '"%s"를 위한 보드 수정',
- 'All projects' => '모든 프로젝트',
- 'Add a new column' => '칼럼의 추가',
+ 'Add a new column' => '컬럼의 추가',
'Title' => '제목',
'Assigned to %s' => '담당자 %s',
- 'Remove a column' => '칼럼 삭제',
- 'Remove a column from a board' => '보드에서 칼럼 삭제',
+ 'Remove a column' => '컬럼 삭제',
'Unable to remove this column.' => '(※)컬럼을 삭제할 수 없었습니다.',
- 'Do you really want to remove this column: "%s"?' => '칼럼을 삭제하시겠습니까: "%s"?',
+ 'Do you really want to remove this column: "%s"?' => '컬럼을 삭제하시겠습니까: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => '이 조작은 이 컬럼에 할당된 『 모든 할일을 삭제 』합니다!',
'Settings' => '설정',
'Application settings' => '애플리케이션의 설정',
@@ -88,8 +85,7 @@ return array(
'(VACUUM command)' => '(VACUUM명령)',
'(Gzip compressed Sqlite file)' => '(GZip명령으로 압축된 Sqlite파일)',
'Close a task' => '할일 마치기',
- 'Edit a task' => '할일 수정',
- 'Column' => '칼럼',
+ 'Column' => '컬럼',
'Color' => '색',
'Assignee' => '담당자',
'Create another task' => '다른 할일 추가',
@@ -98,11 +94,11 @@ return array(
'Do you really want to open this task: "%s"?' => '할일은 시작 하시겠습니까: "%s"?',
'Back to the board' => '보드로 돌아가기',
'There is nobody assigned' => '담당자가 없습니다',
- 'Column on the board:' => '칼럼:',
+ '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 +141,7 @@ return array(
'User removed successfully.' => '사용자를 삭제했습니다.',
'Unable to remove this user.' => '사용자 삭제에 실패했습니다.',
'Board updated successfully.' => '보드를 갱신했습니다.',
- 'Ready' => '준비완료',
+ 'Ready' => '준비중',
'Backlog' => '요구사항',
'Work in progress' => '진행중',
'Done' => '완료',
@@ -162,9 +158,7 @@ return array(
'Task count' => '할일 수',
'User' => '사용자',
'Comments' => '댓글',
- 'Leave a comment' => '댓글 남기기',
'Comment is required' => '댓글을 입력하세요',
- 'Leave a description' => '설명을 입력하세요',
'Comment added successfully.' => '의견을 추가했습니다.',
'Unable to create your comment.' => '댓글의 추가에 실패했습니다.',
'Due Date' => '마감일',
@@ -189,15 +183,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' => '사용자 색 할당',
- 'Column title' => '칼럼의 제목',
+ '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.' => '댓글을 갱신했습니다.',
@@ -226,7 +220,6 @@ return array(
'Search' => '검색',
'Nothing found.' => '결과가 없습니다',
'Due date' => '마감일',
- 'Others formats accepted: %s and %s' => ' 다른 서식: %s 또는 %s',
'Description' => '설명',
'%d comments' => '%d개의 댓글',
'%d comment' => '%d개의 댓글',
@@ -299,9 +292,7 @@ return array(
'Display another project' => '프로젝트 보기',
'Created by %s' => '작성자 %s',
'Tasks Export' => '할일 내보내기',
- 'Tasks exportation for "%s"' => '"%s" 할일 내보내기',
'Start Date' => '시작일',
- 'End Date' => '종료일',
'Execute' => '실행',
'Task Id' => '할일 ID',
'Creator' => '작성자',
@@ -322,14 +313,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' => '공개 접속 설정',
@@ -350,8 +336,8 @@ return array(
'Remote' => '원격',
'Enabled' => '활성화',
'Disabled' => '비활성화',
- 'Username:' => '사용자명',
- 'Name:' => '이름:',
+ 'Login:' => '사용자명',
+ 'Full Name:' => '이름:',
'Email:' => '이메일:',
'Notifications:' => '알림:',
'Notifications' => '알림',
@@ -365,10 +351,10 @@ return array(
'Password modified successfully.' => '패스워드를 변경했습니다.',
'Unable to change the password.' => '비밀 번호가 변경할 수 없었습니다.',
'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" 로 옮겼습니다',
+ '%s moved the task %s to the column "%s"' => '%s이 할일 %s을 컬럼 "%s" 로 옮겼습니다',
'%s created the task %s' => '%s이 할일%s을 추가했습니다',
'%s closed the task %s' => '%s이 할일%s을 마쳤습니다',
'%s created a subtask for the task %s' => '%s이 할일%s의 서브 할일을 추가했습니다',
@@ -386,14 +372,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)' => '새로운 프로젝트의 기본 칼럼 (콤마(,)로 분리됨)',
+ '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' => '행사의 선택',
@@ -412,9 +396,9 @@ return array(
'Refresh interval for private board' => '비공개 보드의 갱신 빈도',
'Refresh interval for public board' => '공개 보드의 갱신 빈도',
'Task highlight period' => '할일의 하이라이트 기간',
- // 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '',
- // 'Frequency in second (60 seconds by default)' => '',
- // 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '',
+ 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '최근 수정된 작업 고려 기간(초), (비활성화:0, 기본값:2일)',
+ 'Frequency in second (60 seconds by default)' => '초당 횟수(기본값:60초)',
+ 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '초당 횟수(비활성화:0, 기본값:10초)',
'Application URL' => '애플리케이션의 URL',
'Token regenerated.' => '토큰이 다시 생성되었습니다.',
'Date format' => '데이터 포멧',
@@ -436,19 +420,16 @@ return array(
'Create a comment from an external provider' => '외부 서비스로부터 의견을 작성한다',
'Project management' => '프로젝트 관리',
'My projects' => '내 프로젝트',
- 'Columns' => '칼럼',
+ 'Columns' => '컬럼',
'Task' => '할일',
'Your are not member of any project.' => '어떤 프로젝트에도 속하지 않습니다.',
'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.' => '그래프를 선묘화하려면 나왔지만 부족합니다',
@@ -465,10 +446,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' => '액티브한 스윔레인',
@@ -494,22 +473,21 @@ return array(
'Subtask Id' => '서브 할일 Id',
'Subtasks' => '서브 할일',
'Subtasks Export' => '서브 할일 출력',
- 'Subtasks exportation for "%s"' => '"%s"의 서브 할일 출력',
'Task Title' => '할일 제목',
'Untitled' => '제목 없음',
'Application default' => '애플리케이션 기본',
'Language:' => '언어:',
'Timezone:' => '시간대:',
- 'All columns' => '모든 칼럼',
+ 'All columns' => '모든 컬럼',
'Calendar' => '달력',
'Next' => '다음에 ',
'#%d' => '#%d',
'All swimlanes' => '모든 스윔레인',
'All colors' => '모든 색',
- 'Moved to column %s' => '"%s" 칼럼으로 이동',
+ 'Moved to column %s' => '"%s" 컬럼으로 이동',
'User dashboard' => '대시보드',
'Allow only one subtask in progress at the same time for a user' => '한 사용자에 대한 하나의 할일만 진행 중에 가능합니다',
- 'Edit column "%s"' => '"%s" 칼럼 수정',
+ 'Edit column "%s"' => '"%s" 컬럼 수정',
'Select the new status of the subtask: "%s"' => '서브 할일의 새로운 상태 선택: "%s"',
'Subtask timesheet' => '서브 할일 타임시트',
'There is nothing to show.' => '기록이 없습니다',
@@ -520,7 +498,7 @@ return array(
'Start' => '시작',
'End' => '종료',
'Task age in days' => '할일이 생긴 시간',
- 'Days in this column' => '이 칼럼에 있는 시간',
+ 'Days in this column' => '이 컬럼에 있는 시간',
'%dd' => '%d일',
'Add a new link' => ' 새로운 링크 추가',
'Do you really want to remove this link: "%s"?' => '링크를 삭제하시겠습니까: "%s"?',
@@ -532,10 +510,8 @@ return array(
'Link labels' => '링크 라벨',
'Link modification' => '링크의 변경',
'Links' => '링크',
- 'Link settings' => '링크 설정',
'Opposite label' => '반대의 라벨',
'Remove a link' => '라벨의 삭제',
- 'Task\'s links' => '할일의 라벨',
'The labels must be different' => ' 다른 라벨을 지정하세요',
'There is no link.' => '링크가 없습니다',
'This label must be unique' => '라벨은 독특할 필요가 있습니다',
@@ -568,40 +544,39 @@ return array(
'Compact view' => '컴팩트 뷰',
'Horizontal scrolling' => '세로 스크롤',
'Compact/wide view' => '컴팩트/와이드 뷰',
- 'No results match:' => '결과가 일치하지 않았습니다',
'Currency' => '통화',
'Private project' => '개인 프로젝트',
- // 'AUD - Australian Dollar' => '',
- // 'CAD - Canadian Dollar' => '',
- // 'CHF - Swiss Francs' => '',
+ 'AUD - Australian Dollar' => 'AUD - 호주 달러',
+ 'CAD - Canadian Dollar' => 'CAD -캐나다 달러',
+ 'CHF - Swiss Francs' => 'CHF - 스위스 프랑',
'Custom Stylesheet' => '커스텀 스타일 시트',
'download' => '다운로드',
- // 'EUR - Euro' => '',
- // 'GBP - British Pound' => '',
- // 'INR - Indian Rupee' => '',
- // 'JPY - Japanese Yen' => '',
- // 'NZD - New Zealand Dollar' => '',
- // 'RSD - Serbian dinar' => '',
- // 'USD - US Dollar' => '',
- 'Destination column' => '이동 후 칼럼',
- 'Move the task to another column when assigned to a user' => '사용자의 할당을 하면 할일을 다른 칼럼에 이동',
- 'Move the task to another column when assignee is cleared' => '사용자의 할당이 없어지면 할일을 다른 칼럼에 이동',
- 'Source column' => '이동 전 칼럼',
+ 'EUR - Euro' => 'EUR - 유로',
+ 'GBP - British Pound' => 'GBP - 영국 파운드',
+ 'INR - Indian Rupee' => 'INR - 인도 루피',
+ 'JPY - Japanese Yen' => 'JPY - 일본 엔',
+ 'NZD - New Zealand Dollar' => 'NZD - 뉴질랜드 달러',
+ 'RSD - Serbian dinar' => 'RSD - 세르비아 디나르',
+ // 'CNY - Chinese Yuan' => '',
+ 'USD - US Dollar' => 'USD - 미국 달러',
+ 'Destination column' => '이동 후 컬럼',
+ 'Move the task to another column when assigned to a user' => '사용자의 할당을 하면 할일을 다른 컬럼에 이동',
+ 'Move the task to another column when assignee is cleared' => '사용자의 할당이 없어지면 할일을 다른 컬럼에 이동',
+ 'Source column' => '이동 전 컬럼',
'Transitions' => '이력',
'Executer' => '실행자',
- 'Time spent in the column' => '칼럼에 있던 시간',
+ 'Time spent in the column' => '컬럼에 있던 시간',
'Task transitions' => '할일 천이',
'Task transitions export' => '할일 천이를 출력',
- 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '이 리포트는 할일의 칼럼 간 이동을 시간, 유저, 경과 시간과 함께 기록한 것입니다.',
+ 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => '이 리포트는 할일의 컬럼 간 이동을 시간, 유저, 경과 시간과 함께 기록한 것입니다.',
'Currency rates' => '환율',
'Rate' => '레이트',
'Change reference currency' => '현재의 기축 통화',
- 'Add a new currency rate' => ' 새로운 통화 환율을 추가',
'Reference currency' => '기축 통화',
'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단 인증을 체크한다',
@@ -613,16 +588,15 @@ return array(
'Check my code' => '코드 체크',
'Secret key: ' => '비밀키: ',
'Test your device' => '디바이스 테스트',
- 'Assign a color when the task is moved to a specific column' => '상세 칼럼으로 이동할 할일의 색깔을 지정하세요',
+ '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).' => '',
+ 'This chart show the task complexity over the time (Work Remaining).' => '이 차트는 시간에 따른 할일의 복잡성을 보여줍니다. (남아있는 일)',
'Screenshot taken %s' => '스크린샷_%s',
'Add a screenshot' => '스크린샷 추가',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => '스크린샷을 CTRL+V 혹은 ⌘+V를 눌러 붙여넣기',
'Screenshot uploaded successfully.' => '스크린샷을 업로드하였습니다',
- // 'SEK - Swedish Krona' => '',
+ 'SEK - Swedish Krona' => 'SEK - 스위스 크로나',
'Identifier' => '식별자',
'Disable two factor authentication' => '이중 인증 비활성화',
'Do you really want to disable the two factor authentication for this user: "%s"?' => '"%s" 담당자의 이중 인증을 비활성화 하시겠습니까?',
@@ -630,7 +604,7 @@ return array(
'Start to type task title...' => '할일 제목을 입력하세요',
'A task cannot be linked to itself' => '할일을 자기자신에게 연결할 수 없습니다',
'The exact same link already exists' => '동일한 링크가 이미 존재합니다',
- // 'Recurrent task is scheduled to be generated' => '',
+ 'Recurrent task is scheduled to be generated' => '반복 할일이 생성된 예정입니다.',
'Score' => '점수',
'The identifier must be unique' => '식별자는 유일해야 합니다',
'This linked task id doesn\'t exists' => '연결된 할일 ID가 존재하지 않습니다',
@@ -643,7 +617,7 @@ return array(
'Base date to calculate new due date' => '새로운 기본 종료날짜 계산',
'Action date' => '시작날짜',
'Base date to calculate new due date: ' => '새로운 기본 종료날짜 계산: ',
- // 'This task has created this child task: ' => '',
+ 'This task has created this child task: ' => '이 할일은 하위 할일을 만들었습니다.',
'Day(s)' => '일',
'Existing due date' => '기존 종료날짜',
'Factor to calculate new due date: ' => '새로운 종료날짜 계산: ',
@@ -652,10 +626,10 @@ return array(
'This task has been created by: ' => '할일을 만들었습니다: ',
'Recurrent task has been generated:' => '반복 할일이 생성되었습니다',
'Timeframe to calculate new due date: ' => '종료날짜 계산 단위',
- // 'Trigger to generate recurrent task: ' => '',
+ 'Trigger to generate recurrent task: ' => '반복 할일 생성 트리거',
'When task is closed' => '할일을 마쳤을때',
- 'When task is moved from first column' => '할일이 첫번째 칼럼으로 옮겨졌을때',
- 'When task is moved to last column' => '할일이 마지막 칼럼으로 옮겨졌을때',
+ 'When task is moved from first column' => '할일이 첫번째 컬럼으로 옮겨졌을때',
+ 'When task is moved to last column' => '할일이 마지막 컬럼으로 옮겨졌을때',
'Year(s)' => '년',
'Calendar settings' => '달력 설정',
'Project calendar view' => '프로젝트 달력 보기',
@@ -665,31 +639,25 @@ return array(
'Show tasks based on the start date' => '시작 날짜로 할일 보기',
'Subtasks time tracking' => '서브 할일 시간 트래킹',
'User calendar view' => '담당자 달력 보기',
- 'Automatically update the start date' => '시작일에 자동 업데이트',
- // 'iCal feed' => '',
+ 'Automatically update the start date' => '시작일에 자동 갱신',
+ 'iCal feed' => 'iCal 피드',
'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' => '이메일 제목',
'Date' => '날짜',
- 'Add a comment log when moving the task between columns' => '칼럼 중간의 할일이 이동할 때 의견 달기',
- 'Move the task to another column when the category is changed' => '카테고리 변경시 할일을 다른 칼럼으로 이동',
+ 'Add a comment log when moving the task between columns' => '컬럼 중간의 할일이 이동할 때 의견 달기',
+ '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' => 'Gravatar',
'%s moved the task %s to the first swimlane' => '%s가 할일 %s를 첫번째 스웜레인으로 이동시켰습니다',
'%s moved the task %s to the swimlane "%s"' => '%s가 할일 %s를 %s 스웜레인으로 이동시켰습니다',
'This report contains all subtasks information for the given date range.' => '해당 기간의 모든 서브 할일 정보가 보고서에 포함됩니다',
@@ -710,7 +678,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:' => '다음의 알림을 받기를 원합니다:',
@@ -718,14 +686,13 @@ return array(
'Only for tasks assigned to me' => '내가 담당자인 일',
'Only for tasks created by me' => '내가 만든 일',
'Only for tasks created by me and assigned to me' => '내가 만들었거나 내가 담당자인 일',
- // '%%Y-%%m-%%d' => '',
- 'Total for all columns' => '모든 칼럼',
+ '%%Y-%%m-%%d' => '%%Y-%%m-%%d',
+ 'Total for all columns' => '모든 컬럼',
'You need at least 2 days of data to show the chart.' => '차트를 보기 위하여 최소 2일의 데이터가 필요합니다',
'<15m' => '<15분',
'<30m' => '<30분',
'Stop timer' => '타이머 정지',
'Start timer' => '타이머 시작',
- 'Add project member' => '프로젝트 맴버 추가',
'My activity stream' => '내 활동기록',
'My calendar' => '내 캘린더',
'Search tasks' => '할일 찾기',
@@ -752,28 +719,26 @@ return array(
'Advanced search' => '검색 문법',
'Example of query: ' => '문법 예제 ',
'Search by project: ' => '프로젝트로 찾기 ',
- 'Search by column: ' => '칼럼으로 찾기 ',
+ 'Search by column: ' => '컬럼으로 찾기 ',
'Search by assignee: ' => '담당자로 찾기 ',
'Search by color: ' => '색깔로 찾기 ',
'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 할일의 칼럼 평균 소요시간을 차트에 표시합니다',
+ 'This chart show the average time spent into each column for the last %d tasks.' => '마지막 %d 할일의 컬럼 평균 소요시간을 차트에 표시합니다',
'Average Lead and Cycle time' => '평균 Lead and Cycle 시간',
'Average lead time: ' => '평균 lead 시간',
'Average cycle time: ' => '평균 cycle 시간',
'Cycle Time' => '사이클 시간',
'Lead Time' => '리드 시간',
'This chart show the average lead and cycle time for the last %d tasks over the time.' => '마지막 %d 할일의 평균 리드와 사이클 시간을 차트에 표시합니다',
- 'Average time into each column' => '각 칼럼의 평균 시간',
+ 'Average time into each column' => '각 컬럼의 평균 시간',
'Lead and cycle time' => '리드와 사이클 시간',
'Lead time: ' => '리드 시간: ',
'Cycle time: ' => '사이클 시간: ',
- 'Time spent into each column' => '각 칼럼에서 걸린 시간',
+ 'Time spent into each column' => '각 컬럼에서 걸린 시간',
'The lead time is the duration between the task creation and the completion.' => '리드 시간은 할일의 생성부터 완료까지의 기간입니다',
'The cycle time is the duration between the start date and the completion.' => '사이클 시간은 할일의 시작일부터 완료까지의 기간입니다',
'If the task is not closed the current time is used instead of the completion date.' => '할일이 종료되지 않았다면, 완료 시간 대신 현재 시간이 사용됩니다',
@@ -782,15 +747,13 @@ return array(
'Remote user' => '원격 담당자',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '예를 들어 LDAP, Google, Github 계정같은 원격 담당자의 비밀번호는 칸반보드 데이터베이스에 저장하지 않습니다',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '만약 "로그인 폼 거절"에 체크한다면, 로그인 폼에 접근할 자격이 무시됩니다',
- 'New remote user' => '새로운 원격유저',
- 'New local user' => '새로운 유저',
'Default task color' => '기본 할일 색',
'This feature does not work with all browsers.' => '이 기능은 일부 브라우저에서 작동하지 않습니다',
'There is no destination project available.' => '가능한 목적 프로젝트가 없습니다',
'Trigger automatically subtask time tracking' => '자동 서브 할일 시간 트래킹 트리거',
'Include closed tasks in the cumulative flow diagram' => '누적 플로우 다이어그램에 종료된 할일을 포함합니다',
'Current swimlane: %s' => '현재 스웜라인: %s',
- 'Current column: %s' => '현재 칼럼: %s',
+ 'Current column: %s' => '현재 컬럼: %s',
'Current category: %s' => '현재 카테고리: %s',
'no category' => '카테고리 아님',
'Current assignee: %s' => '현재 할당자: %s',
@@ -800,21 +763,20 @@ return array(
'License:' => '라이센스:',
'License' => '라이센스',
'Enter the text below' => '아랫글로 들어가기',
- 'Gantt chart for %s' => '%s의 간트 차트',
'Sort by position' => '위치별 정렬',
'Sort by date' => '날짜별 정렬',
'Add task' => '할일 추가',
'Start date:' => '시작일:',
'Due date:' => '만기일:',
'There is no start date or due date for this task.' => '할일의 시작일 또는 만기일이 없습니다',
- // 'Moving or resizing a task will change the start and due date of the task.' => '',
- // 'There is no task in your project.' => '',
+ 'Moving or resizing a task will change the start and due date of the task.' => '할일의 이동 혹은 리사이징으로 시작시간과 마감시간이 변경됩니다.',
+ 'There is no task in your project.' => '프로젝트에 할일이 없습니다.',
'Gantt chart' => '간트 차트',
'People who are project managers' => '프로젝트 매니저',
'People who are project members' => '프로젝트 멤버',
- // 'NOK - Norwegian Krone' => '',
- 'Show this column' => '칼럼 보이기',
- 'Hide this column' => '칼럼 숨기기',
+ 'NOK - Norwegian Krone' => 'NOK - 노르웨이 크로네',
+ 'Show this column' => '컬럼 보이기',
+ 'Hide this column' => '컬럼 숨기기',
'open file' => '열기',
'End date' => '종료 날짜',
'Users overview' => '유저 전체보기',
@@ -828,21 +790,19 @@ return array(
'End date:' => '날짜 수정',
'There is no start date or end date for this project.' => '이 프로젝트에는 시작날짜와 종료날짜가 없습니다',
'Projects Gantt chart' => '프로젝트 간트차트',
- // 'Change task color when using a specific task link' => '',
- // 'Task link creation or modification' => '',
+ 'Change task color when using a specific task link' => '특정 할일 링크를 사용할때 할일의 색깔 변경',
+ 'Task link creation or modification' => '할일 링크 생성 혹은 수정',
'Milestone' => '마일스톤',
'Documentation: %s' => '문서: %s',
'Switch to the Gantt chart view' => '간트 차트 보기로 변경',
- // 'Reset the search/filter box' => '',
+ 'Reset the search/filter box' => '찾기/필터 박스 초기화',
'Documentation' => '문서',
- // 'Table of contents' => '',
+ 'Table of contents' => '목차',
'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.' => '사용자 정의 필터가 성공적으로 생성되었습니다',
@@ -853,16 +813,16 @@ return array(
'Your custom filter have been updated successfully.' => '사용자 정의 필터가 성공적으로 수정되었습니다',
'Unable to update custom filter.' => '정의 필터 수정 비활성화',
'Web' => '웹',
- // 'New attachment on task #%d: %s' => '',
+ 'New attachment on task #%d: %s' => '할일 #%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의 칼럼이 변경되었습니다',
+ 'Column changed for task #%d' => '할일 #%d의 컬럼이 변경되었습니다',
'New position for task #%d' => '할일 #%d이 새로운 위치에 등록되었습니다',
'Swimlane changed for task #%d' => '#%d 할일의 스웜라인이 변경됩니다',
'Assignee changed on task #%d' => '#%d 할일의 담당자가 변경됩니다',
@@ -871,7 +831,7 @@ return array(
'No new notifications.' => '알림이 없습니다',
'Mark all as read' => '모두 읽음',
'Mark as read' => '읽음',
- // 'Total number of tasks in this column across all swimlanes' => '',
+ 'Total number of tasks in this column across all swimlanes' => '모든 스웜라인 칼럼의 할일 수',
'Collapse swimlane' => '스웜라인 붕괴',
'Expand swimlane' => '스웜라인 확장',
'Add a new filter' => '새로운 필터 추가',
@@ -880,7 +840,6 @@ return array(
'Owner' => '소유자',
'Unread notifications' => '읽지않은 알림',
'Notification methods:' => '알림 방법',
- 'Import tasks from CSV file' => 'CSV 파일에서 할일 가져오기',
'Unable to read your file' => '파일을 읽을 수 없습니다',
'%d task(s) have been imported successfully.' => '%d 할일이 성공적으로 추가되었습니다',
'Nothing have been imported!' => '추가가 되지 않았습니다!',
@@ -893,8 +852,8 @@ return array(
'Double Quote' => '이중 따옴표',
'Single Quote' => '따옴표',
'%s attached a file to the task #%d' => '%s가 할일 #%d에 파일을 추가하였습니다',
- 'There is no column or swimlane activated in your project!' => '프로젝트에 활성화된 칼럼이나 스웜라인이 없습니다',
- // 'Append filter (instead of replacement)' => '',
+ 'There is no column or swimlane activated in your project!' => '프로젝트에 활성화된 컬럼이나 스웜라인이 없습니다',
+ 'Append filter (instead of replacement)' => '필터 (변경 대신)추가',
'Append/Replace' => '추가/변경',
'Append' => '추가',
'Replace' => '변경',
@@ -918,7 +877,7 @@ return array(
'%s attached a new file to the task %s' => '%s이 새로운 파일을 할일 %s에 추가했습니다',
'Link type' => '링크 형식',
'Assign automatically a category based on a link' => '링크 기반 자동 카테고리 할당',
- // 'BAM - Konvertible Mark' => '',
+ 'BAM - Konvertible Mark' => 'BAM - 보스니아 마르카',
'Assignee Username' => '담당자의 사용자이름',
'Assignee Name' => '당장자 이름',
'Groups' => '그룹',
@@ -944,10 +903,9 @@ return array(
'Project Member' => '프로젝트 멤버',
'Project Viewer' => '프로젝트 뷰어',
'Your account is locked for %d minutes' => '%d분 동안 계정이 잠깁니다',
- // 'Invalid captcha' => '',
+ '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.' => '그룹이 없습니다',
@@ -968,13 +926,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' => '소요 시간',
@@ -982,14 +937,14 @@ return array(
'Estimated Time' => '예상 시간',
'Actual Time' => '실제 시간',
'Estimated vs actual time' => '예상 vs 실제 시간',
- // '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' => '상세 칼럼의 할일을 종료합니다',
+ 'RUB - Russian Ruble' => 'RUB - 러시아 루블',
+ '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' => '시간에 기반한 1회용 패스워드 알고리즘',
'Two-Factor Provider: ' => '이중 인증: ',
'Disable two-factor authentication' => '이중 인증 비활성화',
'Enable two-factor authentication' => '이중 인증 활성화',
- // 'There is no integration registered at the moment.' => '',
+ 'There is no integration registered at the moment.' => '현재 등록된 통합이 없습니다.',
'Password Reset for Kanboard' => 'Kanboard의 비밀번호 초기화',
'Forgot password?' => '비밀번호 찾기',
'Enable "Forget Password"' => '"비밀번호 분실" 활성화',
@@ -1002,17 +957,16 @@ return array(
'Creation' => '생성',
'Expiration' => '만료',
'Password reset history' => '비밀번호 초기화 기록',
- 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '칼럼 "%s"와 스웜라인 "%s"의 모든 할일이 성공적으로 종료되었습니다',
- '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.' => '칼럼 "%s"와 스웜라인 "%s"의 할일 %d가 종료될 것입니다',
- 'Close all tasks of this column' => '칼럼의 모든 할일 마치기',
+ 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '컬럼 "%s"와 스웜라인 "%s"의 모든 할일이 성공적으로 종료되었습니다',
+ '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.' => '컬럼 "%s"와 스웜라인 "%s"의 할일 %d가 종료될 것입니다',
+ 'Close all tasks of this column' => '컬럼의 모든 할일 마치기',
'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '프로젝트 알림 방법으로 플러그인이 등록되지 않았습니다. 각각의 알림을 프로파일에서 설정하실 수 있습니다',
'My dashboard' => '대시보드',
'My profile' => '프로필',
- 'Project owner: ' => '프로젝트 소유자',
+ 'Project owner: ' => '프로젝트 소유자:',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '프로젝트 구분자는 선택사항이며, 숫자와 문자만 가능합니다. 예: MYPROJECT.',
- // 'Project owner' => '',
- 'Those dates are useful for the project Gantt chart.' => '이 날짜는 프로젝트 간트 차트에 사용됩니다',
+ 'Project owner' => '프로젝트 소유자',
'Private projects do not have users and groups management.' => '비밀 프로젝트는 사용자나 관리 그룹이 소유하지 않습니다',
'There is no project member.' => '프로젝트 맴버가 없습니다',
'Priority' => '우선순위',
@@ -1027,7 +981,7 @@ return array(
'Duration in days' => '기간',
'Send email when there is no activity on a task' => '활동이 없는 할일을 이메일로 보냅니다',
'Unable to fetch link information.' => '링크 정보 가져오기 비활성화',
- // 'Daily background job for tasks' => '',
+ 'Daily background job for tasks' => '할일의 일일 배경 작업 ',
'Auto' => '자동',
'Related' => '연관된',
'Attachment' => '첨부',
@@ -1042,7 +996,7 @@ return array(
'Edit external link' => '외부 링크 수정',
'External link' => '외부 링크',
'Copy and paste your link here...' => '여기에 링크를 복사/붙여넣기',
- // 'URL' => '',
+ 'URL' => '인터넷 주소',
'Internal links' => '내부 링크',
'Assign to me' => '나에게 할당',
'Me' => '나',
@@ -1057,7 +1011,7 @@ return array(
'Reference:' => '참고:',
'Complexity:' => '복합:',
'Swimlane:' => '스웜라인:',
- 'Column:' => '칼럼:',
+ 'Column:' => '컬럼:',
'Position:' => '위치:',
'Creator:' => '생성자:',
'Time estimated:' => '예상 시간:',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => '시작:',
'Moved:' => '이동:',
'Task #%d' => '할일 #%d',
- 'Date and time format' => '날짜와 시간 형식',
'Time format' => '시간 형식',
'Start date: ' => '시작일: ',
'End date: ' => '종료일: ',
@@ -1079,13 +1032,11 @@ return array(
'Do you really want to remove this custom filter: "%s"?' => '정의 필터를 삭제하시겠습니까: "%s"?',
'Remove a custom filter' => '정의 필터 삭제',
'User activated successfully.' => '사용자가 성공적으로 활성화되었습니다',
- // 'Unable to enable this user.' => '',
+ 'Unable to enable this user.' => '이 사용자를 활성화할 수 없습니다.',
'User disabled successfully.' => '사용자가 성공적으로 비활성화되었습니다',
- // 'Unable to disable this user.' => '',
+ 'Unable to disable this user.' => '이 사용자를 비활성화할 수 없습니다.',
'All files have been uploaded successfully.' => '모든 파일이 성공적으로 업로드되었습니다',
- 'View uploaded files' => '업로드 파일 보기',
'The maximum allowed file size is %sB.' => '업로드 파일의 최대 크기는 %sB 입니다',
- 'Choose files again' => '파일 다시 선택',
'Drag and drop your files here' => '파일을 이곳으로 끌고오기',
'choose files' => '파일 선택',
'View profile' => '프로파일 보기',
@@ -1100,11 +1051,11 @@ return array(
'Uploaded by %s' => '%s로 올리기',
'Filename' => '파일 이름',
'Size' => '크기',
- 'Column created successfully.' => '칼럼이 성공적으로 생성되었습니다',
- 'Another column with the same name exists in the project' => '프로젝트에 동일한 이름의 칼럼이 있습니다',
+ 'Column created successfully.' => '컬럼이 성공적으로 생성되었습니다',
+ 'Another column with the same name exists in the project' => '프로젝트에 동일한 이름의 컬럼이 있습니다',
'Default filters' => '기본 필터',
- 'Your board doesn\'t have any columns!' => '보드에 칼럼이 존재하지 않습니다',
- 'Change column position' => '칼럼 위치 변경',
+ 'Your board doesn\'t have any columns!' => '보드에 컬럼이 존재하지 않습니다',
+ 'Change column position' => '컬럼 위치 변경',
'Switch to the project overview' => '프로젝트 개요로 변경',
'User filters' => '사용자 필터',
'Category filters' => '카테고리 필터',
@@ -1125,7 +1076,7 @@ return array(
'Local File' => '로컬 파일',
'Configuration' => '구성',
'PHP version:' => 'PHP 버전:',
- // 'PHP SAPI:' => '',
+ 'PHP SAPI:' => 'PHP SAPI:',
'OS version:' => '운영체제 버전:',
'Database version:' => '데이터베이스 버전:',
'Browser:' => '브라우저:',
@@ -1159,62 +1110,201 @@ 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' => '벌크에 할일 생성',
- // 'Your Kanboard instance is not configured to install plugins from the user interface.' => '',
- 'There is no plugin available.' => '사용할 수 있는 플러그인이 없습니다',
+ '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' => '최신의',
+ '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' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ 'Webhook token' => 'Webhook토큰',
+ '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의 알림',
+ 'Assign a color when the task is moved to a specific swimlane' => '할일이 특정 스웜라인으로 옮겨질 때 색상 지정',
+ 'Assign a priority when the task is moved to a specific swimlane' => '할일이 특정 스웜라인으로 옮겨질 때 우선순위 지정',
+ 'User unlocked successfully.' => '사용자 잠금 성공',
+ 'Unable to unlock the user.' => '사용자 해제 성공',
+ 'Move a task to another swimlane' => '다른 스웜라인으로 할일 이동',
+ 'Creator Name' => '생성자 이름',
+ 'Time spent and estimated' => '소요시간과 예상시간',
+ 'Move position' => '이동 위치',
+ 'Move task to another position on the board' => '할일을 보드의 다른 위치로 이동',
+ 'Insert before this task' => '이 할일 이전에 삽입',
+ 'Insert after this task' => '이 할일 이후에 삽입',
+ 'Unlock this user' => '사용자 잠금',
+ 'Custom Project Roles' => '정의 프로젝트 규칙',
+ 'Add a new custom role' => '새로운 정의 규칙 추가',
+ 'Restrictions for the role "%s"' => '"%s" 역할의 제약',
+ 'Add a new project restriction' => '새로운 프로젝트 제약 추가',
+ 'Add a new drag and drop restriction' => '새로운 드래그 앤 드롭 제약 추가',
+ 'Add a new column restriction' => '새로운 칼럼 제약 추가',
+ 'Edit this role' => '역할 수정',
+ 'Remove this role' => '역할 삭제',
+ 'There is no restriction for this role.' => '역할에 대한 제약이 없습니다.',
+ 'Only moving task between those columns is permitted' => '칼럼간 이동만 허용됩니다.',
+ 'Close a task in a specific column when not moved during a given period' => '특정 기간동안 이동하지 않은 특정 칼럼의 할일 마치기',
+ 'Edit columns' => '칼럼 수정',
+ 'The column restriction has been created successfully.' => '칼럼 제약이 생성되었습니다.',
+ 'Unable to create this column restriction.' => '칼럼 제약을 생성할 수 없습니다.',
+ 'Column restriction removed successfully.' => '칼럼 제약이 삭제되었습니다.',
+ 'Unable to remove this restriction.' => '칼럼 제약을 삭제할 수 없습니다.',
+ 'Your custom project role has been created successfully.' => '정의 프로젝트 역할이 생성되었습니다.',
+ 'Unable to create custom project role.' => '정의 프로젝트 역할을 생성할 수 없습니다.',
+ 'Your custom project role has been updated successfully.' => '정의 프로젝트 역할이 수정되었습니다.',
+ 'Unable to update custom project role.' => '정의 프로젝트 역할을 수정할 수 없습니다.',
+ 'Custom project role removed successfully.' => '정의 프로젝트 역할이 삭제되었습니다.',
+ 'Unable to remove this project role.' => '정의 프로젝트 역할을 삭제할 수 없습니다.',
+ 'The project restriction has been created successfully.' => '프로젝트 제약이 생성되었습니다.',
+ 'Unable to create this project restriction.' => '프로젝트 제약을 생성할 수 없습니다.',
+ 'Project restriction removed successfully.' => '프로젝트 제약을 삭제하였습니다.',
+ 'You cannot create tasks in this column.' => '이 칼럼의 할일을 생성할 수 없습니다.',
+ 'Task creation is permitted for this column' => '이 칼럼의 할일 생성이 허가되었습니다.',
+ 'Closing or opening a task is permitted for this column' => '이 칼럼의 할일 열기 혹은 닫기가 허가되었습니다.',
+ 'Task creation is blocked for this column' => '이 칼럼의 할일 생성이 거부되었습니다.',
+ 'Closing or opening a task is blocked for this column' => '이 칼럼의 할일 열기 혹은 닫기가 거부되었습니다.',
+ 'Task creation is not permitted' => '할일 생성이 거부되었습니다.',
+ 'Closing or opening a task is not permitted' => '할일 열기 혹은 닫기가 거부되었습니다.',
+ 'New drag and drop restriction for the role "%s"' => '"%s" 역할의 새로운 드래그 앤 드롭 제약',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '이 역할에 속한 사용자는 원본 및 대상 칼럼 사이에서만 할일을 이동할 수 있습니다',
+ 'Remove a column restriction' => '칼럼 제약 삭제',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => '칼럼 제약을 "%s" 에서 "%s"로 이동하시겠습니까?',
+ 'New column restriction for the role "%s"' => '"%s" 역할의 새로운 칼럼 제약',
+ 'Rule' => '역할',
+ 'Do you really want to remove this column restriction?' => '칼럼 제약을 삭제하시겠습니까?',
+ 'Custom roles' => '정의 역할',
+ 'New custom project role' => '새로운 정의 프로젝트 역할',
+ 'Edit custom project role' => '정의 프로젝트 역할 수정',
+ 'Remove a custom role' => '정의 역할 삭제',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '"%s" 정의 역할을 삭제하시겠습니까? 이 역할에 할당 된 모든 사람들이 프로젝트 멤버가됩니다.',
+ 'There is no custom role for this project.' => '이 프로젝트에는 정의 역할이 없습니다.',
+ 'New project restriction for the role "%s"' => '"%s" 역할의 새로운 프로젝트 제약',
+ 'Restriction' => '제약',
+ 'Remove a project restriction' => '프로젝트 제약 삭제',
+ 'Do you really want to remove this project restriction: "%s"?' => '"%s" 프로젝트 제약을 삭제하시겠습니까?',
+ 'Duplicate to multiple projects' => '다수의 프로젝트 복제',
+ 'This field is required' => '이 필드는 필수 항목입니다.',
+ 'Moving a task is not permitted' => '할일 이동이 거부되었습니다.',
+ 'This value must be in the range %d to %d' => '값의 범위는 %d 부터 %d 까지 입니다.',
+ 'You are not allowed to move this task.' => '당신은 할일 이동이 거부되었습니다.',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php
index bad6d919..641dfefa 100644
--- a/app/Locale/my_MY/translations.php
+++ b/app/Locale/my_MY/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d tugasan di papan',
'%d tasks in total' => 'Sejumlah %d tugasan',
'Unable to update this board.' => 'Tidak berupaya mengemaskini papan ini',
- 'Edit board' => 'ubah papan',
'Disable' => 'Nyah-Upaya',
'Enable' => 'Aktifkan',
'New project' => 'Projek Baru',
'Do you really want to remove this project: "%s"?' => 'Anda yakin mahu menghapus projek ini : « %s » ?',
'Remove project' => 'Hapus projek',
'Edit the board for "%s"' => 'Ubah papan untuk « %s »',
- 'All projects' => 'Semua projek',
'Add a new column' => 'Tambah kolom baru',
'Title' => 'Judul',
'Assigned to %s' => 'Ditugaskan ke %s',
'Remove a column' => 'Hapus kolom',
- 'Remove a column from a board' => 'Hapus kolom dari papan',
'Unable to remove this column.' => 'Tidak dapat menghapus kolom ini.',
'Do you really want to remove this column: "%s"?' => 'Apakah anda yakin akan menghapus kolom ini : « %s » ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'tindakan ini akan MENGHAPUS SEMUA TUGAS yang terkait dengan kolom ini!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(perintah VACUUM)',
'(Gzip compressed Sqlite file)' => '(File Sqlite yang termampat Gzip)',
'Close a task' => 'Tutup tugas',
- 'Edit a task' => 'Sunting tugas',
'Column' => 'Kolom',
'Color' => 'Warna',
'Assignee' => 'Orang yang ditugaskan',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Jumlah tugas',
'User' => 'Pengguna',
'Comments' => 'Komentar',
- 'Leave a comment' => 'Tinggalkan komentar',
'Comment is required' => 'Komentar diperlukan',
- 'Leave a description' => 'Tinggalkan deskripsi',
'Comment added successfully.' => 'Komentar berhasil ditambahkan.',
'Unable to create your comment.' => 'Tidak dapat menambahkan komentar anda.',
'Due Date' => 'Batas Tanggal Terakhir',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Cari',
'Nothing found.' => 'Tidak ditemukan.',
'Due date' => 'Batas tanggal terakhir',
- 'Others formats accepted: %s and %s' => 'Format lain yang didukung : %s et %s',
'Description' => 'Deskripsi',
'%d comments' => '%d komentar',
'%d comment' => '%d komentar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Id Tugas',
'Creator' => 'Pembuat',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Jauh',
'Enabled' => 'Aktif',
'Disabled' => 'Tidak aktif',
- 'Username:' => 'Nama pengguna :',
- 'Name:' => 'Nama:',
+ 'Login:' => 'Nama pengguna :',
+ 'Full Name:' => 'Nama:',
'Email:' => 'Emel:',
'Notifications:' => 'Makluman:',
'Notifications' => 'Makluman',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Label Pautan',
'Link modification' => 'Modifikasi Pautan',
'Links' => 'Pautan',
- 'Link settings' => 'Pengaturan Pautan',
'Opposite label' => 'Label berlawanan',
'Remove a link' => 'Hapus Pautan',
- 'Task\'s links' => 'Pautan tugas',
'The labels must be different' => 'Label harus berbeda',
'There is no link.' => 'Tidak ada Pautan.',
'This label must be unique' => 'Label ini harus unik',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Tampilan kompak',
'Horizontal scrolling' => 'Horisontal bergulir',
'Compact/wide view' => 'Beralih antara tampilan kompak dan diperluas',
- 'No results match:' => 'Tidak ada hasil :',
'Currency' => 'Mata uang',
'Private project' => 'projek pribadi',
'AUD - Australian Dollar' => 'AUD - Dollar Australia',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Yen Jepang',
'NZD - New Zealand Dollar' => 'NZD - Dollar Selandia baru',
'RSD - Serbian dinar' => 'RSD - Dinar Serbia',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Dollar Amerika',
'Destination column' => 'Kolom tujuan',
'Move the task to another column when assigned to a user' => 'Pindahkan tugas ke kolom lain ketika ditugaskan ke pengguna',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Nilai tukar mata uang',
'Rate' => 'Tarif',
'Change reference currency' => 'Mengubah referensi mata uang',
- 'Add a new currency rate' => 'Tambahkan nilai tukar mata uang baru',
'Reference currency' => 'Referensi mata uang',
'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',
@@ -615,7 +590,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',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Hentikan timer',
'Start timer' => 'Mulai timer',
- 'Add project member' => 'Tambahkan anggota projek',
'My activity stream' => 'Aliran kegiatan saya',
'My calendar' => 'Kalender saya',
'Search tasks' => 'Cari tugas',
@@ -758,8 +725,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.',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Pengguna jauh',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Pengguna jauh tidak menyimpan kata laluan mereka dalam basis data Kanboard, contoh: Akaun LDAP, Google dan Github.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Jika anda mencentang kotak "Larang formulir login", kredensial masuk ke formulis login akan diabaikan.',
- 'New remote user' => 'Pengguna baru jauh',
- 'New local user' => 'Pengguna baru lokal',
'Default task color' => 'Standar warna tugas',
'This feature does not work with all browsers.' => 'Ciri ini tidak dapat digunakan pada semua browsers',
'There is no destination project available.' => 'Tiada destinasi projek yang tersedia.',
@@ -800,7 +763,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',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php
index 19372419..da7e6508 100644
--- a/app/Locale/nb_NO/translations.php
+++ b/app/Locale/nb_NO/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d Oppgaver på hovedsiden',
'%d tasks in total' => '%d Oppgaver i alt',
'Unable to update this board.' => 'Ikke mulig at oppdatere hovedsiden',
- 'Edit board' => 'Endre prosjektsiden',
'Disable' => 'Deaktiver',
'Enable' => 'Aktiver',
'New project' => 'Nytt prosjekt',
'Do you really want to remove this project: "%s"?' => 'Vil du fjerne dette prosjektet: "%s"?',
'Remove project' => 'Fjern prosjekt',
'Edit the board for "%s"' => 'Endre prosjektsiden for "%s"',
- 'All projects' => 'Alle prosjekter',
'Add a new column' => 'Legg til en ny kolonne',
'Title' => 'Tittel',
'Assigned to %s' => 'Tildelt: %s',
'Remove a column' => 'Fjern en kolonne',
- 'Remove a column from a board' => 'Fjern en kolonne fra et board',
'Unable to remove this column.' => 'Ikke mulig ø fjerne denne kolonnen',
'Do you really want to remove this column: "%s"?' => 'Vil du fjerne denne kolonnen: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Denne handlingen vil SLETTE ALLE OPPGAVER tilknyttet denne kolonnen',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM kommando)',
'(Gzip compressed Sqlite file)' => '(Gzip-komprimert Sqlite fil)',
'Close a task' => 'Lukk en oppgave',
- 'Edit a task' => 'Endre en oppgave',
'Column' => 'Kolonne',
'Color' => 'Farge',
'Assignee' => 'Tildelt',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Antall oppgaver',
'User' => 'Bruker',
'Comments' => 'Kommentarer',
- 'Leave a comment' => 'Legg inn en kommentar',
'Comment is required' => 'Kommentar må legges inn',
- 'Leave a description' => 'Legg inn en beskrivelse...',
'Comment added successfully.' => 'Kommentaren er lagt til.',
'Unable to create your comment.' => 'Din kommentar kunne ikke opprettes.',
'Due Date' => 'Forfallsdato',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Søk',
'Nothing found.' => 'Intet funnet.',
'Due date' => 'Forfallsdato',
- 'Others formats accepted: %s and %s' => 'Andre formater: %s og %s',
'Description' => 'Beskrivelse',
'%d comments' => '%d kommentarer',
'%d comment' => '%d kommentar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Oppgave ID',
'Creator' => 'Laget av',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Fjernstyrt',
'Enabled' => 'Aktiv',
'Disabled' => 'Deaktivert',
- 'Username:' => 'Brukernavn',
- 'Name:' => 'Navn:',
+ 'Login:' => 'Brukernavn',
+ 'Full Name:' => 'Navn:',
'Email:' => 'Epost:',
'Notifications:' => 'Varslinger:',
'Notifications' => 'Varslinger',
@@ -386,14 +372,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',
@@ -442,13 +426,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.' => '',
@@ -465,10 +446,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',
@@ -494,7 +473,6 @@ return array(
'Subtask Id' => 'Deloppgave ID',
'Subtasks' => 'Deloppgaver',
'Subtasks Export' => 'Eksporter deloppgaver',
- // 'Subtasks exportation for "%s"' => '',
'Task Title' => 'Oppgavetittel',
// 'Untitled' => '',
'Application default' => 'Standardinstilling',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Relasjonsetiketter',
'Link modification' => 'Relasjonsmodifisering',
'Links' => 'Relasjoner',
- 'Link settings' => 'Relasjonsinnstillinger',
'Opposite label' => 'Etikett for relatert motsatt oppgave',
'Remove a link' => 'Fjern relasjon',
- // 'Task\'s links' => '',
// 'The labels must be different' => '',
// 'There is no link.' => '',
// 'This label must be unique' => '',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Kompakt visning',
'Horizontal scrolling' => 'Bla horisontalt',
'Compact/wide view' => 'Kompakt/bred visning',
- 'No results match:' => 'Ingen resultater',
'Currency' => 'Valuta',
'Private project' => 'Privat prosjekt',
// 'AUD - Australian Dollar' => '',
@@ -582,6 +557,7 @@ return array(
// 'JPY - Japanese Yen' => '',
// 'NZD - New Zealand Dollar' => '',
// 'RSD - Serbian dinar' => '',
+ // 'CNY - Chinese Yuan' => '',
// 'USD - US Dollar' => '',
'Destination column' => 'Ny kolonne',
'Move the task to another column when assigned to a user' => 'Flytt oppgaven til en annen kolonne når den er tildelt en bruker',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Valutakurser',
// 'Rate' => '',
// 'Change reference currency' => '',
- // 'Add a new currency rate' => '',
// 'Reference currency' => '',
// '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' => '',
@@ -615,7 +590,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' => '',
@@ -680,14 +654,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' => '',
@@ -725,7 +693,6 @@ return array(
// '<30m' => '',
'Stop timer' => 'Stopp timer',
'Start timer' => 'Start timer',
- 'Add project member' => 'Legg til prosjektmedlem',
'My activity stream' => 'Aktivitetslogg',
'My calendar' => 'Min kalender',
'Search tasks' => 'Søk oppgave',
@@ -758,8 +725,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.' => '',
@@ -782,8 +747,6 @@ return array(
// 'Remote user' => '',
// 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '',
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
- 'New remote user' => 'Ny eksternbruker',
- 'New local user' => 'Ny internbruker',
'Default task color' => 'Standard oppgavefarge',
// 'This feature does not work with all browsers.' => '',
// 'There is no destination project available.' => '',
@@ -800,7 +763,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',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php
index 8ba0d394..140c0406 100644
--- a/app/Locale/nl_NL/translations.php
+++ b/app/Locale/nl_NL/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d taken op het bord',
'%d tasks in total' => '%d taken in totaal',
'Unable to update this board.' => 'Update van dit bord niet mogelijk.',
- 'Edit board' => 'Bord bewerken',
'Disable' => 'Deactiveren',
'Enable' => 'Activeren',
'New project' => 'Nieuw project',
'Do you really want to remove this project: "%s"?' => 'Weet u zeker dat u dit project wil verwijderen : « %s » ?',
'Remove project' => 'Project verwijderen',
'Edit the board for "%s"' => 'Bord bewerken voor « %s »',
- 'All projects' => 'Alle projecten',
'Add a new column' => 'Kolom toevoegen',
'Title' => 'Titel',
'Assigned to %s' => 'Toegewezen aan %s',
'Remove a column' => 'Kolom verwijderen',
- 'Remove a column from a board' => 'Kolom verwijderen van het bord',
'Unable to remove this column.' => 'Verwijderen van deze kolom niet mogelijk.',
'Do you really want to remove this column: "%s"?' => 'Weet u zeker dat u deze kolom wil verwijderen : « %s » ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Deze actie zal ALLE TAKEN VERWIJDEREN die zijn geassocieerd met deze kolom!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM commando)',
'(Gzip compressed Sqlite file)' => '(Gzip ingepakt Sqlite bestand)',
'Close a task' => 'Taak sluiten',
- 'Edit a task' => 'Taak bewerken',
'Column' => 'Kolom',
'Color' => 'Kleur',
'Assignee' => 'Toegewezene',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Aantal taken',
'User' => 'Gebruiker',
'Comments' => 'Commentaar',
- 'Leave a comment' => 'Schrijf een commentaar',
'Comment is required' => 'Commentaar is verplicht',
- 'Leave a description' => 'Schrijf een omschrijving',
'Comment added successfully.' => 'Commentaar succesvol toegevoegd.',
'Unable to create your comment.' => 'Commentaar toevoegen niet gelukt.',
'Due Date' => 'Vervaldag',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Zoek',
'Nothing found.' => 'Niets gevonden.',
'Due date' => 'Vervaldatum',
- 'Others formats accepted: %s and %s' => 'Andere toegestane formaten : %s en %s',
'Description' => 'Omschrijving',
'%d comments' => '%d commentaren',
'%d comment' => '%d commentaar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Taak Id',
'Creator' => 'Aangemaakt door',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Remote',
'Enabled' => 'Actief',
'Disabled' => 'Inactief',
- 'Username:' => 'Gebruikersnaam :',
- 'Name:' => 'Naam :',
+ 'Login:' => 'Gebruikersnaam :',
+ 'Full Name:' => 'Naam :',
'Email:' => 'Email :',
'Notifications:' => 'Notificaties :',
'Notifications' => 'Notificaties',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Link labels',
'Link modification' => 'Link aanpassing',
'Links' => 'Links',
- 'Link settings' => 'Link instellingen',
'Opposite label' => 'Tegenovergesteld label',
'Remove a link' => 'Link verwijderen',
- 'Task\'s links' => 'Links van taak',
'The labels must be different' => 'De labels moeten verschillend zijn',
'There is no link.' => 'Er is geen link.',
'This label must be unique' => 'Dit label moet uniek zijn',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Compacte weergave',
// 'Horizontal scrolling' => '',
'Compact/wide view' => 'Compacte/breedbeeld-weergave',
- 'No results match:' => 'Geen resultaten voor',
'Currency' => 'Valuta',
'Private project' => 'Privé project',
// 'AUD - Australian Dollar' => '',
@@ -582,6 +557,7 @@ return array(
// 'JPY - Japanese Yen' => '',
// 'NZD - New Zealand Dollar' => '',
// 'RSD - Serbian dinar' => '',
+ // 'CNY - Chinese Yuan' => '',
// 'USD - US Dollar' => '',
'Destination column' => 'Doel kolom',
// 'Move the task to another column when assigned to a user' => '',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Wisselkoersen',
'Rate' => 'Koers',
// 'Change reference currency' => '',
- // 'Add a new currency rate' => '',
// 'Reference currency' => '',
// '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' => '',
@@ -615,7 +590,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' => '',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Stop timer',
'Start timer' => 'Start timer',
- 'Add project member' => 'Voeg projectlid toe',
'My activity stream' => 'Mijn activiteiten',
'My calendar' => 'Mijn kalender',
'Search tasks' => 'Zoek taken',
@@ -758,8 +725,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.' => '',
@@ -782,8 +747,6 @@ return array(
// 'Remote user' => '',
// 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '',
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
- // 'New remote user' => '',
- // 'New local user' => '',
// 'Default task color' => '',
// 'This feature does not work with all browsers.' => '',
// 'There is no destination project available.' => '',
@@ -800,7 +763,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- //' Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index 09c247d8..16988195 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d zadań na tablicy',
'%d tasks in total' => '%d wszystkich zadań',
'Unable to update this board.' => 'Nie można zaktualizować tablicy.',
- 'Edit board' => 'Edytuj tablicę',
'Disable' => 'Wyłącz',
'Enable' => 'Włącz',
'New project' => 'Nowy projekt',
'Do you really want to remove this project: "%s"?' => 'Na pewno chcesz usunąć projekt: "%s"?',
'Remove project' => 'Usuń projekt',
'Edit the board for "%s"' => 'Edytuj tablicę dla "%s"',
- 'All projects' => 'Wszystkie projekty',
'Add a new column' => 'Dodaj nową kolumnę',
'Title' => 'Nazwa',
'Assigned to %s' => 'Przypisane do %s',
'Remove a column' => 'Usuń kolumnę',
- 'Remove a column from a board' => 'Usuń kolumnę z tablicy',
'Unable to remove this column.' => 'Nie udało się usunąć kolumny.',
'Do you really want to remove this column: "%s"?' => 'Na pewno chcesz usunąć kolumnę: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Wszystkie zadania w kolumnie zostaną usunięte!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(komenda VACUUM)',
'(Gzip compressed Sqlite file)' => '(baza danych spakowana Gzip)',
'Close a task' => 'Zakończ zadanie',
- 'Edit a task' => 'Edytuj zadanie',
'Column' => 'Kolumna',
'Color' => 'Kolor',
'Assignee' => 'Odpowiedzialny',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Liczba zadań',
'User' => 'Użytkownik',
'Comments' => 'Komentarze',
- 'Leave a comment' => 'Wstaw komentarz',
'Comment is required' => 'Komentarz jest wymagany',
- 'Leave a description' => 'Dodaj opis',
'Comment added successfully.' => 'Komentarz dodany',
'Unable to create your comment.' => 'Nie udało się dodać komentarza',
'Due Date' => 'Termin',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Szukaj',
'Nothing found.' => 'Nic nie znaleziono',
'Due date' => 'Termin',
- 'Others formats accepted: %s and %s' => 'Inne akceptowane formaty: %s and %s',
'Description' => 'Opis',
'%d comments' => '%d Komentarzy',
'%d comment' => '%d Komentarz',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Identyfikator Zadania',
'Creator' => 'Autor',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Zdalne',
'Enabled' => 'Odblokowane',
'Disabled' => 'Zablokowane',
- 'Username:' => 'Nazwa Użytkownika (login):',
- 'Name:' => 'Imię i Nazwisko',
+ 'Login:' => 'Nazwa Użytkownika (login):',
+ 'Full Name:' => 'Imię i Nazwisko',
'Email:' => 'Email: ',
'Notifications:' => 'Powiadomienia: ',
'Notifications' => 'Powiadomienia',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Etykiety linku',
'Link modification' => 'Modyfikuj link',
'Links' => 'Linki',
- 'Link settings' => 'Ustawienia linku',
'Opposite label' => 'Etykieta odwrotna',
'Remove a link' => 'Usuń link',
- 'Task\'s links' => 'Linki zadania',
'The labels must be different' => 'Etykiety muszą być różne',
'There is no link.' => 'Brak linku',
'This label must be unique' => 'Etykieta musi być unikatowa',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Widok kompaktowy',
'Horizontal scrolling' => 'Przewijanie poziome',
'Compact/wide view' => 'Pełny/Kompaktowy widok',
- 'No results match:' => 'Brak wyników:',
'Currency' => 'Waluta',
'Private project' => 'Projekt prywatny',
'AUD - Australian Dollar' => 'AUD - Dolar australijski',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Jen japoński',
'NZD - New Zealand Dollar' => 'NZD - Dolar nowozelandzki',
'RSD - Serbian dinar' => 'RSD - Dinar serbski',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Dolar amerykański',
'Destination column' => 'Kolumna docelowa',
'Move the task to another column when assigned to a user' => 'Przenieś zadanie do innej kolumny gdy zostanie przypisane do osoby',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Kursy walut',
'Rate' => 'Kurs',
'Change reference currency' => 'Zmień walutę referencyjną',
- 'Add a new currency rate' => 'Dodaj nowy kurs waluty',
'Reference currency' => 'Waluta referencyjna',
'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',
@@ -615,7 +590,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',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
// '<30m' => '',
'Stop timer' => 'Zatrzymaj pomiar czasu',
'Start timer' => 'Uruchom pomiar czasu',
- 'Add project member' => 'Dodaj uczestnika projektu',
'My activity stream' => 'Moja aktywność',
'My calendar' => 'Mój kalendarz',
'Search tasks' => 'Szukaj zadań',
@@ -758,8 +725,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ń.',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Zdalny użytkownik',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Zdalni użykownicy nie przechowują swojego hasła w bazie danych Kanboard, przykłady: konta LDAP, Google and Github.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Jeśli zaznaczysz "Zablokuj możliwość logowania", dane podane przy logowaniu zostaną zignorowane.',
- 'New remote user' => 'Nowy użytkownik zdalny',
- 'New local user' => 'Nowy użytkownik lokalny',
'Default task color' => 'Domyślny kolor zadań',
'This feature does not work with all browsers.' => 'Ta funkcja może nie działać z każdą przeglądarką.',
'There is no destination project available.' => 'Żaden docelowy projekt nie jest aktualnie dostępny.',
@@ -800,7 +763,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',
@@ -841,8 +803,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.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Właściciel',
'Unread notifications' => 'Nieprzeczytane powiadomienia',
'Notification methods:' => 'Metody powiadomień:',
- 'Import tasks from CSV file' => 'Importuj zadania z pliku CSV',
'Unable to read your file' => 'Nie można odczytać pliku',
'%d task(s) have been imported successfully.' => '%d zadań zostało zaimportowanych.',
'Nothing have been imported!' => 'Nic nie zostało zaimportowane!',
@@ -947,7 +906,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.',
@@ -968,13 +926,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',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'Właściciel projektu: ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identyfikator projektu jest opcjonalny i musi być alfanumeryczny, przykład: MYPROJECT.',
'Project owner' => 'Właściciel projektu',
- 'Those dates are useful for the project Gantt chart.' => 'Daty te są przydatne dla wykresu Gantta.',
'Private projects do not have users and groups management.' => 'Projekty prywatne nie wspierają obsługi użytkowników i grup.',
'There is no project member.' => 'Projekt nie ma uczestników.',
'Priority' => 'Priorytet',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Rozpoczęte:',
'Moved:' => 'Przeniesione:',
'Task #%d' => 'Zadanie #%d',
- 'Date and time format' => 'Format daty oraz czasu',
'Time format' => 'Format czasu',
'Start date: ' => 'Data rozpoczęcia: ',
'End date: ' => 'Data zakończenia: ',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Użytkownik został wyłączony.',
'Unable to disable this user.' => 'Nie można wyłączyć użytkownika.',
'All files have been uploaded successfully.' => 'Wszystkie pliki zostały pomyślnie przesłane.',
- 'View uploaded files' => 'Zobacz przesłane pliki',
'The maximum allowed file size is %sB.' => 'Maksymalny rozmiar pliku to %sB.',
- 'Choose files again' => 'Wybierz jeszcze raz pliki',
'Drag and drop your files here' => 'Przeciągnij i upuść pliki tutaj',
'choose files' => 'wybierz pliki',
'View profile' => 'Zobacz profil',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index 7659ba2b..ef8fc4c9 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d tarefas no board',
'%d tasks in total' => '%d tarefas no total',
'Unable to update this board.' => 'Não foi possível atualizar este board.',
- 'Edit board' => 'Editar board',
'Disable' => 'Desativar',
'Enable' => 'Ativar',
'New project' => 'Novo projeto',
'Do you really want to remove this project: "%s"?' => 'Você realmente deseja remover este projeto: "%s"?',
'Remove project' => 'Remover projeto',
'Edit the board for "%s"' => 'Editar o board para "%s"',
- 'All projects' => 'Todos os projetos',
'Add a new column' => 'Adicionar uma nova coluna',
'Title' => 'Título',
'Assigned to %s' => 'Designado para %s',
'Remove a column' => 'Remover uma coluna',
- 'Remove a column from a board' => 'Remover uma coluna do board',
'Unable to remove this column.' => 'Não foi possível remover esta coluna.',
'Do you really want to remove this column: "%s"?' => 'Você realmente deseja remover esta coluna: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Esta ação irá REMOVER TODAS AS TAREFAS associadas a esta coluna!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Comando VACUUM)',
'(Gzip compressed Sqlite file)' => '(Arquivo Sqlite comprimido com Gzip)',
'Close a task' => 'Finalizar uma tarefa',
- 'Edit a task' => 'Editar uma tarefa',
'Column' => 'Coluna',
'Color' => 'Cor',
'Assignee' => 'Designação',
@@ -119,7 +115,7 @@ return array(
'The title is required' => 'O título é obrigatório',
'Settings saved successfully.' => 'Configurações salvas com sucesso.',
'Unable to save your settings.' => 'Não é possível salvar suas configurações.',
- 'Database optimization done.' => 'Otimização do banco de dados finalizada.',
+ 'Database optimization done.' => 'Otimização do banco de dados concluida.',
'Your project have been created successfully.' => 'Seu projeto foi criado com sucesso.',
'Unable to create your project.' => 'Não é possível criar o seu projeto.',
'Project updated successfully.' => 'Projeto atualizado com sucesso.',
@@ -145,10 +141,10 @@ return array(
'User removed successfully.' => 'Usuário removido com sucesso.',
'Unable to remove this user.' => 'Não é possível remover este usuário.',
'Board updated successfully.' => 'Board atualizado com sucesso.',
- 'Ready' => 'Pronto',
+ 'Ready' => 'A fazer',
'Backlog' => 'Backlog',
'Work in progress' => 'Em andamento',
- 'Done' => 'Finalizado',
+ 'Done' => 'Feito',
'Application version:' => 'Versão da aplicação:',
'Id' => 'Id',
'%d closed tasks' => '%d tarefas finalizadas',
@@ -162,12 +158,10 @@ return array(
'Task count' => 'Número de tarefas',
'User' => 'Usuário',
'Comments' => 'Comentários',
- 'Leave a comment' => 'Deixe um comentário',
'Comment is required' => 'Comentário é obrigatório',
- 'Leave a description' => 'Deixe uma descrição',
'Comment added successfully.' => 'Comentário adicionado com sucesso.',
'Unable to create your comment.' => 'Não é possível criar o seu comentário.',
- 'Due Date' => 'Data de vencimento',
+ 'Due Date' => 'Data fim estimada',
'Invalid date' => 'Data inválida',
'Automatic actions' => 'Ações automáticas',
'Your automatic action have been created successfully.' => 'Sua ação automética foi criada com sucesso.',
@@ -225,8 +219,7 @@ return array(
'Closed' => 'Finalizado',
'Search' => 'Pesquisar',
'Nothing found.' => 'Nada foi encontrado.',
- 'Due date' => 'Data de vencimento',
- 'Others formats accepted: %s and %s' => 'Outros formatos permitidos: %s e %s',
+ 'Due date' => 'Data fim estimada',
'Description' => 'Descrição',
'%d comments' => '%d comentários',
'%d comment' => '%d comentário',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'ID da Tarefa',
'Creator' => 'Criado por',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Remoto',
'Enabled' => 'Habilitado',
'Disabled' => 'Desabilitado',
- 'Username:' => 'Usuário:',
- 'Name:' => 'Nome:',
+ 'Login:' => 'Usuário:',
+ 'Full Name:' => 'Nome:',
'Email:' => 'E-mail:',
'Notifications:' => 'Notificações:',
'Notifications' => 'Notificações',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Etiquetas das associações',
'Link modification' => 'Modificação de uma associação',
'Links' => 'Associações',
- 'Link settings' => 'Configuração das associações',
'Opposite label' => 'Nome da etiqueta oposta',
'Remove a link' => 'Remover uma associação',
- 'Task\'s links' => 'Associações das tarefas',
'The labels must be different' => 'As etiquetas devem ser diferentes',
'There is no link.' => 'Não há nenhuma associação.',
'This label must be unique' => 'Esta etiqueta deve ser unica',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Vista reduzida',
'Horizontal scrolling' => 'Rolagem horizontal',
'Compact/wide view' => 'Alternar entre a vista compacta e ampliada',
- 'No results match:' => 'Nenhum resultado:',
'Currency' => 'Moeda',
'Private project' => 'Projeto privado',
'AUD - Australian Dollar' => 'AUD - Dólar australiano',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Iene japonês',
'NZD - New Zealand Dollar' => 'NZD - Dólar Neozelandês',
'RSD - Serbian dinar' => 'RSD - Dinar sérvio',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Dólar norte-americano',
'Destination column' => 'Coluna de destino',
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um usuário',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Taxas de câmbio das moedas estrangeiras',
'Rate' => 'Taxa',
'Change reference currency' => 'Mudar a moeda de referência',
- 'Add a new currency rate' => 'Adicionar uma nova taxa para uma moeda',
'Reference currency' => 'Moeda de Referência',
'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',
@@ -615,7 +590,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',
@@ -638,22 +612,22 @@ return array(
'Edit recurrence' => 'Modificar a recorrência',
'Generate recurrent task' => 'Gerar uma tarefa recorrente',
'Trigger to generate recurrent task' => 'Trigger para gerar tarefa recorrente',
- 'Factor to calculate new due date' => 'Fator para o cálculo da nova data limite',
- 'Timeframe to calculate new due date' => 'Escala de tempo para o cálculo da nova data limite',
- 'Base date to calculate new due date' => 'Data a ser utilizada para calcular a nova data limite',
+ 'Factor to calculate new due date' => 'Fator para o cálculo da nova data fim estimada',
+ 'Timeframe to calculate new due date' => 'Escala de tempo para o cálculo da nova data fim estimada',
+ 'Base date to calculate new due date' => 'Data a ser utilizada para calcular a nova data fim estimada',
'Action date' => 'Data da ação',
- 'Base date to calculate new due date: ' => 'Data a ser utilizada para calcular a nova data limite: ',
+ 'Base date to calculate new due date: ' => 'Data a ser utilizada para calcular a nova data fim estimada: ',
'This task has created this child task: ' => 'Esta tarefa criou a tarefa filha: ',
'Day(s)' => 'Dia(s)',
- 'Existing due date' => 'Data limite existente',
- 'Factor to calculate new due date: ' => 'Fator para calcular a nova data limite: ',
+ 'Existing due date' => 'data fim estimada existente',
+ 'Factor to calculate new due date: ' => 'Fator para calcular a nova data fim estimada: ',
'Month(s)' => 'Mês(es)',
'Recurrence' => 'Recorrência',
'This task has been created by: ' => 'Esta tarefa foi criada por: ',
'Recurrent task has been generated:' => 'A tarefa recorrente foi gerada:',
- 'Timeframe to calculate new due date: ' => 'Escala de tempo para o cálculo da nova data limite: ',
+ 'Timeframe to calculate new due date: ' => 'Escala de tempo para o cálculo da nova data fim estimada: ',
'Trigger to generate recurrent task: ' => 'Trigger para gerar tarefa recorrente: ',
- 'When task is closed' => 'Quando a tarefa é fechada',
+ 'When task is closed' => 'Quando a tarefa é finalizada',
'When task is moved from first column' => 'Quando a tarefa é movida fora da primeira coluna',
'When task is moved to last column' => 'Quando a tarefa é movida para a última coluna',
'Year(s)' => 'Ano(s)',
@@ -680,14 +654,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',
@@ -705,7 +673,7 @@ return array(
'New category: %s' => 'Nova categoria: %s',
'New color: %s' => 'Nova cor: %s',
'New complexity: %d' => 'Nova complexidade: %d',
- 'The due date have been removed' => 'A data limite foi retirada',
+ 'The due date have been removed' => 'A data fim estimada foi retirada',
'There is no description anymore' => 'Agora não tem mais descrição',
'Recurrence settings have been modified' => 'As configurações da recorrência foram modificadas',
'Time spent changed: %sh' => 'O tempo despendido foi mudado: %sh',
@@ -725,16 +693,15 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Stop timer',
'Start timer' => 'Start timer',
- 'Add project member' => 'Adicionar membro ao projeto',
'My activity stream' => 'Meu feed de atividades',
'My calendar' => 'Minha agenda',
'Search tasks' => 'Pesquisar tarefas',
'Reset filters' => 'Redefinir os filtros',
- 'My tasks due tomorrow' => 'Minhas tarefas que expiram amanhã',
+ 'My tasks due tomorrow' => 'Minhas tarefas que expirarão amanhã',
'Tasks due today' => 'Tarefas que expiram hoje',
- 'Tasks due tomorrow' => 'Tarefas que expiram amanhã',
+ 'Tasks due tomorrow' => 'Tarefas que expirarão amanhã',
'Tasks due yesterday' => 'Tarefas que expiraram ontem',
- 'Closed tasks' => 'Tarefas fechadas',
+ 'Closed tasks' => 'Tarefas finalizadas',
'Open tasks' => 'Tarefas abertas',
'Not assigned' => 'Não designada',
'View advanced search syntax' => 'Ver a sintaxe para pesquisa avançada',
@@ -757,9 +724,7 @@ return array(
'Search by color: ' => 'Pesquisar por cor: ',
'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"',
+ 'Search by due date: ' => 'Pesquisar por data fim estimada: ',
'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.',
@@ -776,19 +741,17 @@ return array(
'Time spent into each column' => 'Tempo gasto em cada coluna',
'The lead time is the duration between the task creation and the completion.' => 'O Lead time é o tempo gasto entre a criação da tarefa e a sua conclusão.',
'The cycle time is the duration between the start date and the completion.' => 'O Cycle time é o tempo gasto entre a data de início e a sua conclusão.',
- 'If the task is not closed the current time is used instead of the completion date.' => 'Se a tarefa não está fechada, a hora atual é usada no lugar da data de conclusão.',
+ 'If the task is not closed the current time is used instead of the completion date.' => 'Se a tarefa não está finalizada, a hora atual é usada no lugar da data de conclusão.',
'Set automatically the start date' => 'Definir automaticamente a data de início',
'Edit Authentication' => 'Modificar a autenticação',
'Remote user' => 'Usuário remoto',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Os usuários remotos não conservam as suas senhas no banco de dados Kanboard, exemplos: contas LDAP, Github ou Google.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se você marcar "Interdir o formulário de autenticação", os identificadores entrados no formulário de login serão ignorado.',
- 'New remote user' => 'Criar um usuário remoto',
- 'New local user' => 'Criar um usuário local',
'Default task color' => 'Cor padrão para as tarefas',
'This feature does not work with all browsers.' => 'Esta funcionalidade não é compatível com todos os navegadores.',
'There is no destination project available.' => 'Não há nenhum projeto de destino disponível.',
'Trigger automatically subtask time tracking' => 'Ativar automaticamente o monitoramento do tempo para as subtarefas',
- 'Include closed tasks in the cumulative flow diagram' => 'Incluir as tarefas fechadas no diagrama de fluxo acumulado',
+ 'Include closed tasks in the cumulative flow diagram' => 'Incluir as tarefas finalizadas no diagrama de fluxo acumulado',
'Current swimlane: %s' => 'Swimlane atual: %s',
'Current column: %s' => 'Coluna atual: %s',
'Current category: %s' => 'Categoria atual: %s',
@@ -800,14 +763,13 @@ 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',
'Start date:' => 'Data de início:',
- 'Due date:' => 'Data de expiração:',
- 'There is no start date or due date for this task.' => 'Não existe data de início ou data de expiração para esta tarefa.',
- 'Moving or resizing a task will change the start and due date of the task.' => 'Mover ou redimensionar uma tarefa irá alterar a data de início e expiração da tarefa.',
+ 'Due date:' => 'Data fim estimada:',
+ 'There is no start date or due date for this task.' => 'Não existe data de início ou data fim estimada para esta tarefa.',
+ 'Moving or resizing a task will change the start and due date of the task.' => 'Mover ou redimensionar uma tarefa irá alterar a data de início e conclusão estimada da tarefa.',
'There is no task in your project.' => 'Não há tarefas em seu projeto.',
'Gantt chart' => 'Gráfico de Gantt',
'People who are project managers' => 'Pessoas que são gerente de projeto',
@@ -841,8 +803,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.',
@@ -860,7 +820,7 @@ return array(
'Subtask updated on task #%d' => 'Subtarefa atualizada na tarefa #%d',
'New task #%d: %s' => 'Nova tarefa #%d: %s',
'Task updated #%d' => 'Tarefa #%d atualizada',
- 'Task #%d closed' => 'Tarefa #%d fechada',
+ 'Task #%d closed' => 'Tarefa #%d finalizada',
'Task #%d opened' => 'Tarefa #%d aberta',
'Column changed for task #%d' => 'Coluna alterada para a tarefa #%d',
'New position for task #%d' => 'Nova posição para a tarefa #%d',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Líder',
'Unread notifications' => 'Notificações não lidas',
'Notification methods:' => 'Métodos de notificação:',
- 'Import tasks from CSV file' => 'Importar tarefas a partir de arquivo CSV',
'Unable to read your file' => 'Não foi possível ler seu arquivo',
'%d task(s) have been imported successfully.' => '%d tarefa(s) importada(s) com sucesso.',
'Nothing have been imported!' => 'Nada foi importado!',
@@ -909,7 +868,7 @@ return array(
'Your file must be encoded in UTF-8' => 'Seu arquivo deve estar codificado em UTF-8',
'The first row must be the header' => 'A primeira linha deve ser o cabeçalho',
'Duplicates are not verified for you' => 'Registros duplicados não são verificados',
- 'The due date must use the ISO format: YYYY-MM-DD' => 'A data de vencimento deve utilizar o formato ISO: YYYY-MM-DD',
+ 'The due date must use the ISO format: YYYY-MM-DD' => 'A data fim estimada deve utilizar o formato ISO: YYYY-MM-DD',
'Download CSV template' => 'Baixar modelo de arquivo CSV',
'No external integration registered.' => 'Nenhuma integração externa registrada.',
'Duplicates are not imported' => 'Registros duplicados não são importados',
@@ -947,7 +906,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.',
@@ -968,13 +926,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',
@@ -984,7 +939,7 @@ return array(
'Estimated vs actual time' => 'Tempo estimado vs tempo real',
'RUB - Russian Ruble' => 'RUB - Rublo russo',
'Assign the task to the person who does the action when the column is changed' => 'Atribuir a tarefa a pessoa que faz a ação quando a coluna é alterada',
- 'Close a task in a specific column' => 'Fechar uma tarefa em uma coluna específica',
+ 'Close a task in a specific column' => 'Finalizar uma tarefa em uma coluna específica',
'Time-based One-time Password Algorithm' => 'Senha de uso único baseado no tempo',
'Two-Factor Provider: ' => 'Provedor de autenticação a dois fatores: ',
'Disable two-factor authentication' => 'Desativar a autenticação a dois fatores',
@@ -1002,17 +957,16 @@ return array(
'Creation' => 'Criação',
'Expiration' => 'Expiração',
'Password reset history' => 'Histórico da redefinição da senha',
- 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas as tarefas da coluna "%s" e da Swimlane "%s" foram fechadas com sucesso.',
- 'Do you really want to close all tasks of this column?' => 'Você realmente deseja fechar todas as tarefas desta coluna?',
- '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarefa(s) na coluna "%s" e na Swimlane "%s" serão fechadas.',
- 'Close all tasks of this column' => 'Fechar todas as tarefas desta coluna',
+ 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas as tarefas da coluna "%s" e da Swimlane "%s" foram finalizadas com sucesso.',
+ 'Do you really want to close all tasks of this column?' => 'Você realmente deseja finalizar todas as tarefas desta coluna?',
+ '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarefa(s) na coluna "%s" e na Swimlane "%s" serão finalizadas.',
+ 'Close all tasks of this column' => 'Finalizar todas as tarefas desta coluna',
'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nenhuma extensão tem registado um método de notificação de projecto. Você ainda pode definir notificações individuais em seu perfil de usuário.',
'My dashboard' => 'Meu Painel',
'My profile' => 'Meu perfil',
'Project owner: ' => 'Líder do projeto: ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'O identificador do projeto é opcional e deve ser alfanumérico, por exemplo MEUPROJETO.',
'Project owner' => 'Líder do projeto',
- 'Those dates are useful for the project Gantt chart.' => 'Estas datas são úteis para o gráfico de Gantt dos projetos.',
'Private projects do not have users and groups management.' => 'Projetos privados não têm gestão de usuários e grupos.',
'There is no project member.' => 'Não há nenhum membro do projeto.',
'Priority' => 'Prioridade',
@@ -1023,7 +977,7 @@ return array(
'Lowest priority' => 'Prioridade baixa',
'Highest priority' => 'Prioridade alta',
'If you put zero to the low and high priority, this feature will be disabled.' => 'Se você colocar zero para a prioridade alta e baixa, essa funcionalidade será desativada.',
- 'Close a task when there is no activity' => 'Fechar uma tarefa sem atividade',
+ 'Close a task when there is no activity' => 'Finalizar uma tarefa sem atividade',
'Duration in days' => 'Duração em dias',
'Send email when there is no activity on a task' => 'Enviar um e-mail quando não há nenhuma atividade em uma tarefa',
'Unable to fetch link information.' => 'Não foi possível obter informações sobre o link.',
@@ -1052,7 +1006,7 @@ return array(
'Groups management' => 'Gestão dos grupos',
'Create from another project' => 'Criar a partir de outro projeto',
'open' => 'aberto',
- 'closed' => 'fechado',
+ 'closed' => 'finalizado',
'Priority:' => 'Prioridade:',
'Reference:' => 'Referência:',
'Complexity:' => 'Complexidade:',
@@ -1065,15 +1019,14 @@ return array(
'Time spent:' => 'Tempo gasto:',
'Created:' => 'Criado:',
'Modified:' => 'Modificado:',
- 'Completed:' => 'Completado:',
+ 'Completed:' => 'Finalizado:',
'Started:' => 'Começado:',
'Moved:' => 'Movido:',
'Task #%d' => 'Tarefa #%d',
- 'Date and time format' => 'Formato da hora e da data',
'Time format' => 'Formato da hora',
'Start date: ' => 'Data de início: ',
'End date: ' => 'Data final: ',
- 'New due date: ' => 'Nova data limite: ',
+ 'New due date: ' => 'Nova data fim estimada: ',
'Start date changed: ' => 'Data de início alterada: ',
'Disable private projects' => 'Desativar os projetos privados',
'Do you really want to remove this custom filter: "%s"?' => 'Você realmente quer remover este filtro personalizado: "%s"?',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Usuário desactivado com sucesso.',
'Unable to disable this user.' => 'Impossível de desativar esse usuário.',
'All files have been uploaded successfully.' => 'Todos os arquivos foram enviados com sucesso.',
- 'View uploaded files' => 'Ver os arquivos enviados',
'The maximum allowed file size is %sB.' => 'O tamanho máximo dos arquivos é %sB.',
- 'Choose files again' => 'Selecionar novamente arquivos',
'Drag and drop your files here' => 'Arraste e solte os arquivos aqui',
'choose files' => 'selecione os arquivos',
'View profile' => 'Ver o perfil',
@@ -1140,81 +1091,220 @@ return array(
'Upload my avatar image' => 'Enviar a minha imagem de avatar',
'Remove my image' => 'Remover a minha imagem',
'The OAuth2 state parameter is invalid' => 'O parâmetro "state" de OAuth2 não é válido',
- // 'User not found.' => '',
- // 'Search in activity stream' => '',
- // 'My activities' => '',
- // 'Activity until yesterday' => '',
- // 'Activity until today' => '',
- // 'Search by creator: ' => '',
- // 'Search by creation date: ' => '',
- // 'Search by task status: ' => '',
- // 'Search by task title: ' => '',
- // 'Activity stream search' => '',
- // 'Projects where "%s" is manager' => '',
- // 'Projects where "%s" is member' => '',
- // 'Open tasks assigned to "%s"' => '',
- // 'Closed tasks assigned to "%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' => '',
- // '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' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ 'User not found.' => 'Usuário não encontrado.',
+ 'Search in activity stream' => 'Procurar no fluxo de atividade',
+ 'My activities' => 'Minhas atividades',
+ 'Activity until yesterday' => 'Atividade até ontem',
+ 'Activity until today' => 'Atividade até hoje',
+ 'Search by creator: ' => 'Buscar por criador:',
+ 'Search by creation date: ' => 'Buscar por data de criação:',
+ 'Search by task status: ' => 'Buscar por estado da tarefa:',
+ 'Search by task title: ' => 'Buscar por título da tarefa',
+ 'Activity stream search' => 'Procurar fluxo de atividade',
+ 'Projects where "%s" is manager' => 'Projetos onde "%s" é gestor',
+ 'Projects where "%s" is member' => 'Projetos onde "%s" é membro',
+ 'Open tasks assigned to "%s"' => 'Tarefas abertas atribuídas a "%s"',
+ 'Closed tasks assigned to "%s"' => 'Tarefas fechadas atribuídas a "%s"',
+ 'Assign automatically a color based on a priority' => 'Atribuir automaticamente uma cor de acordo com a prioridade',
+ 'Overdue tasks for the project(s) "%s"' => 'Tarefas em atraso para o(s) projeto(s) "%s"',
+ 'Upload files' => 'Enviar arquivos',
+ 'Installed Plugins' => 'Plugins Instalados',
+ 'Plugin Directory' => 'Pasta de Plugins',
+ 'Plugin installed successfully.' => 'Plugin instalado com sucesso.',
+ 'Plugin updated successfully.' => 'Plugin atualizado com sucesso.',
+ 'Plugin removed successfully.' => 'Plugin removido com sucesso.',
+ 'Subtask converted to task successfully.' => 'Sub-tarefa convertida para tarefa com sucesso.',
+ 'Unable to convert the subtask.' => 'Não foi possivel converter a sub-tarefa.',
+ 'Unable to extract plugin archive.' => 'Não foi possivel extrair o arquivo do plugin.',
+ 'Plugin not found.' => 'Plugin não encontrado.',
+ 'You don\'t have the permission to remove this plugin.' => 'Você não tem permissão para remover este plugin.',
+ 'Unable to download plugin archive.' => 'Não foi possivel transferir o arquivo do plugin.',
+ 'Unable to write temporary file for plugin.' => 'Não foi possivel escrever o arquivo temporário para o plugin.',
+ 'Unable to open plugin archive.' => 'Não foi possivel abrir o arquivo do plugin.',
+ 'There is no file in the plugin archive.' => 'Ficheiro não encontrado dentro do arquivo do plugin.',
+ 'Create tasks in bulk' => 'Criar tarefas em massa',
+ 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Este Kanboard não está configurado para instalar plugins através do interface de usuário.',
+ 'There is no plugin available.' => 'Não existe nenhum plugin instalado.',
+ 'Install' => 'Instalar',
+ 'Update' => 'Atualizar',
+ 'Up to date' => 'Atualizado',
+ 'Not available' => 'Não disponivel',
+ 'Remove plugin' => 'Remover plugin',
+ 'Do you really want to remove this plugin: "%s"?' => 'Tem a certeza que quer remover este plugin: "%s%"?',
+ 'Uninstall' => 'Desinstalar',
+ 'Listing' => 'Listando',
+ 'Metadata' => 'Metadata',
+ 'Manage projects' => 'Gerir projetos',
+ 'Convert to task' => 'Converter para tarefa',
+ 'Convert sub-task to task' => 'Converter sub-tarefa para tarefa',
+ 'Do you really want to convert this sub-task to a task?' => 'Tem a certeza que pretende converter esta sub-tarefa para tarefa?',
+ 'My task title' => 'Titulo da minha tarefa',
+ 'Enter one task by line.' => 'Escreva uma tarefa por linha.',
+ 'Number of failed login:' => 'Número de logins falhados:',
+ 'Account locked until:' => 'Conta bloqueada até:',
+ 'Email settings' => 'Definições de Email',
+ 'Email sender address' => 'Endereço de envio de Email',
+ 'Email transport' => 'Transportador de Email',
+ 'Webhook token' => 'Token do Webhook',
+ 'Project tags management' => 'Gestão de etiquetas do Projeto',
+ 'Tag created successfully.' => 'Etiqueta criada com sucesso.',
+ 'Unable to create this tag.' => 'Não foi possivel criar esta etiqueta.',
+ 'Tag updated successfully.' => 'Etiqueta atualizada com sucesso.',
+ 'Unable to update this tag.' => 'Não foi possivel atualizar 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 Projeto',
+ 'There is no specific tag for this project at the moment.' => 'Atualmente não existe nenhuma etiqueta para este projeto.',
+ 'Tag' => 'Etiqueta',
+ 'Remove a tag' => 'Remover etiqueta',
+ 'Do you really want to remove this tag: "%s"?' => 'Tem certeza que pretende remover esta etiqueta: "%s"?',
+ 'Global tags' => 'Etiquetas globais',
+ 'There is no global tag at the moment.' => 'Atualmente 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 a tarefa quando não houver atividade 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 tarefas desta coluna no painel',
+ '%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 tarefa #%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 automaticamente 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' => 'Visão geral das tarefas de %s',
+ 'Subtasks overview for %s' => 'Visão geral das sub-tarefas de %s',
+ 'Projects overview for %s' => 'Visão geral dos projetos de %s',
+ 'Activity stream for %s' => 'Fluxo de atividade de %s',
+ 'Calendar for %s' => 'Calendário de %s',
+ 'Notifications for %s' => 'Notificações de %s',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Atribuir uma cor quando a tarefa é movida para uma swimlane especifica',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuir uma prioridade quando a tarefa é movida para uma swimlane especifica',
+ 'User unlocked successfully.' => 'Usuário desbloqueado com sucesso.',
+ 'Unable to unlock the user.' => 'Não foi possivel desbloquear o usuário.',
+ 'Move a task to another swimlane' => 'Mover a tarefa para outra swimlane',
+ 'Creator Name' => 'Nome do Criador',
+ 'Time spent and estimated' => 'Tempo gasto e estimado',
+ 'Move position' => 'Mover posição',
+ 'Move task to another position on the board' => 'Mover tarefa para outra posição no quadro',
+ 'Insert before this task' => 'Inserir antes desta tarefa',
+ 'Insert after this task' => 'Inserir depois desta tarefa',
+ 'Unlock this user' => 'Desbloquear este usuário',
+ 'Custom Project Roles' => 'Funções Personalizadas do Projeto',
+ 'Add a new custom role' => 'Adicionar uma nova função personalizada',
+ 'Restrictions for the role "%s"' => 'Restrições para a função: "%s"',
+ 'Add a new project restriction' => 'Adicionar uma nova restrição de Projeto',
+ 'Add a new drag and drop restriction' => 'Adicionar uma nova restrição de arrastar e largar',
+ 'Add a new column restriction' => 'Adicionar uma nova restrição de colunas',
+ 'Edit this role' => 'Editar esta função',
+ 'Remove this role' => 'Remover esta função',
+ 'There is no restriction for this role.' => 'Não existem restrições para esta função.',
+ 'Only moving task between those columns is permitted' => 'Só é permitido mover a tarefa entre as seguintes colunas',
+ 'Close a task in a specific column when not moved during a given period' => 'Fecha a tarefa numa coluna especifica quando não é movimentada durante um dado periodo',
+ 'Edit columns' => 'Editar colunas',
+ 'The column restriction has been created successfully.' => 'A restrição de coluna foi criada com sucesso.',
+ 'Unable to create this column restriction.' => 'Não foi possivel criar esta restrição de coluna.',
+ 'Column restriction removed successfully.' => 'Restrição de coluna removida com sucesso.',
+ 'Unable to remove this restriction.' => 'Não foi possivel remover esta restrição.',
+ 'Your custom project role has been created successfully.' => 'A sua função de projeto personalizada foi criada com sucesso.',
+ 'Unable to create custom project role.' => 'Não foi possivel criar a função de projeto personalizada.',
+ 'Your custom project role has been updated successfully.' => 'A sua função de projeto personalizada foi atualizada com sucesso.',
+ 'Unable to update custom project role.' => 'Não foi possivel atualizar a função de projeto personalizada.',
+ 'Custom project role removed successfully.' => 'Função de projeto removida com sucesso.',
+ 'Unable to remove this project role.' => 'Não foi possivel remover esta função de projeto.',
+ 'The project restriction has been created successfully.' => 'A restrição de projeto foi criada com sucesso.',
+ 'Unable to create this project restriction.' => 'Não foi possivel remover esta restrição de projeto.',
+ 'Project restriction removed successfully.' => 'Restrição de projeto removida com sucesso.',
+ 'You cannot create tasks in this column.' => 'Não pode criar tarefas nesta coluna.',
+ 'Task creation is permitted for this column' => 'A criação de tarefas é permitida nesta coluna',
+ 'Closing or opening a task is permitted for this column' => 'Fechar ou abrir tarefas é permitido nesta coluna',
+ 'Task creation is blocked for this column' => 'A criação de tarefas está bloqueada nesta coluna',
+ 'Closing or opening a task is blocked for this column' => 'Fechar ou abrir tarefas está bloqueado nesta coluna',
+ 'Task creation is not permitted' => 'A criação de tarefas não é permitida',
+ 'Closing or opening a task is not permitted' => 'Fechar ou abrir tarefas não é permitido',
+ 'New drag and drop restriction for the role "%s"' => 'Nova restrição de arrastar e largar para a função: "%s"',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Pessoas pertecentes a esta função poderam mover terefas só entre as colunas de origem e de destino.',
+ 'Remove a column restriction' => 'Remover a restrição de coluna',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Tem a certeza que quer remover a restrição de coluna: "%s" para "%s"?',
+ 'New column restriction for the role "%s"' => 'Nova restrição de coluna para a função: "%s"',
+ 'Rule' => 'Regra',
+ 'Do you really want to remove this column restriction?' => 'Tem a certeza que quer remover esta restrição de coluna?',
+ 'Custom roles' => 'Funções personalizadas',
+ 'New custom project role' => 'Nova função de projeto personalizada',
+ 'Edit custom project role' => 'Editar função de projeto personalizada',
+ 'Remove a custom role' => 'Remover função de projeto personalizada',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Tem certeza que quer remover esta função personalizada: "%s"? Todas as pessoas atribuídas a esta função ficarão membros do projeto.',
+ 'There is no custom role for this project.' => 'Não existem funções personalizadas para este projeto',
+ 'New project restriction for the role "%s"' => 'Nova restrição de projeto para a função: "%s"',
+ 'Restriction' => 'Restrição',
+ 'Remove a project restriction' => 'Remover uma restrição de projeto',
+ 'Do you really want to remove this project restriction: "%s"?' => 'Tem a certeza que quer remover a restrição de projeto: "%s"?',
+ 'Duplicate to multiple projects' => 'Duplicar para vários projetos',
+ 'This field is required' => 'Este campo é obrigatório',
+ 'Moving a task is not permitted' => 'Mover uma tarefa não é permitido',
+ 'This value must be in the range %d to %d' => 'Este valor precisa estar no intervalo %d até %d',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php
index 1f9a7030..c4e14ed3 100644
--- a/app/Locale/pt_PT/translations.php
+++ b/app/Locale/pt_PT/translations.php
@@ -47,11 +47,11 @@ return array(
'Edit user' => 'Editar utilizador',
'Logout' => 'Sair',
'Bad username or password' => 'Utilizador ou senha inválidos',
- 'Edit project' => 'Editar projecto',
+ 'Edit project' => 'Editar projeto',
'Name' => 'Nome',
- 'Projects' => 'Projectos',
- 'No project' => 'Nenhum projecto',
- 'Project' => 'Projecto',
+ 'Projects' => 'Projetos',
+ 'No project' => 'Nenhum projeto',
+ 'Project' => 'Projeto',
'Status' => 'Estado',
'Tasks' => 'Tarefas',
'Board' => 'Quadro',
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d tarefas no quadro',
'%d tasks in total' => '%d tarefas no total',
'Unable to update this board.' => 'Não foi possível actualizar este quadro.',
- 'Edit board' => 'Editar quadro',
'Disable' => 'Desactivar',
'Enable' => 'Activar',
- 'New project' => 'Novo projecto',
- 'Do you really want to remove this project: "%s"?' => 'Tem a certeza que quer remover este projecto: "%s" ?',
- 'Remove project' => 'Remover projecto',
+ 'New project' => 'Novo projeto',
+ 'Do you really want to remove this project: "%s"?' => 'Tem a certeza que quer remover este projeto: "%s" ?',
+ 'Remove project' => 'Remover projeto',
'Edit the board for "%s"' => 'Editar o quadro para "%s"',
- 'All projects' => 'Todos os projectos',
'Add a new column' => 'Adicionar uma nova coluna',
'Title' => 'Título',
'Assigned to %s' => 'Designado para %s',
'Remove a column' => 'Remover uma coluna',
- 'Remove a column from a board' => 'Remover uma coluna do quadro',
'Unable to remove this column.' => 'Não foi possível remover esta coluna.',
'Do you really want to remove this column: "%s"?' => 'Tem a certeza que quer remover esta coluna: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Esta acção irá REMOVER TODAS AS TAREFAS associadas a esta coluna!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Comando VACUUM)',
'(Gzip compressed Sqlite file)' => '(Arquivo Sqlite comprimido com Gzip)',
'Close a task' => 'Finalizar uma tarefa',
- 'Edit a task' => 'Editar uma tarefa',
'Column' => 'Coluna',
'Color' => 'Cor',
'Assignee' => 'Assignado',
@@ -112,24 +108,24 @@ return array(
'The user id is required' => 'O ID de utilizador é obrigatório',
'Passwords don\'t match' => 'As senhas não coincidem',
'The confirmation is required' => 'A confirmação é obrigatória',
- 'The project is required' => 'O projecto é obrigatório',
+ 'The project is required' => 'O projeto é obrigatório',
'The id is required' => 'O ID é obrigatório',
- 'The project id is required' => 'O ID do projecto é obrigatório',
- 'The project name is required' => 'O nome do projecto é obrigatório',
+ 'The project id is required' => 'O ID do projeto é obrigatório',
+ 'The project name is required' => 'O nome do projeto é obrigatório',
'The title is required' => 'O título é obrigatório',
'Settings saved successfully.' => 'Configurações guardadas com sucesso.',
'Unable to save your settings.' => 'Não é possível guardar as suas configurações.',
'Database optimization done.' => 'Otimização da base de dados finalizada.',
- 'Your project have been created successfully.' => 'Projecto foi criado com sucesso.',
- 'Unable to create your project.' => 'Não é possível criar o projecto.',
- 'Project updated successfully.' => 'Projecto actualizado com sucesso.',
- 'Unable to update this project.' => 'Não é possível actualizar este projecto.',
- 'Unable to remove this project.' => 'Não é possível remover este projecto.',
- 'Project removed successfully.' => 'Projecto removido com sucesso.',
- 'Project activated successfully.' => 'Projecto activado com sucesso.',
- 'Unable to activate this project.' => 'Não é possível activar este projecto.',
- 'Project disabled successfully.' => 'Projecto desactivado com sucesso.',
- 'Unable to disable this project.' => 'Não é possível desactivar este projecto.',
+ 'Your project have been created successfully.' => 'Projeto foi criado com sucesso.',
+ 'Unable to create your project.' => 'Não é possível criar o projeto.',
+ 'Project updated successfully.' => 'Projeto actualizado com sucesso.',
+ 'Unable to update this project.' => 'Não é possível actualizar este projeto.',
+ 'Unable to remove this project.' => 'Não é possível remover este projeto.',
+ 'Project removed successfully.' => 'Projeto removido com sucesso.',
+ 'Project activated successfully.' => 'Projeto activado com sucesso.',
+ 'Unable to activate this project.' => 'Não é possível activar este projeto.',
+ 'Project disabled successfully.' => 'Projeto desactivado com sucesso.',
+ 'Unable to disable this project.' => 'Não é possível desactivar este projeto.',
'Unable to open this task.' => 'Não é possível abrir esta tarefa.',
'Task opened successfully.' => 'Tarefa aberta com sucesso.',
'Unable to close this task.' => 'Não é possível finalizar esta tarefa.',
@@ -152,7 +148,7 @@ return array(
'Application version:' => 'Versão da aplicação:',
'Id' => 'Id',
'%d closed tasks' => '%d tarefas finalizadas',
- 'No task for this project' => 'Não há tarefa para este projecto',
+ 'No task for this project' => 'Não há tarefa para este projeto',
'Public link' => 'Link público',
'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!',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Número de tarefas',
'User' => 'Utilizador',
'Comments' => 'Comentários',
- 'Leave a comment' => 'Deixe um comentário',
'Comment is required' => 'Comentário é obrigatório',
- 'Leave a description' => 'Deixe uma descrição',
'Comment added successfully.' => 'Comentário adicionado com sucesso.',
'Unable to create your comment.' => 'Não é possível criar o seu comentário.',
'Due Date' => 'Data de vencimento',
@@ -175,7 +169,7 @@ return array(
'Remove an action' => 'Remover uma acção',
'Unable to remove this action.' => 'Não é possível remover esta acção.',
'Action removed successfully.' => 'Acção removida com sucesso.',
- 'Automatic actions for the project "%s"' => 'Acções automáticas para o projecto "%s"',
+ 'Automatic actions for the project "%s"' => 'Acções automáticas para o projeto "%s"',
'Add an action' => 'Adicionar Acção',
'Event name' => 'Nome do evento',
'Action name' => 'Nome da acção',
@@ -189,7 +183,7 @@ return array(
'Remove an automatic action' => 'Remover uma acção automática',
'Assign the task to a specific user' => 'Designar a tarefa para um utilizador específico',
'Assign the task to the person who does the action' => 'Designar a tarefa para a pessoa que executa a acção',
- 'Duplicate the task to another project' => 'Duplicar a tarefa para um outro projecto',
+ 'Duplicate the task to another project' => 'Duplicar a tarefa para um outro projeto',
'Move a task to another column' => 'Mover a tarefa para outra coluna',
'Task modification' => 'Modificação de tarefa',
'Task creation' => 'Criação de tarefa',
@@ -197,7 +191,7 @@ return array(
'Assign a color to a specific user' => 'Designar uma cor para um utilizador específico',
'Column title' => 'Título da coluna',
'Position' => 'Posição',
- 'Duplicate to another project' => 'Duplicar para outro projecto',
+ 'Duplicate to another project' => 'Duplicar para outro projeto',
'Duplicate' => 'Duplicar',
'link' => 'link',
'Comment updated successfully.' => 'Comentário actualizado com sucesso.',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Pesquisar',
'Nothing found.' => 'Nada encontrado.',
'Due date' => 'Data de vencimento',
- 'Others formats accepted: %s and %s' => 'Outros formatos permitidos: %s e %s',
'Description' => 'Descrição',
'%d comments' => '%d comentários',
'%d comment' => '%d comentário',
@@ -253,7 +246,7 @@ return array(
'Remove a category' => 'Remover uma categoria',
'Category removed successfully.' => 'Categoria removida com sucesso.',
'Unable to remove this category.' => 'Não foi possível remover esta categoria.',
- 'Category modification for the project "%s"' => 'Modificação de categoria para o projecto "%s"',
+ 'Category modification for the project "%s"' => 'Modificação de categoria para o projeto "%s"',
'Category Name' => 'Nome da Categoria',
'Add a new category' => 'Adicionar uma nova categoria',
'Do you really want to remove this category: "%s"?' => 'Tem a certeza que quer remover esta categoria: "%s"',
@@ -296,21 +289,19 @@ return array(
'Sub-task added successfully.' => 'Subtarefa adicionada com sucesso.',
'Maximum size: ' => 'Tamanho máximo: ',
'Unable to upload the file.' => 'Não foi possível carregar o arquivo.',
- 'Display another project' => 'Mostrar outro projecto',
+ 'Display another project' => 'Mostrar 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',
'Task Id' => 'ID da Tarefa',
'Creator' => 'Criado por',
'Modification date' => 'Data da modificação',
'Completion date' => 'Data da finalização',
'Clone' => 'Clonar',
- 'Project cloned successfully.' => 'Projecto clonado com sucesso.',
- 'Unable to clone this project.' => 'Não foi possível clonar este projecto.',
- 'Enable email notifications' => 'Activar notificações por email',
+ 'Project cloned successfully.' => 'Projeto clonado com sucesso.',
+ 'Unable to clone this project.' => 'Não foi possível clonar este projeto.',
+ 'Enable email notifications' => 'Activar notificações por e-mail',
'Task position:' => 'Posição da tarefa:',
'The task #%d have been opened.' => 'A tarefa #%d foi aberta.',
'The task #%d have been closed.' => 'A tarefa #%d foi finalizada.',
@@ -322,26 +313,21 @@ 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:',
+ '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',
'Active tasks' => 'Tarefas activas',
'Disable public access' => 'Desactivar o acesso público',
'Enable public access' => 'Activar o acesso público',
'Public access disabled' => 'Acesso público desactivado',
- 'Do you really want to disable this project: "%s"?' => 'Tem a certeza que quer desactivar este projecto: "%s"?',
- 'Do you really want to enable this project: "%s"?' => 'Tem a certeza que quer activar este projecto: "%s"?',
- 'Project activation' => 'Activação do projecto',
- 'Move the task to another project' => 'Mover a tarefa para outro projecto',
- 'Move to another project' => 'Mover para outro projecto',
+ 'Do you really want to disable this project: "%s"?' => 'Tem a certeza que quer desactivar este projeto: "%s"?',
+ 'Do you really want to enable this project: "%s"?' => 'Tem a certeza que quer activar este projeto: "%s"?',
+ 'Project activation' => 'Activação do projeto',
+ 'Move the task to another project' => 'Mover a tarefa para outro projeto',
+ 'Move to another project' => 'Mover para outro projeto',
'Do you really want to duplicate this task?' => 'Tem a certeza que quer duplicar esta tarefa?',
'Duplicate a task' => 'Duplicar uma tarefa',
'External accounts' => 'Contas externas',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Remoto',
'Enabled' => 'Activado',
'Disabled' => 'Desactivado',
- 'Username:' => 'Utilizador:',
- 'Name:' => 'Nome:',
+ 'Login:' => 'Utilizador:',
+ 'Full Name:' => 'Nome:',
'Email:' => 'E-mail:',
'Notifications:' => 'Notificações:',
'Notifications' => 'Notificações',
@@ -377,7 +363,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',
@@ -386,14 +372,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)',
+ 'Default columns for new projects (Comma-separated)' => 'Colunas padrão para novos projetos (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',
@@ -419,8 +403,8 @@ return array(
'Token regenerated.' => 'Token ',
'Date format' => 'Formato da data',
'ISO format is always accepted, example: "%s" and "%s"' => 'O formato ISO é sempre aceite, exemplo: "%s" e "%s"',
- 'New private project' => 'Novo projecto privado',
- 'This project is private' => 'Este projecto é privado',
+ 'New private project' => 'Novo projeto privado',
+ 'This project is private' => 'Este projeto é privado',
'Add' => 'Adicionar',
'Start date' => 'Data de início',
'Time estimated' => 'Tempo estimado',
@@ -429,32 +413,29 @@ return array(
'Activity stream' => 'Atividades Recentes',
'Dashboard' => 'Painel de Controlo',
'Confirmation' => 'Confirmação',
- 'Allow everybody to access to this project' => 'Permitir a todos os acesso a este projecto',
- 'Everybody have access to this project.' => 'Todos possuem acesso a este projecto.',
+ 'Allow everybody to access to this project' => 'Permitir a todos os acesso a este projeto',
+ 'Everybody have access to this project.' => 'Todos possuem acesso a este projeto.',
'Webhooks' => 'Webhooks',
'API' => 'API',
'Create a comment from an external provider' => 'Criar um comentário por meio de um serviço externo',
- 'Project management' => 'Gestão de projectos',
- 'My projects' => 'Os meus projectos',
+ 'Project management' => 'Gestão de projetos',
+ 'My projects' => 'Os meus projetos',
'Columns' => 'Colunas',
'Task' => 'Tarefas',
- 'Your are not member of any project.' => 'Você não é membro de nenhum projecto.',
+ 'Your are not member of any project.' => 'Você não é membro de nenhum projeto.',
'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',
+ '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.',
'Previous' => 'Anterior',
'The id must be an integer' => 'O ID deve ser um número inteiro',
- 'The project id must be an integer' => 'O ID do projecto deve ser um inteiro',
+ 'The project id must be an integer' => 'O ID do projeto deve ser um inteiro',
'The status must be an integer' => 'O estado deve ser um número inteiro',
'The subtask id is required' => 'O ID da subtarefa é obrigatório',
'The subtask id must be an integer' => 'O ID da subtarefa deve ser um número inteiro',
@@ -465,10 +446,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"',
+ 'Daily project summary' => 'Resumo diário do projeto',
+ 'Daily project summary export' => 'Exportação diária do resumo do projeto',
'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',
@@ -479,7 +458,7 @@ return array(
'Inactive swimlanes' => 'Desactivar swimlanes',
'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 modification for the project "%s"' => 'Modificação de swimlane para o projeto "%s"',
'Swimlane removed successfully.' => 'Swimlane removido com sucesso.',
'Swimlanes' => 'Swimlanes',
'Swimlane updated successfully.' => 'Swimlane atualizado com sucesso.',
@@ -488,13 +467,12 @@ return array(
'Unable to update this swimlane.' => 'Não foi possível atualizar este swimlane.',
'Your swimlane have been created successfully.' => 'Seu swimlane foi criado com sucesso.',
'Example: "Bug, Feature Request, Improvement"' => 'Exemplo: "Bug, Feature Request, Improvement"',
- 'Default categories for new projects (Comma-separated)' => 'Categorias padrão para novos projectos (Separadas por vírgula)',
+ 'Default categories for new projects (Comma-separated)' => 'Categorias padrão para novos projetos (Separadas por vírgula)',
'Integrations' => 'Integrações',
'Integration with third-party services' => 'Integração com serviços de terceiros',
'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',
@@ -515,7 +493,7 @@ return array(
'There is nothing to show.' => 'Não há nada para mostrar',
'Time Tracking' => 'Gestão de tempo',
'You already have one subtask in progress' => 'Já tem uma subtarefa em andamento',
- 'Which parts of the project do you want to duplicate?' => 'Quais as partes do projecto que deseja duplicar?',
+ 'Which parts of the project do you want to duplicate?' => 'Quais as partes do projeto que deseja duplicar?',
'Disallow login form' => 'Desactivar login',
'Start' => 'Inicio',
'End' => 'Fim',
@@ -532,27 +510,25 @@ return array(
'Link labels' => 'Etiquetas das associações',
'Link modification' => 'Modificação de uma associação',
'Links' => 'Associações',
- 'Link settings' => 'Configuração das associações',
'Opposite label' => 'Nome da etiqueta oposta',
'Remove a link' => 'Remover uma associação',
- 'Task\'s links' => 'Associações das tarefas',
'The labels must be different' => 'As etiquetas devem ser diferentes',
'There is no link.' => 'Não há nenhuma associação.',
'This label must be unique' => 'Esta etiqueta deve ser unica',
'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',
@@ -568,9 +544,8 @@ return array(
'Compact view' => 'Vista reduzida',
'Horizontal scrolling' => 'Deslocamento horizontal',
'Compact/wide view' => 'Alternar entre a vista compacta e ampliada',
- 'No results match:' => 'Nenhum resultado:',
'Currency' => 'Moeda',
- 'Private project' => 'Projecto privado',
+ 'Private project' => 'Projeto privado',
'AUD - Australian Dollar' => 'AUD - Dólar australiano',
'CAD - Canadian Dollar' => 'CAD - Dólar canadense',
'CHF - Swiss Francs' => 'CHF - Francos Suíços',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Iene japonês',
'NZD - New Zealand Dollar' => 'NZD - Dólar Neozelandês',
'RSD - Serbian dinar' => 'RSD - Dinar sérvio',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Dólar norte-americano',
'Destination column' => 'Coluna de destino',
'Move the task to another column when assigned to a user' => 'Mover a tarefa para uma outra coluna quando esta está atribuída a um utilizador',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Taxas de câmbio das moedas estrangeiras',
'Rate' => 'Taxa',
'Change reference currency' => 'Mudar a moeda de referência',
- 'Add a new currency rate' => 'Adicionar uma nova taxa para uma moeda',
'Reference currency' => 'Moeda de Referência',
'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',
@@ -615,7 +590,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',
@@ -658,8 +632,8 @@ return array(
'When task is moved to last column' => 'Quando a tarefa é movida para a última coluna',
'Year(s)' => 'Ano(s)',
'Calendar settings' => 'Configurações do calendário',
- 'Project calendar view' => 'Vista em modo projecto do calendário',
- 'Project settings' => 'Configurações dos projectos',
+ 'Project calendar view' => 'Vista em modo projeto do calendário',
+ 'Project settings' => 'Configurações dos projetos',
'Show subtasks based on the time tracking' => 'Mostrar as subtarefas com base no controle de tempo',
'Show tasks based on the creation date' => 'Mostrar as tarefas em função da data de criação',
'Show tasks based on the start date' => 'Mostrar as tarefas em função da data de início',
@@ -672,7 +646,7 @@ return array(
'Two factor authentication disabled' => 'Autenticação com factor duplo desactivado',
'Two factor authentication enabled' => 'Autenticação com factor duplo activado',
'Unable to update this user.' => 'Impossível de actualizar este utilizador.',
- 'There is no user management for private projects.' => 'Não há gestão de utilizadores para projectos privados.',
+ 'There is no user management for private projects.' => 'Não há gestão de utilizadores para projetos privados.',
'User that will receive the email' => 'O utilizador que vai receber o e-mail',
'Email subject' => 'Assunto do e-mail',
'Date' => 'Data',
@@ -680,21 +654,15 @@ 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',
'%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s no swimlane "%s"',
'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as sub-tarefas para o período selecionado.',
'This report contains all tasks information for the given date range.' => 'Este relatório contém informações de todas as tarefas para o período selecionado.',
- 'Project activities for %s' => 'Actividade do projecto "%s"',
+ 'Project activities for %s' => 'Actividade do projeto "%s"',
'view the board on Kanboard' => 'ver o painel no Kanboard',
'The task have been moved to the first swimlane' => 'A tarefa foi movida para o primeiro Swimlane',
'The task have been moved to another swimlane:' => 'A tarefa foi movida para outro Swimlane:',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Parar temporizador',
'Start timer' => 'Iniciar temporizador',
- 'Add project member' => 'Adicionar um membro ao projecto',
'My activity stream' => 'O meu feed de actividade',
'My calendar' => 'A minha agenda',
'Search tasks' => 'Pesquisar tarefas',
@@ -751,16 +718,14 @@ return array(
'Filter' => 'Filtro',
'Advanced search' => 'Pesquisa avançada',
'Example of query: ' => 'Exemplo de consulta: ',
- 'Search by project: ' => 'Pesquisar por projecto: ',
+ 'Search by project: ' => 'Pesquisar por projeto: ',
'Search by column: ' => 'Pesquisar por coluna: ',
'Search by assignee: ' => 'Pesquisar por assignado: ',
'Search by color: ' => 'Pesquisar por cor: ',
'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',
@@ -782,11 +747,9 @@ return array(
'Remote user' => 'Utilizador remoto',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Utilizadores remotos não guardam a password na base de dados do Kanboard, por exemplo: LDAP, contas do Google e Github.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Se activar a opção "Desactivar login", as credenciais digitadas no login serão ignoradas.',
- 'New remote user' => 'Novo utilizador remoto',
- 'New local user' => 'Novo utilizador local',
'Default task color' => 'Cor de tarefa por defeito',
'This feature does not work with all browsers.' => 'Esta funcionalidade não funciona em todos os browsers',
- 'There is no destination project available.' => 'Não há projecto de destino disponivel',
+ 'There is no destination project available.' => 'Não há projeto de destino disponivel',
'Trigger automatically subtask time tracking' => 'Activar automáticamente subtarefa de controlo de tempo',
'Include closed tasks in the cumulative flow diagram' => 'Incluir tarefas fechadas no diagrama de fluxo acumulado',
'Current swimlane: %s' => 'Swimlane actual: %s',
@@ -800,7 +763,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',
@@ -808,10 +770,10 @@ return array(
'Due date:' => 'Data de vencimento:',
'There is no start date or due date for this task.' => 'Não existe data de inicio ou data de vencimento para esta tarefa.',
'Moving or resizing a task will change the start and due date of the task.' => 'Mover ou redimensionar a tarefa irá alterar a data de inicio e vencimento da tarefa.',
- 'There is no task in your project.' => 'Não existe tarefa no seu projecto.',
+ 'There is no task in your project.' => 'Não existe tarefa no seu projeto.',
'Gantt chart' => 'Gráfico de Gantt',
- 'People who are project managers' => 'Pessoas que são gestores do projecto',
- 'People who are project members' => 'Pessoas que são membros do projecto',
+ 'People who are project managers' => 'Pessoas que são gestores do projeto',
+ 'People who are project members' => 'Pessoas que são membros do projeto',
'NOK - Norwegian Krone' => 'NOK - Coroa Norueguesa',
'Show this column' => 'Mostrar esta coluna',
'Hide this column' => 'Esconder esta coluna',
@@ -819,15 +781,15 @@ return array(
'End date' => 'Data de fim',
'Users overview' => 'Visão geral de Utilizadores',
'Members' => 'Membros',
- 'Shared project' => 'Projecto partilhado',
- 'Project managers' => 'Gestores do projecto',
- 'Gantt chart for all projects' => 'Gráfico de Gantt para todos os projectos',
- 'Projects list' => 'Lista de projectos',
- 'Gantt chart for this project' => 'Gráfico de Gantt para este projecto',
- 'Project board' => 'Quadro de projecto',
+ 'Shared project' => 'Projeto partilhado',
+ 'Project managers' => 'Gestores do projeto',
+ 'Gantt chart for all projects' => 'Gráfico de Gantt para todos os projetos',
+ 'Projects list' => 'Lista de projetos',
+ 'Gantt chart for this project' => 'Gráfico de Gantt para este projeto',
+ 'Project board' => 'Quadro de projeto',
'End date:' => 'Data de fim:',
- 'There is no start date or end date for this project.' => 'Não existe data de inicio ou fim para este projecto.',
- 'Projects Gantt chart' => 'Gráfico de Gantt dos projectos',
+ 'There is no start date or end date for this project.' => 'Não existe data de inicio ou fim para este projeto.',
+ 'Projects Gantt chart' => 'Gráfico de Gantt dos projetos',
'Change task color when using a specific task link' => 'Alterar cor da tarefa quando se usar um tipo especifico de ligação de tarefa',
'Task link creation or modification' => 'Criação ou modificação de ligação de tarefa',
'Milestone' => 'Objectivo',
@@ -841,8 +803,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.',
@@ -875,12 +835,11 @@ return array(
'Collapse swimlane' => 'Colapsar swimlane',
'Expand swimlane' => 'Expandir swimlane',
'Add a new filter' => 'Adicionar um novo filtro',
- 'Share with all project members' => 'Partilhar com todos os membros do projecto',
+ 'Share with all project members' => 'Partilhar com todos os membros do projeto',
'Shared' => 'Partilhado',
'Owner' => 'Dono',
'Unread notifications' => 'Notificações por ler',
'Notification methods:' => 'Métodos de notificação:',
- 'Import tasks from CSV file' => 'Importar tarefas de um ficheiro CSV',
'Unable to read your file' => 'Não foi possivel ler o ficheiro',
'%d task(s) have been imported successfully.' => '%d tarefa(s) importada(s) com successo.',
'Nothing have been imported!' => 'Nada foi importado',
@@ -893,7 +852,7 @@ return array(
'Double Quote' => 'Aspas',
'Single Quote' => 'Plica',
'%s attached a file to the task #%d' => '%s anexou um ficheiro à tarefa #%d',
- 'There is no column or swimlane activated in your project!' => 'Não existe nenhuma coluna ou swimlane activado no seu projecto!',
+ 'There is no column or swimlane activated in your project!' => 'Não existe nenhuma coluna ou swimlane activado no seu projeto!',
'Append filter (instead of replacement)' => 'Acrescentar filtro (em vez de substituir)',
'Append/Replace' => 'Acrescentar/Substituir',
'Append' => 'Acrescentar',
@@ -917,7 +876,7 @@ return array(
'Passwords will be encrypted if present' => 'Senhas serão encriptadas se presentes',
'%s attached a new file to the task %s' => '%s anexou um novo ficheiro à tarefa %s',
'Link type' => 'Tipo de ligação',
- 'Assign automatically a category based on a link' => 'Assignar automáticamente a categoria baseada num link',
+ 'Assign automatically a category based on a link' => 'Atribuir automáticamente a categoria baseada num link',
'BAM - Konvertible Mark' => 'BAM - Marca Conversível',
'Assignee Username' => 'Utilizador do Assignado',
'Assignee Name' => 'Nome do Assignado',
@@ -938,16 +897,15 @@ return array(
'Remove group' => 'Remover grupo.',
'Group removed successfully.' => 'Grupo removido com sucesso.',
'Unable to remove this group.' => 'Não foi possivel remover este grupo.',
- 'Project Permissions' => 'Permissões de Projecto',
+ 'Project Permissions' => 'Permissões de Projeto',
'Manager' => 'Gestor',
- 'Project Manager' => 'Gestor de Projecto',
- 'Project Member' => 'Membro de Projecto',
- 'Project Viewer' => 'Visualizador de Projecto',
+ 'Project Manager' => 'Gestor de Projeto',
+ 'Project Member' => 'Membro de Projeto',
+ 'Project Viewer' => 'Visualizador de Projeto',
'Your account is locked for %d minutes' => 'A sua conta está bloqueada por %d minutos',
'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.',
@@ -967,14 +925,11 @@ return array(
'Group Name' => 'Nome do Grupo',
'Enter group name...' => 'Escreva o nome do Grupo',
'Role:' => 'Função:',
- 'Project members' => 'Membros do projecto',
- 'Compare hours for "%s"' => 'Comparar horas para "%s"',
+ 'Project members' => 'Membros do projeto',
'%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',
@@ -983,7 +938,7 @@ return array(
'Actual Time' => 'Tempo Real',
'Estimated vs actual time' => 'Tempo estimado vs real',
'RUB - Russian Ruble' => 'RUB - Rublo Russo',
- 'Assign the task to the person who does the action when the column is changed' => 'Assignar a tarefa à pessoa que realiza a acção quando a coluna é alterada',
+ 'Assign the task to the person who does the action when the column is changed' => 'Atribuir a tarefa à pessoa que realiza a acção quando a coluna é alterada',
'Close a task in a specific column' => 'Fechar tarefa numa coluna especifica',
'Time-based One-time Password Algorithm' => 'Algoritmo de password para uso único baseado em tempo',
'Two-Factor Provider: ' => 'Provedor de Dois Passos: ',
@@ -1006,15 +961,14 @@ return array(
'Do you really want to close all tasks of this column?' => 'Tem a certeza que quer fechar todas as tarefas nesta coluna?',
'%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarefa(s) na coluna "%s" e na swimlane "%s" serão fechadas.',
'Close all tasks of this column' => 'Fechar todas as tarefas nesta coluna',
- 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nenhum plugin tem registado um método de notificação do projecto. Pode continuar a configurar notificações individuais no seu perfil de utilizador.',
+ 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nenhum plugin tem registado um método de notificação do projeto. Pode continuar a configurar notificações individuais no seu perfil de utilizador.',
'My dashboard' => 'Meu painel',
'My profile' => 'Meu perfil',
- 'Project owner: ' => 'Dono do projecto: ',
- 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'O identificador do projecto é opcional e tem de ser alfa-numerico, exemplo: MEUPROJECTO.',
- 'Project owner' => 'Dono do projecto',
- 'Those dates are useful for the project Gantt chart.' => 'Estas datas são uteis para o gráfico de Grantt do projecto.',
- 'Private projects do not have users and groups management.' => 'Projectos privados não têm gestão de utilizadores nem de grupos.',
- 'There is no project member.' => 'Não existe membro do projecto.',
+ 'Project owner: ' => 'Dono do projeto: ',
+ 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'O identificador do projeto é opcional e tem de ser alfa-numerico, exemplo: MEUPROJETO.',
+ 'Project owner' => 'Dono do projeto',
+ 'Private projects do not have users and groups management.' => 'Projetos privados não têm gestão de utilizadores nem de grupos.',
+ 'There is no project member.' => 'Não existe membro do projeto.',
'Priority' => 'Prioridade',
'Task priority' => 'Prioridade da Tarefa',
'General' => 'Geral',
@@ -1025,7 +979,7 @@ return array(
'If you put zero to the low and high priority, this feature will be disabled.' => 'Se colocar zero na prioridade baixa ou alta, essa funcionalidade será desactivada.',
'Close a task when there is no activity' => 'Fechar tarefa quando não há actividade',
'Duration in days' => 'Duração em dias',
- 'Send email when there is no activity on a task' => 'Enviar email quando não há actividade numa tarefa',
+ 'Send email when there is no activity on a task' => 'Enviar e-mail quando não há actividade numa tarefa',
'Unable to fetch link information.' => 'Impossivel obter informação da ligação.',
'Daily background job for tasks' => 'Trabalho diário em segundo plano para tarefas',
'Auto' => 'Auto',
@@ -1044,13 +998,13 @@ return array(
'Copy and paste your link here...' => 'Copie e cole a sua ligação aqui...',
'URL' => 'URL',
'Internal links' => 'Ligações internas',
- 'Assign to me' => 'Assignar a mim',
+ 'Assign to me' => 'Atribuir a mim',
'Me' => 'Eu',
'Do not duplicate anything' => 'Não duplicar nada',
- 'Projects management' => 'Gestão de projectos',
+ 'Projects management' => 'Gestão de projetos',
'Users management' => 'Gestão de utilizadores',
'Groups management' => 'Gestão de grupos',
- 'Create from another project' => 'Criar apartir de outro projecto',
+ 'Create from another project' => 'Criar apartir de outro projeto',
'open' => 'aberto',
'closed' => 'fechado',
'Priority:' => 'Prioridade:',
@@ -1069,13 +1023,12 @@ return array(
'Started:' => 'Iniciado:',
'Moved:' => 'Movido:',
'Task #%d' => 'Tarefa #%d',
- 'Date and time format' => 'Formato tempo e data',
'Time format' => 'Formato tempo',
'Start date: ' => 'Data inicio: ',
'End date: ' => 'Data final: ',
'New due date: ' => 'Nova data estimada: ',
'Start date changed: ' => 'Data inicio alterada: ',
- 'Disable private projects' => 'Desactivar projectos privados',
+ 'Disable private projects' => 'Desactivar projetos privados',
'Do you really want to remove this custom filter: "%s"?' => 'Tem a certeza que quer remover este filtro personalizado: "%s"?',
'Remove a custom filter' => 'Remover o filtro personalizado',
'User activated successfully.' => 'Utilizador activado com sucesso.',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Utilizador desactivado com sucesso.',
'Unable to disable this user.' => 'Não foi possivel desactivar este utilizador.',
'All files have been uploaded successfully.' => 'Todos os ficheiros foram enviados com sucesso.',
- 'View uploaded files' => 'Ver ficheiros enviados',
'The maximum allowed file size is %sB.' => 'O tamanho máximo permitido é %sB.',
- 'Choose files again' => 'Escolher ficheiros novamente',
'Drag and drop your files here' => 'Arraste e deixe os ficheiros para aqui',
'choose files' => 'escolher ficheiros',
'View profile' => 'Ver perfil',
@@ -1101,11 +1052,11 @@ return array(
'Filename' => 'Nome do ficheiro',
'Size' => 'Tamanho',
'Column created successfully.' => 'Coluna criada com sucesso.',
- 'Another column with the same name exists in the project' => 'Já existe outra coluna com o mesmo nome no projecto',
+ 'Another column with the same name exists in the project' => 'Já existe outra coluna com o mesmo nome no projeto',
'Default filters' => 'Filtros padrão',
'Your board doesn\'t have any columns!' => 'O seu quadro não tem nenhuma coluna!',
'Change column position' => 'Mudar posição da coluna',
- 'Switch to the project overview' => 'Mudar para vista geral do projecto',
+ 'Switch to the project overview' => 'Mudar para vista geral do projeto',
'User filters' => 'Filtros de utilizador',
'Category filters' => 'Filtros de categoria',
'Upload a file' => 'Enviar um ficheiro',
@@ -1113,15 +1064,15 @@ return array(
'Last activity' => 'Ultima actividade',
'Change subtask position' => 'Mudar posição da sub-tarefa',
'This value must be greater than %d' => 'Este valor tem de ser maior que %d',
- 'Another swimlane with the same name exists in the project' => 'Já existe outra swimlane com o mesmo nome no projecto',
+ 'Another swimlane with the same name exists in the project' => 'Já existe outra swimlane com o mesmo nome no projeto',
'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Exemplo: http://example.kanboard.net/ (usado para gerar URLs absolutos)',
'Actions duplicated successfully.' => 'Acções duplicadas com sucesso.',
'Unable to duplicate actions.' => 'Não foi possivel duplicar acções.',
'Add a new action' => 'Adicionar nova acção',
- 'Import from another project' => 'Importar de outro projecto',
+ 'Import from another project' => 'Importar de outro projeto',
'There is no action at the moment.' => 'De momento não existe acção.',
- 'Import actions from another project' => 'Importar acções de outro projecto',
- 'There is no available project.' => 'Não existe projecto disponivel.',
+ 'Import actions from another project' => 'Importar acções de outro projeto',
+ 'There is no available project.' => 'Não existe projeto disponivel.',
'Local File' => 'Ficheiro Local',
'Configuration' => 'Configuração',
'PHP version:' => 'Versão PHP:',
@@ -1150,12 +1101,12 @@ return array(
'Search by task status: ' => 'Procurar por estado da tarefa: ',
'Search by task title: ' => 'Procurar por titulo da tarefa: ',
'Activity stream search' => 'Procurar fluxo de actividade',
- 'Projects where "%s" is manager' => 'Projectos onde "%s" é gestor',
- 'Projects where "%s" is member' => 'Projectos onde "%s" é membro',
+ 'Projects where "%s" is manager' => 'Projetos onde "%s" é gestor',
+ 'Projects where "%s" is member' => 'Projetos onde "%s" é membro',
'Open tasks assigned to "%s"' => 'Tarefas abertas assignadas a "%s"',
'Closed tasks assigned to "%s"' => 'Tarefas fechadas assignadas a "%s"',
- 'Assign automatically a color based on a priority' => 'Assignar uma cor automáticamente de acordo com a prioridade',
- 'Overdue tasks for the project(s) "%s"' => 'Tarefas em atraso para o(s) projecto(s) "%s"',
+ 'Assign automatically a color based on a priority' => 'Atribuir uma cor automáticamente de acordo com a prioridade',
+ 'Overdue tasks for the project(s) "%s"' => 'Tarefas em atraso para o(s) projeto(s) "%s"',
'Upload files' => 'Enviar ficheiros',
'Installed Plugins' => 'Plugins Instalados',
'Plugin Directory' => 'Directoria de Plugins',
@@ -1164,9 +1115,9 @@ return array(
'Plugin removed successfully.' => 'Plugin removido com sucesso.',
'Subtask converted to task successfully.' => 'Sub-tarefa convertida para tarefa com sucesso.',
'Unable to convert the subtask.' => 'Não foi possivel converter a sub-tarefa.',
- 'Unable to extract plugin archive.' => 'Não foi possivel extrair arquivo do plugin.',
+ 'Unable to extract plugin archive.' => 'Não foi possivel extrair o arquivo do plugin.',
'Plugin not found.' => 'Plugin não encontrado.',
- 'You don\'t have the permission to remove this plugin.' => 'Não tem acesso para remover este plugin.',
+ 'You don\'t have the permission to remove this plugin.' => 'Não tem permissão para remover este plugin.',
'Unable to download plugin archive.' => 'Não foi possivel transferir o arquivo do plugin.',
'Unable to write temporary file for plugin.' => 'Não foi possivel escrever o ficheiro temporário para o plugin.',
'Unable to open plugin archive.' => 'Não foi possivel abrir o arquivo do plugin.',
@@ -1183,7 +1134,7 @@ return array(
'Uninstall' => 'Desinstalar',
'Listing' => 'A Listar',
'Metadata' => 'Metadata',
- 'Manage projects' => 'Gerir projectos',
+ 'Manage projects' => 'Gerir projetos',
'Convert to task' => 'Converter para tarefa',
'Convert sub-task to task' => 'Converter sub-tarefa para tarefa',
'Do you really want to convert this sub-task to a task?' => 'Tem a certeza que pretende converter esta sub-tarefa para tarefa?',
@@ -1191,12 +1142,11 @@ return array(
'Enter one task by line.' => 'Escreva uma tarefa por linha.',
'Number of failed login:' => 'Número de logins falhados:',
'Account locked until:' => 'Conta bloqueada até:',
- 'Email settings' => 'Definições de Email',
- 'Email sender address' => 'Endereço de envido de Email',
- 'Email transport' => 'Transportador de Email',
+ 'Email settings' => 'Definições de E-mail',
+ 'Email sender address' => 'Endereço de envido de E-mail',
+ 'Email transport' => 'Transportador de E-mail',
'Webhook token' => 'Token do Webhook',
- 'Imports' => 'Importados',
- 'Project tags management' => 'Gestão de etiquetas do Projecto',
+ 'Project tags management' => 'Gestão de etiquetas do Projeto',
'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.',
@@ -1208,13 +1158,153 @@ return array(
'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.',
+ 'Project tags' => 'Etiquetas do Projeto',
+ 'There is no specific tag for this project at the moment.' => 'De momento não existe nenhuma etiqueta para este projeto.',
'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' => '',
- //'Hide tasks in this column in the Dashboard' => '',
+ '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 projetos 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',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Atribuir uma cor quando a tarefa é movida para uma swimlane especifica',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuir uma prioridade quando a tarefa é movida para uma swimlane especifica',
+ 'User unlocked successfully.' => 'Utilizador desbloqueado com sucesso.',
+ 'Unable to unlock the user.' => 'Não foi possivel desbloquear o utilizador.',
+ 'Move a task to another swimlane' => 'Mover a tarefa para outra swimlane',
+ 'Creator Name' => 'Nome do Criador',
+ 'Time spent and estimated' => 'Tempo gasto e estimado',
+ 'Move position' => 'Mover posição',
+ 'Move task to another position on the board' => 'Mover tarefa para outra posição no quadro',
+ 'Insert before this task' => 'Inserir antes desta tarefa',
+ 'Insert after this task' => 'Inserir depois desta tarefa',
+ 'Unlock this user' => 'Desbloquear este utilizador',
+ 'Custom Project Roles' => 'Funções Personalizadas do Projeto',
+ 'Add a new custom role' => 'Adicionar uma nova função personalizada',
+ 'Restrictions for the role "%s"' => 'Restrições para a função: "%s"',
+ 'Add a new project restriction' => 'Adicionar uma nova restrição de Projeto',
+ 'Add a new drag and drop restriction' => 'Adicionar uma nova restrição de arrastar e largar',
+ 'Add a new column restriction' => 'Adicionar uma nova restrição de colunas',
+ 'Edit this role' => 'Editar esta função',
+ 'Remove this role' => 'Remover esta função',
+ 'There is no restriction for this role.' => 'Não existem restrições para esta função.',
+ 'Only moving task between those columns is permitted' => 'Só é permitido mover a tarefa entre as seguintes colunas',
+ 'Close a task in a specific column when not moved during a given period' => 'Fecha a tarefa numa coluna especifica quando não é movimentada durante um dado periodo',
+ 'Edit columns' => 'Editar colunas',
+ 'The column restriction has been created successfully.' => 'A restrição de coluna foi criada com sucesso.',
+ 'Unable to create this column restriction.' => 'Não foi possivel criar esta restrição de coluna.',
+ 'Column restriction removed successfully.' => 'Restrição de coluna removida com sucesso.',
+ 'Unable to remove this restriction.' => 'Não foi possivel remover esta restrição.',
+ 'Your custom project role has been created successfully.' => 'O sua função de projeto personalizada foi criada com sucesso.',
+ 'Unable to create custom project role.' => 'Não foi possivel criar a função de projeto personalizada.',
+ 'Your custom project role has been updated successfully.' => 'A sua função de projeto personalizada foi atualizada com sucesso.',
+ 'Unable to update custom project role.' => 'Não foi possivel atualizar a função de projeto personalizada.',
+ 'Custom project role removed successfully.' => 'Função de projeto removida com sucesso.',
+ 'Unable to remove this project role.' => 'Não foi possivel remover esta função de projeto.',
+ 'The project restriction has been created successfully.' => 'A restrição de projeto foi criada com sucesso.',
+ 'Unable to create this project restriction.' => 'Não foi possivel remover esta restrição de projeto.',
+ 'Project restriction removed successfully.' => 'Restrição de projeto removida com sucesso.',
+ 'You cannot create tasks in this column.' => 'Não pode criar tarefas nesta coluna.',
+ 'Task creation is permitted for this column' => 'A criação de tarefas é permitida nesta coluna',
+ 'Closing or opening a task is permitted for this column' => 'Fechar ou abrir tarefas é permitido nesta coluna',
+ 'Task creation is blocked for this column' => 'A criação de tarefas está bloqueada nesta coluna',
+ 'Closing or opening a task is blocked for this column' => 'Fechar ou abrir tarefas está bloqueado nesta coluna',
+ 'Task creation is not permitted' => 'A criação de tarefas não é permitida',
+ 'Closing or opening a task is not permitted' => 'Fechar ou abrir tarefas não é permitido',
+ 'New drag and drop restriction for the role "%s"' => 'Nova restrição de arrastar e largar para a função: "%s"',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Pessoas pertecentes a esta função poderam mover terefas só entre as colunas de origem e de destino.',
+ 'Remove a column restriction' => 'Remover a restrição de coluna',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Tem a certeza que quer remover a restrição de coluna: "%s" para "%s"?',
+ 'New column restriction for the role "%s"' => 'Nova restrição de coluna para a função: "%s"',
+ 'Rule' => 'Regra',
+ 'Do you really want to remove this column restriction?' => 'Tem a certeza que quer remover esta restrição de coluna?',
+ 'Custom roles' => 'Funções personalizadas',
+ 'New custom project role' => 'Nova função de projeto personalizada',
+ 'Edit custom project role' => 'Editar função de projeto personalizada',
+ 'Remove a custom role' => 'Remover função de projeto personalizada',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Tem a certeza que quer remover esta função personalizada: "%s"? Todas as pessoas atribuídas a esta função ficarão membros do projecto.',
+ 'There is no custom role for this project.' => 'Não existem funções personalizadas para este projeto',
+ 'New project restriction for the role "%s"' => 'Nova restrição de projeto para a função: "%s"',
+ 'Restriction' => 'Restrição',
+ 'Remove a project restriction' => 'Remover uma restrição de projeto',
+ 'Do you really want to remove this project restriction: "%s"?' => 'Tem a certeza que quer remover a restrição de projeto: "%s"?',
+ 'Duplicate to multiple projects' => 'Duplicar para vários projetos',
+ 'This field is required' => 'Este campo é obrigatório',
+ 'Moving a task is not permitted' => 'Não é permitido mover uma tarefa',
+ 'This value must be in the range %d to %d' => 'Este valor deve estar entre %d e %d',
+ 'You are not allowed to move this task.' => 'Não lhe é permitido mover esta tarefa.',
+ 'API User Access' => 'Acesso de Utilizador ao API',
+ 'Preview' => 'Pré-Visualizar',
+ 'Write' => 'Escrever',
+ 'Write your text in Markdown' => 'Escreva o seu texto em Markdown',
+ 'New External Task: %s' => 'Nova Tarefa Externa: %s',
+ 'No personal API access token registered.' => 'Nenhum token pessoal de acesso ao API ',
+ 'Your personal API access token is "%s"' => 'O seu token de acesso pessoal ao API é "%s"',
+ 'Remove your token' => 'Remover o seu token',
+ 'Generate a new token' => 'Gerar um novo token',
+ 'Showing %d-%d of %d' => 'A mostrar %d-%d de %d',
+ 'Outgoing Emails' => 'E-mails de saída',
+ 'Add or change currency rate' => 'Adicionar ou alterar taxa da moeda',
+ 'Reference currency: %s' => 'Moeda de referência: %s',
+ 'Add custom filters' => 'Adicionar filtros personalizados',
+ 'Export' => 'Exportar',
+ 'Add link label' => 'Adicionar etiqueta de associação',
+ 'Incompatible Plugins' => 'Plugins incompatíveis',
+ 'Compatibility' => 'Compatibilidade',
+ 'Permissions and ownership' => 'Permissões e propriedade',
+ 'Priorities' => 'Prioridades',
+ 'Close this window' => 'Feche esta janela',
+ 'Unable to upload this file.' => 'Não foi possível enviar este ficheiro.',
+ 'Import tasks' => 'Importar tarefas',
+ 'Choose a project' => 'Escolha um projeto',
+ 'Profile' => 'Perfil',
+ 'Application role' => 'Função na Aplicação',
+ '%d invitations were sent.' => '%d convites foram enviados.',
+ '%d invitation was sent.' => '%d convite foi enviado.',
+ 'Unable to create this user.' => 'Não foi possível criar este utilizador.',
+ 'Kanboard Invitation' => 'Convite de Kanboard',
+ 'Visible on dashboard' => 'Visível no painel',
+ 'Created at:' => 'Criado a:',
+ 'Updated at:' => 'Atualizado a:',
+ 'There is no custom filter.' => 'Não existe nenhum filtro personalizado.',
+ 'New User' => 'Novo Utilizador',
+ 'Authentication' => 'Autenticação',
+ 'If checked, this user will use a third-party system for authentication.' => 'Se selecionado, este utilizador irá utilizar um serviços de terceiros para autenticação.',
+ 'The password is necessary only for local users.' => 'A password só é necessária para utilizadores locais.',
+ 'You have been invited to register on Kanboard.' => 'Foi convidado para se registrar no Kanboard.',
+ 'Click here to join your team' => 'Clique aqui para se juntar à sua equipa',
+ 'Invite people' => 'Convidar pessoas',
+ 'Emails' => 'E-mails',
+ 'Enter one email address by line.' => 'Insira um endereço de e-mail por linha.',
+ 'Add these people to this project' => 'Adicione estas pessoas a este projeto',
+ 'Add this person to this project' => 'Adicione esta pessoa a este projeto',
+ 'Sign-up' => 'Registe-se',
+ 'Credentials' => 'Credenciais',
+ 'New user' => 'Novo utilizador',
+ 'This username is already taken' => 'Este nome de usuário já foi utilizado',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index fe950172..7bf57fb2 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -36,14 +36,14 @@ return array(
'Remove user' => 'Удалить пользователя',
'Do you really want to remove this user: "%s"?' => 'Вы точно хотите удалить пользователя: « %s » ?',
'All users' => 'Все пользователи',
- 'Username' => 'Имя пользователя',
+ 'Username' => 'Логин',
'Password' => 'Пароль',
'Administrator' => 'Администратор',
'Sign in' => 'Войти',
'Users' => 'Пользователи',
'No user' => 'Нет пользователя',
'Forbidden' => 'Запрещено',
- 'Access Forbidden' => 'Доступ запрещен',
+ 'Access Forbidden' => 'Доступ запрещён',
'Edit user' => 'Изменить пользователя',
'Logout' => 'Выйти',
'Bad username or password' => 'Неверное имя пользователя или пароль',
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d задач на доске',
'%d tasks in total' => 'всего %d задач',
'Unable to update this board.' => 'Не удалось обновить эту доску.',
- 'Edit board' => 'Изменить доску',
- 'Disable' => 'Выключить',
- 'Enable' => 'Включить',
+ 'Disable' => 'Выключить проект',
+ 'Enable' => 'Включить проект',
'New project' => 'Новый проект',
'Do you really want to remove this project: "%s"?' => 'Вы точно хотите удалить проект: "%s"?',
'Remove project' => 'Удалить проект',
'Edit the board for "%s"' => 'Изменить доску для "%s"',
- 'All projects' => 'Все проекты',
'Add a new column' => 'Добавить новую колонку',
'Title' => 'Название',
'Assigned to %s' => 'Назначено %s',
'Remove a column' => 'Удалить колонку',
- 'Remove a column from a board' => 'Удалить колонку с доски',
'Unable to remove this column.' => 'Не удалось удалить эту колонку.',
'Do you really want to remove this column: "%s"?' => 'Вы точно хотите удалить эту колонку: "%s" ?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Вы УДАЛИТЕ ВСЕ ЗАДАЧИ находящиеся в этой колонке!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Команда VACUUM)',
'(Gzip compressed Sqlite file)' => '(Сжать GZip файл SQLite)',
'Close a task' => 'Закрыть задачу',
- 'Edit a task' => 'Изменить задачу',
'Column' => 'Колонка',
'Color' => 'Цвет',
'Assignee' => 'Назначена',
@@ -125,7 +121,7 @@ return array(
'Project updated successfully.' => 'Проект успешно обновлен.',
'Unable to update this project.' => 'Не удалось обновить проект.',
'Unable to remove this project.' => 'Не удалось удалить проект.',
- 'Project removed successfully.' => 'Проект удален.',
+ 'Project removed successfully.' => 'Проект удалён.',
'Project activated successfully.' => 'Проект активирован.',
'Unable to activate this project.' => 'Невозможно активировать проект.',
'Project disabled successfully.' => 'Проект успешно деактивирован.',
@@ -140,9 +136,9 @@ return array(
'Task created successfully.' => 'Задача создана.',
'User created successfully.' => 'Пользователь создан.',
'Unable to create your user.' => 'Не удалось создать пользователя.',
- 'User updated successfully.' => 'Пользователь обновлен.',
+ 'User updated successfully.' => 'Пользователь обновлён.',
'Unable to update your user.' => 'Не удалось обновить пользователя.',
- 'User removed successfully.' => 'Пользователь удален.',
+ 'User removed successfully.' => 'Пользователь удалён.',
'Unable to remove this user.' => 'Не удалось удалить пользователя.',
'Board updated successfully.' => 'Доска успешно обновлена.',
'Ready' => 'Готовые',
@@ -151,20 +147,18 @@ return array(
'Done' => 'Выполнено',
'Application version:' => 'Версия приложения:',
'Id' => 'ID',
- '%d closed tasks' => '%d завершенных задач',
+ '%d closed tasks' => '%d завершённых задач',
'No task for this project' => 'Нет задач для этого проекта',
'Public link' => 'Ссылка для просмотра',
'Timezone' => 'Часовой пояс',
- 'Sorry, I didn\'t find this information in my database!' => 'К сожалению, информация в базе данных не найдена !',
+ 'Sorry, I didn\'t find this information in my database!' => 'К сожалению, информация в базе данных не найдена!',
'Page not found' => 'Страница не найдена',
'Complexity' => 'Сложность',
'Task limit' => 'Лимит задач',
'Task count' => 'Количество задач',
'User' => 'Пользователь',
'Comments' => 'Комментарии',
- 'Leave a comment' => 'Оставить комментарий',
'Comment is required' => 'Нужен комментарий',
- 'Leave a description' => 'Напишите описание',
'Comment added successfully.' => 'Комментарий успешно добавлен.',
'Unable to create your comment.' => 'Невозможно создать комментарий.',
'Due Date' => 'Сделать до',
@@ -187,23 +181,23 @@ return array(
'Define action parameters' => 'Задать параметры действия',
'Do you really want to remove this action: "%s"?' => 'Вы точно хотите удалить это действие: « %s » ?',
'Remove an automatic action' => 'Удалить автоматическое действие',
- 'Assign the task to a specific user' => 'Назначить задачу определенному пользователю',
+ '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' => 'Переместить задачу в другую колонку',
'Task modification' => 'Изменение задачи',
'Task creation' => 'Создание задачи',
'Closing a task' => 'Завершение задачи',
- 'Assign a color to a specific user' => 'Назначить определенный цвет пользователю',
+ 'Assign a color to a specific user' => 'Назначить определённый цвет пользователю',
'Column title' => 'Название колонки',
'Position' => 'Расположение',
'Duplicate to another project' => 'Клонировать в другой проект',
'Duplicate' => 'Клонировать',
'link' => 'ссылка',
- 'Comment updated successfully.' => 'Комментарий обновлен.',
+ 'Comment updated successfully.' => 'Комментарий обновлён.',
'Unable to update your comment.' => 'Не удалось обновить ваш комментарий.',
'Remove a comment' => 'Удалить комментарий',
- 'Comment removed successfully.' => 'Комментарий удален.',
+ 'Comment removed successfully.' => 'Комментарий удалён.',
'Unable to remove this comment.' => 'Не удалось удалить этот комментарий.',
'Do you really want to remove this comment?' => 'Вы точно хотите удалить этот комментарий?',
'Current password for the user "%s"' => 'Текущий пароль для пользователя « %s »',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Поиск',
'Nothing found.' => 'Ничего не найдено.',
'Due date' => 'Срок',
- 'Others formats accepted: %s and %s' => 'Другой формат приемлем: %s и %s',
'Description' => 'Описание',
'%d comments' => '%d комментариев',
'%d comment' => '%d комментарий',
@@ -234,14 +227,14 @@ return array(
'Your external account is not linked anymore to your profile.' => 'Ваш внешний аккаунт больше не связан с Вашим профилем.',
'Unable to unlink your external account.' => 'Не удалось отвязать Ваш внешний аккаунт.',
'External authentication failed' => 'Внешняя авторизация не удалась',
- 'Your external account is linked to your profile successfully.' => 'Ваш внешний аккаунт успешно подключен к профилю.',
+ 'Your external account is linked to your profile successfully.' => 'Ваш внешний аккаунт успешно подключён к профилю.',
'Email' => 'E-mail',
'Task removed successfully.' => 'Задача удалена.',
'Unable to remove this task.' => 'Не удалось удалить эту задачу.',
'Remove a task' => 'Удалить задачу',
'Do you really want to remove this task: "%s"?' => 'Вы точно хотите удалить эту задачу « %s » ?',
- 'Assign automatically a color based on a category' => 'Автоматически назначать цвет по категории',
- 'Assign automatically a category based on a color' => 'Автоматически назначать категорию по цвету',
+ 'Assign automatically a color based on a category' => 'Автоматически назначить цвет по категории',
+ 'Assign automatically a category based on a color' => 'Автоматически назначить категорию по цвету',
'Task creation or modification' => 'Создание или изменение задачи',
'Category' => 'Категория',
'Category:' => 'Категория:',
@@ -262,7 +255,7 @@ return array(
'The name is required' => 'Требуется название',
'Remove a file' => 'Удалить файл',
'Unable to remove this file.' => 'Не удалось удалить файл.',
- 'File removed successfully.' => 'Файл удален.',
+ 'File removed successfully.' => 'Файл удалён.',
'Attach a document' => 'Прикрепить файл',
'Do you really want to remove this file: "%s"?' => 'Вы точно хотите удалить этот файл « %s » ?',
'Attachments' => 'Вложения',
@@ -277,7 +270,7 @@ return array(
'Remaining:' => 'Осталось:',
'hours' => 'часов',
'spent' => 'затрачено',
- 'estimated' => 'расчетное',
+ 'estimated' => 'расчётное',
'Sub-Tasks' => 'Подзадачи',
'Add a sub-task' => 'Добавить подзадачу',
'Original estimate' => 'Заплан.',
@@ -299,9 +292,7 @@ return array(
'Display another project' => 'Показать другой проект',
'Created by %s' => 'Создано %s',
'Tasks Export' => 'Экспорт задач',
- 'Tasks exportation for "%s"' => 'Задача экспортирована для « %s »',
'Start Date' => 'Дата начала',
- 'End Date' => 'Дата завершения',
'Execute' => 'Выполнить',
'Task Id' => 'ID задачи',
'Creator' => 'Автор',
@@ -321,22 +312,17 @@ return array(
'Time tracking:' => 'Отслеживание времени:',
'New sub-task' => 'Новая подзадача',
'New attachment added "%s"' => 'Добавлено вложение « %s »',
- 'New comment posted by %s' => 'Новый комментарий написан « %s »',
- 'New attachment' => 'Новое вложение',
+ 'New comment posted by %s' => 'Новый комментарий добавлен « %s »',
'New comment' => 'Новый комментарий',
- 'Comment updated' => 'Комментарий обновлен',
+ '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' => 'Общий доступ',
'Active tasks' => 'Активные задачи',
'Disable public access' => 'Отключить общий доступ',
'Enable public access' => 'Включить общий доступ',
- 'Public access disabled' => 'Общий доступ отключен',
+ 'Public access disabled' => 'Общий доступ отключён',
'Do you really want to disable this project: "%s"?' => 'Вы точно хотите деактивировать проект: "%s"?',
'Do you really want to enable this project: "%s"?' => 'Вы точно хотите активировать проект: "%s"?',
'Project activation' => 'Активация проекта',
@@ -347,11 +333,11 @@ return array(
'External accounts' => 'Внешняя аутентификация',
'Account type' => 'Тип профиля',
'Local' => 'Локальный',
- 'Remote' => 'Удаленный',
- 'Enabled' => 'Включен',
+ 'Remote' => 'Удалённый',
+ 'Enabled' => 'Включён',
'Disabled' => 'Выключены',
- 'Username:' => 'Имя пользователя:',
- 'Name:' => 'Имя:',
+ 'Login:' => 'Логин:',
+ 'Full Name:' => 'Имя:',
'Email:' => 'E-mail:',
'Notifications:' => 'Уведомления:',
'Notifications' => 'Уведомления',
@@ -362,7 +348,7 @@ return array(
'External authentications' => 'Внешняя аутентификация',
'Never connected.' => 'Ранее не соединялось.',
'No external authentication enabled.' => 'Нет активной внешней аутентификации.',
- 'Password modified successfully.' => 'Пароль изменен.',
+ 'Password modified successfully.' => 'Пароль изменён.',
'Unable to change the password.' => 'Не удалось сменить пароль.',
'Change category' => 'Смена категории',
'%s updated the task %s' => '%s обновил задачу %s',
@@ -386,14 +372,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',
+ 'Task assignee change' => 'Изменён назначенный',
+ '%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' => 'Выберите событие',
@@ -409,10 +393,10 @@ return array(
'Webhook settings' => 'Параметры Webhook',
'Reset token' => 'Перезагрузить токен',
'API endpoint:' => 'API endpoint:',
- 'Refresh interval for private board' => 'Период обновления для частных досок',
+ 'Refresh interval for private board' => 'Период обновления для приватных досок',
'Refresh interval for public board' => 'Период обновления для публичных досок',
'Task highlight period' => 'Время подсвечивания задачи',
- 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Период (в секундах) в течении которого задача считается недавно измененной (0 для выключения, 2 дня по умолчанию)',
+ 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Период (в секундах) в течении которого задача считается недавно изменённой (0 для выключения, 2 дня по умолчанию)',
'Frequency in second (60 seconds by default)' => 'Частота в секундах (60 секунд по умолчанию)',
'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Частота в секундах (0 для выключения, 10 секунд по умолчанию)',
'Application URL' => 'URL приложения',
@@ -442,13 +426,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.' => 'Недостаточно данных, чтобы показать график.',
@@ -465,10 +446,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' => 'Активные дорожки',
@@ -494,7 +473,6 @@ return array(
'Subtask Id' => 'Id подзадачи',
'Subtasks' => 'Подзадачи',
'Subtasks Export' => 'Экспортировать подзадачи',
- 'Subtasks exportation for "%s"' => 'Экспорт подзадач для "%s"',
'Task Title' => 'Загловок задачи',
'Untitled' => 'Заголовок отсутствует',
'Application default' => 'Приложение по умолчанию',
@@ -507,13 +485,13 @@ return array(
'All swimlanes' => 'Все дорожки',
'All colors' => 'Все цвета',
'Moved to column %s' => 'Перемещена в колонку %s',
- 'User dashboard' => 'Пользователь панели мониторинга',
+ 'User dashboard' => 'Панель управления',
'Allow only one subtask in progress at the same time for a user' => 'Разрешена только одна подзадача в разработке одновременно для одного пользователя',
'Edit column "%s"' => 'Редактировать колонку "%s"',
'Select the new status of the subtask: "%s"' => 'Выбрать новый статус для подзадачи: "%s"',
'Subtask timesheet' => 'Табель времени подзадач',
'There is nothing to show.' => 'Здесь ничего нет.',
- 'Time Tracking' => 'Учет времени',
+ 'Time Tracking' => 'Учёт времени',
'You already have one subtask in progress' => 'У вас уже есть одна задача в разработке',
'Which parts of the project do you want to duplicate?' => 'Какие части проекта должны быть дублированы?',
'Disallow login form' => 'Запретить форму входа',
@@ -532,16 +510,14 @@ return array(
'Link labels' => 'Метки для ссылки',
'Link modification' => 'Обновление ссылки',
'Links' => 'Ссылки',
- 'Link settings' => 'Настройки ссылки',
'Opposite label' => 'Ярлык напротив',
'Remove a link' => 'Удалить ссылку',
- 'Task\'s links' => 'Ссылки задачи',
'The labels must be different' => 'Ярлыки должны быть разными',
'There is no link.' => 'Это не ссылка',
'This label must be unique' => 'Этот ярлык должен быть уникальным ',
- 'Unable to create your link.' => 'Не удается создать эту ссылку.',
- 'Unable to update your link.' => 'Не удается обновить эту ссылку.',
- 'Unable to remove this link.' => 'Не удается удалить эту ссылку.',
+ 'Unable to create your link.' => 'Не удаётся создать эту ссылку.',
+ 'Unable to update your link.' => 'Не удаётся обновить эту ссылку.',
+ 'Unable to remove this link.' => 'Не удаётся удалить эту ссылку.',
'relates to' => 'относится к',
'blocks' => 'блокирована',
'is blocked by' => 'блокирует',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Компактный вид',
'Horizontal scrolling' => 'Широкий вид',
'Compact/wide view' => 'Компактный/широкий вид',
- 'No results match:' => 'Отсутствуют результаты:',
'Currency' => 'Валюта',
'Private project' => 'Приватный проект',
'AUD - Australian Dollar' => 'AUD - Австралийский доллар',
@@ -579,9 +554,10 @@ return array(
'EUR - Euro' => 'EUR - Евро',
'GBP - British Pound' => 'GBP - Британский фунт',
'INR - Indian Rupee' => 'INR - Индийский рупий',
- 'JPY - Japanese Yen' => 'JPY - Японскай йена',
+ 'JPY - Japanese Yen' => 'JPY - Японская йена',
'NZD - New Zealand Dollar' => 'NZD - Новозеландский доллар',
'RSD - Serbian dinar' => 'RSD - Сербский динар',
+ 'CNY - Chinese Yuan' => 'CNY - Китайский юань',
'USD - US Dollar' => 'USD - доллар США',
'Destination column' => 'Колонка назначения',
'Move the task to another column when assigned to a user' => 'Переместить задачу в другую колонку, когда она назначена пользователю',
@@ -592,16 +568,15 @@ return array(
'Time spent in the column' => 'Время проведенное в колонке',
'Task transitions' => 'Перемещения задач',
'Task transitions export' => 'Экспорт перемещений задач',
- 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Этот отчет содержит все перемещения задач в колонках с датой, пользователем и времени, затраченным для каждого перемещения.',
+ 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Этот отчёт содержит все перемещения задач в колонках с датой, пользователем и времени, затраченным для каждого перемещения.',
'Currency rates' => 'Курсы валют',
'Rate' => 'Курс',
'Change reference currency' => 'Изменить справочник валют',
- 'Add a new currency rate' => 'Добавить новый валютный курс',
'Reference currency' => 'Справочник валют',
'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' => 'Проверка кода двухфакторной авторизации',
@@ -615,13 +590,12 @@ 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',
+ 'This chart show the task complexity over the time (Work Remaining).' => 'Эта диаграмма показывает сложность задачи по времени (оставшейся работы).',
+ 'Screenshot taken %s' => 'Скриншот сделан %s',
'Add a screenshot' => 'Прикрепить картинку',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Сделайте скриншот и нажмите CTRL+V или ⌘+V для вложения',
- 'Screenshot uploaded successfully.' => 'Скриншет успешно загружен',
+ 'Screenshot uploaded successfully.' => 'Скриншот успешно загружен',
'SEK - Swedish Krona' => 'SEK - Шведская крона',
'Identifier' => 'Идентификатор',
'Disable two factor authentication' => 'Выключить двухфакторную авторизацию',
@@ -633,25 +607,25 @@ return array(
'Recurrent task is scheduled to be generated' => 'Периодическая задача запланирована к созданию',
'Score' => 'Оценка',
'The identifier must be unique' => 'Идентификатор должен быть уникальным',
- 'This linked task id doesn\'t exists' => 'Этот ID звязанной задачи не существует',
+ 'This linked task id doesn\'t exists' => 'Этот ID связанной задачи не существует',
'This value must be alphanumeric' => 'Это значение должно быть буквенно-цифровым',
'Edit recurrence' => 'Редактировать повторы',
'Generate recurrent task' => 'Создать повторяющуюся задачу',
'Trigger to generate recurrent task' => 'Триггер для генерации периодической задачи',
- 'Factor to calculate new due date' => 'Коэффициент для рассчета новой даты',
- 'Timeframe to calculate new due date' => 'Вычисление для рассчета новой даты',
+ 'Factor to calculate new due date' => 'Коэффициент для расчёта новой даты',
+ 'Timeframe to calculate new due date' => 'Вычисление для расчёта новой даты',
'Base date to calculate new due date' => 'Базовая дата вычисления новой даты',
'Action date' => 'Дата действия',
'Base date to calculate new due date: ' => 'Базовая дата вычисления новой даты: ',
'This task has created this child task: ' => 'Эта задача создала эту дочернюю задачу:',
'Day(s)' => 'День(й)',
'Existing due date' => 'Существующий срок',
- 'Factor to calculate new due date: ' => 'Коэффициент для рассчета новой даты: ',
+ 'Factor to calculate new due date: ' => 'Коэффициент для расчёта новой даты: ',
'Month(s)' => 'Месяц(а)',
'Recurrence' => 'Повторение',
'This task has been created by: ' => 'Эта задача была создана: ',
'Recurrent task has been generated:' => 'Периодическая задача была сформирована:',
- 'Timeframe to calculate new due date: ' => 'Вычисление для рассчета новой даты: ',
+ 'Timeframe to calculate new due date: ' => 'Вычисление для расчёта новой даты: ',
'Trigger to generate recurrent task: ' => 'Триггер для генерации периодической задачи: ',
'When task is closed' => 'Когда задача закрывается',
'When task is moved from first column' => 'Когда задача перемещается из первой колонки',
@@ -671,7 +645,7 @@ return array(
'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' => 'Пользователь, который будет получать e-mail',
'Email subject' => 'Тема e-mail',
@@ -680,20 +654,14 @@ 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 на первую дорожку',
'%s moved the task %s to the swimlane "%s"' => '%s переместил задачу %s на дорожку "%s"',
- 'This report contains all subtasks information for the given date range.' => 'Этот отчет содержит всю информацию подзадач в заданном диапазоне дат.',
- 'This report contains all tasks information for the given date range.' => 'Этот отчет содержит всю информацию для задачи в заданном диапазоне дат.',
+ 'This report contains all subtasks information for the given date range.' => 'Этот отчёт содержит всю информацию подзадач в заданном диапазоне дат.',
+ 'This report contains all tasks information for the given date range.' => 'Этот отчёт содержит всю информацию для задачи в заданном диапазоне дат.',
'Project activities for %s' => 'Активность проекта для %s',
'view the board on Kanboard' => 'посмотреть доску на Kanboard',
'The task have been moved to the first swimlane' => 'Эта задача была перемещена в первую дорожку',
@@ -709,8 +677,8 @@ return array(
'There is no description anymore' => 'Здесь больше нет описания',
'Recurrence settings have been modified' => 'Настройки повтора были изменены',
'Time spent changed: %sh' => 'Изменение количества затраченного времени: %sh',
- 'Time estimated changed: %sh' => 'Ожидаемый срок изменен: %sh',
- 'The field "%s" have been updated' => 'Поле "%s" ,было изменено',
+ 'Time estimated changed: %sh' => 'Ожидаемый срок изменён: %sh',
+ '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:' => 'Я хочу получать уведомления для:',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30м',
'Stop timer' => 'Остановить таймер',
'Start timer' => 'Запустить таймер',
- 'Add project member' => 'Добавить номер проекта',
'My activity stream' => 'Лента моей активности',
'My calendar' => 'Мой календарь',
'Search tasks' => 'Поиск задачи',
@@ -758,8 +725,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.',
@@ -774,16 +739,14 @@ return array(
'Lead time: ' => 'Время выполнения:',
'Cycle time: ' => 'Время цикла:',
'Time spent into each column' => 'Время, проведенное в каждой колонке',
- 'The lead time is the duration between the task creation and the completion.' => 'Время выполнения - период между созданием задачи и завершения.',
+ 'The lead time is the duration between the task creation and the completion.' => 'Время выполнения - период между созданием задачи и её завершением.',
'The cycle time is the duration between the start date and the completion.' => 'Время цикла - период времени между датой начала и завершения.',
'If the task is not closed the current time is used instead of the completion date.' => 'Если задача не закрыта, то текущая дата будет указана в дате завершения задачи.',
'Set automatically the start date' => 'Установить автоматическую дату начала',
'Edit Authentication' => 'Редактировать авторизацию',
'Remote user' => 'Удаленный пользователь',
- 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Учетные данные для входа через LDAP, Google и Github не будут сохранены в Kanboard.',
- 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Если вы установите флажок "Запретить форму входа", учетные данные, введенные в форму входа будет игнорироваться.',
- 'New remote user' => 'Новый удаленный пользователь',
- 'New local user' => 'Новый локальный пользователь',
+ 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Учётные данные для входа через LDAP, Google и Github не будут сохранены в Kanboard.',
+ 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Если вы установите флажок "Запретить форму входа", учётные данные, введенные в форму входа будет игнорироваться.',
'Default task color' => 'Стандартные цвета задач',
'This feature does not work with all browsers.' => 'Эта функция доступна не во всех браузерах.',
'There is no destination project available.' => 'Нет доступного для назначения проекта.',
@@ -800,14 +763,13 @@ return array(
'License:' => 'Лицензия:',
'License' => 'Лицензия',
'Enter the text below' => 'Введите текст ниже',
- 'Gantt chart for %s' => 'Диаграмма Ганта для %s',
'Sort by position' => 'Сортировать по позиции',
'Sort by date' => 'Сортировать по дате',
'Add task' => 'Добавить задачу',
'Start date:' => 'Дата начала:',
'Due date:' => 'Дата завершения:',
'There is no start date or due date for this task.' => 'Для этой задачи нет даты начала или завершения.',
- 'Moving or resizing a task will change the start and due date of the task.' => 'Изменение или перемещение задачи повлечет изменение даты начала завершения задачи.',
+ 'Moving or resizing a task will change the start and due date of the task.' => 'Изменение или перемещение задачи повлечет изменение даты начала и завершения задачи.',
'There is no task in your project.' => 'В Вашем проекте задач нет.',
'Gantt chart' => 'Диаграмма Ганта',
'People who are project managers' => 'Люди, которые менеджеры проекта',
@@ -841,8 +803,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.' => 'Фильтр был успешно создан.',
@@ -865,7 +825,7 @@ return array(
'Column changed for task #%d' => 'Обновлена колонка у задачи #%d',
'New position for task #%d' => 'Новая позиция для задачи #%d',
'Swimlane changed for task #%d' => 'Изменена дорожка у задачи #%d',
- 'Assignee changed on task #%d' => 'Изменен назначенный у задачи #%d',
+ 'Assignee changed on task #%d' => 'Изменён назначенный у задачи #%d',
'%d overdue tasks' => '%d просроченных задач',
'Task #%d is overdue' => 'Задача #%d просрочена',
'No new notifications.' => 'Нет новых уведомлений.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Владелец',
'Unread notifications' => 'Непрочитанные уведомления',
'Notification methods:' => 'Способы уведомления:',
- 'Import tasks from CSV file' => 'Импорт задач из CSV-файла',
'Unable to read your file' => 'Невозможно прочитать файл',
'%d task(s) have been imported successfully.' => '%d задач было успешно импортировано.',
'Nothing have been imported!' => 'Ничего не было импортировано!',
@@ -917,7 +876,7 @@ return array(
'Passwords will be encrypted if present' => 'Пароли будут зашифрованы (если указаны)',
'%s attached a new file to the task %s' => '%s добавил новый файл к задаче %s',
'Link type' => 'Тип ссылки',
- 'Assign automatically a category based on a link' => 'Автоматически назначать категории на основе ссылки',
+ 'Assign automatically a category based on a link' => 'Автоматически назначить категории на основе ссылки',
'BAM - Konvertible Mark' => 'BAM - Конвертируемая марка',
'Assignee Username' => 'Логин назначенного',
'Assignee Name' => 'Имя назначенного',
@@ -947,7 +906,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.' => 'Нет созданных групп.',
@@ -960,7 +918,7 @@ return array(
'Allowed Users' => 'Разрешенные пользователи',
'No user have been allowed specifically.' => 'Нет заданных разрешений для пользователей.',
'Role' => 'Роль',
- 'Enter user name...' => 'Введите имя пользователя...',
+ 'Enter user name...' => 'Введите логин пользователя...',
'Allowed Groups' => 'Разрешенные группы',
'No group have been allowed specifically.' => 'Нет заданных разрешений для групп.',
'Group' => 'Группа',
@@ -968,13 +926,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' => 'Затрачено часов',
@@ -982,13 +937,13 @@ return array(
'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' => 'Назначить задачу пользователю, который произвел изменение в колонке',
+ 'RUB - Russian Ruble' => 'РУБ - Российский рубль',
+ 'Assign the task to the person who does the action when the column is changed' => 'Назначить задачу пользователю, который произвёл изменение в колонке',
'Close a task in a specific column' => 'Закрыть задачу в выбранной колонке',
'Time-based One-time Password Algorithm' => 'Зависимый от времени, одноразовый алгоритм пароля',
- 'Two-Factor Provider: ' => 'Провайдер двух-факторной авторизации: ',
- 'Disable two-factor authentication' => 'Отключить двух-факторную авторизацию',
- 'Enable two-factor authentication' => 'Включить двух-факторную авторизацию',
+ 'Two-Factor Provider: ' => 'Провайдер двухфакторной авторизации: ',
+ 'Disable two-factor authentication' => 'Отключить двухфакторную авторизацию',
+ 'Enable two-factor authentication' => 'Включить двухфакторную авторизацию',
'There is no integration registered at the moment.' => 'Интеграции в данный момент не зарегистрированы.',
'Password Reset for Kanboard' => 'Сброс пароля для Kanboard',
'Forgot password?' => 'Забыли пароль?',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'Владелец проекта:',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Идентификатор проекта не обязателен и должен содержать буквенно-цифровые символы, пример: MYPROJECT',
'Project owner' => 'Владелец проекта',
- 'Those dates are useful for the project Gantt chart.' => 'Эти даты используются для диаграммы Ганта проекта.',
'Private projects do not have users and groups management.' => 'Приватные проекты не имеют управления пользователями и группами.',
'There is no project member.' => 'Нет участников проекта.',
'Priority' => 'Приоритет',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'Начата:',
'Moved:' => 'Перемещена:',
'Task #%d' => 'Задача #%d',
- 'Date and time format' => 'Формат даты и времени',
'Time format' => 'Формат времени',
'Start date: ' => 'Дата начала:',
'End date: ' => 'Дата окончания:',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => 'Пользователь был успешно выключен.',
'Unable to disable this user.' => 'Не удалось выключить пользователя.',
'All files have been uploaded successfully.' => 'Все файлы были успешно загружены.',
- 'View uploaded files' => 'Просмотр загруженных файлов',
'The maximum allowed file size is %sB.' => 'Максимально допустимый размер файла: %sB.',
- 'Choose files again' => 'Выбрать файлы повторно',
'Drag and drop your files here' => 'Переместите ваши файлы сюда',
'choose files' => 'выбор файлов',
'View profile' => 'Просмотр профиля',
@@ -1194,16 +1145,15 @@ return array(
'Email settings' => 'Настройки почты',
'Email sender address' => 'Адрес отправителя',
'Email transport' => 'Почтовый транспорт',
- // 'Webhook token' => '',
- // 'Imports' => '',
+ 'Webhook token' => 'Webhook токены',
'Project tags management' => 'Управление метками проекта',
'Tag created successfully.' => 'Метка успешно создана.',
'Unable to create this tag.' => 'Невозможно создать эту метку.',
- 'Tag updated successfully.' => 'Метак успешно обновлена.',
+ 'Tag updated successfully.' => 'Метка успешно обновлена.',
'Unable to update this tag.' => 'Невозможно обновить эту метку.',
'Tag removed successfully.' => 'Метка успешно удалена.',
'Unable to remove this tag.' => 'Невозможно удалить эту метку.',
- 'Global tags management' => 'Управление глоабльными метками',
+ 'Global tags management' => 'Управление глобальными метками',
'Tags' => 'Метки',
'Tags management' => 'Управление метками',
'Add new tag' => 'Добавить новую метку',
@@ -1216,5 +1166,145 @@ return array(
'Global tags' => 'Глобальные метка',
'There is no global tag at the moment.' => 'Нет глобальных меток.',
'This field cannot be empty' => 'Это поле не может быть пустым',
- 'Hide tasks in this column in the Dashboard' => 'Не показывать задачи из этой колонки в кабинете',
+ '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',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Назначить цвет, когда задача будет перемещена в указанную дорожку',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Назначить приоритет, когда задача будет перемещена в указанную дорожку',
+ 'User unlocked successfully.' => 'Пользователь успешно разблокирован.',
+ 'Unable to unlock the user.' => 'Не удаётся разблокировать пользователя.',
+ 'Move a task to another swimlane' => 'Переместить задачу в другую дорожку',
+ 'Creator Name' => 'Создатель',
+ 'Time spent and estimated' => 'Предполагаемое и затраченное время',
+ 'Move position' => 'Переместить позицию',
+ 'Move task to another position on the board' => 'Переместить задачу на другую позицию на доске',
+ 'Insert before this task' => 'Вставить перед этой задачей',
+ 'Insert after this task' => 'Вставить после этой задачи',
+ 'Unlock this user' => 'Разблокировать пользователя',
+ 'Custom Project Roles' => 'Пользовательские проектные роли',
+ 'Add a new custom role' => 'Добавить новую роль',
+ 'Restrictions for the role "%s"' => 'Ограничения для роли "%s"',
+ 'Add a new project restriction' => 'Добавить ограничение на проект',
+ 'Add a new drag and drop restriction' => 'Добавить ограничение на перемещение',
+ 'Add a new column restriction' => 'Добавить ограничение на колонку',
+ 'Edit this role' => 'Изменить роль',
+ 'Remove this role' => 'Удалить роль',
+ 'There is no restriction for this role.' => 'Для этой роли ограничения не заданы.',
+ 'Only moving task between those columns is permitted' => 'Разрешено перемещение только между этими колонками',
+ 'Close a task in a specific column when not moved during a given period' => 'Закрывать задачу в укзанной колонке, если она не была перемещена в течение указанного периода',
+ 'Edit columns' => 'Изменить колонки',
+ 'The column restriction has been created successfully.' => 'Ограничение на колонку успешно создано.',
+ 'Unable to create this column restriction.' => 'Невозможно создать ограничение на колонку.',
+ 'Column restriction removed successfully.' => 'Ограничение на колонку успешно удалено.',
+ 'Unable to remove this restriction.' => 'Не удаётся удалить ограничение.',
+ 'Your custom project role has been created successfully.' => 'Проектная роль была успешно создана.',
+ 'Unable to create custom project role.' => 'Невозможно создать проектную роль.',
+ 'Your custom project role has been updated successfully.' => 'Проектная роль была успешно обновлена.',
+ 'Unable to update custom project role.' => 'Не удаётся обновить проектную роль.',
+ 'Custom project role removed successfully.' => 'Проектная роль была успешно удалена.',
+ 'Unable to remove this project role.' => 'Не удаётся удалить проектную роль.',
+ 'The project restriction has been created successfully.' => 'Ограничение на проект было успешно создано.',
+ 'Unable to create this project restriction.' => 'Не удаётся создать ограничение на проект.',
+ 'Project restriction removed successfully.' => 'Ограничение на проект успешно удалено.',
+ 'You cannot create tasks in this column.' => 'Вы не можете создавать задачи в этой колонке.',
+ 'Task creation is permitted for this column' => 'Разрешено создание задач в этой колонке',
+ 'Closing or opening a task is permitted for this column' => 'Разрешено открытие и закрытие задач в этой колонке',
+ 'Task creation is blocked for this column' => 'Заблокировано создание задач в этой колонке',
+ 'Closing or opening a task is blocked for this column' => 'Заблокировано открытие и закрытие задач в этой колонке',
+ 'Task creation is not permitted' => 'Создание задач не разрешено',
+ 'Closing or opening a task is not permitted' => 'Открытие и закрытие задач не разрешено',
+ 'New drag and drop restriction for the role "%s"' => 'Новое ограничение на перемещение для роли "%s"',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Пользователи с этой ролью смогут перемещать задачи только между указанными колонками.',
+ 'Remove a column restriction' => 'Удаление ограничения на колонку',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Вы действительно хотите удалить это ограничение на перемещение: "%s" в "%s"?',
+ 'New column restriction for the role "%s"' => 'Новое ограничение на колонку для для роли "%s"',
+ 'Rule' => 'Правило',
+ 'Do you really want to remove this column restriction?' => 'Вы действительно хотите удалить это ограничение на колонку?',
+ 'Custom roles' => 'Проектные роли',
+ 'New custom project role' => 'Создание пользовательской проектной роли',
+ 'Edit custom project role' => 'Изменение проектной роли',
+ 'Remove a custom role' => 'Удаление проектной роли',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Вы действительно хотите удалить проектную роль "%s"? Все пользователи с этой ролью станут обычными участниками проекта.',
+ 'There is no custom role for this project.' => 'Для этого проекта пользовательские роли не заданы',
+ 'New project restriction for the role "%s"' => 'Новое проектное ограничение для роли "%s"',
+ 'Restriction' => 'Ограничение',
+ 'Remove a project restriction' => 'Удаление ограничения на проект',
+ 'Do you really want to remove this project restriction: "%s"?' => 'Вы действительно хотите удалить ограничение на проект: "%s"?',
+ 'Duplicate to multiple projects' => 'Дублировать в несколько проектов',
+ 'This field is required' => 'Заполните это поле',
+ 'Moving a task is not permitted' => 'Перемещение задачи не разрешено',
+ 'This value must be in the range %d to %d' => 'Значение должно находиться в диапазоне от %d до %d',
+ 'You are not allowed to move this task.' => 'Вам не разрешено перемещать эту задачу.',
+ 'API User Access' => 'Доступ к API',
+ 'Preview' => 'Предпросмотр',
+ 'Write' => 'Редактирование',
+ 'Write your text in Markdown' => 'Добавьте Ваше описание в формате Markdown',
+ 'New External Task: %s' => 'Новая внешняя задача: %s',
+ 'No personal API access token registered.' => 'Персональные токены доступа к API не созданы.',
+ 'Your personal API access token is "%s"' => 'Ваш персональный токен доступа к API: "%s"',
+ 'Remove your token' => 'Удалить токен',
+ 'Generate a new token' => 'Сгенерировать новый токен',
+ 'Showing %d-%d of %d' => 'Показывается %d-%d из %d',
+ 'Outgoing Emails' => 'Исходящие e-mail',
+ 'Add or change currency rate' => 'Добавить или изменить курс валют',
+ 'Reference currency: %s' => 'Базовая валюта: %s',
+ 'Add custom filters' => 'Добавить пользовательские фильтры',
+ 'Export' => 'Экспорт',
+ 'Add link label' => 'Добавить связь в задаче',
+ 'Incompatible Plugins' => 'Несовместимые плагины',
+ 'Compatibility' => 'Совместимость',
+ 'Permissions and ownership' => 'Разрешения и владение проектом',
+ 'Priorities' => 'Приоритеты',
+ 'Close this window' => 'Закрыть окно',
+ 'Unable to upload this file.' => 'Не удаётся загрузить файл.',
+ 'Import tasks' => 'Импорт задач',
+ 'Choose a project' => 'Выберите проект',
+ 'Profile' => 'Профиль',
+ 'Application role' => 'Роль в приложении',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ 'There is no custom filter.' => 'Пользовательские фильтры отсутствуют.',
+ 'New User' => 'Добавление пользователя',
+ 'Authentication' => 'Данные входа',
+ 'If checked, this user will use a third-party system for authentication.' => 'Если включено, то пользователь будет использовать стороннюю систему для авторизации.',
+ 'The password is necessary only for local users.' => 'Пароль необходим только для локальных пользователей',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ 'Invite people' => 'Приглашение пользователей',
+ 'Emails' => 'Адреса e-mail',
+ 'Enter one email address by line.' => 'Вводите по одному e-mail на строку.',
+ 'Add these people to this project' => 'Добавить приглашенных в проект',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ 'New user' => 'Добавить пользователя',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php
index 7aec7142..bee83a94 100644
--- a/app/Locale/sr_Latn_RS/translations.php
+++ b/app/Locale/sr_Latn_RS/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d zadataka na tabli',
'%d tasks in total' => '%d zadataka ukupno',
'Unable to update this board.' => 'Nemogu da ažuriram ovu tablu.',
- 'Edit board' => 'Izmeni tablu',
'Disable' => 'Onemogući',
'Enable' => 'Omogući',
'New project' => 'Novi projekat',
'Do you really want to remove this project: "%s"?' => 'Da li želiš da ukloniš projekat: "%s"?',
'Remove project' => 'Ukloni projekat',
'Edit the board for "%s"' => 'Izmeni tablu za "%s"',
- 'All projects' => 'Svi projekti',
'Add a new column' => 'Dodaj novu kolonu',
'Title' => 'Naslov',
'Assigned to %s' => 'Dodeljen korisniku %s',
'Remove a column' => 'Ukloni kolonu',
- 'Remove a column from a board' => 'Ukloni kolonu sa table',
'Unable to remove this column.' => 'Nemoguće uklanjanje kolone.',
'Do you really want to remove this column: "%s"?' => 'Da li zaista želiš da ukoniš ovu kolonu: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Ova akcija BRIŠE SVE ZADATKE vezane za ovu kolonu!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(komanda VACUUM)',
'(Gzip compressed Sqlite file)' => '(Sqlite baza spakovana Gzip-om)',
'Close a task' => 'Zatvori zadatak',
- 'Edit a task' => 'Izmeni zadatak',
'Column' => 'Kolona',
'Color' => 'Boja',
'Assignee' => 'Dodeli',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Broj zadataka',
'User' => 'Korisnik',
'Comments' => 'Komentari',
- 'Leave a comment' => 'Ostavi komentar',
'Comment is required' => 'Komentar je obavezan',
- 'Leave a description' => 'Dodaj opis',
'Comment added successfully.' => 'Komentar uspešno ostavljen',
'Unable to create your comment.' => 'Nemoguće kreiranje komentara',
'Due Date' => 'Termin',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Traži',
'Nothing found.' => 'Ništa nije pronađeno',
'Due date' => 'Termin',
- 'Others formats accepted: %s and %s' => 'Ostali formati: %s i %s',
'Description' => 'Opis',
'%d comments' => '%d Komentara',
'%d comment' => '%d Komentar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Identifikator Zadatka',
'Creator' => 'Autor',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Udaljno',
'Enabled' => 'Omogući',
'Disabled' => 'Onemogući',
- 'Username:' => 'Korisničko ime:',
- 'Name:' => 'Ime i Prezime',
+ 'Login:' => 'Korisničko ime:',
+ 'Full Name:' => 'Ime i Prezime',
'Email:' => 'Email: ',
'Notifications:' => 'Obaveštenja: ',
'Notifications' => 'Obaveštenja',
@@ -386,14 +372,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',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
// 'Link labels' => '',
// 'Link modification' => '',
// 'Links' => '',
- // 'Link settings' => '',
// 'Opposite label' => '',
// 'Remove a link' => '',
- // 'Task\'s links' => '',
// 'The labels must be different' => '',
// 'There is no link.' => '',
// 'This label must be unique' => '',
@@ -568,7 +544,6 @@ return array(
// 'Compact view' => '',
// 'Horizontal scrolling' => '',
// 'Compact/wide view' => '',
- // 'No results match:' => '',
// 'Currency' => '',
// 'Private project' => '',
// 'AUD - Australian Dollar' => '',
@@ -582,6 +557,7 @@ return array(
// 'JPY - Japanese Yen' => '',
// 'NZD - New Zealand Dollar' => '',
// 'RSD - Serbian dinar' => '',
+ // 'CNY - Chinese Yuan' => '',
// 'USD - US Dollar' => '',
// 'Destination column' => '',
// 'Move the task to another column when assigned to a user' => '',
@@ -596,12 +572,11 @@ return array(
// 'Currency rates' => '',
// 'Rate' => '',
// 'Change reference currency' => '',
- // 'Add a new currency rate' => '',
// 'Reference currency' => '',
// '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' => '',
@@ -615,7 +590,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' => '',
@@ -680,14 +654,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' => '',
@@ -725,7 +693,6 @@ return array(
// '<30m' => '',
// 'Stop timer' => '',
// 'Start timer' => '',
- // 'Add project member' => '',
// 'My activity stream' => '',
// 'My calendar' => '',
// 'Search tasks' => '',
@@ -758,8 +725,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.' => '',
@@ -782,8 +747,6 @@ return array(
// 'Remote user' => '',
// 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '',
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
- // 'New remote user' => '',
- // 'New local user' => '',
// 'Default task color' => '',
// 'This feature does not work with all browsers.' => '',
// 'There is no destination project available.' => '',
@@ -800,7 +763,6 @@ return array(
// 'License:' => '',
// 'License' => '',
// 'Enter the text below' => '',
- // 'Gantt chart for %s' => '',
// 'Sort by position' => '',
// 'Sort by date' => '',
// 'Add task' => '',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index bce1ccfb..ec6f75e1 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d uppgifter på tavlan',
'%d tasks in total' => '%d uppgifter totalt',
'Unable to update this board.' => 'Kunde inte uppdatera tavlan',
- 'Edit board' => 'Ändra tavlan',
'Disable' => 'Inaktivera',
'Enable' => 'Aktivera',
'New project' => 'Nytt projekt',
'Do you really want to remove this project: "%s"?' => 'Vill du verkligen ta bort projektet: "%s" ?',
'Remove project' => 'Ta bort projekt',
'Edit the board for "%s"' => 'Ändra tavlan för "%s"',
- 'All projects' => 'Alla projekt',
'Add a new column' => 'Lägg till ny kolumn',
'Title' => 'Titel',
'Assigned to %s' => 'Tilldelad %s',
'Remove a column' => 'Ta bort en kolumn',
- 'Remove a column from a board' => 'Ta bort en kolumn från tavlan',
'Unable to remove this column.' => 'Kunde inte ta bort kolumnen.',
'Do you really want to remove this column: "%s"?' => 'Vill du verkligen ta bort kolumnen: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Denna åtgärd kommer att TA BORT ALLA uppgifter kopplade till kolumnen!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(Vacuum kommando)',
'(Gzip compressed Sqlite file)' => '(Gzip komprimera Sqlite filen)',
'Close a task' => 'Stäng en uppgift',
- 'Edit a task' => 'Ändra en uppgift',
'Column' => 'Kolumn',
'Color' => 'Färg',
'Assignee' => 'Uppdragsinnehavare',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Antal uppgifter',
'User' => 'Användare',
'Comments' => 'Kommentarer',
- 'Leave a comment' => 'Lämna en kommentar',
'Comment is required' => 'En kommentar måste lämnas',
- 'Leave a description' => 'Lämna en beskrivning',
'Comment added successfully.' => 'Kommentaren har lagts till.',
'Unable to create your comment.' => 'Kommentaren kunde inte laddas upp.',
'Due Date' => 'Måldatum',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Sök',
'Nothing found.' => 'Inget kunde hittas.',
'Due date' => 'Måldatum',
- 'Others formats accepted: %s and %s' => 'Andra format som accepteras: %s and %s',
'Description' => 'Beskrivning',
'%d comments' => '%d kommentarer',
'%d comment' => '%d kommentar',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Uppgift ID',
'Creator' => 'Skapare',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Fjärr',
'Enabled' => 'Aktiverad',
'Disabled' => 'Inaktiverad',
- 'Username:' => 'Användarnam:',
- 'Name:' => 'Namn:',
+ 'Login:' => 'Användarnam:',
+ 'Full Name:' => 'Namn:',
'Email:' => 'E-post:',
'Notifications:' => 'Notiser:',
'Notifications' => 'Notiser',
@@ -386,14 +372,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',
@@ -442,13 +426,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',
@@ -465,10 +446,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',
@@ -494,7 +473,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',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Länketiketter',
'Link modification' => 'Länkändring',
'Links' => 'Länkar',
- 'Link settings' => 'Länkinställningar',
'Opposite label' => 'Motpartslänk',
'Remove a link' => 'Ta bort en länk',
- 'Task\'s links' => 'Uppgiftslänkar',
'The labels must be different' => 'Etiketterna måste vara olika',
'There is no link.' => 'Det finns ingen länk',
'This label must be unique' => 'Länken måste vara unik',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'Kompakt vy',
'Horizontal scrolling' => 'Horisontell scroll',
'Compact/wide view' => 'Kompakt/bred vy',
- 'No results match:' => 'Inga matchande resultat',
'Currency' => 'Valuta',
'Private project' => 'Privat projekt',
'AUD - Australian Dollar' => 'AUD - Australiska dollar',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - Japanska Yen',
'NZD - New Zealand Dollar' => 'NZD - Nya Zeeländska Dollar',
'RSD - Serbian dinar' => 'RSD - Serbiska Dinarer',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - Amerikanska Dollar',
'Destination column' => 'Målkolumn',
'Move the task to another column when assigned to a user' => 'Flytta uppgiften till en annan kolumn när den tilldelats en användare',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'Valutakurser',
'Rate' => 'Kurs',
'Change reference currency' => 'Ändra referenskurs',
- 'Add a new currency rate' => 'Lägg till ny valutakurs',
'Reference currency' => 'Referensvaluta',
'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',
@@ -615,7 +590,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',
@@ -680,14 +654,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',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30m',
'Stop timer' => 'Stoppa timer',
'Start timer' => 'Starta timer',
- 'Add project member' => 'Lägg till projektmedlem',
'My activity stream' => 'Min aktivitetsström',
'My calendar' => 'Min kalender',
'Search tasks' => 'Sök uppgifter',
@@ -758,8 +725,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.',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Extern användare',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Externa användares lösenord lagras inte i Kanboard-databasen, exempel: LDAP, Google och Github-konton.',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Om du aktiverar boxen "Tillåt inte loginformulär" kommer inloggningsuppgifter i formuläret att ignoreras.',
- 'New remote user' => 'Ny extern användare',
- 'New local user' => 'Ny lokal användare',
'Default task color' => 'Standardfärg för uppgifter',
'This feature does not work with all browsers.' => 'Denna funktion fungerar inte i alla webbläsare.',
'There is no destination project available.' => 'Det finns inget destinationsprojekt tillgängligt.',
@@ -800,7 +763,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',
@@ -841,8 +803,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.' => '',
@@ -880,7 +840,6 @@ return array(
// '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!' => '',
@@ -947,7 +906,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.' => '',
@@ -968,13 +926,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' => '',
@@ -1012,7 +967,6 @@ return array(
// 'Project owner: ' => '',
// 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
// 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
// 'Private projects do not have users and groups management.' => '',
// 'There is no project member.' => '',
// 'Priority' => '',
@@ -1069,7 +1023,6 @@ return array(
// 'Started:' => '',
// 'Moved:' => '',
// 'Task #%d' => '',
- // 'Date and time format' => '',
// 'Time format' => '',
// 'Start date: ' => '',
// 'End date: ' => '',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 48a0b3de..dcd858d0 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -42,11 +42,11 @@ return array(
'Sign in' => 'เข้าสู่ระบบ',
'Users' => 'ผู้ใช้',
'No user' => 'ไม่มีผู้ใช้',
- 'Forbidden' => 'ไม่อนุญาติ',
- 'Access Forbidden' => 'ไม่อนุญาติให้เข้า',
+ 'Forbidden' => 'ไม่อนุญาต',
+ 'Access Forbidden' => 'ไม่อนุญาตให้เข้า',
'Edit user' => 'แก้ไขผู้ใช้',
'Logout' => 'ออกจากระบบ',
- 'Bad username or password' => 'ชื่อผู้ใช่หรือรหัสผ่านผิด',
+ 'Bad username or password' => 'ชื่อผู้ใช้หรือรหัสผ่านผิด',
'Edit project' => 'แก้ไขโปรเจค',
'Name' => 'ชื่อ',
'Projects' => 'โปรเจค',
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '%d งานบนบอร์ด',
'%d tasks in total' => '%d งานทั้งหมด',
'Unable to update this board.' => 'ไม่สามารถปรับปรุงบอร์ดได้.',
- 'Edit board' => 'แก้ไขบอร์ด',
'Disable' => 'ปิดการทำงาน',
'Enable' => 'เปิดการทำงาน',
'New project' => 'โปรเจคใหม่',
'Do you really want to remove this project: "%s"?' => 'คุณต้องการเอาโปรเจค « %s » ออกใช่หรือไม่?',
'Remove project' => 'ลบโปรเจค',
'Edit the board for "%s"' => 'แก้ไขบอร์ดสำหรับ « %s »',
- 'All projects' => 'โปรเจคทั้งหมด',
'Add a new column' => 'เพิ่มคอลัมน์ใหม่',
'Title' => 'หัวเรื่อง',
'Assigned to %s' => 'กำหนดให้ %s',
'Remove a column' => 'ลบคอลัมน์',
- 'Remove a column from a board' => 'ลบคอลัมน์ออกจากบอร์ด',
'Unable to remove this column.' => 'ไม่สามารถลบคอลัมน์นี้',
'Do you really want to remove this column: "%s"?' => 'คุณต้องการลบคอลัมน์ « %s » ออกใช่หรือไม่?',
'This action will REMOVE ALL TASKS associated to this column!' => 'การกระทำนี้จะลบงานที่เกี่ยวข้องกับคอลัมน์นี้',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM command)',
'(Gzip compressed Sqlite file)' => '(Gzip compressed Sqlite file)',
'Close a task' => 'ปิดงาน',
- 'Edit a task' => 'แก้ไขงาน',
'Column' => 'คอลัมน์',
'Color' => 'สี',
'Assignee' => 'กำหนดให้',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'นับงาน',
'User' => 'ผู้ใช้',
'Comments' => 'ความคิดเห็น',
- 'Leave a comment' => 'ออกความคิดเห็น',
'Comment is required' => 'ต้องการความคิดเห็น',
- 'Leave a description' => 'แสดงคำอธิบาย',
'Comment added successfully.' => 'เพิ่มความคิดเห็นเรียบร้อยแล้ว',
'Unable to create your comment.' => 'ไม่สามารถสร้างความคิดเห็น',
'Due Date' => 'วันที่ครบกำหนด',
@@ -213,7 +207,7 @@ return array(
'Last logins' => 'เข้าใช้ล่าสุด',
'Login date' => 'วันที่เข้าใข้',
'Authentication method' => 'วิธีการยืนยันตัวตน',
- 'IP address' => 'ไอพี แอดเดรส',
+ 'IP address' => 'ที่อยู่ไอพี',
'User agent' => 'User agent',
'Persistent connections' => 'Persistent connections',
'No session.' => 'No session.',
@@ -226,39 +220,38 @@ return array(
'Search' => 'ค้นหา',
'Nothing found.' => 'ค้นหาไม่พบ.',
'Due date' => 'วันที่ครบกำหนด',
- 'Others formats accepted: %s and %s' => 'รูปแบบอื่นที่ได้รับการยอมรับ: %s และ %s',
'Description' => 'คำอธิบาย',
'%d comments' => '%d ความคิดเห็น',
'%d comment' => '%d ความคิดเห็น',
- 'Email address invalid' => 'อีเมลผิด',
- 'Your external account is not linked anymore to your profile.' => 'บัญชีภายนอกของคุณไม่ได้เชื่อมโยงอีกต่อไปในโปรไฟล์ของคุณ',
+ 'Email address invalid' => 'ที่อยู่อีเมลไม่ถูกต้อง',
+ 'Your external account is not linked anymore to your profile.' => 'บัญชีภายนอกของคุณไม่ได้เชื่อมโยงมายังโปรไฟล์ของคุณอีกต่อ',
'Unable to unlink your external account.' => 'ไม่สามารถยกเลิกการเชื่อมโยงบัญชีภายนอกของคุณ',
'External authentication failed' => 'การตรวจสอบภายนอกล้มเหลว',
- 'Your external account is linked to your profile successfully.' => 'บัญชีภายนอกของคุณลิงค์กับโปรไฟล์ของคุณเรียบร้อย',
+ 'Your external account is linked to your profile successfully.' => 'ทำการเชื่อมโยงบัญชีภายนอกของคุณกับโปรไฟล์ของคุณเรียบร้อย',
'Email' => 'อีเมล',
'Task removed successfully.' => 'ลบงานเรียบร้อยแล้ว',
'Unable to remove this task.' => 'ไม่สามารถลบงานนี้',
- 'Remove a task' => 'ลบงาาน',
+ 'Remove a task' => 'ลบงาน',
'Do you really want to remove this task: "%s"?' => 'คุณต้องการลบงาน "%s" ออกใช่หรือไม่?',
'Assign automatically a color based on a category' => 'กำหนดสีอัตโนมัติขึ้นอยู่กับหมวด',
'Assign automatically a category based on a color' => 'กำหนดหมวดอัตโนมัติขึ้นอยู่กับสี',
'Task creation or modification' => 'สร้างหรือแก้ไขงาน',
- 'Category' => 'หมวด',
- 'Category:' => 'หมวด:',
- 'Categories' => 'หมวด',
- 'Your category have been created successfully.' => 'สร้างหมวดเรียบร้อยแล้ว',
- 'Unable to create your category.' => 'ไม่สามารถสร้างหมวดได้',
+ 'Category' => 'หมวดหมู่',
+ 'Category:' => 'หมวดหมู่:',
+ 'Categories' => 'หมวดหมู่',
+ 'Your category have been created successfully.' => 'สร้างหมวดหมู่เรียบร้อยแล้ว',
+ 'Unable to create your category.' => 'ไม่สามารถสร้างหมวดหมู่ได้',
'Your category have been updated successfully.' => 'ปรับปรุงหมวดเรียบร้อยแล้ว',
- 'Unable to update your category.' => 'ไม่สามารถปรับปรุงหมวดได้',
- 'Remove a category' => 'ลบหมวด',
+ 'Unable to update your category.' => 'ไม่สามารถปรับปรุงหมวดหมู่ได้',
+ 'Remove a category' => 'ลบหมวดหมู่',
'Category removed successfully.' => 'ลบหมวดเรียบร้อยแล้ว',
- 'Unable to remove this category.' => 'ไม่สามารถลบหมวดได้',
- 'Category modification for the project "%s"' => 'แก้ไขหมวดสำหรับโปรเจค "%s"',
- 'Category Name' => 'ชื่อหมวด',
- 'Add a new category' => 'เพิ่มหมวดใหม่',
- 'Do you really want to remove this category: "%s"?' => 'คุณต้องการลบหมวด "%s" ใช่หรือไม่?',
- 'All categories' => 'หมวดทั้งหมด',
- 'No category' => 'ไม่มีหมวด',
+ 'Unable to remove this category.' => 'ไม่สามารถลบหมวดหมู่ได้',
+ 'Category modification for the project "%s"' => 'แก้ไขหมวดหมู่สำหรับโปรเจค "%s"',
+ 'Category Name' => 'ชื่อหมวดหมู่',
+ 'Add a new category' => 'เพิ่มหมวดหมู่ใหม่',
+ 'Do you really want to remove this category: "%s"?' => 'คุณต้องการลบหมวดหมู่ "%s" ใช่หรือไม่?',
+ 'All categories' => 'หมวดหมู่ทั้งหมด',
+ 'No category' => 'ไม่มีหมวดหมู่',
'The name is required' => 'ต้องการชื่อ',
'Remove a file' => 'ลบไฟล์',
'Unable to remove this file.' => 'ไม่สามารถลบไฟล์ได้',
@@ -294,14 +287,12 @@ return array(
'Unable to update your sub-task.' => 'ไม่สามารถปรับปรุงานย่อยได้',
'Unable to create your sub-task.' => 'ไม่สามารถสร้างงานย่อยได้',
'Sub-task added successfully.' => 'เพิ่มงานย่อยเรียบร้อยแล้ว',
- 'Maximum size: ' => 'ขนาดสูงสุด:',
+ 'Maximum size: ' => 'ขนาดไฟล์สูงสุด:',
'Unable to upload the file.' => 'ไม่สามารถอัพโหลดไฟล์ได้',
'Display another project' => 'แสดงโปรเจคอื่น',
'Created by %s' => 'สร้างโดย %s',
'Tasks Export' => 'ส่งออกงาน',
- 'Tasks exportation for "%s"' => 'ส่งออกงานสำหรับ "%s"',
'Start Date' => 'เริ่มวันที่',
- 'End Date' => 'สิ้นสุดวันที่',
'Execute' => 'ประมวลผล',
'Task Id' => 'งาน ไอดี',
'Creator' => 'ผู้สร้าง',
@@ -322,14 +313,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' => 'การเข้าถึงสาธารณะ',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'รีโมท',
'Enabled' => 'เปิดการใช้',
'Disabled' => 'ปิดการใช้',
- 'Username:' => 'ชื่อผู้ใช้:',
- 'Name:' => 'ชื่อ:',
+ 'Login:' => 'ชื่อผู้ใช้:',
+ 'Full Name:' => 'ชื่อ:',
'Email:' => 'อีเมล:',
'Notifications:' => 'แจ้งเตือน:',
'Notifications' => 'การแจ้งเตือน',
@@ -361,40 +347,38 @@ return array(
'Password modification' => 'แก้ไขรหัสผ่าน',
'External authentications' => 'การยืนยันภายนอก',
'Never connected.' => 'ไม่เชื่อมต่อ',
- 'No external authentication enabled.' => 'ไม่เปิดการใช้งานการยืนยันภายนอก',
- 'Password modified successfully.' => 'แก้ไขรหัสผ่านเรียบร้อยแล้ว',
+ 'No external authentication enabled.' => 'ปิดการใช้งานการยืนยันภายนอก',
+ 'Password modified successfully.' => 'แก้ไขรหัสผ่านเรียบร้อย',
'Unable to change the password.' => 'ไม่สามารถเปลี่ยนรหัสผ่านได้',
- 'Change category' => 'เปลี่ยนหมวด',
- '%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"',
- '%s created the task %s' => '%s สร้างงานแล้ว %s',
- '%s closed the task %s' => '%s ปิดงานแล้ว %s',
- '%s created a subtask for the task %s' => '%s สร้างงานย่อยสำหรับงานแล้ว %s',
- '%s updated a subtask for the task %s' => '%s ปรับปรุงงานย่อยสำหรับงานแล้ว %s',
- 'Assigned to %s with an estimate of %s/%sh' => 'กำหนดให้ %s โดยประมาณแล้ว %s/%sh',
- 'Not assigned, estimate of %sh' => 'ไม่กำหนดแล้ว, ประมาณเวลาที่ใช้ %s ชั่วโมง',
- '%s updated a comment on the task %s' => '%s ปรับปรุงความคิดเห็นในงานแล้ว %s',
- '%s commented the task %s' => '%s แสดงความคิดเห็นของงานแล้ว %s',
+ 'Change category' => 'เปลี่ยนหมวดหมู่',
+ '%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"',
+ '%s created the task %s' => '%s ได้สร้างงาน %s',
+ '%s closed the task %s' => '%s ได้ปิดงาน %s',
+ '%s created a subtask for the task %s' => '%s ได้สร้างงานย่อยสำหรับงาน %s',
+ '%s updated a subtask for the task %s' => '%s ได้ปรับปรุงงานย่อยสำหรับงาน %s',
+ 'Assigned to %s with an estimate of %s/%sh' => 'มอบหมายให้ %s โดยประมาณเวลาที่ใช้ %s/%sh',
+ 'Not assigned, estimate of %sh' => 'ไม่ระบุผู้รับผิดชอบ, ประมาณเวลาที่ใช้ %s ชั่วโมง',
+ '%s updated a comment on the task %s' => '%s ได้ปรับปรุงความคิดเห็นในงาน %s',
+ '%s commented the task %s' => '%s ได้แสดงความคิดเห็นในงาน %s',
'%s\'s activity' => 'กิจกรรม %s',
'RSS feed' => 'RSS feed',
- '%s updated a comment on the task #%d' => '%s ปรับปรุงความคิดเห็นบนงานแล้ว #%d',
- '%s commented on the task #%d' => '%s แสดงความคิดเห็นบนงานแล้ว #%d',
- '%s updated a subtask for the task #%d' => '%s ปรับปรุงงานย่อยสำหรับงานแล้ว #%d',
- '%s created a subtask for the task #%d' => '%s สร้างงานย่อยสำหรับงานแล้ว #%d',
- '%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 updated a comment on the task #%d' => '%s ได้ปรับปรุงความคิดเห็นในงาน #%d',
+ '%s commented on the task #%d' => '%s ได้แสดงความคิดเห็นบนงาน #%d',
+ '%s updated a subtask for the task #%d' => '%s ได้ปรับปรุงงานย่อยสำหรับงาน #%d',
+ '%s created a subtask for the task #%d' => '%s ได้สร้างงานย่อยสำหรับงาน #%d',
+ '%s updated the task #%d' => '%s ได้ปรับปรุงงาน #%d',
+ '%s created the task #%d' => '%s ได้สร้างงาน #%d',
+ '%s closed the task #%d' => '%s ได้ปิดงาน #%d',
+ '%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 %s to %s' => '%s เปลี่ยนผู้รับผิดชอบของงาน %s เป็น %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' => 'เลือกเหตุการณ์',
'Create a task from an external provider' => 'สร้างงานจากบริการภายนอก',
@@ -409,8 +393,8 @@ return array(
// 'Webhook settings' => '',
// 'Reset token' => '',
// 'API endpoint:' => '',
- 'Refresh interval for private board' => 'ระยะรีเฟรชบอร์ดส่วนตัว',
- 'Refresh interval for public board' => 'ระยะรีเฟรชบอร์ดสาธารณะ',
+ 'Refresh interval for private board' => 'ระยะเวลารีเฟรชบอร์ดส่วนตัว',
+ 'Refresh interval for public board' => 'ระยะเวลารีเฟรชบอร์ดสาธารณะ',
'Task highlight period' => 'ช่วงเวลาไฮไลต์งาน',
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'ช่วงเวลา (เป็นวินาที) ใช้ในการตัดสินใจว่าเป็นการแก้ไขเร็วๆ นี้ (0 ไม่ใช้งาน, ค่าเริ่มต้น 2 วัน)',
'Frequency in second (60 seconds by default)' => 'ความถี่ (ค่าเริ่มต้นทุก 60 วินาที) ',
@@ -438,37 +422,32 @@ return array(
'My projects' => 'โปรเจคของฉัน',
'Columns' => 'คอลัมน์',
'Task' => 'งาน',
- 'Your are not member of any project.' => 'คุณไม่ได้เป็นสมาชิกของโปรเจค',
+ 'Your are not member of any project.' => 'คุณไม่ได้เป็นสมาชิกของโปรเจคใดๆ',
'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' => 'เลียนแบบโปรเจคนี้',
+ 'Clone this project' => 'สำเนาโปรเจคนี้',
'Column removed successfully.' => 'ลบคอลัมน์สำเร็จ',
- 'Not enough data to show the graph.' => 'ไม่มีข้อมูลแสดงเป็นกราฟ',
+ 'Not enough data to show the graph.' => 'ไม่มีข้อมูลเพียงพอสำหรับการแสดงกราฟ',
'Previous' => 'ก่อนหน้า',
'The id must be an integer' => 'ไอดีต้องเป็นตัวเลขจำนวนเต็ม',
- 'The project id must be an integer' => 'ไอดีโปรเจคต้องเป็นตัวเลข',
- 'The status must be an integer' => 'สถานะต้องเป็นตัวเลข',
+ 'The project id must be an integer' => 'ไอดีโปรเจคต้องเป็นตัวเลขเท่านั้น',
+ 'The status must be an integer' => 'สถานะต้องเป็นตัวเลขเท่านั้น',
'The subtask id is required' => 'ต้องการงานย่อย',
- 'The subtask id must be an integer' => 'ไอดีงานย่อยต้องเป็นตัวเลข',
+ 'The subtask id must be an integer' => 'ไอดีงานย่อยต้องเป็นตัวเลขเท่านั้น',
'The task id is required' => 'ต้องการไอดีงาน',
- 'The task id must be an integer' => 'ไอดีงานต้องเป็นตัวเลข',
- 'The user id must be an integer' => 'ไอดีผู้ใช้ต้องเป็นตัวเลข',
+ 'The task id must be an integer' => 'ไอดีงานต้องเป็นตัวเลขเท่านั้น',
+ 'The user id must be an integer' => 'ไอดีผู้ใช้ต้องเป็นตัวเลขเท่านั้น',
'This value is required' => 'ต้องการค่านี้',
'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' => 'สวิมเลนพร้อมใช้งาน',
@@ -491,10 +470,9 @@ return array(
'Default categories for new projects (Comma-separated)' => 'ค่าเริ่มต้นหมวดสำหรับโปรเจคใหม่ (Comma-separated)',
'Integrations' => 'การใช้ร่วมกัน',
'Integration with third-party services' => 'การใช้งานร่วมกับบริการ third-party',
- 'Subtask Id' => 'รหัสงานย่อย',
+ 'Subtask Id' => 'ไอดีของงานย่อย',
'Subtasks' => 'งานย่อย',
'Subtasks Export' => 'ส่งออก งานย่อย',
- 'Subtasks exportation for "%s"' => 'ส่งออกงานย่อยสำหรับ "%s"',
'Task Title' => 'ชื่องาน',
'Untitled' => 'ไม่มีชื่อ',
'Application default' => 'แอพพลิเคชันเริ่มต้น',
@@ -525,17 +503,15 @@ return array(
'Add a new link' => 'เพิ่มลิงค์ใหม่',
'Do you really want to remove this link: "%s"?' => 'คุณต้องการลบลิงค์นี้: "%s"?',
'Do you really want to remove this link with task #%d?' => 'คุณต้องการลบลิงค์นี้ของงาน #%d?',
- 'Field required' => 'ต้องใส่',
+ 'Field required' => 'จำเป็นต้องใส่',
'Link added successfully.' => 'เพิ่มลิงค์เรียบร้อยแล้ว',
'Link updated successfully.' => 'ปรับปรุงลิงค์เรียบร้อยแล้ว',
'Link removed successfully.' => 'ลบลิงค์เรียบร้อยแล้ว',
'Link labels' => 'ป้ายลิงค์',
'Link modification' => 'แก้ไขลิงค์',
'Links' => 'ลิงค์',
- 'Link settings' => 'ตั้งค่าลิงค์',
'Opposite label' => 'ป้ายชื่อตรงข้าม',
'Remove a link' => 'ลบลิงค์',
- 'Task\'s links' => 'ลิงค์',
'The labels must be different' => 'ป้ายชื่อต้องต่างกัน',
'There is no link.' => 'ไม่มีลิงค์',
'This label must be unique' => 'ป้ายชื่อต้องไม่ซ้ำกัน',
@@ -568,7 +544,6 @@ return array(
'Compact view' => 'มุมมองพอดี',
'Horizontal scrolling' => 'เลื่อนตามแนวนอน',
'Compact/wide view' => 'พอดี/กว้าง มุมมอง',
- 'No results match:' => 'ไม่มีผลลัพท์ที่ตรง',
'Currency' => 'สกุลเงิน',
'Private project' => 'โปรเจคส่วนตัว',
'AUD - Australian Dollar' => 'AUD - ดอลลาร์ออสเตรเลีย',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => 'JPY - เยน',
'NZD - New Zealand Dollar' => 'NZD - ดอลลาร์นิวซีแลนด์',
'RSD - Serbian dinar' => 'RSD - ดีนาร์เซอร์เบีย',
+ // 'CNY - Chinese Yuan' => '',
'USD - US Dollar' => 'USD - ดอลลาร์สหรัฐ',
'Destination column' => 'คอลัมน์เป้าหมาย',
'Move the task to another column when assigned to a user' => 'ย้ายงานไปคอลัมน์อื่นเมื่อกำหนดบุคคลรับผิดชอบ',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => 'อัตราค่าเงิน',
'Rate' => 'อัตรา',
'Change reference currency' => 'เปลี่ยนการอ้างถึงค่าเงิน',
- 'Add a new currency rate' => 'เพิ่มอัตราค่าเงินใหม่',
'Reference currency' => 'อ้างถึงค่าเงิน',
'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' => '',
@@ -615,7 +590,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',
@@ -680,14 +654,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 ไปสวินเลนแรก',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30นาที',
'Stop timer' => 'หยุดจับเวลา',
'Start timer' => 'เริ่มจับเวลา',
- 'Add project member' => 'เพิ่มสมาชิกโปรเจค',
'My activity stream' => 'กิจกรรมที่เกิดขึ้นของฉัน',
'My calendar' => 'ปฎิทินของฉัน',
'Search tasks' => 'ค้นหางาน',
@@ -758,8 +725,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 งานล่าสุด',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'ผู้ใช้รีโมท',
// 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '',
// 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '',
- 'New remote user' => 'เพิ่มผู้ใช้รีโมทใหม่',
- 'New local user' => 'เพิ่มผู้ใช้ท้องถิ่นใหม่',
'Default task color' => 'สีเริ่มต้นของงาน',
'This feature does not work with all browsers.' => 'คุณลักษณะนี้ไม่สามารถทำงานได้ทุกเบราเซอร์',
// 'There is no destination project available.' => '',
@@ -800,7 +763,6 @@ return array(
'License:' => 'สัญญาอนุญาต:',
'License' => 'สัญญาอนุญาต',
'Enter the text below' => 'พิมพ์ข้อความด้านล่าง',
- 'Gantt chart for %s' => 'แผนภูมิแกรนท์สำหรับ %s',
'Sort by position' => 'เรียงตามตำแหน่ง',
'Sort by date' => 'เรียงตามวัน',
'Add task' => 'เพิ่มงาน',
@@ -841,8 +803,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.' => 'ตัวกรองกำหนดเองของคุณสร้างเรียบร้อย',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'เจ้าของ',
'Unread notifications' => 'การแจ้งเตือนยังไม่ได้อ่าน',
'Notification methods:' => 'ลักษณะการแจ้งเตือน:',
- 'Import tasks from CSV file' => 'นำเข้างานจากไฟล์ CSV',
'Unable to read your file' => 'ไม่สามารถอ่านไฟล์ของคุณ',
'%d task(s) have been imported successfully.' => '%d งานนำเข้าเรียบร้อย',
'Nothing have been imported!' => 'ไม่มีอะไรถูกนำเข้า',
@@ -947,7 +906,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.' => 'ไม่มีกลุ่ม',
@@ -958,23 +916,20 @@ return array(
'Remove this user' => 'เอาผู้ใช้คนนี้ออก',
'Permissions' => 'การอนุญาตใช้งาน',
'Allowed Users' => 'การอนุญาตผู้ใช้',
- 'No user have been allowed specifically.' => 'ไม่มีผู้ใช้ได้รับอนุญาติเป็นพิเศษ',
+ 'No user have been allowed specifically.' => 'ไม่มีผู้ใช้ได้รับอนุญาตเป็นพิเศษ',
'Role' => 'บทบาท',
'Enter user name...' => 'พิมพ์ชื่อผู้ใช้...',
'Allowed Groups' => 'อนุญาตกลุ่ม',
- 'No group have been allowed specifically.' => 'ไม่มีกลุ่มได้รับอนุญาติเป็นพิเศษ',
+ 'No group have been allowed specifically.' => 'ไม่มีกลุ่มได้รับอนุญาตเป็นพิเศษ',
'Group' => 'กลุ่ม',
'Group Name' => 'ชื่อกลุ่ม',
'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' => 'เวลาที่ใช้',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => 'เจ้าของโปรเจค: ',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'ตัวบ่งชี้โปรโจคเป็นตัวเลือกเสริมและต้องเป็นตัวอักษรหรือตัวเลข ตัวอย่าง: MYPROJECT',
'Project owner' => 'เจ้าของโปรเจค',
- 'Those dates are useful for the project Gantt chart.' => 'วันที่ใช้สำหรับแผนภูมิแกรนท์ของโปรเจค',
'Private projects do not have users and groups management.' => 'โปรเจคส่วนตัวไม่มีการจัดการผู้ใช้และกลุ่ม',
'There is no project member.' => 'ไม่มีสมาชิกโปรเจค',
'Priority' => 'ความสำคัญ',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => 'เริ่ม:',
'Moved:' => 'ย้าย:',
'Task #%d' => 'งานที่ #%d',
- 'Date and time format' => 'รูปแบบของวันเวลา',
'Time format' => 'รูปแบบของเวลา',
'Start date: ' => 'เริ่มวันที่:',
'End date: ' => 'จบวันที่:',
@@ -1083,9 +1036,7 @@ return array(
// 'User disabled successfully.' => '',
// 'Unable to disable this user.' => '',
// 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
// 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
// 'Drag and drop your files here' => '',
// 'choose files' => '',
// 'View profile' => '',
@@ -1195,7 +1146,6 @@ return array(
// 'Email sender address' => '',
// 'Email transport' => '',
// 'Webhook token' => '',
- // 'Imports' => '',
// 'Project tags management' => '',
// 'Tag created successfully.' => '',
// 'Unable to create this tag.' => '',
@@ -1216,5 +1166,145 @@ return array(
// 'Global tags' => '',
// 'There is no global tag at the moment.' => '',
// 'This field cannot be empty' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ // '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' => '',
+ // 'Assign a color when the task is moved to a specific swimlane' => '',
+ // 'Assign a priority when the task is moved to a specific swimlane' => '',
+ // 'User unlocked successfully.' => '',
+ // 'Unable to unlock the user.' => '',
+ // 'Move a task to another swimlane' => '',
+ // 'Creator Name' => '',
+ // 'Time spent and estimated' => '',
+ // 'Move position' => '',
+ // 'Move task to another position on the board' => '',
+ // 'Insert before this task' => '',
+ // 'Insert after this task' => '',
+ // 'Unlock this user' => '',
+ // 'Custom Project Roles' => '',
+ // 'Add a new custom role' => '',
+ // 'Restrictions for the role "%s"' => '',
+ // 'Add a new project restriction' => '',
+ // 'Add a new drag and drop restriction' => '',
+ // 'Add a new column restriction' => '',
+ // 'Edit this role' => '',
+ // 'Remove this role' => '',
+ // 'There is no restriction for this role.' => '',
+ // 'Only moving task between those columns is permitted' => '',
+ // 'Close a task in a specific column when not moved during a given period' => '',
+ // 'Edit columns' => '',
+ // 'The column restriction has been created successfully.' => '',
+ // 'Unable to create this column restriction.' => '',
+ // 'Column restriction removed successfully.' => '',
+ // 'Unable to remove this restriction.' => '',
+ // 'Your custom project role has been created successfully.' => '',
+ // 'Unable to create custom project role.' => '',
+ // 'Your custom project role has been updated successfully.' => '',
+ // 'Unable to update custom project role.' => '',
+ // 'Custom project role removed successfully.' => '',
+ // 'Unable to remove this project role.' => '',
+ // 'The project restriction has been created successfully.' => '',
+ // 'Unable to create this project restriction.' => '',
+ // 'Project restriction removed successfully.' => '',
+ // 'You cannot create tasks in this column.' => '',
+ // 'Task creation is permitted for this column' => '',
+ // 'Closing or opening a task is permitted for this column' => '',
+ // 'Task creation is blocked for this column' => '',
+ // 'Closing or opening a task is blocked for this column' => '',
+ // 'Task creation is not permitted' => '',
+ // 'Closing or opening a task is not permitted' => '',
+ // 'New drag and drop restriction for the role "%s"' => '',
+ // 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '',
+ // 'Remove a column restriction' => '',
+ // 'Do you really want to remove this column restriction: "%s" to "%s"?' => '',
+ // 'New column restriction for the role "%s"' => '',
+ // 'Rule' => '',
+ // 'Do you really want to remove this column restriction?' => '',
+ // 'Custom roles' => '',
+ // 'New custom project role' => '',
+ // 'Edit custom project role' => '',
+ // 'Remove a custom role' => '',
+ // 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '',
+ // 'There is no custom role for this project.' => '',
+ // 'New project restriction for the role "%s"' => '',
+ // 'Restriction' => '',
+ // 'Remove a project restriction' => '',
+ // 'Do you really want to remove this project restriction: "%s"?' => '',
+ // 'Duplicate to multiple projects' => '',
+ // 'This field is required' => '',
+ // 'Moving a task is not permitted' => '',
+ // 'This value must be in the range %d to %d' => '',
+ // 'You are not allowed to move this task.' => '',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php
index b25ef122..85a667a6 100644
--- a/app/Locale/tr_TR/translations.php
+++ b/app/Locale/tr_TR/translations.php
@@ -19,15 +19,15 @@ return array(
'Red' => 'Kırmızı',
'Orange' => 'Turuncu',
'Grey' => 'Gri',
- 'Brown' => 'Kahverengi',
- 'Deep Orange' => 'Koyu Turuncu',
+ 'Brown' => 'K.rengi',
+ 'Deep Orange' => 'Kavuniçi',
'Dark Grey' => 'Koyu Gri',
'Pink' => 'Pembe',
'Teal' => 'Turkuaz',
'Cyan' => 'Cam Göbeği',
- 'Lime' => 'Limon rengi',
+ 'Lime' => 'Limoni',
'Light Green' => 'Açık Yeşil',
- 'Amber' => 'Koyu sarı',
+ 'Amber' => 'Amber',
'Save' => 'Kaydet',
'Login' => 'Giriş',
'Official website:' => 'Resmi internet sitesi:',
@@ -54,26 +54,23 @@ return array(
'Project' => 'Proje',
'Status' => 'Durum',
'Tasks' => 'Görevler',
- 'Board' => 'Tablo',
+ 'Board' => 'Pano',
'Actions' => 'İşlemler',
'Inactive' => 'Aktif değil',
'Active' => 'Aktif',
- '%d tasks on the board' => '%d görev bu tabloda',
+ '%d tasks on the board' => '%d görev bu panoda',
'%d tasks in total' => '%d görev toplam',
- 'Unable to update this board.' => 'Bu tablo güncellenemiyor.',
- 'Edit board' => 'Tabloyu düzenle',
+ 'Unable to update this board.' => 'Bu pano güncellenemiyor.',
'Disable' => 'Devre dışı bırak',
'Enable' => 'Etkinleştir',
'New project' => 'Yeni proje',
'Do you really want to remove this project: "%s"?' => 'Bu projeyi gerçekten silmek istiyor musunuz: "%s"?',
'Remove project' => 'Projeyi sil',
- 'Edit the board for "%s"' => 'Tabloyu "%s" için güncelle',
- 'All projects' => 'Tüm projeler',
+ 'Edit the board for "%s"' => 'Panoyu "%s" için güncelle',
'Add a new column' => 'Yeni sütun ekle',
'Title' => 'Başlık',
'Assigned to %s' => '%s kullanıcısına atanmış',
'Remove a column' => 'Bir sütunu sil',
- 'Remove a column from a board' => 'Tablodan bir sütunu sil',
'Unable to remove this column.' => 'Bu sütun silinemiyor.',
'Do you really want to remove this column: "%s"?' => 'Bu sütunu gerçekten silmek istiyor musunuz: "%s"?',
'This action will REMOVE ALL TASKS associated to this column!' => 'Bu komut sütun içindeki TÜM GÖREVLERİ silecek!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM komutu)',
'(Gzip compressed Sqlite file)' => '(Gzip ile sıkıştırılmış Sqlite dosyası)',
'Close a task' => 'Bir görevi kapat',
- 'Edit a task' => 'Bir görevi düzenle',
'Column' => 'Sütun',
'Color' => 'Renk',
'Assignee' => 'Atanan',
@@ -96,9 +92,9 @@ return array(
'New task' => 'Yeni görev',
'Open a task' => 'Bir görevi aç',
'Do you really want to open this task: "%s"?' => 'Bu görevi gerçekten açmak istiyor musunuz: "%s"?',
- 'Back to the board' => 'Tabloya dön',
+ 'Back to the board' => 'Panoya dön',
'There is nobody assigned' => 'Kimse atanmamış',
- 'Column on the board:' => 'Tablodaki sütun:',
+ 'Column on the board:' => 'Panodaki sütun:',
'Close this task' => 'Görevi kapat',
'Open this task' => 'Görevi aç',
'There is no description.' => 'Açıklama yok.',
@@ -144,7 +140,7 @@ return array(
'Unable to update your user.' => 'Kullanıcı güncellenemiyor.',
'User removed successfully.' => 'Kullanıcı silindi.',
'Unable to remove this user.' => 'Bu kullanıcı silinemiyor.',
- 'Board updated successfully.' => 'Tablo başarıyla güncellendi.',
+ 'Board updated successfully.' => 'Pano başarıyla güncellendi.',
'Ready' => 'Hazır',
'Backlog' => 'Bekleme listesi',
'Work in progress' => 'İşlemde',
@@ -162,9 +158,7 @@ return array(
'Task count' => 'Görev sayısı',
'User' => 'Kullanıcı',
'Comments' => 'Yorumlar',
- 'Leave a comment' => 'Bir yorum ekle',
'Comment is required' => 'Yorum gerekli',
- 'Leave a description' => 'Açıklama ekleyin',
'Comment added successfully.' => 'Yorum eklendi',
'Unable to create your comment.' => 'Yorumunuz oluşturulamadı',
'Due Date' => 'Bitiş Tarihi',
@@ -214,7 +208,7 @@ return array(
'Login date' => 'Giriş tarihi',
'Authentication method' => 'Doğrulama yöntemi',
'IP address' => 'IP adresi',
- 'User agent' => 'Kullanıcı sistemi',
+ 'User agent' => 'Kullanıcı Arayüzü',
'Persistent connections' => 'Kalıcı bağlantılar',
'No session.' => 'Oturum yok.',
'Expiration date' => 'Geçerlilik sonu',
@@ -226,7 +220,6 @@ return array(
'Search' => 'Ara',
'Nothing found.' => 'Hiçbir şey bulunamadı.',
'Due date' => 'Bitiş tarihi',
- 'Others formats accepted: %s and %s' => 'Diğer kabul edilen formatlar: %s ve %s',
'Description' => 'Açıklama',
'%d comments' => '%d yorum',
'%d comment' => '%d yorum',
@@ -270,7 +263,7 @@ return array(
'Add a comment' => 'Yorum ekle',
'Edit a comment' => 'Yorum değiştir',
'Summary' => 'Özet',
- 'Time tracking' => 'Zaman takibi',
+ 'Time tracking' => 'Süre Çizelgesi',
'Estimate:' => 'Tahmini:',
'Spent:' => 'Harcanan:',
'Do you really want to remove this sub-task?' => 'Bu alt görevi silmek istediğinize emin misiniz',
@@ -282,7 +275,7 @@ return array(
'Add a sub-task' => 'Alt görev ekle',
'Original estimate' => 'Orjinal tahmin',
'Create another sub-task' => 'Başka bir alt görev daha oluştur',
- 'Time spent' => 'Harcanan zaman',
+ 'Time spent' => 'Geçen süre',
'Edit a sub-task' => 'Alt görev düzenle',
'Remove a sub-task' => 'Alt görev sil',
'The time must be a numeric value' => 'Zaman alfanümerik bir değer olmalı',
@@ -299,9 +292,7 @@ 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',
'Task Id' => 'Görev Kimliği',
'Creator' => 'Oluşturan',
@@ -322,14 +313,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',
@@ -350,8 +336,8 @@ return array(
'Remote' => 'Uzak',
'Enabled' => 'Etkinleştirildi',
'Disabled' => 'Devre dışı bırakıldı',
- 'Username:' => 'Kullanıcı adı',
- 'Name:' => 'Ad',
+ 'Login:' => 'Kullanıcı adı',
+ 'Full Name:' => 'Ad',
'Email:' => 'E-posta',
'Notifications:' => 'Bildirimler:',
'Notifications' => 'Bildirimler',
@@ -386,14 +372,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',
@@ -405,12 +389,12 @@ return array(
'Database' => 'Veritabanı',
'About' => 'Hakkında',
'Database driver:' => 'Veritabanı sürücüsü:',
- 'Board settings' => 'Tablo ayarları',
+ 'Board settings' => 'Pano ayarları',
'Webhook settings' => 'Webhook ayarları',
'Reset token' => 'Belirteci sıfırla',
'API endpoint:' => 'API bitiş noktası:',
- 'Refresh interval for private board' => 'Özel tablolar için yenileme sıklığı',
- 'Refresh interval for public board' => 'Dışa açık tablolar için yenileme sıklığı',
+ 'Refresh interval for private board' => 'Özel panolar için yenileme sıklığı',
+ 'Refresh interval for public board' => 'Dışa açık panolar için yenileme sıklığı',
'Task highlight period' => 'Görevi öne çıkarma süresi',
'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Bir görevin yeni değiştirilmiş sayılması için süre (saniye olarak) (Bu özelliği iptal etmek için 0, varsayılan değer 2 gün)',
'Frequency in second (60 seconds by default)' => 'Saniye olarak frekans (varsayılan 60 saniye)',
@@ -442,13 +426,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.',
@@ -465,10 +446,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',
@@ -494,7 +473,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ı',
@@ -511,14 +489,14 @@ return array(
'Allow only one subtask in progress at the same time for a user' => 'Bir kullanıcı için aynı anda yalnızca bir alt göreve izin ver',
'Edit column "%s"' => '"%s" sütununu değiştir',
'Select the new status of the subtask: "%s"' => '"%s" alt görevi için yeni durum seçin.',
- 'Subtask timesheet' => 'Alt görev için zaman takip tablosu',
+ 'Subtask timesheet' => 'Alt görev için süre tablosu',
'There is nothing to show.' => 'Gösterilecek hiçbir şey yok.',
- 'Time Tracking' => 'Zaman takibi',
+ 'Time Tracking' => 'Süre Çizelgesi',
'You already have one subtask in progress' => 'Zaten işlemde olan bir alt görev var',
'Which parts of the project do you want to duplicate?' => 'Projenin hangi kısımlarının kopyasını oluşturmak istiyorsunuz?',
'Disallow login form' => 'Giriş formu erişimini engelle',
'Start' => 'Başlangıç',
- 'End' => 'Son',
+ 'End' => 'Bitiş',
'Task age in days' => 'Görev yaşı gün olarak',
'Days in this column' => 'Bu sütunda geçirilen gün',
'%dd' => '%dG',
@@ -532,10 +510,8 @@ return array(
'Link labels' => 'Link etiketleri',
'Link modification' => 'Link değiştirme',
'Links' => 'Links',
- 'Link settings' => 'Link ayarları',
'Opposite label' => 'Zıt etiket',
'Remove a link' => 'Bir link silmek',
- 'Task\'s links' => 'Görevin linkleri',
'The labels must be different' => 'Etiketler farklı olmalı',
'There is no link.' => 'Hiç bir bağ yok',
'This label must be unique' => 'Bu etiket tek olmalı',
@@ -561,71 +537,69 @@ return array(
'Expand/collapse tasks' => 'Görevleri genişlet/daralt',
'Close dialog box' => 'İletiyi kapat',
'Submit a form' => 'Formu gönder',
- 'Board view' => 'Tablo görünümü',
+ 'Board view' => 'Pano görünümü',
'Keyboard shortcuts' => 'Klavye kısayolları',
- 'Open board switcher' => 'Tablo seçim listesini aç',
+ 'Open board switcher' => 'Pano seçim listesini aç',
'Application' => 'Uygulama',
'Compact view' => 'Ekrana sığdır',
'Horizontal scrolling' => 'Geniş görünüm',
'Compact/wide view' => 'Ekrana sığdır / Geniş görünüm',
- 'No results match:' => 'Uygun sonuç bulunamadı',
'Currency' => 'Para birimi',
'Private project' => 'Özel proje',
- // 'AUD - Australian Dollar' => '',
- // 'CAD - Canadian Dollar' => '',
- // 'CHF - Swiss Francs' => '',
- // 'Custom Stylesheet' => '',
+ 'AUD - Australian Dollar' => 'AUD - Avustralya Doları',
+ 'CAD - Canadian Dollar' => 'CAD - Kanada Doları',
+ 'CHF - Swiss Francs' => 'CHF - İsviçre Frangı',
+ 'Custom Stylesheet' => 'Özel Sitil Css',
'download' => 'indir',
// 'EUR - Euro' => '',
- // 'GBP - British Pound' => '',
- // 'INR - Indian Rupee' => '',
- // 'JPY - Japanese Yen' => '',
- // 'NZD - New Zealand Dollar' => '',
+ 'GBP - British Pound' => 'GBP - İngiliz Paund',
+ 'INR - Indian Rupee' => 'INR - Hint Rupesi',
+ 'JPY - Japanese Yen' => 'JPY - Japon Yeni',
+ 'NZD - New Zealand Dollar' => 'NZD - Yeni Zelanda Doları',
// 'RSD - Serbian dinar' => '',
- // 'USD - US Dollar' => '',
+ // 'CNY - Chinese Yuan' => '',
+ 'USD - US Dollar' => 'USD$ Amerikan Doları',
'Destination column' => 'Hedef Sütun',
'Move the task to another column when assigned to a user' => 'Bir kullanıcıya atandığında görevi başka bir sütuna taşı',
'Move the task to another column when assignee is cleared' => 'Atanmış kullanıcı kaldırıldığında görevi başka bir sütuna taşı',
'Source column' => 'Kaynak sütun',
'Transitions' => 'Geçişler',
'Executer' => 'Uygulayıcı',
- 'Time spent in the column' => 'Sütunda harcanan süre',
+ 'Time spent in the column' => 'Sütunda geçen süre',
'Task transitions' => 'Görev geçişleri',
'Task transitions export' => 'Görev geçişlerini dışa aktar',
'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Bu rapor her bir görevin sütunlar arası geçişlerini tarih, kullanıcı ve sütunda harcanan zaman detaylarıyla içerir.',
'Currency rates' => 'Döviz kurları',
'Rate' => 'Kurlar',
'Change reference currency' => 'Referans kur değiştir',
- 'Add a new currency rate' => 'Yeni bir kur ekle',
'Reference currency' => 'Referans kur',
'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',
- 'The two factor authentication code is not valid.' => 'İki kademeli doğrulama kodu geçersiz',
- 'The two factor authentication code is valid.' => 'İki kademeli doğrulama kodu onaylandı',
+ 'Check two factor authentication code' => 'Çift-Kademeli doğrulama kodunu kontrol et',
+ 'The two factor authentication code is not valid.' => 'Çift-Kademeli doğrulama kodu geçersiz',
+ 'The two factor authentication code is valid.' => 'Çift-Kademeli doğrulama kodu onaylandı',
'Code' => 'Kod',
- 'Two factor authentication' => 'İki kademeli doğrulama',
+ 'Two factor authentication' => 'Çift-Kademeli doğrulama',
'This QR code contains the key URI: ' => 'Bu QR kodu anahtar URI içerir',
'Check my code' => 'Kodu kontrol et',
'Secret key: ' => 'Gizli anahtar',
'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',
'Add a screenshot' => 'Bir ekran görüntüsü ekle',
'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Bir ekran görüntüsü alın ve buraya yapıştırmak için CTRL+V veya ⌘+V tuşlarına basın.',
'Screenshot uploaded successfully.' => 'Ekran görüntüsü başarıyla yüklendi',
- // 'SEK - Swedish Krona' => '',
+ 'SEK - Swedish Krona' => ' SEK - İsveç Kronu',
'Identifier' => 'Kimlik',
- 'Disable two factor authentication' => 'İki kademeli doğrulamayı iptal et',
- 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Bu kullanıcı için iki kademeli doğrulamayı iptal etmek istediğinize emin misiniz: "%s"?',
+ 'Disable two factor authentication' => 'Çift-Kademeli doğrulamayı iptal et',
+ 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Bu kullanıcı için Çift-Kademeli doğrulamayı iptal etmek istediğinize emin misiniz: "%s"?',
'Edit link' => 'Linki düzenle',
'Start to type task title...' => 'Görev başlığını yazmaya başlayın...',
'A task cannot be linked to itself' => 'Bir görevden kendine link atanamaz',
@@ -669,8 +643,8 @@ return array(
'iCal feed' => 'iCal akışı',
'Preferences' => 'Ayarlar',
'Security' => 'Güvenlik',
- 'Two factor authentication disabled' => 'İki kademeli doğrulamayı devre dışı bırak',
- 'Two factor authentication enabled' => 'İki kademeli doğrulamayı etkinleştir',
+ 'Two factor authentication disabled' => 'Çift-Kademeli doğrulamayı devre dışı bırak',
+ 'Two factor authentication enabled' => 'Çift-Kademeli doğrulamayı etkinleştir',
'Unable to update this user.' => 'Bu kullanıcı güncellenemiyor',
'There is no user management for private projects.' => 'Özel projeler için kullanıcı yönetimi yoktur.',
'User that will receive the email' => 'Email alacak kullanıcı',
@@ -680,14 +654,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ı',
@@ -695,7 +663,7 @@ return array(
'This report contains all subtasks information for the given date range.' => 'Bu rapor belirtilen tarih aralığında tüm alt görev bilgilerini içerir.',
'This report contains all tasks information for the given date range.' => 'Bu rapor belirtilen tarih aralığında tüm görev bilgilerini içerir.',
'Project activities for %s' => '%s için proje aktiviteleri',
- 'view the board on Kanboard' => 'Tabloyu Kanboard\'da görüntüle',
+ 'view the board on Kanboard' => 'Pano yu Kanboard\'da görüntüle',
'The task have been moved to the first swimlane' => 'Görev birinci kulvara taşındı',
'The task have been moved to another swimlane:' => 'Görev başka bir kulvara taşındı:',
'New title: %s' => 'Yeni başlık: %s',
@@ -708,7 +676,7 @@ return array(
'The due date have been removed' => 'Tamamlanma tarihi silindi',
'There is no description anymore' => 'Artık açıklama yok',
'Recurrence settings have been modified' => 'Tekrarlanma ayarları değiştirildi',
- 'Time spent changed: %sh' => 'Harcanan zaman değiştirildi: %sh',
+ 'Time spent changed: %sh' => 'Geçen süre değiştirildi: %sh',
'Time estimated changed: %sh' => 'Tahmini süre değiştirildi: %sh',
'The field "%s" have been updated' => '"%s" hanesi değiştirildi',
'The description has been modified:' => 'Açıklama değiştirildi',
@@ -725,7 +693,6 @@ return array(
'<30m' => '<30dk',
'Stop timer' => 'Zamanlayıcıyı durdur',
'Start timer' => 'Zamanlayıcıyı başlat',
- 'Add project member' => 'Proje üyesi ekle',
'My activity stream' => 'Olay akışım',
'My calendar' => 'Takvimim',
'Search tasks' => 'Görevleri ara',
@@ -738,9 +705,9 @@ return array(
'Open tasks' => 'Açık görevler',
'Not assigned' => 'Atanmamış',
'View advanced search syntax' => 'Gelişmiş arama kodlarını göster',
- 'Overview' => 'Genel bakış',
- 'Board/Calendar/List view' => 'Tablo/Takvim/Liste görünümü',
- 'Switch to the board view' => 'Tablo görünümüne geç',
+ 'Overview' => 'Özet Görünüm',
+ 'Board/Calendar/List view' => 'Pano/Takvim/Liste görünümü',
+ 'Switch to the board view' => 'Pano görünümüne geç',
'Switch to the calendar view' => 'Takvim görünümüne geç',
'Switch to the list view' => 'Liste görünümüne geç',
'Go to the search/filter box' => 'Arama/Filtreleme kutusuna git',
@@ -758,11 +725,9 @@ 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.',
+ 'Average time spent into each column' => 'Her bir sütunda geçirilen ortalama süre',
+ 'Average time spent' => 'Geçen ortalama süre',
+ '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 süreyi gösterir.',
'Average Lead and Cycle time' => 'Ortalama teslim ve çevrim süresi',
'Average lead time: ' => 'Ortalama teslim süresi',
'Average cycle time: ' => 'Ortalama çevrim süresi',
@@ -773,7 +738,7 @@ return array(
'Lead and cycle time' => 'Teslim ve çevrim süresi',
'Lead time: ' => 'Teslim süresi: ',
'Cycle time: ' => 'Çevrim süresi: ',
- 'Time spent into each column' => 'Her sütunda harcanan zaman',
+ 'Time spent into each column' => 'Her sütunda geçen süre',
'The lead time is the duration between the task creation and the completion.' => 'Teslim süresi, görevin oluşturulması ile tamamlanması arasında geçen süredir.',
'The cycle time is the duration between the start date and the completion.' => 'Çevrim süresi, görevin başlangıç tarihi ile tamamlanması arasında geçen süredir.',
'If the task is not closed the current time is used instead of the completion date.' => 'Eğer görev henüz kapatılmamışsa, tamamlanma tarihi yerine şu anki tarih kullanılır.',
@@ -782,8 +747,6 @@ return array(
'Remote user' => 'Uzak kullanıcı',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Uzak kullanıcıların şifreleri Kanboard veritabanında saklanmaz, örnek: LDAP, Google ve Github hesapları',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Eğer giriş formuna erişimi engelleyi seçerseniz, giriş formuna girilen bilgiler gözardı edilir.',
- 'New remote user' => 'Yeni uzak kullanıcı',
- 'New local user' => 'Yeni yerel kullanıcı',
'Default task color' => 'Varsayılan görev rengi',
'This feature does not work with all browsers.' => 'Bu özellik tüm tarayıcılarla çalışmaz',
'There is no destination project available.' => 'Seçilebilecek hedef proje yok.',
@@ -800,7 +763,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',
@@ -810,9 +772,9 @@ return array(
'Moving or resizing a task will change the start and due date of the task.' => 'Bir görevin boyutunu değiştirmek, görevin başlangıç ve tamamlanması gereken tarihlerini değiştirir.',
'There is no task in your project.' => 'Projenizde hiç görev yok.',
'Gantt chart' => 'Gantt diyagramı',
- 'People who are project managers' => 'Proje yöneticisi olan kişiler',
+ 'People who are project managers' => 'Proje müdürü olan kişiler',
'People who are project members' => 'Proje üyesi olan kişiler',
- // 'NOK - Norwegian Krone' => '',
+ 'NOK - Norwegian Krone' => ' NOK - Norveç Kronu',
'Show this column' => 'Bu sütunu göster',
'Hide this column' => 'Bu sütunu gizle',
'open file' => 'dosyayı aç',
@@ -820,11 +782,11 @@ return array(
'Users overview' => 'Kullanıcılara genel bakış',
'Members' => 'Üyeler',
'Shared project' => 'Paylaşılan proje',
- 'Project managers' => 'Proje yöneticileri',
+ 'Project managers' => 'Proje müdürleri',
'Gantt chart for all projects' => 'Tüm projeler için Gantt diyagramı',
'Projects list' => 'Proje listesi',
'Gantt chart for this project' => 'Bu proje için Gantt diyagramı',
- 'Project board' => 'Proje tablosu',
+ 'Project board' => 'Proje Panosu',
'End date:' => 'Bitiş tarihi:',
'There is no start date or end date for this project.' => 'Bu proje için başlangıç veya bitiş tarihi yok.',
'Projects Gantt chart' => 'Projeler Gantt diyagramı',
@@ -841,8 +803,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.',
@@ -880,7 +840,6 @@ return array(
'Owner' => 'Sahibi',
'Unread notifications' => 'Okunmamış bildirimler',
'Notification methods:' => 'Bildirim yöntemleri:',
- 'Import tasks from CSV file' => 'CSV dosyasından görevleri içeri aktar',
'Unable to read your file' => 'Dosya okunamıyor',
'%d task(s) have been imported successfully.' => '%d görev başarıyla içeri aktarıldı.',
'Nothing have been imported!' => 'Hiçbir şey içeri aktarılamadı!',
@@ -939,15 +898,14 @@ return array(
'Group removed successfully.' => 'Grup başarıyla silindi.',
'Unable to remove this group.' => 'Grup silinemedi.',
'Project Permissions' => 'Proje izimleri',
- 'Manager' => 'Yönetici',
- 'Project Manager' => 'Proje yöneticisi',
+ 'Manager' => 'Müdür',
+ 'Project Manager' => 'Proje müdürü',
'Project Member' => 'Proje üyesi',
'Project Viewer' => 'Proje izleyicisi',
'Your account is locked for %d minutes' => 'Hesabınız %d dakika boyunca kilitlendi',
'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.',
@@ -968,13 +926,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',
@@ -982,13 +937,13 @@ return array(
'Estimated Time' => 'Tahmini süre',
'Actual Time' => 'Gerçekleşen süre',
'Estimated vs actual time' => 'Tahmini vs gerçekleşen süre',
- // 'RUB - Russian Ruble' => '',
+ 'RUB - Russian Ruble' => ' RUB - Rus Rublesi',
'Assign the task to the person who does the action when the column is changed' => 'Sütun değiştirildiği zaman görevi eylemi gerçekleştiren kişiye ata',
'Close a task in a specific column' => 'Belirli bir sütundaki görevi kapat',
- 'Time-based One-time Password Algorithm' => 'Zamana bağlı tek kullanımlık şifre algoritması',
- 'Two-Factor Provider: ' => 'Çift kademeli doğrulama sağlayıcısı',
- 'Disable two-factor authentication' => 'Çift kademeli doğrulamayı devre dışı bırak',
- 'Enable two-factor authentication' => 'Çift kademeli doğrulamayı etkinleştir',
+ 'Time-based One-time Password Algorithm' => 'Zamana bağlı Tek-Kullanımlık şifre algoritması',
+ 'Two-Factor Provider: ' => 'Çift-Kademeli doğrulama sağlayıcısı: ',
+ 'Disable two-factor authentication' => 'Çift-Kademeli doğrulamayı devre dışı bırak',
+ 'Enable two-factor authentication' => 'Çift-Kademeli doğrulamayı etkinleştir',
'There is no integration registered at the moment.' => 'Şu anda kayıtlı bir entegrasyon bulunmuyor.',
'Password Reset for Kanboard' => 'Kanboard için şifre sıfırlama',
'Forgot password?' => 'Şifrenizi mi unuttunuz?',
@@ -1006,215 +961,350 @@ return array(
'Do you really want to close all tasks of this column?' => 'Bu sütundaki tüm görevleri kapatmak istediğinize emin misiniz?',
'%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '"%s" sütunu ve "%s" kulvarındaki %d görev kapatılacak.',
'Close all tasks of this column' => 'Bu sütundaki tüm görevleri kapat',
- // 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '',
- // 'My dashboard' => '',
- // 'My profile' => '',
- // 'Project owner: ' => '',
- // 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '',
- // 'Project owner' => '',
- // 'Those dates are useful for the project Gantt chart.' => '',
- // 'Private projects do not have users and groups management.' => '',
- // 'There is no project member.' => '',
- // 'Priority' => '',
- // 'Task priority' => '',
- // 'General' => '',
- // 'Dates' => '',
- // 'Default priority' => '',
- // 'Lowest priority' => '',
- // 'Highest priority' => '',
- // 'If you put zero to the low and high priority, this feature will be disabled.' => '',
- // 'Close a task when there is no activity' => '',
- // 'Duration in days' => '',
- // 'Send email when there is no activity on a task' => '',
- // 'Unable to fetch link information.' => '',
- // 'Daily background job for tasks' => '',
- // 'Auto' => '',
- // 'Related' => '',
- // 'Attachment' => '',
- // 'Title not found' => '',
- // 'Web Link' => '',
- // 'External links' => '',
- // 'Add external link' => '',
- // 'Type' => '',
- // 'Dependency' => '',
- // 'Add internal link' => '',
- // 'Add a new external link' => '',
- // 'Edit external link' => '',
- // 'External link' => '',
- // 'Copy and paste your link here...' => '',
- // 'URL' => '',
- // 'Internal links' => '',
- // 'Assign to me' => '',
- // 'Me' => '',
- // 'Do not duplicate anything' => '',
- // 'Projects management' => '',
- // 'Users management' => '',
- // 'Groups management' => '',
- // 'Create from another project' => '',
- // 'open' => '',
- // 'closed' => '',
- // 'Priority:' => '',
- // 'Reference:' => '',
- // 'Complexity:' => '',
- // 'Swimlane:' => '',
- // 'Column:' => '',
- // 'Position:' => '',
- // 'Creator:' => '',
- // 'Time estimated:' => '',
- // '%s hours' => '',
- // 'Time spent:' => '',
- // 'Created:' => '',
- // 'Modified:' => '',
- // 'Completed:' => '',
- // 'Started:' => '',
- // 'Moved:' => '',
- // 'Task #%d' => '',
- // 'Date and time format' => '',
- // 'Time format' => '',
- // 'Start date: ' => '',
- // 'End date: ' => '',
- // 'New due date: ' => '',
- // 'Start date changed: ' => '',
- // 'Disable private projects' => '',
- // 'Do you really want to remove this custom filter: "%s"?' => '',
- // 'Remove a custom filter' => '',
- // 'User activated successfully.' => '',
- // 'Unable to enable this user.' => '',
- // 'User disabled successfully.' => '',
- // 'Unable to disable this user.' => '',
- // 'All files have been uploaded successfully.' => '',
- // 'View uploaded files' => '',
- // 'The maximum allowed file size is %sB.' => '',
- // 'Choose files again' => '',
- // 'Drag and drop your files here' => '',
- // 'choose files' => '',
- // 'View profile' => '',
- // 'Two Factor' => '',
- // 'Disable user' => '',
- // 'Do you really want to disable this user: "%s"?' => '',
- // 'Enable user' => '',
- // 'Do you really want to enable this user: "%s"?' => '',
- // 'Download' => '',
- // 'Uploaded: %s' => '',
- // 'Size: %s' => '',
- // 'Uploaded by %s' => '',
- // 'Filename' => '',
- // 'Size' => '',
- // 'Column created successfully.' => '',
- // 'Another column with the same name exists in the project' => '',
- // 'Default filters' => '',
- // 'Your board doesn\'t have any columns!' => '',
- // 'Change column position' => '',
- // 'Switch to the project overview' => '',
- // 'User filters' => '',
- // 'Category filters' => '',
- // 'Upload a file' => '',
- // 'View file' => '',
- // 'Last activity' => '',
- // 'Change subtask position' => '',
- // 'This value must be greater than %d' => '',
- // 'Another swimlane with the same name exists in the project' => '',
- // 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => '',
- // 'Actions duplicated successfully.' => '',
- // 'Unable to duplicate actions.' => '',
- // 'Add a new action' => '',
- // 'Import from another project' => '',
- // 'There is no action at the moment.' => '',
- // 'Import actions from another project' => '',
- // 'There is no available project.' => '',
- // 'Local File' => '',
- // 'Configuration' => '',
- // 'PHP version:' => '',
- // 'PHP SAPI:' => '',
- // 'OS version:' => '',
- // 'Database version:' => '',
- // 'Browser:' => '',
- // 'Task view' => '',
- // 'Edit task' => '',
- // 'Edit description' => '',
- // 'New internal link' => '',
- // 'Display list of keyboard shortcuts' => '',
- // 'Menu' => '',
- // 'Set start date' => '',
+ 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Proje bildirimleri için hiçbir plugin kaydedilmedi. Yine de profil sayfanızdan bildirim ayarları yapabilirsiniz.',
+ 'My dashboard' => 'Dashboard um',
+ 'My profile' => 'Profilim',
+ 'Project owner: ' => 'Proje sahibi',
+ 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Proje kodu opsiyoneldir ve alfanümerik olmalıdır, örneğin: PROJE1',
+ 'Project owner' => 'Proje sahibi',
+ 'Private projects do not have users and groups management.' => '(Kişiye) Özel projelerde kullanıcı ve grup yönetimi yoktur.',
+ 'There is no project member.' => 'Proje ekibi yok.',
+ 'Priority' => 'Öncelik',
+ 'Task priority' => 'Görev önceliği',
+ 'General' => 'Genel',
+ 'Dates' => 'Tarihler',
+ 'Default priority' => 'Varsayılan öncelik',
+ 'Lowest priority' => 'En düşük öncelik',
+ 'Highest priority' => 'En yüksek öncelik',
+ 'If you put zero to the low and high priority, this feature will be disabled.' => 'Eğer en düşük ve en yüksek önceliğe sıfır girerseniz, bu özellik devre dışı bırakılacak.',
+ 'Close a task when there is no activity' => 'Bir aktivite olmadığında görevi kapat',
+ 'Duration in days' => 'Süre (gün olarak)',
+ 'Send email when there is no activity on a task' => 'Bir görevde aktivite olmadığında e-posta gönder',
+ 'Unable to fetch link information.' => 'Link eşleştirilemiyor.',
+ 'Daily background job for tasks' => 'Görevler için günlük otomatik arkaplan işleri',
+ 'Auto' => 'Otomatik',
+ 'Related' => 'İlişkili',
+ 'Attachment' => 'Ek',
+ 'Title not found' => 'Başlık bulunamadı',
+ 'Web Link' => 'Web Linki',
+ 'External links' => 'Harici linkler',
+ 'Add external link' => 'Harici link ekle',
+ 'Type' => 'Tip',
+ 'Dependency' => 'Bağımlılık',
+ 'Add internal link' => 'Dahili link ekle',
+ 'Add a new external link' => 'Yeni bir harici link ekle',
+ 'Edit external link' => 'Harici linki düzenle',
+ 'External link' => 'Harici link',
+ 'Copy and paste your link here...' => 'Linkinizi kopyalayıp buraya yapıştırın...',
+ 'URL' => 'URL',
+ 'Internal links' => 'Dahili linkler',
+ 'Assign to me' => 'Bana ata',
+ 'Me' => 'Ben',
+ 'Do not duplicate anything' => 'Hiçbirşeyi duplike etme',
+ 'Projects management' => 'Proje yönetimi',
+ 'Users management' => 'Kullanıcı yönetimi',
+ 'Groups management' => 'Grup yönetimi',
+ 'Create from another project' => 'Başka bir projeden oluştur',
+ 'open' => 'açık',
+ 'closed' => 'kapalı',
+ 'Priority:' => 'Öncelik',
+ 'Reference:' => 'Referans',
+ 'Complexity:' => 'Zorluk',
+ 'Swimlane:' => 'Kulvar',
+ 'Column:' => 'Kolon',
+ 'Position:' => 'Pozisyon',
+ 'Creator:' => 'Oluşturan',
+ 'Time estimated:' => 'Tahmini zaman',
+ '%s hours' => '%s saat',
+ 'Time spent:' => 'Geçen Süre:',
+ 'Created:' => 'Oluşturuldu',
+ 'Modified:' => 'Güncellendi',
+ 'Completed:' => 'Tamamlandı',
+ 'Started:' => 'Başlatıldı',
+ 'Moved:' => 'Taşındı',
+ 'Task #%d' => 'Görev #%d',
+ 'Time format' => 'Saat formatı',
+ 'Start date: ' => 'Başlangıç tarihi',
+ 'End date: ' => 'Bitiş tarihi',
+ 'New due date: ' => 'Yeni hedef tarih',
+ 'Start date changed: ' => 'Başlangıç tarihi değişti:',
+ 'Disable private projects' => 'Özel projeleri engelle',
+ 'Do you really want to remove this custom filter: "%s"?' => 'Bu özel filtreyi gerçekten kaldırmak istiyor musunuz: "%s"?',
+ 'Remove a custom filter' => 'Özel filtre kaldır',
+ 'User activated successfully.' => 'Kullanıcı başarıyla aktifleştirildi',
+ 'Unable to enable this user.' => 'Bu kullanıcı etkinleştirilemiyor.',
+ 'User disabled successfully.' => 'Kullanıcı başarıyla engellendi.',
+ 'Unable to disable this user.' => 'Bu kullanıcı engellenemez.',
+ 'All files have been uploaded successfully.' => 'Tüm dosyalar başarıyla yüklendi.',
+ 'The maximum allowed file size is %sB.' => 'Maksimum dosya büyüklüğü %s B.',
+ 'Drag and drop your files here' => 'Dosyalarınızı buraya sürükleyip bırakın',
+ 'choose files' => 'dosyaları seç',
+ 'View profile' => 'Profili göster',
+ 'Two Factor' => 'İki faktör',
+ 'Disable user' => 'Kullanıcıyı engelle',
+ 'Do you really want to disable this user: "%s"?' => '%s kullanıcısını gerçekten engellemek istiyor musunuz?',
+ 'Enable user' => 'Kullanıcıyı etkinleştir',
+ 'Do you really want to enable this user: "%s"?' => '%s kullanıcısını gerçekten etkinleştirmek istiyor musunuz?',
+ 'Download' => 'İndir',
+ 'Uploaded: %s' => 'Yükle: %s',
+ 'Size: %s' => 'Boyut: %s',
+ 'Uploaded by %s' => '%s tarafından yüklendi',
+ 'Filename' => 'Dosya adı',
+ 'Size' => 'Boyutu',
+ 'Column created successfully.' => 'Kolon başarıyla oluşturuldu.',
+ 'Another column with the same name exists in the project' => 'Projede aynı isimli başka bir kolon var',
+ 'Default filters' => 'Varsayılan filtreler',
+ 'Your board doesn\'t have any columns!' => 'Pano nuzda kolon bulunmuyor!',
+ 'Change column position' => 'Kolon sıralamasını değiştir',
+ 'Switch to the project overview' => 'Proje özetine geç',
+ 'User filters' => 'Kullanıcı filtreleri',
+ 'Category filters' => 'Kategori filtreleri',
+ 'Upload a file' => 'Bir dosya yükle',
+ 'View file' => 'Dosyayı göster',
+ 'Last activity' => 'Son aktivite',
+ 'Change subtask position' => 'Alt görev sırasını değiştir',
+ 'This value must be greater than %d' => 'Bu değer %d den büyük olmalı',
+ 'Another swimlane with the same name exists in the project' => 'Projede aynı isimli başka bir kulvar var',
+ 'Example: http://example.kanboard.net/ (used to generate absolute URLs)' => 'Örneğin: http://ornek.dediteknoloji.com/ (sabit URLler oluşturmak için)',
+ 'Actions duplicated successfully.' => 'İşlemler başarıyla çoklandı.',
+ 'Unable to duplicate actions.' => 'İşlemler çoklanamıyor.',
+ 'Add a new action' => 'Yeni bir işlem ekle',
+ 'Import from another project' => 'Başka bir projeden aktar',
+ 'There is no action at the moment.' => 'Şu anda bir işlem yok',
+ 'Import actions from another project' => 'Başka bir projeden işlemleri aktar',
+ 'There is no available project.' => 'Uygun bir proje yok.',
+ 'Local File' => 'Yerel dosya',
+ 'Configuration' => 'Konfigürasyon',
+ 'PHP version:' => 'PHP versiyonu:',
+ 'PHP SAPI:' => 'PHP SAPI:',
+ 'OS version:' => 'OS versiyonu:',
+ 'Database version:' => 'Veritabanı versiyonu:',
+ 'Browser:' => 'Tarayıcı:',
+ 'Task view' => 'Görev görünümü',
+ 'Edit task' => 'Görev güncelle',
+ 'Edit description' => 'Açıklamayı güncelle',
+ 'New internal link' => 'Yeni iç link',
+ 'Display list of keyboard shortcuts' => 'Kısayol tuşları listesini göster',
+ 'Menu' => 'Menü',
+ 'Set start date' => 'Başlangıç tarihi belirle',
// 'Avatar' => '',
- // 'Upload my avatar image' => '',
- // 'Remove my image' => '',
- // 'The OAuth2 state parameter is invalid' => '',
- // 'User not found.' => '',
- // 'Search in activity stream' => '',
- // 'My activities' => '',
- // 'Activity until yesterday' => '',
- // 'Activity until today' => '',
- // 'Search by creator: ' => '',
- // 'Search by creation date: ' => '',
- // 'Search by task status: ' => '',
- // 'Search by task title: ' => '',
- // 'Activity stream search' => '',
- // 'Projects where "%s" is manager' => '',
- // 'Projects where "%s" is member' => '',
- // 'Open tasks assigned to "%s"' => '',
- // 'Closed tasks assigned to "%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' => '',
- // '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' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ 'Upload my avatar image' => 'Avatar resmimi yükle',
+ 'Remove my image' => 'Resmimi kaldır',
+ 'The OAuth2 state parameter is invalid' => 'OAuth2 durum parametresi geçersiz',
+ 'User not found.' => 'Kullanıcı bulunamadı',
+ 'Search in activity stream' => 'Aktivite akışında ara',
+ 'My activities' => 'Aktivitelerim',
+ 'Activity until yesterday' => 'Düne kadar olan aktiviteler',
+ 'Activity until today' => 'Bugüne kadar olan aktiviteler',
+ 'Search by creator: ' => 'Oluşturan ile ara',
+ 'Search by creation date: ' => 'Oluşturma tarihi ile ara',
+ 'Search by task status: ' => 'Görev durumu ile ara',
+ 'Search by task title: ' => 'Görev başlığı ile ara',
+ 'Activity stream search' => 'Aktivite akışı araması',
+ 'Projects where "%s" is manager' => '%s in müdürü olduğu projeler',
+ 'Projects where "%s" is member' => '%s in ekip üyesi olduğu projeler',
+ 'Open tasks assigned to "%s"' => '%s e atanan görevleri aç',
+ 'Closed tasks assigned to "%s"' => '%s e atanan görevleri kapat',
+ 'Assign automatically a color based on a priority' => 'Önceliğe bağlı olarak bir renk belirle',
+ 'Overdue tasks for the project(s) "%s"' => '%s proje(leri) için süresi geçen görevler',
+ 'Upload files' => 'Dosyaları yükle',
+ 'Installed Plugins' => 'Yüklenmiş Pluginler',
+ 'Plugin Directory' => 'Plugin Klasörü',
+ 'Plugin installed successfully.' => 'Plugin başarıyla kuruldu.',
+ 'Plugin updated successfully.' => 'Plugin başarıyla güncellendi.',
+ 'Plugin removed successfully.' => 'Plugin başarıyla kaldırıldı.',
+ 'Subtask converted to task successfully.' => 'Alt görev başarıyla göreve dönüştürüldü.',
+ 'Unable to convert the subtask.' => 'Alt görev dönüştürülemedi',
+ 'Unable to extract plugin archive.' => 'Plugin (sıkıştırılmış) dosyası açılamadı.',
+ 'Plugin not found.' => 'Plugin bulunamadı',
+ 'You don\'t have the permission to remove this plugin.' => 'Bu plugin\'i kaldırmak için yetkiniz yok.',
+ 'Unable to download plugin archive.' => 'Plugin dosyası indirilemedi.',
+ 'Unable to write temporary file for plugin.' => 'Plugin için geçici dosya oluşturulamadı.',
+ 'Unable to open plugin archive.' => 'Plugin dosyası açılamadı.',
+ 'There is no file in the plugin archive.' => 'Plugin (sıkıştırılmış) dosyasında, dosya bulunmuyor.',
+ 'Create tasks in bulk' => 'Toplu olarak görev oluştur',
+ 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Kanbord sisteminiz arayüzden plugin kurulacak şekilde ayarlanmamış.',
+ 'There is no plugin available.' => 'Uygun plugin yok.',
+ 'Install' => 'Kur',
+ 'Update' => 'Güncelle',
+ 'Up to date' => 'Güncel',
+ 'Not available' => 'Uygun değil',
+ 'Remove plugin' => 'Plugini kaldır',
+ 'Do you really want to remove this plugin: "%s"?' => '"%s" plugin i gerçekten kaldırmak istiyor musunuz?',
+ 'Uninstall' => 'Kaldır',
+ 'Listing' => 'Listeliyor',
+ 'Metadata' => 'Meta veri',
+ 'Manage projects' => 'Projeler',
+ 'Convert to task' => 'Göreve dönüştür',
+ 'Convert sub-task to task' => 'Alt görevi göreve dönüştür',
+ 'Do you really want to convert this sub-task to a task?' => 'Bu alt görevi, göreve dönüştüreceğinize emin misiniz?',
+ 'My task title' => 'Görev başlığım',
+ 'Enter one task by line.' => 'Görevleri satır satır girin',
+ 'Number of failed login:' => 'Başarısız login denemesi sayısı',
+ 'Account locked until:' => 'Hesap şu zamana kadar kilitlendi:',
+ 'Email settings' => 'E-posta ayarları',
+ 'Email sender address' => 'E-posta gönderen adresi',
+ 'Email transport' => 'E-posta taşıma',
+ 'Webhook token' => 'Webhook token',
+ 'Project tags management' => 'Proje etiket yönetimi',
+ 'Tag created successfully.' => 'Etiket başarıyla oluturuldu.',
+ 'Unable to create this tag.' => 'Etiket oluşturulamıyor.',
+ 'Tag updated successfully.' => 'Etiket başarıyla güncellendi.',
+ 'Unable to update this tag.' => 'Etiket güncellenemedi.',
+ 'Tag removed successfully.' => 'Etiket başarıyla kaldırıldı.',
+ 'Unable to remove this tag.' => 'Etiket kaldırılamadı.',
+ 'Global tags management' => 'Genel etiket yönetimi',
+ 'Tags' => 'Etiketler',
+ 'Tags management' => 'Etiket yönetimi',
+ 'Add new tag' => 'Yeni etiket ekle',
+ 'Edit a tag' => 'Etiket güncelle',
+ 'Project tags' => 'Proje etiketleri',
+ 'There is no specific tag for this project at the moment.' => 'Proje için şu anda bir etiket bulunmuyor.',
+ 'Tag' => 'Etiket',
+ 'Remove a tag' => 'Etiket kaldır',
+ 'Do you really want to remove this tag: "%s"?' => '"%s" etiketini kaldıracağınıza emin misiniz?',
+ 'Global tags' => 'Genel etiketler',
+ 'There is no global tag at the moment.' => 'Şu anda genel bir etiket bulunmuyor.',
+ 'This field cannot be empty' => 'Bu alan boş bırakılamaz',
+ 'Close a task when there is no activity in an specific column' => 'Bir kolonda hareket olmadığında bir görevi kapat',
+ '%s removed a subtask for the task #%d' => '%d görevi için %s bir alt görevi kaldırdı',
+ '%s removed a comment on the task #%d' => '%s %d görevi için bir yorumu kaldırdı',
+ 'Comment removed on task #%d' => '%d görevindeki yorum kaldırıldı',
+ 'Subtask removed on task #%d' => '%d görevindeki alt görev kaldırıldı',
+ 'Hide tasks in this column in the dashboard' => 'Panoda bu kolondaki görevleri gizle',
+ '%s removed a comment on the task %s' => '%s %s görevinde bir yorumu kaldırdı',
+ '%s removed a subtask for the task %s' => '%s %s alt görevini kaldırdı',
+ 'Comment removed' => 'Yorum kaldırıldı',
+ 'Subtask removed' => 'Alt görev kaldırıldı',
+ '%s set a new internal link for the task #%d' => '%d görevi için %s bir iç link oluşturdu',
+ '%s removed an internal link for the task #%d' => '%d görevi için %s bir iç linki kaldırdı',
+ 'A new internal link for the task #%d have been defined' => '%d görevi için yeni bir iç link tanımlandı',
+ 'Internal link removed for the task #%d' => '%d görevi için iç link kaldırıldı',
+ '%s set a new internal link for the task %s' => '%s görevi için %s yeni bir iç link oluşturdu',
+ '%s removed an internal link for the task %s' => '%s %s görevi için bir iç linki kaldırdı',
+ 'Automatically set the due date on task creation' => 'Görev oluştururken hedef tarihi otomatik ata',
+ 'Move the task to another column when closed' => 'Görev kapandığında başka bir kolona taşı',
+ 'Move the task to another column when not moved during a given period' => 'Bir kolonda belirli bir süre hareketsiz kalırsa görevi başka bir kolona taşı',
+ 'Dashboard for %s' => '%s için Pano',
+ 'Tasks overview for %s' => '%s için görev özeti',
+ 'Subtasks overview for %s' => '%s için alt görev özeti',
+ 'Projects overview for %s' => '%s için proje özeti',
+ 'Activity stream for %s' => '%s için akış',
+ 'Calendar for %s' => '%s için takvim',
+ 'Notifications for %s' => '%s için bildirimler',
+ 'Assign a color when the task is moved to a specific swimlane' => 'Görev bir kulvara taşındığında rengini değiştir',
+ 'Assign a priority when the task is moved to a specific swimlane' => 'Görev bir kulvara taşındığında önceliğini değiştir',
+ 'User unlocked successfully.' => 'Kullanıcı kilidi başarıyla kaldırıldı.',
+ 'Unable to unlock the user.' => 'Kullanıcı kilitlenemiyor.',
+ 'Move a task to another swimlane' => 'Görevi başka bir kulvara taşı',
+ 'Creator Name' => 'Oluşturan Adı',
+ 'Time spent and estimated' => 'Tahmini ve geçen süre',
+ 'Move position' => 'Taşıma sırası',
+ 'Move task to another position on the board' => 'Görevin sırasını değiştir',
+ 'Insert before this task' => 'Bu görevin öncesine oluştur',
+ 'Insert after this task' => 'Bu görevin ardına oluştur',
+ 'Unlock this user' => 'Bu kullanıcının kilidini kaldır',
+ 'Custom Project Roles' => 'Ek Proje Rolleri',
+ 'Add a new custom role' => 'Proje rolü ekle',
+ 'Restrictions for the role "%s"' => '"%s" rolü için kısıtlamalar',
+ 'Add a new project restriction' => 'Yeni kısıtlama ekle',
+ 'Add a new drag and drop restriction' => 'Sürükle bırak kısıtlaması ekle',
+ 'Add a new column restriction' => 'Kolon kısıtlaması ekle',
+ 'Edit this role' => 'Rolü güncelle',
+ 'Remove this role' => 'Rolü kaldır',
+ 'There is no restriction for this role.' => 'Bu rol için bir kısıtlama yok',
+ 'Only moving task between those columns is permitted' => 'Sadece belirlenen kolonlar arasında taşıma mümkündür',
+ 'Close a task in a specific column when not moved during a given period' => 'Bir kolonda belirli bir süre hareketsiz kalan görev kapatılsın',
+ 'Edit columns' => 'Kolonları güncelle',
+ 'The column restriction has been created successfully.' => 'Kolon kısıtlaması başarıyla oluşturuldu.',
+ 'Unable to create this column restriction.' => 'Kolon kısıtlaması oluşturulamadı.',
+ 'Column restriction removed successfully.' => 'Kolon kısıtlaması başarıyla kaldırıldı.',
+ 'Unable to remove this restriction.' => 'Bu kısıtlama kaldırılamıyor.',
+ 'Your custom project role has been created successfully.' => 'Ek proje rolü başarıyla oluşturuldu.',
+ 'Unable to create custom project role.' => 'Ek proje rolü oluşturulamadı',
+ 'Your custom project role has been updated successfully.' => 'Ek proje rolü başarıyla güncellendi.',
+ 'Unable to update custom project role.' => 'Ek proje rolü güncellenemiyor.',
+ 'Custom project role removed successfully.' => 'Ek proje rolü başarıyla kaldırıldı.',
+ 'Unable to remove this project role.' => 'Proje rolü kaldırılamıyor.',
+ 'The project restriction has been created successfully.' => 'Proje kısıtlaması başarıyla oluşturuldu.',
+ 'Unable to create this project restriction.' => 'Proje kısıtlaması oluşturulamadı',
+ 'Project restriction removed successfully.' => 'Proje kısıtlaması başarıyla kaldırıldı.',
+ 'You cannot create tasks in this column.' => 'Bu kolonda görev oluşturamazsınız.',
+ 'Task creation is permitted for this column' => 'Bu kolonda görev oluşturulabilir.',
+ 'Closing or opening a task is permitted for this column' => 'Bu kolonda görev açma yahut kapatma yapılabilir.',
+ 'Task creation is blocked for this column' => 'Bu kolonda görev oluşturma bloke edilmiştir.',
+ 'Closing or opening a task is blocked for this column' => 'Bu kolonda görev açma yahut kapatma bloke edilmiştir.',
+ 'Task creation is not permitted' => 'Görev oluşturmaya izin verilmemiştir',
+ 'Closing or opening a task is not permitted' => 'Görev açmaya yahut kapatmaya izin verilmemiştir',
+ 'New drag and drop restriction for the role "%s"' => '"%s" rolü için yeni sürükle bırak kısıtlaması',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Bu roldeki kullanıcılar sadece kaynak ve hedef kolonlar arasında görevi hareket ettirebilir.',
+ 'Remove a column restriction' => 'Kolon kısıtlamasını kaldır',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Kolon kısıtlamasının "%s" den "%s" e değiştireceğinize emin misiniz?',
+ 'New column restriction for the role "%s"' => '"%s" rolü için yeni kolon kısıtlaması',
+ 'Rule' => 'Kural',
+ 'Do you really want to remove this column restriction?' => 'Kolon kısıtlamasını kaldırmak istediğinize emin misiniz?',
+ 'Custom roles' => 'Ek roller',
+ 'New custom project role' => 'Yeni ek proje rolü',
+ 'Edit custom project role' => 'Ek proje rolünü güncelle',
+ 'Remove a custom role' => 'Ek rolü kaldır',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '"%s" ek rolünü kaldıracağınıza emin misiniz? Bu role ait tüm kullanıcılar proje kullanıcısı olacak.',
+ 'There is no custom role for this project.' => 'Bu proje için ek rol bulunmuyor.',
+ 'New project restriction for the role "%s"' => '"%s" rolü için yeni proje kısıtlaması',
+ 'Restriction' => 'Kısıtlama',
+ 'Remove a project restriction' => 'Proje kısıtlaması kaldır',
+ 'Do you really want to remove this project restriction: "%s"?' => '"%s" proje kısıtlamasını kaldırmak istediğinize emin misiniz?',
+ 'Duplicate to multiple projects' => 'Birden çok projeye çokla',
+ 'This field is required' => 'Bu alan gerekli',
+ 'Moving a task is not permitted' => 'Görev taşımaya izin verilmemiş',
+ 'This value must be in the range %d to %d' => 'Bu değer şu aralıkta olmalı: "%d" "%d"',
+ 'You are not allowed to move this task.' => 'Bu görevi taşımaya izniniz yok.',
+ 'API User Access' => 'API Kullanıcı Erişimi',
+ 'Preview' => 'Öngörünüm',
+ 'Write' => 'Yaz',
+ 'Write your text in Markdown' => 'Metninizi Markdown a yazın',
+ 'New External Task: %s' => 'Yeni Harici Görev: %s',
+ 'No personal API access token registered.' => 'Kişisel API erişim belirteç-token ınız kaydedilmedi.',
+ 'Your personal API access token is "%s"' => 'Kişisel API erişim belirteç-token ınız"%s"',
+ 'Remove your token' => 'Belirteç-token ınızı kaldır',
+ 'Generate a new token' => 'Yeni bir belirteç-token oluştur',
+ // 'Showing %d-%d of %d' => '',
+ // 'Outgoing Emails' => '',
+ 'Add or change currency rate' => 'Kur Oranını ekle veya değiştir',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index d000b706..0f91e29f 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -61,19 +61,16 @@ return array(
'%d tasks on the board' => '看板目前有%d个任务',
'%d tasks in total' => '总共有%d个任务',
'Unable to update this board.' => '无法更新该看板。',
- 'Edit board' => '修改看板',
'Disable' => '停用',
'Enable' => '启用',
'New project' => '新建项目',
'Do you really want to remove this project: "%s"?' => '确定要移除项目"%s"吗?',
'Remove project' => '移除项目',
'Edit the board for "%s"' => '为"%s"修改看板',
- 'All projects' => '所有项目',
'Add a new column' => '添加新栏目',
'Title' => '标题',
'Assigned to %s' => '指派给 %s',
'Remove a column' => '移除一个栏目',
- 'Remove a column from a board' => '从看板移除一个栏目',
'Unable to remove this column.' => '无法移除该栏目。',
'Do you really want to remove this column: "%s"?' => '确定要移除栏目"%s"吗?',
'This action will REMOVE ALL TASKS associated to this column!' => '该动作将移除与该栏目相关的所有项目!',
@@ -88,7 +85,6 @@ return array(
'(VACUUM command)' => '(VACUUM 指令)',
'(Gzip compressed Sqlite file)' => '(用Gzip压缩的Sqlite文件)',
'Close a task' => '关闭一个任务',
- 'Edit a task' => '修改一个任务',
'Column' => '栏目',
'Color' => '颜色',
'Assignee' => '负责人',
@@ -105,7 +101,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' => '用户名必须唯一',
@@ -162,9 +158,7 @@ return array(
'Task count' => '任务数',
'User' => '用户',
'Comments' => '评论',
- 'Leave a comment' => '留言',
'Comment is required' => '必须得有评论',
- 'Leave a description' => '给一个描述',
'Comment added successfully.' => '评论成功添加。',
'Unable to create your comment.' => '无法创建评论。',
'Due Date' => '到期时间',
@@ -226,7 +220,6 @@ return array(
'Search' => '查找',
'Nothing found.' => '没找到。',
'Due date' => '到期时间',
- 'Others formats accepted: %s and %s' => '可以使用的其它格式:%s 和 %s',
'Description' => '描述',
'%d comments' => '%d个评论',
'%d comment' => '%d个评论',
@@ -299,9 +292,7 @@ return array(
'Display another project' => '显示其它项目',
'Created by %s' => '创建者:%s',
'Tasks Export' => '任务导出',
- 'Tasks exportation for "%s"' => '导出"%s"的任务',
'Start Date' => '开始时间',
- 'End Date' => '结束时间',
'Execute' => '执行',
'Task Id' => '任务ID',
'Creator' => '创建者',
@@ -322,14 +313,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' => '公开访问',
@@ -350,8 +336,8 @@ return array(
'Remote' => '远程',
'Enabled' => '启用',
'Disabled' => '停用',
- 'Username:' => '用户名:',
- 'Name:' => '姓名:',
+ 'Login:' => '用户名:',
+ 'Full Name:' => '姓名:',
'Email:' => '电子邮件:',
'Notifications:' => '通知:',
'Notifications' => '通知',
@@ -386,14 +372,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' => '选择一个事件',
@@ -442,13 +426,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.' => '数据不足,无法绘图。',
@@ -465,10 +446,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' => '活动里程碑',
@@ -494,7 +473,6 @@ return array(
'Subtask Id' => '子任务 Id',
'Subtasks' => '子任务',
'Subtasks Export' => '子任务导出',
- 'Subtasks exportation for "%s"' => '导出"%s"的子任务',
'Task Title' => '任务标题',
'Untitled' => '无标题',
'Application default' => '程序默认',
@@ -532,10 +510,8 @@ return array(
'Link labels' => '关联标签',
'Link modification' => '关联修改',
'Links' => '关联',
- 'Link settings' => '关联设置',
'Opposite label' => '反向标签',
'Remove a link' => '删除关联',
- 'Task\'s links' => '任务的关联',
'The labels must be different' => '标签不能一样',
'There is no link.' => '当前没有关联',
'This label must be unique' => '关联必须唯一',
@@ -568,7 +544,6 @@ return array(
'Compact view' => '紧凑视图',
'Horizontal scrolling' => '水平滚动',
'Compact/wide view' => '紧凑/宽视图',
- 'No results match:' => '无匹配结果:',
'Currency' => '货币',
'Private project' => '私人项目',
'AUD - Australian Dollar' => '澳元',
@@ -582,6 +557,7 @@ return array(
'JPY - Japanese Yen' => '日元',
'NZD - New Zealand Dollar' => '新西兰元',
'RSD - Serbian dinar' => '第纳尔',
+ 'CNY - Chinese Yuan' => '人民币',
'USD - US Dollar' => '美元',
'Destination column' => '目标栏目',
'Move the task to another column when assigned to a user' => '指定负责人时移动到其它栏目',
@@ -596,12 +572,11 @@ return array(
'Currency rates' => '汇率',
'Rate' => '汇率',
'Change reference currency' => '修改参考货币',
- 'Add a new currency rate' => '添加新汇率',
'Reference currency' => '参考货币',
'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' => '检查双重认证码',
@@ -615,7 +590,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',
@@ -680,14 +654,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移动到了首个里程碑',
@@ -725,7 +693,6 @@ return array(
'<30m' => '小于30分钟',
'Stop timer' => '停止计时器',
'Start timer' => '开启计时器',
- 'Add project member' => '添加项目成员',
'My activity stream' => '我的活动流',
'My calendar' => '我的日程表',
'Search tasks' => '搜索任务',
@@ -758,8 +725,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条任务在每个任务栏下的平均花费时间',
@@ -776,14 +741,12 @@ return array(
// 'Time spent into each column' => '',
// 'The lead time is the duration between the task creation and the completion.' => '',
// 'The cycle time is the duration between the start date and the completion.' => '',
- // 'If the task is not closed the current time is used instead of the completion date.' => '',
- // 'Set automatically the start date' => '',
+ 'If the task is not closed the current time is used instead of the completion date.' => '如果当前任务未关闭时用当前时间代替完成时间',
+ 'Set automatically the start date' => '设置自动开始日期',
'Edit Authentication' => '编辑认证信息',
'Remote user' => '远程用户',
'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => '远程用户不会在看板数据库保存密码,例如:LDAP,GOOGLE,GitHub。',
'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => '如果选中“禁止登陆来自”,登陆表单内的验证信息将被忽略。',
- 'New remote user' => '新加远程用户',
- 'New local user' => '新加本地用户',
'Default task color' => '默认任务颜色',
'This feature does not work with all browsers.' => '本功能只在部分浏览器下工作正常。',
'There is no destination project available.' => '当前没有目标项目可用',
@@ -800,7 +763,6 @@ return array(
'License:' => '授权许可:',
'License' => '授权许可',
'Enter the text below' => '输入下方的文本',
- 'Gantt chart for %s' => '%s的甘特图',
'Sort by position' => '按位置排序',
'Sort by date' => '按日期排序',
'Add task' => '添加任务',
@@ -841,8 +803,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.' => '成功创建过滤器',
@@ -880,7 +840,6 @@ return array(
'Owner' => '所有人',
'Unread notifications' => '未读通知',
'Notification methods:' => '通知提醒方式:',
- 'Import tasks from CSV file' => '从CSV文件导入任务',
'Unable to read your file' => '无法读取文件',
'%d task(s) have been imported successfully.' => '成功导入%d条任务。',
'Nothing have been imported!' => '没有信息被导入!',
@@ -947,7 +906,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.' => '当前没有用户组',
@@ -968,13 +926,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' => '花费小时数',
@@ -1012,7 +967,6 @@ return array(
'Project owner: ' => '项目负责人',
'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '项目标识符是可选的,且只能是数字或字母组成,例如:MYPROJECT。',
'Project owner' => '项目负责人',
- 'Those dates are useful for the project Gantt chart.' => '那些时间对于项目甘特图非常有用',
'Private projects do not have users and groups management.' => '私有项目下没有成员或组可管理',
'There is no project member.' => '当前没有项目成员',
'Priority' => '优先级',
@@ -1069,7 +1023,6 @@ return array(
'Started:' => '已开始:',
'Moved:' => '已移走',
'Task #%d' => '任务#%d',
- 'Date and time format' => '时间和日期格式',
'Time format' => '时间格式',
'Start date: ' => '开始时间:',
'End date: ' => '结束时间:',
@@ -1083,9 +1036,7 @@ return array(
'User disabled successfully.' => '用户已禁用。',
'Unable to disable this user.' => '无法禁用该用户。',
'All files have been uploaded successfully.' => '所有文件已成功上传。',
- 'View uploaded files' => '查看已上传文件',
'The maximum allowed file size is %sB.' => '最大上传尺寸 %sB',
- 'Choose files again' => '重新选择文件',
'Drag and drop your files here' => '拖放文件到这里',
'choose files' => '选择文件',
'View profile' => '查看个人信息',
@@ -1122,99 +1073,238 @@ return array(
'There is no action at the moment.' => '当前没有动作。',
'Import actions from another project' => '从另一个项目中导入动作',
'There is no available project.' => '当前没有可用项目',
- // 'Local File' => '',
- // 'Configuration' => '',
+ 'Local File' => '本地文件',
+ 'Configuration' => '配置选项',
// 'PHP version:' => '',
// 'PHP SAPI:' => '',
// 'OS version:' => '',
// 'Database version:' => '',
// 'Browser:' => '',
- // 'Task view' => '',
- // 'Edit task' => '',
- // 'Edit description' => '',
- // 'New internal link' => '',
- // 'Display list of keyboard shortcuts' => '',
- // 'Menu' => '',
- // 'Set start date' => '',
- // 'Avatar' => '',
- // 'Upload my avatar image' => '',
- // 'Remove my image' => '',
- // 'The OAuth2 state parameter is invalid' => '',
- // 'User not found.' => '',
- // 'Search in activity stream' => '',
- // 'My activities' => '',
- // 'Activity until yesterday' => '',
- // 'Activity until today' => '',
- // 'Search by creator: ' => '',
- // 'Search by creation date: ' => '',
- // 'Search by task status: ' => '',
- // 'Search by task title: ' => '',
- // 'Activity stream search' => '',
- // 'Projects where "%s" is manager' => '',
- // 'Projects where "%s" is member' => '',
- // 'Open tasks assigned to "%s"' => '',
- // 'Closed tasks assigned to "%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' => '',
- // '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' => '',
- // 'Hide tasks in this column in the Dashboard' => '',
+ 'Task view' => '任务浏览',
+ 'Edit task' => '编辑任务',
+ 'Edit description' => '编辑描述',
+ 'New internal link' => '新建内部链接',
+ 'Display list of keyboard shortcuts' => '显示快捷键列表',
+ 'Menu' => '菜单',
+ 'Set start date' => '设置开始日期',
+ 'Avatar' => '头像',
+ 'Upload my avatar image' => '上传我的头像',
+ 'Remove my image' => '删除我的头像',
+ 'The OAuth2 state parameter is invalid' => 'OAuth2状态参数无效',
+ 'User not found.' => '用户未找到',
+ 'Search in activity stream' => '在活动足迹里搜索',
+ 'My activities' => '我的活动足迹',
+ 'Activity until yesterday' => '今天以前的活动足迹',
+ 'Activity until today' => '今天为止的活动足迹',
+ 'Search by creator: ' => '以创建者搜索',
+ 'Search by creation date: ' => '以创建日期搜索',
+ 'Search by task status: ' => '以任务状态搜索',
+ 'Search by task title: ' => '以任务标题搜索',
+ 'Activity stream search' => '活动足迹搜索',
+ 'Projects where "%s" is manager' => '"%s" 管理的项目',
+ '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"' => '"%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' => 'Web钩子Token',
+ '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的通知',
+ 'Assign a color when the task is moved to a specific swimlane' => '当任务移动到指定里程碑时标记颜色',
+ 'Assign a priority when the task is moved to a specific swimlane' => '当任务移动到指定里程碑时标记优先级',
+ 'User unlocked successfully.' => '用户解锁成功。',
+ 'Unable to unlock the user.' => '无法解锁用户。',
+ 'Move a task to another swimlane' => '移动任务到里程碑',
+ 'Creator Name' => '创建人名称',
+ 'Time spent and estimated' => '时间花费预估',
+ 'Move position' => '移动位置',
+ 'Move task to another position on the board' => '移动任务到另一个位置',
+ 'Insert before this task' => '在此任务之前插入',
+ 'Insert after this task' => '在此任务之后插入',
+ 'Unlock this user' => '解锁用户',
+ 'Custom Project Roles' => '自定义项目角色',
+ 'Add a new custom role' => '添加新角色',
+ 'Restrictions for the role "%s"' => '"%s"角色限制',
+ 'Add a new project restriction' => '添加新的项目限制',
+ 'Add a new drag and drop restriction' => '添加新的拖放限制',
+ 'Add a new column restriction' => '添加新的栏目限制',
+ 'Edit this role' => '编辑角色',
+ 'Remove this role' => '删除角色',
+ 'There is no restriction for this role.' => '当前角色无限制。',
+ 'Only moving task between those columns is permitted' => '只能在以下栏目间移动任务',
+ 'Close a task in a specific column when not moved during a given period' => '当指定栏目下的任务在指定时间内未移动时关闭任务',
+ 'Edit columns' => '编辑栏目',
+ 'The column restriction has been created successfully.' => '成功创建栏目限制。',
+ 'Unable to create this column restriction.' => '无法创建栏目限制。',
+ 'Column restriction removed successfully.' => '成功删除栏目限制。',
+ 'Unable to remove this restriction.' => '无法删除限制。',
+ 'Your custom project role has been created successfully.' => '自定义项目角色创建成功。',
+ 'Unable to create custom project role.' => '无法删除自定义项目角色。',
+ 'Your custom project role has been updated successfully.' => '自定义项目角色更新成功。',
+ 'Unable to update custom project role.' => '无法更新自定义项目角色。',
+ 'Custom project role removed successfully.' => '成功删除自定义项目角色。',
+ 'Unable to remove this project role.' => '无法删除项目角色。',
+ 'The project restriction has been created successfully.' => '成功创建项目限制。',
+ 'Unable to create this project restriction.' => '无法创建项目限制。',
+ 'Project restriction removed successfully.' => '成功删除项目限制。',
+ 'You cannot create tasks in this column.' => '你不能在此栏目下创建任务。',
+ 'Task creation is permitted for this column' => '当前栏目下不能创建任务。',
+ 'Closing or opening a task is permitted for this column' => '当前栏目下不能开关任务。',
+ 'Task creation is blocked for this column' => '当前栏目下禁止创建任务。',
+ 'Closing or opening a task is blocked for this column' => '禁止在此栏下开关任务',
+ 'Task creation is not permitted' => '不能创建任务',
+ 'Closing or opening a task is not permitted' => '禁止开关任务',
+ 'New drag and drop restriction for the role "%s"' => '为角色"%s"新建拖动限制',
+ 'People belonging to this role will be able to move tasks only between the source and the destination column.' => '此角色下的用户将只能在源栏目和目标栏目间移动任务。',
+ 'Remove a column restriction' => '移除栏目限制',
+ 'Do you really want to remove this column restriction: "%s" to "%s"?' => '你真的要删除栏目限制:"%s"到"%s"?',
+ 'New column restriction for the role "%s"' => '为角色"%s"增加栏目限制?',
+ 'Rule' => '规则',
+ 'Do you really want to remove this column restriction?' => '你真的要移除栏目限制?',
+ 'Custom roles' => '自定义角色',
+ 'New custom project role' => '新建项目角色',
+ 'Edit custom project role' => '编辑项目角色',
+ 'Remove a custom role' => '删除自定义角色',
+ 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '你真的要删除这个自定义橘色:"%s"?当前角色下的用户将转换成普通项目成员。',
+ 'There is no custom role for this project.' => '当前项目没有自定义角色',
+ 'New project restriction for the role "%s"' => '给角色"%s"新建项目限制',
+ 'Restriction' => '限制',
+ 'Remove a project restriction' => '移除项目限制',
+ 'Do you really want to remove this project restriction: "%s"?' => '你真的要删除项目限制:"%s"?',
+ 'Duplicate to multiple projects' => '复制到多个项目',
+ 'This field is required' => '此项必填',
+ 'Moving a task is not permitted' => '禁止移动任务',
+ 'This value must be in the range %d to %d' => '输入值必须在%d到%d之间',
+ 'You are not allowed to move this task.' => '你不能移动此任务',
+ // 'API User Access' => '',
+ // 'Preview' => '',
+ // 'Write' => '',
+ // 'Write your text in Markdown' => '',
+ // 'New External Task: %s' => '',
+ // 'No personal API access token registered.' => '',
+ // 'Your personal API access token is "%s"' => '',
+ // 'Remove your token' => '',
+ // 'Generate a new token' => '',
+ 'Showing %d-%d of %d' => '本页显示 %d-%d 条,共有: %d 条',
+ // 'Outgoing Emails' => '',
+ // 'Add or change currency rate' => '',
+ // 'Reference currency: %s' => '',
+ // 'Add custom filters' => '',
+ // 'Export' => '',
+ // 'Add link label' => '',
+ // 'Incompatible Plugins' => '',
+ // 'Compatibility' => '',
+ // 'Permissions and ownership' => '',
+ // 'Priorities' => '',
+ // 'Close this window' => '',
+ // 'Unable to upload this file.' => '',
+ // 'Import tasks' => '',
+ // 'Choose a project' => '',
+ // 'Profile' => '',
+ // 'Application role' => '',
+ // '%d invitations were sent.' => '',
+ // '%d invitation was sent.' => '',
+ // 'Unable to create this user.' => '',
+ // 'Kanboard Invitation' => '',
+ // 'Visible on dashboard' => '',
+ // 'Created at:' => '',
+ // 'Updated at:' => '',
+ // 'There is no custom filter.' => '',
+ // 'New User' => '',
+ // 'Authentication' => '',
+ // 'If checked, this user will use a third-party system for authentication.' => '',
+ // 'The password is necessary only for local users.' => '',
+ // 'You have been invited to register on Kanboard.' => '',
+ // 'Click here to join your team' => '',
+ // 'Invite people' => '',
+ // 'Emails' => '',
+ // 'Enter one email address by line.' => '',
+ // 'Add these people to this project' => '',
+ // 'Add this person to this project' => '',
+ // 'Sign-up' => '',
+ // 'Credentials' => '',
+ // 'New user' => '',
+ // 'This username is already taken' => '',
);
diff --git a/app/Middleware/BootstrapMiddleware.php b/app/Middleware/BootstrapMiddleware.php
index 727f600c..778221b3 100644
--- a/app/Middleware/BootstrapMiddleware.php
+++ b/app/Middleware/BootstrapMiddleware.php
@@ -32,6 +32,7 @@ class BootstrapMiddleware extends BaseMiddleware
{
$this->response->withContentSecurityPolicy($this->container['cspRules']);
$this->response->withSecurityHeaders();
+ $this->response->withP3P();
if (ENABLE_XFRAME) {
$this->response->withXframe();
diff --git a/app/Middleware/PostAuthenticationMiddleware.php b/app/Middleware/PostAuthenticationMiddleware.php
index f7eccbce..8ad1f1a3 100644
--- a/app/Middleware/PostAuthenticationMiddleware.php
+++ b/app/Middleware/PostAuthenticationMiddleware.php
@@ -26,9 +26,9 @@ class PostAuthenticationMiddleware extends BaseMiddleware
if ($this->request->isAjax()) {
$this->response->text('Not Authorized', 401);
+ } else {
+ $this->response->redirect($this->helper->url->to('TwoFactorController', 'code'));
}
-
- $this->response->redirect($this->helper->url->to('TwoFactorController', 'code'));
}
$this->next();
diff --git a/app/Model/ActionParameterModel.php b/app/Model/ActionParameterModel.php
index 9895da0f..cdac396e 100644
--- a/app/Model/ActionParameterModel.php
+++ b/app/Model/ActionParameterModel.php
@@ -157,6 +157,9 @@ class ActionParameterModel extends Base
case 'user_id':
case 'owner_id':
return $this->projectPermissionModel->isAssignable($project_id, $value) ? $value : false;
+ case 'swimlane_id':
+ $column = $this->swimlaneModel->getById($value);
+ return empty($column) ? false : $this->swimlaneModel->getIdByName($project_id, $column['name']) ?: false;
default:
return $value;
}
diff --git a/app/Model/ColorModel.php b/app/Model/ColorModel.php
index 9e69dda2..9fa7ff85 100644
--- a/app/Model/ColorModel.php
+++ b/app/Model/ColorModel.php
@@ -154,6 +154,8 @@ class ColorModel extends Base
$listing[$color_id] = t($color['name']);
}
+ $this->hook->reference('model:color:get-list', $listing);
+
return $listing;
}
diff --git a/app/Model/ColumnModel.php b/app/Model/ColumnModel.php
index 0a9c55a8..5498ef54 100644
--- a/app/Model/ColumnModel.php
+++ b/app/Model/ColumnModel.php
@@ -138,11 +138,12 @@ 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 = '', $hide_in_dashboard = 0)
{
@@ -166,6 +167,7 @@ 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 = '', $hide_in_dashboard = 0)
diff --git a/app/Model/ColumnMoveRestrictionModel.php b/app/Model/ColumnMoveRestrictionModel.php
new file mode 100644
index 00000000..c2603efd
--- /dev/null
+++ b/app/Model/ColumnMoveRestrictionModel.php
@@ -0,0 +1,122 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class ColumnMoveRestrictionModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class ColumnMoveRestrictionModel extends Base
+{
+ const TABLE = 'column_has_move_restrictions';
+
+ /**
+ * Fetch one restriction
+ *
+ * @param int $project_id
+ * @param int $restriction_id
+ * @return array|null
+ */
+ public function getById($project_id, $restriction_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.restriction_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.role_id',
+ self::TABLE.'.src_column_id',
+ self::TABLE.'.dst_column_id',
+ 'pr.role',
+ 'sc.title as src_column_title',
+ 'dc.title as dst_column_title'
+ )
+ ->left(ColumnModel::TABLE, 'sc', 'id', self::TABLE, 'src_column_id')
+ ->left(ColumnModel::TABLE, 'dc', 'id', self::TABLE, 'dst_column_id')
+ ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->eq(self::TABLE.'.restriction_id', $restriction_id)
+ ->findOne();
+ }
+
+ /**
+ * Get all project column restrictions
+ *
+ * @param int $project_id
+ * @return array
+ */
+ public function getAll($project_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.restriction_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.role_id',
+ self::TABLE.'.src_column_id',
+ self::TABLE.'.dst_column_id',
+ 'pr.role',
+ 'sc.title as src_column_title',
+ 'dc.title as dst_column_title'
+ )
+ ->left(ColumnModel::TABLE, 'sc', 'id', self::TABLE, 'src_column_id')
+ ->left(ColumnModel::TABLE, 'dc', 'id', self::TABLE, 'dst_column_id')
+ ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->findAll();
+ }
+
+ /**
+ * Get all sortable column Ids
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return array
+ */
+ public function getSortableColumns($project_id, $role)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(self::TABLE.'.src_column_id', self::TABLE.'.dst_column_id')
+ ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->eq('pr.role', $role)
+ ->findAll();
+ }
+
+ /**
+ * Create a new column restriction
+ *
+ * @param int $project_id
+ * @param int $role_id
+ * @param int $src_column_id
+ * @param int $dst_column_id
+ * @return bool|int
+ */
+ public function create($project_id, $role_id, $src_column_id, $dst_column_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->persist(array(
+ 'project_id' => $project_id,
+ 'role_id' => $role_id,
+ 'src_column_id' => $src_column_id,
+ 'dst_column_id' => $dst_column_id,
+ ));
+ }
+
+ /**
+ * Remove a permission
+ *
+ * @param int $restriction_id
+ * @return bool
+ */
+ public function remove($restriction_id)
+ {
+ return $this->db->table(self::TABLE)->eq('restriction_id', $restriction_id)->remove();
+ }
+}
diff --git a/app/Model/ColumnRestrictionModel.php b/app/Model/ColumnRestrictionModel.php
new file mode 100644
index 00000000..92b2ac60
--- /dev/null
+++ b/app/Model/ColumnRestrictionModel.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class ColumnRestrictionModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class ColumnRestrictionModel extends Base
+{
+ const TABLE = 'column_has_restrictions';
+
+ const RULE_ALLOW_TASK_CREATION = 'allow.task_creation';
+ const RULE_ALLOW_TASK_OPEN_CLOSE = 'allow.task_open_close';
+ const RULE_BLOCK_TASK_CREATION = 'block.task_creation';
+ const RULE_BLOCK_TASK_OPEN_CLOSE = 'block.task_open_close';
+
+ /**
+ * Get rules
+ *
+ * @return array
+ */
+ public function getRules()
+ {
+ return array(
+ self::RULE_ALLOW_TASK_CREATION => t('Task creation is permitted for this column'),
+ self::RULE_ALLOW_TASK_OPEN_CLOSE => t('Closing or opening a task is permitted for this column'),
+ self::RULE_BLOCK_TASK_CREATION => t('Task creation is blocked for this column'),
+ self::RULE_BLOCK_TASK_OPEN_CLOSE => t('Closing or opening a task is blocked for this column'),
+ );
+ }
+
+ /**
+ * Fetch one restriction
+ *
+ * @param int $project_id
+ * @param int $restriction_id
+ * @return array|null
+ */
+ public function getById($project_id, $restriction_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.restriction_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.role_id',
+ self::TABLE.'.column_id',
+ self::TABLE.'.rule',
+ 'pr.role',
+ 'c.title as column_title'
+ )
+ ->left(ColumnModel::TABLE, 'c', 'id', self::TABLE, 'column_id')
+ ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->eq(self::TABLE.'.restriction_id', $restriction_id)
+ ->findOne();
+ }
+
+ /**
+ * Get all project column restrictions
+ *
+ * @param int $project_id
+ * @return array
+ */
+ public function getAll($project_id)
+ {
+ $rules = $this->getRules();
+ $restrictions = $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.restriction_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.role_id',
+ self::TABLE.'.column_id',
+ self::TABLE.'.rule',
+ 'pr.role',
+ 'c.title as column_title'
+ )
+ ->left(ColumnModel::TABLE, 'c', 'id', self::TABLE, 'column_id')
+ ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->findAll();
+
+ foreach ($restrictions as &$restriction) {
+ $restriction['title'] = $rules[$restriction['rule']];
+ }
+
+ return $restrictions;
+ }
+
+ /**
+ * Get restrictions
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return array
+ */
+ public function getAllByRole($project_id, $role)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.restriction_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.role_id',
+ self::TABLE.'.column_id',
+ self::TABLE.'.rule',
+ 'pr.role'
+ )
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->eq('pr.role', $role)
+ ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
+ ->findAll();
+ }
+
+ /**
+ * Create a new column restriction
+ *
+ * @param int $project_id
+ * @param int $role_id
+ * @param int $column_id
+ * @param int $rule
+ * @return bool|int
+ */
+ public function create($project_id, $role_id, $column_id, $rule)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->persist(array(
+ 'project_id' => $project_id,
+ 'role_id' => $role_id,
+ 'column_id' => $column_id,
+ 'rule' => $rule,
+ ));
+ }
+
+ /**
+ * Remove a permission
+ *
+ * @param int $restriction_id
+ * @return bool
+ */
+ public function remove($restriction_id)
+ {
+ return $this->db->table(self::TABLE)->eq('restriction_id', $restriction_id)->remove();
+ }
+}
diff --git a/app/Model/CommentModel.php b/app/Model/CommentModel.php
index 4231f29d..e44a5ecd 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,6 +26,7 @@ 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';
/**
@@ -60,6 +60,7 @@ class CommentModel extends Base
->columns(
self::TABLE.'.id',
self::TABLE.'.date_creation',
+ self::TABLE.'.date_modification',
self::TABLE.'.task_id',
self::TABLE.'.user_id',
self::TABLE.'.comment',
@@ -69,7 +70,7 @@ class CommentModel extends Base
UserModel::TABLE.'.avatar_path'
)
->join(UserModel::TABLE, 'id', 'user_id')
- ->orderBy(self::TABLE.'.date_creation', $sorting)
+ ->orderBy(self::TABLE.'.date_modification', $sorting)
->eq(self::TABLE.'.task_id', $task_id)
->findAll();
}
@@ -90,6 +91,7 @@ class CommentModel extends Base
self::TABLE.'.task_id',
self::TABLE.'.user_id',
self::TABLE.'.date_creation',
+ self::TABLE.'.date_modification',
self::TABLE.'.comment',
self::TABLE.'.reference',
UserModel::TABLE.'.username',
@@ -127,12 +129,11 @@ class CommentModel extends Base
public function create(array $values)
{
$values['date_creation'] = time();
+ $values['date_modification'] = time();
$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;
@@ -150,10 +151,10 @@ class CommentModel extends Base
$result = $this->db
->table(self::TABLE)
->eq('id', $values['id'])
- ->update(array('comment' => $values['comment']));
+ ->update(array('comment' => $values['comment'], 'date_modification' => time()));
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;
@@ -168,6 +169,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/CurrencyModel.php b/app/Model/CurrencyModel.php
index bfd9697c..55a5b2fe 100644
--- a/app/Model/CurrencyModel.php
+++ b/app/Model/CurrencyModel.php
@@ -42,6 +42,8 @@ class CurrencyModel extends Base
'NOK' => t('NOK - Norwegian Krone'),
'BAM' => t('BAM - Konvertible Mark'),
'RUB' => t('RUB - Russian Ruble'),
+ 'CNY' => t('CNY - Chinese Yuan'),
+ 'TRL' => t('TRL - Turkish Lira'),
);
}
diff --git a/app/Model/FileModel.php b/app/Model/FileModel.php
index 8cdea9a0..b5852b08 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;
}
@@ -212,9 +210,7 @@ abstract class FileModel extends Base
*/
public function isImage($filename)
{
- $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
-
- switch ($extension) {
+ switch (get_file_extension($filename)) {
case 'jpeg':
case 'jpg':
case 'png':
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/InviteModel.php b/app/Model/InviteModel.php
new file mode 100644
index 00000000..13d75f69
--- /dev/null
+++ b/app/Model/InviteModel.php
@@ -0,0 +1,73 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Security\Token;
+
+/**
+ * Class InviteModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class InviteModel extends Base
+{
+ const TABLE = 'invites';
+
+ public function createInvites(array $emails, $projectId)
+ {
+ $emails = array_unique($emails);
+ $nb = 0;
+
+ foreach ($emails as $email) {
+ $email = trim($email);
+
+ if (! empty($email) && $this->createInvite($email, $projectId)) {
+ $nb++;
+ }
+ }
+
+ return $nb;
+ }
+
+ protected function createInvite($email, $projectId)
+ {
+ $values = array(
+ 'email' => $email,
+ 'project_id' => $projectId,
+ 'token' => Token::getToken(),
+ );
+
+ if ($this->db->table(self::TABLE)->insert($values)) {
+ $this->sendInvite($values);
+ return true;
+ }
+
+ return false;
+ }
+
+ protected function sendInvite(array $values)
+ {
+ $this->emailClient->send(
+ $values['email'],
+ $values['email'],
+ e('Kanboard Invitation'),
+ $this->template->render('user_invite/email', array('token' => $values['token']))
+ );
+ }
+
+ public function getByToken($token)
+ {
+ return $this->db->table(self::TABLE)
+ ->eq('token', $token)
+ ->findOne();
+ }
+
+ public function remove($email)
+ {
+ return $this->db->table(self::TABLE)
+ ->eq('email', $email)
+ ->remove();
+ }
+}
diff --git a/app/Model/MetadataModel.php b/app/Model/MetadataModel.php
index 6177e5f3..0ab94092 100644
--- a/app/Model/MetadataModel.php
+++ b/app/Model/MetadataModel.php
@@ -101,7 +101,8 @@ abstract class MetadataModel extends Base
if ($this->exists($entity_id, $key)) {
$results[] = $this->db->table($this->getTable())
->eq($this->getEntityKey(), $entity_id)
- ->eq('name', $key)->update(array(
+ ->eq('name', $key)
+ ->update(array(
'value' => $value,
'changed_on' => $timestamp,
'changed_by' => $user_id,
diff --git a/app/Model/NotificationModel.php b/app/Model/NotificationModel.php
index 4d697b5e..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,158 +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 $event_name
- * @param array $event_data
+ * @param string $eventName
+ * @param array $eventData
* @return integer
*/
- public function getTaskIdFromEvent($event_name, array $event_data)
+ public function getTaskIdFromEvent($eventName, array $eventData)
{
- switch ($event_name) {
- case TaskFileModel::EVENT_CREATE:
- return $event_data['file']['task_id'];
- case CommentModel::EVENT_CREATE:
- case CommentModel::EVENT_UPDATE:
- return $event_data['comment']['task_id'];
- case SubtaskModel::EVENT_CREATE:
- case SubtaskModel::EVENT_UPDATE:
- return $event_data['subtask']['task_id'];
- case TaskModel::EVENT_CREATE:
- case TaskModel::EVENT_UPDATE:
- case TaskModel::EVENT_CLOSE:
- case TaskModel::EVENT_OPEN:
- case TaskModel::EVENT_MOVE_COLUMN:
- case TaskModel::EVENT_MOVE_POSITION:
- case TaskModel::EVENT_MOVE_SWIMLANE:
- case TaskModel::EVENT_ASSIGNEE_CHANGE:
- case CommentModel::EVENT_USER_MENTION:
- case TaskModel::EVENT_USER_MENTION:
- return $event_data['task']['id'];
- case TaskModel::EVENT_OVERDUE:
- return $event_data['tasks'][0]['id'];
- default:
- return 0;
+ 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 94b83c80..d32fa367 100644
--- a/app/Model/ProjectDuplicationModel.php
+++ b/app/Model/ProjectDuplicationModel.php
@@ -159,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 850531c9..aba5eee2 100644
--- a/app/Model/ProjectModel.php
+++ b/app/Model/ProjectModel.php
@@ -318,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
@@ -419,6 +419,14 @@ class ProjectModel extends Base
$values['identifier'] = strtoupper($values['identifier']);
}
+ if (! empty($values['start_date'])) {
+ $values['start_date'] = $this->dateParser->getIsoDate($values['start_date']);
+ }
+
+ if (! empty($values['end_date'])) {
+ $values['end_date'] = $this->dateParser->getIsoDate($values['end_date']);
+ }
+
$this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end'));
return $this->exists($values['id']) &&
diff --git a/app/Model/ProjectPermissionModel.php b/app/Model/ProjectPermissionModel.php
index a7c1857c..dabd406c 100644
--- a/app/Model/ProjectPermissionModel.php
+++ b/app/Model/ProjectPermissionModel.php
@@ -62,17 +62,33 @@ class ProjectPermissionModel extends Base
->withFilter(new ProjectUserRoleProjectFilter($project_id))
->withFilter(new ProjectUserRoleUsernameFilter($input))
->getQuery()
- ->findAllByColumn('username');
+ ->columns(
+ UserModel::TABLE.'.id',
+ UserModel::TABLE.'.username',
+ UserModel::TABLE.'.name',
+ UserModel::TABLE.'.email',
+ UserModel::TABLE.'.avatar_path'
+ )
+ ->findAll();
$groupMembers = $this->projectGroupRoleQuery
->withFilter(new ProjectGroupRoleProjectFilter($project_id))
->withFilter(new ProjectGroupRoleUsernameFilter($input))
->getQuery()
- ->findAllByColumn('username');
+ ->columns(
+ UserModel::TABLE.'.id',
+ UserModel::TABLE.'.username',
+ UserModel::TABLE.'.name',
+ UserModel::TABLE.'.email',
+ UserModel::TABLE.'.avatar_path'
+ )
+ ->findAll();
- $members = array_unique(array_merge($userMembers, $groupMembers));
+ $userMembers = array_column_index_unique($userMembers, 'username');
+ $groupMembers = array_column_index_unique($groupMembers, 'username');
+ $members = array_merge($userMembers, $groupMembers);
- sort($members);
+ ksort($members);
return $members;
}
@@ -122,8 +138,13 @@ class ProjectPermissionModel extends Base
*/
public function isAssignable($project_id, $user_id)
{
- return $this->userModel->isActive($user_id) &&
- in_array($this->projectUserRoleModel->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER));
+ if ($this->userModel->isActive($user_id)) {
+ $role = $this->projectUserRoleModel->getUserRole($project_id, $user_id);
+
+ return ! empty($role) && $role !== Role::PROJECT_VIEWER;
+ }
+
+ return false;
}
/**
@@ -152,6 +173,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/ProjectRoleModel.php b/app/Model/ProjectRoleModel.php
new file mode 100644
index 00000000..962ff44f
--- /dev/null
+++ b/app/Model/ProjectRoleModel.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+use Kanboard\Core\Security\Role;
+
+/**
+ * Class ProjectRoleModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class ProjectRoleModel extends Base
+{
+ const TABLE = 'project_has_roles';
+
+ /**
+ * Get list of project roles
+ *
+ * @param int $project_id
+ * @return array
+ */
+ public function getList($project_id)
+ {
+ $defaultRoles = $this->role->getProjectRoles();
+ $customRoles = $this->db
+ ->hashtable(self::TABLE)
+ ->eq('project_id', $project_id)
+ ->getAll('role', 'role');
+
+ return $defaultRoles + $customRoles;
+ }
+
+ /**
+ * Get a role
+ *
+ * @param int $project_id
+ * @param int $role_id
+ * @return array|null
+ */
+ public function getById($project_id, $role_id)
+ {
+ return $this->db->table(self::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('role_id', $role_id)
+ ->findOne();
+ }
+
+ /**
+ * Get all project roles
+ *
+ * @param int $project_id
+ * @return array
+ */
+ public function getAll($project_id)
+ {
+ return $this->db->table(self::TABLE)
+ ->eq('project_id', $project_id)
+ ->asc('role')
+ ->findAll();
+ }
+
+ /**
+ * Get all project roles with restrictions
+ *
+ * @param int $project_id
+ * @return array
+ */
+ public function getAllWithRestrictions($project_id)
+ {
+ $roles = $this->getAll($project_id);
+
+ $column_restrictions = $this->columnRestrictionModel->getAll($project_id);
+ $column_restrictions = array_column_index($column_restrictions, 'role_id');
+ array_merge_relation($roles, $column_restrictions, 'column_restrictions', 'role_id');
+
+ $column_move_restrictions = $this->columnMoveRestrictionModel->getAll($project_id);
+ $column_move_restrictions = array_column_index($column_move_restrictions, 'role_id');
+ array_merge_relation($roles, $column_move_restrictions, 'column_move_restrictions', 'role_id');
+
+ $project_restrictions = $this->projectRoleRestrictionModel->getAll($project_id);
+ $project_restrictions = array_column_index($project_restrictions, 'role_id');
+ array_merge_relation($roles, $project_restrictions, 'project_restrictions', 'role_id');
+
+ return $roles;
+ }
+
+ /**
+ * Create a new project role
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return bool|int
+ */
+ public function create($project_id, $role)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->persist(array(
+ 'project_id' => $project_id,
+ 'role' => $role,
+ ));
+ }
+
+ /**
+ * Update a project role
+ *
+ * @param int $role_id
+ * @param int $project_id
+ * @param string $role
+ * @return bool
+ */
+ public function update($role_id, $project_id, $role)
+ {
+ $this->db->startTransaction();
+
+ $previousRole = $this->getById($project_id, $role_id);
+
+ $r1 = $this->db
+ ->table(ProjectUserRoleModel::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('role', $previousRole['role'])
+ ->update(array(
+ 'role' => $role
+ ));
+
+ $r2 = $this->db
+ ->table(ProjectGroupRoleModel::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('role', $previousRole['role'])
+ ->update(array(
+ 'role' => $role
+ ));
+
+ $r3 = $this->db
+ ->table(self::TABLE)
+ ->eq('role_id', $role_id)
+ ->eq('project_id', $project_id)
+ ->update(array(
+ 'role' => $role,
+ ));
+
+ if ($r1 && $r2 && $r3) {
+ $this->db->closeTransaction();
+ return true;
+ }
+
+ $this->db->cancelTransaction();
+ return false;
+ }
+
+ /**
+ * Remove a project role
+ *
+ * @param int $project_id
+ * @param int $role_id
+ * @return bool
+ */
+ public function remove($project_id, $role_id)
+ {
+ $this->db->startTransaction();
+
+ $role = $this->getById($project_id, $role_id);
+
+ $r1 = $this->db
+ ->table(ProjectUserRoleModel::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('role', $role['role'])
+ ->update(array(
+ 'role' => Role::PROJECT_MEMBER
+ ));
+
+ $r2 = $this->db
+ ->table(ProjectGroupRoleModel::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('role', $role['role'])
+ ->update(array(
+ 'role' => Role::PROJECT_MEMBER
+ ));
+
+ $r3 = $this->db
+ ->table(self::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('role_id', $role_id)
+ ->remove();
+
+ if ($r1 && $r2 && $r3) {
+ $this->db->closeTransaction();
+ return true;
+ }
+
+ $this->db->cancelTransaction();
+ return false;
+ }
+}
diff --git a/app/Model/ProjectRoleRestrictionModel.php b/app/Model/ProjectRoleRestrictionModel.php
new file mode 100644
index 00000000..8ccdcf9c
--- /dev/null
+++ b/app/Model/ProjectRoleRestrictionModel.php
@@ -0,0 +1,130 @@
+<?php
+
+namespace Kanboard\Model;
+
+use Kanboard\Core\Base;
+
+/**
+ * Class ProjectRoleRestrictionModel
+ *
+ * @package Kanboard\Model
+ * @author Frederic Guillot
+ */
+class ProjectRoleRestrictionModel extends Base
+{
+ const TABLE = 'project_role_has_restrictions';
+
+ const RULE_TASK_CREATION = 'task_creation';
+ const RULE_TASK_OPEN_CLOSE = 'task_open_close';
+ const RULE_TASK_MOVE = 'task_move';
+
+ /**
+ * Get rules
+ *
+ * @return array
+ */
+ public function getRules()
+ {
+ return array(
+ self::RULE_TASK_CREATION => t('Task creation is not permitted'),
+ self::RULE_TASK_OPEN_CLOSE => t('Closing or opening a task is not permitted'),
+ self::RULE_TASK_MOVE => t('Moving a task is not permitted'),
+ );
+ }
+
+ /**
+ * Get a single restriction
+ *
+ * @param integer $project_id
+ * @param integer $restriction_id
+ * @return array|null
+ */
+ public function getById($project_id, $restriction_id)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->eq('project_id', $project_id)
+ ->eq('restriction_id', $restriction_id)
+ ->findOne();
+ }
+
+ /**
+ * Get restrictions
+ *
+ * @param int $project_id
+ * @return array
+ */
+ public function getAll($project_id)
+ {
+ $rules = $this->getRules();
+ $restrictions = $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.restriction_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.role_id',
+ self::TABLE.'.rule'
+ )
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->findAll();
+
+ foreach ($restrictions as &$restriction) {
+ $restriction['title'] = $rules[$restriction['rule']];
+ }
+
+ return $restrictions;
+ }
+
+ /**
+ * Get restrictions
+ *
+ * @param int $project_id
+ * @param string $role
+ * @return array
+ */
+ public function getAllByRole($project_id, $role)
+ {
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.restriction_id',
+ self::TABLE.'.project_id',
+ self::TABLE.'.role_id',
+ self::TABLE.'.rule',
+ 'pr.role'
+ )
+ ->eq(self::TABLE.'.project_id', $project_id)
+ ->eq('role', $role)
+ ->left(ProjectRoleModel::TABLE, 'pr', 'role_id', self::TABLE, 'role_id')
+ ->findAll();
+ }
+
+ /**
+ * Create a new restriction
+ *
+ * @param int $project_id
+ * @param int $role_id
+ * @param string $rule
+ * @return bool|int
+ */
+ public function create($project_id, $role_id, $rule)
+ {
+ return $this->db->table(self::TABLE)
+ ->persist(array(
+ 'project_id' => $project_id,
+ 'role_id' => $role_id,
+ 'rule' => $rule,
+ ));
+ }
+
+ /**
+ * Remove a restriction
+ *
+ * @param integer $restriction_id
+ * @return bool
+ */
+ public function remove($restriction_id)
+ {
+ return $this->db->table(self::TABLE)->eq('restriction_id', $restriction_id)->remove();
+ }
+}
diff --git a/app/Model/ProjectUserRoleModel.php b/app/Model/ProjectUserRoleModel.php
index a0df0cfa..76094431 100644
--- a/app/Model/ProjectUserRoleModel.php
+++ b/app/Model/ProjectUserRoleModel.php
@@ -166,7 +166,7 @@ class ProjectUserRoleModel extends Base
->join(UserModel::TABLE, 'id', 'user_id')
->eq(UserModel::TABLE.'.is_active', 1)
->eq(self::TABLE.'.project_id', $project_id)
- ->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER))
+ ->neq(self::TABLE.'.role', Role::PROJECT_VIEWER)
->findAll();
$groupMembers = $this->projectGroupRoleModel->getAssignableUsers($project_id);
diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php
index a97bddbf..737a933d 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
@@ -66,7 +53,7 @@ class SubtaskModel extends Base
->join(TaskModel::TABLE, 'id', 'task_id')
->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0;
}
-
+
/**
* Get available status
*
@@ -83,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
@@ -178,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
@@ -235,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;
@@ -248,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;
}
/**
@@ -377,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();
}
/**
@@ -416,29 +246,53 @@ 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'));
+ $this->hook->reference('model:subtask:modification:prepare', $values);
+ }
- $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;
+ $this->hook->reference('model:subtask:creation:prepare', $values);
+ }
+
+ /**
+ * 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/TaskCreationModel.php b/app/Model/TaskCreationModel.php
index cd70a028..bd95c1a4 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
@@ -42,7 +41,10 @@ class TaskCreationModel extends Base
$this->taskTagModel->save($values['project_id'], $task_id, $tags);
}
- $this->fireEvents($task_id, $values);
+ $this->queueManager->push($this->taskEventJob->withParams(
+ $task_id,
+ array(TaskModel::EVENT_CREATE_UPDATE, TaskModel::EVENT_CREATE)
+ ));
}
return (int) $task_id;
@@ -51,15 +53,15 @@ 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->removeFields($values, array('another_task', 'duplicate_multiple_projects'));
$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'])) {
@@ -83,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/TaskFileModel.php b/app/Model/TaskFileModel.php
index 7603019a..0163da28 100644
--- a/app/Model/TaskFileModel.php
+++ b/app/Model/TaskFileModel.php
@@ -61,18 +61,6 @@ class TaskFileModel extends FileModel
}
/**
- * Get event name
- *
- * @abstract
- * @access protected
- * @return string
- */
- protected function getEventName()
- {
- return self::EVENT_CREATE;
- }
-
- /**
* Get projectId from fileId
*
* @access public
@@ -101,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 7268052c..3185afb7 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,19 +62,20 @@ 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.'.column_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')
@@ -103,36 +103,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',
@@ -298,59 +298,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();
}
/**
diff --git a/app/Model/TaskLinkModel.php b/app/Model/TaskLinkModel.php
index 09978eae..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,8 @@ 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
@@ -53,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();
}
/**
@@ -140,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;
}
/**
@@ -210,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;
+ $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);
- // Update the opposite link
- $values = array(
- 'task_id' => $opposite_task_id,
- 'opposite_task_id' => $task_id,
- 'link_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;
}
/**
@@ -261,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/TaskModificationModel.php b/app/Model/TaskModificationModel.php
index be5f53c8..0ae29293 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,14 +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, $original_task);
+ $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;
@@ -39,43 +38,56 @@ 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;
}
/**
@@ -89,11 +101,13 @@ class TaskModificationModel extends Base
$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', 'id'));
+ $this->helper->model->removeFields($values, array('id'));
$this->helper->model->resetFields($values, array('date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent'));
$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);
}
/**
diff --git a/app/Model/TaskPositionModel.php b/app/Model/TaskPositionModel.php
index 9fdb8f7d..aeb7edde 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;
}
@@ -72,9 +72,10 @@ class TaskPositionModel extends Base
$this->db->startTransaction();
$r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $original_swimlane_id);
$r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $new_swimlane_id);
+ $r3 = $this->saveTaskTimestamps($task_id);
$this->db->closeTransaction();
- return $r1 && $r2;
+ return $r1 && $r2 && $r3;
}
/**
@@ -94,9 +95,10 @@ class TaskPositionModel extends Base
$this->db->startTransaction();
$r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $swimlane_id);
$r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $swimlane_id);
+ $r3 = $this->saveTaskTimestamps($task_id);
$this->db->closeTransaction();
- return $r1 && $r2;
+ return $r1 && $r2 && $r3;
}
/**
@@ -167,6 +169,18 @@ class TaskPositionModel extends Base
return false;
}
+ return true;
+ }
+
+ /**
+ * Update task timestamps
+ *
+ * @access private
+ * @param integer $task_id
+ * @return bool
+ */
+ private function saveTaskTimestamps($task_id)
+ {
$now = time();
return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(array(
@@ -212,8 +226,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 +239,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/TaskProjectMoveModel.php b/app/Model/TaskProjectMoveModel.php
index eda23c0b..ae3ae084 100644
--- a/app/Model/TaskProjectMoveModel.php
+++ b/app/Model/TaskProjectMoveModel.php
@@ -2,8 +2,6 @@
namespace Kanboard\Model;
-use Kanboard\Event\TaskEvent;
-
/**
* Task Project Move
*
@@ -32,9 +30,8 @@ class TaskProjectMoveModel extends TaskDuplicationModel
$this->checkDestinationProjectValues($values);
$this->tagDuplicationModel->syncTaskTagsToAnotherProject($task_id, $project_id);
- if ($this->db->table(TaskModel::TABLE)->eq('id', $task['id'])->update($values)) {
- $event = new TaskEvent(array_merge($task, $values, array('task_id' => $task['id'])));
- $this->dispatcher->dispatch(TaskModel::EVENT_MOVE_PROJECT, $event);
+ 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;
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/UserMentionModel.php b/app/Model/UserMentionModel.php
deleted file mode 100644
index cdb9949e..00000000
--- a/app/Model/UserMentionModel.php
+++ /dev/null
@@ -1,62 +0,0 @@
-<?php
-
-namespace Kanboard\Model;
-
-use Kanboard\Core\Base;
-use Kanboard\Event\GenericEvent;
-
-/**
- * User Mention
- *
- * @package Kanboard\Model
- * @author Frederic Guillot
- */
-class UserMentionModel extends Base
-{
- /**
- * Get list of mentioned users
- *
- * @access public
- * @param string $content
- * @return array
- */
- public function getMentionedUsers($content)
- {
- $users = array();
-
- if (preg_match_all('/@([^\s]+)/', $content, $matches)) {
- $users = $this->db->table(UserModel::TABLE)
- ->columns('id', 'username', 'name', 'email', 'language')
- ->eq('notifications_enabled', 1)
- ->neq('id', $this->userSession->getId())
- ->in('username', array_unique($matches[1]))
- ->findAll();
- }
-
- return $users;
- }
-
- /**
- * Fire events for user mentions
- *
- * @access public
- * @param string $content
- * @param string $eventName
- * @param GenericEvent $event
- */
- public function fireEvents($content, $eventName, GenericEvent $event)
- {
- if (empty($event['project_id'])) {
- $event['project_id'] = $this->taskFinderModel->getProjectId($event['task_id']);
- }
-
- $users = $this->getMentionedUsers($content);
-
- foreach ($users as $user) {
- if ($this->projectPermissionModel->isMember($event['project_id'], $user['id'])) {
- $event['mention'] = $user;
- $this->dispatcher->dispatch($eventName, $event);
- }
- }
- }
-}
diff --git a/app/Model/UserMetadataModel.php b/app/Model/UserMetadataModel.php
index e931d3ba..42fe4c6d 100644
--- a/app/Model/UserMetadataModel.php
+++ b/app/Model/UserMetadataModel.php
@@ -10,6 +10,9 @@ namespace Kanboard\Model;
*/
class UserMetadataModel extends MetadataModel
{
+ const KEY_COMMENT_SORTING_DIRECTION = 'comment.sorting.direction';
+ const KEY_BOARD_COLLAPSED = 'board.collapsed.';
+
/**
* Get the table
*
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 99fed66f..7771440e 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -6,7 +6,110 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
-const VERSION = 112;
+const VERSION = 120;
+
+function version_120(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE invites (
+ email VARCHAR(255) NOT NULL,
+ project_id INTEGER NOT NULL,
+ token VARCHAR(255) NOT NULL,
+ PRIMARY KEY(email, token)
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+
+ $pdo->exec("DELETE FROM settings WHERE `option`='application_datetime_format'");
+}
+
+function version_119(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE `comments` ADD COLUMN `date_modification` BIGINT(20)');
+ $pdo->exec('UPDATE `comments` SET `date_modification` = `date_creation` WHERE `date_modification` IS NULL');
+}
+
+function version_118(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE `users` ADD COLUMN `api_access_token` VARCHAR(255) DEFAULT NULL');
+}
+
+function version_117(PDO $pdo)
+{
+ $pdo->exec("ALTER TABLE `settings` MODIFY `value` TEXT");
+}
+
+function version_116(PDO $pdo)
+{
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN external_provider VARCHAR(255)");
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN external_uri VARCHAR(255)");
+}
+
+function version_115(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE column_has_restrictions (
+ restriction_id INT NOT NULL AUTO_INCREMENT,
+ project_id INT NOT NULL,
+ role_id INT NOT NULL,
+ column_id INT NOT NULL,
+ rule VARCHAR(255) NOT NULL,
+ UNIQUE(role_id, column_id, rule),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE,
+ FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE,
+ PRIMARY KEY(restriction_id)
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+}
+
+function version_114(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_role_has_restrictions (
+ restriction_id INT NOT NULL AUTO_INCREMENT,
+ project_id INT NOT NULL,
+ role_id INT NOT NULL,
+ rule VARCHAR(255) NOT NULL,
+ UNIQUE(role_id, rule),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE,
+ PRIMARY KEY(restriction_id)
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+}
+
+function version_113(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_has_roles (
+ role_id INT NOT NULL AUTO_INCREMENT,
+ `role` VARCHAR(255) NOT NULL,
+ project_id INT NOT NULL,
+ UNIQUE(project_id, `role`),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ PRIMARY KEY(role_id)
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+
+ $pdo->exec("
+ CREATE TABLE column_has_move_restrictions (
+ restriction_id INT NOT NULL AUTO_INCREMENT,
+ project_id INT NOT NULL,
+ role_id INT NOT NULL,
+ src_column_id INT NOT NULL,
+ dst_column_id INT NOT NULL,
+ UNIQUE(role_id, src_column_id, dst_column_id),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE,
+ FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE,
+ FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE,
+ PRIMARY KEY(restriction_id)
+ ) ENGINE=InnoDB CHARSET=utf8
+ ");
+
+ $pdo->exec("ALTER TABLE `project_has_users` MODIFY `role` VARCHAR(255) NOT NULL");
+ $pdo->exec("ALTER TABLE `project_has_groups` MODIFY `role` VARCHAR(255) NOT NULL");
+}
function version_112(PDO $pdo)
{
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
index b982bcae..8054340c 100644
--- a/app/Schema/Postgres.php
+++ b/app/Schema/Postgres.php
@@ -6,7 +6,106 @@ use PDO;
use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
-const VERSION = 91;
+const VERSION = 99;
+
+function version_99(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE invites (
+ email VARCHAR(255) NOT NULL,
+ project_id INTEGER NOT NULL,
+ token VARCHAR(255) NOT NULL,
+ PRIMARY KEY(email, token)
+ )
+ ");
+
+ $pdo->exec("DELETE FROM settings WHERE \"option\"='application_datetime_format'");
+}
+
+function version_98(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE "comments" ADD COLUMN date_modification BIGINT');
+ $pdo->exec('UPDATE "comments" SET date_modification = date_creation WHERE date_modification IS NULL');
+}
+
+function version_97(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE "users" ADD COLUMN api_access_token VARCHAR(255) DEFAULT NULL');
+}
+
+function version_96(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE "settings" ALTER COLUMN "value" TYPE TEXT');
+}
+
+function version_95(PDO $pdo)
+{
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN external_provider VARCHAR(255)");
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN external_uri VARCHAR(255)");
+}
+
+function version_94(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE column_has_restrictions (
+ restriction_id SERIAL PRIMARY KEY,
+ project_id INTEGER NOT NULL,
+ role_id INTEGER NOT NULL,
+ column_id INTEGER NOT NULL,
+ rule VARCHAR(255) NOT NULL,
+ UNIQUE(role_id, column_id, rule),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE,
+ FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE
+ )
+ ");
+}
+
+function version_93(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_role_has_restrictions (
+ restriction_id SERIAL PRIMARY KEY,
+ project_id INTEGER NOT NULL,
+ role_id INTEGER NOT NULL,
+ rule VARCHAR(255) NOT NULL,
+ UNIQUE(role_id, rule),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE
+ )
+ ");
+}
+
+function version_92(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_has_roles (
+ role_id SERIAL PRIMARY KEY,
+ role VARCHAR(255) NOT NULL,
+ project_id INTEGER NOT NULL,
+ UNIQUE(project_id, role),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
+ )
+ ");
+
+ $pdo->exec("
+ CREATE TABLE column_has_move_restrictions (
+ restriction_id SERIAL PRIMARY KEY,
+ project_id INTEGER NOT NULL,
+ role_id INTEGER NOT NULL,
+ src_column_id INTEGER NOT NULL,
+ dst_column_id INTEGER NOT NULL,
+ UNIQUE(role_id, src_column_id, dst_column_id),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE,
+ FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE,
+ FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE
+ )
+ ");
+
+ $pdo->exec('ALTER TABLE "project_has_users" ALTER COLUMN "role" TYPE VARCHAR(255)');
+ $pdo->exec('ALTER TABLE "project_has_groups" ALTER COLUMN "role" TYPE VARCHAR(255)');
+}
function version_91(PDO $pdo)
{
diff --git a/app/Schema/Sql/mysql.sql b/app/Schema/Sql/mysql.sql
index 8d2f2dc0..487560f3 100644
--- a/app/Schema/Sql/mysql.sql
+++ b/app/Schema/Sql/mysql.sql
@@ -35,6 +35,44 @@ CREATE TABLE `actions` (
CONSTRAINT `actions_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 `column_has_move_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `column_has_move_restrictions` (
+ `restriction_id` int(11) NOT NULL AUTO_INCREMENT,
+ `project_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ `src_column_id` int(11) NOT NULL,
+ `dst_column_id` int(11) NOT NULL,
+ PRIMARY KEY (`restriction_id`),
+ UNIQUE KEY `role_id` (`role_id`,`src_column_id`,`dst_column_id`),
+ KEY `project_id` (`project_id`),
+ KEY `src_column_id` (`src_column_id`),
+ KEY `dst_column_id` (`dst_column_id`),
+ CONSTRAINT `column_has_move_restrictions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_move_restrictions_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `project_has_roles` (`role_id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_move_restrictions_ibfk_3` FOREIGN KEY (`src_column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_move_restrictions_ibfk_4` FOREIGN KEY (`dst_column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `column_has_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `column_has_restrictions` (
+ `restriction_id` int(11) NOT NULL AUTO_INCREMENT,
+ `project_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ `column_id` int(11) NOT NULL,
+ `rule` varchar(255) NOT NULL,
+ PRIMARY KEY (`restriction_id`),
+ UNIQUE KEY `role_id` (`role_id`,`column_id`,`rule`),
+ KEY `project_id` (`project_id`),
+ KEY `column_id` (`column_id`),
+ CONSTRAINT `column_has_restrictions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_restrictions_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `project_has_roles` (`role_id`) ON DELETE CASCADE,
+ CONSTRAINT `column_has_restrictions_ibfk_3` FOREIGN KEY (`column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `columns`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -44,8 +82,8 @@ CREATE TABLE `columns` (
`position` int(11) NOT NULL,
`project_id` int(11) NOT NULL,
`task_limit` int(11) DEFAULT '0',
- `hide_in_dashboard` 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`),
@@ -62,6 +100,7 @@ CREATE TABLE `comments` (
`date_creation` bigint(20) DEFAULT NULL,
`comment` text,
`reference` varchar(50) DEFAULT '',
+ `date_modification` bigint(20) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `user_id` (`user_id`),
KEY `comments_reference_idx` (`reference`),
@@ -119,6 +158,16 @@ CREATE TABLE `groups` (
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `invites`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `invites` (
+ `email` varchar(255) NOT NULL,
+ `project_id` int(11) NOT NULL,
+ `token` varchar(255) NOT NULL,
+ PRIMARY KEY (`email`,`token`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `last_logins`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -260,7 +309,7 @@ DROP TABLE IF EXISTS `project_has_groups`;
CREATE TABLE `project_has_groups` (
`group_id` int(11) NOT NULL,
`project_id` int(11) NOT NULL,
- `role` varchar(25) NOT NULL,
+ `role` varchar(255) NOT NULL,
UNIQUE KEY `group_id` (`group_id`,`project_id`),
KEY `project_id` (`project_id`),
CONSTRAINT `project_has_groups_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE,
@@ -292,19 +341,46 @@ CREATE TABLE `project_has_notification_types` (
CONSTRAINT `project_has_notification_types_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 `project_has_roles`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `project_has_roles` (
+ `role_id` int(11) NOT NULL AUTO_INCREMENT,
+ `role` varchar(255) NOT NULL,
+ `project_id` int(11) NOT NULL,
+ PRIMARY KEY (`role_id`),
+ UNIQUE KEY `project_id` (`project_id`,`role`),
+ CONSTRAINT `project_has_roles_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 `project_has_users`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `project_has_users` (
`project_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
- `role` varchar(25) NOT NULL DEFAULT 'project-viewer',
+ `role` varchar(255) NOT NULL,
UNIQUE KEY `idx_project_user` (`project_id`,`user_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `project_has_users_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
CONSTRAINT `project_has_users_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
+DROP TABLE IF EXISTS `project_role_has_restrictions`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!40101 SET character_set_client = utf8 */;
+CREATE TABLE `project_role_has_restrictions` (
+ `restriction_id` int(11) NOT NULL AUTO_INCREMENT,
+ `project_id` int(11) NOT NULL,
+ `role_id` int(11) NOT NULL,
+ `rule` varchar(255) NOT NULL,
+ PRIMARY KEY (`restriction_id`),
+ UNIQUE KEY `role_id` (`role_id`,`rule`),
+ KEY `project_id` (`project_id`),
+ CONSTRAINT `project_role_has_restrictions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE,
+ CONSTRAINT `project_role_has_restrictions_ibfk_2` FOREIGN KEY (`role_id`) REFERENCES `project_has_roles` (`role_id`) ON DELETE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+/*!40101 SET character_set_client = @saved_cs_client */;
DROP TABLE IF EXISTS `projects`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
@@ -359,7 +435,7 @@ DROP TABLE IF EXISTS `settings`;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `settings` (
`option` varchar(100) NOT NULL,
- `value` varchar(255) DEFAULT '',
+ `value` text,
`changed_by` int(11) NOT NULL DEFAULT '0',
`changed_on` int(11) NOT NULL DEFAULT '0',
PRIMARY KEY (`option`)
@@ -537,6 +613,8 @@ CREATE TABLE `tasks` (
`recurrence_parent` int(11) DEFAULT NULL,
`recurrence_child` int(11) DEFAULT NULL,
`priority` int(11) DEFAULT '0',
+ `external_provider` varchar(255) DEFAULT NULL,
+ `external_uri` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_task_active` (`is_active`),
KEY `column_id` (`column_id`),
@@ -648,6 +726,7 @@ CREATE TABLE `users` (
`role` varchar(25) NOT NULL DEFAULT 'app-user',
`is_active` tinyint(1) DEFAULT '1',
`avatar_path` varchar(255) DEFAULT NULL,
+ `api_access_token` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `users_username_idx` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
@@ -671,7 +750,7 @@ CREATE TABLE `users` (
LOCK TABLES `settings` WRITE;
/*!40000 ALTER TABLE `settings` DISABLE KEYS */;
-INSERT INTO `settings` VALUES ('api_token','19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929',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','1d62395a742260738a406789366a84138ced50a1be62e8862c5cf8d0a561',0,0),('webhook_url','',0,0);
+INSERT INTO `settings` VALUES ('api_token','b428b1088a5ee28730a8405774084f7a35f27f2a218cdb63582f99c008ee',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','e0d3e2613a79f1d751135f4a4d6c3e5b869348db229e57f706b5b54fc3c7',0,0),('webhook_url','',0,0);
/*!40000 ALTER TABLE `settings` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
@@ -700,4 +779,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$Kv6fus67I/ZG/3LYJ7bRLeis8bk8455Lwtu12ElgnGm3lhRs/z7Ni', 'app-admin');INSERT INTO schema_version VALUES ('111');
+INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$R3.YibAi6H0ZzzrSHFa7qeaiEqep1X2TF/bKQX5jMQ7fZ9KIfzUte', 'app-admin');INSERT INTO schema_version VALUES ('120');
diff --git a/app/Schema/Sql/postgres.sql b/app/Schema/Sql/postgres.sql
index ae8b4fd5..8c6b0dd8 100644
--- a/app/Schema/Sql/postgres.sql
+++ b/app/Schema/Sql/postgres.sql
@@ -2,8 +2,8 @@
-- PostgreSQL database dump
--
--- Dumped from database version 9.5.2
--- Dumped by pg_dump version 9.5.2
+-- Dumped from database version 9.6.1
+-- Dumped by pg_dump version 9.6.1
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -89,6 +89,70 @@ ALTER SEQUENCE "actions_id_seq" OWNED BY "actions"."id";
--
+-- Name: column_has_move_restrictions; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "column_has_move_restrictions" (
+ "restriction_id" integer NOT NULL,
+ "project_id" integer NOT NULL,
+ "role_id" integer NOT NULL,
+ "src_column_id" integer NOT NULL,
+ "dst_column_id" integer NOT NULL
+);
+
+
+--
+-- Name: column_has_move_restrictions_restriction_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "column_has_move_restrictions_restriction_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: column_has_move_restrictions_restriction_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "column_has_move_restrictions_restriction_id_seq" OWNED BY "column_has_move_restrictions"."restriction_id";
+
+
+--
+-- Name: column_has_restrictions; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "column_has_restrictions" (
+ "restriction_id" integer NOT NULL,
+ "project_id" integer NOT NULL,
+ "role_id" integer NOT NULL,
+ "column_id" integer NOT NULL,
+ "rule" character varying(255) NOT NULL
+);
+
+
+--
+-- Name: column_has_restrictions_restriction_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "column_has_restrictions_restriction_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: column_has_restrictions_restriction_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "column_has_restrictions_restriction_id_seq" OWNED BY "column_has_restrictions"."restriction_id";
+
+
+--
-- Name: columns; Type: TABLE; Schema: public; Owner: -
--
@@ -98,8 +162,8 @@ CREATE TABLE "columns" (
"position" integer,
"project_id" integer NOT NULL,
"task_limit" integer DEFAULT 0,
- "hide_in_dashboard" integer DEFAULT 0,
- "description" "text"
+ "description" "text",
+ "hide_in_dashboard" boolean DEFAULT false
);
@@ -132,7 +196,8 @@ CREATE TABLE "comments" (
"user_id" integer DEFAULT 0,
"date_creation" bigint NOT NULL,
"comment" "text",
- "reference" character varying(50) DEFAULT ''::character varying
+ "reference" character varying(50) DEFAULT ''::character varying,
+ "date_modification" bigint
);
@@ -240,6 +305,17 @@ ALTER SEQUENCE "groups_id_seq" OWNED BY "groups"."id";
--
+-- Name: invites; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "invites" (
+ "email" character varying(255) NOT NULL,
+ "project_id" integer NOT NULL,
+ "token" character varying(255) NOT NULL
+);
+
+
+--
-- Name: last_logins; Type: TABLE; Schema: public; Owner: -
--
@@ -499,7 +575,7 @@ ALTER SEQUENCE "project_has_files_id_seq" OWNED BY "project_has_files"."id";
CREATE TABLE "project_has_groups" (
"group_id" integer NOT NULL,
"project_id" integer NOT NULL,
- "role" character varying(25) NOT NULL
+ "role" character varying(255) NOT NULL
);
@@ -547,17 +623,78 @@ ALTER SEQUENCE "project_has_notification_types_id_seq" OWNED BY "project_has_not
--
+-- Name: project_has_roles; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "project_has_roles" (
+ "role_id" integer NOT NULL,
+ "role" character varying(255) NOT NULL,
+ "project_id" integer NOT NULL
+);
+
+
+--
+-- Name: project_has_roles_role_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "project_has_roles_role_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: project_has_roles_role_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "project_has_roles_role_id_seq" OWNED BY "project_has_roles"."role_id";
+
+
+--
-- Name: project_has_users; Type: TABLE; Schema: public; Owner: -
--
CREATE TABLE "project_has_users" (
"project_id" integer NOT NULL,
"user_id" integer NOT NULL,
- "role" character varying(25) DEFAULT 'project-viewer'::character varying NOT NULL
+ "role" character varying(255) DEFAULT 'project-viewer'::character varying NOT NULL
+);
+
+
+--
+-- Name: project_role_has_restrictions; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE "project_role_has_restrictions" (
+ "restriction_id" integer NOT NULL,
+ "project_id" integer NOT NULL,
+ "role_id" integer NOT NULL,
+ "rule" character varying(255) NOT NULL
);
--
+-- Name: project_role_has_restrictions_restriction_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE "project_role_has_restrictions_restriction_id_seq"
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: project_role_has_restrictions_restriction_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE "project_role_has_restrictions_restriction_id_seq" OWNED BY "project_role_has_restrictions"."restriction_id";
+
+
+--
-- Name: projects; Type: TABLE; Schema: public; Owner: -
--
@@ -652,7 +789,7 @@ CREATE TABLE "schema_version" (
CREATE TABLE "settings" (
"option" character varying(100) NOT NULL,
- "value" character varying(255) DEFAULT ''::character varying,
+ "value" "text" DEFAULT ''::character varying,
"changed_by" integer DEFAULT 0 NOT NULL,
"changed_on" integer DEFAULT 0 NOT NULL
);
@@ -948,7 +1085,9 @@ CREATE TABLE "tasks" (
"recurrence_basedate" integer DEFAULT 0 NOT NULL,
"recurrence_parent" integer,
"recurrence_child" integer,
- "priority" integer DEFAULT 0
+ "priority" integer DEFAULT 0,
+ "external_provider" character varying(255),
+ "external_uri" character varying(255)
);
@@ -1117,7 +1256,8 @@ CREATE TABLE "users" (
"gitlab_id" integer,
"role" character varying(25) DEFAULT 'app-user'::character varying NOT NULL,
"is_active" boolean DEFAULT true,
- "avatar_path" character varying(255)
+ "avatar_path" character varying(255),
+ "api_access_token" character varying(255) DEFAULT NULL::character varying
);
@@ -1141,203 +1281,231 @@ ALTER SEQUENCE "users_id_seq" OWNED BY "users"."id";
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: action_has_params id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "action_has_params" ALTER COLUMN "id" SET DEFAULT "nextval"('"action_has_params_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: actions id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "actions" ALTER COLUMN "id" SET DEFAULT "nextval"('"actions_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: column_has_move_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"column_has_move_restrictions_restriction_id_seq"'::"regclass");
+
+
+--
+-- Name: column_has_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"column_has_restrictions_restriction_id_seq"'::"regclass");
+
+
+--
+-- Name: columns id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns" ALTER COLUMN "id" SET DEFAULT "nextval"('"columns_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: comments id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "comments" ALTER COLUMN "id" SET DEFAULT "nextval"('"comments_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: custom_filters id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "custom_filters" ALTER COLUMN "id" SET DEFAULT "nextval"('"custom_filters_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: groups id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "groups" ALTER COLUMN "id" SET DEFAULT "nextval"('"groups_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: last_logins id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "last_logins" ALTER COLUMN "id" SET DEFAULT "nextval"('"last_logins_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: links id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "links" ALTER COLUMN "id" SET DEFAULT "nextval"('"links_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_activities id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_activities_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_daily_column_stats id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_daily_summaries_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_daily_stats id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_stats" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_daily_stats_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_categories id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_has_categories_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_files id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_files" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_has_files_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_notification_types id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types" ALTER COLUMN "id" SET DEFAULT "nextval"('"project_has_notification_types_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: project_has_roles role_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles" ALTER COLUMN "role_id" SET DEFAULT "nextval"('"project_has_roles_role_id_seq"'::"regclass");
+
+
+--
+-- Name: project_role_has_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"project_role_has_restrictions_restriction_id_seq"'::"regclass");
+
+
+--
+-- Name: projects id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "projects" ALTER COLUMN "id" SET DEFAULT "nextval"('"projects_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: remember_me id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "remember_me" ALTER COLUMN "id" SET DEFAULT "nextval"('"remember_me_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: subtask_time_tracking id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking" ALTER COLUMN "id" SET DEFAULT "nextval"('"subtask_time_tracking_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: subtasks id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtasks" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_subtasks_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: swimlanes id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes" ALTER COLUMN "id" SET DEFAULT "nextval"('"swimlanes_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: tags 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: -
+-- Name: task_has_external_links 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");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: task_has_files id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_files" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_files_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: task_has_links id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links" ALTER COLUMN "id" SET DEFAULT "nextval"('"task_has_links_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: tasks id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks" ALTER COLUMN "id" SET DEFAULT "nextval"('"tasks_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: transitions id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions" ALTER COLUMN "id" SET DEFAULT "nextval"('"transitions_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: user_has_notification_types id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notification_types" ALTER COLUMN "id" SET DEFAULT "nextval"('"user_has_notification_types_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: user_has_unread_notifications id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_unread_notifications" ALTER COLUMN "id" SET DEFAULT "nextval"('"user_has_unread_notifications_id_seq"'::"regclass");
--
--- Name: id; Type: DEFAULT; Schema: public; Owner: -
+-- Name: users id; Type: DEFAULT; Schema: public; Owner: -
--
ALTER TABLE ONLY "users" ALTER COLUMN "id" SET DEFAULT "nextval"('"users_id_seq"'::"regclass");
--
--- Name: action_has_params_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: action_has_params action_has_params_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "action_has_params"
@@ -1345,7 +1513,7 @@ ALTER TABLE ONLY "action_has_params"
--
--- Name: actions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: actions actions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "actions"
@@ -1353,7 +1521,39 @@ ALTER TABLE ONLY "actions"
--
--- Name: columns_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: column_has_move_restrictions column_has_move_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_pkey" PRIMARY KEY ("restriction_id");
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_role_id_src_column_id_dst_colu_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_role_id_src_column_id_dst_colu_key" UNIQUE ("role_id", "src_column_id", "dst_column_id");
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_pkey" PRIMARY KEY ("restriction_id");
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_role_id_column_id_rule_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_role_id_column_id_rule_key" UNIQUE ("role_id", "column_id", "rule");
+
+
+--
+-- Name: columns columns_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns"
@@ -1361,7 +1561,7 @@ ALTER TABLE ONLY "columns"
--
--- Name: columns_title_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: columns columns_title_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns"
@@ -1369,7 +1569,7 @@ ALTER TABLE ONLY "columns"
--
--- Name: comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: comments comments_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "comments"
@@ -1377,7 +1577,7 @@ ALTER TABLE ONLY "comments"
--
--- Name: currencies_currency_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: currencies currencies_currency_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "currencies"
@@ -1385,7 +1585,7 @@ ALTER TABLE ONLY "currencies"
--
--- Name: custom_filters_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: custom_filters custom_filters_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "custom_filters"
@@ -1393,7 +1593,7 @@ ALTER TABLE ONLY "custom_filters"
--
--- Name: group_has_users_group_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: group_has_users group_has_users_group_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "group_has_users"
@@ -1401,7 +1601,7 @@ ALTER TABLE ONLY "group_has_users"
--
--- Name: groups_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: groups groups_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "groups"
@@ -1409,7 +1609,7 @@ ALTER TABLE ONLY "groups"
--
--- Name: groups_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: groups groups_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "groups"
@@ -1417,7 +1617,15 @@ ALTER TABLE ONLY "groups"
--
--- Name: last_logins_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: invites invites_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "invites"
+ ADD CONSTRAINT "invites_pkey" PRIMARY KEY ("email", "token");
+
+
+--
+-- Name: last_logins last_logins_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "last_logins"
@@ -1425,7 +1633,7 @@ ALTER TABLE ONLY "last_logins"
--
--- Name: links_label_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: links links_label_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "links"
@@ -1433,7 +1641,7 @@ ALTER TABLE ONLY "links"
--
--- Name: links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: links links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "links"
@@ -1441,7 +1649,7 @@ ALTER TABLE ONLY "links"
--
--- Name: password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: password_reset password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "password_reset"
@@ -1449,7 +1657,7 @@ ALTER TABLE ONLY "password_reset"
--
--- Name: plugin_schema_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: plugin_schema_versions plugin_schema_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "plugin_schema_versions"
@@ -1457,7 +1665,7 @@ ALTER TABLE ONLY "plugin_schema_versions"
--
--- Name: project_activities_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1465,7 +1673,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_daily_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_stats project_daily_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_stats"
@@ -1473,7 +1681,7 @@ ALTER TABLE ONLY "project_daily_stats"
--
--- Name: project_daily_summaries_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_column_stats project_daily_summaries_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats"
@@ -1481,7 +1689,7 @@ ALTER TABLE ONLY "project_daily_column_stats"
--
--- Name: project_has_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_categories project_has_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories"
@@ -1489,7 +1697,7 @@ ALTER TABLE ONLY "project_has_categories"
--
--- Name: project_has_categories_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_categories project_has_categories_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories"
@@ -1497,7 +1705,7 @@ ALTER TABLE ONLY "project_has_categories"
--
--- Name: project_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_files project_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_files"
@@ -1505,7 +1713,7 @@ ALTER TABLE ONLY "project_has_files"
--
--- Name: project_has_groups_group_id_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_groups project_has_groups_group_id_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_groups"
@@ -1513,7 +1721,7 @@ ALTER TABLE ONLY "project_has_groups"
--
--- Name: project_has_metadata_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_metadata project_has_metadata_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_metadata"
@@ -1521,7 +1729,7 @@ ALTER TABLE ONLY "project_has_metadata"
--
--- Name: project_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_notification_types project_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types"
@@ -1529,7 +1737,7 @@ ALTER TABLE ONLY "project_has_notification_types"
--
--- Name: project_has_notification_types_project_id_notification_type_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_notification_types project_has_notification_types_project_id_notification_type_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types"
@@ -1537,7 +1745,23 @@ ALTER TABLE ONLY "project_has_notification_types"
--
--- Name: project_has_users_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_roles project_has_roles_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles"
+ ADD CONSTRAINT "project_has_roles_pkey" PRIMARY KEY ("role_id");
+
+
+--
+-- Name: project_has_roles project_has_roles_project_id_role_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles"
+ ADD CONSTRAINT "project_has_roles_project_id_role_key" UNIQUE ("project_id", "role");
+
+
+--
+-- Name: project_has_users project_has_users_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_users"
@@ -1545,7 +1769,23 @@ ALTER TABLE ONLY "project_has_users"
--
--- Name: projects_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: project_role_has_restrictions project_role_has_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_pkey" PRIMARY KEY ("restriction_id");
+
+
+--
+-- Name: project_role_has_restrictions project_role_has_restrictions_role_id_rule_key; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_role_id_rule_key" UNIQUE ("role_id", "rule");
+
+
+--
+-- Name: projects projects_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "projects"
@@ -1553,7 +1793,7 @@ ALTER TABLE ONLY "projects"
--
--- Name: remember_me_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: remember_me remember_me_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "remember_me"
@@ -1561,7 +1801,7 @@ ALTER TABLE ONLY "remember_me"
--
--- Name: settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "settings"
@@ -1569,7 +1809,7 @@ ALTER TABLE ONLY "settings"
--
--- Name: subtask_time_tracking_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: subtask_time_tracking subtask_time_tracking_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking"
@@ -1577,7 +1817,7 @@ ALTER TABLE ONLY "subtask_time_tracking"
--
--- Name: swimlanes_name_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: swimlanes swimlanes_name_project_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes"
@@ -1585,7 +1825,7 @@ ALTER TABLE ONLY "swimlanes"
--
--- Name: swimlanes_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: swimlanes swimlanes_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes"
@@ -1593,7 +1833,7 @@ ALTER TABLE ONLY "swimlanes"
--
--- Name: tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: tags tags_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tags"
@@ -1601,7 +1841,7 @@ ALTER TABLE ONLY "tags"
--
--- Name: tags_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: tags tags_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tags"
@@ -1609,7 +1849,7 @@ ALTER TABLE ONLY "tags"
--
--- Name: task_has_external_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_external_links task_has_external_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_external_links"
@@ -1617,7 +1857,7 @@ ALTER TABLE ONLY "task_has_external_links"
--
--- Name: task_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_files task_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_files"
@@ -1625,7 +1865,7 @@ ALTER TABLE ONLY "task_has_files"
--
--- Name: task_has_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -1633,7 +1873,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_metadata_task_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_metadata task_has_metadata_task_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_metadata"
@@ -1641,7 +1881,7 @@ ALTER TABLE ONLY "task_has_metadata"
--
--- Name: task_has_subtasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: subtasks task_has_subtasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtasks"
@@ -1649,7 +1889,7 @@ ALTER TABLE ONLY "subtasks"
--
--- Name: task_has_tags_tag_id_task_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_tags task_has_tags_tag_id_task_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_tags"
@@ -1657,7 +1897,7 @@ ALTER TABLE ONLY "task_has_tags"
--
--- Name: tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: tasks tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks"
@@ -1665,7 +1905,7 @@ ALTER TABLE ONLY "tasks"
--
--- Name: transitions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -1673,7 +1913,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: user_has_metadata_user_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_metadata user_has_metadata_user_id_name_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_metadata"
@@ -1681,7 +1921,7 @@ ALTER TABLE ONLY "user_has_metadata"
--
--- Name: user_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notification_types user_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notification_types"
@@ -1689,7 +1929,7 @@ ALTER TABLE ONLY "user_has_notification_types"
--
--- Name: user_has_notifications_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notifications user_has_notifications_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notifications"
@@ -1697,7 +1937,7 @@ ALTER TABLE ONLY "user_has_notifications"
--
--- Name: user_has_unread_notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_unread_notifications user_has_unread_notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_unread_notifications"
@@ -1705,7 +1945,7 @@ ALTER TABLE ONLY "user_has_unread_notifications"
--
--- Name: users_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "users"
@@ -1839,7 +2079,7 @@ CREATE UNIQUE INDEX "users_username_idx" ON "users" USING "btree" ("username");
--
--- Name: action_has_params_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: action_has_params action_has_params_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "action_has_params"
@@ -1847,7 +2087,7 @@ ALTER TABLE ONLY "action_has_params"
--
--- Name: actions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: actions actions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "actions"
@@ -1855,7 +2095,63 @@ ALTER TABLE ONLY "actions"
--
--- Name: columns_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: column_has_move_restrictions column_has_move_restrictions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_dst_column_id_fkey" FOREIGN KEY ("dst_column_id") REFERENCES "columns"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_role_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "project_has_roles"("role_id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_move_restrictions column_has_move_restrictions_src_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_move_restrictions"
+ ADD CONSTRAINT "column_has_move_restrictions_src_column_id_fkey" FOREIGN KEY ("src_column_id") REFERENCES "columns"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_column_id_fkey" FOREIGN KEY ("column_id") REFERENCES "columns"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: column_has_restrictions column_has_restrictions_role_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "column_has_restrictions"
+ ADD CONSTRAINT "column_has_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "project_has_roles"("role_id") ON DELETE CASCADE;
+
+
+--
+-- Name: columns columns_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "columns"
@@ -1863,7 +2159,7 @@ ALTER TABLE ONLY "columns"
--
--- Name: comments_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: comments comments_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "comments"
@@ -1871,7 +2167,7 @@ ALTER TABLE ONLY "comments"
--
--- Name: group_has_users_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: group_has_users group_has_users_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "group_has_users"
@@ -1879,7 +2175,7 @@ ALTER TABLE ONLY "group_has_users"
--
--- Name: group_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: group_has_users group_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "group_has_users"
@@ -1887,7 +2183,7 @@ ALTER TABLE ONLY "group_has_users"
--
--- Name: last_logins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: last_logins last_logins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "last_logins"
@@ -1895,7 +2191,7 @@ ALTER TABLE ONLY "last_logins"
--
--- Name: password_reset_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: password_reset password_reset_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "password_reset"
@@ -1903,7 +2199,7 @@ ALTER TABLE ONLY "password_reset"
--
--- Name: project_activities_creator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_creator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1911,7 +2207,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_activities_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1919,7 +2215,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_activities_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_activities project_activities_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_activities"
@@ -1927,7 +2223,7 @@ ALTER TABLE ONLY "project_activities"
--
--- Name: project_daily_stats_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_stats project_daily_stats_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_stats"
@@ -1935,7 +2231,7 @@ ALTER TABLE ONLY "project_daily_stats"
--
--- Name: project_daily_summaries_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_column_stats project_daily_summaries_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats"
@@ -1943,7 +2239,7 @@ ALTER TABLE ONLY "project_daily_column_stats"
--
--- Name: project_daily_summaries_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_daily_column_stats project_daily_summaries_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_daily_column_stats"
@@ -1951,7 +2247,7 @@ ALTER TABLE ONLY "project_daily_column_stats"
--
--- Name: project_has_categories_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_categories project_has_categories_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_categories"
@@ -1959,7 +2255,7 @@ ALTER TABLE ONLY "project_has_categories"
--
--- Name: project_has_files_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_files project_has_files_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_files"
@@ -1967,7 +2263,7 @@ ALTER TABLE ONLY "project_has_files"
--
--- Name: project_has_groups_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_groups project_has_groups_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_groups"
@@ -1975,7 +2271,7 @@ ALTER TABLE ONLY "project_has_groups"
--
--- Name: project_has_groups_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_groups project_has_groups_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_groups"
@@ -1983,7 +2279,7 @@ ALTER TABLE ONLY "project_has_groups"
--
--- Name: project_has_metadata_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_metadata project_has_metadata_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_metadata"
@@ -1991,7 +2287,7 @@ ALTER TABLE ONLY "project_has_metadata"
--
--- Name: project_has_notification_types_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_notification_types project_has_notification_types_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_notification_types"
@@ -1999,7 +2295,15 @@ ALTER TABLE ONLY "project_has_notification_types"
--
--- Name: project_has_users_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_roles project_has_roles_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_has_roles"
+ ADD CONSTRAINT "project_has_roles_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: project_has_users project_has_users_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_users"
@@ -2007,7 +2311,7 @@ ALTER TABLE ONLY "project_has_users"
--
--- Name: project_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_has_users project_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "project_has_users"
@@ -2015,7 +2319,23 @@ ALTER TABLE ONLY "project_has_users"
--
--- Name: remember_me_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: project_role_has_restrictions project_role_has_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "projects"("id") ON DELETE CASCADE;
+
+
+--
+-- Name: project_role_has_restrictions project_role_has_restrictions_role_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY "project_role_has_restrictions"
+ ADD CONSTRAINT "project_role_has_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "project_has_roles"("role_id") ON DELETE CASCADE;
+
+
+--
+-- Name: remember_me remember_me_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "remember_me"
@@ -2023,7 +2343,7 @@ ALTER TABLE ONLY "remember_me"
--
--- Name: subtask_time_tracking_subtask_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: subtask_time_tracking subtask_time_tracking_subtask_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking"
@@ -2031,7 +2351,7 @@ ALTER TABLE ONLY "subtask_time_tracking"
--
--- Name: subtask_time_tracking_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: subtask_time_tracking subtask_time_tracking_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtask_time_tracking"
@@ -2039,7 +2359,7 @@ ALTER TABLE ONLY "subtask_time_tracking"
--
--- Name: swimlanes_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: swimlanes swimlanes_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "swimlanes"
@@ -2047,7 +2367,7 @@ ALTER TABLE ONLY "swimlanes"
--
--- Name: task_has_external_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_external_links task_has_external_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_external_links"
@@ -2055,7 +2375,7 @@ ALTER TABLE ONLY "task_has_external_links"
--
--- Name: task_has_files_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_files task_has_files_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_files"
@@ -2063,7 +2383,7 @@ ALTER TABLE ONLY "task_has_files"
--
--- Name: task_has_links_link_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_link_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -2071,7 +2391,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_links_opposite_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_opposite_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -2079,7 +2399,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_links task_has_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_links"
@@ -2087,7 +2407,7 @@ ALTER TABLE ONLY "task_has_links"
--
--- Name: task_has_metadata_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_metadata task_has_metadata_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_metadata"
@@ -2095,7 +2415,7 @@ ALTER TABLE ONLY "task_has_metadata"
--
--- Name: task_has_subtasks_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: subtasks task_has_subtasks_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "subtasks"
@@ -2103,7 +2423,7 @@ ALTER TABLE ONLY "subtasks"
--
--- Name: task_has_tags_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_tags task_has_tags_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_tags"
@@ -2111,7 +2431,7 @@ ALTER TABLE ONLY "task_has_tags"
--
--- Name: task_has_tags_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: task_has_tags task_has_tags_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "task_has_tags"
@@ -2119,7 +2439,7 @@ ALTER TABLE ONLY "task_has_tags"
--
--- Name: tasks_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: tasks tasks_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks"
@@ -2127,7 +2447,7 @@ ALTER TABLE ONLY "tasks"
--
--- Name: tasks_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: tasks tasks_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "tasks"
@@ -2135,7 +2455,7 @@ ALTER TABLE ONLY "tasks"
--
--- Name: transitions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2143,7 +2463,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2151,7 +2471,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_src_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_src_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2159,7 +2479,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2167,7 +2487,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: transitions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: transitions transitions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "transitions"
@@ -2175,7 +2495,7 @@ ALTER TABLE ONLY "transitions"
--
--- Name: user_has_metadata_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_metadata user_has_metadata_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_metadata"
@@ -2183,7 +2503,7 @@ ALTER TABLE ONLY "user_has_metadata"
--
--- Name: user_has_notification_types_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notification_types user_has_notification_types_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notification_types"
@@ -2191,7 +2511,7 @@ ALTER TABLE ONLY "user_has_notification_types"
--
--- Name: user_has_notifications_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notifications user_has_notifications_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notifications"
@@ -2199,7 +2519,7 @@ ALTER TABLE ONLY "user_has_notifications"
--
--- Name: user_has_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_notifications user_has_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_notifications"
@@ -2207,7 +2527,7 @@ ALTER TABLE ONLY "user_has_notifications"
--
--- Name: user_has_unread_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
+-- Name: user_has_unread_notifications user_has_unread_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: -
--
ALTER TABLE ONLY "user_has_unread_notifications"
@@ -2222,8 +2542,8 @@ ALTER TABLE ONLY "user_has_unread_notifications"
-- PostgreSQL database dump
--
--- Dumped from database version 9.5.2
--- Dumped by pg_dump version 9.5.2
+-- Dumped from database version 9.6.1
+-- Dumped by pg_dump version 9.6.1
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -2243,7 +2563,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', '1aff324d30632aaed0d4f4dc1281be0d5bbc7b4fcddccc4badcd6c8f3d43', 0, 0);
+INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('webhook_token', 'a41accd9dabc3d45a3d26de5edd5c43b3b0082cabebfbc7a175b8d6b2e5a', 0, 0);
+INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', 'dbbf966938e4400746ebcb2337bb4a3cdce0bab2dd053d57dbbc243ccc9d', 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);
@@ -2261,7 +2582,6 @@ INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('default_co
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('subtask_time_tracking', '1', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('cfd_include_closed_tasks', '1', 0, 0);
INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('password_reset', '1', 0, 0);
-INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token', '19ffd9709d03ce50675c3a43d1c49c1ac207f4bc45f06c5b2701fbdf8929', 0, 0);
--
@@ -2272,8 +2592,8 @@ INSERT INTO settings (option, value, changed_by, changed_on) VALUES ('api_token'
-- PostgreSQL database dump
--
--- Dumped from database version 9.5.2
--- Dumped by pg_dump version 9.5.2
+-- Dumped from database version 9.6.1
+-- Dumped by pg_dump version 9.6.1
SET statement_timeout = 0;
SET lock_timeout = 0;
@@ -2313,4 +2633,4 @@ SELECT pg_catalog.setval('links_id_seq', 11, true);
-- PostgreSQL database dump complete
--
-INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$Kv6fus67I/ZG/3LYJ7bRLeis8bk8455Lwtu12ElgnGm3lhRs/z7Ni', 'app-admin');INSERT INTO schema_version VALUES ('90');
+INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$R3.YibAi6H0ZzzrSHFa7qeaiEqep1X2TF/bKQX5jMQ7fZ9KIfzUte', 'app-admin');INSERT INTO schema_version VALUES ('99');
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index 2a7735ee..87630b1a 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -6,7 +6,98 @@ use Kanboard\Core\Security\Token;
use Kanboard\Core\Security\Role;
use PDO;
-const VERSION = 103;
+const VERSION = 110;
+
+function version_110(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE invites (
+ email TEXT NOT NULL,
+ project_id INTEGER NOT NULL,
+ token TEXT NOT NULL,
+ PRIMARY KEY(email, token)
+ )
+ ");
+
+ $pdo->exec("DELETE FROM settings WHERE \"option\"='application_datetime_format'");
+}
+
+function version_109(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE comments ADD COLUMN date_modification INTEGER');
+ $pdo->exec('UPDATE comments SET date_modification = date_creation WHERE date_modification IS NULL;');
+}
+
+function version_108(PDO $pdo)
+{
+ $pdo->exec('ALTER TABLE users ADD COLUMN api_access_token VARCHAR(255) DEFAULT NULL');
+}
+
+function version_107(PDO $pdo)
+{
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN external_provider TEXT");
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN external_uri TEXT");
+}
+
+function version_106(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE column_has_restrictions (
+ restriction_id INTEGER PRIMARY KEY,
+ project_id INTEGER NOT NULL,
+ role_id INTEGER NOT NULL,
+ column_id INTEGER NOT NULL,
+ rule VARCHAR(255) NOT NULL,
+ UNIQUE(role_id, column_id, rule),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE,
+ FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE
+ )
+ ");
+}
+
+function version_105(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_role_has_restrictions (
+ restriction_id INTEGER PRIMARY KEY,
+ project_id INTEGER NOT NULL,
+ role_id INTEGER NOT NULL,
+ rule VARCHAR(255) NOT NULL,
+ UNIQUE(role_id, rule),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE
+ )
+ ");
+}
+
+function version_104(PDO $pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_has_roles (
+ role_id INTEGER PRIMARY KEY,
+ role TEXT NOT NULL,
+ project_id INTEGER NOT NULL,
+ UNIQUE(project_id, role),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
+ )
+ ");
+
+ $pdo->exec("
+ CREATE TABLE column_has_move_restrictions (
+ restriction_id INTEGER PRIMARY KEY,
+ project_id INTEGER NOT NULL,
+ role_id INTEGER NOT NULL,
+ src_column_id INTEGER NOT NULL,
+ dst_column_id INTEGER NOT NULL,
+ UNIQUE(role_id, src_column_id, dst_column_id),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
+ FOREIGN KEY(role_id) REFERENCES project_has_roles(role_id) ON DELETE CASCADE,
+ FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE,
+ FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE
+ )
+ ");
+}
function version_103(PDO $pdo)
{
diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php
index 34202052..81f2b39e 100644
--- a/app/ServiceProvider/ActionProvider.php
+++ b/app/ServiceProvider/ActionProvider.php
@@ -2,9 +2,12 @@
namespace Kanboard\ServiceProvider;
-use Kanboard\Action\TaskAssignColorPriority;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
+use Kanboard\Action\TaskAssignColorPriority;
+use Kanboard\Action\TaskAssignDueDateOnCreation;
+use Kanboard\Action\TaskMoveColumnClosed;
+use Kanboard\Action\TaskMoveColumnNotMovedPeriod;
use Kanboard\Core\Action\ActionManager;
use Kanboard\Action\CommentCreation;
use Kanboard\Action\CommentCreationMoveTaskColumn;
@@ -32,6 +35,10 @@ use Kanboard\Action\TaskMoveColumnUnAssigned;
use Kanboard\Action\TaskOpen;
use Kanboard\Action\TaskUpdateStartDate;
use Kanboard\Action\TaskCloseNoActivity;
+use Kanboard\Action\TaskCloseNoActivityColumn;
+use Kanboard\Action\TaskCloseNotMovedColumn;
+use Kanboard\Action\TaskAssignColorSwimlane;
+use Kanboard\Action\TaskAssignPrioritySwimlane;
/**
* Action Provider
@@ -68,6 +75,8 @@ 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 TaskCloseNotMovedColumn($container));
$container['actionManager']->register(new TaskCreation($container));
$container['actionManager']->register(new TaskDuplicateAnotherProject($container));
$container['actionManager']->register(new TaskEmail($container));
@@ -75,9 +84,14 @@ 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));
+ $container['actionManager']->register(new TaskAssignColorSwimlane($container));
+ $container['actionManager']->register(new TaskAssignPrioritySwimlane($container));
return $container;
}
diff --git a/app/ServiceProvider/ApiProvider.php b/app/ServiceProvider/ApiProvider.php
index 5cf6231c..2c9abec7 100644
--- a/app/ServiceProvider/ApiProvider.php
+++ b/app/ServiceProvider/ApiProvider.php
@@ -10,6 +10,7 @@ use Kanboard\Api\Procedure\CategoryProcedure;
use Kanboard\Api\Procedure\ColumnProcedure;
use Kanboard\Api\Procedure\CommentProcedure;
use Kanboard\Api\Procedure\ProjectFileProcedure;
+use Kanboard\Api\Procedure\TagProcedure;
use Kanboard\Api\Procedure\TaskExternalLinkProcedure;
use Kanboard\Api\Procedure\TaskFileProcedure;
use Kanboard\Api\Procedure\GroupProcedure;
@@ -22,8 +23,10 @@ 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\TaskTagProcedure;
use Kanboard\Api\Procedure\UserProcedure;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
@@ -69,9 +72,12 @@ class ApiProvider implements ServiceProviderInterface
->withObject(new TaskProcedure($container))
->withObject(new TaskLinkProcedure($container))
->withObject(new TaskExternalLinkProcedure($container))
+ ->withObject(new TaskMetadataProcedure($container))
+ ->withObject(new TaskTagProcedure($container))
->withObject(new UserProcedure($container))
->withObject(new GroupProcedure($container))
->withObject(new GroupMemberProcedure($container))
+ ->withObject(new TagProcedure($container))
->withBeforeMethod('beforeProcedure')
;
diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php
index 978bc05b..6a9a820e 100644
--- a/app/ServiceProvider/AuthenticationProvider.php
+++ b/app/ServiceProvider/AuthenticationProvider.php
@@ -2,6 +2,7 @@
namespace Kanboard\ServiceProvider;
+use Kanboard\Auth\ApiAccessTokenAuth;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
use Kanboard\Core\Security\AuthenticationManager;
@@ -44,6 +45,8 @@ class AuthenticationProvider implements ServiceProviderInterface
$container['authenticationManager']->register(new LdapAuth($container));
}
+ $container['authenticationManager']->register(new ApiAccessTokenAuth($container));
+
$container['projectAccessMap'] = $this->getProjectAccessMap();
$container['applicationAccessMap'] = $this->getApplicationAccessMap();
$container['apiAccessMap'] = $this->getApiAccessMap();
@@ -85,7 +88,6 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('ExportController', '*', Role::PROJECT_MANAGER);
$acl->add('TaskFileController', array('screenshot', 'create', 'save', 'remove', 'confirm'), Role::PROJECT_MEMBER);
$acl->add('TaskGanttController', '*', Role::PROJECT_MANAGER);
- $acl->add('TaskGanttCreationController', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectViewController', array('share', 'updateSharing', 'integrations', 'updateIntegrations', 'notifications', 'updateNotifications', 'duplicate', 'doDuplication'), Role::PROJECT_MANAGER);
$acl->add('ProjectPermissionController', '*', Role::PROJECT_MANAGER);
$acl->add('ProjectEditController', '*', Role::PROJECT_MANAGER);
@@ -133,7 +135,8 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('BoardViewController', 'readonly', Role::APP_PUBLIC);
$acl->add('ICalendarController', '*', Role::APP_PUBLIC);
$acl->add('FeedController', '*', Role::APP_PUBLIC);
- $acl->add('AvatarFileController', 'show', Role::APP_PUBLIC);
+ $acl->add('AvatarFileController', array('show', 'image'), Role::APP_PUBLIC);
+ $acl->add('UserInviteController', array('signup', 'register'), Role::APP_PUBLIC);
$acl->add('ConfigController', '*', Role::APP_ADMIN);
$acl->add('TagController', '*', Role::APP_ADMIN);
@@ -151,7 +154,7 @@ class AuthenticationProvider implements ServiceProviderInterface
$acl->add('UserCreationController', '*', Role::APP_ADMIN);
$acl->add('UserListController', '*', Role::APP_ADMIN);
$acl->add('UserStatusController', '*', Role::APP_ADMIN);
- $acl->add('UserCredentialController', array('changeAuthentication', 'saveAuthentication'), Role::APP_ADMIN);
+ $acl->add('UserCredentialController', array('changeAuthentication', 'saveAuthentication', 'unlock'), Role::APP_ADMIN);
return $acl;
}
@@ -194,19 +197,21 @@ class AuthenticationProvider implements ServiceProviderInterface
$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('CategoryProcedure', array('removeCategory', 'createCategory', 'updateCategory'), Role::PROJECT_MANAGER);
+ $acl->add('ColumnProcedure', array('updateColumn', 'addColumn', 'removeColumn', 'changeColumnPosition'), Role::PROJECT_MANAGER);
$acl->add('CommentProcedure', array('removeComment', 'createComment', 'updateComment'), Role::PROJECT_MEMBER);
- $acl->add('ProjectPermissionProcedure', '*', Role::PROJECT_MANAGER);
+ $acl->add('ProjectPermissionProcedure', array('addProjectUser', 'addProjectGroup', 'removeProjectUser', 'removeProjectGroup', 'changeProjectUserRole', 'changeProjectGroupRole'), 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('SubtaskProcedure', array('removeSubtask', 'createSubtask', 'updateSubtask'), Role::PROJECT_MEMBER);
+ $acl->add('SubtaskTimeTrackingProcedure', array('setSubtaskStartTime', 'setSubtaskEndTime'), Role::PROJECT_MEMBER);
+ $acl->add('SwimlaneProcedure', array('addSwimlane', 'updateSwimlane', 'removeSwimlane', 'disableSwimlane', 'enableSwimlane', 'changeSwimlanePosition'), Role::PROJECT_MANAGER);
+ $acl->add('ProjectFileProcedure', array('createProjectFile', 'removeProjectFile', 'removeAllProjectFiles'), Role::PROJECT_MEMBER);
+ $acl->add('TaskFileProcedure', array('createTaskFile', 'removeTaskFile', 'removeAllTaskFiles'), Role::PROJECT_MEMBER);
+ $acl->add('TaskLinkProcedure', array('createTaskLink', 'updateTaskLink', 'removeTaskLink'), Role::PROJECT_MEMBER);
$acl->add('TaskExternalLinkProcedure', array('createExternalTaskLink', 'updateExternalTaskLink', 'removeExternalTaskLink'), Role::PROJECT_MEMBER);
- $acl->add('TaskProcedure', '*', Role::PROJECT_MEMBER);
+ $acl->add('TaskProcedure', array('openTask', 'closeTask', 'removeTask', 'moveTaskPosition', 'moveTaskToProject', 'duplicateTaskToProject', 'createTask', 'updateTask'), Role::PROJECT_MEMBER);
+ $acl->add('TaskTagProcedure', array('setTaskTags'), Role::PROJECT_MEMBER);
+ $acl->add('TagProcedure', array('createTag', 'updateTag', 'removeTag'), Role::PROJECT_MEMBER);
return $acl;
}
diff --git a/app/ServiceProvider/CacheProvider.php b/app/ServiceProvider/CacheProvider.php
new file mode 100644
index 00000000..af8a8e7a
--- /dev/null
+++ b/app/ServiceProvider/CacheProvider.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Kanboard\ServiceProvider;
+
+use Kanboard\Core\Cache\FileCache;
+use Kanboard\Core\Cache\MemoryCache;
+use Kanboard\Decorator\ColumnMoveRestrictionCacheDecorator;
+use Kanboard\Decorator\ColumnRestrictionCacheDecorator;
+use Kanboard\Decorator\MetadataCacheDecorator;
+use Kanboard\Decorator\ProjectRoleRestrictionCacheDecorator;
+use Kanboard\Decorator\UserCacheDecorator;
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+
+/**
+ * Cache Provider
+ *
+ * @package Kanboard\ServiceProvider
+ * @author Frederic Guillot
+ */
+class CacheProvider implements ServiceProviderInterface
+{
+ /**
+ * Register providers
+ *
+ * @access public
+ * @param \Pimple\Container $container
+ * @return \Pimple\Container
+ */
+ public function register(Container $container)
+ {
+ $container['memoryCache'] = function() {
+ return new MemoryCache();
+ };
+
+ if (CACHE_DRIVER === 'file') {
+ $container['cacheDriver'] = function() {
+ return new FileCache();
+ };
+ } else {
+ $container['cacheDriver'] = $container['memoryCache'];
+ }
+
+ $container['userCacheDecorator'] = function($c) {
+ return new UserCacheDecorator(
+ $c['memoryCache'],
+ $c['userModel']
+ );
+ };
+
+ $container['userMetadataCacheDecorator'] = function($c) {
+ return new MetadataCacheDecorator(
+ $c['cacheDriver'],
+ $c['userMetadataModel'],
+ 'user.metadata.',
+ $c['userSession']->getId()
+ );
+ };
+
+ $container['columnMoveRestrictionCacheDecorator'] = function($c) {
+ return new ColumnMoveRestrictionCacheDecorator(
+ $c['memoryCache'],
+ $c['columnMoveRestrictionModel']
+ );
+ };
+
+ $container['columnRestrictionCacheDecorator'] = function($c) {
+ return new ColumnRestrictionCacheDecorator(
+ $c['memoryCache'],
+ $c['columnRestrictionModel']
+ );
+ };
+
+ $container['projectRoleRestrictionCacheDecorator'] = function($c) {
+ return new ProjectRoleRestrictionCacheDecorator(
+ $c['memoryCache'],
+ $c['projectRoleRestrictionModel']
+ );
+ };
+
+ return $container;
+ }
+}
diff --git a/app/ServiceProvider/ClassProvider.php b/app/ServiceProvider/ClassProvider.php
index e32c0d43..50ce531f 100644
--- a/app/ServiceProvider/ClassProvider.php
+++ b/app/ServiceProvider/ClassProvider.php
@@ -34,12 +34,15 @@ class ClassProvider implements ServiceProviderInterface
'CategoryModel',
'ColorModel',
'ColumnModel',
+ 'ColumnRestrictionModel',
+ 'ColumnMoveRestrictionModel',
'CommentModel',
'ConfigModel',
'CurrencyModel',
'CustomFilterModel',
'GroupModel',
'GroupMemberModel',
+ 'InviteModel',
'LanguageModel',
'LastLoginModel',
'LinkModel',
@@ -55,11 +58,16 @@ class ClassProvider implements ServiceProviderInterface
'ProjectNotificationModel',
'ProjectMetadataModel',
'ProjectGroupRoleModel',
+ 'ProjectRoleModel',
+ 'ProjectRoleRestrictionModel',
'ProjectTaskDuplicationModel',
'ProjectTaskPriorityModel',
'ProjectUserRoleModel',
'RememberMeSessionModel',
'SubtaskModel',
+ 'SubtaskPositionModel',
+ 'SubtaskStatusModel',
+ 'SubtaskTaskConversionModel',
'SubtaskTimeTrackingModel',
'SwimlaneModel',
'TagDuplicationModel',
@@ -84,7 +92,6 @@ class ClassProvider implements ServiceProviderInterface
'TransitionModel',
'UserModel',
'UserLockingModel',
- 'UserMentionModel',
'UserNotificationModel',
'UserNotificationFilterModel',
'UserUnreadNotificationModel',
@@ -94,6 +101,8 @@ class ClassProvider implements ServiceProviderInterface
'ActionValidator',
'AuthValidator',
'CategoryValidator',
+ 'ColumnMoveRestrictionValidator',
+ 'ColumnRestrictionValidator',
'ColumnValidator',
'CommentValidator',
'CurrencyValidator',
@@ -103,6 +112,7 @@ class ClassProvider implements ServiceProviderInterface
'LinkValidator',
'PasswordResetValidator',
'ProjectValidator',
+ 'ProjectRoleValidator',
'SubtaskValidator',
'SwimlaneValidator',
'TagValidator',
@@ -119,6 +129,12 @@ class ClassProvider implements ServiceProviderInterface
'TaskExport',
'TransitionExport',
),
+ 'Pagination' => array(
+ 'TaskPagination',
+ 'SubtaskPagination',
+ 'ProjectPagination',
+ 'UserPagination',
+ ),
'Core' => array(
'DateParser',
'Lexer',
@@ -131,9 +147,6 @@ class ClassProvider implements ServiceProviderInterface
'Response',
'RememberMeCookie',
),
- 'Core\Cache' => array(
- 'MemoryCache',
- ),
'Core\Plugin' => array(
'Hook',
),
diff --git a/app/ServiceProvider/CommandProvider.php b/app/ServiceProvider/CommandProvider.php
index 55c2712b..f17ba352 100644
--- a/app/ServiceProvider/CommandProvider.php
+++ b/app/ServiceProvider/CommandProvider.php
@@ -3,6 +3,9 @@
namespace Kanboard\ServiceProvider;
use Kanboard\Console\CronjobCommand;
+use Kanboard\Console\DatabaseMigrationCommand;
+use Kanboard\Console\DatabaseVersionCommand;
+use Kanboard\Console\JobCommand;
use Kanboard\Console\LocaleComparatorCommand;
use Kanboard\Console\LocaleSyncCommand;
use Kanboard\Console\PluginInstallCommand;
@@ -50,11 +53,14 @@ class CommandProvider implements ServiceProviderInterface
$application->add(new TaskTriggerCommand($container));
$application->add(new CronjobCommand($container));
$application->add(new WorkerCommand($container));
+ $application->add(new JobCommand($container));
$application->add(new ResetPasswordCommand($container));
$application->add(new ResetTwoFactorCommand($container));
$application->add(new PluginUpgradeCommand($container));
$application->add(new PluginInstallCommand($container));
$application->add(new PluginUninstallCommand($container));
+ $application->add(new DatabaseMigrationCommand($container));
+ $application->add(new DatabaseVersionCommand($container));
$container['cli'] = $application;
return $container;
diff --git a/app/ServiceProvider/DatabaseProvider.php b/app/ServiceProvider/DatabaseProvider.php
index a3f57457..9998ac43 100644
--- a/app/ServiceProvider/DatabaseProvider.php
+++ b/app/ServiceProvider/DatabaseProvider.php
@@ -27,6 +27,10 @@ class DatabaseProvider implements ServiceProviderInterface
{
$container['db'] = $this->getInstance();
+ if (DB_RUN_MIGRATIONS) {
+ self::runMigrations($container['db']);
+ }
+
if (DEBUG) {
$container['db']->getStatementHandler()
->withLogging()
@@ -38,7 +42,7 @@ class DatabaseProvider implements ServiceProviderInterface
}
/**
- * Setup the database driver and execute schema migration
+ * Setup the database driver
*
* @access public
* @return \PicoDb\Database
@@ -59,12 +63,39 @@ class DatabaseProvider implements ServiceProviderInterface
throw new LogicException('Database driver not supported');
}
- if ($db->schema()->check(\Schema\VERSION)) {
- return $db;
- } else {
+ return $db;
+ }
+
+ /**
+ * Get current database version
+ *
+ * @static
+ * @access public
+ * @param Database $db
+ * @return int
+ */
+ public static function getSchemaVersion(Database $db)
+ {
+ return $db->getDriver()->getSchemaVersion();
+ }
+
+ /**
+ * Execute database migrations
+ *
+ * @static
+ * @access public
+ * @throws RuntimeException
+ * @param Database $db
+ * @return bool
+ */
+ public static function runMigrations(Database $db)
+ {
+ if (! $db->schema()->check(\Schema\VERSION)) {
$messages = $db->getLogMessages();
throw new RuntimeException('Unable to run SQL migrations: '.implode(', ', $messages).' (You may have to fix it manually)');
}
+
+ return true;
}
/**
@@ -79,7 +110,7 @@ class DatabaseProvider implements ServiceProviderInterface
return new Database(array(
'driver' => 'sqlite',
- 'filename' => DB_FILENAME
+ 'filename' => DB_FILENAME,
));
}
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/ExternalTaskProvider.php b/app/ServiceProvider/ExternalTaskProvider.php
new file mode 100644
index 00000000..52484ae6
--- /dev/null
+++ b/app/ServiceProvider/ExternalTaskProvider.php
@@ -0,0 +1,29 @@
+<?php
+
+namespace Kanboard\ServiceProvider;
+
+use Kanboard\Core\ExternalTask\ExternalTaskManager;
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+
+/**
+ * Class ExternalTaskProvider
+ *
+ * @package Kanboard\ServiceProvider
+ * @author Frederic Guillot
+ */
+class ExternalTaskProvider implements ServiceProviderInterface
+{
+ /**
+ * Register providers
+ *
+ * @access public
+ * @param \Pimple\Container $container
+ * @return \Pimple\Container
+ */
+ public function register(Container $container)
+ {
+ $container['externalTaskManager'] = new ExternalTaskManager();
+ return $container;
+ }
+}
diff --git a/app/ServiceProvider/FilterProvider.php b/app/ServiceProvider/FilterProvider.php
index 20281a09..1cc4da8a 100644
--- a/app/ServiceProvider/FilterProvider.php
+++ b/app/ServiceProvider/FilterProvider.php
@@ -14,13 +14,17 @@ use Kanboard\Filter\TaskCategoryFilter;
use Kanboard\Filter\TaskColorFilter;
use Kanboard\Filter\TaskColumnFilter;
use Kanboard\Filter\TaskCommentFilter;
+use Kanboard\Filter\TaskCompletionDateFilter;
use Kanboard\Filter\TaskCreationDateFilter;
use Kanboard\Filter\TaskCreatorFilter;
use Kanboard\Filter\TaskDescriptionFilter;
use Kanboard\Filter\TaskDueDateFilter;
+use Kanboard\Filter\TaskStartDateFilter;
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;
@@ -137,6 +141,7 @@ class FilterProvider implements ServiceProviderInterface
->withFilter(TaskColorFilter::getInstance()
->setColorModel($c['colorModel'])
)
+ ->withFilter(new TaskPriorityFilter())
->withFilter(new TaskColumnFilter())
->withFilter(new TaskCommentFilter())
->withFilter(TaskCreationDateFilter::getInstance()
@@ -149,6 +154,12 @@ class FilterProvider implements ServiceProviderInterface
->withFilter(TaskDueDateFilter::getInstance()
->setDateParser($c['dateParser'])
)
+ ->withFilter(TaskStartDateFilter::getInstance()
+ ->setDateParser($c['dateParser'])
+ )
+ ->withFilter(TaskCompletionDateFilter::getInstance()
+ ->setDateparser($c['dateParser'])
+ )
->withFilter(new TaskIdFilter())
->withFilter(TaskLinkFilter::getInstance()
->setDatabase($c['db'])
@@ -156,6 +167,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())
diff --git a/app/ServiceProvider/FormatterProvider.php b/app/ServiceProvider/FormatterProvider.php
new file mode 100644
index 00000000..dbba3f3c
--- /dev/null
+++ b/app/ServiceProvider/FormatterProvider.php
@@ -0,0 +1,48 @@
+<?php
+
+namespace Kanboard\ServiceProvider;
+
+use Kanboard\Core\Tool;
+use Pimple\Container;
+use Pimple\ServiceProviderInterface;
+
+/**
+ * Class FormatterProvider
+ *
+ * @package Kanboard\ServiceProvider
+ * @author Frederic Guillot
+ */
+class FormatterProvider implements ServiceProviderInterface
+{
+ protected $formatters = array(
+ 'Formatter' => array(
+ 'BoardColumnFormatter',
+ 'BoardFormatter',
+ 'BoardSwimlaneFormatter',
+ 'BoardTaskFormatter',
+ 'GroupAutoCompleteFormatter',
+ 'ProjectActivityEventFormatter',
+ 'ProjectGanttFormatter',
+ 'SubtaskTimeTrackingCalendarFormatter',
+ 'TaskAutoCompleteFormatter',
+ 'TaskCalendarFormatter',
+ 'TaskGanttFormatter',
+ 'TaskICalFormatter',
+ 'TaskSuggestMenuFormatter',
+ 'UserAutoCompleteFormatter',
+ 'UserMentionFormatter',
+ )
+ );
+
+ /**
+ * Registers services on the given container.
+ *
+ * @param Container $container
+ * @return Container
+ */
+ public function register(Container $container)
+ {
+ Tool::buildFactories($container, $this->formatters);
+ return $container;
+ }
+}
diff --git a/app/ServiceProvider/HelperProvider.php b/app/ServiceProvider/HelperProvider.php
index a909e3cf..dcaf81c6 100644
--- a/app/ServiceProvider/HelperProvider.php
+++ b/app/ServiceProvider/HelperProvider.php
@@ -35,9 +35,11 @@ class HelperProvider implements ServiceProviderInterface
$container['helper']->register('url', '\Kanboard\Helper\UrlHelper');
$container['helper']->register('user', '\Kanboard\Helper\UserHelper');
$container['helper']->register('avatar', '\Kanboard\Helper\AvatarHelper');
+ $container['helper']->register('projectRole', '\Kanboard\Helper\ProjectRoleHelper');
$container['helper']->register('projectHeader', '\Kanboard\Helper\ProjectHeaderHelper');
$container['helper']->register('projectActivity', '\Kanboard\Helper\ProjectActivityHelper');
$container['helper']->register('mail', '\Kanboard\Helper\MailHelper');
+ $container['helper']->register('modal', '\Kanboard\Helper\ModalHelper');
$container['template'] = new Template($container['helper']);
diff --git a/app/ServiceProvider/JobProvider.php b/app/ServiceProvider/JobProvider.php
new file mode 100644
index 00000000..4e5e0f1a
--- /dev/null
+++ b/app/ServiceProvider/JobProvider.php
@@ -0,0 +1,72 @@
+<?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 Kanboard\Job\UserMentionJob;
+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);
+ });
+
+ $container['userMentionJob'] = $container->factory(function ($c) {
+ return new UserMentionJob($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 8801e3d0..08759b22 100644
--- a/app/ServiceProvider/RouteProvider.php
+++ b/app/ServiceProvider/RouteProvider.php
@@ -65,10 +65,7 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('project/:project_id/overview', 'ProjectOverviewController', 'show');
// ProjectEdit routes
- $container['route']->addRoute('project/:project_id/edit', 'ProjectEditController', 'edit');
- $container['route']->addRoute('project/:project_id/edit/dates', 'ProjectEditController', 'dates');
- $container['route']->addRoute('project/:project_id/edit/description', 'ProjectEditController', 'description');
- $container['route']->addRoute('project/:project_id/edit/priority', 'ProjectEditController', 'priority');
+ $container['route']->addRoute('project/:project_id/edit', 'ProjectEditController', 'show');
// ProjectUser routes
$container['route']->addRoute('projects/managers/:user_id', 'ProjectUserOverviewController', 'managers');
@@ -109,8 +106,8 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('export/summary/:project_id', 'ExportController', 'summary');
// Analytics routes
- $container['route']->addRoute('analytics/tasks/:project_id', 'AnalyticController', 'tasks');
- $container['route']->addRoute('analytics/users/:project_id', 'AnalyticController', 'users');
+ $container['route']->addRoute('analytics/tasks/:project_id', 'AnalyticController', 'taskDistribution');
+ $container['route']->addRoute('analytics/users/:project_id', 'AnalyticController', 'userDistribution');
$container['route']->addRoute('analytics/cfd/:project_id', 'AnalyticController', 'cfd');
$container['route']->addRoute('analytics/burndown/:project_id', 'AnalyticController', 'burndown');
$container['route']->addRoute('analytics/average-time-column/:project_id', 'AnalyticController', 'averageTimeByColumn');
@@ -158,6 +155,7 @@ class RouteProvider implements ServiceProviderInterface
$container['route']->addRoute('user/:user_id/authentication', 'UserCredentialController', 'changeAuthentication');
$container['route']->addRoute('user/:user_id/2fa', 'TwoFactorController', 'index');
$container['route']->addRoute('user/:user_id/avatar', 'AvatarFileController', 'show');
+ $container['route']->addRoute('user/:user_id/api', 'UserApiAccessController', 'show');
// Groups
$container['route']->addRoute('groups', 'GroupListController', 'index');
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..ad16685b 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;
@@ -15,32 +15,31 @@ class NotificationSubscriber extends BaseSubscriber implements EventSubscriberIn
public static function getSubscribedEvents()
{
return array(
- TaskModel::EVENT_USER_MENTION => 'handleEvent',
- TaskModel::EVENT_CREATE => 'handleEvent',
- TaskModel::EVENT_UPDATE => 'handleEvent',
- TaskModel::EVENT_CLOSE => 'handleEvent',
- TaskModel::EVENT_OPEN => 'handleEvent',
- TaskModel::EVENT_MOVE_COLUMN => 'handleEvent',
- TaskModel::EVENT_MOVE_POSITION => 'handleEvent',
- TaskModel::EVENT_MOVE_SWIMLANE => 'handleEvent',
- TaskModel::EVENT_ASSIGNEE_CHANGE => 'handleEvent',
- SubtaskModel::EVENT_CREATE => 'handleEvent',
- SubtaskModel::EVENT_UPDATE => 'handleEvent',
- CommentModel::EVENT_CREATE => 'handleEvent',
- CommentModel::EVENT_UPDATE => 'handleEvent',
- CommentModel::EVENT_USER_MENTION => 'handleEvent',
- TaskFileModel::EVENT_CREATE => 'handleEvent',
+ TaskModel::EVENT_USER_MENTION => 'handleEvent',
+ TaskModel::EVENT_CREATE => 'handleEvent',
+ TaskModel::EVENT_UPDATE => 'handleEvent',
+ TaskModel::EVENT_CLOSE => 'handleEvent',
+ TaskModel::EVENT_OPEN => 'handleEvent',
+ TaskModel::EVENT_MOVE_COLUMN => 'handleEvent',
+ TaskModel::EVENT_MOVE_POSITION => 'handleEvent',
+ TaskModel::EVENT_MOVE_SWIMLANE => 'handleEvent',
+ 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 21cd3996..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->taskRecurrenceModel->duplicateRecurringTask($event['task_id']);
- } elseif ($event['recurrence_trigger'] == TaskModel::RECURRING_TRIGGER_LAST_COLUMN && $this->columnModel->getLastColumnId($event['project_id']) == $event['dst_column_id']) {
- $this->taskRecurrenceModel->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,8 +33,9 @@ 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) {
+ 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..90db0450 100644
--- a/app/Template/action/index.php
+++ b/app/Template/action/index.php
@@ -2,12 +2,10 @@
<h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2>
<ul>
<li>
- <i class="fa fa-plus fa-fw"></i>
- <?= $this->url->link(t('Add a new action'), 'ActionCreationController', 'create', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->medium('plus', t('Add a new action'), 'ActionCreationController', 'create', array('project_id' => $project['id'])) ?>
</li>
<li>
- <i class="fa fa-copy fa-fw"></i>
- <?= $this->url->link(t('Import from another project'), 'ProjectActionDuplicationController', 'show', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->medium('copy', t('Import from another project'), 'ProjectActionDuplicationController', 'show', array('project_id' => $project['id'])) ?>
</li>
</ul>
</div>
@@ -15,10 +13,10 @@
<?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>
+ <th class="column-60"><?= t('Automatic actions') ?></th>
+ <th class="column-25"><?= t('Action parameters') ?></th>
<th><?= t('Action') ?></th>
</tr>
@@ -54,6 +52,8 @@
<?= $this->text->in($param_value, $categories_list) ?>
<?php elseif ($this->text->contains($param_name, 'link_id')): ?>
<?= $this->text->in($param_value, $links_list) ?>
+ <?php elseif ($this->text->contains($param_name, 'swimlane_id')): ?>
+ <?= $this->text->in($param_value, $swimlane_list) ?>
<?php else: ?>
<?= $this->text->e($param_value) ?>
<?php endif ?>
@@ -63,7 +63,7 @@
</ul>
</td>
<td>
- <?= $this->url->link(t('Remove'), 'ActionController', 'confirm', array('project_id' => $project['id'], 'action_id' => $action['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ActionController', 'confirm', array('project_id' => $project['id'], 'action_id' => $action['id'])) ?>
</td>
</tr>
<?php endforeach ?>
diff --git a/app/Template/action/remove.php b/app/Template/action/remove.php
index 384bec7a..e3cdb206 100644
--- a/app/Template/action/remove.php
+++ b/app/Template/action/remove.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to remove this action: "%s"?', $this->text->in($action['event_name'], $available_events).'/'.$this->text->in($action['action_name'], $available_actions)) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'ActionController', 'remove', array('project_id' => $project['id'], 'action_id' => $action['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ActionController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'ActionController',
+ 'remove',
+ array('project_id' => $project['id'], 'action_id' => $action['id'])
+ ) ?>
</div>
diff --git a/app/Template/action_creation/create.php b/app/Template/action_creation/create.php
index c0d2880e..862ee474 100644
--- a/app/Template/action_creation/create.php
+++ b/app/Template/action_creation/create.php
@@ -1,16 +1,14 @@
<div class="page-header">
<h2><?= t('Add an action') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('ActionCreationController', 'event', array('project_id' => $project['id'])) ?>">
+<form method="post" action="<?= $this->url->href('ActionCreationController', 'event', array('project_id' => $project['id'])) ?>">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Action'), 'action_name') ?>
<?= $this->form->select('action_name', $available_actions, $values) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Next step') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ActionController', 'index', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons(array(
+ 'submitLabel' => t('Next step')
+ )) ?>
</form>
diff --git a/app/Template/action_creation/event.php b/app/Template/action_creation/event.php
index cdf00310..e4166548 100644
--- a/app/Template/action_creation/event.php
+++ b/app/Template/action_creation/event.php
@@ -2,8 +2,7 @@
<h2><?= t('Choose an event') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('ActionCreationController', 'params', array('project_id' => $project['id'])) ?>">
-
+<form method="post" action="<?= $this->url->href('ActionCreationController', 'params', array('project_id' => $project['id'])) ?>">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
@@ -19,9 +18,7 @@
<?= t('When the selected event occurs execute the corresponding action.') ?>
</div>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Next step') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ActionController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons(array(
+ 'submitLabel' => t('Next step')
+ )) ?>
</form>
diff --git a/app/Template/action_creation/params.php b/app/Template/action_creation/params.php
index fa892177..0cc98f50 100644
--- a/app/Template/action_creation/params.php
+++ b/app/Template/action_creation/params.php
@@ -2,8 +2,7 @@
<h2><?= t('Define action parameters') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('ActionCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
-
+<form method="post" action="<?= $this->url->href('ActionCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
@@ -41,15 +40,14 @@
<?php elseif ($this->text->contains($param_name, 'duration')): ?>
<?= $this->form->label($param_desc, $param_name) ?>
<?= $this->form->number('params['.$param_name.']', $values) ?>
+ <?php elseif ($this->text->contains($param_name, 'swimlane_id')): ?>
+ <?= $this->form->label($param_desc, $param_name) ?>
+ <?= $this->form->select('params['.$param_name.']', $swimlane_list, $values) ?>
<?php else: ?>
<?= $this->form->label($param_desc, $param_name) ?>
<?= $this->form->text('params['.$param_name.']', $values) ?>
<?php endif ?>
<?php endforeach ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ActionController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/activity/project.php b/app/Template/activity/project.php
index ce1c8c0f..ce1f8bba 100644
--- a/app/Template/activity/project.php
+++ b/app/Template/activity/project.php
@@ -1,14 +1,12 @@
-<section id="main">
- <?= $this->projectHeader->render($project, 'AnalyticController', $this->app->getRouterAction()) ?>
+<div class="page-header">
+ <h2><?= t('%s\'s activity', $project['name']) ?></h2>
<?php if ($project['is_public']): ?>
- <div class="menu-inline pull-right">
<ul>
- <li><i class="fa fa-rss-square fa-fw"></i><?= $this->url->link(t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></li>
- <li><i class="fa fa-calendar fa-fw"></i><?= $this->url->link(t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?></li>
+ <li><?= $this->url->icon('rss-square', t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></li>
+ <li><?= $this->url->icon('calendar', t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?></li>
</ul>
- </div>
<?php endif ?>
+</div>
- <?= $this->render('event/events', array('events' => $events)) ?>
-</section>
+<?= $this->render('event/events', array('events' => $events)) ?>
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..1af69c8b 100644
--- a/app/Template/analytic/avg_time_columns.php
+++ b/app/Template/analytic/avg_time_columns.php
@@ -1,29 +1,31 @@
-<div class="page-header">
- <h2><?= t('Average time spent into each column') ?></h2>
-</div>
+<?php if (! $is_ajax): ?>
+ <div class="page-header">
+ <h2><?= t('Average time spent into each column') ?></h2>
+ </div>
+<?php endif ?>
<?php if (empty($metrics)): ?>
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
<?php else: ?>
- <section id="analytic-avg-time-column">
-
- <div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>' data-label="<?= t('Average time spent') ?>"></div>
+ <?= $this->app->component('chart-project-avg-time-column', array(
+ 'metrics' => $metrics,
+ 'label' => t('Average time spent'),
+ )) ?>
- <table class="table-stripped">
+ <table class="table-striped">
<tr>
<th><?= t('Column') ?></th>
<th><?= t('Average time spent') ?></th>
</tr>
<?php foreach ($metrics as $column): ?>
- <tr>
- <td><?= $this->text->e($column['title']) ?></td>
- <td><?= $this->dt->duration($column['average']) ?></td>
- </tr>
+ <tr>
+ <td><?= $this->text->e($column['title']) ?></td>
+ <td><?= $this->dt->duration($column['average']) ?></td>
+ </tr>
<?php endforeach ?>
- </table>
+ </table>
- <p class="alert alert-info">
- <?= t('This chart show the average time spent into each column for the last %d tasks.', 1000) ?>
- </p>
- </section>
+ <p class="alert alert-info">
+ <?= t('This chart show the average time spent into each column for the last %d tasks.', 1000) ?>
+ </p>
<?php endif ?>
diff --git a/app/Template/analytic/burndown.php b/app/Template/analytic/burndown.php
index e979595d..d62c9ba0 100644
--- a/app/Template/analytic/burndown.php
+++ b/app/Template/analytic/burndown.php
@@ -1,34 +1,26 @@
-<div class="page-header">
- <h2><?= t('Burndown chart') ?></h2>
-</div>
+<?php if (! $is_ajax): ?>
+ <div class="page-header">
+ <h2><?= t('Burndown chart') ?></h2>
+ </div>
+<?php endif ?>
<?php if (! $display_graph): ?>
<p class="alert"><?= t('You need at least 2 days of data to show the chart.') ?></p>
<?php else: ?>
- <section id="analytic-burndown">
- <div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>' data-date-format="<?= e('%%Y-%%m-%%d') ?>" data-label-total="<?= t('Total for all columns') ?>"></div>
- </section>
+ <?= $this->app->component('chart-project-burndown', array(
+ 'metrics' => $metrics,
+ 'labelTotal' => t('Total for all columns'),
+ 'dateFormat' => e('%%Y-%%m-%%d'),
+ )) ?>
<?php endif ?>
<hr/>
<form method="post" class="form-inline" action="<?= $this->url->href('AnalyticController', 'burndown', array('project_id' => $project['id'])) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
-
- <div class="form-inline-group">
- <?= $this->form->label(t('Start Date'), 'from') ?>
- <?= $this->form->text('from', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
- </div>
-
- <div class="form-inline-group">
- <?= $this->form->label(t('End Date'), 'to') ?>
- <?= $this->form->text('to', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
- </div>
-
- <div class="form-inline-group">
- <button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
- </div>
+ <?= $this->form->date(t('Start date'), 'from', $values) ?>
+ <?= $this->form->date(t('End date'), 'to', $values) ?>
+ <?= $this->modal->submitButtons(array('submitLabel' => t('Execute'))) ?>
</form>
<p class="alert alert-info"><?= t('This chart show the task complexity over the time (Work Remaining).') ?></p>
diff --git a/app/Template/analytic/cfd.php b/app/Template/analytic/cfd.php
index 8dfb5b00..dcd7b58f 100644
--- a/app/Template/analytic/cfd.php
+++ b/app/Template/analytic/cfd.php
@@ -1,32 +1,23 @@
-<div class="page-header">
- <h2><?= t('Cumulative flow diagram') ?></h2>
-</div>
+<?php if (! $is_ajax): ?>
+ <div class="page-header">
+ <h2><?= t('Cumulative flow diagram') ?></h2>
+ </div>
+<?php endif ?>
<?php if (! $display_graph): ?>
<p class="alert"><?= t('You need at least 2 days of data to show the chart.') ?></p>
<?php else: ?>
- <section id="analytic-cfd">
- <div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>' data-date-format="<?= e('%%Y-%%m-%%d') ?>"></div>
- </section>
+ <?= $this->app->component('chart-project-cumulative-flow', array(
+ 'metrics' => $metrics,
+ 'dateFormat' => e('%%Y-%%m-%%d'),
+ )) ?>
<?php endif ?>
<hr/>
<form method="post" class="form-inline" action="<?= $this->url->href('AnalyticController', 'cfd', array('project_id' => $project['id'])) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
-
- <div class="form-inline-group">
- <?= $this->form->label(t('Start Date'), 'from') ?>
- <?= $this->form->text('from', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
- </div>
-
- <div class="form-inline-group">
- <?= $this->form->label(t('End Date'), 'to') ?>
- <?= $this->form->text('to', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
- </div>
-
- <div class="form-inline-group">
- <button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
- </div>
+ <?= $this->form->date(t('Start date'), 'from', $values) ?>
+ <?= $this->form->date(t('End date'), 'to', $values) ?>
+ <?= $this->modal->submitButtons(array('submitLabel' => t('Execute'))) ?>
</form>
diff --git a/app/Template/analytic/layout.php b/app/Template/analytic/layout.php
index e3c6099f..7159094c 100644
--- a/app/Template/analytic/layout.php
+++ b/app/Template/analytic/layout.php
@@ -1,10 +1,14 @@
-<section id="main">
+<?php if ($is_ajax): ?>
+ <div class="page-header">
+ <h2><?= $title ?></h2>
+ </div>
+<?php else: ?>
<?= $this->projectHeader->render($project, 'TaskListController', 'show') ?>
- <section class="sidebar-container">
- <?= $this->render($sidebar_template, array('project' => $project)) ?>
+<?php endif ?>
+<section class="sidebar-container">
+ <?= $this->render($sidebar_template, array('project' => $project)) ?>
- <div class="sidebar-content">
- <?= $content_for_sublayout ?>
- </div>
- </section>
+ <div class="sidebar-content">
+ <?= $content_for_sublayout ?>
+ </div>
</section>
diff --git a/app/Template/analytic/lead_cycle_time.php b/app/Template/analytic/lead_cycle_time.php
index 2dccc136..780b47b6 100644
--- a/app/Template/analytic/lead_cycle_time.php
+++ b/app/Template/analytic/lead_cycle_time.php
@@ -1,8 +1,10 @@
-<div class="page-header">
- <h2><?= t('Average Lead and Cycle time') ?></h2>
-</div>
+<?php if (! $is_ajax): ?>
+ <div class="page-header">
+ <h2><?= t('Average Lead and Cycle time') ?></h2>
+ </div>
+<?php endif ?>
-<div class="listing">
+<div class="panel">
<ul>
<li><?= t('Average lead time: ').'<strong>'.$this->dt->duration($average['avg_lead_time']) ?></strong></li>
<li><?= t('Average cycle time: ').'<strong>'.$this->dt->duration($average['avg_cycle_time']) ?></strong></li>
@@ -12,31 +14,20 @@
<?php if (empty($metrics)): ?>
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
<?php else: ?>
- <section id="analytic-lead-cycle-time">
-
- <div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>' data-label-cycle="<?= t('Cycle Time') ?>" data-label-lead="<?= t('Lead Time') ?>"></div>
-
- <form method="post" class="form-inline" action="<?= $this->url->href('AnalyticController', 'leadAndCycleTime', array('project_id' => $project['id'])) ?>" autocomplete="off">
-
- <?= $this->form->csrf() ?>
-
- <div class="form-inline-group">
- <?= $this->form->label(t('Start Date'), 'from') ?>
- <?= $this->form->text('from', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
- </div>
-
- <div class="form-inline-group">
- <?= $this->form->label(t('End Date'), 'to') ?>
- <?= $this->form->text('to', $values, array(), array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
- </div>
-
- <div class="form-inline-group">
- <button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
- </div>
- </form>
-
- <p class="alert alert-info">
- <?= t('This chart show the average lead and cycle time for the last %d tasks over the time.', 1000) ?>
- </p>
- </section>
+ <?= $this->app->component('chart-project-lead-cycle-time', array(
+ 'metrics' => $metrics,
+ 'labelCycle' => t('Cycle Time'),
+ 'labelLead' => t('Lead Time'),
+ )) ?>
+
+ <form method="post" class="form-inline" action="<?= $this->url->href('AnalyticController', 'leadAndCycleTime', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->date(t('Start date'), 'from', $values) ?>
+ <?= $this->form->date(t('End date'), 'to', $values) ?>
+ <?= $this->modal->submitButtons(array('submitLabel' => t('Execute'))) ?>
+ </form>
+
+ <p class="alert alert-info">
+ <?= t('This chart show the average lead and cycle time for the last %d tasks over the time.', 1000) ?>
+ </p>
<?php endif ?>
diff --git a/app/Template/analytic/sidebar.php b/app/Template/analytic/sidebar.php
index de3dccf8..d5ce88cb 100644
--- a/app/Template/analytic/sidebar.php
+++ b/app/Template/analytic/sidebar.php
@@ -1,29 +1,27 @@
<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'])) ?>
+ <li <?= $this->app->checkMenuSelection('AnalyticController', 'taskDistribution') ?>>
+ <?= $this->modal->replaceLink(t('Task distribution'), 'AnalyticController', 'taskDistribution', array('project_id' => $project['id'])) ?>
</li>
- <li <?= $this->app->checkMenuSelection('AnalyticController', 'users') ?>>
- <?= $this->url->link(t('User repartition'), 'AnalyticController', 'users', array('project_id' => $project['id'])) ?>
+ <li <?= $this->app->checkMenuSelection('AnalyticController', 'userDistribution') ?>>
+ <?= $this->modal->replaceLink(t('User repartition'), 'AnalyticController', 'userDistribution', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('AnalyticController', 'cfd') ?>>
- <?= $this->url->link(t('Cumulative flow diagram'), 'AnalyticController', 'cfd', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->replaceLink(t('Cumulative flow diagram'), 'AnalyticController', 'cfd', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('AnalyticController', 'burndown') ?>>
- <?= $this->url->link(t('Burndown chart'), 'AnalyticController', 'burndown', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->replaceLink(t('Burndown chart'), 'AnalyticController', 'burndown', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('AnalyticController', 'averageTimeByColumn') ?>>
- <?= $this->url->link(t('Average time into each column'), 'AnalyticController', 'averageTimeByColumn', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->replaceLink(t('Average time into each column'), 'AnalyticController', 'averageTimeByColumn', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('AnalyticController', 'leadAndCycleTime') ?>>
- <?= $this->url->link(t('Lead and cycle time'), 'AnalyticController', 'leadAndCycleTime', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->replaceLink(t('Lead and cycle time'), 'AnalyticController', 'leadAndCycleTime', array('project_id' => $project['id'])) ?>
</li>
- <li <?= $this->app->checkMenuSelection('AnalyticController', 'compareHours') ?>>
- <?= $this->url->link(t('Estimated vs actual time'), 'AnalyticController', 'compareHours', array('project_id' => $project['id'])) ?>
+ <li <?= $this->app->checkMenuSelection('AnalyticController', 'timeComparison') ?>>
+ <?= $this->modal->replaceLink(t('Estimated vs actual time'), 'AnalyticController', 'timeComparison', array('project_id' => $project['id'])) ?>
</li>
<?= $this->hook->render('template:analytic:sidebar', array('project' => $project)) ?>
-
</ul>
</div>
diff --git a/app/Template/analytic/tasks.php b/app/Template/analytic/task_distribution.php
index 9e7b1fd7..671d462f 100644
--- a/app/Template/analytic/tasks.php
+++ b/app/Template/analytic/task_distribution.php
@@ -1,15 +1,17 @@
-<div class="page-header">
- <h2><?= t('Task distribution') ?></h2>
-</div>
+<?php if (! $is_ajax): ?>
+ <div class="page-header">
+ <h2><?= t('Task distribution') ?></h2>
+ </div>
+<?php endif ?>
<?php if (empty($metrics)): ?>
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
<?php else: ?>
- <section id="analytic-task-repartition">
-
- <div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>'></div>
+ <?= $this->app->component('chart-project-task-distribution', array(
+ 'metrics' => $metrics,
+ )) ?>
- <table>
+ <table class="table-striped">
<tr>
<th><?= t('Column') ?></th>
<th><?= t('Number of tasks') ?></th>
@@ -29,6 +31,4 @@
</tr>
<?php endforeach ?>
</table>
-
- </section>
<?php endif ?>
diff --git a/app/Template/analytic/compare_hours.php b/app/Template/analytic/time_comparison.php
index 70d8d02b..754c68f2 100644
--- a/app/Template/analytic/compare_hours.php
+++ b/app/Template/analytic/time_comparison.php
@@ -1,8 +1,10 @@
-<div class="page-header">
- <h2><?= t('Compare Estimated Time vs Actual Time') ?></h2>
-</div>
+<?php if (! $is_ajax): ?>
+ <div class="page-header">
+ <h2><?= t('Estimated vs actual time') ?></h2>
+ </div>
+<?php endif ?>
-<div class="listing">
+<div class="panel">
<ul>
<li><?= t('Estimated hours: ').'<strong>'.$this->text->e($metrics['open']['time_estimated'] + $metrics['closed']['time_estimated']) ?></strong></li>
<li><?= t('Actual hours: ').'<strong>'.$this->text->e($metrics['open']['time_spent'] + $metrics['closed']['time_spent']) ?></strong></li>
@@ -12,24 +14,24 @@
<?php if (empty($metrics)): ?>
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
<?php else: ?>
-<section id="analytic-compare-hours">
- <div id="chart"
- data-metrics='<?= json_encode($metrics, JSON_HEX_APOS)?>'
- data-label-spent="<?= t('Hours Spent') ?>"
- data-label-estimated="<?= t('Hours Estimated') ?>"
- data-label-closed="<?= t('Closed') ?>"
- data-label-open="<?= t('Open') ?>"></div>
-
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('No tasks found.') ?></p>
<?php elseif (! $paginator->isEmpty()): ?>
- <table class="table-fixed table-small">
+ <?= $this->app->component('chart-project-time-comparison', array(
+ 'metrics' => $metrics,
+ 'labelSpent' => t('Hours Spent'),
+ 'labelEstimated' => t('Hours Estimated'),
+ 'labelClosed' => t('Closed'),
+ 'labelOpen' => t('Open'),
+ )) ?>
+
+ <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>
- <th class="column-5"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
- <th class="column-10"><?= $paginator->order(t('Estimated Time'), 'tasks.time_estimated') ?></th>
- <th class="column-10"><?= $paginator->order(t('Actual Time'), 'tasks.time_spent') ?></th>
+ <th class="column-10"><?= $paginator->order(t('Status'), 'tasks.is_active') ?></th>
+ <th class="column-12"><?= $paginator->order(t('Estimated Time'), 'tasks.time_estimated') ?></th>
+ <th class="column-12"><?= $paginator->order(t('Actual Time'), 'tasks.time_spent') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $task): ?>
<tr>
@@ -58,5 +60,4 @@
<?= $paginator ?>
<?php endif ?>
-</section>
<?php endif ?>
diff --git a/app/Template/analytic/users.php b/app/Template/analytic/user_distribution.php
index 9d1d3a1e..cae6fa57 100644
--- a/app/Template/analytic/users.php
+++ b/app/Template/analytic/user_distribution.php
@@ -1,15 +1,17 @@
-<div class="page-header">
- <h2><?= t('User repartition') ?></h2>
-</div>
+<?php if (! $is_ajax): ?>
+ <div class="page-header">
+ <h2><?= t('User repartition') ?></h2>
+ </div>
+<?php endif ?>
<?php if (empty($metrics)): ?>
<p class="alert"><?= t('Not enough data to show the graph.') ?></p>
<?php else: ?>
- <section id="analytic-user-repartition">
-
- <div id="chart" data-metrics='<?= json_encode($metrics, JSON_HEX_APOS) ?>'></div>
+ <?= $this->app->component('chart-project-user-distribution', array(
+ 'metrics' => $metrics,
+ )) ?>
- <table>
+ <table class="table-striped">
<tr>
<th><?= t('User') ?></th>
<th><?= t('Number of tasks') ?></th>
@@ -29,6 +31,4 @@
</tr>
<?php endforeach ?>
</table>
-
- </section>
<?php endif ?>
diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php
index 6336234a..df16715f 100644
--- a/app/Template/board/table_column.php
+++ b/app/Template/board/table_column.php
@@ -5,16 +5,16 @@
<!-- 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 -->
<div class="board-column-expanded">
- <?php if (! $not_editable && $this->user->hasProjectAccess('TaskCreationController', 'show', $column['project_id'])): ?>
+ <?php if (! $not_editable && $this->projectRole->canCreateTaskInColumn($column['project_id'], $column['id'])): ?>
<div class="board-add-icon">
- <?= $this->url->link('+', 'TaskCreationController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'popover', t('Add a new task')) ?>
+ <?= $this->modal->largeIcon('plus', t('Add a new task'), 'TaskCreationController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?>
</div>
<?php endif ?>
@@ -35,18 +35,19 @@
<i class="fa fa-minus-square fa-fw"></i>
<a href="#" class="board-toggle-column-view" data-column-id="<?= $column['id'] ?>"><?= t('Hide this column') ?></a>
</li>
- <?php if ($this->user->hasProjectAccess('TaskCreationController', 'show', $column['project_id'])): ?>
+ <?php if ($this->projectRole->canCreateTaskInColumn($column['project_id'], $column['id'])): ?>
+ <li>
+ <?= $this->modal->medium('align-justify', t('Create tasks in bulk'), 'TaskBulkController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?>
+ </li>
+ <?php endif ?>
+
+ <?php if ($column['nb_tasks'] > 0 && $this->projectRole->canChangeTaskStatusInColumn($column['project_id'], $column['id'])): ?>
<li>
- <i class="fa fa-align-justify fa-fw" aria-hidden="true"></i>
- <?= $this->url->link(t('Create tasks in bulk'), 'TaskBulkController', 'show', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('close', t('Close all tasks of this column'), 'BoardPopoverController', 'confirmCloseColumnTasks', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?>
</li>
- <?php if ($column['nb_tasks'] > 0): ?>
- <li>
- <i class="fa fa-close fa-fw"></i>
- <?= $this->url->link(t('Close all tasks of this column'), 'BoardPopoverController', 'confirmCloseColumnTasks', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?>
- </li>
- <?php endif ?>
<?php endif ?>
+
+ <?= $this->hook->render('template:board:column:dropdown', array('swimlane' => $swimlane, 'column' => $column)) ?>
</ul>
</span>
<?php endif ?>
@@ -73,6 +74,7 @@
(<span id="task-number-column-<?= $column['id'] ?>"><?= $column['nb_tasks'] ?></span>)
</span>
<?php endif ?>
+ <?= $this->hook->render('template:board:column:header', array('swimlane' => $swimlane, 'column' => $column)) ?>
</div>
</th>
diff --git a/app/Template/board/table_tasks.php b/app/Template/board/table_tasks.php
index fd9ce5e7..e03ca90c 100644
--- a/app/Template/board/table_tasks.php
+++ b/app/Template/board/table_tasks.php
@@ -4,10 +4,16 @@
<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'] ?>">
+ <div
+ class="board-task-list board-column-expanded <?= $this->projectRole->isSortableColumn($column['project_id'], $column['id']) ? 'sortable-column' : '' ?>"
+ data-column-id="<?= $column['id'] ?>"
+ data-swimlane-id="<?= $swimlane['id'] ?>"
+ data-task-limit="<?= $column['task_limit'] ?>">
+
<?php foreach ($column['tasks'] as $task): ?>
<?= $this->render($not_editable ? 'board/task_public' : 'board/task_private', array(
'project' => $project,
diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php
index bc34363c..15f1f713 100644
--- a/app/Template/board/task_footer.php
+++ b/app/Template/board/task_footer.php
@@ -10,7 +10,7 @@
'edit',
array('task_id' => $task['id'], 'project_id' => $task['project_id']),
false,
- 'popover' . (! empty($task['category_description']) ? ' tooltip' : ''),
+ 'js-modal-medium' . (! empty($task['category_description']) ? ' tooltip' : ''),
! empty($task['category_description']) ? $this->text->markdownAttribute($task['category_description']) : t('Change category')
) ?>
<?php endif ?>
@@ -38,9 +38,11 @@
<?php if (! empty($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">
+ <span class="task-board-date task-board-date-today">
<?php elseif (time() > $task['date_due']): ?>
- <span class="task-board-date task-board-date-overdue">
+ <span class="task-board-date task-board-date-overdue">
+ <?php else: ?>
+ <span class="task-board-date">
<?php endif ?>
<i class="fa fa-calendar"></i>
<?= $this->dt->date($task['date_due']) ?>
@@ -81,8 +83,10 @@
</span>
<?php endif ?>
- <?php if (! empty($task['time_estimated'])): ?>
- <span class="task-time-estimated" title="<?= t('Time estimated') ?>"><?= $this->text->e($task['time_estimated']).'h' ?></span>
+ <?php if (! empty($task['time_estimated']) || ! empty($task['time_spent'])): ?>
+ <span class="task-time-estimated" title="<?= t('Time spent and estimated') ?>">
+ <?= $this->text->e($task['time_spent']) ?>/<?= $this->text->e($task['time_estimated']) ?>h
+ </span>
<?php endif ?>
<?php if ($task['is_milestone'] == 1): ?>
diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php
index 94b396a6..01da46db 100644
--- a/app/Template/board/task_private.php
+++ b/app/Template/board/task_private.php
@@ -1,6 +1,7 @@
<div class="
task-board
- <?= $task['is_active'] == 1 ? ($this->user->hasProjectAccess('BoardViewController', 'save', $task['project_id']) ? 'draggable-item ' : '').'task-board-status-open '.($task['date_modification'] > (time() - $board_highlight_period) ? 'task-board-recent' : '') : 'task-board-status-closed' ?>
+ <?= $task['is_draggable'] ? 'draggable-item ' : '' ?>
+ <?= $task['is_active'] == 1 ? 'task-board-status-open '.($task['date_modification'] > (time() - $board_highlight_period) ? 'task-board-recent' : '') : 'task-board-status-closed' ?>
color-<?= $task['color_id'] ?>"
data-task-id="<?= $task['id'] ?>"
data-column-id="<?= $task['column_id'] ?>"
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..5cb72741 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>
@@ -9,9 +9,9 @@
</tr>
<tr>
<td>
- <i class="fa fa-download fa-fw"></i><?= $this->url->link(t('download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
+ <?= $this->url->icon('download', t('download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
<?php if ($file['is_image'] == 1): ?>
- &nbsp;<i class="fa fa-eye"></i> <?= $this->url->link(t('open file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?>
+ &nbsp;<?= $this->modal->large('eye', t('open file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
<?php endif ?>
</td>
</tr>
diff --git a/app/Template/board/tooltip_subtasks.php b/app/Template/board/tooltip_subtasks.php
index 0322d373..335c28ce 100644
--- a/app/Template/board/tooltip_subtasks.php
+++ b/app/Template/board/tooltip_subtasks.php
@@ -1,7 +1,8 @@
<div class="tooltip-large">
- <table>
+ <table class="table-small">
<tr>
- <th class="column-80"><?= t('Subtask') ?></th>
+ <th class="column-70"><?= t('Subtask') ?></th>
+ <?= $this->hook->render('template:board:tooltip:subtasks:header:before-assignee') ?>
<th><?= t('Assignee') ?></th>
</tr>
<?php foreach ($subtasks as $subtask): ?>
@@ -9,6 +10,7 @@
<td>
<?= $this->subtask->toggleStatus($subtask, $task['project_id']) ?>
</td>
+ <?= $this->hook->render('template:board:tooltip:subtasks:rows', array('subtask' => $subtask)) ?>
<td>
<?php if (! empty($subtask['username'])): ?>
<?= $this->text->e($subtask['name'] ?: $subtask['username']) ?>
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/board_popover/close_all_tasks_column.php b/app/Template/board_popover/close_all_tasks_column.php
index 57f703e3..ab7c2d47 100644
--- a/app/Template/board_popover/close_all_tasks_column.php
+++ b/app/Template/board_popover/close_all_tasks_column.php
@@ -1,18 +1,15 @@
-<section id="main">
- <div class="page-header">
- <h2><?= t('Do you really want to close all tasks of this column?') ?></h2>
- </div>
- <form method="post" action="<?= $this->url->href('BoardPopoverController', 'closeColumnTasks', array('project_id' => $project['id'])) ?>">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('column_id', $values) ?>
- <?= $this->form->hidden('swimlane_id', $values) ?>
+<div class="page-header">
+ <h2><?= t('Do you really want to close all tasks of this column?') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('BoardPopoverController', 'closeColumnTasks', array('project_id' => $project['id'])) ?>">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('column_id', $values) ?>
+ <?= $this->form->hidden('swimlane_id', $values) ?>
- <p class="alert"><?= t('%d task(s) in the column "%s" and the swimlane "%s" will be closed.', $nb_tasks, $column, $swimlane) ?></p>
+ <p class="alert"><?= t('%d task(s) in the column "%s" and the swimlane "%s" will be closed.', $nb_tasks, $column, $swimlane) ?></p>
- <div class="form-actions">
- <button type="submit" class="btn btn-red"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
- </form>
-</section>
+ <?= $this->modal->submitButtons(array(
+ 'submitLabel' => t('Yes'),
+ 'color' => 'red',
+ )) ?>
+</form>
diff --git a/app/Template/calendar/show.php b/app/Template/calendar/show.php
index 3635f627..009fc07e 100644
--- a/app/Template/calendar/show.php
+++ b/app/Template/calendar/show.php
@@ -1,9 +1,9 @@
<section id="main">
<?= $this->projectHeader->render($project, 'CalendarController', 'show') ?>
- <div id="calendar"
- data-save-url="<?= $this->url->href('CalendarController', 'save', array('project_id' => $project['id'])) ?>"
- data-check-url="<?= $this->url->href('CalendarController', 'project', array('project_id' => $project['id'])) ?>"
- data-check-interval="<?= $check_interval ?>"
- >
- </div>
+
+ <?= $this->calendar->render(
+ $this->url->href('CalendarController', 'project', array('project_id' => $project['id'])),
+ $this->url->href('CalendarController', 'save', array('project_id' => $project['id']))
+ ) ?>
+
</section>
diff --git a/app/Template/category/edit.php b/app/Template/category/edit.php
index fac56db3..108826f3 100644
--- a/app/Template/category/edit.php
+++ b/app/Template/category/edit.php
@@ -2,22 +2,17 @@
<h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('CategoryController', 'update', array('project_id' => $project['id'], 'category_id' => $values['id'])) ?>" autocomplete="off">
-
+<form method="post" action="<?= $this->url->href('CategoryController', 'update', array('project_id' => $project['id'], 'category_id' => $values['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Category Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textarea('description', $values, $errors, array(), 'markdown-editor') ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'CategoryController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/category/index.php b/app/Template/category/index.php
index a103d89f..e93138fd 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>
@@ -15,10 +15,10 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <?= $this->url->link(t('Edit'), 'CategoryController', 'edit', array('project_id' => $project['id'], 'category_id' => $category_id), false, 'popover') ?>
+ <?= $this->modal->medium('edit', t('Edit'), 'CategoryController', 'edit', array('project_id' => $project['id'], 'category_id' => $category_id)) ?>
</li>
<li>
- <?= $this->url->link(t('Remove'), 'CategoryController', 'confirm', array('project_id' => $project['id'], 'category_id' => $category_id), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'CategoryController', 'confirm', array('project_id' => $project['id'], 'category_id' => $category_id)) ?>
</li>
</ul>
</div>
diff --git a/app/Template/category/remove.php b/app/Template/category/remove.php
index e7b9c9b4..79e8a560 100644
--- a/app/Template/category/remove.php
+++ b/app/Template/category/remove.php
@@ -1,17 +1,15 @@
-<section id="main">
- <div class="page-header">
- <h2><?= t('Remove a category') ?></h2>
- </div>
+<div class="page-header">
+ <h2><?= t('Remove a category') ?></h2>
+</div>
- <div class="confirm">
- <p class="alert alert-info">
- <?= t('Do you really want to remove this category: "%s"?', $category['name']) ?>
- </p>
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this category: "%s"?', $category['name']) ?>
+ </p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'CategoryController', 'remove', array('project_id' => $project['id'], 'category_id' => $category['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'CategoryController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
- </div>
-</section>
+ <?= $this->modal->confirmButtons(
+ 'CategoryController',
+ 'remove',
+ array('project_id' => $project['id'], 'category_id' => $category['id'])
+ ) ?>
+</div>
diff --git a/app/Template/column/create.php b/app/Template/column/create.php
index 387b6a47..aad9606b 100644
--- a/app/Template/column/create.php
+++ b/app/Template/column/create.php
@@ -1,26 +1,21 @@
<div class="page-header">
<h2><?= t('Add a new column') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('ColumnController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
-
+<form method="post" action="<?= $this->url->href('ColumnController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Title'), 'title') ?>
- <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Task limit'), 'task_limit') ?>
- <?= $this->form->number('task_limit', $values, $errors) ?>
+ <?= $this->form->number('task_limit', $values, $errors, array('tabindex="2"')) ?>
- <?= $this->form->checkbox('hide_in_dashboard', t('Hide tasks in this column in the Dashboard'), 1) ?>
+ <?= $this->form->checkbox('hide_in_dashboard', t('Hide tasks in this column in the dashboard'), 1, false, '', array('tabindex' => 3)) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textarea('description', $values, $errors, array(), 'markdown-editor') ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 4)) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'column', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/column/edit.php b/app/Template/column/edit.php
index abd70119..e590b5cc 100644
--- a/app/Template/column/edit.php
+++ b/app/Template/column/edit.php
@@ -2,8 +2,7 @@
<h2><?= t('Edit column "%s"', $column['title']) ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('ColumnController', 'update', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>" autocomplete="off">
-
+<form method="post" action="<?= $this->url->href('ColumnController', 'update', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
@@ -15,14 +14,10 @@
<?= $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->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') ?>
+ <?= $this->form->textEditor('description', $values, $errors) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ColumnController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/column/index.php b/app/Template/column/index.php
index 04760a16..eaaae332 100644
--- a/app/Template/column/index.php
+++ b/app/Template/column/index.php
@@ -2,8 +2,7 @@
<h2><?= t('Edit the board for "%s"', $project['name']) ?></h2>
<ul>
<li>
- <i class="fa fa-plus fa-fw"></i>
- <?= $this->url->link(t('Add a new column'), 'ColumnController', 'create', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->medium('plus', t('Add a new column'), 'ColumnController', 'create', array('project_id' => $project['id'])) ?>
</li>
</ul>
</div>
@@ -12,12 +11,13 @@
<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>
<th class="column-70"><?= t('Column title') ?></th>
- <th class="column-25"><?= t('Task limit') ?></th>
+ <th class="column-10"><?= t('Task limit') ?></th>
+ <th class="column-20"><?= t('Visible on dashboard') ?></th>
<th class="column-5"><?= t('Actions') ?></th>
</tr>
</thead>
@@ -37,14 +37,17 @@
<?= $this->text->e($column['task_limit']) ?>
</td>
<td>
+ <?= $column['hide_in_dashboard'] == 1 ? t('Yes') : t('No') ?>
+ </td>
+ <td>
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <?= $this->url->link(t('Edit'), 'ColumnController', 'edit', array('project_id' => $project['id'], 'column_id' => $column['id']), false, 'popover') ?>
+ <?= $this->modal->medium('edit', t('Edit'), 'ColumnController', 'edit', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
</li>
<li>
- <?= $this->url->link(t('Remove'), 'ColumnController', 'confirm', array('project_id' => $project['id'], 'column_id' => $column['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnController', 'confirm', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?>
</li>
</ul>
</div>
diff --git a/app/Template/column/remove.php b/app/Template/column/remove.php
index b231a9a7..4134b175 100644
--- a/app/Template/column/remove.php
+++ b/app/Template/column/remove.php
@@ -8,8 +8,9 @@
<?= t('This action will REMOVE ALL TASKS associated to this column!') ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'ColumnController', 'remove', array('project_id' => $project['id'], 'column_id' => $column['id'], 'remove' => 'yes'), true, 'btn btn-red') ?>
- <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ColumnController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'ColumnController',
+ 'remove',
+ array('project_id' => $project['id'], 'column_id' => $column['id'])
+ ) ?>
</div>
diff --git a/app/Template/column_move_restriction/create.php b/app/Template/column_move_restriction/create.php
new file mode 100644
index 00000000..852df971
--- /dev/null
+++ b/app/Template/column_move_restriction/create.php
@@ -0,0 +1,18 @@
+<div class="page-header">
+ <h2><?= t('New drag and drop restriction for the role "%s"', $role['role']) ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('ColumnMoveRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+ <?= $this->form->hidden('role_id', $values) ?>
+
+ <?= $this->form->label(t('Source column'), 'src_column_id') ?>
+ <?= $this->form->select('src_column_id', $columns, $values, $errors) ?>
+
+ <?= $this->form->label(t('Destination column'), 'dst_column_id') ?>
+ <?= $this->form->select('dst_column_id', $columns, $values, $errors) ?>
+
+ <?= $this->modal->submitButtons() ?>
+
+ <p class="alert alert-info"><?= t('People belonging to this role will be able to move tasks only between the source and the destination column.') ?></p>
+</form>
diff --git a/app/Template/column_move_restriction/remove.php b/app/Template/column_move_restriction/remove.php
new file mode 100644
index 00000000..4902cd23
--- /dev/null
+++ b/app/Template/column_move_restriction/remove.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Remove a column restriction') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this column restriction: "%s" to "%s"?', $restriction['src_column_title'], $restriction['dst_column_title']) ?>
+ </p>
+
+ <?= $this->modal->confirmButtons(
+ 'ColumnMoveRestrictionController',
+ 'remove',
+ array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])
+ ) ?>
+</div>
diff --git a/app/Template/column_restriction/create.php b/app/Template/column_restriction/create.php
new file mode 100644
index 00000000..be158f19
--- /dev/null
+++ b/app/Template/column_restriction/create.php
@@ -0,0 +1,16 @@
+<div class="page-header">
+ <h2><?= t('New column restriction for the role "%s"', $role['role']) ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('ColumnRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+ <?= $this->form->hidden('role_id', $values) ?>
+
+ <?= $this->form->label(t('Rule'), 'rule') ?>
+ <?= $this->form->select('rule', $rules, $values, $errors) ?>
+
+ <?= $this->form->label(t('Column'), 'column_id') ?>
+ <?= $this->form->select('column_id', $columns, $values, $errors) ?>
+
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/column_restriction/remove.php b/app/Template/column_restriction/remove.php
new file mode 100644
index 00000000..edbd9d6e
--- /dev/null
+++ b/app/Template/column_restriction/remove.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Remove a column restriction') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this column restriction?') ?>
+ </p>
+
+ <?= $this->modal->confirmButtons(
+ 'ColumnRestrictionController',
+ 'remove',
+ array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])
+ ) ?>
+</div>
diff --git a/app/Template/comment/create.php b/app/Template/comment/create.php
index 0358107a..8a421759 100644
--- a/app/Template/comment/create.php
+++ b/app/Template/comment/create.php
@@ -1,29 +1,12 @@
<div class="page-header">
<h2><?= t('Add a comment') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('CommentController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('CommentController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', $values) ?>
<?= $this->form->hidden('user_id', $values) ?>
- <div class="markdown-editor-small">
- <?= $this->form->textarea(
- 'comment',
- $values,
- $errors,
- array(
- 'autofocus',
- 'required',
- 'placeholder="'.t('Leave a comment').'"',
- 'data-mention-search-url="'.$this->url->href('UserAjaxController', 'mention', array('project_id' => $task['project_id'])).'"',
- ),
- 'markdown-editor'
- ) ?>
- </div>
+ <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/comment/edit.php b/app/Template/comment/edit.php
index f69fc0c1..04f6ffd4 100644
--- a/app/Template/comment/edit.php
+++ b/app/Template/comment/edit.php
@@ -2,26 +2,13 @@
<h2><?= t('Edit a comment') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('CommentController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off">
-
+<form method="post" action="<?= $this->url->href('CommentController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->hidden('task_id', $values) ?>
<?= $this->form->hidden('user_id', $values) ?>
- <div class="markdown-editor-small">
- <?= $this->form->textarea(
- 'comment',
- $values,
- $errors,
- array('autofocus', 'required', 'placeholder="'.t('Leave a comment').'"'),
- 'markdown-editor'
- ) ?>
- </div>
+ <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true)) ?>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/comment/remove.php b/app/Template/comment/remove.php
index 55587b67..03f16e00 100644
--- a/app/Template/comment/remove.php
+++ b/app/Template/comment/remove.php
@@ -13,9 +13,9 @@
'hide_actions' => true
)) ?>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'CommentController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'CommentController',
+ 'remove',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])
+ ) ?>
</div>
diff --git a/app/Template/comment/show.php b/app/Template/comment/show.php
index 8419a14e..f8d9607e 100644
--- a/app/Template/comment/show.php
+++ b/app/Template/comment/show.php
@@ -4,10 +4,12 @@
<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"><?= t('Created at:') ?> <?= $this->dt->datetime($comment['date_creation']) ?></small>
+ <small class="comment-date"><?= t('Updated at:')?> <?= $this->dt->datetime($comment['date_modification']) ?></small>
+
</div>
<div class="comment-content">
@@ -25,12 +27,10 @@
</li>
<?php if ($editable && ($this->user->isAdmin() || $this->user->isCurrentUser($comment['user_id']))): ?>
<li>
- <i class="fa fa-remove fa-fw"></i>
- <?= $this->url->link(t('remove'), 'CommentController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), false, 'popover') ?>
+ <?= $this->modal->medium('edit', t('edit'), 'CommentController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>
</li>
<li>
- <i class="fa fa-edit fa-fw"></i>
- <?= $this->url->link(t('edit'), 'CommentController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('remove'), 'CommentController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'comment_id' => $comment['id'])) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/comments/create.php b/app/Template/comments/create.php
index 3fa6ddc0..06173575 100644
--- a/app/Template/comments/create.php
+++ b/app/Template/comments/create.php
@@ -3,20 +3,7 @@
<?= $this->form->hidden('task_id', $values) ?>
<?= $this->form->hidden('user_id', $values) ?>
- <div class="markdown-editor-small">
- <?= $this->form->textarea(
- 'comment',
- $values,
- $errors,
- array(
- 'data-markdown-editor-disable-toolbar="true"',
- 'required',
- 'placeholder="'.t('Leave a comment').'"',
- 'data-mention-search-url="'.$this->url->href('UserAjaxController', 'mention', array('project_id' => $task['project_id'])).'"',
- ),
- 'markdown-editor'
- ) ?>
- </div>
+ <?= $this->form->textEditor('comment', $values, $errors, array('required' => true)) ?>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
diff --git a/app/Template/comments/show.php b/app/Template/comments/show.php
index 43f6b2c2..3edf7076 100644
--- a/app/Template/comments/show.php
+++ b/app/Template/comments/show.php
@@ -5,8 +5,9 @@
<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>
+ <?= $this->url->icon('sort', t('change sorting'), 'CommentController', 'toggleSorting', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </small>
</div>
<?php endif ?>
<?php foreach ($comments as $comment): ?>
@@ -24,6 +25,7 @@
'values' => array(
'user_id' => $this->user->getId(),
'task_id' => $task['id'],
+ 'project_id' => $task['project_id'],
),
'errors' => array(),
'task' => $task,
diff --git a/app/Template/config/about.php b/app/Template/config/about.php
index 8e2d1325..3f078c3d 100644
--- a/app/Template/config/about.php
+++ b/app/Template/config/about.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('About') ?></h2>
</div>
-<div class="listing">
+<div class="panel">
<ul>
<li>
<?= t('Official website:') ?>
@@ -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:') ?>
@@ -21,7 +21,7 @@
<div class="page-header">
<h2><?= t('Configuration') ?></h2>
</div>
-<div class="listing">
+<div class="panel">
<ul>
<li>
<?= t('Application version:') ?>
@@ -58,7 +58,7 @@
<div class="page-header">
<h2><?= t('Database') ?></h2>
</div>
- <div class="listing">
+ <div class="panel">
<ul>
<li>
<?= t('Database size:') ?>
@@ -81,6 +81,6 @@
<div class="page-header">
<h2><?= t('License') ?></h2>
</div>
-<div class="listing">
+<div class="panel">
<?= nl2br(file_get_contents(ROOT_DIR.DIRECTORY_SEPARATOR.'LICENSE')) ?>
</div>
diff --git a/app/Template/config/api.php b/app/Template/config/api.php
index 95f77355..9a3b06ce 100644
--- a/app/Template/config/api.php
+++ b/app/Template/config/api.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('API') ?></h2>
</div>
-<section class="listing">
+<div class="panel">
<ul>
<li>
<?= t('API token:') ?>
@@ -9,10 +9,10 @@
</li>
<li>
<?= t('API endpoint:') ?>
- <input type="text" class="auto-select" readonly="readonly" value="<?= $this->url->base().'jsonrpc.php' ?>">
- </li>
- <li>
- <?= $this->url->link(t('Reset token'), 'ConfigController', 'token', array('type' => 'api'), true) ?>
+ <strong><?= $this->url->base().'jsonrpc.php' ?></strong>
</li>
</ul>
-</section>
+</div>
+
+<?= $this->url->link(t('Reset token'), 'ConfigController', 'token', array('type' => 'api'), true, 'btn btn-red') ?>
+
diff --git a/app/Template/config/application.php b/app/Template/config/application.php
index 0f842f6e..d3d8c858 100644
--- a/app/Template/config/application.php
+++ b/app/Template/config/application.php
@@ -2,33 +2,35 @@
<h2><?= t('Application settings') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'application')) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
- <?= $this->form->label(t('Application URL'), 'application_url') ?>
- <?= $this->form->text('application_url', $values, $errors, array('placeholder="http://example.kanboard.net/"')) ?>
- <p class="form-help"><?= t('Example: http://example.kanboard.net/ (used to generate absolute URLs)') ?></p>
-
- <?= $this->form->label(t('Language'), 'application_language') ?>
- <?= $this->form->select('application_language', $languages, $values, $errors) ?>
+ <fieldset>
+ <?= $this->form->label(t('Application URL'), 'application_url') ?>
+ <?= $this->form->text('application_url', $values, $errors, array('placeholder="http://example.kanboard.net/"')) ?>
+ <p class="form-help"><?= t('Example: http://example.kanboard.net/ (used to generate absolute URLs)') ?></p>
- <?= $this->form->label(t('Timezone'), 'application_timezone') ?>
- <?= $this->form->select('application_timezone', $timezones, $values, $errors) ?>
+ <?= $this->form->label(t('Language'), 'application_language') ?>
+ <?= $this->form->select('application_language', $languages, $values, $errors) ?>
- <?= $this->form->label(t('Date format'), 'application_date_format') ?>
- <?= $this->form->select('application_date_format', $date_formats, $values, $errors) ?>
- <p class="form-help"><?= t('ISO format is always accepted, example: "%s" and "%s"', date('Y-m-d'), date('Y_m_d')) ?></p>
+ <?= $this->form->checkbox('password_reset', t('Enable "Forget Password"'), 1, $values['password_reset'] == 1) ?>
+ </fieldset>
- <?= $this->form->label(t('Date and time format'), 'application_datetime_format') ?>
- <?= $this->form->select('application_datetime_format', $datetime_formats, $values, $errors) ?>
+ <fieldset>
+ <?= $this->form->label(t('Timezone'), 'application_timezone') ?>
+ <?= $this->form->select('application_timezone', $timezones, $values, $errors) ?>
- <?= $this->form->label(t('Time format'), 'application_time_format') ?>
- <?= $this->form->select('application_time_format', $time_formats, $values, $errors) ?>
+ <?= $this->form->label(t('Date format'), 'application_date_format') ?>
+ <?= $this->form->select('application_date_format', $date_formats, $values, $errors) ?>
+ <p class="form-help"><?= t('ISO format is always accepted, example: "%s" and "%s"', date('Y-m-d'), date('Y_m_d')) ?></p>
- <?= $this->form->checkbox('password_reset', t('Enable "Forget Password"'), 1, $values['password_reset'] == 1) ?>
+ <?= $this->form->label(t('Time format'), 'application_time_format') ?>
+ <?= $this->form->select('application_time_format', $time_formats, $values, $errors) ?>
+ </fieldset>
- <?= $this->form->label(t('Custom Stylesheet'), 'application_stylesheet') ?>
- <?= $this->form->textarea('application_stylesheet', $values, $errors) ?>
+ <fieldset>
+ <?= $this->form->label(t('Custom Stylesheet'), 'application_stylesheet') ?>
+ <?= $this->form->textarea('application_stylesheet', $values, $errors) ?>
+ </fieldset>
<?= $this->hook->render('template:config:application', array('values' => $values, 'errors' => $errors)) ?>
diff --git a/app/Template/config/board.php b/app/Template/config/board.php
index 62a736e7..35058f0f 100644
--- a/app/Template/config/board.php
+++ b/app/Template/config/board.php
@@ -2,20 +2,21 @@
<h2><?= t('Board settings') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'board')) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
- <?= $this->form->label(t('Task highlight period'), 'board_highlight_period') ?>
- <?= $this->form->number('board_highlight_period', $values, $errors) ?>
- <p class="form-help"><?= t('Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)') ?></p>
+ <fieldset>
+ <?= $this->form->label(t('Task highlight period'), 'board_highlight_period') ?>
+ <?= $this->form->number('board_highlight_period', $values, $errors) ?>
+ <p class="form-help"><?= t('Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)') ?></p>
- <?= $this->form->label(t('Refresh interval for public board'), 'board_public_refresh_interval') ?>
- <?= $this->form->number('board_public_refresh_interval', $values, $errors) ?>
- <p class="form-help"><?= t('Frequency in second (60 seconds by default)') ?></p>
+ <?= $this->form->label(t('Refresh interval for public board'), 'board_public_refresh_interval') ?>
+ <?= $this->form->number('board_public_refresh_interval', $values, $errors) ?>
+ <p class="form-help"><?= t('Frequency in second (60 seconds by default)') ?></p>
- <?= $this->form->label(t('Refresh interval for private board'), 'board_private_refresh_interval') ?>
- <?= $this->form->number('board_private_refresh_interval', $values, $errors) ?>
- <p class="form-help"><?= t('Frequency in second (0 to disable this feature, 10 seconds by default)') ?></p>
+ <?= $this->form->label(t('Refresh interval for private board'), 'board_private_refresh_interval') ?>
+ <?= $this->form->number('board_private_refresh_interval', $values, $errors) ?>
+ <p class="form-help"><?= t('Frequency in second (0 to disable this feature, 10 seconds by default)') ?></p>
+ </fieldset>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
diff --git a/app/Template/config/calendar.php b/app/Template/config/calendar.php
index 90e034e9..0cc3d064 100644
--- a/app/Template/config/calendar.php
+++ b/app/Template/config/calendar.php
@@ -1,34 +1,36 @@
<div class="page-header">
<h2><?= t('Calendar settings') ?></h2>
</div>
-<section>
<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'calendar')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
- <div class="listing">
- <h3><?= t('Project calendar view') ?></h3>
+ <fieldset>
+ <legend><?= t('Project calendar view') ?></legend>
<?= $this->form->radios('calendar_project_tasks', array(
'date_creation' => t('Show tasks based on the creation date'),
'date_started' => t('Show tasks based on the start date'),
- ), $values) ?>
- </div>
+ ),
+ $values
+ ) ?>
+ </fieldset>
- <div class="listing">
- <h3><?= t('User calendar view') ?></h3>
+ <fieldset>
+ <legend><?= t('User calendar view') ?></legend>
<?= $this->form->radios('calendar_user_tasks', array(
'date_creation' => t('Show tasks based on the creation date'),
'date_started' => t('Show tasks based on the start date'),
- ), $values) ?>
- </div>
+ ),
+ $values
+ ) ?>
+ </fieldset>
- <div class="listing">
- <h3><?= t('Subtasks time tracking') ?></h3>
+ <fieldset>
+ <legend><?= t('Subtasks time tracking') ?></legend>
<?= $this->form->checkbox('calendar_user_subtasks_time_tracking', t('Show subtasks based on the time tracking'), 1, $values['calendar_user_subtasks_time_tracking'] == 1) ?>
- </div>
+ </fieldset>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
</div>
</form>
-</section>
diff --git a/app/Template/config/email.php b/app/Template/config/email.php
index 6ff76eca..bc2c2ae5 100644
--- a/app/Template/config/email.php
+++ b/app/Template/config/email.php
@@ -4,11 +4,14 @@
<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'email')) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
- <?= $this->form->label(t('Email sender address'), 'mail_sender_address') ?>
- <?= $this->form->text('mail_sender_address', $values, $errors, array('placeholder="'.MAIL_FROM.'"')) ?>
+ <fieldset>
+ <legend><?= t('Outgoing Emails') ?></legend>
+ <?= $this->form->label(t('Email sender address'), 'mail_sender_address') ?>
+ <?= $this->form->text('mail_sender_address', $values, $errors, array('placeholder="'.MAIL_FROM.'"')) ?>
- <?= $this->form->label(t('Email transport'), 'mail_transport') ?>
- <?= $this->form->select('mail_transport', $mail_transports, $values, $errors) ?>
+ <?= $this->form->label(t('Email transport'), 'mail_transport') ?>
+ <?= $this->form->select('mail_transport', $mail_transports, $values, $errors) ?>
+ </fieldset>
<?= $this->hook->render('template:config:email', array('values' => $values, 'errors' => $errors)) ?>
diff --git a/app/Template/config/integrations.php b/app/Template/config/integrations.php
index 3ba4e865..07a90ce2 100644
--- a/app/Template/config/integrations.php
+++ b/app/Template/config/integrations.php
@@ -7,11 +7,11 @@
<?= $this->hook->render('template:config:integrations', array('values' => $values)) ?>
<h3><img src="<?= $this->url->dir() ?>assets/img/gravatar-icon.png"/>&nbsp;<?= t('Gravatar') ?></h3>
- <div class="listing">
+ <div class="panel">
<?= $this->form->checkbox('integration_gravatar', t('Enable Gravatar images'), 1, $values['integration_gravatar'] == 1) ?>
+ <div class="form-actions">
+ <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
+ </div>
</div>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
</form>
diff --git a/app/Template/config/keyboard_shortcuts.php b/app/Template/config/keyboard_shortcuts.php
index 1b1a9477..6ac71ee0 100644
--- a/app/Template/config/keyboard_shortcuts.php
+++ b/app/Template/config/keyboard_shortcuts.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('Keyboard shortcuts') ?></h2>
</div>
-<div class="listing">
+<div class="panel">
<h3><?= t('Board/Calendar/List view') ?></h3>
<ul>
<li><?= t('Switch to the project overview') ?> = <strong>v o</strong></li>
diff --git a/app/Template/config/project.php b/app/Template/config/project.php
index 6d8d131a..514a9baa 100644
--- a/app/Template/config/project.php
+++ b/app/Template/config/project.php
@@ -2,24 +2,27 @@
<h2><?= t('Project settings') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'project')) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
- <?= $this->form->label(t('Default task color'), 'default_color') ?>
- <?= $this->form->select('default_color', $colors, $values, $errors) ?>
+ <fieldset>
+ <?= $this->form->label(t('Default task color'), 'default_color') ?>
+ <?= $this->form->select('default_color', $colors, $values, $errors) ?>
- <?= $this->form->label(t('Default columns for new projects (Comma-separated)'), 'board_columns') ?>
- <?= $this->form->text('board_columns', $values, $errors) ?>
- <p class="form-help"><?= t('Default values are "%s"', $default_columns) ?></p>
+ <?= $this->form->label(t('Default columns for new projects (Comma-separated)'), 'board_columns') ?>
+ <?= $this->form->text('board_columns', $values, $errors) ?>
+ <p class="form-help"><?= t('Default values are "%s"', $default_columns) ?></p>
- <?= $this->form->label(t('Default categories for new projects (Comma-separated)'), 'project_categories') ?>
- <?= $this->form->text('project_categories', $values, $errors) ?>
- <p class="form-help"><?= t('Example: "Bug, Feature Request, Improvement"') ?></p>
+ <?= $this->form->label(t('Default categories for new projects (Comma-separated)'), 'project_categories') ?>
+ <?= $this->form->text('project_categories', $values, $errors) ?>
+ <p class="form-help"><?= t('Example: "Bug, Feature Request, Improvement"') ?></p>
+ </fieldset>
- <?= $this->form->checkbox('disable_private_project', t('Disable private projects'), 1, isset($values['disable_private_project']) && $values['disable_private_project'] == 1) ?>
- <?= $this->form->checkbox('subtask_restriction', t('Allow only one subtask in progress at the same time for a user'), 1, $values['subtask_restriction'] == 1) ?>
- <?= $this->form->checkbox('subtask_time_tracking', t('Trigger automatically subtask time tracking'), 1, $values['subtask_time_tracking'] == 1) ?>
- <?= $this->form->checkbox('cfd_include_closed_tasks', t('Include closed tasks in the cumulative flow diagram'), 1, $values['cfd_include_closed_tasks'] == 1) ?>
+ <fieldset>
+ <?= $this->form->checkbox('disable_private_project', t('Disable private projects'), 1, isset($values['disable_private_project']) && $values['disable_private_project'] == 1) ?>
+ <?= $this->form->checkbox('subtask_restriction', t('Allow only one subtask in progress at the same time for a user'), 1, $values['subtask_restriction'] == 1) ?>
+ <?= $this->form->checkbox('subtask_time_tracking', t('Trigger automatically subtask time tracking'), 1, $values['subtask_time_tracking'] == 1) ?>
+ <?= $this->form->checkbox('cfd_include_closed_tasks', t('Include closed tasks in the cumulative flow diagram'), 1, $values['cfd_include_closed_tasks'] == 1) ?>
+ </fieldset>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php
index e304f0d0..95be963b 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') ?>
@@ -23,10 +22,10 @@
<?= $this->url->link(t('Tags management'), 'TagController', 'index') ?>
</li>
<li <?= $this->app->checkMenuSelection('LinkController') ?>>
- <?= $this->url->link(t('Link settings'), 'LinkController', 'index') ?>
+ <?= $this->url->link(t('Link labels'), 'LinkController', 'show') ?>
</li>
- <li <?= $this->app->checkMenuSelection('CurrencyController', 'index') ?>>
- <?= $this->url->link(t('Currency rates'), 'CurrencyController', 'index') ?>
+ <li <?= $this->app->checkMenuSelection('CurrencyController') ?>>
+ <?= $this->url->link(t('Currency rates'), 'CurrencyController', 'show') ?>
</li>
<li <?= $this->app->checkMenuSelection('ConfigController', 'integrations') ?>>
<?= $this->url->link(t('Integrations'), 'ConfigController', 'integrations') ?>
diff --git a/app/Template/config/webhook.php b/app/Template/config/webhook.php
index e3245873..bc4bbfdf 100644
--- a/app/Template/config/webhook.php
+++ b/app/Template/config/webhook.php
@@ -1,9 +1,7 @@
<div class="page-header">
<h2><?= t('Webhook settings') ?></h2>
</div>
-<section>
<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'webhook')) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
<?= $this->form->label(t('Webhook URL'), 'webhook_url') ?>
@@ -13,19 +11,13 @@
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
</div>
</form>
-</section>
-<div class="page-header">
+<div class="page-header margin-top">
<h2><?= t('Webhook token') ?></h2>
</div>
-<section class="listing">
- <ul>
- <li>
- <?= t('Webhook token:') ?>
- <strong><?= $this->text->e($values['webhook_token']) ?></strong>
- </li>
- <li>
- <?= $this->url->link(t('Reset token'), 'ConfigController', 'token', array('type' => 'webhook'), true) ?>
- </li>
- </ul>
-</section>
+<div class="panel">
+ <?= t('Webhook token:') ?>
+ <strong><?= $this->text->e($values['webhook_token']) ?></strong>
+</div>
+
+<?= $this->url->link(t('Reset token'), 'ConfigController', 'token', array('type' => 'webhook'), true, 'btn btn-red') ?>
diff --git a/app/Template/currency/change.php b/app/Template/currency/change.php
new file mode 100644
index 00000000..59a7ce37
--- /dev/null
+++ b/app/Template/currency/change.php
@@ -0,0 +1,9 @@
+<div class="page-header">
+ <h2><?= t('Change reference currency') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('CurrencyController', 'update') ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->label(t('Reference currency'), 'application_currency') ?>
+ <?= $this->form->select('application_currency', $currencies, $values, $errors) ?>
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/currency/create.php b/app/Template/currency/create.php
new file mode 100644
index 00000000..578ece81
--- /dev/null
+++ b/app/Template/currency/create.php
@@ -0,0 +1,11 @@
+<div class="page-header">
+ <h2><?= t('Add or change currency rate') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('CurrencyController', 'save') ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->label(t('Currency'), 'currency') ?>
+ <?= $this->form->select('currency', $currencies, $values, $errors) ?>
+ <?= $this->form->label(t('Rate'), 'rate') ?>
+ <?= $this->form->text('rate', $values, $errors, array('autofocus'), 'form-numeric') ?>
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/currency/index.php b/app/Template/currency/index.php
deleted file mode 100644
index 9881cee5..00000000
--- a/app/Template/currency/index.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<div class="page-header">
- <h2><?= t('Currency rates') ?></h2>
-</div>
-
-<?php if (! empty($rates)): ?>
-
-<table class="table-stripped">
- <tr>
- <th class="column-35"><?= t('Currency') ?></th>
- <th><?= t('Rate') ?></th>
- </tr>
- <?php foreach ($rates as $rate): ?>
- <tr>
- <td>
- <strong><?= $this->text->e($rate['currency']) ?></strong>
- </td>
- <td>
- <?= n($rate['rate']) ?>
- </td>
- </tr>
- <?php endforeach ?>
-</table>
-
-<hr/>
-<h3><?= t('Change reference currency') ?></h3>
-<?php endif ?>
-<form method="post" action="<?= $this->url->href('CurrencyController', 'reference') ?>" autocomplete="off">
-
- <?= $this->form->csrf() ?>
-
- <?= $this->form->label(t('Reference currency'), 'application_currency') ?>
- <?= $this->form->select('application_currency', $currencies, $config_values, $errors) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
-</form>
-
-<hr/>
-<h3><?= t('Add a new currency rate') ?></h3>
-<form method="post" action="<?= $this->url->href('CurrencyController', 'create') ?>" autocomplete="off">
-
- <?= $this->form->csrf() ?>
-
- <?= $this->form->label(t('Currency'), 'currency') ?>
- <?= $this->form->select('currency', $currencies, $values, $errors) ?>
-
- <?= $this->form->label(t('Rate'), 'rate') ?>
- <?= $this->form->text('rate', $values, $errors, array(), 'form-numeric') ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
-</form>
diff --git a/app/Template/currency/show.php b/app/Template/currency/show.php
new file mode 100644
index 00000000..4b7f34bc
--- /dev/null
+++ b/app/Template/currency/show.php
@@ -0,0 +1,34 @@
+<div class="page-header">
+ <h2><?= t('Currency rates') ?></h2>
+ <ul>
+ <li>
+ <?= $this->modal->medium('plus', t('Add or change currency rate'), 'CurrencyController', 'create') ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('edit', t('Change reference currency'), 'CurrencyController', 'change') ?>
+ </li>
+ </ul>
+</div>
+
+<div class="panel">
+ <strong><?= t('Reference currency: %s', $application_currency) ?></strong>
+</div>
+
+<?php if (! empty($rates)): ?>
+ <table class="table-striped">
+ <tr>
+ <th class="column-35"><?= t('Currency') ?></th>
+ <th><?= t('Rate') ?></th>
+ </tr>
+ <?php foreach ($rates as $rate): ?>
+ <tr>
+ <td>
+ <strong><?= $this->text->e($rate['currency']) ?></strong>
+ </td>
+ <td>
+ <?= n($rate['rate']) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?>
diff --git a/app/Template/custom_filter/add.php b/app/Template/custom_filter/create.php
index 3801cc30..24e896ee 100644
--- a/app/Template/custom_filter/add.php
+++ b/app/Template/custom_filter/create.php
@@ -2,23 +2,20 @@
<h2><?= t('Add a new filter') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('CustomFilterController', '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('required', 'maxlength="100"')) ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?>
<?= $this->form->label(t('Filter'), 'filter') ?>
<?= $this->form->text('filter', $values, $errors, array('required', 'maxlength="100"')) ?>
- <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?>
<?= $this->form->checkbox('is_shared', t('Share with all project members'), 1) ?>
<?php endif ?>
<?= $this->form->checkbox('append', t('Append filter (instead of replacement)'), 1) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/custom_filter/edit.php b/app/Template/custom_filter/edit.php
index 26da8da2..b64dee53 100644
--- a/app/Template/custom_filter/edit.php
+++ b/app/Template/custom_filter/edit.php
@@ -2,8 +2,7 @@
<h2><?= t('Edit custom filter') ?></h2>
</div>
-<form class="form-popover" method="post" action="<?= $this->url->href('CustomFilterController', 'update', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?>" autocomplete="off">
-
+<form method="post" action="<?= $this->url->href('CustomFilterController', 'update', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
@@ -16,7 +15,7 @@
<?= $this->form->label(t('Filter'), 'filter') ?>
<?= $this->form->text('filter', $values, $errors, array('required', 'maxlength="100"')) ?>
- <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?>
<?= $this->form->checkbox('is_shared', t('Share with all project members'), 1, $values['is_shared'] == 1) ?>
<?php else: ?>
<?= $this->form->hidden('is_shared', $values) ?>
@@ -24,9 +23,5 @@
<?= $this->form->checkbox('append', t('Append filter (instead of replacement)'), 1, $values['append'] == 1) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'CustomFilterController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/custom_filter/index.php b/app/Template/custom_filter/index.php
index 08c8040c..9475c278 100644
--- a/app/Template/custom_filter/index.php
+++ b/app/Template/custom_filter/index.php
@@ -1,9 +1,13 @@
-<?php if (! empty($custom_filters)): ?>
<div class="page-header">
<h2><?= t('Custom filters') ?></h2>
+ <ul>
+ <li>
+ <?= $this->modal->medium('filter', t('Add custom filters'), 'CustomFilterController', 'create', array('project_id' => $project['id'])) ?>
+ </li>
+ </ul>
</div>
-<div>
- <table>
+<?php if (! empty($custom_filters)): ?>
+ <table class="table-striped table-scrolling">
<tr>
<th class="column-15"><?= t('Name') ?></th>
<th class="column-30"><?= t('Filter') ?></th>
@@ -36,8 +40,8 @@
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
- <li><?= $this->url->link(t('Remove'), 'CustomFilterController', 'confirm', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id']), false, 'popover') ?></li>
- <li><?= $this->url->link(t('Edit'), 'CustomFilterController', 'edit', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id']), false, 'popover') ?></li>
+ <li><?= $this->modal->medium('edit', t('Edit'), 'CustomFilterController', 'edit', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?></li>
+ <li><?= $this->modal->confirm('trash-o', t('Remove'), 'CustomFilterController', 'confirm', array('project_id' => $filter['project_id'], 'filter_id' => $filter['id'])) ?></li>
</ul>
</div>
<?php endif ?>
@@ -45,7 +49,7 @@
</tr>
<?php endforeach ?>
</table>
-</div>
+<?php else: ?>
+ <p class="alert"><?= t('There is no custom filter.') ?></p>
<?php endif ?>
-<?= $this->render('custom_filter/add', array('project' => $project, 'values' => $values, 'errors' => $errors)) ?>
diff --git a/app/Template/custom_filter/remove.php b/app/Template/custom_filter/remove.php
index 609f19b2..1c576fa2 100644
--- a/app/Template/custom_filter/remove.php
+++ b/app/Template/custom_filter/remove.php
@@ -1,17 +1,15 @@
-<section id="main">
- <div class="page-header">
- <h2><?= t('Remove a custom filter') ?></h2>
- </div>
+<div class="page-header">
+ <h2><?= t('Remove a custom filter') ?></h2>
+</div>
- <div class="confirm">
- <p class="alert alert-info">
- <?= t('Do you really want to remove this custom filter: "%s"?', $filter['name']) ?>
- </p>
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this custom filter: "%s"?', $filter['name']) ?>
+ </p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'CustomFilterController', 'remove', array('project_id' => $project['id'], 'filter_id' => $filter['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'CustomFilterController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
- </div>
-</section>
+ <?= $this->modal->confirmButtons(
+ 'CustomFilterController',
+ 'remove',
+ array('project_id' => $project['id'], 'filter_id' => $filter['id'])
+ ) ?>
+</div>
diff --git a/app/Template/dashboard/calendar.php b/app/Template/dashboard/calendar.php
index 75c96d83..0b768b31 100644
--- a/app/Template/dashboard/calendar.php
+++ b/app/Template/dashboard/calendar.php
@@ -1,5 +1,4 @@
-<div id="calendar"
- data-check-url="<?= $this->url->href('CalendarController', 'user', array('user_id' => $user['id'])) ?>"
- data-save-url="<?= $this->url->href('CalendarController', 'save') ?>"
->
-</div>
+<?= $this->calendar->render(
+ $this->url->href('CalendarController', 'user', array('user_id' => $user['id'])),
+ $this->url->href('CalendarController', 'save')
+) ?>
diff --git a/app/Template/dashboard/layout.php b/app/Template/dashboard/layout.php
index 795537a6..15ab8a1a 100644
--- a/app/Template/dashboard/layout.php
+++ b/app/Template/dashboard/layout.php
@@ -3,23 +3,19 @@
<ul>
<?php if ($this->user->hasAccess('ProjectCreationController', 'create')): ?>
<li>
- <i class="fa fa-plus fa-fw"></i>
- <?= $this->url->link(t('New project'), 'ProjectCreationController', 'create', array(), false, 'popover') ?>
+ <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?>
</li>
<?php endif ?>
<?php if ($this->app->config('disable_private_project', 0) == 0): ?>
<li>
- <i class="fa fa-lock fa-fw"></i>
- <?= $this->url->link(t('New private project'), 'ProjectCreationController', 'createPrivate', array(), false, 'popover') ?>
+ <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?>
</li>
<?php endif ?>
<li>
- <i class="fa fa-search fa-fw"></i>
- <?= $this->url->link(t('Search'), 'SearchController', 'index') ?>
+ <?= $this->url->icon('search', t('Search'), 'SearchController', 'index') ?>
</li>
<li>
- <i class="fa fa-folder fa-fw"></i>
- <?= $this->url->link(t('Project management'), 'ProjectListController', 'show') ?>
+ <?= $this->url->icon('folder', t('Project management'), 'ProjectListController', 'show') ?>
</li>
</ul>
</div>
diff --git a/app/Template/dashboard/notifications.php b/app/Template/dashboard/notifications.php
index 3b70b49f..81adb348 100644
--- a/app/Template/dashboard/notifications.php
+++ b/app/Template/dashboard/notifications.php
@@ -7,21 +7,34 @@
<?php else: ?>
<ul>
<li>
- <i class="fa fa-check-square-o fa-fw"></i>
- <?= $this->url->link(t('Mark all as read'), 'WebNotificationController', 'flush', array('user_id' => $user['id'])) ?>
+ <?= $this->url->icon('check-square-o', t('Mark all as read'), 'WebNotificationController', 'flush', array('user_id' => $user['id'])) ?>
</li>
</ul>
</div>
- <table class="table-fixed table-small">
+ <table class="table-striped table-scrolling table-small">
<tr>
+ <th class="column-20"><?= t('Project') ?></th>
<th><?= t('Notification') ?></th>
- <th class="column-20"><?= t('Date') ?></th>
+ <th class="column-15"><?= t('Date') ?></th>
<th class="column-15"><?= t('Action') ?></th>
</tr>
<?php foreach ($notifications as $notification): ?>
<tr>
<td>
+ <?php if (isset($notification['event_data']['task']['project_name'])): ?>
+ <?= $this->url->link(
+ $this->text->e($notification['event_data']['task']['project_name']),
+ 'BoardViewController',
+ 'show',
+ array('project_id' => $notification['event_data']['task']['project_id'])
+ )
+ ?>
+ <?php elseif (isset($notification['event_data']['project_name'])): ?>
+ <?= $this->text->e($notification['event_data']['project_name']) ?>
+ <?php endif ?>
+ </td>
+ <td>
<?php if ($this->text->contains($notification['event_name'], 'subtask')): ?>
<i class="fa fa-tasks fa-fw"></i>
<?php elseif ($this->text->contains($notification['event_name'], 'task.move')): ?>
@@ -46,8 +59,7 @@
<?= $this->dt->datetime($notification['date_creation']) ?>
</td>
<td>
- <i class="fa fa-check fa-fw"></i>
- <?= $this->url->link(t('Mark as read'), 'WebNotificationController', 'remove', array('user_id' => $user['id'], 'notification_id' => $notification['id'])) ?>
+ <?= $this->url->icon('check', t('Mark as read'), 'WebNotificationController', 'remove', array('user_id' => $user['id'], 'notification_id' => $notification['id'])) ?>
</td>
</tr>
<?php endforeach ?>
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..b1d877cf 100644
--- a/app/Template/dashboard/show.php
+++ b/app/Template/dashboard/show.php
@@ -1,12 +1,19 @@
-<div class="filter-box">
+<div class="filter-box margin-bottom">
<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..13770f0f 100644
--- a/app/Template/dashboard/subtasks.php
+++ b/app/Template/dashboard/subtasks.php
@@ -4,12 +4,13 @@
<?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>
+ <?= $this->hook->render('template:dashboard:subtasks:header:before-timetracking', array('paginator' => $paginator)) ?>
<th class="column-20"><?= t('Time tracking') ?></th>
</tr>
<?php foreach ($paginator->getCollection() as $subtask): ?>
@@ -26,6 +27,7 @@
<td>
<?= $this->subtask->toggleStatus($subtask, $subtask['project_id']) ?>
</td>
+ <?= $this->hook->render('template:dashboard:subtasks:rows', array('subtask' => $subtask)) ?>
<td>
<?php if (! empty($subtask['time_spent'])): ?>
<strong><?= $this->text->e($subtask['time_spent']).'h' ?></strong> <?= t('spent') ?>
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/doc/show.php b/app/Template/doc/show.php
index a8dbd762..879e45b6 100644
--- a/app/Template/doc/show.php
+++ b/app/Template/doc/show.php
@@ -2,8 +2,7 @@
<div class="page-header">
<ul>
<li>
- <i class="fa fa-life-ring fa-fw"></i>
- <?= $this->url->link(t('Table of contents'), 'DocumentationController', 'show', array('file' => 'index')) ?>
+ <?= $this->url->icon('life-ring', t('Table of contents'), 'DocumentationController', 'show', array('file' => 'index')) ?>
</li>
</ul>
</div>
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/header.php b/app/Template/export/header.php
new file mode 100644
index 00000000..35591352
--- /dev/null
+++ b/app/Template/export/header.php
@@ -0,0 +1,17 @@
+<div class="page-header">
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= $title ?></h2>
+ <ul>
+ <li <?= $this->app->checkMenuSelection('ExportController', 'tasks') ?>>
+ <?= $this->modal->replaceLink(t('Tasks'), 'ExportController', 'tasks', array('project_id' => $project['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('ExportController', 'subtasks') ?>>
+ <?= $this->modal->replaceLink(t('Subtasks'), 'ExportController', 'subtasks', array('project_id' => $project['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('ExportController', 'transitions') ?>>
+ <?= $this->modal->replaceLink(t('Task transitions'), 'ExportController', 'transitions', array('project_id' => $project['id'])) ?>
+ </li>
+ <li <?= $this->app->checkMenuSelection('ExportController', 'summary') ?>>
+ <?= $this->modal->replaceLink(t('Daily project summary'), 'ExportController', 'summary', array('project_id' => $project['id'])) ?>
+ </li>
+ </ul>
+</div>
diff --git a/app/Template/export/sidebar.php b/app/Template/export/sidebar.php
deleted file mode 100644
index 55fbaeef..00000000
--- a/app/Template/export/sidebar.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<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'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('ExportController', 'subtasks') ?>>
- <?= $this->url->link(t('Subtasks'), 'ExportController', 'subtasks', array('project_id' => $project['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('ExportController', 'transitions') ?>>
- <?= $this->url->link(t('Task transitions'), 'ExportController', 'transitions', array('project_id' => $project['id'])) ?>
- </li>
- <li <?= $this->app->checkMenuSelection('ExportController', 'summary') ?>>
- <?= $this->url->link(t('Daily project summary'), 'ExportController', 'summary', array('project_id' => $project['id'])) ?>
- </li>
- <?= $this->hook->render('template:export:sidebar') ?>
- </ul>
-</div>
diff --git a/app/Template/export/subtasks.php b/app/Template/export/subtasks.php
index a82cb3d1..0e47772b 100644
--- a/app/Template/export/subtasks.php
+++ b/app/Template/export/subtasks.php
@@ -1,24 +1,16 @@
-<div class="page-header">
- <h2><?= t('Subtasks exportation for "%s"', $project['name']) ?></h2>
-</div>
+<?= $this->render('export/header', array('project' => $project, 'title' => $title)) ?>
<p class="alert alert-info"><?= t('This report contains all subtasks information for the given date range.') ?></p>
-<form method="get" action="?" autocomplete="off">
-
- <?= $this->form->hidden('controller', $values) ?>
- <?= $this->form->hidden('action', $values) ?>
+<form class="js-modal-ignore-form" method="post" action="<?= $this->url->href('ExportController', 'subtasks', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
-
- <?= $this->form->label(t('Start Date'), 'from') ?>
- <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <?= $this->form->label(t('End Date'), 'to') ?>
- <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div>
+ <?= $this->form->date(t('Start date'), 'from', $values) ?>
+ <?= $this->form->date(t('End date'), 'to', $values) ?>
<div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
+ <button type="submit" class="btn btn-blue js-form-export"><?= t('Export') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'ExportController', 'subtasks', array('project_id' => $project['id']), false, 'js-modal-close') ?>
</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..7dc7482f 100644
--- a/app/Template/export/summary.php
+++ b/app/Template/export/summary.php
@@ -1,24 +1,16 @@
-<div class="page-header">
- <h2><?= t('Daily project summary export for "%s"', $project['name']) ?></h2>
-</div>
+<?= $this->render('export/header', array('project' => $project, 'title' => $title)) ?>
<p class="alert alert-info"><?= t('This export contains the number of tasks per column grouped per day.') ?></p>
-<form method="get" action="?" autocomplete="off">
-
- <?= $this->form->hidden('controller', $values) ?>
- <?= $this->form->hidden('action', $values) ?>
+<form class="js-modal-ignore-form" method="post" action="<?= $this->url->href('ExportController', 'summary', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
-
- <?= $this->form->label(t('Start Date'), 'from') ?>
- <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <?= $this->form->label(t('End Date'), 'to') ?>
- <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div>
+ <?= $this->form->date(t('Start date'), 'from', $values) ?>
+ <?= $this->form->date(t('End date'), 'to', $values) ?>
<div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
+ <button type="submit" class="btn btn-blue js-form-export"><?= t('Export') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'ExportController', 'summary', array('project_id' => $project['id']), false, 'js-modal-close') ?>
</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..232ff8eb 100644
--- a/app/Template/export/tasks.php
+++ b/app/Template/export/tasks.php
@@ -1,24 +1,16 @@
-<div class="page-header">
- <h2><?= t('Tasks exportation for "%s"', $project['name']) ?></h2>
-</div>
+<?= $this->render('export/header', array('project' => $project, 'title' => $title)) ?>
<p class="alert alert-info"><?= t('This report contains all tasks information for the given date range.') ?></p>
-<form method="get" action="?" autocomplete="off">
-
- <?= $this->form->hidden('controller', $values) ?>
- <?= $this->form->hidden('action', $values) ?>
+<form class="js-modal-ignore-form" method="post" action="<?= $this->url->href('ExportController', 'tasks', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
-
- <?= $this->form->label(t('Start Date'), 'from') ?>
- <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <?= $this->form->label(t('End Date'), 'to') ?>
- <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div>
+ <?= $this->form->date(t('Start date'), 'from', $values) ?>
+ <?= $this->form->date(t('End date'), 'to', $values) ?>
<div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
+ <button type="submit" class="btn btn-blue js-form-export"><?= t('Export') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'ExportController', 'tasks', array('project_id' => $project['id']), false, 'js-modal-close') ?>
</div>
-</form> \ No newline at end of file
+</form>
diff --git a/app/Template/export/transitions.php b/app/Template/export/transitions.php
index 093930a3..4f3749b8 100644
--- a/app/Template/export/transitions.php
+++ b/app/Template/export/transitions.php
@@ -1,24 +1,16 @@
-<div class="page-header">
- <h2><?= t('Task transitions export') ?></h2>
-</div>
+<?= $this->render('export/header', array('project' => $project, 'title' => $title)) ?>
<p class="alert alert-info"><?= t('This report contains all column moves for each task with the date, the user and the time spent for each transition.') ?></p>
-<form method="get" action="?" autocomplete="off">
-
- <?= $this->form->hidden('controller', $values) ?>
- <?= $this->form->hidden('action', $values) ?>
+<form class="js-modal-ignore-form" method="post" action="<?= $this->url->href('ExportController', 'transitions', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
<?= $this->form->hidden('project_id', $values) ?>
-
- <?= $this->form->label(t('Start Date'), 'from') ?>
- <?= $this->form->text('from', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <?= $this->form->label(t('End Date'), 'to') ?>
- <?= $this->form->text('to', $values, $errors, array('required', 'placeholder="'.$this->text->in($date_format, $date_formats).'"'), 'form-date') ?>
-
- <div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div>
+ <?= $this->form->date(t('Start date'), 'from', $values) ?>
+ <?= $this->form->date(t('End date'), 'to', $values) ?>
<div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Execute') ?></button>
+ <button type="submit" class="btn btn-blue js-form-export"><?= t('Export') ?></button>
+ <?= t('or') ?>
+ <?= $this->url->link(t('cancel'), 'ExportController', 'transitions', array('project_id' => $project['id']), false, 'js-modal-close') ?>
</div>
-</form> \ No newline at end of file
+</form>
diff --git a/app/Template/external_task_creation/step1.php b/app/Template/external_task_creation/step1.php
new file mode 100644
index 00000000..2a3b0144
--- /dev/null
+++ b/app/Template/external_task_creation/step1.php
@@ -0,0 +1,16 @@
+<form method="post" action="<?= $this->url->href('ExternalTaskCreationController', 'step2', array('project_id' => $project['id'], 'provider_name' => $provider_name)) ?>">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('swimlane_id', $values) ?>
+ <?= $this->form->hidden('column_id', $values) ?>
+
+ <?= $this->render($template, array(
+ 'project' => $project,
+ 'values' => $values,
+ )) ?>
+
+ <?php if (! empty($error_message)): ?>
+ <div class="alert alert-error"><?= $this->text->e($error_message) ?></div>
+ <?php endif ?>
+
+ <?= $this->modal->submitButtons(array('submitLabel' => t('Next'))) ?>
+</form>
diff --git a/app/Template/external_task_creation/step2.php b/app/Template/external_task_creation/step2.php
new file mode 100644
index 00000000..baace3ae
--- /dev/null
+++ b/app/Template/external_task_creation/step2.php
@@ -0,0 +1,22 @@
+<form method="post" action="<?= $this->url->href('ExternalTaskCreationController', 'step3', array('project_id' => $project['id'], 'provider_name' => $provider_name)) ?>">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('external_provider', $values) ?>
+ <?= $this->form->hidden('external_uri', $values) ?>
+
+ <?= $this->render($template, array(
+ 'project' => $project,
+ 'external_task' => $external_task,
+ 'values' => $values,
+ 'errors' => $errors,
+ 'users_list' => $users_list,
+ 'categories_list' => $categories_list,
+ 'swimlanes_list' => $swimlanes_list,
+ 'columns_list' => $columns_list,
+ )) ?>
+
+ <?php if (! empty($error_message)): ?>
+ <div class="alert alert-error"><?= $this->text->e($error_message) ?></div>
+ <?php endif ?>
+
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/external_task_modification/show.php b/app/Template/external_task_modification/show.php
new file mode 100644
index 00000000..55180b96
--- /dev/null
+++ b/app/Template/external_task_modification/show.php
@@ -0,0 +1,22 @@
+<form method="post" action="<?= $this->url->href('TaskModificationController', 'update', array('task_id' => $task['id'], 'project_id' => $project['id'])) ?>">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?php if (! empty($error_message)): ?>
+ <p class="alert alert-error"><?= $this->text->e($error_message) ?></p>
+ <?php else: ?>
+ <?= $this->render($template, array(
+ 'project' => $project,
+ 'task' => $task,
+ 'external_task' => $external_task,
+ 'tags' => $tags,
+ 'users_list' => $users_list,
+ 'categories_list' => $categories_list,
+ 'values' => $values,
+ 'errors' => $errors,
+ )) ?>
+ <?php endif ?>
+
+ <?= $this->modal->submitButtons() ?>
+</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/associate.php b/app/Template/group/associate.php
index 87787568..cd7f2b77 100644
--- a/app/Template/group/associate.php
+++ b/app/Template/group/associate.php
@@ -3,18 +3,21 @@
</div>
<?php if (empty($users)): ?>
<p class="alert"><?= t('There is no user available.') ?></p>
+ <div class="form-actions">
+ <?= $this->url->link(t('Close this window'), 'GroupListController', 'index', array(), false, 'btn js-modal-close') ?>
+ </div>
<?php else: ?>
- <form class="popover-form" method="post" action="<?= $this->url->href('GroupListController', 'addUser', array('group_id' => $group['id'])) ?>" autocomplete="off">
+ <form method="post" action="<?= $this->url->href('GroupListController', 'addUser', array('group_id' => $group['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('group_id', $values) ?>
<?= $this->form->label(t('User'), 'user_id') ?>
- <?= $this->form->select('user_id', $users, $values, $errors, array('required'), 'chosen-select') ?>
+ <?= $this->app->component('select-dropdown-autocomplete', array(
+ 'name' => 'user_id',
+ 'items' => $users,
+ 'defaultValue' => isset($values['user_id']) ? $values['user_id'] : key($users),
+ )) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
<?php endif ?>
diff --git a/app/Template/group/dissociate.php b/app/Template/group/dissociate.php
index 50ef6d61..24836397 100644
--- a/app/Template/group/dissociate.php
+++ b/app/Template/group/dissociate.php
@@ -4,9 +4,9 @@
<div class="confirm">
<p class="alert alert-info"><?= t('Do you really want to remove the user "%s" from the group "%s"?', $user['name'] ?: $user['username'], $group['name']) ?></p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'GroupListController', 'removeUser', array('group_id' => $group['id'], 'user_id' => $user['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'GroupListController', 'users', array('group_id' => $group['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'GroupListController',
+ 'removeUser',
+ array('group_id' => $group['id'], 'user_id' => $user['id'])
+ ) ?>
</div>
diff --git a/app/Template/group/index.php b/app/Template/group/index.php
index 1062e18c..fe3f398e 100644
--- a/app/Template/group/index.php
+++ b/app/Template/group/index.php
@@ -1,14 +1,14 @@
<section id="main">
<div class="page-header">
<ul>
- <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'UserListController', 'show') ?></li>
- <li><i class="fa fa-user-plus fa-fw"></i><?= $this->url->link(t('New group'), 'GroupCreationController', 'show', array(), false, 'popover') ?></li>
+ <li><?= $this->url->icon('user', t('All users'), 'UserListController', 'show') ?></li>
+ <li><?= $this->modal->medium('user-plus', t('New group'), 'GroupCreationController', 'show') ?></li>
</ul>
</div>
<?php if ($paginator->isEmpty()): ?>
<p class="alert"><?= t('There is no group.') ?></p>
<?php else: ?>
- <table class="table-small table-fixed">
+ <table class="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>
@@ -30,10 +30,10 @@
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
- <li><?= $this->url->link(t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id']), false, 'popover') ?></li>
- <li><?= $this->url->link(t('Members'), 'GroupListController', 'users', array('group_id' => $group['id'])) ?></li>
- <li><?= $this->url->link(t('Edit'), 'GroupModificationController', 'show', array('group_id' => $group['id']), false, 'popover') ?></li>
- <li><?= $this->url->link(t('Remove'), 'GroupListController', 'confirm', array('group_id' => $group['id']), false, 'popover') ?></li>
+ <li><?= $this->modal->medium('plus', t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id'])) ?></li>
+ <li><?= $this->url->icon('users', t('Members'), 'GroupListController', 'users', array('group_id' => $group['id'])) ?></li>
+ <li><?= $this->modal->medium('edit', t('Edit'), 'GroupModificationController', 'show', array('group_id' => $group['id'])) ?></li>
+ <li><?= $this->modal->confirm('trash-o', t('Remove'), 'GroupListController', 'confirm', array('group_id' => $group['id'])) ?></li>
</ul>
</div>
</td>
diff --git a/app/Template/group/remove.php b/app/Template/group/remove.php
index 408b3d83..77d602f9 100644
--- a/app/Template/group/remove.php
+++ b/app/Template/group/remove.php
@@ -4,9 +4,9 @@
<div class="confirm">
<p class="alert alert-info"><?= t('Do you really want to remove this group: "%s"?', $group['name']) ?></p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'GroupListController', 'remove', array('group_id' => $group['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'GroupListController',
+ 'remove',
+ array('group_id' => $group['id'])
+ ) ?>
</div>
diff --git a/app/Template/group/users.php b/app/Template/group/users.php
index a4895ab7..ef179674 100644
--- a/app/Template/group/users.php
+++ b/app/Template/group/users.php
@@ -1,14 +1,14 @@
<section id="main">
<div class="page-header">
<ul>
- <li><i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?></li>
- <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id']), false, 'popover') ?></li>
+ <li><?= $this->url->icon('users', t('View all groups'), 'GroupListController', 'index') ?></li>
+ <li><?= $this->modal->medium('plus', t('Add group member'), 'GroupListController', 'associate', array('group_id' => $group['id'])) ?></li>
</ul>
</div>
<?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>
@@ -31,8 +31,7 @@
<a href="mailto:<?= $this->text->e($user['email']) ?>"><?= $this->text->e($user['email']) ?></a>
</td>
<td>
- <i class="fa fa-times fa-fw" aria-hidden="true"></i>
- <?= $this->url->link(t('Remove this user'), 'GroupListController', 'dissociate', array('group_id' => $group['id'], 'user_id' => $user['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove this user'), 'GroupListController', 'dissociate', array('group_id' => $group['id'], 'user_id' => $user['id'])) ?>
</td>
</tr>
<?php endforeach ?>
diff --git a/app/Template/group_creation/show.php b/app/Template/group_creation/show.php
index b219bd70..9f4f5608 100644
--- a/app/Template/group_creation/show.php
+++ b/app/Template/group_creation/show.php
@@ -1,15 +1,11 @@
<div class="page-header">
<h2><?= t('New group') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('GroupCreationController', 'save') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('GroupCreationController', 'save') ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->label(t('Name'), 'name') ?>
<?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/group_modification/show.php b/app/Template/group_modification/show.php
index ddf07369..df4ed01e 100644
--- a/app/Template/group_modification/show.php
+++ b/app/Template/group_modification/show.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('Edit group') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('GroupModificationController', 'save') ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('GroupModificationController', 'save') ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
@@ -10,9 +10,5 @@
<?= $this->form->label(t('Name'), 'name') ?>
<?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="100"')) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'GroupListController', 'index', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/header.php b/app/Template/header.php
index 13521ae7..2af58d93 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>
+ <div class="board-selector-container">
+ <?php if (! empty($board_selector)): ?>
+ <?= $this->render('header/board_selector', array('board_selector' => $board_selector)) ?>
+ <?php endif ?>
+ </div>
+ <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..6f87b0d1
--- /dev/null
+++ b/app/Template/header/board_selector.php
@@ -0,0 +1,13 @@
+<?= $this->app->component('select-dropdown-autocomplete', array(
+ 'name' => 'boardId',
+ 'placeholder' => t('Display another project'),
+ 'items' => $board_selector,
+ 'redirect' => array(
+ 'regex' => 'PROJECT_ID',
+ 'url' => $this->url->to('BoardViewController', 'show', array('project_id' => 'PROJECT_ID')),
+ ),
+ 'onFocus' => array(
+ 'board.selector.open',
+ )
+)) ?>
+
diff --git a/app/Template/header/creation_dropdown.php b/app/Template/header/creation_dropdown.php
new file mode 100644
index 00000000..9bdf5ad2
--- /dev/null
+++ b/app/Template/header/creation_dropdown.php
@@ -0,0 +1,21 @@
+<?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>
+ <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?>
+ </li>
+ <?php endif ?>
+ <?php if ($is_private_project_enabled): ?>
+ <li>
+ <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?>
+ </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..a74bdd52
--- /dev/null
+++ b/app/Template/header/user_dropdown.php
@@ -0,0 +1,40 @@
+<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>
+ <?= $this->url->icon('tachometer', t('My dashboard'), 'DashboardController', 'show', array('user_id' => $this->user->getId())) ?>
+ </li>
+ <li>
+ <?= $this->url->icon('home', t('My profile'), 'UserViewController', 'show', array('user_id' => $this->user->getId())) ?>
+ </li>
+ <li>
+ <?= $this->url->icon('folder', t('Projects management'), 'ProjectListController', 'show') ?>
+ </li>
+ <?php if ($this->user->hasAccess('UserListController', 'show')): ?>
+ <li>
+ <?= $this->url->icon('user', t('Users management'), 'UserListController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->url->icon('group', t('Groups management'), 'GroupListController', 'index') ?>
+ </li>
+ <li>
+ <?= $this->url->icon('cubes', t('Plugins'), 'PluginController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->url->icon('cog', t('Settings'), 'ConfigController', 'index') ?>
+ </li>
+ <?php endif ?>
+
+ <?= $this->hook->render('template:header:dropdown') ?>
+
+ <li>
+ <?= $this->url->icon('life-ring', t('Documentation'), 'DocumentationController', 'show') ?>
+ </li>
+ <?php if (! DISABLE_LOGOUT): ?>
+ <li>
+ <?= $this->url->icon('sign-out', 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..241b99df 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)): ?>
@@ -54,6 +53,7 @@
>
<?php if (isset($no_layout) && $no_layout): ?>
+ <?= $this->app->flashMessage() ?>
<?= $content_for_layout ?>
<?php else: ?>
<?= $this->hook->render('template:layout:top') ?>
diff --git a/app/Template/link/create.php b/app/Template/link/create.php
index 23990604..37610a3b 100644
--- a/app/Template/link/create.php
+++ b/app/Template/link/create.php
@@ -1,18 +1,12 @@
<div class="page-header">
- <h2><?= t('Add a new link') ?></h2>
+ <h2><?= t('Add link label') ?></h2>
</div>
<form action="<?= $this->url->href('LinkController', 'save') ?>" method="post" autocomplete="off">
-
<?= $this->form->csrf() ?>
-
<?= $this->form->label(t('Label'), 'label') ?>
- <?= $this->form->text('label', $values, $errors, array('required')) ?>
-
+ <?= $this->form->text('label', $values, $errors, array('required', 'autofocus')) ?>
<?= $this->form->label(t('Opposite label'), 'opposite_label') ?>
<?= $this->form->text('opposite_label', $values, $errors) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/link/edit.php b/app/Template/link/edit.php
index 0ad73275..4be56573 100644
--- a/app/Template/link/edit.php
+++ b/app/Template/link/edit.php
@@ -13,9 +13,5 @@
<?= $this->form->label(t('Opposite label'), 'opposite_id') ?>
<?= $this->form->select('opposite_id', $labels, $values, $errors) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'link', 'index') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/link/index.php b/app/Template/link/index.php
deleted file mode 100644
index 7e32069a..00000000
--- a/app/Template/link/index.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<div class="page-header">
- <h2><?= t('Link labels') ?></h2>
-</div>
-<?php if (! empty($links)): ?>
-<table>
- <tr>
- <th class="column-70"><?= t('Link labels') ?></th>
- <th><?= t('Actions') ?></th>
- </tr>
- <?php foreach ($links as $link): ?>
- <tr>
- <td>
- <strong><?= t($link['label']) ?></strong>
-
- <?php if (! empty($link['opposite_label'])): ?>
- | <?= t($link['opposite_label']) ?>
- <?php endif ?>
- </td>
- <td>
- <ul>
- <?= $this->url->link(t('Edit'), 'LinkController', 'edit', array('link_id' => $link['id'])) ?>
- <?= t('or') ?>
- <?= $this->url->link(t('Remove'), 'LinkController', 'confirm', array('link_id' => $link['id'])) ?>
- </ul>
- </td>
- </tr>
- <?php endforeach ?>
-</table>
-<?php else: ?>
- <?= t('There is no link.') ?>
-<?php endif ?>
-
-<?= $this->render('link/create', array('values' => $values, 'errors' => $errors)) ?>
diff --git a/app/Template/link/remove.php b/app/Template/link/remove.php
index b7fbef5e..e5ea2466 100644
--- a/app/Template/link/remove.php
+++ b/app/Template/link/remove.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to remove this link: "%s"?', $link['label']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'LinkController', 'remove', array('link_id' => $link['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'LinkController', 'index') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'LinkController',
+ 'remove',
+ array('link_id' => $link['id'])
+ ) ?>
</div>
diff --git a/app/Template/link/show.php b/app/Template/link/show.php
new file mode 100644
index 00000000..6aadd66b
--- /dev/null
+++ b/app/Template/link/show.php
@@ -0,0 +1,36 @@
+<div class="page-header">
+ <h2><?= t('Link labels') ?></h2>
+ <ul>
+ <li>
+ <?= $this->modal->medium('plus', t('Add link label'), 'LinkController', 'create') ?>
+ </li>
+ </ul>
+</div>
+<?php if (! empty($links)): ?>
+ <table class="table-striped table-scrolling">
+ <tr>
+ <th class="column-70"><?= t('Link labels') ?></th>
+ <th><?= t('Actions') ?></th>
+ </tr>
+ <?php foreach ($links as $link): ?>
+ <tr>
+ <td>
+ <strong><?= t($link['label']) ?></strong>
+
+ <?php if (! empty($link['opposite_label'])): ?>
+ | <?= t($link['opposite_label']) ?>
+ <?php endif ?>
+ </td>
+ <td>
+ <ul>
+ <?= $this->modal->medium('edit', t('Edit'), 'LinkController', 'edit', array('link_id' => $link['id'])) ?>
+ <?= t('or') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'LinkController', 'confirm', array('link_id' => $link['id'])) ?>
+ </ul>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php else: ?>
+ <?= t('There is no link.') ?>
+<?php endif ?>
diff --git a/app/Template/notification/comment_create.php b/app/Template/notification/comment_create.php
index fefc8ba1..41262a7e 100644
--- a/app/Template/notification/comment_create.php
+++ b/app/Template/notification/comment_create.php
@@ -6,6 +6,6 @@
<h3><?= t('New comment') ?></h3>
<?php endif ?>
-<?= $this->text->markdown($comment['comment']) ?>
+<?= $this->text->markdown($comment['comment'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/comment_delete.php b/app/Template/notification/comment_delete.php
new file mode 100644
index 00000000..14babbd9
--- /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'], true) ?>
+
+<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?>
diff --git a/app/Template/notification/comment_update.php b/app/Template/notification/comment_update.php
index 2477d8b3..f1cffae6 100644
--- a/app/Template/notification/comment_update.php
+++ b/app/Template/notification/comment_update.php
@@ -2,6 +2,6 @@
<h3><?= t('Comment updated') ?></h3>
-<?= $this->text->markdown($comment['comment']) ?>
+<?= $this->text->markdown($comment['comment'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/comment_user_mention.php b/app/Template/notification/comment_user_mention.php
index 372183df..0990e7ab 100644
--- a/app/Template/notification/comment_user_mention.php
+++ b/app/Template/notification/comment_user_mention.php
@@ -2,6 +2,6 @@
<p><?= $this->text->e($task['title']) ?></p>
-<?= $this->text->markdown($comment['comment']) ?>
+<?= $this->text->markdown($comment['comment'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/footer.php b/app/Template/notification/footer.php
index 6ac260cb..da437eea 100644
--- a/app/Template/notification/footer.php
+++ b/app/Template/notification/footer.php
@@ -2,6 +2,6 @@
Kanboard
<?php if (isset($application_url) && ! empty($application_url)): ?>
- - <a href="<?= $this->url->href('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', true) ?>"><?= t('view the task on Kanboard') ?></a>
- - <a href="<?= $this->url->href('BoardViewController', 'show', array('project_id' => $task['project_id']), false, '', true) ?>"><?= t('view the board on Kanboard') ?></a>
+ - <?= $this->url->absoluteLink(t('view the task on Kanboard'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ - <?= $this->url->absoluteLink(t('view the board on Kanboard'), 'BoardViewController', 'show', array('project_id' => $task['project_id'])) ?>
<?php endif ?>
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_assignee_change.php b/app/Template/notification/task_assignee_change.php
index 53f7c5c1..f075fdbf 100644
--- a/app/Template/notification/task_assignee_change.php
+++ b/app/Template/notification/task_assignee_change.php
@@ -14,7 +14,7 @@
<?php if (! empty($task['description'])): ?>
<h2><?= t('Description') ?></h2>
- <?= $this->text->markdown($task['description']) ?: t('There is no description.') ?>
+ <?= $this->text->markdown($task['description'], true) ?: t('There is no description.') ?>
<?php endif ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/task_create.php b/app/Template/notification/task_create.php
index 3cd68ac0..3439e357 100644
--- a/app/Template/notification/task_create.php
+++ b/app/Template/notification/task_create.php
@@ -37,7 +37,7 @@
<?php if (! empty($task['description'])): ?>
<h2><?= t('Description') ?></h2>
- <?= $this->text->markdown($task['description']) ?>
+ <?= $this->text->markdown($task['description'], true) ?>
<?php endif ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
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..b66a1531
--- /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->absoluteLink(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..d556fb20
--- /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->absoluteLink(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/notification/task_open.php b/app/Template/notification/task_open.php
index 6eb4ec08..2c85e9f7 100644
--- a/app/Template/notification/task_open.php
+++ b/app/Template/notification/task_open.php
@@ -2,4 +2,4 @@
<p><?= t('The task #%d have been opened.', $task['id']) ?></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_overdue.php b/app/Template/notification/task_overdue.php
index 406e41f7..01ad0a01 100644
--- a/app/Template/notification/task_overdue.php
+++ b/app/Template/notification/task_overdue.php
@@ -13,16 +13,16 @@
<tr style="overflow: hidden; background: #fff; text-align: left; padding-top: .5em; padding-bottom: .5em; padding-left: 3px; padding-right: 3px;">
<td style="border: 1px solid #eee;">#<?= $task['id'] ?></td>
<td style="border: 1px solid #eee;">
- <?php if ($application_url): ?>
- <a href="<?= $this->url->href('TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, '', true) ?>"><?= $this->text->e($task['title']) ?></a>
+ <?php if (! empty($application_url)): ?>
+ <?= $this->url->absoluteLink($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
<?php else: ?>
<?= $this->text->e($task['title']) ?>
<?php endif ?>
</td>
<td style="border: 1px solid #eee;"><?= $this->dt->date($task['date_due']) ?></td>
- <td style="border: 1px solid #eee;"><?= $task['project_name'] ?></td>
+ <td style="border: 1px solid #eee;"><?= $this->text->e($task['project_name']) ?></td>
<td style="border: 1px solid #eee;">
- <?php if ($task['assignee_username']): ?>
+ <?php if (! empty($task['assignee_username'])): ?>
<?= $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?>
<?php endif ?>
</td>
diff --git a/app/Template/notification/task_update.php b/app/Template/notification/task_update.php
index 8adb2553..9abe8e0a 100644
--- a/app/Template/notification/task_update.php
+++ b/app/Template/notification/task_update.php
@@ -1,4 +1,4 @@
<h2><?= $this->text->e($task['title']) ?> (#<?= $task['id'] ?>)</h2>
-<?= $this->render('task/changes', array('changes' => $changes, 'task' => $task)) ?>
+<?= $this->render('task/changes', array('changes' => $changes, 'task' => $task, 'public' => true)) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/notification/task_user_mention.php b/app/Template/notification/task_user_mention.php
index 3d8c8e95..71ad348b 100644
--- a/app/Template/notification/task_user_mention.php
+++ b/app/Template/notification/task_user_mention.php
@@ -2,6 +2,6 @@
<p><?= $this->text->e($task['title']) ?></p>
<h2><?= t('Description') ?></h2>
-<?= $this->text->markdown($task['description']) ?>
+<?= $this->text->markdown($task['description'], true) ?>
<?= $this->render('notification/footer', array('task' => $task, 'application_url' => $application_url)) ?> \ No newline at end of file
diff --git a/app/Template/plugin/directory.php b/app/Template/plugin/directory.php
index b6c6734c..b2fffcb9 100644
--- a/app/Template/plugin/directory.php
+++ b/app/Template/plugin/directory.php
@@ -28,11 +28,9 @@
<td>
<?php if ($is_configured): ?>
<?php if (! isset($installed_plugins[$plugin['title']])): ?>
- <i class="fa fa-cloud-download fa-fw" aria-hidden="true"></i>
- <?= $this->url->link(t('Install'), 'PluginController', 'install', array('archive_url' => urlencode($plugin['download'])), true) ?>
+ <?= $this->url->icon('cloud-download', t('Install'), 'PluginController', 'install', array('archive_url' => urlencode($plugin['download'])), true) ?>
<?php elseif ($installed_plugins[$plugin['title']] < $plugin['version']): ?>
- <i class="fa fa-refresh fa-fw" aria-hidden="true"></i>
- <?= $this->url->link(t('Update'), 'PluginController', 'update', array('archive_url' => urlencode($plugin['download'])), true) ?>
+ <?= $this->url->icon('refresh', t('Update'), 'PluginController', 'update', array('archive_url' => urlencode($plugin['download'])), true) ?>
<?php else: ?>
<i class="fa fa-check-circle-o" aria-hidden="true"></i>
<?= t('Up to date') ?>
diff --git a/app/Template/plugin/remove.php b/app/Template/plugin/remove.php
index bd8f4eb8..1280f8aa 100644
--- a/app/Template/plugin/remove.php
+++ b/app/Template/plugin/remove.php
@@ -5,9 +5,9 @@
<div class="confirm">
<p class="alert alert-info"><?= t('Do you really want to remove this plugin: "%s"?', $plugin->getPluginName()) ?></p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'PluginController', 'uninstall', array('pluginId' => $plugin_id), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'PluginController', 'show', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'PluginController',
+ 'uninstall',
+ array('pluginId' => $plugin_id)
+ ) ?>
</div>
diff --git a/app/Template/plugin/show.php b/app/Template/plugin/show.php
index 9c3d6d20..266568ac 100644
--- a/app/Template/plugin/show.php
+++ b/app/Template/plugin/show.php
@@ -1,3 +1,43 @@
+<?php if (! empty($incompatible_plugins)): ?>
+ <div class="page-header">
+ <h2><?= t('Incompatible Plugins') ?></h2>
+ </div>
+ <table>
+ <tr>
+ <th class="column-35"><?= t('Name') ?></th>
+ <th class="column-25"><?= t('Author') ?></th>
+ <th class="column-10"><?= t('Version') ?></th>
+ <th class="column-12"><?= t('Compatibility') ?></th>
+ <?php if ($is_configured): ?>
+ <th><?= t('Action') ?></th>
+ <?php endif ?>
+ </tr>
+
+ <?php foreach ($incompatible_plugins as $pluginFolder => $plugin): ?>
+ <tr>
+ <td>
+ <?php if ($plugin->getPluginHomepage()): ?>
+ <a href="<?= $plugin->getPluginHomepage() ?>" target="_blank" rel="noreferrer"><?= $this->text->e($plugin->getPluginName()) ?></a>
+ <?php else: ?>
+ <?= $this->text->e($plugin->getPluginName()) ?>
+ <?php endif ?>
+ </td>
+ <td><?= $this->text->e($plugin->getPluginAuthor()) ?></td>
+ <td><?= $this->text->e($plugin->getPluginVersion()) ?></td>
+ <td><?= $this->text->e($plugin->getCompatibleVersion()) ?></td>
+ <?php if ($is_configured): ?>
+ <td>
+ <?= $this->modal->confirm('trash-o', t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder)) ?>
+ </td>
+ <?php endif ?>
+ </tr>
+ <tr>
+ <td colspan="<?= $is_configured ? 6 : 5 ?>"><?= $this->text->e($plugin->getPluginDescription()) ?></td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?>
+
<div class="page-header">
<h2><?= t('Installed Plugins') ?></h2>
</div>
@@ -28,8 +68,7 @@
<td><?= $this->text->e($plugin->getPluginVersion()) ?></td>
<?php if ($is_configured): ?>
<td>
- <i class="fa fa-trash-o fa-fw" aria-hidden="true"></i>
- <?= $this->url->link(t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder)) ?>
</td>
<?php endif ?>
</tr>
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/dropdown.php b/app/Template/project/dropdown.php
index 90dccf21..447cd0b4 100644
--- a/app/Template/project/dropdown.php
+++ b/app/Template/project/dropdown.php
@@ -2,42 +2,35 @@
<a href="#" class="dropdown-menu dashboard-table-link">#<?= $project['id'] ?></a>
<ul>
<li>
- <i class="fa fa-th fa-fw"></i>
- <?= $this->url->link(t('Board'), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?>
+ <?= $this->url->icon('th', t('Board'), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
- <i class="fa fa-calendar fa-fw"></i>
- <?= $this->url->link(t('Calendar'), 'CalendarController', 'show', array('project_id' => $project['id'])) ?>
+ <?= $this->url->icon('calendar', t('Calendar'), 'CalendarController', 'show', array('project_id' => $project['id'])) ?>
</li>
<li>
- <i class="fa fa-list fa-fw"></i>
- <?= $this->url->link(t('Listing'), 'TaskListController', 'show', array('project_id' => $project['id'])) ?>
+ <?= $this->url->icon('list', t('Listing'), 'TaskListController', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php if ($this->user->hasProjectAccess('TaskGanttController', 'show', $project['id'])): ?>
<li>
- <i class="fa fa-sliders fa-fw"></i>
- <?= $this->url->link(t('Gantt'), 'TaskGanttController', 'show', array('project_id' => $project['id'])) ?>
+ <?= $this->url->icon('sliders', t('Gantt'), 'TaskGanttController', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<li>
- <i class="fa fa-dashboard fa-fw"></i>&nbsp;
- <?= $this->url->link(t('Activity'), 'ActivityController', 'project', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->medium('dashboard', t('Activity'), 'ActivityController', 'project', array('project_id' => $project['id'])) ?>
</li>
- <?php if ($this->user->hasProjectAccess('AnalyticController', 'tasks', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('AnalyticController', 'taskDistribution', $project['id'])): ?>
<li>
- <i class="fa fa-line-chart fa-fw"></i>&nbsp;
- <?= $this->url->link(t('Analytics'), 'AnalyticController', 'tasks', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->large('line-chart', t('Analytics'), 'AnalyticController', 'taskDistribution', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<?= $this->hook->render('template:project:dropdown', array('project' => $project)) ?>
- <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?>
<li>
- <i class="fa fa-cog fa-fw"></i>
- <?= $this->url->link(t('Settings'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?>
+ <?= $this->url->icon('cog', t('Settings'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php
index d0f50596..812eb351 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'])) ?>
@@ -10,9 +9,9 @@
</li>
<?php endif ?>
- <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?>
<li <?= $this->app->checkMenuSelection('ProjectEditController') ?>>
- <?= $this->url->link(t('Edit project'), 'ProjectEditController', 'edit', array('project_id' => $project['id'])) ?>
+ <?= $this->url->link(t('Edit project'), 'ProjectEditController', 'show', array('project_id' => $project['id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('ProjectViewController', 'share') ?>>
<?= $this->url->link(t('Public access'), 'ProjectViewController', 'share', array('project_id' => $project['id'])) ?>
@@ -39,6 +38,9 @@
<li <?= $this->app->checkMenuSelection('ProjectPermissionController') ?>>
<?= $this->url->link(t('Permissions'), 'ProjectPermissionController', 'index', array('project_id' => $project['id'])) ?>
</li>
+ <li <?= $this->app->checkMenuSelection('ProjectRoleController') ?>>
+ <?= $this->url->link(t('Custom roles'), 'ProjectRoleController', 'show', array('project_id' => $project['id'])) ?>
+ </li>
<?php endif ?>
<li <?= $this->app->checkMenuSelection('ActionController') ?>>
<?= $this->url->link(t('Automatic actions'), 'ActionController', 'index', array('project_id' => $project['id'])) ?>
@@ -48,15 +50,15 @@
</li>
<?php if ($project['is_active']): ?>
<li>
- <?= $this->url->link(t('Disable'), 'ProjectStatusController', 'confirmDisable', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->confirmLink(t('Disable'), 'ProjectStatusController', 'confirmDisable', array('project_id' => $project['id'])) ?>
<?php else: ?>
<li>
- <?= $this->url->link(t('Enable'), 'ProjectStatusController', 'confirmEnable', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->confirmLink(t('Enable'), 'ProjectStatusController', 'confirmEnable', array('project_id' => $project['id'])) ?>
<?php endif ?>
</li>
<?php if ($this->user->hasProjectAccess('ProjectStatusController', 'remove', $project['id'])): ?>
<li>
- <?= $this->url->link(t('Remove'), 'ProjectStatusController', 'confirmRemove', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->confirmLink(t('Remove'), 'ProjectStatusController', 'confirmRemove', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<?php endif ?>
diff --git a/app/Template/project_action_duplication/show.php b/app/Template/project_action_duplication/show.php
index 2eebb262..c2f52e35 100644
--- a/app/Template/project_action_duplication/show.php
+++ b/app/Template/project_action_duplication/show.php
@@ -4,16 +4,12 @@
<?php if (empty($projects_list)): ?>
<p class="alert"><?= t('There is no available project.') ?></p>
<?php else: ?>
- <form class="popover-form" method="post" action="<?= $this->url->href('ProjectActionDuplicationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <form method="post" action="<?= $this->url->href('ProjectActionDuplicationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->label(t('Create from another project'), 'src_project_id') ?>
<?= $this->form->select('src_project_id', $projects_list) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'Action', 'index', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
<?php endif ?>
diff --git a/app/Template/project_creation/create.php b/app/Template/project_creation/create.php
index d00883ba..171bd17a 100644
--- a/app/Template/project_creation/create.php
+++ b/app/Template/project_creation/create.php
@@ -2,7 +2,7 @@
<div class="page-header">
<h2><?= $title ?></h2>
</div>
- <form class="popover-form" id="project-creation-form" method="post" action="<?= $this->url->href('ProjectCreationController', 'save') ?>" autocomplete="off">
+ <form id="project-creation-form" method="post" action="<?= $this->url->href('ProjectCreationController', 'save') ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('is_private', $values) ?>
@@ -12,14 +12,14 @@
<?php if (count($projects_list) > 1): ?>
<?= $this->form->label(t('Create from another project'), 'src_project_id') ?>
- <?= $this->form->select('src_project_id', $projects_list, $values) ?>
+ <?= $this->form->select('src_project_id', $projects_list, $values, array(), array(), 'js-project-creation-select-options') ?>
<?php endif ?>
- <div class="project-creation-options" <?= isset($values['src_project_id']) && $values['src_project_id'] > 0 ? '' : 'style="display: none"' ?>>
+ <div class="js-project-creation-options" <?= isset($values['src_project_id']) && $values['src_project_id'] > 0 ? '' : 'style="display: none"' ?>>
<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) ?>
@@ -29,11 +29,7 @@
<?= $this->form->checkbox('projectTaskDuplicationModel', t('Tasks'), 1, false) ?>
</div>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ProjectListController', 'show', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
<?php if ($is_private): ?>
<div class="alert alert-info">
diff --git a/app/Template/project_edit/dates.php b/app/Template/project_edit/dates.php
deleted file mode 100644
index 48135ddc..00000000
--- a/app/Template/project_edit/dates.php
+++ /dev/null
@@ -1,26 +0,0 @@
-<div class="page-header">
- <h2><?= t('Edit project') ?></h2>
- <ul>
- <li ><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li class="active"><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- </ul>
-</div>
-<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'dates')) ?>" autocomplete="off">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('id', $values) ?>
- <?= $this->form->hidden('name', $values) ?>
-
- <?= $this->form->label(t('Start date'), 'start_date') ?>
- <?= $this->form->text('start_date', $values, $errors, array('maxlength="10"'), 'form-date') ?>
-
- <?= $this->form->label(t('End date'), 'end_date') ?>
- <?= $this->form->text('end_date', $values, $errors, array('maxlength="10"'), 'form-date') ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
-</form>
-
-<p class="alert alert-info"><?= t('Those dates are useful for the project Gantt chart.') ?></p>
diff --git a/app/Template/project_edit/description.php b/app/Template/project_edit/description.php
deleted file mode 100644
index f7e7be46..00000000
--- a/app/Template/project_edit/description.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<div class="page-header">
- <h2><?= t('Edit project') ?></h2>
- <ul>
- <li><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li class="active"><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- </ul>
-</div>
-<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'description')) ?>" autocomplete="off">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('id', $values) ?>
- <?= $this->form->hidden('name', $values) ?>
- <?= $this->form->textarea('description', $values, $errors, array(), 'markdown-editor') ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
-</form>
diff --git a/app/Template/project_edit/general.php b/app/Template/project_edit/general.php
deleted file mode 100644
index c7421477..00000000
--- a/app/Template/project_edit/general.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<div class="page-header">
- <h2><?= t('Edit project') ?></h2>
- <ul>
- <li class="active"><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- </ul>
-</div>
-<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'edit')) ?>" autocomplete="off">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('id', $values) ?>
-
- <?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array('required', 'maxlength="50"')) ?>
-
- <?= $this->form->label(t('Identifier'), 'identifier') ?>
- <?= $this->form->text('identifier', $values, $errors, array('maxlength="50"')) ?>
- <p class="form-help"><?= t('The project identifier is optional and must be alphanumeric, example: MYPROJECT.') ?></p>
-
- <hr>
- <div class="form-inline">
- <?= $this->form->label(t('Project owner'), 'owner_id') ?>
- <?= $this->form->select('owner_id', $owners, $values, $errors) ?>
- </div>
-
- <?php if ($this->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])): ?>
- <hr>
- <?= $this->form->checkbox('is_private', t('Private project'), 1, $project['is_private'] == 1) ?>
- <p class="form-help"><?= t('Private projects do not have users and groups management.') ?></p>
- <?php endif ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
-</form>
diff --git a/app/Template/project_edit/show.php b/app/Template/project_edit/show.php
new file mode 100644
index 00000000..46cdb8fa
--- /dev/null
+++ b/app/Template/project_edit/show.php
@@ -0,0 +1,58 @@
+<div class="page-header">
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= t('Edit project') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'edit')) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('id', $values) ?>
+
+ <fieldset>
+ <legend><?= t('General') ?></legend>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array('required', 'maxlength="50"', 'autofocus', 'tabindex="1"')) ?>
+
+ <?= $this->form->label(t('Identifier'), 'identifier') ?>
+ <?= $this->form->text('identifier', $values, $errors, array('maxlength="50"', 'tabindex="2"')) ?>
+ <p class="form-help"><?= t('The project identifier is optional and must be alphanumeric, example: MYPROJECT.') ?></p>
+
+ <?= $this->form->label(t('Description'), 'description') ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 3)) ?>
+ </fieldset>
+
+ <fieldset>
+ <legend><?= t('Permissions and ownership') ?></legend>
+
+ <?php if ($this->user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])): ?>
+ <?= $this->form->checkbox('is_private', t('Private project'), 1, $project['is_private'] == 1) ?>
+ <p class="form-help"><?= t('Private projects do not have users and groups management.') ?></p>
+ <?php endif ?>
+
+ <div class="form-inline">
+ <?= $this->form->label(t('Project owner'), 'owner_id') ?>
+ <?= $this->form->select('owner_id', $owners, $values, $errors, array('tabindex="5"')) ?>
+ </div>
+ </fieldset>
+
+ <fieldset>
+ <legend><?= t('Dates') ?></legend>
+
+ <?= $this->form->date(t('Start date'), 'start_date', $values, $errors, array('tabindex="6"')) ?>
+ <?= $this->form->date(t('End date'), 'end_date', $values, $errors, array('tabindex="7"')) ?>
+ </fieldset>
+
+ <fieldset>
+ <legend><?= t('Priorities') ?></legend>
+
+ <?= $this->form->label(t('Default priority'), 'priority_default') ?>
+ <?= $this->form->number('priority_default', $values, $errors, array('tabindex="8"')) ?>
+
+ <?= $this->form->label(t('Lowest priority'), 'priority_start') ?>
+ <?= $this->form->number('priority_start', $values, $errors, array('tabindex="9"')) ?>
+
+ <?= $this->form->label(t('Highest priority'), 'priority_end') ?>
+ <?= $this->form->number('priority_end', $values, $errors, array('tabindex="10"')) ?>
+ <p class="form-help"><?= t('If you put zero to the low and high priority, this feature will be disabled.') ?></p>
+ </fieldset>
+
+ <?= $this->modal->submitButtons(array('tabindex' => 11)) ?>
+</form>
diff --git a/app/Template/project_edit/task_priority.php b/app/Template/project_edit/task_priority.php
deleted file mode 100644
index 3ef4b3cb..00000000
--- a/app/Template/project_edit/task_priority.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<div class="page-header">
- <h2><?= t('Edit project') ?></h2>
- <ul>
- <li ><?= $this->url->link(t('General'), 'ProjectEditController', 'edit', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Dates'), 'ProjectEditController', 'dates', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li><?= $this->url->link(t('Description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- <li class="active"><?= $this->url->link(t('Task priority'), 'ProjectEditController', 'priority', array('project_id' => $project['id']), false, 'popover-link') ?></li>
- </ul>
-</div>
-<form method="post" class="popover-form" action="<?= $this->url->href('ProjectEditController', 'update', array('project_id' => $project['id'], 'redirect' => 'priority')) ?>" autocomplete="off">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('id', $values) ?>
- <?= $this->form->hidden('name', $values) ?>
-
- <?= $this->form->label(t('Default priority'), 'priority_default') ?>
- <?= $this->form->number('priority_default', $values, $errors) ?>
-
- <?= $this->form->label(t('Lowest priority'), 'priority_start') ?>
- <?= $this->form->number('priority_start', $values, $errors) ?>
-
- <?= $this->form->label(t('Highest priority'), 'priority_end') ?>
- <?= $this->form->number('priority_end', $values, $errors) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- </div>
-</form>
-
-<p class="alert alert-info"><?= t('If you put zero to the low and high priority, this feature will be disabled.') ?></p>
diff --git a/app/Template/project_file/create.php b/app/Template/project_file/create.php
index e262799b..de35f87c 100644
--- a/app/Template/project_file/create.php
+++ b/app/Template/project_file/create.php
@@ -1,33 +1,20 @@
<div class="page-header">
<h2><?= t('Attach a document') ?></h2>
</div>
-<div id="file-done" style="display:none">
- <p class="alert alert-success">
- <?= t('All files have been uploaded successfully.') ?>
- <?= $this->url->link(t('View uploaded files'), 'ProjectOverviewController', 'show', array('project_id' => $project['id'])) ?>
- </p>
-</div>
-
-<div id="file-error-max-size" style="display:none">
- <p class="alert alert-error">
- <?= t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)) ?>
- <a href="#" id="file-browser"><?= t('Choose files again') ?></a>
- </p>
-</div>
-<div
- id="file-dropzone"
- data-max-size="<?= $max_size ?>"
- data-url="<?= $this->url->href('ProjectFileController', 'save', array('project_id' => $project['id'])) ?>">
- <div id="file-dropzone-inner">
- <?= t('Drag and drop your files here') ?> <?= t('or') ?> <a href="#" id="file-browser"><?= t('choose files') ?></a>
- </div>
-</div>
-
-<input type="file" name="files[]" multiple style="display:none" id="file-form-element">
+<?= $this->app->component('file-upload', array(
+ 'maxSize' => $max_size,
+ 'url' => $this->url->to('ProjectFileController', 'save', array('project_id' => $project['id'])),
+ 'labelDropzone' => t('Drag and drop your files here'),
+ 'labelOr' => t('or'),
+ 'labelChooseFiles' => t('choose files'),
+ 'labelOversize' => t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)),
+ 'labelSuccess' => t('All files have been uploaded successfully.'),
+ 'labelCloseSuccess' => t('Close this window'),
+ 'labelUploadError' => t('Unable to upload this file.'),
+)) ?>
-<div class="form-actions">
- <input type="submit" value="<?= t('Upload files') ?>" class="btn btn-blue" id="file-upload-button" disabled>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ProjectOverviewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
-</div>
+<?= $this->modal->submitButtons(array(
+ 'submitLabel' => t('Upload files'),
+ 'disabled' => true,
+)) ?>
diff --git a/app/Template/project_file/remove.php b/app/Template/project_file/remove.php
index 0517a9e7..043b8fc8 100644
--- a/app/Template/project_file/remove.php
+++ b/app/Template/project_file/remove.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to remove this file: "%s"?', $this->text->e($file['name'])) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'ProjectFileController', 'remove', array('project_id' => $project['id'], 'file_id' => $file['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'ProjectOverviewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'ProjectFileController',
+ 'remove',
+ array('project_id' => $project['id'], 'file_id' => $file['id'])
+ ) ?>
</div>
diff --git a/app/Template/project_gantt/show.php b/app/Template/project_gantt/show.php
index af22a6ed..725f348d 100644
--- a/app/Template/project_gantt/show.php
+++ b/app/Template/project_gantt/show.php
@@ -1,11 +1,23 @@
<section id="main">
<div class="page-header">
<ul>
+ <?php if ($this->user->hasAccess('ProjectCreationController', 'create')): ?>
+ <li>
+ <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->app->config('disable_private_project', 0) == 0): ?>
+ <li>
+ <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?>
+ </li>
+ <?php endif ?>
<li>
- <i class="fa fa-folder fa-fw"></i><?= $this->url->link(t('Projects list'), 'ProjectListController', 'show') ?>
+ <?= $this->url->icon('folder', t('Projects list'), 'ProjectListController', 'show') ?>
</li>
<?php if ($this->user->hasAccess('ProjectUserOverviewController', 'managers')): ?>
- <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('Users overview'), 'ProjectUserOverviewController', 'managers') ?></li>
+ <li>
+ <?= $this->url->icon('user', t('Users overview'), 'ProjectUserOverviewController', 'managers') ?>
+ </li>
<?php endif ?>
</ul>
</div>
diff --git a/app/Template/project_header/dropdown.php b/app/Template/project_header/dropdown.php
index 79a1b389..83c2b97f 100644
--- a/app/Template/project_header/dropdown.php
+++ b/app/Template/project_header/dropdown.php
@@ -4,12 +4,10 @@
<?php if ($board_view): ?>
<li>
<span class="filter-display-mode" <?= $this->board->isCollapsed($project['id']) ? '' : 'style="display: none;"' ?>>
- <i class="fa fa-expand fa-fw"></i>
- <?= $this->url->link(t('Expand tasks'), 'BoardAjaxController', 'expand', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?>
+ <?= $this->url->icon('expand', t('Expand tasks'), 'BoardAjaxController', 'expand', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?>
</span>
<span class="filter-display-mode" <?= $this->board->isCollapsed($project['id']) ? 'style="display: none;"' : '' ?>>
- <i class="fa fa-compress fa-fw"></i>
- <?= $this->url->link(t('Collapse tasks'), 'BoardAjaxController', 'collapse', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?>
+ <?= $this->url->icon('compress', t('Collapse tasks'), 'BoardAjaxController', 'collapse', array('project_id' => $project['id']), false, 'board-display-mode', t('Keyboard shortcut: "%s"', 's')) ?>
</span>
</li>
<li>
@@ -20,75 +18,58 @@
<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'])): ?>
<li>
- <i class="fa fa-plus fa-fw"></i>
- <?= $this->url->link(t('Add a new task'), 'TaskCreationController', 'show', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->large('plus', t('Add a new task'), 'TaskCreationController', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<li>
- <i class="fa fa-dashboard fa-fw"></i>
- <?= $this->url->link(t('Activity'), 'ActivityController', 'project', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->medium('dashboard', t('Activity'), 'ActivityController', 'project', array('project_id' => $project['id'])) ?>
</li>
<?php if ($this->user->hasProjectAccess('CustomFilterController', 'index', $project['id'])): ?>
<li>
- <i class="fa fa-filter fa-fw"></i>
- <?= $this->url->link(t('Custom filters'), 'CustomFilterController', 'index', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->medium('filter', t('Add custom filters'), 'CustomFilterController', 'create', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<?php if ($project['is_public']): ?>
<li>
- <i class="fa fa-share-alt fa-fw"></i>
- <?= $this->url->link(t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?>
+ <?= $this->url->icon('share-alt', t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?>
</li>
<?php endif ?>
<?= $this->hook->render('template:project:dropdown', array('project' => $project)) ?>
- <?php if ($this->user->hasProjectAccess('AnalyticController', 'tasks', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('AnalyticController', 'taskDistribution', $project['id'])): ?>
<li>
- <i class="fa fa-line-chart fa-fw"></i>
- <?= $this->url->link(t('Analytics'), 'AnalyticController', 'tasks', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->large('line-chart', t('Analytics'), 'AnalyticController', 'taskDistribution', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<?php if ($this->user->hasProjectAccess('ExportController', 'tasks', $project['id'])): ?>
<li>
- <i class="fa fa-upload fa-fw"></i>
- <?= $this->url->link(t('Exports'), 'ExportController', 'tasks', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->medium('upload', t('Exports'), 'ExportController', 'tasks', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<?php if ($this->user->hasProjectAccess('TaskImportController', 'tasks', $project['id'])): ?>
<li>
- <i class="fa fa-download fa-fw"></i>
- <?= $this->url->link(t('Imports'), 'TaskImportController', 'show', array('project_id' => $project['id'])) ?>
+ <?= $this->modal->medium('download', t('Import tasks'), 'TaskImportController', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
- <?php if ($this->user->hasProjectAccess('ProjectEditController', 'edit', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?>
<li>
- <i class="fa fa-cog fa-fw"></i>
- <?= $this->url->link(t('Settings'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?>
+ <?= $this->url->icon('cog', t('Settings'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?>
</li>
<?php endif ?>
<li>
- <i class="fa fa-folder fa-fw" aria-hidden="true"></i>
- <?= $this->url->link(t('Manage projects'), 'ProjectListController', 'show') ?>
+ <?= $this->url->icon('folder', t('Manage projects'), 'ProjectListController', 'show') ?>
</li>
</ul>
</div>
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_header/views.php b/app/Template/project_header/views.php
index 3a41c91b..0328a051 100644
--- a/app/Template/project_header/views.php
+++ b/app/Template/project_header/views.php
@@ -1,24 +1,19 @@
<ul class="views">
<li <?= $this->app->checkMenuSelection('ProjectOverviewController') ?>>
- <i class="fa fa-eye fa-fw"></i>
- <?= $this->url->link(t('Overview'), 'ProjectOverviewController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-overview', t('Keyboard shortcut: "%s"', 'v o')) ?>
+ <?= $this->url->icon('eye', t('Overview'), 'ProjectOverviewController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-overview', t('Keyboard shortcut: "%s"', 'v o')) ?>
</li>
<li <?= $this->app->checkMenuSelection('BoardViewController') ?>>
- <i class="fa fa-th fa-fw"></i>
- <?= $this->url->link(t('Board'), 'BoardViewController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-board', t('Keyboard shortcut: "%s"', 'v b')) ?>
+ <?= $this->url->icon('th', t('Board'), 'BoardViewController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-board', t('Keyboard shortcut: "%s"', 'v b')) ?>
</li>
- <li <?= $this->app->checkMenuSelection('Calendar') ?>>
- <i class="fa fa-calendar fa-fw"></i>
- <?= $this->url->link(t('Calendar'), 'CalendarController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-calendar', t('Keyboard shortcut: "%s"', 'v c')) ?>
+ <li <?= $this->app->checkMenuSelection('CalendarController') ?>>
+ <?= $this->url->icon('calendar', t('Calendar'), 'CalendarController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-calendar', t('Keyboard shortcut: "%s"', 'v c')) ?>
</li>
<li <?= $this->app->checkMenuSelection('TaskListController') ?>>
- <i class="fa fa-list fa-fw"></i>
- <?= $this->url->link(t('List'), 'TaskListController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-listing', t('Keyboard shortcut: "%s"', 'v l')) ?>
+ <?= $this->url->icon('list', t('List'), 'TaskListController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-listing', t('Keyboard shortcut: "%s"', 'v l')) ?>
</li>
<?php if ($this->user->hasProjectAccess('TaskGanttController', 'show', $project['id'])): ?>
<li <?= $this->app->checkMenuSelection('TaskGanttController') ?>>
- <i class="fa fa-sliders fa-fw"></i>
- <?= $this->url->link(t('Gantt'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-gantt', t('Keyboard shortcut: "%s"', 'v g')) ?>
+ <?= $this->url->icon('sliders', t('Gantt'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-gantt', t('Keyboard shortcut: "%s"', 'v g')) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/project_list/show.php b/app/Template/project_list/show.php
index 8b9f1396..a6364585 100644
--- a/app/Template/project_list/show.php
+++ b/app/Template/project_list/show.php
@@ -1,24 +1,40 @@
<section id="main">
<div class="page-header">
<ul>
+ <?= $this->hook->render('template:project-list:menu:before') ?>
+
+ <?php if ($this->user->hasAccess('ProjectCreationController', 'create')): ?>
+ <li>
+ <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->app->config('disable_private_project', 0) == 0): ?>
+ <li>
+ <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?>
+ </li>
+ <?php endif ?>
+
<?php if ($this->user->hasAccess('ProjectUserOverviewController', 'managers')): ?>
- <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('Users overview'), 'ProjectUserOverviewController', 'managers') ?></li>
+ <li><?= $this->url->icon('user', t('Users overview'), 'ProjectUserOverviewController', 'managers') ?></li>
<?php endif ?>
+
<?php if ($this->user->hasAccess('ProjectGanttController', 'show')): ?>
- <li><i class="fa fa-sliders fa-fw"></i><?= $this->url->link(t('Projects Gantt chart'), 'ProjectGanttController', 'show') ?></li>
+ <li><?= $this->url->icon('sliders', t('Projects Gantt chart'), 'ProjectGanttController', 'show') ?></li>
<?php endif ?>
+
+ <?= $this->hook->render('template:project-list:menu:after') ?>
</ul>
</div>
<?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 +89,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/attachments.php b/app/Template/project_overview/attachments.php
index ab8cf2ad..b8baadd0 100644
--- a/app/Template/project_overview/attachments.php
+++ b/app/Template/project_overview/attachments.php
@@ -5,7 +5,7 @@
<div class="accordion-content">
<?php if ($this->user->hasProjectAccess('ProjectFileController', 'create', $project['id'])): ?>
<div class="buttons-header">
- <?= $this->url->button('fa-plus', t('Upload a file'), 'ProjectFileController', 'create', array('project_id' => $project['id']), 'popover') ?>
+ <?= $this->modal->mediumButton('plus', t('Upload a file'), 'ProjectFileController', 'create', array('project_id' => $project['id'])) ?>
</div>
<?php endif ?>
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/description.php b/app/Template/project_overview/description.php
index 0c2027ed..80b93efe 100644
--- a/app/Template/project_overview/description.php
+++ b/app/Template/project_overview/description.php
@@ -3,9 +3,9 @@
<h3><a href="#" class="fa accordion-toggle"></a> <?= t('Description') ?></h3>
</div>
<div class="accordion-content">
- <?php if ($this->user->hasProjectAccess('ProjectEditController', 'description', $project['id'])): ?>
+ <?php if ($this->user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?>
<div class="buttons-header">
- <?= $this->url->button('fa-edit', t('Edit description'), 'ProjectEditController', 'description', array('project_id' => $project['id']), 'popover') ?>
+ <?= $this->modal->mediumButton('edit', t('Edit description'), 'ProjectEditController', 'show', array('project_id' => $project['id'])) ?>
</div>
<?php endif ?>
<article class="markdown">
diff --git a/app/Template/project_overview/files.php b/app/Template/project_overview/files.php
index fa870938..85de52f7 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>
@@ -15,18 +15,19 @@
<ul>
<?php if ($this->file->getPreviewType($file['name']) !== null): ?>
<li>
- <i class="fa fa-eye fa-fw"></i>
- <?= $this->url->link(t('View file'), 'FileViewerController', 'show', array('project_id' => $project['id'], 'file_id' => $file['id']), false, 'popover') ?>
+ <?= $this->modal->large('eye', t('View file'), 'FileViewerController', 'show', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>
+ </li>
+ <?php elseif ($this->file->getBrowserViewType($file['name']) !== null): ?>
+ <li>
+ <?= $this->url->icon('eye', t('View file'), 'FileViewerController', 'browser', array('project_id' => $project['id'], 'file_id' => $file['id']), false, '', '', true) ?>
</li>
<?php endif ?>
<li>
- <i class="fa fa-download fa-fw"></i>
- <?= $this->url->link(t('Download'), 'FileViewerController', 'download', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>
+ <?= $this->url->icon('download', t('Download'), 'FileViewerController', 'download', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>
</li>
<?php if ($this->user->hasProjectAccess('ProjectFileController', 'remove', $project['id'])): ?>
<li>
- <i class="fa fa-trash fa-fw"></i>
- <?= $this->url->link(t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/project_overview/images.php b/app/Template/project_overview/images.php
index 7f38e2b1..7e7962e8 100644
--- a/app/Template/project_overview/images.php
+++ b/app/Template/project_overview/images.php
@@ -2,20 +2,28 @@
<div class="file-thumbnails">
<?php foreach ($images as $file): ?>
<div class="file-thumbnail">
- <a href="<?= $this->url->href('FileViewerController', 'show', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>" class="popover"><img src="<?= $this->url->href('FileViewerController', 'thumbnail', array('file_id' => $file['id'], 'project_id' => $project['id'])) ?>" title="<?= $this->text->e($file['name']) ?>" alt="<?= $this->text->e($file['name']) ?>"></a>
+ <?= $this->app->component('image-slideshow', array(
+ 'images' => $images,
+ 'image' => $file,
+ 'regex' => 'FILE_ID',
+ 'url' => array(
+ 'image' => $this->url->to('FileViewerController', 'image', array('file_id' => 'FILE_ID', 'project_id' => $project['id'])),
+ 'thumbnail' => $this->url->to('FileViewerController', 'thumbnail', array('file_id' => 'FILE_ID', 'project_id' => $project['id'])),
+ 'download' => $this->url->to('FileViewerController', 'download', array('file_id' => 'FILE_ID', 'project_id' => $project['id'])),
+ )
+ )) ?>
+
<div class="file-thumbnail-content">
<div class="file-thumbnail-title">
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-text"><?= $this->text->e($file['name']) ?> <i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <i class="fa fa-download fa-fw"></i>
- <?= $this->url->link(t('Download'), 'FileViewerController', 'download', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>
+ <?= $this->url->icon('download', t('Download'), 'FileViewerController', 'download', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>
</li>
<?php if ($this->user->hasProjectAccess('ProjectFileController', 'remove', $project['id'])): ?>
<li>
- <i class="fa fa-trash fa-fw"></i>
- <?= $this->url->link(t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectFileController', 'confirm', array('project_id' => $project['id'], 'file_id' => $file['id'])) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/project_overview/information.php b/app/Template/project_overview/information.php
index fdf0f753..0fe53e08 100644
--- a/app/Template/project_overview/information.php
+++ b/app/Template/project_overview/information.php
@@ -3,7 +3,7 @@
<h3><a href="#" class="fa accordion-toggle"></a> <?= t('Information') ?></h3>
</div>
<div class="accordion-content">
- <div class="listing">
+ <div class="panel">
<ul>
<?php if ($project['owner_id'] > 0): ?>
<li><?= t('Project owner: ') ?><strong><?= $this->text->e($project['owner_name'] ?: $project['owner_username']) ?></strong></li>
@@ -29,9 +29,9 @@
<?php endif ?>
<?php if ($project['is_public']): ?>
- <li><i class="fa fa-share-alt"></i> <?= $this->url->link(t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?></li>
- <li><i class="fa fa-rss-square"></i> <?= $this->url->link(t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></li>
- <li><i class="fa fa-calendar"></i> <?= $this->url->link(t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?></li>
+ <li><?= $this->url->icon('share-alt', t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?></li>
+ <li><?= $this->url->icon('rss-square', t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></li>
+ <li><?= $this->url->icon('calendar', t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?></li>
<?php endif ?>
</ul>
</div>
diff --git a/app/Template/project_overview/show.php b/app/Template/project_overview/show.php
index 6b2bc2cf..d87b2775 100644
--- a/app/Template/project_overview/show.php
+++ b/app/Template/project_overview/show.php
@@ -1,6 +1,7 @@
<section id="main">
<?= $this->projectHeader->render($project, 'ProjectOverviewController', 'show') ?>
<?= $this->render('project_overview/columns', array('project' => $project)) ?>
+ <?= $this->hook->render('template:project-overview:before-description', array('project' => $project)) ?>
<?= $this->render('project_overview/description', array('project' => $project)) ?>
<?= $this->render('project_overview/attachments', array('project' => $project, 'images' => $images, 'files' => $files)) ?>
<?= $this->render('project_overview/information', array('project' => $project, 'users' => $users, 'roles' => $roles)) ?>
diff --git a/app/Template/project_permission/groups.php b/app/Template/project_permission/groups.php
new file mode 100644
index 00000000..c9914344
--- /dev/null
+++ b/app/Template/project_permission/groups.php
@@ -0,0 +1,60 @@
+<div class="page-header">
+ <h2><?= t('Allowed Groups') ?></h2>
+</div>
+
+<?php if (empty($groups)): ?>
+ <div class="alert"><?= t('No group have been allowed specifically.') ?></div>
+<?php else: ?>
+ <table class="table-scrolling">
+ <tr>
+ <th class="column-50"><?= t('Group') ?></th>
+ <th><?= t('Role') ?></th>
+ <?php if ($project['is_private'] == 0): ?>
+ <th class="column-15"><?= t('Actions') ?></th>
+ <?php endif ?>
+ </tr>
+ <?php foreach ($groups as $group): ?>
+ <tr>
+ <td><?= $this->text->e($group['name']) ?></td>
+ <td>
+ <?= $this->app->component('project-select-role', array(
+ 'roles' => $roles,
+ 'role' => $group['role'],
+ 'id' => $group['id'],
+ 'url' => $this->url->to('ProjectPermissionController', 'changeGroupRole', array('project_id' => $project['id'])),
+ )) ?>
+ </td>
+ <td>
+ <?= $this->url->icon('trash-o', t('Remove'), 'ProjectPermissionController', 'removeGroup', array('project_id' => $project['id'], 'group_id' => $group['id']), true) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?>
+
+<?php if ($project['is_private'] == 0): ?>
+ <div class="panel">
+ <form method="post" action="<?= $this->url->href('ProjectPermissionController', 'addGroup', array('project_id' => $project['id'])) ?>" autocomplete="off" class="form-inline">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', array('project_id' => $project['id'])) ?>
+ <?= $this->form->hidden('group_id', $values) ?>
+ <?= $this->form->hidden('external_id', $values) ?>
+
+ <?= $this->form->label(t('Group Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array(
+ 'required',
+ 'placeholder="'.t('Enter group name...').'"',
+ 'title="'.t('Enter group name...').'"',
+ 'data-dst-field="group_id"',
+ 'data-dst-extra-field="external_id"',
+ 'data-search-url="'.$this->url->href('GroupAjaxController', 'autocomplete').'"',
+ ),
+ 'autocomplete') ?>
+
+ <?= $this->form->select('role', $roles, $values, $errors) ?>
+
+ <button type="submit" class="btn btn-blue"><?= t('Add') ?></button>
+ </form>
+ </div>
+<?php endif ?>
+
diff --git a/app/Template/project_permission/index.php b/app/Template/project_permission/index.php
index d850ec50..689966b6 100644
--- a/app/Template/project_permission/index.php
+++ b/app/Template/project_permission/index.php
@@ -5,125 +5,21 @@
<?php if ($project['is_everybody_allowed']): ?>
<div class="alert"><?= t('Everybody have access to this project.') ?></div>
<?php else: ?>
-
- <?php if (empty($users)): ?>
- <div class="alert"><?= t('No user have been allowed specifically.') ?></div>
- <?php else: ?>
- <table>
- <tr>
- <th class="column-50"><?= t('User') ?></th>
- <th><?= t('Role') ?></th>
- <?php if ($project['is_private'] == 0): ?>
- <th class="column-15"><?= t('Actions') ?></th>
- <?php endif ?>
- </tr>
- <?php foreach ($users as $user): ?>
- <tr>
- <td><?= $this->text->e($user['name'] ?: $user['username']) ?></td>
- <td>
- <?= $this->form->select(
- 'role-'.$user['id'],
- $roles,
- array('role-'.$user['id'] => $user['role']),
- array(),
- array('data-url="'.$this->url->href('ProjectPermissionController', 'changeUserRole', array('project_id' => $project['id'])).'"', 'data-id="'.$user['id'].'"'),
- 'project-change-role'
- ) ?>
- </td>
- <td>
- <?= $this->url->link(t('Remove'), 'ProjectPermissionController', 'removeUser', array('project_id' => $project['id'], 'user_id' => $user['id']), true) ?>
- </td>
- </tr>
- <?php endforeach ?>
- </table>
- <?php endif ?>
-
- <?php if ($project['is_private'] == 0): ?>
- <div class="listing">
- <form method="post" action="<?= $this->url->href('ProjectPermissionController', 'addUser', array('project_id' => $project['id'])) ?>" autocomplete="off" class="form-inline">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('project_id', array('project_id' => $project['id'])) ?>
- <?= $this->form->hidden('user_id', $values) ?>
-
- <?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array(
- 'required',
- 'placeholder="'.t('Enter user name...').'"',
- 'title="'.t('Enter user name...').'"',
- 'data-dst-field="user_id"',
- 'data-search-url="'.$this->url->href('UserAjaxController', 'autocomplete').'"',
- ),
- 'autocomplete') ?>
-
- <?= $this->form->select('role', $roles, $values, $errors) ?>
-
- <button type="submit" class="btn btn-blue"><?= t('Add') ?></button>
- </form>
- </div>
- <?php endif ?>
-
- <div class="page-header">
- <h2><?= t('Allowed Groups') ?></h2>
- </div>
-
- <?php if (empty($groups)): ?>
- <div class="alert"><?= t('No group have been allowed specifically.') ?></div>
- <?php else: ?>
- <table>
- <tr>
- <th class="column-50"><?= t('Group') ?></th>
- <th><?= t('Role') ?></th>
- <?php if ($project['is_private'] == 0): ?>
- <th class="column-15"><?= t('Actions') ?></th>
- <?php endif ?>
- </tr>
- <?php foreach ($groups as $group): ?>
- <tr>
- <td><?= $this->text->e($group['name']) ?></td>
- <td>
- <?= $this->form->select(
- 'role-'.$group['id'],
- $roles,
- array('role-'.$group['id'] => $group['role']),
- array(),
- array('data-url="'.$this->url->href('ProjectPermissionController', 'changeGroupRole', array('project_id' => $project['id'])).'"', 'data-id="'.$group['id'].'"'),
- 'project-change-role'
- ) ?>
- </td>
- <td>
- <?= $this->url->link(t('Remove'), 'ProjectPermissionController', 'removeGroup', array('project_id' => $project['id'], 'group_id' => $group['id']), true) ?>
- </td>
- </tr>
- <?php endforeach ?>
- </table>
- <?php endif ?>
-
- <?php if ($project['is_private'] == 0): ?>
- <div class="listing">
- <form method="post" action="<?= $this->url->href('ProjectPermissionController', 'addGroup', array('project_id' => $project['id'])) ?>" autocomplete="off" class="form-inline">
- <?= $this->form->csrf() ?>
- <?= $this->form->hidden('project_id', array('project_id' => $project['id'])) ?>
- <?= $this->form->hidden('group_id', $values) ?>
- <?= $this->form->hidden('external_id', $values) ?>
-
- <?= $this->form->label(t('Group Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array(
- 'required',
- 'placeholder="'.t('Enter group name...').'"',
- 'title="'.t('Enter group name...').'"',
- 'data-dst-field="group_id"',
- 'data-dst-extra-field="external_id"',
- 'data-search-url="'.$this->url->href('GroupAjaxController', 'autocomplete').'"',
- ),
- 'autocomplete') ?>
-
- <?= $this->form->select('role', $roles, $values, $errors) ?>
-
- <button type="submit" class="btn btn-blue"><?= t('Add') ?></button>
- </form>
- </div>
- <?php endif ?>
-
+ <?= $this->render('project_permission/users', array(
+ 'project' => $project,
+ 'roles' => $roles,
+ 'users' => $users,
+ 'errors' => $errors,
+ 'values' => $values,
+ )) ?>
+
+ <?= $this->render('project_permission/groups', array(
+ 'project' => $project,
+ 'roles' => $roles,
+ 'groups' => $groups,
+ 'errors' => $errors,
+ 'values' => $values,
+ )) ?>
<?php endif ?>
<?php if ($project['is_private'] == 0): ?>
diff --git a/app/Template/project_permission/users.php b/app/Template/project_permission/users.php
new file mode 100644
index 00000000..bc92d060
--- /dev/null
+++ b/app/Template/project_permission/users.php
@@ -0,0 +1,53 @@
+<?php if (empty($users)): ?>
+ <div class="alert"><?= t('No user have been allowed specifically.') ?></div>
+<?php else: ?>
+ <table class="table-scrolling">
+ <tr>
+ <th class="column-50"><?= t('User') ?></th>
+ <th><?= t('Role') ?></th>
+ <?php if ($project['is_private'] == 0): ?>
+ <th class="column-15"><?= t('Actions') ?></th>
+ <?php endif ?>
+ </tr>
+ <?php foreach ($users as $user): ?>
+ <tr>
+ <td><?= $this->text->e($user['name'] ?: $user['username']) ?></td>
+ <td>
+ <?= $this->app->component('project-select-role', array(
+ 'roles' => $roles,
+ 'role' => $user['role'],
+ 'id' => $user['id'],
+ 'url' => $this->url->to('ProjectPermissionController', 'changeUserRole', array('project_id' => $project['id'])),
+ )) ?>
+ </td>
+ <td>
+ <?= $this->url->icon('trash-o', t('Remove'), 'ProjectPermissionController', 'removeUser', array('project_id' => $project['id'], 'user_id' => $user['id']), true) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+<?php endif ?>
+
+<?php if ($project['is_private'] == 0): ?>
+ <div class="panel">
+ <form method="post" action="<?= $this->url->href('ProjectPermissionController', 'addUser', array('project_id' => $project['id'])) ?>" autocomplete="off" class="form-inline">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', array('project_id' => $project['id'])) ?>
+ <?= $this->form->hidden('user_id', $values) ?>
+
+ <?= $this->form->label(t('Name'), 'name') ?>
+ <?= $this->form->text('name', $values, $errors, array(
+ 'required',
+ 'placeholder="'.t('Enter user name...').'"',
+ 'title="'.t('Enter user name...').'"',
+ 'data-dst-field="user_id"',
+ 'data-search-url="'.$this->url->href('UserAjaxController', 'autocomplete').'"',
+ ),
+ 'autocomplete') ?>
+
+ <?= $this->form->select('role', $roles, $values, $errors) ?>
+
+ <button type="submit" class="btn btn-blue"><?= t('Add') ?></button>
+ </form>
+ </div>
+<?php endif ?>
diff --git a/app/Template/project_role/create.php b/app/Template/project_role/create.php
new file mode 100644
index 00000000..f554eb17
--- /dev/null
+++ b/app/Template/project_role/create.php
@@ -0,0 +1,12 @@
+<div class="page-header">
+ <h2><?= t('New custom project role') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('ProjectRoleController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+
+ <?= $this->form->label(t('Role'), 'role') ?>
+ <?= $this->form->text('role', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/project_role/edit.php b/app/Template/project_role/edit.php
new file mode 100644
index 00000000..740ac0fe
--- /dev/null
+++ b/app/Template/project_role/edit.php
@@ -0,0 +1,13 @@
+<div class="page-header">
+ <h2><?= t('Edit custom project role') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('ProjectRoleController', 'update', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+ <?= $this->form->hidden('role_id', $values) ?>
+
+ <?= $this->form->label(t('Role'), 'role') ?>
+ <?= $this->form->text('role', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/project_role/remove.php b/app/Template/project_role/remove.php
new file mode 100644
index 00000000..44d24eda
--- /dev/null
+++ b/app/Template/project_role/remove.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Remove a custom role') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.', $role['role']) ?>
+ </p>
+
+ <?= $this->modal->confirmButtons(
+ 'ProjectRoleController',
+ 'remove',
+ array('project_id' => $project['id'], 'role_id' => $role['role_id'])
+ ) ?>
+</div>
diff --git a/app/Template/project_role/show.php b/app/Template/project_role/show.php
new file mode 100644
index 00000000..5377f7bb
--- /dev/null
+++ b/app/Template/project_role/show.php
@@ -0,0 +1,93 @@
+<div class="page-header">
+ <h2><?= t('Custom Project Roles') ?></h2>
+ <ul>
+ <li>
+ <?= $this->modal->medium('plus', t('Add a new custom role'), 'ProjectRoleController', 'create', array('project_id' => $project['id'])) ?>
+ </li>
+ </ul>
+</div>
+
+<?php if (empty($roles)): ?>
+ <div class="alert"><?= t('There is no custom role for this project.') ?></div>
+<?php else: ?>
+ <?php foreach ($roles as $role): ?>
+ <table class="table-striped">
+ <tr>
+ <th>
+ <div class="dropdown">
+ <a href="#" class="dropdown-menu"><?= t('Restrictions for the role "%s"', $role['role']) ?> <i class="fa fa-caret-down"></i></a>
+ <ul>
+ <li>
+ <?= $this->modal->medium('plus', t('Add a new project restriction'), 'ProjectRoleRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('plus', t('Add a new drag and drop restriction'), 'ColumnMoveRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('plus', t('Add a new column restriction'), 'ColumnRestrictionController', 'create', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('edit', t('Edit this role'), 'ProjectRoleController', 'edit', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>
+ </li>
+ <li>
+ <?= $this->modal->confirm('trash-o', t('Remove this role'), 'ProjectRoleController', 'confirm', array('project_id' => $project['id'], 'role_id' => $role['role_id'])) ?>
+ </li>
+ </ul>
+ </div>
+ </th>
+ <th class="column-15">
+ <?= t('Actions') ?>
+ </th>
+ </tr>
+ <?php if (empty($role['project_restrictions']) && empty($role['column_restrictions']) && empty($role['column_move_restrictions'])): ?>
+ <tr>
+ <td colspan="2"><?= t('There is no restriction for this role.') ?></td>
+ </tr>
+ <?php else: ?>
+ <?php foreach ($role['project_restrictions'] as $restriction): ?>
+ <tr>
+ <td>
+ <i class="fa fa-ban fa-fw" aria-hidden="true"></i>
+ <strong><?= t('Project') ?></strong>
+ <i class="fa fa-arrow-right fa-fw" aria-hidden="true"></i>
+ <?= $this->text->e($restriction['title']) ?>
+ </td>
+ <td>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectRoleRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ <?php foreach ($role['column_restrictions'] as $restriction): ?>
+ <tr>
+ <td>
+ <?php if (strpos($restriction['rule'], 'block') === 0): ?>
+ <i class="fa fa-ban fa-fw" aria-hidden="true"></i>
+ <?php else: ?>
+ <i class="fa fa-check-circle-o fa-fw" aria-hidden="true"></i>
+ <?php endif ?>
+ <strong><?= $this->text->e($restriction['column_title']) ?></strong>
+ <i class="fa fa-arrow-right fa-fw" aria-hidden="true"></i>
+ <?= $this->text->e($restriction['title']) ?>
+ </td>
+ <td>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ <?php foreach ($role['column_move_restrictions'] as $restriction): ?>
+ <tr>
+ <td>
+ <i class="fa fa-check-circle-o fa-fw" aria-hidden="true"></i>
+ <strong><?= $this->text->e($restriction['src_column_title']) ?> / <?= $this->text->e($restriction['dst_column_title']) ?></strong>
+ <i class="fa fa-arrow-right fa-fw" aria-hidden="true"></i>
+ <?= t('Only moving task between those columns is permitted') ?>
+ </td>
+ <td>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnMoveRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ <?php endif ?>
+ </table>
+ <?php endforeach ?>
+<?php endif ?>
diff --git a/app/Template/project_role_restriction/create.php b/app/Template/project_role_restriction/create.php
new file mode 100644
index 00000000..2b6a61dc
--- /dev/null
+++ b/app/Template/project_role_restriction/create.php
@@ -0,0 +1,15 @@
+<section id="main">
+ <div class="page-header">
+ <h2><?= t('New project restriction for the role "%s"', $role['role']) ?></h2>
+ </div>
+ <form method="post" action="<?= $this->url->href('ProjectRoleRestrictionController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('project_id', $values) ?>
+ <?= $this->form->hidden('role_id', $values) ?>
+
+ <?= $this->form->label(t('Restriction'), 'rule') ?>
+ <?= $this->form->select('rule', $restrictions, $values, $errors) ?>
+
+ <?= $this->modal->submitButtons() ?>
+ </form>
+</section>
diff --git a/app/Template/project_role_restriction/remove.php b/app/Template/project_role_restriction/remove.php
new file mode 100644
index 00000000..1a994199
--- /dev/null
+++ b/app/Template/project_role_restriction/remove.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Remove a project restriction') ?></h2>
+</div>
+
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this project restriction: "%s"?', $this->text->in($restriction['rule'], $restrictions)) ?>
+ </p>
+
+ <?= $this->modal->confirmButtons(
+ 'ProjectRoleRestrictionController',
+ 'remove',
+ array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])
+ ) ?>
+</div>
diff --git a/app/Template/project_status/disable.php b/app/Template/project_status/disable.php
index d8145d3c..d607cedb 100644
--- a/app/Template/project_status/disable.php
+++ b/app/Template/project_status/disable.php
@@ -7,8 +7,9 @@
<?= t('Do you really want to disable this project: "%s"?', $project['name']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'ProjectStatusController', 'disable', array('project_id' => $project['id']), true, 'btn btn-red') ?>
- <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'ProjectStatusController',
+ 'disable',
+ array('project_id' => $project['id'])
+ ) ?>
</div>
diff --git a/app/Template/project_status/enable.php b/app/Template/project_status/enable.php
index 1f76d093..fd8f8c72 100644
--- a/app/Template/project_status/enable.php
+++ b/app/Template/project_status/enable.php
@@ -7,8 +7,9 @@
<?= t('Do you really want to enable this project: "%s"?', $project['name']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'ProjectStatusController', 'enable', array('project_id' => $project['id']), true, 'btn btn-red') ?>
- <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'ProjectStatusController',
+ 'enable',
+ array('project_id' => $project['id'])
+ ) ?>
</div>
diff --git a/app/Template/project_status/remove.php b/app/Template/project_status/remove.php
index 8959ef75..27ae2ae0 100644
--- a/app/Template/project_status/remove.php
+++ b/app/Template/project_status/remove.php
@@ -7,8 +7,9 @@
<?= t('Do you really want to remove this project: "%s"?', $project['name']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'ProjectStatusController', 'remove', array('project_id' => $project['id']), true, 'btn btn-red') ?>
- <?= t('or') ?> <?= $this->url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'ProjectStatusController',
+ 'remove',
+ array('project_id' => $project['id'])
+ ) ?>
</div>
diff --git a/app/Template/project_tag/create.php b/app/Template/project_tag/create.php
index bfd1084a..a0e6243b 100644
--- a/app/Template/project_tag/create.php
+++ b/app/Template/project_tag/create.php
@@ -1,16 +1,12 @@
<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">
+<form method="post" 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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/project_tag/edit.php b/app/Template/project_tag/edit.php
index 9bf261bd..8cb1e209 100644
--- a/app/Template/project_tag/edit.php
+++ b/app/Template/project_tag/edit.php
@@ -1,7 +1,7 @@
<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">
+<form method="post" 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) ?>
@@ -9,9 +9,5 @@
<?= $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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/project_tag/index.php b/app/Template/project_tag/index.php
index 8e8dd96c..29d7082b 100644
--- a/app/Template/project_tag/index.php
+++ b/app/Template/project_tag/index.php
@@ -2,8 +2,7 @@
<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') ?>
+ <?= $this->modal->medium('plus', t('Add new tag'), 'ProjectTagController', 'create', array('project_id' => $project['id'])) ?>
</li>
</ul>
</div>
@@ -11,7 +10,7 @@
<?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 class="table-striped table-scrolling">
<tr>
<th class="column-80"><?= t('Tag') ?></th>
<th><?= t('Action') ?></th>
@@ -20,10 +19,8 @@
<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') ?>
+ <?= $this->modal->medium('edit', t('Edit'), 'ProjectTagController', 'edit', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'ProjectTagController', 'confirm', array('tag_id' => $tag['id'], 'project_id' => $project['id'])) ?>
</td>
</tr>
<?php endforeach ?>
diff --git a/app/Template/project_tag/remove.php b/app/Template/project_tag/remove.php
index f4aadab1..9f957d10 100644
--- a/app/Template/project_tag/remove.php
+++ b/app/Template/project_tag/remove.php
@@ -7,9 +7,9 @@
<?= 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>
+ <?= $this->modal->confirmButtons(
+ 'ProjectTagController',
+ 'remove',
+ array('tag_id' => $tag['id'], 'project_id' => $project['id'])
+ ) ?>
</div>
diff --git a/app/Template/project_user_overview/layout.php b/app/Template/project_user_overview/layout.php
index 19b83436..9115ef3c 100644
--- a/app/Template/project_user_overview/layout.php
+++ b/app/Template/project_user_overview/layout.php
@@ -1,20 +1,27 @@
<section id="main">
<div class="page-header">
<ul>
+ <?php if ($this->user->hasAccess('ProjectCreationController', 'create')): ?>
+ <li>
+ <?= $this->modal->medium('plus', t('New project'), 'ProjectCreationController', 'create') ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->app->config('disable_private_project', 0) == 0): ?>
+ <li>
+ <?= $this->modal->medium('lock', t('New private project'), 'ProjectCreationController', 'createPrivate') ?>
+ </li>
+ <?php endif ?>
<li>
- <i class="fa fa-folder fa-fw"></i>
- <?= $this->url->link(t('Projects list'), 'ProjectListController', 'show') ?>
+ <?= $this->url->icon('folder', t('Projects list'), 'ProjectListController', 'show') ?>
</li>
<?php if ($this->user->hasAccess('ProjectGanttController', 'show')): ?>
<li>
- <i class="fa fa-sliders fa-fw"></i>
- <?= $this->url->link(t('Projects Gantt chart'), 'ProjectGanttController', 'show') ?>
+ <?= $this->url->icon('sliders', t('Projects Gantt chart'), 'ProjectGanttController', 'show') ?>
</li>
<?php endif ?>
</ul>
</div>
<section class="sidebar-container">
-
<?= $this->render($sidebar_template, array('users' => $users, 'filter' => $filter)) ?>
<div class="sidebar-content">
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..493f4cbf 100644
--- a/app/Template/project_user_overview/sidebar.php
+++ b/app/Template/project_user_overview/sidebar.php
@@ -1,16 +1,17 @@
<div class="sidebar">
- <h2><?= t('Actions') ?></h2>
+ <?= $this->app->component('select-dropdown-autocomplete', array(
+ 'name' => 'user_id',
+ 'items' => $users,
+ 'defaultValue' => $filter['user_id'],
+ 'sortByKeys' => true,
+ 'redirect' => array(
+ 'regex' => 'USER_ID',
+ 'url' => $this->url->to('ProjectUserOverviewController', $this->app->getRouterAction(), array('user_id' => 'USER_ID')),
+ ),
+ )) ?>
- <?= $this->form->select(
- 'user_id',
- $users,
- $filter,
- array(),
- array('data-redirect-url="'.$this->url->href('ProjectUserOverviewController', $this->app->getRouterAction(), array('user_id' => 'USER_ID')).'"', 'data-redirect-regex="USER_ID"'),
- 'chosen-select select-auto-redirect'
- ) ?>
+ <br>
- <br><br>
<ul>
<li <?= $this->app->checkMenuSelection('ProjectUserOverviewController', 'managers') ?>>
<?= $this->url->link(t('Project managers'), 'ProjectUserOverviewController', 'managers', $filter) ?>
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 d66ff591..561378d1 100644
--- a/app/Template/project_view/duplicate.php
+++ b/app/Template/project_view/duplicate.php
@@ -11,7 +11,7 @@
<?= $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) ?>
diff --git a/app/Template/project_view/share.php b/app/Template/project_view/share.php
index 409f37e6..87c63916 100644
--- a/app/Template/project_view/share.php
+++ b/app/Template/project_view/share.php
@@ -4,11 +4,11 @@
<?php if ($project['is_public']): ?>
- <div class="listing">
+ <div class="panel">
<ul class="no-bullet">
- <li><strong><i class="fa fa-share-alt"></i> <?= $this->url->link(t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?></strong></li>
- <li><strong><i class="fa fa-rss-square"></i> <?= $this->url->link(t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></strong></li>
- <li><strong><i class="fa fa-calendar"></i> <?= $this->url->link(t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token']), false, '', '', true) ?></strong></li>
+ <li><strong><?= $this->url->icon('share-alt', t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?></strong></li>
+ <li><strong><?= $this->url->icon('rss-square', t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></strong></li>
+ <li><strong><?= $this->url->icon('calendar', t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token']), false, '', '', true) ?></strong></li>
</ul>
</div>
diff --git a/app/Template/project_view/show.php b/app/Template/project_view/show.php
index 0ad8852b..29d558b1 100644
--- a/app/Template/project_view/show.php
+++ b/app/Template/project_view/show.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('Summary') ?></h2>
</div>
-<ul class="listing">
+<ul class="panel">
<li><strong><?= $project['is_active'] ? t('Active') : t('Inactive') ?></strong></li>
<?php if ($project['owner_id'] > 0): ?>
@@ -13,9 +13,9 @@
<?php endif ?>
<?php if ($project['is_public']): ?>
- <li><i class="fa fa-share-alt"></i> <?= $this->url->link(t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?></li>
- <li><i class="fa fa-rss-square"></i> <?= $this->url->link(t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></li>
- <li><i class="fa fa-calendar"></i> <?= $this->url->link(t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?></li>
+ <li><?= $this->url->icon('share-alt', t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?></li>
+ <li><?= $this->url->icon('rss-square', t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?></li>
+ <li><?= $this->url->icon('calendar', t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?></li>
<?php else: ?>
<li><?= t('Public access disabled') ?></li>
<?php endif ?>
@@ -52,12 +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-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>
+ <th class="column-20"><?= t('Hide tasks in this column in the dashboard') ?></th>
</tr>
<?php foreach ($stats['columns'] as $column): ?>
<tr>
diff --git a/app/Template/search/activity.php b/app/Template/search/activity.php
index 9abc7d7e..2582162b 100644
--- a/app/Template/search/activity.php
+++ b/app/Template/search/activity.php
@@ -2,8 +2,7 @@
<div class="page-header">
<ul>
<li>
- <i class="fa fa-search fa-fw"></i>
- <?= $this->url->link(t('Search tasks'), 'SearchController', 'index') ?>
+ <?= $this->url->icon('search', t('Search tasks'), 'SearchController', 'index') ?>
</li>
</ul>
</div>
@@ -12,13 +11,18 @@
<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>
<?php if (empty($values['search'])): ?>
- <div class="listing">
+ <div class="panel">
<h3><?= t('Advanced search') ?></h3>
<p><?= t('Example of query: ') ?><strong>project:"My project" creator:me</strong></p>
<ul>
diff --git a/app/Template/search/index.php b/app/Template/search/index.php
index bc528af7..9a5f2931 100644
--- a/app/Template/search/index.php
+++ b/app/Template/search/index.php
@@ -2,8 +2,7 @@
<div class="page-header">
<ul>
<li>
- <i class="fa fa-search fa-fw"></i>
- <?= $this->url->link(t('Activity stream search'), 'SearchController', 'activity') ?>
+ <?= $this->url->icon('search', t('Activity stream search'), 'SearchController', 'activity') ?>
</li>
</ul>
</div>
@@ -12,13 +11,18 @@
<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>
<?php if (empty($values['search'])): ?>
- <div class="listing">
+ <div class="panel">
<h3><?= t('Advanced search') ?></h3>
<p><?= t('Example of query: ') ?><strong>project:"My project" assignee:me due:tomorrow</strong></p>
<ul>
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/create.php b/app/Template/subtask/create.php
index 3c080f7c..96ad7a46 100644
--- a/app/Template/subtask/create.php
+++ b/app/Template/subtask/create.php
@@ -2,19 +2,17 @@
<h2><?= t('Add a sub-task') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('SubtaskController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('SubtaskController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', $values) ?>
- <?= $this->subtask->selectTitle($values, $errors, array('autofocus')) ?>
- <?= $this->subtask->selectAssignee($users_list, $values, $errors) ?>
- <?= $this->subtask->selectTimeEstimated($values, $errors) ?>
+ <?= $this->subtask->renderTitleField($values, $errors, array('autofocus')) ?>
+ <?= $this->subtask->renderAssigneeField($users_list, $values, $errors) ?>
+ <?= $this->subtask->renderTimeEstimatedField($values, $errors) ?>
+ <?= $this->hook->render('template:subtask:form:create', array('values' => $values, 'errors' => $errors)) ?>
+
<?= $this->form->checkbox('another_subtask', t('Create another sub-task'), 1, isset($values['another_subtask']) && $values['another_subtask'] == 1) ?>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/subtask/edit.php b/app/Template/subtask/edit.php
index 8f256cea..7c0266a8 100644
--- a/app/Template/subtask/edit.php
+++ b/app/Template/subtask/edit.php
@@ -2,19 +2,17 @@
<h2><?= t('Edit a sub-task') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('SubtaskController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off">
-
+<form method="post" action="<?= $this->url->href('SubtaskController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->hidden('task_id', $values) ?>
- <?= $this->subtask->selectTitle($values, $errors, array('autofocus')) ?>
- <?= $this->subtask->selectAssignee($users_list, $values, $errors) ?>
- <?= $this->subtask->selectTimeEstimated($values, $errors) ?>
- <?= $this->subtask->selectTimeSpent($values, $errors) ?>
- <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>
+ <?= $this->subtask->renderTitleField($values, $errors, array('autofocus')) ?>
+ <?= $this->subtask->renderAssigneeField($users_list, $values, $errors) ?>
+ <?= $this->subtask->renderTimeEstimatedField($values, $errors) ?>
+ <?= $this->subtask->renderTimeSpentField($values, $errors) ?>
+
+ <?= $this->hook->render('template:subtask:form:edit', array('values' => $values, 'errors' => $errors)) ?>
+
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/subtask/menu.php b/app/Template/subtask/menu.php
index d5d1bf85..a0743a70 100644
--- a/app/Template/subtask/menu.php
+++ b/app/Template/subtask/menu.php
@@ -2,16 +2,13 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <i class="fa fa-pencil-square-o" aria-hidden="true"></i>
- <?= $this->url->link(t('Edit'), 'SubtaskController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?>
+ <?= $this->modal->medium('edit', t('Edit'), 'SubtaskController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>
</li>
<li>
- <i class="fa fa-trash-o" aria-hidden="true"></i>
- <?= $this->url->link(t('Remove'), 'SubtaskController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'SubtaskController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>
</li>
<li>
- <i class="fa fa-clone" aria-hidden="true"></i>
- <?= $this->url->link(t('Convert to task'), 'SubtaskConverterController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('clone', t('Convert to task'), 'SubtaskConverterController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>
</li>
</ul>
</div>
diff --git a/app/Template/subtask/remove.php b/app/Template/subtask/remove.php
index 426c1a93..cf9bbc35 100644
--- a/app/Template/subtask/remove.php
+++ b/app/Template/subtask/remove.php
@@ -12,9 +12,9 @@
</ul>
</div>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'SubtaskController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'SubtaskController',
+ 'remove',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])
+ ) ?>
</div>
diff --git a/app/Template/subtask/table.php b/app/Template/subtask/table.php
index 4c6484ef..156e08c1 100644
--- a/app/Template/subtask/table.php
+++ b/app/Template/subtask/table.php
@@ -1,12 +1,13 @@
<?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>
<tr>
<th class="column-40"><?= t('Title') ?></th>
<th><?= t('Assignee') ?></th>
+ <?= $this->hook->render('template:subtask:table:header:before-timetracking') ?>
<th><?= t('Time tracking') ?></th>
<?php if ($editable): ?>
<th class="column-5"></th>
@@ -29,6 +30,7 @@
<?= $this->text->e($subtask['name'] ?: $subtask['username']) ?>
<?php endif ?>
</td>
+ <?= $this->hook->render('template:subtask:table:rows', array('subtask' => $subtask)) ?>
<td>
<ul class="no-bullet">
<li>
@@ -43,12 +45,10 @@
<?php if ($editable && $subtask['user_id'] == $this->user->getId()): ?>
<li>
<?php if ($subtask['is_timer_started']): ?>
- <i class="fa fa-pause"></i>
- <?= $this->url->link(t('Stop timer'), 'SubtaskStatusController', 'timer', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'subtask-toggle-timer') ?>
+ <?= $this->url->icon('pause', t('Stop timer'), 'SubtaskStatusController', 'timer', array('timer' => 'stop', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'subtask-toggle-timer') ?>
(<?= $this->dt->age($subtask['timer_start_date']) ?>)
<?php else: ?>
- <i class="fa fa-play-circle-o"></i>
- <?= $this->url->link(t('Start timer'), 'SubtaskStatusController', 'timer', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'subtask-toggle-timer') ?>
+ <?= $this->url->icon('play-circle-o', t('Start timer'), 'SubtaskStatusController', 'timer', array('timer' => 'start', 'project_id' => $task['project_id'], 'task_id' => $subtask['task_id'], 'subtask_id' => $subtask['id']), false, 'subtask-toggle-timer') ?>
<?php endif ?>
</li>
<?php endif ?>
diff --git a/app/Template/subtask_converter/show.php b/app/Template/subtask_converter/show.php
index 63f45482..9ecc70c8 100644
--- a/app/Template/subtask_converter/show.php
+++ b/app/Template/subtask_converter/show.php
@@ -12,9 +12,9 @@
</ul>
</div>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'SubtaskConverterController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'SubtaskConverterController',
+ 'save',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])
+ ) ?>
</div>
diff --git a/app/Template/subtask_restriction/show.php b/app/Template/subtask_restriction/show.php
index ec8b8d5b..b6c56a12 100644
--- a/app/Template/subtask_restriction/show.php
+++ b/app/Template/subtask_restriction/show.php
@@ -1,7 +1,7 @@
<div class="page-header">
<h2><?= t('You already have one subtask in progress') ?></h2>
</div>
-<form class="popover-form" action="<?= $this->url->href('SubtaskRestrictionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" method="post">
+<form action="<?= $this->url->href('SubtaskRestrictionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'subtask_id' => $subtask['id'])) ?>" method="post">
<?= $this->form->csrf() ?>
@@ -9,9 +9,5 @@
<?= $this->form->radios('status', $status_list) ?>
<?= $this->form->hidden('id', $subtask_inprogress) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-red"><?= 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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/swimlane/create.php b/app/Template/swimlane/create.php
index f5aa1591..7d05e731 100644
--- a/app/Template/swimlane/create.php
+++ b/app/Template/swimlane/create.php
@@ -1,20 +1,16 @@
<div class="page-header">
<h2><?= t('Add a new swimlane') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('SwimlaneController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('SwimlaneController', '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="50"')) ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textarea('description', $values, $errors, array(), 'markdown-editor') ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/swimlane/edit.php b/app/Template/swimlane/edit.php
index b10cdd52..c1c41196 100644
--- a/app/Template/swimlane/edit.php
+++ b/app/Template/swimlane/edit.php
@@ -2,7 +2,7 @@
<h2><?= t('Swimlane modification for the project "%s"', $project['name']) ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('SwimlaneController', 'update', array('project_id' => $project['id'], 'swimlane_id' => $values['id'])) ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('SwimlaneController', 'update', array('project_id' => $project['id'], 'swimlane_id' => $values['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
@@ -10,14 +10,10 @@
<?= $this->form->hidden('project_id', $values) ?>
<?= $this->form->label(t('Name'), 'name') ?>
- <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
+ <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="50"', 'tabindex="1"')) ?>
<?= $this->form->label(t('Description'), 'description') ?>
- <?= $this->form->textarea('description', $values, $errors, array(), 'markdown-editor') ?>
+ <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/swimlane/edit_default.php b/app/Template/swimlane/edit_default.php
index f271c513..a2c3ee73 100644
--- a/app/Template/swimlane/edit_default.php
+++ b/app/Template/swimlane/edit_default.php
@@ -1,18 +1,14 @@
<div class="page-header">
<h2><?= t('Change default swimlane') ?></h2>
</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('SwimlaneController', 'updateDefault', array('project_id' => $project['id'])) ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('SwimlaneController', 'updateDefault', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->label(t('Name'), 'default_swimlane') ?>
- <?= $this->form->text('default_swimlane', $values, $errors, array('required', 'maxlength="50"')) ?>
+ <?= $this->form->text('default_swimlane', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
<?= $this->form->checkbox('show_default_swimlane', t('Show default swimlane'), 1, $values['show_default_swimlane'] == 1) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/swimlane/index.php b/app/Template/swimlane/index.php
index 4f78a405..e05b9088 100644
--- a/app/Template/swimlane/index.php
+++ b/app/Template/swimlane/index.php
@@ -2,8 +2,7 @@
<h2><?= t('Swimlanes') ?></h2>
<ul>
<li>
- <i class="fa fa-plus fa-fw"></i>
- <?= $this->url->link(t('Add a new swimlane'), 'SwimlaneController', 'create', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->medium('plus', t('Add a new swimlane'), 'SwimlaneController', 'create', array('project_id' => $project['id'])) ?>
</li>
</ul>
</div>
diff --git a/app/Template/swimlane/remove.php b/app/Template/swimlane/remove.php
index f16b778c..02d1e322 100644
--- a/app/Template/swimlane/remove.php
+++ b/app/Template/swimlane/remove.php
@@ -1,17 +1,15 @@
-<section id="main">
- <div class="page-header">
- <h2><?= t('Remove a swimlane') ?></h2>
- </div>
+<div class="page-header">
+ <h2><?= t('Remove a swimlane') ?></h2>
+</div>
- <div class="confirm">
- <p class="alert alert-info">
- <?= t('Do you really want to remove this swimlane: "%s"?', $swimlane['name']) ?>
- </p>
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this swimlane: "%s"?', $swimlane['name']) ?>
+ </p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'SwimlaneController', 'remove', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'SwimlaneController', 'index', array('project_id' => $project['id']), false, 'close-popover') ?>
- </div>
- </div>
-</section>
+ <?= $this->modal->confirmButtons(
+ 'SwimlaneController',
+ 'remove',
+ array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])
+ ) ?>
+</div>
diff --git a/app/Template/swimlane/table.php b/app/Template/swimlane/table.php
index be123b08..2d783a00 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>
@@ -20,13 +20,13 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <?= $this->url->link(t('Edit'), 'SwimlaneController', 'editDefault', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->medium('edit', t('Edit'), 'SwimlaneController', 'editDefault', array('project_id' => $project['id'])) ?>
</li>
<li>
<?php if ($default_swimlane['show_default_swimlane'] == 1): ?>
- <?= $this->url->link(t('Disable'), 'SwimlaneController', 'disableDefault', array('project_id' => $project['id']), true) ?>
+ <?= $this->url->icon('toggle-off', t('Disable'), 'SwimlaneController', 'disableDefault', array('project_id' => $project['id']), true) ?>
<?php else: ?>
- <?= $this->url->link(t('Enable'), 'SwimlaneController', 'enableDefault', array('project_id' => $project['id']), true) ?>
+ <?= $this->url->icon('toggle-on', t('Enable'), 'SwimlaneController', 'enableDefault', array('project_id' => $project['id']), true) ?>
<?php endif ?>
</li>
</ul>
@@ -55,17 +55,17 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <?= $this->url->link(t('Edit'), 'SwimlaneController', 'edit', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?>
+ <?= $this->modal->medium('edit', t('Edit'), 'SwimlaneController', 'edit', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?>
</li>
<li>
<?php if ($swimlane['is_active']): ?>
- <?= $this->url->link(t('Disable'), 'SwimlaneController', 'disable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
+ <?= $this->url->icon('toggle-off', t('Disable'), 'SwimlaneController', 'disable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php else: ?>
- <?= $this->url->link(t('Enable'), 'SwimlaneController', 'enable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
+ <?= $this->url->icon('toggle-on', t('Enable'), 'SwimlaneController', 'enable', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), true) ?>
<?php endif ?>
</li>
<li>
- <?= $this->url->link(t('Remove'), 'SwimlaneController', 'confirm', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'SwimlaneController', 'confirm', array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id'])) ?>
</li>
</ul>
</div>
diff --git a/app/Template/tag/create.php b/app/Template/tag/create.php
index 9b32bc46..752a63e5 100644
--- a/app/Template/tag/create.php
+++ b/app/Template/tag/create.php
@@ -1,16 +1,12 @@
<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">
+<form method="post" 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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/tag/edit.php b/app/Template/tag/edit.php
index f751ff49..adef3568 100644
--- a/app/Template/tag/edit.php
+++ b/app/Template/tag/edit.php
@@ -1,7 +1,7 @@
<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">
+<form method="post" 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) ?>
@@ -9,9 +9,5 @@
<?= $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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/tag/index.php b/app/Template/tag/index.php
index 2a495eb3..834e8e7c 100644
--- a/app/Template/tag/index.php
+++ b/app/Template/tag/index.php
@@ -2,8 +2,7 @@
<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') ?>
+ <?= $this->modal->medium('plus', t('Add new tag'), 'TagController', 'create') ?>
</li>
</ul>
</div>
@@ -11,7 +10,7 @@
<?php if (empty($tags)): ?>
<p class="alert"><?= t('There is no global tag at the moment.') ?></p>
<?php else: ?>
- <table class="table-striped">
+ <table class="table-striped table-scrolling">
<tr>
<th class="column-80"><?= t('Tag') ?></th>
<th><?= t('Action') ?></th>
@@ -20,10 +19,8 @@
<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') ?>
+ <?= $this->modal->medium('edit', t('Edit'), 'TagController', 'edit', array('tag_id' => $tag['id'])) ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'TagController', 'confirm', array('tag_id' => $tag['id'])) ?>
</td>
</tr>
<?php endforeach ?>
diff --git a/app/Template/tag/remove.php b/app/Template/tag/remove.php
index 46ea3f99..47ba8d3d 100644
--- a/app/Template/tag/remove.php
+++ b/app/Template/tag/remove.php
@@ -7,9 +7,9 @@
<?= 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>
+ <?= $this->modal->confirmButtons(
+ 'TagController',
+ 'remove',
+ array('tag_id' => $tag['id'])
+ ) ?>
</div>
diff --git a/app/Template/task/analytics.php b/app/Template/task/analytics.php
index db2d0cef..2d968188 100644
--- a/app/Template/task/analytics.php
+++ b/app/Template/task/analytics.php
@@ -1,20 +1,29 @@
-<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>
-<div class="listing">
+<div class="panel">
<ul>
<li><?= t('Lead time: ').'<strong>'.$this->dt->duration($lead_time) ?></strong></li>
<li><?= t('Cycle time: ').'<strong>'.$this->dt->duration($cycle_time) ?></strong></li>
</ul>
</div>
-<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">
+<h3><?= t('Time spent into each column') ?></h3>
+
+<?= $this->app->component('chart-task-time-column', array(
+ 'metrics' => $time_spent_columns,
+ 'label' => t('Time spent'),
+)) ?>
+
+<table class="table-striped">
<tr>
<th><?= t('Column') ?></th>
<th><?= t('Time spent') ?></th>
diff --git a/app/Template/task/changes.php b/app/Template/task/changes.php
index 9d36f09f..2c2bf267 100644
--- a/app/Template/task/changes.php
+++ b/app/Template/task/changes.php
@@ -69,6 +69,10 @@
<?php if (! empty($changes['description'])): ?>
<p><strong><?= t('The description has been modified:') ?></strong></p>
- <div class="markdown"><?= $this->text->markdown($task['description']) ?></div>
+ <?php if (isset($public)): ?>
+ <div class="markdown"><?= $this->text->markdown($task['description'], true) ?></div>
+ <?php else: ?>
+ <div class="markdown"><?= $this->text->markdown($task['description']) ?></div>
+ <?php endif ?>
<?php endif ?>
<?php endif ?> \ No newline at end of file
diff --git a/app/Template/task/details.php b/app/Template/task/details.php
index 695957f9..24099a3c 100644
--- a/app/Template/task/details.php
+++ b/app/Template/task/details.php
@@ -31,18 +31,19 @@
</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>
+ <small>
+ <?= $this->url->icon('external-link', t('Public link'), 'TaskViewController', 'readonly', array('task_id' => $task['id'], 'token' => $project['token']), false, '', '', true) ?>
+ </small>
</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>
+ <small>
+ <?= $this->url->icon('th', t('Back to the board'), 'BoardViewController', 'readonly', array('token' => $project['token'])) ?>
+ </small>
</li>
<?php endif ?>
- <li class="smaller">
<?= $this->hook->render('template:task:details:first-column', array('task' => $task)) ?>
</ul>
@@ -157,9 +158,15 @@
<?php endif ?>
</div>
+ <?php if (! empty($task['external_uri']) && ! empty($task['external_provider'])): ?>
+ <?= $this->app->component('external-task-view', array(
+ 'url' => $this->url->href('ExternalTaskViewController', 'show', array('project_id' => $task['project_id'], 'task_id' => $task['id'])),
+ )) ?>
+ <?php endif ?>
+
<?php if ($editable && empty($task['date_started'])): ?>
- <div class="task-summary-buttons">
- <?= $this->url->button('fa-play', t('Set start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <div class="buttons-header">
+ <?= $this->url->button('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 95c7a88c..1daf8896 100644
--- a/app/Template/task/dropdown.php
+++ b/app/Template/task/dropdown.php
@@ -1,62 +1,49 @@
<div class="dropdown">
- <a href="#" class="dropdown-menu">#<?= $task['id'] ?></a>
+ <a href="#" class="dropdown-menu">#<?= $task['id'] ?> <i class="fa fa-caret-down"></i></a>
<ul>
<?php if (array_key_exists('date_started', $task) && empty($task['date_started'])): ?>
<li>
- <i class="fa fa-play fa-fw"></i>
- <?= $this->url->link(t('Set automatically the start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <?= $this->url->icon('play', t('Set automatically the start date'), 'TaskModificationController', 'start', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<?php endif ?>
<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') ?>
+ <?= $this->modal->large('edit', t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</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') ?>
+ <?= $this->modal->medium('plus', t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-code-fork fa-fw"></i>
- <?= $this->url->link(t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->medium('code-fork', t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-external-link fa-fw"></i>
- <?= $this->url->link(t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->medium('external-link', t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-comment-o fa-fw"></i>
- <?= $this->url->link(t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('comment-o', t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-camera fa-fw"></i>
- <?= $this->url->link(t('Add a screenshot'), 'TaskPopoverController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->medium('camera', t('Add a screenshot'), 'TaskPopoverController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-files-o fa-fw"></i>
- <?= $this->url->link(t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('files-o', t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-clipboard fa-fw"></i>
- <?= $this->url->link(t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('clipboard', t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-clone fa-fw"></i>
- <?= $this->url->link(t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('clone', t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
- <?php if ($this->user->canRemoveTask($task)): ?>
+ <?php if ($this->projectRole->canRemoveTask($task)): ?>
<li>
- <i class="fa fa-trash-o fa-fw"></i>
- <?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<?php endif ?>
- <?php if (isset($task['is_active'])): ?>
+ <?php if (isset($task['is_active']) && $this->projectRole->canChangeTaskStatusInColumn($task['project_id'], $task['column_id'])): ?>
<li>
<?php if ($task['is_active'] == 1): ?>
- <i class="fa fa-times fa-fw"></i>
- <?= $this->url->link(t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->confirm('times', t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
<?php else: ?>
- <i class="fa fa-check-square-o fa-fw"></i>
- <?= $this->url->link(t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->confirm('check-square-o', t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
<?php endif ?>
</li>
<?php endif ?>
diff --git a/app/Template/task/show.php b/app/Template/task/show.php
index 80786715..a5c2d5a7 100644
--- a/app/Template/task/show.php
+++ b/app/Template/task/show.php
@@ -7,47 +7,59 @@
'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 b44e6f0b..f8f1ebde 100644
--- a/app/Template/task/sidebar.php
+++ b/app/Template/task/sidebar.php
@@ -1,26 +1,23 @@
<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>
- <?= $this->url->link(t('Summary'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <?= $this->url->icon('newspaper-o', t('Summary'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('ActivityController', 'task') ?>>
- <i class="fa fa-dashboard fa-fw"></i>
- <?= $this->url->link(t('Activity stream'), 'ActivityController', 'task', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <?= $this->url->icon('dashboard', t('Activity stream'), 'ActivityController', 'task', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('TaskViewController', 'transitions') ?>>
- <i class="fa fa-arrows-h fa-fw"></i>
- <?= $this->url->link(t('Transitions'), 'TaskViewController', 'transitions', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <?= $this->url->icon('arrows-h', t('Transitions'), 'TaskViewController', 'transitions', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li <?= $this->app->checkMenuSelection('TaskViewController', 'analytics') ?>>
- <i class="fa fa-bar-chart fa-fw"></i>
- <?= $this->url->link(t('Analytics'), 'TaskViewController', 'analytics', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <?= $this->url->icon('bar-chart', t('Analytics'), 'TaskViewController', 'analytics', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<?php if ($task['time_estimated'] > 0 || $task['time_spent'] > 0): ?>
<li <?= $this->app->checkMenuSelection('TaskViewController', 'timetracking') ?>>
- <i class="fa fa-clock-o fa-fw"></i>
- <?= $this->url->link(t('Time tracking'), 'TaskViewController', 'timetracking', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ <?= $this->url->icon('clock-o', t('Time tracking'), 'TaskViewController', 'timetracking', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<?php endif ?>
@@ -28,65 +25,62 @@
</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') ?>
+ <?= $this->modal->large('edit', t('Edit the task'), 'TaskModificationController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</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') ?>
+ <?= $this->modal->medium('refresh fa-rotate-90', t('Edit recurrence'), 'TaskRecurrenceController', 'edit', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</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') ?>
+ <?= $this->modal->medium('plus', t('Add a sub-task'), 'SubtaskController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-code-fork fa-fw"></i>
- <?= $this->url->link(t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->medium('code-fork', t('Add internal link'), 'TaskInternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-external-link fa-fw"></i>
- <?= $this->url->link(t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->medium('external-link', t('Add external link'), 'TaskExternalLinkController', 'find', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-comment-o fa-fw"></i>
- <?= $this->url->link(t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('comment-o', t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-file fa-fw"></i>
- <?= $this->url->link(t('Attach a document'), 'TaskFileController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->medium('file', t('Attach a document'), 'TaskFileController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-camera fa-fw"></i>
- <?= $this->url->link(t('Add a screenshot'), 'TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->medium('camera', t('Add a screenshot'), 'TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-files-o fa-fw"></i>
- <?= $this->url->link(t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('files-o', t('Duplicate'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-clipboard fa-fw"></i>
- <?= $this->url->link(t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('clipboard', t('Duplicate to another project'), 'TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
<li>
- <i class="fa fa-clone fa-fw"></i>
- <?= $this->url->link(t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <?= $this->modal->small('clone', t('Move to another project'), 'TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</li>
- <li>
+ <?php if ($task['is_active'] == 1 && $this->projectRole->isSortableColumn($task['project_id'], $task['column_id'])): ?>
+ <li>
+ <?= $this->modal->small('arrows', t('Move position'), 'TaskMovePositionController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
+ <?php endif ?>
+ <?php if ($this->projectRole->canChangeTaskStatusInColumn($task['project_id'], $task['column_id'])): ?>
<?php if ($task['is_active'] == 1): ?>
- <i class="fa fa-times fa-fw"></i>
- <?= $this->url->link(t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <li>
+ <?= $this->modal->confirm('times', t('Close this task'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
<?php else: ?>
- <i class="fa fa-check-square-o fa-fw"></i>
- <?= $this->url->link(t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?>
+ <li>
+ <?= $this->modal->confirm('check-square-o', t('Open this task'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
<?php endif ?>
- </li>
- <?php if ($this->user->canRemoveTask($task)): ?>
+ <?php endif ?>
+ <?php if ($this->projectRole->canRemoveTask($task)): ?>
<li>
- <i class="fa fa-trash-o fa-fw"></i>
- <?= $this->url->link(t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board'), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskSuppressionController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => 'board')) ?>
</li>
<?php endif ?>
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/time_tracking_summary.php b/app/Template/task/time_tracking_summary.php
index 9886ccfa..63de733a 100644
--- a/app/Template/task/time_tracking_summary.php
+++ b/app/Template/task/time_tracking_summary.php
@@ -1,13 +1,13 @@
<?php if ($task['time_estimated'] > 0 || $task['time_spent'] > 0): ?>
+ <div class="page-header">
+ <h2><?= t('Time tracking') ?></h2>
+ </div>
-<div class="page-header">
- <h2><?= t('Time tracking') ?></h2>
-</div>
-
-<ul class="listing">
- <li><?= t('Estimate:') ?> <strong><?= $this->text->e($task['time_estimated']) ?></strong> <?= t('hours') ?></li>
- <li><?= t('Spent:') ?> <strong><?= $this->text->e($task['time_spent']) ?></strong> <?= t('hours') ?></li>
- <li><?= t('Remaining:') ?> <strong><?= $this->text->e($task['time_estimated'] - $task['time_spent']) ?></strong> <?= t('hours') ?></li>
-</ul>
-
+ <div class="panel">
+ <ul>
+ <li><?= t('Estimate:') ?> <strong><?= $this->text->e($task['time_estimated']) ?></strong> <?= t('hours') ?></li>
+ <li><?= t('Spent:') ?> <strong><?= $this->text->e($task['time_spent']) ?></strong> <?= t('hours') ?></li>
+ <li><?= t('Remaining:') ?> <strong><?= $this->text->e($task['time_estimated'] - $task['time_spent']) ?></strong> <?= t('hours') ?></li>
+ </ul>
+ </div>
<?php endif ?> \ No newline at end of file
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 e9b138d5..acf80d8c 100644
--- a/app/Template/task_bulk/show.php
+++ b/app/Template/task_bulk/show.php
@@ -1,24 +1,21 @@
<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">
+<form method="post" action="<?= $this->url->href('TaskBulkController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('column_id', $values) ?>
<?= $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) ?>
+ <?= $this->task->renderColorField($values) ?>
+ <?= $this->task->renderAssigneeField($users_list, $values, $errors) ?>
+ <?= $this->task->renderCategoryField($categories_list, $values, $errors) ?>
<?= $this->form->label(t('Tasks'), 'tasks') ?>
<?= $this->form->textarea('tasks', $values, $errors, array('placeholder="'.t('My task title').'"')) ?>
<p class="form-help"><?= t('Enter one task by line.') ?></p>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/task_creation/duplicate_projects.php b/app/Template/task_creation/duplicate_projects.php
new file mode 100644
index 00000000..202b3ffe
--- /dev/null
+++ b/app/Template/task_creation/duplicate_projects.php
@@ -0,0 +1,25 @@
+<div class="page-header">
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= $this->text->e($task['title']) ?></h2>
+</div>
+
+<?php if (empty($projects_list)): ?>
+ <p class="alert"><?= t('There is no destination project available.') ?></p>
+ <div class="form-actions">
+ <?= $this->url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $task['project_id']), false, 'js-modal-close btn') ?>
+ </div>
+<?php else: ?>
+ <form method="post" action="<?= $this->url->href('TaskCreationController', 'duplicateProjects', array('project_id' => $task['project_id'])) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+ <?= $this->form->hidden('task_id', $values) ?>
+
+ <?= $this->form->select(
+ 'project_ids[]',
+ $projects_list,
+ $values,
+ array(),
+ array('multiple')
+ ) ?>
+
+ <?= $this->modal->submitButtons() ?>
+ </form>
+<?php endif ?>
diff --git a/app/Template/task_creation/show.php b/app/Template/task_creation/show.php
index 57e77f37..e957087f 100644
--- a/app/Template/task_creation/show.php
+++ b/app/Template/task_creation/show.php
@@ -1,49 +1,48 @@
<div class="page-header">
- <h2><?= t('New task') ?></h2>
+ <h2><?= $this->text->e($project['name']) ?> &gt; <?= t('New task') ?><?= $this->task->getNewTaskDropdown($project['id'], $values['swimlane_id'], $values['column_id']) ?></h2>
</div>
-
-<form class="popover-form" method="post" action="<?= $this->url->href('TaskCreationController', 'save', array('project_id' => $values['project_id'])) ?>" autocomplete="off">
+<form method="post" action="<?= $this->url->href('TaskCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
+ <?= $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) ?>
-
- <?php if (! isset($duplicate)): ?>
- <?= $this->form->checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?>
- <?php endif ?>
+ <div class="task-form-container">
+ <div class="task-form-main-column">
+ <?= $this->task->renderTitleField($values, $errors) ?>
+ <?= $this->task->renderDescriptionField($values, $errors) ?>
+ <?= $this->task->renderTagField($project) ?>
<?= $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) ?>
+ <div class="task-form-secondary-column">
+ <?= $this->task->renderColorField($values) ?>
+ <?= $this->task->renderAssigneeField($users_list, $values, $errors) ?>
+ <?= $this->task->renderCategoryField($categories_list, $values, $errors) ?>
+ <?= $this->task->renderSwimlaneField($swimlanes_list, $values, $errors) ?>
+ <?= $this->task->renderColumnField($columns_list, $values, $errors) ?>
+ <?= $this->task->renderPriorityField($project, $values) ?>
<?= $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="task-form-secondary-column">
+ <?= $this->task->renderDueDateField($values, $errors) ?>
+ <?= $this->task->renderStartDateField($values, $errors) ?>
+ <?= $this->task->renderTimeEstimatedField($values, $errors) ?>
+ <?= $this->task->renderTimeSpentField($values, $errors) ?>
+ <?= $this->task->renderScoreField($values, $errors) ?>
+ <?= $this->task->renderReferenceField($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'), 'BoardViewController', 'show', array('project_id' => $values['project_id']), false, 'close-popover') ?>
+ <div class="task-form-bottom">
+ <?php if (! isset($duplicate)): ?>
+ <?= $this->form->checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?>
+ <?= $this->form->checkbox('duplicate_multiple_projects', t('Duplicate to multiple projects'), 1) ?>
+ <?php endif ?>
+
+ <?= $this->modal->submitButtons() ?>
+ </div>
</div>
</form>
diff --git a/app/Template/task_duplication/copy.php b/app/Template/task_duplication/copy.php
index 58b4d837..d96960fc 100644
--- a/app/Template/task_duplication/copy.php
+++ b/app/Template/task_duplication/copy.php
@@ -4,23 +4,26 @@
<?php if (empty($projects_list)): ?>
<p class="alert"><?= t('There is no destination project available.') ?></p>
+ <div class="form-actions">
+ <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'js-modal-close btn') ?>
+ </div>
<?php else: ?>
- <form class="popover-form" method="post" action="<?= $this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
-
+ <form method="post" action="<?= $this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->label(t('Project'), 'project_id') ?>
- <?= $this->form->select(
- 'project_id',
- $projects_list,
- $values,
- array(),
- array('data-redirect="'.$this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')).'"'),
- 'task-reload-project-destination'
- ) ?>
- <span class="loading-icon" style="display: none">&nbsp;<i class="fa fa-spinner fa-spin"></i></span>
+ <?= $this->app->component('select-dropdown-autocomplete', array(
+ 'name' => 'project_id',
+ 'items' => $projects_list,
+ 'defaultValue' => isset($values['project_id']) ? $values['project_id'] : null,
+ 'placeholder' => t('Choose a project'),
+ 'replace' => array(
+ 'regex' => 'PROJECT_ID',
+ 'url' => $this->url->href('TaskDuplicationController', 'copy', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')),
+ )
+ )) ?>
<?= $this->form->label(t('Swimlane'), 'swimlane_id') ?>
<?= $this->form->select('swimlane_id', $swimlanes_list, $values) ?>
@@ -38,11 +41,6 @@
<?= $this->form->select('owner_id', $users_list, $values) ?>
<p class="form-help"><?= t('Current assignee: %s', ($task['assignee_name'] ?: $task['assignee_username']) ?: e('not assigned')) ?></p>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
-
<?php endif ?>
diff --git a/app/Template/task_duplication/duplicate.php b/app/Template/task_duplication/duplicate.php
index c0baf94e..aa02b78c 100644
--- a/app/Template/task_duplication/duplicate.php
+++ b/app/Template/task_duplication/duplicate.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to duplicate this task?') ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TaskDuplicationController', 'duplicate', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TaskDuplicationController',
+ 'duplicate',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes')
+ ) ?>
</div>
diff --git a/app/Template/task_duplication/move.php b/app/Template/task_duplication/move.php
index 8f01c4b9..16ce2464 100644
--- a/app/Template/task_duplication/move.php
+++ b/app/Template/task_duplication/move.php
@@ -4,23 +4,27 @@
<?php if (empty($projects_list)): ?>
<p class="alert"><?= t('There is no destination project available.') ?></p>
+ <div class="form-actions">
+ <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'js-modal-close btn') ?>
+ </div>
<?php else: ?>
- <form class="popover-form" method="post" action="<?= $this->url->href('TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
+ <form method="post" action="<?= $this->url->href('TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->label(t('Project'), 'project_id') ?>
- <?= $this->form->select(
- 'project_id',
- $projects_list,
- $values,
- array(),
- array('data-redirect="'.$this->url->href('TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')).'"'),
- 'task-reload-project-destination'
- ) ?>
- <span class="loading-icon" style="display: none">&nbsp;<i class="fa fa-spinner fa-spin"></i></span>
+ <?= $this->app->component('select-dropdown-autocomplete', array(
+ 'name' => 'project_id',
+ 'items' => $projects_list,
+ 'defaultValue' => isset($values['project_id']) ? $values['project_id'] : null,
+ 'placeholder' => t('Choose a project'),
+ 'replace' => array(
+ 'regex' => 'PROJECT_ID',
+ 'url' => $this->url->href('TaskDuplicationController', 'move', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'dst_project_id' => 'PROJECT_ID')),
+ )
+ )) ?>
<?= $this->form->label(t('Swimlane'), 'swimlane_id') ?>
<?= $this->form->select('swimlane_id', $swimlanes_list, $values) ?>
@@ -38,11 +42,7 @@
<?= $this->form->select('owner_id', $users_list, $values) ?>
<p class="form-help"><?= t('Current assignee: %s', ($task['assignee_name'] ?: $task['assignee_username']) ?: e('not assigned')) ?></p>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
<?php endif ?>
diff --git a/app/Template/task_external_link/create.php b/app/Template/task_external_link/create.php
index beddfc90..04d9bed0 100644
--- a/app/Template/task_external_link/create.php
+++ b/app/Template/task_external_link/create.php
@@ -2,12 +2,7 @@
<h2><?= t('Add a new external link') ?></h2>
</div>
-<form class="popover-form" action="<?= $this->url->href('TaskExternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
+<form action="<?= $this->url->href('TaskExternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
<?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskExternalLinkController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/task_external_link/edit.php b/app/Template/task_external_link/edit.php
index 917a28b9..df10d444 100644
--- a/app/Template/task_external_link/edit.php
+++ b/app/Template/task_external_link/edit.php
@@ -2,12 +2,7 @@
<h2><?= t('Edit external link') ?></h2>
</div>
-<form class="popover-form" action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
+<form action="<?= $this->url->href('TaskExternalLinkController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
<?= $this->render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskExternalLinkController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/task_external_link/find.php b/app/Template/task_external_link/find.php
index a88b29ce..a3665c0d 100644
--- a/app/Template/task_external_link/find.php
+++ b/app/Template/task_external_link/find.php
@@ -2,7 +2,7 @@
<h2><?= t('Add a new external link') ?></h2>
</div>
-<form class="popover-form" action="<?= $this->url->href('TaskExternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
+<form action="<?= $this->url->href('TaskExternalLinkController', 'create', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
@@ -20,9 +20,5 @@
<?= $this->form->label(t('Link type'), 'type') ?>
<?= $this->form->select('type', $types, $values) ?>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Next') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/task_external_link/remove.php b/app/Template/task_external_link/remove.php
index 2a888a60..ed8ad15f 100644
--- a/app/Template/task_external_link/remove.php
+++ b/app/Template/task_external_link/remove.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to remove this link: "%s"?', $link['title']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TaskExternalLinkController', 'remove', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskExternalLinkController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TaskExternalLinkController',
+ 'remove',
+ array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])
+ ) ?>
</div>
diff --git a/app/Template/task_external_link/table.php b/app/Template/task_external_link/table.php
index 56ef0363..aaa234bb 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>
@@ -32,8 +32,12 @@
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
- <li><?= $this->url->link(t('Edit'), 'TaskExternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
- <li><?= $this->url->link(t('Remove'), 'TaskExternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
+ <li>
+ <?= $this->modal->medium('edit', t('Edit'), 'TaskExternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
+ <li>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskExternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
</ul>
</div>
</td>
diff --git a/app/Template/task_file/create.php b/app/Template/task_file/create.php
index e05cf829..eebb08eb 100644
--- a/app/Template/task_file/create.php
+++ b/app/Template/task_file/create.php
@@ -1,33 +1,20 @@
<div class="page-header">
<h2><?= t('Attach a document') ?></h2>
</div>
-<div id="file-done" style="display:none">
- <p class="alert alert-success">
- <?= t('All files have been uploaded successfully.') ?>
- <?= $this->url->link(t('View uploaded files'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
- </p>
-</div>
-
-<div id="file-error-max-size" style="display:none">
- <p class="alert alert-error">
- <?= t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)) ?>
- <a href="#" id="file-browser"><?= t('Choose files again') ?></a>
- </p>
-</div>
-<div
- id="file-dropzone"
- data-max-size="<?= $max_size ?>"
- data-url="<?= $this->url->href('TaskFileController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>">
- <div id="file-dropzone-inner">
- <?= t('Drag and drop your files here') ?> <?= t('or') ?> <a href="#" id="file-browser"><?= t('choose files') ?></a>
- </div>
-</div>
-
-<input type="file" name="files[]" multiple style="display:none" id="file-form-element">
+<?= $this->app->component('file-upload', array(
+ 'maxSize' => $max_size,
+ 'url' => $this->url->to('TaskFileController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
+ 'labelDropzone' => t('Drag and drop your files here'),
+ 'labelOr' => t('or'),
+ 'labelChooseFiles' => t('choose files'),
+ 'labelOversize' => t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)),
+ 'labelSuccess' => t('All files have been uploaded successfully.'),
+ 'labelCloseSuccess' => t('Close this window'),
+ 'labelUploadError' => t('Unable to upload this file.'),
+)) ?>
-<div class="form-actions">
- <input type="submit" value="<?= t('Upload files') ?>" class="btn btn-blue" id="file-upload-button" disabled>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
-</div>
+<?= $this->modal->submitButtons(array(
+ 'submitLabel' => t('Upload files'),
+ 'disabled' => true,
+)) ?>
diff --git a/app/Template/task_file/files.php b/app/Template/task_file/files.php
index 7ca59b1c..28633dc4 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>
@@ -15,18 +15,20 @@
<ul>
<?php if ($this->file->getPreviewType($file['name']) !== null): ?>
<li>
+ <?= $this->modal->large('eye', t('View file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
+ </li>
+ <?php elseif ($this->file->getBrowserViewType($file['name']) !== null): ?>
+ <li>
<i class="fa fa-eye fa-fw"></i>
- <?= $this->url->link(t('View file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?>
+ <?= $this->url->link(t('View file'), 'FileViewerController', 'browser', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, '', '', true) ?>
</li>
<?php endif ?>
<li>
- <i class="fa fa-download fa-fw"></i>
- <?= $this->url->link(t('Download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
+ <?= $this->url->icon('download', t('Download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
</li>
<?php if ($this->user->hasProjectAccess('TaskFileController', 'remove', $task['project_id'])): ?>
<li>
- <i class="fa fa-trash fa-fw"></i>
- <?= $this->url->link(t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/task_file/images.php b/app/Template/task_file/images.php
index 81c33151..2143455d 100644
--- a/app/Template/task_file/images.php
+++ b/app/Template/task_file/images.php
@@ -2,20 +2,28 @@
<div class="file-thumbnails">
<?php foreach ($images as $file): ?>
<div class="file-thumbnail">
- <a href="<?= $this->url->href('FileViewerController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>" class="popover"><img src="<?= $this->url->href('FileViewerController', 'thumbnail', array('file_id' => $file['id'], 'project_id' => $task['project_id'], 'task_id' => $file['task_id'])) ?>" title="<?= $this->text->e($file['name']) ?>" alt="<?= $this->text->e($file['name']) ?>"></a>
+ <?= $this->app->component('image-slideshow', array(
+ 'images' => $images,
+ 'image' => $file,
+ 'regex' => 'FILE_ID',
+ 'url' => array(
+ 'image' => $this->url->to('FileViewerController', 'image', array('file_id' => 'FILE_ID', 'project_id' => $task['project_id'], 'task_id' => $task['id'])),
+ 'thumbnail' => $this->url->to('FileViewerController', 'thumbnail', array('file_id' => 'FILE_ID', 'project_id' => $task['project_id'], 'task_id' => $task['id'])),
+ 'download' => $this->url->to('FileViewerController', 'download', array('file_id' => 'FILE_ID', 'project_id' => $task['project_id'], 'task_id' => $task['id'])),
+ )
+ )) ?>
+
<div class="file-thumbnail-content">
<div class="file-thumbnail-title">
<div class="dropdown">
- <a href="#" class="dropdown-menu dropdown-menu-link-text"><?= $this->text->e($file['name']) ?> <i class="fa fa-caret-down"></i></a>
+ <a href="#" class="dropdown-menu dropdown-menu-link-text" title="<?= $this->text->e($file['name']) ?>"><?= $this->text->e($file['name']) ?> <i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <i class="fa fa-download fa-fw"></i>
- <?= $this->url->link(t('Download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
+ <?= $this->url->icon('download', t('Download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
</li>
<?php if ($this->user->hasProjectAccess('TaskFileController', 'remove', $task['project_id'])): ?>
<li>
- <i class="fa fa-trash fa-fw"></i>
- <?= $this->url->link(t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskFileController', 'confirm', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/task_file/remove.php b/app/Template/task_file/remove.php
index 42894f05..e7ffe560 100644
--- a/app/Template/task_file/remove.php
+++ b/app/Template/task_file/remove.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to remove this file: "%s"?', $this->text->e($file['name'])) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TaskFileController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TaskFileController',
+ 'remove',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'file_id' => $file['id'])
+ ) ?>
</div>
diff --git a/app/Template/task_file/screenshot.php b/app/Template/task_file/screenshot.php
index 6300159f..dad8c233 100644
--- a/app/Template/task_file/screenshot.php
+++ b/app/Template/task_file/screenshot.php
@@ -6,14 +6,10 @@
<p id="screenshot-inner"><?= t('Take a screenshot and press CTRL+V or ⌘+V to paste here.') ?></p>
</div>
-<form class="popover-form" action="<?= $this->url->href('TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post">
- <input type="hidden" name="screenshot"/>
+<form action="<?= $this->url->href('TaskFileController', 'screenshot', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post">
<?= $this->form->csrf() ?>
- <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>
+ <?= $this->app->component('screenshot') ?>
+ <?= $this->modal->submitButtons() ?>
</form>
<p class="alert alert-info"><?= t('This feature does not work with all browsers.') ?></p>
diff --git a/app/Template/task_gantt/show.php b/app/Template/task_gantt/show.php
index c5d338fb..61a476b7 100644
--- a/app/Template/task_gantt/show.php
+++ b/app/Template/task_gantt/show.php
@@ -3,16 +3,13 @@
<div class="menu-inline">
<ul>
<li <?= $sorting === 'board' ? 'class="active"' : '' ?>>
- <i class="fa fa-sort-numeric-asc fa-fw"></i>
- <?= $this->url->link(t('Sort by position'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'sorting' => 'board')) ?>
+ <?= $this->url->icon('sort-numeric-asc', t('Sort by position'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'sorting' => 'board')) ?>
</li>
<li <?= $sorting === 'date' ? 'class="active"' : '' ?>>
- <i class="fa fa-sort-amount-asc fa-fw"></i>
- <?= $this->url->link(t('Sort by date'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'sorting' => 'date')) ?>
+ <?= $this->url->icon('sort-amount-asc', t('Sort by date'), 'TaskGanttController', 'show', array('project_id' => $project['id'], 'sorting' => 'date')) ?>
</li>
<li>
- <i class="fa fa-plus fa-fw"></i>
- <?= $this->url->link(t('Add task'), 'TaskGanttCreationController', 'show', array('project_id' => $project['id']), false, 'popover') ?>
+ <?= $this->modal->large('plus', t('Add task'), 'TaskCreationController', 'show', array('project_id' => $project['id'])) ?>
</li>
</ul>
</div>
diff --git a/app/Template/task_gantt_creation/show.php b/app/Template/task_gantt_creation/show.php
deleted file mode 100644
index 7521d805..00000000
--- a/app/Template/task_gantt_creation/show.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<div class="page-header">
- <h2><?= t('New task') ?></h2>
-</div>
-<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-columns">
- <div class="form-column">
- <?= $this->task->selectTitle($values, $errors) ?>
- <?= $this->task->selectDescription($values, $errors) ?>
- <?= $this->task->selectTags($project) ?>
-
- <?= $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->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) ?>
-
- <?= $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'), 'TaskGanttController', 'show', array('project_id' => $values['project_id']), false, 'close-popover') ?>
- </div>
-</form>
diff --git a/app/Template/task_import/show.php b/app/Template/task_import/show.php
index cc6a7b3a..20b020d3 100644
--- a/app/Template/task_import/show.php
+++ b/app/Template/task_import/show.php
@@ -15,14 +15,11 @@
<p class="form-help"><?= t('Maximum size: ') ?><?= is_integer($max_size) ? $this->text->bytes($max_size) : $max_size ?></p>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Import') ?></button>
- </div>
+ <?= $this->modal->submitButtons(array('submitLabel' => t('Import'))) ?>
</form>
-<div class="page-header">
- <h2><?= t('Instructions') ?></h2>
-</div>
-<div class="alert">
+
+<div class="panel">
+ <h3><?= t('Instructions') ?></h3>
<ul>
<li><?= t('Your file must use the predefined CSV format') ?></li>
<li><?= t('Your file must be encoded in UTF-8') ?></li>
@@ -30,5 +27,7 @@
<li><?= t('Duplicates are not verified for you') ?></li>
<li><?= t('The due date must use the ISO format: YYYY-MM-DD') ?></li>
</ul>
+ <p class="margin-top">
+ <?= $this->url->icon('download', t('Download CSV template'), 'TaskImportController', 'template', array('project_id' => $project['id'])) ?>
+ </p>
</div>
-<p><i class="fa fa-download fa-fw"></i><?= $this->url->link(t('Download CSV template'), 'TaskImportController', 'template', array('project_id' => $project['id'])) ?></p>
diff --git a/app/Template/task_import/sidebar.php b/app/Template/task_import/sidebar.php
deleted file mode 100644
index 4cd92af8..00000000
--- a/app/Template/task_import/sidebar.php
+++ /dev/null
@@ -1,9 +0,0 @@
-<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'])) ?>
- </li>
- <?= $this->hook->render('template:task-import:sidebar') ?>
- </ul>
-</div>
diff --git a/app/Template/task_internal_link/create.php b/app/Template/task_internal_link/create.php
index fed29605..3c39b87c 100644
--- a/app/Template/task_internal_link/create.php
+++ b/app/Template/task_internal_link/create.php
@@ -2,7 +2,7 @@
<h2><?= t('Add a new link') ?></h2>
</div>
-<form class="popover-form" action="<?= $this->url->href('TaskInternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
+<form action="<?= $this->url->href('TaskInternalLinkController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
<?= $this->form->csrf() ?>
<?= $this->form->hidden('task_id', array('task_id' => $task['id'])) ?>
@@ -25,9 +25,5 @@
),
'autocomplete') ?>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/task_internal_link/edit.php b/app/Template/task_internal_link/edit.php
index f4df57bd..5abf7b65 100644
--- a/app/Template/task_internal_link/edit.php
+++ b/app/Template/task_internal_link/edit.php
@@ -26,9 +26,5 @@
),
'autocomplete') ?>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
diff --git a/app/Template/task_internal_link/remove.php b/app/Template/task_internal_link/remove.php
index 966ad116..f8d91a81 100644
--- a/app/Template/task_internal_link/remove.php
+++ b/app/Template/task_internal_link/remove.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to remove this link with task #%d?', $link['opposite_task_id']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TaskInternalLinkController', 'remove', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TaskInternalLinkController',
+ 'remove',
+ array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])
+ ) ?>
</div>
diff --git a/app/Template/task_internal_link/table.php b/app/Template/task_internal_link/table.php
index 424d4791..255ecc97 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): ?>
@@ -72,8 +72,12 @@
<div class="dropdown">
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
- <li><?= $this->url->link(t('Edit'), 'TaskInternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
- <li><?= $this->url->link(t('Remove'), 'TaskInternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'popover') ?></li>
+ <li>
+ <?= $this->modal->medium('edit', t('Edit'), 'TaskInternalLinkController', 'edit', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
+ <li>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'TaskInternalLinkController', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </li>
</ul>
</div>
</td>
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/show.php b/app/Template/task_modification/show.php
index cc38582c..710abedf 100644
--- a/app/Template/task_modification/show.php
+++ b/app/Template/task_modification/show.php
@@ -1,44 +1,42 @@
<div class="page-header">
- <h2><?= t('Edit a task') ?></h2>
+ <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">
+<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) ?>
+ <div class="task-form-container">
+ <div class="task-form-main-column">
+ <?= $this->task->renderTitleField($values, $errors) ?>
+ <?= $this->task->renderDescriptionField($values, $errors) ?>
+ <?= $this->task->renderTagField($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) ?>
+ <div class="task-form-secondary-column">
+ <?= $this->task->renderColorField($values) ?>
+ <?= $this->task->renderAssigneeField($users_list, $values, $errors) ?>
+ <?= $this->task->renderCategoryField($categories_list, $values, $errors) ?>
+ <?= $this->task->renderPriorityField($project, $values) ?>
<?= $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="task-form-secondary-column">
+ <?= $this->task->renderDueDateField($values, $errors) ?>
+ <?= $this->task->renderStartDateField($values, $errors) ?>
+ <?= $this->task->renderTimeEstimatedField($values, $errors) ?>
+ <?= $this->task->renderTimeSpentField($values, $errors) ?>
+ <?= $this->task->renderScoreField($values, $errors) ?>
+ <?= $this->task->renderReferenceField($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 class="task-form-bottom">
+ <?= $this->modal->submitButtons() ?>
+ </div>
</div>
</form>
diff --git a/app/Template/task_move_position/show.php b/app/Template/task_move_position/show.php
new file mode 100644
index 00000000..a73be785
--- /dev/null
+++ b/app/Template/task_move_position/show.php
@@ -0,0 +1,19 @@
+<div class="page-header">
+ <h2><?= t('Move task to another position on the board') ?></h2>
+</div>
+
+<form>
+
+<?= $this->app->component('task-move-position', array(
+ 'saveUrl' => $this->url->href('TaskMovePositionController', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])),
+ 'board' => $board,
+ 'swimlaneLabel' => t('Swimlane'),
+ 'columnLabel' => t('Column'),
+ 'positionLabel' => t('Position'),
+ 'beforeLabel' => t('Insert before this task'),
+ 'afterLabel' => t('Insert after this task'),
+)) ?>
+
+<?= $this->modal->submitButtons() ?>
+
+</form>
diff --git a/app/Template/task_recurrence/edit.php b/app/Template/task_recurrence/edit.php
index 09d14826..0db27d46 100644
--- a/app/Template/task_recurrence/edit.php
+++ b/app/Template/task_recurrence/edit.php
@@ -3,7 +3,7 @@
</div>
<?php if ($task['recurrence_status'] != \Kanboard\Model\TaskModel::RECURRING_STATUS_NONE): ?>
-<div class="listing">
+<div class="panel">
<?= $this->render('task_recurrence/info', array(
'task' => $task,
'recurrence_trigger_list' => $recurrence_trigger_list,
@@ -15,7 +15,7 @@
<?php if ($task['recurrence_status'] != \Kanboard\Model\TaskModel::RECURRING_STATUS_PROCESSED): ?>
- <form class="popover-form" method="post" action="<?= $this->url->href('TaskRecurrenceController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
+ <form method="post" action="<?= $this->url->href('TaskRecurrenceController', 'update', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
@@ -37,11 +37,7 @@
<?= $this->form->label(t('Base date to calculate new due date'), 'recurrence_basedate') ?>
<?= $this->form->select('recurrence_basedate', $recurrence_basedate_list, $values, $errors) ?>
- <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>
+ <?= $this->modal->submitButtons() ?>
</form>
<?php endif ?>
diff --git a/app/Template/task_status/close.php b/app/Template/task_status/close.php
index 2d7b0ce5..0bf3c8e3 100644
--- a/app/Template/task_status/close.php
+++ b/app/Template/task_status/close.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to close the task "%s" as well as all subtasks?', $task['title']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TaskStatusController', 'close', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red popover-link') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TaskStatusController',
+ 'close',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes')
+ ) ?>
</div>
diff --git a/app/Template/task_status/open.php b/app/Template/task_status/open.php
index 242b5db5..42765e34 100644
--- a/app/Template/task_status/open.php
+++ b/app/Template/task_status/open.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to open this task: "%s"?', $task['title']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TaskStatusController', 'open', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes'), true, 'btn btn-red popover-link') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TaskStatusController',
+ 'open',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'confirmation' => 'yes')
+ ) ?>
</div>
diff --git a/app/Template/task_suppression/remove.php b/app/Template/task_suppression/remove.php
index 5d0f7720..4b0666d8 100644
--- a/app/Template/task_suppression/remove.php
+++ b/app/Template/task_suppression/remove.php
@@ -7,9 +7,9 @@
<?= t('Do you really want to remove this task: "%s"?', $this->text->e($task['title'])) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TaskSuppressionController', 'remove', array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => $redirect), true, 'btn btn-red popover-link') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TaskSuppressionController',
+ 'remove',
+ array('task_id' => $task['id'], 'project_id' => $task['project_id'], 'redirect' => $redirect)
+ ) ?>
</div>
diff --git a/app/Template/twofactor/disable.php b/app/Template/twofactor/disable.php
index bc419181..c44c450f 100644
--- a/app/Template/twofactor/disable.php
+++ b/app/Template/twofactor/disable.php
@@ -7,8 +7,9 @@
<?= t('Do you really want to disable the two factor authentication for this user: "%s"?', $user['name'] ?: $user['username']) ?>
</p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'TwoFactorController', 'disable', array('user_id' => $user['id'], 'disable' => 'yes'), true, 'btn btn-red') ?>
- <?= t('or') ?> <?= $this->url->link(t('cancel'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'TwoFactorController',
+ 'disable',
+ array('user_id' => $user['id'], 'disable' => 'yes')
+ ) ?>
</div>
diff --git a/app/Template/twofactor/show.php b/app/Template/twofactor/show.php
index 0aeef427..a5bdeccb 100644
--- a/app/Template/twofactor/show.php
+++ b/app/Template/twofactor/show.php
@@ -3,7 +3,7 @@
</div>
<?php if (! empty($secret) || ! empty($qrcode_url) || ! empty($key_url)): ?>
-<div class="listing">
+<div class="panel">
<?php if (! empty($secret)): ?>
<p><?= t('Secret key: ') ?><strong><?= $this->text->e($secret) ?></strong></p>
<?php endif ?>
diff --git a/app/Template/user_api_access/show.php b/app/Template/user_api_access/show.php
new file mode 100644
index 00000000..3d58e0d5
--- /dev/null
+++ b/app/Template/user_api_access/show.php
@@ -0,0 +1,17 @@
+<div class="page-header">
+ <h2><?= t('API User Access') ?></h2>
+</div>
+
+<p class="alert">
+ <?php if (empty($user['api_access_token'])): ?>
+ <?= t('No personal API access token registered.') ?>
+ <?php else: ?>
+ <?= t('Your personal API access token is "%s"', $user['api_access_token']) ?>
+ <?php endif ?>
+</p>
+
+<?php if (! empty($user['api_access_token'])): ?>
+ <?= $this->url->link(t('Remove your token'), 'UserApiAccessController', 'remove', array('user_id' => $user['id']), true, 'btn btn-red') ?>
+<?php endif ?>
+
+<?= $this->url->link(t('Generate a new token'), 'UserApiAccessController', 'generate', array('user_id' => $user['id']), true, 'btn btn-blue') ?>
diff --git a/app/Template/user_creation/remote.php b/app/Template/user_creation/remote.php
deleted file mode 100644
index 41d0d3c7..00000000
--- a/app/Template/user_creation/remote.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<div class="page-header">
- <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-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->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) ?>
-
- <?= $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('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) ?>
- </div>
- </div>
-
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?>
- </div>
-</form>
-<div class="alert alert-info">
- <ul>
- <li><?= t('Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.') ?></li>
- <li><?= t('If you check the box "Disallow login form", credentials entered in the login form will be ignored.') ?></li>
- </ul>
-</div>
diff --git a/app/Template/user_creation/show.php b/app/Template/user_creation/show.php
new file mode 100644
index 00000000..597dce55
--- /dev/null
+++ b/app/Template/user_creation/show.php
@@ -0,0 +1,67 @@
+<div class="page-header">
+ <h2><?= t('New User') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+
+ <div class="form-columns">
+ <div class="form-column">
+ <fieldset>
+ <legend><?= t('Profile') ?></legend>
+
+ <?= $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) ?>
+ </fieldset>
+
+ <fieldset>
+ <legend><?= t('Authentication') ?></legend>
+ <?= $this->form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?>
+ <p class="form-help"><?= t('If checked, this user will use a third-party system for authentication.') ?></p>
+
+ <?= $this->form->label(t('Password'), 'password') ?>
+ <?= $this->form->password('password', $values, $errors) ?>
+ <p class="form-help"><?= t('The password is necessary only for local users.') ?></p>
+
+ <?= $this->form->label(t('Confirmation'), 'confirmation') ?>
+ <?= $this->form->password('confirmation', $values, $errors) ?>
+ </fieldset>
+ </div>
+
+ <div class="form-column">
+ <fieldset>
+ <legend><?= t('Security') ?></legend>
+
+ <?= $this->form->label(t('Role'), 'role') ?>
+ <?= $this->form->select('role', $roles, $values, $errors) ?>
+
+ <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?>
+ </fieldset>
+
+ <fieldset>
+ <legend><?= t('Preferences') ?></legend>
+ <?= $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->checkbox('notifications_enabled', t('Enable email notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?>
+ </fieldset>
+
+ <fieldset>
+ <legend><?= t('Projects') ?></legend>
+
+ <?= $this->form->label(t('Add this person to this project'), 'project_id') ?>
+ <?= $this->form->select('project_id', $projects, $values, $errors) ?>
+ </fieldset>
+ </div>
+ </div>
+
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/user_credential/authentication.php b/app/Template/user_credential/authentication.php
index fbe2e915..98c0d758 100644
--- a/app/Template/user_credential/authentication.php
+++ b/app/Template/user_credential/authentication.php
@@ -4,13 +4,15 @@
<form method="post" action="<?= $this->url->href('UserCredentialController', 'saveAuthentication', array('user_id' => $user['id'])) ?>" autocomplete="off">
<?= $this->form->csrf() ?>
- <?= $this->form->hidden('id', $values) ?>
- <?= $this->form->hidden('username', $values) ?>
+ <fieldset>
+ <?= $this->form->hidden('id', $values) ?>
+ <?= $this->form->hidden('username', $values) ?>
- <?= $this->hook->render('template:user:authentication:form', array('values' => $values, 'errors' => $errors, 'user' => $user)) ?>
+ <?= $this->hook->render('template:user:authentication:form', array('values' => $values, 'errors' => $errors, 'user' => $user)) ?>
- <?= $this->form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?>
- <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?>
+ <?= $this->form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?>
+ <?= $this->form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?>
+ </fieldset>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
diff --git a/app/Template/user_credential/password.php b/app/Template/user_credential/password.php
index 5a6e4403..bd7a47da 100644
--- a/app/Template/user_credential/password.php
+++ b/app/Template/user_credential/password.php
@@ -6,14 +6,16 @@
<?= $this->form->hidden('id', $values) ?>
<?= $this->form->csrf() ?>
- <?= $this->form->label(t('Current password for the user "%s"', $this->user->getFullname()), 'current_password') ?>
- <?= $this->form->password('current_password', $values, $errors) ?>
+ <fieldset>
+ <?= $this->form->label(t('Current password for the user "%s"', $this->user->getFullname()), 'current_password') ?>
+ <?= $this->form->password('current_password', $values, $errors) ?>
- <?= $this->form->label(t('New password for the user "%s"', $this->user->getFullname($user)), 'password') ?>
- <?= $this->form->password('password', $values, $errors) ?>
+ <?= $this->form->label(t('New password for the user "%s"', $this->user->getFullname($user)), 'password') ?>
+ <?= $this->form->password('password', $values, $errors) ?>
- <?= $this->form->label(t('Confirmation'), 'confirmation') ?>
- <?= $this->form->password('confirmation', $values, $errors) ?>
+ <?= $this->form->label(t('Confirmation'), 'confirmation') ?>
+ <?= $this->form->password('confirmation', $values, $errors) ?>
+ </fieldset>
<div class="form-actions">
<button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
diff --git a/app/Template/user_import/show.php b/app/Template/user_import/show.php
index 663f107e..3b0e599c 100644
--- a/app/Template/user_import/show.php
+++ b/app/Template/user_import/show.php
@@ -2,8 +2,7 @@
<h2><?= t('Import users from CSV file') ?></h2>
<ul>
<li>
- <i class="fa fa-download fa-fw"></i>
- <?= $this->url->link(t('Download CSV template'), 'UserImportController', 'template') ?>
+ <?= $this->url->icon('download', t('Download CSV template'), 'UserImportController', 'template') ?>
</li>
</ul>
</div>
@@ -33,9 +32,5 @@
<p class="form-help"><?= t('Maximum size: ') ?><?= is_integer($max_size) ? $this->text->bytes($max_size) : $max_size ?></p>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Import') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->submitButtons(array('submitLabel' => t('Import'))) ?>
</form>
diff --git a/app/Template/user_invite/email.php b/app/Template/user_invite/email.php
new file mode 100644
index 00000000..674e4a84
--- /dev/null
+++ b/app/Template/user_invite/email.php
@@ -0,0 +1,12 @@
+<p>
+ <?= t('You have been invited to register on Kanboard.') ?>
+</p>
+
+<p>
+ <?= $this->url->absoluteLink(t('Click here to join your team'), 'UserInviteController', 'signup', array('token' => $token)) ?>
+</p>
+
+<?php if ($this->app->config('application_url')): ?>
+ <hr>
+ <a href="<?= $this->app->config('application_url') ?>">Kanboard</a>
+<?php endif ?>
diff --git a/app/Template/user_invite/show.php b/app/Template/user_invite/show.php
new file mode 100644
index 00000000..9d822248
--- /dev/null
+++ b/app/Template/user_invite/show.php
@@ -0,0 +1,15 @@
+<div class="page-header">
+ <h2><?= t('Invite people') ?></h2>
+</div>
+<form method="post" action="<?= $this->url->href('UserInviteController', 'save') ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+
+ <?= $this->form->label(t('Emails'), 'emails') ?>
+ <?= $this->form->textarea('emails', $values, $errors, array('required', 'autofocus')) ?>
+ <p class="form-help"><?= t('Enter one email address by line.') ?></p>
+
+ <?= $this->form->label(t('Add these people to this project'), 'project_id') ?>
+ <?= $this->form->select('project_id', $projects, $values, $errors) ?>
+
+ <?= $this->modal->submitButtons() ?>
+</form>
diff --git a/app/Template/user_creation/local.php b/app/Template/user_invite/signup.php
index 059a0114..51edbab7 100644
--- a/app/Template/user_creation/local.php
+++ b/app/Template/user_invite/signup.php
@@ -1,11 +1,13 @@
-<div class="page-header">
- <h2><?= t('New local user') ?></h2>
-</div>
-<form class="popover-form" method="post" action="<?= $this->url->href('UserCreationController', 'save') ?>" autocomplete="off">
- <?= $this->form->csrf() ?>
-
- <div class="form-columns">
- <div class="form-column">
+<div class="form-login">
+ <div class="page-header">
+ <h2><?= t('Sign-up') ?></h2>
+ </div>
+ <form method="post" action="<?= $this->url->href('UserInviteController', 'register', array('token' => $token)) ?>" autocomplete="off">
+ <?= $this->form->csrf() ?>
+
+ <fieldset>
+ <legend><?= t('Profile') ?></legend>
+
<?= $this->form->label(t('Username'), 'username') ?>
<?= $this->form->text('username', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?>
@@ -13,18 +15,20 @@
<?= $this->form->text('name', $values, $errors) ?>
<?= $this->form->label(t('Email'), 'email') ?>
- <?= $this->form->email('email', $values, $errors) ?>
+ <?= $this->form->email('email', $values, $errors, array('required')) ?>
+ </fieldset>
+ <fieldset>
+ <legend><?= t('Credentials') ?></legend>
<?= $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>
+ </fieldset>
- <div class="form-column">
- <?= $this->form->label(t('Add project member'), 'project_id') ?>
- <?= $this->form->select('project_id', $projects, $values, $errors) ?>
+ <fieldset>
+ <legend><?= t('Preferences') ?></legend>
<?= $this->form->label(t('Timezone'), 'timezone') ?>
<?= $this->form->select('timezone', $timezones, $values, $errors) ?>
@@ -32,16 +36,11 @@
<?= $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->checkbox('notifications_enabled', t('Enable email notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?>
- </div>
- </div>
+ </fieldset>
- <div class="form-actions">
- <button type="submit" class="btn btn-blue"><?= t('Save') ?></button>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?>
- </div>
-</form>
+ <div class="form-actions">
+ <button class="btn btn-blue"><?= t('Sign-up') ?></button>
+ </div>
+ </form>
+</div> \ No newline at end of file
diff --git a/app/Template/user_list/dropdown.php b/app/Template/user_list/dropdown.php
index 9e90c230..d18f20aa 100644
--- a/app/Template/user_list/dropdown.php
+++ b/app/Template/user_list/dropdown.php
@@ -2,25 +2,21 @@
<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a>
<ul>
<li>
- <i class="fa fa-user fa-fw"></i>
- <?= $this->url->link(t('View profile'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
+ <?= $this->url->icon('user', t('View profile'), 'UserViewController', 'show', array('user_id' => $user['id'])) ?>
</li>
<?php if ($user['is_active'] == 1 && $this->user->hasAccess('UserStatusController', 'disable') && ! $this->user->isCurrentUser($user['id'])): ?>
<li>
- <i class="fa fa-times fa-fw"></i>
- <?= $this->url->link(t('Disable'), 'UserStatusController', 'confirmDisable', array('user_id' => $user['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('times', t('Disable'), 'UserStatusController', 'confirmDisable', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
<?php if ($user['is_active'] == 0 && $this->user->hasAccess('UserStatusController', 'enable') && ! $this->user->isCurrentUser($user['id'])): ?>
<li>
- <i class="fa fa-check-square-o fa-fw"></i>
- <?= $this->url->link(t('Enable'), 'UserStatusController', 'confirmEnable', array('user_id' => $user['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('check-square-o', t('Enable'), 'UserStatusController', 'confirmEnable', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
<?php if ($this->user->hasAccess('UserStatusController', 'remove') && ! $this->user->isCurrentUser($user['id'])): ?>
<li>
- <i class="fa fa-trash-o fa-fw"></i>
- <?= $this->url->link(t('Remove'), 'UserStatusController', 'confirmRemove', array('user_id' => $user['id']), false, 'popover') ?>
+ <?= $this->modal->confirm('trash-o', t('Remove'), 'UserStatusController', 'confirmRemove', array('user_id' => $user['id'])) ?>
</li>
<?php endif ?>
</ul>
diff --git a/app/Template/user_list/show.php b/app/Template/user_list/show.php
index b2bd9377..e83895ea 100644
--- a/app/Template/user_list/show.php
+++ b/app/Template/user_list/show.php
@@ -2,17 +2,25 @@
<div class="page-header">
<?php if ($this->user->hasAccess('UserCreationController', 'show')): ?>
<ul>
- <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'UserCreationController', 'show', array(), false, 'popover') ?></li>
- <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'UserCreationController', 'show', array('remote' => 1), false, 'popover') ?></li>
- <li><i class="fa fa-upload fa-fw"></i><?= $this->url->link(t('Import'), 'UserImportController', 'show', array(), false, 'popover') ?></li>
- <li><i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?></li>
+ <li>
+ <?= $this->modal->medium('plus', t('New user'), 'UserCreationController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('paper-plane', t('Invite people'), 'UserInviteController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('upload', t('Import'), 'UserImportController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->url->icon('users', t('View all groups'), 'GroupListController', 'index') ?>
+ </li>
</ul>
<?php endif ?>
</div>
<?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..d3f3e0cc 100644
--- a/app/Template/user_modification/show.php
+++ b/app/Template/user_modification/show.php
@@ -2,29 +2,36 @@
<h2><?= t('Edit user') ?></h2>
</div>
<form method="post" action="<?= $this->url->href('UserModificationController', 'save', array('user_id' => $user['id'])) ?>" autocomplete="off">
-
<?= $this->form->csrf() ?>
-
<?= $this->form->hidden('id', $values) ?>
- <?= $this->form->label(t('Username'), 'username') ?>
- <?= $this->form->text('username', $values, $errors, array('required', isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1 ? 'readonly' : '', 'maxlength="50"')) ?>
+ <fieldset>
+ <legend><?= t('Profile') ?></legend>
+ <?= $this->form->label(t('Username'), 'username') ?>
+ <?= $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->label(t('Name'), 'name') ?>
+ <?= $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->label(t('Email'), 'email') ?>
+ <?= $this->form->email('email', $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_email') ? '' : 'readonly')) ?>
+ </fieldset>
- <?= $this->form->label(t('Timezone'), 'timezone') ?>
- <?= $this->form->select('timezone', $timezones, $values, $errors) ?>
+ <fieldset>
+ <legend><?= t('Preferences') ?></legend>
+ <?= $this->form->label(t('Timezone'), 'timezone') ?>
+ <?= $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->label(t('Language'), 'language') ?>
+ <?= $this->form->select('language', $languages, $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_language') ? '' : 'disabled')) ?>
+ </fieldset>
<?php if ($this->user->isAdmin()): ?>
- <?= $this->form->label(t('Role'), 'role') ?>
+ <fieldset>
+ <legend><?= t('Security') ?></legend>
+ <?= $this->form->label(t('Application role'), 'role') ?>
<?= $this->form->select('role', $roles, $values, $errors) ?>
+ </fieldset>
<?php endif ?>
<div class="form-actions">
diff --git a/app/Template/user_status/disable.php b/app/Template/user_status/disable.php
index d30b0c20..1309b080 100644
--- a/app/Template/user_status/disable.php
+++ b/app/Template/user_status/disable.php
@@ -5,9 +5,9 @@
<div class="confirm">
<p class="alert alert-info"><?= t('Do you really want to disable this user: "%s"?', $user['name'] ?: $user['username']) ?></p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'UserStatusController', 'disable', array('user_id' => $user['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'UserStatusController',
+ 'disable',
+ array('user_id' => $user['id'])
+ ) ?>
</div>
diff --git a/app/Template/user_status/enable.php b/app/Template/user_status/enable.php
index 29d25eee..2413739e 100644
--- a/app/Template/user_status/enable.php
+++ b/app/Template/user_status/enable.php
@@ -5,9 +5,9 @@
<div class="confirm">
<p class="alert alert-info"><?= t('Do you really want to enable this user: "%s"?', $user['name'] ?: $user['username']) ?></p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'UserStatusController', 'enable', array('user_id' => $user['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'UserStatusController',
+ 'enable',
+ array('user_id' => $user['id'])
+ ) ?>
</div>
diff --git a/app/Template/user_status/remove.php b/app/Template/user_status/remove.php
index 2b8f2df5..6cd3f63a 100644
--- a/app/Template/user_status/remove.php
+++ b/app/Template/user_status/remove.php
@@ -5,9 +5,9 @@
<div class="confirm">
<p class="alert alert-info"><?= t('Do you really want to remove this user: "%s"?', $user['name'] ?: $user['username']) ?></p>
- <div class="form-actions">
- <?= $this->url->link(t('Yes'), 'UserStatusController', 'remove', array('user_id' => $user['id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->url->link(t('cancel'), 'UserListController', 'show', array(), false, 'close-popover') ?>
- </div>
+ <?= $this->modal->confirmButtons(
+ 'UserStatusController',
+ 'remove',
+ array('user_id' => $user['id'])
+ ) ?>
</div>
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/layout.php b/app/Template/user_view/layout.php
index c3604b99..8f24adcc 100644
--- a/app/Template/user_view/layout.php
+++ b/app/Template/user_view/layout.php
@@ -2,11 +2,18 @@
<div class="page-header">
<?php if ($this->user->hasAccess('UserCreationController', 'show')): ?>
<ul>
- <li><i class="fa fa-user fa-fw"></i><?= $this->url->link(t('All users'), 'UserListController', 'show') ?></li>
- <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New local user'), 'UserCreationController', 'show', array(), false, 'popover') ?></li>
- <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New remote user'), 'UserCreationController', 'show', array('remote' => 1), false, 'popover') ?></li>
- <li><i class="fa fa-upload fa-fw"></i><?= $this->url->link(t('Import'), 'UserImportController', 'show', array(), false, 'popover') ?></li>
- <li><i class="fa fa-users fa-fw"></i><?= $this->url->link(t('View all groups'), 'GroupListController', 'index') ?></li>
+ <li>
+ <?= $this->url->icon('user', t('All users'), 'UserListController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('plus', t('New user'), 'UserCreationController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->modal->medium('upload', t('Import'), 'UserImportController', 'show') ?>
+ </li>
+ <li>
+ <?= $this->url->icon('users', t('View all groups'), 'GroupListController', 'index') ?>
+ </li>
</ul>
<?php endif ?>
</div>
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/profile.php b/app/Template/user_view/profile.php
index 9c9d3282..486ca428 100644
--- a/app/Template/user_view/profile.php
+++ b/app/Template/user_view/profile.php
@@ -1,9 +1,11 @@
<section id="main">
<br>
<?= $this->avatar->render($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path']) ?>
- <ul class="listing">
- <li><?= t('Username:') ?> <strong><?= $this->text->e($user['username']) ?></strong></li>
- <li><?= t('Name:') ?> <strong><?= $this->text->e($user['name']) ?: t('None') ?></strong></li>
- <li><?= t('Email:') ?> <strong><?= $this->text->e($user['email']) ?: t('None') ?></strong></li>
- </ul>
-</section> \ No newline at end of file
+ <div class="panel">
+ <ul>
+ <li><?= t('Login:') ?> <strong><?= $this->text->e($user['username']) ?></strong></li>
+ <li><?= t('Full Name:') ?> <strong><?= $this->text->e($user['name']) ?: t('None') ?></strong></li>
+ <li><?= t('Email:') ?> <strong><?= $this->text->e($user['email']) ?: t('None') ?></strong></li>
+ </ul>
+ </div>
+</section>
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/share.php b/app/Template/user_view/share.php
index 570b766e..318d98ea 100644
--- a/app/Template/user_view/share.php
+++ b/app/Template/user_view/share.php
@@ -3,10 +3,10 @@
</div>
<?php if (! empty($user['token'])): ?>
- <div class="listing">
+ <div class="panel">
<ul class="no-bullet">
- <li><strong><i class="fa fa-rss-square"></i> <?= $this->url->link(t('RSS feed'), 'FeedController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
- <li><strong><i class="fa fa-calendar"></i> <?= $this->url->link(t('iCal feed'), 'ICalendarController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ <li><strong><?= $this->url->icon('rss-square', t('RSS feed'), 'FeedController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ <li><strong><?= $this->url->icon('calendar', t('iCal feed'), 'ICalendarController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
</ul>
</div>
<?= $this->url->link(t('Disable public access'), 'UserViewController', 'share', array('user_id' => $user['id'], 'switch' => 'disable'), true, 'btn btn-red') ?>
diff --git a/app/Template/user_view/show.php b/app/Template/user_view/show.php
index fc11f8a1..e57fd4cd 100644
--- a/app/Template/user_view/show.php
+++ b/app/Template/user_view/show.php
@@ -1,9 +1,9 @@
<div class="page-header">
<h2><?= t('Summary') ?></h2>
</div>
-<ul class="listing">
- <li><?= t('Username:') ?> <strong><?= $this->text->e($user['username']) ?></strong></li>
- <li><?= t('Name:') ?> <strong><?= $this->text->e($user['name']) ?: t('None') ?></strong></li>
+<ul class="panel">
+ <li><?= t('Login:') ?> <strong><?= $this->text->e($user['username']) ?></strong></li>
+ <li><?= t('Full Name:') ?> <strong><?= $this->text->e($user['name']) ?: t('None') ?></strong></li>
<li><?= t('Email:') ?> <strong><?= $this->text->e($user['email']) ?: t('None') ?></strong></li>
<li><?= t('Status:') ?> <strong><?= $user['is_active'] ? t('Active') : t('Inactive') ?></strong></li>
</ul>
@@ -11,20 +11,25 @@
<div class="page-header">
<h2><?= t('Security') ?></h2>
</div>
-<ul class="listing">
+<ul class="panel">
<li><?= t('Role:') ?> <strong><?= $this->user->getRoleName($user['role']) ?></strong></li>
<li><?= t('Account type:') ?> <strong><?= $user['is_ldap_user'] ? t('Remote') : t('Local') ?></strong></li>
<li><?= $user['twofactor_activated'] == 1 ? t('Two factor authentication enabled') : t('Two factor authentication disabled') ?></li>
<li><?= t('Number of failed login:') ?> <strong><?= $user['nb_failed_login'] ?></strong></li>
<?php if ($user['lock_expiration_date'] != 0): ?>
<li><?= t('Account locked until:') ?> <strong><?= $this->dt->datetime($user['lock_expiration_date']) ?></strong></li>
+ <?php if ($this->user->isAdmin()): ?>
+ <li>
+ <?= $this->url->link(t('Unlock this user'), 'UserCredentialController', 'unlock', array('user_id' => $user['id']), true) ?>
+ </li>
+ <?php endif ?>
<?php endif ?>
</ul>
<div class="page-header">
<h2><?= t('Preferences') ?></h2>
</div>
-<ul class="listing">
+<ul class="panel">
<li><?= t('Timezone:') ?> <strong><?= $this->text->in($user['timezone'], $timezones) ?></strong></li>
<li><?= t('Language:') ?> <strong><?= $this->text->in($user['language'], $languages) ?></strong></li>
<li><?= t('Notifications:') ?> <strong><?= $user['notifications_enabled'] == 1 ? t('Enabled') : t('Disabled') ?></strong></li>
@@ -35,10 +40,10 @@
<h2><?= t('Public access') ?></h2>
</div>
- <div class="listing">
+ <div class="panel">
<ul class="no-bullet">
- <li><strong><i class="fa fa-rss-square"></i> <?= $this->url->link(t('RSS feed'), 'FeedController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
- <li><strong><i class="fa fa-calendar"></i> <?= $this->url->link(t('iCal feed'), 'ICalendarController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ <li><strong><?= $this->url->icon('rss-square', t('RSS feed'), 'FeedController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
+ <li><strong><?= $this->url->icon('calendar', t('iCal feed'), 'ICalendarController', 'user', array('token' => $user['token']), false, '', '', true) ?></strong></li>
</ul>
</div>
<?php endif ?>
diff --git a/app/Template/user_view/sidebar.php b/app/Template/user_view/sidebar.php
index d200a7f5..ef494e42 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,31 @@
</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 if ($this->user->hasAccess('UserApiAccessController', 'show')): ?>
+ <li <?= $this->app->checkMenuSelection('UserApiAccessController', 'show') ?>>
+ <?= $this->url->link(t('API'), 'UserApiAccessController', 'show', 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/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/Validator/ColumnMoveRestrictionValidator.php b/app/Validator/ColumnMoveRestrictionValidator.php
new file mode 100644
index 00000000..99769c6b
--- /dev/null
+++ b/app/Validator/ColumnMoveRestrictionValidator.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace Kanboard\Validator;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Class ColumnMoveRestrictionValidator
+ *
+ * @package Kanboard\Validator
+ * @author Frederic Guillot
+ */
+class ColumnMoveRestrictionValidator 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, array(
+ new Validators\Required('project_id', t('This field is required')),
+ new Validators\Integer('project_id', t('This value must be an integer')),
+ new Validators\Required('role_id', t('This field is required')),
+ new Validators\Integer('role_id', t('This value must be an integer')),
+ new Validators\Required('src_column_id', t('This field is required')),
+ new Validators\Integer('src_column_id', t('This value must be an integer')),
+ new Validators\Required('dst_column_id', t('This field is required')),
+ new Validators\Integer('dst_column_id', t('This value must be an integer')),
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/app/Validator/ColumnRestrictionValidator.php b/app/Validator/ColumnRestrictionValidator.php
new file mode 100644
index 00000000..b1b2e5a0
--- /dev/null
+++ b/app/Validator/ColumnRestrictionValidator.php
@@ -0,0 +1,40 @@
+<?php
+
+namespace Kanboard\Validator;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Class ColumnRestrictionValidator
+ *
+ * @package Kanboard\Validator
+ * @author Frederic Guillot
+ */
+class ColumnRestrictionValidator 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, array(
+ new Validators\Required('project_id', t('This field is required')),
+ new Validators\Integer('project_id', t('This value must be an integer')),
+ new Validators\Required('role_id', t('This field is required')),
+ new Validators\Integer('role_id', t('This value must be an integer')),
+ new Validators\Required('rule', t('This field is required')),
+ new Validators\Required('column_id', t('This field is required')),
+ new Validators\Integer('column_id', t('This value must be an integer')),
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/app/Validator/ProjectRoleValidator.php b/app/Validator/ProjectRoleValidator.php
new file mode 100644
index 00000000..4db630db
--- /dev/null
+++ b/app/Validator/ProjectRoleValidator.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Kanboard\Validator;
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Class ProjectRoleValidator
+ *
+ * @package Kanboard\Validator
+ * @author Frederic Guillot
+ */
+class ProjectRoleValidator 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());
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+
+ /**
+ * 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('role_id', t('The id is required')),
+ );
+
+ $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+
+ /**
+ * Common validation rules
+ *
+ * @access private
+ * @return array
+ */
+ private function commonValidationRules()
+ {
+ return array(
+ new Validators\Required('role', t('This field is required')),
+ new Validators\MaxLength('role', t('The maximum length is %d characters', 100), 100),
+ new Validators\Required('project_id', t('This field is required')),
+ new Validators\Integer('project_id', t('This value must be an integer')),
+ new Validators\Integer('role_id', t('This value must be an integer')),
+ );
+ }
+}
diff --git a/app/Validator/ProjectValidator.php b/app/Validator/ProjectValidator.php
index 8c6117a4..5b10026c 100644
--- a/app/Validator/ProjectValidator.php
+++ b/app/Validator/ProjectValidator.php
@@ -28,7 +28,6 @@ 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\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,15 +46,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, array_merge($this->commonValidationRules(), $rules));
+ $rules = array(
+ new Validators\Required('name', t('The project name is required')),
+ );
+
+ $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
return array(
$v->execute(),
@@ -77,6 +76,7 @@ class ProjectValidator extends BaseValidator
}
$rules = array(
+ new Validators\NotEmpty('name', t('This field cannot be empty')),
new Validators\Required('id', t('This value is required')),
);
diff --git a/app/Validator/TaskValidator.php b/app/Validator/TaskValidator.php
index 8aa5c440..e824dc05 100644
--- a/app/Validator/TaskValidator.php
+++ b/app/Validator/TaskValidator.php
@@ -28,6 +28,7 @@ class TaskValidator extends BaseValidator
new Validators\Integer('owner_id', t('This value must be an integer')),
new Validators\Integer('creator_id', t('This value must be an integer')),
new Validators\Integer('score', t('This value must be an integer')),
+ new Validators\Range('score', t('This value must be in the range %d to %d', -2147483647, 2147483647), -2147483647, 2147483647),
new Validators\Integer('category_id', t('This value must be an integer')),
new Validators\Integer('swimlane_id', t('This value must be an integer')),
new Validators\Integer('recurrence_child', t('This value must be an integer')),
@@ -41,7 +42,7 @@ class TaskValidator extends BaseValidator
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
new Validators\MaxLength('reference', t('The maximum length is %d characters', 50), 50),
new Validators\Date('date_due', t('Invalid date'), $this->dateParser->getParserFormats()),
- new Validators\Date('date_started', t('Invalid date'), $this->dateParser->getParserFormats()),
+ new Validators\Date('date_started', t('Invalid date'), array($this->dateParser->getUserDateTimeFormat())),
new Validators\Numeric('time_spent', t('This value must be numeric')),
new Validators\Numeric('time_estimated', t('This value must be numeric')),
);
@@ -96,27 +97,6 @@ class TaskValidator extends BaseValidator
}
/**
- * Validate description creation
- *
- * @access public
- * @param array $values Form values
- * @return array $valid, $errors [0] = Success or not, [1] = List of errors
- */
- public function validateDescriptionCreation(array $values)
- {
- $rules = array(
- new Validators\Required('id', t('The id is required')),
- );
-
- $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
-
- return array(
- $v->execute(),
- $v->getErrors()
- );
- }
-
- /**
* Validate edit recurrence
*
* @access public
diff --git a/app/Validator/UserValidator.php b/app/Validator/UserValidator.php
index 9911de50..fe402c47 100644
--- a/app/Validator/UserValidator.php
+++ b/app/Validator/UserValidator.php
@@ -25,7 +25,7 @@ class UserValidator extends BaseValidator
return array(
new Validators\MaxLength('role', t('The maximum length is %d characters', 25), 25),
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
- new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), UserModel::TABLE, 'id'),
+ new Validators\Unique('username', t('This username is already taken'), $this->db->getConnection(), UserModel::TABLE, 'id'),
new Validators\Email('email', t('Email address invalid')),
new Validators\Integer('is_ldap_user', t('This value must be an integer')),
);
diff --git a/app/common.php b/app/common.php
index 72be3603..fd55c0bb 100644
--- a/app/common.php
+++ b/app/common.php
@@ -35,6 +35,7 @@ $container->register(new Kanboard\ServiceProvider\MailProvider());
$container->register(new Kanboard\ServiceProvider\HelperProvider());
$container->register(new Kanboard\ServiceProvider\SessionProvider());
$container->register(new Kanboard\ServiceProvider\LoggingProvider());
+$container->register(new Kanboard\ServiceProvider\CacheProvider());
$container->register(new Kanboard\ServiceProvider\DatabaseProvider());
$container->register(new Kanboard\ServiceProvider\AuthenticationProvider());
$container->register(new Kanboard\ServiceProvider\NotificationProvider());
@@ -44,8 +45,11 @@ $container->register(new Kanboard\ServiceProvider\GroupProvider());
$container->register(new Kanboard\ServiceProvider\RouteProvider());
$container->register(new Kanboard\ServiceProvider\ActionProvider());
$container->register(new Kanboard\ServiceProvider\ExternalLinkProvider());
+$container->register(new Kanboard\ServiceProvider\ExternalTaskProvider());
$container->register(new Kanboard\ServiceProvider\AvatarProvider());
$container->register(new Kanboard\ServiceProvider\FilterProvider());
+$container->register(new Kanboard\ServiceProvider\FormatterProvider());
+$container->register(new Kanboard\ServiceProvider\JobProvider());
$container->register(new Kanboard\ServiceProvider\QueueProvider());
$container->register(new Kanboard\ServiceProvider\ApiProvider());
$container->register(new Kanboard\ServiceProvider\CommandProvider());
diff --git a/app/constants.php b/app/constants.php
index fc120692..ba14cde6 100644
--- a/app/constants.php
+++ b/app/constants.php
@@ -12,6 +12,12 @@ defined('DATA_DIR') or define('DATA_DIR', ROOT_DIR.DIRECTORY_SEPARATOR.'data');
// Files directory (attachments)
defined('FILES_DIR') or define('FILES_DIR', DATA_DIR.DIRECTORY_SEPARATOR.'files');
+// Available cache drivers are "file" and "memory"
+defined('CACHE_DRIVER') or define('CACHE_DRIVER', 'memory');
+
+// Cache folder (file driver)
+defined('CACHE_DIR') or define('CACHE_DIR', DATA_DIR.DIRECTORY_SEPARATOR.'cache');
+
// Plugins settings
defined('PLUGINS_DIR') or define('PLUGINS_DIR', ROOT_DIR.DIRECTORY_SEPARATOR.'plugins');
defined('PLUGIN_API_URL') or define('PLUGIN_API_URL', 'https://kanboard.net/plugins.json');
@@ -29,6 +35,9 @@ defined('LOG_FILE') or define('LOG_FILE', DATA_DIR.DIRECTORY_SEPARATOR.'debug.lo
// Application version
defined('APP_VERSION') or define('APP_VERSION', build_app_version('$Format:%d$', '$Format:%H$'));
+// Run automatically database migrations
+defined('DB_RUN_MIGRATIONS') or define('DB_RUN_MIGRATIONS', true);
+
// Database driver: sqlite, mysql or postgres
defined('DB_DRIVER') or define('DB_DRIVER', 'sqlite');
@@ -134,3 +143,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 eaf33a52..7cd59c41 100644
--- a/app/functions.php
+++ b/app/functions.php
@@ -25,13 +25,13 @@ function array_merge_relation(array &$input, array &$relations, $relation, $colu
* Create indexed array from a list of dict
*
* $input = [
- * ['k1' => 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 2, 'k2' => 5]
+ * ['k1' => 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 1, 'k2' => 5]
* ]
*
* array_column_index($input, 'k1') will returns:
*
* [
- * 1 => [['k1' => 1, 'k2' => 2], ['k1' => 2, 'k2' => 5]],
+ * 1 => [['k1' => 1, 'k2' => 2], ['k1' => 1, 'k2' => 5]],
* 3 => [['k1' => 3, 'k2' => 4]],
* ]
*
@@ -53,6 +53,37 @@ function array_column_index(array &$input, $column)
}
/**
+ * Create indexed array from a list of dict with unique values
+ *
+ * $input = [
+ * ['k1' => 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 1, 'k2' => 5]
+ * ]
+ *
+ * array_column_index_unique($input, 'k1') will returns:
+ *
+ * [
+ * 1 => ['k1' => 1, 'k2' => 2],
+ * 3 => ['k1' => 3, 'k2' => 4],
+ * ]
+ *
+ * @param array $input
+ * @param string $column
+ * @return array
+ */
+function array_column_index_unique(array &$input, $column)
+{
+ $result = array();
+
+ foreach ($input as &$row) {
+ if (isset($row[$column]) && ! isset($result[$row[$column]])) {
+ $result[$row[$column]] = $row;
+ }
+ }
+
+ return $result;
+}
+
+/**
* Sum all values from a single column in the input array
*
* $input = [
@@ -105,6 +136,27 @@ function build_app_version($ref, $commit_hash)
}
/**
+ * Get upload max size.
+ *
+ * @return string
+ */
+function get_upload_max_size()
+{
+ return min(ini_get('upload_max_filesize'), ini_get('post_max_size'));
+}
+
+/**
+ * Get file extension
+ *
+ * @param $filename
+ * @return string
+ */
+function get_file_extension($filename)
+{
+ return strtolower(pathinfo($filename, PATHINFO_EXTENSION));
+}
+
+/**
* Translate a string
*
* @return string