From 27411ebedcaee97bf83fda033f4858a9ebef49f5 Mon Sep 17 00:00:00 2001 From: zephyr Date: Mon, 1 Jun 2026 21:23:12 -0700 Subject: [PATCH] =?UTF-8?q?=E7=9C=8B=E6=9D=BF=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .htaccess | 34 + CONTRIBUTING.md | 140 + ChangeLog | 2140 +++++++++++ LICENSE | 21 + app/.htaccess | 7 + app/Action/Base.php | 305 ++ app/Action/CommentCreation.php | 87 + app/Action/CommentCreationMoveTaskColumn.php | 100 + app/Action/StopSubtaskTimerMoveTaskColumn.php | 102 + app/Action/SubtaskTimerMoveTaskColumn.php | 107 + app/Action/TaskAssignCategoryColor.php | 98 + app/Action/TaskAssignCategoryLabel.php | 91 + app/Action/TaskAssignCategoryLink.php | 102 + .../TaskAssignCategorySwimlaneChange.php | 99 + app/Action/TaskAssignColorCategory.php | 98 + app/Action/TaskAssignColorColumn.php | 99 + app/Action/TaskAssignColorLink.php | 97 + app/Action/TaskAssignColorOnDueDate.php | 99 + app/Action/TaskAssignColorOnStartDate.php | 99 + app/Action/TaskAssignColorPriority.php | 98 + app/Action/TaskAssignColorSwimlane.php | 99 + app/Action/TaskAssignColorUser.php | 99 + app/Action/TaskAssignCreator.php | 98 + app/Action/TaskAssignCurrentUser.php | 95 + app/Action/TaskAssignCurrentUserColumn.php | 101 + ...ignCurrentUserColumnIfNoUserAlreadySet.php | 103 + app/Action/TaskAssignDueDateOnCreation.php | 96 + app/Action/TaskAssignDueDateOnMoveColumn.php | 97 + app/Action/TaskAssignPrioritySwimlane.php | 99 + app/Action/TaskAssignSpecificUser.php | 99 + .../TaskAssignToUserOnCreationInColumn.php | 109 + app/Action/TaskAssignUser.php | 88 + app/Action/TaskAssignUserSwimlaneChange.php | 99 + app/Action/TaskClose.php | 80 + app/Action/TaskCloseColumn.php | 90 + app/Action/TaskCloseNoActivity.php | 95 + app/Action/TaskCloseNoActivityColumn.php | 96 + app/Action/TaskCloseNotMovedColumn.php | 96 + app/Action/TaskCreation.php | 89 + app/Action/TaskDuplicateAnotherProject.php | 101 + app/Action/TaskEmail.php | 119 + app/Action/TaskEmailNoActivity.php | 124 + app/Action/TaskMoveAnotherProject.php | 94 + app/Action/TaskMoveColumnAssigned.php | 104 + app/Action/TaskMoveColumnCategoryChange.php | 105 + app/Action/TaskMoveColumnClosed.php | 102 + app/Action/TaskMoveColumnNotMovedPeriod.php | 104 + app/Action/TaskMoveColumnOnDueDate.php | 103 + app/Action/TaskMoveColumnOnStartDate.php | 101 + app/Action/TaskMoveColumnUnAssigned.php | 104 + app/Action/TaskMoveSwimlaneAssigned.php | 107 + app/Action/TaskMoveSwimlaneCategoryChange.php | 103 + app/Action/TaskOpen.php | 80 + app/Action/TaskUpdateStartDate.php | 97 + .../TaskUpdateStartDateOnMoveColumn.php | 96 + app/Analytic/AverageLeadCycleTimeAnalytic.php | 116 + .../AverageTimeSpentColumnAnalytic.php | 154 + .../EstimatedActualColumnAnalytic.php | 49 + .../EstimatedTimeComparisonAnalytic.php | 50 + app/Analytic/TaskDistributionAnalytic.php | 48 + app/Analytic/UserDistributionAnalytic.php | 56 + app/Api/Authorization/ActionAuthorization.php | 19 + .../Authorization/CategoryAuthorization.php | 19 + app/Api/Authorization/ColumnAuthorization.php | 19 + .../Authorization/CommentAuthorization.php | 46 + .../Authorization/ProcedureAuthorization.php | 32 + .../Authorization/ProjectAuthorization.php | 35 + .../Authorization/SubtaskAuthorization.php | 19 + app/Api/Authorization/TagAuthorization.php | 23 + app/Api/Authorization/TaskAuthorization.php | 19 + .../Authorization/TaskFileAuthorization.php | 19 + .../Authorization/TaskLinkAuthorization.php | 19 + app/Api/Authorization/UserAuthorization.php | 22 + .../Middleware/AuthenticationMiddleware.php | 101 + app/Api/Procedure/ActionProcedure.php | 91 + app/Api/Procedure/AppProcedure.php | 47 + app/Api/Procedure/BaseProcedure.php | 40 + app/Api/Procedure/BoardProcedure.php | 24 + app/Api/Procedure/CategoryProcedure.php | 61 + app/Api/Procedure/ColumnProcedure.php | 66 + app/Api/Procedure/CommentProcedure.php | 113 + app/Api/Procedure/GroupMemberProcedure.php | 37 + app/Api/Procedure/GroupProcedure.php | 49 + app/Api/Procedure/LinkProcedure.php | 111 + app/Api/Procedure/MeProcedure.php | 67 + app/Api/Procedure/ProjectFileProcedure.php | 80 + .../Procedure/ProjectMetaDataProcedure.php | 38 + .../Procedure/ProjectPermissionProcedure.php | 69 + app/Api/Procedure/ProjectProcedure.php | 152 + app/Api/Procedure/SubtaskProcedure.php | 80 + .../SubtaskTimeTrackingProcedure.php | 39 + app/Api/Procedure/SwimlaneProcedure.php | 121 + app/Api/Procedure/TagProcedure.php | 49 + .../Procedure/TaskExternalLinkProcedure.php | 123 + app/Api/Procedure/TaskFileProcedure.php | 81 + app/Api/Procedure/TaskLinkProcedure.php | 109 + app/Api/Procedure/TaskMetadataProcedure.php | 38 + app/Api/Procedure/TaskProcedure.php | 222 ++ app/Api/Procedure/TaskTagProcedure.php | 28 + app/Api/Procedure/UserProcedure.php | 147 + app/Auth/ApiAccessTokenAuth.php | 118 + app/Auth/DatabaseAuth.php | 126 + app/Auth/LdapAuth.php | 176 + app/Auth/RememberMeAuth.php | 79 + app/Auth/ReverseProxyAuth.php | 91 + app/Auth/TotpAuth.php | 146 + app/Console/BaseCommand.php | 69 + app/Console/CronjobCommand.php | 33 + app/Console/CssCommand.php | 131 + app/Console/DatabaseMigrationCommand.php | 24 + app/Console/DatabaseVersionCommand.php | 24 + app/Console/JobCommand.php | 36 + app/Console/JsCommand.php | 91 + app/Console/LocaleComparatorCommand.php | 82 + app/Console/LocaleSyncCommand.php | 55 + app/Console/PluginInstallCommand.php | 38 + app/Console/PluginUninstallCommand.php | 38 + app/Console/PluginUpgradeCommand.php | 56 + app/Console/ProjectActivityArchiveCommand.php | 22 + app/Console/ProjectArchiveCommand.php | 31 + .../ProjectDailyColumnStatsExportCommand.php | 35 + .../ProjectDailyStatsCalculationCommand.php | 29 + app/Console/ResetPasswordCommand.php | 80 + app/Console/ResetTwoFactorCommand.php | 37 + app/Console/SubtaskExportCommand.php | 35 + app/Console/TaskExportCommand.php | 35 + .../TaskOverdueNotificationCommand.php | 206 + app/Console/TaskTriggerCommand.php | 52 + app/Console/TransitionExportCommand.php | 35 + app/Console/VersionCommand.php | 29 + app/Console/WorkerCommand.php | 29 + app/Controller/ActionController.php | 79 + app/Controller/ActionCreationController.php | 127 + app/Controller/ActivityController.php | 62 + app/Controller/AnalyticController.php | 192 + app/Controller/AppController.php | 47 + app/Controller/AuthController.php | 78 + app/Controller/AvatarFileController.php | 122 + app/Controller/BaseController.php | 338 ++ app/Controller/BoardAjaxController.php | 150 + app/Controller/BoardPopoverController.php | 47 + app/Controller/BoardTooltipController.php | 112 + app/Controller/BoardViewController.php | 72 + app/Controller/CaptchaController.php | 29 + app/Controller/CategoryController.php | 161 + app/Controller/ColumnController.php | 200 + .../ColumnMoveRestrictionController.php | 103 + .../ColumnRestrictionController.php | 103 + app/Controller/CommentController.php | 178 + app/Controller/CommentListController.php | 48 + app/Controller/CommentMailController.php | 74 + app/Controller/ConfigController.php | 254 ++ app/Controller/CronjobController.php | 32 + app/Controller/CurrencyController.php | 104 + app/Controller/CustomFilterController.php | 193 + app/Controller/DashboardController.php | 78 + app/Controller/DocumentationController.php | 19 + app/Controller/ExportController.php | 114 + .../ExternalTaskCreationController.php | 97 + app/Controller/ExternalTaskViewController.php | 30 + app/Controller/FeedController.php | 58 + app/Controller/FileViewerController.php | 163 + app/Controller/GroupAjaxController.php | 24 + app/Controller/GroupCreationController.php | 49 + app/Controller/GroupListController.php | 186 + .../GroupModificationController.php | 53 + app/Controller/ICalendarController.php | 118 + app/Controller/LinkController.php | 162 + app/Controller/OAuthController.php | 130 + app/Controller/PasswordResetController.php | 132 + app/Controller/PluginController.php | 153 + .../PredefinedTaskDescriptionController.php | 116 + .../ProjectActionDuplicationController.php | 44 + app/Controller/ProjectCreationController.php | 139 + app/Controller/ProjectEditController.php | 82 + app/Controller/ProjectFileController.php | 89 + app/Controller/ProjectListController.php | 47 + app/Controller/ProjectOverviewController.php | 33 + .../ProjectPermissionController.php | 221 ++ .../ProjectPredefinedContentController.php | 50 + app/Controller/ProjectRoleController.php | 162 + .../ProjectRoleRestrictionController.php | 96 + app/Controller/ProjectStatusController.php | 100 + app/Controller/ProjectTagController.php | 182 + .../ProjectUserOverviewController.php | 130 + app/Controller/ProjectViewController.php | 228 ++ app/Controller/SearchController.php | 70 + app/Controller/SubtaskController.php | 222 ++ app/Controller/SubtaskConverterController.php | 45 + .../SubtaskRestrictionController.php | 64 + app/Controller/SubtaskStatusController.php | 102 + app/Controller/SwimlaneController.php | 219 ++ app/Controller/TagController.php | 124 + app/Controller/TaskAjaxController.php | 87 + .../TaskBulkChangePropertyController.php | 102 + app/Controller/TaskBulkController.php | 108 + .../TaskBulkMoveColumnController.php | 44 + app/Controller/TaskCreationController.php | 187 + app/Controller/TaskDuplicationController.php | 159 + app/Controller/TaskExternalLinkController.php | 184 + app/Controller/TaskFileController.php | 108 + app/Controller/TaskImportController.php | 76 + app/Controller/TaskInternalLinkController.php | 183 + app/Controller/TaskListController.php | 71 + app/Controller/TaskMailController.php | 62 + app/Controller/TaskModificationController.php | 182 + app/Controller/TaskMovePositionController.php | 53 + app/Controller/TaskPopoverController.php | 26 + app/Controller/TaskRecurrenceController.php | 66 + app/Controller/TaskReorderController.php | 43 + app/Controller/TaskStatusController.php | 62 + app/Controller/TaskSuppressionController.php | 53 + app/Controller/TaskViewController.php | 144 + app/Controller/TwoFactorController.php | 232 ++ app/Controller/UserAjaxController.php | 48 + app/Controller/UserApiAccessController.php | 59 + app/Controller/UserCreationController.php | 84 + app/Controller/UserCredentialController.php | 134 + app/Controller/UserImportController.php | 84 + app/Controller/UserInviteController.php | 119 + app/Controller/UserListController.php | 60 + app/Controller/UserModificationController.php | 77 + app/Controller/UserStatusController.php | 111 + app/Controller/UserViewController.php | 235 ++ app/Controller/WebNotificationController.php | 96 + app/Core/Action/ActionManager.php | 167 + app/Core/Base.php | 261 ++ app/Core/Cache/BaseCache.php | 38 + app/Core/Cache/CacheInterface.php | 45 + app/Core/Cache/MemoryCache.php | 65 + .../Controller/AccessForbiddenException.php | 14 + app/Core/Controller/BaseException.php | 52 + app/Core/Controller/BaseMiddleware.php | 58 + app/Core/Controller/PageNotFoundException.php | 14 + app/Core/Controller/Runner.php | 105 + app/Core/Csv.php | 222 ++ app/Core/DateParser.php | 326 ++ app/Core/Event/EventManager.php | 66 + .../ExternalLink/ExternalLinkInterface.php | 36 + app/Core/ExternalLink/ExternalLinkManager.php | 197 + .../ExternalLinkProviderInterface.php | 71 + .../ExternalLinkProviderNotFound.php | 15 + .../ExternalTask/AccessForbiddenException.php | 13 + .../ExternalTask/ExternalTaskException.php | 15 + .../ExternalTask/ExternalTaskInterface.php | 26 + app/Core/ExternalTask/ExternalTaskManager.php | 68 + .../ExternalTaskProviderInterface.php | 94 + app/Core/ExternalTask/NotFoundException.php | 13 + .../ProviderNotFoundException.php | 13 + app/Core/Filter/CriteriaInterface.php | 40 + app/Core/Filter/FilterInterface.php | 56 + app/Core/Filter/FormatterInterface.php | 31 + app/Core/Filter/Lexer.php | 159 + app/Core/Filter/LexerBuilder.php | 151 + app/Core/Filter/OrCriteria.php | 68 + app/Core/Filter/QueryBuilder.php | 115 + .../Group/GroupBackendProviderInterface.php | 21 + app/Core/Group/GroupManager.php | 71 + app/Core/Group/GroupProviderInterface.php | 40 + app/Core/Helper.php | 119 + app/Core/Http/Client.php | 451 +++ app/Core/Http/ClientException.php | 9 + app/Core/Http/InvalidStatusException.php | 26 + app/Core/Http/OAuth2.php | 151 + app/Core/Http/RememberMeCookie.php | 120 + app/Core/Http/Request.php | 596 +++ app/Core/Http/Response.php | 419 +++ app/Core/Http/Route.php | 188 + app/Core/Http/Router.php | 131 + app/Core/Ldap/Client.php | 247 ++ app/Core/Ldap/ClientException.php | 15 + app/Core/Ldap/ConnectionException.php | 15 + app/Core/Ldap/Entries.php | 63 + app/Core/Ldap/Entry.php | 99 + app/Core/Ldap/Group.php | 130 + app/Core/Ldap/Query.php | 98 + app/Core/Ldap/User.php | 366 ++ app/Core/Log/Base.php | 89 + app/Core/Log/File.php | 48 + app/Core/Log/Logger.php | 94 + app/Core/Log/Stderr.php | 25 + app/Core/Log/Stdout.php | 25 + app/Core/Log/Syslog.php | 72 + app/Core/Log/System.php | 25 + app/Core/Mail/Client.php | 138 + app/Core/Mail/ClientInterface.php | 25 + app/Core/Mail/Transport/Mail.php | 70 + app/Core/Mail/Transport/Sendmail.php | 25 + app/Core/Mail/Transport/Smtp.php | 43 + app/Core/Markdown.php | 186 + .../Notification/NotificationInterface.php | 32 + app/Core/ObjectStorage/FileStorage.php | 197 + .../ObjectStorage/ObjectStorageException.php | 9 + .../ObjectStorage/ObjectStorageInterface.php | 67 + app/Core/Paginator.php | 543 +++ app/Core/Plugin/Base.php | 147 + app/Core/Plugin/Directory.php | 52 + app/Core/Plugin/Hook.php | 116 + app/Core/Plugin/Installer.php | 144 + app/Core/Plugin/Loader.php | 137 + app/Core/Plugin/PluginException.php | 15 + app/Core/Plugin/PluginInstallerException.php | 13 + app/Core/Plugin/SchemaHandler.php | 122 + app/Core/Plugin/Version.php | 38 + app/Core/Queue/JobHandler.php | 90 + app/Core/Queue/QueueManager.php | 75 + app/Core/Security/AccessMap.php | 175 + app/Core/Security/AuthenticationManager.php | 200 + .../AuthenticationProviderInterface.php | 28 + app/Core/Security/Authorization.php | 46 + .../OAuthAuthenticationProviderInterface.php | 46 + ...ptionalAuthenticationProviderInterface.php | 20 + ...asswordAuthenticationProviderInterface.php | 36 + .../PostAuthenticationProviderInterface.php | 60 + .../PreAuthenticationProviderInterface.php | 20 + app/Core/Security/Role.php | 76 + .../SessionCheckProviderInterface.php | 20 + app/Core/Security/Token.php | 135 + app/Core/Session/FlashMessage.php | 76 + app/Core/Session/SessionHandler.php | 107 + app/Core/Session/SessionManager.php | 117 + app/Core/Template.php | 124 + app/Core/Thumbnail.php | 177 + app/Core/Tool.php | 109 + app/Core/Translator.php | 214 ++ app/Core/User/Avatar/AvatarManager.php | 93 + .../User/Avatar/AvatarProviderInterface.php | 30 + app/Core/User/GroupSync.php | 65 + .../User/UserBackendProviderInterface.php | 21 + app/Core/User/UserManager.php | 71 + app/Core/User/UserProfile.php | 68 + app/Core/User/UserProperty.php | 74 + app/Core/User/UserProviderInterface.php | 103 + app/Core/User/UserSession.php | 300 ++ app/Core/User/UserSync.php | 82 + .../ColumnMoveRestrictionCacheDecorator.php | 59 + .../ColumnRestrictionCacheDecorator.php | 59 + app/Decorator/MetadataCacheDecorator.php | 96 + .../ProjectRoleRestrictionCacheDecorator.php | 59 + app/Decorator/UserCacheDecorator.php | 59 + app/Event/AuthFailureEvent.php | 44 + app/Event/AuthSuccessEvent.php | 43 + app/Event/CommentEvent.php | 7 + app/Event/GenericEvent.php | 71 + app/Event/ProjectFileEvent.php | 15 + app/Event/SubtaskEvent.php | 7 + app/Event/TaskEvent.php | 7 + app/Event/TaskFileEvent.php | 7 + app/Event/TaskLinkEvent.php | 7 + app/Event/TaskListEvent.php | 11 + app/Event/UserProfileSyncEvent.php | 64 + app/EventBuilder/BaseEventBuilder.php | 44 + app/EventBuilder/CommentEventBuilder.php | 98 + app/EventBuilder/EventIteratorBuilder.php | 63 + app/EventBuilder/ProjectFileEventBuilder.php | 77 + app/EventBuilder/SubtaskEventBuilder.php | 125 + app/EventBuilder/TaskEventBuilder.php | 233 ++ app/EventBuilder/TaskFileEventBuilder.php | 94 + app/EventBuilder/TaskLinkEventBuilder.php | 89 + app/Export/SubtaskExport.php | 124 + app/Export/TaskExport.php | 169 + app/Export/TransitionExport.php | 76 + app/ExternalLink/AttachmentLink.php | 26 + app/ExternalLink/AttachmentLinkProvider.php | 117 + app/ExternalLink/BaseLink.php | 44 + app/ExternalLink/BaseLinkProvider.php | 33 + app/ExternalLink/FileLink.php | 26 + app/ExternalLink/FileLinkProvider.php | 89 + app/ExternalLink/WebLink.php | 43 + app/ExternalLink/WebLinkProvider.php | 77 + app/Filter/BaseComparisonFilter.php | 48 + app/Filter/BaseDateFilter.php | 103 + app/Filter/BaseDateRangeFilter.php | 54 + app/Filter/BaseFilter.php | 74 + .../ProjectActivityCreationDateFilter.php | 38 + app/Filter/ProjectActivityCreatorFilter.php | 66 + app/Filter/ProjectActivityProjectIdFilter.php | 38 + .../ProjectActivityProjectIdsFilter.php | 43 + .../ProjectActivityProjectNameFilter.php | 38 + app/Filter/ProjectActivityTaskIdFilter.php | 38 + .../ProjectActivityTaskStatusFilter.php | 43 + app/Filter/ProjectActivityTaskTitleFilter.php | 25 + app/Filter/ProjectGroupRoleProjectFilter.php | 38 + app/Filter/ProjectGroupRoleUsernameFilter.php | 44 + app/Filter/ProjectIdsFilter.php | 43 + app/Filter/ProjectStatusFilter.php | 45 + app/Filter/ProjectTypeFilter.php | 45 + app/Filter/ProjectUserRoleProjectFilter.php | 38 + app/Filter/ProjectUserRoleUsernameFilter.php | 41 + app/Filter/TaskAssigneeFilter.php | 79 + app/Filter/TaskCategoryFilter.php | 49 + app/Filter/TaskColorFilter.php | 60 + app/Filter/TaskColumnFilter.php | 44 + app/Filter/TaskCommentFilter.php | 76 + app/Filter/TaskCompletionDateFilter.php | 38 + app/Filter/TaskCompletionDateRangeFilter.php | 38 + app/Filter/TaskCreationDateFilter.php | 38 + app/Filter/TaskCreationDateRangeFilter.php | 38 + app/Filter/TaskCreatorFilter.php | 78 + app/Filter/TaskDescriptionFilter.php | 38 + app/Filter/TaskDueDateFilter.php | 45 + app/Filter/TaskDueDateRangeFilter.php | 44 + app/Filter/TaskIdExclusionFilter.php | 38 + app/Filter/TaskIdFilter.php | 38 + app/Filter/TaskLinkFilter.php | 79 + app/Filter/TaskModificationDateFilter.php | 38 + .../TaskModificationDateRangeFilter.php | 38 + app/Filter/TaskMovedDateFilter.php | 38 + app/Filter/TaskMovedDateRangeFilter.php | 38 + app/Filter/TaskPriorityFilter.php | 38 + app/Filter/TaskProjectFilter.php | 46 + app/Filter/TaskProjectsFilter.php | 43 + app/Filter/TaskReferenceFilter.php | 51 + app/Filter/TaskScoreFilter.php | 37 + app/Filter/TaskStartDateFilter.php | 38 + app/Filter/TaskStartsWithIdFilter.php | 38 + app/Filter/TaskStatusFilter.php | 43 + app/Filter/TaskSubtaskAssigneeFilter.php | 133 + app/Filter/TaskSwimlaneFilter.php | 40 + app/Filter/TaskTagFilter.php | 87 + app/Filter/TaskTitleFilter.php | 46 + app/Filter/UserNameFilter.php | 35 + app/Formatter/BaseFormatter.php | 36 + app/Formatter/BoardColumnFormatter.php | 115 + app/Formatter/BoardFormatter.php | 80 + app/Formatter/BoardSwimlaneFormatter.php | 138 + app/Formatter/BoardTaskFormatter.php | 101 + app/Formatter/GroupAutoCompleteFormatter.php | 58 + .../ProjectActivityEventFormatter.php | 79 + app/Formatter/ProjectApiFormatter.php | 46 + app/Formatter/ProjectsApiFormatter.php | 38 + app/Formatter/SubtaskListFormatter.php | 34 + .../SubtaskTimeTrackingCalendarFormatter.php | 38 + app/Formatter/TaskApiFormatter.php | 37 + app/Formatter/TaskAutoCompleteFormatter.php | 56 + app/Formatter/TaskICalFormatter.php | 151 + app/Formatter/TaskListFormatter.php | 30 + .../TaskListSubtaskAssigneeFormatter.php | 56 + app/Formatter/TaskListSubtaskFormatter.php | 29 + app/Formatter/TaskSuggestMenuFormatter.php | 63 + app/Formatter/TasksApiFormatter.php | 38 + app/Formatter/UserAutoCompleteFormatter.php | 60 + app/Formatter/UserMentionFormatter.php | 63 + app/Group/DatabaseBackendGroupProvider.php | 34 + app/Group/DatabaseGroupProvider.php | 66 + app/Group/LdapBackendGroupProvider.php | 56 + app/Group/LdapGroupProvider.php | 76 + app/Helper/AppHelper.php | 209 ++ app/Helper/AssetHelper.php | 69 + app/Helper/AvatarHelper.php | 68 + app/Helper/BoardHelper.php | 27 + app/Helper/CommentHelper.php | 23 + app/Helper/DateHelper.php | 171 + app/Helper/FileHelper.php | 152 + app/Helper/FormHelper.php | 482 +++ app/Helper/HookHelper.php | 104 + app/Helper/LayoutHelper.php | 218 ++ app/Helper/MailHelper.php | 68 + app/Helper/ModalHelper.php | 96 + app/Helper/ModelHelper.php | 94 + app/Helper/ProjectActivityHelper.php | 104 + app/Helper/ProjectHeaderHelper.php | 87 + app/Helper/ProjectRoleHelper.php | 338 ++ app/Helper/SubtaskHelper.php | 174 + app/Helper/TaskHelper.php | 380 ++ app/Helper/TextHelper.php | 121 + app/Helper/UrlHelper.php | 214 ++ app/Helper/UserHelper.php | 198 + app/Import/TaskImport.php | 179 + app/Import/UserImport.php | 126 + app/Job/BaseJob.php | 33 + app/Job/CommentEventJob.php | 50 + app/Job/EmailJob.php | 55 + app/Job/HttpAsyncJob.php | 44 + app/Job/NotificationJob.php | 43 + app/Job/ProjectFileEventJob.php | 44 + app/Job/ProjectMetricJob.php | 40 + app/Job/SubtaskEventJob.php | 49 + app/Job/TaskEventJob.php | 75 + app/Job/TaskFileEventJob.php | 44 + app/Job/TaskLinkEventJob.php | 44 + app/Job/UserMentionJob.php | 75 + app/Locale/ar_SY/translations.php | 1472 ++++++++ app/Locale/bg_BG/translations.php | 1472 ++++++++ app/Locale/bs_BA/translations.php | 1472 ++++++++ app/Locale/ca_ES/translations.php | 1472 ++++++++ app/Locale/cs_CZ/translations.php | 1472 ++++++++ app/Locale/da_DK/translations.php | 1472 ++++++++ app/Locale/de_DE/translations.php | 1472 ++++++++ app/Locale/de_DE_du/translations.php | 1472 ++++++++ app/Locale/el_GR/translations.php | 1472 ++++++++ app/Locale/es_ES/translations.php | 1472 ++++++++ app/Locale/es_VE/translations.php | 1472 ++++++++ app/Locale/fa_IR/translations.php | 1472 ++++++++ app/Locale/fi_FI/translations.php | 1472 ++++++++ app/Locale/fr_FR/translations.php | 1472 ++++++++ app/Locale/hr_HR/translations.php | 1472 ++++++++ app/Locale/hu_HU/translations.php | 1472 ++++++++ app/Locale/id_ID/translations.php | 1472 ++++++++ app/Locale/it_IT/translations.php | 1472 ++++++++ app/Locale/ja_JP/translations.php | 1472 ++++++++ app/Locale/ko_KR/translations.php | 1472 ++++++++ app/Locale/mk_MK/translations.php | 1472 ++++++++ app/Locale/my_MY/translations.php | 1472 ++++++++ app/Locale/nb_NO/translations.php | 1472 ++++++++ app/Locale/nl_NL/translations.php | 1472 ++++++++ app/Locale/pl_PL/translations.php | 1472 ++++++++ app/Locale/pt_BR/translations.php | 1472 ++++++++ app/Locale/pt_PT/translations.php | 1472 ++++++++ app/Locale/ro_RO/translations.php | 1472 ++++++++ app/Locale/ru_RU/translations.php | 1472 ++++++++ app/Locale/sk_SK/translations.php | 1472 ++++++++ app/Locale/sr_Latn_RS/translations.php | 1472 ++++++++ app/Locale/sv_SE/translations.php | 1472 ++++++++ app/Locale/th_TH/translations.php | 1472 ++++++++ app/Locale/tr_TR/translations.php | 1472 ++++++++ app/Locale/uk_UA/translations.php | 1472 ++++++++ app/Locale/vi_VN/translations.php | 1472 ++++++++ app/Locale/zh_CN/translations.php | 1472 ++++++++ app/Locale/zh_TW/translations.php | 1472 ++++++++ .../ApplicationAuthorizationMiddleware.php | 27 + app/Middleware/AuthenticationMiddleware.php | 60 + app/Middleware/BootstrapMiddleware.php | 46 + .../PostAuthenticationMiddleware.php | 36 + .../ProjectAuthorizationMiddleware.php | 34 + app/Model/ActionModel.php | 202 + app/Model/ActionParameterModel.php | 173 + app/Model/AvatarFileModel.php | 159 + app/Model/BoardModel.php | 97 + app/Model/CaptchaModel.php | 63 + app/Model/CategoryModel.php | 228 ++ app/Model/ColorModel.php | 231 ++ app/Model/ColumnModel.php | 262 ++ app/Model/ColumnMoveRestrictionModel.php | 174 + app/Model/ColumnRestrictionModel.php | 192 + app/Model/CommentModel.php | 222 ++ app/Model/ConfigModel.php | 122 + app/Model/CurrencyModel.php | 123 + app/Model/CustomFilterModel.php | 141 + app/Model/FileModel.php | 416 ++ app/Model/GroupMemberModel.php | 131 + app/Model/GroupModel.php | 158 + app/Model/InviteModel.php | 73 + app/Model/LanguageModel.php | 237 ++ app/Model/LastLoginModel.php | 92 + app/Model/LinkModel.php | 178 + app/Model/MetadataModel.php | 140 + app/Model/NotificationModel.php | 100 + app/Model/NotificationTypeModel.php | 128 + app/Model/PasswordResetModel.php | 95 + app/Model/PredefinedTaskDescriptionModel.php | 52 + app/Model/ProjectActivityModel.php | 79 + app/Model/ProjectDailyColumnStatsModel.php | 254 ++ app/Model/ProjectDailyStatsModel.php | 76 + app/Model/ProjectDuplicationModel.php | 190 + app/Model/ProjectFileModel.php | 85 + app/Model/ProjectGroupRoleModel.php | 197 + app/Model/ProjectMetadataModel.php | 54 + app/Model/ProjectModel.php | 611 +++ app/Model/ProjectNotificationModel.php | 67 + app/Model/ProjectNotificationTypeModel.php | 57 + app/Model/ProjectPermissionModel.php | 199 + app/Model/ProjectRoleModel.php | 234 ++ app/Model/ProjectRoleRestrictionModel.php | 167 + app/Model/ProjectTaskDuplicationModel.php | 35 + app/Model/ProjectTaskPriorityModel.php | 74 + app/Model/ProjectUserRoleModel.php | 275 ++ app/Model/RememberMeSessionModel.php | 152 + app/Model/SettingModel.php | 127 + app/Model/SubtaskModel.php | 337 ++ app/Model/SubtaskPositionModel.php | 47 + app/Model/SubtaskStatusModel.php | 88 + app/Model/SubtaskTaskConversionModel.php | 53 + app/Model/SubtaskTimeTrackingModel.php | 291 ++ app/Model/SwimlaneModel.php | 459 +++ app/Model/TagDuplicationModel.php | 96 + app/Model/TagModel.php | 217 ++ app/Model/TaskAnalyticModel.php | 72 + app/Model/TaskCreationModel.php | 94 + app/Model/TaskDuplicationModel.php | 169 + app/Model/TaskExternalLinkModel.php | 103 + app/Model/TaskFileModel.php | 115 + app/Model/TaskFinderModel.php | 415 ++ app/Model/TaskLinkModel.php | 305 ++ app/Model/TaskMetadataModel.php | 35 + app/Model/TaskModel.php | 156 + app/Model/TaskModificationModel.php | 134 + app/Model/TaskPositionModel.php | 315 ++ app/Model/TaskProjectDuplicationModel.php | 82 + app/Model/TaskProjectMoveModel.php | 65 + app/Model/TaskRecurrenceModel.php | 159 + app/Model/TaskReorderModel.php | 107 + app/Model/TaskStatusModel.php | 144 + app/Model/TaskTagModel.php | 189 + app/Model/ThemeModel.php | 29 + app/Model/TimezoneModel.php | 54 + app/Model/TransitionModel.php | 130 + app/Model/UserLockingModel.php | 105 + app/Model/UserMetadataModel.php | 38 + app/Model/UserModel.php | 448 +++ app/Model/UserNotificationFilterModel.php | 206 + app/Model/UserNotificationModel.php | 183 + app/Model/UserNotificationTypeModel.php | 52 + app/Model/UserUnreadNotificationModel.php | 117 + .../ActivityStreamNotification.php | 48 + app/Notification/MailNotification.php | 84 + app/Notification/WebNotification.php | 47 + app/Notification/WebhookNotification.php | 62 + app/Pagination/DashboardPagination.php | 54 + app/Pagination/ProjectPagination.php | 38 + app/Pagination/SubtaskPagination.php | 38 + app/Pagination/TaskPagination.php | 39 + app/Pagination/UserPagination.php | 32 + app/Schema/Migration.php | 79 + app/Schema/Mssql.php | 733 ++++ app/Schema/Mysql.php | 1700 +++++++++ app/Schema/Postgres.php | 1473 ++++++++ app/Schema/Sql/mysql.sql | 811 ++++ app/Schema/Sql/postgres.sql | 2751 ++++++++++++++ app/Schema/Sqlite.php | 1572 ++++++++ app/ServiceProvider/ActionProvider.php | 128 + app/ServiceProvider/ApiProvider.php | 89 + .../AuthenticationProvider.php | 239 ++ app/ServiceProvider/AvatarProvider.php | 33 + app/ServiceProvider/CacheProvider.php | 76 + app/ServiceProvider/ClassProvider.php | 195 + app/ServiceProvider/CommandProvider.php | 78 + app/ServiceProvider/DatabaseProvider.php | 196 + .../EventDispatcherProvider.php | 42 + app/ServiceProvider/ExternalLinkProvider.php | 36 + app/ServiceProvider/ExternalTaskProvider.php | 29 + app/ServiceProvider/FilterProvider.php | 231 ++ app/ServiceProvider/FormatterProvider.php | 53 + app/ServiceProvider/GroupProvider.php | 40 + app/ServiceProvider/HelperProvider.php | 47 + app/ServiceProvider/JobProvider.php | 72 + app/ServiceProvider/LoggingProvider.php | 57 + app/ServiceProvider/MailProvider.php | 34 + app/ServiceProvider/NotificationProvider.php | 45 + app/ServiceProvider/ObjectStorageProvider.php | 51 + app/ServiceProvider/PluginProvider.php | 31 + app/ServiceProvider/QueueProvider.php | 29 + app/ServiceProvider/RouteProvider.php | 285 ++ app/ServiceProvider/SessionProvider.php | 37 + app/ServiceProvider/UserProvider.php | 35 + app/Subscriber/AuthSubscriber.php | 117 + app/Subscriber/BaseSubscriber.php | 15 + app/Subscriber/BootstrapSubscriber.php | 42 + app/Subscriber/LdapUserPhotoSubscriber.php | 49 + app/Subscriber/NotificationSubscriber.php | 47 + .../ProjectDailySummarySubscriber.php | 27 + .../ProjectModificationDateSubscriber.php | 33 + app/Subscriber/RecurringTaskSubscriber.php | 42 + app/Subscriber/TransitionSubscriber.php | 28 + app/Template/action/index.php | 80 + app/Template/action/remove.php | 15 + app/Template/action_creation/create.php | 13 + app/Template/action_creation/event.php | 23 + app/Template/action_creation/params.php | 54 + app/Template/activity/filter_dropdown.php | 14 + app/Template/activity/project.php | 12 + app/Template/activity/task.php | 12 + app/Template/activity/user.php | 4 + app/Template/analytic/avg_time_columns.php | 31 + app/Template/analytic/burndown.php | 26 + app/Template/analytic/cfd.php | 23 + .../analytic/estimated_actual_column.php | 30 + app/Template/analytic/layout.php | 14 + app/Template/analytic/lead_cycle_time.php | 33 + app/Template/analytic/sidebar.php | 30 + app/Template/analytic/task_distribution.php | 34 + app/Template/analytic/time_comparison.php | 63 + app/Template/analytic/user_distribution.php | 34 + app/Template/app/filters_helper.php | 21 + app/Template/app/forbidden.php | 5 + app/Template/app/notfound.php | 5 + app/Template/auth/index.php | 53 + app/Template/avatar_file/show.php | 23 + app/Template/board/table_column.php | 127 + app/Template/board/table_column_first.php | 20 + app/Template/board/table_container.php | 67 + app/Template/board/table_swimlane.php | 25 + app/Template/board/table_tasks.php | 40 + app/Template/board/task_avatar.php | 20 + app/Template/board/task_footer.php | 141 + app/Template/board/task_private.php | 71 + app/Template/board/task_public.php | 25 + app/Template/board/tooltip_description.php | 5 + app/Template/board/tooltip_external_links.php | 22 + app/Template/board/tooltip_files.php | 24 + app/Template/board/tooltip_subtasks.php | 24 + app/Template/board/tooltip_tasklinks.php | 34 + app/Template/board/view_private.php | 12 + app/Template/board/view_public.php | 11 + .../board_popover/close_all_tasks_column.php | 15 + app/Template/category/create.php | 14 + app/Template/category/edit.php | 18 + app/Template/category/index.php | 42 + app/Template/category/remove.php | 15 + app/Template/column/create.php | 19 + app/Template/column/edit.php | 20 + app/Template/column/index.php | 64 + app/Template/column/remove.php | 15 + .../column_move_restriction/create.php | 20 + .../column_move_restriction/remove.php | 15 + app/Template/column_restriction/create.php | 16 + app/Template/column_restriction/remove.php | 15 + app/Template/comment/create.php | 36 + app/Template/comment/edit.php | 11 + app/Template/comment/remove.php | 21 + app/Template/comment/show.php | 75 + app/Template/comment_list/create.php | 30 + app/Template/comment_list/show.php | 31 + app/Template/comment_mail/create.php | 73 + app/Template/comment_mail/email.php | 3 + app/Template/config/about.php | 94 + app/Template/config/api.php | 17 + app/Template/config/application.php | 44 + app/Template/config/board.php | 26 + app/Template/config/email.php | 25 + app/Template/config/integrations.php | 14 + app/Template/config/keyboard_shortcuts.php | 33 + app/Template/config/layout.php | 9 + app/Template/config/project.php | 30 + app/Template/config/sidebar.php | 38 + app/Template/config/upload_db.php | 16 + app/Template/config/webhook.php | 23 + app/Template/currency/change.php | 9 + app/Template/currency/create.php | 11 + app/Template/currency/show.php | 34 + app/Template/custom_filter/create.php | 20 + app/Template/custom_filter/edit.php | 25 + app/Template/custom_filter/index.php | 57 + app/Template/custom_filter/remove.php | 15 + app/Template/dashboard/layout.php | 9 + app/Template/dashboard/overview.php | 105 + app/Template/dashboard/projects.php | 27 + app/Template/dashboard/sidebar.php | 24 + app/Template/dashboard/subtasks.php | 49 + app/Template/dashboard/tasks.php | 41 + app/Template/event/comment_create.php | 11 + app/Template/event/comment_delete.php | 11 + app/Template/event/comment_update.php | 13 + app/Template/event/events.php | 19 + app/Template/event/subtask_create.php | 23 + app/Template/event/subtask_delete.php | 15 + app/Template/event/subtask_update.php | 23 + app/Template/event/task_assignee_change.php | 17 + app/Template/event/task_close.php | 10 + app/Template/event/task_create.php | 10 + app/Template/event/task_file_create.php | 10 + app/Template/event/task_file_destroy.php | 10 + .../task_internal_link_create_update.php | 14 + .../event/task_internal_link_delete.php | 14 + app/Template/event/task_move_column.php | 11 + app/Template/event/task_move_position.php | 12 + app/Template/event/task_move_project.php | 12 + app/Template/event/task_move_swimlane.php | 18 + app/Template/event/task_open.php | 10 + app/Template/event/task_update.php | 15 + app/Template/export/header.php | 18 + app/Template/export/subtasks.php | 17 + app/Template/export/summary.php | 17 + app/Template/export/tasks.php | 17 + app/Template/export/transitions.php | 17 + app/Template/external_task_creation/step1.php | 16 + app/Template/external_task_creation/step2.php | 22 + .../external_task_modification/show.php | 22 + app/Template/feed/project.php | 23 + app/Template/feed/user.php | 23 + app/Template/file_viewer/show.php | 14 + app/Template/group/associate.php | 23 + app/Template/group/dissociate.php | 12 + app/Template/group/dropdown.php | 9 + app/Template/group/index.php | 71 + app/Template/group/remove.php | 12 + app/Template/group/user_dropdown.php | 11 + app/Template/group/users.php | 44 + app/Template/group_creation/show.php | 11 + app/Template/group_modification/show.php | 14 + app/Template/header.php | 43 + app/Template/header/board_selector.php | 13 + app/Template/header/creation_dropdown.php | 21 + app/Template/header/custom_board_selector.php | 26 + app/Template/header/title.php | 29 + app/Template/header/user_dropdown.php | 41 + app/Template/header/user_notifications.php | 7 + app/Template/layout.php | 81 + app/Template/link/create.php | 12 + app/Template/link/edit.php | 17 + app/Template/link/remove.php | 15 + app/Template/link/show.php | 36 + app/Template/notification/comment_create.php | 15 + app/Template/notification/comment_delete.php | 11 + app/Template/notification/comment_update.php | 11 + .../notification/comment_user_mention.php | 11 + app/Template/notification/footer.php | 9 + app/Template/notification/subtask_create.php | 21 + app/Template/notification/subtask_delete.php | 15 + app/Template/notification/subtask_update.php | 27 + .../notification/task_assignee_change.php | 24 + app/Template/notification/task_close.php | 9 + app/Template/notification/task_create.php | 47 + .../notification/task_file_create.php | 9 + .../notification/task_file_destroy.php | 9 + .../task_internal_link_create_update.php | 13 + .../task_internal_link_delete.php | 13 + .../notification/task_move_column.php | 15 + .../notification/task_move_position.php | 15 + .../notification/task_move_project.php | 9 + .../notification/task_move_swimlane.php | 23 + app/Template/notification/task_open.php | 9 + app/Template/notification/task_overdue.php | 41 + app/Template/notification/task_update.php | 8 + .../notification/task_user_mention.php | 11 + app/Template/password_reset/change.php | 16 + app/Template/password_reset/create.php | 18 + app/Template/password_reset/email.php | 6 + app/Template/plugin/directory.php | 53 + app/Template/plugin/layout.php | 9 + app/Template/plugin/remove.php | 13 + app/Template/plugin/show.php | 80 + app/Template/plugin/sidebar.php | 10 + .../predefined_task_description/create.php | 14 + .../predefined_task_description/edit.php | 14 + .../predefined_task_description/remove.php | 15 + app/Template/project/dropdown.php | 28 + app/Template/project/layout.php | 11 + app/Template/project/sidebar.php | 74 + .../project_action_duplication/show.php | 15 + app/Template/project_creation/create.php | 52 + app/Template/project_edit/show.php | 76 + app/Template/project_file/create.php | 21 + app/Template/project_file/remove.php | 15 + app/Template/project_header/dropdown.php | 83 + app/Template/project_header/header.php | 13 + app/Template/project_header/search.php | 58 + app/Template/project_header/views.php | 22 + app/Template/project_list/header.php | 12 + app/Template/project_list/listing.php | 56 + app/Template/project_list/project_details.php | 15 + app/Template/project_list/project_icons.php | 23 + app/Template/project_list/project_title.php | 16 + app/Template/project_list/sort_menu.php | 26 + app/Template/project_overview/activity.php | 5 + app/Template/project_overview/attachments.php | 12 + app/Template/project_overview/columns.php | 8 + app/Template/project_overview/description.php | 12 + app/Template/project_overview/files.php | 49 + app/Template/project_overview/images.php | 49 + app/Template/project_overview/information.php | 36 + app/Template/project_overview/show.php | 40 + app/Template/project_permission/groups.php | 59 + app/Template/project_permission/index.php | 19 + app/Template/project_permission/users.php | 60 + .../project_predefined_content/show.php | 45 + app/Template/project_role/create.php | 12 + app/Template/project_role/edit.php | 13 + app/Template/project_role/remove.php | 15 + app/Template/project_role/show.php | 97 + .../project_role_restriction/create.php | 15 + .../project_role_restriction/remove.php | 15 + app/Template/project_status/disable.php | 15 + app/Template/project_status/enable.php | 15 + app/Template/project_status/remove.php | 15 + app/Template/project_tag/create.php | 14 + app/Template/project_tag/edit.php | 14 + app/Template/project_tag/index.php | 62 + app/Template/project_tag/make_global.php | 15 + app/Template/project_tag/remove.php | 15 + app/Template/project_user_overview/layout.php | 29 + app/Template/project_user_overview/roles.php | 32 + .../project_user_overview/sidebar.php | 32 + app/Template/project_user_overview/tasks.php | 46 + .../project_user_overview/tooltip_users.php | 16 + app/Template/project_view/duplicate.php | 30 + app/Template/project_view/importTasks.php | 15 + app/Template/project_view/integrations.php | 15 + app/Template/project_view/notifications.php | 20 + app/Template/project_view/share.php | 20 + app/Template/project_view/show.php | 98 + app/Template/search/activity.php | 40 + app/Template/search/index.php | 44 + app/Template/search/results.php | 33 + app/Template/subtask/create.php | 27 + app/Template/subtask/edit.php | 16 + app/Template/subtask/menu.php | 16 + app/Template/subtask/remove.php | 20 + app/Template/subtask/show.php | 10 + app/Template/subtask/table.php | 47 + app/Template/subtask/timer.php | 15 + app/Template/subtask_converter/show.php | 20 + app/Template/subtask_restriction/show.php | 16 + app/Template/swimlane/create.php | 17 + app/Template/swimlane/edit.php | 18 + app/Template/swimlane/index.php | 28 + app/Template/swimlane/remove.php | 15 + app/Template/swimlane/table.php | 59 + app/Template/tag/create.php | 15 + app/Template/tag/edit.php | 16 + app/Template/tag/index.php | 35 + app/Template/tag/remove.php | 15 + app/Template/task/analytics.php | 45 + app/Template/task/changes.php | 78 + app/Template/task/description.php | 8 + app/Template/task/details.php | 173 + app/Template/task/dropdown.php | 80 + app/Template/task/layout.php | 17 + app/Template/task/public.php | 36 + app/Template/task/show.php | 53 + app/Template/task/sidebar.php | 109 + app/Template/task/time_tracking_details.php | 34 + app/Template/task/time_tracking_summary.php | 13 + app/Template/task/transitions.php | 33 + app/Template/task_bulk/show.php | 30 + .../task_bulk_change_property/show.php | 126 + app/Template/task_bulk_move_column/show.php | 16 + app/Template/task_comments/create.php | 30 + app/Template/task_comments/show.php | 39 + .../task_creation/duplicate_projects.php | 25 + app/Template/task_creation/show.php | 132 + app/Template/task_duplication/copy.php | 46 + app/Template/task_duplication/duplicate.php | 15 + app/Template/task_duplication/move.php | 48 + app/Template/task_external_link/create.php | 8 + app/Template/task_external_link/edit.php | 8 + app/Template/task_external_link/find.php | 23 + app/Template/task_external_link/form.php | 11 + app/Template/task_external_link/remove.php | 15 + app/Template/task_external_link/show.php | 10 + app/Template/task_external_link/table.php | 47 + app/Template/task_file/create.php | 21 + app/Template/task_file/files.php | 50 + app/Template/task_file/images.php | 50 + app/Template/task_file/remove.php | 15 + app/Template/task_file/screenshot.php | 15 + app/Template/task_file/show.php | 7 + app/Template/task_import/show.php | 37 + app/Template/task_internal_link/create.php | 30 + app/Template/task_internal_link/edit.php | 28 + app/Template/task_internal_link/remove.php | 15 + app/Template/task_internal_link/show.php | 12 + app/Template/task_internal_link/table.php | 80 + app/Template/task_list/header.php | 41 + app/Template/task_list/listing.php | 41 + app/Template/task_list/sort_menu.php | 41 + app/Template/task_list/task_avatars.php | 20 + app/Template/task_list/task_details.php | 32 + app/Template/task_list/task_icons.php | 103 + app/Template/task_list/task_subtasks.php | 22 + app/Template/task_list/task_title.php | 14 + app/Template/task_mail/create.php | 47 + app/Template/task_mail/email.php | 46 + app/Template/task_modification/show.php | 44 + app/Template/task_move_position/show.php | 20 + app/Template/task_recurrence/edit.php | 43 + app/Template/task_recurrence/info.php | 39 + app/Template/task_status/close.php | 15 + app/Template/task_status/open.php | 15 + app/Template/task_suppression/remove.php | 15 + app/Template/twofactor/check.php | 17 + app/Template/twofactor/disable.php | 15 + app/Template/twofactor/index.php | 15 + app/Template/twofactor/show.php | 32 + app/Template/user_api_access/show.php | 17 + app/Template/user_creation/show.php | 74 + .../user_credential/authentication.php | 25 + app/Template/user_credential/password.php | 21 + app/Template/user_import/show.php | 38 + app/Template/user_invite/email.php | 12 + app/Template/user_invite/show.php | 15 + app/Template/user_invite/signup.php | 50 + app/Template/user_list/dropdown.php | 100 + app/Template/user_list/header.php | 13 + app/Template/user_list/listing.php | 51 + app/Template/user_list/sort_menu.php | 29 + app/Template/user_list/user_details.php | 26 + app/Template/user_list/user_icons.php | 59 + app/Template/user_list/user_title.php | 14 + app/Template/user_modification/show.php | 44 + app/Template/user_status/disable.php | 13 + app/Template/user_status/enable.php | 13 + app/Template/user_status/remove.php | 13 + app/Template/user_view/external.php | 11 + app/Template/user_view/integrations.php | 13 + app/Template/user_view/last.php | 24 + app/Template/user_view/layout.php | 26 + app/Template/user_view/notifications.php | 22 + app/Template/user_view/password_reset.php | 26 + app/Template/user_view/profile.php | 12 + app/Template/user_view/sessions.php | 26 + app/Template/user_view/share.php | 15 + app/Template/user_view/show.php | 53 + app/Template/user_view/sidebar.php | 108 + app/Template/user_view/timesheet.php | 29 + app/Template/web_notification/show.php | 68 + app/User/Avatar/AvatarFileProvider.php | 42 + app/User/Avatar/LetterAvatarProvider.php | 172 + app/User/DatabaseBackendUserProvider.php | 43 + app/User/DatabaseUserProvider.php | 143 + app/User/LdapUserProvider.php | 242 ++ app/User/OAuthUserProvider.php | 132 + app/User/ReverseProxyUserProvider.php | 188 + app/Validator/ActionValidator.php | 38 + app/Validator/AuthValidator.php | 119 + app/Validator/BaseValidator.php | 55 + app/Validator/CategoryValidator.php | 74 + .../ColumnMoveRestrictionValidator.php | 41 + app/Validator/ColumnRestrictionValidator.php | 40 + app/Validator/ColumnValidator.php | 75 + app/Validator/CommentValidator.php | 103 + app/Validator/ConfigValidator.php | 40 + app/Validator/CurrencyValidator.php | 36 + app/Validator/CustomFilterValidator.php | 74 + app/Validator/ExternalLinkValidator.php | 77 + app/Validator/GroupValidator.php | 71 + app/Validator/LinkValidator.php | 59 + app/Validator/PasswordResetValidator.php | 95 + .../PredefinedTaskDescriptionValidator.php | 22 + app/Validator/ProjectRoleValidator.php | 70 + app/Validator/ProjectValidator.php | 92 + app/Validator/SubtaskValidator.php | 101 + app/Validator/SwimlaneValidator.php | 74 + app/Validator/TagValidator.php | 76 + app/Validator/TaskLinkValidator.php | 71 + app/Validator/TaskValidator.php | 230 ++ app/Validator/UserValidator.php | 135 + app/check_setup.php | 49 + app/common.php | 58 + app/constants.php | 193 + app/functions.php | 361 ++ assets/css/auto.min.css | 1 + assets/css/custom_dashboard.css | 834 +++++ assets/css/custom_login.css | 223 ++ assets/css/dark.min.css | 1 + .../css/images/ui-bg_flat_0_aaaaaa_40x100.png | Bin 0 -> 86 bytes .../images/ui-bg_flat_75_ffffff_40x100.png | Bin 0 -> 74 bytes .../images/ui-bg_glass_55_fbf9ee_1x400.png | Bin 0 -> 111 bytes .../images/ui-bg_glass_65_ffffff_1x400.png | Bin 0 -> 90 bytes .../images/ui-bg_glass_75_dadada_1x400.png | Bin 0 -> 102 bytes .../images/ui-bg_glass_75_e6e6e6_1x400.png | Bin 0 -> 102 bytes .../images/ui-bg_glass_95_fef1ec_1x400.png | Bin 0 -> 115 bytes .../ui-bg_highlight-soft_75_cccccc_1x100.png | Bin 0 -> 86 bytes assets/css/images/ui-icons_222222_256x240.png | Bin 0 -> 3686 bytes assets/css/images/ui-icons_2e83ff_256x240.png | Bin 0 -> 3686 bytes assets/css/images/ui-icons_444444_256x240.png | Bin 0 -> 6992 bytes assets/css/images/ui-icons_454545_256x240.png | Bin 0 -> 3686 bytes assets/css/images/ui-icons_555555_256x240.png | Bin 0 -> 6988 bytes assets/css/images/ui-icons_777620_256x240.png | Bin 0 -> 4549 bytes assets/css/images/ui-icons_777777_256x240.png | Bin 0 -> 6999 bytes assets/css/images/ui-icons_888888_256x240.png | Bin 0 -> 3686 bytes assets/css/images/ui-icons_cc0000_256x240.png | Bin 0 -> 4549 bytes assets/css/images/ui-icons_cd0a0a_256x240.png | Bin 0 -> 3686 bytes assets/css/images/ui-icons_ffffff_256x240.png | Bin 0 -> 6299 bytes assets/css/light.min.css | 1 + assets/css/print.min.css | 1 + assets/css/vendor.min.css | 16 + assets/fonts/FontAwesome.otf | Bin 0 -> 134808 bytes assets/fonts/fontawesome-webfont.eot | Bin 0 -> 165742 bytes assets/fonts/fontawesome-webfont.svg | 2671 +++++++++++++ assets/fonts/fontawesome-webfont.ttf | Bin 0 -> 165548 bytes assets/fonts/fontawesome-webfont.woff | Bin 0 -> 98024 bytes assets/fonts/fontawesome-webfont.woff2 | Bin 0 -> 77160 bytes assets/img/adaptive-favicon.svg | 13 + assets/img/favicon.png | Bin 0 -> 1336 bytes assets/img/inkscape-optimized-icon.svg | 6 + assets/img/inkscape-path-icon.svg | 90 + assets/img/inkscape-text-icon.svg | 103 + assets/img/touch-icon-ipad-retina.png | Bin 0 -> 2673 bytes assets/img/touch-icon-ipad.png | Bin 0 -> 1496 bytes assets/img/touch-icon-iphone-retina.png | Bin 0 -> 2267 bytes assets/img/touch-icon-iphone.png | Bin 0 -> 1214 bytes assets/js/app.min.js | 333 ++ assets/js/vendor.min.js | 3329 +++++++++++++++++ cli | 25 + config.default.php | 300 ++ data/.htaccess | 7 + data/web.config | 6 + docker-compose.mysql.yml | 45 + docker-compose.postgres.yml | 43 + docker-compose.sqlite.yml | 22 + favicon.ico | Bin 0 -> 13094 bytes healthcheck.php | 22 + index.php | 12 + jsonrpc.php | 5 + libs/Captcha/CaptchaBuilder.php | 739 ++++ libs/Captcha/CaptchaBuilderInterface.php | 29 + libs/Captcha/Font/captcha0.ttf | Bin 0 -> 49224 bytes libs/Captcha/Font/captcha1.ttf | Bin 0 -> 76232 bytes libs/Captcha/Font/captcha2.ttf | Bin 0 -> 24108 bytes libs/Captcha/Font/captcha3.ttf | Bin 0 -> 15976 bytes libs/Captcha/Font/captcha4.ttf | Bin 0 -> 906980 bytes libs/Captcha/Font/captcha5.ttf | Bin 0 -> 49724 bytes libs/Captcha/ImageFileHandler.php | 105 + libs/Captcha/LICENSE | 19 + libs/Captcha/PhraseBuilder.php | 75 + libs/Captcha/PhraseBuilderInterface.php | 21 + libs/SimpleQueue/Adapter/AmqpQueueAdapter.php | 138 + .../Adapter/BeanstalkQueueAdapter.php | 120 + .../Exception/NotSupportedException.php | 14 + libs/SimpleQueue/Job.php | 98 + libs/SimpleQueue/Queue.php | 92 + libs/SimpleQueue/QueueAdapterInterface.php | 58 + libs/SimpleValidator/Validator.php | 44 + libs/SimpleValidator/Validators/Alpha.php | 15 + .../Validators/AlphaNumeric.php | 15 + libs/SimpleValidator/Validators/Base.php | 37 + libs/SimpleValidator/Validators/Date.php | 46 + libs/SimpleValidator/Validators/Email.php | 67 + libs/SimpleValidator/Validators/Equals.php | 27 + libs/SimpleValidator/Validators/Exists.php | 38 + .../Validators/GreaterThan.php | 23 + .../Validators/GreaterThanOrEqual.php | 23 + libs/SimpleValidator/Validators/InArray.php | 23 + libs/SimpleValidator/Validators/Integer.php | 25 + libs/SimpleValidator/Validators/Ip.php | 15 + libs/SimpleValidator/Validators/Length.php | 26 + libs/SimpleValidator/Validators/MaxLength.php | 24 + libs/SimpleValidator/Validators/MinLength.php | 24 + libs/SimpleValidator/Validators/NotEmpty.php | 15 + libs/SimpleValidator/Validators/NotEquals.php | 28 + .../SimpleValidator/Validators/NotInArray.php | 15 + libs/SimpleValidator/Validators/Numeric.php | 15 + libs/SimpleValidator/Validators/Range.php | 33 + libs/SimpleValidator/Validators/Required.php | 11 + libs/SimpleValidator/Validators/Timezone.php | 15 + libs/SimpleValidator/Validators/URL.php | 15 + libs/SimpleValidator/Validators/Unique.php | 48 + libs/erusev/parsedown/LICENSE.txt | 20 + libs/erusev/parsedown/Parsedown.php | 1712 +++++++++ .../Attribute/AsEventListener.php | 29 + libs/event-dispatcher/CHANGELOG.md | 91 + .../Debug/TraceableEventDispatcher.php | 366 ++ .../Debug/WrappedListener.php | 127 + .../AddEventAliasesPass.php | 46 + .../RegisterListenersPass.php | 238 ++ libs/event-dispatcher/EventDispatcher.php | 280 ++ .../EventDispatcherInterface.php | 70 + .../EventSubscriberInterface.php | 49 + libs/event-dispatcher/GenericEvent.php | 182 + .../ImmutableEventDispatcher.php | 91 + libs/event-dispatcher/LICENSE | 19 + .../LegacyEventDispatcherProxy.php | 31 + libs/ical/Component.php | 184 + libs/ical/Component/Alarm.php | 150 + libs/ical/Component/Calendar.php | 324 ++ libs/ical/Component/Event.php | 941 +++++ libs/ical/Component/Timezone.php | 57 + libs/ical/Component/TimezoneRule.php | 211 ++ libs/ical/LICENSE | 19 + libs/ical/ParameterBag.php | 107 + libs/ical/Property.php | 147 + libs/ical/Property/ArrayValue.php | 41 + libs/ical/Property/DateTimeProperty.php | 40 + libs/ical/Property/DateTimesProperty.php | 46 + libs/ical/Property/Event/Attachment.php | 39 + libs/ical/Property/Event/Attendees.php | 95 + libs/ical/Property/Event/Geo.php | 82 + libs/ical/Property/Event/Organizer.php | 29 + libs/ical/Property/Event/RecurrenceId.php | 121 + libs/ical/Property/Event/RecurrenceRule.php | 547 +++ libs/ical/Property/RawStringValue.php | 20 + libs/ical/Property/StringValue.php | 67 + libs/ical/Property/ValueInterface.php | 24 + libs/ical/PropertyBag.php | 74 + libs/ical/Util/ComponentUtil.php | 62 + libs/ical/Util/DateUtil.php | 79 + libs/jsonrpc/LICENSE | 21 + libs/jsonrpc/README.markdown | 412 ++ libs/jsonrpc/src/JsonRPC/Client.php | 198 + .../Exception/AccessDeniedException.php | 13 + .../AuthenticationFailureException.php | 13 + .../Exception/ConnectionFailureException.php | 13 + .../Exception/InvalidJsonFormatException.php | 13 + .../InvalidJsonRpcFormatException.php | 13 + .../ResponseEncodingFailureException.php | 13 + .../JsonRPC/Exception/ResponseException.php | 62 + .../Exception/RpcCallFailedException.php | 15 + .../Exception/ServerErrorException.php | 13 + libs/jsonrpc/src/JsonRPC/HttpClient.php | 449 +++ .../jsonrpc/src/JsonRPC/MiddlewareHandler.php | 114 + .../src/JsonRPC/MiddlewareInterface.php | 27 + libs/jsonrpc/src/JsonRPC/ProcedureHandler.php | 296 ++ .../JsonRPC/Request/BatchRequestParser.php | 55 + .../src/JsonRPC/Request/RequestBuilder.php | 129 + .../src/JsonRPC/Request/RequestParser.php | 200 + .../src/JsonRPC/Response/ResponseBuilder.php | 336 ++ .../src/JsonRPC/Response/ResponseParser.php | 154 + libs/jsonrpc/src/JsonRPC/Server.php | 386 ++ .../src/JsonRPC/Validator/HostValidator.php | 73 + .../Validator/JsonEncodingValidator.php | 44 + .../JsonRPC/Validator/JsonFormatValidator.php | 30 + .../JsonRPC/Validator/RpcFormatValidator.php | 35 + .../src/JsonRPC/Validator/UserValidator.php | 21 + libs/minify/LICENSE | 18 + libs/minify/data/js/keywords_after.txt | 7 + libs/minify/data/js/keywords_before.txt | 26 + libs/minify/data/js/keywords_reserved.txt | 63 + libs/minify/data/js/operators.txt | 46 + libs/minify/data/js/operators_after.txt | 43 + libs/minify/data/js/operators_before.txt | 43 + libs/minify/src/CSS.php | 751 ++++ libs/minify/src/Exception.php | 20 + libs/minify/src/Exceptions/BasicException.php | 23 + .../src/Exceptions/FileImportException.php | 21 + libs/minify/src/Exceptions/IOException.php | 21 + libs/minify/src/JS.php | 612 +++ libs/minify/src/Minify.php | 459 +++ libs/path-converter/LICENSE | 18 + libs/path-converter/src/Converter.php | 196 + .../path-converter/src/ConverterInterface.php | 24 + libs/path-converter/src/NoConverter.php | 23 + libs/phpqrcode/LICENSE | 165 + libs/phpqrcode/cache/frame_1.dat | 2 + libs/phpqrcode/cache/frame_1.png | Bin 0 -> 126 bytes libs/phpqrcode/cache/frame_10.dat | Bin 0 -> 204 bytes libs/phpqrcode/cache/frame_10.png | Bin 0 -> 202 bytes libs/phpqrcode/cache/frame_11.dat | Bin 0 -> 210 bytes libs/phpqrcode/cache/frame_11.png | Bin 0 -> 205 bytes libs/phpqrcode/cache/frame_12.dat | Bin 0 -> 222 bytes libs/phpqrcode/cache/frame_12.png | Bin 0 -> 216 bytes libs/phpqrcode/cache/frame_13.dat | Bin 0 -> 223 bytes libs/phpqrcode/cache/frame_13.png | Bin 0 -> 210 bytes libs/phpqrcode/cache/frame_14.dat | Bin 0 -> 227 bytes libs/phpqrcode/cache/frame_14.png | Bin 0 -> 213 bytes libs/phpqrcode/cache/frame_15.dat | Bin 0 -> 242 bytes libs/phpqrcode/cache/frame_15.png | Bin 0 -> 219 bytes libs/phpqrcode/cache/frame_16.dat | 1 + libs/phpqrcode/cache/frame_16.png | Bin 0 -> 211 bytes libs/phpqrcode/cache/frame_17.dat | Bin 0 -> 237 bytes libs/phpqrcode/cache/frame_17.png | Bin 0 -> 211 bytes libs/phpqrcode/cache/frame_18.dat | 2 + libs/phpqrcode/cache/frame_18.png | Bin 0 -> 228 bytes libs/phpqrcode/cache/frame_19.dat | 3 + libs/phpqrcode/cache/frame_19.png | Bin 0 -> 225 bytes libs/phpqrcode/cache/frame_2.dat | 1 + libs/phpqrcode/cache/frame_2.png | Bin 0 -> 144 bytes libs/phpqrcode/cache/frame_20.dat | Bin 0 -> 250 bytes libs/phpqrcode/cache/frame_20.png | Bin 0 -> 225 bytes libs/phpqrcode/cache/frame_21.dat | 1 + libs/phpqrcode/cache/frame_21.png | Bin 0 -> 235 bytes libs/phpqrcode/cache/frame_22.dat | 3 + libs/phpqrcode/cache/frame_22.png | Bin 0 -> 226 bytes libs/phpqrcode/cache/frame_23.dat | 3 + libs/phpqrcode/cache/frame_23.png | Bin 0 -> 220 bytes libs/phpqrcode/cache/frame_24.dat | 1 + libs/phpqrcode/cache/frame_24.png | Bin 0 -> 242 bytes libs/phpqrcode/cache/frame_25.dat | 3 + libs/phpqrcode/cache/frame_25.png | Bin 0 -> 242 bytes libs/phpqrcode/cache/frame_26.dat | 2 + libs/phpqrcode/cache/frame_26.png | Bin 0 -> 244 bytes libs/phpqrcode/cache/frame_27.dat | Bin 0 -> 284 bytes libs/phpqrcode/cache/frame_27.png | Bin 0 -> 237 bytes libs/phpqrcode/cache/frame_28.dat | Bin 0 -> 318 bytes libs/phpqrcode/cache/frame_28.png | Bin 0 -> 234 bytes libs/phpqrcode/cache/frame_29.dat | 2 + libs/phpqrcode/cache/frame_29.png | Bin 0 -> 232 bytes libs/phpqrcode/cache/frame_3.dat | 1 + libs/phpqrcode/cache/frame_3.png | Bin 0 -> 147 bytes libs/phpqrcode/cache/frame_30.dat | Bin 0 -> 324 bytes libs/phpqrcode/cache/frame_30.png | Bin 0 -> 255 bytes libs/phpqrcode/cache/frame_31.dat | 1 + libs/phpqrcode/cache/frame_31.png | Bin 0 -> 260 bytes libs/phpqrcode/cache/frame_32.dat | 2 + libs/phpqrcode/cache/frame_32.png | Bin 0 -> 262 bytes libs/phpqrcode/cache/frame_33.dat | 14 + libs/phpqrcode/cache/frame_33.png | Bin 0 -> 253 bytes libs/phpqrcode/cache/frame_34.dat | Bin 0 -> 331 bytes libs/phpqrcode/cache/frame_34.png | Bin 0 -> 256 bytes libs/phpqrcode/cache/frame_35.dat | Bin 0 -> 342 bytes libs/phpqrcode/cache/frame_35.png | Bin 0 -> 243 bytes libs/phpqrcode/cache/frame_36.dat | Bin 0 -> 370 bytes libs/phpqrcode/cache/frame_36.png | Bin 0 -> 272 bytes libs/phpqrcode/cache/frame_37.dat | Bin 0 -> 376 bytes libs/phpqrcode/cache/frame_37.png | Bin 0 -> 279 bytes libs/phpqrcode/cache/frame_38.dat | 1 + libs/phpqrcode/cache/frame_38.png | Bin 0 -> 279 bytes libs/phpqrcode/cache/frame_39.dat | Bin 0 -> 404 bytes libs/phpqrcode/cache/frame_39.png | Bin 0 -> 264 bytes libs/phpqrcode/cache/frame_4.dat | 1 + libs/phpqrcode/cache/frame_4.png | Bin 0 -> 149 bytes libs/phpqrcode/cache/frame_40.dat | 2 + libs/phpqrcode/cache/frame_40.png | Bin 0 -> 267 bytes libs/phpqrcode/cache/frame_5.dat | 1 + libs/phpqrcode/cache/frame_5.png | Bin 0 -> 150 bytes libs/phpqrcode/cache/frame_6.dat | Bin 0 -> 132 bytes libs/phpqrcode/cache/frame_6.png | Bin 0 -> 151 bytes libs/phpqrcode/cache/frame_7.dat | Bin 0 -> 196 bytes libs/phpqrcode/cache/frame_7.png | Bin 0 -> 189 bytes libs/phpqrcode/cache/frame_8.dat | Bin 0 -> 201 bytes libs/phpqrcode/cache/frame_8.png | Bin 0 -> 204 bytes libs/phpqrcode/cache/frame_9.dat | Bin 0 -> 206 bytes libs/phpqrcode/cache/frame_9.png | Bin 0 -> 199 bytes libs/phpqrcode/cache/mask_0/mask_101_0.dat | Bin 0 -> 157 bytes libs/phpqrcode/cache/mask_0/mask_105_0.dat | Bin 0 -> 162 bytes libs/phpqrcode/cache/mask_0/mask_109_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_113_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_117_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_121_0.dat | 1 + libs/phpqrcode/cache/mask_0/mask_125_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_129_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_133_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_137_0.dat | 1 + libs/phpqrcode/cache/mask_0/mask_141_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_145_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_149_0.dat | 3 + libs/phpqrcode/cache/mask_0/mask_153_0.dat | 1 + libs/phpqrcode/cache/mask_0/mask_157_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_161_0.dat | Bin 0 -> 241 bytes libs/phpqrcode/cache/mask_0/mask_165_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_169_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_173_0.dat | 1 + libs/phpqrcode/cache/mask_0/mask_177_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_21_0.dat | Bin 0 -> 48 bytes libs/phpqrcode/cache/mask_0/mask_25_0.dat | Bin 0 -> 57 bytes libs/phpqrcode/cache/mask_0/mask_29_0.dat | Bin 0 -> 59 bytes libs/phpqrcode/cache/mask_0/mask_33_0.dat | Bin 0 -> 62 bytes libs/phpqrcode/cache/mask_0/mask_37_0.dat | Bin 0 -> 65 bytes libs/phpqrcode/cache/mask_0/mask_41_0.dat | Bin 0 -> 68 bytes libs/phpqrcode/cache/mask_0/mask_45_0.dat | Bin 0 -> 106 bytes libs/phpqrcode/cache/mask_0/mask_49_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_53_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_57_0.dat | 4 + libs/phpqrcode/cache/mask_0/mask_61_0.dat | Bin 0 -> 119 bytes libs/phpqrcode/cache/mask_0/mask_65_0.dat | Bin 0 -> 123 bytes libs/phpqrcode/cache/mask_0/mask_69_0.dat | 1 + libs/phpqrcode/cache/mask_0/mask_73_0.dat | 1 + libs/phpqrcode/cache/mask_0/mask_77_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_81_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_85_0.dat | 2 + libs/phpqrcode/cache/mask_0/mask_89_0.dat | 1 + libs/phpqrcode/cache/mask_0/mask_93_0.dat | 3 + libs/phpqrcode/cache/mask_0/mask_97_0.dat | Bin 0 -> 150 bytes libs/phpqrcode/cache/mask_1/mask_101_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_105_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_109_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_113_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_117_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_121_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_125_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_129_1.dat | Bin 0 -> 164 bytes libs/phpqrcode/cache/mask_1/mask_133_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_137_1.dat | 3 + libs/phpqrcode/cache/mask_1/mask_141_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_145_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_149_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_153_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_157_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_161_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_165_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_169_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_173_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_177_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_21_1.dat | Bin 0 -> 42 bytes libs/phpqrcode/cache/mask_1/mask_25_1.dat | Bin 0 -> 48 bytes libs/phpqrcode/cache/mask_1/mask_29_1.dat | Bin 0 -> 50 bytes libs/phpqrcode/cache/mask_1/mask_33_1.dat | Bin 0 -> 53 bytes libs/phpqrcode/cache/mask_1/mask_37_1.dat | Bin 0 -> 56 bytes libs/phpqrcode/cache/mask_1/mask_41_1.dat | Bin 0 -> 58 bytes libs/phpqrcode/cache/mask_1/mask_45_1.dat | Bin 0 -> 82 bytes libs/phpqrcode/cache/mask_1/mask_49_1.dat | Bin 0 -> 84 bytes libs/phpqrcode/cache/mask_1/mask_53_1.dat | Bin 0 -> 87 bytes libs/phpqrcode/cache/mask_1/mask_57_1.dat | Bin 0 -> 92 bytes libs/phpqrcode/cache/mask_1/mask_61_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_65_1.dat | Bin 0 -> 99 bytes libs/phpqrcode/cache/mask_1/mask_69_1.dat | Bin 0 -> 102 bytes libs/phpqrcode/cache/mask_1/mask_73_1.dat | Bin 0 -> 104 bytes libs/phpqrcode/cache/mask_1/mask_77_1.dat | Bin 0 -> 110 bytes libs/phpqrcode/cache/mask_1/mask_81_1.dat | Bin 0 -> 114 bytes libs/phpqrcode/cache/mask_1/mask_85_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_89_1.dat | 1 + libs/phpqrcode/cache/mask_1/mask_93_1.dat | 2 + libs/phpqrcode/cache/mask_1/mask_97_1.dat | 2 + libs/phpqrcode/cache/mask_2/mask_101_2.dat | 3 + libs/phpqrcode/cache/mask_2/mask_105_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_109_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_113_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_117_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_121_2.dat | Bin 0 -> 127 bytes libs/phpqrcode/cache/mask_2/mask_125_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_129_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_133_2.dat | 10 + libs/phpqrcode/cache/mask_2/mask_137_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_141_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_145_2.dat | 4 + libs/phpqrcode/cache/mask_2/mask_149_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_153_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_157_2.dat | 3 + libs/phpqrcode/cache/mask_2/mask_161_2.dat | Bin 0 -> 190 bytes libs/phpqrcode/cache/mask_2/mask_165_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_169_2.dat | Bin 0 -> 196 bytes libs/phpqrcode/cache/mask_2/mask_173_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_177_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_21_2.dat | Bin 0 -> 35 bytes libs/phpqrcode/cache/mask_2/mask_25_2.dat | Bin 0 -> 41 bytes libs/phpqrcode/cache/mask_2/mask_29_2.dat | Bin 0 -> 45 bytes libs/phpqrcode/cache/mask_2/mask_33_2.dat | Bin 0 -> 47 bytes libs/phpqrcode/cache/mask_2/mask_37_2.dat | Bin 0 -> 47 bytes libs/phpqrcode/cache/mask_2/mask_41_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_45_2.dat | Bin 0 -> 68 bytes libs/phpqrcode/cache/mask_2/mask_49_2.dat | Bin 0 -> 70 bytes libs/phpqrcode/cache/mask_2/mask_53_2.dat | Bin 0 -> 73 bytes libs/phpqrcode/cache/mask_2/mask_57_2.dat | Bin 0 -> 76 bytes libs/phpqrcode/cache/mask_2/mask_61_2.dat | Bin 0 -> 78 bytes libs/phpqrcode/cache/mask_2/mask_65_2.dat | Bin 0 -> 89 bytes libs/phpqrcode/cache/mask_2/mask_69_2.dat | Bin 0 -> 88 bytes libs/phpqrcode/cache/mask_2/mask_73_2.dat | Bin 0 -> 94 bytes libs/phpqrcode/cache/mask_2/mask_77_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_81_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_85_2.dat | 2 + libs/phpqrcode/cache/mask_2/mask_89_2.dat | 1 + libs/phpqrcode/cache/mask_2/mask_93_2.dat | Bin 0 -> 103 bytes libs/phpqrcode/cache/mask_2/mask_97_2.dat | 2 + libs/phpqrcode/cache/mask_3/mask_101_3.dat | 1 + libs/phpqrcode/cache/mask_3/mask_105_3.dat | 1 + libs/phpqrcode/cache/mask_3/mask_109_3.dat | 1 + libs/phpqrcode/cache/mask_3/mask_113_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_117_3.dat | 4 + libs/phpqrcode/cache/mask_3/mask_121_3.dat | Bin 0 -> 212 bytes libs/phpqrcode/cache/mask_3/mask_125_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_129_3.dat | 8 + libs/phpqrcode/cache/mask_3/mask_133_3.dat | Bin 0 -> 216 bytes libs/phpqrcode/cache/mask_3/mask_137_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_141_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_145_3.dat | 3 + libs/phpqrcode/cache/mask_3/mask_149_3.dat | 1 + libs/phpqrcode/cache/mask_3/mask_153_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_157_3.dat | Bin 0 -> 248 bytes libs/phpqrcode/cache/mask_3/mask_161_3.dat | 3 + libs/phpqrcode/cache/mask_3/mask_165_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_169_3.dat | 1 + libs/phpqrcode/cache/mask_3/mask_173_3.dat | 1 + libs/phpqrcode/cache/mask_3/mask_177_3.dat | Bin 0 -> 312 bytes libs/phpqrcode/cache/mask_3/mask_21_3.dat | Bin 0 -> 60 bytes libs/phpqrcode/cache/mask_3/mask_25_3.dat | Bin 0 -> 75 bytes libs/phpqrcode/cache/mask_3/mask_29_3.dat | Bin 0 -> 75 bytes libs/phpqrcode/cache/mask_3/mask_33_3.dat | Bin 0 -> 79 bytes libs/phpqrcode/cache/mask_3/mask_37_3.dat | Bin 0 -> 83 bytes libs/phpqrcode/cache/mask_3/mask_41_3.dat | Bin 0 -> 85 bytes libs/phpqrcode/cache/mask_3/mask_45_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_49_3.dat | Bin 0 -> 127 bytes libs/phpqrcode/cache/mask_3/mask_53_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_57_3.dat | Bin 0 -> 126 bytes libs/phpqrcode/cache/mask_3/mask_61_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_65_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_69_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_73_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_77_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_81_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_85_3.dat | Bin 0 -> 160 bytes libs/phpqrcode/cache/mask_3/mask_89_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_93_3.dat | 2 + libs/phpqrcode/cache/mask_3/mask_97_3.dat | Bin 0 -> 175 bytes libs/phpqrcode/cache/mask_4/mask_101_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_105_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_109_4.dat | Bin 0 -> 182 bytes libs/phpqrcode/cache/mask_4/mask_113_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_117_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_121_4.dat | Bin 0 -> 208 bytes libs/phpqrcode/cache/mask_4/mask_125_4.dat | Bin 0 -> 213 bytes libs/phpqrcode/cache/mask_4/mask_129_4.dat | Bin 0 -> 220 bytes libs/phpqrcode/cache/mask_4/mask_133_4.dat | 3 + libs/phpqrcode/cache/mask_4/mask_137_4.dat | Bin 0 -> 248 bytes libs/phpqrcode/cache/mask_4/mask_141_4.dat | Bin 0 -> 254 bytes libs/phpqrcode/cache/mask_4/mask_145_4.dat | Bin 0 -> 255 bytes libs/phpqrcode/cache/mask_4/mask_149_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_153_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_157_4.dat | 1 + libs/phpqrcode/cache/mask_4/mask_161_4.dat | 1 + libs/phpqrcode/cache/mask_4/mask_165_4.dat | 3 + libs/phpqrcode/cache/mask_4/mask_169_4.dat | Bin 0 -> 297 bytes libs/phpqrcode/cache/mask_4/mask_173_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_177_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_21_4.dat | Bin 0 -> 57 bytes libs/phpqrcode/cache/mask_4/mask_25_4.dat | Bin 0 -> 76 bytes libs/phpqrcode/cache/mask_4/mask_29_4.dat | Bin 0 -> 78 bytes libs/phpqrcode/cache/mask_4/mask_33_4.dat | Bin 0 -> 89 bytes libs/phpqrcode/cache/mask_4/mask_37_4.dat | Bin 0 -> 86 bytes libs/phpqrcode/cache/mask_4/mask_41_4.dat | Bin 0 -> 89 bytes libs/phpqrcode/cache/mask_4/mask_45_4.dat | Bin 0 -> 120 bytes libs/phpqrcode/cache/mask_4/mask_49_4.dat | Bin 0 -> 124 bytes libs/phpqrcode/cache/mask_4/mask_53_4.dat | Bin 0 -> 128 bytes libs/phpqrcode/cache/mask_4/mask_57_4.dat | Bin 0 -> 130 bytes libs/phpqrcode/cache/mask_4/mask_61_4.dat | Bin 0 -> 132 bytes libs/phpqrcode/cache/mask_4/mask_65_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_69_4.dat | 1 + libs/phpqrcode/cache/mask_4/mask_73_4.dat | 3 + libs/phpqrcode/cache/mask_4/mask_77_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_81_4.dat | 3 + libs/phpqrcode/cache/mask_4/mask_85_4.dat | Bin 0 -> 154 bytes libs/phpqrcode/cache/mask_4/mask_89_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_93_4.dat | 2 + libs/phpqrcode/cache/mask_4/mask_97_4.dat | Bin 0 -> 176 bytes libs/phpqrcode/cache/mask_5/mask_101_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_105_5.dat | Bin 0 -> 224 bytes libs/phpqrcode/cache/mask_5/mask_109_5.dat | Bin 0 -> 211 bytes libs/phpqrcode/cache/mask_5/mask_113_5.dat | 9 + libs/phpqrcode/cache/mask_5/mask_117_5.dat | 1 + libs/phpqrcode/cache/mask_5/mask_121_5.dat | Bin 0 -> 256 bytes libs/phpqrcode/cache/mask_5/mask_125_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_129_5.dat | Bin 0 -> 259 bytes libs/phpqrcode/cache/mask_5/mask_133_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_137_5.dat | 3 + libs/phpqrcode/cache/mask_5/mask_141_5.dat | Bin 0 -> 297 bytes libs/phpqrcode/cache/mask_5/mask_145_5.dat | Bin 0 -> 300 bytes libs/phpqrcode/cache/mask_5/mask_149_5.dat | 3 + libs/phpqrcode/cache/mask_5/mask_153_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_157_5.dat | 1 + libs/phpqrcode/cache/mask_5/mask_161_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_165_5.dat | Bin 0 -> 332 bytes libs/phpqrcode/cache/mask_5/mask_169_5.dat | 1 + libs/phpqrcode/cache/mask_5/mask_173_5.dat | 4 + libs/phpqrcode/cache/mask_5/mask_177_5.dat | 11 + libs/phpqrcode/cache/mask_5/mask_21_5.dat | Bin 0 -> 74 bytes libs/phpqrcode/cache/mask_5/mask_25_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_29_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_33_5.dat | Bin 0 -> 106 bytes libs/phpqrcode/cache/mask_5/mask_37_5.dat | Bin 0 -> 103 bytes libs/phpqrcode/cache/mask_5/mask_41_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_45_5.dat | 1 + libs/phpqrcode/cache/mask_5/mask_49_5.dat | Bin 0 -> 146 bytes libs/phpqrcode/cache/mask_5/mask_53_5.dat | 1 + libs/phpqrcode/cache/mask_5/mask_57_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_61_5.dat | 1 + libs/phpqrcode/cache/mask_5/mask_65_5.dat | Bin 0 -> 163 bytes libs/phpqrcode/cache/mask_5/mask_69_5.dat | Bin 0 -> 167 bytes libs/phpqrcode/cache/mask_5/mask_73_5.dat | Bin 0 -> 184 bytes libs/phpqrcode/cache/mask_5/mask_77_5.dat | 1 + libs/phpqrcode/cache/mask_5/mask_81_5.dat | 3 + libs/phpqrcode/cache/mask_5/mask_85_5.dat | Bin 0 -> 186 bytes libs/phpqrcode/cache/mask_5/mask_89_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_93_5.dat | 2 + libs/phpqrcode/cache/mask_5/mask_97_5.dat | 1 + libs/phpqrcode/cache/mask_6/mask_101_6.dat | 2 + libs/phpqrcode/cache/mask_6/mask_105_6.dat | 3 + libs/phpqrcode/cache/mask_6/mask_109_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_113_6.dat | 3 + libs/phpqrcode/cache/mask_6/mask_117_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_121_6.dat | Bin 0 -> 309 bytes libs/phpqrcode/cache/mask_6/mask_125_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_129_6.dat | Bin 0 -> 310 bytes libs/phpqrcode/cache/mask_6/mask_133_6.dat | Bin 0 -> 296 bytes libs/phpqrcode/cache/mask_6/mask_137_6.dat | 2 + libs/phpqrcode/cache/mask_6/mask_141_6.dat | 10 + libs/phpqrcode/cache/mask_6/mask_145_6.dat | Bin 0 -> 357 bytes libs/phpqrcode/cache/mask_6/mask_149_6.dat | 2 + libs/phpqrcode/cache/mask_6/mask_153_6.dat | Bin 0 -> 367 bytes libs/phpqrcode/cache/mask_6/mask_157_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_161_6.dat | Bin 0 -> 399 bytes libs/phpqrcode/cache/mask_6/mask_165_6.dat | Bin 0 -> 400 bytes libs/phpqrcode/cache/mask_6/mask_169_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_173_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_177_6.dat | 14 + libs/phpqrcode/cache/mask_6/mask_21_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_25_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_29_6.dat | 3 + libs/phpqrcode/cache/mask_6/mask_33_6.dat | Bin 0 -> 124 bytes libs/phpqrcode/cache/mask_6/mask_37_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_41_6.dat | Bin 0 -> 132 bytes libs/phpqrcode/cache/mask_6/mask_45_6.dat | Bin 0 -> 189 bytes libs/phpqrcode/cache/mask_6/mask_49_6.dat | 2 + libs/phpqrcode/cache/mask_6/mask_53_6.dat | Bin 0 -> 195 bytes libs/phpqrcode/cache/mask_6/mask_57_6.dat | 2 + libs/phpqrcode/cache/mask_6/mask_61_6.dat | 2 + libs/phpqrcode/cache/mask_6/mask_65_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_69_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_73_6.dat | Bin 0 -> 230 bytes libs/phpqrcode/cache/mask_6/mask_77_6.dat | 1 + libs/phpqrcode/cache/mask_6/mask_81_6.dat | 3 + libs/phpqrcode/cache/mask_6/mask_85_6.dat | Bin 0 -> 229 bytes libs/phpqrcode/cache/mask_6/mask_89_6.dat | Bin 0 -> 263 bytes libs/phpqrcode/cache/mask_6/mask_93_6.dat | Bin 0 -> 276 bytes libs/phpqrcode/cache/mask_6/mask_97_6.dat | 2 + libs/phpqrcode/cache/mask_7/mask_101_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_105_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_109_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_113_7.dat | 11 + libs/phpqrcode/cache/mask_7/mask_117_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_121_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_125_7.dat | Bin 0 -> 288 bytes libs/phpqrcode/cache/mask_7/mask_129_7.dat | Bin 0 -> 282 bytes libs/phpqrcode/cache/mask_7/mask_133_7.dat | Bin 0 -> 281 bytes libs/phpqrcode/cache/mask_7/mask_137_7.dat | 5 + libs/phpqrcode/cache/mask_7/mask_141_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_145_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_149_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_153_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_157_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_161_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_165_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_169_7.dat | Bin 0 -> 383 bytes libs/phpqrcode/cache/mask_7/mask_173_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_177_7.dat | Bin 0 -> 407 bytes libs/phpqrcode/cache/mask_7/mask_21_7.dat | 4 + libs/phpqrcode/cache/mask_7/mask_25_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_29_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_33_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_37_7.dat | Bin 0 -> 122 bytes libs/phpqrcode/cache/mask_7/mask_41_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_45_7.dat | Bin 0 -> 173 bytes libs/phpqrcode/cache/mask_7/mask_49_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_53_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_57_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_61_7.dat | 2 + libs/phpqrcode/cache/mask_7/mask_65_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_69_7.dat | Bin 0 -> 202 bytes libs/phpqrcode/cache/mask_7/mask_73_7.dat | Bin 0 -> 221 bytes libs/phpqrcode/cache/mask_7/mask_77_7.dat | Bin 0 -> 226 bytes libs/phpqrcode/cache/mask_7/mask_81_7.dat | 1 + libs/phpqrcode/cache/mask_7/mask_85_7.dat | Bin 0 -> 213 bytes libs/phpqrcode/cache/mask_7/mask_89_7.dat | Bin 0 -> 244 bytes libs/phpqrcode/cache/mask_7/mask_93_7.dat | Bin 0 -> 248 bytes libs/phpqrcode/cache/mask_7/mask_97_7.dat | 2 + libs/phpqrcode/lib/PHPQRCode.php | 42 + libs/phpqrcode/lib/PHPQRCode/Autoloader.php | 48 + libs/phpqrcode/lib/PHPQRCode/Constants.php | 58 + libs/phpqrcode/lib/PHPQRCode/FrameFiller.php | 96 + libs/phpqrcode/lib/PHPQRCode/QRbitstream.php | 182 + libs/phpqrcode/lib/PHPQRCode/QRcode.php | 158 + libs/phpqrcode/lib/PHPQRCode/QRencode.php | 137 + libs/phpqrcode/lib/PHPQRCode/QRimage.php | 95 + libs/phpqrcode/lib/PHPQRCode/QRinput.php | 486 +++ libs/phpqrcode/lib/PHPQRCode/QRinputItem.php | 246 ++ libs/phpqrcode/lib/PHPQRCode/QRmask.php | 325 ++ libs/phpqrcode/lib/PHPQRCode/QRrawcode.php | 117 + libs/phpqrcode/lib/PHPQRCode/QRrs.php | 56 + libs/phpqrcode/lib/PHPQRCode/QRrsItem.php | 162 + libs/phpqrcode/lib/PHPQRCode/QRrsblock.php | 25 + libs/phpqrcode/lib/PHPQRCode/QRspec.php | 586 +++ libs/phpqrcode/lib/PHPQRCode/QRsplit.php | 316 ++ libs/phpqrcode/lib/PHPQRCode/QRstr.php | 35 + libs/phpqrcode/lib/PHPQRCode/QRtools.php | 171 + libs/picodb/LICENSE | 21 + .../picodb/lib/PicoDb/Builder/BaseBuilder.php | 86 + .../lib/PicoDb/Builder/ConditionBuilder.php | 377 ++ .../lib/PicoDb/Builder/InsertBuilder.php | 36 + .../lib/PicoDb/Builder/OrConditionBuilder.php | 43 + .../lib/PicoDb/Builder/UpdateBuilder.php | 56 + libs/picodb/lib/PicoDb/Database.php | 392 ++ libs/picodb/lib/PicoDb/Driver/Base.php | 264 ++ libs/picodb/lib/PicoDb/Driver/Mssql.php | 247 ++ libs/picodb/lib/PicoDb/Driver/Mysql.php | 268 ++ libs/picodb/lib/PicoDb/Driver/Postgres.php | 212 ++ libs/picodb/lib/PicoDb/Driver/Sqlite.php | 207 + libs/picodb/lib/PicoDb/DriverFactory.php | 49 + libs/picodb/lib/PicoDb/Hashtable.php | 112 + libs/picodb/lib/PicoDb/LargeObject.php | 167 + libs/picodb/lib/PicoDb/SQLException.php | 15 + libs/picodb/lib/PicoDb/Schema.php | 119 + libs/picodb/lib/PicoDb/StatementHandler.php | 355 ++ libs/picodb/lib/PicoDb/Table.php | 768 ++++ libs/picodb/lib/PicoDb/UrlParser.php | 93 + libs/picodb/phpunit.xml | 30 + libs/swiftmailer/LICENSE | 19 + libs/swiftmailer/classes/Swift.php | 80 + libs/swiftmailer/classes/Swift/Attachment.php | 71 + .../AbstractFilterableInputStream.php | 181 + .../Swift/ByteStream/ArrayByteStream.php | 182 + .../Swift/ByteStream/FileByteStream.php | 231 ++ .../ByteStream/TemporaryFileByteStream.php | 42 + .../classes/Swift/CharacterReader.php | 67 + .../GenericFixedWidthReader.php | 97 + .../Swift/CharacterReader/UsAsciiReader.php | 84 + .../Swift/CharacterReader/Utf8Reader.php | 176 + .../classes/Swift/CharacterReaderFactory.php | 26 + .../SimpleCharacterReaderFactory.php | 124 + .../classes/Swift/CharacterStream.php | 89 + .../CharacterStream/ArrayCharacterStream.php | 293 ++ .../CharacterStream/NgCharacterStream.php | 267 ++ .../classes/Swift/ConfigurableSpool.php | 63 + .../classes/Swift/DependencyContainer.php | 373 ++ .../classes/Swift/DependencyException.php | 27 + .../classes/Swift/EmbeddedFile.php | 69 + libs/swiftmailer/classes/Swift/Encoder.php | 28 + .../classes/Swift/Encoder/Base64Encoder.php | 58 + .../classes/Swift/Encoder/QpEncoder.php | 300 ++ .../classes/Swift/Encoder/Rfc2231Encoder.php | 92 + libs/swiftmailer/classes/Swift/Encoding.php | 62 + .../classes/Swift/Events/CommandEvent.php | 65 + .../classes/Swift/Events/CommandListener.php | 24 + .../classes/Swift/Events/Event.php | 38 + .../classes/Swift/Events/EventDispatcher.php | 83 + .../classes/Swift/Events/EventListener.php | 18 + .../classes/Swift/Events/EventObject.php | 63 + .../classes/Swift/Events/ResponseEvent.php | 65 + .../classes/Swift/Events/ResponseListener.php | 24 + .../classes/Swift/Events/SendEvent.php | 129 + .../classes/Swift/Events/SendListener.php | 31 + .../Swift/Events/SimpleEventDispatcher.php | 156 + .../Swift/Events/TransportChangeEvent.php | 27 + .../Swift/Events/TransportChangeListener.php | 45 + .../Swift/Events/TransportExceptionEvent.php | 46 + .../Events/TransportExceptionListener.php | 24 + .../classes/Swift/FailoverTransport.php | 45 + libs/swiftmailer/classes/Swift/FileSpool.php | 208 + libs/swiftmailer/classes/Swift/FileStream.php | 24 + libs/swiftmailer/classes/Swift/Filterable.php | 32 + libs/swiftmailer/classes/Swift/Image.php | 57 + .../classes/Swift/InputByteStream.php | 75 + .../swiftmailer/classes/Swift/IoException.php | 29 + libs/swiftmailer/classes/Swift/KeyCache.php | 105 + .../classes/Swift/KeyCache/ArrayKeyCache.php | 206 + .../classes/Swift/KeyCache/DiskKeyCache.php | 321 ++ .../Swift/KeyCache/KeyCacheInputStream.php | 51 + .../classes/Swift/KeyCache/NullKeyCache.php | 115 + .../KeyCache/SimpleKeyCacheInputStream.php | 127 + .../classes/Swift/LoadBalancedTransport.php | 45 + .../classes/Swift/MailTransport.php | 47 + libs/swiftmailer/classes/Swift/Mailer.php | 114 + .../Swift/Mailer/ArrayRecipientIterator.php | 55 + .../Swift/Mailer/RecipientIterator.php | 32 + .../swiftmailer/classes/Swift/MemorySpool.php | 110 + libs/swiftmailer/classes/Swift/Message.php | 289 ++ .../classes/Swift/Mime/Attachment.php | 149 + .../classes/Swift/Mime/CharsetObserver.php | 24 + .../classes/Swift/Mime/ContentEncoder.php | 34 + .../ContentEncoder/Base64ContentEncoder.php | 104 + .../ContentEncoder/NativeQpContentEncoder.php | 123 + .../ContentEncoder/PlainContentEncoder.php | 162 + .../Mime/ContentEncoder/QpContentEncoder.php | 134 + .../ContentEncoder/QpContentEncoderProxy.php | 98 + .../Mime/ContentEncoder/RawContentEncoder.php | 64 + .../classes/Swift/Mime/EmbeddedFile.php | 45 + .../classes/Swift/Mime/EncodingObserver.php | 24 + .../classes/Swift/Mime/Grammar.php | 176 + .../swiftmailer/classes/Swift/Mime/Header.php | 93 + .../classes/Swift/Mime/HeaderEncoder.php | 24 + .../HeaderEncoder/Base64HeaderEncoder.php | 55 + .../Mime/HeaderEncoder/QpHeaderEncoder.php | 65 + .../classes/Swift/Mime/HeaderFactory.php | 78 + .../classes/Swift/Mime/HeaderSet.php | 169 + .../Swift/Mime/Headers/AbstractHeader.php | 501 +++ .../classes/Swift/Mime/Headers/DateHeader.php | 125 + .../Mime/Headers/IdentificationHeader.php | 180 + .../Swift/Mime/Headers/MailboxHeader.php | 351 ++ .../Swift/Mime/Headers/OpenDKIMHeader.php | 133 + .../Mime/Headers/ParameterizedHeader.php | 258 ++ .../classes/Swift/Mime/Headers/PathHeader.php | 143 + .../Swift/Mime/Headers/UnstructuredHeader.php | 112 + .../classes/Swift/Mime/Message.php | 223 ++ .../classes/Swift/Mime/MimeEntity.php | 117 + .../classes/Swift/Mime/MimePart.php | 212 ++ .../Swift/Mime/ParameterizedHeader.php | 34 + .../Swift/Mime/SimpleHeaderFactory.php | 193 + .../classes/Swift/Mime/SimpleHeaderSet.php | 414 ++ .../classes/Swift/Mime/SimpleMessage.php | 655 ++++ .../classes/Swift/Mime/SimpleMimeEntity.php | 846 +++++ libs/swiftmailer/classes/Swift/MimePart.php | 59 + .../classes/Swift/NullTransport.php | 36 + .../classes/Swift/OutputByteStream.php | 46 + .../classes/Swift/Plugins/AntiFloodPlugin.php | 141 + .../Swift/Plugins/BandwidthMonitorPlugin.php | 164 + .../Swift/Plugins/Decorator/Replacements.php | 31 + .../classes/Swift/Plugins/DecoratorPlugin.php | 204 + .../Swift/Plugins/ImpersonatePlugin.php | 69 + .../classes/Swift/Plugins/Logger.php | 36 + .../classes/Swift/Plugins/LoggerPlugin.php | 142 + .../Swift/Plugins/Loggers/ArrayLogger.php | 72 + .../Swift/Plugins/Loggers/EchoLogger.php | 58 + .../classes/Swift/Plugins/MessageLogger.php | 74 + .../Swift/Plugins/Pop/Pop3Connection.php | 31 + .../Swift/Plugins/Pop/Pop3Exception.php | 27 + .../Swift/Plugins/PopBeforeSmtpPlugin.php | 273 ++ .../Swift/Plugins/RedirectingPlugin.php | 213 ++ .../classes/Swift/Plugins/Reporter.php | 32 + .../classes/Swift/Plugins/ReporterPlugin.php | 61 + .../Swift/Plugins/Reporters/HitReporter.php | 59 + .../Swift/Plugins/Reporters/HtmlReporter.php | 39 + .../classes/Swift/Plugins/Sleeper.php | 24 + .../classes/Swift/Plugins/ThrottlerPlugin.php | 200 + .../classes/Swift/Plugins/Timer.php | 24 + .../swiftmailer/classes/Swift/Preferences.php | 100 + .../Swift/ReplacementFilterFactory.php | 27 + .../classes/Swift/RfcComplianceException.php | 27 + .../classes/Swift/SendmailTransport.php | 45 + .../classes/Swift/SignedMessage.php | 23 + libs/swiftmailer/classes/Swift/Signer.php | 20 + .../classes/Swift/Signers/BodySigner.php | 33 + .../classes/Swift/Signers/DKIMSigner.php | 712 ++++ .../classes/Swift/Signers/DomainKeySigner.php | 524 +++ .../classes/Swift/Signers/HeaderSigner.php | 65 + .../classes/Swift/Signers/OpenDKIMSigner.php | 190 + .../classes/Swift/Signers/SMimeSigner.php | 436 +++ .../classes/Swift/SmtpTransport.php | 58 + libs/swiftmailer/classes/Swift/Spool.php | 53 + .../classes/Swift/SpoolTransport.php | 47 + .../classes/Swift/StreamFilter.php | 35 + .../ByteArrayReplacementFilter.php | 170 + .../StreamFilters/StringReplacementFilter.php | 70 + .../StringReplacementFilterFactory.php | 45 + .../classes/Swift/SwiftException.php | 29 + libs/swiftmailer/classes/Swift/Transport.php | 54 + .../Swift/Transport/AbstractSmtpTransport.php | 499 +++ .../Esmtp/Auth/CramMd5Authenticator.php | 81 + .../Esmtp/Auth/LoginAuthenticator.php | 51 + .../Esmtp/Auth/NTLMAuthenticator.php | 725 ++++ .../Esmtp/Auth/PlainAuthenticator.php | 50 + .../Esmtp/Auth/XOAuth2Authenticator.php | 70 + .../Swift/Transport/Esmtp/AuthHandler.php | 263 ++ .../Swift/Transport/Esmtp/Authenticator.php | 35 + .../classes/Swift/Transport/EsmtpHandler.php | 86 + .../Swift/Transport/EsmtpTransport.php | 411 ++ .../Swift/Transport/FailoverTransport.php | 88 + .../classes/Swift/Transport/IoBuffer.php | 67 + .../Swift/Transport/LoadBalancedTransport.php | 183 + .../classes/Swift/Transport/MailInvoker.php | 32 + .../classes/Swift/Transport/MailTransport.php | 297 ++ .../classes/Swift/Transport/NullTransport.php | 93 + .../Swift/Transport/SendmailTransport.php | 160 + .../Swift/Transport/SimpleMailInvoker.php | 39 + .../classes/Swift/Transport/SmtpAgent.php | 36 + .../Swift/Transport/SpoolTransport.php | 117 + .../classes/Swift/Transport/StreamBuffer.php | 325 ++ .../classes/Swift/TransportException.php | 29 + libs/swiftmailer/classes/Swift/Validate.php | 43 + .../dependency_maps/cache_deps.php | 23 + .../dependency_maps/message_deps.php | 9 + .../swiftmailer/dependency_maps/mime_deps.php | 123 + .../dependency_maps/transport_deps.php | 76 + libs/swiftmailer/mime_types.php | 1007 +++++ libs/swiftmailer/preferences.php | 25 + libs/swiftmailer/swift_init.php | 28 + libs/swiftmailer/swift_required.php | 30 + libs/swiftmailer/swift_required_pear.php | 30 + .../swiftmailer_generate_mimes_config.php | 193 + robots.txt | 2 + web.config | 22 + 1827 files changed, 192340 insertions(+) create mode 100644 .htaccess create mode 100644 CONTRIBUTING.md create mode 100644 ChangeLog create mode 100644 LICENSE create mode 100644 app/.htaccess create mode 100644 app/Action/Base.php create mode 100644 app/Action/CommentCreation.php create mode 100644 app/Action/CommentCreationMoveTaskColumn.php create mode 100644 app/Action/StopSubtaskTimerMoveTaskColumn.php create mode 100644 app/Action/SubtaskTimerMoveTaskColumn.php create mode 100644 app/Action/TaskAssignCategoryColor.php create mode 100644 app/Action/TaskAssignCategoryLabel.php create mode 100644 app/Action/TaskAssignCategoryLink.php create mode 100644 app/Action/TaskAssignCategorySwimlaneChange.php create mode 100644 app/Action/TaskAssignColorCategory.php create mode 100644 app/Action/TaskAssignColorColumn.php create mode 100644 app/Action/TaskAssignColorLink.php create mode 100644 app/Action/TaskAssignColorOnDueDate.php create mode 100644 app/Action/TaskAssignColorOnStartDate.php create mode 100644 app/Action/TaskAssignColorPriority.php create mode 100644 app/Action/TaskAssignColorSwimlane.php create mode 100644 app/Action/TaskAssignColorUser.php create mode 100644 app/Action/TaskAssignCreator.php create mode 100644 app/Action/TaskAssignCurrentUser.php create mode 100644 app/Action/TaskAssignCurrentUserColumn.php create mode 100644 app/Action/TaskAssignCurrentUserColumnIfNoUserAlreadySet.php create mode 100644 app/Action/TaskAssignDueDateOnCreation.php create mode 100644 app/Action/TaskAssignDueDateOnMoveColumn.php create mode 100644 app/Action/TaskAssignPrioritySwimlane.php create mode 100644 app/Action/TaskAssignSpecificUser.php create mode 100644 app/Action/TaskAssignToUserOnCreationInColumn.php create mode 100644 app/Action/TaskAssignUser.php create mode 100644 app/Action/TaskAssignUserSwimlaneChange.php create mode 100644 app/Action/TaskClose.php create mode 100644 app/Action/TaskCloseColumn.php create mode 100644 app/Action/TaskCloseNoActivity.php create mode 100644 app/Action/TaskCloseNoActivityColumn.php create mode 100644 app/Action/TaskCloseNotMovedColumn.php create mode 100644 app/Action/TaskCreation.php create mode 100644 app/Action/TaskDuplicateAnotherProject.php create mode 100644 app/Action/TaskEmail.php create mode 100644 app/Action/TaskEmailNoActivity.php create mode 100644 app/Action/TaskMoveAnotherProject.php create mode 100644 app/Action/TaskMoveColumnAssigned.php create mode 100644 app/Action/TaskMoveColumnCategoryChange.php create mode 100644 app/Action/TaskMoveColumnClosed.php create mode 100644 app/Action/TaskMoveColumnNotMovedPeriod.php create mode 100644 app/Action/TaskMoveColumnOnDueDate.php create mode 100644 app/Action/TaskMoveColumnOnStartDate.php create mode 100644 app/Action/TaskMoveColumnUnAssigned.php create mode 100644 app/Action/TaskMoveSwimlaneAssigned.php create mode 100644 app/Action/TaskMoveSwimlaneCategoryChange.php create mode 100644 app/Action/TaskOpen.php create mode 100644 app/Action/TaskUpdateStartDate.php create mode 100644 app/Action/TaskUpdateStartDateOnMoveColumn.php create mode 100644 app/Analytic/AverageLeadCycleTimeAnalytic.php create mode 100644 app/Analytic/AverageTimeSpentColumnAnalytic.php create mode 100644 app/Analytic/EstimatedActualColumnAnalytic.php create mode 100644 app/Analytic/EstimatedTimeComparisonAnalytic.php create mode 100644 app/Analytic/TaskDistributionAnalytic.php create mode 100644 app/Analytic/UserDistributionAnalytic.php create mode 100644 app/Api/Authorization/ActionAuthorization.php create mode 100644 app/Api/Authorization/CategoryAuthorization.php create mode 100644 app/Api/Authorization/ColumnAuthorization.php create mode 100644 app/Api/Authorization/CommentAuthorization.php create mode 100644 app/Api/Authorization/ProcedureAuthorization.php create mode 100644 app/Api/Authorization/ProjectAuthorization.php create mode 100644 app/Api/Authorization/SubtaskAuthorization.php create mode 100644 app/Api/Authorization/TagAuthorization.php create mode 100644 app/Api/Authorization/TaskAuthorization.php create mode 100644 app/Api/Authorization/TaskFileAuthorization.php create mode 100644 app/Api/Authorization/TaskLinkAuthorization.php create mode 100644 app/Api/Authorization/UserAuthorization.php create mode 100644 app/Api/Middleware/AuthenticationMiddleware.php create mode 100644 app/Api/Procedure/ActionProcedure.php create mode 100644 app/Api/Procedure/AppProcedure.php create mode 100644 app/Api/Procedure/BaseProcedure.php create mode 100644 app/Api/Procedure/BoardProcedure.php create mode 100644 app/Api/Procedure/CategoryProcedure.php create mode 100644 app/Api/Procedure/ColumnProcedure.php create mode 100644 app/Api/Procedure/CommentProcedure.php create mode 100644 app/Api/Procedure/GroupMemberProcedure.php create mode 100644 app/Api/Procedure/GroupProcedure.php create mode 100644 app/Api/Procedure/LinkProcedure.php create mode 100644 app/Api/Procedure/MeProcedure.php create mode 100644 app/Api/Procedure/ProjectFileProcedure.php create mode 100644 app/Api/Procedure/ProjectMetaDataProcedure.php create mode 100644 app/Api/Procedure/ProjectPermissionProcedure.php create mode 100644 app/Api/Procedure/ProjectProcedure.php create mode 100644 app/Api/Procedure/SubtaskProcedure.php create mode 100644 app/Api/Procedure/SubtaskTimeTrackingProcedure.php create mode 100644 app/Api/Procedure/SwimlaneProcedure.php create mode 100644 app/Api/Procedure/TagProcedure.php create mode 100644 app/Api/Procedure/TaskExternalLinkProcedure.php create mode 100644 app/Api/Procedure/TaskFileProcedure.php create mode 100644 app/Api/Procedure/TaskLinkProcedure.php create mode 100644 app/Api/Procedure/TaskMetadataProcedure.php create mode 100644 app/Api/Procedure/TaskProcedure.php create mode 100644 app/Api/Procedure/TaskTagProcedure.php create mode 100644 app/Api/Procedure/UserProcedure.php create mode 100644 app/Auth/ApiAccessTokenAuth.php create mode 100644 app/Auth/DatabaseAuth.php create mode 100644 app/Auth/LdapAuth.php create mode 100644 app/Auth/RememberMeAuth.php create mode 100644 app/Auth/ReverseProxyAuth.php create mode 100644 app/Auth/TotpAuth.php create mode 100644 app/Console/BaseCommand.php create mode 100644 app/Console/CronjobCommand.php create mode 100644 app/Console/CssCommand.php create mode 100644 app/Console/DatabaseMigrationCommand.php create mode 100644 app/Console/DatabaseVersionCommand.php create mode 100644 app/Console/JobCommand.php create mode 100644 app/Console/JsCommand.php create mode 100644 app/Console/LocaleComparatorCommand.php create mode 100644 app/Console/LocaleSyncCommand.php create mode 100644 app/Console/PluginInstallCommand.php create mode 100644 app/Console/PluginUninstallCommand.php create mode 100644 app/Console/PluginUpgradeCommand.php create mode 100644 app/Console/ProjectActivityArchiveCommand.php create mode 100644 app/Console/ProjectArchiveCommand.php create mode 100644 app/Console/ProjectDailyColumnStatsExportCommand.php create mode 100644 app/Console/ProjectDailyStatsCalculationCommand.php create mode 100644 app/Console/ResetPasswordCommand.php create mode 100644 app/Console/ResetTwoFactorCommand.php create mode 100644 app/Console/SubtaskExportCommand.php create mode 100644 app/Console/TaskExportCommand.php create mode 100644 app/Console/TaskOverdueNotificationCommand.php create mode 100644 app/Console/TaskTriggerCommand.php create mode 100644 app/Console/TransitionExportCommand.php create mode 100644 app/Console/VersionCommand.php create mode 100644 app/Console/WorkerCommand.php create mode 100644 app/Controller/ActionController.php create mode 100644 app/Controller/ActionCreationController.php create mode 100644 app/Controller/ActivityController.php create mode 100644 app/Controller/AnalyticController.php create mode 100644 app/Controller/AppController.php create mode 100644 app/Controller/AuthController.php create mode 100644 app/Controller/AvatarFileController.php create mode 100644 app/Controller/BaseController.php create mode 100644 app/Controller/BoardAjaxController.php create mode 100644 app/Controller/BoardPopoverController.php create mode 100644 app/Controller/BoardTooltipController.php create mode 100644 app/Controller/BoardViewController.php create mode 100644 app/Controller/CaptchaController.php create mode 100644 app/Controller/CategoryController.php create mode 100644 app/Controller/ColumnController.php create mode 100644 app/Controller/ColumnMoveRestrictionController.php create mode 100644 app/Controller/ColumnRestrictionController.php create mode 100644 app/Controller/CommentController.php create mode 100644 app/Controller/CommentListController.php create mode 100644 app/Controller/CommentMailController.php create mode 100644 app/Controller/ConfigController.php create mode 100644 app/Controller/CronjobController.php create mode 100644 app/Controller/CurrencyController.php create mode 100644 app/Controller/CustomFilterController.php create mode 100644 app/Controller/DashboardController.php create mode 100644 app/Controller/DocumentationController.php create mode 100644 app/Controller/ExportController.php create mode 100644 app/Controller/ExternalTaskCreationController.php create mode 100644 app/Controller/ExternalTaskViewController.php create mode 100644 app/Controller/FeedController.php create mode 100644 app/Controller/FileViewerController.php create mode 100644 app/Controller/GroupAjaxController.php create mode 100644 app/Controller/GroupCreationController.php create mode 100644 app/Controller/GroupListController.php create mode 100644 app/Controller/GroupModificationController.php create mode 100644 app/Controller/ICalendarController.php create mode 100644 app/Controller/LinkController.php create mode 100644 app/Controller/OAuthController.php create mode 100644 app/Controller/PasswordResetController.php create mode 100644 app/Controller/PluginController.php create mode 100644 app/Controller/PredefinedTaskDescriptionController.php create mode 100644 app/Controller/ProjectActionDuplicationController.php create mode 100644 app/Controller/ProjectCreationController.php create mode 100644 app/Controller/ProjectEditController.php create mode 100644 app/Controller/ProjectFileController.php create mode 100644 app/Controller/ProjectListController.php create mode 100644 app/Controller/ProjectOverviewController.php create mode 100644 app/Controller/ProjectPermissionController.php create mode 100644 app/Controller/ProjectPredefinedContentController.php create mode 100644 app/Controller/ProjectRoleController.php create mode 100644 app/Controller/ProjectRoleRestrictionController.php create mode 100644 app/Controller/ProjectStatusController.php create mode 100644 app/Controller/ProjectTagController.php create mode 100644 app/Controller/ProjectUserOverviewController.php create mode 100644 app/Controller/ProjectViewController.php create mode 100644 app/Controller/SearchController.php create mode 100644 app/Controller/SubtaskController.php create mode 100644 app/Controller/SubtaskConverterController.php create mode 100644 app/Controller/SubtaskRestrictionController.php create mode 100644 app/Controller/SubtaskStatusController.php create mode 100644 app/Controller/SwimlaneController.php create mode 100644 app/Controller/TagController.php create mode 100644 app/Controller/TaskAjaxController.php create mode 100644 app/Controller/TaskBulkChangePropertyController.php create mode 100644 app/Controller/TaskBulkController.php create mode 100644 app/Controller/TaskBulkMoveColumnController.php create mode 100644 app/Controller/TaskCreationController.php create mode 100644 app/Controller/TaskDuplicationController.php create mode 100644 app/Controller/TaskExternalLinkController.php create mode 100644 app/Controller/TaskFileController.php create mode 100644 app/Controller/TaskImportController.php create mode 100644 app/Controller/TaskInternalLinkController.php create mode 100644 app/Controller/TaskListController.php create mode 100644 app/Controller/TaskMailController.php create mode 100644 app/Controller/TaskModificationController.php create mode 100644 app/Controller/TaskMovePositionController.php create mode 100644 app/Controller/TaskPopoverController.php create mode 100644 app/Controller/TaskRecurrenceController.php create mode 100644 app/Controller/TaskReorderController.php create mode 100644 app/Controller/TaskStatusController.php create mode 100644 app/Controller/TaskSuppressionController.php create mode 100644 app/Controller/TaskViewController.php create mode 100644 app/Controller/TwoFactorController.php create mode 100644 app/Controller/UserAjaxController.php create mode 100644 app/Controller/UserApiAccessController.php create mode 100644 app/Controller/UserCreationController.php create mode 100644 app/Controller/UserCredentialController.php create mode 100644 app/Controller/UserImportController.php create mode 100644 app/Controller/UserInviteController.php create mode 100644 app/Controller/UserListController.php create mode 100644 app/Controller/UserModificationController.php create mode 100644 app/Controller/UserStatusController.php create mode 100644 app/Controller/UserViewController.php create mode 100644 app/Controller/WebNotificationController.php create mode 100644 app/Core/Action/ActionManager.php create mode 100644 app/Core/Base.php create mode 100644 app/Core/Cache/BaseCache.php create mode 100644 app/Core/Cache/CacheInterface.php create mode 100644 app/Core/Cache/MemoryCache.php create mode 100644 app/Core/Controller/AccessForbiddenException.php create mode 100644 app/Core/Controller/BaseException.php create mode 100644 app/Core/Controller/BaseMiddleware.php create mode 100644 app/Core/Controller/PageNotFoundException.php create mode 100644 app/Core/Controller/Runner.php create mode 100644 app/Core/Csv.php create mode 100644 app/Core/DateParser.php create mode 100644 app/Core/Event/EventManager.php create mode 100644 app/Core/ExternalLink/ExternalLinkInterface.php create mode 100644 app/Core/ExternalLink/ExternalLinkManager.php create mode 100644 app/Core/ExternalLink/ExternalLinkProviderInterface.php create mode 100644 app/Core/ExternalLink/ExternalLinkProviderNotFound.php create mode 100644 app/Core/ExternalTask/AccessForbiddenException.php create mode 100644 app/Core/ExternalTask/ExternalTaskException.php create mode 100644 app/Core/ExternalTask/ExternalTaskInterface.php create mode 100644 app/Core/ExternalTask/ExternalTaskManager.php create mode 100644 app/Core/ExternalTask/ExternalTaskProviderInterface.php create mode 100644 app/Core/ExternalTask/NotFoundException.php create mode 100644 app/Core/ExternalTask/ProviderNotFoundException.php create mode 100644 app/Core/Filter/CriteriaInterface.php create mode 100644 app/Core/Filter/FilterInterface.php create mode 100644 app/Core/Filter/FormatterInterface.php create mode 100644 app/Core/Filter/Lexer.php create mode 100644 app/Core/Filter/LexerBuilder.php create mode 100644 app/Core/Filter/OrCriteria.php create mode 100644 app/Core/Filter/QueryBuilder.php create mode 100644 app/Core/Group/GroupBackendProviderInterface.php create mode 100644 app/Core/Group/GroupManager.php create mode 100644 app/Core/Group/GroupProviderInterface.php create mode 100644 app/Core/Helper.php create mode 100644 app/Core/Http/Client.php create mode 100644 app/Core/Http/ClientException.php create mode 100644 app/Core/Http/InvalidStatusException.php create mode 100644 app/Core/Http/OAuth2.php create mode 100644 app/Core/Http/RememberMeCookie.php create mode 100644 app/Core/Http/Request.php create mode 100644 app/Core/Http/Response.php create mode 100644 app/Core/Http/Route.php create mode 100644 app/Core/Http/Router.php create mode 100644 app/Core/Ldap/Client.php create mode 100644 app/Core/Ldap/ClientException.php create mode 100644 app/Core/Ldap/ConnectionException.php create mode 100644 app/Core/Ldap/Entries.php create mode 100644 app/Core/Ldap/Entry.php create mode 100644 app/Core/Ldap/Group.php create mode 100644 app/Core/Ldap/Query.php create mode 100644 app/Core/Ldap/User.php create mode 100644 app/Core/Log/Base.php create mode 100644 app/Core/Log/File.php create mode 100644 app/Core/Log/Logger.php create mode 100644 app/Core/Log/Stderr.php create mode 100644 app/Core/Log/Stdout.php create mode 100644 app/Core/Log/Syslog.php create mode 100644 app/Core/Log/System.php create mode 100644 app/Core/Mail/Client.php create mode 100644 app/Core/Mail/ClientInterface.php create mode 100644 app/Core/Mail/Transport/Mail.php create mode 100644 app/Core/Mail/Transport/Sendmail.php create mode 100644 app/Core/Mail/Transport/Smtp.php create mode 100644 app/Core/Markdown.php create mode 100644 app/Core/Notification/NotificationInterface.php create mode 100644 app/Core/ObjectStorage/FileStorage.php create mode 100644 app/Core/ObjectStorage/ObjectStorageException.php create mode 100644 app/Core/ObjectStorage/ObjectStorageInterface.php create mode 100644 app/Core/Paginator.php create mode 100644 app/Core/Plugin/Base.php create mode 100644 app/Core/Plugin/Directory.php create mode 100644 app/Core/Plugin/Hook.php create mode 100644 app/Core/Plugin/Installer.php create mode 100644 app/Core/Plugin/Loader.php create mode 100644 app/Core/Plugin/PluginException.php create mode 100644 app/Core/Plugin/PluginInstallerException.php create mode 100644 app/Core/Plugin/SchemaHandler.php create mode 100644 app/Core/Plugin/Version.php create mode 100644 app/Core/Queue/JobHandler.php create mode 100644 app/Core/Queue/QueueManager.php create mode 100644 app/Core/Security/AccessMap.php create mode 100644 app/Core/Security/AuthenticationManager.php create mode 100644 app/Core/Security/AuthenticationProviderInterface.php create mode 100644 app/Core/Security/Authorization.php create mode 100644 app/Core/Security/OAuthAuthenticationProviderInterface.php create mode 100644 app/Core/Security/OptionalAuthenticationProviderInterface.php create mode 100644 app/Core/Security/PasswordAuthenticationProviderInterface.php create mode 100644 app/Core/Security/PostAuthenticationProviderInterface.php create mode 100644 app/Core/Security/PreAuthenticationProviderInterface.php create mode 100644 app/Core/Security/Role.php create mode 100644 app/Core/Security/SessionCheckProviderInterface.php create mode 100644 app/Core/Security/Token.php create mode 100644 app/Core/Session/FlashMessage.php create mode 100644 app/Core/Session/SessionHandler.php create mode 100644 app/Core/Session/SessionManager.php create mode 100644 app/Core/Template.php create mode 100644 app/Core/Thumbnail.php create mode 100644 app/Core/Tool.php create mode 100644 app/Core/Translator.php create mode 100644 app/Core/User/Avatar/AvatarManager.php create mode 100644 app/Core/User/Avatar/AvatarProviderInterface.php create mode 100644 app/Core/User/GroupSync.php create mode 100644 app/Core/User/UserBackendProviderInterface.php create mode 100644 app/Core/User/UserManager.php create mode 100644 app/Core/User/UserProfile.php create mode 100644 app/Core/User/UserProperty.php create mode 100644 app/Core/User/UserProviderInterface.php create mode 100644 app/Core/User/UserSession.php create mode 100644 app/Core/User/UserSync.php create mode 100644 app/Decorator/ColumnMoveRestrictionCacheDecorator.php create mode 100644 app/Decorator/ColumnRestrictionCacheDecorator.php create mode 100644 app/Decorator/MetadataCacheDecorator.php create mode 100644 app/Decorator/ProjectRoleRestrictionCacheDecorator.php create mode 100644 app/Decorator/UserCacheDecorator.php create mode 100644 app/Event/AuthFailureEvent.php create mode 100644 app/Event/AuthSuccessEvent.php create mode 100644 app/Event/CommentEvent.php create mode 100644 app/Event/GenericEvent.php create mode 100644 app/Event/ProjectFileEvent.php create mode 100644 app/Event/SubtaskEvent.php create mode 100644 app/Event/TaskEvent.php create mode 100644 app/Event/TaskFileEvent.php create mode 100644 app/Event/TaskLinkEvent.php create mode 100644 app/Event/TaskListEvent.php create mode 100644 app/Event/UserProfileSyncEvent.php create mode 100644 app/EventBuilder/BaseEventBuilder.php create mode 100644 app/EventBuilder/CommentEventBuilder.php create mode 100644 app/EventBuilder/EventIteratorBuilder.php create mode 100644 app/EventBuilder/ProjectFileEventBuilder.php create mode 100644 app/EventBuilder/SubtaskEventBuilder.php create mode 100644 app/EventBuilder/TaskEventBuilder.php create mode 100644 app/EventBuilder/TaskFileEventBuilder.php create mode 100644 app/EventBuilder/TaskLinkEventBuilder.php create mode 100644 app/Export/SubtaskExport.php create mode 100644 app/Export/TaskExport.php create mode 100644 app/Export/TransitionExport.php create mode 100644 app/ExternalLink/AttachmentLink.php create mode 100644 app/ExternalLink/AttachmentLinkProvider.php create mode 100644 app/ExternalLink/BaseLink.php create mode 100644 app/ExternalLink/BaseLinkProvider.php create mode 100644 app/ExternalLink/FileLink.php create mode 100644 app/ExternalLink/FileLinkProvider.php create mode 100644 app/ExternalLink/WebLink.php create mode 100644 app/ExternalLink/WebLinkProvider.php create mode 100644 app/Filter/BaseComparisonFilter.php create mode 100644 app/Filter/BaseDateFilter.php create mode 100644 app/Filter/BaseDateRangeFilter.php create mode 100644 app/Filter/BaseFilter.php create mode 100644 app/Filter/ProjectActivityCreationDateFilter.php create mode 100644 app/Filter/ProjectActivityCreatorFilter.php create mode 100644 app/Filter/ProjectActivityProjectIdFilter.php create mode 100644 app/Filter/ProjectActivityProjectIdsFilter.php create mode 100644 app/Filter/ProjectActivityProjectNameFilter.php create mode 100644 app/Filter/ProjectActivityTaskIdFilter.php create mode 100644 app/Filter/ProjectActivityTaskStatusFilter.php create mode 100644 app/Filter/ProjectActivityTaskTitleFilter.php create mode 100644 app/Filter/ProjectGroupRoleProjectFilter.php create mode 100644 app/Filter/ProjectGroupRoleUsernameFilter.php create mode 100644 app/Filter/ProjectIdsFilter.php create mode 100644 app/Filter/ProjectStatusFilter.php create mode 100644 app/Filter/ProjectTypeFilter.php create mode 100644 app/Filter/ProjectUserRoleProjectFilter.php create mode 100644 app/Filter/ProjectUserRoleUsernameFilter.php create mode 100644 app/Filter/TaskAssigneeFilter.php create mode 100644 app/Filter/TaskCategoryFilter.php create mode 100644 app/Filter/TaskColorFilter.php create mode 100644 app/Filter/TaskColumnFilter.php create mode 100644 app/Filter/TaskCommentFilter.php create mode 100644 app/Filter/TaskCompletionDateFilter.php create mode 100644 app/Filter/TaskCompletionDateRangeFilter.php create mode 100644 app/Filter/TaskCreationDateFilter.php create mode 100644 app/Filter/TaskCreationDateRangeFilter.php create mode 100644 app/Filter/TaskCreatorFilter.php create mode 100644 app/Filter/TaskDescriptionFilter.php create mode 100644 app/Filter/TaskDueDateFilter.php create mode 100644 app/Filter/TaskDueDateRangeFilter.php create mode 100644 app/Filter/TaskIdExclusionFilter.php create mode 100644 app/Filter/TaskIdFilter.php create mode 100644 app/Filter/TaskLinkFilter.php create mode 100644 app/Filter/TaskModificationDateFilter.php create mode 100644 app/Filter/TaskModificationDateRangeFilter.php create mode 100644 app/Filter/TaskMovedDateFilter.php create mode 100644 app/Filter/TaskMovedDateRangeFilter.php create mode 100644 app/Filter/TaskPriorityFilter.php create mode 100644 app/Filter/TaskProjectFilter.php create mode 100644 app/Filter/TaskProjectsFilter.php create mode 100644 app/Filter/TaskReferenceFilter.php create mode 100644 app/Filter/TaskScoreFilter.php create mode 100644 app/Filter/TaskStartDateFilter.php create mode 100644 app/Filter/TaskStartsWithIdFilter.php create mode 100644 app/Filter/TaskStatusFilter.php create mode 100644 app/Filter/TaskSubtaskAssigneeFilter.php create mode 100644 app/Filter/TaskSwimlaneFilter.php create mode 100644 app/Filter/TaskTagFilter.php create mode 100644 app/Filter/TaskTitleFilter.php create mode 100644 app/Filter/UserNameFilter.php create mode 100644 app/Formatter/BaseFormatter.php create mode 100644 app/Formatter/BoardColumnFormatter.php create mode 100644 app/Formatter/BoardFormatter.php create mode 100644 app/Formatter/BoardSwimlaneFormatter.php create mode 100644 app/Formatter/BoardTaskFormatter.php create mode 100644 app/Formatter/GroupAutoCompleteFormatter.php create mode 100644 app/Formatter/ProjectActivityEventFormatter.php create mode 100644 app/Formatter/ProjectApiFormatter.php create mode 100644 app/Formatter/ProjectsApiFormatter.php create mode 100644 app/Formatter/SubtaskListFormatter.php create mode 100644 app/Formatter/SubtaskTimeTrackingCalendarFormatter.php create mode 100644 app/Formatter/TaskApiFormatter.php create mode 100644 app/Formatter/TaskAutoCompleteFormatter.php create mode 100644 app/Formatter/TaskICalFormatter.php create mode 100644 app/Formatter/TaskListFormatter.php create mode 100644 app/Formatter/TaskListSubtaskAssigneeFormatter.php create mode 100644 app/Formatter/TaskListSubtaskFormatter.php create mode 100644 app/Formatter/TaskSuggestMenuFormatter.php create mode 100644 app/Formatter/TasksApiFormatter.php create mode 100644 app/Formatter/UserAutoCompleteFormatter.php create mode 100644 app/Formatter/UserMentionFormatter.php create mode 100644 app/Group/DatabaseBackendGroupProvider.php create mode 100644 app/Group/DatabaseGroupProvider.php create mode 100644 app/Group/LdapBackendGroupProvider.php create mode 100644 app/Group/LdapGroupProvider.php create mode 100644 app/Helper/AppHelper.php create mode 100644 app/Helper/AssetHelper.php create mode 100644 app/Helper/AvatarHelper.php create mode 100644 app/Helper/BoardHelper.php create mode 100644 app/Helper/CommentHelper.php create mode 100644 app/Helper/DateHelper.php create mode 100644 app/Helper/FileHelper.php create mode 100644 app/Helper/FormHelper.php create mode 100644 app/Helper/HookHelper.php create mode 100644 app/Helper/LayoutHelper.php create mode 100644 app/Helper/MailHelper.php create mode 100644 app/Helper/ModalHelper.php create mode 100644 app/Helper/ModelHelper.php create mode 100644 app/Helper/ProjectActivityHelper.php create mode 100644 app/Helper/ProjectHeaderHelper.php create mode 100644 app/Helper/ProjectRoleHelper.php create mode 100644 app/Helper/SubtaskHelper.php create mode 100644 app/Helper/TaskHelper.php create mode 100644 app/Helper/TextHelper.php create mode 100644 app/Helper/UrlHelper.php create mode 100644 app/Helper/UserHelper.php create mode 100644 app/Import/TaskImport.php create mode 100644 app/Import/UserImport.php create mode 100644 app/Job/BaseJob.php create mode 100644 app/Job/CommentEventJob.php create mode 100644 app/Job/EmailJob.php create mode 100644 app/Job/HttpAsyncJob.php create mode 100644 app/Job/NotificationJob.php create mode 100644 app/Job/ProjectFileEventJob.php create mode 100644 app/Job/ProjectMetricJob.php create mode 100644 app/Job/SubtaskEventJob.php create mode 100644 app/Job/TaskEventJob.php create mode 100644 app/Job/TaskFileEventJob.php create mode 100644 app/Job/TaskLinkEventJob.php create mode 100644 app/Job/UserMentionJob.php create mode 100644 app/Locale/ar_SY/translations.php create mode 100644 app/Locale/bg_BG/translations.php create mode 100644 app/Locale/bs_BA/translations.php create mode 100644 app/Locale/ca_ES/translations.php create mode 100644 app/Locale/cs_CZ/translations.php create mode 100644 app/Locale/da_DK/translations.php create mode 100644 app/Locale/de_DE/translations.php create mode 100644 app/Locale/de_DE_du/translations.php create mode 100644 app/Locale/el_GR/translations.php create mode 100644 app/Locale/es_ES/translations.php create mode 100644 app/Locale/es_VE/translations.php create mode 100644 app/Locale/fa_IR/translations.php create mode 100644 app/Locale/fi_FI/translations.php create mode 100644 app/Locale/fr_FR/translations.php create mode 100644 app/Locale/hr_HR/translations.php create mode 100644 app/Locale/hu_HU/translations.php create mode 100644 app/Locale/id_ID/translations.php create mode 100644 app/Locale/it_IT/translations.php create mode 100644 app/Locale/ja_JP/translations.php create mode 100644 app/Locale/ko_KR/translations.php create mode 100644 app/Locale/mk_MK/translations.php create mode 100644 app/Locale/my_MY/translations.php create mode 100644 app/Locale/nb_NO/translations.php create mode 100644 app/Locale/nl_NL/translations.php create mode 100644 app/Locale/pl_PL/translations.php create mode 100644 app/Locale/pt_BR/translations.php create mode 100644 app/Locale/pt_PT/translations.php create mode 100644 app/Locale/ro_RO/translations.php create mode 100644 app/Locale/ru_RU/translations.php create mode 100644 app/Locale/sk_SK/translations.php create mode 100644 app/Locale/sr_Latn_RS/translations.php create mode 100644 app/Locale/sv_SE/translations.php create mode 100644 app/Locale/th_TH/translations.php create mode 100644 app/Locale/tr_TR/translations.php create mode 100644 app/Locale/uk_UA/translations.php create mode 100644 app/Locale/vi_VN/translations.php create mode 100644 app/Locale/zh_CN/translations.php create mode 100644 app/Locale/zh_TW/translations.php create mode 100644 app/Middleware/ApplicationAuthorizationMiddleware.php create mode 100644 app/Middleware/AuthenticationMiddleware.php create mode 100644 app/Middleware/BootstrapMiddleware.php create mode 100644 app/Middleware/PostAuthenticationMiddleware.php create mode 100644 app/Middleware/ProjectAuthorizationMiddleware.php create mode 100644 app/Model/ActionModel.php create mode 100644 app/Model/ActionParameterModel.php create mode 100644 app/Model/AvatarFileModel.php create mode 100644 app/Model/BoardModel.php create mode 100644 app/Model/CaptchaModel.php create mode 100644 app/Model/CategoryModel.php create mode 100644 app/Model/ColorModel.php create mode 100644 app/Model/ColumnModel.php create mode 100644 app/Model/ColumnMoveRestrictionModel.php create mode 100644 app/Model/ColumnRestrictionModel.php create mode 100644 app/Model/CommentModel.php create mode 100644 app/Model/ConfigModel.php create mode 100644 app/Model/CurrencyModel.php create mode 100644 app/Model/CustomFilterModel.php create mode 100644 app/Model/FileModel.php create mode 100644 app/Model/GroupMemberModel.php create mode 100644 app/Model/GroupModel.php create mode 100644 app/Model/InviteModel.php create mode 100644 app/Model/LanguageModel.php create mode 100644 app/Model/LastLoginModel.php create mode 100644 app/Model/LinkModel.php create mode 100644 app/Model/MetadataModel.php create mode 100644 app/Model/NotificationModel.php create mode 100644 app/Model/NotificationTypeModel.php create mode 100644 app/Model/PasswordResetModel.php create mode 100644 app/Model/PredefinedTaskDescriptionModel.php create mode 100644 app/Model/ProjectActivityModel.php create mode 100644 app/Model/ProjectDailyColumnStatsModel.php create mode 100644 app/Model/ProjectDailyStatsModel.php create mode 100644 app/Model/ProjectDuplicationModel.php create mode 100644 app/Model/ProjectFileModel.php create mode 100644 app/Model/ProjectGroupRoleModel.php create mode 100644 app/Model/ProjectMetadataModel.php create mode 100644 app/Model/ProjectModel.php create mode 100644 app/Model/ProjectNotificationModel.php create mode 100644 app/Model/ProjectNotificationTypeModel.php create mode 100644 app/Model/ProjectPermissionModel.php create mode 100644 app/Model/ProjectRoleModel.php create mode 100644 app/Model/ProjectRoleRestrictionModel.php create mode 100644 app/Model/ProjectTaskDuplicationModel.php create mode 100644 app/Model/ProjectTaskPriorityModel.php create mode 100644 app/Model/ProjectUserRoleModel.php create mode 100644 app/Model/RememberMeSessionModel.php create mode 100644 app/Model/SettingModel.php create mode 100644 app/Model/SubtaskModel.php create mode 100644 app/Model/SubtaskPositionModel.php create mode 100644 app/Model/SubtaskStatusModel.php create mode 100644 app/Model/SubtaskTaskConversionModel.php create mode 100644 app/Model/SubtaskTimeTrackingModel.php create mode 100644 app/Model/SwimlaneModel.php create mode 100644 app/Model/TagDuplicationModel.php create mode 100644 app/Model/TagModel.php create mode 100644 app/Model/TaskAnalyticModel.php create mode 100644 app/Model/TaskCreationModel.php create mode 100644 app/Model/TaskDuplicationModel.php create mode 100644 app/Model/TaskExternalLinkModel.php create mode 100644 app/Model/TaskFileModel.php create mode 100644 app/Model/TaskFinderModel.php create mode 100644 app/Model/TaskLinkModel.php create mode 100644 app/Model/TaskMetadataModel.php create mode 100644 app/Model/TaskModel.php create mode 100644 app/Model/TaskModificationModel.php create mode 100644 app/Model/TaskPositionModel.php create mode 100644 app/Model/TaskProjectDuplicationModel.php create mode 100644 app/Model/TaskProjectMoveModel.php create mode 100644 app/Model/TaskRecurrenceModel.php create mode 100644 app/Model/TaskReorderModel.php create mode 100644 app/Model/TaskStatusModel.php create mode 100644 app/Model/TaskTagModel.php create mode 100644 app/Model/ThemeModel.php create mode 100644 app/Model/TimezoneModel.php create mode 100644 app/Model/TransitionModel.php create mode 100644 app/Model/UserLockingModel.php create mode 100644 app/Model/UserMetadataModel.php create mode 100644 app/Model/UserModel.php create mode 100644 app/Model/UserNotificationFilterModel.php create mode 100644 app/Model/UserNotificationModel.php create mode 100644 app/Model/UserNotificationTypeModel.php create mode 100644 app/Model/UserUnreadNotificationModel.php create mode 100644 app/Notification/ActivityStreamNotification.php create mode 100644 app/Notification/MailNotification.php create mode 100644 app/Notification/WebNotification.php create mode 100644 app/Notification/WebhookNotification.php create mode 100644 app/Pagination/DashboardPagination.php create mode 100644 app/Pagination/ProjectPagination.php create mode 100644 app/Pagination/SubtaskPagination.php create mode 100644 app/Pagination/TaskPagination.php create mode 100644 app/Pagination/UserPagination.php create mode 100644 app/Schema/Migration.php create mode 100644 app/Schema/Mssql.php create mode 100644 app/Schema/Mysql.php create mode 100644 app/Schema/Postgres.php create mode 100644 app/Schema/Sql/mysql.sql create mode 100644 app/Schema/Sql/postgres.sql create mode 100644 app/Schema/Sqlite.php create mode 100644 app/ServiceProvider/ActionProvider.php create mode 100644 app/ServiceProvider/ApiProvider.php create mode 100644 app/ServiceProvider/AuthenticationProvider.php create mode 100644 app/ServiceProvider/AvatarProvider.php create mode 100644 app/ServiceProvider/CacheProvider.php create mode 100644 app/ServiceProvider/ClassProvider.php create mode 100644 app/ServiceProvider/CommandProvider.php create mode 100644 app/ServiceProvider/DatabaseProvider.php create mode 100644 app/ServiceProvider/EventDispatcherProvider.php create mode 100644 app/ServiceProvider/ExternalLinkProvider.php create mode 100644 app/ServiceProvider/ExternalTaskProvider.php create mode 100644 app/ServiceProvider/FilterProvider.php create mode 100644 app/ServiceProvider/FormatterProvider.php create mode 100644 app/ServiceProvider/GroupProvider.php create mode 100644 app/ServiceProvider/HelperProvider.php create mode 100644 app/ServiceProvider/JobProvider.php create mode 100644 app/ServiceProvider/LoggingProvider.php create mode 100644 app/ServiceProvider/MailProvider.php create mode 100644 app/ServiceProvider/NotificationProvider.php create mode 100644 app/ServiceProvider/ObjectStorageProvider.php create mode 100644 app/ServiceProvider/PluginProvider.php create mode 100644 app/ServiceProvider/QueueProvider.php create mode 100644 app/ServiceProvider/RouteProvider.php create mode 100644 app/ServiceProvider/SessionProvider.php create mode 100644 app/ServiceProvider/UserProvider.php create mode 100644 app/Subscriber/AuthSubscriber.php create mode 100644 app/Subscriber/BaseSubscriber.php create mode 100644 app/Subscriber/BootstrapSubscriber.php create mode 100644 app/Subscriber/LdapUserPhotoSubscriber.php create mode 100644 app/Subscriber/NotificationSubscriber.php create mode 100644 app/Subscriber/ProjectDailySummarySubscriber.php create mode 100644 app/Subscriber/ProjectModificationDateSubscriber.php create mode 100644 app/Subscriber/RecurringTaskSubscriber.php create mode 100644 app/Subscriber/TransitionSubscriber.php create mode 100644 app/Template/action/index.php create mode 100644 app/Template/action/remove.php create mode 100644 app/Template/action_creation/create.php create mode 100644 app/Template/action_creation/event.php create mode 100644 app/Template/action_creation/params.php create mode 100644 app/Template/activity/filter_dropdown.php create mode 100644 app/Template/activity/project.php create mode 100644 app/Template/activity/task.php create mode 100644 app/Template/activity/user.php create mode 100644 app/Template/analytic/avg_time_columns.php create mode 100644 app/Template/analytic/burndown.php create mode 100644 app/Template/analytic/cfd.php create mode 100644 app/Template/analytic/estimated_actual_column.php create mode 100644 app/Template/analytic/layout.php create mode 100644 app/Template/analytic/lead_cycle_time.php create mode 100644 app/Template/analytic/sidebar.php create mode 100644 app/Template/analytic/task_distribution.php create mode 100644 app/Template/analytic/time_comparison.php create mode 100644 app/Template/analytic/user_distribution.php create mode 100644 app/Template/app/filters_helper.php create mode 100644 app/Template/app/forbidden.php create mode 100644 app/Template/app/notfound.php create mode 100644 app/Template/auth/index.php create mode 100644 app/Template/avatar_file/show.php create mode 100644 app/Template/board/table_column.php create mode 100644 app/Template/board/table_column_first.php create mode 100644 app/Template/board/table_container.php create mode 100644 app/Template/board/table_swimlane.php create mode 100644 app/Template/board/table_tasks.php create mode 100644 app/Template/board/task_avatar.php create mode 100644 app/Template/board/task_footer.php create mode 100644 app/Template/board/task_private.php create mode 100644 app/Template/board/task_public.php create mode 100644 app/Template/board/tooltip_description.php create mode 100644 app/Template/board/tooltip_external_links.php create mode 100644 app/Template/board/tooltip_files.php create mode 100644 app/Template/board/tooltip_subtasks.php create mode 100644 app/Template/board/tooltip_tasklinks.php create mode 100644 app/Template/board/view_private.php create mode 100644 app/Template/board/view_public.php create mode 100644 app/Template/board_popover/close_all_tasks_column.php create mode 100644 app/Template/category/create.php create mode 100644 app/Template/category/edit.php create mode 100644 app/Template/category/index.php create mode 100644 app/Template/category/remove.php create mode 100644 app/Template/column/create.php create mode 100644 app/Template/column/edit.php create mode 100644 app/Template/column/index.php create mode 100644 app/Template/column/remove.php create mode 100644 app/Template/column_move_restriction/create.php create mode 100644 app/Template/column_move_restriction/remove.php create mode 100644 app/Template/column_restriction/create.php create mode 100644 app/Template/column_restriction/remove.php create mode 100644 app/Template/comment/create.php create mode 100644 app/Template/comment/edit.php create mode 100644 app/Template/comment/remove.php create mode 100644 app/Template/comment/show.php create mode 100644 app/Template/comment_list/create.php create mode 100644 app/Template/comment_list/show.php create mode 100644 app/Template/comment_mail/create.php create mode 100644 app/Template/comment_mail/email.php create mode 100644 app/Template/config/about.php create mode 100644 app/Template/config/api.php create mode 100644 app/Template/config/application.php create mode 100644 app/Template/config/board.php create mode 100644 app/Template/config/email.php create mode 100644 app/Template/config/integrations.php create mode 100644 app/Template/config/keyboard_shortcuts.php create mode 100644 app/Template/config/layout.php create mode 100644 app/Template/config/project.php create mode 100644 app/Template/config/sidebar.php create mode 100644 app/Template/config/upload_db.php create mode 100644 app/Template/config/webhook.php create mode 100644 app/Template/currency/change.php create mode 100644 app/Template/currency/create.php create mode 100644 app/Template/currency/show.php create mode 100644 app/Template/custom_filter/create.php create mode 100644 app/Template/custom_filter/edit.php create mode 100644 app/Template/custom_filter/index.php create mode 100644 app/Template/custom_filter/remove.php create mode 100644 app/Template/dashboard/layout.php create mode 100644 app/Template/dashboard/overview.php create mode 100644 app/Template/dashboard/projects.php create mode 100644 app/Template/dashboard/sidebar.php create mode 100644 app/Template/dashboard/subtasks.php create mode 100644 app/Template/dashboard/tasks.php create mode 100644 app/Template/event/comment_create.php create mode 100644 app/Template/event/comment_delete.php create mode 100644 app/Template/event/comment_update.php create mode 100644 app/Template/event/events.php create mode 100644 app/Template/event/subtask_create.php create mode 100644 app/Template/event/subtask_delete.php create mode 100644 app/Template/event/subtask_update.php create mode 100644 app/Template/event/task_assignee_change.php create mode 100644 app/Template/event/task_close.php create mode 100644 app/Template/event/task_create.php create mode 100644 app/Template/event/task_file_create.php create mode 100644 app/Template/event/task_file_destroy.php create mode 100644 app/Template/event/task_internal_link_create_update.php create mode 100644 app/Template/event/task_internal_link_delete.php create mode 100644 app/Template/event/task_move_column.php create mode 100644 app/Template/event/task_move_position.php create mode 100644 app/Template/event/task_move_project.php create mode 100644 app/Template/event/task_move_swimlane.php create mode 100644 app/Template/event/task_open.php create mode 100644 app/Template/event/task_update.php create mode 100644 app/Template/export/header.php create mode 100644 app/Template/export/subtasks.php create mode 100644 app/Template/export/summary.php create mode 100644 app/Template/export/tasks.php create mode 100644 app/Template/export/transitions.php create mode 100644 app/Template/external_task_creation/step1.php create mode 100644 app/Template/external_task_creation/step2.php create mode 100644 app/Template/external_task_modification/show.php create mode 100644 app/Template/feed/project.php create mode 100644 app/Template/feed/user.php create mode 100644 app/Template/file_viewer/show.php create mode 100644 app/Template/group/associate.php create mode 100644 app/Template/group/dissociate.php create mode 100644 app/Template/group/dropdown.php create mode 100644 app/Template/group/index.php create mode 100644 app/Template/group/remove.php create mode 100644 app/Template/group/user_dropdown.php create mode 100644 app/Template/group/users.php create mode 100644 app/Template/group_creation/show.php create mode 100644 app/Template/group_modification/show.php create mode 100644 app/Template/header.php create mode 100644 app/Template/header/board_selector.php create mode 100644 app/Template/header/creation_dropdown.php create mode 100644 app/Template/header/custom_board_selector.php create mode 100644 app/Template/header/title.php create mode 100644 app/Template/header/user_dropdown.php create mode 100644 app/Template/header/user_notifications.php create mode 100644 app/Template/layout.php create mode 100644 app/Template/link/create.php create mode 100644 app/Template/link/edit.php create mode 100644 app/Template/link/remove.php create mode 100644 app/Template/link/show.php create mode 100644 app/Template/notification/comment_create.php create mode 100644 app/Template/notification/comment_delete.php create mode 100644 app/Template/notification/comment_update.php create mode 100644 app/Template/notification/comment_user_mention.php create mode 100644 app/Template/notification/footer.php create mode 100644 app/Template/notification/subtask_create.php create mode 100644 app/Template/notification/subtask_delete.php create mode 100644 app/Template/notification/subtask_update.php create mode 100644 app/Template/notification/task_assignee_change.php create mode 100644 app/Template/notification/task_close.php create mode 100644 app/Template/notification/task_create.php create mode 100644 app/Template/notification/task_file_create.php create mode 100644 app/Template/notification/task_file_destroy.php create mode 100644 app/Template/notification/task_internal_link_create_update.php create mode 100644 app/Template/notification/task_internal_link_delete.php create mode 100644 app/Template/notification/task_move_column.php create mode 100644 app/Template/notification/task_move_position.php create mode 100644 app/Template/notification/task_move_project.php create mode 100644 app/Template/notification/task_move_swimlane.php create mode 100644 app/Template/notification/task_open.php create mode 100644 app/Template/notification/task_overdue.php create mode 100644 app/Template/notification/task_update.php create mode 100644 app/Template/notification/task_user_mention.php create mode 100644 app/Template/password_reset/change.php create mode 100644 app/Template/password_reset/create.php create mode 100644 app/Template/password_reset/email.php create mode 100644 app/Template/plugin/directory.php create mode 100644 app/Template/plugin/layout.php create mode 100644 app/Template/plugin/remove.php create mode 100644 app/Template/plugin/show.php create mode 100644 app/Template/plugin/sidebar.php create mode 100644 app/Template/predefined_task_description/create.php create mode 100644 app/Template/predefined_task_description/edit.php create mode 100644 app/Template/predefined_task_description/remove.php create mode 100644 app/Template/project/dropdown.php create mode 100644 app/Template/project/layout.php create mode 100644 app/Template/project/sidebar.php create mode 100644 app/Template/project_action_duplication/show.php create mode 100644 app/Template/project_creation/create.php create mode 100644 app/Template/project_edit/show.php create mode 100644 app/Template/project_file/create.php create mode 100644 app/Template/project_file/remove.php create mode 100644 app/Template/project_header/dropdown.php create mode 100644 app/Template/project_header/header.php create mode 100644 app/Template/project_header/search.php create mode 100644 app/Template/project_header/views.php create mode 100644 app/Template/project_list/header.php create mode 100644 app/Template/project_list/listing.php create mode 100644 app/Template/project_list/project_details.php create mode 100644 app/Template/project_list/project_icons.php create mode 100644 app/Template/project_list/project_title.php create mode 100644 app/Template/project_list/sort_menu.php create mode 100644 app/Template/project_overview/activity.php create mode 100644 app/Template/project_overview/attachments.php create mode 100644 app/Template/project_overview/columns.php create mode 100644 app/Template/project_overview/description.php create mode 100644 app/Template/project_overview/files.php create mode 100644 app/Template/project_overview/images.php create mode 100644 app/Template/project_overview/information.php create mode 100644 app/Template/project_overview/show.php create mode 100644 app/Template/project_permission/groups.php create mode 100644 app/Template/project_permission/index.php create mode 100644 app/Template/project_permission/users.php create mode 100644 app/Template/project_predefined_content/show.php create mode 100644 app/Template/project_role/create.php create mode 100644 app/Template/project_role/edit.php create mode 100644 app/Template/project_role/remove.php create mode 100644 app/Template/project_role/show.php create mode 100644 app/Template/project_role_restriction/create.php create mode 100644 app/Template/project_role_restriction/remove.php create mode 100644 app/Template/project_status/disable.php create mode 100644 app/Template/project_status/enable.php create mode 100644 app/Template/project_status/remove.php create mode 100644 app/Template/project_tag/create.php create mode 100644 app/Template/project_tag/edit.php create mode 100644 app/Template/project_tag/index.php create mode 100644 app/Template/project_tag/make_global.php create mode 100644 app/Template/project_tag/remove.php create mode 100644 app/Template/project_user_overview/layout.php create mode 100644 app/Template/project_user_overview/roles.php create mode 100644 app/Template/project_user_overview/sidebar.php create mode 100644 app/Template/project_user_overview/tasks.php create mode 100644 app/Template/project_user_overview/tooltip_users.php create mode 100644 app/Template/project_view/duplicate.php create mode 100644 app/Template/project_view/importTasks.php create mode 100644 app/Template/project_view/integrations.php create mode 100644 app/Template/project_view/notifications.php create mode 100644 app/Template/project_view/share.php create mode 100644 app/Template/project_view/show.php create mode 100644 app/Template/search/activity.php create mode 100644 app/Template/search/index.php create mode 100644 app/Template/search/results.php create mode 100644 app/Template/subtask/create.php create mode 100644 app/Template/subtask/edit.php create mode 100644 app/Template/subtask/menu.php create mode 100644 app/Template/subtask/remove.php create mode 100644 app/Template/subtask/show.php create mode 100644 app/Template/subtask/table.php create mode 100644 app/Template/subtask/timer.php create mode 100644 app/Template/subtask_converter/show.php create mode 100644 app/Template/subtask_restriction/show.php create mode 100644 app/Template/swimlane/create.php create mode 100644 app/Template/swimlane/edit.php create mode 100644 app/Template/swimlane/index.php create mode 100644 app/Template/swimlane/remove.php create mode 100644 app/Template/swimlane/table.php create mode 100644 app/Template/tag/create.php create mode 100644 app/Template/tag/edit.php create mode 100644 app/Template/tag/index.php create mode 100644 app/Template/tag/remove.php create mode 100644 app/Template/task/analytics.php create mode 100644 app/Template/task/changes.php create mode 100644 app/Template/task/description.php create mode 100644 app/Template/task/details.php create mode 100644 app/Template/task/dropdown.php create mode 100644 app/Template/task/layout.php create mode 100644 app/Template/task/public.php create mode 100644 app/Template/task/show.php create mode 100644 app/Template/task/sidebar.php create mode 100644 app/Template/task/time_tracking_details.php create mode 100644 app/Template/task/time_tracking_summary.php create mode 100644 app/Template/task/transitions.php create mode 100644 app/Template/task_bulk/show.php create mode 100644 app/Template/task_bulk_change_property/show.php create mode 100644 app/Template/task_bulk_move_column/show.php create mode 100644 app/Template/task_comments/create.php create mode 100644 app/Template/task_comments/show.php create mode 100644 app/Template/task_creation/duplicate_projects.php create mode 100644 app/Template/task_creation/show.php create mode 100644 app/Template/task_duplication/copy.php create mode 100644 app/Template/task_duplication/duplicate.php create mode 100644 app/Template/task_duplication/move.php create mode 100644 app/Template/task_external_link/create.php create mode 100644 app/Template/task_external_link/edit.php create mode 100644 app/Template/task_external_link/find.php create mode 100644 app/Template/task_external_link/form.php create mode 100644 app/Template/task_external_link/remove.php create mode 100644 app/Template/task_external_link/show.php create mode 100644 app/Template/task_external_link/table.php create mode 100644 app/Template/task_file/create.php create mode 100644 app/Template/task_file/files.php create mode 100644 app/Template/task_file/images.php create mode 100644 app/Template/task_file/remove.php create mode 100644 app/Template/task_file/screenshot.php create mode 100644 app/Template/task_file/show.php create mode 100644 app/Template/task_import/show.php create mode 100644 app/Template/task_internal_link/create.php create mode 100644 app/Template/task_internal_link/edit.php create mode 100644 app/Template/task_internal_link/remove.php create mode 100644 app/Template/task_internal_link/show.php create mode 100644 app/Template/task_internal_link/table.php create mode 100644 app/Template/task_list/header.php create mode 100644 app/Template/task_list/listing.php create mode 100644 app/Template/task_list/sort_menu.php create mode 100644 app/Template/task_list/task_avatars.php create mode 100644 app/Template/task_list/task_details.php create mode 100644 app/Template/task_list/task_icons.php create mode 100644 app/Template/task_list/task_subtasks.php create mode 100644 app/Template/task_list/task_title.php create mode 100644 app/Template/task_mail/create.php create mode 100644 app/Template/task_mail/email.php create mode 100644 app/Template/task_modification/show.php create mode 100644 app/Template/task_move_position/show.php create mode 100644 app/Template/task_recurrence/edit.php create mode 100644 app/Template/task_recurrence/info.php create mode 100644 app/Template/task_status/close.php create mode 100644 app/Template/task_status/open.php create mode 100644 app/Template/task_suppression/remove.php create mode 100644 app/Template/twofactor/check.php create mode 100644 app/Template/twofactor/disable.php create mode 100644 app/Template/twofactor/index.php create mode 100644 app/Template/twofactor/show.php create mode 100644 app/Template/user_api_access/show.php create mode 100644 app/Template/user_creation/show.php create mode 100644 app/Template/user_credential/authentication.php create mode 100644 app/Template/user_credential/password.php create mode 100644 app/Template/user_import/show.php create mode 100644 app/Template/user_invite/email.php create mode 100644 app/Template/user_invite/show.php create mode 100644 app/Template/user_invite/signup.php create mode 100644 app/Template/user_list/dropdown.php create mode 100644 app/Template/user_list/header.php create mode 100644 app/Template/user_list/listing.php create mode 100644 app/Template/user_list/sort_menu.php create mode 100644 app/Template/user_list/user_details.php create mode 100644 app/Template/user_list/user_icons.php create mode 100644 app/Template/user_list/user_title.php create mode 100644 app/Template/user_modification/show.php create mode 100644 app/Template/user_status/disable.php create mode 100644 app/Template/user_status/enable.php create mode 100644 app/Template/user_status/remove.php create mode 100644 app/Template/user_view/external.php create mode 100644 app/Template/user_view/integrations.php create mode 100644 app/Template/user_view/last.php create mode 100644 app/Template/user_view/layout.php create mode 100644 app/Template/user_view/notifications.php create mode 100644 app/Template/user_view/password_reset.php create mode 100644 app/Template/user_view/profile.php create mode 100644 app/Template/user_view/sessions.php create mode 100644 app/Template/user_view/share.php create mode 100644 app/Template/user_view/show.php create mode 100644 app/Template/user_view/sidebar.php create mode 100644 app/Template/user_view/timesheet.php create mode 100644 app/Template/web_notification/show.php create mode 100644 app/User/Avatar/AvatarFileProvider.php create mode 100644 app/User/Avatar/LetterAvatarProvider.php create mode 100644 app/User/DatabaseBackendUserProvider.php create mode 100644 app/User/DatabaseUserProvider.php create mode 100644 app/User/LdapUserProvider.php create mode 100644 app/User/OAuthUserProvider.php create mode 100644 app/User/ReverseProxyUserProvider.php create mode 100644 app/Validator/ActionValidator.php create mode 100644 app/Validator/AuthValidator.php create mode 100644 app/Validator/BaseValidator.php create mode 100644 app/Validator/CategoryValidator.php create mode 100644 app/Validator/ColumnMoveRestrictionValidator.php create mode 100644 app/Validator/ColumnRestrictionValidator.php create mode 100644 app/Validator/ColumnValidator.php create mode 100644 app/Validator/CommentValidator.php create mode 100644 app/Validator/ConfigValidator.php create mode 100644 app/Validator/CurrencyValidator.php create mode 100644 app/Validator/CustomFilterValidator.php create mode 100644 app/Validator/ExternalLinkValidator.php create mode 100644 app/Validator/GroupValidator.php create mode 100644 app/Validator/LinkValidator.php create mode 100644 app/Validator/PasswordResetValidator.php create mode 100644 app/Validator/PredefinedTaskDescriptionValidator.php create mode 100644 app/Validator/ProjectRoleValidator.php create mode 100644 app/Validator/ProjectValidator.php create mode 100644 app/Validator/SubtaskValidator.php create mode 100644 app/Validator/SwimlaneValidator.php create mode 100644 app/Validator/TagValidator.php create mode 100644 app/Validator/TaskLinkValidator.php create mode 100644 app/Validator/TaskValidator.php create mode 100644 app/Validator/UserValidator.php create mode 100644 app/check_setup.php create mode 100644 app/common.php create mode 100644 app/constants.php create mode 100644 app/functions.php create mode 100644 assets/css/auto.min.css create mode 100644 assets/css/custom_dashboard.css create mode 100644 assets/css/custom_login.css create mode 100644 assets/css/dark.min.css create mode 100644 assets/css/images/ui-bg_flat_0_aaaaaa_40x100.png create mode 100644 assets/css/images/ui-bg_flat_75_ffffff_40x100.png create mode 100644 assets/css/images/ui-bg_glass_55_fbf9ee_1x400.png create mode 100644 assets/css/images/ui-bg_glass_65_ffffff_1x400.png create mode 100644 assets/css/images/ui-bg_glass_75_dadada_1x400.png create mode 100644 assets/css/images/ui-bg_glass_75_e6e6e6_1x400.png create mode 100644 assets/css/images/ui-bg_glass_95_fef1ec_1x400.png create mode 100644 assets/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png create mode 100644 assets/css/images/ui-icons_222222_256x240.png create mode 100644 assets/css/images/ui-icons_2e83ff_256x240.png create mode 100644 assets/css/images/ui-icons_444444_256x240.png create mode 100644 assets/css/images/ui-icons_454545_256x240.png create mode 100644 assets/css/images/ui-icons_555555_256x240.png create mode 100644 assets/css/images/ui-icons_777620_256x240.png create mode 100644 assets/css/images/ui-icons_777777_256x240.png create mode 100644 assets/css/images/ui-icons_888888_256x240.png create mode 100644 assets/css/images/ui-icons_cc0000_256x240.png create mode 100644 assets/css/images/ui-icons_cd0a0a_256x240.png create mode 100644 assets/css/images/ui-icons_ffffff_256x240.png create mode 100644 assets/css/light.min.css create mode 100644 assets/css/print.min.css create mode 100644 assets/css/vendor.min.css create mode 100644 assets/fonts/FontAwesome.otf create mode 100644 assets/fonts/fontawesome-webfont.eot create mode 100644 assets/fonts/fontawesome-webfont.svg create mode 100644 assets/fonts/fontawesome-webfont.ttf create mode 100644 assets/fonts/fontawesome-webfont.woff create mode 100644 assets/fonts/fontawesome-webfont.woff2 create mode 100644 assets/img/adaptive-favicon.svg create mode 100644 assets/img/favicon.png create mode 100644 assets/img/inkscape-optimized-icon.svg create mode 100644 assets/img/inkscape-path-icon.svg create mode 100644 assets/img/inkscape-text-icon.svg create mode 100644 assets/img/touch-icon-ipad-retina.png create mode 100644 assets/img/touch-icon-ipad.png create mode 100644 assets/img/touch-icon-iphone-retina.png create mode 100644 assets/img/touch-icon-iphone.png create mode 100644 assets/js/app.min.js create mode 100644 assets/js/vendor.min.js create mode 100644 cli create mode 100644 config.default.php create mode 100644 data/.htaccess create mode 100644 data/web.config create mode 100644 docker-compose.mysql.yml create mode 100644 docker-compose.postgres.yml create mode 100644 docker-compose.sqlite.yml create mode 100644 favicon.ico create mode 100644 healthcheck.php create mode 100644 index.php create mode 100644 jsonrpc.php create mode 100644 libs/Captcha/CaptchaBuilder.php create mode 100644 libs/Captcha/CaptchaBuilderInterface.php create mode 100644 libs/Captcha/Font/captcha0.ttf create mode 100644 libs/Captcha/Font/captcha1.ttf create mode 100644 libs/Captcha/Font/captcha2.ttf create mode 100644 libs/Captcha/Font/captcha3.ttf create mode 100644 libs/Captcha/Font/captcha4.ttf create mode 100644 libs/Captcha/Font/captcha5.ttf create mode 100644 libs/Captcha/ImageFileHandler.php create mode 100644 libs/Captcha/LICENSE create mode 100644 libs/Captcha/PhraseBuilder.php create mode 100644 libs/Captcha/PhraseBuilderInterface.php create mode 100644 libs/SimpleQueue/Adapter/AmqpQueueAdapter.php create mode 100644 libs/SimpleQueue/Adapter/BeanstalkQueueAdapter.php create mode 100644 libs/SimpleQueue/Exception/NotSupportedException.php create mode 100644 libs/SimpleQueue/Job.php create mode 100644 libs/SimpleQueue/Queue.php create mode 100644 libs/SimpleQueue/QueueAdapterInterface.php create mode 100644 libs/SimpleValidator/Validator.php create mode 100644 libs/SimpleValidator/Validators/Alpha.php create mode 100644 libs/SimpleValidator/Validators/AlphaNumeric.php create mode 100644 libs/SimpleValidator/Validators/Base.php create mode 100644 libs/SimpleValidator/Validators/Date.php create mode 100644 libs/SimpleValidator/Validators/Email.php create mode 100644 libs/SimpleValidator/Validators/Equals.php create mode 100644 libs/SimpleValidator/Validators/Exists.php create mode 100644 libs/SimpleValidator/Validators/GreaterThan.php create mode 100644 libs/SimpleValidator/Validators/GreaterThanOrEqual.php create mode 100644 libs/SimpleValidator/Validators/InArray.php create mode 100644 libs/SimpleValidator/Validators/Integer.php create mode 100644 libs/SimpleValidator/Validators/Ip.php create mode 100644 libs/SimpleValidator/Validators/Length.php create mode 100644 libs/SimpleValidator/Validators/MaxLength.php create mode 100644 libs/SimpleValidator/Validators/MinLength.php create mode 100644 libs/SimpleValidator/Validators/NotEmpty.php create mode 100644 libs/SimpleValidator/Validators/NotEquals.php create mode 100644 libs/SimpleValidator/Validators/NotInArray.php create mode 100644 libs/SimpleValidator/Validators/Numeric.php create mode 100644 libs/SimpleValidator/Validators/Range.php create mode 100644 libs/SimpleValidator/Validators/Required.php create mode 100644 libs/SimpleValidator/Validators/Timezone.php create mode 100644 libs/SimpleValidator/Validators/URL.php create mode 100644 libs/SimpleValidator/Validators/Unique.php create mode 100644 libs/erusev/parsedown/LICENSE.txt create mode 100644 libs/erusev/parsedown/Parsedown.php create mode 100644 libs/event-dispatcher/Attribute/AsEventListener.php create mode 100644 libs/event-dispatcher/CHANGELOG.md create mode 100644 libs/event-dispatcher/Debug/TraceableEventDispatcher.php create mode 100644 libs/event-dispatcher/Debug/WrappedListener.php create mode 100644 libs/event-dispatcher/DependencyInjection/AddEventAliasesPass.php create mode 100644 libs/event-dispatcher/DependencyInjection/RegisterListenersPass.php create mode 100644 libs/event-dispatcher/EventDispatcher.php create mode 100644 libs/event-dispatcher/EventDispatcherInterface.php create mode 100644 libs/event-dispatcher/EventSubscriberInterface.php create mode 100644 libs/event-dispatcher/GenericEvent.php create mode 100644 libs/event-dispatcher/ImmutableEventDispatcher.php create mode 100644 libs/event-dispatcher/LICENSE create mode 100644 libs/event-dispatcher/LegacyEventDispatcherProxy.php create mode 100644 libs/ical/Component.php create mode 100644 libs/ical/Component/Alarm.php create mode 100644 libs/ical/Component/Calendar.php create mode 100644 libs/ical/Component/Event.php create mode 100644 libs/ical/Component/Timezone.php create mode 100644 libs/ical/Component/TimezoneRule.php create mode 100644 libs/ical/LICENSE create mode 100644 libs/ical/ParameterBag.php create mode 100644 libs/ical/Property.php create mode 100644 libs/ical/Property/ArrayValue.php create mode 100644 libs/ical/Property/DateTimeProperty.php create mode 100644 libs/ical/Property/DateTimesProperty.php create mode 100644 libs/ical/Property/Event/Attachment.php create mode 100644 libs/ical/Property/Event/Attendees.php create mode 100644 libs/ical/Property/Event/Geo.php create mode 100644 libs/ical/Property/Event/Organizer.php create mode 100644 libs/ical/Property/Event/RecurrenceId.php create mode 100644 libs/ical/Property/Event/RecurrenceRule.php create mode 100644 libs/ical/Property/RawStringValue.php create mode 100644 libs/ical/Property/StringValue.php create mode 100644 libs/ical/Property/ValueInterface.php create mode 100644 libs/ical/PropertyBag.php create mode 100644 libs/ical/Util/ComponentUtil.php create mode 100644 libs/ical/Util/DateUtil.php create mode 100644 libs/jsonrpc/LICENSE create mode 100644 libs/jsonrpc/README.markdown create mode 100644 libs/jsonrpc/src/JsonRPC/Client.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/AccessDeniedException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/AuthenticationFailureException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/ConnectionFailureException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/InvalidJsonFormatException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/InvalidJsonRpcFormatException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/ResponseEncodingFailureException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/ResponseException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/RpcCallFailedException.php create mode 100644 libs/jsonrpc/src/JsonRPC/Exception/ServerErrorException.php create mode 100644 libs/jsonrpc/src/JsonRPC/HttpClient.php create mode 100644 libs/jsonrpc/src/JsonRPC/MiddlewareHandler.php create mode 100644 libs/jsonrpc/src/JsonRPC/MiddlewareInterface.php create mode 100644 libs/jsonrpc/src/JsonRPC/ProcedureHandler.php create mode 100644 libs/jsonrpc/src/JsonRPC/Request/BatchRequestParser.php create mode 100644 libs/jsonrpc/src/JsonRPC/Request/RequestBuilder.php create mode 100644 libs/jsonrpc/src/JsonRPC/Request/RequestParser.php create mode 100644 libs/jsonrpc/src/JsonRPC/Response/ResponseBuilder.php create mode 100644 libs/jsonrpc/src/JsonRPC/Response/ResponseParser.php create mode 100644 libs/jsonrpc/src/JsonRPC/Server.php create mode 100644 libs/jsonrpc/src/JsonRPC/Validator/HostValidator.php create mode 100644 libs/jsonrpc/src/JsonRPC/Validator/JsonEncodingValidator.php create mode 100644 libs/jsonrpc/src/JsonRPC/Validator/JsonFormatValidator.php create mode 100644 libs/jsonrpc/src/JsonRPC/Validator/RpcFormatValidator.php create mode 100644 libs/jsonrpc/src/JsonRPC/Validator/UserValidator.php create mode 100644 libs/minify/LICENSE create mode 100644 libs/minify/data/js/keywords_after.txt create mode 100644 libs/minify/data/js/keywords_before.txt create mode 100644 libs/minify/data/js/keywords_reserved.txt create mode 100644 libs/minify/data/js/operators.txt create mode 100644 libs/minify/data/js/operators_after.txt create mode 100644 libs/minify/data/js/operators_before.txt create mode 100644 libs/minify/src/CSS.php create mode 100644 libs/minify/src/Exception.php create mode 100644 libs/minify/src/Exceptions/BasicException.php create mode 100644 libs/minify/src/Exceptions/FileImportException.php create mode 100644 libs/minify/src/Exceptions/IOException.php create mode 100644 libs/minify/src/JS.php create mode 100644 libs/minify/src/Minify.php create mode 100644 libs/path-converter/LICENSE create mode 100644 libs/path-converter/src/Converter.php create mode 100644 libs/path-converter/src/ConverterInterface.php create mode 100644 libs/path-converter/src/NoConverter.php create mode 100644 libs/phpqrcode/LICENSE create mode 100644 libs/phpqrcode/cache/frame_1.dat create mode 100644 libs/phpqrcode/cache/frame_1.png create mode 100644 libs/phpqrcode/cache/frame_10.dat create mode 100644 libs/phpqrcode/cache/frame_10.png create mode 100644 libs/phpqrcode/cache/frame_11.dat create mode 100644 libs/phpqrcode/cache/frame_11.png create mode 100644 libs/phpqrcode/cache/frame_12.dat create mode 100644 libs/phpqrcode/cache/frame_12.png create mode 100644 libs/phpqrcode/cache/frame_13.dat create mode 100644 libs/phpqrcode/cache/frame_13.png create mode 100644 libs/phpqrcode/cache/frame_14.dat create mode 100644 libs/phpqrcode/cache/frame_14.png create mode 100644 libs/phpqrcode/cache/frame_15.dat create mode 100644 libs/phpqrcode/cache/frame_15.png create mode 100644 libs/phpqrcode/cache/frame_16.dat create mode 100644 libs/phpqrcode/cache/frame_16.png create mode 100644 libs/phpqrcode/cache/frame_17.dat create mode 100644 libs/phpqrcode/cache/frame_17.png create mode 100644 libs/phpqrcode/cache/frame_18.dat create mode 100644 libs/phpqrcode/cache/frame_18.png create mode 100644 libs/phpqrcode/cache/frame_19.dat create mode 100644 libs/phpqrcode/cache/frame_19.png create mode 100644 libs/phpqrcode/cache/frame_2.dat create mode 100644 libs/phpqrcode/cache/frame_2.png create mode 100644 libs/phpqrcode/cache/frame_20.dat create mode 100644 libs/phpqrcode/cache/frame_20.png create mode 100644 libs/phpqrcode/cache/frame_21.dat create mode 100644 libs/phpqrcode/cache/frame_21.png create mode 100644 libs/phpqrcode/cache/frame_22.dat create mode 100644 libs/phpqrcode/cache/frame_22.png create mode 100644 libs/phpqrcode/cache/frame_23.dat create mode 100644 libs/phpqrcode/cache/frame_23.png create mode 100644 libs/phpqrcode/cache/frame_24.dat create mode 100644 libs/phpqrcode/cache/frame_24.png create mode 100644 libs/phpqrcode/cache/frame_25.dat create mode 100644 libs/phpqrcode/cache/frame_25.png create mode 100644 libs/phpqrcode/cache/frame_26.dat create mode 100644 libs/phpqrcode/cache/frame_26.png create mode 100644 libs/phpqrcode/cache/frame_27.dat create mode 100644 libs/phpqrcode/cache/frame_27.png create mode 100644 libs/phpqrcode/cache/frame_28.dat create mode 100644 libs/phpqrcode/cache/frame_28.png create mode 100644 libs/phpqrcode/cache/frame_29.dat create mode 100644 libs/phpqrcode/cache/frame_29.png create mode 100644 libs/phpqrcode/cache/frame_3.dat create mode 100644 libs/phpqrcode/cache/frame_3.png create mode 100644 libs/phpqrcode/cache/frame_30.dat create mode 100644 libs/phpqrcode/cache/frame_30.png create mode 100644 libs/phpqrcode/cache/frame_31.dat create mode 100644 libs/phpqrcode/cache/frame_31.png create mode 100644 libs/phpqrcode/cache/frame_32.dat create mode 100644 libs/phpqrcode/cache/frame_32.png create mode 100644 libs/phpqrcode/cache/frame_33.dat create mode 100644 libs/phpqrcode/cache/frame_33.png create mode 100644 libs/phpqrcode/cache/frame_34.dat create mode 100644 libs/phpqrcode/cache/frame_34.png create mode 100644 libs/phpqrcode/cache/frame_35.dat create mode 100644 libs/phpqrcode/cache/frame_35.png create mode 100644 libs/phpqrcode/cache/frame_36.dat create mode 100644 libs/phpqrcode/cache/frame_36.png create mode 100644 libs/phpqrcode/cache/frame_37.dat create mode 100644 libs/phpqrcode/cache/frame_37.png create mode 100644 libs/phpqrcode/cache/frame_38.dat create mode 100644 libs/phpqrcode/cache/frame_38.png create mode 100644 libs/phpqrcode/cache/frame_39.dat create mode 100644 libs/phpqrcode/cache/frame_39.png create mode 100644 libs/phpqrcode/cache/frame_4.dat create mode 100644 libs/phpqrcode/cache/frame_4.png create mode 100644 libs/phpqrcode/cache/frame_40.dat create mode 100644 libs/phpqrcode/cache/frame_40.png create mode 100644 libs/phpqrcode/cache/frame_5.dat create mode 100644 libs/phpqrcode/cache/frame_5.png create mode 100644 libs/phpqrcode/cache/frame_6.dat create mode 100644 libs/phpqrcode/cache/frame_6.png create mode 100644 libs/phpqrcode/cache/frame_7.dat create mode 100644 libs/phpqrcode/cache/frame_7.png create mode 100644 libs/phpqrcode/cache/frame_8.dat create mode 100644 libs/phpqrcode/cache/frame_8.png create mode 100644 libs/phpqrcode/cache/frame_9.dat create mode 100644 libs/phpqrcode/cache/frame_9.png create mode 100644 libs/phpqrcode/cache/mask_0/mask_101_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_105_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_109_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_113_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_117_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_121_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_125_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_129_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_133_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_137_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_141_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_145_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_149_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_153_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_157_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_161_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_165_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_169_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_173_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_177_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_21_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_25_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_29_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_33_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_37_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_41_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_45_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_49_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_53_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_57_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_61_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_65_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_69_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_73_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_77_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_81_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_85_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_89_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_93_0.dat create mode 100644 libs/phpqrcode/cache/mask_0/mask_97_0.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_101_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_105_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_109_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_113_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_117_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_121_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_125_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_129_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_133_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_137_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_141_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_145_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_149_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_153_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_157_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_161_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_165_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_169_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_173_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_177_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_21_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_25_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_29_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_33_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_37_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_41_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_45_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_49_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_53_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_57_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_61_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_65_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_69_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_73_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_77_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_81_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_85_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_89_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_93_1.dat create mode 100644 libs/phpqrcode/cache/mask_1/mask_97_1.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_101_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_105_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_109_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_113_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_117_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_121_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_125_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_129_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_133_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_137_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_141_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_145_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_149_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_153_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_157_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_161_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_165_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_169_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_173_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_177_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_21_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_25_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_29_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_33_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_37_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_41_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_45_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_49_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_53_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_57_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_61_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_65_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_69_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_73_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_77_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_81_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_85_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_89_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_93_2.dat create mode 100644 libs/phpqrcode/cache/mask_2/mask_97_2.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_101_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_105_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_109_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_113_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_117_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_121_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_125_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_129_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_133_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_137_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_141_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_145_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_149_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_153_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_157_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_161_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_165_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_169_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_173_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_177_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_21_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_25_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_29_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_33_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_37_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_41_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_45_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_49_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_53_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_57_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_61_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_65_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_69_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_73_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_77_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_81_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_85_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_89_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_93_3.dat create mode 100644 libs/phpqrcode/cache/mask_3/mask_97_3.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_101_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_105_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_109_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_113_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_117_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_121_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_125_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_129_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_133_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_137_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_141_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_145_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_149_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_153_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_157_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_161_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_165_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_169_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_173_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_177_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_21_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_25_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_29_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_33_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_37_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_41_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_45_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_49_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_53_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_57_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_61_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_65_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_69_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_73_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_77_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_81_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_85_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_89_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_93_4.dat create mode 100644 libs/phpqrcode/cache/mask_4/mask_97_4.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_101_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_105_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_109_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_113_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_117_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_121_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_125_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_129_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_133_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_137_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_141_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_145_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_149_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_153_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_157_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_161_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_165_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_169_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_173_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_177_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_21_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_25_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_29_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_33_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_37_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_41_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_45_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_49_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_53_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_57_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_61_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_65_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_69_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_73_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_77_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_81_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_85_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_89_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_93_5.dat create mode 100644 libs/phpqrcode/cache/mask_5/mask_97_5.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_101_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_105_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_109_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_113_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_117_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_121_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_125_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_129_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_133_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_137_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_141_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_145_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_149_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_153_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_157_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_161_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_165_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_169_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_173_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_177_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_21_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_25_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_29_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_33_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_37_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_41_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_45_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_49_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_53_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_57_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_61_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_65_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_69_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_73_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_77_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_81_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_85_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_89_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_93_6.dat create mode 100644 libs/phpqrcode/cache/mask_6/mask_97_6.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_101_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_105_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_109_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_113_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_117_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_121_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_125_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_129_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_133_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_137_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_141_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_145_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_149_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_153_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_157_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_161_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_165_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_169_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_173_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_177_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_21_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_25_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_29_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_33_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_37_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_41_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_45_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_49_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_53_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_57_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_61_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_65_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_69_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_73_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_77_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_81_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_85_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_89_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_93_7.dat create mode 100644 libs/phpqrcode/cache/mask_7/mask_97_7.dat create mode 100644 libs/phpqrcode/lib/PHPQRCode.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/Autoloader.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/Constants.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/FrameFiller.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRbitstream.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRcode.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRencode.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRimage.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRinput.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRinputItem.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRmask.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRrawcode.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRrs.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRrsItem.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRrsblock.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRspec.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRsplit.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRstr.php create mode 100644 libs/phpqrcode/lib/PHPQRCode/QRtools.php create mode 100644 libs/picodb/LICENSE create mode 100644 libs/picodb/lib/PicoDb/Builder/BaseBuilder.php create mode 100644 libs/picodb/lib/PicoDb/Builder/ConditionBuilder.php create mode 100644 libs/picodb/lib/PicoDb/Builder/InsertBuilder.php create mode 100644 libs/picodb/lib/PicoDb/Builder/OrConditionBuilder.php create mode 100644 libs/picodb/lib/PicoDb/Builder/UpdateBuilder.php create mode 100644 libs/picodb/lib/PicoDb/Database.php create mode 100644 libs/picodb/lib/PicoDb/Driver/Base.php create mode 100644 libs/picodb/lib/PicoDb/Driver/Mssql.php create mode 100644 libs/picodb/lib/PicoDb/Driver/Mysql.php create mode 100644 libs/picodb/lib/PicoDb/Driver/Postgres.php create mode 100644 libs/picodb/lib/PicoDb/Driver/Sqlite.php create mode 100644 libs/picodb/lib/PicoDb/DriverFactory.php create mode 100644 libs/picodb/lib/PicoDb/Hashtable.php create mode 100644 libs/picodb/lib/PicoDb/LargeObject.php create mode 100644 libs/picodb/lib/PicoDb/SQLException.php create mode 100644 libs/picodb/lib/PicoDb/Schema.php create mode 100644 libs/picodb/lib/PicoDb/StatementHandler.php create mode 100644 libs/picodb/lib/PicoDb/Table.php create mode 100644 libs/picodb/lib/PicoDb/UrlParser.php create mode 100644 libs/picodb/phpunit.xml create mode 100644 libs/swiftmailer/LICENSE create mode 100644 libs/swiftmailer/classes/Swift.php create mode 100644 libs/swiftmailer/classes/Swift/Attachment.php create mode 100644 libs/swiftmailer/classes/Swift/ByteStream/AbstractFilterableInputStream.php create mode 100644 libs/swiftmailer/classes/Swift/ByteStream/ArrayByteStream.php create mode 100644 libs/swiftmailer/classes/Swift/ByteStream/FileByteStream.php create mode 100644 libs/swiftmailer/classes/Swift/ByteStream/TemporaryFileByteStream.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterReader.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterReader/GenericFixedWidthReader.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterReader/UsAsciiReader.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterReader/Utf8Reader.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterReaderFactory.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterReaderFactory/SimpleCharacterReaderFactory.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterStream.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterStream/ArrayCharacterStream.php create mode 100644 libs/swiftmailer/classes/Swift/CharacterStream/NgCharacterStream.php create mode 100644 libs/swiftmailer/classes/Swift/ConfigurableSpool.php create mode 100644 libs/swiftmailer/classes/Swift/DependencyContainer.php create mode 100644 libs/swiftmailer/classes/Swift/DependencyException.php create mode 100644 libs/swiftmailer/classes/Swift/EmbeddedFile.php create mode 100644 libs/swiftmailer/classes/Swift/Encoder.php create mode 100644 libs/swiftmailer/classes/Swift/Encoder/Base64Encoder.php create mode 100644 libs/swiftmailer/classes/Swift/Encoder/QpEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Encoder/Rfc2231Encoder.php create mode 100644 libs/swiftmailer/classes/Swift/Encoding.php create mode 100644 libs/swiftmailer/classes/Swift/Events/CommandEvent.php create mode 100644 libs/swiftmailer/classes/Swift/Events/CommandListener.php create mode 100644 libs/swiftmailer/classes/Swift/Events/Event.php create mode 100644 libs/swiftmailer/classes/Swift/Events/EventDispatcher.php create mode 100644 libs/swiftmailer/classes/Swift/Events/EventListener.php create mode 100644 libs/swiftmailer/classes/Swift/Events/EventObject.php create mode 100644 libs/swiftmailer/classes/Swift/Events/ResponseEvent.php create mode 100644 libs/swiftmailer/classes/Swift/Events/ResponseListener.php create mode 100644 libs/swiftmailer/classes/Swift/Events/SendEvent.php create mode 100644 libs/swiftmailer/classes/Swift/Events/SendListener.php create mode 100644 libs/swiftmailer/classes/Swift/Events/SimpleEventDispatcher.php create mode 100644 libs/swiftmailer/classes/Swift/Events/TransportChangeEvent.php create mode 100644 libs/swiftmailer/classes/Swift/Events/TransportChangeListener.php create mode 100644 libs/swiftmailer/classes/Swift/Events/TransportExceptionEvent.php create mode 100644 libs/swiftmailer/classes/Swift/Events/TransportExceptionListener.php create mode 100644 libs/swiftmailer/classes/Swift/FailoverTransport.php create mode 100644 libs/swiftmailer/classes/Swift/FileSpool.php create mode 100644 libs/swiftmailer/classes/Swift/FileStream.php create mode 100644 libs/swiftmailer/classes/Swift/Filterable.php create mode 100644 libs/swiftmailer/classes/Swift/Image.php create mode 100644 libs/swiftmailer/classes/Swift/InputByteStream.php create mode 100644 libs/swiftmailer/classes/Swift/IoException.php create mode 100644 libs/swiftmailer/classes/Swift/KeyCache.php create mode 100644 libs/swiftmailer/classes/Swift/KeyCache/ArrayKeyCache.php create mode 100644 libs/swiftmailer/classes/Swift/KeyCache/DiskKeyCache.php create mode 100644 libs/swiftmailer/classes/Swift/KeyCache/KeyCacheInputStream.php create mode 100644 libs/swiftmailer/classes/Swift/KeyCache/NullKeyCache.php create mode 100644 libs/swiftmailer/classes/Swift/KeyCache/SimpleKeyCacheInputStream.php create mode 100644 libs/swiftmailer/classes/Swift/LoadBalancedTransport.php create mode 100644 libs/swiftmailer/classes/Swift/MailTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Mailer.php create mode 100644 libs/swiftmailer/classes/Swift/Mailer/ArrayRecipientIterator.php create mode 100644 libs/swiftmailer/classes/Swift/Mailer/RecipientIterator.php create mode 100644 libs/swiftmailer/classes/Swift/MemorySpool.php create mode 100644 libs/swiftmailer/classes/Swift/Message.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Attachment.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/CharsetObserver.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ContentEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ContentEncoder/Base64ContentEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ContentEncoder/NativeQpContentEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ContentEncoder/PlainContentEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ContentEncoder/QpContentEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ContentEncoder/QpContentEncoderProxy.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ContentEncoder/RawContentEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/EmbeddedFile.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/EncodingObserver.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Grammar.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Header.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/HeaderEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/HeaderEncoder/Base64HeaderEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/HeaderEncoder/QpHeaderEncoder.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/HeaderFactory.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/HeaderSet.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/AbstractHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/DateHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/IdentificationHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/MailboxHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/OpenDKIMHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/ParameterizedHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/PathHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Headers/UnstructuredHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/Message.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/MimeEntity.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/MimePart.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/ParameterizedHeader.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/SimpleHeaderFactory.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/SimpleHeaderSet.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/SimpleMessage.php create mode 100644 libs/swiftmailer/classes/Swift/Mime/SimpleMimeEntity.php create mode 100644 libs/swiftmailer/classes/Swift/MimePart.php create mode 100644 libs/swiftmailer/classes/Swift/NullTransport.php create mode 100644 libs/swiftmailer/classes/Swift/OutputByteStream.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/AntiFloodPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/BandwidthMonitorPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Decorator/Replacements.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/DecoratorPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/ImpersonatePlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Logger.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/LoggerPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Loggers/ArrayLogger.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Loggers/EchoLogger.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/MessageLogger.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Pop/Pop3Connection.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Pop/Pop3Exception.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/PopBeforeSmtpPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/RedirectingPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Reporter.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/ReporterPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Reporters/HitReporter.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Reporters/HtmlReporter.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Sleeper.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/ThrottlerPlugin.php create mode 100644 libs/swiftmailer/classes/Swift/Plugins/Timer.php create mode 100644 libs/swiftmailer/classes/Swift/Preferences.php create mode 100644 libs/swiftmailer/classes/Swift/ReplacementFilterFactory.php create mode 100644 libs/swiftmailer/classes/Swift/RfcComplianceException.php create mode 100644 libs/swiftmailer/classes/Swift/SendmailTransport.php create mode 100644 libs/swiftmailer/classes/Swift/SignedMessage.php create mode 100644 libs/swiftmailer/classes/Swift/Signer.php create mode 100644 libs/swiftmailer/classes/Swift/Signers/BodySigner.php create mode 100644 libs/swiftmailer/classes/Swift/Signers/DKIMSigner.php create mode 100644 libs/swiftmailer/classes/Swift/Signers/DomainKeySigner.php create mode 100644 libs/swiftmailer/classes/Swift/Signers/HeaderSigner.php create mode 100644 libs/swiftmailer/classes/Swift/Signers/OpenDKIMSigner.php create mode 100644 libs/swiftmailer/classes/Swift/Signers/SMimeSigner.php create mode 100644 libs/swiftmailer/classes/Swift/SmtpTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Spool.php create mode 100644 libs/swiftmailer/classes/Swift/SpoolTransport.php create mode 100644 libs/swiftmailer/classes/Swift/StreamFilter.php create mode 100644 libs/swiftmailer/classes/Swift/StreamFilters/ByteArrayReplacementFilter.php create mode 100644 libs/swiftmailer/classes/Swift/StreamFilters/StringReplacementFilter.php create mode 100644 libs/swiftmailer/classes/Swift/StreamFilters/StringReplacementFilterFactory.php create mode 100644 libs/swiftmailer/classes/Swift/SwiftException.php create mode 100644 libs/swiftmailer/classes/Swift/Transport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/AbstractSmtpTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/Esmtp/Auth/CramMd5Authenticator.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/Esmtp/Auth/LoginAuthenticator.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/Esmtp/Auth/NTLMAuthenticator.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/Esmtp/Auth/PlainAuthenticator.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/Esmtp/Auth/XOAuth2Authenticator.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/Esmtp/AuthHandler.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/Esmtp/Authenticator.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/EsmtpHandler.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/EsmtpTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/FailoverTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/IoBuffer.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/LoadBalancedTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/MailInvoker.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/MailTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/NullTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/SendmailTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/SimpleMailInvoker.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/SmtpAgent.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/SpoolTransport.php create mode 100644 libs/swiftmailer/classes/Swift/Transport/StreamBuffer.php create mode 100644 libs/swiftmailer/classes/Swift/TransportException.php create mode 100644 libs/swiftmailer/classes/Swift/Validate.php create mode 100644 libs/swiftmailer/dependency_maps/cache_deps.php create mode 100644 libs/swiftmailer/dependency_maps/message_deps.php create mode 100644 libs/swiftmailer/dependency_maps/mime_deps.php create mode 100644 libs/swiftmailer/dependency_maps/transport_deps.php create mode 100644 libs/swiftmailer/mime_types.php create mode 100644 libs/swiftmailer/preferences.php create mode 100644 libs/swiftmailer/swift_init.php create mode 100644 libs/swiftmailer/swift_required.php create mode 100644 libs/swiftmailer/swift_required_pear.php create mode 100644 libs/swiftmailer/swiftmailer_generate_mimes_config.php create mode 100644 robots.txt create mode 100644 web.config diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..f28b0ca --- /dev/null +++ b/.htaccess @@ -0,0 +1,34 @@ +# Pass HTTP Authorization header via environment variable to PHP backend +# to make HTTP Basic Authentication work for Apache/FastCGI/php-fpm +# setups (required to authenticate over the API) + + SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0 + + + + Options -MultiViews + + + + SetEnv HTTP_MOD_REWRITE On + + + + # Uncomment this line depending of your Apache configuration + # RewriteBase / + + RewriteEngine On + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [QSA,L] + + ############################ + ## Uncomment the two lines below to enable force HTTPS capabilities + ############################ + + # RewriteCond %{HTTPS} !=on + # RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R,L] + + + + ModPagespeed Off + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1bc62ca --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,140 @@ +# Contributing to Kanboard + +Kanboard is a free and open source Kanban project management software that welcomes contributions from the community. + +## Project Status + +**Important Note**: Kanboard is currently in maintenance mode. This means: + +- The original author is not actively developing major new features +- New releases are published regularly based on community contributions +- Pull requests for bug fixes and small improvements are welcomed +- The project follows established guidelines for all contributions + +## Ways to Contribute + +### 🛠Bug Reports + +If you find a bug, please help us improve Kanboard by reporting it: + +1. **Check existing issues** first to avoid duplicates +2. **Use the GitHub issue tracker** to report bugs +3. **Provide detailed information** including: + - Kanboard version + - PHP version + - Web server (Apache, Nginx, etc.) + - Database type and version + - Operating system + - Steps to reproduce the issue + - Expected vs actual behavior + - Screenshots, server and browser logs if applicable + +### 🔧 Bug Fixes and Small Improvements + +We welcome pull requests that fix bugs or make small improvements: + +1. **Fork the repository** and create a new branch +2. **Keep changes focused** - one issue per pull request +3. **Test your changes** thoroughly +4. **Follow the existing code style** +5. **Submit a pull request** with a clear description + +### 📚 Documentation + +Help improve Kanboard's documentation: + +- Fix typos or unclear explanations +- Add missing documentation for features +- Translate documentation to other languages +- Improve code comments + +### 🌠Translations + +Kanboard supports multiple languages. Help translate the interface: + +1. Check the `app/Locale` directory for existing translations +2. Create or update translation files +3. Follow the existing translation format +4. Test your translations in the application + +Refer to the [Translation Guide](https://docs.kanboard.org/v1/dev/translations/) for more details. + +## Development Setup + +### Prerequisites + +- PHP 8.1 or higher +- Web server (Apache, Nginx, or PHP built-in server) +- Database (MySQL, PostgreSQL, or SQLite) +- Composer (for dependency management) + +### Local Development + +1. **Clone the repository**: + ```bash + git clone https://github.com/kanboard/kanboard.git + cd kanboard + ``` + +2. **Install dependencies**: + ```bash + composer install + ``` + +3. **Set up your environment**: + - Copy `config.default.php` to `config.php` + - Configure your database settings + - Set up your web server to point to the project directory, or use the PHP built-in server: + ```bash + php -S localhost:8000 -t . + ``` + +4. **Run unit tests** to ensure everything is working: + ```bash + make test-sqlite # or make test-mysql, make test-postgresql + ``` + +### Testing + +- Test your changes in different browsers +- Test with multiple database types (MySQL, PostgreSQL, SQLite) +- Test with different PHP versions if possible +- Ensure existing functionalities are not broken by your changes +- Run the unit tests and integration tests + +## Pull Request Guidelines + +Before submitting a pull request, please: + +1. **Read the pull request template** (`.github/pull_request_template.md`) +2. **Create a focused branch** from `main` for your changes +3. **Write clear commit messages** using the [conventional commit format](https://www.conventionalcommits.org/) +4. **Keep your changes small and focused** - large PRs are harder to review +5. **Test your changes thoroughly** to ensure they work as expected +6. **Ensure your code passes all tests** and does not introduce new issues +7. **Add or update tests** if when appropriate +8. **Review your code for style and quality** before submitting +9. **Update documentation** if needed + +## Code Style Guidelines + +- Be consistent with existing code style +- Follow PSR-1 and PSR-2 coding standards +- Configure your code editor to use 4 spaces for indentation +- Use meaningful variable and function names +- Add comments for complex logic +- Keep functions and methods focused and small +- Use type hints where appropriate + +Refer to [Kanboard's coding standards](https://docs.kanboard.org/v1/dev/coding_standards/) for more details. + +## Resources + +- **Official Website**: +- **Documentation**: +- **Forums**: or +- **GitHub Issues**: + +## License + +By contributing to Kanboard, you agree that your contributions will be licensed under the same [MIT License](LICENSE) that covers the project. diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..4fb0861 --- /dev/null +++ b/ChangeLog @@ -0,0 +1,2140 @@ +Version 1.2.52 (April 4, 2026) +------------------------------ + +* fix: revoke public tokens for inactive users +* fix: use timing-safe comparison for token validation +* fix: validate task ownership before applying property changes +* fix: enforce visibility controls for public and unprivileged access +* fix: use parameterized queries in task finder and iCal + +Version 1.2.51 (March 7, 2026) +------------------------------ + +* test: upgrade to phpunit 12 +* fix(css): Fix accessibility issue of insufficient text to background contrast +* fix: validate user external ID column +* fix: restrict invite signup input to expected fields only +* fix: prevent unsafe deserialization in database session handler (CWE-502) +* fix: check file attachment ownership before deletion +* fix: add permission checks in API procedures +* fix: add followRedirects parameter to HTTP client methods to prevent SSRF bypasses +* feat: add SSRF protection for webhook notifications +* build(deps): bump pimple/pimple from 3.6.1 to 3.6.2 +* build(deps): bump docker/setup-qemu-action from 3 to 4 +* build(deps): bump docker/setup-buildx-action from 3 to 4 +* build(deps): bump docker/metadata-action from 5 to 6 +* build(deps): bump docker/login-action from 3 to 4 +* build(deps): bump docker/build-push-action from 6 to 7 +* build(deps): bump actions/upload-artifact from 6 to 7 + +Version 1.2.50 (February 7, 2026) +--------------------------------- + +* test: bump phpunit version +* fix: add missing authorization checks +* fix: enforce plugin installer check in PluginController actions +* fix: enable Parsedown safe mode for Markdown rendering +* fix: add missing project authorization checks in controllers +* fix: add CSRF protection for project role changes and enforce JSON content type +* chore: use php-cs-fixer docker image in GitHub workflow instead of Composer +* build(deps): bump pimple/pimple from 3.5.0 to 3.6.1 + +Version 1.2.49 (January 6, 2026) +-------------------------------- + +* fix(ldap): ensure LDAP placeholders are escaped +* fix: restore Ctrl+Enter submission on the task creation form +* feat(locale): update translations +* feat: prevent protocol-relative URL redirects after login +* feat: block private network access for external web link (configurable) +* feat: add `TRUSTED_PROXY_NETWORKS` config option +* ci: add workflow to mirror GitHub repo to Codeberg +* chore: remove outdated `tests/Dockerfile` +* chore: regenerate autoload Composer files +* build(deps): bump alpine from 3.22 to 3.23 +* build(deps): bump actions/upload-artifact from 4 to 6 +* build(deps): bump actions/checkout from 5 to 6 + +Version 1.2.48 (October 18, 2025) +-------------------------------- + +* fix: handle Windows-style paths in `sanitize_path` function +* feat(locale): added missing German translation phrases +* feat(locale): added Arabic translation +* feat(api): add board, rss and ical public links to the API response +* feat: display sub-tasks completion in numbers (x/y) alongside percentage +* feat: add basic support for right-to-left (RTL) languages +* chore: update .gitattributes to ignore additional configuration files +* build(deps): bump actions/setup-python from 5 to 6 +* build(deps): bump actions/checkout from 4 to 5 + +Version 1.2.47 (August 11, 2025) +-------------------------------- + +* refactor: add namespace to test files +* fix: the `$escape` parameter must be provided in PHP 8.4 for CSV functions +* fix: sanitize and validate uploaded files path +* fix: do not load `RememberMeAuth` provider when `REMEMBER_ME_AUTH` is `false` +* fix: avoid PHP warning when external user creation is disabled +* feat!: remove file cache driver to avoid using `unserialize()` +* feat!: ignore legacy events serialized with PHP due to potential security issues +* feat: add new actions: `TaskAssignCurrentUserColumnIfNoUserAlreadySet` and `TaskAssignToUserOnCreationInColumn` +* feat: Add new `pdf()` method in `Core\Http\Response` +* ci: run `php-cs-fixer` on GitHub Actions +* ci: remove unnecessary labels from issue templates +* chore: replace deprecated `gh-cli` feature source in devcontainer configuration + +Version 1.2.46 (June 22, 2025) +------------------------------ + +* refactor: update return type in filter apply methods +* fix(security): prevent potential `Host` header injection via `SERVER_NAME` + - You must specify the Kanboard application URL explicitly to generate correct URLs from email notifications. The default is `http://localhost/`. +* fix: make various PHP 8.x compatibility changes +* fix: avoid `Implicitly nullable parameter declarations` errors in PHP 8.4 +* feat: validate plugin archive URL before downloading +* feat: use PHP 8.4 in the official Docker image +* feat: show CAPTCHA on login form regardless of user existence +* feat: add new option to enable notifications by default for new users +* feat: add healthcheck endpoint `healthcheck.php`, and new Docker Compose files for MariaDB, Postgres, and SQLite +* feat: add `TRUSTED_PROXY_HEADERS` config option + - If you use a reverse proxy, you can now specify which headers to trust for the client IP address. Nothing is trusted by default. +* docs: add `CONTRIBUTING.md` file +* ci(docker): avoid using `set-output` deprecated command +* chore!: PHP 8.1 is now the minimum version supported + - **!! PHP 7.4 is no longer supported !!** +* chore: update `docker-compose.yml` sample file to the latest specs +* chore: remove obsolete `Vagrantfile` +* build(deps): bump Alpine Docker image from 3.21 to 3.22 + +Version 1.2.45 (May 12, 2025) +----------------------------- + +* refactor: reuse existing helpers in tasks import form +* fix(filter): handle `null` input in the `Lexer` class +* fix(docker): legacy key/value format with whitespace separator should not be used +* fix(api): allow and validate creator ID assignment in task creation +* feat(routes): add `view` routes for project and task file browsing +* feat(locale): update all language files using machine translation +* feat(api): add priority fields to `createProject` and `updateProject` procedures +* feat: allow attaching screenshots and files when creating a task +* feat: add task title to overdue notification title +* ci: replace GitHub Issue Markdown templates with YAML forms +* ci: remove broken SQL Server unit tests pipeline +* ci: improve pull request template +* ci: add commit linter to validate conventional commit messages in pull requests + +Version 1.2.44 (March 21, 2025) +------------------------------- + +* fix: prevent internal task titles from wrapping under the dropdown menu icon +* feat(locale): update Greek and French translations +* feat: display tag color squares next to their names in project and global settings +* feat: enable bulk addition/removal of internal links +* feat: provide an option to add tags without replacing existing ones during bulk operations + +Version 1.2.43 (December 18, 2024) +---------------------------------- + +* fix: verify the session hasn't expired before returning data +* fix: avoid PHP 8.4 deprecation notices in third-party libraries +* fix: avoid Composer warnings regarding PSR compatibility +* feat(locale): add missing Brazilian Portuguese translations +* ci: run GitHub Actions tests with `ubuntu-24.04` +* chore: don't `export-ignore` the ChangeLog +* build(deps): bump `symfony/service-contracts` from `2.5.3` to `2.5.4` +* build(deps): bump `symfony/event-dispatcher-contracts` from `2.5.3` to `2.5.4` +* build(deps): bump `symfony/deprecation-contracts` from `2.5.3` to `2.5.4` +* build(deps): bump `alpine` from `3.20` to `3.21` + +Version 1.2.42 (November 10, 2024) +---------------------------------- + +* fix: validate translation filename before loading locales +* fix: avoid path traversal in `FileStorage` +* feat: add Peruvian Sol to the list of currencies +* build(deps): bump `symfony/finder` from `5.4.43` to `5.4.45` +* build(deps-dev): bump `symfony/stopwatch` from `5.4.40` to `5.4.45` + +Version 1.2.41 (October 25, 2024) +--------------------------------- + +* feat: add new plugin hooks in project forms +* feat: add option to add BOM at the beginning of CSV files (required for Microsoft Excel) +* feat: validate app config form values +* feat: add cancel button on 2FA code validation screen +* fix: add CSRF check to the logout endpoint +* fix: add HTML escaping when displaying exception message +* fix: add URL validation for external task links +* fix: correct broken migration logic for Sqlite + +Version 1.2.40 (September 25, 2024) +----------------------------------- + +* build(deps): bump symfony/finder from 5.4.42 to 5.4.43 +* chore: add php83-xmlwriter package to the Docker image +* ci: update GitHub pull-request template +* fix: avoid PHP error if no subtask in progress is found +* fix: avoid potential XSS and HTML injection in comment replies +* fix: prevent duplicated columns when enabling per-swimlane column task limits +* fix(api): check comment visibility in API procedures +* fix(api): verify comment ownership in API procedures +* fix(mssql): escape identifiers in timesheet queries +* fix(mssql): use ANSI OFFSET/FETCH syntax for pagination queries +* fix(test): use explicit ORDER BY for queries returning multiple rows +* test: add unit tests for Subtask Time Tracking query methods +* test: ensure pagination produces correct chunks + +Version 1.2.39 (August 18, 2024) +-------------------------------- + +* fix: remove CSS which caused responsive issues on mobile +* fix: incorrect template condition that set the username field to read only for remote users +* fix: tasks count across swimlanes was incorrect +* fix: avoid warning from libpng when loading PNG image with incorrect iCCP profiles +* feat: improve column header task counts +* feat: add `apple-mobile-web-app-capable` meta tag +* build(deps): bump `symfony/finder` from `5.4.40` to `5.4.42` + +Version 1.2.38 (July 20, 2024) +------------------------------ + +* fix: avoid browser caching issue when showing file attachments +* fix: comments visibility was not taken into consideration on event activities page +* fix: send comment via email was broken due to missing comment visibility logic implemented in v1.2.36 +* feat(locale): update Greek translations +* feat(locale): update Italian translations +* build(deps): bump `symfony/console` from `5.4.40` to `5.4.41` +* build(deps): bump `docker/build-push-action` from `5` to `6` + +Version 1.2.37 (June 5, 2024) +----------------------------- + +* Add CSRF check and remove `project_id` form value for `addUser` and `addGroup` actions ([CVE-2024-36399](https://github.com/kanboard/kanboard/security/advisories/GHSA-x8v7-3ghx-65cv)) +* Update `symfony/*` dependencies +* Update Docker image to Alpine 3.20 +* Update Russian and Hungarian translation +* Add `color_id` argument to `createCategory` and `updateCategory` API procedures +* Add link to create a comment before the list +* Fix: unable to create comments with "c" shortcut or "Add a comment" menu + +Version 1.2.36 (May 2, 2024) +---------------------------- + +* Add comments visibility +* Add explicit int casting to avoid PHP 8 TypeError when having empty automatic action parameters +* Add new config option `DASHBOARD_MAX_PROJECTS` +* Add reply feature to comments +* Fix search bar layout when adding more buttons via third-party plugins +* Introduce a Git hook to automatically update `version.txt` during Git checkout +* Performance improvements: + * Don't count closed tasks when rendering the board + * Force the use of the cache when there is no custom roles +* Use unique plugin name instead of plugin title for plugin registry logic +* Update dependencies + +Version 1.2.35 (February 2, 2024) +--------------------------------- + +* Add missing HTML escaping when showing group membership in user profile ([CVE-2024-22720](https://github.com/kanboard/kanboard/security/advisories/GHSA-8p3h-v7fc-xppj)) +* Update Dutch translation +* Update Bulgarian translation +* Bump `phpunit/phpunit` from `9.6.15` to `9.6.16` +* Bump `symfony/console` from `5.4.32` to `5.4.34` + +Version 1.2.34 (December 13, 2023) +---------------------------------- + +* Upgrade Docker image to Alpine 3.19 and PHP 8.3 +* API: Avoid PHP notice when searching for a project name that does not exist +* Update Bulgarian translation +* Bump `symfony/console` from `5.4.28` to `5.4.32` +* Bump `phpunit/phpunit` from `9.6.13` to `9.6.15` + +Version 1.2.33 (October 15, 2023) +--------------------------------- + +* Do not close modals when clicking on the background +* Add Bulgarian translation +* Update Ukrainian and Russian translations +* Show the two factor form in the middle of the screen like the login form does +* Do not override the `creator_id` with the current logged user if the task is imported +* Add basic Dev Container configs +* Add adaptive SVG favicon and more SVG variants: + * See https://web.dev/building-an-adaptive-favicon/ + * Added more variant of the original Inkscape icon: + - Text SVG + - Vectorized text path SVG + - Optimized SVG icon +* Remove `project_id` from task links (A few were missed in #4892) +* Remove unused and invalid method in `ProjectModel` +* Update `phpunit/phpunit` and `symfony/*` dependencies +* Update vendor folder + +Version 1.2.32 (July 11, 2023) +------------------------------ + +* Fix unexpected EventDispatcher exception in cronjob and during logout +* Integration Tests: Run `apt update` before installing Apache +* Automatic action `TaskMoveColumnClosed` does not log column movement +* Tweak Sqlite connection settings to reduce database locked errors +* Bump `phpunit/phpunit` from `9.6.9` to `9.6.10` + +Version 1.2.31 (July 3, 2023) +----------------------------- + +Security Fixes: + +- [CVE-2023-36813: Avoid potential SQL injections without breaking compatibility with plugins](https://github.com/kanboard/kanboard/security/advisories/GHSA-9gvq-78jp-jxcx) + +Other fixes and updates: + +- Run tests with PHP 8 on GitHub Actions +- Bump Symfony dependencies +- Update Composer dependencies to be able to run tests with PHP 8.2 +- Add `/usr/bin/php` symlink in the Docker image +- Replace usage of `at()` matcher with alternatives in unit tests +- Adjust plugin directory test case to work on released versions +- Fix incorrect background dynamic property in captcha library +- Update translations + +Version 1.2.30 (June 2, 2023) +----------------------------- + +Security Fixes: + +- [CVE-2023-33956: Parameter based Indirect Object Referencing leading to private file exposure](https://github.com/kanboard/kanboard/security/advisories/GHSA-r36m-44gg-wxg2) +- [CVE-2023-33968: Missing access control allows user to move and duplicate tasks to any project in the software](https://github.com/kanboard/kanboard/security/advisories/GHSA-gf8r-4p6m-v8vr) +- [CVE-2023-33969: Stored XSS in the Task External Link Functionality](https://github.com/kanboard/kanboard/security/advisories/GHSA-8qvf-9847-gpc9) +- [CVE-2023-33970: Missing access control in internal task links feature](https://github.com/kanboard/kanboard/security/advisories/GHSA-wfch-8rhv-v286) + +Other Fixes: + +- Avoid PHP warning caused by `session_regenerate_id()` +- Avoid CSS issue when upgrading to v1.2.29 without flushing user sessions + +Version 1.2.29 (May 23, 2023) +----------------------------- + +* Avoid potential clipboard based cross-site scripting ([CVE-2023-32685](https://github.com/kanboard/kanboard/security/advisories/GHSA-hjmw-gm82-r4gv)) +* Upgrade Docker image to PHP 8.2 and Alpine 3.18 +* Add themes support: dark, light and automatic mode +* Fix broken "Hide this Column" feature +* Do not close modals when clicking on the background if the form has changed +* Fix incorrect route for "My Activity Stream" +* Fix incorrect parameter encoding when using URLs rewriting +* Add support for task links in Markdown headings +* Handle 413 responses from Nginx when uploading files too large +* Restore all previously loaded translations when sending user notifications +* Regenerate session ID after successful authentication +* Use `SESSION_DURATION` option to define the session lifetime stored in the database + - The option `SESSION_DURATION` is used to define the cookie lifetime. + - With this change, Kanboard will try to use first `SESSION_DURATION` instead of the + default `session.gc_maxlifetime` value. +* Bump `phpunit/phpunit` from `9.6.6` to `9.6.8` + +Version 1.2.28 (April 8, 2023) +------------------------------ + +* Trigger `EVENT_MOVE_COLUMN` event when moving task to another swimlane +* Allow moving closed tasks when using the API +* Duplicate external links when duplicating tasks +* Add support for comparison operator to priority filter +* Prevents users to convert subtaks to tasks when custom role does not allow it +* Avoid deprecation messages when sending an email with PHP 8.2 +* Declare most common routes to have nice URLs +* Improve wording of bulk action modal to move tasks position +* Allow closing modals by clicking on the background +* Improve wording of the menu to close all tasks in a given column/swimlane +* Fix bug that prevent reordering subtasks after changing the status +* Bump version of `phpunit/phpunit`, `symfony/stopwatch`, and `symfony/finder` +* Use `GITHUB_TOKEN` instead of a personal token to run GitHub Actions +* Duplicate attachments & external links during task duplication & importing +* Move Docker image to run automated tests to GitHub Registry +* Push Docker images to an additional registry Quay.io (RedHat) +* Use the appropriate config for the start column in user iCal export +* Improved translations + +Version 1.2.27 (March 5, 2023) +------------------------------ + +- Fix category filter when the category name is a number +- Better handling of max file upload size according to PHP settings + - Allow unlimited size + - Better parsing of PHP size +- Add dropdown menu on the board to reorder tasks by ID +- Separate `font-family` specification for input and textarea. This avoids the use of `!important` in custom CSS +- Change the total number of tasks displayed in the table header to match the description "Total number of tasks in this column across all swimlanes" +- Allow full name to be retrieved by the reverse proxy authentication +- Fix `pull-right` CSS class alignment +- Use a separate dropdown menu for column sorting +- Use `assertEqualsWithDelta()` to test `time_spent` +- Add `color_id` argument to tag API procedures +- Update task time spent/estimated when removing a subtask +- Command `db:migrate` should work even if `DB_RUN_MIGRATIONS` is false +- Always trim the username before saving changes in the database +- Avoid Postgres SQL error when using project filter with a large integer +- Enable Sqlite WAL mode by default: + - WAL provides more concurrency as readers do not block writers and, + a writer does not block readers. Reading and writing can proceed concurrently. + This change might reduce the number of errors related to locked databases. +- Update translations +- Update PHP dependencies: `phpunit/phpunit`, `symfony/stopwatch` and `symfony/finder` + +Version 1.2.26 (January 14, 2023) +--------------------------------- + +- Fire events after `TaskMoveColumnOnDueDate` action +- Update date parsing logic to be compatible with PHP 8.2 +- Fix potential XSS on the Settings / API page +- Use wildcard operator for tag filter +- Fix broken user mentions in popup comment form +- Test Docker image build on pull-requests +- Bump Alpine Linux Docker image from 3.16 to 3.17 +- Update translations +- Fixed a bug about unselecting in the file `list-item-selection.js` +- Add functionality to import tasks from a project +- Add missing jQuery UI CSS dependency + +Version 1.2.25 (November 12, 2022) +---------------------------------- + +- Add experimental support for Microsoft SQL Server +- Add Open Container labels to Dockerfile +- Update links to the new documentation website +- Update German translation + +Version 1.2.24 (October 9, 2022) +-------------------------------- + +* Fixed deprecation warnings when a project or a task description is null +* Fixed missing condition in `TaskAssignDueDateOnMoveColumn` action +* Fixed Reopening of dropdown menus +* Fixed internal link creation on subtask to task conversion if language is not English +* Use a HMAC to sign and validate CSRF tokens, instead of generating random ones and storing them in the session data +* Set explicitly the time picker control to select instead of slider +* Bump `phpunit/phpunit` from `9.5.24` to `9.5.25` +* Bump `symfony/stopwatch` from `5.4.5` to `5.4.13` +* Moved `version.txt` to `app` folder +* Updated translations + +Version 1.2.23 (September 4, 2022) +---------------------------------- + +* Open SVG, Ogg, and some video file attachments in browser +* Added more video, music, code and spreadsheet extensions to show better file attachment icons +* Updated jQuery to latest stable version +* Updated Docker image to PHP 8.1 and Alpine Linux 3.16 +* Renamed default branch from `master` to `main` +* Bumped `phpunit/phpunit` from `9.5.14` to `9.5.23` +* Bumped `symfony/finder` from `5.4.3` to `5.4.11` +* Fixed subtask translation when using different languages +* Added Project Overview document template hook +* Updated translations +* Fixed wrong foreign key constraint on table `subtask_time_tracking table`. The constraints references a no-longer-existing table `task_has_subtasks` +* Fixed regression regarding subtask reordering +* Changed minimum requirement to PHP 7.4 + - PHP versions lower than 7.4 are end-of-life: https://www.php.net/supported-versions.php + - Libraries used by Kanboard have dropped support for older versions of PHP + +Version 1.2.22 (February 12, 2022) +---------------------------------- + +* Add support for PHP 8.x (Minimum requirement is now PHP >= 7.4) +* Remove `project_id` from task URLs +* Update `da_DK` translations +* Add automatic action to set the due date when the task is moved away from a specific column +* Condense wording on inferred action and update translations +* Add EVENT_CREATE and EVENT_CREATE_UPDATE events to TaskMoveColumnCategoryChange action + +Version 1.2.21 (December 16, 2021) +---------------------------------- + +* Fix and update Composer autoload +* Add plugin hook for document attachments +* Improve board column header alignment +* Ignore `project_id` for file attachments download URL (already checked elsewhere) +* Update translations +* Clarify meaning of `LDAP_USER_CREATION` in `config.default.php` +* Fix wrong internal link when converting a subtask to task (MySQL only) +* Use the overridable Markdown parser for previews +* Update `call_user_func_array()` calls to be compatible with PHP 8 +* Enable external group synchronization deactivation +* Fix tooltip shifting on long descriptions +* Add `position` argument to API procedure `updateSubtask()` +* Bump Docker image to Alpine 3.15.0 +* Bump `symfony/stopwatch` to 5.4.0 +* Bump `pimple/pimple` to 3.5.0 + +Version 1.2.20 (June 8, 2021) +----------------------------- + +* Duplicate tags when moving or duplicating tasks to another project +* Bump symfony/stopwatch to 5.3.0 +* Avoid user enumeration by using avatar image URL +* Invalidate captcha after it is used +* Avoid user enumeration using password reset functionality +* Add missing CSRF checks +* Fix bug in search when using the plus sign +* Close dialogs using Escape key even if focus is in input field +* Add a min="0" attribute to task_list form input +* Keep swimlane headers at the top +* Catch error when trying to upload empty or invalid avatar image +* Added new template hooks +* Update translations + +Version 1.2.19 (April 16, 2021) +------------------------------- + +* Trim user agent for RememberMe sessions because MySQL use a varchar(255) column +* Update Docker image to Alpine 3.13.4 +* Added "Deutsch (du)" language +* Fixed `createLdapUser` API procedure when LDAP groups are not configured +* Write RememberMe cookie only after the two-factor code has been validated +* Avoid warning when removing a plugin zip archive +* Update Hungarian translation +* Add new hook `model:task:duplication:aftersave` +* Bump symfony/stopwatch from 5.2.3 to 5.2.4 +* Bump pimple/pimple from 3.3.1 to 3.4.0 +* Bump gregwar/captcha from 1.1.8 to 1.1.9 +* Added new analytic component: "Estimated vs actual time per column" +* Do not retain any changes between shared plugins variables +* Display number of tasks according to filter +* Add support for LDAP protocol/host/port configuration by URL; make `BASE_DN` optional + - `ldap_connect($host, $port)` function signature is deprecated + - Querying an AD Global Catalog across an entire forest requires an empty base DN +* Use an absolute file path in `AssetHelper` class for `css()` & `js()` functions +* Remove whitespace at the end of `APP_VERSION` constant +* Add IP address to authentication error logs +* Add interpolation expressions to e-mail subject in automatic action "Send a task by email to someone" + - For example: `Email subject = {{column_title}}: {{title}} (#{{id}})` +* Add Hungarian Forint to the list of currencies + +Version 1.2.18 (December 28, 2020) +---------------------------------- + +* Sqlite migrations should have foreign keys disabled outside the transaction + => Existing behavior could lead to data loss if schema is changed + => If you are using Sqlite, skip version 1.2.17, upgrade directly to v1.2.18 +* Use more secure default Nginx SSL configuration in Docker image +* Update vendor folder +* Add missing pt_br translations +* Update ja_JP translations + +Version 1.2.17 (December 27, 2020) +---------------------------------- + +* Fix grammatical errors +* Add autocomplete attribute to HTML forms +* Added "Mexican Peso" to the list of currencies +* Added an option to send a copy of all generated e-mails to a BCC address +* Don't force role of users if no LDAP groups defined +* Keep the tags when converting a subtask to task +* Bump symfony/stopwatch from 5.1.8 to 5.2.0 +* Bump pimple/pimple from 3.3.0 to 3.3.1 +* Bump symfony/stopwatch from 5.2.0 to 5.2.1 +* Publish Docker images to GitHub container registry in addition to Docker Hub +* Use Github Actions to publish Docker images +* Check if the user is assigned to any role in the project +* Fix tasks.swimlane_id foreign key for Sqlite +* Remove unused namespaces +* Add mk_MK (Macedonian) translation +* Update translations + +Version 1.2.16 (October 9, 2020) +-------------------------------- + +* Update Composer dependencies +* Update translations +* Add link to toggle column scrolling in board view +* Add missing environment variables in php-fpm config +* Add setting that makes possible any new LDAP user to be Manager by default +* Add ARIA label to modal link with title attribute +* Add ARIA label to user mention +* Add ARIA label to letter avatars +* Add ARIA label to project select role without label +* Add ARIA label to dropdown autocomplete without label +* Add ARIA label to form text editor without label +* Add ARIA label to icons with title attributes +* Add ARIA label for form inputs without labels +* Add ARIA label for elements with titles +* Add hidden accessible form input labels +* Add hidden accessible titles +* Hide user name from screen readers +* Correct table collapsed column titles +* Prevent the original page from being modified by the opened link +* Allow email to be retrieve by SSO ReverseProxy +* Fix grammatically incorrect error message +* Add option to configure SMTP HELO name +* Add new config parameter SESSION_HANDLER +* Fix clearing of all Javascript storage +* Added standard notification footer to comment email template + +Version 1.2.15 (June 19, 2020) +------------------------------ + +* Update dependencies +* Added PUT method using CURLOPT_CUSTOMREQUEST +* Run integration tests on Github Actions +* Fixed capitalization of sAMAccountName for LDAP_USER_ATTRIBUTE_USERNAME example +* Added missing closing HTML tag in template +* Update Docker image to Alpine 3.12 +* Removed paragonie/random_compat (not required for PHP 7) +* Setup Dependabot on GitHub +* Allow use of the user's DN as the group filter substitution +* Add subtask events to ProjectModificationDateSubscriber +* Update Vagrantfile to Ubuntu 20.04 +* Open large modal when clicking on edit category link +* Set margin-bottom at 0 only for the last child of a tooltip element +* Prevent last swimlane to be hidden if there is only one +* Execute tooltip listeners only once when the DOM is ready +* Use Ajax request for Markdown preview +* Make tooltip events bubble +* Keep newlines in markdown +* Show the color dropdown when creating a new automatic action +* Update translations +* Correct duration calculation +* Copy subtask assignee when duplicating a task +* Save task list order in user session +* Add action to assign a user when the swimlane change + +Version 1.2.14 (April 15, 2020) +------------------------------- + +* Update translations +* Add new event subtask.create_update +* Replace Travis CI by GitHub Actions +* Add option to enable or disable global tags per projects +* Show group membership(s) in user summary and user list +* Docker: use real hostname instead of "localhost" +* Add new task/project image hooks +* Fix invalid RSS feed encoding +* Add new plugin hooks +* Rename "private" projects to "personal" +* Add per-project and per-swimlane task limits +* Use parent task color when converting a subtask to task +* Add environment variables support to configure the application +* Add the possibility to make project tags global from project settings +* Fix regex to detect external links with attachments +* Use KANBOARD_URL to build URIs if specified +* Make time_spent and time_estimated fields editable for updateTask and createTask API calls +* Kanboard now requires PHP >= 7.2 since other versions are deprecated +* Avoid page shrinking when drag and drop cards on iOS devices +* Added a hover color to i elements inside the "dropdown-submenu-open" class +* Avoid duplicating Dockerfiles for each architecture + +Version 1.2.13 (December 15, 2019) +---------------------------------- + +* Adjust width of time tracking column +* Make subtasks not wrap under icons +* Make column scrollable in Kanban view +* Add composer dependency roave/security-advisories +* Add colors to tag and category lists +* Update Parsedown to v1.7.3 (security update) +* Make sure the elements behind the alert notification are clickable after animation +* Make sure incompatible plugins can be uninstalled from the web ui +* Move "data-js-lang" attribute to HTML "lang" attribute +* Update language codes for time picker so the calendars are translated correctly +* Dropdown in project managers view covers heading +* Fix date picker datetime parsing when using pre-defined localized versions of am/pm +* Show ISO date format in application settings +* Datepicker stores its Spanish locales as "es", not "es-ES" or "es-VE" +* Increase width of color picker to avoid text overlap in Polish +* Close open menu when clicking again on the button +* Fix width of filter bar in mobile +* In PHP-7.4, nested ternary operators are to be bracketed +* Change string indexing from {0} to [0] (deprecated in PHP 7.4) +* Update translations + +Version 1.2.12 (October 26, 2019) +--------------------------------- + +* Update Docker image to Alpine Linux 3.10.3 +* Add new template hook: "template:project-permission:after-adduser" +* Upgrade jQuery to version 3.4.1 +* Add Spanish (Venezuela) translation +* Removed color_id requirement for tag API calls +* Fix subtask restriction modal when clicking on the icon instead of link +* Use PHPUnit 5 for Vagrant +* Prevent last project manager role from being removed +* Check API token before LDAP authentication +* Make sure task limit consider all open tasks (not only filtered tasks) +* Update translations +* Change user filter and category icon +* Add "anybody" filter +* Disable user scaling to avoid page shrinking when drag&drop on mobile +* Fix condition for action "Automatically update the start date when task move away from certain column" +* Add tests for task link and subtask assignee filters +* Changes filters from in array to subqueries +* Add hash to image URL to force browser to update avatar image when changed + +Version 1.2.11 (August 24, 2019) +-------------------------------- + +Breaking Changes: + +* Internet Explorer support is now deprecated +* Add project ID to ExternalTaskProviderInterface::fetch() + +Fixes and Improvements: + +* Fixed issue of tooltip not disapearing +* Update Docker image to Alpine Linux 3.10.2 +* Hide due date time on the card if time is 00:00 +* Add new plugin hooks in view switcher +* Ignore Dockerfiles from git archive +* Remove dependency on nodejs and gulp +* Remove dependency on Sass + - Convert *.sass files to vanilla CSS + - Start using CSS variables + - Add PHP minifier +* Add link button to text editor +* Implements check for duplicate default categories +* Implements check for duplicate default columns +* Fix HTML parsing in Markdown editor +* Change checkboxes alignment in task creation form +* Add support for reference:none +* Fix tabindexes on task creation and modification forms +* Add option to clone filters on project duplication + - Fixed missing metadata option from project "create from" + - Added option to clone project custom filters + - Added append option to custom field tests + - Added a test that uses the "append" option + - Fixed disabled swimlane duplication error with Postgresql +* Update translations +* Save thumbnails as PNG to have transparency +* New action to update the start date when a task move away from a column +* Add the possibility to sort columns by due date +* Add "identifier" beside "name" while creating a new project + +Version 1.2.10 (June 21, 2019) +------------------------------ + +* Add Auto-Submitted E-mail header as per RFC 8384 +* Add HTML tag in email notifications +* Add new hook template:export:header +* Do not show duplicated results when multiple comments match +* Add Docker manifest with multiple architectures (arm32v6, arm32v7, arm64v8, amd64) +* Update Docker image to Alpine 3.10.0 +* Add View File on popover to tooltip +* Fix text file preview +* Set "start date" and "end date" on projects from API +* Add cURL support to HTTP Client + - Add HTTP_PROXY_EXCLUDE option when cURL is used + - Show HTTP client backend in about page + - Fallback to legacy Stream Contexts if cURL extension is not available +* Add Bitcoin to the currency list +* Add automatic action to move task between columns based on due date +* Fixes icon opacity when hovered +* Hide one task count when there is only one swimlane +* Update translations + +Version 1.2.9 (April 5, 2019) +----------------------------- + +* Add Slovak translation +* Update translations +* Changes search by reference to case insentive +* Fix postgres explicit schema name usage +* Simplify local Docker image build +* Show a 404 when accessing data folder from URL (Docker Image) +* Clarify the comment about MAIL_SMTP_ENCRYPTION +* Remove dependency on Bower +* Replaces accordion Javascript component by
HTML element +* Fix MySQL migration when using increment values different from 1 +* Add missing webhook event: task.move.project +* Add new actions to reorder tasks by column + +Version 1.2.8 (February 2, 2019) +-------------------------------- + +Breaking Changes: + +* Authorize only API tokens when 2FA is enabled (no user password) +* Disable by default plugin installer for security reasons: + - There is no code review or any approval process to submit a plugin. + - This is up to the Kanboard instance owner to validate if a plugin is legit. + +Fixes and Improvements: + +* Limit avatar image size +* Avoid CSRF in users CSV import +* Avoid XSS in pagination sorting +* Do not show projects dropdown when prompting the 2FA code +* Always returns a 404 instead of 403 to avoid people discovering users +* Check if user role has changed while the session is open +* Add missing CSRF check in TwoFactorController::deactivate() +* Hide edit button when user cannot edit task +* Fix permission check before "Assign to me" +* Fix permission check before showing project options +* Fix assignable users on a group with a custom role +* Fix import of automatic actions when parameters are "unassigned" or "no category" +* Update license year +* Update Docker image to Alpine 3.9 +* Update translations +* Fix PHP error in task views (tag colors) +* Limit assignee drop-down selector scope + +Version 1.2.7 (December 19, 2018) +--------------------------------- + +* Write log entry on file removal +* Auto link duplicated tasks +* Auto link tasks duplicated to another project +* Auto link tasks created from a subtask +* Redirect to board view of the current task after duplication +* Fix broken link to contributor page +* Add automatic action for moving a task to a swimlane based on category change +* Add automatic action to assign a category based on swimlane change +* Add ordering comments by id along with creation date +* Fix custom roles duplication (source and destination column_id) +* Add locale en_GB +* New automatic action: move the task to another swimlane when assigned +* Disable php_uname() warning for restrictive environments +* Add hook to board settings +* Add method remove() to settings model +* Add php7-bcmath to Docker image +* Add sorting by reference in list view +* Added priority, swimlane, and column values from parent task to task converted from subtask +* Update translations + +Version 1.2.6 (October 10, 2018) +-------------------------------- + +* Escape table name 'groups' because groups is a reserved word as of MySql 8.0.2 +* Reduce number of SQL queries when doing groups sync +* Make swimlane filter compatible with numeric title +* Duplicate reference fields when duplicating a task +* Do not try to redirect to login page when offline +* Define fixed width for auto-complete dropdown +* Fix task drag and drop slowdown when a column is hidden +* Make PLUGINS_DIR absolute in config.default.php +* Add custom roles project duplication +* Allow 'No assignee' for external task on single user public boards +* Add tag and category colors +* Exclude task links and user mentions from nesting (Markdown parser) +* When forcing HTTPS, handle subfolder URLs properly +* Add search within a range of dates for completion, modification, creation, and moved fields +* Update Docker image to Alpine Linux 3.8 +* Make sure the presense of mod_env is checked in .htaccess +* Make HTTP client timeout configurable +* Use SET NAMES instead of charset for MySQL connection +* Vendoring deprecated Composer libs +* Update translations and fix typos + +Version 1.2.5 (June 15, 2018) +----------------------------- + +* Update jQuery to latest version +* Defer javascript files loading by default +* Add quick link "assign me" in different views +* Add bulk task operations in list view +* Add checkboxes in list view to move tasks to another column at once +* Make sure automatic actions are applied to all tasks when using bulk operations +* Add ability to run cron jobs by calling URL +* Add basic print stylesheet +* Add dashboard and search task footer hooks +* Reword project settings label +* Improve Docker image config overrides +* Fix cronjob in Docker image +* Increase Nginx fastcgi buffers for Docker image +* Increase size of the "users.language" column +* Update translations and improve English texts + +Version 1.2.4 (May 16, 2018) +---------------------------- + +* Rewrite tooltip code without jQuery +* Update Parsedown library +* Remove all attachments when removing a project +* Improve whitespace handling in "cli locale:compare" command +* Don't markdown project owner's name in header tooltip +* Add SSL to Docker image +* Avoid people to remove themselves from project permissions +* Fix escaping issue in Markdown editor +* Add data/config.php to .gitignore +* Clarified text label for notification settings +* Add Ukrainian translation +* Do not show inactive users in group members dropdown +* Improve dashboard pagination +* Make list view more compact +* Hide private projects checkbox if the feature is disabled +* Make cli locale commands working outside of source tree +* Make subtask title text field wider when editing subtasks +* Add link to open images in a new tab +* Make hardcoded hours string translatable +* Translation updates + +Version 1.2.3 (April 18, 2018) +------------------------------ + +New features: + +* Add Project MetaData API calls +* Add default filter per user + +Improvements: + +* Use utf8mb4 encoding for MySQL instead of utf8 (Emoji support) +* Increase text fields length in several tables +* Move documentation to https://docs.kanboard.org/ +* Make sure no empty group is submitted on project permissions page +* Translate subtasks status and internal links labels in notifications +* Add link to tasks and projects in overdue notifications +* Add missing translations +* Move custom libs to the source tree + +Bug fixes: + +* Fix margin for task recurrence tooltip + +Version 1.2.2 (March 30, 2018) +------------------------------ + +Improvements: + +* Add thumbnail quality parameter (default to 95) +* Always display SQL errors +* Move SimpleLogger lib into app source tree +* Add system log driver and use it by default +* Display exceptions from plugins while refreshing board +* Redirect to original URL after OAuth login +* Add author name and email arguments to mail client +* Improve HTTP client to raise exceptions +* Update translations + +Bug fixes: + +* Fix broken daily summary export +* Fix role precedence in LDAP integration + +Version 1.2.1 (February 28, 2018) +--------------------------------- + +New features: + +* Add automatic action to change column once a start date is reached +* Add automatic action to change color once start date is reached +* Add CSS class to categories to allow custom styling +* Add option to disable Mysql SSL server verification +* Add timeout parameter for database connection +* Add error log for authentication failure to allow fail2ban integration + +Improvements: + +* Set the correct swimlane/column ID when moving a task via its internal dialog +* Allow filtering for tasks without due date +* Add plugin hook 'aftersave' after creating Task +* Run SessionHandler::write() into a transaction +* Remove dependency on PicoFeed +* Add CSRF check for task and project files upload +* Add missing CSRF check on avatar upload form +* Add missing CSRF check in saveUploadDB() method +* Update Vagrantfile to use Ubuntu Xenial +* Send event author in webhook notification +* Update translations +* Update documentation + +Version 1.2.0 (December 27, 2017) +--------------------------------- + +Breaking changes: + +* Kanboard supports only PHP >= 5.6 (PHP 5.3, 5.4 and 5.5 are not supported anymore) + +New features: + +* PHP sessions are now stored into the database, + In this way, it's easier to run Kanboard behind a load-balancer + +Improvements: + +* Copy category from parent task when creating a task from a subtask +* Translation updates and improvements + +Version 1.1.1 (December 9, 2017) +-------------------------------- + +Breaking changes: + +* The Docker tag "stable" is not used anymore, instead use a specific version tag +* Task limit apply across all swimlanes +* Kanboard is now using the domain kanboard.org + +New features: + +* New automatic action to create a subtask assigned to the creator and start the timer +* New automatic action to stop the timer of subtasks +* Add command line tool to remove project activities after one year +* Add command line tool to disable projects not touched during one year +* Add config option to exclude fields from auth providers sync +* Add new plugin hooks + +Improvements: + +* Open audio files in a new tab +* Upgrade Docker image to Alpine Linux 3.7 +* Improve Docker build to use Docker Hub hooks +* The application version is now included into the Docker image +* Disable private projects when disabling a user +* Allow administrators to update username of remote users +* Improve layout on mobile/tablet devices +* Changed board column headings to show swimlane-column total in bold +* Enable dragging to collapsed columns +* Add missing checks for requirements + +Bug fixes: + +* Add class "js-modal-replace" to icons to make it clickable +* Improve permission checks on custom filters page to avoid forbidden access + +Version 1.1.0 (November 20, 2017) +--------------------------------- + +Breaking changes: + +* Remove feature "Allow everybody to access to this project" (You must define project members and groups) +* Composer dependencies are now included in the repository to be able to use git-archive (except development dependencies) + +New features: + +* Add predefined templates for task descriptions +* Add the possibility to send tasks and comments to multiple recipients +* Add users, groups and projects search +* Add command line argument to display Kanboard version +* Add user backend provider system (to be used by external plugins) +* Add Romanian and Chinese (Taiwan) translation + +Improvements: + +* Minor CSS improvements +* Add help message on project sharing page +* Task CSV import is now able to handle the priority, start date, tags and one external link +* Improve iCalendar feed to include tasks with start/end date and due date with a time +* Check if the start date is before due date +* You can get an archive of Kanboard by using the download button in Github or the command git archive +* Translation updates + +Bug fixes: + +* Fix project dropdown visibility when page is scrolled down +* Task move events must be executed synchronously +* Handle CSV files with only "\r" line endings + +Version 1.0.48 (October 23, 2017) +--------------------------------- + +Improvements: + +* Add bulk subtasks creation +* Add filter by score/complexity +* Improved display of the header bar +* Displays bullets from lists in tooltips +* Updated translations +* Add tags and priority to task export +* Make the number of events stored in project activities configurable +* Do not use jQuery tooltip for task title in collapsed mode +* Remove dependency on Yarn +* Improve external task integration +* Add support for array parameters in automatic actions +* Add tooltip to subtask icons +* Add attribute title to external links +* Render a link if the reference is a URL +* Add icon to edit a task quickly on the board +* Improve .htaccess when using HTTP Basic Authentication for Apache/FastCGI +* Add note to specify incompatibility with mod_security + +Version 1.0.47 (October 3, 2017) +-------------------------------- + +New features: + +* Vietnamese translation + +Improvements: + +* Updated translations + +Security Issues: + +* Avoid people to alter other project resources by changing form data + +Version 1.0.46 (August 13, 2017) +-------------------------------- + +Security Issues: + +* Fix two privilege escalation issues: a standard user could reset the password +of another user (including admin) by altering form data. +(CVE-2017-12850 and CVE-2017-12851, discovered by "chbi"). + +Improvements: + +* Add "Create another link" checkbox for internal link as in sub-task creation +* Updated translations + +Bug fixes: + +* Fix parsing issue in phpToBytes() method + +Version 1.0.45 (June 23, 2017) +------------------------------ + +New features: + +* Automatic action to assign tasks to its creator +* Add the possibility to create a comment when a task is sent by email +* Add dropdown menu to autocomplete email field from project members +* Add configurable list of predefined subjects when sending a task or a a comment by email +* Add command line argument to filter overdue notification for a given project + +Improvements: + +* Improve SQL migrations when old default swimlanes have the same name as a normal swimlanes + +Bug fixes: + +* Add missing subtask permissions for project viewer role +* Fix Javascript language mapping + +Version 1.0.44 (May 28, 2017) +----------------------------- + +Improvements: + +* Use datetime field for due date +* Update Docker image to Alpine Linux 3.6 +* Add the possibility to pass API token as environment variable for Docker container +* Add wildcard search for task reference field +* Improve automated action TaskAssignColorOnDueDate to update task only when necessary +* Add task and project API formatters +* Update translations + +Bug fixes: + +* Fix broken user mentions in comment form at the bottom of the task view page +* Ensure project tags are removed when the project is removed +* Avoid PHP notice when regenerating API token for a user +* Fix wrong dropdown menu in group members list +* Show only active users in auto-complete forms (project permissions) +* Check owner existence before to create project + +Version 1.0.43 (April 30, 2017) +------------------------------- + +Improvements: + +* Add "[DUPLICATE]" prefix to duplicated tasks title +* Add sorting by position and start date in task list view +* Update translations + +Bug fixes: + +* Add missing plugin parameter for search box (Gantt and calendar plugin) +* Fix broken start date button + +Version 1.0.42 (April 8, 2017) +------------------------------ + +New features: + +* New restrictions for custom project roles + +Improvements: + +* Improved dashboard + +Breaking Changes: + +* Move calendar to external plugin: https://github.com/kanboard/plugin-calendar +* Move Gantt charts to external plugin: https://github.com/kanboard/plugin-gantt +* Move Gravatar to external plugin: https://github.com/kanboard/plugin-gravatar + +Bug fixes: + +* Fix typo in Sqlite schema + +Version 1.0.41 (March 19, 2017) +------------------------------- + +New features: + +* Add Croatian language translation + +Improvements: + +* Simplify dashboard to use new tasks list view +* Move notifications outside of dashboard +* Render QR code for TwoFactor authentication without Google Chart API +* Add toggle button to show/hide subtasks in task list view +* Use same layout as task listing for task search +* Display tags in task list view +* Make user actions available from contextual menu +* Change users and groups list layout +* Project priority is always rendered now +* Do not list private projects when adding a new user +* Restore link for task title on board + +Breaking Changes: + +* Remove method getQrCodeUrl() from PostAuthenticationProviderInterface + +Version 1.0.40 (February 24, 2017) +---------------------------------- + +New features: + +* Send comments by email +* Send tasks by email +* Add Reply-To header to emails sent from Kanboard +* Upload Sqlite database from user interface +* Automatic action to change task color when due date is expired + +Improvements: + +* Make link to calendar view bookable +* Reintroduce word search in board selector +* Properly resize task list height on column toggle +* Show total score across all swimlanes +* Redesign task list view and project list view +* Allow people to remove missing automatic actions (installed from a removed plugins) +* Improve task view tables +* Simplify automatic actions table +* Show category description in tooltip +* Show category creation form in modal dialog +* Prevent people to remove swimlanes that contains tasks +* Show task count in swimlane table +* Use contextual menu instead of action column in users management + +Breaking changes: + +* The concept of "default swimlane" has been removed +* Previous default swimlanes are migrated to an independent swimlanes +* Columns "default_swimlane" and "show_default_swimlane" from "projects" table are not used anymore +* Remove API method "getDefaultSwimlane()" +* Add mandatory argument "project_id" to API method "updateSwimlane()" +* Change interface for mail transports + +Bug fixes: + +* Upload files button stay disabled when there are other submit buttons on the same page +* Hiding subtasks from hidden tasks in dashboard + +Security: + +* Fix XSS in LetterAvatarProvider (render broken image) + +Those issues are harmless if you use default Kanboard settings for CSP rules: + +* Avoid potential XSS in project overview when listing users +* Avoid potential XSS in Gantt chart + +Version 1.0.39 (February 12, 2017) +---------------------------------- + +Improvements: + +* Add menu entry in task dropdown to add attachments +* Improve error reporting when file upload is not configured properly +* Open comments on board view with a modal dialog instead of tooltip +* Improve card icons alignment on board +* Adjust modal dialog width on mobile devices +* Add priority column in list view +* Change wording for project status (use "closed" instead of "inactive") +* Prevent people to remove columns that contains tasks +* Improve LDAP error reporting +* Add configuration parameter to disable email configuration from user interface +* Add email address field for projects +* Improve forget password behavior (notify the user that an email has been sent or not) +* Do not display current project in board selector +* Do not set default task assignee for team projects +* Comments are highlighted if hash (#comment-123) is present in URL +* Documentation translated in Turkish + +Bug fixes: + +* Search with multiple expressions with double quotes was not working +* Fix broken subtask restriction per user +* Fix CFD chart (stack wrongly ordered) + +Version 1.0.38 (January 28, 2017) +---------------------------------- + +New features: + +* User invitations by email + +Improvements: + +* Simplify user creation form +* Add modification date for comments +* Add project creation links to project management pages +* More API procedures are now available to project members and project viewers +* Simplify date and time configuration to avoid potential validation issues +* Show dashboard column visibility in columns page +* Add new template hooks +* Update translations (id_ID, de_DE, ru_RU, fr_FR, pt_PT) +* Add command to execute individual job (mostly for debugging) + +Regressions: + +* Stay on the same page when a task is closed +* Wrong URL in modal to move task to another project + +Bug fixes: + +* Fix broken link when clicking on user avatar for tasks on board +* Fix wrong datetime formatting when task form shows validation errors +* Empty arrays are serialized to a list instead of a dict (Json API) +* Always unbind internal listeners when closing a modal dialog +* Fix installation errors on MySQL 8.0.0 (unescaped reserved keyword) +* Avoid PHP notice when column form validation failed +* Fix wrong default value for add group member modal +* Add missing filter (completed) for task search + +Version 1.0.37 (January 14, 2017) +--------------------------------- + +Improvements: + +* Improve keyboard shortcuts handling +* Improve auto-complete dropdown elements sorting +* Larger task form +* Rewrite dialog and confirmation boxes (inline popups) +* Remove TaskGanttCreationController +* Add helpers to open modal boxes +* Make icons clickable in menus +* Open task imports in modal box +* Open form to create customer filters in modal box +* Open project activities in modal box +* Display project analytics in modal box +* Display project exports in modal box +* Improve accordion component +* Improve currencies page navigation +* Improve link labels page navigation +* Improve settings page layout +* Offer the possibility to define version compatibility from plugins +* Add task creation event to the automatic action to send task by email + +Bug fixes: + +* Closing screenshot dialog prevent input elements to get focus + +Version 1.0.36 (December 30, 2016) +---------------------------------- + +New features: + +* Add slideshow for images +* Add API calls to manage tags +* Offer the possibility to override internal formatter objects from plugins +* Open PDF attachments in browser tab (preview) + +Improvements: + +* Add pagination details +* Handle username with dots in user mentions +* Rewrite UI component that change user/group roles +* Replace Chosen jQuery plugin by custom UI component +* Remove dependency on Mousetrap Javascript library +* Disable PageSpeed module from .htaccess if present +* Add currency of Chinese Yuan + +Bug fixes: + +* Fix compatibility issue with PHP 5.3 for array_combine function +* Fix wrong controller name on project activity page when using filters +* Uploaded avatar images are now visible in public board view + +Version 1.0.35 (December 4, 2016) +--------------------------------- + +New features: + +* Add external tasks plugin interfaces +* Add personal API access token for users +* Rewrite of Markdown editor (remove CodeMirror) +* Suggest menu for task ID and user mentions in Markdown editor +* Add config parameter to disable automatic SQL migrations + +Improvements: + +* Add button to close inline popups +* Simplify `.htaccess` to avoid potential issues with possible specific Apache configurations +* Replace notifications Javascript code by CSS +* Refactoring of user mentions job +* Remove Nitrous installer +* Update translations +* Rewrite some components in Vanilla Javascript +* Started Javascript code refactoring to avoid to much dependencies on jQuery +* Remove dependency on VueJS and CoreMirror +* Add P3P headers to avoid potential issues with IE + +Breaking changes: + +* Rename command line tool `./kanboard` to `./cli` + +Bug fixes: + +* Change column type for application settings value (field too small) +* Fix link generation when user mention is followed by a punctuation mark +* Make user mentions works again + +Version 1.0.34 (October 11, 2016) +--------------------------------- + +New features: + +* Custom project roles with configurable restrictions +* Duplicate a task to multiple projects during creation +* New automatic action: + - Close a task in a specific column when not moved during a given period + +Improvements: + +* Do not close the popover when clicking on the background +* Add visual icon to show a dropdown action on task +* Avoid 'blur' effect on popover +* Accept more file types for external links +* Restrict search to active projects +* Improve task status filter +* Add filter tag:none +* Always apply merge hooks in task creation controller +* Update task moved date only when the column or swimlane is changed +* Add new subtask hooks +* Add the actual use of TaskStartDateFilter +* Update translations and documentation + +Bug fixes: + +* Send absolute links in email notifications +* Restrict task complexity to a specific range to avoid integer overflow +* Do not show closed tasks in task move position form +* Avoid "Controller not found" in Settings > Links + +Version 1.0.33 (September 5, 2016) +---------------------------------- + +New features: + +* Move a task without drag and drop (smartphones and tablets) +* Add the possibility to unlock users from the user interface +* New API calls for task metadata +* New automatic actions: + - Define color by Swimlane + - Define priority by Swimlane + +Improvements: + +* Introduce Vue.js to manage user interface components +* Add column "Reference" and "Creator Name" in CSV task export +* Show both time spent and estimated on the board +* Store board collapsed mode user preference in the database +* Store comment sorting direction in the database +* Avoid tags overlapping on the board +* Show project name in notifications +* Allow priority changes for inverted priority scales +* Add the possibility to attach template hooks with local variables and callback +* Add "reference" hooks +* Show project name in task forms +* Convert vanilla CSS to SASS +* Make user interface more responsive for smartphones and tablets +* Support version operators for plugin directory: >= and > +* Update Spanish documentation + +Other changes: + +* Time spent (in hours) for subtasks are not rounded too the nearest quarter anymore + +Bug fixes: + +* Fix improper HTML escaping for textarea (potential XSS) +* Do not show closed tasks on public boards +* Fix undefined constant in config example file +* Fix PHP notice when sending overdue notifications +* Fix wrong project date format (shown as 01/01/1970) + - If the dates still not correct, modify and save the date + +Version 1.0.32 (July 31, 2016) +------------------------------ + +New features: + +* New automated actions: + - Close tasks without activity in a specific column + - Set due date automatically + - Move a task to another column when closed + - Move a task to another column when not moved during a given period +* New filter "moved" for moved date of tasks +* Added internal task links to activity stream +* Added new event for removed comments +* Added search filter for task priority +* Added the possibility to hide tasks in dashboard for a specific column +* Documentation translated in Russian + +Improvements: + +* Improve background worker and job handler +* New template hooks +* Removed individual column scrolling on board, columns use the height of all tasks +* Improve project page titles +* Remove sidebar titles when not necessary +* Internal events management refactoring +* Handle header X-Real-IP to get IP address +* Display project name for task auto-complete fields +* Make search attributes not case sensitive +* Display TOTP issuer for 2FA +* Make sure that the table schema_version use InnoDB for Mysql +* Use the library PicoFeed to generate RSS/Atom feeds +* Change all links to the new repository + +Bug fixes: + +* Allow users to see inactive projects +* Fixed typo in template that prevent project permissions to be duplicated +* Fixed search query with multiple assignees (nested OR conditions) +* Fixed Markdown editor auto-grow on the task form (Safari) +* Fixed compatibility issue with PHP 5.3 for OAuthUserProvider class + +Version 1.0.31 (July 3, 2016) +----------------------------- + +New features: + +* Added tags: global and specific by project +* Added application and project roles validation for API procedure calls +* Added new API call: "getProjectByIdentifier" +* Added new API calls for external task links, project attachments and subtask time tracking + +Improvements: + +* Use PHP 7 for the Docker image +* Preserve role for existing users when using ReverseProxy authentication +* Handle priority for task and project duplication +* Expose task reference field to the user interface +* Improve ICal export +* Added argument owner_id and identifier to project API calls +* Rewrite integration tests to run with Docker containers +* Use the same task form layout everywhere +* Removed some tasks dropdown menus that are now available with task edit form +* Make embedded documentation readable in multiple languages (if a translation is available) +* Added acceptance tests (browser tests) + +Bug fixes: + +* Fixed broken CSV exports +* Fixed identical background color for LetterAvatar on 32bits platforms (Hash greater than PHP_MAX_INT) +* Fixed lexer issue with non word characters +* Flush memory cache in worker to get latest config values +* Fixed empty title for web notification with only one overdue task +* Take default swimlane into consideration for SwimlaneModel::getFirstActiveSwimlane() +* Fixed "due today" highlighting + +Breaking changes: + +* Docker volume paths are changed to /var/www/app/{data,plugins} + +Version 1.0.30 (June 8, 2016) +----------------------------- + +Improvements: + +* Show tasks that are due today in a different color + +Bug fixes: + +* Fixed wrong controller for search in dashboard +* Fixed plural form in alert message +* Fixed CSS cosmetic issue with popover and tooltips + +Version 1.0.29 (June 5, 2016) +----------------------------- + +New features: + +* Manage plugin from the user interface and from the command line +* Added support for background workers +* Added the possibility to convert a subtask to a task +* Added menu entry to add tasks from all project views +* Add tasks in bulk from the board +* Add dropdown for projects +* Added config parameter to allow self-signed certificates for the HTTP client + +Improvements: + +* Display local date format in date picker +* Configure email settings with the user interface in addition to config file +* Upgrade Docker image to Alpine Linux 3.4 +* Move task import to a separate section +* Mark web notification as read when clicking on it +* Support strtotime strings for date search +* Reset failed login counter and unlock user when changing password +* Task do not open anymore in a new window on the Gantt chart +* Do not display task progress for tasks with no start/end date +* Use Gulp and Bower to manage assets +* Controller and Middleware refactoring +* Replace jQuery mobile detection by the library isMobile + +Bug fixes: + +* Fixed user date format parsing for dates that can be valid in multiple formats +* Do not sync user role if LDAP groups are not configured +* Fixed issue with unicode handling for letter based avatars and user initials +* Do not send notifications to disabled users +* Fixed wrong redirect when removing a task from the task view page + +Breaking changes: + +* Webhook to create tasks have been removed, use the API instead +* All controllers have been renamed, people who are not using URL rewriting will see different URLs +* All models have been renamed, plugin maintainers will have to update their plugins + +Version 1.0.28 (May 8, 2016) +---------------------------- + +New features: + +* Added automated action to change task color based on the priority +* Added support for LDAP Posix Groups (OpenLDAP with memberUid or groupOfNames) +* Added support for LDAP user photo attribute (Avatar image) +* Added support for language LDAP attribute +* Added support for Mysql SSL connection +* Search in activity stream +* Search in comments +* Search by task creator +* Added command line utility to reset user password and to disable 2FA + +Improvements: + +* Improve Avatar upload form +* User roles are now synced with LDAP at each login +* Improve web page title on the task view +* Unify task drop-down menu between different views +* Improve LDAP user group membership synchronization +* Category and user filters do not append anymore in search field +* Added more template hooks +* Added tasks search with the API +* Added priority field to API procedures +* Added API procedure "getMemberGroups" +* Added parameters for overdue tasks notifications: group by projects and send only to managers +* Allow people to install Kanboard outside of the DocumentRoot +* Allow plugins to be loaded from another folder +* Filter/Lexer/QueryBuilder refactoring + +Bug fixes: + +* Allow a project owner to manage his own public project +* Fixed PHP warning when removing a user with no Avatar image +* Fixed improper Markdown escaping for some tooltips +* Closing all tasks by column, also update closed tasks +* Fixed wrong task link generation within Markdown text +* Fixed wrong URL on comment toggle link for sorting +* Fixed form submission with Meta+Enter keyboard shortcut +* Removed PHP notices in comment suppression view + +Version 1.0.27 (March 27, 2016) +------------------------------- + +New features: + +* Added Markdown editor +* Added user avatars with pluggable system + - Default is a letter based avatar + - Gravatar + - Avatar Image upload +* Added Korean translation + +Improvements: + +* Added more logging for LDAP client +* Improve schema migration process +* Improve notification configuration form +* Handle state in OAuth2 client +* Allow to use the original template in overridden templates +* Unification of the project header +* Refactoring of Javascript code +* Improve comments design +* Improve task summary sections +* Put back the action sidebar in task view +* Added support for multiple placeholders for LDAP_USER_FILTER +* Added local file link provider +* Show configuration in settings page +* Added "?" to display list of keyboard shortcuts +* Added new keyboard shortcuts for task view +* Always display project name and task title in task views +* Improve automatic action creation +* Move notifications to the bottom of the screen +* Added the possibility to import automatic actions from another project +* Added Ajax loading icon for submit buttons +* Added support for HTTP header "X-Forwarded-Proto: https" + +Bug fixes: + +* Fix bad unique constraints in Mysql table user_has_notifications +* Force integer type for aggregated metrics (Burndown chart concat values instead of summing) +* Fixes cycle time calculation when the start date is defined in the future +* Access allowed to any tasks from the shared public board by changing the URL parameters +* Fix invalid user filter for API procedure createLdapUser() +* Ambiguous column name with very old version of Sqlite + +Version 1.0.26 (February 28, 2016) +---------------------------------- + +Breaking changes: + +* API procedures: + - "moveColumnUp" and "moveColumnDown" are replaced by "changeColumnPosition" + - "moveSwimlaneUp" and "moveSwimlaneDown" are replaced by "changeSwimlanePosition" + +New features: + +* Add drag and drop to change subtasks, swimlanes and columns positions +* Add file drag and drop and asynchronous upload +* Enable/Disable users +* Add setting option to disable private projects +* Add new config option to disable logout + +Improvements: + +* Use inline popup to create new columns +* Improve filter box design +* Improve image thumbnails and files table +* Add confirmation inline popup to remove custom filter +* Increase client_max_body_size value for Nginx +* Split Board model into multiple classes +* Improve logging for the Docker image + +Bug fixes: + +* Fix PHP notices during creation of first project and in subtasks table +* Fix filter dropdown not accessible when there are too many items +* Fix regression: unable to change project in "task move/duplicate to another project" + +Version 1.0.25 (February 7, 2016) +--------------------------------- + +Breaking changes: + +* Core functionalities moved to external plugins: + - Google Auth: https://github.com/kanboard/plugin-google-auth + - Github Auth: https://github.com/kanboard/plugin-github-auth + - Gitlab Auth: https://github.com/kanboard/plugin-gitlab-auth + +New features: + +* When creating a new project, have the possibility to select another project to duplicate +* Add a "Me" button to assignee form element +* Add external links for tasks with plugin api +* Add project owner (Directly Responsible Individual) +* Add configurable task priority +* Add Greek translation +* Add automatic actions to close tasks with no activity +* Add automatic actions to send an email when there is no activity on a task +* Regroup all daily background tasks in one command: "cronjob" +* Add task dropdown menu on listing pages + +Improvements: + +* New Dockerfile based on Alpine Linux and Nginx/PHP-FPM +* The date time format can be chosen in application settings +* Export only open tasks in iCal feed +* Remove time form on task summary page and move that to task edit form +* Replace box shadow by a larger border width when a task is recently modified +* Do not refresh the whole page when changing subtask status +* Add dropdown menu with inline popup for all task actions +* Change sidebar style +* Change task summary layout +* Use inline popup for subtasks, categories, swimlanes, actions and columns +* Move homepage menus to the user dropdown +* Have a new task assigned to the creator by default instead of "no assignee" +* Show progress for task links in board tooltips +* Simplify code to handle ajax popover and redirects +* Simplify layout and templates generation +* Move task form elements to Task helper + +Bug fixes: + +* Category label is broken on the board if there's a url in the description +* Fix pagination on task time tracking page + +Version 1.0.24 (January 23, 2016) +--------------------------------- + +New features: + +* Forgot Password +* Add drop-down menu on each board column title to close all tasks +* Add Malay language +* Add new API procedures for groups, roles, project permissions and to move/duplicate tasks to another project + +Improvements: + +* Avoid to send XHR request when a task has not moved after a drag and drop +* Set maximum dropzone height when the individual column scrolling is disabled +* Always show the search box in board selector +* Replace logout link by a drop-down menu +* Handle notification for group members attached to a project +* Return the highest role for a project when a user is member of multiple groups +* Show in user interface the saving state of the task +* Add drop-down menu for subtasks, categories, swimlanes, columns, custom filters, task links and groups +* Add new template hooks +* Application settings are not cached anymore in the session +* Do not check board status during task move +* Move validators to a separate namespace +* Improve and write unit tests for reports +* Reduce the number of SQL queries for project daily column stats +* Remove event subscriber to update date_moved field +* Make sure that some event subscribers are not executed multiple times +* Show rendering time of individual templates when debug mode is enabled +* Make sure that no events are fired if nothing has been modified in the task +* Make dashboard section title clickable +* Add unit tests for LastLogin + +Bug fixes: + +* Automatic action listeners were using the same instance +* Fix wrong link for category in task footer +* Unable to set currency rate with Postgres database +* Avoid automatic actions that change the color to fire subsequent events +* Unable to unassign a task from the API +* Revert back previous optimizations of TaskPosition (incompatibility with some environment) + +Version 1.0.23 (January 9, 2016) +-------------------------------- + +Breaking changes: + +* Plugin API changes for Automatic Actions +* Automatic Action to close a task doesn't have the column parameter anymore (use the action "Close a task in a specific column") +* Action name stored in the database is now the absolute class name +* Core functionalities moved to external plugins: + - Github Webhook: https://github.com/kanboard/plugin-github-webhook + - Gitlab Webhook: https://github.com/kanboard/plugin-gitlab-webhook + - Bitbucket Webhook: https://github.com/kanboard/plugin-bitbucket-webhook + +New features: + +* Added support of user mentions (@username) +* Added report to compare working hours between open and closed tasks +* Added the possibility to define custom routes from plugins +* Added new method to remove metadata + +Improvements: + +* Improve Two-Factor activation and plugin API +* Improving performance during task position change (SQL queries are 3 times faster than before) +* Do not show window scrollbars when individual column scrolling is enabled +* Automatic Actions code improvements and unit tests +* Increase action name column length in actions table + +Bug fixes: + +* Fix compatibility issue with FreeBSD for session.hash_function parameter +* Fix wrong constant name that causes a PHP error in project management section +* Fix pagination in group members listing +* Avoid PHP error when enabling LDAP group provider with PHP < 5.5 + +Version 1.0.22 (December 13, 2015) +---------------------------------- + +Breaking changes: + +* LDAP configuration parameters changes (See documentation) +* SQL table changes: + - "users" table: added new column "role" and removed columns "is_admin" and "is_project_admin" + - "project_has_users" table: replaced column "is_owner" with column "role" + - Sqlite does not support alter table, old columns still there but unused +* API procedure changes: + - createUser + - createLdapUser + - updateUser + - updateTask +* Event removed: "session.bootstrap", use "app.boostrap" instead + +New features: + +* Add pluggable authentication and authorization system (complete rewrite) +* Add groups (teams/organization) +* Add LDAP groups synchronization +* Add project group permissions +* Add new project role Viewer +* Add generic LDAP client library +* Add search query attribute for task link +* Add the possibility to define API token in config file +* Add capability to reopen Gitlab issues +* Try to load config.php from /data if not available + +Version 1.0.21 (November 22, 2015) +---------------------------------- + +Breaking changes: + +* Projects with duplicate names are now allowed: + - For Postgres and Mysql the unique constraint is removed by database migration + - However Sqlite does not support alter table, only new databases will have the unique constraint removed + +New features: + +* New automatic action: Assign a category based on a link +* Added Bosnian translation + +Improvements: + +* Dropdown menu entries are now clickable outside of the html link +* Improve error handling of plugins +* Use PHP7 function random_bytes() to generate tokens if available +* CSV task export show the assignee name in addition to the assignee username +* Add new hooks for plugins +* Remove workaround for "INSERT ON DUPLICATE KEY UPDATE..." + +Internal code refactoring: + +* Rewrite of session management +* Move some classes to a new namespace Kanboard\Core\Http + +Bug fixes: + +* Loading cs_CZ locale display the wrong language in datetime picker +* Datepicker is closed unexpectedly on blur event +* Fix bug in daily project summary CSV export +* Fix PHP error when adding a new user with email notification enabled +* Add missing template for activity stream to show event "file.create" +* Fix wrong value for PLUGINS_DIR in config.default.php +* Make CSV export compatible with PHP 5.3 +* Avoid Safari to append .html at the end of downloaded files + +Version 1.0.20 (October 24, 2015) +--------------------------------- + +Breaking changes: + +* Add namespace Kanboard (update your plugins) +* Move Mailgun, Sendgrid, Postmark, Slack, Hipchat and Jabber to plugins +* ReverseProxy authentication check for each request that the username match the user session + +New features: + +* Add CSV import for users and tasks +* Add Task, User and Project metadata for plugin creators + +Improvements: + +* Allow to change comments sorting +* Add the possibility to append or not custom filters +* Make mail transports pluggable +* Do not show scroll-bars when a column is collapsed on Windows systems +* Regenerate thumbnails if missing + +Bug fixes: + +* People should not see any tasks during a search when they are not associated to a project +* Avoid disabling the default swimlane during renaming when there is no other activated swimlane + +Version 1.0.19 (October 11, 2015) +--------------------------------- + +New features: + +* Added web notifications +* Added LDAP group sync +* Added swimlane description +* New plugin system (alpha) +* Added Bahasa Indonesia translation +* Added API procedures: getMyOverdueTasks, getOverdueTasksByProject and GetMyProjects +* Added user API access for procedure getProjectActivity() +* Added config parameter to enable/disable Syslog +* Added custom filters +* Added http client proxy support + +Core functionalities moved to plugins: + +* Budget planning: https://github.com/kanboard/plugin-budget +* SubtaskForecast: https://github.com/kanboard/plugin-subtask-forecast +* Timetable: https://github.com/kanboard/plugin-timetable + +Improvements: + +* When duplicating a task redirect to the new task +* Include more shortcut links into the view "My projects" +* Duplicate a project with tasks will copy the new tasks in the same columns +* Offer alternative method to create Mysql and Postgres databases (import sql dump) +* Make sure there is always a trailing slash for application_url +* Do not show the checkbox "Show default swimlane" when there is no active swimlanes +* Append filters instead of replacing value for users and categories drop-downs +* Do not show empty swimlanes in public view +* Change swimlane layout to save space on the screen +* Add the possibility to set/unset max column height (column scrolling) +* Show "Open this task" in drop-down menu for closed tasks +* Show assignee on card only when someone is assigned (hide nobody text) +* Highlight selected item in drop-down menus +* Gantt chart: change bar color according to task progress +* Replace color drop-down by color picker in task forms +* Creating another task stay in the popover (no full page refresh anymore) +* Avoid scrollbar in Gantt chart for row title on Windows platform +* Remove unnecessary margin for calendar header +* Show localized documentation if available +* Add event subtask.delete +* Add abstract storage layer +* Add abstract cache layer +* Add Docker tag for stable version + +Others: + +* Data directory permission are not checked anymore +* Data directory is not mandatory anymore for people that use a remote database and remote object storage + +Bug fixes: + +* Fix typo in template that prevents Gitlab OAuth link to be displayed +* Fix Markdown preview links focus +* Avoid drop-down menu to be truncated inside a column with scrolling +* Deleting subtask doesn't update task time tracking +* Fix Mysql error about gitlab_id when creating remote user +* Fix subtask timer bug (event called recursively) +* Fix Postgres issue "Cardinality violation" when there is multiple "is_milestone_of" links +* Fix issue with due date greater than year 2038 + +Version 1.0.18 (August 30, 2015) +-------------------------------- + +New features: + +* Include documentation in the application +* Add Gitlab authentication +* Add users and categories filters on the board +* Add hide/show columns +* Add Gantt chart for projects and tasks +* Add new role "Project Administrator" +* Add login brute force protection with captcha and account lockdown +* Add new api procedures: getDefaultTaskColor(), getDefaultTaskColors() and getColorList() +* Add user api access +* Add config parameter to define session duration +* Add config parameter to disable/enable RememberMe authentication +* Add start/end date for projects +* Add new automated action to change task color based on the task link +* Add milestone marker in board task +* Add search for task title when using an integer only input +* Add Portuguese (European) translation +* Add Norwegian translation + +Improvements: + +* Add handle to move tasks on touch devices +* Improve file attachments tooltip on the board +* Adjust automatically the height of the placeholder during drag and drop +* Show all tasks when using no search criteria +* Add column vertical scrolling +* Set dynamically column height based on viewport size +* Enable support for Github Enterprise when using Github Authentication +* Update iCalendar library to display organizer name +* Improve sidebar menus +* Add no referrer policy in meta tags +* Run automated unit tests with Sqlite/Mysql/Postgres on Travis-ci +* Add Makefile and remove the "scripts" directory + +Bug fixes: + +* Wrong template name for subtasks tooltip due to previous refactoring +* Fix broken url for closed tasks in project view +* Fix permission issue when changing the url manually +* Fix bug task estimate is reset when using subtask timer +* Fix screenshot feature with Firefox 40 +* Fix bug when uploading files with Cyrilic characters + +Version 1.0.17 (July 27, 2015) +------------------------------ + +New features: + +* Added url rewrite and new routes +* Added new search engine with advanced syntax +* Added global search section +* Added search form on the dashboard +* Added new dashboard layout +* Added new layout for board/calendar/list views +* Added filters helper for search forms +* Added setting option to disable subtask timer +* Added setting option to include or exclude closed tasks into CFD +* Added setting option to define the default task color +* Added new config option to disable automatic creation of LDAP accounts +* Added loading icon on board view +* Prompt user when moving or duplicate a task to another project +* Added current values when moving/duplicate a task to another project and add a loading icon +* Added memory consumption to debug log +* Added form to create remote user +* Added edit form for user authentication +* Added config option to hide login form +* Display OAuth2 urls on integration page +* Added keyboard shortcuts to switch between board/calendar/list view +* Added keyboard shortcut to focus on the search box +* Added Slack channel override +* Added new report: Lead and cycle time for projects +* Added new report: Average time spent into each column +* Added task analytics +* Added icon to set the start date automatically +* Added datetime picker for start date + +Improvements: + +* Updated documentation +* Display user initials when tasks are in collapsed mode +* Show title in tooltip for collapsed tasks +* Improve alert box fadeout to avoid an empty space +* Set focus on the drop-down for category popover +* Make escape keyboard shortcut global +* Check the box remember me by default +* Store redirect login url in session instead of using url parameter +* Update Gitlab webhook +* Do not rewrite remember me cookie for each request +* Set the assignee as organizer for ical events +* Increase date range for ics export +* Reduce spacing on cards +* Move board collapse/expand mode to server side to avoid board flickering +* Use ajax requests for board collapse/expand +* Do not set anchor for the default swimlane on the link back to board +* Replace timeserie axis to category axis for charts +* Hide task age in compact mode +* Improve quick-add subtasks form +* Reduce the size of the filter box for smaller screen +* Added icon to hide/show sidebar +* Update GitLab logo +* Improve Dockerfile + +Translations: + +* Added Czech translation +* Updated Spanish translation +* Updated German Translation + +Bug fixes: + +* Screenshot drop-down: unexpected scroll down on the board view and focus lost when clicking on the drop zone +* No creator when duplicating a task +* Avoid the creation of multiple subtask timer for the same task and user + +Code refactoring: + +* Split task controller into smaller classes +* Remove method Category::getBoardCategories() +* Rewrite movePosition() to improve performances +* Refactoring of Github and Google authentication + +Breaking changes: + +* New OAuth url for Google and Github authentication + +API: + +* Add urls in api response for tasks and projects + +Other: + +* Added automated Docker build +* Remove edit recurrence from the task menu on the board +* Switch to MIT License instead of AGPLv3 + +Version 1.0.0 to 1.0.16 +----------------------- + +* See commit history and website news diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..821d033 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Frédéric Guillot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/app/.htaccess b/app/.htaccess new file mode 100644 index 0000000..c47998c --- /dev/null +++ b/app/.htaccess @@ -0,0 +1,7 @@ += 2.3> + Require all denied + + + Order allow,deny + Deny from all + diff --git a/app/Action/Base.php b/app/Action/Base.php new file mode 100644 index 0000000..78b8c20 --- /dev/null +++ b/app/Action/Base.php @@ -0,0 +1,305 @@ +params as $key => $value) { + $params[] = $key.'='.var_export($value, true); + } + + return $this->getName().'('.implode('|', $params).')'; + } + + /** + * Set project id + * + * @access public + * @param integer $project_id + * @return Base + */ + public function setProjectId($project_id) + { + $this->projectId = $project_id; + return $this; + } + + /** + * Get project id + * + * @access public + * @return integer + */ + public function getProjectId() + { + return $this->projectId; + } + + /** + * Set an user defined parameter + * + * @access public + * @param string $name Parameter name + * @param mixed $value Value + * @return Base + */ + public function setParam($name, $value) + { + $this->params[$name] = $value; + return $this; + } + + /** + * Get an user defined parameter + * + * @access public + * @param string $name Parameter name + * @param mixed $default Default value + * @return mixed + */ + public function getParam($name, $default = null) + { + return isset($this->params[$name]) ? $this->params[$name] : $default; + } + + /** + * Check if an action is executable (right project and required parameters) + * + * @access public + * @param array $data + * @param string $eventName + * @return bool + */ + public function isExecutable(array $data, $eventName) + { + return $this->hasCompatibleEvent($eventName) && + $this->hasRequiredProject($data) && + $this->hasRequiredParameters($data) && + $this->hasRequiredCondition($data); + } + + /** + * Check if the event is compatible with the action + * + * @access public + * @param string $eventName + * @return bool + */ + public function hasCompatibleEvent($eventName) + { + return in_array($eventName, $this->getEvents()); + } + + /** + * Check if the event data has the required project + * + * @access public + * @param array $data Event data dictionary + * @return bool + */ + public function hasRequiredProject(array $data) + { + return (isset($data['project_id']) && $data['project_id'] == $this->getProjectId()) || + (isset($data['task']['project_id']) && $data['task']['project_id'] == $this->getProjectId()); + } + + /** + * Check if the event data has required parameters to execute the action + * + * @access public + * @param array $data Event data dictionary + * @return bool True if all keys are there + */ + public function hasRequiredParameters(array $data, array $parameters = array()) + { + $parameters = $parameters ?: $this->getEventRequiredParameters(); + + foreach ($parameters as $key => $value) { + if (is_array($value)) { + return isset($data[$key]) && $this->hasRequiredParameters($data[$key], $value); + } elseif (! isset($data[$value])) { + return false; + } + } + + return true; + } + + /** + * Execute the action + * + * @access public + * @param \Kanboard\Event\GenericEvent $event + * @param string $eventName + * @return bool + */ + public function execute(GenericEvent $event, $eventName) + { + $data = $event->getAll(); + $hash = md5(serialize($data).$eventName); + + // Do not call twice the same action with the same arguments. + if (isset($this->callStack[$hash])) { + return false; + } else { + $this->callStack[$hash] = true; + } + + $executable = $this->isExecutable($data, $eventName); + $executed = false; + + if ($executable) { + $executed = $this->doAction($data); + } + + $this->logger->debug($this.' ['.$eventName.'] => executable='.var_export($executable, true).' exec_success='.var_export($executed, true)); + + return $executed; + } + + /** + * Register a new event for the automatic action + * + * @access public + * @param string $event + * @param string $description + * @return Base + */ + public function addEvent($event, $description = '') + { + if ($description !== '') { + $this->eventManager->register($event, $description); + } + + $this->compatibleEvents[] = $event; + return $this; + } + + /** + * Get all compatible events of an automatic action + * + * @access public + * @return array + */ + public function getEvents() + { + return array_unique(array_merge($this->getCompatibleEvents(), $this->compatibleEvents)); + } +} diff --git a/app/Action/CommentCreation.php b/app/Action/CommentCreation.php new file mode 100644 index 0000000..301d2cf --- /dev/null +++ b/app/Action/CommentCreation.php @@ -0,0 +1,87 @@ +commentModel->create(array( + 'reference' => isset($data['reference']) ? $data['reference'] : '', + 'comment' => $data['comment'], + 'task_id' => $data['task_id'], + 'user_id' => isset($data['user_id']) && $this->projectPermissionModel->isAssignable($this->getProjectId(), $data['user_id']) ? $data['user_id'] : 0, + )); + } + + /** + * 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 ! empty($data['comment']); + } +} diff --git a/app/Action/CommentCreationMoveTaskColumn.php b/app/Action/CommentCreationMoveTaskColumn.php new file mode 100644 index 0000000..d5bdd80 --- /dev/null +++ b/app/Action/CommentCreationMoveTaskColumn.php @@ -0,0 +1,100 @@ + t('Column')); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'column_id', + 'project_id', + ), + ); + } + + /** + * Execute the action (append to the task description). + * + * @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) + { + if (! $this->userSession->isLogged()) { + return false; + } + + $column = $this->columnModel->getById($data['task']['column_id']); + + return (bool) $this->commentModel->create(array( + 'comment' => t('Moved to column %s', $column['title']), + 'task_id' => $data['task_id'], + 'user_id' => $this->userSession->getId(), + )); + } + + /** + * 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('column_id'); + } +} diff --git a/app/Action/StopSubtaskTimerMoveTaskColumn.php b/app/Action/StopSubtaskTimerMoveTaskColumn.php new file mode 100644 index 0000000..e6f9e43 --- /dev/null +++ b/app/Action/StopSubtaskTimerMoveTaskColumn.php @@ -0,0 +1,102 @@ + t('Column'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'id', + 'column_id', + 'project_id', + ), + ); + } + + /** + * Execute the action (append to the task description). + * + * @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) + { + $subtasks = $this->subtaskModel->getAll($data['task']['id']); + $results = array(); + + foreach ($subtasks as $subtask) { + $results[] = $this->subtaskModel->update(array('id' => $subtask['id'], 'status' => SubtaskModel::STATUS_DONE)); + $results[] = $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $subtask['user_id']); + } + + return !in_array(false, $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 $data['task']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/SubtaskTimerMoveTaskColumn.php b/app/Action/SubtaskTimerMoveTaskColumn.php new file mode 100644 index 0000000..896f24b --- /dev/null +++ b/app/Action/SubtaskTimerMoveTaskColumn.php @@ -0,0 +1,107 @@ + t('Column'), + 'subtask' => t('Subtask Title'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'id', + 'column_id', + 'project_id', + 'creator_id', + ), + ); + } + + /** + * Execute the action (append to the task description). + * + * @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) + { + $subtaskID = $this->subtaskModel->create(array( + 'title' => $this->getParam('subtask'), + 'user_id' => $data['task']['creator_id'], + 'task_id' => $data['task']['id'], + 'status' => SubtaskModel::STATUS_INPROGRESS, + )); + + if ($subtaskID !== false) { + return $this->subtaskTimeTrackingModel->toggleTimer($subtaskID, $data['task']['creator_id'], SubtaskModel::STATUS_INPROGRESS); + } + + return 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('column_id'); + } +} diff --git a/app/Action/TaskAssignCategoryColor.php b/app/Action/TaskAssignCategoryColor.php new file mode 100644 index 0000000..9228e1f --- /dev/null +++ b/app/Action/TaskAssignCategoryColor.php @@ -0,0 +1,98 @@ + t('Color'), + 'category_id' => t('Category'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'color_id', + ), + ); + } + + /** + * Execute the action (change the category) + * + * @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'], + 'category_id' => $this->getParam('category_id'), + ); + + 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']['color_id'] == $this->getParam('color_id'); + } +} diff --git a/app/Action/TaskAssignCategoryLabel.php b/app/Action/TaskAssignCategoryLabel.php new file mode 100644 index 0000000..c390414 --- /dev/null +++ b/app/Action/TaskAssignCategoryLabel.php @@ -0,0 +1,91 @@ + t('Label'), + 'category_id' => t('Category'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'label', + ); + } + + /** + * Execute the action (change the category) + * + * @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'], + 'category_id' => $this->getParam('category_id'), + ); + + 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['label'] == $this->getParam('label') && empty($data['category_id']); + } +} diff --git a/app/Action/TaskAssignCategoryLink.php b/app/Action/TaskAssignCategoryLink.php new file mode 100644 index 0000000..6c4b6c9 --- /dev/null +++ b/app/Action/TaskAssignCategoryLink.php @@ -0,0 +1,102 @@ + t('Category'), + 'link_id' => t('Link type'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_link' => array( + 'task_id', + 'link_id', + ) + ); + } + + /** + * Execute the action (change the category) + * + * @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_link']['task_id'], + 'category_id' => $this->getParam('category_id'), + ); + + 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) + { + if ($data['task_link']['link_id'] == $this->getParam('link_id')) { + return empty($data['task']['category_id']); + } + + return false; + } +} diff --git a/app/Action/TaskAssignCategorySwimlaneChange.php b/app/Action/TaskAssignCategorySwimlaneChange.php new file mode 100644 index 0000000..3299858 --- /dev/null +++ b/app/Action/TaskAssignCategorySwimlaneChange.php @@ -0,0 +1,99 @@ + t('Swimlane'), + 'category_id' => t('Category'), + ); + } + + /** + * 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 category) + * + * @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'], + 'category_id' => $this->getParam('category_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/TaskAssignColorCategory.php b/app/Action/TaskAssignColorCategory.php new file mode 100644 index 0000000..a136ffd --- /dev/null +++ b/app/Action/TaskAssignColorCategory.php @@ -0,0 +1,98 @@ + t('Color'), + 'category_id' => t('Category'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'category_id', + ), + ); + } + + /** + * Execute the action (change 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']['category_id'] == $this->getParam('category_id'); + } +} diff --git a/app/Action/TaskAssignColorColumn.php b/app/Action/TaskAssignColorColumn.php new file mode 100644 index 0000000..da6e3ae --- /dev/null +++ b/app/Action/TaskAssignColorColumn.php @@ -0,0 +1,99 @@ + t('Column'), + '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', + 'column_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']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/TaskAssignColorLink.php b/app/Action/TaskAssignColorLink.php new file mode 100644 index 0000000..19c37af --- /dev/null +++ b/app/Action/TaskAssignColorLink.php @@ -0,0 +1,97 @@ + t('Color'), + 'link_id' => t('Link type'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_link' => array( + 'task_id', + 'link_id', + ) + ); + } + + /** + * Execute the action (change 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_link']['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_link']['link_id'] == $this->getParam('link_id'); + } +} diff --git a/app/Action/TaskAssignColorOnDueDate.php b/app/Action/TaskAssignColorOnDueDate.php new file mode 100644 index 0000000..dfec317 --- /dev/null +++ b/app/Action/TaskAssignColorOnDueDate.php @@ -0,0 +1,99 @@ + t('Color'), + ); + } + + /** + * Get all tasks + * + * @access public + * @return array + */ + + public function getEventRequiredParameters() + { + return array('tasks'); + } + + /** + * Execute the action (change 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) + { + $results = array(); + + foreach ($data['tasks'] as $task) { + if ($task['date_due'] <= time() && $task['date_due'] > 0 && $task['color_id'] != $this->getParam('color_id')) { + $values = array( + 'id' => $task['id'], + 'color_id' => $this->getParam('color_id'), + ); + $results[] = $this->taskModificationModel->update($values, 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/TaskAssignColorOnStartDate.php b/app/Action/TaskAssignColorOnStartDate.php new file mode 100644 index 0000000..364c283 --- /dev/null +++ b/app/Action/TaskAssignColorOnStartDate.php @@ -0,0 +1,99 @@ + t('Color'), + ); + } + + /** + * Get all tasks + * + * @access public + * @return array + */ + + public function getEventRequiredParameters() + { + return array('tasks'); + } + + /** + * Execute the action (change 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) + { + $results = array(); + + foreach ($data['tasks'] as $task) { + if ($task['date_started'] <= time() && $task['date_started'] > 0 && $task['color_id'] != $this->getParam('color_id')) { + $values = array( + 'id' => $task['id'], + 'color_id' => $this->getParam('color_id'), + ); + $results[] = $this->taskModificationModel->update($values, 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/TaskAssignColorPriority.php b/app/Action/TaskAssignColorPriority.php new file mode 100644 index 0000000..37f7ffe --- /dev/null +++ b/app/Action/TaskAssignColorPriority.php @@ -0,0 +1,98 @@ + t('Color'), + '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', + 'priority', + ), + ); + } + + /** + * Execute the action (change 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']['priority'] == $this->getParam('priority'); + } +} diff --git a/app/Action/TaskAssignColorSwimlane.php b/app/Action/TaskAssignColorSwimlane.php new file mode 100644 index 0000000..31f2d25 --- /dev/null +++ b/app/Action/TaskAssignColorSwimlane.php @@ -0,0 +1,99 @@ + 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 new file mode 100644 index 0000000..468d019 --- /dev/null +++ b/app/Action/TaskAssignColorUser.php @@ -0,0 +1,99 @@ + t('Color'), + 'user_id' => t('Assignee'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'owner_id', + ), + ); + } + + /** + * Execute the action (change 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']['owner_id'] == $this->getParam('user_id'); + } +} diff --git a/app/Action/TaskAssignCreator.php b/app/Action/TaskAssignCreator.php new file mode 100644 index 0000000..ce10b35 --- /dev/null +++ b/app/Action/TaskAssignCreator.php @@ -0,0 +1,98 @@ + t('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', + 'creator_id', + ), + ); + } + + /** + * Execute the action + * + * @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'], + 'owner_id' => $data['task']['creator_id'], + ); + + 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']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/TaskAssignCurrentUser.php b/app/Action/TaskAssignCurrentUser.php new file mode 100644 index 0000000..dee5e7d --- /dev/null +++ b/app/Action/TaskAssignCurrentUser.php @@ -0,0 +1,95 @@ +userSession->isLogged()) { + return false; + } + + $values = array( + 'id' => $data['task_id'], + 'owner_id' => $this->userSession->getId(), + ); + + 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 true; + } +} diff --git a/app/Action/TaskAssignCurrentUserColumn.php b/app/Action/TaskAssignCurrentUserColumn.php new file mode 100644 index 0000000..60ada7e --- /dev/null +++ b/app/Action/TaskAssignCurrentUserColumn.php @@ -0,0 +1,101 @@ + t('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', + ), + ); + } + + /** + * Execute the action + * + * @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) + { + if (! $this->userSession->isLogged()) { + return false; + } + + $values = array( + 'id' => $data['task_id'], + 'owner_id' => $this->userSession->getId(), + ); + + 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']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/TaskAssignCurrentUserColumnIfNoUserAlreadySet.php b/app/Action/TaskAssignCurrentUserColumnIfNoUserAlreadySet.php new file mode 100644 index 0000000..23d9e1c --- /dev/null +++ b/app/Action/TaskAssignCurrentUserColumnIfNoUserAlreadySet.php @@ -0,0 +1,103 @@ + t('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', + 'owner_id', + ), + ); + } + + /** + * Execute the action + * + * @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) + { + if (!$this->userSession->isLogged()) { + return false; + } + + if (!$data['task']['owner_id']) { + $values = array( + 'id' => $data['task_id'], + 'owner_id' => $this->userSession->getId(), + ); + return $this->taskModificationModel->update($values); + } + return 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('column_id'); + } +} diff --git a/app/Action/TaskAssignDueDateOnCreation.php b/app/Action/TaskAssignDueDateOnCreation.php new file mode 100644 index 0000000..5c6e2b6 --- /dev/null +++ b/app/Action/TaskAssignDueDateOnCreation.php @@ -0,0 +1,96 @@ + 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/TaskAssignDueDateOnMoveColumn.php b/app/Action/TaskAssignDueDateOnMoveColumn.php new file mode 100644 index 0000000..103be27 --- /dev/null +++ b/app/Action/TaskAssignDueDateOnMoveColumn.php @@ -0,0 +1,97 @@ + t('Duration in days'), + 'column_id' => t('Column'), + ); + } + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + ), + 'src_column_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 !empty($data['src_column_id']) && $data['src_column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/TaskAssignPrioritySwimlane.php b/app/Action/TaskAssignPrioritySwimlane.php new file mode 100644 index 0000000..7eaca7c --- /dev/null +++ b/app/Action/TaskAssignPrioritySwimlane.php @@ -0,0 +1,99 @@ + 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 new file mode 100644 index 0000000..daf9e1d --- /dev/null +++ b/app/Action/TaskAssignSpecificUser.php @@ -0,0 +1,99 @@ + t('Column'), + 'user_id' => t('Assignee'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + ), + ); + } + + /** + * Execute the action (assign the given user) + * + * @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'], + 'owner_id' => $this->getParam('user_id'), + ); + + 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']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/TaskAssignToUserOnCreationInColumn.php b/app/Action/TaskAssignToUserOnCreationInColumn.php new file mode 100644 index 0000000..6501b5a --- /dev/null +++ b/app/Action/TaskAssignToUserOnCreationInColumn.php @@ -0,0 +1,109 @@ + t('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', + 'creator_id', + ), + ); + } + + /** + * Execute the action + * + * @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) + { + + $assignee_id = $this->userModel->getIdByUsername($data['task']['assignee_username']); + + if ($data['task']['assignee_username']) { + $values = array( + 'id' => $data['task_id'], + 'owner_id' => $assignee_id, + ); + return $this->taskModificationModel->update($values); + } + + $values = array( + 'id' => $data['task_id'], + 'owner_id' => $data['task']['creator_id'], + ); + + 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']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/TaskAssignUser.php b/app/Action/TaskAssignUser.php new file mode 100644 index 0000000..8727b67 --- /dev/null +++ b/app/Action/TaskAssignUser.php @@ -0,0 +1,88 @@ + $data['task_id'], + 'owner_id' => $data['owner_id'], + ); + + 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 $this->projectPermissionModel->isAssignable($this->getProjectId(), $data['owner_id']); + } +} diff --git a/app/Action/TaskAssignUserSwimlaneChange.php b/app/Action/TaskAssignUserSwimlaneChange.php new file mode 100644 index 0000000..3f97b20 --- /dev/null +++ b/app/Action/TaskAssignUserSwimlaneChange.php @@ -0,0 +1,99 @@ + t('Swimlane'), + 'user_id' => t('Assignee'), + ); + } + + /** + * 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 category) + * + * @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'], + 'owner_id' => $this->getParam('user_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/TaskClose.php b/app/Action/TaskClose.php new file mode 100644 index 0000000..e476e9b --- /dev/null +++ b/app/Action/TaskClose.php @@ -0,0 +1,80 @@ +taskStatusModel->close($data['task_id']); + } + + /** + * 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/TaskCloseColumn.php b/app/Action/TaskCloseColumn.php new file mode 100644 index 0000000..523996f --- /dev/null +++ b/app/Action/TaskCloseColumn.php @@ -0,0 +1,90 @@ + t('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', + ) + ); + } + + /** + * 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) + { + return $this->taskStatusModel->close($data['task_id']); + } + + /** + * 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('column_id'); + } +} diff --git a/app/Action/TaskCloseNoActivity.php b/app/Action/TaskCloseNoActivity.php new file mode 100644 index 0000000..b1f75a8 --- /dev/null +++ b/app/Action/TaskCloseNoActivity.php @@ -0,0 +1,95 @@ + t('Duration in days') + ); + } + + /** + * 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 = (int)$this->getParam('duration') * 86400; + + foreach ($data['tasks'] as $task) { + $duration = time() - $task['date_modification']; + + if ($duration > $max) { + $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/TaskCloseNoActivityColumn.php b/app/Action/TaskCloseNoActivityColumn.php new file mode 100644 index 0000000..10c9fc0 --- /dev/null +++ b/app/Action/TaskCloseNoActivityColumn.php @@ -0,0 +1,96 @@ + 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 = (int)$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 0000000..dd002dd --- /dev/null +++ b/app/Action/TaskCloseNotMovedColumn.php @@ -0,0 +1,96 @@ + 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 = (int)$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 new file mode 100644 index 0000000..01d9122 --- /dev/null +++ b/app/Action/TaskCreation.php @@ -0,0 +1,89 @@ +taskCreationModel->create(array( + 'project_id' => $data['project_id'], + 'title' => $data['title'], + 'reference' => $data['reference'], + 'description' => isset($data['description']) ? $data['description'] : '', + )); + } + + /** + * 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/TaskDuplicateAnotherProject.php b/app/Action/TaskDuplicateAnotherProject.php new file mode 100644 index 0000000..0ad7713 --- /dev/null +++ b/app/Action/TaskDuplicateAnotherProject.php @@ -0,0 +1,101 @@ + t('Column'), + 'project_id' => t('Project'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + ) + ); + } + + /** + * Execute the action (duplicate the task to another project) + * + * @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) + { + $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 + ); + } + + /** + * 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('column_id') && $data['task']['project_id'] != $this->getParam('project_id'); + } +} diff --git a/app/Action/TaskEmail.php b/app/Action/TaskEmail.php new file mode 100644 index 0000000..13210a7 --- /dev/null +++ b/app/Action/TaskEmail.php @@ -0,0 +1,119 @@ + t('Column'), + 'user_id' => t('User that will receive the email'), + 'subject' => t('Email subject'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + ), + ); + } + + /** + * 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) + { + $user = $this->userModel->getById($this->getParam('user_id')); + $subject = $this->getParam('subject'); + + foreach ($data["task"] as $key => $value) { + if ($value !== null) { + $placeholder = sprintf('{{%s}}', $key); + $subject = str_replace($placeholder, $value, $subject); + } + } + + if (! empty($user['email'])) { + $this->emailClient->send( + $user['email'], + $user['name'] ?: $user['username'], + $subject, + $this->template->render('notification/task_create', array( + 'task' => $data['task'], + )) + ); + + return true; + } + + return 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('column_id'); + } +} diff --git a/app/Action/TaskEmailNoActivity.php b/app/Action/TaskEmailNoActivity.php new file mode 100644 index 0000000..9d59ac8 --- /dev/null +++ b/app/Action/TaskEmailNoActivity.php @@ -0,0 +1,124 @@ + t('User that will receive the email'), + 'subject' => t('Email subject'), + 'duration' => t('Duration in days'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array('tasks'); + } + + /** + * 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; + } + + /** + * 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) + { + $results = array(); + $max = (int)$this->getParam('duration') * 86400; + $user = $this->userModel->getById($this->getParam('user_id')); + + if (! empty($user['email'])) { + foreach ($data['tasks'] as $task) { + $duration = time() - $task['date_modification']; + + if ($duration > $max) { + $results[] = $this->sendEmail($task['id'], $user); + } + } + } + + return in_array(true, $results, true); + } + + /** + * Send email + * + * @access private + * @param integer $task_id + * @param array $user + * @return boolean + */ + private function sendEmail($task_id, array $user) + { + $task = $this->taskFinderModel->getDetails($task_id); + + $this->emailClient->send( + $user['email'], + $user['name'] ?: $user['username'], + $this->getParam('subject'), + $this->template->render('notification/task_create', array('task' => $task)) + ); + + return true; + } +} diff --git a/app/Action/TaskMoveAnotherProject.php b/app/Action/TaskMoveAnotherProject.php new file mode 100644 index 0000000..0fa22b1 --- /dev/null +++ b/app/Action/TaskMoveAnotherProject.php @@ -0,0 +1,94 @@ + t('Column'), + 'project_id' => t('Project'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + ) + ); + } + + /** + * Execute the action (move the task to another project) + * + * @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->taskProjectMoveModel->moveToProject($data['task_id'], $this->getParam('project_id')); + } + + /** + * 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('column_id') && $data['task']['project_id'] != $this->getParam('project_id'); + } +} diff --git a/app/Action/TaskMoveColumnAssigned.php b/app/Action/TaskMoveColumnAssigned.php new file mode 100644 index 0000000..1cfe674 --- /dev/null +++ b/app/Action/TaskMoveColumnAssigned.php @@ -0,0 +1,104 @@ + 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( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + 'owner_id', + 'position', + 'swimlane_id', + ) + ); + } + + /** + * 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'), + $data['task']['position'], + $data['task']['swimlane_id'], + 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('src_column_id') && $data['task']['owner_id'] > 0; + } +} diff --git a/app/Action/TaskMoveColumnCategoryChange.php b/app/Action/TaskMoveColumnCategoryChange.php new file mode 100644 index 0000000..58108ea --- /dev/null +++ b/app/Action/TaskMoveColumnCategoryChange.php @@ -0,0 +1,105 @@ + t('Destination column'), + 'category_id' => t('Category'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + 'category_id', + 'position', + 'swimlane_id', + ) + ); + } + + /** + * 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'), + $data['task']['position'], + $data['task']['swimlane_id'], + 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']['category_id'] == $this->getParam('category_id'); + } +} diff --git a/app/Action/TaskMoveColumnClosed.php b/app/Action/TaskMoveColumnClosed.php new file mode 100644 index 0000000..da86d89 --- /dev/null +++ b/app/Action/TaskMoveColumnClosed.php @@ -0,0 +1,102 @@ + 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'], + true, + 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 0000000..db7617a --- /dev/null +++ b/app/Action/TaskMoveColumnNotMovedPeriod.php @@ -0,0 +1,104 @@ + 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 = (int)$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/TaskMoveColumnOnDueDate.php b/app/Action/TaskMoveColumnOnDueDate.php new file mode 100644 index 0000000..3f91dd0 --- /dev/null +++ b/app/Action/TaskMoveColumnOnDueDate.php @@ -0,0 +1,103 @@ + 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(); + $min = (int)$this->getParam('duration') * 86400; + + foreach ($data['tasks'] as $task) { + $duration = $task['date_due'] - time(); + + if ($task['date_due'] > 0 && $duration < $min && $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'], + true + ); + } + } + + 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/TaskMoveColumnOnStartDate.php b/app/Action/TaskMoveColumnOnStartDate.php new file mode 100644 index 0000000..e4f4dc1 --- /dev/null +++ b/app/Action/TaskMoveColumnOnStartDate.php @@ -0,0 +1,101 @@ + 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(); + + foreach ($data['tasks'] as $task) { + + if ($task['date_started'] <= time() && $task['date_started'] > 0 && $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 new file mode 100644 index 0000000..ab63d62 --- /dev/null +++ b/app/Action/TaskMoveColumnUnAssigned.php @@ -0,0 +1,104 @@ + 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( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + 'owner_id', + 'position', + 'swimlane_id', + ) + ); + } + + /** + * 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'), + $data['task']['position'], + $data['task']['swimlane_id'], + 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('src_column_id') && $data['task']['owner_id'] == 0; + } +} diff --git a/app/Action/TaskMoveSwimlaneAssigned.php b/app/Action/TaskMoveSwimlaneAssigned.php new file mode 100644 index 0000000..f9fafd3 --- /dev/null +++ b/app/Action/TaskMoveSwimlaneAssigned.php @@ -0,0 +1,107 @@ + t('Assignee'), + 'dest_swimlane_id' => t('Destination swimlane'), + + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + 'owner_id', + 'position', + 'swimlane_id', + ) + ); + } + + /** + * 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'], + $data['task']['column_id'], + $data['task']['position'], + $this->getParam('dest_swimlane_id'), + 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 $data['task']['swimlane_id'] != $this->getParam('dest_swimlane_id') && $data['task']['owner_id'] == $this->getParam("user_id"); + } +} diff --git a/app/Action/TaskMoveSwimlaneCategoryChange.php b/app/Action/TaskMoveSwimlaneCategoryChange.php new file mode 100644 index 0000000..4e80dd8 --- /dev/null +++ b/app/Action/TaskMoveSwimlaneCategoryChange.php @@ -0,0 +1,103 @@ + t('Destination swimlane'), + 'category_id' => t('Category'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + 'column_id', + 'category_id', + 'position', + 'swimlane_id', + ) + ); + } + + /** + * Execute the action (move the task to another swimlane) + * + * @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'], + $data['task']['column_id'], + $data['task']['position'], + $this->getParam('dest_swimlane_id'), + 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('dest_swimlane_id') && $data['task']['category_id'] == $this->getParam('category_id'); + } +} diff --git a/app/Action/TaskOpen.php b/app/Action/TaskOpen.php new file mode 100644 index 0000000..4901783 --- /dev/null +++ b/app/Action/TaskOpen.php @@ -0,0 +1,80 @@ +taskStatusModel->open($data['task_id']); + } + + /** + * 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/TaskUpdateStartDate.php b/app/Action/TaskUpdateStartDate.php new file mode 100644 index 0000000..160f6ee --- /dev/null +++ b/app/Action/TaskUpdateStartDate.php @@ -0,0 +1,97 @@ + t('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', + ), + ); + } + + /** + * 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_started' => time(), + ); + + 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']['column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Action/TaskUpdateStartDateOnMoveColumn.php b/app/Action/TaskUpdateStartDateOnMoveColumn.php new file mode 100644 index 0000000..2255e83 --- /dev/null +++ b/app/Action/TaskUpdateStartDateOnMoveColumn.php @@ -0,0 +1,96 @@ + t('Column'), + ); + } + + /** + * Get the required parameter for the event + * + * @access public + * @return string[] + */ + public function getEventRequiredParameters() + { + return array( + 'task_id', + 'task' => array( + 'project_id', + ), + 'src_column_id', + ); + } + + /** + * Execute the action (set the task date_started) + * + * @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_started' => time(), + ); + + 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 empty($data['task']['date_started']) && $data['src_column_id'] == $this->getParam('column_id'); + } +} diff --git a/app/Analytic/AverageLeadCycleTimeAnalytic.php b/app/Analytic/AverageLeadCycleTimeAnalytic.php new file mode 100644 index 0000000..d6cd1f8 --- /dev/null +++ b/app/Analytic/AverageLeadCycleTimeAnalytic.php @@ -0,0 +1,116 @@ + 0, + 'total_lead_time' => 0, + 'total_cycle_time' => 0, + 'avg_lead_time' => 0, + 'avg_cycle_time' => 0, + ); + + $tasks = $this->getTasks($project_id); + + foreach ($tasks as &$task) { + $stats['count']++; + $stats['total_lead_time'] += $this->calculateLeadTime($task); + $stats['total_cycle_time'] += $this->calculateCycleTime($task); + } + + $stats['avg_lead_time'] = $this->calculateAverage($stats, 'total_lead_time'); + $stats['avg_cycle_time'] = $this->calculateAverage($stats, 'total_cycle_time'); + + return $stats; + } + + /** + * Calculate average + * + * @access private + * @param array &$stats + * @param string $field + * @return float + */ + private function calculateAverage(array &$stats, $field) + { + if ($stats['count'] > 0) { + return (int) ($stats[$field] / $stats['count']); + } + + return 0; + } + + /** + * Calculate lead time + * + * @access private + * @param array &$task + * @return integer + */ + private function calculateLeadTime(array &$task) + { + $end = $task['date_completed'] ?: time(); + $start = $task['date_creation']; + + return $end - $start; + } + + /** + * Calculate cycle time + * + * @access private + * @param array &$task + * @return integer + */ + private function calculateCycleTime(array &$task) + { + $end = (int) $task['date_completed'] ?: time(); + $start = (int) $task['date_started']; + + // Start date can be in the future when defined with the Gantt chart + if ($start > 0 && $end > $start) { + return $end - $start; + } + + return 0; + } + + /** + * Get the 1000 last created tasks + * + * @access private + * @param integer $project_id + * @return array + */ + private function getTasks($project_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->columns('date_completed', 'date_creation', 'date_started') + ->eq('project_id', $project_id) + ->desc('id') + ->limit(1000) + ->findAll(); + } +} diff --git a/app/Analytic/AverageTimeSpentColumnAnalytic.php b/app/Analytic/AverageTimeSpentColumnAnalytic.php new file mode 100644 index 0000000..3556fb9 --- /dev/null +++ b/app/Analytic/AverageTimeSpentColumnAnalytic.php @@ -0,0 +1,154 @@ +initialize($project_id); + + $this->processTasks($stats, $project_id); + $this->calculateAverage($stats); + + return $stats; + } + + /** + * Initialize default values for each column + * + * @access private + * @param integer $project_id + * @return array + */ + private function initialize($project_id) + { + $stats = array(); + $columns = $this->columnModel->getList($project_id); + + foreach ($columns as $column_id => $column_title) { + $stats[$column_id] = array( + 'count' => 0, + 'time_spent' => 0, + 'average' => 0, + 'title' => $column_title, + ); + } + + return $stats; + } + + /** + * Calculate time spent for each tasks for each columns + * + * @access private + * @param array $stats + * @param integer $project_id + */ + private function processTasks(array &$stats, $project_id) + { + $tasks = $this->getTasks($project_id); + + foreach ($tasks as &$task) { + foreach ($this->getTaskTimeByColumns($task) as $column_id => $time_spent) { + if (isset($stats[$column_id])) { + $stats[$column_id]['count']++; + $stats[$column_id]['time_spent'] += $time_spent; + } + } + } + } + + /** + * Calculate averages + * + * @access private + * @param array $stats + */ + private function calculateAverage(array &$stats) + { + foreach ($stats as &$column) { + $this->calculateColumnAverage($column); + } + } + + /** + * Calculate column average + * + * @access private + * @param array $column + */ + private function calculateColumnAverage(array &$column) + { + if ($column['count'] > 0) { + $column['average'] = (int) ($column['time_spent'] / $column['count']); + } + } + + /** + * Get time spent for each column for a given task + * + * @access private + * @param array $task + * @return array + */ + private function getTaskTimeByColumns(array &$task) + { + $columns = $this->transitionModel->getTimeSpentByTask($task['id']); + + if (! isset($columns[$task['column_id']])) { + $columns[$task['column_id']] = 0; + } + + $columns[$task['column_id']] += $this->getTaskTimeSpentInCurrentColumn($task); + + return $columns; + } + + /** + * Calculate time spent of a task in the current column + * + * @access private + * @param array $task + * @return integer + */ + private function getTaskTimeSpentInCurrentColumn(array &$task) + { + $end = $task['date_completed'] ?: time(); + return $end - $task['date_moved']; + } + + /** + * Fetch the last 1000 tasks + * + * @access private + * @param integer $project_id + * @return array + */ + private function getTasks($project_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->columns('id', 'date_completed', 'date_moved', 'column_id') + ->eq('project_id', $project_id) + ->desc('id') + ->limit(1000) + ->findAll(); + } +} diff --git a/app/Analytic/EstimatedActualColumnAnalytic.php b/app/Analytic/EstimatedActualColumnAnalytic.php new file mode 100644 index 0000000..a0bc1d1 --- /dev/null +++ b/app/Analytic/EstimatedActualColumnAnalytic.php @@ -0,0 +1,49 @@ +db->table(TaskModel::TABLE) + ->columns('SUM(time_estimated) AS hours_estimated', 'SUM(time_spent) AS hours_spent', 'column_id') + ->eq('project_id', $project_id) + ->groupBy('column_id') + ->findAll(); + + $columns = $this->columnModel->getList($project_id); + + $metrics = []; + foreach ($columns as $column_id => $column_title) { + $metrics[$column_id] = array( + 'hours_spent' => 0, + 'hours_estimated' => 0, + 'title' => $column_title, + ); + } + + foreach ($rows as $row) { + $metrics[$row['column_id']]['hours_spent'] = (float) $row['hours_spent']; + $metrics[$row['column_id']]['hours_estimated'] = (float) $row['hours_estimated']; + } + + return $metrics; + } +} diff --git a/app/Analytic/EstimatedTimeComparisonAnalytic.php b/app/Analytic/EstimatedTimeComparisonAnalytic.php new file mode 100644 index 0000000..020478f --- /dev/null +++ b/app/Analytic/EstimatedTimeComparisonAnalytic.php @@ -0,0 +1,50 @@ +db->table(TaskModel::TABLE) + ->columns('SUM(time_estimated) AS time_estimated', 'SUM(time_spent) AS time_spent', 'is_active') + ->eq('project_id', $project_id) + ->groupBy('is_active') + ->findAll(); + + $metrics = array( + 'open' => array( + 'time_spent' => 0, + 'time_estimated' => 0, + ), + 'closed' => array( + 'time_spent' => 0, + 'time_estimated' => 0, + ), + ); + + foreach ($rows as $row) { + $key = $row['is_active'] == TaskModel::STATUS_OPEN ? 'open' : 'closed'; + $metrics[$key]['time_spent'] = (float) $row['time_spent']; + $metrics[$key]['time_estimated'] = (float) $row['time_estimated']; + } + + return $metrics; + } +} diff --git a/app/Analytic/TaskDistributionAnalytic.php b/app/Analytic/TaskDistributionAnalytic.php new file mode 100644 index 0000000..447d88a --- /dev/null +++ b/app/Analytic/TaskDistributionAnalytic.php @@ -0,0 +1,48 @@ +columnModel->getAll($project_id); + + foreach ($columns as $column) { + $nb_tasks = $this->taskFinderModel->countByColumnId($project_id, $column['id']); + $total += $nb_tasks; + + $metrics[] = array( + 'column_title' => $column['title'], + 'nb_tasks' => $nb_tasks, + ); + } + + if ($total === 0) { + return array(); + } + + foreach ($metrics as &$metric) { + $metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2); + } + + return $metrics; + } +} diff --git a/app/Analytic/UserDistributionAnalytic.php b/app/Analytic/UserDistributionAnalytic.php new file mode 100644 index 0000000..421d424 --- /dev/null +++ b/app/Analytic/UserDistributionAnalytic.php @@ -0,0 +1,56 @@ +taskFinderModel->getAll($project_id); + $users = $this->projectUserRoleModel->getAssignableUsersList($project_id); + + foreach ($tasks as $task) { + $user = isset($users[$task['owner_id']]) ? $users[$task['owner_id']] : $users[0]; + $total++; + + if (! isset($metrics[$user])) { + $metrics[$user] = array( + 'nb_tasks' => 0, + 'percentage' => 0, + 'user' => $user, + ); + } + + $metrics[$user]['nb_tasks']++; + } + + if ($total === 0) { + return array(); + } + + foreach ($metrics as &$metric) { + $metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2); + } + + ksort($metrics); + + return array_values($metrics); + } +} diff --git a/app/Api/Authorization/ActionAuthorization.php b/app/Api/Authorization/ActionAuthorization.php new file mode 100644 index 0000000..4b41ad8 --- /dev/null +++ b/app/Api/Authorization/ActionAuthorization.php @@ -0,0 +1,19 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->actionModel->getProjectId($action_id)); + } + } +} diff --git a/app/Api/Authorization/CategoryAuthorization.php b/app/Api/Authorization/CategoryAuthorization.php new file mode 100644 index 0000000..f17265a --- /dev/null +++ b/app/Api/Authorization/CategoryAuthorization.php @@ -0,0 +1,19 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->categoryModel->getProjectId($category_id)); + } + } +} diff --git a/app/Api/Authorization/ColumnAuthorization.php b/app/Api/Authorization/ColumnAuthorization.php new file mode 100644 index 0000000..37aecda --- /dev/null +++ b/app/Api/Authorization/ColumnAuthorization.php @@ -0,0 +1,19 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->columnModel->getProjectId($column_id)); + } + } +} diff --git a/app/Api/Authorization/CommentAuthorization.php b/app/Api/Authorization/CommentAuthorization.php new file mode 100644 index 0000000..56125ac --- /dev/null +++ b/app/Api/Authorization/CommentAuthorization.php @@ -0,0 +1,46 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->commentModel->getProjectId($comment_id)); + $this->checkCommentAccess($comment_id); + } + } + + /** + * @param $comment_id ID of the comment to check + * @return void + * @throws AccessDeniedException + */ + protected function checkCommentAccess($comment_id) + { + if (empty($comment_id)) { + throw new AccessDeniedException('Comment Not Found'); + } + + $commentVisibility = $this->commentModel->getVisibility($comment_id); + $userRole = $this->userSession->getRole(); + + if ($userRole === Role::APP_MANAGER && $commentVisibility === Role::APP_ADMIN) { + throw new AccessDeniedException('Comment Access Denied'); + } + + if ($userRole === Role::APP_USER && $commentVisibility !== Role::APP_USER) { + throw new AccessDeniedException('Comment Access Denied'); + } + } +} diff --git a/app/Api/Authorization/ProcedureAuthorization.php b/app/Api/Authorization/ProcedureAuthorization.php new file mode 100644 index 0000000..070a637 --- /dev/null +++ b/app/Api/Authorization/ProcedureAuthorization.php @@ -0,0 +1,32 @@ +userSession->isLogged() && in_array($procedure, $this->userSpecificProcedures)) { + throw new AccessDeniedException('This procedure is not available with the API credentials'); + } + } +} diff --git a/app/Api/Authorization/ProjectAuthorization.php b/app/Api/Authorization/ProjectAuthorization.php new file mode 100644 index 0000000..7dcdc44 --- /dev/null +++ b/app/Api/Authorization/ProjectAuthorization.php @@ -0,0 +1,35 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $project_id); + } + } + + protected function checkProjectPermission($class, $method, $project_id) + { + if (empty($project_id)) { + throw new AccessDeniedException('Project Not Found'); + } + + $role = $this->projectUserRoleModel->getUserRole($project_id, $this->userSession->getId()); + + if (! $this->apiProjectAuthorization->isAllowed($class, $method, $role)) { + throw new AccessDeniedException('Project Access Denied'); + } + } +} diff --git a/app/Api/Authorization/SubtaskAuthorization.php b/app/Api/Authorization/SubtaskAuthorization.php new file mode 100644 index 0000000..fcb5792 --- /dev/null +++ b/app/Api/Authorization/SubtaskAuthorization.php @@ -0,0 +1,19 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->subtaskModel->getProjectId($subtask_id)); + } + } +} diff --git a/app/Api/Authorization/TagAuthorization.php b/app/Api/Authorization/TagAuthorization.php new file mode 100644 index 0000000..247f57d --- /dev/null +++ b/app/Api/Authorization/TagAuthorization.php @@ -0,0 +1,23 @@ +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 new file mode 100644 index 0000000..6e04421 --- /dev/null +++ b/app/Api/Authorization/TaskAuthorization.php @@ -0,0 +1,19 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($task_id)); + } + } +} diff --git a/app/Api/Authorization/TaskFileAuthorization.php b/app/Api/Authorization/TaskFileAuthorization.php new file mode 100644 index 0000000..e40783e --- /dev/null +++ b/app/Api/Authorization/TaskFileAuthorization.php @@ -0,0 +1,19 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->taskFileModel->getProjectId($file_id)); + } + } +} diff --git a/app/Api/Authorization/TaskLinkAuthorization.php b/app/Api/Authorization/TaskLinkAuthorization.php new file mode 100644 index 0000000..2f5fc8d --- /dev/null +++ b/app/Api/Authorization/TaskLinkAuthorization.php @@ -0,0 +1,19 @@ +userSession->isLogged()) { + $this->checkProjectPermission($class, $method, $this->taskLinkModel->getProjectId($task_link_id)); + } + } +} diff --git a/app/Api/Authorization/UserAuthorization.php b/app/Api/Authorization/UserAuthorization.php new file mode 100644 index 0000000..3fd6865 --- /dev/null +++ b/app/Api/Authorization/UserAuthorization.php @@ -0,0 +1,22 @@ +userSession->isLogged() && ! $this->apiAuthorization->isAllowed($class, $method, $this->userSession->getRole())) { + throw new AccessDeniedException('You are not allowed to access to this resource'); + } + } +} diff --git a/app/Api/Middleware/AuthenticationMiddleware.php b/app/Api/Middleware/AuthenticationMiddleware.php new file mode 100644 index 0000000..42ac208 --- /dev/null +++ b/app/Api/Middleware/AuthenticationMiddleware.php @@ -0,0 +1,101 @@ +dispatcher->dispatch(new Event, 'app.bootstrap'); + session_set('scope', 'API'); + + if ($this->isUserAuthenticated($username, $password)) { + $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'); + } + } + + /** + * Check user credentials + * + * @access public + * @param string $username + * @param string $password + * @return boolean + */ + private function isUserAuthenticated($username, $password) + { + if ($username === 'jsonrpc') { + return false; + } + + if ($this->userLockingModel->isLocked($username)) { + return false; + } + + if ($this->userModel->has2FA($username)) { + $this->logger->info('This API user ('.$username.') as 2FA enabled: only API keys are authorized'); + $this->authenticationManager->reset(); + $this->authenticationManager->register(new ApiAccessTokenAuth($this->container)); + } + + return $this->authenticationManager->passwordAuthentication($username, $password); + } + + /** + * Check administrative credentials + * + * @access public + * @param string $username + * @param string $password + * @return boolean + */ + private function isAppAuthenticated($username, $password) + { + return $username === 'jsonrpc' && hash_equals($this->getApiToken(), $password); + } + + /** + * Get API Token + * + * @access private + * @return string + */ + private function getApiToken() + { + if (defined('API_AUTHENTICATION_TOKEN')) { + return API_AUTHENTICATION_TOKEN; + } + + if (getenv('API_AUTHENTICATION_TOKEN')) { + return getenv('API_AUTHENTICATION_TOKEN'); + } + + return $this->configModel->get('api_token'); + } +} diff --git a/app/Api/Procedure/ActionProcedure.php b/app/Api/Procedure/ActionProcedure.php new file mode 100644 index 0000000..72fb9bb --- /dev/null +++ b/app/Api/Procedure/ActionProcedure.php @@ -0,0 +1,91 @@ +actionManager->getAvailableActions(); + } + + public function getAvailableActionEvents() + { + return (object) $this->eventManager->getAll(); + } + + public function getCompatibleActionEvents($action_name) + { + return (object) $this->actionManager->getCompatibleEvents($action_name); + } + + public function removeAction($action_id) + { + ActionAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAction', $action_id); + return $this->actionModel->remove($action_id); + } + + public function getActions($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActions', $project_id); + return $this->actionModel->getAllByProject($project_id); + } + + public function createAction($project_id, $event_name, $action_name, array $params) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createAction', $project_id); + $values = array( + 'project_id' => $project_id, + 'event_name' => $event_name, + 'action_name' => $action_name, + 'params' => $params, + ); + + list($valid, ) = $this->actionValidator->validateCreation($values); + + if (! $valid) { + return false; + } + + // Check if the action exists + $actions = $this->actionManager->getAvailableActions(); + + if (! isset($actions[$action_name])) { + return false; + } + + // Check the event + $action = $this->actionManager->getAction($action_name); + + if (! in_array($event_name, $action->getEvents())) { + return false; + } + + $required_params = $action->getActionRequiredParameters(); + + // Check missing parameters + foreach ($required_params as $param => $value) { + if (! isset($params[$param])) { + return false; + } + } + + // Check extra parameters + foreach ($params as $param => $value) { + if (! isset($required_params[$param])) { + return false; + } + } + + return $this->actionModel->create($values); + } +} diff --git a/app/Api/Procedure/AppProcedure.php b/app/Api/Procedure/AppProcedure.php new file mode 100644 index 0000000..60af4a6 --- /dev/null +++ b/app/Api/Procedure/AppProcedure.php @@ -0,0 +1,47 @@ +timezoneModel->getCurrentTimezone(); + } + + public function getVersion() + { + return APP_VERSION; + } + + public function getDefaultTaskColor() + { + return $this->colorModel->getDefaultColor(); + } + + public function getDefaultTaskColors() + { + return $this->colorModel->getDefaultColors(); + } + + public function getColorList() + { + return $this->colorModel->getList(); + } + + public function getApplicationRoles() + { + return $this->role->getApplicationRoles(); + } + + public function getProjectRoles() + { + return $this->role->getProjectRoles(); + } +} diff --git a/app/Api/Procedure/BaseProcedure.php b/app/Api/Procedure/BaseProcedure.php new file mode 100644 index 0000000..16ef5e0 --- /dev/null +++ b/app/Api/Procedure/BaseProcedure.php @@ -0,0 +1,40 @@ +container)->check($procedure); + UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure); + } + + protected function filterValues(array $values) + { + foreach ($values as $key => $value) { + if (is_null($value)) { + unset($values[$key]); + } + } + + return $values; + } + + protected function getClassName() + { + $reflection = new ReflectionClass(get_called_class()); + return $reflection->getShortName(); + } +} diff --git a/app/Api/Procedure/BoardProcedure.php b/app/Api/Procedure/BoardProcedure.php new file mode 100644 index 0000000..69daaf0 --- /dev/null +++ b/app/Api/Procedure/BoardProcedure.php @@ -0,0 +1,24 @@ +container)->check($this->getClassName(), 'getBoard', $project_id); + + return $this->boardFormatter + ->withProjectId($project_id) + ->withQuery($this->taskFinderModel->getExtendedQuery()) + ->format(); + } +} diff --git a/app/Api/Procedure/CategoryProcedure.php b/app/Api/Procedure/CategoryProcedure.php new file mode 100644 index 0000000..aab0ade --- /dev/null +++ b/app/Api/Procedure/CategoryProcedure.php @@ -0,0 +1,61 @@ +container)->check($this->getClassName(), 'getCategory', $category_id); + return $this->categoryModel->getById($category_id); + } + + public function getAllCategories($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllCategories', $project_id); + return $this->categoryModel->getAll($project_id); + } + + public function removeCategory($category_id) + { + CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeCategory', $category_id); + return $this->categoryModel->remove($category_id); + } + + public function createCategory($project_id, $name, $color_id = null) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createCategory', $project_id); + + $values = array( + 'project_id' => $project_id, + 'name' => $name, + 'color_id' => $color_id, + ); + + list($valid, ) = $this->categoryValidator->validateCreation($values); + return $valid ? $this->categoryModel->create($values) : false; + } + + public function updateCategory($id, $name, $color_id = null) + { + CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id); + + $values = array( + 'id' => $id, + 'name' => $name, + 'color_id' => $color_id, + ); + + list($valid, ) = $this->categoryValidator->validateModification($values); + return $valid && $this->categoryModel->update($values); + } +} diff --git a/app/Api/Procedure/ColumnProcedure.php b/app/Api/Procedure/ColumnProcedure.php new file mode 100644 index 0000000..e9e2ea8 --- /dev/null +++ b/app/Api/Procedure/ColumnProcedure.php @@ -0,0 +1,66 @@ +container)->check($this->getClassName(), 'getColumns', $project_id); + return $this->columnModel->getAll($project_id); + } + + public function getColumn($column_id) + { + ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumn', $column_id); + return $this->columnModel->getById($column_id); + } + + public function updateColumn($column_id, $title, $task_limit = 0, $description = '') + { + ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateColumn', $column_id); + return $this->columnModel->update($column_id, $title, $task_limit, $description); + } + + public function addColumn($project_id, $title, $task_limit = 0, $description = '') + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addColumn', $project_id); + return $this->columnModel->create($project_id, $title, $task_limit, $description); + } + + public function removeColumn($column_id) + { + ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeColumn', $column_id); + + $projectId = $this->columnModel->getProjectId($column_id); + $nbTasks = $this->taskFinderModel->countByColumnId($projectId, $column_id, array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED)); + + if ($nbTasks > 0) { + $this->logger->error(__METHOD__.': This column cannot be removed because it contains '.$nbTasks.' tasks'); + return false; + } + + return $this->columnModel->remove($column_id); + } + + public function changeColumnPosition($project_id, $column_id, $position) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeColumnPosition', $project_id); + + if ((int) $this->columnModel->getProjectId($column_id) !== (int) $project_id) { + return false; + } + + return $this->columnModel->changePosition($project_id, $column_id, $position); + } +} diff --git a/app/Api/Procedure/CommentProcedure.php b/app/Api/Procedure/CommentProcedure.php new file mode 100644 index 0000000..3209924 --- /dev/null +++ b/app/Api/Procedure/CommentProcedure.php @@ -0,0 +1,113 @@ +container)->check($this->getClassName(), 'getComment', $comment_id); + return $this->commentModel->getById($comment_id); + } + + public function getAllComments($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllComments', $task_id); + $comments = $this->commentModel->getAll($task_id); + $filteredComments = []; + $userRole = $this->userSession->getRole(); + + foreach ($comments as $comment) { + if ($userRole === Role::APP_MANAGER && $comment['visibility'] === Role::APP_ADMIN) { + continue; + } + + if ($userRole === Role::APP_USER && $comment['visibility'] !== Role::APP_USER) { + continue; + } + + $filteredComments[] = $comment; + } + + return $filteredComments; + } + + public function removeComment($comment_id) + { + CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeComment', $comment_id); + + if ($this->userSession->isLogged()) { + $loggedUserID = $this->userSession->getId(); + $comment = $this->commentModel->getById($comment_id); + if ($comment['user_id'] != $loggedUserID) { + return false; + } + } + + return $this->commentModel->remove($comment_id); + } + + public function createComment($task_id, $user_id, $content, $reference = '', $visibility = Role::APP_USER) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createComment', $task_id); + + if ($this->userSession->isLogged()) { + $loggedUserID = $this->userSession->getId(); + if ($user_id != $loggedUserID) { + return false; + } + + $userRole = $this->userSession->getRole(); + if ($userRole === Role::APP_MANAGER && $visibility === Role::APP_ADMIN) { + return false; + } + + if ($userRole === Role::APP_USER && $visibility !== Role::APP_USER) { + return false; + } + } + + $values = array( + 'task_id' => $task_id, + 'user_id' => $user_id, + 'comment' => $content, + 'reference' => $reference, + 'visibility' => $visibility, + ); + + list($valid, ) = $this->commentValidator->validateCreation($values); + + return $valid ? $this->commentModel->create($values) : false; + } + + public function updateComment($id, $content) + { + CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id); + + if ($this->userSession->isLogged()) { + $loggedUserID = $this->userSession->getId(); + $comment = $this->commentModel->getById($id); + if ($comment['user_id'] != $loggedUserID) { + return false; + } + } + + $values = array( + 'id' => $id, + 'comment' => $content, + ); + + list($valid, ) = $this->commentValidator->validateModification($values); + return $valid && $this->commentModel->update($values); + } +} diff --git a/app/Api/Procedure/GroupMemberProcedure.php b/app/Api/Procedure/GroupMemberProcedure.php new file mode 100644 index 0000000..081d6ac --- /dev/null +++ b/app/Api/Procedure/GroupMemberProcedure.php @@ -0,0 +1,37 @@ +groupMemberModel->getGroups($user_id); + } + + public function getGroupMembers($group_id) + { + return $this->groupMemberModel->getMembers($group_id); + } + + public function addGroupMember($group_id, $user_id) + { + return $this->groupMemberModel->addUser($group_id, $user_id); + } + + public function removeGroupMember($group_id, $user_id) + { + return $this->groupMemberModel->removeUser($group_id, $user_id); + } + + public function isGroupMember($group_id, $user_id) + { + return $this->groupMemberModel->isMember($group_id, $user_id); + } +} diff --git a/app/Api/Procedure/GroupProcedure.php b/app/Api/Procedure/GroupProcedure.php new file mode 100644 index 0000000..804940a --- /dev/null +++ b/app/Api/Procedure/GroupProcedure.php @@ -0,0 +1,49 @@ +groupModel->create($name, $external_id); + } + + public function updateGroup($group_id, $name = null, $external_id = null) + { + $values = array( + 'id' => $group_id, + 'name' => $name, + 'external_id' => $external_id, + ); + + foreach ($values as $key => $value) { + if (is_null($value)) { + unset($values[$key]); + } + } + + return $this->groupModel->update($values); + } + + public function removeGroup($group_id) + { + return $this->groupModel->remove($group_id); + } + + public function getGroup($group_id) + { + return $this->groupModel->getById($group_id); + } + + public function getAllGroups() + { + return $this->groupModel->getAll(); + } +} diff --git a/app/Api/Procedure/LinkProcedure.php b/app/Api/Procedure/LinkProcedure.php new file mode 100644 index 0000000..b4cecf3 --- /dev/null +++ b/app/Api/Procedure/LinkProcedure.php @@ -0,0 +1,111 @@ +linkModel->getById($link_id); + } + + /** + * Get a link by name + * + * @access public + * @param string $label + * @return array + */ + public function getLinkByLabel($label) + { + return $this->linkModel->getByLabel($label); + } + + /** + * Get the opposite link id + * + * @access public + * @param integer $link_id Link id + * @return integer + */ + public function getOppositeLinkId($link_id) + { + return $this->linkModel->getOppositeLinkId($link_id); + } + + /** + * Get all links + * + * @access public + * @return array + */ + public function getAllLinks() + { + return $this->linkModel->getAll(); + } + + /** + * Create a new link label + * + * @access public + * @param string $label + * @param string $opposite_label + * @return boolean|integer + */ + public function createLink($label, $opposite_label = '') + { + $values = array( + 'label' => $label, + 'opposite_label' => $opposite_label, + ); + + list($valid, ) = $this->linkValidator->validateCreation($values); + return $valid ? $this->linkModel->create($label, $opposite_label) : false; + } + + /** + * Update a link + * + * @access public + * @param integer $link_id + * @param integer $opposite_link_id + * @param string $label + * @return boolean + */ + public function updateLink($link_id, $opposite_link_id, $label) + { + $values = array( + 'id' => $link_id, + 'opposite_id' => $opposite_link_id, + 'label' => $label, + ); + + list($valid, ) = $this->linkValidator->validateModification($values); + return $valid && $this->linkModel->update($values); + } + + /** + * Remove a link a the relation to its opposite + * + * @access public + * @param integer $link_id + * @return boolean + */ + public function removeLink($link_id) + { + return $this->linkModel->remove($link_id); + } +} diff --git a/app/Api/Procedure/MeProcedure.php b/app/Api/Procedure/MeProcedure.php new file mode 100644 index 0000000..3ccba0e --- /dev/null +++ b/app/Api/Procedure/MeProcedure.php @@ -0,0 +1,67 @@ +userSession->getId(); + + return $this->taskListSubtaskAssigneeFormatter + ->withQuery($this->taskFinderModel->getUserQuery($userId)) + ->withUserId($userId) + ->format(); + } + + public function getMyActivityStream() + { + $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + return $this->helper->projectActivity->getProjectsEvents($project_ids, 100); + } + + public function createMyPrivateProject($name, $description = null) + { + if ($this->configModel->get('disable_private_project', 0) == 1) { + return false; + } + + $values = array( + 'name' => $name, + 'description' => $description, + 'is_private' => 1, + ); + + list($valid, ) = $this->projectValidator->validateCreation($values); + return $valid ? $this->projectModel->create($values, $this->userSession->getId(), true) : false; + } + + public function getMyProjectsList() + { + return (object) $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId()); + } + + public function getMyOverdueTasks() + { + return $this->taskFinderModel->getOverdueTasksByUser($this->userSession->getId()); + } + + public function getMyProjects() + { + $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + $projects = $this->projectModel->getAllByIds($project_ids); + + return $this->projectsApiFormatter->withProjects($projects)->format(); + } +} diff --git a/app/Api/Procedure/ProjectFileProcedure.php b/app/Api/Procedure/ProjectFileProcedure.php new file mode 100644 index 0000000..232019d --- /dev/null +++ b/app/Api/Procedure/ProjectFileProcedure.php @@ -0,0 +1,80 @@ +container)->check($this->getClassName(), 'getProjectFile', $project_id); + $file = $this->projectFileModel->getById($file_id); + + if (empty($file) || (int) $file['project_id'] !== (int) $project_id) { + return false; + } + + return $file; + } + + public function getAllProjectFiles($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllProjectFiles', $project_id); + return $this->projectFileModel->getAll($project_id); + } + + public function downloadProjectFile($project_id, $file_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadProjectFile', $project_id); + + try { + $file = $this->projectFileModel->getById($file_id); + + if (! empty($file) && (int) $file['project_id'] === (int) $project_id) { + return base64_encode($this->objectStorage->get($file['path'])); + } + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + } + + return ''; + } + + public function createProjectFile($project_id, $filename, $blob) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createProjectFile', $project_id); + + try { + return $this->projectFileModel->uploadContent($project_id, $filename, $blob); + } catch (ObjectStorageException $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + return false; + } + } + + public function removeProjectFile($project_id, $file_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectFile', $project_id); + $file = $this->projectFileModel->getById($file_id); + + if (empty($file) || (int) $file['project_id'] !== (int) $project_id) { + return false; + } + + return $this->projectFileModel->remove($file_id); + } + + public function removeAllProjectFiles($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllProjectFiles', $project_id); + return $this->projectFileModel->removeAll($project_id); + } +} diff --git a/app/Api/Procedure/ProjectMetaDataProcedure.php b/app/Api/Procedure/ProjectMetaDataProcedure.php new file mode 100644 index 0000000..f14dc63 --- /dev/null +++ b/app/Api/Procedure/ProjectMetaDataProcedure.php @@ -0,0 +1,38 @@ +container)->check($this->getClassName(), 'getProjectMetadata', $project_id); + return (object) $this->projectMetadataModel->getAll($project_id); + } + + public function getProjectMetadataByName($project_id, $name) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectMetadataByName', $project_id); + return $this->projectMetadataModel->get($project_id, $name); + } + + public function saveProjectMetadata($project_id, array $values) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'saveProjectMetadata', $project_id); + return $this->projectMetadataModel->save($project_id, $values); + } + + public function removeProjectMetadata($project_id, $name) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectMetadata', $project_id); + return $this->projectMetadataModel->remove($project_id, $name); + } +} diff --git a/app/Api/Procedure/ProjectPermissionProcedure.php b/app/Api/Procedure/ProjectPermissionProcedure.php new file mode 100644 index 0000000..1938a06 --- /dev/null +++ b/app/Api/Procedure/ProjectPermissionProcedure.php @@ -0,0 +1,69 @@ +container)->check($this->getClassName(), 'getProjectUsers', $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 (object) $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned); + } + + public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectUser', $project_id); + return $this->projectUserRoleModel->addUser($project_id, $user_id, $role); + } + + public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectGroup', $project_id); + return $this->projectGroupRoleModel->addGroup($project_id, $group_id, $role); + } + + public function removeProjectUser($project_id, $user_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectUser', $project_id); + return $this->projectUserRoleModel->removeUser($project_id, $user_id); + } + + public function removeProjectGroup($project_id, $group_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectGroup', $project_id); + return $this->projectGroupRoleModel->removeGroup($project_id, $group_id); + } + + public function changeProjectUserRole($project_id, $user_id, $role) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectUserRole', $project_id); + return $this->projectUserRoleModel->changeUserRole($project_id, $user_id, $role); + } + + public function changeProjectGroupRole($project_id, $group_id, $role) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectGroupRole', $project_id); + return $this->projectGroupRoleModel->changeGroupRole($project_id, $group_id, $role); + } + + public function getProjectUserRole($project_id, $user_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUserRole', $project_id); + return $this->projectUserRoleModel->getUserRole($project_id, $user_id); + } +} diff --git a/app/Api/Procedure/ProjectProcedure.php b/app/Api/Procedure/ProjectProcedure.php new file mode 100644 index 0000000..6da00ae --- /dev/null +++ b/app/Api/Procedure/ProjectProcedure.php @@ -0,0 +1,152 @@ +container)->check($this->getClassName(), 'getProjectById', $project_id); + $project = $this->projectModel->getById($project_id); + return $this->projectApiFormatter->withProject($project)->format(); + } + + public function getProjectByName($name) + { + $project = $this->projectModel->getByName($name); + + if (empty($project)) { + return false; + } + + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByName', $project['id']); + return $this->projectApiFormatter->withProject($project)->format(); + } + + public function getProjectByIdentifier($identifier) + { + $project = $this->projectModel->getByIdentifier($identifier); + + if (empty($project)) { + return false; + } + + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByIdentifier', $project['id']); + return $this->projectApiFormatter->withProject($project)->format(); + } + + public function getProjectByEmail($email) + { + $project = $this->projectModel->getByEmail($email); + + if (empty($project)) { + return false; + } + + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByEmail', $project['id']); + return $this->projectApiFormatter->withProject($project)->format(); + } + + public function getAllProjects() + { + return $this->projectsApiFormatter->withProjects($this->projectModel->getAll())->format(); + } + + public function removeProject($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProject', $project_id); + return $this->projectModel->remove($project_id); + } + + public function enableProject($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProject', $project_id); + return $this->projectModel->enable($project_id); + } + + public function disableProject($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProject', $project_id); + return $this->projectModel->disable($project_id); + } + + public function enableProjectPublicAccess($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProjectPublicAccess', $project_id); + return $this->projectModel->enablePublicAccess($project_id); + } + + public function disableProjectPublicAccess($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProjectPublicAccess', $project_id); + return $this->projectModel->disablePublicAccess($project_id); + } + + public function getProjectActivities(array $project_ids) + { + foreach ($project_ids as $project_id) { + ProjectAuthorization::getInstance($this->container) + ->check($this->getClassName(), 'getProjectActivities', $project_id); + } + + return $this->helper->projectActivity->getProjectsEvents($project_ids); + } + + public function getProjectActivity($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectActivity', $project_id); + return $this->helper->projectActivity->getProjectEvents($project_id); + } + + public function createProject($name, $description = null, $owner_id = 0, $identifier = null, $start_date = null, $end_date = null, $priority_default = null, $priority_start = null, $priority_end = null, $email = null) + { + if ($owner_id === 0 && $this->userSession->isLogged()) { + $owner_id = $this->userSession->getId(); + } + + $values = $this->filterValues(array( + 'name' => $name, + 'description' => $description, + 'identifier' => $identifier, + 'start_date' => $start_date, + 'end_date' => $end_date, + 'priority_default' => $priority_default, + 'priority_start' => $priority_start, + 'priority_end' => $priority_end, + 'email' => $email, + )); + + list($valid, ) = $this->projectValidator->validateCreation($values); + return $valid ? $this->projectModel->create($values, $owner_id, $this->userSession->isLogged()) : false; + } + + public function updateProject($project_id, $name = null, $description = null, $owner_id = null, $identifier = null, $start_date = null, $end_date = null, $priority_default = null, $priority_start = null, $priority_end = null, $email = null) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateProject', $project_id); + + $values = $this->filterValues(array( + 'id' => $project_id, + 'name' => $name, + 'description' => $description, + 'owner_id' => $owner_id, + 'identifier' => $identifier, + 'start_date' => $start_date, + 'end_date' => $end_date, + 'priority_default' => $priority_default, + 'priority_start' => $priority_start, + 'priority_end' => $priority_end, + 'email' => $email, + )); + + list($valid, ) = $this->projectValidator->validateModification($values); + return $valid && $this->projectModel->update($values); + } +} diff --git a/app/Api/Procedure/SubtaskProcedure.php b/app/Api/Procedure/SubtaskProcedure.php new file mode 100644 index 0000000..ad0a9d3 --- /dev/null +++ b/app/Api/Procedure/SubtaskProcedure.php @@ -0,0 +1,80 @@ +container)->check($this->getClassName(), 'getSubtask', $subtask_id); + return $this->subtaskModel->getById($subtask_id); + } + + public function getAllSubtasks($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSubtasks', $task_id); + return $this->subtaskModel->getAll($task_id); + } + + public function removeSubtask($subtask_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSubtask', $subtask_id); + return $this->subtaskModel->remove($subtask_id); + } + + public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createSubtask', $task_id); + + $values = array( + 'title' => $title, + 'task_id' => $task_id, + 'user_id' => $user_id, + 'time_estimated' => $time_estimated, + 'time_spent' => $time_spent, + 'status' => $status, + ); + + list($valid, ) = $this->subtaskValidator->validateCreation($values); + return $valid ? $this->subtaskModel->create($values) : false; + } + + public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null, $position = null) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSubtask', $task_id); + + $subtask = $this->subtaskModel->getById($id); + if (empty($subtask) || (int) $subtask['task_id'] !== (int) $task_id) { + return false; + } + + $values = array( + 'id' => $id, + 'task_id' => $task_id, + 'title' => $title, + 'user_id' => $user_id, + 'time_estimated' => $time_estimated, + 'time_spent' => $time_spent, + 'status' => $status, + 'position' => $position + ); + + foreach ($values as $key => $value) { + if (is_null($value)) { + unset($values[$key]); + } + } + + list($valid, ) = $this->subtaskValidator->validateApiModification($values); + return $valid && $this->subtaskModel->update($values); + } +} diff --git a/app/Api/Procedure/SubtaskTimeTrackingProcedure.php b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php new file mode 100644 index 0000000..5ceaa08 --- /dev/null +++ b/app/Api/Procedure/SubtaskTimeTrackingProcedure.php @@ -0,0 +1,39 @@ +container)->check($this->getClassName(), 'hasSubtaskTimer', $subtask_id); + return $this->subtaskTimeTrackingModel->hasTimer($subtask_id, $user_id); + } + + public function setSubtaskStartTime($subtask_id, $user_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskStartTime', $subtask_id); + return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id); + } + + public function setSubtaskEndTime($subtask_id, $user_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskEndTime', $subtask_id); + return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id); + } + + public function getSubtaskTimeSpent($subtask_id, $user_id) + { + SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id); + return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id); + } +} diff --git a/app/Api/Procedure/SwimlaneProcedure.php b/app/Api/Procedure/SwimlaneProcedure.php new file mode 100644 index 0000000..0840ea6 --- /dev/null +++ b/app/Api/Procedure/SwimlaneProcedure.php @@ -0,0 +1,121 @@ +container)->check($this->getClassName(), 'getActiveSwimlanes', $project_id); + return $this->swimlaneModel->getAllByStatus($project_id, SwimlaneModel::ACTIVE); + } + + public function getAllSwimlanes($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSwimlanes', $project_id); + return $this->swimlaneModel->getAll($project_id); + } + + public function getSwimlaneById($swimlane_id) + { + $swimlane = $this->swimlaneModel->getById($swimlane_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneById', $swimlane['project_id']); + return $swimlane; + } + + public function getSwimlaneByName($project_id, $name) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneByName', $project_id); + return $this->swimlaneModel->getByName($project_id, $name); + } + + public function getSwimlane($swimlane_id) + { + $swimlane = $this->swimlaneModel->getById($swimlane_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlane', $swimlane['project_id']); + return $swimlane; + } + + public function addSwimlane($project_id, $name, $description = '') + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addSwimlane', $project_id); + return $this->swimlaneModel->create($project_id, $name, $description); + } + + public function updateSwimlane($project_id, $swimlane_id, $name, $description = null) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSwimlane', $project_id); + + $swimlane = $this->swimlaneModel->getById($swimlane_id); + + if (empty($swimlane) || (int) $swimlane['project_id'] !== (int) $project_id) { + return false; + } + + $values = array( + 'project_id' => $project_id, + 'id' => $swimlane_id, + 'name' => $name, + ); + + if (! is_null($description)) { + $values['description'] = $description; + } + + list($valid, $errors) = $this->swimlaneValidator->validateModification($values); + + if (! $valid) { + $this->logger->debug(__METHOD__.': Validation error: '.var_export($errors, true)); + return false; + } + + return $this->swimlaneModel->update($swimlane_id, $values); + } + + public function removeSwimlane($project_id, $swimlane_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSwimlane', $project_id); + + $swimlane = $this->swimlaneModel->getById($swimlane_id); + + if (empty($swimlane) || (int) $swimlane['project_id'] !== (int) $project_id) { + return false; + } + + return $this->swimlaneModel->remove($project_id, $swimlane_id); + } + + public function disableSwimlane($project_id, $swimlane_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableSwimlane', $project_id); + return $this->swimlaneModel->disable($project_id, $swimlane_id); + } + + public function enableSwimlane($project_id, $swimlane_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableSwimlane', $project_id); + return $this->swimlaneModel->enable($project_id, $swimlane_id); + } + + public function changeSwimlanePosition($project_id, $swimlane_id, $position) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeSwimlanePosition', $project_id); + + $swimlane = $this->swimlaneModel->getById($swimlane_id); + + if (empty($swimlane) || (int) $swimlane['project_id'] !== (int) $project_id) { + return false; + } + + return $this->swimlaneModel->changePosition($project_id, $swimlane_id, $position); + } +} diff --git a/app/Api/Procedure/TagProcedure.php b/app/Api/Procedure/TagProcedure.php new file mode 100644 index 0000000..c28b9bc --- /dev/null +++ b/app/Api/Procedure/TagProcedure.php @@ -0,0 +1,49 @@ +userSession->isLogged() && ! $this->userSession->isAdmin()) { + $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + return $this->tagModel->getAllByProjectIds($project_ids); + } + + 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, $color_id = null) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTag', $project_id); + return $this->tagModel->create($project_id, $tag, $color_id); + } + + public function updateTag($tag_id, $tag, $color_id = null) + { + TagAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTag', $tag_id); + return $this->tagModel->update($tag_id, $tag, $color_id); + } + + 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/TaskExternalLinkProcedure.php b/app/Api/Procedure/TaskExternalLinkProcedure.php new file mode 100644 index 0000000..67043b8 --- /dev/null +++ b/app/Api/Procedure/TaskExternalLinkProcedure.php @@ -0,0 +1,123 @@ +externalLinkManager->getTypes(); + } + + public function getExternalTaskLinkProviderDependencies($providerName) + { + try { + return $this->externalLinkManager->getProvider($providerName)->getDependencies(); + } catch (ExternalLinkProviderNotFound $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + return false; + } + } + + public function getExternalTaskLinkById($task_id, $link_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLink', $task_id); + $link = $this->taskExternalLinkModel->getById($link_id); + + if (empty($link) || (int) $link['task_id'] !== (int) $task_id) { + return false; + } + + return $link; + } + + public function getAllExternalTaskLinks($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLinks', $task_id); + return $this->taskExternalLinkModel->getAll($task_id); + } + + public function createExternalTaskLink($task_id, $url, $dependency, $type = ExternalLinkManager::TYPE_AUTO, $title = '') + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createExternalTaskLink', $task_id); + + try { + $provider = $this->externalLinkManager + ->setUserInputText($url) + ->setUserInputType($type) + ->find(); + + $link = $provider->getLink(); + + $values = array( + 'task_id' => $task_id, + 'title' => $title ?: $link->getTitle(), + 'url' => $link->getUrl(), + 'link_type' => $provider->getType(), + 'dependency' => $dependency, + ); + + list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); + + if (! $valid) { + $this->logger->error(__METHOD__.': '.var_export($errors)); + return false; + } + + return $this->taskExternalLinkModel->create($values); + } catch (ExternalLinkProviderNotFound $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + } + + return false; + } + + public function updateExternalTaskLink($task_id, $link_id, $title = null, $url = null, $dependency = null) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateExternalTaskLink', $task_id); + + $link = $this->taskExternalLinkModel->getById($link_id); + + if (empty($link) || (int) $link['task_id'] !== (int) $task_id) { + return false; + } + + $values = $this->filterValues(array( + 'title' => $title, + 'url' => $url, + 'dependency' => $dependency, + )); + + $values = array_merge($link, $values); + list($valid, $errors) = $this->externalLinkValidator->validateModification($values); + + if (! $valid) { + $this->logger->error(__METHOD__.': '.var_export($errors)); + return false; + } + + return $this->taskExternalLinkModel->update($values); + } + + public function removeExternalTaskLink($task_id, $link_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeExternalTaskLink', $task_id); + $link = $this->taskExternalLinkModel->getById($link_id); + + if (empty($link) || (int) $link['task_id'] !== (int) $task_id) { + return false; + } + + return $this->taskExternalLinkModel->remove($link_id); + } +} diff --git a/app/Api/Procedure/TaskFileProcedure.php b/app/Api/Procedure/TaskFileProcedure.php new file mode 100644 index 0000000..b8aeac4 --- /dev/null +++ b/app/Api/Procedure/TaskFileProcedure.php @@ -0,0 +1,81 @@ +container)->check($this->getClassName(), 'getTaskFile', $file_id); + return $this->taskFileModel->getById($file_id); + } + + public function getAllTaskFiles($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskFiles', $task_id); + return $this->taskFileModel->getAll($task_id); + } + + public function downloadTaskFile($file_id) + { + TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id); + + try { + $file = $this->taskFileModel->getById($file_id); + + if (! empty($file)) { + return base64_encode($this->objectStorage->get($file['path'])); + } + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + } + + return ''; + } + + public function createTaskFile($project_id, $task_id, $filename, $blob) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $project_id); + + if (!$this->taskFinderModel->exists($task_id)) { + return false; + } + + $taskProjectID = $this->taskFinderModel->getProjectId($task_id); + if ($taskProjectID != $project_id) { + return false; + } + + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $task_id); + + try { + return $this->taskFileModel->uploadContent($task_id, $filename, $blob); + } catch (ObjectStorageException $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + return false; + } + } + + public function removeTaskFile($file_id) + { + TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskFile', $file_id); + return $this->taskFileModel->remove($file_id); + } + + public function removeAllTaskFiles($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllTaskFiles', $task_id); + return $this->taskFileModel->removeAll($task_id); + } +} diff --git a/app/Api/Procedure/TaskLinkProcedure.php b/app/Api/Procedure/TaskLinkProcedure.php new file mode 100644 index 0000000..f9d72a3 --- /dev/null +++ b/app/Api/Procedure/TaskLinkProcedure.php @@ -0,0 +1,109 @@ +container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id); + return $this->taskLinkModel->getById($task_link_id); + } + + /** + * Get all links attached to a task + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getAllTaskLinks($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $task_id); + return $this->taskLinkModel->getAll($task_id); + } + + /** + * 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 + */ + public function createTaskLink($task_id, $opposite_task_id, $link_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskLink', $task_id); + + if ($this->userSession->isLogged()) { + $opposite_task = $this->taskFinderModel->getById($opposite_task_id); + + if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) { + return false; + } + } + + return $this->taskLinkModel->create($task_id, $opposite_task_id, $link_id); + } + + /** + * Update a task link + * + * @access public + * @param integer $task_link_id Task link id + * @param integer $task_id Task id + * @param integer $opposite_task_id Opposite task id + * @param integer $link_id Link id + * @return boolean + */ + public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTaskLink', $task_id); + + $taskLink = $this->taskLinkModel->getById($task_link_id); + + if (empty($taskLink) || (int) $taskLink['task_id'] !== (int) $task_id) { + return false; + } + + if ($this->userSession->isLogged()) { + $opposite_task = $this->taskFinderModel->getById($opposite_task_id); + + if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) { + return false; + } + } + + return $this->taskLinkModel->update($task_link_id, $task_id, $opposite_task_id, $link_id); + } + + /** + * Remove a link between two tasks + * + * @access public + * @param integer $task_link_id + * @return boolean + */ + public function removeTaskLink($task_link_id) + { + TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id); + return $this->taskLinkModel->remove($task_link_id); + } +} diff --git a/app/Api/Procedure/TaskMetadataProcedure.php b/app/Api/Procedure/TaskMetadataProcedure.php new file mode 100644 index 0000000..03ec96e --- /dev/null +++ b/app/Api/Procedure/TaskMetadataProcedure.php @@ -0,0 +1,38 @@ +container)->check($this->getClassName(), 'getTaskMetadata', $task_id); + return (object) $this->taskMetadataModel->getAll($task_id); + } + + public function getTaskMetadataByName($task_id, $name) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskMetadataByName', $task_id); + return $this->taskMetadataModel->get($task_id, $name); + } + + public function saveTaskMetadata($task_id, array $values) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'saveTaskMetadata', $task_id); + return $this->taskMetadataModel->save($task_id, $values); + } + + public function removeTaskMetadata($task_id, $name) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskMetadata', $task_id); + return $this->taskMetadataModel->remove($task_id, $name); + } +} diff --git a/app/Api/Procedure/TaskProcedure.php b/app/Api/Procedure/TaskProcedure.php new file mode 100644 index 0000000..d614ce3 --- /dev/null +++ b/app/Api/Procedure/TaskProcedure.php @@ -0,0 +1,222 @@ +container)->check($this->getClassName(), 'searchTasks', $project_id); + return $this->taskLexer->build($query)->withFilter(new TaskProjectFilter($project_id))->toArray(); + } + + public function getTask($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id); + $task = $this->taskFinderModel->getById($task_id); + return $this->taskApiFormatter->withTask($task)->format(); + } + + public function getTaskByReference($project_id, $reference) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskByReference', $project_id); + $task = $this->taskFinderModel->getByReference($project_id, $reference); + return $this->taskApiFormatter->withTask($task)->format(); + } + + public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id); + $tasks = $this->taskFinderModel->getAll($project_id, $status_id); + return $this->tasksApiFormatter->withTasks($tasks)->format(); + } + + public function getOverdueTasks() + { + return $this->taskFinderModel->getOverdueTasks(); + } + + public function getOverdueTasksByProject($project_id) + { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getOverdueTasksByProject', $project_id); + return $this->taskFinderModel->getOverdueTasksByProject($project_id); + } + + public function openTask($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'openTask', $task_id); + return $this->taskStatusModel->open($task_id); + } + + public function closeTask($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'closeTask', $task_id); + return $this->taskStatusModel->close($task_id); + } + + public function removeTask($task_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTask', $task_id); + return $this->taskModel->remove($task_id); + } + + public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $task_id); + $taskProjectId = $this->taskFinderModel->getProjectId($task_id); + + if ($taskProjectId !== (int) $project_id) { + return false; + } + + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $project_id); + return $this->taskPositionModel->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id, true, false); + } + + public function moveTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $task_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $project_id); + return $this->taskProjectMoveModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); + } + + public function duplicateTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) + { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $task_id); + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $project_id); + return $this->taskProjectDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); + } + + public function createTask( + $title, + $project_id, + $color_id = '', + $column_id = 0, + $owner_id = 0, + $creator_id = 0, + $date_due = '', + $description = '', + $category_id = 0, + $score = 0, + $swimlane_id = null, + $priority = 0, + $recurrence_status = 0, + $recurrence_trigger = 0, + $recurrence_factor = 0, + $recurrence_timeframe = 0, + $recurrence_basedate = 0, + $reference = '', + array $tags = array(), + $date_started = '', + $time_spent = null, + $time_estimated = null + ) { + ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id); + + if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) { + return false; + } + + if ($creator_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $creator_id)) { + return false; + } + + $values = array( + 'title' => $title, + 'project_id' => $project_id, + 'color_id' => $color_id, + 'column_id' => $column_id, + 'owner_id' => $owner_id, + 'creator_id' => $creator_id, + 'date_due' => $date_due, + 'description' => $description, + 'category_id' => $category_id, + 'score' => $score, + 'swimlane_id' => $swimlane_id, + 'recurrence_status' => $recurrence_status, + 'recurrence_trigger' => $recurrence_trigger, + 'recurrence_factor' => $recurrence_factor, + 'recurrence_timeframe' => $recurrence_timeframe, + 'recurrence_basedate' => $recurrence_basedate, + 'reference' => $reference, + 'priority' => $priority, + 'tags' => $tags, + 'date_started' => $date_started, + 'time_spent' => $time_spent, + 'time_estimated' => $time_estimated, + ); + + list($valid, ) = $this->taskValidator->validateCreation($values); + + return $valid ? $this->taskCreationModel->create($values) : false; + } + + 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, + $tags = null, + $date_started = null, + $time_spent = null, + $time_estimated = null + ) { + TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id); + $project_id = $this->taskFinderModel->getProjectId($id); + + if ($project_id === 0) { + return false; + } + + if ($owner_id !== null && $owner_id != 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) { + return false; + } + + $values = $this->filterValues(array( + 'id' => $id, + 'title' => $title, + 'color_id' => $color_id, + 'owner_id' => $owner_id, + 'date_due' => $date_due, + 'description' => $description, + 'category_id' => $category_id, + 'score' => $score, + 'recurrence_status' => $recurrence_status, + 'recurrence_trigger' => $recurrence_trigger, + 'recurrence_factor' => $recurrence_factor, + 'recurrence_timeframe' => $recurrence_timeframe, + 'recurrence_basedate' => $recurrence_basedate, + 'reference' => $reference, + 'priority' => $priority, + 'tags' => $tags, + 'date_started' => $date_started, + 'time_spent' => $time_spent, + 'time_estimated' => $time_estimated, + )); + + list($valid) = $this->taskValidator->validateApiModification($values); + return $valid && $this->taskModificationModel->update($values); + } +} diff --git a/app/Api/Procedure/TaskTagProcedure.php b/app/Api/Procedure/TaskTagProcedure.php new file mode 100644 index 0000000..8e8607c --- /dev/null +++ b/app/Api/Procedure/TaskTagProcedure.php @@ -0,0 +1,28 @@ +container)->check($this->getClassName(), 'setTaskTags', $project_id); + 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/Api/Procedure/UserProcedure.php b/app/Api/Procedure/UserProcedure.php new file mode 100644 index 0000000..676f771 --- /dev/null +++ b/app/Api/Procedure/UserProcedure.php @@ -0,0 +1,147 @@ +userModel->getById($user_id); + } + + public function getUserByName($username) + { + return $this->userModel->getByUsername($username); + } + + public function getAllUsers() + { + return $this->userModel->getAll(); + } + + public function removeUser($user_id) + { + return $this->userModel->remove($user_id); + } + + public function disableUser($user_id) + { + return $this->userModel->disable($user_id); + } + + public function enableUser($user_id) + { + return $this->userModel->enable($user_id); + } + + public function isActiveUser($user_id) + { + return $this->userModel->isActive($user_id); + } + + public function createUser($username, $password, $name = '', $email = '', $role = Role::APP_USER) + { + $values = array( + 'username' => $username, + 'password' => $password, + 'confirmation' => $password, + 'name' => $name, + 'email' => $email, + 'role' => $role, + ); + + list($valid, ) = $this->userValidator->validateCreation($values); + + if ($valid) { + $user_id = $this->userModel->create($values); + if ($user_id !== false && $this->configModel->get('notifications_enabled', 0) == 1) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]); + } + return $user_id; + } + + return false; + } + + /** + * Create LDAP user in the database + * + * Only "anonymous" and "proxy" LDAP authentication are supported by this method + * + * User information will be fetched from the LDAP server + * + * @access public + * @param string $username + * @return bool|int + */ + public function createLdapUser($username) + { + if (LDAP_BIND_TYPE === 'user') { + $this->logger->error('LDAP authentication "user" is not supported by this API call'); + return false; + } + + try { + + $ldap = LdapClient::connect(); + $ldap->setLogger($this->logger); + $user = LdapUser::getUser($ldap, $username); + + if ($user === null) { + $this->logger->info('User not found in LDAP server'); + return false; + } + + if ($user->getUsername() === '') { + throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME'); + } + + $values = array( + 'username' => $user->getUsername(), + 'name' => $user->getName(), + 'email' => $user->getEmail(), + 'role' => $user->getRole() ?: Role::APP_USER, + 'is_ldap_user' => 1, + ); + + $user_id = $this->userModel->create($values); + + if ($user_id !== false && $this->configModel->get('notifications_enabled', 0) == 1) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]); + } + + return $user_id; + } catch (LdapException $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + public function updateUser($id, $username = null, $name = null, $email = null, $role = null) + { + $values = $this->filterValues(array( + 'id' => $id, + 'username' => $username, + 'name' => $name, + 'email' => $email, + 'role' => $role, + )); + + list($valid, ) = $this->userValidator->validateApiModification($values); + return $valid && $this->userModel->update($values); + } +} diff --git a/app/Auth/ApiAccessTokenAuth.php b/app/Auth/ApiAccessTokenAuth.php new file mode 100644 index 0000000..88e1686 --- /dev/null +++ b/app/Auth/ApiAccessTokenAuth.php @@ -0,0 +1,118 @@ +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 new file mode 100644 index 0000000..1982f57 --- /dev/null +++ b/app/Auth/DatabaseAuth.php @@ -0,0 +1,126 @@ +db + ->table(UserModel::TABLE) + ->columns('id', 'password') + ->eq('username', $this->username) + ->eq('disable_login_form', 0) + ->eq('is_ldap_user', 0) + ->eq('is_active', 1) + ->findOne(); + + if (! empty($user) && password_verify($this->password, $user['password'])) { + $this->userInfo = $user; + return true; + } + + return false; + } + + /** + * Check if the user session is valid + * + * @access public + * @return boolean + */ + public function isValidSession() + { + return $this->userModel->isValidSession($this->userSession->getId(), $this->userSession->getRole()); + } + + /** + * 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/LdapAuth.php b/app/Auth/LdapAuth.php new file mode 100644 index 0000000..05ffbeb --- /dev/null +++ b/app/Auth/LdapAuth.php @@ -0,0 +1,176 @@ +getLdapUsername(), $this->getLdapPassword()); + $client->setLogger($this->logger); + + $user = LdapUser::getUser($client, $this->username); + + if ($user === null) { + $this->logger->info('User ('.$this->username.') not found in LDAP server'); + return false; + } + + if ($user->getUsername() === '') { + throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME'); + } + + $this->logger->info('Authenticate this user: '.$user->getDn()); + + if ($client->authenticate($user->getDn(), $this->password)) { + $this->userInfo = $user; + return true; + } + + } catch (LdapException $e) { + $this->logger->error($e->getMessage()); + } + + return false; + } + + /** + * Get user object + * + * @access public + * @return \Kanboard\User\LdapUserProvider + */ + public function getUser() + { + return $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; + } + + /** + * Get LDAP username (proxy auth) + * + * @access public + * @return string + */ + public function getLdapUsername() + { + switch ($this->getLdapBindType()) { + case 'proxy': + return LDAP_USERNAME; + case 'user': + return sprintf(LDAP_USERNAME, $this->username); + default: + return null; + } + } + + /** + * Get LDAP password (proxy auth) + * + * @access public + * @return string + */ + public function getLdapPassword() + { + switch ($this->getLdapBindType()) { + case 'proxy': + return LDAP_PASSWORD; + case 'user': + return $this->password; + default: + return null; + } + } + + /** + * Get LDAP bind type + * + * @access public + * @return integer + */ + public function getLdapBindType() + { + if (LDAP_BIND_TYPE !== 'user' && LDAP_BIND_TYPE !== 'proxy' && LDAP_BIND_TYPE !== 'anonymous') { + throw new LogicException('Wrong value for the parameter LDAP_BIND_TYPE'); + } + + return LDAP_BIND_TYPE; + } +} diff --git a/app/Auth/RememberMeAuth.php b/app/Auth/RememberMeAuth.php new file mode 100644 index 0000000..e0f4ceb --- /dev/null +++ b/app/Auth/RememberMeAuth.php @@ -0,0 +1,79 @@ +rememberMeCookie->read(); + + if ($credentials !== false) { + $session = $this->rememberMeSessionModel->find($credentials['token'], $credentials['sequence']); + + if (! empty($session)) { + $this->rememberMeCookie->write( + $session['token'], + $this->rememberMeSessionModel->updateSequence($session['token']), + $session['expiration'] + ); + + $this->userInfo = $this->userModel->getById($session['user_id']); + + return true; + } + } + + return false; + } + + /** + * Get user object + * + * @access public + * @return DatabaseUserProvider + */ + public function getUser() + { + if (empty($this->userInfo)) { + return null; + } + + return new DatabaseUserProvider($this->userInfo); + } +} diff --git a/app/Auth/ReverseProxyAuth.php b/app/Auth/ReverseProxyAuth.php new file mode 100644 index 0000000..edbb4c7 --- /dev/null +++ b/app/Auth/ReverseProxyAuth.php @@ -0,0 +1,91 @@ +request->getRemoteUser(); + $email = $this->request->getRemoteEmail(); + $fullname = $this->request->getRemoteName(); + + if (! empty($username)) { + $userProfile = $this->userCacheDecorator->getByUsername($username); + $this->userInfo = new ReverseProxyUserProvider($username, $email, $fullname, $userProfile ?: array()); + return true; + } + + return false; + } + + /** + * Check if the user session is valid + * + * @access public + * @return boolean + */ + public function isValidSession() + { + return $this->request->getRemoteUser() === $this->userSession->getUsername(); + } + + /** + * Get user object + * + * @access public + * @return ReverseProxyUserProvider + */ + public function getUser() + { + return $this->userInfo; + } + + /** + * Check if the authentication provider should be used + * + * @access public + * @return boolean + */ + public function isEnabled() + { + return ! empty($this->request->getRemoteUser()); + } +} diff --git a/app/Auth/TotpAuth.php b/app/Auth/TotpAuth.php new file mode 100644 index 0000000..abfb216 --- /dev/null +++ b/app/Auth/TotpAuth.php @@ -0,0 +1,146 @@ +checkTotp(Base32::decode($this->secret), $this->code); + } + + /** + * Called before to prompt the user + * + * @access public + */ + public function beforeCode() + { + + } + + /** + * Set validation code + * + * @access public + * @param string $code + */ + public function setCode($code) + { + $this->code = $code; + } + + /** + * Generate secret + * + * @access public + * @return string + */ + public function generateSecret() + { + $this->secret = GoogleAuthenticator::generateRandom(); + return $this->secret; + } + + /** + * Set secret token + * + * @access public + * @param string $secret + */ + public function setSecret($secret) + { + $this->secret = $secret; + } + + /** + * Get secret token + * + * @access public + * @return string + */ + public function getSecret() + { + return $this->secret; + } + + /** + * Get QR code url + * + * @access public + * @param string $label + * @return string + */ + public function getQrCodeUrl($label) + { + if (empty($this->secret)) { + return ''; + } + + $options = array('issuer' => TOTP_ISSUER); + return GoogleAuthenticator::getQrCodeUrl('totp', $label, $this->secret, null, $options); + } + + /** + * Get key url (empty if no url can be provided) + * + * @access public + * @param string $label + * @return string + */ + public function getKeyUrl($label) + { + if (empty($this->secret)) { + return ''; + } + + $options = array('issuer' => TOTP_ISSUER); + return GoogleAuthenticator::getKeyUri('totp', $label, $this->secret, null, $options); + } +} diff --git a/app/Console/BaseCommand.php b/app/Console/BaseCommand.php new file mode 100644 index 0000000..8ea67ae --- /dev/null +++ b/app/Console/BaseCommand.php @@ -0,0 +1,69 @@ +container = $container; + } + + /** + * Load automatically models + * + * @access public + * @param string $name Model name + * @return mixed + */ + public function __get($name) + { + return $this->container[$name]; + } +} diff --git a/app/Console/CronjobCommand.php b/app/Console/CronjobCommand.php new file mode 100644 index 0000000..4eabbec --- /dev/null +++ b/app/Console/CronjobCommand.php @@ -0,0 +1,33 @@ +setName('cronjob') + ->setDescription('Execute daily cronjob'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + foreach ($this->commands as $command) { + $job = $this->getApplication()->find($command); + $job->run(new ArrayInput(array('command' => $command)), new NullOutput()); + } + return 0; + } +} diff --git a/app/Console/CssCommand.php b/app/Console/CssCommand.php new file mode 100644 index 0000000..30ad1f1 --- /dev/null +++ b/app/Console/CssCommand.php @@ -0,0 +1,131 @@ +setName('css') + ->setDescription('Minify CSS files') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->minifyFiles(self::CSS_SRC_PATH, array_merge(['themes'.DIRECTORY_SEPARATOR.'light.css'], $this->appFiles), 'light.min.css'); + $this->minifyFiles(self::CSS_SRC_PATH, array_merge(['themes'.DIRECTORY_SEPARATOR.'dark.css'], $this->appFiles), 'dark.min.css'); + $this->minifyFiles(self::CSS_SRC_PATH, array_merge(['themes'.DIRECTORY_SEPARATOR.'auto.css'], $this->appFiles), 'auto.min.css'); + $this->minifyFiles(self::CSS_SRC_PATH, $this->printFiles, 'print.min.css'); + + $vendorBundle = concat_files($this->vendorFiles); + file_put_contents('assets/css/vendor.min.css', $vendorBundle); + return 0; + } + + private function minifyFiles($folder, array $files, $destination) + { + $minifier = new Minify\CSS(); + + foreach ($files as $file) { + $filename = $folder.$file; + if (! file_exists($filename)) { + die("$filename not found\n"); + } + $minifier->add($filename); + } + + $minifier->minify(self::CSS_DIST_PATH . $destination); + } +} diff --git a/app/Console/DatabaseMigrationCommand.php b/app/Console/DatabaseMigrationCommand.php new file mode 100644 index 0000000..d5c3209 --- /dev/null +++ b/app/Console/DatabaseMigrationCommand.php @@ -0,0 +1,24 @@ +setName('db:migrate') + ->setDescription('Execute SQL migrations'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + parent::execute($input, $output); + DatabaseProvider::runMigrations($this->container['db']); + return 0; + } +} diff --git a/app/Console/DatabaseVersionCommand.php b/app/Console/DatabaseVersionCommand.php new file mode 100644 index 0000000..32ac180 --- /dev/null +++ b/app/Console/DatabaseVersionCommand.php @@ -0,0 +1,24 @@ +setName('db:version') + ->setDescription('Show database schema version'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('Current version: '.DatabaseProvider::getSchemaVersion($this->container['db']).''); + $output->writeln('Last version: '.\Schema\VERSION.''); + return 0; + } +} diff --git a/app/Console/JobCommand.php b/app/Console/JobCommand.php new file mode 100644 index 0000000..ed043d3 --- /dev/null +++ b/app/Console/JobCommand.php @@ -0,0 +1,36 @@ +setName('job') + ->setDescription('Execute individual job (read payload from stdin)') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $payload = fgets(STDIN); + + $job = new Job(); + $job->unserialize($payload); + + JobHandler::getInstance($this->container)->executeJob($job); + return 0; + } +} diff --git a/app/Console/JsCommand.php b/app/Console/JsCommand.php new file mode 100644 index 0000000..cac3c66 --- /dev/null +++ b/app/Console/JsCommand.php @@ -0,0 +1,91 @@ +setName('js') + ->setDescription('Minify Javascript files') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $appBundle = concat_files($this->appFiles); + $vendorBundle = concat_files($this->vendorFiles); + + $minifier = new Minify\JS($appBundle); + + file_put_contents('assets/js/app.min.js', $minifier->minify()); + file_put_contents('assets/js/vendor.min.js', $vendorBundle); + return 0; + } +} diff --git a/app/Console/LocaleComparatorCommand.php b/app/Console/LocaleComparatorCommand.php new file mode 100644 index 0000000..e206f36 --- /dev/null +++ b/app/Console/LocaleComparatorCommand.php @@ -0,0 +1,82 @@ +setName('locale:compare') + ->setDescription('Compare application translations with the '.self::REF_LOCALE.' locale'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $strings = array(); + $it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator(APP_DIR)); + $it->rewind(); + + while ($it->valid()) { + if (! $it->isDot() && substr($it->key(), -4) === '.php') { + $strings = array_merge($strings, $this->search($it->key())); + } + + $it->next(); + } + + $this->compare(array_unique($strings)); + return 0; + } + + public function show(array $strings) + { + foreach ($strings as $string) { + echo " '".str_replace("'", "\'", $string)."' => '',".PHP_EOL; + } + } + + public function compare(array $strings) + { + $reference_file = APP_DIR.DIRECTORY_SEPARATOR.'Locale'.DIRECTORY_SEPARATOR.self::REF_LOCALE.DIRECTORY_SEPARATOR.'translations.php'; + $reference = include $reference_file; + + echo str_repeat('#', 70).PHP_EOL; + echo 'MISSING STRINGS'.PHP_EOL; + echo str_repeat('#', 70).PHP_EOL; + $this->show(array_diff($strings, array_keys($reference))); + + echo str_repeat('#', 70).PHP_EOL; + echo 'USELESS STRINGS'.PHP_EOL; + echo str_repeat('#', 70).PHP_EOL; + $this->show(array_diff(array_keys($reference), $strings)); + } + + public function search($filename) + { + $content = file_get_contents($filename); + $strings = array(); + + if (preg_match_all('/\b[et]\s*\(\s*(\'\K.*?\')\s*[\)\,]/', $content, $matches) && isset($matches[1])) { + $strings = $matches[1]; + } + + if (preg_match_all('/\bdt\s*\(\s*(\'\K.*?\')\s*[\)\,]/', $content, $matches) && isset($matches[1])) { + $strings = array_merge($strings, $matches[1]); + } + + array_walk($strings, function (&$value) { + $value = trim($value, "'"); + $value = str_replace("\'", "'", $value); + }); + + return $strings; + } +} diff --git a/app/Console/LocaleSyncCommand.php b/app/Console/LocaleSyncCommand.php new file mode 100644 index 0000000..7283b5e --- /dev/null +++ b/app/Console/LocaleSyncCommand.php @@ -0,0 +1,55 @@ +setName('locale:sync') + ->setDescription('Synchronize all translations based on the '.self::REF_LOCALE.' locale'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $reference_file = APP_DIR.DIRECTORY_SEPARATOR.'Locale'.DIRECTORY_SEPARATOR.self::REF_LOCALE.DIRECTORY_SEPARATOR.'translations.php'; + $reference = include $reference_file; + + foreach (new DirectoryIterator(APP_DIR.DIRECTORY_SEPARATOR.'Locale') as $fileInfo) { + if (! $fileInfo->isDot() && $fileInfo->isDir() && $fileInfo->getFilename() !== self::REF_LOCALE) { + $filename = APP_DIR.DIRECTORY_SEPARATOR.'Locale'.DIRECTORY_SEPARATOR.$fileInfo->getFilename().DIRECTORY_SEPARATOR.'translations.php'; + echo $fileInfo->getFilename().' ('.$filename.')'.PHP_EOL; + + file_put_contents($filename, $this->updateFile($reference, $filename)); + } + } + return 0; + } + + public function updateFile(array $reference, $outdated_file) + { + $outdated = include $outdated_file; + + $output = ' $value) { + $escapedKey = str_replace("'", "\'", $key); + if (! empty($outdated[$key])) { + $output .= " '".$escapedKey."' => '".str_replace("'", "\'", $outdated[$key])."',\n"; + } else { + $output .= " // '".$escapedKey."' => '',\n"; + } + } + + $output .= "];\n"; + return $output; + } +} diff --git a/app/Console/PluginInstallCommand.php b/app/Console/PluginInstallCommand.php new file mode 100644 index 0000000..f4bb698 --- /dev/null +++ b/app/Console/PluginInstallCommand.php @@ -0,0 +1,38 @@ +setName('plugin:install') + ->setDescription('Install a plugin from a remote Zip archive') + ->addArgument('url', InputArgument::REQUIRED, 'Archive URL'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + if (!Installer::isConfigured()) { + throw new LogicException('Kanboard is not configured to install plugins itself'); + } + + try { + $installer = new Installer($this->container); + $installer->install($input->getArgument('url')); + $output->writeln('Plugin installed successfully'); + return 0; + } catch (PluginInstallerException $e) { + $output->writeln(''.$e->getMessage().''); + return 1; + } + } +} diff --git a/app/Console/PluginUninstallCommand.php b/app/Console/PluginUninstallCommand.php new file mode 100644 index 0000000..13a6faf --- /dev/null +++ b/app/Console/PluginUninstallCommand.php @@ -0,0 +1,38 @@ +setName('plugin:uninstall') + ->setDescription('Remove a plugin') + ->addArgument('pluginId', InputArgument::REQUIRED, 'Plugin directory name'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + if (!Installer::isConfigured()) { + throw new LogicException('Kanboard is not configured to install plugins itself'); + } + + try { + $installer = new Installer($this->container); + $installer->uninstall($input->getArgument('pluginId')); + $output->writeln('Plugin removed successfully'); + return 0; + } catch (PluginInstallerException $e) { + $output->writeln(''.$e->getMessage().''); + return 1; + } + } +} diff --git a/app/Console/PluginUpgradeCommand.php b/app/Console/PluginUpgradeCommand.php new file mode 100644 index 0000000..9a8558b --- /dev/null +++ b/app/Console/PluginUpgradeCommand.php @@ -0,0 +1,56 @@ +setName('plugin:upgrade') + ->setDescription('Update all installed plugins') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + if (!Installer::isConfigured()) { + throw new LogicException('Kanboard is not configured to install plugins itself'); + } + + $installer = new Installer($this->container); + $availablePlugins = Directory::getInstance($this->container)->getAvailablePlugins(); + + foreach ($this->pluginLoader->getPlugins() as $installedPlugin) { + $pluginDetails = $this->getPluginDetails($availablePlugins, $installedPlugin); + + if ($pluginDetails === null) { + $output->writeln('* Plugin not available in the directory: '.$installedPlugin->getPluginName().''); + } elseif ($pluginDetails['version'] > $installedPlugin->getPluginVersion()) { + $output->writeln('* Updating plugin: '.$installedPlugin->getPluginName().''); + $installer->update($pluginDetails['download']); + } else { + $output->writeln('* Plugin up to date: '.$installedPlugin->getPluginName().''); + } + } + return 0; + } + + protected function getPluginDetails(array $availablePlugins, BasePlugin $installedPlugin) + { + foreach ($availablePlugins as $availablePluginName => $availablePlugin) { + if ($availablePluginName === $installedPlugin->getPluginName()) { + return $availablePlugin; + } + } + + return null; + } +} diff --git a/app/Console/ProjectActivityArchiveCommand.php b/app/Console/ProjectActivityArchiveCommand.php new file mode 100644 index 0000000..9dd6b61 --- /dev/null +++ b/app/Console/ProjectActivityArchiveCommand.php @@ -0,0 +1,22 @@ +setName('projects:archive-activities') + ->setDescription('Remove project activities after one year'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->projectActivityModel->cleanup(strtotime('-1 year')); + return 0; + } +} diff --git a/app/Console/ProjectArchiveCommand.php b/app/Console/ProjectArchiveCommand.php new file mode 100644 index 0000000..c11be1f --- /dev/null +++ b/app/Console/ProjectArchiveCommand.php @@ -0,0 +1,31 @@ +setName('projects:archive') + ->setDescription('Disable projects not touched during one year'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $projects = $this->db->table(ProjectModel::TABLE) + ->eq('is_active', 1) + ->lt('last_modified', strtotime('-1 year')) + ->findAll(); + + foreach ($projects as $project) { + $output->writeln('Deactivating project: #'.$project['id'].' - '.$project['name']); + $this->projectModel->disable($project['id']); + } + return 0; + } +} diff --git a/app/Console/ProjectDailyColumnStatsExportCommand.php b/app/Console/ProjectDailyColumnStatsExportCommand.php new file mode 100644 index 0000000..635addc --- /dev/null +++ b/app/Console/ProjectDailyColumnStatsExportCommand.php @@ -0,0 +1,35 @@ +setName('export:daily-project-column-stats') + ->setDescription('Daily project column stats CSV export (number of tasks per column and per day)') + ->addArgument('project_id', InputArgument::REQUIRED, 'Project id') + ->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)') + ->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $data = $this->projectDailyColumnStatsModel->getAggregatedMetrics( + $input->getArgument('project_id'), + $input->getArgument('start_date'), + $input->getArgument('end_date') + ); + + if (is_array($data)) { + Csv::output($data); + } + return 0; + } +} diff --git a/app/Console/ProjectDailyStatsCalculationCommand.php b/app/Console/ProjectDailyStatsCalculationCommand.php new file mode 100644 index 0000000..77170ca --- /dev/null +++ b/app/Console/ProjectDailyStatsCalculationCommand.php @@ -0,0 +1,29 @@ +setName('projects:daily-stats') + ->setDescription('Calculate daily statistics for all projects'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $projects = $this->projectModel->getAllByStatus(ProjectModel::ACTIVE); + + foreach ($projects as $project) { + $output->writeln('Run calculation for '.$project['name']); + $this->projectDailyColumnStatsModel->updateTotals($project['id'], date('Y-m-d')); + $this->projectDailyStatsModel->updateTotals($project['id'], date('Y-m-d')); + } + return 0; + } +} diff --git a/app/Console/ResetPasswordCommand.php b/app/Console/ResetPasswordCommand.php new file mode 100644 index 0000000..f66818c --- /dev/null +++ b/app/Console/ResetPasswordCommand.php @@ -0,0 +1,80 @@ +setName('user:reset-password') + ->setDescription('Change user password') + ->addArgument('username', InputArgument::REQUIRED, 'Username') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $helper = $this->getHelper('question'); + $username = $input->getArgument('username'); + + $passwordQuestion = new Question('What is the new password for '.$username.'? (characters are not printed)'.PHP_EOL); + $passwordQuestion->setHidden(true); + $passwordQuestion->setHiddenFallback(false); + + $password = $helper->ask($input, $output, $passwordQuestion); + + $confirmationQuestion = new Question('Confirmation:'.PHP_EOL); + $confirmationQuestion->setHidden(true); + $confirmationQuestion->setHiddenFallback(false); + + $confirmation = $helper->ask($input, $output, $confirmationQuestion); + + if ($this->validatePassword($output, $password, $confirmation)) { + $this->resetPassword($output, $username, $password); + } + return 0; + } + + private function validatePassword(OutputInterface $output, $password, $confirmation) + { + list($valid, $errors) = $this->passwordResetValidator->validateModification(array( + 'password' => $password, + 'confirmation' => $confirmation, + )); + + if (!$valid) { + foreach ($errors as $error_list) { + foreach ($error_list as $error) { + $output->writeln(''.$error.''); + } + } + } + + return $valid; + } + + private function resetPassword(OutputInterface $output, $username, $password) + { + $userId = $this->userModel->getIdByUsername($username); + + if (empty($userId)) { + $output->writeln('User not found'); + return false; + } + + if (!$this->userModel->update(array('id' => $userId, 'password' => $password))) { + $output->writeln('Unable to update password'); + return false; + } + + $output->writeln('Password updated successfully'); + + return true; + } +} diff --git a/app/Console/ResetTwoFactorCommand.php b/app/Console/ResetTwoFactorCommand.php new file mode 100644 index 0000000..0ab9854 --- /dev/null +++ b/app/Console/ResetTwoFactorCommand.php @@ -0,0 +1,37 @@ +setName('user:reset-2fa') + ->setDescription('Remove two-factor authentication for a user') + ->addArgument('username', InputArgument::REQUIRED, 'Username'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $username = $input->getArgument('username'); + $userId = $this->userModel->getIdByUsername($username); + + if (empty($userId)) { + $output->writeln('User not found'); + return 1; + } + + if (!$this->userModel->update(array('id' => $userId, 'twofactor_activated' => 0, 'twofactor_secret' => ''))) { + $output->writeln('Unable to update user profile'); + return 1; + } + + $output->writeln('Two-factor authentication disabled'); + return 0; + } +} diff --git a/app/Console/SubtaskExportCommand.php b/app/Console/SubtaskExportCommand.php new file mode 100644 index 0000000..c855c22 --- /dev/null +++ b/app/Console/SubtaskExportCommand.php @@ -0,0 +1,35 @@ +setName('export:subtasks') + ->setDescription('Subtasks CSV export') + ->addArgument('project_id', InputArgument::REQUIRED, 'Project id') + ->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)') + ->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $data = $this->subtaskExport->export( + $input->getArgument('project_id'), + $input->getArgument('start_date'), + $input->getArgument('end_date') + ); + + if (is_array($data)) { + Csv::output($data); + } + return 0; + } +} diff --git a/app/Console/TaskExportCommand.php b/app/Console/TaskExportCommand.php new file mode 100644 index 0000000..bdfcff7 --- /dev/null +++ b/app/Console/TaskExportCommand.php @@ -0,0 +1,35 @@ +setName('export:tasks') + ->setDescription('Tasks CSV export') + ->addArgument('project_id', InputArgument::REQUIRED, 'Project id') + ->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)') + ->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $data = $this->taskExport->export( + $input->getArgument('project_id'), + $input->getArgument('start_date'), + $input->getArgument('end_date') + ); + + if (is_array($data)) { + Csv::output($data); + } + return 0; + } +} diff --git a/app/Console/TaskOverdueNotificationCommand.php b/app/Console/TaskOverdueNotificationCommand.php new file mode 100644 index 0000000..3326b0e --- /dev/null +++ b/app/Console/TaskOverdueNotificationCommand.php @@ -0,0 +1,206 @@ +setName('notification:overdue-tasks') + ->setDescription('Send notifications for overdue tasks') + ->addOption('show', null, InputOption::VALUE_NONE, 'Show sent overdue tasks') + ->addOption('group', null, InputOption::VALUE_NONE, 'Group all overdue tasks for one user (from all projects) in one email') + ->addOption('manager', null, InputOption::VALUE_NONE, 'Send all overdue tasks to project manager(s) in one email') + ->addOption('project', 'p', InputOption::VALUE_REQUIRED, 'Send notifications only the given project') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + if ($input->getOption('project')) { + $tasks = $this->taskFinderModel->getOverdueTasksQuery() + ->beginOr() + ->eq(TaskModel::TABLE.'.project_id', $input->getOption('project')) + ->eq(ProjectModel::TABLE.'.identifier', $input->getOption('project')) + ->closeOr() + ->findAll(); + } else { + $tasks = $this->taskFinderModel->getOverdueTasks(); + } + + if ($input->getOption('group')) { + $tasks = $this->sendGroupOverdueTaskNotifications($tasks); + } elseif ($input->getOption('manager')) { + $tasks = $this->sendOverdueTaskNotificationsToManagers($tasks); + } else { + $tasks = $this->sendOverdueTaskNotifications($tasks); + } + + if ($input->getOption('show')) { + $this->showTable($output, $tasks); + } + return 0; + } + + public function showTable(OutputInterface $output, array $tasks) + { + $rows = array(); + + foreach ($tasks as $task) { + $rows[] = array( + $task['id'], + $task['title'], + date('Y-m-d H:i', $task['date_due']), + $task['project_id'], + $task['project_name'], + $task['assignee_name'] ?: $task['assignee_username'], + ); + } + + $table = new Table($output); + $table + ->setHeaders(array('Id', 'Title', 'Due date', 'Project Id', 'Project name', 'Assignee')) + ->setRows($rows) + ->render(); + } + + /** + * Send all overdue tasks for one user in one email + * + * @access public + * @param array $tasks + * @return array + */ + public function sendGroupOverdueTaskNotifications(array $tasks) + { + foreach ($this->groupByColumn($tasks, 'owner_id') as $user_tasks) { + $users = $this->userNotificationModel->getUsersWithNotificationEnabled($user_tasks[0]['project_id']); + + foreach ($users as $user) { + $this->sendUserOverdueTaskNotifications($user, $user_tasks); + } + } + + return $tasks; + } + + /** + * Send all overdue tasks in one email to project manager(s) + * + * @access public + * @param array $tasks + * @return array + */ + public function sendOverdueTaskNotificationsToManagers(array $tasks) + { + foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) { + $users = $this->userNotificationModel->getUsersWithNotificationEnabled($project_id); + $managers = array(); + + foreach ($users as $user) { + $role = $this->projectUserRoleModel->getUserRole($project_id, $user['id']); + if ($role == Role::PROJECT_MANAGER) { + $managers[] = $user; + } + } + + foreach ($managers as $manager) { + $this->sendUserOverdueTaskNotificationsToManagers($manager, $project_tasks); + } + } + + return $tasks; + } + + /** + * Send overdue tasks + * + * @access public + * @param array $tasks + * @return array + */ + public function sendOverdueTaskNotifications(array $tasks) + { + foreach ($this->groupByColumn($tasks, 'project_id') as $project_id => $project_tasks) { + $users = $this->userNotificationModel->getUsersWithNotificationEnabled($project_id); + + foreach ($users as $user) { + $this->sendUserOverdueTaskNotifications($user, $project_tasks); + } + } + + return $tasks; + } + + /** + * Send overdue tasks for a given user + * + * @access public + * @param array $user + * @param array $tasks + */ + public function sendUserOverdueTaskNotifications(array $user, array $tasks) + { + $user_tasks = array(); + $project_names = array(); + + foreach ($tasks as $task) { + if ($this->userNotificationFilterModel->shouldReceiveNotification($user, array('task' => $task))) { + $user_tasks[] = $task; + $project_names[$task['project_id']] = $task['project_name']; + } + } + + if (! empty($user_tasks)) { + $this->userNotificationModel->sendUserNotification( + $user, + TaskModel::EVENT_OVERDUE, + array('tasks' => $user_tasks, 'project_name' => implode(', ', $project_names)) + ); + } + } + + /** + * Send overdue tasks for a project manager(s) + * + * @access public + * @param array $manager + * @param array $tasks + */ + public function sendUserOverdueTaskNotificationsToManagers(array $manager, array $tasks) + { + $this->userNotificationModel->sendUserNotification( + $manager, + TaskModel::EVENT_OVERDUE, + array('tasks' => $tasks, 'project_name' => $tasks[0]['project_name']) + ); + } + + /** + * Group a collection of records by a column + * + * @access public + * @param array $collection + * @param string $column + * @return array + */ + public function groupByColumn(array $collection, $column) + { + $result = array(); + + foreach ($collection as $item) { + $result[$item[$column]][] = $item; + } + + return $result; + } +} diff --git a/app/Console/TaskTriggerCommand.php b/app/Console/TaskTriggerCommand.php new file mode 100644 index 0000000..ac1af7f --- /dev/null +++ b/app/Console/TaskTriggerCommand.php @@ -0,0 +1,52 @@ +setName('trigger:tasks') + ->setDescription('Trigger scheduler event for all tasks'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + foreach ($this->getProjectIds() as $project_id) { + $tasks = $this->taskFinderModel->getAll($project_id); + $nb_tasks = count($tasks); + + if ($nb_tasks > 0) { + $output->writeln('Trigger task event: project_id='.$project_id.', nb_tasks='.$nb_tasks); + $this->sendEvent($tasks, $project_id); + } + } + return 0; + } + + private function getProjectIds() + { + $listeners = $this->dispatcher->getListeners(TaskModel::EVENT_DAILY_CRONJOB); + $project_ids = array(); + + foreach ($listeners as $listener) { + $project_ids[] = $listener[0]->getProjectId(); + } + + return array_unique($project_ids); + } + + private function sendEvent(array &$tasks, $project_id) + { + $event = new TaskListEvent(array('project_id' => $project_id)); + $event->setTasks($tasks); + + $this->dispatcher->dispatch($event, TaskModel::EVENT_DAILY_CRONJOB); + } +} diff --git a/app/Console/TransitionExportCommand.php b/app/Console/TransitionExportCommand.php new file mode 100644 index 0000000..16fa5c8 --- /dev/null +++ b/app/Console/TransitionExportCommand.php @@ -0,0 +1,35 @@ +setName('export:transitions') + ->setDescription('Task transitions CSV export') + ->addArgument('project_id', InputArgument::REQUIRED, 'Project id') + ->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)') + ->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $data = $this->transitionExport->export( + $input->getArgument('project_id'), + $input->getArgument('start_date'), + $input->getArgument('end_date') + ); + + if (is_array($data)) { + Csv::output($data); + } + return 0; + } +} diff --git a/app/Console/VersionCommand.php b/app/Console/VersionCommand.php new file mode 100644 index 0000000..76332c9 --- /dev/null +++ b/app/Console/VersionCommand.php @@ -0,0 +1,29 @@ +setName('version') + ->setDescription('Display Kanboard version') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln(APP_VERSION); + return 0; + } +} diff --git a/app/Console/WorkerCommand.php b/app/Console/WorkerCommand.php new file mode 100644 index 0000000..6214d97 --- /dev/null +++ b/app/Console/WorkerCommand.php @@ -0,0 +1,29 @@ +setName('worker') + ->setDescription('Execute queue worker') + ; + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->queueManager->listen(); + return 0; + } +} diff --git a/app/Controller/ActionController.php b/app/Controller/ActionController.php new file mode 100644 index 0000000..43acf59 --- /dev/null +++ b/app/Controller/ActionController.php @@ -0,0 +1,79 @@ +getProject(); + $actions = $this->actionModel->getAllByProject($project['id']); + + $this->response->html($this->helper->layout->project('action/index', array( + 'values' => array('project_id' => $project['id']), + 'project' => $project, + 'actions' => $actions, + 'available_actions' => $this->actionManager->getAvailableActions(), + 'available_events' => $this->eventManager->getAll(), + 'available_params' => $this->actionManager->getAvailableParameters($actions), + 'columns_list' => $this->columnModel->getList($project['id']), + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']), + 'projects_list' => $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId()), + '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') + ))); + } + + /** + * Confirmation dialog before removing an action + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $action = $this->getAction($project); + + $this->response->html($this->helper->layout->project('action/remove', array( + 'action' => $action, + 'available_events' => $this->eventManager->getAll(), + 'available_actions' => $this->actionManager->getAvailableActions(), + 'project' => $project, + 'title' => t('Remove an action') + ))); + } + + /** + * Remove an action + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $action = $this->getAction($project); + + if (! empty($action) && $this->actionModel->remove($action['id'])) { + $this->flash->success(t('Action removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this action.')); + } + + $this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/ActionCreationController.php b/app/Controller/ActionCreationController.php new file mode 100644 index 0000000..2a38c74 --- /dev/null +++ b/app/Controller/ActionCreationController.php @@ -0,0 +1,127 @@ +getProject(); + + $this->response->html($this->template->render('action_creation/create', array( + 'project' => $project, + 'values' => array('project_id' => $project['id']), + 'available_actions' => $this->actionManager->getAvailableActions(), + ))); + } + + /** + * Choose the event according to the action (step 2) + * + * @access public + */ + public function event() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + + if (empty($values['action_name'])) { + return $this->create(); + } + + return $this->response->html($this->template->render('action_creation/event', array( + 'values' => $values, + 'project' => $project, + 'available_actions' => $this->actionManager->getAvailableActions(), + 'events' => $this->actionManager->getCompatibleEvents($values['action_name']), + ))); + } + + /** + * Define action parameters (step 3) + * + * @access public + */ + public function params() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + + if (empty($values['action_name']) || empty($values['event_name'])) { + $this->create(); + return; + } + + $action = $this->actionManager->getAction($values['action_name']); + $action_params = $action->getActionRequiredParameters(); + + if (empty($action_params)) { + $this->doCreation($project, $values + array('params' => array())); + } + + $projects_list = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); + unset($projects_list[$project['id']]); + + $this->response->html($this->template->render('action_creation/params', array( + 'values' => $values, + 'action_params' => $action_params, + 'columns_list' => $this->columnModel->getList($project['id']), + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']), + 'projects_list' => $projects_list, + 'colors_list' => $this->colorModel->getList(), + 'categories_list' => $this->categoryModel->getList($project['id']), + 'links_list' => $this->linkModel->getList(0, false), + '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']), + ))); + } + + /** + * Save the action (last step) + * + * @access public + */ + public function save() + { + $this->doCreation($this->getProject(), $this->request->getValues()); + } + + /** + * Common method to save the action + * + * @access private + * @param array $project Project properties + * @param array $values Form values + */ + private function doCreation(array $project, array $values) + { + $values['project_id'] = $project['id']; + list($valid, ) = $this->actionValidator->validateCreation($values); + + if ($valid) { + if ($this->actionModel->create($values) !== false) { + $this->flash->success(t('Your automatic action has been created successfully.')); + } else { + $this->flash->failure(t('Unable to create your automatic action.')); + } + } + + $this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/ActivityController.php b/app/Controller/ActivityController.php new file mode 100644 index 0000000..50f1e0b --- /dev/null +++ b/app/Controller/ActivityController.php @@ -0,0 +1,62 @@ +getUser(); + + $this->response->html($this->helper->layout->dashboard('activity/user', array( + 'title' => t('Activity stream for %s', $this->helper->user->getFullname($user)), + 'events' => $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id']), 100), + 'user' => $user, + ))); + } + + /** + * Activity page for a project + * + * @access public + */ + public function project() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->app('activity/project', array( + 'title' => t('%s\'s activity', $project['name']), + 'events' => $this->helper->projectActivity->getProjectEvents($project['id']), + 'project' => $project, + ))); + } + + /** + * Display task activities + * + * @access public + */ + public function task() + { + $task = $this->getTask(); + + $this->response->html($this->helper->layout->task('activity/task', array( + 'title' => $task['title'], + 'task' => $task, + 'project' => $this->projectModel->getById($task['project_id']), + 'events' => $this->helper->projectActivity->getTaskEvents($task['id']), + 'tags' => $this->taskTagModel->getTagsByTask($task['id']), + ))); + } +} diff --git a/app/Controller/AnalyticController.php b/app/Controller/AnalyticController.php new file mode 100644 index 0000000..656e673 --- /dev/null +++ b/app/Controller/AnalyticController.php @@ -0,0 +1,192 @@ +getProject(); + list($from, $to) = $this->getDates(); + + $this->response->html($this->helper->layout->analytic('analytic/lead_cycle_time', array( + 'values' => array( + 'from' => $from, + 'to' => $to, + ), + 'project' => $project, + 'average' => $this->averageLeadCycleTimeAnalytic->build($project['id']), + 'metrics' => $this->projectDailyStatsModel->getRawMetrics($project['id'], $from, $to), + 'title' => t('Lead and cycle time'), + ))); + } + + /** + * Show comparison between actual and estimated hours chart + * + * @access public + */ + public function timeComparison() + { + $project = $this->getProject(); + + $paginator = $this->paginator + ->setUrl('AnalyticController', 'timeComparison', array('project_id' => $project['id'])) + ->setMax(30) + ->setOrder(TaskModel::TABLE.'.id') + ->setQuery( + $this->taskQuery + ->withFilter(new TaskProjectFilter($project['id'])) + ->getQuery() + ) + ->calculate(); + + $this->response->html($this->helper->layout->analytic('analytic/time_comparison', array( + 'project' => $project, + 'paginator' => $paginator, + 'metrics' => $this->estimatedTimeComparisonAnalytic->build($project['id']), + 'title' => t('Estimated vs actual time'), + ))); + } + + /** + * Show average time spent by column + * + * @access public + */ + public function averageTimeByColumn() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->analytic('analytic/avg_time_columns', array( + 'project' => $project, + 'metrics' => $this->averageTimeSpentColumnAnalytic->build($project['id']), + 'title' => t('Average time into each column'), + ))); + } + + /** + * Show tasks distribution graph + * + * @access public + */ + public function taskDistribution() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->analytic('analytic/task_distribution', array( + 'project' => $project, + 'metrics' => $this->taskDistributionAnalytic->build($project['id']), + 'title' => t('Task distribution'), + ))); + } + + /** + * Show users repartition + * + * @access public + */ + public function userDistribution() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->analytic('analytic/user_distribution', array( + 'project' => $project, + 'metrics' => $this->userDistributionAnalytic->build($project['id']), + 'title' => t('User repartition'), + ))); + } + + /** + * Show cumulative flow diagram + * + * @access public + */ + public function cfd() + { + $this->commonAggregateMetrics('analytic/cfd', 'total', t('Cumulative flow diagram')); + } + + /** + * Show burndown chart + * + * @access public + */ + public function burndown() + { + $this->commonAggregateMetrics('analytic/burndown', 'score', t('Burndown chart')); + } + + /** + * Estimated vs actual time per column + * + * @access public + */ + public function estimatedVsActualByColumn() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->analytic('analytic/estimated_actual_column', array( + 'project' => $project, + 'metrics' => $this->estimatedActualColumnAnalytic->build($project['id']), + 'title' => t('Estimated vs actual time per column'), + ))); + } + + /** + * Common method for CFD and Burdown chart + * + * @access private + * @param string $template + * @param string $column + * @param string $title + */ + private function commonAggregateMetrics($template, $column, $title) + { + $project = $this->getProject(); + list($from, $to) = $this->getDates(); + + $displayGraph = $this->projectDailyColumnStatsModel->countDays($project['id'], $from, $to) >= 2; + $metrics = $displayGraph ? $this->projectDailyColumnStatsModel->getAggregatedMetrics($project['id'], $from, $to, $column) : array(); + + $this->response->html($this->helper->layout->analytic($template, array( + 'values' => array( + 'from' => $from, + 'to' => $to, + ), + 'display_graph' => $displayGraph, + 'metrics' => $metrics, + 'project' => $project, + 'title' => $title, + ))); + } + + private function getDates() + { + $values = $this->request->getValues(); + + $from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week'))); + $to = $this->request->getStringParam('to', date('Y-m-d')); + + if (! empty($values)) { + $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 new file mode 100644 index 0000000..645cae7 --- /dev/null +++ b/app/Controller/AppController.php @@ -0,0 +1,47 @@ +request->isAjax()) { + $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, + )), 403); + } + } + + /** + * Page not found + * + * @access public + * @param boolean $withoutLayout + */ + public function notFound($withoutLayout = false) + { + $this->response->html($this->helper->layout->app('app/notfound', array( + 'title' => t('Page not found'), + 'no_layout' => $withoutLayout, + ))); + } +} diff --git a/app/Controller/AuthController.php b/app/Controller/AuthController.php new file mode 100644 index 0000000..83c93bb --- /dev/null +++ b/app/Controller/AuthController.php @@ -0,0 +1,78 @@ +userSession->isLogged()) { + $this->response->redirect($this->helper->url->to('DashboardController', 'show')); + } else { + $showCaptcha = false; + if (! empty($values['username']) && $this->userLockingModel->hasCaptcha($values['username'])) { + $showCaptcha = true; + } elseif ($this->captchaModel->isLocked($this->request->getIpAddress())) { + $showCaptcha = true; + } + $this->response->html($this->helper->layout->app('auth/index', array( + 'captcha' => $showCaptcha, + 'errors' => $errors, + 'values' => $values, + 'no_layout' => true, + 'title' => t('Login') + ))); + } + } + + /** + * Check credentials + * + * @access public + */ + public function check() + { + $values = $this->request->getValues(); + + if (REMEMBER_ME_AUTH) { + session_set('hasRememberMe', ! empty($values['remember_me'])); + } + + list($valid, $errors) = $this->authValidator->validateForm($values); + + if ($valid) { + $this->redirectAfterLogin(); + } else { + $this->login($values, $errors); + } + } + + /** + * Logout and destroy session + * + * @access public + */ + public function logout() + { + if (! DISABLE_LOGOUT) { + $this->checkCSRFParam(); + $this->sessionManager->close(); + $this->response->redirect($this->helper->url->to('AuthController', 'login')); + } else { + $this->response->redirect($this->helper->url->to('DashboardController', 'show')); + } + } +} diff --git a/app/Controller/AvatarFileController.php b/app/Controller/AvatarFileController.php new file mode 100644 index 0000000..de5267c --- /dev/null +++ b/app/Controller/AvatarFileController.php @@ -0,0 +1,122 @@ +getUser(); + + $this->response->html($this->helper->layout->user('avatar_file/show', array( + 'user' => $user, + ))); + } + + /** + * Upload Avatar + */ + public function upload() + { + $this->checkCSRFParam(); + $user = $this->getUser(); + + if (! $this->request->getFileInfo('avatar')['name']) { + $this->flash->failure(t('You must select a file to upload as your avatar!')); + } elseif (! $this->avatarFileModel->isAvatarImage($this->request->getFileInfo('avatar')['name'])) { + $this->flash->failure(t('The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)')); + } else { + if (! $this->avatarFileModel->uploadImageFile($user['id'], $this->request->getFileInfo('avatar'))) { + $this->flash->failure(t('Unable to upload files, check the permissions of your data folder.')); + } + } + + $this->renderResponse($user['id']); + } + + /** + * Remove Avatar image + */ + public function remove() + { + $this->checkCSRFParam(); + $user = $this->getUser(); + $this->avatarFileModel->remove($user['id']); + $this->userSession->refresh($user['id']); + $this->renderResponse($user['id']); + } + + /** + * Show Avatar image (public) + */ + public function image() + { + $user_id = $this->request->getIntegerParam('user_id'); + $size = $this->request->getStringParam('size', 48); + $hash = $this->request->getStringParam('hash'); + + if ($size > 100) { + $this->response->status(400); + return; + } + + $filename = $this->avatarFileModel->getFilename($user_id); + $etag = md5($filename.$size); + + if ($hash !== $etag) { + $this->response->status(404); + return; + } + + $this->response->withCache(365 * 86400, $etag); + $this->response->withContentType('image/png'); + + if ($this->request->getHeader('If-None-Match') !== '"'.$etag.'"') { + $this->response->send(); + $this->render($filename, $size); + } else { + $this->response->status(304); + } + } + + /** + * Render thumbnail from object storage + * + * @access private + * @param string $filename + * @param integer $size + */ + private function render($filename, $size) + { + try { + $blob = $this->objectStorage->get($filename); + + Thumbnail::createFromString($blob) + ->resize($size, $size) + ->toOutput(); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + } + } + + protected function renderResponse($userId) + { + if ($this->request->isAjax()) { + $this->show(); + } else { + $this->response->redirect($this->helper->url->to('AvatarFileController', 'show', array('user_id' => $userId))); + } + } +} diff --git a/app/Controller/BaseController.php b/app/Controller/BaseController.php new file mode 100644 index 0000000..e364025 --- /dev/null +++ b/app/Controller/BaseController.php @@ -0,0 +1,338 @@ +token->validateCSRFToken($this->request->getStringParam('csrf_token'))) { + throw new AccessForbiddenException(); + } + } + + protected function checkReusableCSRFParam() + { + if (! $this->token->validateReusableCSRFToken($this->request->getRawValue('csrf_token'))) { + throw new AccessForbiddenException(); + } + } + + protected function checkReusableGETCSRFParam() + { + if (! $this->token->validateReusableCSRFToken($this->request->getStringParam('csrf_token'))) { + throw new AccessForbiddenException(); + } + } + + protected function checkCSRFForm() + { + if (! $this->token->validateCSRFToken($this->request->getRawValue('csrf_token'))) { + throw new AccessForbiddenException(); + } + } + + /** + * Check webhook token + * + * @access protected + */ + protected function checkWebhookToken() + { + if (! hash_equals($this->configModel->get('webhook_token'), $this->request->getStringParam('token'))) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + } + + /** + * Common method to get a task for task views + * + * @access protected + * @return array + * @throws PageNotFoundException + * @throws AccessForbiddenException + */ + protected function getTask() + { + $project_id = $this->request->getIntegerParam('project_id'); + $task = $this->taskFinderModel->getDetails($this->request->getIntegerParam('task_id')); + + if (empty($task)) { + throw new PageNotFoundException(); + } + + if ($project_id !== 0 && $project_id != $task['project_id']) { + throw new AccessForbiddenException(); + } + + return $task; + } + + /** + * Get Task or Project file + * + * @access protected + * @return array + * @throws PageNotFoundException + */ + protected function getFile() + { + $project_id = $this->request->getIntegerParam('project_id'); + $task_id = $this->request->getIntegerParam('task_id'); + $file_id = $this->request->getIntegerParam('file_id'); + $model = 'projectFileModel'; + + if ($task_id > 0) { + $model = 'taskFileModel'; + } + + $file = $this->$model->getById($file_id); + + if (empty($file)) { + throw new PageNotFoundException(); + } + + if (isset($file['task_id']) && $file['task_id'] != $task_id) { + throw new PageNotFoundException(); + } + + if (isset($file['project_id']) && $file['project_id'] != $project_id) { + throw new PageNotFoundException(); + } + + $file['model'] = $model; + return $file; + } + + /** + * Common method to get a project + * + * @access protected + * @param integer $project_id Default project id + * @return array + * @throws PageNotFoundException + */ + protected function getProject($project_id = 0) + { + $project_id = $this->request->getIntegerParam('project_id', $project_id); + $project = $this->projectModel->getByIdWithOwnerAndTaskCount($project_id); + + if (empty($project)) { + throw new PageNotFoundException(); + } + + return $project; + } + + /** + * Common method to get the user + * + * @access protected + * @return array + * @throws PageNotFoundException + * @throws AccessForbiddenException + */ + protected function getUser() + { + $user = $this->userModel->getById($this->request->getIntegerParam('user_id', $this->userSession->getId())); + + if (empty($user)) { + throw new PageNotFoundException(); + } + + if (! $this->userSession->isAdmin() && $this->userSession->getId() != $user['id']) { + // Always returns a 404 otherwise people might guess which user exist. + throw new PageNotFoundException(); + } + + return $user; + } + + protected function getSubtask(array $task) + { + $subtask = $this->subtaskModel->getById($this->request->getIntegerParam('subtask_id')); + + if (empty($subtask)) { + throw new PageNotFoundException(); + } + + if ($subtask['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $subtask; + } + + protected function getComment(array $task) + { + $comment = $this->commentModel->getById($this->request->getIntegerParam('comment_id')); + + if (empty($comment)) { + throw new PageNotFoundException(); + } + + if (! $this->userSession->isAdmin() && $comment['user_id'] != $this->userSession->getId()) { + throw new AccessForbiddenException(); + } + + if ($comment['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $comment; + } + + protected function getExternalTaskLink(array $task) + { + $link = $this->taskExternalLinkModel->getById($this->request->getIntegerParam('link_id')); + + if (empty($link)) { + throw new PageNotFoundException(); + } + + if ($link['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $link; + } + + protected function getInternalTaskLink(array $task) + { + $link = $this->taskLinkModel->getById($this->request->getIntegerParam('link_id')); + + if (empty($link)) { + throw new PageNotFoundException(); + } + + if ($link['task_id'] != $task['id']) { + throw new AccessForbiddenException(); + } + + return $link; + } + + protected function getColumn(array $project) + { + $column = $this->columnModel->getById($this->request->getIntegerParam('column_id')); + + if (empty($column)) { + throw new PageNotFoundException(); + } + + if ($column['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $column; + } + + protected function getSwimlane(array $project) + { + $swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id')); + + if (empty($swimlane)) { + throw new PageNotFoundException(); + } + + if ($swimlane['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $swimlane; + } + + protected function getCategory(array $project) + { + $category = $this->categoryModel->getById($this->request->getIntegerParam('category_id')); + + if (empty($category)) { + throw new PageNotFoundException(); + } + + if ($category['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $category; + } + + protected function getProjectTag(array $project) + { + $tag = $this->tagModel->getById($this->request->getIntegerParam('tag_id')); + + if (empty($tag)) { + throw new PageNotFoundException(); + } + + if ($tag['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $tag; + } + + protected function getAction(array $project) + { + $action = $this->actionModel->getById($this->request->getIntegerParam('action_id')); + + if (empty($action)) { + throw new PageNotFoundException(); + } + + if ($action['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $action; + } + + protected function getCustomFilter(array $project) + { + $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + + if (empty($filter)) { + throw new PageNotFoundException(); + } + + if ($filter['project_id'] != $project['id']) { + throw new AccessForbiddenException(); + } + + return $filter; + } + + /** + * Redirect the user after the authentication + * + * @access protected + */ + protected function redirectAfterLogin() + { + $redirectUri = $this->helper->url->to('DashboardController', 'show'); + + if (session_exists('redirectAfterLogin')) { + if ($this->request->isSafeRedirectUri(session_get('redirectAfterLogin'))) { + $redirectUri = session_get('redirectAfterLogin'); + } + session_remove('redirectAfterLogin'); + } + + $this->response->redirect($redirectUri); + } +} diff --git a/app/Controller/BoardAjaxController.php b/app/Controller/BoardAjaxController.php new file mode 100644 index 0000000..3f726b6 --- /dev/null +++ b/app/Controller/BoardAjaxController.php @@ -0,0 +1,150 @@ +checkReusableGETCSRFParam(); + $project_id = $this->request->getIntegerParam('project_id'); + + if (! $project_id || ! $this->request->isAjax()) { + throw new AccessForbiddenException(); + } + + $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")); + } + + try { + $result =$this->taskPositionModel->movePosition( + $project_id, + $values['task_id'], + $values['dst_column_id'], + $values['position'], + $values['swimlane_id'] + ); + + if (! $result) { + $this->response->status(400); + } else { + $this->response->html($this->renderBoard($project_id), 201); + } + } catch (Exception $e) { + $this->response->html('
'.$e->getMessage().'
'); + } + } + + /** + * Check if the board have been changed + * + * @access public + */ + public function check() + { + $project_id = $this->request->getIntegerParam('project_id'); + $timestamp = $this->request->getIntegerParam('timestamp'); + + if (! $project_id || ! $this->request->isAjax()) { + throw new AccessForbiddenException(); + } elseif (! $this->projectModel->isModifiedSince($project_id, $timestamp)) { + $this->response->status(304); + } else { + $this->response->html($this->renderBoard($project_id)); + } + } + + /** + * Reload the board with new filters + * + * @access public + */ + public function reload() + { + $project_id = $this->request->getIntegerParam('project_id'); + + if (! $project_id || ! $this->request->isAjax()) { + throw new AccessForbiddenException(); + } + + $values = $this->request->getJson(); + $this->userSession->setFilters($project_id, empty($values['search']) ? '' : $values['search']); + + $this->response->html($this->renderBoard($project_id)); + } + + /** + * Enable collapsed mode + * + * @access public + */ + public function collapse() + { + $this->changeDisplayMode(1); + } + + /** + * Enable expanded mode + * + * @access public + */ + public function expand() + { + $this->changeDisplayMode(0); + } + + /** + * Change display mode + * + * @access private + * @param int $mode + */ + private function changeDisplayMode($mode) + { + $project_id = $this->request->getIntegerParam('project_id'); + $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, $mode); + + if ($this->request->isAjax()) { + $this->response->html($this->renderBoard($project_id)); + } else { + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project_id))); + } + } + + /** + * Render board + * + * @access protected + * @param integer $project_id + * @return string + */ + protected function renderBoard($project_id) + { + return $this->template->render('board/table_container', array( + 'project' => $this->projectModel->getById($project_id), + 'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->configModel->get('board_highlight_period'), + 'swimlanes' => $this->taskLexer + ->build($this->userSession->getFilters($project_id)) + ->format($this->boardFormatter->withProjectId($project_id)) + )); + } +} diff --git a/app/Controller/BoardPopoverController.php b/app/Controller/BoardPopoverController.php new file mode 100644 index 0000000..bbbe815 --- /dev/null +++ b/app/Controller/BoardPopoverController.php @@ -0,0 +1,47 @@ +getProject(); + $column_id = $this->request->getIntegerParam('column_id'); + $swimlane_id = $this->request->getIntegerParam('swimlane_id'); + + $this->response->html($this->template->render('board_popover/close_all_tasks_column', array( + 'project' => $project, + 'nb_tasks' => $this->taskFinderModel->countByColumnAndSwimlaneId($project['id'], $column_id, $swimlane_id), + 'column' => $this->columnModel->getColumnTitleById($column_id), + 'swimlane' => $this->swimlaneModel->getNameById($swimlane_id), + 'values' => array('column_id' => $column_id, 'swimlane_id' => $swimlane_id), + ))); + } + + /** + * Close all column tasks + * + * @access public + */ + public function closeColumnTasks() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $this->taskStatusModel->closeTasksBySwimlaneAndColumn($values['swimlane_id'], $values['column_id']); + $this->flash->success(t('All tasks of the column "%s" and the swimlane "%s" have been closed successfully.', $this->columnModel->getColumnTitleById($values['column_id']), $this->swimlaneModel->getNameById($values['swimlane_id']))); + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/BoardTooltipController.php b/app/Controller/BoardTooltipController.php new file mode 100644 index 0000000..8824bf9 --- /dev/null +++ b/app/Controller/BoardTooltipController.php @@ -0,0 +1,112 @@ +getTask(); + $this->response->html($this->template->render('board/tooltip_tasklinks', array( + 'links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']), + 'task' => $task, + ))); + } + + /** + * Get links on mouseover + * + * @access public + */ + public function externallinks() + { + $task = $this->getTask(); + $this->response->html($this->template->render('board/tooltip_external_links', array( + 'links' => $this->taskExternalLinkModel->getAll($task['id']), + 'task' => $task, + ))); + } + + /** + * Get subtasks on mouseover + * + * @access public + */ + public function subtasks() + { + $task = $this->getTask(); + $this->response->html($this->template->render('board/tooltip_subtasks', array( + 'subtasks' => $this->subtaskModel->getAll($task['id']), + 'task' => $task, + ))); + } + + /** + * Display all attachments during the task mouseover + * + * @access public + */ + public function attachments() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('board/tooltip_files', array( + 'files' => $this->taskFileModel->getAll($task['id']), + 'task' => $task, + ))); + } + + /** + * Display task description + * + * @access public + */ + public function description() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('board/tooltip_description', array( + 'task' => $task + ))); + } + + /** + * Get recurrence information on mouseover + * + * @access public + */ + public function recurrence() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('task_recurrence/info', array( + 'task' => $task, + 'recurrence_trigger_list' => $this->taskRecurrenceModel->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->taskRecurrenceModel->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->taskRecurrenceModel->getRecurrenceBasedateList(), + ))); + } + + /** + * Display swimlane description in tooltip + * + * @access public + */ + public function swimlane() + { + $this->getProject(); + $swimlane = $this->swimlaneModel->getById($this->request->getIntegerParam('swimlane_id')); + $this->response->html($this->template->render('board/tooltip_description', array('task' => $swimlane))); + } +} diff --git a/app/Controller/BoardViewController.php b/app/Controller/BoardViewController.php new file mode 100644 index 0000000..98dd348 --- /dev/null +++ b/app/Controller/BoardViewController.php @@ -0,0 +1,72 @@ +request->getStringParam('token'); + $project = $this->projectModel->getByToken($token); + + if (empty($project)) { + 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' => $this->boardFormatter + ->withProjectId($project['id']) + ->withQuery($query) + ->format(), + 'title' => $project['name'], + 'description' => $project['description'], + 'no_layout' => true, + 'not_editable' => true, + 'board_public_refresh_interval' => $this->configModel->get('board_public_refresh_interval'), + 'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->configModel->get('board_highlight_period'), + ))); + } + + /** + * Show a board for a given project + * + * @access public + */ + public function show() + { + $project = $this->getProject(); + $search = $this->helper->projectHeader->getSearchQuery($project); + + $this->response->html($this->helper->layout->app('board/view_private', array( + 'project' => $project, + 'title' => $project['name'], + 'description' => $this->helper->projectHeader->getDescription($project), + 'board_private_refresh_interval' => $this->configModel->get('board_private_refresh_interval'), + 'board_highlight_period' => $this->configModel->get('board_highlight_period'), + 'swimlanes' => $this->taskLexer + ->build($search) + ->format($this->boardFormatter->withProjectId($project['id'])) + ))); + } +} diff --git a/app/Controller/CaptchaController.php b/app/Controller/CaptchaController.php new file mode 100644 index 0000000..5b4ea61 --- /dev/null +++ b/app/Controller/CaptchaController.php @@ -0,0 +1,29 @@ +response->withContentType('image/jpeg')->send(); + + $builder = new CaptchaBuilder; + $builder->build(); + session_set('captcha', $builder->getPhrase()); + $builder->output(); + } +} diff --git a/app/Controller/CategoryController.php b/app/Controller/CategoryController.php new file mode 100644 index 0000000..c27533b --- /dev/null +++ b/app/Controller/CategoryController.php @@ -0,0 +1,161 @@ +getProject(); + + $this->response->html($this->helper->layout->project('category/index', array( + 'categories' => $this->categoryModel->getAll($project['id']), + 'project' => $project, + 'colors' => $this->colorModel->getList(), + 'title' => t('Categories'), + ))); + } + + /** + * Show form to create new category + * + * @param array $values + * @param array $errors + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->template->render('category/create', array( + 'values' => $values + array('project_id' => $project['id']), + 'colors' => $this->colorModel->getList(), + 'errors' => $errors, + 'project' => $project, + ))); + } + + /** + * Validate and save a new category + * + * @access public + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + + list($valid, $errors) = $this->categoryValidator->validateCreation($values); + + if ($valid) { + if ($this->categoryModel->create($values) !== false) { + $this->flash->success(t('Your category has been created successfully.')); + $this->response->redirect($this->helper->url->to('CategoryController', 'index', array('project_id' => $project['id'])), true); + return; + } else { + $errors = array('name' => array(t('Another category with the same name exists in this project'))); + } + } + + $this->create($values, $errors); + } + + /** + * Edit a category (display the form) + * + * @access public + * @param array $values + * @param array $errors + * @throws PageNotFoundException + */ + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $category = $this->getCategory($project); + + $this->response->html($this->template->render('category/edit', array( + 'values' => empty($values) ? $category : $values, + 'colors' => $this->colorModel->getList(), + 'errors' => $errors, + 'project' => $project, + ))); + } + + /** + * Edit a category (validate the form and update the database) + * + * @access public + */ + public function update() + { + $project = $this->getProject(); + $category = $this->getCategory($project); + + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['id'] = $category['id']; + + list($valid, $errors) = $this->categoryValidator->validateModification($values); + + if ($valid) { + if ($this->categoryModel->update($values)) { + $this->flash->success(t('This category has been updated successfully.')); + return $this->response->redirect($this->helper->url->to('CategoryController', 'index', array('project_id' => $project['id']))); + } else { + $this->flash->failure(t('Unable to update this category.')); + } + } + + return $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a category + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $category = $this->getCategory($project); + + $this->response->html($this->helper->layout->project('category/remove', array( + 'project' => $project, + 'category' => $category, + ))); + } + + /** + * Remove a category + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $category = $this->getCategory($project); + + if ($this->categoryModel->remove($category['id'])) { + $this->flash->success(t('Category removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this category.')); + } + + $this->response->redirect($this->helper->url->to('CategoryController', 'index', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/ColumnController.php b/app/Controller/ColumnController.php new file mode 100644 index 0000000..73a4667 --- /dev/null +++ b/app/Controller/ColumnController.php @@ -0,0 +1,200 @@ +getProject(); + $columns = $this->columnModel->getAllWithTaskCount($project['id']); + + $this->response->html($this->helper->layout->project('column/index', array( + 'columns' => $columns, + 'project' => $project, + 'title' => t('Edit columns') + ))); + } + + /** + * Show form to create a new column + * + * @access public + * @param array $values + * @param array $errors + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + if (empty($values)) { + $values = array('project_id' => $project['id']); + } + + $this->response->html($this->template->render('column/create', array( + 'values' => $values, + 'errors' => $errors, + 'project' => $project, + ))); + } + + /** + * Validate and add a new column + * + * @access public + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues() + array('hide_in_dashboard' => 0); + $values['project_id'] = $project['id']; + + list($valid, $errors) = $this->columnValidator->validateCreation($values); + + if ($valid) { + $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.')); + $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')); + } + } + + $this->create($values, $errors); + } + + /** + * Display a form to edit a column + * + * @access public + * @param array $values + * @param array $errors + */ + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $column = $this->getColumn($project); + + $this->response->html($this->helper->layout->project('column/edit', array( + 'errors' => $errors, + 'values' => $values ?: $column, + 'project' => $project, + 'column' => $column, + ))); + } + + /** + * Validate and update a column + * + * @access public + */ + public function update() + { + $project = $this->getProject(); + $column = $this->getColumn($project); + + $values = $this->request->getValues() + array('hide_in_dashboard' => 0); + $values['project_id'] = $project['id']; + $values['id'] = $column['id']; + + list($valid, $errors) = $this->columnValidator->validateModification($values); + + if ($valid) { + $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.')); + $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.')); + } + } + + $this->edit($values, $errors); + } + + /** + * Move column position + * + * @access public + */ + public function move() + { + $this->checkReusableGETCSRFParam(); + $project = $this->getProject(); + $values = $this->request->getJson(); + + if (! empty($values) && isset($values['column_id']) && isset($values['position'])) { + $result = $this->columnModel->changePosition($project['id'], $values['column_id'], $values['position']); + $this->response->json(array('result' => $result)); + } else { + throw new AccessForbiddenException(); + } + } + + /** + * Confirm column suppression + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $column = $this->getColumn($project); + + $this->response->html($this->helper->layout->project('column/remove', array( + 'column' => $column, + 'project' => $project, + ))); + } + + /** + * Remove a column + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $column = $this->getColumn($project); + + if ($this->columnModel->remove($column['id'])) { + $this->flash->success(t('Column removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this column.')); + } + + $this->response->redirect($this->helper->url->to('ColumnController', 'index', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/ColumnMoveRestrictionController.php b/app/Controller/ColumnMoveRestrictionController.php new file mode 100644 index 0000000..9a75bf7 --- /dev/null +++ b/app/Controller/ColumnMoveRestrictionController.php @@ -0,0 +1,103 @@ +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'], + isset($values['only_assigned']) && $values['only_assigned'] == 1 + ); + + 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 0000000..ce2a1ca --- /dev/null +++ b/app/Controller/ColumnRestrictionController.php @@ -0,0 +1,103 @@ +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 new file mode 100644 index 0000000..fb6af21 --- /dev/null +++ b/app/Controller/CommentController.php @@ -0,0 +1,178 @@ +getTask(); + $values['project_id'] = $task['project_id']; + + $this->response->html($this->helper->layout->task('comment/create', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task + ))); + } + + /** + * Add a comment + * + * @access public + */ + public function save() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $values['user_id'] = $this->userSession->getId(); + + list($valid, $errors) = $this->commentValidator->validateCreation($values); + + if ($valid) { + if ($this->commentModel->create($values) !== false) { + $this->flash->success(t('Comment added successfully.')); + } else { + $this->flash->failure(t('Unable to create your comment.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true); + } else { + $this->create($values, $errors); + } + } + + /** + * Edit a comment + * + * @access public + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + * @throws PageNotFoundException + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + $comment = $this->getComment($task); + + if (empty($values)) { + $values = $comment; + } + + $values['project_id'] = $task['project_id']; + + $this->response->html($this->template->render('comment/edit', array( + 'values' => $values, + 'errors' => $errors, + 'comment' => $comment, + 'task' => $task, + ))); + } + + /** + * Update and validate a comment + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $comment = $this->getComment($task); + + $values = $this->request->getValues(); + $values['id'] = $comment['id']; + $values['task_id'] = $task['id']; + $values['user_id'] = $comment['user_id']; + + list($valid, $errors) = $this->commentValidator->validateModification($values); + + if ($valid) { + if ($this->commentModel->update($values)) { + $this->flash->success(t('Comment updated successfully.')); + } else { + $this->flash->failure(t('Unable to update your comment.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + return; + } + + $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a comment + * + * @access public + */ + public function confirm() + { + $task = $this->getTask(); + $comment = $this->getComment($task); + + $this->response->html($this->template->render('comment/remove', array( + 'comment' => $comment, + 'task' => $task, + 'title' => t('Remove a comment') + ))); + } + + /** + * Remove a comment + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $task = $this->getTask(); + $comment = $this->getComment($task); + + if ($this->commentModel->remove($comment['id'])) { + $this->flash->success(t('Comment removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this comment.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true); + } + + /** + * Toggle comment sorting + * + * @access public + */ + public function toggleSorting() + { + $this->checkReusableGETCSRFParam(); + $task = $this->getTask(); + $this->helper->comment->toggleSorting(); + + $this->response->redirect($this->helper->url->to( + 'TaskViewController', + 'show', + array('task_id' => $task['id']), + 'comments' + )); + } +} diff --git a/app/Controller/CommentListController.php b/app/Controller/CommentListController.php new file mode 100644 index 0000000..f9e66df --- /dev/null +++ b/app/Controller/CommentListController.php @@ -0,0 +1,48 @@ +getTask(); + $commentSortingDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC'); + + $this->response->html($this->template->render('comment_list/show', array( + 'task' => $task, + 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection), + 'editable' => $this->helper->user->hasProjectAccess('CommentController', 'edit', $task['project_id']), + ))); + } + + public function save() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $values['user_id'] = $this->userSession->getId(); + + list($valid, ) = $this->commentValidator->validateCreation($values); + + if ($valid && $this->commentModel->create($values) !== false) { + $this->flash->success(t('Comment added successfully.')); + } + + $this->show(); + } + + public function toggleSorting() + { + $this->helper->comment->toggleSorting(); + $this->show(); + } +} diff --git a/app/Controller/CommentMailController.php b/app/Controller/CommentMailController.php new file mode 100644 index 0000000..575b850 --- /dev/null +++ b/app/Controller/CommentMailController.php @@ -0,0 +1,74 @@ +getTask(); + + $this->response->html($this->helper->layout->task('comment_mail/create', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'members' => $this->projectPermissionModel->getMembersWithEmail($task['project_id']), + ))); + } + + public function save() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $values['user_id'] = $this->userSession->getId(); + + list($valid, $errors) = $this->commentValidator->validateEmailCreation($values); + + if ($valid) { + $this->sendByEmail($values, $task); + $values = $this->prepareComment($values); + + if ($this->commentModel->create($values) !== false) { + $this->flash->success(t('Comment sent by email successfully.')); + } else { + $this->flash->failure(t('Unable to create your comment.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true); + } else { + $this->create($values, $errors); + } + } + + protected function sendByEmail(array $values, array $task) + { + $html = $this->template->render('comment_mail/email', array('email' => $values, 'task' => $task)); + $emails = explode_csv_field($values['emails']); + + foreach ($emails as $email) { + $this->emailClient->send( + $email, + $email, + $values['subject'], + $html + ); + } + } + + protected function prepareComment(array $values) + { + $values['comment'] .= "\n\n_".t('Sent by email to "%s" (%s)', $values['emails'], $values['subject']).'_'; + + unset($values['subject']); + unset($values['emails']); + + return $values; + } +} diff --git a/app/Controller/ConfigController.php b/app/Controller/ConfigController.php new file mode 100644 index 0000000..227b3d4 --- /dev/null +++ b/app/Controller/ConfigController.php @@ -0,0 +1,254 @@ +response->html($this->helper->layout->config('config/about', array( + 'db_size' => $this->configModel->getDatabaseSize(), + 'db_version' => $this->db->getDriver()->getDatabaseVersion(), + 'db_options' => $this->configModel->getDatabaseOptions(), + 'user_agent' => $this->request->getServerVariable('HTTP_USER_AGENT'), + 'title' => t('Settings').' > '.t('About'), + ))); + } + + /** + * Save settings + * + */ + public function save() + { + $values = $this->request->getValues(); + $redirect = $this->request->getStringParam('redirect', 'application'); + + switch ($redirect) { + case 'application': + $values += array('password_reset' => 0, 'notifications_enabled' => 0); + break; + case 'project': + $values += array( + 'subtask_restriction' => 0, + 'subtask_time_tracking' => 0, + 'cfd_include_closed_tasks' => 0, + 'disable_private_project' => 0, + ); + break; + } + + list($valid, $errors) = $this->configValidator->validate($values); + + if (!$valid) { + switch ($redirect) { + case 'email': + return $this->email($values, $errors); + case 'project': + return $this->project($values, $errors); + case 'board': + return $this->board($values, $errors); + default: + return $this->application($values, $errors); + } + } + + if ($this->configModel->save($values)) { + $this->languageModel->loadCurrentLanguage(); + $this->flash->success(t('Settings saved successfully.')); + } else { + $this->flash->failure(t('Unable to save your settings.')); + } + + $this->response->redirect($this->helper->url->to('ConfigController', $redirect)); + } + + /** + * Display the application settings page + * + * @access public + */ + public function application(array $values = [], array $errors = []) + { + $this->response->html($this->helper->layout->config('config/application', array( + 'mail_transports' => $this->emailClient->getAvailableTransports(), + 'languages' => $this->languageModel->getLanguages(), + 'timezones' => $this->timezoneModel->getTimezones(), + 'date_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getDateFormats(true)), + 'time_formats' => $this->dateParser->getAvailableFormats($this->dateParser->getTimeFormats()), + 'title' => t('Settings').' > '.t('Application settings'), + 'errors' => $errors, + 'values' => $values, + ))); + } + + /** + * Display the email settings page + * + * @access public + */ + public function email(array $values = [], array $errors = []) + { + if (empty($values)) { + $values = $this->configModel->getAll(); + } + + if (empty($values['mail_transport'])) { + $values['mail_transport'] = MAIL_TRANSPORT; + } + + $this->response->html($this->helper->layout->config('config/email', array( + 'values' => $values, + 'mail_transports' => $this->emailClient->getAvailableTransports(), + 'title' => t('Settings').' > '.t('Email settings'), + 'errors' => $errors, + ))); + } + + /** + * Display the project settings page + * + * @access public + */ + public function project(array $values = [], array $errors = []) + { + $this->response->html($this->helper->layout->config('config/project', array( + 'colors' => $this->colorModel->getList(), + 'default_columns' => implode(', ', $this->boardModel->getDefaultColumns()), + 'title' => t('Settings').' > '.t('Project settings'), + 'errors' => $errors, + 'values' => $values, + ))); + } + + /** + * Display the board settings page + * + * @access public + */ + public function board(array $values = [], array $errors = []) + { + $this->response->html($this->helper->layout->config('config/board', array( + 'title' => t('Settings').' > '.t('Board settings'), + 'errors' => $errors, + 'values' => $values, + ))); + } + + /** + * Display the integration settings page + * + * @access public + */ + public function integrations() + { + $this->response->html($this->helper->layout->config('config/integrations', array( + 'title' => t('Settings').' > '.t('Integrations'), + ))); + } + + /** + * Display the webhook settings page + * + * @access public + */ + public function webhook() + { + $this->response->html($this->helper->layout->config('config/webhook', array( + 'title' => t('Settings').' > '.t('Webhook settings'), + ))); + } + + /** + * Display the api settings page + * + * @access public + */ + public function api() + { + $this->response->html($this->helper->layout->config('config/api', array( + 'title' => t('Settings').' > '.t('API'), + ))); + } + + /** + * Download the Sqlite database + * + * @access public + */ + public function downloadDb() + { + $this->checkCSRFParam(); + $this->response->withFileDownload('db.sqlite.gz'); + $this->response->binary($this->configModel->downloadDatabase()); + } + + /** + * Optimize the Sqlite database + * + * @access public + */ + public function optimizeDb() + { + $this->checkCSRFParam(); + $this->configModel->optimizeDatabase(); + $this->flash->success(t('Database optimization done.')); + $this->response->redirect($this->helper->url->to('ConfigController', 'index')); + } + + /** + * Display the Sqlite database upload page + * + * @access public + */ + public function uploadDb() + { + $this->response->html($this->template->render('config/upload_db')); + } + + /** + * Replace current Sqlite db with uploaded file + * + * @access public + */ + public function saveUploadedDb() + { + $this->checkCSRFParam(); + $filename = $this->request->getFilePath('file'); + + if (!file_exists($filename) || !$this->configModel->uploadDatabase($filename)) { + $this->flash->failure(t('Unable to read uploaded file.')); + } else { + $this->flash->success(t('Database uploaded successfully.')); + } + + $this->response->redirect($this->helper->url->to('ConfigController', 'index')); + } + + /** + * Regenerate webhook token + * + * @access public + */ + public function token() + { + $type = $this->request->getStringParam('type'); + + $this->checkCSRFParam(); + $this->configModel->regenerateToken($type.'_token'); + + $this->flash->success(t('Token regenerated.')); + $this->response->redirect($this->helper->url->to('ConfigController', $type)); + } +} diff --git a/app/Controller/CronjobController.php b/app/Controller/CronjobController.php new file mode 100644 index 0000000..f5cb02a --- /dev/null +++ b/app/Controller/CronjobController.php @@ -0,0 +1,32 @@ +checkWebhookToken(); + + $input = new ArrayInput(array( + 'command' => 'cronjob', + )); + $output = new NullOutput(); + + $this->cli->setAutoExit(false); + $this->cli->run($input, $output); + + $this->response->html('Cronjob executed'); + } +} diff --git a/app/Controller/CurrencyController.php b/app/Controller/CurrencyController.php new file mode 100644 index 0000000..513dd98 --- /dev/null +++ b/app/Controller/CurrencyController.php @@ -0,0 +1,104 @@ +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') . ' > ' . t('Currency rates'), + ))); + } + + /** + * Add or change currency rate + * + * @access public + * @param array $values + * @param array $errors + */ + public function create(array $values = array(), array $errors = array()) + { + $this->response->html($this->template->render('currency/create', array( + 'values' => $values, + 'errors' => $errors, + 'currencies' => $this->currencyModel->getCurrencies(), + ))); + } + + /** + * Validate and save a new currency rate + * + * @access public + */ + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->currencyValidator->validateCreation($values); + + if ($valid) { + if ($this->currencyModel->create($values['currency'], $values['rate'])) { + $this->flash->success(t('The currency rate has been added successfully.')); + $this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true); + return; + } else { + $this->flash->failure(t('Unable to add this currency rate.')); + } + } + + $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(), + ))); + } + + /** + * Save reference currency + * + * @access public + */ + public function update() + { + $values = $this->request->getValues(); + + if ($this->configModel->save($values)) { + $this->flash->success(t('Settings saved successfully.')); + } else { + $this->flash->failure(t('Unable to save your settings.')); + } + + $this->response->redirect($this->helper->url->to('CurrencyController', 'show'), true); + } +} diff --git a/app/Controller/CustomFilterController.php b/app/Controller/CustomFilterController.php new file mode 100644 index 0000000..a08d777 --- /dev/null +++ b/app/Controller/CustomFilterController.php @@ -0,0 +1,193 @@ +getProject(); + + $this->response->html($this->helper->layout->project('custom_filter/index', array( + 'project' => $project, + 'custom_filters' => $this->customFilterModel->getAll($project['id'], $this->userSession->getId()), + 'title' => t('Custom filters'), + ))); + } + + /** + * 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 + */ + public function save() + { + $project = $this->getProject(); + + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['user_id'] = $this->userSession->getId(); + + list($valid, $errors) = $this->customFilterValidator->validateCreation($values); + + if ($valid) { + if ($this->customFilterModel->create($values) !== false) { + $this->flash->success(t('Your custom filter has been created successfully.')); + $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.')); + } + } + + $this->create($values, $errors); + } + + /** + * Confirmation dialog before removing a custom filter + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $filter = $this->getCustomFilter($project); + + $this->response->html($this->helper->layout->project('custom_filter/remove', array( + 'project' => $project, + 'filter' => $filter, + 'title' => t('Remove a custom filter') + ))); + } + + /** + * Remove a custom filter + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $filter = $this->getCustomFilter($project); + + $this->checkPermission($project, $filter); + + if ($this->customFilterModel->remove($filter['id'])) { + $this->flash->success(t('Custom filter removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this custom filter.')); + } + + $this->response->redirect($this->helper->url->to('CustomFilterController', 'index', array('project_id' => $project['id']))); + } + + /** + * Edit a custom filter (display the form) + * + * @access public + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + + $this->checkPermission($project, $filter); + + $this->response->html($this->helper->layout->project('custom_filter/edit', array( + 'values' => empty($values) ? $filter : $values, + 'errors' => $errors, + 'project' => $project, + 'filter' => $filter, + 'title' => t('Edit custom filter') + ))); + } + + /** + * Edit a custom filter (validate the form and update the database) + * + * @access public + */ + public function update() + { + $project = $this->getProject(); + $filter = $this->customFilterModel->getById($this->request->getIntegerParam('filter_id')); + + $this->checkPermission($project, $filter); + + $values = $this->request->getValues(); + $values['id'] = $filter['id']; + $values['project_id'] = $project['id']; + + if (! isset($values['is_shared'])) { + $values += array('is_shared' => 0); + } + + if (! isset($values['append'])) { + $values += array('append' => 0); + } + + list($valid, $errors) = $this->customFilterValidator->validateModification($values); + + if ($valid) { + if ($this->customFilterModel->update($values)) { + $this->flash->success(t('Your custom filter has been updated successfully.')); + $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.')); + } + } + + $this->edit($values, $errors); + } + + private function checkPermission(array $project, array $filter) + { + $userID = $this->userSession->getId(); + + if ($filter['user_id'] != $userID) { + if ($this->projectUserRoleModel->getUserRole($project['id'], $userID) !== Role::PROJECT_MANAGER && ! $this->userSession->isAdmin()) { + throw new AccessForbiddenException(); + } + } + } +} diff --git a/app/Controller/DashboardController.php b/app/Controller/DashboardController.php new file mode 100644 index 0000000..f1d4047 --- /dev/null +++ b/app/Controller/DashboardController.php @@ -0,0 +1,78 @@ +getUser(); + + $this->response->html($this->helper->layout->dashboard('dashboard/overview', array( + 'title' => t('Dashboard for %s', $this->helper->user->getFullname($user)), + 'user' => $user, + 'overview_paginator' => $this->dashboardPagination->getOverview($user['id']), + 'project_paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'show', DASHBOARD_MAX_PROJECTS), + ))); + } + + /** + * My tasks + * + * @access public + */ + public function tasks() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->dashboard('dashboard/tasks', array( + 'title' => t('Tasks overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->taskPagination->getDashboardPaginator($user['id'], 'tasks', 50), + 'user' => $user, + ))); + } + + /** + * My subtasks + * + * @access public + */ + public function subtasks() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->dashboard('dashboard/subtasks', array( + 'title' => t('Subtasks overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->subtaskPagination->getDashboardPaginator($user['id']), + 'user' => $user, + 'nb_subtasks' => $this->subtaskModel->countByAssigneeAndTaskStatus($user['id']), + ))); + } + + /** + * My projects + * + * @access public + */ + public function projects() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->dashboard('dashboard/projects', array( + 'title' => t('Projects overview for %s', $this->helper->user->getFullname($user)), + 'paginator' => $this->projectPagination->getDashboardPaginator($user['id'], 'projects', 25), + 'user' => $user, + ))); + } +} diff --git a/app/Controller/DocumentationController.php b/app/Controller/DocumentationController.php new file mode 100644 index 0000000..1d17d41 --- /dev/null +++ b/app/Controller/DocumentationController.php @@ -0,0 +1,19 @@ +response->html($this->template->render('config/keyboard_shortcuts')); + } +} diff --git a/app/Controller/ExportController.php b/app/Controller/ExportController.php new file mode 100644 index 0000000..a405a46 --- /dev/null +++ b/app/Controller/ExportController.php @@ -0,0 +1,114 @@ +getProject(); + + if ($this->request->isPost()) { + $from = $this->request->getRawValue('from'); + $to = $this->request->getRawValue('to'); + + if ($from && $to) { + $data = $this->$model->$method($project['id'], $from, $to); + $this->response->withFileDownload($filename.'.csv'); + $this->response->csv($data, $this->request->getRawValue('bom') === '1'); + } + } else { + $this->response->html($this->template->render('export/'.$action, array( + 'values' => array( + 'project_id' => $project['id'], + 'from' => '', + 'to' => '', + ), + 'errors' => array(), + 'project' => $project, + 'title' => $page_title, + ))); + } + } + + /** + * Task export + * + * @access public + */ + public function tasks() + { + $this->common('taskExport', 'export', t('Tasks'), 'tasks', t('Tasks Export')); + } + + /** + * Subtask export + * + * @access public + */ + public function subtasks() + { + $this->common('subtaskExport', 'export', t('Subtasks'), 'subtasks', t('Subtasks Export')); + } + + /** + * Daily project summary export + * + * @access public + */ + public function summary() + { + $project = $this->getProject(); + + if ($this->request->isPost()) { + $from = $this->request->getRawValue('from'); + $to = $this->request->getRawValue('to'); + + if ($from && $to) { + $from = $this->dateParser->getIsoDate($from); + $to = $this->dateParser->getIsoDate($to); + $data = $this->projectDailyColumnStatsModel->getAggregatedMetrics($project['id'], $from, $to); + $this->response->withFileDownload(t('Summary').'.csv'); + $this->response->csv($data, $this->request->getRawValue('bom') === '1'); + } + } else { + $this->response->html($this->template->render('export/summary', array( + 'values' => array( + 'project_id' => $project['id'], + 'from' => '', + 'to' => '', + ), + 'errors' => array(), + 'project' => $project, + 'title' => t('Daily project summary export'), + ))); + } + } + + /** + * Transition export + * + * @access public + */ + public function transitions() + { + $this->common('transitionExport', 'export', t('Transitions'), 'transitions', t('Task transitions export')); + } +} diff --git a/app/Controller/ExternalTaskCreationController.php b/app/Controller/ExternalTaskCreationController.php new file mode 100644 index 0000000..23ee853 --- /dev/null +++ b/app/Controller/ExternalTaskCreationController.php @@ -0,0 +1,97 @@ +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), $project['id']); + + $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'], $project['id']); + } + + $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, $project['is_private'] == 1), + '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); + } elseif (! $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('task_id' => $taskId)), true); + } + } +} diff --git a/app/Controller/ExternalTaskViewController.php b/app/Controller/ExternalTaskViewController.php new file mode 100644 index 0000000..6719b65 --- /dev/null +++ b/app/Controller/ExternalTaskViewController.php @@ -0,0 +1,30 @@ +getTask(); + $taskProvider = $this->externalTaskManager->getProvider($task['external_provider']); + $externalTask = $taskProvider->fetch($task['external_uri'], $task['project_id']); + + $this->response->html($this->template->render($taskProvider->getViewTemplate(), array( + 'task' => $task, + 'external_task' => $externalTask, + ))); + } catch (ExternalTaskException $e) { + $this->response->html('
'.$e->getMessage().'
'); + } + } +} diff --git a/app/Controller/FeedController.php b/app/Controller/FeedController.php new file mode 100644 index 0000000..f494ab1 --- /dev/null +++ b/app/Controller/FeedController.php @@ -0,0 +1,58 @@ +request->getStringParam('token'); + $user = $this->userModel->getByToken($token); + + if (empty($user)) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + + $events = $this->helper->projectActivity->getProjectsEvents($this->projectPermissionModel->getActiveProjectIds($user['id'])); + + $this->response->xml($this->template->render('feed/user', [ + 'user' => $user, + 'events' => $events, + ])); + } + + /** + * RSS feed for a project + * + * @access public + */ + public function project() + { + $token = $this->request->getStringParam('token'); + $project = $this->projectModel->getByToken($token); + + if (empty($project)) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + + $events = $this->helper->projectActivity->getProjectEvents($project['id']); + + $this->response->xml($this->template->render('feed/project', [ + 'project' => $project, + 'events' => $events, + ])); + } +} diff --git a/app/Controller/FileViewerController.php b/app/Controller/FileViewerController.php new file mode 100644 index 0000000..a9a5f89 --- /dev/null +++ b/app/Controller/FileViewerController.php @@ -0,0 +1,163 @@ +objectStorage->get($file['path']); + } + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + } + + return $content; + } + + /** + * Output file with cache + * + * @param array $file + * @param $mimetype + */ + protected function renderFileWithCache(array $file, $mimetype) + { + if ($this->request->getHeader('If-None-Match') === '"'.$file['etag'].'"') { + $this->response->status(304); + } else { + try { + $this->response->withContentType($mimetype); + $this->response->withCache(5 * 86400, $file['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 + */ + public function show() + { + $file = $this->getFile(); + $type = $this->helper->file->getPreviewType($file['name']); + $params = ['file_id' => $file['id']]; + + if (array_key_exists('etag', $file)) { + $params['etag'] = $file['etag']; + } + + $project_id = $this->request->getIntegerParam('project_id'); + if ($project_id !== 0) { + $params['project_id'] = $project_id; + } + + if ($file['model'] === 'taskFileModel') { + $params['task_id'] = $file['task_id']; + } + + $this->response->html($this->template->render('file_viewer/show', array( + 'file' => $file, + 'params' => $params, + 'type' => $type, + 'content' => $this->getFileContent($file), + ))); + } + + /** + * Display image + * + * @access public + */ + public function image() + { + $file = $this->getFile(); + $this->renderFileWithCache($file, $this->helper->file->getImageMimeType($file['name'])); + } + + /** + * Display file in browser + * + * @access public + */ + public function browser() + { + $file = $this->getFile(); + $this->renderFileWithCache($file, $this->helper->file->getBrowserViewType($file['name'])); + } + + /** + * Display image thumbnail + * + * @access public + */ + public function thumbnail() + { + $file = $this->getFile(); + $model = $file['model']; + $filename = $this->$model->getThumbnailPath($file['path']); + + $this->response->withCache(5 * 86400, $file['etag']); + $this->response->withContentType('image/png'); + + if ($this->request->getHeader('If-None-Match') === '"'.$file['etag'].'"') { + $this->response->status(304); + } else { + + $this->response->send(); + + try { + + $this->objectStorage->output($filename); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + + // Try to generate thumbnail on the fly for images uploaded before Kanboard < 1.0.19 + $data = $this->objectStorage->get($file['path']); + $this->$model->generateThumbnailFromData($file['path'], $data); + $this->objectStorage->output($this->$model->getThumbnailPath($file['path'])); + } + } + } + + /** + * File download + * + * @access public + */ + public function download() + { + try { + $file = $this->getFile(); + $this->response->withFileDownload($file['name']); + $this->response->send(); + $this->objectStorage->output($file['path']); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + } + } +} diff --git a/app/Controller/GroupAjaxController.php b/app/Controller/GroupAjaxController.php new file mode 100644 index 0000000..308bba9 --- /dev/null +++ b/app/Controller/GroupAjaxController.php @@ -0,0 +1,24 @@ +request->getStringParam('term'); + $groups = $this->groupManager->find($search); + $this->response->json($this->groupAutoCompleteFormatter->withGroups($groups)->format()); + } +} diff --git a/app/Controller/GroupCreationController.php b/app/Controller/GroupCreationController.php new file mode 100644 index 0000000..b297b19 --- /dev/null +++ b/app/Controller/GroupCreationController.php @@ -0,0 +1,49 @@ +response->html($this->template->render('group_creation/show', array( + 'errors' => $errors, + 'values' => $values, + ))); + } + + /** + * Validate and save a new group + * + * @access public + */ + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->groupValidator->validateCreation($values); + + if ($valid) { + if ($this->groupModel->create($values['name']) !== false) { + $this->flash->success(t('Group created successfully.')); + return $this->response->redirect($this->helper->url->to('GroupListController', 'index'), true); + } else { + $this->flash->failure(t('Unable to create your group.')); + } + } + + return $this->show($values, $errors); + } +} diff --git a/app/Controller/GroupListController.php b/app/Controller/GroupListController.php new file mode 100644 index 0000000..f56e63a --- /dev/null +++ b/app/Controller/GroupListController.php @@ -0,0 +1,186 @@ +request->getStringParam('search'); + $query = $this->groupModel->getQuery(); + + if ($search !== '') { + $query->ilike('groups.name', '%'.$search.'%'); + } + + $paginator = $this->paginator + ->setUrl('GroupListController', 'index') + ->setMax(30) + ->setOrder(GroupModel::TABLE.'.name') + ->setQuery($query) + ->calculate(); + + $this->response->html($this->helper->layout->app('group/index', array( + 'title' => t('Groups').' ('.$paginator->getTotal().')', + 'paginator' => $paginator, + 'values' => array( + 'search' => $search + ), + ))); + } + + /** + * List all users + * + * @access public + */ + public function users() + { + $group_id = $this->request->getIntegerParam('group_id'); + $group = $this->groupModel->getById($group_id); + + $paginator = $this->paginator + ->setUrl('GroupListController', 'users', array('group_id' => $group_id)) + ->setMax(30) + ->setOrder(UserModel::TABLE.'.username') + ->setQuery($this->groupMemberModel->getQuery($group_id)) + ->calculate(); + + $this->response->html($this->helper->layout->app('group/users', array( + 'title' => t('Members of %s', $group['name']).' ('.$paginator->getTotal().')', + 'paginator' => $paginator, + 'group' => $group, + ))); + } + + /** + * Form to associate a user to a group + * + * @access public + * @param array $values + * @param array $errors + */ + public function associate(array $values = array(), array $errors = array()) + { + $group_id = $this->request->getIntegerParam('group_id'); + $group = $this->groupModel->getById($group_id); + + if (empty($values)) { + $values['group_id'] = $group_id; + } + + $this->response->html($this->template->render('group/associate', array( + 'users' => $this->userModel->prepareList($this->groupMemberModel->getNotMembers($group_id)), + 'group' => $group, + 'errors' => $errors, + 'values' => $values, + ))); + } + + /** + * Add user to a group + * + * @access public + */ + public function addUser() + { + $values = $this->request->getValues(); + + if (isset($values['group_id']) && isset($values['user_id'])) { + if ($this->groupMemberModel->addUser($values['group_id'], $values['user_id'])) { + $this->flash->success(t('Group member added successfully.')); + return $this->response->redirect($this->helper->url->to('GroupListController', 'users', array('group_id' => $values['group_id'])), true); + } else { + $this->flash->failure(t('Unable to add group member.')); + } + } + + return $this->associate($values); + } + + /** + * Confirmation dialog to remove a user from a group + * + * @access public + */ + public function dissociate() + { + $group_id = $this->request->getIntegerParam('group_id'); + $user_id = $this->request->getIntegerParam('user_id'); + $group = $this->groupModel->getById($group_id); + $user = $this->userModel->getById($user_id); + + $this->response->html($this->template->render('group/dissociate', array( + 'group' => $group, + 'user' => $user, + ))); + } + + /** + * Remove a user from a group + * + * @access public + */ + public function removeUser() + { + $this->checkCSRFParam(); + $group_id = $this->request->getIntegerParam('group_id'); + $user_id = $this->request->getIntegerParam('user_id'); + + if ($this->groupMemberModel->removeUser($group_id, $user_id)) { + $this->flash->success(t('User removed successfully from this group.')); + } else { + $this->flash->failure(t('Unable to remove this user from the group.')); + } + + $this->response->redirect($this->helper->url->to('GroupListController', 'users', array('group_id' => $group_id)), true); + } + + /** + * Confirmation dialog to remove a group + * + * @access public + */ + public function confirm() + { + $group_id = $this->request->getIntegerParam('group_id'); + $group = $this->groupModel->getById($group_id); + + $this->response->html($this->template->render('group/remove', array( + 'group' => $group, + ))); + } + + /** + * Remove a group + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $group_id = $this->request->getIntegerParam('group_id'); + + if ($this->groupModel->remove($group_id)) { + $this->flash->success(t('Group removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this group.')); + } + + $this->response->redirect($this->helper->url->to('GroupListController', 'index'), true); + } +} diff --git a/app/Controller/GroupModificationController.php b/app/Controller/GroupModificationController.php new file mode 100644 index 0000000..bd181b7 --- /dev/null +++ b/app/Controller/GroupModificationController.php @@ -0,0 +1,53 @@ +groupModel->getById($this->request->getIntegerParam('group_id')); + } + + $this->response->html($this->template->render('group_modification/show', array( + 'errors' => $errors, + 'values' => $values, + ))); + } + + /** + * Validate and save a group + * + * @access public + */ + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->groupValidator->validateModification($values); + + if ($valid) { + if ($this->groupModel->update($values) !== false) { + $this->flash->success(t('Group updated successfully.')); + return $this->response->redirect($this->helper->url->to('GroupListController', 'index'), true); + } else { + $this->flash->failure(t('Unable to update your group.')); + } + } + + return $this->show($values, $errors); + } +} diff --git a/app/Controller/ICalendarController.php b/app/Controller/ICalendarController.php new file mode 100644 index 0000000..fbbd63a --- /dev/null +++ b/app/Controller/ICalendarController.php @@ -0,0 +1,118 @@ +request->getStringParam('token'); + $user = $this->userModel->getByToken($token); + + if (empty($user)) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + + $startRange = strtotime('-2 months'); + $endRange = strtotime('+6 months'); + + $startColumn = $this->configModel->get('calendar_user_tasks', 'date_started'); + + $calendar = new iCalendar('Kanboard'); + $calendar->setName($user['name'] ?: $user['username']); + $calendar->setDescription($user['name'] ?: $user['username']); + $calendar->setPublishedTTL('PT1H'); + + $queryDueDateOnly = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange))) + ->withFilter(new TaskAssigneeFilter($user['id'])) + ->getQuery(); + + $queryStartAndDueDate = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskAssigneeFilter($user['id'])) + ->getQuery() + ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due')); + + $this->response->ical($this->taskICalFormatter + ->setCalendar($calendar) + ->addTasksWithDueDateOnly($queryDueDateOnly) + ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due') + ->format()); + } + + public function project() + { + $token = $this->request->getStringParam('token'); + $project = $this->projectModel->getByToken($token); + + if (empty($project)) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + + $startRange = strtotime('-2 months'); + $endRange = strtotime('+6 months'); + + $startColumn = $this->configModel->get('calendar_project_tasks', 'date_started'); + + $calendar = new iCalendar('Kanboard'); + $calendar->setName($project['name']); + $calendar->setDescription($project['name']); + $calendar->setPublishedTTL('PT1H'); + + $queryDueDateOnly = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskProjectFilter($project['id'])) + ->withFilter(new TaskDueDateRangeFilter(array($startRange, $endRange))) + ->getQuery(); + + $queryStartAndDueDate = QueryBuilder::create() + ->withQuery($this->taskFinderModel->getICalQuery()) + ->withFilter(new TaskStatusFilter(TaskModel::STATUS_OPEN)) + ->withFilter(new TaskProjectFilter($project['id'])) + ->getQuery() + ->addCondition($this->getConditionForTasksWithStartAndDueDate($startRange, $endRange, $startColumn, 'date_due')); + + $this->response->ical($this->taskICalFormatter + ->setCalendar($calendar) + ->addTasksWithDueDateOnly($queryDueDateOnly) + ->addTasksWithStartAndDueDate($queryStartAndDueDate, $startColumn, 'date_due') + ->format()); + } + + protected function getConditionForTasksWithStartAndDueDate($start_time, $end_time, $start_column, $end_column) + { + $start_time = (int) $start_time; + $end_time = (int) $end_time; + + $start_column = $this->db->escapeIdentifier($start_column); + $end_column = $this->db->escapeIdentifier($end_column); + + $conditions = array( + "($start_column >= '$start_time' AND $start_column <= '$end_time')", + "($start_column <= '$start_time' AND $end_column >= '$start_time')", + "($start_column <= '$start_time' AND ($end_column = '0' OR $end_column IS NULL))", + ); + + return $start_column.' IS NOT NULL AND '.$start_column.' > 0 AND ('.implode(' OR ', $conditions).')'; + } +} diff --git a/app/Controller/LinkController.php b/app/Controller/LinkController.php new file mode 100644 index 0000000..2ad8a2b --- /dev/null +++ b/app/Controller/LinkController.php @@ -0,0 +1,162 @@ +linkModel->getById($this->request->getIntegerParam('link_id')); + + if (empty($link)) { + throw new PageNotFoundException(); + } + + return $link; + } + + /** + * 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').' > '.t('Link labels'), + ))); + } + + /** + * Add new link label + * + * @access public + * @param array $values + * @param array $errors + */ + public function create(array $values = array(), array $errors = array()) + { + $this->response->html($this->template->render('link/create', array( + 'links' => $this->linkModel->getMergedList(), + 'values' => $values, + 'errors' => $errors, + ))); + } + + /** + * Validate and save a new link + * + * @access public + */ + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->linkValidator->validateCreation($values); + + if ($valid) { + if ($this->linkModel->create($values['label'], $values['opposite_label']) !== false) { + $this->flash->success(t('Link added successfully.')); + $this->response->redirect($this->helper->url->to('LinkController', 'show'), true); + return; + } else { + $this->flash->failure(t('Unable to create your link.')); + } + } + + $this->create($values, $errors); + } + + /** + * Edit form + * + * @access public + * @param array $values + * @param array $errors + * @throws PageNotFoundException + */ + public function edit(array $values = array(), array $errors = array()) + { + $link = $this->getLink(); + $link['label'] = t($link['label']); + + $this->response->html($this->template->render('link/edit', array( + 'values' => $values ?: $link, + 'errors' => $errors, + 'labels' => $this->linkModel->getList($link['id']), + 'link' => $link, + ))); + } + + /** + * Edit a link (validate the form and update the database) + * + * @access public + */ + public function update() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->linkValidator->validateModification($values); + + if ($valid) { + if ($this->linkModel->update($values)) { + $this->flash->success(t('Link updated successfully.')); + $this->response->redirect($this->helper->url->to('LinkController', 'show'), true); + return; + } else { + $this->flash->failure(t('Unable to update your link.')); + } + } + + $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a link + * + * @access public + */ + public function confirm() + { + $link = $this->getLink(); + + $this->response->html($this->template->render('link/remove', array( + 'link' => $link, + ))); + } + + /** + * Remove a link + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $link = $this->getLink(); + + if ($this->linkModel->remove($link['id'])) { + $this->flash->success(t('Link removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this link.')); + } + + $this->response->redirect($this->helper->url->to('LinkController', 'show'), true); + } +} diff --git a/app/Controller/OAuthController.php b/app/Controller/OAuthController.php new file mode 100644 index 0000000..abd9b2e --- /dev/null +++ b/app/Controller/OAuthController.php @@ -0,0 +1,130 @@ +request->getStringParam('code'); + $state = $this->request->getStringParam('state'); + + if (! empty($code)) { + $this->step2($provider, $code, $state); + } else { + $this->response->redirect($this->authenticationManager->getProvider($provider)->getService()->getAuthorizationUrl()); + } + } + + /** + * Link or authenticate the user + * + * @access protected + * @param string $providerName + * @param string $code + * @param string $state + */ + protected function step2($providerName, $code, $state) + { + $provider = $this->authenticationManager->getProvider($providerName); + $provider->setCode($code); + $hasValidState = $provider->getService()->isValidateState($state); + + if ($this->userSession->isLogged()) { + if ($hasValidState) { + $this->link($provider); + } else { + $this->flash->failure(t('The OAuth2 state parameter is invalid')); + $this->response->redirect($this->helper->url->to('UserViewController', 'external', array('user_id' => $this->userSession->getId()))); + } + } else { + if ($hasValidState) { + $this->authenticate($providerName); + } else { + $this->authenticationFailure(t('The OAuth2 state parameter is invalid')); + } + } + } + + /** + * Link the account + * + * @access protected + * @param OAuthAuthenticationProviderInterface $provider + */ + protected function link(OAuthAuthenticationProviderInterface $provider) + { + if (! $provider->authenticate()) { + $this->flash->failure(t('External authentication failed')); + } else { + $this->userProfile->assign($this->userSession->getId(), $provider->getUser()); + $this->flash->success(t('Your external account is linked to your profile successfully.')); + } + + $this->response->redirect($this->helper->url->to('UserViewController', 'external', array('user_id' => $this->userSession->getId()))); + } + + /** + * Unlink external account + * + * @access public + */ + public function unlink() + { + $backend = $this->request->getStringParam('backend'); + $this->checkCSRFParam(); + + if ($this->authenticationManager->getProvider($backend)->unlink($this->userSession->getId())) { + $this->flash->success(t('Your external account is not linked anymore to your profile.')); + } else { + $this->flash->failure(t('Unable to unlink your external account.')); + } + + $this->response->redirect($this->helper->url->to('UserViewController', 'external', array('user_id' => $this->userSession->getId()))); + } + + /** + * Authenticate the account + * + * @access protected + * @param string $providerName + */ + protected function authenticate($providerName) + { + if ($this->authenticationManager->oauthAuthentication($providerName)) { + $this->redirectAfterLogin(); + } else { + $this->authenticationFailure(t('External authentication failed')); + } + } + + /** + * Show login failure page + * + * @access protected + * @param string $message + */ + protected function authenticationFailure($message) + { + $this->response->html($this->helper->layout->app('auth/index', array( + 'errors' => array('login' => $message), + 'values' => array(), + 'no_layout' => true, + 'title' => t('Login') + ))); + } +} diff --git a/app/Controller/PasswordResetController.php b/app/Controller/PasswordResetController.php new file mode 100644 index 0000000..07edd6c --- /dev/null +++ b/app/Controller/PasswordResetController.php @@ -0,0 +1,132 @@ +checkActivation(); + + $this->response->html($this->helper->layout->app('password_reset/create', array( + 'errors' => $errors, + 'values' => $values, + 'no_layout' => true, + ))); + } + + /** + * Validate and send the email + */ + public function save() + { + $this->checkActivation(); + + $values = $this->request->getValues(); + list($valid, $errors) = $this->passwordResetValidator->validateCreation($values); + + if ($valid) { + $this->sendEmail($values['username']); + $this->response->redirect($this->helper->url->to('AuthController', 'login')); + } else { + $this->create($values, $errors); + } + } + + /** + * Show the form to set a new password + * + * @param array $values + * @param array $errors + * @throws \Kanboard\Core\Controller\BaseException + */ + public function change(array $values = array(), array $errors = array()) + { + $this->checkActivation(); + + $token = $this->request->getStringParam('token'); + $user_id = $this->passwordResetModel->getUserIdByToken($token); + + if ($user_id !== false) { + $this->response->html($this->helper->layout->app('password_reset/change', array( + 'token' => $token, + 'errors' => $errors, + 'values' => $values, + 'no_layout' => true, + ))); + } else { + $this->response->redirect($this->helper->url->to('AuthController', 'login')); + } + } + + /** + * Set the new password + */ + public function update() + { + $this->checkActivation(); + + $token = $this->request->getStringParam('token'); + $values = $this->request->getValues(); + list($valid, $errors) = $this->passwordResetValidator->validateModification($values); + + if ($valid) { + $user_id = $this->passwordResetModel->getUserIdByToken($token); + + if ($user_id !== false) { + $this->userModel->update(array('id' => $user_id, 'password' => $values['password'])); + $this->passwordResetModel->disable($user_id); + } + + return $this->response->redirect($this->helper->url->to('AuthController', 'login')); + } + + return $this->change($values, $errors); + } + + /** + * Send the email + * + * @param string $username + */ + protected function sendEmail($username) + { + $token = $this->passwordResetModel->create($username); + + if ($token !== false) { + $user = $this->userCacheDecorator->getByUsername($username); + + $this->emailClient->send( + $user['email'], + $user['name'] ?: $user['username'], + t('Password Reset for Kanboard'), + $this->template->render('password_reset/email', array('token' => $token)) + ); + } + } + + /** + * Check feature availability + */ + protected function checkActivation() + { + if ($this->configModel->get('password_reset', 0) == 0) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + } +} diff --git a/app/Controller/PluginController.php b/app/Controller/PluginController.php new file mode 100644 index 0000000..a11ed57 --- /dev/null +++ b/app/Controller/PluginController.php @@ -0,0 +1,153 @@ +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(), + ))); + } + + /** + * Display list of available plugins + */ + public function directory() + { + $installedPlugins = []; + $availablePlugins = []; + $isConfigured = Installer::isConfigured(); + + if ($isConfigured) { + foreach ($this->pluginLoader->getPlugins() as $plugin) { + $installedPlugins[$plugin->getPluginName()] = $plugin->getPluginVersion(); + } + + $availablePlugins = Directory::getInstance($this->container)->getAvailablePlugins(); + } + + $this->response->html($this->helper->layout->plugin('plugin/directory', array( + 'installed_plugins' => $installedPlugins, + 'available_plugins' => $availablePlugins, + 'title' => t('Plugin Directory'), + 'is_configured' => $isConfigured, + ))); + } + + /** + * Install plugin from URL + * + * @throws \Kanboard\Core\Controller\AccessForbiddenException + */ + public function install() + { + if (! Installer::isConfigured()) { + throw new AccessForbiddenException(); + } + + $this->checkCSRFParam(); + $pluginArchiveUrl = urldecode($this->request->getStringParam('archive_url')); + + try { + $installer = new Installer($this->container); + $installer->install($pluginArchiveUrl); + $this->flash->success(t('Plugin installed successfully.')); + } catch (PluginInstallerException $e) { + $this->flash->failure($e->getMessage()); + } + + $this->response->redirect($this->helper->url->to('PluginController', 'show')); + } + + /** + * Update plugin from URL + * + * @throws \Kanboard\Core\Controller\AccessForbiddenException + */ + public function update() + { + if (! Installer::isConfigured()) { + throw new AccessForbiddenException(); + } + + $this->checkCSRFParam(); + $pluginArchiveUrl = urldecode($this->request->getStringParam('archive_url')); + + try { + $installer = new Installer($this->container); + $installer->update($pluginArchiveUrl); + $this->flash->success(t('Plugin updated successfully.')); + } catch (PluginInstallerException $e) { + $this->flash->failure($e->getMessage()); + } + + $this->response->redirect($this->helper->url->to('PluginController', 'show')); + } + + /** + * Confirmation before to remove the plugin + */ + public function confirm() + { + if (! Installer::isConfigured()) { + throw new AccessForbiddenException(); + } + + $pluginId = $this->request->getStringParam('pluginId'); + $plugins = array_merge( + $this->pluginLoader->getPlugins(), + $this->pluginLoader->getIncompatiblePlugins() + ); + + $this->response->html($this->template->render('plugin/remove', array( + 'plugin_id' => $pluginId, + 'plugin' => $plugins[$pluginId], + ))); + } + + /** + * Remove a plugin + * + * @throws \Kanboard\Core\Controller\AccessForbiddenException + */ + public function uninstall() + { + if (! Installer::isConfigured()) { + throw new AccessForbiddenException(); + } + + $this->checkCSRFParam(); + $pluginId = $this->request->getStringParam('pluginId'); + + try { + $installer = new Installer($this->container); + $installer->uninstall($pluginId); + $this->flash->success(t('Plugin removed successfully.')); + } catch (PluginInstallerException $e) { + $this->flash->failure($e->getMessage()); + } + + $this->response->redirect($this->helper->url->to('PluginController', 'show')); + } +} diff --git a/app/Controller/PredefinedTaskDescriptionController.php b/app/Controller/PredefinedTaskDescriptionController.php new file mode 100644 index 0000000..2187c30 --- /dev/null +++ b/app/Controller/PredefinedTaskDescriptionController.php @@ -0,0 +1,116 @@ +getProject(); + + $this->response->html($this->template->render('predefined_task_description/create', array( + 'values' => $values, + 'errors' => $errors, + 'project' => $project, + ))); + } + + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values); + + if ($valid) { + if ($this->predefinedTaskDescriptionModel->create($project['id'], $values['title'], $values['description']) !== false) { + $this->flash->success(t('Template created successfully.')); + } else { + $this->flash->failure(t('Unable to create this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->create($values, $errors); + } + } + + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id')); + + $this->response->html($this->template->render('predefined_task_description/edit', array( + 'values' => empty($values) ? $template : $values, + 'template' => $template, + 'errors' => $errors, + 'project' => $project, + ))); + } + + public function update() + { + $project = $this->getProject(); + $template = $this->getTemplate($project); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->predefinedTaskDescriptionValidator->validate($values); + + if ($valid) { + if ($this->predefinedTaskDescriptionModel->update($project['id'], $template['id'], $values['title'], $values['description']) !== false) { + $this->flash->success(t('Template updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->edit($values, $errors); + } + } + + public function confirm() + { + $project = $this->getProject(); + $template = $this->getTemplate($project); + + $this->response->html($this->template->render('predefined_task_description/remove', array( + 'template' => $template, + 'project' => $project, + ))); + } + + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $template = $this->getTemplate($project); + + if ($this->predefinedTaskDescriptionModel->remove($project['id'], $template['id'])) { + $this->flash->success(t('Template removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this template.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } + + protected function getTemplate(array $project) + { + $template = $this->predefinedTaskDescriptionModel->getById($project['id'], $this->request->getIntegerParam('id')); + + if (empty($template)) { + throw new PageNotFoundException(); + } + + return $template; + } +} diff --git a/app/Controller/ProjectActionDuplicationController.php b/app/Controller/ProjectActionDuplicationController.php new file mode 100644 index 0000000..713f5f1 --- /dev/null +++ b/app/Controller/ProjectActionDuplicationController.php @@ -0,0 +1,44 @@ +getProject(); + $projects = $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId()); + unset($projects[$project['id']]); + + $this->response->html($this->template->render('project_action_duplication/show', array( + 'project' => $project, + 'projects_list' => $projects, + ))); + } + + public function save() + { + $project = $this->getProject(); + $src_project_id = $this->request->getValue('src_project_id'); + + if (empty($src_project_id) || ! $this->projectPermissionModel->isUserAllowed($src_project_id, $this->userSession->getId())) { + throw new AccessForbiddenException(); + } + + if ($this->actionModel->duplicate($src_project_id, $project['id'])) { + $this->flash->success(t('Actions duplicated successfully.')); + } else { + $this->flash->failure(t('Unable to duplicate actions.')); + } + + $this->response->redirect($this->helper->url->to('ActionController', 'index', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/ProjectCreationController.php b/app/Controller/ProjectCreationController.php new file mode 100644 index 0000000..029c43f --- /dev/null +++ b/app/Controller/ProjectCreationController.php @@ -0,0 +1,139 @@ + t('Do not duplicate anything')) + $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); + + $this->response->html($this->helper->layout->app('project_creation/create', array( + 'values' => $values, + 'errors' => $errors, + 'is_private' => $is_private, + 'projects_list' => $projects_list, + 'title' => $is_private ? t('New personal project') : t('New project'), + ))); + } + + /** + * Display a form to create a private project + * + * @access public + * @param array $values + * @param array $errors + */ + public function createPrivate(array $values = array(), array $errors = array()) + { + $values['is_private'] = 1; + $this->create($values, $errors); + } + + /** + * Validate and save a new project + * + * @access public + */ + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->projectValidator->validateCreation($values); + + if ($valid) { + $project_id = $this->createOrDuplicate($values); + + if ($project_id > 0) { + $this->flash->success(t('Your project has been created successfully.')); + return $this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project_id))); + } + + $this->flash->failure(t('Unable to create your project.')); + } + + return $this->create($values, $errors); + } + + /** + * Create or duplicate a project + * + * @access private + * @param array $values + * @return boolean|integer + */ + private function createOrDuplicate(array $values) + { + if (empty($values['src_project_id'])) { + return $this->createNewProject($values); + } + + return $this->duplicateNewProject($values); + } + + /** + * Save a new project + * + * @access private + * @param array $values + * @return boolean|integer + */ + private function createNewProject(array $values) + { + $project = array( + 'name' => $values['name'], + 'is_private' => $values['is_private'], + 'identifier' => $values['identifier'], + 'per_swimlane_task_limits' => array_key_exists('per_swimlane_task_limits', $values) ? $values['per_swimlane_task_limits'] : 0, + 'task_limit' => $values['task_limit'], + ); + + return $this->projectModel->create($project, $this->userSession->getId(), true); + } + + /** + * Create from another project + * + * @access private + * @param array $values + * @return boolean|integer + */ + private function duplicateNewProject(array $values) + { + $selection = array(); + + if (! $this->projectPermissionModel->isUserAllowed($values['src_project_id'], $this->userSession->getId())) { + throw new AccessForbiddenException(); + } + + foreach ($this->projectDuplicationModel->getOptionalSelection() as $item) { + if (isset($values[$item]) && $values[$item] == 1) { + $selection[] = $item; + } + } + + return $this->projectDuplicationModel->duplicate( + $values['src_project_id'], + $selection, + $this->userSession->getId(), + $values['name'], + $values['is_private'] == 1, + $values['identifier'] + ); + } +} diff --git a/app/Controller/ProjectEditController.php b/app/Controller/ProjectEditController.php new file mode 100644 index 0000000..dd53450 --- /dev/null +++ b/app/Controller/ProjectEditController.php @@ -0,0 +1,82 @@ +getProject(); + + $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') + ))); + } + + /** + * Validate and update a project + * + * @access public + */ + public function update() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $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', 'show', array('project_id' => $project['id'])), true); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + } + + return $this->show($values, $errors); + } + + /** + * Prepare form values + * + * @access private + * @param array $project + * @param array $values + * @return array + */ + private function prepareValues(array $project, array $values) + { + $values['id'] = $project['id']; + + 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; + } +} diff --git a/app/Controller/ProjectFileController.php b/app/Controller/ProjectFileController.php new file mode 100644 index 0000000..e515aa3 --- /dev/null +++ b/app/Controller/ProjectFileController.php @@ -0,0 +1,89 @@ +getProject(); + + $this->response->html($this->template->render('project_file/create', array( + 'project' => $project, + 'max_size' => get_upload_max_size(), + ))); + } + + /** + * Save uploaded files + * + * @access public + */ + public function save() + { + $this->checkReusableCSRFParam(); + $project = $this->getProject(); + $result = $this->projectFileModel->uploadFiles($project['id'], $this->request->getFileInfo('files')); + + if ($this->request->isAjax()) { + if (! $result) { + $this->response->json(array('message' => t('Unable to upload files, check the permissions of your data folder.')), 500); + } else { + $this->response->json(array('message' => 'OK')); + } + } else { + if (! $result) { + $this->flash->failure(t('Unable to upload files, check the permissions of your data folder.')); + } + + $this->response->redirect($this->helper->url->to('ProjectOverviewController', 'show', array('project_id' => $project['id'])), true); + } + } + + /** + * Remove a file + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $file = $this->getFile(); + + if ($this->projectFileModel->remove($file['id'])) { + $this->flash->success(t('File removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this file.')); + } + + $this->response->redirect($this->helper->url->to('ProjectOverviewController', 'show', array('project_id' => $project['id']))); + } + + /** + * Confirmation dialog before removing a file + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $file = $this->getFile(); + + $this->response->html($this->template->render('project_file/remove', array( + 'project' => $project, + 'file' => $file, + ))); + } +} diff --git a/app/Controller/ProjectListController.php b/app/Controller/ProjectListController.php new file mode 100644 index 0000000..490a685 --- /dev/null +++ b/app/Controller/ProjectListController.php @@ -0,0 +1,47 @@ +userSession->isAdmin()) { + $projectIds = $this->projectModel->getAllIds(); + } else { + $projectIds = $this->projectPermissionModel->getProjectIds($this->userSession->getId()); + } + + $query = $this->projectModel->getQueryByProjectIds($projectIds); + $search = $this->request->getStringParam('search'); + + if ($search !== '') { + $query->ilike('projects.name', '%' . $search . '%'); + } + + $paginator = $this->paginator + ->setUrl('ProjectListController', 'show') + ->setMax(20) + ->setOrder('name') + ->setQuery($query) + ->calculate(); + + $this->response->html($this->helper->layout->dashboard('project_list/listing', array( + 'paginator' => $paginator, + 'title' => t('Projects') . ' (' . $paginator->getTotal() . ')', + 'values' => array('search' => $search), + 'user' => $this->getUser(), + ))); + } +} diff --git a/app/Controller/ProjectOverviewController.php b/app/Controller/ProjectOverviewController.php new file mode 100644 index 0000000..477c1dd --- /dev/null +++ b/app/Controller/ProjectOverviewController.php @@ -0,0 +1,33 @@ +getProject(); + $columns = $this->columnModel->getAllWithTaskCount($project['id']); + + $this->response->html($this->helper->layout->app('project_overview/show', array( + 'project' => $project, + 'columns' => $columns, + 'title' => $project['name'], + 'description' => $this->helper->projectHeader->getDescription($project), + 'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']), + '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 new file mode 100644 index 0000000..f12a3f2 --- /dev/null +++ b/app/Controller/ProjectPermissionController.php @@ -0,0 +1,221 @@ +getProject(); + + if (empty($values)) { + $values['role'] = Role::PROJECT_MEMBER; + } + + $this->response->html($this->helper->layout->project('project_permission/index', array( + 'project' => $project, + 'users' => $this->projectUserRoleModel->getUsers($project['id']), + 'groups' => $this->projectGroupRoleModel->getGroups($project['id']), + 'roles' => $this->projectRoleModel->getList($project['id']), + 'values' => $values, + 'errors' => $errors, + 'title' => t('Project Permissions'), + ))); + } + + /** + * Add user to the project + * + * @access public + */ + public function addUser() + { + $this->checkCSRFForm(); + + $project = $this->getProject(); + $values = $this->request->getValues(); + + if (empty($values['user_id']) && ! empty($values['external_id']) && ! empty($values['external_id_column'])) { + $values['user_id'] = $this->userModel->getOrCreateExternalUserId($values['username'], $values['name'], $values['external_id_column'], $values['external_id']); + } + + if (empty($values['user_id'])) { + $this->flash->failure(t('User not found.')); + } elseif ($this->projectUserRoleModel->addUser($project['id'], $values['user_id'], $values['role'])) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id']))); + } + + /** + * Revoke user access + * + * @access public + */ + public function removeUser() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $user_id = $this->request->getIntegerParam('user_id'); + + if ($this->projectUserRoleModel->removeUser($project['id'], $user_id)) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id']))); + } + + /** + * Change user role + * + * @access public + */ + public function changeUserRole() + { + $this->checkReusableGETCSRFParam(); + $project = $this->getProject(); + + if (! $this->request->isAjax()) { + $this->response->json(array('status' => 'error'), 400); + return; + } + + $values = $this->request->getJson(); + + if (empty($project) || + empty($values) + ) { + $this->response->json(array('status' => 'error'), 500); + return; + } + + $userRole = $this->projectUserRoleModel->getUserRole($project['id'], $values['id']); + $usersGroupedByRole = $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']); + + if ($userRole === 'project-manager' && + $values['role'] !== 'project-manager' && + count($usersGroupedByRole['project-manager']) <= 1 + ) { + $this->response->json(array('status' => 'error'), 500); + return; + } + + $this->projectUserRoleModel->changeUserRole($project['id'], $values['id'], $values['role']); + + $this->response->json(array('status' => 'ok')); + } + + /** + * Add group to the project + * + * @access public + */ + public function addGroup() + { + $this->checkCSRFForm(); + + $project = $this->getProject(); + $values = $this->request->getValues(); + + if (empty($values['group_id']) && ! empty($values['external_id'])) { + $values['group_id'] = $this->groupModel->getOrCreateExternalGroupId($values['name'], $values['external_id']); + } + + if (empty($values['group_id'])) { + $this->flash->failure(t('Unable to find this group.')); + } else { + if ($this->projectGroupRoleModel->addGroup($project['id'], $values['group_id'], $values['role'])) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + } + + $this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id']))); + } + + /** + * Revoke group access + * + * @access public + */ + public function removeGroup() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $group_id = $this->request->getIntegerParam('group_id'); + + if ($this->projectGroupRoleModel->removeGroup($project['id'], $group_id)) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectPermissionController', 'index', array('project_id' => $project['id']))); + } + + /** + * Change group role + * + * @access public + */ + public function changeGroupRole() + { + $this->checkReusableGETCSRFParam(); + $project = $this->getProject(); + + if (! $this->request->isAjax()) { + $this->response->json(array('status' => 'error'), 400); + return; + } + + $values = $this->request->getJson(); + + if (! empty($project) && ! empty($values) && $this->projectGroupRoleModel->changeGroupRole($project['id'], $values['id'], $values['role'])) { + $this->response->json(array('status' => 'ok')); + } else { + $this->response->json(array('status' => 'error')); + } + } +} diff --git a/app/Controller/ProjectPredefinedContentController.php b/app/Controller/ProjectPredefinedContentController.php new file mode 100644 index 0000000..709d6b7 --- /dev/null +++ b/app/Controller/ProjectPredefinedContentController.php @@ -0,0 +1,50 @@ +getProject(); + + $this->response->html($this->helper->layout->project('project_predefined_content/show', array( + 'values' => empty($values) ? $project : $values, + 'errors' => $errors, + 'project' => $project, + 'predefined_task_descriptions' => $this->predefinedTaskDescriptionModel->getAll($project['id']), + 'title' => t('Predefined Contents'), + ))); + } + + public function update() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $values = array( + 'id' => $project['id'], + 'name' => $project['name'], + 'predefined_email_subjects' => isset($values['predefined_email_subjects']) ? $values['predefined_email_subjects'] : '', + ); + + 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('ProjectPredefinedContentController', 'show', array('project_id' => $project['id'])), true); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + } + + return $this->show($values, $errors); + } +} diff --git a/app/Controller/ProjectRoleController.php b/app/Controller/ProjectRoleController.php new file mode 100644 index 0000000..9550375 --- /dev/null +++ b/app/Controller/ProjectRoleController.php @@ -0,0 +1,162 @@ +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 0000000..4fa9b13 --- /dev/null +++ b/app/Controller/ProjectRoleRestrictionController.php @@ -0,0 +1,96 @@ +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/ProjectStatusController.php b/app/Controller/ProjectStatusController.php new file mode 100644 index 0000000..4fdbb6e --- /dev/null +++ b/app/Controller/ProjectStatusController.php @@ -0,0 +1,100 @@ +getProject(); + + $this->response->html($this->template->render('project_status/enable', array( + 'project' => $project, + ))); + } + + /** + * Enable the project + */ + public function enable() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + + if ($this->projectModel->enable($project['id'])) { + $this->flash->success(t('Project activated successfully.')); + } else { + $this->flash->failure(t('Unable to activate this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project['id'])), true); + } + + /** + * Disable a project (confirmation dialog box) + */ + public function confirmDisable() + { + $project = $this->getProject(); + + $this->response->html($this->template->render('project_status/disable', array( + 'project' => $project, + ))); + } + + /** + * Disable a project + */ + public function disable() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + + if ($this->projectModel->disable($project['id'])) { + $this->flash->success(t('Project disabled successfully.')); + } else { + $this->flash->failure(t('Unable to disable this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project['id'])), true); + } + + /** + * Remove a project (confirmation dialog box) + */ + public function confirmRemove() + { + $project = $this->getProject(); + + $this->response->html($this->template->render('project_status/remove', array( + 'project' => $project, + 'title' => t('Remove project') + ))); + } + + /** + * Remove a project + */ + public function remove() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + + if ($this->projectModel->remove($project['id'])) { + $this->flash->success(t('Project removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectListController', 'show')); + } +} diff --git a/app/Controller/ProjectTagController.php b/app/Controller/ProjectTagController.php new file mode 100644 index 0000000..8907bcf --- /dev/null +++ b/app/Controller/ProjectTagController.php @@ -0,0 +1,182 @@ +getProject(); + + $this->response->html($this->helper->layout->project('project_tag/index', array( + 'project' => $project, + 'tags' => $this->tagModel->getAllByProject($project['id']), + 'colors' => $this->colorModel->getList(), + 'title' => t('Project tags management'), + ))); + } + + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->template->render('project_tag/create', array( + 'project' => $project, + 'values' => $values, + 'colors' => $this->colorModel->getList(), + 'errors' => $errors, + ))); + } + + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + + list($valid, $errors) = $this->tagValidator->validateCreation($values); + + if ($valid) { + if ($this->tagModel->create($project['id'], $values['name'], $values['color_id']) > 0) { + $this->flash->success(t('Tag created successfully.')); + } else { + $this->flash->failure(t('Unable to create this tag.')); + } + + $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id']))); + } else { + $this->create($values, $errors); + } + } + + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $tag = $this->getProjectTag($project); + + if (empty($values)) { + $values = $tag; + } + + $this->response->html($this->template->render('project_tag/edit', array( + 'project' => $project, + 'tag' => $tag, + 'values' => $values, + 'colors' => $this->colorModel->getList(), + 'errors' => $errors, + ))); + } + + public function update() + { + $project = $this->getProject(); + $tag = $this->getProjectTag($project); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['id'] = $tag['id']; + + list($valid, $errors) = $this->tagValidator->validateModification($values); + + if ($valid) { + if ($this->tagModel->update($values['id'], $values['name'], $values['color_id'])) { + $this->flash->success(t('Tag updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this tag.')); + } + + $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id']))); + } else { + $this->edit($values, $errors); + } + } + + public function confirm() + { + $project = $this->getProject(); + $tag = $this->getProjectTag($project); + + $this->response->html($this->template->render('project_tag/remove', array( + 'tag' => $tag, + 'project' => $project, + ))); + } + + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $tag = $this->getProjectTag($project); + + if ($this->tagModel->remove($tag['id'])) { + $this->flash->success(t('Tag removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this tag.')); + } + + $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id']))); + } + + /** + * Confirm dialog to make a tag global + * + * @return void + */ + public function confirmMakeGlobalTag() + { + $project = $this->getProject(); + $tag = $this->getProjectTag($project); + + $this->response->html($this->template->render('project_tag/make_global', array( + 'tag' => $tag, + 'project' => $project, + ))); + } + + /** + * Make a tag global and flash result + * + * @return void + */ + public function makeGlobalTag() + { + if ($this->userSession->isAdmin()) { + $project = $this->getProject(); + $tag = $this->getProjectTag($project); + + if ($this->tagModel->update($tag['id'], $tag['name'], $tag['color_id'], 0)) { + $this->flash->success(t('Tag updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this tag.')); + } + + $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id']))); + } + } + + /** + * Update project tag settings + * + * @return void + */ + public function updateSettings() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $values['enable_global_tags'] = array_key_exists('enable_global_tags', $values) ? $values['enable_global_tags'] : 0; + + if ($this->projectModel->changeGlobalTagUsage($project['id'], $values['enable_global_tags'])) { + $this->flash->success(t('Project updated successfully.')); + return $this->response->redirect($this->helper->url->to('ProjectTagController', 'index', array('project_id' => $project['id']))); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + } +} diff --git a/app/Controller/ProjectUserOverviewController.php b/app/Controller/ProjectUserOverviewController.php new file mode 100644 index 0000000..5faf579 --- /dev/null +++ b/app/Controller/ProjectUserOverviewController.php @@ -0,0 +1,130 @@ +request->getIntegerParam('user_id', UserModel::EVERYBODY_ID); + + if ($this->userSession->isAdmin()) { + $project_ids = $this->projectModel->getAllIds(); + } else { + $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + } + + return array($user_id, $project_ids, $this->userModel->getActiveUsersList(true)); + } + + private function role($role, $action, $title, $title_user) + { + list($user_id, $project_ids, $users) = $this->common(); + + $query = $this->projectPermissionModel->getQueryByRole($project_ids, $role)->callback(array($this->projectModel, 'applyColumnStats')); + + if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) { + $query->eq(UserModel::TABLE.'.id', $user_id); + $title = t($title_user, $users[$user_id]); + } + + $paginator = $this->paginator + ->setUrl('ProjectUserOverviewController', $action, array('user_id' => $user_id)) + ->setMax(30) + ->setOrder('projects.name') + ->setQuery($query) + ->calculate(); + + $this->response->html($this->helper->layout->projectUser('project_user_overview/roles', array( + 'paginator' => $paginator, + 'title' => $title, + 'user_id' => $user_id, + 'users' => $users, + ))); + } + + private function tasks($is_active, $action, $title, $title_user) + { + list($user_id, $project_ids, $users) = $this->common(); + + $query = $this->taskFinderModel->getProjectUserOverviewQuery($project_ids, $is_active); + + if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) { + $query->eq(TaskModel::TABLE.'.owner_id', $user_id); + $title = t($title_user, $users[$user_id]); + } + + $paginator = $this->paginator + ->setUrl('ProjectUserOverviewController', $action, array('user_id' => $user_id)) + ->setMax(50) + ->setOrder(TaskModel::TABLE.'.id') + ->setQuery($query) + ->calculate(); + + $this->response->html($this->helper->layout->projectUser('project_user_overview/tasks', array( + 'paginator' => $paginator, + 'title' => $title, + 'user_id' => $user_id, + 'users' => $users, + ))); + } + + /** + * Display the list of project managers + * + */ + public function managers() + { + $this->role(Role::PROJECT_MANAGER, 'managers', t('People who are project managers'), 'Projects where "%s" is manager'); + } + + /** + * Display the list of project members + * + */ + public function members() + { + $this->role(Role::PROJECT_MEMBER, 'members', t('People who are project members'), 'Projects where "%s" is member'); + } + + /** + * Display the list of open taks + * + */ + public function opens() + { + $this->tasks(TaskModel::STATUS_OPEN, 'opens', t('Open tasks'), 'Open tasks assigned to "%s"'); + } + + /** + * Display the list of closed tasks + * + */ + public function closed() + { + $this->tasks(TaskModel::STATUS_CLOSED, 'closed', t('Closed tasks'), 'Closed tasks assigned to "%s"'); + } + + /** + * Users tooltip + */ + public function users() + { + $project = $this->getProject(); + + $this->response->html($this->template->render('project_user_overview/tooltip_users', array( + 'users' => $this->projectUserRoleModel->getAllUsersGroupedByRole($project['id']), + 'roles' => $this->projectRoleModel->getList($project['id']), + ))); + } +} diff --git a/app/Controller/ProjectViewController.php b/app/Controller/ProjectViewController.php new file mode 100644 index 0000000..b448398 --- /dev/null +++ b/app/Controller/ProjectViewController.php @@ -0,0 +1,228 @@ +getProject(); + $columns = $this->columnModel->getAllWithTaskCount($project['id']); + + $this->response->html($this->helper->layout->project('project_view/show', array( + 'project' => $project, + 'columns' => $columns, + 'title' => $project['name'], + ))); + } + + /** + * Public access management + * + * @access public + */ + public function share() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->project('project_view/share', array( + 'project' => $project, + 'title' => t('Public access'), + ))); + } + + /** + * Change project sharing + * + * @throws \Kanboard\Core\Controller\AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function updateSharing() + { + $project = $this->getProject(); + $this->checkCSRFParam(); + $switch = $this->request->getStringParam('switch'); + + if ($this->projectModel->{$switch.'PublicAccess'}($project['id'])) { + $this->flash->success(t('Project updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectViewController', 'share', array('project_id' => $project['id']))); + } + + /** + * Integrations page + * + * @access public + */ + public function integrations() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->project('project_view/integrations', array( + 'project' => $project, + 'title' => t('Integrations'), + 'webhook_token' => $this->configModel->get('webhook_token'), + 'values' => $this->projectMetadataModel->getAll($project['id']), + 'errors' => array(), + ))); + } + + /** + * Update integrations + * + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function updateIntegrations() + { + $project = $this->getProject(); + + $this->projectMetadataModel->save($project['id'], $this->request->getValues()); + $this->flash->success(t('Project updated successfully.')); + $this->response->redirect($this->helper->url->to('ProjectViewController', 'integrations', array('project_id' => $project['id']))); + } + + /** + * Display project notifications + * + * @access public + */ + public function notifications() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->project('project_view/notifications', array( + 'notifications' => $this->projectNotificationModel->readSettings($project['id']), + 'types' => $this->projectNotificationTypeModel->getTypes(), + 'project' => $project, + 'title' => t('Notifications'), + ))); + } + + /** + * Update notifications + * + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function updateNotifications() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + + $this->projectNotificationModel->saveSettings($project['id'], $values); + $this->flash->success(t('Project updated successfully.')); + $this->response->redirect($this->helper->url->to('ProjectViewController', 'notifications', array('project_id' => $project['id']))); + } + + /** + * Duplicate a project + * + * @author Antonio Rabelo + * @author Michael Lüpkes + * @access public + */ + public function duplicate() + { + $project = $this->getProject(); + + $this->response->html($this->helper->layout->project('project_view/duplicate', array( + 'project' => $project, + 'title' => t('Clone this project') + ))); + } + + /** + * Do project duplication + */ + public function doDuplication() + { + $this->checkCSRFForm(); + + $project = $this->getProject(); + $values = $this->request->getRawFormValues(); + + $project_id = $this->projectDuplicationModel->duplicate($project['id'], array_keys($values), $this->userSession->getId()); + + if ($project_id !== false) { + $this->flash->success(t('Project cloned successfully.')); + } else { + $this->flash->failure(t('Unable to clone this project.')); + } + + $this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project_id))); + } + + /** + * Import another project's tasks into the currently opened project. + * + * @return void + * @throws \Kanboard\Core\Controller\AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function importTasks() + { + $project = $this->getProject(); + + // Fetch list of projects to copy tasks from. + // Remove current project from the list of the user's projects. + $otherProjects = array_filter( + $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()), + static function ($projectId) use ($project) { + return (int) $project['id'] !== $projectId; + }, + ARRAY_FILTER_USE_KEY + ); + + $this->response->html($this->helper->layout->project('project_view/importTasks', array( + 'project' => $project, + 'title' => t('Import tasks from another project'), + 'projects' => $otherProjects, + 'values' => [], + 'errors' => [], + ))); + } + + /** + * Handle a form submission to copy tasks of a project. + * + * @return void + * @throws \Kanboard\Core\Controller\AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function doTasksImport() + { + $this->checkCSRFForm(); + + $project = $this->getProject(); + $values = $this->request->getValues(); + $srcProjectId = isset($values['src_project_id']) ? (int) $values['src_project_id'] : 0; + + if (empty($srcProjectId) || !$this->projectPermissionModel->isUserAllowed($srcProjectId, $this->userSession->getId())) { + $this->response->redirect($this->helper->url->to('ProjectViewController', 'importTasks', array('project_id' => $project['id']))); + return; + } + + if ($this->projectTaskDuplicationModel->duplicate($srcProjectId, $project['id'])) { + $this->flash->success(t('Tasks copied successfully.')); + } else { + $this->flash->failure(t('Unable to copy tasks.')); + $this->response->redirect($this->helper->url->to('ProjectViewController', 'importTasks', array('project_id' => $project['id']))); + return; + } + + $this->response->redirect($this->helper->url->to('ProjectViewController', 'show', array('project_id' => $project['id']))); + } +} diff --git a/app/Controller/SearchController.php b/app/Controller/SearchController.php new file mode 100644 index 0000000..2cd6d5d --- /dev/null +++ b/app/Controller/SearchController.php @@ -0,0 +1,70 @@ +projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); + $search = urldecode($this->request->getStringParam('search')); + $nb_tasks = 0; + + $paginator = $this->paginator + ->setUrl('SearchController', 'index', array('search' => $search)) + ->setMax(30) + ->setOrder(TaskModel::TABLE.'.id') + ->setDirection('DESC'); + + if ($search !== '' && ! empty($projects)) { + $paginator + ->setFormatter($this->taskListFormatter) + ->setQuery( + $this->taskLexer + ->build($search) + ->withFilter(new TaskProjectsFilter(array_keys($projects))) + ->getQuery() + ) + ->calculate(); + + $nb_tasks = $paginator->getTotal(); + } + + $this->response->html($this->helper->layout->app('search/index', array( + 'values' => array( + 'search' => $search, + 'controller' => 'SearchController', + 'action' => 'index', + ), + 'paginator' => $paginator, + 'title' => t('Search tasks').($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '') + ))); + } + + public function activity() + { + $search = urldecode($this->request->getStringParam('search')); + $events = $this->helper->projectActivity->searchEvents($search); + $nb_events = count($events); + + $this->response->html($this->helper->layout->app('search/activity', array( + 'values' => array( + 'search' => $search, + 'controller' => 'SearchController', + 'action' => 'activity', + ), + 'title' => t('Search in activity stream').($nb_events > 0 ? ' ('.$nb_events.')' : ''), + 'nb_events' => $nb_events, + 'events' => $events, + ))); + } +} diff --git a/app/Controller/SubtaskController.php b/app/Controller/SubtaskController.php new file mode 100644 index 0000000..ffb4516 --- /dev/null +++ b/app/Controller/SubtaskController.php @@ -0,0 +1,222 @@ +getTask(); + + if (empty($values)) { + $values = $this->prepareValues($task); + } + + $this->response->html($this->template->render('subtask/create', array( + 'values' => $values, + 'errors' => $errors, + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']), + 'task' => $task, + ))); + } + + /** + * Prepare form values + * + * @access protected + * @param array $task + * @return array + */ + protected function prepareValues(array $task) + { + $values = array( + 'task_id' => $task['id'], + 'user_id' => $task['owner_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 + * + * @access public + */ + public function save() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $subtasks = explode("\r\n", isset($values['title']) ? $values['title'] : ''); + $subtasksAdded = 0; + + foreach ($subtasks as $subtask) { + $subtask = trim($subtask); + + if (! empty($subtask)) { + $subtaskValues = $values; + $subtaskValues['title'] = $subtask; + + list($valid, $errors) = $this->subtaskValidator->validateCreation($subtaskValues); + + if (! $valid) { + $this->create($values, $errors); + return false; + } + + if (! $this->subtaskModel->create($subtaskValues)) { + $this->flash->failure(t('Unable to create your sub-task.')); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'subtasks'), true); + return false; + } + + $subtasksAdded++; + } + } + + if (isset($values['another_subtask']) && $values['another_subtask'] == 1) { + return $this->create(array( + 'project_id' => $task['project_id'], + 'task_id' => $task['id'], + 'user_id' => $values['user_id'], + 'another_subtask' => 1, + 'subtasks_added' => $subtasksAdded, + )); + } elseif ($subtasksAdded > 0) { + if ($subtasksAdded === 1) { + $this->flash->success(t('Subtask added successfully.')); + } else { + $this->flash->success(t('%d subtasks added successfully.', $subtasksAdded)); + } + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'subtasks'), true); + } + + /** + * Edit form + * + * @access public + * @param array $values + * @param array $errors + * @throws AccessForbiddenException + * @throws PageNotFoundException + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + + $this->response->html($this->template->render('subtask/edit', array( + 'values' => empty($values) ? $subtask : $values, + 'errors' => $errors, + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($task['project_id']), + 'status_list' => $this->subtaskModel->getStatusList(), + 'subtask' => $subtask, + 'task' => $task, + ))); + } + + /** + * Update and validate a subtask + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + + $values = $this->request->getValues(); + $values['id'] = $subtask['id']; + $values['task_id'] = $task['id']; + + list($valid, $errors) = $this->subtaskValidator->validateModification($values); + + if ($valid) { + if ($this->subtaskModel->update($values)) { + $this->flash->success(t('Sub-task updated successfully.')); + } else { + $this->flash->failure(t('Unable to update your sub-task.')); + } + + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } + + return $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a subtask + * + * @access public + */ + public function confirm() + { + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + + $this->response->html($this->template->render('subtask/remove', array( + 'subtask' => $subtask, + 'task' => $task, + ))); + } + + /** + * Remove a subtask + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + + if ($this->subtaskModel->remove($subtask['id'])) { + $this->flash->success(t('Sub-task removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this sub-task.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } + + /** + * Move subtask position + * + * @access public + */ + public function movePosition() + { + $task = $this->getTask(); + $values = $this->request->getJson(); + + if (! empty($values) && $this->helper->user->hasProjectAccess('SubtaskController', 'movePosition', $task['project_id'])) { + $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 new file mode 100644 index 0000000..acda221 --- /dev/null +++ b/app/Controller/SubtaskConverterController.php @@ -0,0 +1,45 @@ +getTask(); + $subtask = $this->getSubtask($task); + + $this->response->html($this->template->render('subtask_converter/show', array( + 'subtask' => $subtask, + 'task' => $task, + ))); + } + + public function save() + { + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + + if (! $this->helper->projectRole->canCreateTaskInColumn($task['project_id'], $task['column_id'])) { + $this->flash->failure(t('You cannot create tasks in this column.')); + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', ['task_id' => $task['id']])); + return; + } + + $task_id = $this->subtaskTaskConversionModel->convertToTask($task['project_id'], $subtask['id']); + + if ($task_id !== false) { + $this->flash->success(t('Subtask converted to task successfully.')); + } else { + $this->flash->failure(t('Unable to convert the subtask.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task_id)), true); + } +} diff --git a/app/Controller/SubtaskRestrictionController.php b/app/Controller/SubtaskRestrictionController.php new file mode 100644 index 0000000..7e35439 --- /dev/null +++ b/app/Controller/SubtaskRestrictionController.php @@ -0,0 +1,64 @@ +getTask(); + $subtask = $this->getSubtask($task); + $subtaskInProgress = $this->subtaskStatusModel->getSubtaskInProgress($this->userSession->getId()); + + $this->response->html($this->template->render('subtask_restriction/show', array( + 'status_list' => array( + SubtaskModel::STATUS_TODO => 'Todo', + SubtaskModel::STATUS_DONE => 'Done', + ), + 'subtask_inprogress' => $subtaskInProgress, + 'subtask' => $subtask, + 'task' => $task, + ))); + } + + /** + * Change status of the in progress subtask and the other subtask + * + * @access public + */ + public function save() + { + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + $values = $this->request->getValues(); + + if (! empty($values)) { + // Change status of the previous "in progress" subtask + $this->subtaskModel->update(array( + 'id' => $values['id'], + 'status' => $values['status'], + )); + + // Set the current subtask to "in progress" + $this->subtaskModel->update(array( + 'id' => $subtask['id'], + 'status' => SubtaskModel::STATUS_INPROGRESS, + )); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } +} diff --git a/app/Controller/SubtaskStatusController.php b/app/Controller/SubtaskStatusController.php new file mode 100644 index 0000000..36a1b13 --- /dev/null +++ b/app/Controller/SubtaskStatusController.php @@ -0,0 +1,102 @@ + In Progress -> Done + * + * @access public + */ + public function change() + { + $this->checkReusableGETCSRFParam(); + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + $fragment = $this->request->getStringParam('fragment'); + + $status = $this->subtaskStatusModel->toggleStatus($subtask['id']); + $subtask['status'] = $status; + + if ($fragment === 'table') { + $html = $this->renderTable($task); + } elseif ($fragment === 'rows') { + $html = $this->renderRows($task); + } else { + $html = $this->helper->subtask->renderToggleStatus($task, $subtask); + } + + $this->response->html($html); + } + + /** + * Start/stop timer for subtasks + * + * @access public + */ + public function timer() + { + $this->checkReusableGETCSRFParam(); + $task = $this->getTask(); + $subtask = $this->getSubtask($task); + $timer = $this->request->getStringParam('timer'); + + if ($timer === 'start') { + $this->subtaskTimeTrackingModel->logStartTime($subtask['id'], $this->userSession->getId()); + } elseif ($timer === 'stop') { + $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $this->userSession->getId()); + $this->subtaskTimeTrackingModel->updateTaskTimeTracking($task['id']); + } + + $this->response->html($this->template->render('subtask/timer', array( + 'task' => $task, + 'subtask' => $this->subtaskModel->getByIdWithDetails($subtask['id']), + ))); + } + + /** + * Render table + * + * @access protected + * @param array $task + * @return string + */ + protected function renderTable(array $task) + { + return $this->template->render('subtask/table', array( + 'task' => $task, + 'subtasks' => $this->subtaskModel->getAll($task['id']), + 'editable' => true, + )); + } + + /** + * Render task list rows + * + * @access protected + * @param array $task + * @return string + */ + protected function renderRows(array $task) + { + $userId = $this->request->getIntegerParam('user_id'); + + if ($userId > 0) { + $task['subtasks'] = $this->subtaskModel->getAllByTaskIdsAndAssignee(array($task['id']), $userId); + } else { + $task['subtasks'] = $this->subtaskModel->getAll($task['id']); + } + + return $this->template->render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $userId, + )); + } +} diff --git a/app/Controller/SwimlaneController.php b/app/Controller/SwimlaneController.php new file mode 100644 index 0000000..e0fc407 --- /dev/null +++ b/app/Controller/SwimlaneController.php @@ -0,0 +1,219 @@ +getProject(); + $swimlanes = $this->swimlaneModel->getAllWithTaskCount($project['id']); + + $this->response->html($this->helper->layout->project('swimlane/index', array( + 'active_swimlanes' => $swimlanes['active'], + 'inactive_swimlanes' => $swimlanes['inactive'], + 'project' => $project, + 'title' => t('Swimlanes') + ))); + } + + /** + * Create a new swimlane + * + * @access public + * @param array $values + * @param array $errors + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function create(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + + $this->response->html($this->template->render('swimlane/create', array( + 'values' => $values + array('project_id' => $project['id']), + 'errors' => $errors, + 'project' => $project, + ))); + } + + /** + * Validate and save a new swimlane + * + * @access public + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + + list($valid, $errors) = $this->swimlaneValidator->validateCreation($values); + + if ($valid) { + if ($this->swimlaneModel->create($project['id'], $values['name'], $values['description'], $values['task_limit']) !== false) { + $this->flash->success(t('Your swimlane has been created successfully.')); + $this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id'])), true); + return; + } else { + $errors = array('name' => array(t('Another swimlane with the same name exists in the project'))); + } + } + + $this->create($values, $errors); + } + + /** + * Edit a swimlane (display the form) + * + * @access public + * @param array $values + * @param array $errors + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function edit(array $values = array(), array $errors = array()) + { + $project = $this->getProject(); + $swimlane = $this->getSwimlane($project); + + $this->response->html($this->helper->layout->project('swimlane/edit', array( + 'values' => empty($values) ? $swimlane : $values, + 'errors' => $errors, + 'project' => $project, + ))); + } + + /** + * Edit a swimlane (validate the form and update the database) + * + * @access public + */ + public function update() + { + $project = $this->getProject(); + $swimlane = $this->getSwimlane($project); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $values['id'] = $swimlane['id']; + + list($valid, $errors) = $this->swimlaneValidator->validateModification($values); + + if ($valid) { + if ($this->swimlaneModel->update($values['id'], $values)) { + $this->flash->success(t('Swimlane updated successfully.')); + return $this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id']))); + } else { + $errors = array('name' => array(t('Another swimlane with the same name exists in the project'))); + } + } + + return $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a swimlane + * + * @access public + */ + public function confirm() + { + $project = $this->getProject(); + $swimlane = $this->getSwimlane($project); + + $this->response->html($this->helper->layout->project('swimlane/remove', array( + 'project' => $project, + 'swimlane' => $swimlane, + ))); + } + + /** + * Remove a swimlane + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $swimlane = $this->getSwimlane($project); + + if ($this->swimlaneModel->remove($project['id'], $swimlane['id'])) { + $this->flash->success(t('Swimlane removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this swimlane.')); + } + + $this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id']))); + } + + /** + * Disable a swimlane + * + * @access public + */ + public function disable() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $swimlane = $this->getSwimlane($project); + + if ($this->swimlaneModel->disable($project['id'], $swimlane['id'])) { + $this->flash->success(t('Swimlane updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this swimlane.')); + } + + $this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id']))); + } + + /** + * Enable a swimlane + * + * @access public + */ + public function enable() + { + $this->checkCSRFParam(); + $project = $this->getProject(); + $swimlane = $this->getSwimlane($project); + + if ($this->swimlaneModel->enable($project['id'], $swimlane['id'])) { + $this->flash->success(t('Swimlane updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this swimlane.')); + } + + $this->response->redirect($this->helper->url->to('SwimlaneController', 'index', array('project_id' => $project['id']))); + } + + /** + * Move swimlane position + * + * @access public + */ + public function move() + { + $this->checkReusableGETCSRFParam(); + $project = $this->getProject(); + $values = $this->request->getJson(); + + if (! empty($values) && isset($values['swimlane_id']) && isset($values['position'])) { + $result = $this->swimlaneModel->changePosition($project['id'], $values['swimlane_id'], $values['position']); + $this->response->json(array('result' => $result)); + } else { + throw new AccessForbiddenException(); + } + } +} diff --git a/app/Controller/TagController.php b/app/Controller/TagController.php new file mode 100644 index 0000000..6ad9ac8 --- /dev/null +++ b/app/Controller/TagController.php @@ -0,0 +1,124 @@ +response->html($this->helper->layout->config('tag/index', array( + 'tags' => $this->tagModel->getAllByProject(0), + 'colors' => $this->colorModel->getList(), + 'title' => t('Settings').' > '.t('Global tags management'), + ))); + } + + public function create(array $values = array(), array $errors = array()) + { + if (empty($values)) { + $values['project_id'] = 0; + } + + $this->response->html($this->template->render('tag/create', array( + 'values' => $values, + 'colors' => $this->colorModel->getList(), + 'errors' => $errors, + ))); + } + + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->tagValidator->validateCreation($values); + + if ($valid) { + if ($this->tagModel->create(0, $values['name'], $values['color_id']) > 0) { + $this->flash->success(t('Tag created successfully.')); + } else { + $this->flash->failure(t('Unable to create this tag.')); + } + + $this->response->redirect($this->helper->url->to('TagController', 'index')); + } else { + $this->create($values, $errors); + } + } + + public function edit(array $values = array(), array $errors = array()) + { + $tag_id = $this->request->getIntegerParam('tag_id'); + $tag = $this->tagModel->getById($tag_id); + + if (empty($values)) { + $values = $tag; + } + + $this->response->html($this->template->render('tag/edit', array( + 'tag' => $tag, + 'values' => $values, + 'colors' => $this->colorModel->getList(), + 'errors' => $errors, + ))); + } + + public function update() + { + $tag_id = $this->request->getIntegerParam('tag_id'); + $tag = $this->tagModel->getById($tag_id); + $values = $this->request->getValues(); + list($valid, $errors) = $this->tagValidator->validateModification($values); + + if ($tag['project_id'] != 0) { + throw new AccessForbiddenException(); + } + + if ($valid) { + if ($this->tagModel->update($values['id'], $values['name'], $values['color_id'])) { + $this->flash->success(t('Tag updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this tag.')); + } + + $this->response->redirect($this->helper->url->to('TagController', 'index')); + } else { + $this->edit($values, $errors); + } + } + + public function confirm() + { + $tag_id = $this->request->getIntegerParam('tag_id'); + $tag = $this->tagModel->getById($tag_id); + + $this->response->html($this->template->render('tag/remove', array( + 'tag' => $tag, + ))); + } + + public function remove() + { + $this->checkCSRFParam(); + $tag_id = $this->request->getIntegerParam('tag_id'); + $tag = $this->tagModel->getById($tag_id); + + if ($tag['project_id'] != 0) { + throw new AccessForbiddenException(); + } + + if ($this->tagModel->remove($tag_id)) { + $this->flash->success(t('Tag removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this tag.')); + } + + $this->response->redirect($this->helper->url->to('TagController', 'index')); + } +} diff --git a/app/Controller/TaskAjaxController.php b/app/Controller/TaskAjaxController.php new file mode 100644 index 0000000..2c69e5c --- /dev/null +++ b/app/Controller/TaskAjaxController.php @@ -0,0 +1,87 @@ +request->getStringParam('term'); + $project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId()); + $exclude_task_ids = $this->request->getStringParam('exclude_task_ids'); + $exclude_task_ids = array_filter(array_map('trim', explode(',', $exclude_task_ids))); + + if (empty($project_ids)) { + $this->response->json(array()); + } else { + + $filter = $this->taskQuery->withFilter(new TaskProjectsFilter($project_ids)); + + if (! empty($exclude_task_ids)) { + $filter->withFilter(new TaskIdExclusionFilter($exclude_task_ids)); + } + + if (ctype_digit((string) $search)) { + $filter->withFilter(new TaskIdFilter($search)); + } else { + $filter->withFilter(new TaskTitleFilter($search)); + } + + $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)); + } + } + + /** + * Task edit preview + */ + public function preview() + { + $text = $this->request->getRawValue('text'); + + if (empty($text)) { + $this->response->json(array()); + } else { + $preview = $this->helper->text->markdown($text); + $this->response->json(array($preview)); + } + } + +} diff --git a/app/Controller/TaskBulkChangePropertyController.php b/app/Controller/TaskBulkChangePropertyController.php new file mode 100644 index 0000000..1d5b600 --- /dev/null +++ b/app/Controller/TaskBulkChangePropertyController.php @@ -0,0 +1,102 @@ +getProject(); + + if (empty($values)) { + $values['task_ids'] = $this->request->getStringParam('task_ids'); + } + + $this->response->html($this->template->render('task_bulk_change_property/show', [ + 'project' => $project, + 'values' => $values, + 'errors' => $errors, + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id']), + 'categories_list' => $this->categoryModel->getList($project['id']), + 'internallink_list' => $this->linkModel->getList(), + ])); + } + + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $taskIDs = explode(',', $values['task_ids']); + + foreach ($taskIDs as $taskID) { + if ($this->taskFinderModel->getProjectId($taskID) != $project['id']) { + throw new AccessForbiddenException(); + } + + $changes = []; + + if (isset($values['change_color']) && $values['change_color'] == 1) { + $changes['color_id'] = $values['color_id']; + } + + if (isset($values['change_assignee']) && $values['change_assignee'] == 1) { + $changes['owner_id'] = $values['owner_id']; + } + + if (isset($values['change_priority']) && $values['change_priority'] == 1) { + $changes['priority'] = $values['priority']; + } + + if (isset($values['change_category']) && $values['change_category'] == 1) { + $changes['category_id'] = $values['category_id']; + } + + if (isset($values['change_tags']) && $values['change_tags'] == 1) { + $changes['tags'] = $values['tags']; + if (isset($values['change_tags_only_add_new']) && $values['change_tags_only_add_new'] == 1) { + $changes['tags_only_add_new'] = $values['change_tags_only_add_new']; + } + } + + if (isset($values['change_due_date']) && $values['change_due_date'] == 1) { + $changes['date_due'] = $values['date_due']; + } + + if (isset($values['change_start_date']) && $values['change_start_date'] == 1) { + $changes['date_started'] = $values['date_started']; + } + + if (isset($values['change_estimated_time']) && $values['change_estimated_time'] == 1) { + $changes['time_estimated'] = $values['time_estimated']; + } + + if (isset($values['change_spent_time']) && $values['change_spent_time'] == 1) { + $changes['time_spent'] = $values['time_spent']; + } + + if (isset($values['change_score']) && $values['change_score'] == 1) { + $changes['score'] = $values['score']; + } + + if (isset($values['change_internallink']) && $values['change_internallink'] == 1) { + $this->taskLinkModel->create($taskID, $values['opposite_task_id'], $values['link_id']); + } + + if (isset($values['change_internallink_remove']) && $values['change_internallink_remove'] == 1) { + $task_link_ids = $this->taskLinkModel->getAll($taskID); + foreach ($task_link_ids as $task_link_id) { + $this->taskLinkModel->remove($task_link_id['id']); + } + } + + if (! empty($changes)) { + $changes['id'] = $taskID; + $this->taskModificationModel->update($changes); + } + } + + $this->response->redirect($this->helper->url->to('TaskListController', 'show', ['project_id' => $project['id']]), true); + } +} diff --git a/app/Controller/TaskBulkController.php b/app/Controller/TaskBulkController.php new file mode 100644 index 0000000..ebca3d4 --- /dev/null +++ b/app/Controller/TaskBulkController.php @@ -0,0 +1,108 @@ +getProject(); + + if (empty($values)) { + $values = array( + 'swimlane_id' => $this->request->getIntegerParam('swimlane_id'), + 'column_id' => $this->request->getIntegerParam('column_id'), + 'project_id' => $project['id'], + ); + } + + $this->response->html($this->template->render('task_bulk/show', array( + 'project' => $project, + 'values' => $values, + 'errors' => $errors, + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private'] == 1), + 'colors_list' => $this->colorModel->getList(), + 'categories_list' => $this->categoryModel->getList($project['id']), + 'task_description_templates' => $this->predefinedTaskDescriptionModel->getList($project['id']), + ))); + } + + /** + * Save all tasks in the database + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + list($valid, $errors) = $this->taskValidator->validateBulkCreation($values); + + if (! $valid) { + $this->show($values, $errors); + } elseif (! $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', + 'show', + array('project_id' => $project['id']), + 'swimlane-'. $values['swimlane_id'] + ), true); + } + } + + /** + * Create all tasks + * + * @param array $project + * @param array $values + */ + protected function createTasks(array $project, array $values) + { + $tasks = preg_split('/\r\n|[\r\n]/', $values['tasks']); + + foreach ($tasks as $title) { + $title = trim($title); + + if (! empty($title)) { + $this->taskCreationModel->create(array( + 'title' => $title, + 'column_id' => $values['column_id'], + 'swimlane_id' => $values['swimlane_id'], + 'category_id' => empty($values['category_id']) ? 0 : $values['category_id'], + 'owner_id' => empty($values['owner_id']) ? 0 : $values['owner_id'], + 'color_id' => $values['color_id'], + 'project_id' => $project['id'], + 'description' => $this->getTaskDescription($project, $values), + 'tags' => $values['tags'], + 'priority' => $values['priority'], + 'score' => $values['score'], + 'time_estimated' => $values['time_estimated'], + 'date_due' => $values['date_due'], + )); + } + } + } + + protected function getTaskDescription(array $project, array $values) + { + if (empty($values['task_description_template_id'])) { + return ''; + } + + return $this->predefinedTaskDescriptionModel->getDescriptionById($project['id'], $values['task_description_template_id']); + } +} diff --git a/app/Controller/TaskBulkMoveColumnController.php b/app/Controller/TaskBulkMoveColumnController.php new file mode 100644 index 0000000..f3607df --- /dev/null +++ b/app/Controller/TaskBulkMoveColumnController.php @@ -0,0 +1,44 @@ +getProject(); + + if (empty($values)) { + $values['task_ids'] = $this->request->getStringParam('task_ids'); + } + + $this->response->html($this->template->render('task_bulk_move_column/show', [ + 'project' => $project, + 'values' => $values, + 'errors' => $errors, + 'columns' => $this->columnModel->getList($project['id']), + 'swimlanes' => $this->swimlaneModel->getList($project['id'], false, true), + ])); + } + + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $taskIDs = explode(',', $values['task_ids']); + + foreach ($taskIDs as $taskID) { + $task = $this->taskFinderModel->getById($taskID); + + 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->moveBottom($project['id'], $taskID, $values['swimlane_id'], $values['column_id']); + } + + $this->response->redirect($this->helper->url->to('TaskListController', 'show', ['project_id' => $project['id']]), true); + } +} diff --git a/app/Controller/TaskCreationController.php b/app/Controller/TaskCreationController.php new file mode 100644 index 0000000..2892e73 --- /dev/null +++ b/app/Controller/TaskCreationController.php @@ -0,0 +1,187 @@ +getProject(); + $swimlanesList = $this->swimlaneModel->getList($project['id'], false, true); + $values += $this->prepareValues($project['is_private'], $swimlanesList); + + $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, + 'errors' => $errors, + 'values' => $values + array('project_id' => $project['id']), + 'columns_list' => $this->columnModel->getList($project['id']), + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], true, false, $project['is_private'] == 1), + 'categories_list' => $this->categoryModel->getList($project['id']), + 'swimlanes_list' => $swimlanesList, + 'screenshot' => $screenshot, + 'files' => $files, + ))); + } + + /** + * Validate and save a new task + * + * @access public + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $values['project_id'] = $project['id']; + $files = $this->request->getFileInfo('files'); + + $screenshot = null; + if (array_key_exists('screenshot', $values)) { + $screenshot = $values['screenshot']; + unset($values['screenshot']); + } + + list($valid, $errors) = $this->taskValidator->validateCreation($values); + + if (! $valid) { + $this->flash->failure(t('Unable to create your task.')); + $this->show($values, $screenshot, $files, $errors); + } elseif (! $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); + if ($task_id === 0) { + $this->flash->failure(t('Unable to create this task.')); + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); + return; + } + + if ($screenshot) { + $this->taskFileModel->uploadScreenshot($task_id, $screenshot); + } + + if (isset($files['name'][0]) && $files['name'][0] !== '') { + $filesUploaded = $this->taskFileModel->uploadFiles($task_id, $files); + if (! $filesUploaded) { + $this->flash->failure(t('Unable to upload files, check the permissions of your data folder.')); + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', ['project_id' => $project['id']]), true); + return; + } + } + + $this->flash->success(t('Task created successfully.')); + $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) { + if (! $this->projectPermissionModel->isUserAllowed($project_id, $this->userSession->getId())) { + throw new AccessForbiddenException(); + } + $this->taskProjectDuplicationModel->duplicateToProject($values['task_id'], $project_id); + } + } + + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $project['id'])), true); + } + + /** + * 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['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, + 'column_id' => $values['column_id'], + '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 bool $isPrivateProject + * @param array $swimlanesList + * @return array + */ + protected function prepareValues($isPrivateProject, array $swimlanesList) + { + $values = array( + 'swimlane_id' => $this->request->getIntegerParam('swimlane_id', key($swimlanesList)), + 'column_id' => $this->request->getIntegerParam('column_id'), + 'color_id' => $this->colorModel->getDefaultColor(), + ); + + if ($isPrivateProject) { + $values['owner_id'] = $this->userSession->getId(); + } + + return $values; + } + + /** + * 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/TaskDuplicationController.php b/app/Controller/TaskDuplicationController.php new file mode 100644 index 0000000..ddf2ffb --- /dev/null +++ b/app/Controller/TaskDuplicationController.php @@ -0,0 +1,159 @@ +getTask(); + + if ($this->request->getStringParam('confirmation') === 'yes') { + $this->checkCSRFParam(); + $task_id = $this->taskDuplicationModel->duplicate($task['id']); + + if ($task_id > 0) { + $this->flash->success(t('Task created successfully.')); + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task_id))); + } else { + $this->flash->failure(t('Unable to create this task.')); + return $this->response->redirect($this->helper->url->to('TaskDuplicationController', 'duplicate', array('task_id' => $task['id'])), true); + } + } + + return $this->response->html($this->template->render('task_duplication/duplicate', array( + 'task' => $task, + ))); + } + + /** + * Move a task to another project + * + * @access public + */ + public function move() + { + $task = $this->getTask(); + + if ($this->request->isPost()) { + $values = $this->request->getValues(); + list($valid, ) = $this->taskValidator->validateProjectModification($values); + + if ($valid) { + if (! $this->projectPermissionModel->isUserAllowed($values['project_id'], $this->userSession->getId())) { + throw new AccessForbiddenException(); + } + + if ($this->taskProjectMoveModel->moveToProject( + $task['id'], + $values['project_id'], + $values['swimlane_id'], + $values['column_id'], + $values['category_id'], + $values['owner_id'] + )) { + $this->flash->success(t('Task updated successfully.')); + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']))); + } + } + + $this->flash->failure(t('Unable to update your task.')); + } + + return $this->chooseDestination($task, 'task_duplication/move'); + } + + /** + * Duplicate a task to another project + * + * @access public + */ + public function copy() + { + $task = $this->getTask(); + + if ($this->request->isPost()) { + $values = $this->request->getValues(); + list($valid, ) = $this->taskValidator->validateProjectModification($values); + + if ($valid) { + if (! $this->projectPermissionModel->isUserAllowed($values['project_id'], $this->userSession->getId())) { + throw new AccessForbiddenException(); + } + + $task_id = $this->taskProjectDuplicationModel->duplicateToProject( + $task['id'], + $values['project_id'], + $values['swimlane_id'], + $values['column_id'], + $values['category_id'], + $values['owner_id'] + ); + + if ($task_id > 0) { + $this->flash->success(t('Task created successfully.')); + return $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $task['project_id'])), true); + } + } + + $this->flash->failure(t('Unable to create your task.')); + } + + return $this->chooseDestination($task, 'task_duplication/copy'); + } + + /** + * Choose destination when move/copy task to another project + * + * @access private + * @param array $task + * @param string $template + */ + private function chooseDestination(array $task, $template) + { + $values = array(); + $projects_list = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); + + unset($projects_list[$task['project_id']]); + + if (! empty($projects_list)) { + $dst_project_id = $this->request->getIntegerParam('dst_project_id', key($projects_list)); + + $swimlanes_list = $this->swimlaneModel->getList($dst_project_id, false, true); + $columns_list = $this->columnModel->getList($dst_project_id); + $categories_list = $this->categoryModel->getList($dst_project_id); + $users_list = $this->projectUserRoleModel->getAssignableUsersList($dst_project_id); + + $values = $this->taskDuplicationModel->checkDestinationProjectValues($task); + $values['project_id'] = $dst_project_id; + } else { + $swimlanes_list = array(); + $columns_list = array(); + $categories_list = array(); + $users_list = array(); + } + + $this->response->html($this->template->render($template, array( + 'values' => $values, + 'task' => $task, + 'projects_list' => $projects_list, + 'swimlanes_list' => $swimlanes_list, + 'columns_list' => $columns_list, + 'categories_list' => $categories_list, + 'users_list' => $users_list, + ))); + } +} diff --git a/app/Controller/TaskExternalLinkController.php b/app/Controller/TaskExternalLinkController.php new file mode 100644 index 0000000..02cf472 --- /dev/null +++ b/app/Controller/TaskExternalLinkController.php @@ -0,0 +1,184 @@ +getTask(); + + $this->response->html($this->template->render('task_external_link/find', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'types' => $this->externalLinkManager->getTypes(), + ))); + } + + /** + * Second creation form + * + * @access public + */ + public function create() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + + try { + $provider = $this->externalLinkManager->setUserInput($values)->find(); + $link = $provider->getLink(); + + $this->response->html($this->template->render('task_external_link/create', array( + 'values' => array( + 'title' => $link->getTitle(), + 'url' => $link->getUrl(), + 'link_type' => $provider->getType(), + ), + 'dependencies' => $provider->getDependencies(), + 'errors' => array(), + 'task' => $task, + ))); + + } catch (ExternalLinkProviderNotFound $e) { + $errors = array('text' => array(t('Unable to fetch link information.'))); + $this->find($values, $errors); + } + } + + /** + * Save link + * + * @access public + */ + public function save() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + + list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); + + 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'])), 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, + ))); + } + } + + /** + * Edit form + * + * @access public + * @param array $values + * @param array $errors + * @throws ExternalLinkProviderNotFound + * @throws PageNotFoundException + * @throws \Kanboard\Core\Controller\AccessForbiddenException + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); + $provider = $this->externalLinkManager->getProvider($link['link_type']); + + $this->response->html($this->template->render('task_external_link/edit', array( + 'values' => empty($values) ? $link : $values, + 'errors' => $errors, + 'task' => $task, + 'link' => $link, + 'dependencies' => $provider->getDependencies(), + ))); + } + + /** + * Update link + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); + + $values = $this->request->getValues(); + $values['id'] = $link['id']; + $values['task_id'] = $link['task_id']; + + list($valid, $errors) = $this->externalLinkValidator->validateModification($values); + + if ($valid && $this->taskExternalLinkModel->update($values)) { + $this->flash->success(t('Link updated successfully.')); + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } + + return $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a link + * + * @access public + */ + public function confirm() + { + $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); + + $this->response->html($this->template->render('task_external_link/remove', array( + 'link' => $link, + 'task' => $task, + ))); + } + + /** + * Remove a link + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $task = $this->getTask(); + $link = $this->getExternalTaskLink($task); + + if ($this->taskExternalLinkModel->remove($link['id'])) { + $this->flash->success(t('Link removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this link.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']))); + } +} diff --git a/app/Controller/TaskFileController.php b/app/Controller/TaskFileController.php new file mode 100644 index 0000000..121731f --- /dev/null +++ b/app/Controller/TaskFileController.php @@ -0,0 +1,108 @@ +getTask(); + + if ($this->request->isPost() && $this->taskFileModel->uploadScreenshot($task['id'], $this->request->getValue('screenshot')) !== false) { + $this->flash->success(t('Screenshot uploaded successfully.')); + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } + + return $this->response->html($this->template->render('task_file/screenshot', array( + 'task' => $task, + ))); + } + + /** + * File upload form + * + * @access public + */ + public function create() + { + $task = $this->getTask(); + + $this->response->html($this->template->render('task_file/create', array( + 'task' => $task, + 'max_size' => get_upload_max_size(), + ))); + } + + /** + * File upload (save files) + * + * @access public + */ + public function save() + { + $this->checkReusableCSRFParam(); + $task = $this->getTask(); + $result = $this->taskFileModel->uploadFiles($task['id'], $this->request->getFileInfo('files')); + + if ($this->request->isAjax()) { + if (! $result) { + $this->response->json(array('message' => t('Unable to upload files, check the permissions of your data folder.')), 500); + } else { + $this->response->json(array('message' => 'OK')); + } + } else { + if (! $result) { + $this->flash->failure(t('Unable to upload files, check the permissions of your data folder.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } + } + + /** + * Remove a file + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $task = $this->getTask(); + $file = $this->getFile(); + + if ($file['task_id'] == $task['id'] && $this->taskFileModel->remove($file['id'])) { + $this->flash->success(t('File removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this file.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']))); + } + + /** + * Confirmation dialog before removing a file + * + * @access public + */ + public function confirm() + { + $task = $this->getTask(); + $file = $this->getFile(); + + $this->response->html($this->template->render('task_file/remove', array( + 'task' => $task, + 'file' => $file, + ))); + } +} diff --git a/app/Controller/TaskImportController.php b/app/Controller/TaskImportController.php new file mode 100644 index 0000000..6f36f5f --- /dev/null +++ b/app/Controller/TaskImportController.php @@ -0,0 +1,76 @@ +getProject(); + + $this->response->html($this->template->render('task_import/show', array( + 'project' => $project, + 'values' => $values, + 'errors' => $errors, + 'max_size' => get_upload_max_size(), + 'delimiters' => Csv::getDelimiters(), + 'enclosures' => Csv::getEnclosures(), + ))); + } + + /** + * Process CSV file + */ + public function save() + { + $project = $this->getProject(); + $values = $this->request->getValues(); + $filename = $this->request->getFilePath('file'); + + if (! file_exists($filename)) { + $this->show($values, array('file' => array(t('Unable to read your file')))); + } else { + $taskImport = new TaskImport($this->container); + $taskImport->setProjectId($project['id']); + + $csv = new Csv($values['delimiter'], $values['enclosure']); + $csv->setColumnMapping($taskImport->getColumnMapping()); + $csv->read($filename, array($taskImport, 'importTask')); + + if ($taskImport->getNumberOfImportedTasks() > 0) { + $this->flash->success(t('%d task(s) have been imported successfully.', $taskImport->getNumberOfImportedTasks())); + } else { + $this->flash->failure(t('Nothing has been imported!')); + } + + $this->response->redirect($this->helper->url->to('TaskImportController', 'show', array('project_id' => $project['id'])), true); + } + } + + /** + * Generate template + * + */ + public function template() + { + $taskImport = new TaskImport($this->container); + $this->response->withFileDownload('tasks.csv'); + $this->response->csv(array($taskImport->getColumnMapping())); + } +} diff --git a/app/Controller/TaskInternalLinkController.php b/app/Controller/TaskInternalLinkController.php new file mode 100644 index 0000000..f385307 --- /dev/null +++ b/app/Controller/TaskInternalLinkController.php @@ -0,0 +1,183 @@ +getTask(); + + if (empty($values)) { + $values['another_tasklink'] = $this->request->getIntegerParam('another_tasklink', 0); + $values = $this->hook->merge('controller:tasklink:form:default', $values, array('default_values' => $values)); + } + + $this->response->html($this->template->render('task_internal_link/create', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'labels' => $this->linkModel->getList(0, false), + ))); + } + + /** + * Validation and creation + * + * @access public + */ + public function save() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + + list($valid, $errors) = $this->taskLinkValidator->validateCreation($values); + + if ($valid) { + $opposite_task = $this->taskFinderModel->getById($values['opposite_task_id']); + + if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) { + throw new AccessForbiddenException(); + } + + if ($this->taskLinkModel->create($values['task_id'], $values['opposite_task_id'], $values['link_id']) !== false) { + $this->flash->success(t('Link added successfully.')); + + if (isset($values['another_tasklink']) && $values['another_tasklink'] == 1) { + return $this->create(array( + 'project_id' => $task['project_id'], + 'task_id' => $task['id'], + 'link_id' => $values['link_id'], + 'another_tasklink' => 1 + )); + } + + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } + + $errors = array('title' => array(t('The exact same link already exists'))); + $this->flash->failure(t('Unable to create your link.')); + } + + return $this->create($values, $errors); + } + + /** + * Edit form + * + * @access public + * @param array $values + * @param array $errors + * @throws PageNotFoundException + * @throws \Kanboard\Core\Controller\AccessForbiddenException + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + $task_link = $this->getInternalTaskLink($task); + + if (empty($values)) { + $opposite_task = $this->taskFinderModel->getById($task_link['opposite_task_id']); + $values = $task_link; + $values['title'] = '#'.$opposite_task['id'].' - '.$opposite_task['title']; + } + + $this->response->html($this->template->render('task_internal_link/edit', array( + 'values' => $values, + 'errors' => $errors, + 'task_link' => $task_link, + 'task' => $task, + 'labels' => $this->linkModel->getList(0, false) + ))); + } + + /** + * Validation and update + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $task_link = $this->getInternalTaskLink($task); + + $values = $this->request->getValues(); + $values['task_id'] = $task['id']; + $values['id'] = $task_link['id']; + + list($valid, $errors) = $this->taskLinkValidator->validateModification($values); + + if ($valid) { + $opposite_task = $this->taskFinderModel->getById($values['opposite_task_id']); + + if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) { + throw new AccessForbiddenException(); + } + + if ($this->taskLinkModel->update($values['id'], $values['task_id'], $values['opposite_task_id'], $values['link_id'])) { + $this->flash->success(t('Link updated successfully.')); + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])).'#links'); + } + + $this->flash->failure(t('Unable to update your link.')); + } + + return $this->edit($values, $errors); + } + + /** + * Confirmation dialog before removing a link + * + * @access public + */ + public function confirm() + { + $task = $this->getTask(); + $link = $this->getInternalTaskLink($task); + + $this->response->html($this->template->render('task_internal_link/remove', array( + 'link' => $link, + 'task' => $task, + ))); + } + + /** + * Remove a link + * + * @access public + */ + public function remove() + { + $this->checkCSRFParam(); + $task = $this->getTask(); + $link = $this->getInternalTaskLink($task); + + if ($this->taskLinkModel->remove($link['id'])) { + $this->flash->success(t('Link removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this link.')); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']))); + } +} diff --git a/app/Controller/TaskListController.php b/app/Controller/TaskListController.php new file mode 100644 index 0000000..48e0199 --- /dev/null +++ b/app/Controller/TaskListController.php @@ -0,0 +1,71 @@ +getProject(); + $search = $this->helper->projectHeader->getSearchQuery($project); + + if ($this->request->getIntegerParam('show_subtasks') !== 0 || + $this->request->getIntegerParam('hide_subtasks') !== 0 || + $this->request->getStringParam('direction') !== '' || + $this->request->getStringParam('order') !== '') { + $this->checkReusableGETCSRFParam(); + } + + if ($this->request->getIntegerParam('show_subtasks')) { + session_set('subtaskListToggle', true); + } elseif ($this->request->getIntegerParam('hide_subtasks')) { + session_set('subtaskListToggle', false); + } + + if ($this->userSession->hasSubtaskListActivated()) { + $formatter = $this->taskListSubtaskFormatter; + } else { + $formatter = $this->taskListFormatter; + } + + list($order, $direction) = $this->userSession->getListOrder($project['id']); + $direction = $this->request->getStringParam('direction', $direction); + $order = $this->request->getStringParam('order', $order); + $this->userSession->setListOrder($project['id'], $order, $direction); + + $paginator = $this->paginator + ->setUrl('TaskListController', 'show', array('project_id' => $project['id'], 'csrf_token' => $this->token->getReusableCSRFToken())) + ->setMax(30) + ->setOrder($order) + ->setDirection($direction) + ->setFormatter($formatter) + ->setQuery( + $this->taskLexer + ->build($search) + ->withFilter(new TaskProjectFilter($project['id'])) + ->getQuery() + ) + ->calculate(); + + $this->response->html($this->helper->layout->app('task_list/listing', array( + 'project' => $project, + 'title' => $project['name'], + 'description' => $this->helper->projectHeader->getDescription($project), + 'paginator' => $paginator, + ))); + } +} diff --git a/app/Controller/TaskMailController.php b/app/Controller/TaskMailController.php new file mode 100644 index 0000000..5197f4f --- /dev/null +++ b/app/Controller/TaskMailController.php @@ -0,0 +1,62 @@ +getTask(); + + $this->response->html($this->helper->layout->task('task_mail/create', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'members' => $this->projectPermissionModel->getMembersWithEmail($task['project_id']), + ))); + } + + public function send() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->taskValidator->validateEmailCreation($values); + + if ($valid) { + $this->sendByEmail($values, $task); + $this->flash->success(t('Task sent by email successfully.')); + + $this->commentModel->create(array( + 'comment' => t('This task was sent by email to "%s" with subject "%s".', $values['emails'], $values['subject']), + 'user_id' => $this->userSession->getId(), + 'task_id' => $task['id'], + )); + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']), 'comments'), true); + } else { + $this->create($values, $errors); + } + } + + protected function sendByEmail(array $values, array $task) + { + $emails = explode_csv_field($values['emails']); + $html = $this->template->render('task_mail/email', array('task' => $task)); + + foreach ($emails as $email) { + $this->emailClient->send( + $email, + $email, + $values['subject'], + $html + ); + } + } +} diff --git a/app/Controller/TaskModificationController.php b/app/Controller/TaskModificationController.php new file mode 100644 index 0000000..3e6f5d1 --- /dev/null +++ b/app/Controller/TaskModificationController.php @@ -0,0 +1,182 @@ +checkReusableGETCSRFParam(); + $task = $this->getTask(); + $values = ['id' => $task['id'], 'owner_id' => $this->userSession->getId()]; + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + + if (! $this->helper->projectRole->canChangeAssignee($task)) { + throw new AccessForbiddenException(t('You are not allowed to change the assignee.')); + } + + $this->taskModificationModel->update($values); + $this->redirectAfterQuickAction($task); + } + + /** + * Set the start date automatically + * + * @access public + */ + public function start() + { + $this->checkReusableGETCSRFParam(); + $task = $this->getTask(); + $values = ['id' => $task['id'], 'date_started' => time()]; + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + + $this->taskModificationModel->update($values); + $this->redirectAfterQuickAction($task); + } + + protected function redirectAfterQuickAction(array $task) + { + switch ($this->request->getStringParam('redirect')) { + case 'board': + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', ['project_id' => $task['project_id']])); + break; + case 'list': + $this->response->redirect($this->helper->url->to('TaskListController', 'show', ['project_id' => $task['project_id']])); + break; + case 'dashboard': + $this->response->redirect($this->helper->url->to('DashboardController', 'show', [], 'project-tasks-'.$task['project_id'])); + break; + case 'dashboard-tasks': + $this->response->redirect($this->helper->url->to('DashboardController', 'tasks', ['user_id' => $this->userSession->getId()])); + break; + default: + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', ['task_id' => $task['id']])); + } + } + + /** + * Display a form to edit a task + * + * @access public + * @param array $values + * @param array $errors + * @throws \Kanboard\Core\Controller\AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function edit(array $values = array(), array $errors = array()) + { + $task = $this->getTask(); + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + + $project = $this->projectModel->getById($task['project_id']); + + if (empty($values)) { + $values = $task; + } + + $values = $this->hook->merge('controller:task:form:default', $values, array('default_values' => $values)); + $values = $this->hook->merge('controller:task-modification:form:default', $values, array('default_values' => $values)); + + $params = array( + 'project' => $project, + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + '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'], $task['project_id']); + } 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)); + } + } + + /** + * Validate and update a task + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['id'] = $task['id']; + $values['project_id'] = $task['project_id']; + + list($valid, $errors) = $this->taskValidator->validateModification($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('task_id' => $task['id'])), true); + } else { + $this->flash->failure(t('Unable to update your task.')); + $this->edit($values, $errors); + } + } + + protected function updateTask(array &$task, array &$values, array &$errors) + { + if (isset($values['owner_id']) && $values['owner_id'] != $task['owner_id'] && !$this->helper->projectRole->canChangeAssignee($task)) { + throw new AccessForbiddenException(t('You are not allowed to change the assignee.')); + } + + if (! $this->helper->projectRole->canUpdateTask($task)) { + throw new AccessForbiddenException(t('You are not allowed to update tasks assigned to someone else.')); + } + + $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 0000000..5303a54 --- /dev/null +++ b/app/Controller/TaskMovePositionController.php @@ -0,0 +1,53 @@ +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() + { + $this->checkReusableGETCSRFParam(); + $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']))); + } +} diff --git a/app/Controller/TaskPopoverController.php b/app/Controller/TaskPopoverController.php new file mode 100644 index 0000000..4e3c11a --- /dev/null +++ b/app/Controller/TaskPopoverController.php @@ -0,0 +1,26 @@ +getTask(); + + $this->response->html($this->template->render('task_file/screenshot', array( + 'task' => $task, + ))); + } +} diff --git a/app/Controller/TaskRecurrenceController.php b/app/Controller/TaskRecurrenceController.php new file mode 100644 index 0000000..cd23ea3 --- /dev/null +++ b/app/Controller/TaskRecurrenceController.php @@ -0,0 +1,66 @@ +getTask(); + + if (empty($values)) { + $values = $task; + } + + $this->response->html($this->template->render('task_recurrence/edit', array( + 'values' => $values, + 'errors' => $errors, + 'task' => $task, + 'recurrence_status_list' => $this->taskRecurrenceModel->getRecurrenceStatusList(), + 'recurrence_trigger_list' => $this->taskRecurrenceModel->getRecurrenceTriggerList(), + 'recurrence_timeframe_list' => $this->taskRecurrenceModel->getRecurrenceTimeframeList(), + 'recurrence_basedate_list' => $this->taskRecurrenceModel->getRecurrenceBasedateList(), + ))); + } + + /** + * Update recurrence form + * + * @access public + */ + public function update() + { + $task = $this->getTask(); + $values = $this->request->getValues(); + $values['id'] = $task['id']; + + list($valid, $errors) = $this->taskValidator->validateEditRecurrence($values); + + if ($valid) { + if ($this->taskModificationModel->update($values)) { + $this->flash->success(t('Task updated successfully.')); + } else { + $this->flash->failure(t('Unable to update your task.')); + } + + return $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } + + return $this->edit($values, $errors); + } +} diff --git a/app/Controller/TaskReorderController.php b/app/Controller/TaskReorderController.php new file mode 100644 index 0000000..1b258e6 --- /dev/null +++ b/app/Controller/TaskReorderController.php @@ -0,0 +1,43 @@ +checkCSRFParam(); + $project = $this->getProject(); + + if (! $this->helper->user->hasProjectAccess('TaskModificationController', 'update', $project['id'])) { + throw new AccessForbiddenException(); + } + + $swimlaneID = $this->request->getIntegerParam('swimlane_id'); + $columnID = $this->request->getIntegerParam('column_id'); + $direction = $this->request->getStringParam('direction'); + $sort = $this->request->getStringParam('sort'); + + switch ($sort) { + case 'id': + $this->taskReorderModel->reorderByTaskId($project['id'], $swimlaneID, $columnID, $direction); + break; + case 'priority': + $this->taskReorderModel->reorderByPriority($project['id'], $swimlaneID, $columnID, $direction); + break; + case 'assignee-priority': + $this->taskReorderModel->reorderByAssigneeAndPriority($project['id'], $swimlaneID, $columnID, $direction); + break; + case 'assignee': + $this->taskReorderModel->reorderByAssignee($project['id'], $swimlaneID, $columnID, $direction); + break; + case 'due-date': + $this->taskReorderModel->reorderByDueDate($project['id'], $swimlaneID, $columnID, $direction); + break; + } + + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', ['project_id' => $project['id']])); + } +} diff --git a/app/Controller/TaskStatusController.php b/app/Controller/TaskStatusController.php new file mode 100644 index 0000000..5dbd07e --- /dev/null +++ b/app/Controller/TaskStatusController.php @@ -0,0 +1,62 @@ +changeStatus('close', 'task_status/close', t('Task closed successfully.'), t('Unable to close this task.')); + } + + /** + * Open a task + * + * @access public + */ + public function open() + { + $this->changeStatus('open', 'task_status/open', t('Task opened successfully.'), t('Unable to open this task.')); + } + + /** + * Common method to change status + * + * @access private + * @param string $method + * @param string $template + * @param string $success_message + * @param string $failure_message + */ + private function changeStatus($method, $template, $success_message, $failure_message) + { + $task = $this->getTask(); + + if ($this->request->getStringParam('confirmation') === 'yes') { + $this->checkCSRFParam(); + + if ($this->taskStatusModel->$method($task['id'])) { + $this->flash->success($success_message); + } else { + $this->flash->failure($failure_message); + } + + $this->response->redirect($this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id'])), true); + } else { + $this->response->html($this->template->render($template, array( + 'task' => $task, + ))); + } + } +} diff --git a/app/Controller/TaskSuppressionController.php b/app/Controller/TaskSuppressionController.php new file mode 100644 index 0000000..019bd97 --- /dev/null +++ b/app/Controller/TaskSuppressionController.php @@ -0,0 +1,53 @@ +getTask(); + + if (! $this->helper->projectRole->canRemoveTask($task)) { + throw new AccessForbiddenException(); + } + + $this->response->html($this->template->render('task_suppression/remove', array( + 'task' => $task, + 'redirect' => $this->request->getStringParam('redirect'), + ))); + } + + /** + * Remove a task + */ + public function remove() + { + $task = $this->getTask(); + $this->checkCSRFParam(); + + if (! $this->helper->projectRole->canRemoveTask($task)) { + throw new AccessForbiddenException(); + } + + if ($this->taskModel->remove($task['id'])) { + $this->flash->success(t('Task removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this task.')); + } + + $redirect = $this->request->getStringParam('redirect') === ''; + $this->response->redirect($this->helper->url->to('BoardViewController', 'show', array('project_id' => $task['project_id'])), $redirect); + } +} diff --git a/app/Controller/TaskViewController.php b/app/Controller/TaskViewController.php new file mode 100644 index 0000000..c7ab6ed --- /dev/null +++ b/app/Controller/TaskViewController.php @@ -0,0 +1,144 @@ +projectModel->getByToken($this->request->getStringParam('token')); + + if (empty($project)) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + + $task = $this->taskFinderModel->getDetails($this->request->getIntegerParam('task_id')); + + if (empty($task)) { + throw PageNotFoundException::getInstance()->withoutLayout(); + } + + if ($task['project_id'] != $project['id']) { + throw AccessForbiddenException::getInstance()->withoutLayout(); + } + + $this->response->html($this->helper->layout->app('task/public', array( + 'project' => $project, + 'comments' => array_filter($this->commentModel->getAll($task['id']), function ($comment) { + return $comment['visibility'] === Role::APP_USER; + }), + 'subtasks' => $this->subtaskModel->getAll($task['id']), + 'links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']), + 'task' => $task, + 'columns_list' => $this->columnModel->getList($task['project_id']), + 'colors_list' => $this->colorModel->getList(), + 'tags' => $this->taskTagModel->getTagsByTask($task['id']), + 'title' => $task['title'], + 'no_layout' => true, + 'auto_refresh' => true, + 'not_editable' => true, + ))); + } + + /** + * Show a task + * + * @access public + */ + public function show() + { + $task = $this->getTask(); + $subtasks = $this->subtaskModel->getAll($task['id']); + $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']), + 'files' => $this->taskFileModel->getAllDocuments($task['id']), + 'images' => $this->taskFileModel->getAllImages($task['id']), + 'comments' => $this->commentModel->getAll($task['id'], $commentSortingDirection), + 'subtasks' => $subtasks, + 'internal_links' => $this->taskLinkModel->getAllGroupedByLabel($task['id']), + 'external_links' => $this->taskExternalLinkModel->getAll($task['id']), + 'link_label_list' => $this->linkModel->getList(0, false), + 'tags' => $this->taskTagModel->getTagsByTask($task['id']), + ))); + } + + /** + * Display task analytics + * + * @access public + */ + public function analytics() + { + $task = $this->getTask(); + + $this->response->html($this->helper->layout->task('task/analytics', array( + 'task' => $task, + 'project' => $this->projectModel->getById($task['project_id']), + 'lead_time' => $this->taskAnalyticModel->getLeadTime($task), + 'cycle_time' => $this->taskAnalyticModel->getCycleTime($task), + 'time_spent_columns' => $this->taskAnalyticModel->getTimeSpentByColumn($task), + 'tags' => $this->taskTagModel->getTagsByTask($task['id']), + ))); + } + + /** + * Display the time tracking details + * + * @access public + */ + public function timetracking() + { + $task = $this->getTask(); + + $subtask_paginator = $this->paginator + ->setUrl('TaskViewController', 'timetracking', array('task_id' => $task['id'], 'pagination' => 'subtasks')) + ->setMax(15) + ->setOrder('start') + ->setDirection('DESC') + ->setQuery($this->subtaskTimeTrackingModel->getTaskQuery($task['id'])) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); + + $this->response->html($this->helper->layout->task('task/time_tracking_details', array( + 'task' => $task, + 'project' => $this->projectModel->getById($task['project_id']), + 'subtask_paginator' => $subtask_paginator, + 'tags' => $this->taskTagModel->getTagsByTask($task['id']), + ))); + } + + /** + * Display the task transitions + * + * @access public + */ + public function transitions() + { + $task = $this->getTask(); + + $this->response->html($this->helper->layout->task('task/transitions', array( + 'task' => $task, + 'project' => $this->projectModel->getById($task['project_id']), + 'transitions' => $this->transitionModel->getAllByTask($task['id']), + 'tags' => $this->taskTagModel->getTagsByTask($task['id']), + ))); + } +} diff --git a/app/Controller/TwoFactorController.php b/app/Controller/TwoFactorController.php new file mode 100644 index 0000000..6ec7353 --- /dev/null +++ b/app/Controller/TwoFactorController.php @@ -0,0 +1,232 @@ +userSession->getId()) { + throw new AccessForbiddenException(); + } + } + + /** + * Show form to disable/enable 2FA + * + * @access public + */ + public function index() + { + $user = $this->getUser(); + $this->checkCurrentUser($user); + session_remove('twoFactorSecret'); + + $this->response->html($this->helper->layout->user('twofactor/index', array( + 'user' => $user, + 'provider' => $this->authenticationManager->getPostAuthenticationProvider()->getName(), + ))); + } + + /** + * Show page with secret and test form + * + * @access public + */ + public function show() + { + $user = $this->getUser(); + $this->checkCurrentUser($user); + + $label = $user['email'] ?: $user['username']; + $provider = $this->authenticationManager->getPostAuthenticationProvider(); + + if (! session_exists('twoFactorSecret')) { + $provider->generateSecret(); + $provider->beforeCode(); + session_set('twoFactorSecret', $provider->getSecret()); + } else { + $provider->setSecret(session_get('twoFactorSecret')); + } + + $this->response->html($this->helper->layout->user('twofactor/show', array( + 'user' => $user, + 'secret' => session_get('twoFactorSecret'), + 'key_url' => $provider->getKeyUrl($label), + ))); + } + + /** + * Test code and save secret + * + * @access public + */ + public function test() + { + $user = $this->getUser(); + $this->checkCurrentUser($user); + + $values = $this->request->getValues(); + + $provider = $this->authenticationManager->getPostAuthenticationProvider(); + $provider->setCode(empty($values['code']) ? '' : $values['code']); + $provider->setSecret(session_get('twoFactorSecret')); + + if ($provider->authenticate()) { + $this->flash->success(t('The two factor authentication code is valid.')); + + $this->userModel->update(array( + 'id' => $user['id'], + 'twofactor_activated' => 1, + 'twofactor_secret' => $this->authenticationManager->getPostAuthenticationProvider()->getSecret(), + )); + + session_remove('twoFactorSecret'); + $this->userSession->disablePostAuthentication(); + + $this->response->redirect($this->helper->url->to('TwoFactorController', 'index', array('user_id' => $user['id'])), true); + } else { + $this->flash->failure(t('The two factor authentication code is not valid.')); + + if ($this->request->isAjax()) { + $this->show(); + } else { + $this->response->redirect($this->helper->url->to('TwoFactorController', 'show', array('user_id' => $user['id']))); + } + } + } + + /** + * Disable 2FA for the current user + * + * @access public + */ + public function deactivate() + { + $this->checkCSRFForm(); + $user = $this->getUser(); + $this->checkCurrentUser($user); + + $this->userModel->update(array( + 'id' => $user['id'], + 'twofactor_activated' => 0, + 'twofactor_secret' => '', + )); + + // Allow the user to test or disable the feature + $this->userSession->disablePostAuthentication(); + + $this->flash->success(t('User updated successfully.')); + $this->response->redirect($this->helper->url->to('TwoFactorController', 'index', array('user_id' => $user['id'])), true); + } + + /** + * Check 2FA + * + * @access public + */ + public function check() + { + $user = $this->getUser(); + $this->checkCurrentUser($user); + + $values = $this->request->getValues(); + + $provider = $this->authenticationManager->getPostAuthenticationProvider(); + $provider->setCode(empty($values['code']) ? '' : $values['code']); + $provider->setSecret($user['twofactor_secret']); + + if ($provider->authenticate()) { + $this->userSession->setPostAuthenticationAsValidated(); + $this->flash->success(t('The two factor authentication code is valid.')); + + if (REMEMBER_ME_AUTH && session_is_true('hasRememberMe')) { + $session = $this->rememberMeSessionModel->create($this->userSession->getId(), $this->request->getIpAddress(), $this->request->getUserAgent()); + $this->rememberMeCookie->write($session['token'], $session['sequence'], $session['expiration']); + } + + $this->response->redirect($this->helper->url->to('DashboardController', 'show')); + } else { + $this->flash->failure(t('The two factor authentication code is not valid.')); + $this->response->redirect($this->helper->url->to('TwoFactorController', 'code')); + } + } + + /** + * Ask the 2FA code + * + * @access public + */ + public function code() + { + if (! session_exists('twoFactorBeforeCodeCalled')) { + $provider = $this->authenticationManager->getPostAuthenticationProvider(); + $provider->beforeCode(); + session_set('twoFactorBeforeCodeCalled', true); + } + + $this->response->html($this->helper->layout->app('twofactor/check', array( + 'title' => t('Check two factor authentication code'), + 'no_layout' => true, + ))); + } + + /** + * Disable 2FA for a user + * + * @access public + */ + public function disable() + { + $user = $this->getUser(); + + if ($this->request->getStringParam('disable') === 'yes') { + $this->checkCSRFParam(); + + $this->userModel->update(array( + 'id' => $user['id'], + 'twofactor_activated' => 0, + 'twofactor_secret' => '', + )); + + $this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])), true); + } else { + $this->response->html($this->helper->layout->user('twofactor/disable', array( + 'user' => $user, + ))); + } + } + + /** + * Render QR Code image + */ + public function qrcode() + { + if (session_exists('twoFactorSecret')) { + $user = $this->getUser(); + $provider = $this->authenticationManager->getPostAuthenticationProvider(); + $provider->setSecret(session_get('twoFactorSecret')); + $url = $provider->getKeyUrl($user['email'] ?: $user['username']); + + if (! empty($url)) { + PHPQRCode\QRcode::png($url, false, 'L', 6, 0); + } + } + } +} diff --git a/app/Controller/UserAjaxController.php b/app/Controller/UserAjaxController.php new file mode 100644 index 0000000..5759620 --- /dev/null +++ b/app/Controller/UserAjaxController.php @@ -0,0 +1,48 @@ +request->getStringParam('term'); + $users = $this->userManager->find($search); + $this->response->json($this->userAutoCompleteFormatter->withUsers($users)->format()); + } + + /** + * User mention auto-completion (Ajax) + * + * @access public + */ + public function mention() + { + $project_id = $this->request->getStringParam('project_id'); + $query = $this->request->getStringParam('search'); + $users = $this->projectPermissionModel->findUsernames($project_id, $query); + + $this->response->json($this->userMentionFormatter->withUsers($users)->format()); + } + + /** + * Check if the user is connected + * + * @access public + */ + public function status() + { + $this->response->text('OK'); + } +} diff --git a/app/Controller/UserApiAccessController.php b/app/Controller/UserApiAccessController.php new file mode 100644 index 0000000..fab64a5 --- /dev/null +++ b/app/Controller/UserApiAccessController.php @@ -0,0 +1,59 @@ +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->renderResponse($user); + } + + public function remove() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + $this->userModel->update(array( + 'id' => $user['id'], + 'api_access_token' => null, + )); + + $this->renderResponse($user); + } + + protected function renderResponse(array $user) + { + if ($this->request->isAjax()) { + $this->show(); + } else { + $this->response->redirect($this->helper->url->to('UserApiAccessController', 'show', array('user_id' => $user['id']))); + } + } +} diff --git a/app/Controller/UserCreationController.php b/app/Controller/UserCreationController.php new file mode 100644 index 0000000..f9897a4 --- /dev/null +++ b/app/Controller/UserCreationController.php @@ -0,0 +1,84 @@ +response->html($this->template->render('user_creation/show', array( + 'themes' => $this->themeModel->getThemes(), + 'timezones' => $this->timezoneModel->getTimezones(true), + 'languages' => $this->languageModel->getLanguages(true), + 'roles' => $this->role->getApplicationRoles(), + 'projects' => $this->projectModel->getList(), + 'errors' => $errors, + 'values' => $values + array('role' => Role::APP_USER), + ))); + } + + /** + * Validate and save a new user + * + * @access public + */ + public function save() + { + $values = $this->request->getValues(); + list($valid, $errors) = $this->userValidator->validateCreation($values); + + if ($valid) { + $this->createUser($values); + } else { + $this->show($values, $errors); + } + } + + /** + * Create user + * + * @param array $values + */ + protected function createUser(array $values) + { + $project_id = empty($values['project_id']) ? 0 : $values['project_id']; + unset($values['project_id']); + + $user_id = $this->userModel->create($values); + + if ($user_id !== false) { + if ($project_id !== 0) { + $this->projectUserRoleModel->addUser($project_id, $user_id, Role::PROJECT_MEMBER); + } + + if ($this->configModel->get('notifications_enabled', 0) == 1) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]); + } elseif (! empty($values['notifications_enabled'])) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE]); + } + + $this->flash->success(t('User created successfully.')); + $this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user_id))); + } else { + $this->flash->failure(t('Unable to create your user.')); + $this->response->redirect($this->helper->url->to('UserListController', 'show')); + } + } +} diff --git a/app/Controller/UserCredentialController.php b/app/Controller/UserCredentialController.php new file mode 100644 index 0000000..a8b90b7 --- /dev/null +++ b/app/Controller/UserCredentialController.php @@ -0,0 +1,134 @@ +getUser(); + + return $this->response->html($this->helper->layout->user('user_credential/password', array( + 'values' => $values + array('id' => $user['id']), + 'errors' => $errors, + 'user' => $user, + ))); + } + + /** + * Save new password + * + * @throws \Kanboard\Core\Controller\AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function savePassword() + { + $user = $this->getUser(); + $values = $this->request->getValues(); + + list($valid, $errors) = $this->userValidator->validatePasswordModification($values); + + if (! $this->userSession->isAdmin()) { + $values = array( + 'id' => $this->userSession->getId(), + 'password' => isset($values['password']) ? $values['password'] : '', + 'confirmation' => isset($values['confirmation']) ? $values['confirmation'] : '', + ); + } + + if ($valid) { + if ($this->userModel->update($values)) { + $this->flash->success(t('Password modified successfully.')); + $this->userLockingModel->resetFailedLogin($user['username']); + $this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])), true); + return; + } else { + $this->flash->failure(t('Unable to change the password.')); + } + } + + $this->changePassword($values, $errors); + } + + /** + * Display a form to edit authentication + * + * @access public + * @param array $values + * @param array $errors + * @throws \Kanboard\Core\Controller\AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function changeAuthentication(array $values = array(), array $errors = array()) + { + $user = $this->getUser(); + + if (empty($values)) { + $values = $user; + unset($values['password']); + } + + return $this->response->html($this->helper->layout->user('user_credential/authentication', array( + 'values' => $values, + 'errors' => $errors, + 'user' => $user, + ))); + } + + /** + * Save authentication + * + * @throws \Kanboard\Core\Controller\AccessForbiddenException + * @throws \Kanboard\Core\Controller\PageNotFoundException + */ + public function saveAuthentication() + { + $user = $this->getUser(); + $values = $this->request->getValues() + array('disable_login_form' => 0, 'is_ldap_user' => 0); + list($valid, $errors) = $this->userValidator->validateModification($values); + + if ($valid) { + if ($this->userModel->update($values)) { + $this->flash->success(t('User updated successfully.')); + $this->response->redirect($this->helper->url->to('UserCredentialController', 'changeAuthentication', array('user_id' => $user['id'])), true); + return; + } else { + $this->flash->failure(t('Unable to update this user.')); + } + } + + $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 new file mode 100644 index 0000000..8f3ee12 --- /dev/null +++ b/app/Controller/UserImportController.php @@ -0,0 +1,84 @@ +response->html($this->template->render('user_import/show', array( + 'values' => $values, + 'errors' => $errors, + 'max_size' => get_upload_max_size(), + 'delimiters' => Csv::getDelimiters(), + 'enclosures' => Csv::getEnclosures(), + ))); + } + + /** + * Submit form + */ + public function save() + { + $values = $this->request->getValues(); + + // Note: $values is empty when the CSRF token is invalid. + if (empty($values)) { + throw new AccessForbiddenException(); + } + + $filename = $this->request->getFilePath('file'); + + if (! file_exists($filename)) { + $this->flash->failure(t('Unable to read your file')); + } else { + $this->importFile($values, $filename); + } + + $this->response->redirect($this->helper->url->to('UserListController', 'show')); + } + + /** + * Generate template + * + */ + public function template() + { + $this->response->withFileDownload('users.csv'); + $this->response->csv(array($this->userImport->getColumnMapping())); + } + + /** + * Process file + * + * @param array $values + * @param $filename + */ + private function importFile(array $values, $filename) + { + $csv = new Csv($values['delimiter'], $values['enclosure']); + $csv->setColumnMapping($this->userImport->getColumnMapping()); + $csv->read($filename, array($this->userImport, 'import')); + + if ($this->userImport->counter > 0) { + $this->flash->success(t('%d user(s) have been imported successfully.', $this->userImport->counter)); + } else { + $this->flash->failure(t('Nothing has been imported!')); + } + } +} diff --git a/app/Controller/UserInviteController.php b/app/Controller/UserInviteController.php new file mode 100644 index 0000000..cd38dc1 --- /dev/null +++ b/app/Controller/UserInviteController.php @@ -0,0 +1,119 @@ +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']), + 'themes' => $this->themeModel->getThemes(), + 'timezones' => $this->timezoneModel->getTimezones(true), + 'languages' => $this->languageModel->getLanguages(true), + ))); + } + + public function register() + { + $invite = $this->getInvite(); + + $input = $this->request->getValues(); + $values = array(); + + foreach (['username', 'name', 'email', 'password', 'confirmation', 'timezone', 'language', 'notifications_enabled'] as $field) { + if (array_key_exists($field, $input)) { + $values[$field] = $input[$field]; + } + } + + 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 ($this->configModel->get('notifications_enabled', 0) == 1) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]); + } elseif (! empty($values['notifications_enabled'])) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, [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 new file mode 100644 index 0000000..010c834 --- /dev/null +++ b/app/Controller/UserListController.php @@ -0,0 +1,60 @@ +userPagination->getListingPaginator(); + + $this->response->html($this->helper->layout->app('user_list/listing', array( + 'title' => t('Users').' ('.$paginator->getTotal().')', + 'paginator' => $paginator, + 'values' => array(), + ))); + } + + /** + * Search in users + * + * @access public + */ + public function search() + { + $search = $this->request->getStringParam('search'); + $paginator = $this->userPagination->getListingPaginator(); + + if ($search !== '' && ! $paginator->isEmpty()) { + $paginator = $paginator + ->setUrl('UserListController', 'search', array('search' => $search)) + ->setQuery( + $this->userQuery + ->withFilter(new UserNameFilter($search)) + ->getQuery() + ) + ->calculate(); + } + + $this->response->html($this->helper->layout->app('user_list/listing', array( + 'title' => t('Users').' ('.$paginator->getTotal().')', + 'values' => array( + 'search' => $search, + ), + 'paginator' => $paginator + ))); + } +} diff --git a/app/Controller/UserModificationController.php b/app/Controller/UserModificationController.php new file mode 100644 index 0000000..be432f0 --- /dev/null +++ b/app/Controller/UserModificationController.php @@ -0,0 +1,77 @@ +getUser(); + + if (empty($values)) { + $values = $user; + unset($values['password']); + } + + return $this->response->html($this->helper->layout->user('user_modification/show', array( + 'values' => $values, + 'errors' => $errors, + 'user' => $user, + 'themes' => $this->themeModel->getThemes(), + 'timezones' => $this->timezoneModel->getTimezones(true), + 'languages' => $this->languageModel->getLanguages(true), + 'roles' => $this->role->getApplicationRoles(), + ))); + } + + /** + * Save user information + */ + public function save() + { + $user = $this->getUser(); + $values = $this->request->getValues(); + + if (! $this->userSession->isAdmin()) { + $values = array( + 'id' => $this->userSession->getId(), + 'username' => isset($values['username']) ? $values['username'] : '', + 'name' => isset($values['name']) ? $values['name'] : '', + 'email' => isset($values['email']) ? $values['email'] : '', + 'theme' => isset($values['theme']) ? $values['theme'] : '', + 'timezone' => isset($values['timezone']) ? $values['timezone'] : '', + 'language' => isset($values['language']) ? $values['language'] : '', + 'filter' => isset($values['filter']) ? $values['filter'] : '', + ); + } + + list($valid, $errors) = $this->userValidator->validateModification($values); + + if ($valid) { + if ($this->userModel->update($values)) { + $this->flash->success(t('User updated successfully.')); + $this->response->redirect($this->helper->url->to('UserViewController', 'show', array('user_id' => $user['id'])), true); + return; + } else { + $this->flash->failure(t('Unable to update this user.')); + } + } + + $this->show($values, $errors); + } +} diff --git a/app/Controller/UserStatusController.php b/app/Controller/UserStatusController.php new file mode 100644 index 0000000..070fb6f --- /dev/null +++ b/app/Controller/UserStatusController.php @@ -0,0 +1,111 @@ +getUser(); + + $this->response->html($this->helper->layout->user('user_status/remove', array( + 'user' => $user, + ))); + } + + /** + * Remove a user + * + * @access public + */ + public function remove() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + if ($this->userModel->remove($user['id'])) { + $this->flash->success(t('User removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this user.')); + } + + $this->response->redirect($this->helper->url->to('UserListController', 'show')); + } + + /** + * Confirm enable a user + * + * @access public + */ + public function confirmEnable() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->user('user_status/enable', array( + 'user' => $user, + ))); + } + + /** + * Enable a user + * + * @access public + */ + public function enable() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + if ($this->userModel->enable($user['id'])) { + $this->flash->success(t('User activated successfully.')); + } else { + $this->flash->failure(t('Unable to enable this user.')); + } + + $this->response->redirect($this->helper->url->to('UserListController', 'show')); + } + + /** + * Confirm disable a user + * + * @access public + */ + public function confirmDisable() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->user('user_status/disable', array( + 'user' => $user, + ))); + } + + /** + * Disable a user + * + * @access public + */ + public function disable() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + if ($this->userModel->disable($user['id'])) { + $this->flash->success(t('User disabled successfully.')); + } else { + $this->flash->failure(t('Unable to disable this user.')); + } + + $this->response->redirect($this->helper->url->to('UserListController', 'show')); + } +} diff --git a/app/Controller/UserViewController.php b/app/Controller/UserViewController.php new file mode 100644 index 0000000..be457bd --- /dev/null +++ b/app/Controller/UserViewController.php @@ -0,0 +1,235 @@ +userModel->getById($this->request->getIntegerParam('user_id')); + + if (empty($user)) { + throw new PageNotFoundException(); + } + + $this->response->html($this->helper->layout->app('user_view/profile', array( + 'title' => $user['name'] ?: $user['username'], + 'user' => $user, + ))); + } + + /** + * Display user information + * + * @access public + */ + public function show() + { + $user = $this->getUser(); + $this->response->html($this->helper->layout->user('user_view/show', array( + 'user' => $user, + 'themes' => $this->themeModel->getThemes(), + 'timezones' => $this->timezoneModel->getTimezones(true), + 'languages' => $this->languageModel->getLanguages(true), + ))); + } + + /** + * Display timesheet + * + * @access public + */ + public function timesheet() + { + $user = $this->getUser(); + + $subtask_paginator = $this->paginator + ->setUrl('UserViewController', 'timesheet', array('user_id' => $user['id'], 'pagination' => 'subtasks')) + ->setMax(20) + ->setOrder('start') + ->setDirection('DESC') + ->setQuery($this->subtaskTimeTrackingModel->getUserQuery($user['id'])) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'subtasks'); + + $this->response->html($this->helper->layout->user('user_view/timesheet', array( + 'subtask_paginator' => $subtask_paginator, + 'user' => $user, + ))); + } + + /** + * Display last password reset + * + * @access public + */ + public function passwordReset() + { + $user = $this->getUser(); + $this->response->html($this->helper->layout->user('user_view/password_reset', array( + 'tokens' => $this->passwordResetModel->getAll($user['id']), + 'user' => $user, + ))); + } + + /** + * Display last connections + * + * @access public + */ + public function lastLogin() + { + $user = $this->getUser(); + $this->response->html($this->helper->layout->user('user_view/last', array( + 'last_logins' => $this->lastLoginModel->getAll($user['id']), + 'user' => $user, + ))); + } + + /** + * Display user sessions + * + * @access public + */ + public function sessions() + { + if (! REMEMBER_ME_AUTH) { + $this->response->status(404); + return; + } + + $user = $this->getUser(); + $this->response->html($this->helper->layout->user('user_view/sessions', array( + 'sessions' => $this->rememberMeSessionModel->getAll($user['id']), + 'user' => $user, + ))); + } + + /** + * Remove a "RememberMe" token + * + * @access public + */ + public function removeSession() + { + $this->checkCSRFParam(); + $user = $this->getUser(); + $this->rememberMeSessionModel->remove($this->request->getIntegerParam('id')); + + if ($this->request->isAjax()) { + $this->sessions(); + } else { + $this->response->redirect($this->helper->url->to('UserViewController', 'sessions', array('user_id' => $user['id'])), true); + } + } + + /** + * Display user notifications + * + * @access public + */ + public function notifications() + { + $user = $this->getUser(); + + if ($this->request->isPost()) { + $values = $this->request->getValues(); + $this->userNotificationModel->saveSettings($user['id'], $values); + $this->flash->success(t('User updated successfully.')); + $this->response->redirect($this->helper->url->to('UserViewController', 'notifications', array('user_id' => $user['id'])), true); + return; + } + + $this->response->html($this->helper->layout->user('user_view/notifications', array( + 'projects' => $this->projectUserRoleModel->getProjectsByUser($user['id'], array(ProjectModel::ACTIVE)), + 'notifications' => $this->userNotificationModel->readSettings($user['id']), + 'types' => $this->userNotificationTypeModel->getTypes(), + 'filters' => $this->userNotificationFilterModel->getFilters(), + 'user' => $user, + ))); + } + + /** + * Display user integrations + * + * @access public + */ + public function integrations() + { + $user = $this->getUser(); + + if ($this->request->isPost()) { + $values = $this->request->getValues(); + $this->userMetadataModel->save($user['id'], $values); + $this->flash->success(t('User updated successfully.')); + $this->response->redirect($this->helper->url->to('UserViewController', 'integrations', array('user_id' => $user['id'])), true); + return; + } + + $this->response->html($this->helper->layout->user('user_view/integrations', array( + 'user' => $user, + 'values' => $this->userMetadataModel->getAll($user['id']), + ))); + } + + /** + * Display external accounts + * + * @access public + */ + public function external() + { + $user = $this->getUser(); + $this->response->html($this->helper->layout->user('user_view/external', array( + 'last_logins' => $this->lastLoginModel->getAll($user['id']), + 'user' => $user, + ))); + } + + /** + * Public access management + * + * @access public + */ + public function share() + { + $user = $this->getUser(); + $switch = $this->request->getStringParam('switch'); + + if ($switch === 'enable' || $switch === 'disable') { + $this->checkCSRFParam(); + + if ($this->userModel->{$switch . 'PublicAccess'}($user['id'])) { + $this->flash->success(t('User updated successfully.')); + } else { + $this->flash->failure(t('Unable to update this user.')); + } + + if (! $this->request->isAjax()) { + $this->response->redirect($this->helper->url->to('UserViewController', 'share', array('user_id' => $user['id'])), true); + return; + } + + $user = $this->getUser(); + } + + $this->response->html($this->helper->layout->user('user_view/share', array( + 'user' => $user, + 'title' => t('Public access'), + ))); + } +} diff --git a/app/Controller/WebNotificationController.php b/app/Controller/WebNotificationController.php new file mode 100644 index 0000000..9562474 --- /dev/null +++ b/app/Controller/WebNotificationController.php @@ -0,0 +1,96 @@ +getUser(); + $notifications = $this->userUnreadNotificationModel->getAll($user['id']); + + $this->response->html($this->template->render('web_notification/show', array( + 'notifications' => $notifications, + 'nb_notifications' => count($notifications), + 'user' => $user, + ))); + } + + /** + * Mark all notifications as read + * + * @access public + */ + public function flush() + { + $this->checkReusableGETCSRFParam(); + $userId = $this->getUserId(); + $this->userUnreadNotificationModel->markAllAsRead($userId); + $this->show(); + } + + /** + * Mark a notification as read + * + * @access public + */ + public function remove() + { + $this->checkReusableGETCSRFParam(); + $user_id = $this->getUserId(); + $notification_id = $this->request->getIntegerParam('notification_id'); + $this->userUnreadNotificationModel->markAsRead($user_id, $notification_id); + $this->show(); + } + + /** + * Redirect to the task and mark notification as read + */ + public function redirect() + { + $user_id = $this->getUserId(); + $notification_id = $this->request->getIntegerParam('notification_id'); + + $notification = $this->userUnreadNotificationModel->getById($notification_id); + $this->userUnreadNotificationModel->markAsRead($user_id, $notification_id); + + if (empty($notification)) { + $this->show(); + } elseif ($this->helper->text->contains($notification['event_name'], 'comment')) { + $this->response->redirect($this->helper->url->to( + 'TaskViewController', + 'show', + array('task_id' => $this->notificationModel->getTaskIdFromEvent($notification['event_name'], $notification['event_data'])), + 'comment-'.$notification['event_data']['comment']['id'] + )); + } else { + $this->response->redirect($this->helper->url->to( + 'TaskViewController', + 'show', + array('task_id' => $this->notificationModel->getTaskIdFromEvent($notification['event_name'], $notification['event_data'])) + )); + } + } + + private function getUserId() + { + $user_id = $this->request->getIntegerParam('user_id'); + + if (! $this->userSession->isAdmin() && $user_id != $this->userSession->getId()) { + $user_id = $this->userSession->getId(); + } + + return $user_id; + } +} diff --git a/app/Core/Action/ActionManager.php b/app/Core/Action/ActionManager.php new file mode 100644 index 0000000..3a77084 --- /dev/null +++ b/app/Core/Action/ActionManager.php @@ -0,0 +1,167 @@ +actions[$action->getName()] = $action; + return $this; + } + + /** + * Get automatic action instance + * + * @access public + * @param string $name Absolute class name with namespace + * @return ActionBase + */ + public function getAction($name) + { + if (isset($this->actions[$name])) { + return $this->actions[$name]; + } + + throw new RuntimeException('Automatic Action Not Found: '.$name); + } + + /** + * Get available automatic actions + * + * @access public + * @return array + */ + public function getAvailableActions() + { + $actions = array(); + + foreach ($this->actions as $action) { + if (count($action->getEvents()) > 0) { + $actions[$action->getName()] = $action->getDescription(); + } + } + + asort($actions); + + return $actions; + } + + /** + * Get all available action parameters + * + * @access public + * @param array $actions + * @return array + */ + public function getAvailableParameters(array $actions) + { + $params = array(); + + foreach ($actions as $action) { + try { + $currentAction = $this->getAction($action['action_name']); + $params[$currentAction->getName()] = $currentAction->getActionRequiredParameters(); + } catch (Exception $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + } + } + + return $params; + } + + /** + * Get list of compatible events for a given action + * + * @access public + * @param string $name + * @return array + */ + public function getCompatibleEvents($name) + { + $events = array(); + $actionEvents = $this->getAction($name)->getEvents(); + + foreach ($this->eventManager->getAll() as $event => $description) { + if (in_array($event, $actionEvents)) { + $events[$event] = $description; + } + } + + return $events; + } + + /** + * Bind automatic actions to events + * + * @access public + * @return ActionManager + */ + public function attachEvents() + { + if ($this->userSession->isLogged()) { + $actions = $this->actionModel->getAllByUser($this->userSession->getId()); + } else { + $actions = $this->actionModel->getAll(); + } + + foreach ($actions as $action) { + try { + $listener = clone $this->getAction($action['action_name']); + $listener->setProjectId($action['project_id']); + + foreach ($action['params'] as $param_name => $param_value) { + $listener->setParam($param_name, $param_value); + } + + $this->dispatcher->addListener($action['event_name'], array($listener, 'execute')); + } catch (Exception $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + } + } + + 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 new file mode 100644 index 0000000..3280625 --- /dev/null +++ b/app/Core/Base.php @@ -0,0 +1,261 @@ +container = $container; + } + + /** + * Load automatically dependencies + * + * @access public + * @param string $name Class name + * @return mixed + */ + public function __get($name) + { + return $this->container[$name]; + } + + /** + * Get object instance + * + * @static + * @access public + * @param Container $container + * @return static + */ + public static function getInstance(Container $container) + { + return new static($container); + } +} diff --git a/app/Core/Cache/BaseCache.php b/app/Core/Cache/BaseCache.php new file mode 100644 index 0000000..b51c4c0 --- /dev/null +++ b/app/Core/Cache/BaseCache.php @@ -0,0 +1,38 @@ +get($key); + + if ($result === null) { + $result = call_user_func_array(array($class, $method), array_splice($args, 1)); + $this->set($key, $result); + } + + return $result; + } +} diff --git a/app/Core/Cache/CacheInterface.php b/app/Core/Cache/CacheInterface.php new file mode 100644 index 0000000..033732c --- /dev/null +++ b/app/Core/Cache/CacheInterface.php @@ -0,0 +1,45 @@ +storage[$key] = $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) + { + return isset($this->storage[$key]) ? $this->storage[$key] : null; + } + + /** + * Clear all cache + * + * @access public + */ + public function flush() + { + $this->storage = array(); + } + + /** + * Remove cached value + * + * @access public + * @param string $key + */ + public function remove($key) + { + unset($this->storage[$key]); + } +} diff --git a/app/Core/Controller/AccessForbiddenException.php b/app/Core/Controller/AccessForbiddenException.php new file mode 100644 index 0000000..b5dccb7 --- /dev/null +++ b/app/Core/Controller/AccessForbiddenException.php @@ -0,0 +1,14 @@ +withoutLayout = true; + return $this; + } + + /** + * Return true if no layout + * + * @access public + * @return boolean + */ + public function hasLayout() + { + return $this->withoutLayout; + } +} diff --git a/app/Core/Controller/BaseMiddleware.php b/app/Core/Controller/BaseMiddleware.php new file mode 100644 index 0000000..e94ad95 --- /dev/null +++ b/app/Core/Controller/BaseMiddleware.php @@ -0,0 +1,58 @@ +nextMiddleware = $nextMiddleware; + return $this; + } + + /** + * @return BaseMiddleware + */ + public function getNextMiddleware() + { + return $this->nextMiddleware; + } + + /** + * Move to next middleware + */ + public function next() + { + if ($this->nextMiddleware !== null) { + if (DEBUG) { + $this->logger->debug(__METHOD__.' => ' . get_class($this->nextMiddleware)); + } + + $this->nextMiddleware->execute(); + } + } +} diff --git a/app/Core/Controller/PageNotFoundException.php b/app/Core/Controller/PageNotFoundException.php new file mode 100644 index 0000000..e96a205 --- /dev/null +++ b/app/Core/Controller/PageNotFoundException.php @@ -0,0 +1,14 @@ +executeMiddleware(); + + if (!$this->response->isResponseAlreadySent()) { + $this->executeController(); + } + } catch (PageNotFoundException $e) { + $controllerObject = new AppController($this->container); + $controllerObject->notFound($e->hasLayout()); + } catch (AccessForbiddenException $e) { + $controllerObject = new AppController($this->container); + $controllerObject->accessForbidden($e->hasLayout(), $e->getMessage()); + } + } + + /** + * Execute all middleware + */ + protected function executeMiddleware() + { + if (DEBUG) { + $this->logger->debug(__METHOD__); + } + + $bootstrapMiddleware = new BootstrapMiddleware($this->container); + $authenticationMiddleware = new AuthenticationMiddleware($this->container); + $postAuthenticationMiddleware = new PostAuthenticationMiddleware($this->container); + $appAuthorizationMiddleware = new ApplicationAuthorizationMiddleware($this->container); + $projectAuthorizationMiddleware = new ProjectAuthorizationMiddleware($this->container); + + $bootstrapMiddleware->setNextMiddleware($authenticationMiddleware); + $authenticationMiddleware->setNextMiddleware($postAuthenticationMiddleware); + $postAuthenticationMiddleware->setNextMiddleware($appAuthorizationMiddleware); + $appAuthorizationMiddleware->setNextMiddleware($projectAuthorizationMiddleware); + + $bootstrapMiddleware->execute(); + } + + /** + * Execute the controller + */ + protected function executeController() + { + $className = $this->getControllerClassName(); + + if (DEBUG) { + $this->logger->debug(__METHOD__.' => '.$className.'::'.$this->router->getAction()); + } + + $controllerObject = new $className($this->container); + $controllerObject->{$this->router->getAction()}(); + } + + /** + * Get controller class name + * + * @access protected + * @return string + * @throws RuntimeException + */ + protected function getControllerClassName() + { + if ($this->router->getPlugin() !== '') { + $className = '\Kanboard\Plugin\\'.$this->router->getPlugin().'\Controller\\'.$this->router->getController(); + } else { + $className = '\Kanboard\Controller\\'.$this->router->getController(); + } + + if (! class_exists($className)) { + throw new RuntimeException('Controller not found'); + } + + if (! method_exists($className, $this->router->getAction())) { + throw new RuntimeException('Action not implemented'); + } + + return $className; + } +} diff --git a/app/Core/Csv.php b/app/Core/Csv.php new file mode 100644 index 0000000..773a6b3 --- /dev/null +++ b/app/Core/Csv.php @@ -0,0 +1,222 @@ +delimiter = $delimiter; + $this->enclosure = $enclosure; + } + + /** + * Get list of delimiters + * + * @static + * @access public + * @return array + */ + public static function getDelimiters() + { + return array( + ',' => t('Comma'), + ';' => t('Semi-colon'), + '\t' => t('Tab'), + '|' => t('Vertical bar'), + ); + } + + /** + * Get list of enclosures + * + * @static + * @access public + * @return array + */ + public static function getEnclosures() + { + return array( + '"' => t('Double Quote'), + "'" => t('Single Quote'), + '' => t('None'), + ); + } + + /** + * Check boolean field value + * + * @static + * @access public + * @param mixed $value + * @return int + */ + public static function getBooleanValue($value) + { + if (! empty($value)) { + $value = trim(strtolower($value)); + return $value === '1' || $value[0] === 't' || $value[0] === 'y' ? 1 : 0; + } + + return 0; + } + + /** + * Output CSV file to standard output + * + * @static + * @access public + * @param array $rows + * @param bool $addBOM + */ + public static function output(array $rows, $addBOM = false) + { + $csv = new static; + $csv->write('php://output', $rows, $addBOM); + } + + /** + * Define column mapping between CSV and SQL columns + * + * @access public + * @param array $columns + * @return Csv + */ + public function setColumnMapping(array $columns) + { + $this->columns = $columns; + return $this; + } + + /** + * Read CSV file + * + * @access public + * @param string $filename + * @param callable $callback Example: function(array $row, $line_number) + * @return Csv + */ + public function read($filename, $callback) + { + $file = new SplFileObject($filename); + $file->setFlags(SplFileObject::READ_CSV); + $file->setCsvControl($this->delimiter, $this->enclosure, '\\'); + $line_number = 0; + + foreach ($file as $row) { + $row = $this->filterRow($row); + + if (! empty($row) && $line_number > 0) { + call_user_func_array($callback, array($this->associateColumns($row), $line_number)); + } + + $line_number++; + } + + return $this; + } + + /** + * Write CSV file + * + * @access public + * @param string $filename + * @param array $rows + * @param bool $addBOM + * @return Csv + */ + public function write($filename, array $rows, $addBOM = false) + { + $fp = fopen($filename, 'w'); + + if (is_resource($fp)) { + if ($addBOM) { + fwrite($fp, "\xEF\xBB\xBF"); + } + + foreach ($rows as $row) { + fputcsv($fp, $row, $this->delimiter, $this->enclosure, '\\'); + } + + fclose($fp); + } + + return $this; + } + + /** + * Associate columns header with row values + * + * @access private + * @param array $row + * @return array + */ + private function associateColumns(array $row) + { + $line = array(); + $index = 0; + + foreach ($this->columns as $sql_name => $csv_name) { + if (isset($row[$index])) { + $line[$sql_name] = $row[$index]; + } else { + $line[$sql_name] = ''; + } + + $index++; + } + + return $line; + } + + /** + * Filter empty rows + * + * @access private + * @param array $row + * @return array + */ + private function filterRow(array $row) + { + return array_filter($row); + } +} diff --git a/app/Core/DateParser.php b/app/Core/DateParser.php new file mode 100644 index 0000000..26cfaa8 --- /dev/null +++ b/app/Core/DateParser.php @@ -0,0 +1,326 @@ +configModel->get('application_date_format', DateParser::DATE_FORMAT); + } + + /** + * Get date time format from settings + * + * @access public + * @return string + */ + public function getUserDateTimeFormat() + { + return $this->getUserDateFormat().' '.$this->getUserTimeFormat(); + } + + /** + * Get time format from settings + * + * @access public + * @return string + */ + public function getUserTimeFormat() + { + return $this->configModel->get('application_time_format', DateParser::TIME_FORMAT); + } + + /** + * List of time formats + * + * @access public + * @return string[] + */ + public function getTimeFormats() + { + return array( + 'H:i', + 'g:i a', + ); + } + + /** + * List of date formats + * + * @access public + * @param boolean $iso + * @return string[] + */ + public function getDateFormats($iso = false) + { + $formats = array( + $this->getUserDateFormat(), + ); + + $isoFormats = array( + 'Y-m-d', + 'Y_m_d', + ); + + $userFormats = array( + 'm/d/Y', + 'd/m/Y', + 'Y/m/d', + 'd.m.Y', + ); + + if ($iso) { + $formats = array_merge($formats, $isoFormats, $userFormats); + } else { + $formats = array_merge($formats, $userFormats); + } + + return array_unique($formats); + } + + /** + * List of datetime formats + * + * @access public + * @param boolean $iso + * @return string[] + */ + public function getDateTimeFormats($iso = false) + { + $formats = array( + $this->getUserDateTimeFormat(), + ); + + foreach ($this->getDateFormats($iso) as $date) { + foreach ($this->getTimeFormats() as $time) { + $formats[] = $date.' '.$time; + } + } + + return array_unique($formats); + } + + /** + * List of all date formats + * + * @access public + * @param boolean $iso + * @return string[] + */ + public function getAllDateFormats($iso = false) + { + return array_merge($this->getDateFormats($iso), $this->getDateTimeFormats($iso)); + } + + /** + * Get available formats (visible in settings) + * + * @access public + * @param array $formats + * @return array + */ + public function getAvailableFormats(array $formats) + { + $values = array(); + + foreach ($formats as $format) { + $values[$format] = date($format).' ('.$format.')'; + } + + return $values; + } + + /** + * Get formats for date parsing + * + * @access public + * @return array + */ + public function getParserFormats() + { + return array( + $this->getUserDateFormat(), + 'Y-m-d', + 'Y_m_d', + $this->getUserDateTimeFormat(), + 'Y-m-d H:i', + 'Y_m_d H:i', + ); + } + + /** + * Parse a date and return a unix timestamp, try different date formats + * + * @access public + * @param string $value Date to parse + * @return integer + */ + public function getTimestamp($value) + { + if (ctype_digit((string) $value)) { + return (int) $value; + } + + foreach ($this->getParserFormats() as $format) { + $timestamp = $this->getValidDate($value, $format); + + if ($timestamp !== 0) { + return $timestamp; + } + } + + return 0; + } + + /** + * Return a timestamp if the given date format is correct otherwise return 0 + * + * @access private + * @param string $value Date to parse + * @param string $format Date format + * @return integer + */ + private function getValidDate($value, $format) + { + $date = DateTime::createFromFormat($format, $value); + + if ($date !== false) { + $errors = DateTime::getLastErrors(); + if ($errors === false || + $errors['error_count'] === 0 && $errors['warning_count'] === 0) { + $timestamp = $date->getTimestamp(); + return $timestamp > 0 ? $timestamp : 0; + } + } + + return 0; + } + + /** + * Return true if the date is within the date range + * + * @access public + * @param DateTime $date + * @param DateTime $start + * @param DateTime $end + * @return boolean + */ + public function withinDateRange(DateTime $date, DateTime $start, DateTime $end) + { + return $date >= $start && $date <= $end; + } + + /** + * Get the total number of hours between 2 datetime objects + * Minutes are rounded to the nearest quarter + * + * @access public + * @param DateTime $d1 + * @param DateTime $d2 + * @return float + */ + public function getHours(DateTime $d1, DateTime $d2) + { + $seconds = abs($d1->getTimestamp() - $d2->getTimestamp()); + return round($seconds / 3600, 2); + } + + /** + * Get ISO-8601 date from user input + * + * @access public + * @param string $value Date to parse + * @return string + */ + public function getIsoDate($value) + { + return date('Y-m-d', $this->getTimestamp($value)); + } + + /** + * Get a timestamp from an ISO date format + * + * @access public + * @param string $value + * @return integer + */ + public function getTimestampFromIsoFormat($value) + { + return $this->removeTimeFromTimestamp(ctype_digit((string) $value) ? $value : strtotime($value)); + } + + /** + * Remove the time from a timestamp + * + * @access public + * @param integer $timestamp + * @return integer + */ + public function removeTimeFromTimestamp($timestamp) + { + return mktime(0, 0, 0, date('m', $timestamp), date('d', $timestamp), date('Y', $timestamp)); + } + + /** + * Format date (form display) + * + * @access public + * @param array $values Database values + * @param string[] $fields Date fields + * @param string $format Date format + * @return array + */ + public function format(array $values, array $fields, $format) + { + foreach ($fields as $field) { + if (! empty($values[$field])) { + if (ctype_digit((string) $values[$field])) { + $values[$field] = date($format, $values[$field]); + } + } else { + $values[$field] = ''; + } + } + + return $values; + } + + /** + * Convert date to timestamp + * + * @access public + * @param array $values Database values + * @param string[] $fields Date fields + * @param boolean $keep_time Keep time or not + * @return array + */ + public function convert(array $values, array $fields, $keep_time = false) + { + foreach ($fields as $field) { + if (! empty($values[$field])) { + $timestamp = $this->getTimestamp($values[$field]); + $values[$field] = $keep_time ? $timestamp : $this->removeTimeFromTimestamp($timestamp); + } + } + + return $values; + } +} diff --git a/app/Core/Event/EventManager.php b/app/Core/Event/EventManager.php new file mode 100644 index 0000000..75e8198 --- /dev/null +++ b/app/Core/Event/EventManager.php @@ -0,0 +1,66 @@ +events[$event] = $description; + return $this; + } + + /** + * Get the list of events and description that can be used from the user interface + * + * @access public + * @return array + */ + public function getAll() + { + $events = array( + TaskLinkModel::EVENT_CREATE_UPDATE => t('Task link creation or modification'), + TaskModel::EVENT_MOVE_COLUMN => t('Move a task to another column'), + TaskModel::EVENT_UPDATE => t('Task modification'), + TaskModel::EVENT_CREATE => t('Task creation'), + TaskModel::EVENT_OPEN => t('Reopen a task'), + TaskModel::EVENT_CLOSE => t('Closing a task'), + 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'), + SubtaskModel::EVENT_CREATE_UPDATE => t('Subtask creation or modification'), + ); + + $events = array_merge($events, $this->events); + asort($events); + + return $events; + } +} diff --git a/app/Core/ExternalLink/ExternalLinkInterface.php b/app/Core/ExternalLink/ExternalLinkInterface.php new file mode 100644 index 0000000..2dbc0a1 --- /dev/null +++ b/app/Core/ExternalLink/ExternalLinkInterface.php @@ -0,0 +1,36 @@ +providers, $provider); + return $this; + } + + /** + * Get provider + * + * @access public + * @param string $type + * @throws ExternalLinkProviderNotFound + * @return ExternalLinkProviderInterface + */ + public function getProvider($type) + { + foreach ($this->providers as $provider) { + if ($provider->getType() === $type) { + return $provider; + } + } + + throw new ExternalLinkProviderNotFound('Unable to find link provider: '.$type); + } + + /** + * Get link types + * + * @access public + * @return array + */ + public function getTypes() + { + $types = array(); + + foreach ($this->providers as $provider) { + $types[$provider->getType()] = $provider->getName(); + } + + asort($types); + + return array(self::TYPE_AUTO => t('Auto')) + $types; + } + + /** + * Get dependency label from a provider + * + * @access public + * @param string $type + * @param string $dependency + * @return string + */ + public function getDependencyLabel($type, $dependency) + { + $provider = $this->getProvider($type); + $dependencies = $provider->getDependencies(); + return isset($dependencies[$dependency]) ? $dependencies[$dependency] : $dependency; + } + + /** + * Find a provider that match + * + * @access public + * @throws ExternalLinkProviderNotFound + * @return ExternalLinkProviderInterface + */ + public function find() + { + if ($this->userInputType === self::TYPE_AUTO) { + $provider = $this->findProvider(); + } else { + $provider = $this->getProvider($this->userInputType); + $provider->setUserTextInput($this->userInputText); + + if (! $provider->match()) { + throw new ExternalLinkProviderNotFound('Unable to parse URL with selected provider'); + } + } + + if ($provider === null) { + throw new ExternalLinkProviderNotFound('Unable to find link information from provided information'); + } + + return $provider; + } + + /** + * Set form values + * + * @access public + * @param array $values + * @return ExternalLinkManager + */ + public function setUserInput(array $values) + { + $this->userInputType = empty($values['type']) ? self::TYPE_AUTO : $values['type']; + $this->userInputText = empty($values['text']) ? '' : trim($values['text']); + return $this; + } + + /** + * Set provider type + * + * @access public + * @param string $userInputType + * @return ExternalLinkManager + */ + public function setUserInputType($userInputType) + { + $this->userInputType = $userInputType; + return $this; + } + + /** + * Set external link + * @param string $userInputText + * @return ExternalLinkManager + */ + public function setUserInputText($userInputText) + { + $this->userInputText = $userInputText; + return $this; + } + + /** + * Find a provider that user input + * + * @access private + * @return ExternalLinkProviderInterface + */ + private function findProvider() + { + foreach ($this->providers as $provider) { + $provider->setUserTextInput($this->userInputText); + + if ($provider->match()) { + return $provider; + } + } + + return null; + } +} diff --git a/app/Core/ExternalLink/ExternalLinkProviderInterface.php b/app/Core/ExternalLink/ExternalLinkProviderInterface.php new file mode 100644 index 0000000..961252e --- /dev/null +++ b/app/Core/ExternalLink/ExternalLinkProviderInterface.php @@ -0,0 +1,71 @@ + 'Related', + * 'child' => 'Child', + * 'parent' => 'Parent', + * 'self' => 'Self', + * ] + * + * The dictionary key is saved in the database. + * + * @access public + * @return array + */ + public function getDependencies(); + + /** + * Set text entered by the user + * + * @access public + * @param string $input + */ + public function setUserTextInput($input); + + /** + * Return true if the provider can parse correctly the user input + * + * @access public + * @return boolean + */ + public function match(); + + /** + * Get the link found with the properties + * + * @access public + * @return ExternalLinkInterface + */ + public function getLink(); +} diff --git a/app/Core/ExternalLink/ExternalLinkProviderNotFound.php b/app/Core/ExternalLink/ExternalLinkProviderNotFound.php new file mode 100644 index 0000000..4fd0520 --- /dev/null +++ b/app/Core/ExternalLink/ExternalLinkProviderNotFound.php @@ -0,0 +1,15 @@ +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(); + } + + /** + * Get all providers + * + * @return ExternalTaskProviderInterface[] + */ + public function getProviders() + { + return $this->providers; + } +} diff --git a/app/Core/ExternalTask/ExternalTaskProviderInterface.php b/app/Core/ExternalTask/ExternalTaskProviderInterface.php new file mode 100644 index 0000000..7a8157f --- /dev/null +++ b/app/Core/ExternalTask/ExternalTaskProviderInterface.php @@ -0,0 +1,94 @@ + 'T_WHITESPACE', + '/^([<=>]{0,2}[0-9]{4}-[0-9]{2}-[0-9]{2})/' => 'T_STRING', + '/^([<=>]{1,2}\w+)/u' => 'T_STRING', + '/^([<=>]{1,2}".+")/' => 'T_STRING', + '/^("(.*?)")/' => 'T_STRING', + '/^(\S+)/u' => 'T_STRING', + '/^(#\d+)/' => 'T_STRING', + ); + + /** + * Default token + * + * @access private + * @var string + */ + private $defaultToken = ''; + + /** + * Add token + * + * @access public + * @param string $regex + * @param string $token + * @return $this + */ + public function addToken($regex, $token) + { + $this->tokenMap = array($regex => $token) + $this->tokenMap; + return $this; + } + + /** + * Set default token + * + * @access public + * @param string $token + * @return $this + */ + public function setDefaultToken($token) + { + $this->defaultToken = $token; + return $this; + } + + /** + * Tokenize input string + * + * @access public + * @param string $input + * @return array + */ + public function tokenize($input) + { + $tokens = array(); + $this->offset = 0; + + if (is_null($input)) { + $input = ""; + } + $input_length = mb_strlen($input, 'UTF-8'); + + while ($this->offset < $input_length) { + $result = $this->match(mb_substr($input, $this->offset, $input_length, 'UTF-8')); + + if ($result === false) { + return array(); + } + + $tokens[] = $result; + } + + return $this->map($tokens); + } + + /** + * Find a token that match and move the offset + * + * @access protected + * @param string $string + * @return array|boolean + */ + protected function match($string) + { + foreach ($this->tokenMap as $pattern => $name) { + if (preg_match($pattern, $string, $matches)) { + $this->offset += mb_strlen($matches[1], 'UTF-8'); + + return array( + 'match' => str_replace('"', '', $matches[1]), + 'token' => $name, + ); + } + } + + return false; + } + + /** + * Build map of tokens and matches + * + * @access protected + * @param array $tokens + * @return array + */ + protected function map(array $tokens) + { + $map = array(); + $leftOver = ''; + + while (false !== ($token = current($tokens))) { + if ($token['token'] === 'T_STRING' || $token['token'] === 'T_WHITESPACE') { + $leftOver .= $token['match']; + } else { + $next = next($tokens); + + if ($next !== false && $next['token'] === 'T_STRING') { + $map[$token['token']][] = $next['match']; + } + } + + next($tokens); + } + + $leftOver = trim($leftOver); + + if ($this->defaultToken !== '' && $leftOver !== '') { + $map[$this->defaultToken] = array($leftOver); + } + + return $map; + } +} diff --git a/app/Core/Filter/LexerBuilder.php b/app/Core/Filter/LexerBuilder.php new file mode 100644 index 0000000..e3ab725 --- /dev/null +++ b/app/Core/Filter/LexerBuilder.php @@ -0,0 +1,151 @@ +lexer = new Lexer(); + $this->queryBuilder = new QueryBuilder(); + } + + /** + * Add a filter + * + * @access public + * @param FilterInterface $filter + * @param bool $default + * @return LexerBuilder + */ + public function withFilter(FilterInterface $filter, $default = false) + { + $attributes = $filter->getAttributes(); + + foreach ($attributes as $attribute) { + $this->filters[$attribute] = $filter; + $this->lexer->addToken(sprintf("/^(%s:)/i", $attribute), $attribute); + + if ($default) { + $this->lexer->setDefaultToken($attribute); + } + } + + return $this; + } + + /** + * Set the query + * + * @access public + * @param Table $query + * @return LexerBuilder + */ + public function withQuery(Table $query) + { + $this->query = $query; + $this->queryBuilder->withQuery($this->query); + return $this; + } + + /** + * Parse the input and build the query + * + * @access public + * @param string $input + * @return QueryBuilder + */ + public function build($input) + { + $tokens = $this->lexer->tokenize($input); + + foreach ($tokens as $token => $values) { + if (isset($this->filters[$token])) { + $this->applyFilters($this->filters[$token], $values); + } + } + + return $this->queryBuilder; + } + + /** + * Apply filters to the query + * + * @access protected + * @param FilterInterface $filter + * @param array $values + */ + protected function applyFilters(FilterInterface $filter, array $values) + { + $len = count($values); + + if ($len > 1) { + $criteria = new OrCriteria(); + $criteria->withQuery($this->query); + + foreach ($values as $value) { + $currentFilter = clone($filter); + $criteria->withFilter($currentFilter->withValue($value)); + } + + $this->queryBuilder->withCriteria($criteria); + } elseif ($len === 1) { + $this->queryBuilder->withFilter($filter->withValue($values[0])); + } + } + + /** + * Clone object with deep copy + */ + public function __clone() + { + $this->lexer = clone $this->lexer; + $this->query = clone $this->query; + $this->queryBuilder = clone $this->queryBuilder; + } +} diff --git a/app/Core/Filter/OrCriteria.php b/app/Core/Filter/OrCriteria.php new file mode 100644 index 0000000..174b845 --- /dev/null +++ b/app/Core/Filter/OrCriteria.php @@ -0,0 +1,68 @@ +query = $query; + return $this; + } + + /** + * Set filter + * + * @access public + * @param FilterInterface $filter + * @return CriteriaInterface + */ + public function withFilter(FilterInterface $filter) + { + $this->filters[] = $filter; + return $this; + } + + /** + * Apply condition + * + * @access public + * @return CriteriaInterface + */ + public function apply() + { + $this->query->beginOr(); + + foreach ($this->filters as $filter) { + $filter->withQuery($this->query)->apply(); + } + + $this->query->closeOr(); + return $this; + } +} diff --git a/app/Core/Filter/QueryBuilder.php b/app/Core/Filter/QueryBuilder.php new file mode 100644 index 0000000..bdd6b94 --- /dev/null +++ b/app/Core/Filter/QueryBuilder.php @@ -0,0 +1,115 @@ +query = $query; + return $this; + } + + /** + * Set a filter + * + * @access public + * @param FilterInterface $filter + * @return QueryBuilder + */ + public function withFilter(FilterInterface $filter) + { + $filter->withQuery($this->query)->apply(); + return $this; + } + + /** + * Set a criteria + * + * @access public + * @param CriteriaInterface $criteria + * @return QueryBuilder + */ + public function withCriteria(CriteriaInterface $criteria) + { + $criteria->withQuery($this->query)->apply(); + return $this; + } + + /** + * Set a formatter + * + * @access public + * @param FormatterInterface $formatter + * @return string|array + */ + public function format(FormatterInterface $formatter) + { + return $formatter->withQuery($this->query)->format(); + } + + /** + * Get the query result as array + * + * @access public + * @return array + */ + public function toArray() + { + return $this->query->findAll(); + } + + /** + * Get Query object + * + * @access public + * @return Table + */ + public function getQuery() + { + return $this->query; + } + + /** + * Clone object with deep copy + */ + public function __clone() + { + $this->query = clone $this->query; + } +} diff --git a/app/Core/Group/GroupBackendProviderInterface.php b/app/Core/Group/GroupBackendProviderInterface.php new file mode 100644 index 0000000..0b6e298 --- /dev/null +++ b/app/Core/Group/GroupBackendProviderInterface.php @@ -0,0 +1,21 @@ +providers[] = $provider; + return $this; + } + + /** + * Find a group from a search query + * + * @access public + * @param string $input + * @return GroupProviderInterface[] + */ + public function find($input) + { + $groups = array(); + + foreach ($this->providers as $provider) { + $groups = array_merge($groups, $provider->find($input)); + } + + return $this->removeDuplicates($groups); + } + + /** + * Remove duplicated groups + * + * @access protected + * @param array $groups + * @return GroupProviderInterface[] + */ + protected function removeDuplicates(array $groups) + { + $result = array(); + + foreach ($groups as $group) { + if (! isset($result[$group->getName()])) { + $result[$group->getName()] = $group; + } + } + + return array_values($result); + } +} diff --git a/app/Core/Group/GroupProviderInterface.php b/app/Core/Group/GroupProviderInterface.php new file mode 100644 index 0000000..f312f89 --- /dev/null +++ b/app/Core/Group/GroupProviderInterface.php @@ -0,0 +1,40 @@ +container = $container; + $this->helpers = new Container; + } + + /** + * Expose helpers with magic getter + * + * @access public + * @param string $helper + * @return mixed + */ + public function __get($helper) + { + return $this->getHelper($helper); + } + + /** + * Allow overriding helpers through magic setter + * + * @access public + * @param string $helper + * @param mixed $instance + */ + public function __set($helper, $instance) + { + $this->helpers[$helper] = $instance; + } + + /** + * Expose helpers with method + * + * @access public + * @param string $helper + * @return mixed + */ + public function getHelper($helper) + { + return $this->helpers[$helper]; + } + + /** + * Register a new Helper + * + * @access public + * @param string $property + * @param string $className + * @return Helper + */ + public function register($property, $className) + { + $container = $this->container; + + $this->helpers[$property] = function () use ($className, $container) { + return new $className($container); + }; + + return $this; + } +} diff --git a/app/Core/Http/Client.php b/app/Core/Http/Client.php new file mode 100644 index 0000000..d860f50 --- /dev/null +++ b/app/Core/Http/Client.php @@ -0,0 +1,451 @@ +doRequest('GET', $url, '', $headers, $raiseForErrors, $followRedirects); + } + + /** + * Send a GET HTTP request and parse JSON response + * + * @access public + * @param string $url + * @param string[] $headers + * @param bool $raiseForErrors + * @param bool $followRedirects + * @return array + */ + public function getJson($url, array $headers = [], $raiseForErrors = false, $followRedirects = true) + { + $response = $this->doRequest('GET', $url, '', array_merge(['Accept: application/json'], $headers), $raiseForErrors, $followRedirects); + return json_decode($response, true) ?: []; + } + + /** + * Send a POST HTTP request encoded in JSON + * + * @access public + * @param string $url + * @param array $data + * @param string[] $headers + * @param bool $raiseForErrors + * @param bool $followRedirects + * @return string + */ + public function postJson($url, array $data, array $headers = [], $raiseForErrors = false, $followRedirects = true) + { + return $this->doRequest( + 'POST', + $url, + json_encode($data), + array_merge(['Content-type: application/json'], $headers), + $raiseForErrors, + $followRedirects + ); + } + + /** + * Send a POST HTTP request encoded in JSON (Fire and forget) + * + * @access public + * @param string $url + * @param array $data + * @param string[] $headers + * @param bool $raiseForErrors + */ + public function postJsonAsync($url, array $data, array $headers = [], $raiseForErrors = false) + { + $this->queueManager->push(HttpAsyncJob::getInstance($this->container)->withParams( + 'POST', + $url, + json_encode($data), + array_merge(['Content-type: application/json'], $headers), + $raiseForErrors + )); + } + + /** + * Send a POST HTTP request encoded in www-form-urlencoded + * + * @access public + * @param string $url + * @param array $data + * @param string[] $headers + * @param bool $raiseForErrors + * @return string + */ + public function postForm($url, array $data, array $headers = [], $raiseForErrors = false) + { + return $this->doRequest( + 'POST', + $url, + http_build_query($data), + array_merge(['Content-type: application/x-www-form-urlencoded'], $headers), + $raiseForErrors + ); + } + + /** + * Send a POST HTTP request encoded in www-form-urlencoded (fire and forget) + * + * @access public + * @param string $url + * @param array $data + * @param string[] $headers + * @param bool $raiseForErrors + */ + public function postFormAsync($url, array $data, array $headers = [], $raiseForErrors = false) + { + $this->queueManager->push(HttpAsyncJob::getInstance($this->container)->withParams( + 'POST', + $url, + http_build_query($data), + array_merge(['Content-type: application/x-www-form-urlencoded'], $headers), + $raiseForErrors + )); + } + + /** + * Make the HTTP request with cURL if detected, socket otherwise + * + * @access public + * @param string $method + * @param string $url + * @param string $content + * @param string[] $headers + * @param bool $raiseForErrors + * @param bool $followRedirects + * @return string + */ + public function doRequest($method, $url, $content, array $headers, $raiseForErrors = false, $followRedirects = true) + { + $requestBody = ''; + + if (! empty($url)) { + if (function_exists('curl_version')) { + if (DEBUG) { + $this->logger->debug('HttpClient::doRequest: cURL detected'); + } + $requestBody = $this->doRequestWithCurl($method, $url, $content, $headers, $raiseForErrors, $followRedirects); + } else { + if (DEBUG) { + $this->logger->debug('HttpClient::doRequest: using socket'); + } + $requestBody = $this->doRequestWithSocket($method, $url, $content, $headers, $raiseForErrors, $followRedirects); + } + } + + return $requestBody; + } + + /** + * Make the HTTP request with socket + * + * @access private + * @param string $method + * @param string $url + * @param string $content + * @param string[] $headers + * @param bool $raiseForErrors + * @param bool $followRedirects + * @return string + */ + private function doRequestWithSocket($method, $url, $content, array $headers, $raiseForErrors = false, $followRedirects = true) + { + $startTime = microtime(true); + $stream = @fopen(trim($url), 'r', false, stream_context_create($this->getContext($method, $content, $headers, $raiseForErrors, $followRedirects))); + + if (! is_resource($stream)) { + $this->logger->error('HttpClient: request failed ('.$url.')'); + + if ($raiseForErrors) { + throw new ClientException('Unreachable URL: '.$url); + } + + return ''; + } + + $body = stream_get_contents($stream); + $metadata = stream_get_meta_data($stream); + + if ($raiseForErrors && array_key_exists('wrapper_data', $metadata)) { + $statusCode = $this->getStatusCode($metadata['wrapper_data']); + + if ($statusCode >= 400) { + throw new InvalidStatusException('Request failed with status code '.$statusCode, $statusCode, $body); + } + } + + if (DEBUG) { + $this->logger->debug('HttpClient: url='.$url); + $this->logger->debug('HttpClient: headers='.var_export($headers, true)); + $this->logger->debug('HttpClient: payload='.$content); + $this->logger->debug('HttpClient: metadata='.var_export($metadata, true)); + $this->logger->debug('HttpClient: body='.$body); + $this->logger->debug('HttpClient: executionTime='.(microtime(true) - $startTime)); + } + + return $body; + } + + + /** + * Make the HTTP request with cURL + * + * @access private + * @param string $method + * @param string $url + * @param string $content + * @param string[] $headers + * @param bool $raiseForErrors + * @param bool $followRedirects + * @return string + */ + private function doRequestWithCurl($method, $url, $content, array $headers, $raiseForErrors = false, $followRedirects = true) + { + $startTime = microtime(true); + $curlSession = @curl_init(); + + curl_setopt($curlSession, CURLOPT_URL, trim($url)); + curl_setopt($curlSession, CURLOPT_USERAGENT, self::HTTP_USER_AGENT); + curl_setopt($curlSession, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1); + curl_setopt($curlSession, CURLOPT_TIMEOUT, HTTP_TIMEOUT); + curl_setopt($curlSession, CURLOPT_FORBID_REUSE, true); + curl_setopt($curlSession, CURLOPT_MAXREDIRS, $followRedirects ? HTTP_MAX_REDIRECTS : 0); + curl_setopt($curlSession, CURLOPT_RETURNTRANSFER, true); + curl_setopt($curlSession, CURLOPT_FOLLOWLOCATION, $followRedirects); + + if ('POST' === $method) { + curl_setopt($curlSession, CURLOPT_POST, true); + curl_setopt($curlSession, CURLOPT_POSTFIELDS, $content); + } elseif ('PUT' === $method) { + curl_setopt($curlSession, CURLOPT_CUSTOMREQUEST, 'PUT'); + curl_setopt($curlSession, CURLOPT_POST, true); + curl_setopt($curlSession, CURLOPT_POSTFIELDS, $content); + } + + if (! empty($headers)) { + curl_setopt($curlSession, CURLOPT_HTTPHEADER, $headers); + } + + if (HTTP_VERIFY_SSL_CERTIFICATE === false) { + curl_setopt($curlSession, CURLOPT_SSL_VERIFYHOST, 0); + curl_setopt($curlSession, CURLOPT_SSL_VERIFYPEER, false); + } + + if (HTTP_PROXY_HOSTNAME) { + curl_setopt($curlSession, CURLOPT_PROXY, HTTP_PROXY_HOSTNAME); + curl_setopt($curlSession, CURLOPT_PROXYPORT, HTTP_PROXY_PORT); + curl_setopt($curlSession, CURLOPT_NOPROXY, HTTP_PROXY_EXCLUDE); + } + + if (HTTP_PROXY_USERNAME) { + curl_setopt($curlSession, CURLOPT_PROXYAUTH, CURLAUTH_BASIC); + curl_setopt($curlSession, CURLOPT_PROXYUSERPWD, HTTP_PROXY_USERNAME.':'.HTTP_PROXY_PASSWORD); + } + + $body = curl_exec($curlSession); + + if ($body === false) { + $errorMsg = curl_error($curlSession); + curl_close($curlSession); + + $this->logger->error('HttpClient: request failed ('.$url.' - '.$errorMsg.')'); + + if ($raiseForErrors) { + throw new ClientException('Unreachable URL: '.$url.' ('.$errorMsg.')'); + } + + return ''; + } + + if ($raiseForErrors) { + $statusCode = curl_getinfo($curlSession, CURLINFO_RESPONSE_CODE); + + if ($statusCode >= 400) { + curl_close($curlSession); + throw new InvalidStatusException('Request failed with status code '.$statusCode, $statusCode, $body); + } + } + + if (DEBUG) { + $this->logger->debug('HttpClient: url='.$url); + $this->logger->debug('HttpClient: headers='.var_export($headers, true)); + $this->logger->debug('HttpClient: payload='.$content); + $this->logger->debug('HttpClient: metadata='.var_export(curl_getinfo($curlSession), true)); + $this->logger->debug('HttpClient: body='.$body); + $this->logger->debug('HttpClient: executionTime='.(microtime(true) - $startTime)); + } + + curl_close($curlSession); + return $body; + } + + /** + * Get stream context + * + * @access private + * @param string $method + * @param string $content + * @param string[] $headers + * @param bool $raiseForErrors + * @param bool $followRedirects + * @return array + */ + private function getContext($method, $content, array $headers, $raiseForErrors = false, $followRedirects = true) + { + $default_headers = [ + 'User-Agent: '.self::HTTP_USER_AGENT, + 'Connection: close', + ]; + + if (HTTP_PROXY_USERNAME) { + $default_headers[] = 'Proxy-Authorization: Basic '.base64_encode(HTTP_PROXY_USERNAME.':'.HTTP_PROXY_PASSWORD); + } + + $headers = array_merge($default_headers, $headers); + + $context = [ + 'http' => [ + 'method' => $method, + 'protocol_version' => 1.1, + 'timeout' => HTTP_TIMEOUT, + 'max_redirects' => $followRedirects ? HTTP_MAX_REDIRECTS : 0, + 'follow_location' => $followRedirects ? 1 : 0, + 'header' => implode("\r\n", $headers), + 'content' => $content, + 'ignore_errors' => $raiseForErrors, + ] + ]; + + if (HTTP_PROXY_HOSTNAME) { + $context['http']['proxy'] = 'tcp://'.HTTP_PROXY_HOSTNAME.':'.HTTP_PROXY_PORT; + $context['http']['request_fulluri'] = true; + } + + if (HTTP_VERIFY_SSL_CERTIFICATE === false) { + $context['ssl'] = [ + 'verify_peer' => false, + 'verify_peer_name' => false, + 'allow_self_signed' => true, + ]; + } + + return $context; + } + + private function getStatusCode(array $lines) + { + $status = 200; + + foreach ($lines as $line) { + if (strpos($line, 'HTTP/1') === 0) { + $status = (int) substr($line, 9, 3); + } + } + + return $status; + } + + /** + * Get backend used for making HTTP connections + * + * @access public + * @return string + */ + public static function backend() + { + return function_exists('curl_version') ? 'cURL' : 'socket'; + } + + /** + * Check if an IP address is private + * + * @access public + * @param string $ip + * @return bool + */ + public function isPrivateIpAddress($ip) + { + if (filter_var($ip, FILTER_VALIDATE_IP) === false) { + return false; + } + + return filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false; + } + + /** + * Check if a URL is private (RFC1918, localhost, etc.) + * + * @access public + * @param string $url + * @return bool + */ + public function isPrivateURL($url) + { + $parsedUrl = parse_url($url); + + if (!isset($parsedUrl['scheme']) || !in_array(strtolower($parsedUrl['scheme']), ['http', 'https'], true)) { + return false; + } + + if (!isset($parsedUrl['host'])) { + return false; + } + + $host = trim($parsedUrl['host']); + if ($host === '') { + return false; + } + + $ipv4Address = gethostbyname($host); + if ($this->isPrivateIpAddress($ipv4Address)) { + return true; + } + + if (function_exists('dns_get_record')) { + $dnsRecords = @dns_get_record($host, DNS_AAAA); + if (is_array($dnsRecords)) { + foreach ($dnsRecords as $record) { + if (isset($record['type']) && $record['type'] === 'AAAA' && isset($record['ipv6'])) { + if ($this->isPrivateIpAddress($record['ipv6'])) { + return true; + } + } + } + } + } + + return false; + } +} diff --git a/app/Core/Http/ClientException.php b/app/Core/Http/ClientException.php new file mode 100644 index 0000000..5eb783f --- /dev/null +++ b/app/Core/Http/ClientException.php @@ -0,0 +1,9 @@ +statusCode = $statusCode; + $this->body = $body; + } + + public function getStatusCode() + { + return $this->statusCode; + } + + public function getBody() + { + return $this->body; + } +} diff --git a/app/Core/Http/OAuth2.php b/app/Core/Http/OAuth2.php new file mode 100644 index 0000000..f47927e --- /dev/null +++ b/app/Core/Http/OAuth2.php @@ -0,0 +1,151 @@ +clientId = $clientId; + $this->secret = $secret; + $this->callbackUrl = $callbackUrl; + $this->authUrl = $authUrl; + $this->tokenUrl = $tokenUrl; + $this->scopes = $scopes; + + return $this; + } + + /** + * Generate OAuth2 state and return the token value + * + * @access public + * @return string + */ + public function getState() + { + if (! session_exists('oauthState')) { + session_set('oauthState', $this->token->getToken()); + } + + return session_get('oauthState'); + } + + /** + * Check the validity of the state (CSRF token) + * + * @access public + * @param string $state + * @return bool + */ + public function isValidateState($state) + { + return $state === $this->getState(); + } + + /** + * Get authorization url + * + * @access public + * @return string + */ + public function getAuthorizationUrl() + { + $params = array( + 'response_type' => 'code', + 'client_id' => $this->clientId, + 'redirect_uri' => $this->callbackUrl, + 'scope' => implode(' ', $this->scopes), + 'state' => $this->getState(), + ); + + return $this->authUrl.'?'.http_build_query($params); + } + + /** + * Get authorization header + * + * @access public + * @return string + */ + public function getAuthorizationHeader() + { + if (strtolower($this->tokenType) === 'bearer') { + return 'Authorization: Bearer '.$this->accessToken; + } + + return ''; + } + + /** + * Get access token + * + * @access public + * @param string $code + * @return string + */ + public function getAccessToken($code) + { + if (empty($this->accessToken) && ! empty($code)) { + $params = array( + 'code' => $code, + 'client_id' => $this->clientId, + 'client_secret' => $this->secret, + 'redirect_uri' => $this->callbackUrl, + 'grant_type' => 'authorization_code', + 'state' => $this->getState(), + ); + + $response = json_decode($this->httpClient->postForm($this->tokenUrl, $params, array('Accept: application/json')), true); + + $this->tokenType = isset($response['token_type']) ? $response['token_type'] : ''; + $this->accessToken = isset($response['access_token']) ? $response['access_token'] : ''; + } + + return $this->accessToken; + } + + /** + * Set access token + * + * @access public + * @param string $token + * @param string $type + * @return $this + */ + public function setAccessToken($token, $type = 'bearer') + { + $this->accessToken = $token; + $this->tokenType = $type; + return $this; + } +} diff --git a/app/Core/Http/RememberMeCookie.php b/app/Core/Http/RememberMeCookie.php new file mode 100644 index 0000000..4b409f5 --- /dev/null +++ b/app/Core/Http/RememberMeCookie.php @@ -0,0 +1,120 @@ + $token, + 'sequence' => $sequence, + ); + } + + /** + * Return true if the current user has a RememberMe cookie + * + * @access public + * @return bool + */ + public function hasCookie() + { + return $this->request->getCookie(self::COOKIE_NAME) !== ''; + } + + /** + * Write and encode the cookie + * + * @access public + * @param string $token Session token + * @param string $sequence Sequence token + * @param string $expiration Cookie expiration + * @return boolean + */ + public function write($token, $sequence, $expiration) + { + return setcookie( + self::COOKIE_NAME, + $this->encode($token, $sequence), + $expiration, + $this->helper->url->dir(), + '', + $this->request->isHTTPS(), + true + ); + } + + /** + * Read and decode the cookie + * + * @access public + * @return mixed + */ + public function read() + { + $cookie = $this->request->getCookie(self::COOKIE_NAME); + + if (empty($cookie)) { + return false; + } + + return $this->decode($cookie); + } + + /** + * Remove the cookie + * + * @access public + * @return boolean + */ + public function remove() + { + return setcookie( + self::COOKIE_NAME, + '', + time() - 3600, + $this->helper->url->dir(), + '', + $this->request->isHTTPS(), + true + ); + } +} diff --git a/app/Core/Http/Request.php b/app/Core/Http/Request.php new file mode 100644 index 0000000..ef86abc --- /dev/null +++ b/app/Core/Http/Request.php @@ -0,0 +1,596 @@ +server = empty($server) ? $_SERVER : $server; + $this->get = empty($get) ? $_GET : $get; + $this->post = empty($post) ? $_POST : $post; + $this->files = empty($files) ? $_FILES : $files; + $this->cookies = empty($cookies) ? $_COOKIE : $cookies; + } + + /** + * Set GET parameters + * + * @param array $params + */ + public function setParams(array $params) + { + $this->get = array_merge($this->get, $params); + } + + /** + * Get query string string parameter + * + * @access public + * @param string $name Parameter name + * @param string $default_value Default value + * @return string + */ + public function getStringParam($name, $default_value = '') + { + return isset($this->get[$name]) ? $this->get[$name] : $default_value; + } + + /** + * Get query string integer parameter + * + * @access public + * @param string $name Parameter name + * @param integer $default_value Default value + * @return integer + */ + public function getIntegerParam($name, $default_value = 0) + { + return isset($this->get[$name]) && ctype_digit((string) $this->get[$name]) ? (int) $this->get[$name] : $default_value; + } + + /** + * Get a form value + * + * @access public + * @param string $name Form field name + * @return string|null + */ + public function getValue($name) + { + $values = $this->getValues(); + return isset($values[$name]) ? $values[$name] : null; + } + + /** + * Get form values and check for CSRF token + * + * @access public + * @return array + */ + public function getValues() + { + if (! empty($this->post) && ! empty($this->post['csrf_token']) && $this->token->validateCSRFToken($this->post['csrf_token'])) { + unset($this->post['csrf_token']); + return $this->filterValues($this->post); + } + + return array(); + } + + /** + * Get POST values without modification + * + * @return array + */ + public function getRawFormValues() + { + return $this->post; + } + + /** + * Get POST value without modification + * + * @param $name + * @return mixed|null + */ + public function getRawValue($name) + { + return isset($this->post[$name]) ? $this->post[$name] : null; + } + + /** + * Get the raw body of the HTTP request + * + * @access public + * @return string + */ + public function getBody() + { + return file_get_contents('php://input'); + } + + /** + * Get the Json request body + * + * @access public + * @param bool $enforceContentType + * @return array + */ + public function getJson($enforceContentType = true) + { + if ($enforceContentType && ! $this->isJsonContentType()) { + return array(); + } + + return json_decode($this->getBody(), true) ?: array(); + } + + /** + * Get the content of an uploaded file + * + * @access public + * @param string $name Form file name + * @return string + */ + public function getFileContent($name) + { + if (isset($this->files[$name]['tmp_name'])) { + return file_get_contents($this->files[$name]['tmp_name']); + } + + return ''; + } + + /** + * Get the path of an uploaded file + * + * @access public + * @param string $name Form file name + * @return string + */ + public function getFilePath($name) + { + return isset($this->files[$name]['tmp_name']) ? $this->files[$name]['tmp_name'] : ''; + } + + /** + * Get info of an uploaded file + * + * @access public + * @param string $name Form file name + * @return array + */ + public function getFileInfo($name) + { + return isset($this->files[$name]) ? $this->files[$name] : array(); + } + + /** + * Return HTTP method + * + * @access public + * @return bool + */ + public function getMethod() + { + return $this->getServerVariable('REQUEST_METHOD'); + } + + /** + * Return true if the HTTP request is sent with the POST method + * + * @access public + * @return bool + */ + public function isPost() + { + return $this->getServerVariable('REQUEST_METHOD') === 'POST'; + } + + /** + * Return true if the HTTP request is an Ajax request + * + * @access public + * @return bool + */ + public function isAjax() + { + return $this->getHeader('X-Requested-With') === 'XMLHttpRequest'; + } + + /** + * Check if the request Content-Type is JSON + * + * @access public + * @return bool + */ + public function isJsonContentType() + { + $contentType = $this->getServerVariable('CONTENT_TYPE'); + + if ($contentType === '') { + $contentType = $this->getServerVariable('HTTP_CONTENT_TYPE'); + } + + return stripos($contentType, 'application/json') === 0; + } + + /** + * Check if the page is requested through HTTPS + * + * Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS + * + * @access public + * @return boolean + */ + public function isHTTPS() + { + if ($this->getServerVariable('HTTP_X_FORWARDED_PROTO') === 'https') { + return true; + } + + return $this->getServerVariable('HTTPS') !== '' && $this->server['HTTPS'] !== 'off'; + } + + /** + * Get cookie value + * + * @access public + * @param string $name + * @return string + */ + public function getCookie($name) + { + return isset($this->cookies[$name]) ? $this->cookies[$name] : ''; + } + + /** + * Return a HTTP header value + * + * @access public + * @param string $name Header name + * @return string + */ + public function getHeader($name) + { + $name = 'HTTP_'.str_replace('-', '_', strtoupper($name)); + return $this->getServerVariable($name); + } + + /** + * Get remote user + * + * @access public + * @param array $trustedProxyNetworks + * @return string + */ + public function getRemoteUser(array $trustedProxyNetworks = []) + { + if (! $this->isTrustedProxy($trustedProxyNetworks)) { + return ''; + } + return $this->getServerVariable(REVERSE_PROXY_USER_HEADER); + } + + /** + * Get remote email + * + * @access public + * @param array $trustedProxyNetworks + * @return string + */ + public function getRemoteEmail(array $trustedProxyNetworks = []) + { + if (! $this->isTrustedProxy($trustedProxyNetworks)) { + return ''; + } + return $this->getServerVariable(REVERSE_PROXY_EMAIL_HEADER); + } + + /** + * Get remote user full name + * + * @access public + * @param array $trustedProxyNetworks + * @return string + */ + public function getRemoteName(array $trustedProxyNetworks = []) + { + if (! $this->isTrustedProxy($trustedProxyNetworks)) { + return ''; + } + return $this->getServerVariable(REVERSE_PROXY_FULLNAME_HEADER); + } + + /** + * Returns query string + * + * @access public + * @return string + */ + public function getQueryString() + { + return $this->getServerVariable('QUERY_STRING'); + } + + /** + * Return URI + * + * @access public + * @return string + */ + public function getUri() + { + return $this->getServerVariable('REQUEST_URI'); + } + + /** + * Check if a redirect URI is safe (relative path) + * + * @access public + * @param string $uri Redirect URI + * @return bool + */ + public function isSafeRedirectUri($uri) + { + $uri = trim($uri); + if ($uri === '') { + return false; + } + + // Reject backslashes + if (str_contains($uri, '\\')) { + return false; + } + + // Reject if it starts with // (protocol-relative) + if (str_starts_with($uri, '//')) { + return false; + } + + // Reject if it does not start with a slash (relative path) + if (! str_starts_with($uri, '/')) { + return false; + } + + $parsedUrl = parse_url($uri); + if ($parsedUrl === false) { + return false; + } + + // Reject if it contains a scheme or host (partial or full URL) + if (isset($parsedUrl['scheme']) || isset($parsedUrl['host'])) { + return false; + } + + return true; + } + + /** + * Get the user agent + * + * @access public + * @return string + */ + public function getUserAgent() + { + return empty($this->server['HTTP_USER_AGENT']) ? t('Unknown') : $this->server['HTTP_USER_AGENT']; + } + + /** + * Get the client IP address + * + * It returns the proxy IP address if the request is sent through a reverse proxy or the direct client IP address otherwise. + * + * @access public + * @return string + */ + public function getClientIpAddress() + { + return $this->getServerVariable('REMOTE_ADDR'); + } + + /** + * Get the real user IP address considering trusted proxy headers and networks + * + * @access public + * @param array $trustedProxyHeaders List of trusted proxy headers (default: TRUSTED_PROXY_HEADERS constant) + * @param array $trustedProxyNetworks List of trusted proxy networks (default: TRUSTED_PROXY_NETWORKS constant) + * @return string + */ + public function getIpAddress(array $trustedProxyHeaders = [], array $trustedProxyNetworks = []) + { + $trustedProxyHeaders = array_filter(array_map('trim', $trustedProxyHeaders ?: explode(',', TRUSTED_PROXY_HEADERS))); + $useProxyHeaders = ! empty($trustedProxyHeaders) && $this->isTrustedProxy($trustedProxyNetworks); + $keys = $useProxyHeaders ? $trustedProxyHeaders : []; + + foreach ($keys as $key) { + if ($this->getServerVariable($key) !== '') { + foreach (explode(',', $this->server[$key]) as $ipAddress) { + $ipAddress = trim($ipAddress); + if (filter_var($ipAddress, FILTER_VALIDATE_IP)) { + return $ipAddress; + } + } + } + } + + return $this->getClientIpAddress(); + } + + /** + * Get start time + * + * @access public + * @return float + */ + public function getStartTime() + { + return $this->getServerVariable('REQUEST_TIME_FLOAT') ?: 0; + } + + /** + * Get server variable + * + * @access public + * @param string $variable + * @return string + */ + public function getServerVariable($variable) + { + 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; + } + + /** + * Check if an IP address belongs to a trusted proxy network + * + * @access public + * @param array $trustedProxyNetworks + * @return bool + */ + public function isTrustedProxy(array $trustedProxyNetworks = []) + { + $ipAddress = $this->getClientIpAddress(); + if ($ipAddress === '') { + return false; + } + + $trustedProxyNetworks = array_filter(array_map('trim', $trustedProxyNetworks ?: explode(',', TRUSTED_PROXY_NETWORKS))); + if (empty($trustedProxyNetworks)) { + return false; + } + + $this->logger->debug('Checking if IP address {ip} belongs to trusted proxy networks: {networks}', ['ip' => $ipAddress, 'networks' => implode(', ', $trustedProxyNetworks)]); + + return $this->isIpInNetworks($ipAddress, $trustedProxyNetworks); + } + + /** + * Check if an IP belongs to any of the provided networks + * + * @access protected + * @param string $ipAddress + * @param array $networks + * @return bool + */ + protected function isIpInNetworks($ipAddress, array $networks) + { + if (! filter_var($ipAddress, FILTER_VALIDATE_IP)) { + return false; + } + + $ipBinary = inet_pton($ipAddress); + + foreach ($networks as $network) { + if ($network === '') { + continue; + } + + $mask = null; + if (strpos($network, '/') !== false) { + list($networkAddress, $mask) = explode('/', $network, 2); + } else { + $networkAddress = $network; + } + + if (! filter_var($networkAddress, FILTER_VALIDATE_IP)) { + continue; + } + + $networkBinary = inet_pton($networkAddress); + + if ($networkBinary === false || strlen($networkBinary) !== strlen($ipBinary)) { + continue; + } + + $maxMask = strlen($networkBinary) * 8; + $mask = ($mask === null || $mask === '') ? $maxMask : max(0, min((int) $mask, $maxMask)); + + if ($this->ipMatchesNetwork($ipBinary, $networkBinary, $mask)) { + return true; + } + } + + return false; + } + + /** + * Perform a binary comparison between an IP and a network mask + * + * @access protected + * @param string $ipBinary + * @param string $networkBinary + * @param int $mask + * @return bool + */ + protected function ipMatchesNetwork($ipBinary, $networkBinary, $mask) + { + if ($mask === 0) { + return true; + } + + $bytes = (int) floor($mask / 8); + $bits = $mask % 8; + + if ($bytes > 0 && strncmp($ipBinary, $networkBinary, $bytes) !== 0) { + return false; + } + + if ($bits === 0) { + return true; + } + + $maskByte = ~((1 << (8 - $bits)) - 1) & 0xFF; + $ipByte = ord($ipBinary[$bytes]); + $networkByte = ord($networkBinary[$bytes]); + + return ($ipByte & $maskByte) === ($networkByte & $maskByte); + } +} diff --git a/app/Core/Http/Response.php b/app/Core/Http/Response.php new file mode 100644 index 0000000..c4323bf --- /dev/null +++ b/app/Core/Http/Response.php @@ -0,0 +1,419 @@ +responseSent; + } + + /** + * Set HTTP status code + * + * @access public + * @param integer $statusCode + * @return $this + */ + public function withStatusCode($statusCode) + { + $this->httpStatusCode = $statusCode; + return $this; + } + + /** + * Set HTTP header + * + * @access public + * @param string $header + * @param string $value + * @return $this + */ + public function withHeader($header, $value) + { + $this->httpHeaders[$header] = $value; + return $this; + } + + /** + * Set content type header + * + * @access public + * @param string $value + * @return $this + */ + public function withContentType($value) + { + $this->httpHeaders['Content-Type'] = $value; + return $this; + } + + /** + * Set default security headers + * + * @access public + * @return $this + */ + public function withSecurityHeaders() + { + $this->httpHeaders['X-Content-Type-Options'] = 'nosniff'; + $this->httpHeaders['X-XSS-Protection'] = '1; mode=block'; + return $this; + } + + /** + * Set header Content-Security-Policy + * + * @access public + * @param array $policies + * @return $this + */ + public function withContentSecurityPolicy(array $policies = array()) + { + $values = ''; + + foreach ($policies as $policy => $acl) { + $values .= $policy.' '.trim($acl).'; '; + } + + $this->withHeader('Content-Security-Policy', $values); + return $this; + } + + /** + * Set header X-Frame-Options + * + * @access public + * @return $this + */ + public function withXframe() + { + $this->withHeader('X-Frame-Options', 'DENY'); + return $this; + } + + /** + * Set header Strict-Transport-Security (only if we use HTTPS) + * + * @access public + * @return $this + */ + public function withStrictTransportSecurity() + { + if ($this->request->isHTTPS()) { + $this->withHeader('Strict-Transport-Security', 'max-age=31536000'); + } + + return $this; + } + + /** + * 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 + * @param string $body + * @return $this + */ + public function withBody($body) + { + $this->httpBody = $body; + return $this; + } + + /** + * Send headers to cache a resource + * + * @access public + * @param integer $duration + * @param string $etag + * @return $this + */ + public function withCache($duration, $etag = '') + { + $this + ->withHeader('Pragma', 'cache') + ->withHeader('Expires', gmdate('D, d M Y H:i:s', time() + $duration) . ' GMT') + ->withHeader('Cache-Control', 'public, max-age=' . $duration) + ; + + if ($etag) { + $this->withHeader('ETag', '"' . $etag . '"'); + } + + return $this; + } + + /** + * Send no cache headers + * + * @access public + * @return $this + */ + public function withoutCache() + { + $this->withHeader('Pragma', 'no-cache'); + $this->withHeader('Expires', 'Sat, 26 Jul 1997 05:00:00 GMT'); + return $this; + } + + /** + * Force the browser to download an attachment + * + * @access public + * @param string $filename + * @return $this + */ + public function withFileDownload($filename) + { + $this->withHeader('Content-Disposition', 'attachment; filename="'.$filename.'"'); + $this->withHeader('Content-Transfer-Encoding', 'binary'); + $this->withHeader('Content-Type', 'application/octet-stream'); + return $this; + } + + /** + * Send headers and body + * + * @access public + */ + public function send() + { + $this->responseSent = true; + + if ($this->httpStatusCode !== 200) { + header('Status: '.$this->httpStatusCode); + header($this->request->getServerVariable('SERVER_PROTOCOL').' '.$this->httpStatusCode); + } + + foreach ($this->httpHeaders as $header => $value) { + header($header.': '.$value); + } + + if (! empty($this->httpBody)) { + echo $this->httpBody; + } + } + + /** + * Send a custom HTTP status code + * + * @access public + * @param integer $statusCode + */ + public function status($statusCode) + { + $this->withStatusCode($statusCode); + $this->send(); + } + + /** + * Redirect to another URL + * + * @access public + * @param string $url Redirection URL + * @param boolean $self If Ajax request and true: refresh the current page + */ + public function redirect($url, $self = false) + { + if ($this->request->isAjax()) { + $this->withHeader('X-Ajax-Redirect', $self ? 'self' : $url); + } else { + $this->withHeader('Location', $url); + } + + $this->send(); + } + + /** + * Send a HTML response + * + * @access public + * @param string $data + * @param integer $statusCode + */ + public function html($data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withContentType('text/html; charset=utf-8'); + $this->withBody($data); + $this->send(); + } + + /** + * Send a text response + * + * @access public + * @param string $data + * @param integer $statusCode + */ + public function text($data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withContentType('text/plain; charset=utf-8'); + $this->withBody($data); + $this->send(); + } + + /** + * Send a CSV response + * + * @access public + * @param array $data Data to serialize in csv + * @param bool $addBOM Add BOM header + */ + public function csv(array $data, $addBOM = false) + { + $this->withoutCache(); + $this->withContentType('text/csv; charset=utf-8'); + $this->send(); + Csv::output($data, $addBOM); + } + + /** + * Send a Json response + * + * @access public + * @param array $data Data to serialize in json + * @param integer $statusCode HTTP status code + */ + public function json(array $data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withContentType('application/json'); + $this->withoutCache(); + $this->withBody(json_encode($data)); + $this->send(); + } + + /** + * Send a XML response + * + * @access public + * @param string $data + * @param integer $statusCode + */ + public function xml($data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withContentType('text/xml; charset=utf-8'); + $this->withoutCache(); + $this->withBody($data); + $this->send(); + } + + /** + * Send a javascript response + * + * @access public + * @param string $data + * @param integer $statusCode + */ + public function js($data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withContentType('text/javascript; charset=utf-8'); + $this->withBody($data); + $this->send(); + } + + /** + * Send a css response + * + * @access public + * @param string $data + * @param integer $statusCode + */ + public function css($data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withContentType('text/css; charset=utf-8'); + $this->withBody($data); + $this->send(); + } + + /** + * Send a binary response + * + * @access public + * @param string $data + * @param integer $statusCode + */ + public function binary($data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withoutCache(); + $this->withHeader('Content-Transfer-Encoding', 'binary'); + $this->withContentType('application/octet-stream'); + $this->withBody($data); + $this->send(); + } + + /** + * Send a iCal response + * + * @access public + * @param string $data + * @param integer $statusCode + */ + public function ical($data, $statusCode = 200) + { + $this->withStatusCode($statusCode); + $this->withContentType('text/calendar; charset=utf-8'); + $this->withBody($data); + $this->send(); + } + + /** + * Send a PDF response + * + * @access public + * @param string $data + * @param integer $statusCode + * @param string $fileName + */ + public function pdf($data, int $statusCode = 200, string $fileName = "") + { + $this->withStatusCode($statusCode); + $this->withContentType('application/pdf'); + + if (!empty($fileName)) { + $this->httpHeaders["content-disposition"] = "attachment; filename=".$fileName; + } + + $this->withBody($data); + $this->send(); + } +} diff --git a/app/Core/Http/Route.php b/app/Core/Http/Route.php new file mode 100644 index 0000000..c28087d --- /dev/null +++ b/app/Core/Http/Route.php @@ -0,0 +1,188 @@ +activated = true; + return $this; + } + + /** + * Add route + * + * @access public + * @param string $path + * @param string $controller + * @param string $action + * @param string $plugin + * @return Route + */ + public function addRoute($path, $controller, $action, $plugin = '') + { + if ($this->activated) { + $path = ltrim($path, '/'); + $items = explode('/', $path); + $params = $this->findParams($items); + + $this->paths[] = array( + 'items' => $items, + 'count' => count($items), + 'controller' => $controller, + 'action' => $action, + 'plugin' => $plugin, + ); + + $this->urls[$plugin][$controller][$action][] = array( + 'path' => $path, + 'params' => $params, + 'count' => count($params), + ); + } + + return $this; + } + + /** + * Find a route according to the given path + * + * @access public + * @param string $path + * @return array + */ + public function findRoute($path) + { + $items = explode('/', ltrim($path, '/')); + $count = count($items); + + foreach ($this->paths as $route) { + if ($count === $route['count']) { + $params = array(); + + for ($i = 0; $i < $count; $i++) { + if ($route['items'][$i][0] === ':') { + $params[substr($route['items'][$i], 1)] = urldecode($items[$i]); + } elseif ($route['items'][$i] !== $items[$i]) { + break; + } + } + + if ($i === $count) { + $this->request->setParams($params); + return array( + 'controller' => $route['controller'], + 'action' => $route['action'], + 'plugin' => $route['plugin'], + ); + } + } + } + + return array( + 'controller' => 'DashboardController', + 'action' => 'show', + 'plugin' => '', + ); + } + + /** + * Find route url + * + * @access public + * @param string $controller + * @param string $action + * @param array $params + * @param string $plugin + * @return string + */ + public function findUrl($controller, $action, array $params = array(), $plugin = '') + { + if ($plugin === '' && isset($params['plugin'])) { + $plugin = $params['plugin']; + unset($params['plugin']); + } + + if (! isset($this->urls[$plugin][$controller][$action])) { + return ''; + } + + foreach ($this->urls[$plugin][$controller][$action] as $route) { + if (array_diff_key($params, $route['params']) === array()) { + $url = $route['path']; + $i = 0; + + foreach ($params as $variable => $value) { + $value = urlencode($value); + $url = str_replace(':'.$variable, $value, $url); + $i++; + } + + if ($i === $route['count']) { + return $url; + } + } + } + + return ''; + } + + /** + * Find url params + * + * @access public + * @param array $items + * @return array + */ + public function findParams(array $items) + { + $params = array(); + + foreach ($items as $item) { + if ($item !== '' && $item[0] === ':') { + $params[substr($item, 1)] = true; + } + } + + return $params; + } +} diff --git a/app/Core/Http/Router.php b/app/Core/Http/Router.php new file mode 100644 index 0000000..530b46a --- /dev/null +++ b/app/Core/Http/Router.php @@ -0,0 +1,131 @@ +currentPluginName; + } + + /** + * Get controller + * + * @access public + * @return string + */ + public function getController() + { + return $this->currentControllerName; + } + + /** + * Get action + * + * @access public + * @return string + */ + public function getAction() + { + return $this->currentActionName; + } + + /** + * Get the path to compare patterns + * + * @access public + * @return string + */ + public function getPath() + { + $path = substr($this->request->getUri(), strlen($this->helper->url->dir())); + + if ($this->request->getQueryString() !== '') { + $path = substr($path, 0, - strlen($this->request->getQueryString()) - 1); + } + + if ($path !== '' && $path[0] === '/') { + $path = substr($path, 1); + } + + return $path; + } + + /** + * Find controller/action from the route table or from get arguments + * + * @access public + */ + public function dispatch() + { + $controller = $this->request->getStringParam('controller'); + $action = $this->request->getStringParam('action'); + $plugin = $this->request->getStringParam('plugin'); + + if ($controller === '') { + $route = $this->route->findRoute($this->getPath()); + $controller = $route['controller']; + $action = $route['action']; + $plugin = $route['plugin']; + } + + $this->currentControllerName = ucfirst($this->sanitize($controller, self::DEFAULT_CONTROLLER)); + $this->currentActionName = $this->sanitize($action, self::DEFAULT_METHOD); + $this->currentPluginName = ucfirst($this->sanitize($plugin)); + } + + /** + * Check controller and action parameter + * + * @access public + * @param string $value + * @param string $default + * @return string + */ + public function sanitize($value, $default = '') + { + return preg_match('/^[a-zA-Z_0-9]+$/', $value) ? $value : $default; + } +} diff --git a/app/Core/Ldap/Client.php b/app/Core/Ldap/Client.php new file mode 100644 index 0000000..e60b21b --- /dev/null +++ b/app/Core/Ldap/Client.php @@ -0,0 +1,247 @@ +open($client->getLdapServer()); + $username = $username ?: $client->getLdapUsername(); + $password = $password ?: $client->getLdapPassword(); + + if (empty($username) && empty($password)) { + $client->useAnonymousAuthentication(); + } else { + $client->authenticate($username, $password); + } + + return $client; + } + + /** + * Get server connection + * + * @access public + * @return resource + */ + public function getConnection() + { + return $this->ldap; + } + + /** + * Establish server connection + * + * @access public + * + * @param string $server LDAP server URI (ldap[s]://hostname:port) or hostname (deprecated) + * @param int $port LDAP port (deprecated) + * @param bool $tls Start TLS + * @param bool $verify Skip SSL certificate verification + * @return Client + * @throws ClientException + * @throws ConnectionException + */ + public function open($server, $port = LDAP_PORT, $tls = LDAP_START_TLS, $verify = LDAP_SSL_VERIFY) + { + if (! function_exists('ldap_connect')) { + throw new ClientException('LDAP: The PHP LDAP extension is required'); + } + + if (! $verify) { + putenv('LDAPTLS_REQCERT=never'); + } + + if (filter_var($server, FILTER_VALIDATE_URL) !== false) { + $this->ldap = @ldap_connect($server); + } else { + $this->ldap = @ldap_connect($server, $port); + } + + if ($this->ldap === false) { + throw new ConnectionException('Malformed LDAP server hostname or LDAP server port'); + } + + ldap_set_option($this->ldap, LDAP_OPT_PROTOCOL_VERSION, 3); + ldap_set_option($this->ldap, LDAP_OPT_REFERRALS, 0); + ldap_set_option($this->ldap, LDAP_OPT_NETWORK_TIMEOUT, 1); + ldap_set_option($this->ldap, LDAP_OPT_TIMELIMIT, 1); + + if ($tls && ! @ldap_start_tls($this->ldap)) { + throw new ConnectionException('Unable to start LDAP TLS (' . $this->getLdapError() . ')'); + } + + return $this; + } + + /** + * Anonymous authentication + * + * @access public + * @throws ClientException + * @return boolean + */ + public function useAnonymousAuthentication() + { + if (! @ldap_bind($this->ldap)) { + $this->checkForServerConnectionError(); + throw new ClientException('Unable to perform anonymous binding => '.$this->getLdapError()); + } + + return true; + } + + /** + * Authentication with username/password + * + * @access public + * @throws ClientException + * @param string $bind_rdn + * @param string $bind_password + * @return boolean + */ + public function authenticate($bind_rdn, $bind_password) + { + if (! @ldap_bind($this->ldap, $bind_rdn, $bind_password)) { + $this->checkForServerConnectionError(); + throw new ClientException('LDAP authentication failure for "'.$bind_rdn.'" => '.$this->getLdapError()); + } + + return true; + } + + /** + * Get LDAP server name + * + * @access public + * @return string + */ + public function getLdapServer() + { + if (! LDAP_SERVER) { + throw new LogicException('LDAP server not configured, check the parameter LDAP_SERVER'); + } + + return LDAP_SERVER; + } + + /** + * Get LDAP username (proxy auth) + * + * @access public + * @return string + */ + public function getLdapUsername() + { + return LDAP_USERNAME; + } + + /** + * Get LDAP password (proxy auth) + * + * @access public + * @return string + */ + public function getLdapPassword() + { + return LDAP_PASSWORD; + } + + /** + * Set logger + * + * @access public + * @param LoggerInterface $logger + * @return Client + */ + public function setLogger(LoggerInterface $logger) + { + $this->logger = $logger; + return $this; + } + + /** + * Get logger + * + * @access public + * @return LoggerInterface + */ + public function getLogger() + { + return $this->logger; + } + + /** + * Test if a logger is defined + * + * @access public + * @return boolean + */ + public function hasLogger() + { + return $this->logger !== null; + } + + /** + * Raise ConnectionException if the application is not able to connect to LDAP server + * + * @access protected + * @throws ConnectionException + */ + protected function checkForServerConnectionError() + { + if (ldap_errno($this->ldap) === -1) { + throw new ConnectionException('Unable to connect to LDAP server (' . $this->getLdapError() . ')'); + } + } + + /** + * Get extended LDAP error message + * + * @return string + */ + protected function getLdapError() + { + ldap_get_option($this->ldap, LDAP_OPT_ERROR_STRING, $extendedErrorMessage); + $errorMessage = ldap_error($this->ldap); + $errorCode = ldap_errno($this->ldap); + + return 'Code="'.$errorCode.'"; Error="'.$errorMessage.'"; ExtendedError="'.$extendedErrorMessage.'"'; + } +} diff --git a/app/Core/Ldap/ClientException.php b/app/Core/Ldap/ClientException.php new file mode 100644 index 0000000..a0f9f84 --- /dev/null +++ b/app/Core/Ldap/ClientException.php @@ -0,0 +1,15 @@ +entries = $entries; + } + + /** + * Get all entries + * + * @access public + * @return Entry[] + */ + public function getAll() + { + $entities = array(); + + if (! isset($this->entries['count'])) { + return $entities; + } + + for ($i = 0; $i < $this->entries['count']; $i++) { + $entities[] = new Entry($this->entries[$i]); + } + + return $entities; + } + + /** + * Get first entry + * + * @access public + * @return Entry + */ + public function getFirstEntry() + { + return new Entry(isset($this->entries[0]) ? $this->entries[0] : array()); + } +} diff --git a/app/Core/Ldap/Entry.php b/app/Core/Ldap/Entry.php new file mode 100644 index 0000000..87a828e --- /dev/null +++ b/app/Core/Ldap/Entry.php @@ -0,0 +1,99 @@ +entry = $entry; + } + + /** + * Get all attribute values + * + * @access public + * @param string $attribute + * @return string[] + */ + public function getAll($attribute) + { + $attributes = array(); + + if ($attribute === null) { + return $attributes; + } + + if (! isset($this->entry[$attribute]['count'])) { + return $attributes; + } + + for ($i = 0; $i < $this->entry[$attribute]['count']; $i++) { + $attributes[] = $this->entry[$attribute][$i]; + } + + return $attributes; + } + + /** + * Get first attribute value + * + * @access public + * @param string $attribute + * @param string $default + * @return string + */ + public function getFirstValue($attribute, $default = '') + { + if ($attribute === null) { + return $default; + } + + return isset($this->entry[$attribute][0]) ? $this->entry[$attribute][0] : $default; + } + + /** + * Get entry distinguished name + * + * @access public + * @return string + */ + public function getDn() + { + return isset($this->entry['dn']) ? $this->entry['dn'] : ''; + } + + /** + * Return true if the given value exists in attribute list + * + * @access public + * @param string $attribute + * @param string $value + * @return boolean + */ + public function hasValue($attribute, $value) + { + $attributes = $this->getAll($attribute); + return in_array($value, $attributes); + } +} diff --git a/app/Core/Ldap/Group.php b/app/Core/Ldap/Group.php new file mode 100644 index 0000000..8b1d5fe --- /dev/null +++ b/app/Core/Ldap/Group.php @@ -0,0 +1,130 @@ +query = $query; + } + + /** + * Get groups + * + * @static + * @access public + * @param Client $client + * @param string $query + * @return LdapGroupProvider[] + */ + public static function getGroups(Client $client, $query) + { + $self = new static(new Query($client)); + return $self->find($query); + } + + /** + * Find groups + * + * @access public + * @param string $query + * @return array + */ + public function find($query) + { + $this->query->execute($this->getBaseDn(), $query, $this->getAttributes()); + $groups = array(); + + if ($this->query->hasResult()) { + $groups = $this->build(); + } + + return $groups; + } + + /** + * Build groups list + * + * @access protected + * @return array + */ + protected function build() + { + $groups = array(); + + foreach ($this->query->getEntries()->getAll() as $entry) { + $groups[] = new LdapGroupProvider($entry->getDn(), $entry->getFirstValue($this->getAttributeName())); + } + + return $groups; + } + + /** + * Ge the list of attributes to fetch when reading the LDAP group entry + * + * Must returns array with index that start at 0 otherwise ldap_search returns a warning "Array initialization wrong" + * + * @access public + * @return array + */ + public function getAttributes() + { + return array_values(array_filter(array( + $this->getAttributeName(), + ))); + } + + /** + * Get LDAP group name attribute + * + * @access public + * @return string + */ + public function getAttributeName() + { + if (! LDAP_GROUP_ATTRIBUTE_NAME) { + throw new LogicException('LDAP full name attribute empty, check the parameter LDAP_GROUP_ATTRIBUTE_NAME'); + } + + return strtolower(LDAP_GROUP_ATTRIBUTE_NAME); + } + + /** + * Get LDAP group base DN + * + * @access public + * @return string + */ + public function getBaseDn() + { + if (! LDAP_GROUP_BASE_DN) { + throw new LogicException('LDAP group base DN empty, check the parameter LDAP_GROUP_BASE_DN'); + } + + return LDAP_GROUP_BASE_DN; + } +} diff --git a/app/Core/Ldap/Query.php b/app/Core/Ldap/Query.php new file mode 100644 index 0000000..0fe7f2d --- /dev/null +++ b/app/Core/Ldap/Query.php @@ -0,0 +1,98 @@ +client = $client; + } + + /** + * Execute query + * + * @access public + * @param string $baseDn + * @param string $filter + * @param array $attributes + * @param integer $limit + * @return $this + */ + public function execute($baseDn, $filter, array $attributes, $limit = 0) + { + if (DEBUG && $this->client->hasLogger()) { + $this->client->getLogger()->debug('BaseDN='.$baseDn); + $this->client->getLogger()->debug('Filter='.$filter); + $this->client->getLogger()->debug('Attributes='.implode(', ', $attributes)); + } + + $sr = @ldap_search($this->client->getConnection(), $baseDn, $filter, $attributes, null, $limit); + if ($sr === false) { + return $this; + } + + $entries = ldap_get_entries($this->client->getConnection(), $sr); + if ($entries === false || count($entries) === 0 || $entries['count'] == 0) { + return $this; + } + + $this->entries = $entries; + + if (DEBUG && $this->client->hasLogger()) { + $this->client->getLogger()->debug('NbEntries='.$entries['count']); + } + + return $this; + } + + /** + * Return true if the query returned a result + * + * @access public + * @return boolean + */ + public function hasResult() + { + return ! empty($this->entries); + } + + /** + * Get LDAP Entries + * + * @access public + * @return Entries + */ + public function getEntries() + { + return new Entries($this->entries); + } +} diff --git a/app/Core/Ldap/User.php b/app/Core/Ldap/User.php new file mode 100644 index 0000000..bc1c8b1 --- /dev/null +++ b/app/Core/Ldap/User.php @@ -0,0 +1,366 @@ +query = $query; + $this->group = $group; + } + + /** + * Get user profile + * + * @static + * @access public + * @param Client $client + * @param string $username + * @return LdapUserProvider + */ + public static function getUser(Client $client, $username) + { + $self = new static(new Query($client), new Group(new Query($client))); + return $self->find($self->getLdapUserPattern($username)); + } + + /** + * Find user + * + * @access public + * @param string $query + * @return LdapUserProvider + */ + public function find($query) + { + $this->query->execute($this->getBaseDn(), $query, $this->getAttributes()); + $user = null; + + if ($this->query->hasResult()) { + $user = $this->build(); + } + + return $user; + } + + /** + * Get user groupIds (DN) + * + * 1) If configured, use memberUid and posixGroup + * 2) Otherwise, use memberOf + * + * @access protected + * @param Entry $entry + * @return string[] + */ + protected function getGroups(Entry $entry) + { + $userattr = ''; + if ('username' == $this->getGroupUserAttribute()) { + $userattr = $entry->getFirstValue($this->getAttributeUsername()); + } elseif ('dn' == $this->getGroupUserAttribute()) { + $userattr = $entry->getDn(); + } + $groupIds = array(); + + if (! empty($userattr) && $this->group !== null && $this->hasGroupUserFilter()) { + $escapedUserAttribute = ldap_escape($userattr, '', LDAP_ESCAPE_FILTER); + $groups = $this->group->find(sprintf($this->getGroupUserFilter(), $escapedUserAttribute)); + + foreach ($groups as $group) { + $groupIds[] = $group->getExternalId(); + } + } else { + $groupIds = $entry->getAll($this->getAttributeGroup()); + } + + return $groupIds; + } + + /** + * Get role from LDAP groups + * + * Note: Do not touch the current role if groups are not configured + * + * @access protected + * @param string[] $groupIds + * @return string + */ + protected function getRole(array $groupIds) + { + if (! $this->hasGroupsConfigured()) { + return null; + } + + if (LDAP_USER_DEFAULT_ROLE_MANAGER) { + $role = Role::APP_MANAGER; + } else { + $role = Role::APP_USER; + } + + foreach ($groupIds as $groupId) { + $groupId = strtolower($groupId); + + if ($groupId === strtolower($this->getGroupAdminDn())) { + $role = Role::APP_ADMIN; + break; + } + + if ($groupId === strtolower($this->getGroupManagerDn())) { + $role = Role::APP_MANAGER; + } + } + + return $role; + } + + /** + * Build user profile + * + * @access protected + * @return LdapUserProvider + */ + protected function build() + { + $entry = $this->query->getEntries()->getFirstEntry(); + $groupIds = $this->getGroups($entry); + + return new LdapUserProvider( + $entry->getDn(), + $entry->getFirstValue($this->getAttributeUsername()), + $entry->getFirstValue($this->getAttributeName()), + $entry->getFirstValue($this->getAttributeEmail()), + $this->getRole($groupIds), + $groupIds, + $entry->getFirstValue($this->getAttributePhoto()), + $entry->getFirstValue($this->getAttributeLanguage()) + ); + } + + /** + * Ge the list of attributes to fetch when reading the LDAP user entry + * + * Must returns array with index that start at 0 otherwise ldap_search returns a warning "Array initialization wrong" + * + * @access public + * @return array + */ + public function getAttributes() + { + return array_values(array_filter(array( + $this->getAttributeUsername(), + $this->getAttributeName(), + $this->getAttributeEmail(), + $this->getAttributeGroup(), + $this->getAttributePhoto(), + $this->getAttributeLanguage(), + ))); + } + + /** + * Get LDAP account id attribute + * + * @access public + * @return string + */ + public function getAttributeUsername() + { + if (! LDAP_USER_ATTRIBUTE_USERNAME) { + throw new LogicException('LDAP username attribute empty, check the parameter LDAP_USER_ATTRIBUTE_USERNAME'); + } + + return strtolower(LDAP_USER_ATTRIBUTE_USERNAME); + } + + /** + * Get LDAP user name attribute + * + * @access public + * @return string + */ + public function getAttributeName() + { + if (! LDAP_USER_ATTRIBUTE_FULLNAME) { + throw new LogicException('LDAP full name attribute empty, check the parameter LDAP_USER_ATTRIBUTE_FULLNAME'); + } + + return strtolower(LDAP_USER_ATTRIBUTE_FULLNAME); + } + + /** + * Get LDAP account email attribute + * + * @access public + * @return string + */ + public function getAttributeEmail() + { + if (! LDAP_USER_ATTRIBUTE_EMAIL) { + throw new LogicException('LDAP email attribute empty, check the parameter LDAP_USER_ATTRIBUTE_EMAIL'); + } + + return strtolower(LDAP_USER_ATTRIBUTE_EMAIL); + } + + /** + * Get LDAP account memberOf attribute + * + * @access public + * @return string + */ + public function getAttributeGroup() + { + return strtolower(LDAP_USER_ATTRIBUTE_GROUPS); + } + + /** + * Get LDAP profile photo attribute + * + * @access public + * @return string + */ + public function getAttributePhoto() + { + return strtolower(LDAP_USER_ATTRIBUTE_PHOTO); + } + + /** + * Get LDAP language attribute + * + * @access public + * @return string + */ + public function getAttributeLanguage() + { + return strtolower(LDAP_USER_ATTRIBUTE_LANGUAGE); + } + + /** + * Get LDAP Group User filter + * + * @access public + * @return string + */ + public function getGroupUserFilter() + { + return LDAP_GROUP_USER_FILTER; + } + + /** + * Get LDAP Group User attribute + * + * @access public + * @return string + */ + public function getGroupUserAttribute() + { + return LDAP_GROUP_USER_ATTRIBUTE; + } + + /** + * Return true if LDAP Group User filter is defined + * + * @access public + * @return string + */ + public function hasGroupUserFilter() + { + return $this->getGroupUserFilter() !== '' && $this->getGroupUserFilter() !== null; + } + + /** + * Return true if LDAP Group mapping are configured + * + * @access public + * @return boolean + */ + public function hasGroupsConfigured() + { + return $this->getGroupAdminDn() || $this->getGroupManagerDn(); + } + + /** + * Get LDAP admin group DN + * + * @access public + * @return string + */ + public function getGroupAdminDn(): string + { + return strtolower(LDAP_GROUP_ADMIN_DN); + } + + /** + * Get LDAP application manager group DN + * + * @access public + * @return string + */ + public function getGroupManagerDn(): string + { + return LDAP_GROUP_MANAGER_DN; + } + + /** + * Get LDAP user base DN + * + * @access public + * @return string + */ + public function getBaseDn() + { + return LDAP_USER_BASE_DN; + } + + /** + * Get LDAP user pattern + * + * @access public + * @param string $username + * @param string $filter + * @return string + */ + public function getLdapUserPattern($username, $filter = LDAP_USER_FILTER) + { + if (! $filter) { + throw new LogicException('LDAP user filter is not configured. Please set the LDAP_USER_FILTER parameter in your configuration file'); + } + + $escapedUsername = ldap_escape($username, '', LDAP_ESCAPE_FILTER); + return str_replace('%s', $escapedUsername, $filter); + } +} diff --git a/app/Core/Log/Base.php b/app/Core/Log/Base.php new file mode 100644 index 0000000..4bb2699 --- /dev/null +++ b/app/Core/Log/Base.php @@ -0,0 +1,89 @@ +level = $level; + } + + /** + * Get minimum log level + * + * @access public + * @return string + */ + public function getLevel() + { + return $this->level; + } + + /** + * Dump to log a variable (by example an array) + * + * @param mixed $variable + */ + public function dump($variable) + { + $this->log(LogLevel::DEBUG, var_export($variable, true)); + } + + /** + * Interpolates context values into the message placeholders. + * + * @access protected + * @param string $message + * @param array $context + * @return string + */ + protected function interpolate($message, array $context = array()) + { + // build a replacement array with braces around the context keys + $replace = array(); + + foreach ($context as $key => $val) { + $replace['{' . $key . '}'] = $val; + } + + // interpolate replacement values into the message and return + return strtr($message, $replace); + } + + /** + * Format log message + * + * @param mixed $level + * @param string $message + * @param array $context + * @return string + */ + protected function formatMessage($level, $message, array $context = array()) + { + return '['.date('Y-m-d H:i:s').'] ['.$level.'] '.$this->interpolate($message, $context).PHP_EOL; + } +} diff --git a/app/Core/Log/File.php b/app/Core/Log/File.php new file mode 100644 index 0000000..19260b3 --- /dev/null +++ b/app/Core/Log/File.php @@ -0,0 +1,48 @@ +filename = $filename; + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string $message + * @param array $context + */ + public function log($level, $message, array $context = array()) + { + $line = $this->formatMessage($level, $message, $context); + + if (file_put_contents($this->filename, $line, FILE_APPEND | LOCK_EX) === false) { + throw new RuntimeException('Unable to write to the log file.'); + } + } +} diff --git a/app/Core/Log/Logger.php b/app/Core/Log/Logger.php new file mode 100644 index 0000000..4155a04 --- /dev/null +++ b/app/Core/Log/Logger.php @@ -0,0 +1,94 @@ +loggers[] = $logger; + } + + /** + * Proxy method to the real loggers + * + * @param mixed $level + * @param string $message + * @param array $context + * @return null + */ + public function log($level, $message, array $context = array()) + { + foreach ($this->loggers as $logger) { + if ($this->getLevelPriority($level) >= $this->getLevelPriority($logger->getLevel())) { + $logger->log($level, $message, $context); + } + } + } + + /** + * Dump variables for debugging + * + * @param mixed $variable + */ + public function dump($variable) + { + foreach ($this->loggers as $logger) { + if ($this->getLevelPriority(LogLevel::DEBUG) >= $this->getLevelPriority($logger->getLevel())) { + $logger->dump($variable); + } + } + } +} diff --git a/app/Core/Log/Stderr.php b/app/Core/Log/Stderr.php new file mode 100644 index 0000000..fbe82a8 --- /dev/null +++ b/app/Core/Log/Stderr.php @@ -0,0 +1,25 @@ +formatMessage($level, $message, $context), FILE_APPEND); + } +} diff --git a/app/Core/Log/Stdout.php b/app/Core/Log/Stdout.php new file mode 100644 index 0000000..9914080 --- /dev/null +++ b/app/Core/Log/Stdout.php @@ -0,0 +1,25 @@ +formatMessage($level, $message, $context), FILE_APPEND); + } +} diff --git a/app/Core/Log/Syslog.php b/app/Core/Log/Syslog.php new file mode 100644 index 0000000..3a354a8 --- /dev/null +++ b/app/Core/Log/Syslog.php @@ -0,0 +1,72 @@ +getSyslogPriority($level); + $syslogMessage = $this->interpolate($message, $context); + + syslog($syslogPriority, $syslogMessage); + } +} diff --git a/app/Core/Log/System.php b/app/Core/Log/System.php new file mode 100644 index 0000000..ce801a0 --- /dev/null +++ b/app/Core/Log/System.php @@ -0,0 +1,25 @@ +interpolate($message, $context)); + } +} diff --git a/app/Core/Mail/Client.php b/app/Core/Mail/Client.php new file mode 100644 index 0000000..c87e9fe --- /dev/null +++ b/app/Core/Mail/Client.php @@ -0,0 +1,138 @@ +transports = new Container; + } + + /** + * Send a HTML email + * + * @access public + * @param string $recipientEmail + * @param string $recipientName + * @param string $subject + * @param string $html + * @return Client + */ + public function send($recipientEmail, $recipientName, $subject, $html, $authorName = null, $authorEmail = null) + { + if (! empty($recipientEmail)) { + $this->queueManager->push(EmailJob::getInstance($this->container)->withParams( + $recipientEmail, + $recipientName, + $subject, + $html, + is_null($authorName) ? $this->getAuthorName() : $authorName, + is_null($authorEmail) ? $this->getAuthorEmail() : $authorEmail + )); + } + + return $this; + } + + /** + * Get author name + * + * @access public + * @return string + */ + public function getAuthorName() + { + $author = 'Kanboard'; + + if ($this->userSession->isLogged()) { + $author = e('%s via Kanboard', $this->helper->user->getFullname()); + } + + return $author; + } + + /** + * Get author email + * + * @access public + * @return string + */ + public function getAuthorEmail() + { + if ($this->userSession->isLogged()) { + $userData = $this->userSession->getAll(); + return ! empty($userData['email']) ? $userData['email'] : ''; + } + + return ''; + } + + /** + * Get mail transport instance + * + * @access public + * @param string $transport + * @return ClientInterface + */ + public function getTransport($transport) + { + return $this->transports[$transport]; + } + + /** + * Add a new mail transport + * + * @access public + * @param string $transport + * @param string $class + * @return Client + */ + public function setTransport($transport, $class) + { + $container = $this->container; + + $this->transports[$transport] = function () use ($class, $container) { + return new $class($container); + }; + + return $this; + } + + /** + * Return the list of registered transports + * + * @access public + * @return array + */ + public function getAvailableTransports() + { + $availableTransports = $this->transports->keys(); + return array_combine($availableTransports, $availableTransports); + } +} diff --git a/app/Core/Mail/ClientInterface.php b/app/Core/Mail/ClientInterface.php new file mode 100644 index 0000000..3bfa32d --- /dev/null +++ b/app/Core/Mail/ClientInterface.php @@ -0,0 +1,25 @@ +setSubject($subject) + ->setFrom($this->helper->mail->getMailSenderAddress(), $authorName) + ->setTo(array($recipientEmail => $recipientName)); + + if (! empty(MAIL_BCC)) { + $message->setBcc(MAIL_BCC); + } + + $headers = $message->getHeaders(); + + // See https://tools.ietf.org/html/rfc3834#section-5 + $headers->addTextHeader('Auto-Submitted', 'auto-generated'); + + if (! empty($authorEmail)) { + $message->setReplyTo($authorEmail); + } + + $message->setBody($html, 'text/html'); + + Swift_Mailer::newInstance($this->getTransport())->send($message); + } catch (Swift_TransportException $e) { + $this->logger->error($e->getMessage()); + } + } + + /** + * Get SwiftMailer transport + * + * @access protected + * @return \Swift_Transport + */ + protected function getTransport() + { + return Swift_MailTransport::newInstance(); + } +} diff --git a/app/Core/Mail/Transport/Sendmail.php b/app/Core/Mail/Transport/Sendmail.php new file mode 100644 index 0000000..a1a2cbd --- /dev/null +++ b/app/Core/Mail/Transport/Sendmail.php @@ -0,0 +1,25 @@ +setUsername(MAIL_SMTP_USERNAME); + $transport->setPassword(MAIL_SMTP_PASSWORD); + if (!is_null(MAIL_SMTP_HELO_NAME)) { + $transport->setLocalDomain(MAIL_SMTP_HELO_NAME); + } + $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 new file mode 100644 index 0000000..f8880b0 --- /dev/null +++ b/app/Core/Markdown.php @@ -0,0 +1,186 @@ +isPublicLink = $isPublicLink; + $this->container = $container; + $this->BlockTypes['#'][0] = 'CustomHeader'; + $this->InlineTypes['#'][] = 'TaskLink'; + $this->InlineTypes['@'][] = 'UserLink'; + $this->inlineMarkerList .= '#@'; + } + + protected function blockCustomHeader($Line) + { + if (preg_match('!#(\d+)!i', $Line['text'], $matches)) { + $link = $this->buildTaskLink($matches[1]); + + if (! empty($link)) { + return [ + 'extent' => strlen($matches[0]), + 'element' => [ + 'name' => 'a', + 'text' => $matches[0], + 'attributes' => ['href' => $link], + ], + ]; + } + } + + return $this->blockHeader($Line); + } + + /** + * Handle Task Links + * + * Replace "#123" by a link to the task + * + * @access public + * @param array $Excerpt + * @return array|null + */ + protected function inlineTaskLink(array $Excerpt) + { + if (preg_match('!#(\d+)!i', $Excerpt['text'], $matches)) { + $link = $this->buildTaskLink($matches[1]); + + if (! empty($link)) { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[0], + 'attributes' => array('href' => $link), + ), + ); + } + } + + return null; + } + + /** + * Handle User Mentions + * + * Replace "@username" by a link to the user + * + * @access public + * @param array $Excerpt + * @return array|null + */ + protected function inlineUserLink(array $Excerpt) + { + if (! $this->isPublicLink && preg_match('/^@([^\s,!:?]+)/', $Excerpt['text'], $matches)) { + $username = rtrim($matches[1], '.'); + $user = $this->container['userCacheDecorator']->getByUsername($username); + + if (! empty($user)) { + $url = $this->container['helper']->url->to('UserViewController', 'profile', array('user_id' => $user['id'])); + $name = $user['name'] ?: $user['username']; + + return array( + 'extent' => strlen($username) + 1, + 'element' => array( + 'name' => 'a', + 'text' => '@' . $username, + 'attributes' => array( + 'href' => $url, + 'class' => 'user-mention-link', + 'title' => $name, + 'aria-label' => $name, + ), + ), + ); + } + } + + return null; + } + + /** + * Build task link + * + * @access private + * @param integer $task_id + * @return string + */ + private function buildTaskLink($task_id) + { + if ($this->isPublicLink) { + $token = $this->container['memoryCache']->proxy($this->container['taskFinderModel'], 'getProjectToken', $task_id); + + if (! empty($token)) { + return $this->container['helper']->url->to( + 'TaskViewController', + 'readonly', + array( + 'token' => $token, + 'task_id' => $task_id, + ), + '', + true + ); + } + + return ''; + } + + return $this->container['helper']->url->to( + 'TaskViewController', + 'show', + array('task_id' => $task_id) + ); + } + + /** + * Exclude from nesting task links and user mentions for links + * + * @param array $Excerpt + * @return array|null + */ + protected function inlineLink($Excerpt) + { + $Inline = parent::inlineLink($Excerpt); + if (is_array($Inline)) { + array_push($Inline['element']['nonNestables'], 'TaskLink', 'UserLink'); + } + return $Inline; + } +} diff --git a/app/Core/Notification/NotificationInterface.php b/app/Core/Notification/NotificationInterface.php new file mode 100644 index 0000000..d336983 --- /dev/null +++ b/app/Core/Notification/NotificationInterface.php @@ -0,0 +1,32 @@ +baseDir = $realBaseDir; + } + + /** + * Fetch object contents + * + * @access public + * @throws ObjectStorageException + * @param string $key + * @return string + */ + public function get($key) + { + return file_get_contents($this->getRealFilePath($key)); + } + + /** + * Save object + * + * @access public + * @throws ObjectStorageException + * @param string $key + * @param string $blob + */ + public function put($key, &$blob) + { + $filename = $this->getSanitizedFilePath($key); + $this->createFolder($key); + + if (file_put_contents($filename, $blob) === false) { + throw new ObjectStorageException('Unable to write the file: '.$filename); + } + } + + /** + * Output directly object content + * + * @access public + * @throws ObjectStorageException + * @param string $key + */ + public function output($key) + { + readfile($this->getRealFilePath($key)); + } + + /** + * Move local file to object storage + * + * @access public + * @throws ObjectStorageException + * @param string $srcFilename + * @param string $key + * @return boolean + */ + public function moveFile($srcFilename, $key) + { + if (! file_exists($srcFilename)) { + throw new ObjectStorageException('Source file does not exist: '.$srcFilename); + } + + $dstFilename = $this->getSanitizedFilePath($key); + $this->createFolder($key); + + if (! rename($srcFilename, $dstFilename)) { + throw new ObjectStorageException('Unable to move the file: '.$srcFilename.' to '.$dstFilename); + } + + return true; + } + + /** + * Move uploaded file to object storage + * + * @access public + * @param string $srcFilename + * @param string $key + * @return boolean + */ + public function moveUploadedFile($srcFilename, $key) + { + if (! file_exists($srcFilename)) { + throw new ObjectStorageException('Source file does not exist: '.$srcFilename); + } + + $dstFilename = $this->getSanitizedFilePath($key); + $this->createFolder($key); + + return move_uploaded_file($srcFilename, $dstFilename); + } + + /** + * Remove object + * + * @access public + * @param string $key + * @return boolean + */ + public function remove($key) + { + $filename = $this->getRealFilePath($key); + $result = unlink($filename); + + // Remove parent folder if empty + $parentFolder = dirname($filename); + $files = glob($parentFolder.DIRECTORY_SEPARATOR.'*'); + + if ($files !== false && is_dir($parentFolder) && count($files) === 0) { + rmdir($parentFolder); + } + + return $result; + } + + private function createFolder(string $key) + { + $folder = strpos($key, DIRECTORY_SEPARATOR) !== false ? $this->baseDir.DIRECTORY_SEPARATOR.dirname($key) : $this->baseDir; + + if (! is_dir($folder) && ! mkdir($folder, 0o755, true)) { + throw new ObjectStorageException('Unable to create folder: '.$folder); + } + } + + private function getRealFilePath(string $key): string + { + $filename = $this->baseDir.DIRECTORY_SEPARATOR.$key; + + // Resolve the real path and make sure the file exists + $realFilePath = realpath($filename); + if ($realFilePath === false) { + throw new ObjectStorageException('Invalid file path: '.$filename); + } + + $this->validateBasePath($realFilePath); + + return $realFilePath; + } + + public function getSanitizedFilePath(string $key): string + { + $filename = $this->baseDir.DIRECTORY_SEPARATOR.$key; + $sanitizedKey = sanitize_path($filename); + + if ($sanitizedKey === false) { + throw new ObjectStorageException('Invalid file path: '.$key); + } + + $this->validateBasePath($sanitizedKey); + + return $sanitizedKey; + } + + private function validateBasePath(string $filePath) + { + if (strpos($filePath, $this->baseDir) !== 0) { + throw new ObjectStorageException('File '.$filePath.' is not in base directory: '.$this->baseDir); + } + } +} diff --git a/app/Core/ObjectStorage/ObjectStorageException.php b/app/Core/ObjectStorage/ObjectStorageException.php new file mode 100644 index 0000000..9e98ff5 --- /dev/null +++ b/app/Core/ObjectStorage/ObjectStorageException.php @@ -0,0 +1,9 @@ +container = $container; + } + + /** + * Set a PicoDb query + * + * @access public + * @param \PicoDb\Table + * @return $this + */ + public function setQuery(Table $query) + { + $this->query = $query; + $this->total = $this->query->count(); + return $this; + } + + /** + * Set Formatter + * + * @param FormatterInterface $formatter + * @return $this + */ + public function setFormatter(FormatterInterface $formatter) + { + $this->formatter = $formatter; + return $this; + } + + /** + * Execute a PicoDb query + * + * @access public + * @return array + */ + public function executeQuery() + { + if ($this->query !== null) { + + $this->query + ->offset($this->offset) + ->limit($this->limit); + + if (preg_match('/^[a-zA-Z0-9._]+$/', $this->order)) { + $this->query->orderBy($this->order, $this->direction); + } else { + $this->order = ''; + } + + if ($this->formatter !== null) { + return $this->formatter->withQuery($this->query)->format(); + } else { + return $this->query->findAll(); + } + } + + return array(); + } + + /** + * Set url parameters + * + * @access public + * @param string $controller + * @param string $action + * @param array $params + * @param string $anchor + * @return $this + */ + public function setUrl($controller, $action, array $params = array(), $anchor = '') + { + $this->controller = $controller; + $this->action = $action; + $this->params = $params; + $this->anchor = $anchor; + return $this; + } + + /** + * Add manually items + * + * @access public + * @param array $items + * @return $this + */ + public function setCollection(array $items) + { + $this->items = $items; + return $this; + } + + /** + * Return the items + * + * @access public + * @return array + */ + public function getCollection() + { + return $this->items ?: $this->executeQuery(); + } + + /** + * Set the total number of items + * + * @access public + * @param integer $total + * @return $this + */ + public function setTotal($total) + { + $this->total = $total; + return $this; + } + + /** + * Get the total number of items + * + * @access public + * @return integer + */ + public function getTotal() + { + return $this->total; + } + + /** + * Set the default page number + * + * @access public + * @param integer $page + * @return $this + */ + public function setPage($page) + { + $this->page = $page; + return $this; + } + + /** + * Get the number of current page + * + * @access public + * @return integer + */ + public function getPage() + { + return $this->page; + } + + /** + * Set the default column order + * + * @access public + * @param string $order + * @return $this + */ + public function setOrder($order) + { + $this->order = $order; + return $this; + } + + /** + * Set the default sorting direction + * + * @access public + * @param string $direction + * @return $this + */ + public function setDirection($direction) + { + $this->direction = $direction; + return $this; + } + + /** + * Set the maximum number of items per page + * + * @access public + * @param integer $limit + * @return $this + */ + public function setMax($limit) + { + $this->limit = $limit; + return $this; + } + + /** + * 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 + * @return boolean + */ + public function isEmpty() + { + return $this->total === 0; + } + + /** + * Execute the offset calculation only if the $condition is true + * + * @access public + * @param boolean $condition + * @return $this + */ + public function calculateOnlyIf($condition) + { + if ($condition) { + $this->calculate(); + } + + return $this; + } + + /** + * Calculate the offset value according to url params and the page number + * + * @access public + * @return $this + */ + public function calculate() + { + $this->page = $this->container['request']->getIntegerParam('page', 1); + $this->direction = $this->container['request']->getStringParam('direction', $this->direction); + $this->order = $this->container['request']->getStringParam('order', $this->order); + + if ($this->page < 1) { + $this->page = 1; + } + + $this->offset = (int) (($this->page - 1) * $this->limit); + + return $this; + } + + /** + * Get url params for link generation + * + * @access public + * @param integer $page + * @param string $order + * @param string $direction + * @return string + */ + public function getUrlParams($page, $order, $direction) + { + $params = array( + 'page' => $page, + 'order' => $order, + 'direction' => $direction, + ); + + return array_merge($this->params, $params); + } + + /** + * Generate the previous link + * + * @access public + * @return string + */ + public function generatePreviousLink() + { + $html = ''; + + if ($this->offset > 0) { + $html .= $this->container['helper']->url->link( + '← '.t('Previous'), + $this->controller, + $this->action, + $this->getUrlParams($this->page - 1, $this->order, $this->direction), + false, + 'js-modal-replace', + t('Previous'), + false, + $this->anchor + ); + } else { + $html .= '← '.t('Previous'); + } + + $html .= ''; + + return $html; + } + + /** + * Generate the next link + * + * @access public + * @return string + */ + public function generateNextLink() + { + $html = ''; + + if (($this->total - $this->offset) > $this->limit) { + $html .= $this->container['helper']->url->link( + t('Next').' →', + $this->controller, + $this->action, + $this->getUrlParams($this->page + 1, $this->order, $this->direction), + false, + 'js-modal-replace', + t('Next'), + false, + $this->anchor + ); + } else { + $html .= t('Next').' →'; + } + + $html .= ''; + + return $html; + } + + /** + * Generate the page showing. + * + * @access public + * @return string + */ + public function generatePageShowing() + { + return ''.t('Showing %d-%d of %d', (($this->getPage() - 1) * $this->getMax() + 1), min($this->getTotal(), $this->getPage() * $this->getMax()), $this->getTotal()).''; + } + + /** + * Return true if there is no pagination to show + * + * @access public + * @return boolean + */ + public function hasNothingToShow() + { + return $this->offset === 0 && ($this->total - $this->offset) <= $this->limit; + } + + /** + * Generation pagination links + * + * @access public + * @return string + */ + public function toHtml() + { + $html = ''; + + if (! $this->hasNothingToShow()) { + $html .= ''; + } + + return $html; + } + + /** + * Magic method to output pagination links + * + * @access public + * @return string + */ + public function __toString() + { + return $this->toHtml(); + } + + /** + * Column sorting + * + * @param string $label Column title + * @param string $column SQL column name + * @return string + */ + public function order($label, $column) + { + $prefix = ''; + $direction = 'ASC'; + + if ($this->order === $column) { + $prefix = $this->direction === 'DESC' ? '▼ ' : '▲ '; + $direction = $this->direction === 'DESC' ? 'ASC' : 'DESC'; + } + + return $prefix.$this->container['helper']->url->link( + $label, + $this->controller, + $this->action, + $this->getUrlParams($this->page, $column, $direction), + false, + 'js-modal-replace' + ); + } +} diff --git a/app/Core/Plugin/Base.php b/app/Core/Plugin/Base.php new file mode 100644 index 0000000..e0b5954 --- /dev/null +++ b/app/Core/Plugin/Base.php @@ -0,0 +1,147 @@ +container['cspRules'] = $rules; + } + + /** + * Returns all classes that needs to be stored in the DI container + * + * @access public + * @return array + */ + public function getClasses() + { + return array(); + } + + /** + * Returns all helper classes that needs to be stored in the DI container + * + * @access public + * @return array + */ + public function getHelpers() + { + return array(); + } + + /** + * Listen on internal events + * + * @access public + * @param string $event + * @param callable $callback + */ + public function on($event, $callback) + { + $container = $this->container; + + $this->dispatcher->addListener($event, function () use ($container, $callback) { + call_user_func($callback, $container); + }); + } + + /** + * Get plugin name + * + * This method should be overridden by your Plugin class + * + * @access public + * @return string + */ + public function getPluginName() + { + return ucfirst(substr(get_called_class(), 16, -7)); + } + + /** + * Get plugin description + * + * This method should be overridden by your Plugin class + * + * @access public + * @return string + */ + public function getPluginDescription() + { + return ''; + } + + /** + * Get plugin author + * + * This method should be overridden by your Plugin class + * + * @access public + * @return string + */ + public function getPluginAuthor() + { + return '?'; + } + + /** + * Get plugin version + * + * This method should be overridden by your Plugin class + * + * @access public + * @return string + */ + public function getPluginVersion() + { + return '?'; + } + + /** + * Get plugin homepage + * + * This method should be overridden by your Plugin class + * + * @access public + * @return string + */ + public function getPluginHomepage() + { + 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 new file mode 100644 index 0000000..dc32e65 --- /dev/null +++ b/app/Core/Plugin/Directory.php @@ -0,0 +1,52 @@ +httpClient->getJson($url); + $plugins = array_filter($plugins, array($this, 'isCompatible')); + $plugins = array_filter($plugins, array($this, 'isInstallable')); + return $plugins; + } + + /** + * Filter plugins + * + * @param array $plugin + * @param string $appVersion + * @return bool + */ + public function isCompatible(array $plugin, $appVersion = APP_VERSION) + { + return Version::isCompatible($plugin['compatible_version'], $appVersion); + } + + /** + * Filter plugins + * + * @param array $plugin + * @return bool + */ + public function isInstallable(array $plugin) + { + return $plugin['remote_install']; + } +} diff --git a/app/Core/Plugin/Hook.php b/app/Core/Plugin/Hook.php new file mode 100644 index 0000000..ca19793 --- /dev/null +++ b/app/Core/Plugin/Hook.php @@ -0,0 +1,116 @@ +hooks[$hook])) { + $this->hooks[$hook] = array(); + } + + $this->hooks[$hook][] = $value; + } + + /** + * Get all bindings for a hook + * + * @access public + * @param string $hook + * @return array + */ + public function getListeners($hook) + { + return isset($this->hooks[$hook]) ? $this->hooks[$hook] : array(); + } + + /** + * Return true if the hook is used + * + * @access public + * @param string $hook + * @return boolean + */ + public function exists($hook) + { + return isset($this->hooks[$hook]); + } + + /** + * Merge listener results with input array + * + * @access public + * @param string $hook + * @param array $values + * @param array $params + * @return array + */ + public function merge($hook, array &$values, array $params = array()) + { + foreach ($this->getListeners($hook) as $listener) { + $result = call_user_func_array($listener, $params); + + if (is_array($result) && ! empty($result)) { + $values = array_merge($values, $result); + } + } + + return $values; + } + + /** + * Execute only first listener + * + * @access public + * @param string $hook + * @param array $params + * @return mixed + */ + public function first($hook, array $params = array()) + { + foreach ($this->getListeners($hook) as $listener) { + return call_user_func_array($listener, $params); + } + + 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 new file mode 100644 index 0000000..cf6079b --- /dev/null +++ b/app/Core/Plugin/Installer.php @@ -0,0 +1,144 @@ +downloadPluginArchive($archiveUrl); + + if (! $zip->extractTo(PLUGINS_DIR)) { + $this->cleanupArchive($zip); + throw new PluginInstallerException(t('Unable to extract plugin archive.')); + } + + $this->cleanupArchive($zip); + } + + /** + * Uninstall a plugin + * + * @access public + * @param string $pluginId + * @throws PluginInstallerException + */ + public function uninstall($pluginId) + { + $pluginFolder = PLUGINS_DIR.DIRECTORY_SEPARATOR.basename($pluginId); + + if (! file_exists($pluginFolder)) { + throw new PluginInstallerException(t('Plugin not found.')); + } + + if (! is_writable($pluginFolder)) { + throw new PluginInstallerException(e('You don\'t have the permission to remove this plugin.')); + } + + Tool::removeAllFiles($pluginFolder); + } + + /** + * Update a plugin + * + * @access public + * @param string $archiveUrl + * @throws PluginInstallerException + */ + public function update($archiveUrl) + { + $zip = $this->downloadPluginArchive($archiveUrl); + + $firstEntry = $zip->statIndex(0); + $this->uninstall($firstEntry['name']); + + if (! $zip->extractTo(PLUGINS_DIR)) { + $this->cleanupArchive($zip); + throw new PluginInstallerException(t('Unable to extract plugin archive.')); + } + + $this->cleanupArchive($zip); + } + + /** + * Download archive from URL + * + * @access protected + * @param string $archiveUrl + * @return ZipArchive + * @throws PluginInstallerException + */ + protected function downloadPluginArchive($archiveUrl) + { + if (!preg_match('/^https?:\/\//', $archiveUrl) || !filter_var($archiveUrl, FILTER_VALIDATE_URL)) { + throw new PluginInstallerException(t('This URL is invalid')); + } + + $archiveData = $this->httpClient->get($archiveUrl); + $archiveFile = tempnam(ini_get('upload_tmp_dir') ? ini_get('upload_tmp_dir') : sys_get_temp_dir(), 'kb_plugin'); + + if (empty($archiveData)) { + unlink($archiveFile); + throw new PluginInstallerException(t('Unable to download plugin archive.')); + } + + if (file_put_contents($archiveFile, $archiveData) === false) { + unlink($archiveFile); + throw new PluginInstallerException(t('Unable to write temporary file for plugin.')); + } + + $zip = new ZipArchive(); + if ($zip->open($archiveFile) !== true) { + unlink($archiveFile); + throw new PluginInstallerException(t('Unable to open plugin archive.')); + } + + if ($zip->numFiles === 0) { + unlink($archiveFile); + throw new PluginInstallerException(t('There is no file in the plugin archive.')); + } + + return $zip; + } + + /** + * Remove archive file + * + * @access protected + * @param ZipArchive $zip + */ + protected function cleanupArchive(ZipArchive $zip) + { + $filename = $zip->filename; + $zip->close(); + unlink($filename); + } +} diff --git a/app/Core/Plugin/Loader.php b/app/Core/Plugin/Loader.php new file mode 100644 index 0000000..38f41d3 --- /dev/null +++ b/app/Core/Plugin/Loader.php @@ -0,0 +1,137 @@ +plugins; + } + + /** + * Get list of not compatible plugins + * + * @access public + * @return Base[] + */ + public function getIncompatiblePlugins() + { + return $this->incompatiblePlugins; + } + + /** + * Scan plugin folder and load plugins + * + * @access public + */ + public function scan() + { + if (file_exists(PLUGINS_DIR)) { + $loader = new ClassLoader(); + $loader->addPsr4('Kanboard\Plugin\\', PLUGINS_DIR); + $loader->register(); + + $dir = new DirectoryIterator(PLUGINS_DIR); + + foreach ($dir as $fileInfo) { + if ($fileInfo->isDir() && substr($fileInfo->getFilename(), 0, 1) !== '.') { + $pluginName = $fileInfo->getFilename(); + $this->initializePlugin($pluginName); + } + } + } + } + + /** + * Load plugin schema + * + * @access public + * @param string $pluginName + */ + public function loadSchema($pluginName) + { + if (SchemaHandler::hasSchema($pluginName)) { + $schemaHandler = new SchemaHandler($this->container); + $schemaHandler->loadSchema($pluginName); + } + } + + /** + * Load plugin + * + * @access public + * @throws LogicException + * @param string $pluginName + * @return Base + */ + public function loadPlugin($pluginName) + { + $className = '\Kanboard\Plugin\\'.$pluginName.'\\Plugin'; + + if (! class_exists($className)) { + throw new LogicException('Unable to load this plugin class: '.$className); + } + + return new $className($this->container); + } + + /** + * Initialize plugin + * + * @access public + * @param string $pluginName + */ + public function initializePlugin($pluginName) + { + try { + $plugin = $this->loadPlugin($pluginName); + + if (Version::isCompatible($plugin->getCompatibleVersion(), APP_VERSION)) { + $this->loadSchema($pluginName); + + if (method_exists($plugin, 'onStartup')) { + $this->dispatcher->addListener('app.bootstrap', array($plugin, 'onStartup')); + } + + 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 0000000..fae7de3 --- /dev/null +++ b/app/Core/Plugin/PluginException.php @@ -0,0 +1,15 @@ +migrateSchema($pluginName); + } + + /** + * Execute plugin schema migrations + * + * @access public + * @param string $pluginName + */ + public function migrateSchema($pluginName) + { + $lastVersion = constant('\Kanboard\Plugin\\'.$pluginName.'\Schema\VERSION'); + $currentVersion = $this->getSchemaVersion($pluginName); + + try { + $this->db->startTransaction(); + $this->db->getDriver()->disableForeignKeys(); + + for ($i = $currentVersion + 1; $i <= $lastVersion; $i++) { + $functionName = '\Kanboard\Plugin\\'.$pluginName.'\Schema\version_'.$i; + + if (function_exists($functionName)) { + call_user_func($functionName, $this->db->getConnection()); + } + } + + $this->db->getDriver()->enableForeignKeys(); + $this->db->closeTransaction(); + $this->setSchemaVersion($pluginName, $i - 1); + } catch (PDOException $e) { + $this->db->cancelTransaction(); + $this->db->getDriver()->enableForeignKeys(); + throw new RuntimeException('Unable to migrate schema for the plugin: '.$pluginName.' => '.$e->getMessage()); + } + } + + /** + * Get current plugin schema version + * + * @access public + * @param string $plugin + * @return integer + */ + public function getSchemaVersion($plugin) + { + return (int) $this->db->table(self::TABLE_SCHEMA)->eq('plugin', strtolower($plugin))->findOneColumn('version'); + } + + /** + * Save last plugin schema version + * + * @access public + * @param string $plugin + * @param integer $version + * @return boolean + */ + public function setSchemaVersion($plugin, $version) + { + $dictionary = array( + strtolower($plugin) => $version + ); + + return $this->db->getDriver()->upsert(self::TABLE_SCHEMA, 'plugin', 'version', $dictionary); + } +} diff --git a/app/Core/Plugin/Version.php b/app/Core/Plugin/Version.php new file mode 100644 index 0000000..13f7209 --- /dev/null +++ b/app/Core/Plugin/Version.php @@ -0,0 +1,38 @@ +=', '>', '<=', '<') 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 new file mode 100644 index 0000000..bc63fe0 --- /dev/null +++ b/app/Core/Queue/JobHandler.php @@ -0,0 +1,90 @@ + get_class($job), + 'params' => $job->getJobParams(), + 'user_id' => $this->userSession->getId(), + )); + } + + /** + * Execute a job + * + * @access public + * @param Job $job + */ + public function executeJob(Job $job) + { + $payload = $job->getBody(); + + 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']); + } catch (Exception $e) { + $this->logger->error(__METHOD__.': Error during job execution: '.$e->getMessage()); + $this->logger->error(__METHOD__ .' => '.json_encode($payload)); + } + } + + /** + * Create the session for the job + * + * @access protected + * @param integer $user_id + */ + protected function prepareJobSession($user_id) + { + session_flush(); + + if ($user_id > 0) { + $user = $this->userModel->getById($user_id); + $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(new Event(), 'app.bootstrap'); + } +} diff --git a/app/Core/Queue/QueueManager.php b/app/Core/Queue/QueueManager.php new file mode 100644 index 0000000..1d7c2d1 --- /dev/null +++ b/app/Core/Queue/QueueManager.php @@ -0,0 +1,75 @@ +queue = $queue; + return $this; + } + + /** + * Send a new job to the queue + * + * @access public + * @param BaseJob $job + * @return $this + */ + 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()); + } + + return $this; + } + + /** + * Wait for new jobs + * + * @access public + * @throws LogicException + */ + public function listen() + { + if ($this->queue === null) { + throw new LogicException('No queue driver defined or unable to connect to broker!'); + } + + while ($job = $this->queue->pull()) { + JobHandler::getInstance($this->container)->executeJob($job); + $this->queue->completed($job); + } + } +} diff --git a/app/Core/Security/AccessMap.php b/app/Core/Security/AccessMap.php new file mode 100644 index 0000000..2431a92 --- /dev/null +++ b/app/Core/Security/AccessMap.php @@ -0,0 +1,175 @@ +defaultRole = $role; + return $this; + } + + /** + * Define role hierarchy + * + * @access public + * @param string $role + * @param array $subroles + * @return AccessMap + */ + public function setRoleHierarchy($role, array $subroles) + { + foreach ($subroles as $subrole) { + if (isset($this->hierarchy[$subrole])) { + $this->hierarchy[$subrole][] = $role; + } else { + $this->hierarchy[$subrole] = array($role); + } + } + + return $this; + } + + /** + * Get computed role hierarchy + * + * @access public + * @param string $role + * @return array + */ + public function getRoleHierarchy($role) + { + $roles = array($role); + + if (isset($this->hierarchy[$role])) { + $roles = array_merge($roles, $this->hierarchy[$role]); + } + + return $roles; + } + + /** + * Get the highest role from a list + * + * @access public + * @param array $roles + * @return string + */ + public function getHighestRole(array $roles) + { + $rank = array(); + + foreach ($roles as $role) { + $rank[$role] = count($this->getRoleHierarchy($role)); + } + + asort($rank); + + return key($rank); + } + + /** + * Add new access rules + * + * @access public + * @param string $controller Controller class name + * @param mixed $methods List of method name or just one method + * @param string $role Lowest role required + * @return AccessMap + */ + public function add($controller, $methods, $role) + { + if (is_array($methods)) { + foreach ($methods as $method) { + $this->addRule($controller, $method, $role); + } + } else { + $this->addRule($controller, $methods, $role); + } + + return $this; + } + + /** + * Add new access rule + * + * @access private + * @param string $controller + * @param string $method + * @param string $role + * @return AccessMap + */ + private function addRule($controller, $method, $role) + { + $controller = strtolower($controller); + $method = strtolower($method); + + if (! isset($this->map[$controller])) { + $this->map[$controller] = array(); + } + + $this->map[$controller][$method] = $role; + + return $this; + } + + /** + * Get roles that match the given controller/method + * + * @access public + * @param string $controller + * @param string $method + * @return array + */ + public function getRoles($controller, $method) + { + $controller = strtolower($controller); + $method = strtolower($method); + + foreach (array($method, '*') as $key) { + if (isset($this->map[$controller][$key])) { + return $this->getRoleHierarchy($this->map[$controller][$key]); + } + } + + return $this->getRoleHierarchy($this->defaultRole); + } +} diff --git a/app/Core/Security/AuthenticationManager.php b/app/Core/Security/AuthenticationManager.php new file mode 100644 index 0000000..8b0808a --- /dev/null +++ b/app/Core/Security/AuthenticationManager.php @@ -0,0 +1,200 @@ +providers = []; + } + + /** + * Register a new authentication provider + * + * @access public + * @param AuthenticationProviderInterface $provider + * @return AuthenticationManager + */ + public function register(AuthenticationProviderInterface $provider) + { + $this->providers[$provider->getName()] = $provider; + return $this; + } + + /** + * Register a new authentication provider + * + * @access public + * @param string $name + * @return AuthenticationProviderInterface|OAuthAuthenticationProviderInterface|PasswordAuthenticationProviderInterface|PreAuthenticationProviderInterface|OAuthAuthenticationProviderInterface + */ + public function getProvider($name) + { + if (! isset($this->providers[$name])) { + throw new LogicException('Authentication provider not found: '.$name); + } + + return $this->providers[$name]; + } + + /** + * Execute providers that are able to validate the current session + * + * @access public + * @return boolean + */ + public function checkCurrentSession() + { + if ($this->userSession->isLogged()) { + foreach ($this->filterProviders('SessionCheckProviderInterface') as $provider) { + if ($provider instanceof OptionalAuthenticationProviderInterface && ! $provider->isEnabled()) { + continue; + } + + if (! $provider->isValidSession()) { + $this->logger->debug('Invalidate session for '.$this->userSession->getUsername()); + session_flush(); + $this->preAuthentication(); + return false; + } + } + } + + return true; + } + + /** + * Execute pre-authentication providers + * + * @access public + * @return boolean + */ + public function preAuthentication() + { + foreach ($this->filterProviders('PreAuthenticationProviderInterface') as $provider) { + if ($provider instanceof OptionalAuthenticationProviderInterface && ! $provider->isEnabled()) { + continue; + } + + if ($provider->authenticate() && $this->userProfile->initialize($provider->getUser())) { + $this->dispatcher->dispatch(new AuthSuccessEvent($provider->getName()), self::EVENT_SUCCESS); + return true; + } + } + + return false; + } + + /** + * Execute username/password authentication providers + * + * @access public + * @param string $username + * @param string $password + * @param boolean $fireEvent + * @return boolean + */ + public function passwordAuthentication($username, $password, $fireEvent = true) + { + foreach ($this->filterProviders('PasswordAuthenticationProviderInterface') as $provider) { + $provider->setUsername($username); + $provider->setPassword($password); + + if ($provider->authenticate() && $this->userProfile->initialize($provider->getUser())) { + if ($fireEvent) { + $this->dispatcher->dispatch(new AuthSuccessEvent($provider->getName()), self::EVENT_SUCCESS); + } + + return true; + } + } + + if ($fireEvent) { + $this->dispatcher->dispatch(new AuthFailureEvent($username), self::EVENT_FAILURE); + } + + return false; + } + + /** + * Perform OAuth2 authentication + * + * @access public + * @param string $name + * @return boolean + */ + public function oauthAuthentication($name) + { + $provider = $this->getProvider($name); + + if ($provider->authenticate() && $this->userProfile->initialize($provider->getUser())) { + $this->dispatcher->dispatch(new AuthSuccessEvent($provider->getName()), self::EVENT_SUCCESS); + return true; + } + + $this->dispatcher->dispatch(new AuthFailureEvent, self::EVENT_FAILURE); + + return false; + } + + /** + * Get the last Post-Authentication provider + * + * @access public + * @return PostAuthenticationProviderInterface + */ + public function getPostAuthenticationProvider() + { + $providers = $this->filterProviders('PostAuthenticationProviderInterface'); + + if (empty($providers)) { + throw new LogicException('You must have at least one Post-Authentication Provider configured'); + } + + return array_pop($providers); + } + + /** + * Filter registered providers by interface type + * + * @access private + * @param string $interface + * @return array + */ + private function filterProviders($interface) + { + $interface = '\Kanboard\Core\Security\\'.$interface; + + return array_filter($this->providers, function (AuthenticationProviderInterface $provider) use ($interface) { + return is_a($provider, $interface); + }); + } +} diff --git a/app/Core/Security/AuthenticationProviderInterface.php b/app/Core/Security/AuthenticationProviderInterface.php new file mode 100644 index 0000000..828e272 --- /dev/null +++ b/app/Core/Security/AuthenticationProviderInterface.php @@ -0,0 +1,28 @@ +accessMap = $accessMap; + } + + /** + * Check if the given role is allowed to access to the specified resource + * + * @access public + * @param string $controller + * @param string $method + * @param string $role + * @return boolean + */ + public function isAllowed($controller, $method, $role) + { + $roles = $this->accessMap->getRoles($controller, $method); + return in_array($role, $roles); + } +} diff --git a/app/Core/Security/OAuthAuthenticationProviderInterface.php b/app/Core/Security/OAuthAuthenticationProviderInterface.php new file mode 100644 index 0000000..3092672 --- /dev/null +++ b/app/Core/Security/OAuthAuthenticationProviderInterface.php @@ -0,0 +1,46 @@ + t('Administrator'), + self::APP_MANAGER => t('Manager'), + self::APP_USER => t('User'), + ); + } + + /** + * Get project roles + * + * @access public + * @return array + */ + public function getProjectRoles() + { + return array( + self::PROJECT_MANAGER => t('Project Manager'), + self::PROJECT_MEMBER => t('Project Member'), + self::PROJECT_VIEWER => t('Project Viewer'), + ); + } + + /** + * 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 + * @param string $role + * @return string + */ + public function getRoleName($role) + { + $roles = $this->getApplicationRoles() + $this->getProjectRoles(); + return isset($roles[$role]) ? $roles[$role] : t('Unknown'); + } +} diff --git a/app/Core/Security/SessionCheckProviderInterface.php b/app/Core/Security/SessionCheckProviderInterface.php new file mode 100644 index 0000000..232fe1d --- /dev/null +++ b/app/Core/Security/SessionCheckProviderInterface.php @@ -0,0 +1,20 @@ +createSessionToken('csrf'); + } + + /** + * Generate and store a reusable CSRF token + * + * @access public + * @return string + */ + public function getReusableCSRFToken() + { + return $this->createSessionToken('pcsrf'); + } + + /** + * Check if the token exists for the current session (a token can be used only one time) + * + * @access public + * @param string $token CSRF token + * @return bool + */ + public function validateCSRFToken($token) + { + return $this->validateSessionToken('csrf', $token); + } + + /** + * Check if the token exists as a reusable CSRF token + * + * @access public + * @param string $token CSRF token + * @return bool + */ + public function validateReusableCSRFToken($token) + { + return $this->validateSessionToken('pcsrf', $token); + } + + /** + * Generate a session token of the given type + * + * @access protected + * @param string $type Token type + * @return string Random token + */ + protected function createSessionToken($type) + { + $nonce = self::getToken(self::$NONCE_LENGTH); + return $nonce . $this->signSessionToken($type, $nonce); + } + + /** + * Check a session token of the given type + * + * @access protected + * @param string $type Token type + * @param string $token Session token + * @return bool + */ + protected function validateSessionToken($type, $token) + { + if (!is_string($token)) { + return false; + } + + if (strlen($token) != (self::$NONCE_LENGTH + self::$HMAC_LENGTH) * 2) { + return false; + } + + $nonce = substr($token, 0, self::$NONCE_LENGTH * 2); + $hmac = substr($token, self::$NONCE_LENGTH * 2, self::$HMAC_LENGTH * 2); + + return hash_equals($this->signSessionToken($type, $nonce), $hmac); + } + + /** + * Sign a nonce with the key belonging to the given type + * + * @access protected + * @param string $type Token type + * @param string $nonce Nonce to sign + * @return string + */ + protected function signSessionToken($type, $nonce) + { + if (!session_exists($type . '_key')) { + session_set($type . '_key', self::getToken(self::$KEY_LENGTH)); + } + + $data = $nonce . '-' . session_id(); + $key = session_get($type . '_key'); + $hmac = hash_hmac(self::$HMAC_ALGO, $data, $key, true); + + return bin2hex(substr($hmac, 0, self::$HMAC_LENGTH)); + } +} diff --git a/app/Core/Session/FlashMessage.php b/app/Core/Session/FlashMessage.php new file mode 100644 index 0000000..037717c --- /dev/null +++ b/app/Core/Session/FlashMessage.php @@ -0,0 +1,76 @@ +setMessage('success', $message); + } + + /** + * Add failure message + * + * @access public + * @param string $message + */ + public function failure($message) + { + $this->setMessage('failure', $message); + } + + /** + * Add new flash message + * + * @access public + * @param string $key + * @param string $message + */ + public function setMessage($key, $message) + { + if (! session_exists('flash')) { + session_set('flash', []); + } + + session_merge('flash', [$key => $message]); + } + + /** + * Get flash message + * + * @access public + * @param string $key + * @return string + */ + public function getMessage($key) + { + $message = ''; + + if (session_exists('flash')) { + $messages = session_get('flash'); + + if (isset($messages[$key])) { + $message = $messages[$key]; + unset($messages[$key]); + session_set('flash', $messages); + } + } + + return $message; + } +} diff --git a/app/Core/Session/SessionHandler.php b/app/Core/Session/SessionHandler.php new file mode 100644 index 0000000..a0594b5 --- /dev/null +++ b/app/Core/Session/SessionHandler.php @@ -0,0 +1,107 @@ +db = $db; + } + + #[\ReturnTypeWillChange] + public function close() + { + return true; + } + + #[\ReturnTypeWillChange] + public function destroy($sessionID) + { + return $this->db->table(self::TABLE)->eq('id', $sessionID)->remove(); + } + + #[\ReturnTypeWillChange] + public function gc($maxlifetime) + { + return $this->db->table(self::TABLE)->lt('expire_at', time())->remove(); + } + + #[\ReturnTypeWillChange] + public function open($savePath, $name) + { + return true; + } + + #[\ReturnTypeWillChange] + public function read($sessionID) + { + $result = $this->db->table(self::TABLE)->eq('id', $sessionID)->gt('expire_at', time())->findOneColumn('data'); + + // Note: Returning false display an error message and write() is never called + // preventing new sessions to be created when calling session_start() + if (empty($result)) { + return ''; + } + + // Sanitize session data to prevent object deserialization attacks (CWE-502). + // Using allowed_classes: false converts any serialized objects to harmless + // __PHP_Incomplete_Class instances, preventing exploitation via gadget chains. + $sanitized = @unserialize($result, ['allowed_classes' => false]); + + // unserialize() returns false both on failure AND when the data legitimately + // represents boolean false (serialized as 'b:0;'). Check the raw string to + // distinguish a real deserialization error from a valid false value. + if ($sanitized === false && $result !== 'b:0;') { + // Data could not be unserialized (e.g. legacy format after handler change); + // discard it so a fresh session is created. + return ''; + } + + return serialize($sanitized); + } + + #[\ReturnTypeWillChange] + public function write($sessionID, $data) + { + if (SESSION_DURATION > 0) { + $lifetime = time() + SESSION_DURATION; + } else { + $lifetime = time() + (ini_get('session.gc_maxlifetime') ?: 1440); + } + + $this->db->startTransaction(); + + if ($this->db->table(self::TABLE)->eq('id', $sessionID)->exists()) { + $this->db->table(self::TABLE)->eq('id', $sessionID)->update([ + 'expire_at' => $lifetime, + 'data' => $data, + ]); + } else { + $this->db->table(self::TABLE)->insert([ + 'id' => $sessionID, + 'expire_at' => $lifetime, + 'data' => $data, + ]); + } + + $this->db->closeTransaction(); + + return true; + } +} diff --git a/app/Core/Session/SessionManager.php b/app/Core/Session/SessionManager.php new file mode 100644 index 0000000..0ce0254 --- /dev/null +++ b/app/Core/Session/SessionManager.php @@ -0,0 +1,117 @@ +db), true); + } + + $this->configure(); + + if (ini_get('session.auto_start') == 1) { + session_destroy(); + } + + session_name('KB_SID'); + session_start(); + } + + /** + * Destroy the session + * + * @access public + */ + public function close() + { + $this->dispatcher->dispatch(new Event(), self::EVENT_DESTROY); + + // Destroy the session cookie + $params = session_get_cookie_params(); + + setcookie( + session_name(), + '', + time() - 42000, + $params['path'], + $params['domain'], + $params['secure'], + $params['httponly'] + ); + + session_unset(); + session_destroy(); + } + + /** + * Define session settings + * + * @access private + */ + private function configure() + { + // Session cookie: HttpOnly and secure flags + session_set_cookie_params( + SESSION_DURATION, + $this->helper->url->dir() ?: '/', + null, + $this->request->isHTTPS(), + true + ); + + // Use php_serialize handler so session data is a single serialize() call, + // which allows safe sanitization in SessionHandler::read() (CWE-502 mitigation). + ini_set('session.serialize_handler', 'php_serialize'); + + // Avoid session id in the URL + ini_set('session.use_only_cookies', '1'); + ini_set('session.use_trans_sid', '0'); + + // Enable strict mode + ini_set('session.use_strict_mode', '1'); + + // Better session hash + ini_set('session.hash_function', '1'); // 'sha512' is not compatible with FreeBSD, only MD5 '0' and SHA-1 '1' seems to work + ini_set('session.hash_bits_per_character', 6); + + // Set an additional entropy + ini_set('session.entropy_file', '/dev/urandom'); + ini_set('session.entropy_length', '256'); + } +} diff --git a/app/Core/Template.php b/app/Core/Template.php new file mode 100644 index 0000000..d3ec26d --- /dev/null +++ b/app/Core/Template.php @@ -0,0 +1,124 @@ +helper = $helper; + } + + /** + * Expose helpers with magic getter + * + * @access public + * @param string $helper + * @return mixed + */ + public function __get($helper) + { + return $this->helper->getHelper($helper); + } + + /** + * Render a template + * + * Example: + * + * $template->render('template_name', ['bla' => 'value']); + * + * @access public + * @param string $__template_name Template name + * @param array $__template_args Key/Value map of template variables + * @return string + */ + public function render($__template_name, array $__template_args = array()) + { + extract($__template_args); + ob_start(); + include $this->getTemplateFile($__template_name); + return ob_get_clean(); + } + + /** + * Define a new template override + * + * @access public + * @param string $original_template + * @param string $new_template + */ + public function setTemplateOverride($original_template, $new_template) + { + $this->overrides[$original_template] = $new_template; + } + + /** + * Find template filename + * + * Core template: 'task/show' or 'kanboard:task/show' + * Plugin template: 'myplugin:task/show' + * + * @access public + * @param string $template + * @return string + */ + public function getTemplateFile($template) + { + $plugin = ''; + $template = isset($this->overrides[$template]) ? $this->overrides[$template] : $template; + + if (strpos($template, ':') !== false) { + list($plugin, $template) = explode(':', $template); + } + + if ($plugin !== 'kanboard' && $plugin !== '') { + return implode(DIRECTORY_SEPARATOR, array(PLUGINS_DIR, ucfirst($plugin), 'Template', $template.'.php')); + } + + return implode(DIRECTORY_SEPARATOR, array(__DIR__, '..', 'Template', $template.'.php')); + } +} diff --git a/app/Core/Thumbnail.php b/app/Core/Thumbnail.php new file mode 100644 index 0000000..1ea9181 --- /dev/null +++ b/app/Core/Thumbnail.php @@ -0,0 +1,177 @@ +fromFile($filename); + return $self; + } + + /** + * Create a thumbnail from a string + * + * @static + * @access public + * @param string $blob + * @return Thumbnail + */ + public static function createFromString($blob) + { + $self = new static(); + $self->fromString($blob); + return $self; + } + + /** + * Load the local image file in memory with GD + * + * @access public + * @param string $filename + * @return Thumbnail + */ + public function fromFile($filename) + { + $this->metadata = getimagesize($filename); + $this->srcImage = @imagecreatefromstring(file_get_contents($filename)); + return $this; + } + + /** + * Load the image blob in memory with GD + * + * @access public + * @param string $blob + * @return Thumbnail + */ + public function fromString($blob) + { + if (!function_exists('getimagesizefromstring')) { + $uri = 'data://application/octet-stream;base64,' . base64_encode($blob); + $this->metadata = getimagesize($uri); + } else { + $this->metadata = getimagesizefromstring($blob); + } + + // Avoid warning from libpng when loading PNG image with obscure or incorrect iCCP profiles + $this->srcImage = @imagecreatefromstring($blob); + return $this; + } + + /** + * Resize the image + * + * @access public + * @param int $width + * @param int $height + * @return Thumbnail + */ + public function resize($width = 250, $height = 100) + { + $srcWidth = $this->metadata[0]; + $srcHeight = $this->metadata[1]; + $dstX = 0; + $dstY = 0; + + if ($width == 0 && $height == 0) { + $width = 100; + $height = 100; + } + + if ($width > 0 && $height == 0) { + $dstWidth = $width; + $dstHeight = floor($srcHeight * ($width / $srcWidth)); + $this->dstImage = imagecreatetruecolor($dstWidth, $dstHeight); + } elseif ($width == 0 && $height > 0) { + $dstWidth = floor($srcWidth * ($height / $srcHeight)); + $dstHeight = $height; + $this->dstImage = imagecreatetruecolor($dstWidth, $dstHeight); + } else { + $srcRatio = $srcWidth / $srcHeight; + $resizeRatio = $width / $height; + + if ($srcRatio <= $resizeRatio) { + $dstWidth = $width; + $dstHeight = floor($srcHeight * ($width / $srcWidth)); + $dstY = ($dstHeight - $height) / 2 * (-1); + } else { + $dstWidth = floor($srcWidth * ($height / $srcHeight)); + $dstHeight = $height; + $dstX = ($dstWidth - $width) / 2 * (-1); + } + + $this->dstImage = imagecreatetruecolor($width, $height); + } + + imagealphablending($this->dstImage, false); + imagesavealpha($this->dstImage, true); + + imagecopyresampled($this->dstImage, $this->srcImage, (int) $dstX, (int) $dstY, 0, 0, (int) $dstWidth, (int) $dstHeight, (int) $srcWidth, (int) $srcHeight); + + return $this; + } + + /** + * Save the thumbnail to a local file + * + * @access public + * @param string $filename + * @return Thumbnail + */ + public function toFile($filename) + { + imagepng($this->dstImage, $filename, $this->compression); + imagedestroy($this->dstImage); + imagedestroy($this->srcImage); + return $this; + } + + /** + * Return the thumbnail as a string + * + * @access public + * @return string + */ + public function toString() + { + ob_start(); + imagepng($this->dstImage, null, $this->compression); + imagedestroy($this->dstImage); + imagedestroy($this->srcImage); + return ob_get_clean(); + } + + /** + * Output the thumbnail directly to the browser or stdout + * + * @access public + */ + public function toOutput() + { + imagepng($this->dstImage, null, $this->compression); + imagedestroy($this->dstImage); + imagedestroy($this->srcImage); + } +} diff --git a/app/Core/Tool.php b/app/Core/Tool.php new file mode 100644 index 0000000..6e45764 --- /dev/null +++ b/app/Core/Tool.php @@ -0,0 +1,109 @@ +isDir()) { + rmdir($file->getRealPath()); + } else { + unlink($file->getRealPath()); + } + } + + if ($removeDirectory) { + rmdir($directory); + } + } + + /** + * Build dependency injection containers from an array + * + * @static + * @access public + * @param Container $container + * @param array $namespaces + * @return Container + */ + public static function buildDIC(Container $container, array $namespaces) + { + foreach ($namespaces as $namespace => $classes) { + foreach ($classes as $name) { + $class = '\\Kanboard\\'.$namespace.'\\'.$name; + $container[lcfirst($name)] = function ($c) use ($class) { + return new $class($c); + }; + } + } + + return $container; + } + + /** + * 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 + * @access public + * @param Container $container + * @param array $namespaces + * @return Container + */ + public static function buildDICHelpers(Container $container, array $namespaces) + { + foreach ($namespaces as $namespace => $classes) { + foreach ($classes as $name) { + $class = '\\Kanboard\\'.$namespace.'\\'.$name; + $container['helper']->register($name, $class); + } + } + + return $container; + } +} diff --git a/app/Core/Translator.php b/app/Core/Translator.php new file mode 100644 index 0000000..a0b4631 --- /dev/null +++ b/app/Core/Translator.php @@ -0,0 +1,214 @@ +translate('I have %d kids', 5); + * + * @access public + * @param string $identifier Default string + * @return string + */ + public function translate($identifier) + { + $args = func_get_args(); + + array_shift($args); + array_unshift($args, $this->get($identifier, $identifier)); + + foreach ($args as &$arg) { + $arg = htmlspecialchars((string) $arg, ENT_QUOTES, 'UTF-8', false); + } + + return call_user_func_array( + 'sprintf', + $args + ); + } + + /** + * Get a translation with no HTML escaping + * + * $translator->translateNoEscaping('I have %d kids', 5); + * + * @access public + * @param string $identifier Default string + * @return string + */ + public function translateNoEscaping($identifier) + { + $args = func_get_args(); + + array_shift($args); + array_unshift($args, $this->get($identifier, $identifier)); + + return call_user_func_array( + 'sprintf', + $args + ); + } + + /** + * Get a formatted number + * + * $translator->number(1234.56); + * + * @access public + * @param float $number Number to format + * @return string + */ + public function number($number) + { + return number_format( + $number, + $this->get('number.decimals', 2), + $this->get('number.decimals_separator', '.'), + $this->get('number.thousands_separator', ',') + ); + } + + /** + * Get a formatted currency number + * + * $translator->currency(1234.56); + * + * @access public + * @param float $amount Number to format + * @return string + */ + public function currency($amount) + { + $position = $this->get('currency.position', 'before'); + $symbol = $this->get('currency.symbol', '$'); + $str = ''; + + if ($position === 'before') { + $str .= $symbol; + } + + $str .= $this->number($amount); + + if ($position === 'after') { + $str .= ' '.$symbol; + } + + return $str; + } + + /** + * Get an identifier from the translations or return the default + * + * @access public + * @param string $identifier Locale identifier + * @param string $default Default value + * @return string + */ + public function get($identifier, $default = '') + { + if (isset(self::$locales[$identifier])) { + return self::$locales[$identifier]; + } else { + return $default; + } + } + + /** + * Load translations + * + * @static + * @access public + * @param string $languageCode Locale code: fr_FR + * @param string $localeDir Locale folder + * @return boolean True if the translation file has been loaded + */ + public static function load($languageCode, $localeDir = '') + { + if (! preg_match('/^[a-z]{2}(_[a-zA-Z]{2,4}(_[a-zA-Z]{2,4})?)?$/', $languageCode)) { + return false; + } + + if ($localeDir === '') { + $localeDir = self::getDefaultFolder(); + } + + $baseDir = realpath($localeDir); + if ($baseDir === false) { + return false; + } + + $realFilePath = realpath(implode(DIRECTORY_SEPARATOR, [$localeDir, $languageCode, 'translations.php'])); + + if ($realFilePath !== false && strpos($realFilePath, $baseDir) === 0) { + self::$locales = array_merge(self::$locales, require($realFilePath)); + return true; + } + + return false; + } + + /** + * Clear locales stored in memory + * + * @static + * @access public + */ + public static function unload() + { + 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/Avatar/AvatarManager.php b/app/Core/User/Avatar/AvatarManager.php new file mode 100644 index 0000000..5b61cbd --- /dev/null +++ b/app/Core/User/Avatar/AvatarManager.php @@ -0,0 +1,93 @@ +providers[] = $provider; + return $this; + } + + /** + * Render avatar HTML element + * + * @access public + * @param string $user_id + * @param string $username + * @param string $name + * @param string $email + * @param string $avatar_path + * @param int $size + * @return string + */ + public function render($user_id, $username, $name, $email, $avatar_path, $size) + { + $user = array( + 'id' => $user_id, + 'username' => $username, + 'name' => $name, + 'email' => $email, + 'avatar_path' => $avatar_path, + ); + + krsort($this->providers); + + foreach ($this->providers as $provider) { + if ($provider->isActive($user)) { + return $provider->render($user, $size); + } + } + + return ''; + } + + /** + * Render default provider for unknown users (first provider registered) + * + * @access public + * @param integer $size + * @return string + */ + public function renderDefault($size) + { + if (count($this->providers) > 0) { + ksort($this->providers); + $provider = current($this->providers); + + $user = array( + 'id' => 0, + 'username' => '', + 'name' => '?', + 'email' => '', + 'avatar_path' => '', + ); + + return $provider->render($user, $size); + } + + return ''; + } +} diff --git a/app/Core/User/Avatar/AvatarProviderInterface.php b/app/Core/User/Avatar/AvatarProviderInterface.php new file mode 100644 index 0000000..e0375d2 --- /dev/null +++ b/app/Core/User/Avatar/AvatarProviderInterface.php @@ -0,0 +1,30 @@ +groupMemberModel->getGroups($userId); + $this->addGroups($userId, $userGroups, $externalGroupIds); + $this->removeGroups($userId, $userGroups, $externalGroupIds); + } + + /** + * Add missing groups to the user + * + * @access protected + * @param integer $userId + * @param array $userGroups + * @param string[] $externalGroupIds + */ + protected function addGroups($userId, array $userGroups, array $externalGroupIds) + { + $userGroupIds = array_column($userGroups, 'external_id', 'external_id'); + $externalGroups = $this->groupModel->getByExternalIds($externalGroupIds); + + foreach ($externalGroups as $externalGroup) { + if (! isset($userGroupIds[$externalGroup['external_id']])) { + $this->groupMemberModel->addUser($externalGroup['id'], $userId); + } + } + } + + /** + * Remove groups from the user + * + * @access protected + * @param integer $userId + * @param array $userGroups + * @param string[] $externalGroupIds + */ + protected function removeGroups($userId, array $userGroups, array $externalGroupIds) + { + foreach ($userGroups as $userGroup) { + if (! empty($userGroup['external_id']) && ! in_array($userGroup['external_id'], $externalGroupIds)) { + $this->groupMemberModel->removeUser($userGroup['id'], $userId); + } + } + } +} diff --git a/app/Core/User/UserBackendProviderInterface.php b/app/Core/User/UserBackendProviderInterface.php new file mode 100644 index 0000000..5f8cab4 --- /dev/null +++ b/app/Core/User/UserBackendProviderInterface.php @@ -0,0 +1,21 @@ +providers[] = $provider; + return $this; + } + + /** + * Find a group from a search query + * + * @access public + * @param string $input + * @return UserProviderInterface[] + */ + public function find($input) + { + $groups = array(); + + foreach ($this->providers as $provider) { + $groups = array_merge($groups, $provider->find($input)); + } + + return $this->removeDuplicates($groups); + } + + /** + * Remove duplicated users + * + * @access protected + * @param array $users + * @return UserProviderInterface[] + */ + protected function removeDuplicates(array $users) + { + $result = array(); + + foreach ($users as $user) { + if (! isset($result[$user->getUsername()])) { + $result[$user->getUsername()] = $user; + } + } + + return array_values($result); + } +} diff --git a/app/Core/User/UserProfile.php b/app/Core/User/UserProfile.php new file mode 100644 index 0000000..26ee0f6 --- /dev/null +++ b/app/Core/User/UserProfile.php @@ -0,0 +1,68 @@ +userModel->getById($userId); + + $values = UserProperty::filterProperties($profile, UserProperty::getProperties($user)); + $values['id'] = $userId; + + if ($this->userModel->update($values)) { + $profile = array_merge($profile, $values); + $this->userSession->initialize($profile); + return true; + } + + return false; + } + + /** + * Synchronize user properties with the local database and create the user session + * + * @access public + * @param UserProviderInterface $user + * @return boolean + */ + public function initialize(UserProviderInterface $user) + { + if ($user->getInternalId()) { + $profile = $this->userModel->getById($user->getInternalId()); + } elseif ($user->getExternalIdColumn() && $user->getExternalId()) { + $profile = $this->userSync->synchronize($user); // The profile can be null when external user creation is disabled + if (LDAP_GROUP_SYNC && ! empty($profile)) { + $this->groupSync->synchronize($profile['id'], $user->getExternalGroupIds()); + } + } + + if (! empty($profile) && $profile['is_active'] == 1) { + $this->userSession->initialize($profile); + $this->dispatcher->dispatch(new UserProfileSyncEvent($profile, $user), self::EVENT_USER_PROFILE_AFTER_SYNC); + return true; + } + + return false; + } +} diff --git a/app/Core/User/UserProperty.php b/app/Core/User/UserProperty.php new file mode 100644 index 0000000..685690c --- /dev/null +++ b/app/Core/User/UserProperty.php @@ -0,0 +1,74 @@ + $user->getUsername(), + 'name' => $user->getName(), + 'email' => $user->getEmail(), + 'role' => $user->getRole(), + $user->getExternalIdColumn() => $user->getExternalId(), + ); + + $properties = array_merge($properties, $user->getExtraAttributes()); + + return array_filter($properties, array(__NAMESPACE__.'\UserProperty', 'isNotEmptyValue')); + } + + /** + * Filter user properties compared to existing user profile + * + * @static + * @access public + * @param array $profile + * @param array $properties + * @return array + */ + public static function filterProperties(array $profile, array $properties) + { + $excludedProperties = explode_csv_field(EXTERNAL_AUTH_EXCLUDE_FIELDS); + $values = array(); + + foreach ($properties as $property => $value) { + if (self::isNotEmptyValue($value) && + ! in_array($property, $excludedProperties) && + array_key_exists($property, $profile) && + $value !== $profile[$property]) { + $values[$property] = $value; + } + } + + return $values; + } + + /** + * Check if a value is not empty + * + * @static + * @access public + * @param string $value + * @return boolean + */ + public static function isNotEmptyValue($value) + { + return $value !== null && $value !== ''; + } +} diff --git a/app/Core/User/UserProviderInterface.php b/app/Core/User/UserProviderInterface.php new file mode 100644 index 0000000..07e01f4 --- /dev/null +++ b/app/Core/User/UserProviderInterface.php @@ -0,0 +1,103 @@ +getId() == $user_id) { + $this->initialize($this->userModel->getById($user_id)); + } + } + + /** + * Update user session + * + * @access public + * @param array $user + */ + public function initialize(array $user) + { + foreach (array('password', 'is_admin', 'is_project_admin', 'twofactor_secret') as $column) { + if (isset($user[$column])) { + unset($user[$column]); + } + } + + $user['id'] = (int) $user['id']; + $user['is_ldap_user'] = isset($user['is_ldap_user']) ? (bool) $user['is_ldap_user'] : false; + $user['twofactor_activated'] = isset($user['twofactor_activated']) ? (bool) $user['twofactor_activated'] : false; + + if (session_status() === PHP_SESSION_ACTIVE) { + // Note: Do not delete the old session to avoid possible race condition and a PHP warning. + session_regenerate_id(false); + } + + session_set('user', $user); + session_set('postAuthenticationValidated', false); + } + + /** + * Get user properties + * + * @access public + * @return array + */ + public function getAll() + { + return session_get('user'); + } + + /** + * Get user application role + * + * @access public + * @return string + */ + public function getRole() + { + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['role']; + } + + /** + * Return true if the user has validated the 2FA key + * + * @access public + * @return bool + */ + public function isPostAuthenticationValidated() + { + return session_is_true('postAuthenticationValidated'); + } + + /** + * Validate 2FA for the current session + * + * @access public + */ + public function setPostAuthenticationAsValidated() + { + session_set('postAuthenticationValidated', true); + } + + /** + * Return true if the user has 2FA enabled + * + * @access public + * @return bool + */ + public function hasPostAuthentication() + { + if (! $this->isLogged()) { + return false; + } + + return session_get('user')['twofactor_activated'] === true; + } + + /** + * Disable 2FA for the current session + * + * @access public + */ + public function disablePostAuthentication() + { + session_merge('user', ['twofactor_activated' => false]); + } + + /** + * Return true if the logged user is admin + * + * @access public + * @return bool + */ + public function isAdmin() + { + return $this->getRole() === Role::APP_ADMIN; + } + + /** + * Get the connected user id + * + * @access public + * @return integer + */ + public function getId() + { + if (! $this->isLogged()) { + return 0; + } + + return session_get('user')['id']; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['username']; + } + + /** + * Get user language + * + * @access public + * @return string + */ + public function getLanguage() + { + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['language']; + } + + /** + * Get user timezone + * + * @access public + * @return string + */ + public function getTimezone() + { + if (! $this->isLogged()) { + return ''; + } + + return session_get('user')['timezone']; + } + + /** + * Get user theme + * + * @access public + * @return string + */ + public function getTheme() + { + if (! $this->isLogged()) { + return 'light'; + } + + $user_session = session_get('user'); + + if (array_key_exists('theme', $user_session)) { + return $user_session['theme']; + } + + return 'light'; + } + + /** + * Return true if subtask list toggle is active + * + * @access public + * @return string + */ + public function hasSubtaskListActivated() + { + return session_is_true('subtaskListToggle'); + } + + /** + * Check is the user is connected + * + * @access public + * @return bool + */ + public function isLogged() + { + return session_exists('user') && session_get('user') !== []; + } + + /** + * Get project filters from the session + * + * @access public + * @param integer $projectID + * @return string + */ + public function getFilters($projectID) + { + if (! session_exists('filters:'.$projectID)) { + return session_get('user') ? session_get('user')['filter'] ?: 'status:open' : 'status:open'; + } + + return session_get('filters:'.$projectID); + } + + /** + * Save project filters in the session + * + * @access public + * @param integer $projectID + * @param string $filters + */ + public function setFilters($projectID, $filters) + { + session_set('filters:'.$projectID, $filters); + } + + /** + * Get project list order from the session + * + * @access public + * @param integer $projectID + * @return array + */ + public function getListOrder($projectID) + { + $default = ['tasks.id', 'DESC']; + + if (! session_exists('listOrder:'.$projectID)) { + return $default; + } + + return session_get('listOrder:'.$projectID); + } + + /** + * Save project list order in the session + * + * @access public + * @param integer $projectID + * @param string $listOrder + * @param string $listDirection + */ + public function setListOrder($projectID, $listOrder, $listDirection) + { + session_set('listOrder:'.$projectID, [$listOrder, $listDirection]); + } +} diff --git a/app/Core/User/UserSync.php b/app/Core/User/UserSync.php new file mode 100644 index 0000000..a536693 --- /dev/null +++ b/app/Core/User/UserSync.php @@ -0,0 +1,82 @@ +userModel->getByExternalId($user->getExternalIdColumn(), $user->getExternalId()); + $properties = UserProperty::getProperties($user); + + if (! empty($profile)) { + $profile = $this->updateUser($profile, $properties); + } elseif ($user->isUserCreationAllowed()) { + $profile = $this->createUser($user, $properties); + } + + return $profile; + } + + /** + * Update user profile + * + * @access public + * @param array $profile + * @param array $properties + * @return array + */ + private function updateUser(array $profile, array $properties) + { + $values = UserProperty::filterProperties($profile, $properties); + + if (! empty($values)) { + $values['id'] = $profile['id']; + $result = $this->userModel->update($values); + return $result ? array_merge($profile, $properties) : $profile; + } + + return $profile; + } + + /** + * Create user + * + * @access public + * @param UserProviderInterface $user + * @param array $properties + * @return array + */ + private function createUser(UserProviderInterface $user, array $properties) + { + $userId = $this->userModel->create($properties); + + if ($userId === false) { + $this->logger->error('Unable to create user profile: '.$user->getExternalId()); + return array(); + } + + if ($this->configModel->get('notifications_enabled', 0) == 1) { + $this->userNotificationTypeModel->saveSelectedTypes($userId, [MailNotification::TYPE, WebNotification::TYPE]); + } + + return $this->userModel->getById($userId); + } +} diff --git a/app/Decorator/ColumnMoveRestrictionCacheDecorator.php b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php new file mode 100644 index 0000000..82140d1 --- /dev/null +++ b/app/Decorator/ColumnMoveRestrictionCacheDecorator.php @@ -0,0 +1,59 @@ +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 0000000..a615030 --- /dev/null +++ b/app/Decorator/ColumnRestrictionCacheDecorator.php @@ -0,0 +1,59 @@ +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 0000000..0897b51 --- /dev/null +++ b/app/Decorator/MetadataCacheDecorator.php @@ -0,0 +1,96 @@ +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 0000000..a6e2404 --- /dev/null +++ b/app/Decorator/ProjectRoleRestrictionCacheDecorator.php @@ -0,0 +1,59 @@ +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 0000000..1cfe31c --- /dev/null +++ b/app/Decorator/UserCacheDecorator.php @@ -0,0 +1,59 @@ +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/AuthFailureEvent.php b/app/Event/AuthFailureEvent.php new file mode 100644 index 0000000..4b073aa --- /dev/null +++ b/app/Event/AuthFailureEvent.php @@ -0,0 +1,44 @@ +username = $username; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + return $this->username; + } +} diff --git a/app/Event/AuthSuccessEvent.php b/app/Event/AuthSuccessEvent.php new file mode 100644 index 0000000..8655104 --- /dev/null +++ b/app/Event/AuthSuccessEvent.php @@ -0,0 +1,43 @@ +authType = $authType; + } + + /** + * Get authentication type + * + * @return string + */ + public function getAuthType() + { + return $this->authType; + } +} diff --git a/app/Event/CommentEvent.php b/app/Event/CommentEvent.php new file mode 100644 index 0000000..3ac6a23 --- /dev/null +++ b/app/Event/CommentEvent.php @@ -0,0 +1,7 @@ +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; + } + + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) + { + if (is_null($offset)) { + $this->container[] = $value; + } else { + $this->container[$offset] = $value; + } + } + + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return isset($this->container[$offset]); + } + + #[\ReturnTypeWillChange] + public function offsetUnset($offset) + { + unset($this->container[$offset]); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return isset($this->container[$offset]) ? $this->container[$offset] : null; + } +} diff --git a/app/Event/ProjectFileEvent.php b/app/Event/ProjectFileEvent.php new file mode 100644 index 0000000..e1d29c4 --- /dev/null +++ b/app/Event/ProjectFileEvent.php @@ -0,0 +1,15 @@ +container['file']['project_id'])) { + return $this->container['file']['project_id']; + } + + return null; + } +} diff --git a/app/Event/SubtaskEvent.php b/app/Event/SubtaskEvent.php new file mode 100644 index 0000000..f34bb60 --- /dev/null +++ b/app/Event/SubtaskEvent.php @@ -0,0 +1,7 @@ +container['tasks'] =& $tasks; + } +} diff --git a/app/Event/UserProfileSyncEvent.php b/app/Event/UserProfileSyncEvent.php new file mode 100644 index 0000000..96ab423 --- /dev/null +++ b/app/Event/UserProfileSyncEvent.php @@ -0,0 +1,64 @@ +profile = $profile; + $this->user = $user; + } + + /** + * Get user profile + * + * @access public + * @return array + */ + public function getProfile() + { + return $this->profile; + } + + /** + * Get user provider object + * + * @access public + * @return UserProviderInterface|LdapUserProvider + */ + public function getUser() + { + return $this->user; + } +} diff --git a/app/EventBuilder/BaseEventBuilder.php b/app/EventBuilder/BaseEventBuilder.php new file mode 100644 index 0000000..5aa777a --- /dev/null +++ b/app/EventBuilder/BaseEventBuilder.php @@ -0,0 +1,44 @@ +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 0000000..73f856e --- /dev/null +++ b/app/EventBuilder/EventIteratorBuilder.php @@ -0,0 +1,63 @@ +builders[] = $builder; + return $this; + } + + #[\ReturnTypeWillChange] + public function rewind() + { + $this->position = 0; + } + + /** + * @return BaseEventBuilder + */ + #[\ReturnTypeWillChange] + public function current() + { + return $this->builders[$this->position]; + } + + #[\ReturnTypeWillChange] + public function key() + { + return $this->position; + } + + #[\ReturnTypeWillChange] + public function next() + { + ++$this->position; + } + + #[\ReturnTypeWillChange] + 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 0000000..6698f78 --- /dev/null +++ b/app/EventBuilder/ProjectFileEventBuilder.php @@ -0,0 +1,77 @@ +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 0000000..c1a41ae --- /dev/null +++ b/app/EventBuilder/SubtaskEventBuilder.php @@ -0,0 +1,125 @@ +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->getByIdWithDetails($this->subtaskId); + + 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 0000000..192cbd1 --- /dev/null +++ b/app/EventBuilder/TaskEventBuilder.php @@ -0,0 +1,233 @@ +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_PROJECT: + return e( + '%s moved the task #%d "%s" to the project "%s"', + $author, + $eventData['task']['id'], + $eventData['task']['title'], + $eventData['task']['project_name'] + ); + 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_PROJECT: + return e('Task #%d "%s" has been moved to the project "%s"', $eventData['task']['id'], $eventData['task']['title'], $eventData['task']['project_name']); + 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 "%s" is overdue', $eventData['tasks'][0]['id'], $eventData['tasks'][0]['title']); + 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 0000000..de514b5 --- /dev/null +++ b/app/EventBuilder/TaskFileEventBuilder.php @@ -0,0 +1,94 @@ +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']); + } + + if ($eventName === TaskFileModel::EVENT_DESTROY) { + return e('%s removed a file from 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']); + } + + if ($eventName === TaskFileModel::EVENT_DESTROY) { + return e('Attachment removed from 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 0000000..2cec20d --- /dev/null +++ b/app/EventBuilder/TaskLinkEventBuilder.php @@ -0,0 +1,89 @@ +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 has 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/SubtaskExport.php b/app/Export/SubtaskExport.php new file mode 100644 index 0000000..64af357 --- /dev/null +++ b/app/Export/SubtaskExport.php @@ -0,0 +1,124 @@ +subtask_status = $this->subtaskModel->getStatusList(); + $subtasks = $this->getSubtasks($project_id, $from, $to); + $results = array($this->getColumns()); + + foreach ($subtasks as $subtask) { + $results[] = $this->format($subtask); + } + + return $results; + } + + /** + * Get column titles + * + * @access public + * @return string[] + */ + public function getColumns() + { + return array( + e('Subtask Id'), + e('Title'), + e('Status'), + e('Assignee'), + e('Time estimated'), + e('Time spent'), + e('Task Id'), + e('Task Title'), + ); + } + + /** + * Format the output of a subtask array + * + * @access public + * @param array $subtask Subtask properties + * @return array + */ + public function format(array $subtask) + { + $values = array(); + $values[] = $subtask['id']; + $values[] = $subtask['title']; + $values[] = t($this->subtask_status[$subtask['status']]); + $values[] = $subtask['assignee_name'] ?: $subtask['assignee_username']; + $values[] = $subtask['time_estimated']; + $values[] = $subtask['time_spent']; + $values[] = $subtask['task_id']; + $values[] = $subtask['task_title']; + + return $values; + } + + /** + * Get all subtasks for a given project + * + * @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) + * @return array + */ + public function getSubtasks($project_id, $from, $to) + { + if (! is_numeric($from)) { + $from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from)); + } + + if (! is_numeric($to)) { + $to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to))); + } + + return $this->db->table(SubtaskModel::TABLE) + ->eq('project_id', $project_id) + ->columns( + SubtaskModel::TABLE.'.*', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name', + TaskModel::TABLE.'.title AS task_title' + ) + ->gte('date_creation', $from) + ->lte('date_creation', $to) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->join(UserModel::TABLE, 'id', 'user_id') + ->asc(SubtaskModel::TABLE.'.id') + ->findAll(); + } +} diff --git a/app/Export/TaskExport.php b/app/Export/TaskExport.php new file mode 100644 index 0000000..f068557 --- /dev/null +++ b/app/Export/TaskExport.php @@ -0,0 +1,169 @@ +getTasks($project_id, $from, $to); + $taskIds = array_column($tasks, 'id'); + $tags = $this->taskTagModel->getTagsByTaskIds($taskIds); + $colors = $this->colorModel->getList(); + $results = array($this->getColumns()); + + foreach ($tasks as &$task) { + $task = $this->format($task, $colors, $tags); + $results[] = array_values($task); + } + + return $results; + } + + /** + * Get the list of tasks for a given project and date range + * + * @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 + */ + protected function getTasks($project_id, $from, $to) + { + if (!is_numeric($from)) { + $from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from)); + } + + if (!is_numeric($to)) { + $to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to))); + } + + 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', + TaskModel::TABLE . '.priority' + ) + ->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 protected + * @param array $task + * @param array $colors + * @param array $tags + * @return array + */ + protected function format(array &$task, array $colors, array &$tags) + { + $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['tags'] = ''; + + $task = $this->dateParser->format( + $task, + array('date_due', 'date_modification', 'date_creation', 'date_started', 'date_completed'), + $this->dateParser->getUserDateTimeFormat() + ); + + if (isset($tags[$task['id']])) { + $taskTags = array_column($tags[$task['id']], 'name'); + $task['tags'] = implode(', ', $taskTags); + } + + return $task; + } + + /** + * Get column titles + * + * @access protected + * @return string[] + */ + protected function getColumns() + { + return array( + e('Task Id'), + e('Reference'), + e('Project'), + e('Status'), + e('Category'), + e('Swimlane'), + e('Column'), + e('Position'), + e('Color'), + e('Due date'), + e('Creator'), + e('Creator Name'), + e('Assignee Username'), + e('Assignee Name'), + e('Complexity'), + e('Title'), + e('Creation date'), + e('Modification date'), + e('Completion date'), + e('Start date'), + e('Time estimated'), + e('Time spent'), + e('Priority'), + e('Tags'), + ); + } +} diff --git a/app/Export/TransitionExport.php b/app/Export/TransitionExport.php new file mode 100644 index 0000000..35f9fe4 --- /dev/null +++ b/app/Export/TransitionExport.php @@ -0,0 +1,76 @@ +getColumns()); + $transitions = $this->transitionModel->getAllByProjectAndDate($project_id, $from, $to); + + foreach ($transitions as $transition) { + $results[] = $this->format($transition); + } + + return $results; + } + + /** + * Get column titles + * + * @access protected + * @return string[] + */ + protected function getColumns() + { + return array( + e('Id'), + e('Task Title'), + e('Source column'), + e('Destination column'), + e('Executer'), + e('Date'), + e('Time spent'), + ); + } + + /** + * Format the output of a transition array + * + * @access protected + * @param array $transition + * @return array + */ + protected function format(array $transition) + { + $values = array( + (int) $transition['id'], + $transition['title'], + $transition['src_column'], + $transition['dst_column'], + $transition['name'] ?: $transition['username'], + date($this->dateParser->getUserDateTimeFormat(), $transition['date']), + round($transition['time_spent'] / 3600, 2) + ); + + return $values; + } +} diff --git a/app/ExternalLink/AttachmentLink.php b/app/ExternalLink/AttachmentLink.php new file mode 100644 index 0000000..5a0d134 --- /dev/null +++ b/app/ExternalLink/AttachmentLink.php @@ -0,0 +1,26 @@ +url, PHP_URL_PATH); + return basename($path); + } +} diff --git a/app/ExternalLink/AttachmentLinkProvider.php b/app/ExternalLink/AttachmentLinkProvider.php new file mode 100644 index 0000000..6041645 --- /dev/null +++ b/app/ExternalLink/AttachmentLinkProvider.php @@ -0,0 +1,117 @@ + t('Related'), + ); + } + + /** + * Return true if the provider can parse correctly the user input + * + * @access public + * @return boolean + */ + public function match() + { + if (preg_match('/^https?:\/\/.*\/.*\.([^\/]+)$/', $this->userInput, $matches)) { + return $this->isValidExtension($matches[1]); + } + + return false; + } + + /** + * Get the link found with the properties + * + * @access public + * @return \Kanboard\Core\ExternalLink\ExternalLinkInterface + */ + public function getLink() + { + $link = new AttachmentLink($this->container); + $link->setUrl($this->userInput); + + return $link; + } + + /** + * Check file extension + * + * @access protected + * @param string $extension + * @return boolean + */ + protected function isValidExtension($extension) + { + $extension = strtolower($extension); + + foreach ($this->extensions as $ext) { + if ($extension === $ext) { + return false; + } + } + + return true; + } +} diff --git a/app/ExternalLink/BaseLink.php b/app/ExternalLink/BaseLink.php new file mode 100644 index 0000000..08693ae --- /dev/null +++ b/app/ExternalLink/BaseLink.php @@ -0,0 +1,44 @@ +url; + } + + /** + * Set link URL + * + * @access public + * @param string $url + */ + public function setUrl($url) + { + $this->url = $url; + } +} diff --git a/app/ExternalLink/BaseLinkProvider.php b/app/ExternalLink/BaseLinkProvider.php new file mode 100644 index 0000000..749cda9 --- /dev/null +++ b/app/ExternalLink/BaseLinkProvider.php @@ -0,0 +1,33 @@ +userInput = trim($input); + } +} diff --git a/app/ExternalLink/FileLink.php b/app/ExternalLink/FileLink.php new file mode 100644 index 0000000..ea13ece --- /dev/null +++ b/app/ExternalLink/FileLink.php @@ -0,0 +1,26 @@ +url, PHP_URL_PATH); + return basename(str_replace('\\', '/', $path)); + } +} diff --git a/app/ExternalLink/FileLinkProvider.php b/app/ExternalLink/FileLinkProvider.php new file mode 100644 index 0000000..eb8c108 --- /dev/null +++ b/app/ExternalLink/FileLinkProvider.php @@ -0,0 +1,89 @@ + t('Related'), + ); + } + + /** + * Return true if the provider can parse correctly the user input + * + * @access public + * @return boolean + */ + public function match() + { + if (strpos($this->userInput, '://') === false) { + return false; + } + + foreach ($this->excludedPrefixes as $prefix) { + if (strpos($this->userInput, $prefix) === 0) { + return false; + } + } + + return true; + } + + /** + * Get the link found with the properties + * + * @access public + * @return \Kanboard\Core\ExternalLink\ExternalLinkInterface + */ + public function getLink() + { + $link = new FileLink($this->container); + $link->setUrl($this->userInput); + + return $link; + } +} diff --git a/app/ExternalLink/WebLink.php b/app/ExternalLink/WebLink.php new file mode 100644 index 0000000..e28935f --- /dev/null +++ b/app/ExternalLink/WebLink.php @@ -0,0 +1,43 @@ +httpClient->isPrivateURL($this->url)) { + $this->logger->info('Blocked attempt to fetch URL from private network: '.$this->url); + return $this->url; + } + + // Do not follow redirects to prevent SSRF bypasses through redirect chains. + $html = $this->httpClient->get($this->url, [], false, false); + + if (preg_match('/(.*)<\/title>/siU', $html, $matches)) { + return trim($matches[1]); + } + + $components = parse_url($this->url); + + if (! empty($components['host']) && ! empty($components['path'])) { + return $components['host'].$components['path']; + } + + return $this->url; + } +} diff --git a/app/ExternalLink/WebLinkProvider.php b/app/ExternalLink/WebLinkProvider.php new file mode 100644 index 0000000..5ec1bbe --- /dev/null +++ b/app/ExternalLink/WebLinkProvider.php @@ -0,0 +1,77 @@ +<?php + +namespace Kanboard\ExternalLink; + +use Kanboard\Core\ExternalLink\ExternalLinkProviderInterface; + +/** + * Web Link Provider + * + * @package externalLink + * @author Frederic Guillot + */ +class WebLinkProvider extends BaseLinkProvider implements ExternalLinkProviderInterface +{ + /** + * Get provider name + * + * @access public + * @return string + */ + public function getName() + { + return t('Web Link'); + } + + /** + * Get link type + * + * @access public + * @return string + */ + public function getType() + { + return 'weblink'; + } + + /** + * Get a dictionary of supported dependency types by the provider + * + * @access public + * @return array + */ + public function getDependencies() + { + return array( + 'related' => t('Related'), + ); + } + + /** + * Return true if the provider can parse correctly the user input + * + * @access public + * @return boolean + */ + public function match() + { + $startWithHttp = strpos($this->userInput, 'http://') === 0 || strpos($this->userInput, 'https://') === 0; + $validUrl = filter_var($this->userInput, FILTER_VALIDATE_URL); + + return $startWithHttp && $validUrl; + } + + /** + * Get the link found with the properties + * + * @access public + * @return \Kanboard\Core\ExternalLink\ExternalLinkInterface + */ + public function getLink() + { + $link = new WebLink($this->container); + $link->setUrl($this->userInput); + + return $link; + } +} diff --git a/app/Filter/BaseComparisonFilter.php b/app/Filter/BaseComparisonFilter.php new file mode 100644 index 0000000..ba0c9c5 --- /dev/null +++ b/app/Filter/BaseComparisonFilter.php @@ -0,0 +1,48 @@ +<?php + +namespace Kanboard\Filter; + +/** + * Base comparison filter class + * + * @package filter + */ +abstract class BaseComparisonFilter extends BaseFilter +{ + /** + * Parse operator in the input string + * + * @access protected + * @return string + */ + protected function parseOperator() + { + $operators = array( + '<=' => 'lte', + '>=' => 'gte', + '<' => 'lt', + '>' => 'gt', + ); + + foreach ($operators as $operator => $method) { + if (strpos($this->value, $operator) === 0) { + $this->value = substr($this->value, strlen($operator)); + return $method; + } + } + + return 'eq'; + } + + /** + * Apply a comparison filter + * + * @access protected + * @param string $field + */ + protected function applyComparisonFilter($field) + { + $method = $this->parseOperator(); + $this->query->$method($field, $this->value); + } +} diff --git a/app/Filter/BaseDateFilter.php b/app/Filter/BaseDateFilter.php new file mode 100644 index 0000000..56fb2d7 --- /dev/null +++ b/app/Filter/BaseDateFilter.php @@ -0,0 +1,103 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\DateParser; + +/** + * Base date filter class + * + * @package filter + * @author Frederic Guillot + */ +abstract class BaseDateFilter extends BaseFilter +{ + /** + * DateParser object + * + * @access protected + * @var DateParser + */ + protected $dateParser; + + /** + * Set DateParser object + * + * @access public + * @param DateParser $dateParser + * @return $this + */ + public function setDateParser(DateParser $dateParser) + { + $this->dateParser = $dateParser; + return $this; + } + + /** + * Parse operator in the input string + * + * @access protected + * @return string + */ + protected function parseOperator() + { + $operators = array( + '<=' => 'lte', + '>=' => 'gte', + '<' => 'lt', + '>' => 'gt', + ); + + foreach ($operators as $operator => $method) { + if (strpos($this->value, $operator) === 0) { + $this->value = substr($this->value, strlen($operator)); + return $method; + } + } + + return ''; + } + + /** + * Apply a date filter + * + * @access protected + * @param string $field + */ + protected function applyDateFilter($field) + { + $method = $this->parseOperator(); + $timestamp = $this->dateParser->getTimestampFromIsoFormat($this->value); + + if ($method !== '') { + $this->query->$method($field, $this->getTimestampFromOperator($method, $timestamp)); + } else { + $this->query->gte($field, $timestamp); + $this->query->lte($field, $timestamp + 86399); + } + } + + /** + * Get timestamp from the operator + * + * @access public + * @param string $method + * @param integer $timestamp + * @return integer + */ + protected function getTimestampFromOperator($method, $timestamp) + { + switch ($method) { + case 'lte': + return $timestamp + 86399; + case 'lt': + return $timestamp; + case 'gte': + return $timestamp; + case 'gt': + return $timestamp + 86400; + } + + return $timestamp; + } +} diff --git a/app/Filter/BaseDateRangeFilter.php b/app/Filter/BaseDateRangeFilter.php new file mode 100644 index 0000000..2bd8378 --- /dev/null +++ b/app/Filter/BaseDateRangeFilter.php @@ -0,0 +1,54 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\DateParser; + +/** + * Base date filter class + * + * @package filter + * @author Kamil Åšciana + */ +abstract class BaseDateRangeFilter extends BaseFilter +{ + /** + * DateParser object + * + * @access protected + * @var DateParser + */ + protected $dateParser; + + /** + * Set DateParser object + * + * @access public + * @param DateParser $dateParser + * @return $this + */ + public function setDateParser(DateParser $dateParser) + { + $this->dateParser = $dateParser; + return $this; + } + + /** + * Apply a date filter + * + * @access protected + * @param string $field + */ + protected function applyDateFilter($field) + { + $dates = explode('..', $this->value); + + if (count($dates)=== 2) { + $timestampFrom = $this->dateParser->getTimestamp($dates[0]." 00:00"); + $timestampTo = $this->dateParser->getTimestamp($dates[1]." 00:00"); + + $this->query->gte($field, $timestampFrom); + $this->query->lte($field, $timestampTo + 86399); + } + } +} diff --git a/app/Filter/BaseFilter.php b/app/Filter/BaseFilter.php new file mode 100644 index 0000000..2687d90 --- /dev/null +++ b/app/Filter/BaseFilter.php @@ -0,0 +1,74 @@ +<?php + +namespace Kanboard\Filter; + +use PicoDb\Table; + +/** + * Base filter class + * + * @package filter + * @author Frederic Guillot + */ +abstract class BaseFilter +{ + /** + * @var Table + */ + protected $query; + + /** + * @var mixed + */ + protected $value; + + /** + * BaseFilter constructor + * + * @access public + * @param mixed $value + */ + public function __construct($value = null) + { + $this->value = $value; + } + + /** + * Get object instance + * + * @static + * @access public + * @param mixed $value + * @return static + */ + public static function getInstance($value = null) + { + return new static($value); + } + + /** + * Set query + * + * @access public + * @param Table $query + * @return $this + */ + public function withQuery(Table $query) + { + $this->query = $query; + return $this; + } + + /** + * Set the value + * + * @access public + * @param string $value + * @return $this + */ + public function withValue($value) + { + $this->value = $value; + return $this; + } +} diff --git a/app/Filter/ProjectActivityCreationDateFilter.php b/app/Filter/ProjectActivityCreationDateFilter.php new file mode 100644 index 0000000..451f654 --- /dev/null +++ b/app/Filter/ProjectActivityCreationDateFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectActivityModel; + +/** + * Filter activity events by creation date + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityCreationDateFilter extends BaseDateFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('created'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(ProjectActivityModel::TABLE.'.date_creation'); + return $this; + } +} diff --git a/app/Filter/ProjectActivityCreatorFilter.php b/app/Filter/ProjectActivityCreatorFilter.php new file mode 100644 index 0000000..1cd0f9b --- /dev/null +++ b/app/Filter/ProjectActivityCreatorFilter.php @@ -0,0 +1,66 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectActivityModel; + +/** + * Filter activity events by creator + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityCreatorFilter extends BaseFilter implements FilterInterface +{ + /** + * Current user id + * + * @access private + * @var int + */ + private $currentUserId = 0; + + /** + * Set current user id + * + * @access public + * @param integer $userId + * @return TaskAssigneeFilter + */ + public function setCurrentUserId($userId) + { + $this->currentUserId = $userId; + return $this; + } + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('creator'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if ($this->value === 'me') { + $this->query->eq(ProjectActivityModel::TABLE . '.creator_id', $this->currentUserId); + } else { + $this->query->beginOr(); + $this->query->ilike('uc.username', '%'.$this->value.'%'); + $this->query->ilike('uc.name', '%'.$this->value.'%'); + $this->query->closeOr(); + } + return $this; + } +} diff --git a/app/Filter/ProjectActivityProjectIdFilter.php b/app/Filter/ProjectActivityProjectIdFilter.php new file mode 100644 index 0000000..7146a05 --- /dev/null +++ b/app/Filter/ProjectActivityProjectIdFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectActivityModel; + +/** + * Filter activity events by projectId + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityProjectIdFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('project_id'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->eq(ProjectActivityModel::TABLE.'.project_id', $this->value); + return $this; + } +} diff --git a/app/Filter/ProjectActivityProjectIdsFilter.php b/app/Filter/ProjectActivityProjectIdsFilter.php new file mode 100644 index 0000000..70968f7 --- /dev/null +++ b/app/Filter/ProjectActivityProjectIdsFilter.php @@ -0,0 +1,43 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectActivityModel; + +/** + * Filter activity events by projectIds + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityProjectIdsFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('projects'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (empty($this->value)) { + $this->query->eq(ProjectActivityModel::TABLE.'.project_id', 0); + } else { + $this->query->in(ProjectActivityModel::TABLE.'.project_id', $this->value); + } + + return $this; + } +} diff --git a/app/Filter/ProjectActivityProjectNameFilter.php b/app/Filter/ProjectActivityProjectNameFilter.php new file mode 100644 index 0000000..b487218 --- /dev/null +++ b/app/Filter/ProjectActivityProjectNameFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectModel; + +/** + * Filter activity events by project name + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityProjectNameFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('project'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->ilike(ProjectModel::TABLE.'.name', '%'.$this->value.'%'); + return $this; + } +} diff --git a/app/Filter/ProjectActivityTaskIdFilter.php b/app/Filter/ProjectActivityTaskIdFilter.php new file mode 100644 index 0000000..b8e074d --- /dev/null +++ b/app/Filter/ProjectActivityTaskIdFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectActivityModel; + +/** + * Filter activity events by taskId + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityTaskIdFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('task_id'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->eq(ProjectActivityModel::TABLE.'.task_id', $this->value); + return $this; + } +} diff --git a/app/Filter/ProjectActivityTaskStatusFilter.php b/app/Filter/ProjectActivityTaskStatusFilter.php new file mode 100644 index 0000000..2c98cab --- /dev/null +++ b/app/Filter/ProjectActivityTaskStatusFilter.php @@ -0,0 +1,43 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter activity events by task status + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityTaskStatusFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('status'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if ($this->value === 'open') { + $this->query->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN); + } elseif ($this->value === 'closed') { + $this->query->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_CLOSED); + } + + return $this; + } +} diff --git a/app/Filter/ProjectActivityTaskTitleFilter.php b/app/Filter/ProjectActivityTaskTitleFilter.php new file mode 100644 index 0000000..bf2afa3 --- /dev/null +++ b/app/Filter/ProjectActivityTaskTitleFilter.php @@ -0,0 +1,25 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; + +/** + * Filter activity events by task title + * + * @package filter + * @author Frederic Guillot + */ +class ProjectActivityTaskTitleFilter extends TaskTitleFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('title'); + } +} diff --git a/app/Filter/ProjectGroupRoleProjectFilter.php b/app/Filter/ProjectGroupRoleProjectFilter.php new file mode 100644 index 0000000..035931b --- /dev/null +++ b/app/Filter/ProjectGroupRoleProjectFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectGroupRoleModel; + +/** + * Filter ProjectGroupRole users by project + * + * @package filter + * @author Frederic Guillot + */ +class ProjectGroupRoleProjectFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array(); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->eq(ProjectGroupRoleModel::TABLE.'.project_id', $this->value); + return $this; + } +} diff --git a/app/Filter/ProjectGroupRoleUsernameFilter.php b/app/Filter/ProjectGroupRoleUsernameFilter.php new file mode 100644 index 0000000..9feac33 --- /dev/null +++ b/app/Filter/ProjectGroupRoleUsernameFilter.php @@ -0,0 +1,44 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\GroupMemberModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Model\UserModel; + +/** + * Filter ProjectGroupRole users by username + * + * @package filter + * @author Frederic Guillot + */ +class ProjectGroupRoleUsernameFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array(); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', ProjectGroupRoleModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', GroupMemberModel::TABLE) + ->ilike(UserModel::TABLE.'.username', $this->value.'%'); + + return $this; + } +} diff --git a/app/Filter/ProjectIdsFilter.php b/app/Filter/ProjectIdsFilter.php new file mode 100644 index 0000000..9af9db5 --- /dev/null +++ b/app/Filter/ProjectIdsFilter.php @@ -0,0 +1,43 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectModel; + +/** + * Filter project by ids + * + * @package filter + * @author Frederic Guillot + */ +class ProjectIdsFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('project_ids'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (empty($this->value)) { + $this->query->eq(ProjectModel::TABLE.'.id', 0); + } else { + $this->query->in(ProjectModel::TABLE.'.id', $this->value); + } + + return $this; + } +} diff --git a/app/Filter/ProjectStatusFilter.php b/app/Filter/ProjectStatusFilter.php new file mode 100644 index 0000000..5379f89 --- /dev/null +++ b/app/Filter/ProjectStatusFilter.php @@ -0,0 +1,45 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectModel; + +/** + * Filter project by status + * + * @package filter + * @author Frederic Guillot + */ +class ProjectStatusFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('status'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (is_int($this->value) || ctype_digit((string) $this->value)) { + $this->query->eq(ProjectModel::TABLE.'.is_active', $this->value); + } elseif ($this->value === 'inactive' || $this->value === 'closed' || $this->value === 'disabled') { + $this->query->eq(ProjectModel::TABLE.'.is_active', 0); + } else { + $this->query->eq(ProjectModel::TABLE.'.is_active', 1); + } + + return $this; + } +} diff --git a/app/Filter/ProjectTypeFilter.php b/app/Filter/ProjectTypeFilter.php new file mode 100644 index 0000000..601d197 --- /dev/null +++ b/app/Filter/ProjectTypeFilter.php @@ -0,0 +1,45 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectModel; + +/** + * Filter project by type + * + * @package filter + * @author Frederic Guillot + */ +class ProjectTypeFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('type'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (is_int($this->value) || ctype_digit((string) $this->value)) { + $this->query->eq(ProjectModel::TABLE.'.is_private', $this->value); + } elseif ($this->value === 'private') { + $this->query->eq(ProjectModel::TABLE.'.is_private', ProjectModel::TYPE_PRIVATE); + } else { + $this->query->eq(ProjectModel::TABLE.'.is_private', ProjectModel::TYPE_TEAM); + } + + return $this; + } +} diff --git a/app/Filter/ProjectUserRoleProjectFilter.php b/app/Filter/ProjectUserRoleProjectFilter.php new file mode 100644 index 0000000..6952364 --- /dev/null +++ b/app/Filter/ProjectUserRoleProjectFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectUserRoleModel; + +/** + * Filter ProjectUserRole users by project + * + * @package filter + * @author Frederic Guillot + */ +class ProjectUserRoleProjectFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array(); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->eq(ProjectUserRoleModel::TABLE.'.project_id', $this->value); + return $this; + } +} diff --git a/app/Filter/ProjectUserRoleUsernameFilter.php b/app/Filter/ProjectUserRoleUsernameFilter.php new file mode 100644 index 0000000..327d3d5 --- /dev/null +++ b/app/Filter/ProjectUserRoleUsernameFilter.php @@ -0,0 +1,41 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\UserModel; + +/** + * Filter ProjectUserRole users by username + * + * @package filter + * @author Frederic Guillot + */ +class ProjectUserRoleUsernameFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array(); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query + ->join(UserModel::TABLE, 'id', 'user_id') + ->ilike(UserModel::TABLE.'.username', $this->value.'%'); + + return $this; + } +} diff --git a/app/Filter/TaskAssigneeFilter.php b/app/Filter/TaskAssigneeFilter.php new file mode 100644 index 0000000..c8c17ab --- /dev/null +++ b/app/Filter/TaskAssigneeFilter.php @@ -0,0 +1,79 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; +use Kanboard\Model\UserModel; + +/** + * Filter tasks by assignee + * + * @package filter + * @author Frederic Guillot + */ +class TaskAssigneeFilter extends BaseFilter implements FilterInterface +{ + /** + * Current user id + * + * @access private + * @var int + */ + private $currentUserId = 0; + + /** + * Set current user id + * + * @access public + * @param integer $userId + * @return TaskAssigneeFilter + */ + public function setCurrentUserId($userId) + { + $this->currentUserId = $userId; + return $this; + } + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('assignee'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (is_int($this->value) || ctype_digit((string) $this->value)) { + $this->query->eq(TaskModel::TABLE.'.owner_id', $this->value); + } else { + switch ($this->value) { + case 'me': + $this->query->eq(TaskModel::TABLE.'.owner_id', $this->currentUserId); + break; + case 'nobody': + $this->query->eq(TaskModel::TABLE.'.owner_id', 0); + break; + case 'anybody': + $this->query->gt(TaskModel::TABLE.'.owner_id', 0); + break; + default: + $this->query->beginOr(); + $this->query->ilike(UserModel::TABLE.'.username', '%'.$this->value.'%'); + $this->query->ilike(UserModel::TABLE.'.name', '%'.$this->value.'%'); + $this->query->closeOr(); + } + } + return $this; + } +} diff --git a/app/Filter/TaskCategoryFilter.php b/app/Filter/TaskCategoryFilter.php new file mode 100644 index 0000000..514d7a5 --- /dev/null +++ b/app/Filter/TaskCategoryFilter.php @@ -0,0 +1,49 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\CategoryModel; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by category + * + * @package filter + * @author Frederic Guillot + */ +class TaskCategoryFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('category'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (is_int($this->value) || ctype_digit((string) $this->value)) { + $this->query->beginOr(); + $this->query->eq(TaskModel::TABLE.'.category_id', $this->value); + $this->query->eq(CategoryModel::TABLE.'.name', $this->value); + $this->query->closeOr(); + } elseif ($this->value === 'none') { + $this->query->eq(TaskModel::TABLE.'.category_id', 0); + } else { + $this->query->eq(CategoryModel::TABLE.'.name', $this->value); + } + + return $this; + } +} diff --git a/app/Filter/TaskColorFilter.php b/app/Filter/TaskColorFilter.php new file mode 100644 index 0000000..2ddb47c --- /dev/null +++ b/app/Filter/TaskColorFilter.php @@ -0,0 +1,60 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ColorModel; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by color + * + * @package filter + * @author Frederic Guillot + */ +class TaskColorFilter extends BaseFilter implements FilterInterface +{ + /** + * Color object + * + * @access private + * @var ColorModel + */ + private $colorModel; + + /** + * Set color model object + * + * @access public + * @param ColorModel $colorModel + * @return TaskColorFilter + */ + public function setColorModel(ColorModel $colorModel) + { + $this->colorModel = $colorModel; + return $this; + } + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('color', 'colour'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->eq(TaskModel::TABLE.'.color_id', $this->colorModel->find($this->value)); + return $this; + } +} diff --git a/app/Filter/TaskColumnFilter.php b/app/Filter/TaskColumnFilter.php new file mode 100644 index 0000000..57db133 --- /dev/null +++ b/app/Filter/TaskColumnFilter.php @@ -0,0 +1,44 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ColumnModel; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by column + * + * @package filter + * @author Frederic Guillot + */ +class TaskColumnFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('column'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (is_int($this->value) || ctype_digit((string) $this->value)) { + $this->query->eq(TaskModel::TABLE.'.column_id', $this->value); + } else { + $this->query->eq(ColumnModel::TABLE.'.title', $this->value); + } + + return $this; + } +} diff --git a/app/Filter/TaskCommentFilter.php b/app/Filter/TaskCommentFilter.php new file mode 100644 index 0000000..e5d9111 --- /dev/null +++ b/app/Filter/TaskCommentFilter.php @@ -0,0 +1,76 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\CommentModel; +use Kanboard\Model\TaskModel; +use PicoDb\Database; + +/** + * Filter tasks by comment + * + * @package filter + * @author Frederic Guillot + */ +class TaskCommentFilter extends BaseFilter implements FilterInterface +{ + /** + * Database object + * + * @access private + * @var Database + */ + private $db; + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('comment'); + } + + /** + * Set database object + * + * @access public + * @param Database $db + * @return $this + */ + public function setDatabase(Database $db) + { + $this->db = $db; + return $this; + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->inSubquery(TaskModel::TABLE.'.id', $this->getSubQuery()); + + return $this; + } + + /** + * Get task ids having this comment + * + * @access public + * @return array + */ + protected function getSubQuery() + { + return $this->db + ->table(CommentModel::TABLE) + ->columns(CommentModel::TABLE.'.task_id') + ->ilike(CommentModel::TABLE.'.comment', '%'.$this->value.'%'); + } +} diff --git a/app/Filter/TaskCompletionDateFilter.php b/app/Filter/TaskCompletionDateFilter.php new file mode 100644 index 0000000..79b5e7d --- /dev/null +++ b/app/Filter/TaskCompletionDateFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by completion date + * + * @package filter + * @author Frederic Guillot + */ +class TaskCompletionDateFilter extends BaseDateFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('completed'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(TaskModel::TABLE.'.date_completed'); + return $this; + } +} diff --git a/app/Filter/TaskCompletionDateRangeFilter.php b/app/Filter/TaskCompletionDateRangeFilter.php new file mode 100644 index 0000000..9272cc1 --- /dev/null +++ b/app/Filter/TaskCompletionDateRangeFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by completion date + * + * @package filter + * @author Kamil Åšciana + */ +class TaskCompletionDateRangeFilter extends BaseDateRangeFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('completedRange'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(TaskModel::TABLE.'.date_completed'); + return $this; + } +} diff --git a/app/Filter/TaskCreationDateFilter.php b/app/Filter/TaskCreationDateFilter.php new file mode 100644 index 0000000..db28ac8 --- /dev/null +++ b/app/Filter/TaskCreationDateFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by creation date + * + * @package filter + * @author Frederic Guillot + */ +class TaskCreationDateFilter extends BaseDateFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('created'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(TaskModel::TABLE.'.date_creation'); + return $this; + } +} diff --git a/app/Filter/TaskCreationDateRangeFilter.php b/app/Filter/TaskCreationDateRangeFilter.php new file mode 100644 index 0000000..7696af0 --- /dev/null +++ b/app/Filter/TaskCreationDateRangeFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by creation date + * + * @package filter + * @author Kamil Åšciana + */ +class TaskCreationDateRangeFilter extends BaseDateRangeFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('createdRange'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(TaskModel::TABLE.'.date_creation'); + return $this; + } +} diff --git a/app/Filter/TaskCreatorFilter.php b/app/Filter/TaskCreatorFilter.php new file mode 100644 index 0000000..8f7037c --- /dev/null +++ b/app/Filter/TaskCreatorFilter.php @@ -0,0 +1,78 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by creator + * + * @package filter + * @author Frederic Guillot + */ +class TaskCreatorFilter extends BaseFilter implements FilterInterface +{ + /** + * Current user id + * + * @access private + * @var int + */ + private $currentUserId = 0; + + /** + * Set current user id + * + * @access public + * @param integer $userId + * @return TaskAssigneeFilter + */ + public function setCurrentUserId($userId) + { + $this->currentUserId = $userId; + return $this; + } + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('creator'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (is_int($this->value) || ctype_digit((string) $this->value)) { + $this->query->eq(TaskModel::TABLE.'.creator_id', $this->value); + } else { + switch ($this->value) { + case 'me': + $this->query->eq(TaskModel::TABLE.'.creator_id', $this->currentUserId); + break; + case 'nobody': + $this->query->eq(TaskModel::TABLE.'.creator_id', 0); + break; + case 'anybody': + $this->query->gt(TaskModel::TABLE.'.creator_id', 0); + break; + default: + $this->query->beginOr(); + $this->query->ilike('uc.username', '%'.$this->value.'%'); + $this->query->ilike('uc.name', '%'.$this->value.'%'); + $this->query->closeOr(); + } + } + return $this; + } +} diff --git a/app/Filter/TaskDescriptionFilter.php b/app/Filter/TaskDescriptionFilter.php new file mode 100644 index 0000000..c73c2f5 --- /dev/null +++ b/app/Filter/TaskDescriptionFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by description + * + * @package filter + * @author Frederic Guillot + */ +class TaskDescriptionFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('description', 'desc'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->ilike(TaskModel::TABLE.'.description', '%'.$this->value.'%'); + return $this; + } +} diff --git a/app/Filter/TaskDueDateFilter.php b/app/Filter/TaskDueDateFilter.php new file mode 100644 index 0000000..194dd43 --- /dev/null +++ b/app/Filter/TaskDueDateFilter.php @@ -0,0 +1,45 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by due date + * + * @package filter + * @author Frederic Guillot + */ +class TaskDueDateFilter extends BaseDateFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('due'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if ($this->value == "none") { + $this->query->eq(TaskModel::TABLE.'.date_due', 0); + } else { + $this->query->neq(TaskModel::TABLE.'.date_due', 0); + $this->query->notNull(TaskModel::TABLE.'.date_due'); + $this->applyDateFilter(TaskModel::TABLE.'.date_due'); + } + + return $this; + } +} diff --git a/app/Filter/TaskDueDateRangeFilter.php b/app/Filter/TaskDueDateRangeFilter.php new file mode 100644 index 0000000..a4250f8 --- /dev/null +++ b/app/Filter/TaskDueDateRangeFilter.php @@ -0,0 +1,44 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by due date range + * + * @package filter + * @author Frederic Guillot + */ +class TaskDueDateRangeFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array(); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->beginOr(); + $this->query->isNull(TaskModel::TABLE.'.date_started'); + $this->query->eq(TaskModel::TABLE.'.date_started', 0); + $this->query->closeOr(); + + $this->query->gte(TaskModel::TABLE.'.date_due', is_numeric($this->value[0]) ? $this->value[0] : strtotime($this->value[0])); + $this->query->lte(TaskModel::TABLE.'.date_due', is_numeric($this->value[1]) ? $this->value[1] : strtotime($this->value[1])); + return $this; + } +} diff --git a/app/Filter/TaskIdExclusionFilter.php b/app/Filter/TaskIdExclusionFilter.php new file mode 100644 index 0000000..20177b2 --- /dev/null +++ b/app/Filter/TaskIdExclusionFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Exclude task ids + * + * @package filter + * @author Frederic Guillot + */ +class TaskIdExclusionFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('exclude'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->notin(TaskModel::TABLE.'.id', $this->value); + return $this; + } +} diff --git a/app/Filter/TaskIdFilter.php b/app/Filter/TaskIdFilter.php new file mode 100644 index 0000000..fdf668b --- /dev/null +++ b/app/Filter/TaskIdFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by id + * + * @package filter + * @author Frederic Guillot + */ +class TaskIdFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('id'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->eq(TaskModel::TABLE.'.id', $this->value); + return $this; + } +} diff --git a/app/Filter/TaskLinkFilter.php b/app/Filter/TaskLinkFilter.php new file mode 100644 index 0000000..ee6ecd1 --- /dev/null +++ b/app/Filter/TaskLinkFilter.php @@ -0,0 +1,79 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\LinkModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\TaskLinkModel; +use PicoDb\Database; +use PicoDb\Table; + +/** + * Filter tasks by link name + * + * @package filter + * @author Frederic Guillot + */ +class TaskLinkFilter extends BaseFilter implements FilterInterface +{ + /** + * Database object + * + * @access private + * @var Database + */ + private $db; + + /** + * Set database object + * + * @access public + * @param Database $db + * @return TaskLinkFilter + */ + public function setDatabase(Database $db) + { + $this->db = $db; + return $this; + } + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('link'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->inSubquery(TaskModel::TABLE.'.id', $this->getSubQuery()); + return $this; + } + + /** + * Get subquery + * + * @access protected + * @return Table + */ + protected function getSubQuery() + { + return $this->db->table(TaskLinkModel::TABLE) + ->columns( + TaskLinkModel::TABLE.'.task_id' + ) + ->join(LinkModel::TABLE, 'id', 'link_id', TaskLinkModel::TABLE) + ->ilike(LinkModel::TABLE.'.label', $this->value); + } +} diff --git a/app/Filter/TaskModificationDateFilter.php b/app/Filter/TaskModificationDateFilter.php new file mode 100644 index 0000000..316f183 --- /dev/null +++ b/app/Filter/TaskModificationDateFilter.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 TaskModificationDateFilter extends BaseDateFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('updated', 'modified'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(TaskModel::TABLE.'.date_modification'); + return $this; + } +} diff --git a/app/Filter/TaskModificationDateRangeFilter.php b/app/Filter/TaskModificationDateRangeFilter.php new file mode 100644 index 0000000..3daa2b5 --- /dev/null +++ b/app/Filter/TaskModificationDateRangeFilter.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 Kamil Åšciana + */ +class TaskModificationDateRangeFilter extends BaseDateRangeFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('updatedRange', 'modifiedRange'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(TaskModel::TABLE.'.date_modification'); + return $this; + } +} diff --git a/app/Filter/TaskMovedDateFilter.php b/app/Filter/TaskMovedDateFilter.php new file mode 100644 index 0000000..d57b7d2 --- /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/TaskMovedDateRangeFilter.php b/app/Filter/TaskMovedDateRangeFilter.php new file mode 100644 index 0000000..b5b826b --- /dev/null +++ b/app/Filter/TaskMovedDateRangeFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by creation date + * + * @package filter + * @author Kamil Åšciana + */ +class TaskMovedDateRangeFilter extends BaseDateRangeFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('movedRange'); + } + + /** + * 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 0000000..e0c446c --- /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 BaseComparisonFilter 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->applyComparisonFilter(TaskModel::TABLE.'.priority'); + return $this; + } +} diff --git a/app/Filter/TaskProjectFilter.php b/app/Filter/TaskProjectFilter.php new file mode 100644 index 0000000..0acb72e --- /dev/null +++ b/app/Filter/TaskProjectFilter.php @@ -0,0 +1,46 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by project + * + * @package filter + * @author Frederic Guillot + */ +class TaskProjectFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('project'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + // Max integer value for Postgres is +2147483647 + // See https://www.postgresql.org/docs/current/datatype-numeric.html + if (is_int($this->value) || ctype_digit((string) $this->value) && $this->value < 2147483647) { + $this->query->eq(TaskModel::TABLE.'.project_id', $this->value); + } else { + $this->query->ilike(ProjectModel::TABLE.'.name', $this->value); + } + + return $this; + } +} diff --git a/app/Filter/TaskProjectsFilter.php b/app/Filter/TaskProjectsFilter.php new file mode 100644 index 0000000..2b6b16c --- /dev/null +++ b/app/Filter/TaskProjectsFilter.php @@ -0,0 +1,43 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by project ids + * + * @package filter + * @author Frederic Guillot + */ +class TaskProjectsFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('projects'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (empty($this->value)) { + $this->query->eq(TaskModel::TABLE.'.project_id', 0); + } else { + $this->query->in(TaskModel::TABLE.'.project_id', $this->value); + } + + return $this; + } +} diff --git a/app/Filter/TaskReferenceFilter.php b/app/Filter/TaskReferenceFilter.php new file mode 100644 index 0000000..612be81 --- /dev/null +++ b/app/Filter/TaskReferenceFilter.php @@ -0,0 +1,51 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by reference + * + * @package filter + * @author Frederic Guillot + */ +class TaskReferenceFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('reference', 'ref'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if ($this->value === 'none') { + $this->query->beginOr(); + $this->query->eq(TaskModel::TABLE.'.reference', ''); + $this->query->isNull(TaskModel::TABLE.'.reference'); + $this->query->closeOr(); + return $this; + } + + if (strpos($this->value, '*') >= 0) { + $this->query->ilike(TaskModel::TABLE.'.reference', str_replace('*', '%', $this->value)); + return $this; + } + + $this->query->eq(TaskModel::TABLE.'.reference', $this->value); + return $this; + } +} diff --git a/app/Filter/TaskScoreFilter.php b/app/Filter/TaskScoreFilter.php new file mode 100644 index 0000000..2c4067f --- /dev/null +++ b/app/Filter/TaskScoreFilter.php @@ -0,0 +1,37 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Class TaskScoreFilter + * + * @package Kanboard\Filter + */ +class TaskScoreFilter extends BaseComparisonFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('score', 'complexity'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyComparisonFilter(TaskModel::TABLE.'.score'); + return $this; + } +} diff --git a/app/Filter/TaskStartDateFilter.php b/app/Filter/TaskStartDateFilter.php new file mode 100644 index 0000000..d5abedb --- /dev/null +++ b/app/Filter/TaskStartDateFilter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by start date + * + * @package filter + * @author Frederic Guillot + */ +class TaskStartDateFilter extends BaseDateFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('started'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->applyDateFilter(TaskModel::TABLE.'.date_started'); + return $this; + } +} diff --git a/app/Filter/TaskStartsWithIdFilter.php b/app/Filter/TaskStartsWithIdFilter.php new file mode 100644 index 0000000..997683a --- /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 new file mode 100644 index 0000000..b5d55f3 --- /dev/null +++ b/app/Filter/TaskStatusFilter.php @@ -0,0 +1,43 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by status + * + * @package filter + * @author Frederic Guillot + */ +class TaskStatusFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('status'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if ($this->value === 'open' || $this->value === 'closed') { + $this->query->eq(TaskModel::TABLE.'.is_active', $this->value === 'open' ? TaskModel::STATUS_OPEN : TaskModel::STATUS_CLOSED); + } elseif (is_int($this->value) || ctype_digit((string) $this->value)) { + $this->query->eq(TaskModel::TABLE.'.is_active', $this->value); + } + + return $this; + } +} diff --git a/app/Filter/TaskSubtaskAssigneeFilter.php b/app/Filter/TaskSubtaskAssigneeFilter.php new file mode 100644 index 0000000..743483c --- /dev/null +++ b/app/Filter/TaskSubtaskAssigneeFilter.php @@ -0,0 +1,133 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\SubtaskModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\UserModel; +use PicoDb\Database; +use PicoDb\Table; + +/** + * Filter tasks by subtasks assignee + * + * @package filter + * @author Frederic Guillot + */ +class TaskSubtaskAssigneeFilter extends BaseFilter implements FilterInterface +{ + /** + * Database object + * + * @access private + * @var Database + */ + private $db; + + /** + * Current user id + * + * @access private + * @var int + */ + private $currentUserId = 0; + + /** + * Set current user id + * + * @access public + * @param integer $userId + * @return TaskSubtaskAssigneeFilter + */ + public function setCurrentUserId($userId) + { + $this->currentUserId = $userId; + return $this; + } + + /** + * Set database object + * + * @access public + * @param Database $db + * @return TaskSubtaskAssigneeFilter + */ + public function setDatabase(Database $db) + { + $this->db = $db; + return $this; + } + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('subtask:assignee'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->inSubquery(TaskModel::TABLE.'.id', $this->getSubQuery()); + return $this; + } + + /** + * Get subquery + * + * @access protected + * @return Table + */ + protected function getSubQuery() + { + $subquery = $this->db->table(SubtaskModel::TABLE) + ->columns(SubtaskModel::TABLE.'.task_id') + ->join(UserModel::TABLE, 'id', 'user_id', SubtaskModel::TABLE) + ->neq(SubtaskModel::TABLE.'.status', SubtaskModel::STATUS_DONE); + + return $this->applySubQueryFilter($subquery); + } + + /** + * Apply subquery filter + * + * @access protected + * @param Table $subquery + * @return Table + */ + protected function applySubQueryFilter(Table $subquery) + { + if (is_int($this->value) || ctype_digit((string) $this->value)) { + $subquery->eq(SubtaskModel::TABLE.'.user_id', $this->value); + } else { + switch ($this->value) { + case 'me': + $subquery->eq(SubtaskModel::TABLE.'.user_id', $this->currentUserId); + break; + case 'nobody': + $subquery->eq(SubtaskModel::TABLE.'.user_id', 0); + break; + case 'anybody': + $subquery->gt(SubtaskModel::TABLE.'.user_id', 0); + break; + default: + $subquery->beginOr(); + $subquery->ilike(UserModel::TABLE.'.username', $this->value.'%'); + $subquery->ilike(UserModel::TABLE.'.name', '%'.$this->value.'%'); + $subquery->closeOr(); + } + } + + return $subquery; + } +} diff --git a/app/Filter/TaskSwimlaneFilter.php b/app/Filter/TaskSwimlaneFilter.php new file mode 100644 index 0000000..a339ec8 --- /dev/null +++ b/app/Filter/TaskSwimlaneFilter.php @@ -0,0 +1,40 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\SwimlaneModel; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by swimlane + * + * @package filter + * @author Frederic Guillot + */ +class TaskSwimlaneFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('swimlane'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->ilike(SwimlaneModel::TABLE.'.name', $this->value); + return $this; + } +} diff --git a/app/Filter/TaskTagFilter.php b/app/Filter/TaskTagFilter.php new file mode 100644 index 0000000..ea76717 --- /dev/null +++ b/app/Filter/TaskTagFilter.php @@ -0,0 +1,87 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TagModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\TaskTagModel; +use PicoDb\Database; + +/** + * Class TaskTagFilter + * + * @package Kanboard\Filter + * @author Frederic Guillot + */ +class TaskTagFilter extends BaseFilter implements FilterInterface +{ + /** + * Database object + * + * @access private + * @var Database + */ + private $db; + + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('tag'); + } + + /** + * Set database object + * + * @access public + * @param Database $db + * @return $this + */ + public function setDatabase(Database $db) + { + $this->db = $db; + return $this; + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if ($this->value === 'none') { + $sub_query = $this->getQueryOfTaskIdsWithoutTags(); + } else { + $sub_query = $this->getQueryOfTaskIdsWithGivenTag(); + } + + $this->query->inSubquery(TaskModel::TABLE.'.id', $sub_query); + + return $this; + } + + protected function getQueryOfTaskIdsWithoutTags() + { + return $this->db + ->table(TaskModel::TABLE) + ->columns(TaskModel::TABLE . '.id') + ->left(TaskTagModel::TABLE, 'tg', 'task_id', TaskModel::TABLE, 'id') + ->isNull('tg.tag_id'); + } + + protected function getQueryOfTaskIdsWithGivenTag() + { + return $this->db + ->table(TagModel::TABLE) + ->columns(TaskTagModel::TABLE.'.task_id') + ->ilike(TagModel::TABLE.'.name', '%'.$this->value.'%') + ->join(TaskTagModel::TABLE, 'tag_id', 'id'); + } +} diff --git a/app/Filter/TaskTitleFilter.php b/app/Filter/TaskTitleFilter.php new file mode 100644 index 0000000..475ed60 --- /dev/null +++ b/app/Filter/TaskTitleFilter.php @@ -0,0 +1,46 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; +use Kanboard\Model\TaskModel; + +/** + * Filter tasks by title + * + * @package filter + * @author Frederic Guillot + */ +class TaskTitleFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('title'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + if (ctype_digit((string) $this->value) || (strlen($this->value) > 1 && $this->value[0] === '#' && ctype_digit(substr($this->value, 1)))) { + $this->query->beginOr(); + $this->query->eq(TaskModel::TABLE.'.id', str_replace('#', '', $this->value)); + $this->query->ilike(TaskModel::TABLE.'.title', '%'.$this->value.'%'); + $this->query->closeOr(); + } else { + $this->query->ilike(TaskModel::TABLE.'.title', '%'.$this->value.'%'); + } + + return $this; + } +} diff --git a/app/Filter/UserNameFilter.php b/app/Filter/UserNameFilter.php new file mode 100644 index 0000000..dfb07fd --- /dev/null +++ b/app/Filter/UserNameFilter.php @@ -0,0 +1,35 @@ +<?php + +namespace Kanboard\Filter; + +use Kanboard\Core\Filter\FilterInterface; + +class UserNameFilter extends BaseFilter implements FilterInterface +{ + /** + * Get search attribute + * + * @access public + * @return string[] + */ + public function getAttributes() + { + return array('name'); + } + + /** + * Apply filter + * + * @access public + * @return FilterInterface + */ + public function apply() + { + $this->query->beginOr() + ->ilike('username', '%'.$this->value.'%') + ->ilike('name', '%'.$this->value.'%') + ->closeOr(); + + return $this; + } +} diff --git a/app/Formatter/BaseFormatter.php b/app/Formatter/BaseFormatter.php new file mode 100644 index 0000000..0d62628 --- /dev/null +++ b/app/Formatter/BaseFormatter.php @@ -0,0 +1,36 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Base; +use PicoDb\Table; + +/** + * Class BaseFormatter + * + * @package formatter + * @author Frederic Guillot + */ +abstract class BaseFormatter extends Base +{ + /** + * Query object + * + * @access protected + * @var Table + */ + protected $query; + + /** + * Set query + * + * @access public + * @param Table $query + * @return $this + */ + public function withQuery(Table $query) + { + $this->query = $query; + return $this; + } +} diff --git a/app/Formatter/BoardColumnFormatter.php b/app/Formatter/BoardColumnFormatter.php new file mode 100644 index 0000000..0bfc210 --- /dev/null +++ b/app/Formatter/BoardColumnFormatter.php @@ -0,0 +1,115 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Board Column Formatter + * + * @package formatter + * @author Frederic Guillot + */ +class BoardColumnFormatter extends BaseFormatter implements FormatterInterface +{ + protected $swimlaneId = 0; + protected $columns = array(); + protected $tasks = array(); + protected $tags = array(); + protected $taskCountBySwimlaneAndColumn = array(); + + /** + * Set swimlaneId + * + * @access public + * @param integer $swimlaneId + * @return $this + */ + public function withSwimlaneId($swimlaneId) + { + $this->swimlaneId = $swimlaneId; + return $this; + } + + /** + * Set columns + * + * @access public + * @param array $columns + * @return $this + */ + public function withColumns(array $columns) + { + $this->columns = $columns; + return $this; + } + + /** + * Set tasks + * + * @access public + * @param array $tasks + * @return $this + */ + public function withTasks(array $tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Set tags + * + * @access public + * @param array $tags + * @return $this + */ + public function withTags(array $tags) + { + $this->tags = $tags; + return $this; + } + + /** + * Set task count by swimlane and column + * + * @access public + * @param array $taskCountBySwimlaneAndColumn + * @return $this + */ + public function withTaskCountBySwimlaneAndColumn(array $taskCountBySwimlaneAndColumn) + { + $this->taskCountBySwimlaneAndColumn = $taskCountBySwimlaneAndColumn; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + foreach ($this->columns as &$column) { + $column['id'] = (int) $column['id']; + $column['tasks'] = $this->boardTaskFormatter + ->withTasks($this->tasks) + ->withTags($this->tags) + ->withSwimlaneId($this->swimlaneId) + ->withColumnId($column['id']) + ->format(); + + $column['nb_tasks'] = count($column['tasks']); + $column['score'] = (int) array_column_sum($column['tasks'], 'score'); + + if (empty($this->taskCountBySwimlaneAndColumn)) { + $column['column_nb_open_tasks'] = $column['nb_open_tasks']; + } else { + $column['column_nb_open_tasks'] = isset($this->taskCountBySwimlaneAndColumn[$this->swimlaneId][$column['id']]) ? $this->taskCountBySwimlaneAndColumn[$this->swimlaneId][$column['id']] : 0; + } + } + + return $this->columns; + } +} diff --git a/app/Formatter/BoardFormatter.php b/app/Formatter/BoardFormatter.php new file mode 100644 index 0000000..53b149e --- /dev/null +++ b/app/Formatter/BoardFormatter.php @@ -0,0 +1,80 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\SwimlaneModel; +use Kanboard\Model\TaskModel; + +/** + * Board Formatter + * + * @package formatter + * @author Frederic Guillot + */ +class BoardFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Project id + * + * @access protected + * @var integer + */ + protected $projectId; + + /** + * Set ProjectId + * + * @access public + * @param integer $projectId + * @return $this + */ + public function withProjectId($projectId) + { + $this->projectId = $projectId; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $project = $this->projectModel->getById($this->projectId); + $swimlanes = $this->swimlaneModel->getAllByStatus($this->projectId, SwimlaneModel::ACTIVE); + $columns = $this->columnModel->getAllWithOpenedTaskCount($this->projectId); + $task_count_by_swimlanes_and_columns = []; + + if ($project['per_swimlane_task_limits']) { + foreach ($this->taskModel->getOpenTaskCountBySwimlaneAndColumn($this->projectId) as $task_count) { + $task_count_by_swimlanes_and_columns[$task_count['swimlane_id']][$task_count['column_id']] = $task_count['nb_open_tasks']; + } + } + + if (empty($swimlanes) || empty($columns)) { + return array(); + } + + $this->hook->reference('formatter:board:query', $this->query); + + $tasks = $this->query + ->eq(TaskModel::TABLE.'.project_id', $this->projectId) + ->asc(TaskModel::TABLE.'.position') + ->findAll(); + + $task_ids = array_column($tasks, 'id'); + $tags = $this->taskTagModel->getTagsByTaskIds($task_ids); + + return $this->boardSwimlaneFormatter + ->withSwimlanes($swimlanes) + ->withColumns($columns) + ->withTasks($tasks) + ->withTags($tags) + ->withTaskCountBySwimlaneAndColumn($task_count_by_swimlanes_and_columns) + ->format(); + } +} diff --git a/app/Formatter/BoardSwimlaneFormatter.php b/app/Formatter/BoardSwimlaneFormatter.php new file mode 100644 index 0000000..3046d69 --- /dev/null +++ b/app/Formatter/BoardSwimlaneFormatter.php @@ -0,0 +1,138 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Board Swimlane Formatter + * + * @package formatter + * @author Frederic Guillot + */ +class BoardSwimlaneFormatter extends BaseFormatter implements FormatterInterface +{ + protected $swimlanes = array(); + protected $columns = array(); + protected $tasks = array(); + protected $tags = array(); + protected $taskCountBySwimlaneAndColumn = array(); + + /** + * Set swimlanes + * + * @access public + * @param array $swimlanes + * @return $this + */ + public function withSwimlanes(array $swimlanes) + { + $this->swimlanes = $swimlanes; + return $this; + } + + /** + * Set columns + * + * @access public + * @param array $columns + * @return $this + */ + public function withColumns(array $columns) + { + $this->columns = $columns; + return $this; + } + + /** + * Set tasks + * + * @access public + * @param array $tasks + * @return $this + */ + public function withTasks(array $tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Set tags + * + * @access public + * @param array $tags + * @return $this + */ + public function withTags(array $tags) + { + $this->tags = $tags; + return $this; + } + + /** + * Set task count by swimlane and column + * + * @access public + * @param array $taskCountBySwimlaneAndColumn + * @return $this + */ + public function withTaskCountBySwimlaneAndColumn(array $taskCountBySwimlaneAndColumn) + { + $this->taskCountBySwimlaneAndColumn = $taskCountBySwimlaneAndColumn; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $nb_swimlanes = count($this->swimlanes); + $nb_columns = count($this->columns); + $tasks_stats_by_column_across_swimlanes = []; + + foreach ($this->columns as $column) { + $tasks_stats_by_column_across_swimlanes[$column['id']] = [ + 'nb_visible_tasks_across_swimlane' => 0, + 'nb_unfiltered_tasks_across_swimlane' => 0, + 'cumulative_score_across_swimlane' => 0, + ]; + } + + foreach ($this->swimlanes as &$swimlane) { + $swimlane['id'] = (int) $swimlane['id']; + $swimlane['columns'] = $this->boardColumnFormatter + ->withSwimlaneId($swimlane['id']) + ->withColumns($this->columns) + ->withTasks($this->tasks) + ->withTags($this->tags) + ->withTaskCountBySwimlaneAndColumn($this->taskCountBySwimlaneAndColumn) + ->format(); + + $swimlane['nb_swimlanes'] = $nb_swimlanes; + $swimlane['nb_columns'] = $nb_columns; + $swimlane['nb_tasks'] = array_column_sum($swimlane['columns'], 'nb_tasks'); + $swimlane['score'] = array_column_sum($swimlane['columns'], 'score'); + + foreach ($swimlane['columns'] as &$column) { + $tasks_stats_by_column_across_swimlanes[$column['id']]['nb_visible_tasks_across_swimlane'] += count($column['tasks']); + $tasks_stats_by_column_across_swimlanes[$column['id']]['nb_unfiltered_tasks_across_swimlane'] = $column['nb_open_tasks']; + $tasks_stats_by_column_across_swimlanes[$column['id']]['cumulative_score_across_swimlane'] += $column['score']; + } + } + + foreach ($this->swimlanes as &$swimlane) { + foreach ($swimlane['columns'] as &$column) { + $column['nb_visible_tasks_across_swimlane'] = $tasks_stats_by_column_across_swimlanes[$column['id']]['nb_visible_tasks_across_swimlane']; + $column['nb_unfiltered_tasks_across_swimlane'] = $tasks_stats_by_column_across_swimlanes[$column['id']]['nb_unfiltered_tasks_across_swimlane']; + $column['cumulative_score_across_swimlane'] = $tasks_stats_by_column_across_swimlanes[$column['id']]['cumulative_score_across_swimlane']; + } + } + + return $this->swimlanes; + } +} diff --git a/app/Formatter/BoardTaskFormatter.php b/app/Formatter/BoardTaskFormatter.php new file mode 100644 index 0000000..cd10f77 --- /dev/null +++ b/app/Formatter/BoardTaskFormatter.php @@ -0,0 +1,101 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Board Task Formatter + * + * @package formatter + * @author Frederic Guillot + */ +class BoardTaskFormatter extends BaseFormatter implements FormatterInterface +{ + protected $tasks = array(); + protected $tags = array(); + protected $columnId = 0; + protected $swimlaneId = 0; + + /** + * Set tags + * + * @access public + * @param array $tags + * @return $this + */ + public function withTags(array $tags) + { + $this->tags = $tags; + return $this; + } + + /** + * Set tasks + * + * @access public + * @param array $tasks + * @return $this + */ + public function withTasks(array $tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Set columnId + * + * @access public + * @param integer $columnId + * @return $this + */ + public function withColumnId($columnId) + { + $this->columnId = $columnId; + return $this; + } + + /** + * Set swimlaneId + * + * @access public + * @param integer $swimlaneId + * @return $this + */ + public function withSwimlaneId($swimlaneId) + { + $this->swimlaneId = $swimlaneId; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $tasks = array_values(array_filter($this->tasks, array($this, 'filterTasks'))); + array_merge_relation($tasks, $this->tags, 'tags', 'id'); + + foreach ($tasks as &$task) { + $task['is_draggable'] = $this->helper->projectRole->isDraggable($task); + } + + return $tasks; + } + + /** + * Keep only tasks of the given column and swimlane + * + * @access protected + * @param array $task + * @return bool + */ + protected function filterTasks(array $task) + { + return $task['column_id'] == $this->columnId && $task['swimlane_id'] == $this->swimlaneId; + } +} diff --git a/app/Formatter/GroupAutoCompleteFormatter.php b/app/Formatter/GroupAutoCompleteFormatter.php new file mode 100644 index 0000000..9d740b7 --- /dev/null +++ b/app/Formatter/GroupAutoCompleteFormatter.php @@ -0,0 +1,58 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; +use Kanboard\Core\Group\GroupProviderInterface; + +/** + * Auto-complete formatter for groups + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class GroupAutoCompleteFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Groups found + * + * @access protected + * @var GroupProviderInterface[] + */ + protected $groups; + + /** + * Set groups + * + * @access public + * @param GroupProviderInterface[] $groups + * @return $this + */ + public function withGroups(array $groups) + { + $this->groups = $groups; + return $this; + } + + /** + * Format groups for the ajax auto-completion + * + * @access public + * @return array + */ + public function format() + { + $result = array(); + + foreach ($this->groups as $group) { + $result[] = array( + 'id' => $group->getInternalId(), + 'external_id' => $group->getExternalId(), + 'value' => $group->getName(), + 'label' => $group->getName(), + ); + } + + return $result; + } +} diff --git a/app/Formatter/ProjectActivityEventFormatter.php b/app/Formatter/ProjectActivityEventFormatter.php new file mode 100644 index 0000000..3741c2a --- /dev/null +++ b/app/Formatter/ProjectActivityEventFormatter.php @@ -0,0 +1,79 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; +use Kanboard\Core\Security\Role; + +class ProjectActivityEventFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $events = $this->query->findAll(); + $res = array(); + + foreach ($events as &$event) { + $eventData = $this->unserializeEvent($event['data']); + if (empty($eventData)) { + continue; + } + + $event += $eventData; + unset($event['data']); + + if (isset($event['comment'])) { + if ($this->userSession->getRole() === Role::APP_USER && $event['comment']['visibility'] !== Role::APP_USER) { + continue; + } + if ($this->userSession->getRole() === Role::APP_MANAGER && $event['comment']['visibility'] === Role::APP_ADMIN) { + continue; + } + } + + $event['author'] = $event['author_name'] ?: $event['author_username']; + $event['event_title'] = $this->notificationModel->getTitleWithAuthor($event['author'], $event['event_name'], $event); + $event['event_content'] = $this->renderEvent($event); + $res[] = $event; + } + + return $res; + } + + /** + * Decode event data, supports unserialize() and json_decode() + * + * @access protected + * @param string $data Serialized data + * @return array + */ + protected function unserializeEvent($data) + { + // Ignore legacy events serialized with PHP due potential security issues. + if ($data[0] === 'a') { + return []; + } + + return json_decode($data, true) ?: []; + } + + /** + * Get the event html content + * + * @access protected + * @param array $params Event properties + * @return string + */ + protected function renderEvent(array $params) + { + return $this->template->render( + 'event/'.str_replace('.', '_', $params['event_name']), + $params + ); + } +} diff --git a/app/Formatter/ProjectApiFormatter.php b/app/Formatter/ProjectApiFormatter.php new file mode 100644 index 0000000..fbb6cdf --- /dev/null +++ b/app/Formatter/ProjectApiFormatter.php @@ -0,0 +1,46 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class ProjectApiFormatter + * + * @package Kanboard\Formatter + */ +class ProjectApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $project = null; + + public function withProject($project) + { + $this->project = $project; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->project)) { + $this->project['url'] = array( + 'board' => $this->helper->url->to('BoardViewController', 'show', array('project_id' => $this->project['id']), '', true), + 'list' => $this->helper->url->to('TaskListController', 'show', array('project_id' => $this->project['id']), '', true), + ); + + // Add public board URL if public access is enabled + if (!empty($this->project['is_public']) && !empty($this->project['token'])) { + $this->project['url']['public_board'] = $this->helper->url->to('BoardViewController', 'readonly', array('token' => $this->project['token']), '', true); + $this->project['url']['rss_feed'] = $this->helper->url->to('FeedController', 'project', array('token' => $this->project['token']), '', true); + $this->project['url']['ical_feed'] = $this->helper->url->to('ICalendarController', 'project', array('token' => $this->project['token']), '', true); + } + } + + return $this->project; + } +} diff --git a/app/Formatter/ProjectsApiFormatter.php b/app/Formatter/ProjectsApiFormatter.php new file mode 100644 index 0000000..0bf97da --- /dev/null +++ b/app/Formatter/ProjectsApiFormatter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class ProjectsApiFormatter + * + * @package Kanboard\Formatter + */ +class ProjectsApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $projects = array(); + + public function withProjects($projects) + { + $this->projects = $projects; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->projects)) { + foreach ($this->projects as &$project) { + $project = $this->projectApiFormatter->withProject($project)->format(); + } + } + + return $this->projects; + } +} diff --git a/app/Formatter/SubtaskListFormatter.php b/app/Formatter/SubtaskListFormatter.php new file mode 100644 index 0000000..8af5b1f --- /dev/null +++ b/app/Formatter/SubtaskListFormatter.php @@ -0,0 +1,34 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class SubtaskListFormatter + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class SubtaskListFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $status = $this->subtaskModel->getStatusList(); + $subtasks = $this->query->findAll(); + + 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; + } +} diff --git a/app/Formatter/SubtaskTimeTrackingCalendarFormatter.php b/app/Formatter/SubtaskTimeTrackingCalendarFormatter.php new file mode 100644 index 0000000..0346172 --- /dev/null +++ b/app/Formatter/SubtaskTimeTrackingCalendarFormatter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +class SubtaskTimeTrackingCalendarFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Format calendar events + * + * @access public + * @return array + */ + public function format() + { + $events = array(); + + foreach ($this->query->findAll() as $row) { + $user = isset($row['username']) ? ' ('.($row['user_fullname'] ?: $row['username']).')' : ''; + + $events[] = array( + 'id' => $row['id'], + 'subtask_id' => $row['subtask_id'], + 'title' => t('#%d', $row['task_id']).' '.$row['subtask_title'].$user, + 'start' => date('Y-m-d\TH:i:s', $row['start']), + 'end' => date('Y-m-d\TH:i:s', $row['end'] ?: time()), + 'backgroundColor' => $this->colorModel->getBackgroundColor($row['color_id']), + 'borderColor' => $this->colorModel->getBorderColor($row['color_id']), + 'textColor' => 'black', + 'url' => $this->helper->url->to('TaskViewController', 'show', array('task_id' => $row['task_id'])), + 'editable' => false, + ); + } + + return $events; + } +} diff --git a/app/Formatter/TaskApiFormatter.php b/app/Formatter/TaskApiFormatter.php new file mode 100644 index 0000000..f2e25d0 --- /dev/null +++ b/app/Formatter/TaskApiFormatter.php @@ -0,0 +1,37 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class TaskApiFormatter + * + * @package Kanboard\Formatter + */ +class TaskApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $task = null; + + public function withTask($task) + { + $this->task = $task; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->task)) { + $this->task['url'] = $this->helper->url->to('TaskViewController', 'show', array('task_id' => $this->task['id']), '', true); + $this->task['color'] = $this->colorModel->getColorProperties($this->task['color_id']); + } + + return $this->task; + } +} diff --git a/app/Formatter/TaskAutoCompleteFormatter.php b/app/Formatter/TaskAutoCompleteFormatter.php new file mode 100644 index 0000000..3a4f1e1 --- /dev/null +++ b/app/Formatter/TaskAutoCompleteFormatter.php @@ -0,0 +1,56 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\TaskModel; + +/** + * Task AutoComplete Formatter + * + * @package formatter + * @author Frederic Guillot + */ +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 + * + * @access public + * @return array + */ + public function format() + { + $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['project_name'].' > #'.$task['id'].' '.$task['title']; + } + + return $tasks; + } +} diff --git a/app/Formatter/TaskICalFormatter.php b/app/Formatter/TaskICalFormatter.php new file mode 100644 index 0000000..abe595f --- /dev/null +++ b/app/Formatter/TaskICalFormatter.php @@ -0,0 +1,151 @@ +<?php + +namespace Kanboard\Formatter; + +use DateTime; +use Eluceo\iCal\Component\Calendar; +use Eluceo\iCal\Component\Event; +use Eluceo\iCal\Property\Event\Attendees; +use Eluceo\iCal\Property\Event\Organizer; +use Kanboard\Core\Filter\FormatterInterface; +use PicoDb\Table; + +/** + * iCal event formatter for tasks + * + * @package formatter + * @author Frederic Guillot + */ +class TaskICalFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Calendar object + * + * @access protected + * @var \Eluceo\iCal\Component\Calendar + */ + protected $vCalendar; + + /** + * Get Ical events + * + * @access public + * @return string + */ + public function format() + { + return $this->vCalendar->render(); + } + + /** + * Set calendar object + * + * @access public + * @param \Eluceo\iCal\Component\Calendar $vCalendar + * @return $this + */ + public function setCalendar(Calendar $vCalendar) + { + $this->vCalendar = $vCalendar; + return $this; + } + + /** + * Transform results to iCal events + * + * @access public + * @param Table $query + * @param string $startColumn + * @param string $endColumn + * @return $this + */ + public function addTasksWithStartAndDueDate(Table $query, $startColumn, $endColumn) + { + foreach ($query->findAll() as $task) { + $start = new DateTime; + $start->setTimestamp($task[$startColumn]); + + $end = new DateTime; + $end->setTimestamp($task[$endColumn] ?: time()); + + $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-'.$startColumn.'-'.$endColumn); + $vEvent->setDtStart($start); + $vEvent->setDtEnd($end); + + $this->vCalendar->addComponent($vEvent); + } + + return $this; + } + + /** + * Transform results to all day iCal events + * + * @access public + * @param Table $query + * @return $this + */ + public function addTasksWithDueDateOnly(Table $query) + { + foreach ($query->findAll() as $task) { + $date = new DateTime; + $date->setTimestamp($task['date_due']); + + $vEvent = $this->getTaskIcalEvent($task, 'task-#'.$task['id'].'-date_due'); + $vEvent->setDtStart($date); + $vEvent->setDtEnd($date); + + if ($date->format('Hi') === '0000') { + $vEvent->setNoTime(true); + } + + $this->vCalendar->addComponent($vEvent); + } + + return $this; + } + + /** + * Get common events for task iCal events + * + * @access protected + * @param array $task + * @param string $uid + * @return Event + */ + protected function getTaskIcalEvent(array &$task, $uid) + { + $dateCreation = new DateTime; + $dateCreation->setTimestamp($task['date_creation']); + + $dateModif = new DateTime; + $dateModif->setTimestamp($task['date_modification']); + + $vEvent = new Event($uid); + $vEvent->setCreated($dateCreation); + $vEvent->setModified($dateModif); + $vEvent->setUseTimezone(true); + $vEvent->setSummary(t('#%d', $task['id']).' '.$task['title']); + $vEvent->setDescription($task['description']); + $vEvent->setDescriptionHTML($this->helper->text->markdown($task['description'])); + $vEvent->setUrl($this->helper->url->base().$this->helper->url->to('TaskViewController', 'show', array('task_id' => $task['id']))); + + if (! empty($task['owner_id'])) { + $attendees = new Attendees; + $attendees->add( + 'MAILTO:'.($task['assignee_email'] ?: $task['assignee_username'].'@kanboard.local'), + array('CN' => $task['assignee_name'] ?: $task['assignee_username']) + ); + $vEvent->setAttendees($attendees); + } + + if (! empty($task['creator_id'])) { + $vEvent->setOrganizer(new Organizer( + 'MAILTO:' . $task['creator_email'] ?: $task['creator_username'].'@kanboard.local', + array('CN' => $task['creator_name'] ?: $task['creator_username']) + )); + } + + return $vEvent; + } +} diff --git a/app/Formatter/TaskListFormatter.php b/app/Formatter/TaskListFormatter.php new file mode 100644 index 0000000..2f83469 --- /dev/null +++ b/app/Formatter/TaskListFormatter.php @@ -0,0 +1,30 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class TaskListFormatter + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class TaskListFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $tasks = $this->query->findAll(); + $taskIds = array_column($tasks, 'id'); + $tags = $this->taskTagModel->getTagsByTaskIds($taskIds); + array_merge_relation($tasks, $tags, 'tags', 'id'); + + return $tasks; + } +} diff --git a/app/Formatter/TaskListSubtaskAssigneeFormatter.php b/app/Formatter/TaskListSubtaskAssigneeFormatter.php new file mode 100644 index 0000000..7339176 --- /dev/null +++ b/app/Formatter/TaskListSubtaskAssigneeFormatter.php @@ -0,0 +1,56 @@ +<?php + +namespace Kanboard\Formatter; + +/** + * Class TaskListSubtaskAssigneeFormatter + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class TaskListSubtaskAssigneeFormatter extends TaskListFormatter +{ + protected $userId = 0; + protected $withoutEmptyTasks = false; + + /** + * Set assignee + * + * @param integer $userId + * @return $this + */ + public function withUserId($userId) + { + $this->userId = $userId; + return $this; + } + + public function withoutEmptyTasks() + { + $this->withoutEmptyTasks = true; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $tasks = parent::format(); + $taskIds = array_column($tasks, 'id'); + $subtasks = $this->subtaskModel->getAllByTaskIdsAndAssignee($taskIds, $this->userId); + $subtasks = array_column_index($subtasks, 'task_id'); + array_merge_relation($tasks, $subtasks, 'subtasks', 'id'); + + if ($this->withoutEmptyTasks) { + $tasks = array_filter($tasks, function (array $task) { + return count($task['subtasks']) > 0; + }); + } + + return $tasks; + } +} diff --git a/app/Formatter/TaskListSubtaskFormatter.php b/app/Formatter/TaskListSubtaskFormatter.php new file mode 100644 index 0000000..4f88967 --- /dev/null +++ b/app/Formatter/TaskListSubtaskFormatter.php @@ -0,0 +1,29 @@ +<?php + +namespace Kanboard\Formatter; + +/** + * Class TaskListSubtaskFormatter + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class TaskListSubtaskFormatter extends TaskListFormatter +{ + /** + * Apply formatter + * + * @access public + * @return array + */ + public function format() + { + $tasks = parent::format(); + $taskIds = array_column($tasks, 'id'); + $subtasks = $this->subtaskModel->getAllByTaskIds($taskIds); + $subtasks = array_column_index($subtasks, 'task_id'); + array_merge_relation($tasks, $subtasks, 'subtasks', 'id'); + + return $tasks; + } +} diff --git a/app/Formatter/TaskSuggestMenuFormatter.php b/app/Formatter/TaskSuggestMenuFormatter.php new file mode 100644 index 0000000..518f99e --- /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/TasksApiFormatter.php b/app/Formatter/TasksApiFormatter.php new file mode 100644 index 0000000..95b1409 --- /dev/null +++ b/app/Formatter/TasksApiFormatter.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class TasksApiFormatter + * + * @package Kanboard\Formatter + */ +class TasksApiFormatter extends BaseFormatter implements FormatterInterface +{ + protected $tasks = array(); + + public function withTasks($tasks) + { + $this->tasks = $tasks; + return $this; + } + + /** + * Apply formatter + * + * @access public + * @return mixed + */ + public function format() + { + if (! empty($this->tasks)) { + foreach ($this->tasks as &$task) { + $task = $this->taskApiFormatter->withTask($task)->format(); + } + } + + return $this->tasks; + } +} diff --git a/app/Formatter/UserAutoCompleteFormatter.php b/app/Formatter/UserAutoCompleteFormatter.php new file mode 100644 index 0000000..aa02cd2 --- /dev/null +++ b/app/Formatter/UserAutoCompleteFormatter.php @@ -0,0 +1,60 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\User\UserProviderInterface; +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Auto-complete formatter for users + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class UserAutoCompleteFormatter extends BaseFormatter implements FormatterInterface +{ + /** + * Users found + * + * @access protected + * @var UserProviderInterface[] + */ + protected $users; + + /** + * Set users + * + * @access public + * @param UserProviderInterface[] $users + * @return $this + */ + public function withUsers(array $users) + { + $this->users = $users; + return $this; + } + + /** + * Format the users for the ajax auto-completion + * + * @access public + * @return array + */ + public function format() + { + $result = array(); + + foreach ($this->users as $user) { + $result[] = array( + 'id' => $user->getInternalId(), + 'username' => $user->getUsername(), + 'external_id' => $user->getExternalId(), + 'external_id_column' => $user->getExternalIdColumn(), + 'value' => $user->getName() === '' ? $user->getUsername() : $user->getName(), + 'label' => $user->getName() === '' ? $user->getUsername() : $user->getName().' ('.$user->getUsername().')', + ); + } + + return $result; + } +} diff --git a/app/Formatter/UserMentionFormatter.php b/app/Formatter/UserMentionFormatter.php new file mode 100644 index 0000000..615e2b4 --- /dev/null +++ b/app/Formatter/UserMentionFormatter.php @@ -0,0 +1,63 @@ +<?php + +namespace Kanboard\Formatter; + +use Kanboard\Core\Filter\FormatterInterface; + +/** + * Class UserMentionFormatter + * + * @package Kanboard\Formatter + * @author Frederic Guillot + */ +class UserMentionFormatter extends BaseFormatter implements FormatterInterface +{ + 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 aria-hidden="true">'.$this->helper->text->e($user['name']).'</small>'; + } + + $result[] = array( + 'value' => $user['username'], + 'html' => $html, + ); + } + + return $result; + } +} diff --git a/app/Group/DatabaseBackendGroupProvider.php b/app/Group/DatabaseBackendGroupProvider.php new file mode 100644 index 0000000..29d04d5 --- /dev/null +++ b/app/Group/DatabaseBackendGroupProvider.php @@ -0,0 +1,34 @@ +<?php + +namespace Kanboard\Group; + +use Kanboard\Core\Base; +use Kanboard\Core\Group\GroupBackendProviderInterface; + +/** + * Database Backend Group Provider + * + * @package group + * @author Frederic Guillot + */ +class DatabaseBackendGroupProvider extends Base implements GroupBackendProviderInterface +{ + /** + * Find a group from a search query + * + * @access public + * @param string $input + * @return DatabaseGroupProvider[] + */ + public function find($input) + { + $result = array(); + $groups = $this->groupModel->search($input); + + foreach ($groups as $group) { + $result[] = new DatabaseGroupProvider($group); + } + + return $result; + } +} diff --git a/app/Group/DatabaseGroupProvider.php b/app/Group/DatabaseGroupProvider.php new file mode 100644 index 0000000..430121a --- /dev/null +++ b/app/Group/DatabaseGroupProvider.php @@ -0,0 +1,66 @@ +<?php + +namespace Kanboard\Group; + +use Kanboard\Core\Group\GroupProviderInterface; + +/** + * Database Group Provider + * + * @package group + * @author Frederic Guillot + */ +class DatabaseGroupProvider implements GroupProviderInterface +{ + /** + * Group properties + * + * @access private + * @var array + */ + private $group = array(); + + /** + * Constructor + * + * @access public + * @param array $group + */ + public function __construct(array $group) + { + $this->group = $group; + } + + /** + * Get internal id + * + * @access public + * @return integer + */ + public function getInternalId() + { + return (int) $this->group['id']; + } + + /** + * Get external id + * + * @access public + * @return string + */ + public function getExternalId() + { + return ''; + } + + /** + * Get group name + * + * @access public + * @return string + */ + public function getName() + { + return $this->group['name']; + } +} diff --git a/app/Group/LdapBackendGroupProvider.php b/app/Group/LdapBackendGroupProvider.php new file mode 100644 index 0000000..411d431 --- /dev/null +++ b/app/Group/LdapBackendGroupProvider.php @@ -0,0 +1,56 @@ +<?php + +namespace Kanboard\Group; + +use LogicException; +use Kanboard\Core\Base; +use Kanboard\Core\Group\GroupBackendProviderInterface; +use Kanboard\Core\Ldap\Client as LdapClient; +use Kanboard\Core\Ldap\ClientException as LdapException; +use Kanboard\Core\Ldap\Group as LdapGroup; + +/** + * LDAP Backend Group Provider + * + * @package group + * @author Frederic Guillot + */ +class LdapBackendGroupProvider extends Base implements GroupBackendProviderInterface +{ + /** + * Find a group from a search query + * + * @access public + * @param string $input + * @return LdapGroupProvider[] + */ + public function find($input) + { + try { + $ldap = LdapClient::connect(); + return LdapGroup::getGroups($ldap, $this->getLdapGroupPattern($input)); + + } catch (LdapException $e) { + $this->logger->error($e->getMessage()); + return array(); + } + } + + /** + * Get LDAP group pattern + * + * @access public + * @param string $input + * @param string $filter + * @return string + */ + public function getLdapGroupPattern($input, $filter = LDAP_GROUP_FILTER) + { + if ($filter === '') { + throw new LogicException('LDAP group filter is empty. Please configure the LDAP_GROUP_FILTER parameter in your configuration file'); + } + + $escapedInput = ldap_escape($input, '', LDAP_ESCAPE_FILTER); + return sprintf($filter, $escapedInput); + } +} diff --git a/app/Group/LdapGroupProvider.php b/app/Group/LdapGroupProvider.php new file mode 100644 index 0000000..b497d48 --- /dev/null +++ b/app/Group/LdapGroupProvider.php @@ -0,0 +1,76 @@ +<?php + +namespace Kanboard\Group; + +use Kanboard\Core\Group\GroupProviderInterface; + +/** + * LDAP Group Provider + * + * @package group + * @author Frederic Guillot + */ +class LdapGroupProvider implements GroupProviderInterface +{ + /** + * Group DN + * + * @access private + * @var string + */ + private $dn = ''; + + /** + * Group Name + * + * @access private + * @var string + */ + private $name = ''; + + /** + * Constructor + * + * @access public + * @param string $dn + * @param string $name + */ + public function __construct($dn, $name) + { + $this->dn = $dn; + $this->name = $name; + } + + /** + * Get internal id + * + * @access public + * @return integer + */ + public function getInternalId() + { + return ''; + } + + /** + * Get external id + * + * @access public + * @return string + */ + public function getExternalId() + { + return $this->dn; + } + + /** + * Get group name + * + * @access public + * @return string + */ + public function getName() + { + return $this->name; + } +} diff --git a/app/Helper/AppHelper.php b/app/Helper/AppHelper.php new file mode 100644 index 0000000..4562f25 --- /dev/null +++ b/app/Helper/AppHelper.php @@ -0,0 +1,209 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Application Helper + * + * @package helper + * @author Frederic Guillot + */ +class AppHelper extends Base +{ + public function tooltipMarkdown($markdownText, $icon = 'fa-info-circle') + { + return '<span class="tooltip"><i class="fa '.$icon.'"></i><script type="text/template"><div class="markdown">'.$this->helper->text->markdown($markdownText).'</div></script></span>'; + } + + public function tooltipHTML($htmlText, $icon = 'fa-info-circle') + { + return '<span class="tooltip"><i class="fa '.$icon.'"></i><script type="text/template"><div class="markdown">'.$htmlText.'</div></script></span>'; + } + + public function tooltipLink($label, $link) + { + return '<span class="tooltip" data-href="'.$link.'">'.$label.'</span>'; + } + + public function getToken() + { + return $this->token; + } + + public function isAjax() + { + return $this->request->isAjax(); + } + + /** + * 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 + * @return mixed + */ + public function config($param, $default = '') + { + return $this->configModel->get($param, $default); + } + + /** + * Make sidebar menu active + * + * @access public + * @param string $controller + * @param string $action + * @param string $plugin + * @return string + */ + public function checkMenuSelection($controller, $action = '', $plugin = '') + { + $result = strtolower($this->getRouterController()) === strtolower($controller); + + if ($result && $action !== '') { + $result = strtolower($this->getRouterAction()) === strtolower($action); + } + + if ($result && $plugin !== '') { + $result = strtolower($this->getPluginName()) === strtolower($plugin); + } + + return $result ? 'class="active"' : ''; + } + + /** + * Get plugin name from route + * + * @access public + * @return string + */ + public function getPluginName() + { + return $this->router->getPlugin(); + } + + /** + * Get router controller + * + * @access public + * @return string + */ + public function getRouterController() + { + return $this->router->getController(); + } + + /** + * Get router action + * + * @access public + * @return string + */ + public function getRouterAction() + { + return $this->router->getAction(); + } + + /** + * Get javascript language code + * + * @access public + * @return string + */ + public function jsLang() + { + return $this->languageModel->getJsLanguageCode(); + } + + /** + * Check if current language requires RTL direction + * + * @access public + * @return bool + */ + public function isRtlLanguage() + { + return $this->languageModel->isRtlLanguage(); + } + + /** + * Get date format for Jquery DatePicker + * + * @access public + * @return string + */ + public function getJsDateFormat() + { + $format = $this->dateParser->getUserDateFormat(); + $format = str_replace('m', 'mm', $format); + $format = str_replace('Y', 'yy', $format); + $format = str_replace('d', 'dd', $format); + + return $format; + } + + /** + * Get time format for Jquery Plugin DateTimePicker + * + * @access public + * @return string + */ + public function getJsTimeFormat() + { + $format = $this->dateParser->getUserTimeFormat(); + $format = str_replace('H', 'HH', $format); + $format = str_replace('i', 'mm', $format); + $format = str_replace('g', 'h', $format); + $format = str_replace('a', 'tt', $format); + + return $format; + } + + /** + * Get current timezone + * + * @access public + * @return string + */ + public function getTimezone() + { + return $this->timezoneModel->getCurrentTimezone(); + } + + /** + * Get session flash message + * + * @access public + * @return string + */ + public function flashMessage() + { + $success_message = $this->flash->getMessage('success'); + $failure_message = $this->flash->getMessage('failure'); + + if (! empty($success_message)) { + return '<div class="alert alert-success alert-fade-out">'.$this->helper->text->e($success_message).'</div>'; + } + + if (! empty($failure_message)) { + return '<div class="alert alert-error">'.$this->helper->text->e($failure_message).'</div>'; + } + + return ''; + } +} diff --git a/app/Helper/AssetHelper.php b/app/Helper/AssetHelper.php new file mode 100644 index 0000000..e2223fc --- /dev/null +++ b/app/Helper/AssetHelper.php @@ -0,0 +1,69 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Asset Helper + * + * @package helper + * @author Frederic Guillot + */ +class AssetHelper extends Base +{ + /** + * Add a Javascript asset + * + * @param string $filename Filename + * @param bool $async + * @return string + */ + public function js($filename, $async = false) + { + $dir = dirname(__DIR__, 2); + $filepath = $dir.'/'.$filename; + return '<script '.($async ? 'async' : '').' defer type="text/javascript" src="'.$this->helper->url->dir().$filename.'?'.filemtime($filepath).'"></script>'; + } + + /** + * Add a stylesheet asset + * + * @param string $filename Filename + * @param boolean $is_file Add file timestamp + * @param string $media Media + * @return string + */ + public function css($filename, $is_file = true, $media = 'screen') + { + $dir = dirname(__DIR__, 2); + $filepath = $dir.'/'.$filename; + return '<link rel="stylesheet" href="'.$this->helper->url->dir().$filename.($is_file ? '?'.filemtime($filepath) : '').'" media="'.$media.'">'; + } + + /** + * Get custom css + * + * @access public + * @return string + */ + public function customCss() + { + if ($this->configModel->get('application_stylesheet')) { + return '<style>'.$this->configModel->get('application_stylesheet').'</style>'; + } + + return ''; + } + + /** + * Get CSS for task colors + * + * @access public + * @return string + */ + public function colorCss() + { + return '<style>'.$this->colorModel->getCss().'</style>'; + } +} diff --git a/app/Helper/AvatarHelper.php b/app/Helper/AvatarHelper.php new file mode 100644 index 0000000..a36d9b4 --- /dev/null +++ b/app/Helper/AvatarHelper.php @@ -0,0 +1,68 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Avatar Helper + * + * @package helper + * @author Frederic Guillot + */ +class AvatarHelper extends Base +{ + /** + * Render user avatar + * + * @access public + * @param string $user_id + * @param string $username + * @param string $name + * @param string $email + * @param string $avatar_path + * @param string $css + * @param int $size + * @return string + */ + public function render($user_id, $username, $name, $email, $avatar_path, $css = 'avatar-left', $size = 48) + { + if (empty($user_id) && empty($username)) { + $html = $this->avatarManager->renderDefault($size); + } else { + $html = $this->avatarManager->render($user_id, $username, $name, $email, $avatar_path, $size); + } + + return '<div class="avatar avatar-'.$size.' '.$css.'">'.$html.'</div>'; + } + + /** + * Render small user avatar + * + * @access public + * @param string $user_id + * @param string $username + * @param string $name + * @param string $email + * @param string $avatar_path + * @param string $css + * @return string + */ + public function small($user_id, $username, $name, $email, $avatar_path, $css = '') + { + return $this->render($user_id, $username, $name, $email, $avatar_path, $css, 20); + } + + /** + * Get a small avatar for the current user + * + * @access public + * @param string $css + * @return string + */ + public function currentUserSmall($css = '') + { + $user = $this->userSession->getAll(); + return $this->small($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path'], $css); + } +} diff --git a/app/Helper/BoardHelper.php b/app/Helper/BoardHelper.php new file mode 100644 index 0000000..f5df3db --- /dev/null +++ b/app/Helper/BoardHelper.php @@ -0,0 +1,27 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; +use Kanboard\Model\UserMetadataModel; + +/** + * Board Helper + * + * @package helper + * @author Frederic Guillot + */ +class BoardHelper extends Base +{ + /** + * Return true if tasks are collapsed + * + * @access public + * @param integer $project_id + * @return boolean + */ + public function isCollapsed($project_id) + { + return $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_BOARD_COLLAPSED.$project_id, 0) == 1; + } +} diff --git a/app/Helper/CommentHelper.php b/app/Helper/CommentHelper.php new file mode 100644 index 0000000..d6426d1 --- /dev/null +++ b/app/Helper/CommentHelper.php @@ -0,0 +1,23 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; +use Kanboard\Model\UserMetadataModel; + +/** + * Class CommentHelper + * + * @package Kanboard\Helper + * @author Frederic Guillot + */ +class CommentHelper extends Base +{ + public function toggleSorting() + { + $oldDirection = $this->userMetadataCacheDecorator->get(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, 'ASC'); + $newDirection = $oldDirection === 'ASC' ? 'DESC' : 'ASC'; + + $this->userMetadataCacheDecorator->set(UserMetadataModel::KEY_COMMENT_SORTING_DIRECTION, $newDirection); + } +} diff --git a/app/Helper/DateHelper.php b/app/Helper/DateHelper.php new file mode 100644 index 0000000..6eb73ee --- /dev/null +++ b/app/Helper/DateHelper.php @@ -0,0 +1,171 @@ +<?php + +namespace Kanboard\Helper; + +use DateTime; +use Kanboard\Core\Base; + +/** + * DateTime helpers + * + * @package helper + * @author Frederic Guillot + */ +class DateHelper extends Base +{ + /** + * Get formatted time + * + * @access public + * @param integer $value + * @return string + */ + public function time($value) + { + return date($this->configModel->get('application_time_format', 'H:i'), $value); + } + + /** + * Get formatted date + * + * @access public + * @param integer $value + * @return string + */ + public function date($value) + { + if (empty($value)) { + return ''; + } + + if (! ctype_digit((string) $value)) { + $value = strtotime($value); + } + + return date($this->configModel->get('application_date_format', 'm/d/Y'), $value); + } + + /** + * Get formatted datetime + * + * @access public + * @param integer $value + * @return string + */ + public function datetime($value) + { + return date($this->dateParser->getUserDateTimeFormat(), $value); + } + + /** + * Get duration in seconds into human format + * + * @access public + * @param integer $seconds + * @return string + */ + public function duration($seconds) + { + if ($seconds == 0) { + return 0; + } + + $dtF = new DateTime("@0"); + $dtT = new DateTime("@$seconds"); + + $format = sprintf("%%a %s, %%h %s, %%i %s, %%s %s", t('days'), t('hours'), t('minutes'), t('seconds')); + return $dtF->diff($dtT)->format($format); + } + + /** + * Get duration in hours into human format + * + * @access public + * @param float $hours + * @return string + */ + public function durationHours($hours) + { + return sprintf('%0.2f %s', round($hours, 2), t('hours')); + } + + /** + * Get the age of an item in quasi human readable format. + * It's in this format: <1h , NNh, NNd + * + * @access public + * @param integer $timestamp Unix timestamp of the artifact for which age will be calculated + * @param integer $now Compare with this timestamp (Default value is the current unix timestamp) + * @return string + */ + public function age($timestamp, $now = null) + { + if ($now === null) { + $now = time(); + } + + $diff = $now - $timestamp; + + if ($diff < 900) { + return t('<15m'); + } + if ($diff < 1200) { + return t('<30m'); + } elseif ($diff < 3600) { + return t('<1h'); + } elseif ($diff < 86400) { + return '~'.t('%dh', $diff / 3600); + } + + return t('%dd', ($now - $timestamp) / 86400); + } + + /** + * Get all hours for day + * + * @access public + * @return array + */ + public function getDayHours() + { + $values = array(); + + foreach (range(0, 23) as $hour) { + foreach (array(0, 30) as $minute) { + $time = sprintf('%02d:%02d', $hour, $minute); + $values[$time] = $time; + } + } + + return $values; + } + + /** + * Get all days of a week + * + * @access public + * @return array + */ + public function getWeekDays() + { + $values = array(); + + foreach (range(1, 7) as $day) { + $values[$day] = $this->getWeekDay($day); + } + + return $values; + } + + /** + * Get the localized day name from the day number + * + * @access public + * @param integer $day Day number + * @return string + */ + public function getWeekDay($day) + { + return date('l', strtotime('next Monday +'.($day - 1).' days')); + } +} diff --git a/app/Helper/FileHelper.php b/app/Helper/FileHelper.php new file mode 100644 index 0000000..d006271 --- /dev/null +++ b/app/Helper/FileHelper.php @@ -0,0 +1,152 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * File helpers + * + * @package helper + * @author Frederic Guillot + */ +class FileHelper extends Base +{ + /** + * Get file icon + * + * @access public + * @param string $filename Filename + * @return string Font-Awesome-Icon-Name + */ + public function icon($filename) + { + switch (get_file_extension($filename)) { + case 'jpeg': + case 'jpg': + case 'png': + case 'gif': + case 'svg': + return 'fa-file-image-o'; + case 'xls': + case 'xlsx': + case 'xlsm': + return 'fa-file-excel-o'; + case 'doc': + case 'docx': + return 'fa-file-word-o'; + case 'ppt': + case 'pptx': + return 'fa-file-powerpoint-o'; + case 'zip': + case 'rar': + case 'tar': + case 'bz2': + case 'xz': + case 'gz': + return 'fa-file-archive-o'; + case 'mp3': + case 'amr': + case 'flac': + case 'm4a': + case 'ogg': + case 'opus': + case 'wav': + case 'wma': + case 'midi': + case 'mid': + return 'fa-file-audio-o'; + case 'avi': + case 'mov': + case 'mp4': + case 'mkv': + case 'webm': + return 'fa-file-video-o'; + case 'php': + case 'html': + case 'css': + case 'js': + return 'fa-file-code-o'; + case 'pdf': + return 'fa-file-pdf-o'; + } + + return 'fa-file-o'; + } + + /** + * Return the image mimetype based on the file extension + * + * @access public + * @param $filename + * @return string + */ + public function getImageMimeType($filename) + { + switch (get_file_extension($filename)) { + case 'jpeg': + case 'jpg': + return 'image/jpeg'; + case 'png': + return 'image/png'; + case 'gif': + return 'image/gif'; + default: + return 'image/jpeg'; + } + } + + /** + * Get the preview type + * + * @access public + * @param string $filename + * @return string + */ + public function getPreviewType($filename) + { + switch (get_file_extension($filename)) { + case 'md': + case 'markdown': + return 'markdown'; + case 'txt': + return 'text'; + } + + 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'; + case 'mp3': + case 'ogg': + case 'flac': + case 'wav': + return 'audio/mpeg'; + case 'avi': + return 'video/x-msvideo'; + case 'webm': + return 'video/webm'; + case 'mov': + return 'video/quicktime'; + case 'm4v': + return 'video/x-m4v'; + case 'mp4': + return 'video/mp4'; + case 'svg': + return 'image/svg+xml'; + } + + return null; + } +} diff --git a/app/Helper/FormHelper.php b/app/Helper/FormHelper.php new file mode 100644 index 0000000..f4f5667 --- /dev/null +++ b/app/Helper/FormHelper.php @@ -0,0 +1,482 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Form helpers + * + * @package helper + * @author Frederic Guillot + */ +class FormHelper extends Base +{ + /** + * Hidden CSRF token field + * + * @access public + * @return string + */ + public function csrf() + { + return '<input type="hidden" name="csrf_token" value="'.$this->token->getCSRFToken().'"/>'; + } + + /** + * Display a hidden form field + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @return string + */ + public function hidden($name, array $values = array()) + { + return '<input type="hidden" name="'.$name.'" id="form-'.$name.'" '.$this->formValue($values, $name).'/>'; + } + + /** + * Display a select field + * + * @access public + * @param string $name Field name + * @param array $options Options + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes + * @param string $class CSS class + * @return string + */ + public function select($name, array $options, array $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + $html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'" '.implode(' ', $attributes).'>'; + + foreach ($options as $id => $value) { + $html .= '<option value="'.$this->helper->text->e($id).'"'; + + if (isset($values->$name) && $id == $values->$name) { + $html .= ' selected="selected"'; + } + if (isset($values[$name]) && $id == $values[$name]) { + $html .= ' selected="selected"'; + } + + $html .= '>'.$this->helper->text->e($value).'</option>'; + } + + $html .= '</select>'; + $html .= $this->errorList($errors, $name); + + return $html; + } + + /** + * Display a color select field + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @return string + */ + public function colorSelect($name, array $values) + { + $colors = $this->colorModel->getList(); + $html = $this->label(t('Color'), $name); + $html .= $this->select($name, $colors, $values, array(), array('tabindex="4"'), 'color-picker'); + return $html; + } + + /** + * Display a radio field group + * + * @access public + * @param string $name Field name + * @param array $options Options + * @param array $values Form values + * @return string + */ + public function radios($name, array $options, array $values = array()) + { + $html = ''; + + foreach ($options as $value => $label) { + $html .= $this->radio($name, $label, $value, isset($values[$name]) && $values[$name] == $value); + } + + return $html; + } + + /** + * Display a radio field + * + * @access public + * @param string $name Field name + * @param string $label Form label + * @param string $value Form value + * @param boolean $selected Field selected or not + * @param string $class CSS class + * @return string + */ + public function radio($name, $label, $value, $selected = false, $class = '') + { + return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.$this->helper->text->e($value).'" '.($selected ? 'checked="checked"' : '').'> '.$this->helper->text->e($label).'</label>'; + } + + /** + * Display a checkboxes group + * + * @access public + * @param string $name Field name + * @param array $options Options + * @param array $values Form values + * @return string + */ + public function checkboxes($name, array $options, array $values = array()) + { + $html = ''; + + foreach ($options as $value => $label) { + $html .= $this->checkbox($name.'['.$value.']', $label, $value, isset($values[$name]) && in_array($value, $values[$name])); + } + + return $html; + } + + /** + * 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 array $attributes + * @return string + */ + public function checkbox($name, $label, $value, $checked = false, $class = '', array $attributes = array()) + { + $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> %s</label>', + $name, + $class, + $this->helper->text->e($value), + $htmlAttributes, + $this->helper->text->e($label) + ); + } + + /** + * Display a form label + * + * @access public + * @param string $name Field name + * @param string $label Form label + * @param array $attributes HTML attributes + * @return string + */ + public function label($label, $name, array $attributes = array()) + { + return '<label for="form-'.$name.'" '.implode(' ', $attributes).'>'.$this->helper->text->e($label).'</label>'; + } + + /** + * Display a textarea + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes HTML attributes + * @param string $class CSS class + * @return string + */ + public function textarea($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + $class .= $this->errorClass($errors, $name); + + $html = '<textarea name="'.$name.'" id="form-'.$name.'" class="'.$class.'" '; + $html .= implode(' ', $attributes).'>'; + $html .= isset($values[$name]) ? $this->helper->text->e($values[$name]) : ''; + $html .= '</textarea>'; + $html .= $this->errorList($errors, $name); + + return $html; + } + + /** + * 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, + 'css' => $this->errorClass($errors, $name), + 'required' => isset($attributes['required']) && $attributes['required'], + 'tabindex' => isset($attributes['tabindex']) ? $attributes['tabindex'] : '-1', + 'labelPreview' => t('Preview'), + 'previewUrl' => $this->helper->url->to('TaskAjaxController', 'preview'), + 'labelWrite' => t('Write'), + 'labelTitle' => t('Title'), + 'placeholder' => t('Write your text in Markdown'), + 'ariaLabel' => isset($attributes['aria-label']) ? $attributes['aria-label'] : '', + '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).'\'>'; + $html .= '<script type="text/template">'.(isset($values[$name]) ? htmlspecialchars($values[$name], ENT_QUOTES, 'UTF-8', true) : '').'</script>'; + $html .= '</div>'; + $html .= $this->errorList($errors, $name); + + return $html; + } + + /** + * Display file field + * + * @access public + * @param string $name + * @param array $errors + * @param boolean $multiple + * @return string + */ + public function file($name, array $errors = array(), $multiple = false) + { + $html = '<input type="file" name="'.$name.'" id="form-'.$name.'" '.($multiple ? 'multiple' : '').'>'; + $html .= $this->errorList($errors, $name); + + return $html; + } + + /** + * Display a input field + * + * @access public + * @param string $type HMTL input tag type + * @param string $name Field name + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes HTML attributes + * @param string $class CSS class + * @return string + */ + public function input($type, $name, $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + $class .= $this->errorClass($errors, $name); + + $html = '<input type="'.$type.'" name="'.$name.'" id="form-'.$name.'" '.$this->formValue($values, $name).' class="'.$class.'" '; + $html .= implode(' ', $attributes).'>'; + + if (in_array('required', $attributes)) { + $html .= '<span class="form-required">*</span>'; + } + + $html .= $this->errorList($errors, $name); + + return $html; + } + + /** + * Display a text field + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes HTML attributes + * @param string $class CSS class + * @return string + */ + public function text($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + return $this->input('text', $name, $values, $errors, $attributes, $class); + } + + /** + * Display a password field + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes HTML attributes + * @param string $class CSS class + * @return string + */ + public function password($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + return $this->input('password', $name, $values, $errors, $attributes, $class); + } + + /** + * Display an email field + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes HTML attributes + * @param string $class CSS class + * @return string + */ + public function email($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + return $this->input('email', $name, $values, $errors, $attributes, $class); + } + + /** + * Display a number field + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes HTML attributes + * @param string $class CSS class + * @return string + */ + public function number($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + return $this->input('number', $name, $values, $errors, $attributes, $class); + } + + /** + * Display a numeric field (allow decimal number) + * + * @access public + * @param string $name Field name + * @param array $values Form values + * @param array $errors Form errors + * @param array $attributes HTML attributes + * @param string $class CSS class + * @return string + */ + public function numeric($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '') + { + return $this->input('text', $name, $values, $errors, $attributes, $class.' form-numeric'); + } + + /** + * 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 + * @param array $errors Error list + * @param string $name Field name + * @return string + */ + private function errorClass(array $errors, $name) + { + return ! isset($errors[$name]) ? '' : ' form-error'; + } + + /** + * Display a list of form errors + * + * @access private + * @param array $errors List of errors + * @param string $name Field name + * @return string + */ + private function errorList(array $errors, $name) + { + $html = ''; + + if (isset($errors[$name])) { + $html .= '<ul class="form-errors">'; + + foreach ($errors[$name] as $error) { + $html .= '<li>'.$this->helper->text->e($error).'</li>'; + } + + $html .= '</ul>'; + } + + return $html; + } + + /** + * Get an escaped form value + * + * @access private + * @param mixed $values Values + * @param string $name Field name + * @return string + */ + private function formValue($values, $name) + { + if (isset($values->$name)) { + return 'value="'.$this->helper->text->e($values->$name).'"'; + } + + return isset($values[$name]) ? 'value="'.$this->helper->text->e($values[$name]).'"' : ''; + } +} diff --git a/app/Helper/HookHelper.php b/app/Helper/HookHelper.php new file mode 100644 index 0000000..9a89daf --- /dev/null +++ b/app/Helper/HookHelper.php @@ -0,0 +1,104 @@ +<?php + +namespace Kanboard\Helper; + +use Closure; +use Kanboard\Core\Base; + +/** + * Template Hook helpers + * + * @package helper + * @author Frederic Guillot + */ +class HookHelper extends Base +{ + /** + * Add assets JS or CSS + * + * @access public + * @param string $type + * @param string $hook + * @return string + */ + public function asset($type, $hook) + { + $buffer = ''; + + foreach ($this->hook->getListeners($hook) as $params) { + $buffer .= $this->helper->asset->$type($params['template']); + } + + return $buffer; + } + + /** + * Render all attached hooks + * + * @access public + * @param string $hook + * @param array $variables + * @return string + */ + public function render($hook, array $variables = array()) + { + $buffer = ''; + + foreach ($this->hook->getListeners($hook) as $params) { + $currentVariables = $variables; + if (! empty($params['variables'])) { + $currentVariables = array_merge($variables, $params['variables']); + } elseif (! empty($params['callable'])) { + $result = call_user_func_array($params['callable'], array_values($variables)); + + if (is_array($result)) { + $currentVariables = array_merge($variables, $result); + } + } + + $buffer .= $this->template->render($params['template'], $currentVariables); + } + + return $buffer; + } + + /** + * Attach a template to a hook + * + * @access public + * @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 attachCallable($hook, $template, Closure $callable) + { + $this->hook->on($hook, array( + 'template' => $template, + 'callable' => $callable, + )); + + return $this; + } +} diff --git a/app/Helper/LayoutHelper.php b/app/Helper/LayoutHelper.php new file mode 100644 index 0000000..6afdb2d --- /dev/null +++ b/app/Helper/LayoutHelper.php @@ -0,0 +1,218 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Layout Helper + * + * @package helper + * @author Frederic Guillot + */ +class LayoutHelper extends Base +{ + /** + * Render a template without the layout if Ajax request + * + * @access public + * @param string $template Template name + * @param array $params Template parameters + * @return string + */ + public function app($template, array $params = array()) + { + $isAjax = $this->request->isAjax(); + $params['is_ajax'] = $isAjax; + + if ($isAjax) { + return $this->template->render($template, $params); + } + + if (! isset($params['no_layout']) && ! isset($params['board_selector'])) { + $params['board_selector'] = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); + + if (isset($params['project']['id'])) { + unset($params['board_selector'][$params['project']['id']]); + } + + $this->hook->reference('helper:layout:board-selector:list', $params['board_selector']); + } + + return $this->pageLayout($template, $params); + } + + /** + * Common layout for user views + * + * @access public + * @param string $template Template name + * @param array $params Template parameters + * @return string + */ + public function user($template, array $params) + { + if (isset($params['user'])) { + $params['title'] = '#'.$params['user']['id'].' '.($params['user']['name'] ?: $params['user']['username']); + } + + return $this->subLayout('user_view/layout', 'user_view/sidebar', $template, $params); + } + + /** + * Common layout for task views + * + * @access public + * @param string $template Template name + * @param array $params Template parameters + * @return string + */ + public function task($template, array $params) + { + $params['page_title'] = $params['task']['project_name'].', #'.$params['task']['id'].' - '.$params['task']['title']; + $params['title'] = $params['task']['project_name']; + return $this->subLayout('task/layout', 'task/sidebar', $template, $params); + } + + /** + * Common layout for project views + * + * @access public + * @param string $template + * @param array $params + * @param string $sidebar + * @return string + */ + public function project($template, array $params, $sidebar = 'project/sidebar') + { + if (empty($params['title'])) { + $params['title'] = $params['project']['name']; + } elseif ($params['project']['name'] !== $params['title']) { + $params['title'] = $params['project']['name'].' > '.$params['title']; + } + + return $this->subLayout('project/layout', $sidebar, $template, $params); + } + + /** + * Common layout for project user views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function projectUser($template, array $params) + { + $params['filter'] = array('user_id' => $params['user_id']); + return $this->subLayout('project_user_overview/layout', 'project_user_overview/sidebar', $template, $params); + } + + /** + * Common layout for config views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function config($template, array $params) + { + if (empty($params['values'])) { + $params['values'] = $this->configModel->getAll(); + } + + if (! isset($params['errors'])) { + $params['errors'] = array(); + } + + return $this->subLayout('config/layout', 'config/sidebar', $template, $params); + } + + /** + * Common layout for plugin views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function plugin($template, array $params) + { + return $this->subLayout('plugin/layout', 'plugin/sidebar', $template, $params); + } + + /** + * Common layout for dashboard views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function dashboard($template, array $params) + { + return $this->subLayout('dashboard/layout', 'dashboard/sidebar', $template, $params); + } + + /** + * Common layout for analytic views + * + * @access public + * @param string $template + * @param array $params + * @return string + */ + public function analytic($template, array $params) + { + if (isset($params['project']['name'])) { + $params['title'] = $params['project']['name'].' > '.$params['title']; + } + + return $this->subLayout('analytic/layout', 'analytic/sidebar', $template, $params, true); + } + + /** + * Render page layout + * + * @access public + * @param string $template Template name + * @param array $params Key/value dictionary + * @param string $layout Layout name + * @return string + */ + public function pageLayout($template, array $params = array(), $layout = 'layout') + { + return $this->template->render( + $layout, + $params + array('content_for_layout' => $this->template->render($template, $params)) + ); + } + + /** + * Common method to generate a sub-layout + * + * @access public + * @param string $sublayout + * @param string $sidebar + * @param string $template + * @param array $params + * @param bool $ignoreAjax + * @return string + */ + 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 (!$ignoreAjax && $isAjax) { + return $content; + } + + $params['content_for_sublayout'] = $content; + $params['sidebar_template'] = $sidebar; + + return $this->app($sublayout, $params); + } +} diff --git a/app/Helper/MailHelper.php b/app/Helper/MailHelper.php new file mode 100644 index 0000000..e0d28df --- /dev/null +++ b/app/Helper/MailHelper.php @@ -0,0 +1,68 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Class MailHelper + * + * @package Kanboard\Helper + * @author Frederic Guillot + */ +class MailHelper extends Base +{ + /** + * Filter mail subject + * + * @access public + * @param string $subject + * @return string + */ + public function filterSubject($subject) + { + $subject = str_ireplace('RE: ', '', $subject); + $subject = str_ireplace('FW: ', '', $subject); + $subject = str_ireplace('Fwd: ', '', $subject); + + return $subject; + } + + /** + * Get mail sender address + * + * @access public + * @return string + */ + public function getMailSenderAddress() + { + if (MAIL_CONFIGURATION) { + $email = $this->configModel->get('mail_sender_address'); + + if (! empty($email)) { + return $email; + } + } + + return MAIL_FROM; + } + + /** + * Get mail transport + * + * @access public + * @return string + */ + public function getMailTransport() + { + if (MAIL_CONFIGURATION) { + $transport = $this->configModel->get('mail_transport'); + + if (! empty($transport)) { + return $transport; + } + } + + return MAIL_TRANSPORT; + } +} diff --git a/app/Helper/ModalHelper.php b/app/Helper/ModalHelper.php new file mode 100644 index 0000000..b528f4a --- /dev/null +++ b/app/Helper/ModalHelper.php @@ -0,0 +1,96 @@ +<?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(), $title = '') + { + $ariaLabel = (empty($title) ? 'aria-hidden="true"' : 'role="img" aria-label="'.$title.'"'); + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-medium" '.$ariaLabel.'></i>'.$label; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-medium', $title); + } + + 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 mediumIcon($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-medium" aria-hidden="true"></i>'; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-medium', $label); + } + + 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'); + } + + public function replaceIconLink($icon, $label, $controller, $action, array $params = array()) + { + $html = '<i class="fa fa-'.$icon.' fa-fw js-modal-replace" aria-hidden="true"></i>'.$label; + return $this->helper->url->link($html, $controller, $action, $params, false, 'js-modal-replace'); + } +} diff --git a/app/Helper/ModelHelper.php b/app/Helper/ModelHelper.php new file mode 100644 index 0000000..d49637c --- /dev/null +++ b/app/Helper/ModelHelper.php @@ -0,0 +1,94 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Model Helper + * + * @package helper + * @author Frederic Guillot + */ +class ModelHelper extends Base +{ + /** + * Remove keys from an array + * + * @access public + * @param array $values Input array + * @param string[] $keys List of keys to remove + */ + public function removeFields(array &$values, array $keys) + { + foreach ($keys as $key) { + if (array_key_exists($key, $values)) { + unset($values[$key]); + } + } + } + + /** + * Remove keys from an array if empty + * + * @access public + * @param array $values Input array + * @param string[] $keys List of keys to remove + */ + public function removeEmptyFields(array &$values, array $keys) + { + foreach ($keys as $key) { + if (array_key_exists($key, $values) && empty($values[$key])) { + unset($values[$key]); + } + } + } + + /** + * Force fields to be at 0 if empty + * + * @access public + * @param array $values Input array + * @param string[] $keys List of keys + */ + public function resetFields(array &$values, array $keys) + { + foreach ($keys as $key) { + if (isset($values[$key]) && empty($values[$key])) { + $values[$key] = 0; + } + } + } + + /** + * Force some fields to be integer + * + * @access public + * @param array $values Input array + * @param string[] $keys List of keys + */ + public function convertIntegerFields(array &$values, array $keys) + { + foreach ($keys as $key) { + if (isset($values[$key])) { + $values[$key] = (int) $values[$key]; + } + } + } + + /** + * Force some fields to be null if empty + * + * @access public + * @param array $values Input array + * @param string[] $keys List of keys + */ + public function convertNullFields(array &$values, array $keys) + { + foreach ($keys as $key) { + if (array_key_exists($key, $values) && empty($values[$key])) { + $values[$key] = null; + } + } + } +} diff --git a/app/Helper/ProjectActivityHelper.php b/app/Helper/ProjectActivityHelper.php new file mode 100644 index 0000000..480db3d --- /dev/null +++ b/app/Helper/ProjectActivityHelper.php @@ -0,0 +1,104 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; +use Kanboard\Filter\ProjectActivityProjectIdFilter; +use Kanboard\Filter\ProjectActivityProjectIdsFilter; +use Kanboard\Filter\ProjectActivityTaskIdFilter; +use Kanboard\Model\ProjectActivityModel; + +/** + * Project Activity Helper + * + * @package helper + * @author Frederic Guillot + */ +class ProjectActivityHelper extends Base +{ + /** + * Search events + * + * @access public + * @param string $search + * @return array + */ + public function searchEvents($search) + { + $projects = $this->projectUserRoleModel->getActiveProjectsByUser($this->userSession->getId()); + $events = array(); + + if ($search !== '') { + $queryBuilder = $this->projectActivityLexer->build($search); + $queryBuilder + ->withFilter(new ProjectActivityProjectIdsFilter(array_keys($projects))) + ->getQuery() + ->desc(ProjectActivityModel::TABLE.'.id') + ->limit(500) + ; + + $events = $queryBuilder->format($this->projectActivityEventFormatter); + } + + return $events; + } + + /** + * Get project activity events + * + * @access public + * @param integer $project_id + * @param int $limit + * @return array + */ + public function getProjectEvents($project_id, $limit = 50) + { + $queryBuilder = $this->projectActivityQuery + ->withFilter(new ProjectActivityProjectIdFilter($project_id)); + + $queryBuilder->getQuery() + ->desc(ProjectActivityModel::TABLE.'.id') + ->limit($limit) + ; + + return $queryBuilder->format($this->projectActivityEventFormatter); + } + + /** + * Get projects activity events + * + * @access public + * @param int[] $project_ids + * @param int $limit + * @return array + */ + public function getProjectsEvents(array $project_ids, $limit = 50) + { + $queryBuilder = $this->projectActivityQuery + ->withFilter(new ProjectActivityProjectIdsFilter($project_ids)); + + $queryBuilder->getQuery() + ->desc(ProjectActivityModel::TABLE.'.id') + ->limit($limit) + ; + + return $queryBuilder->format($this->projectActivityEventFormatter); + } + + /** + * Get task activity events + * + * @access public + * @param integer $task_id + * @return array + */ + public function getTaskEvents($task_id) + { + $queryBuilder = $this->projectActivityQuery + ->withFilter(new ProjectActivityTaskIdFilter($task_id)); + + $queryBuilder->getQuery()->desc(ProjectActivityModel::TABLE.'.id'); + + return $queryBuilder->format($this->projectActivityEventFormatter); + } +} diff --git a/app/Helper/ProjectHeaderHelper.php b/app/Helper/ProjectHeaderHelper.php new file mode 100644 index 0000000..d95cfc2 --- /dev/null +++ b/app/Helper/ProjectHeaderHelper.php @@ -0,0 +1,87 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Project Header Helper + * + * @package helper + * @author Frederic Guillot + */ +class ProjectHeaderHelper extends Base +{ + public $hookBefore = ''; + public $dropdownHtml = ''; + public $viewsHtml = ''; + public $searchHtml = ''; + public $hookAfter = ''; + /** + * Get current search query + * + * @access public + * @param array $project + * @return string + */ + public function getSearchQuery(array $project) + { + $search = $this->request->getStringParam('search', $this->userSession->getFilters($project['id'])); + $this->userSession->setFilters($project['id'], $search); + return rawurldecode($search); + } + + /** + * Render project header (views switcher and search box) + * + * @access public + * @param array $project + * @param string $controller + * @param string $action + * @param bool $boardView + * @param string $plugin + * @return string + */ + public function render(array $project, $controller, $action, $boardView = false, $plugin = '') + { + $filters = array( + 'controller' => $controller, + 'action' => $action, + 'project_id' => $project['id'], + 'search' => $this->getSearchQuery($project), + 'plugin' => $plugin, + ); + + return $this->template->render('project_header/header', array( + 'project' => $project, + 'filters' => $filters, + 'categories_list' => $this->categoryModel->getList($project['id'], false), + 'users_list' => $this->projectUserRoleModel->getAssignableUsersList($project['id'], false), + 'custom_filters_list' => $this->customFilterModel->getAll($project['id'], $this->userSession->getId()), + 'board_view' => $boardView, + )); + } + + /** + * Get project description + * + * @access public + * @param array &$project + * @return string + */ + public function getDescription(array &$project) + { + if ($project['owner_id'] > 0) { + $description = t('Project owner: ').'<strong>'.$this->helper->text->e($project['owner_name'] ?: $project['owner_username']).'</strong>'.PHP_EOL.PHP_EOL; + + if (! empty($project['description'])) { + $description .= '<hr>'.PHP_EOL.PHP_EOL; + $description .= $this->helper->text->markdown($project['description'] ?: ''); + } + } else { + $description = $this->helper->text->markdown($project['description'] ?: ''); + } + + return $description; + } +} diff --git a/app/Helper/ProjectRoleHelper.php b/app/Helper/ProjectRoleHelper.php new file mode 100644 index 0000000..6965083 --- /dev/null +++ b/app/Helper/ProjectRoleHelper.php @@ -0,0 +1,338 @@ +<?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 $projectId + * @return string + */ + public function getProjectUserRole($projectId) + { + return $this->memoryCache->proxy($this->projectUserRoleModel, 'getUserRole', $projectId, $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'], $task['owner_id']); + } + + return false; + } + + /** + * Return true is the column is sortable + * + * @param int $projectId + * @param int $columnId + * @param int $assigneeId + * @return bool + */ + public function isSortableColumn($projectId, $columnId, $assigneeId = null) + { + $role = $this->getProjectUserRole($projectId); + + if ($this->role->isCustomProjectRole($role)) { + $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($projectId, $role); + + foreach ($sortableColumns as $column) { + if ($column['src_column_id'] == $columnId || $column['dst_column_id'] == $columnId) { + if ($column['only_assigned'] == 1 && $assigneeId !== null && $assigneeId != $this->userSession->getId()) { + return false; + } + + return true; + } + } + + return empty($sortableColumns) && $this->isAllowedToMoveTask($projectId, $role); + } + + return true; + } + + /** + * Check if the user can move a task + * + * @param int $projectId + * @param int $srcColumnId + * @param int $dstColumnId + * @return bool|int + */ + public function canMoveTask($projectId, $srcColumnId, $dstColumnId) + { + $role = $this->getProjectUserRole($projectId); + + if ($this->role->isCustomProjectRole($role)) { + if ($srcColumnId == $dstColumnId) { + return true; + } + + $sortableColumns = $this->columnMoveRestrictionCacheDecorator->getSortableColumns($projectId, $role); + + foreach ($sortableColumns as $column) { + if ($column['src_column_id'] == $srcColumnId && $column['dst_column_id'] == $dstColumnId) { + return true; + } + + if ($column['dst_column_id'] == $srcColumnId && $column['src_column_id'] == $dstColumnId) { + return true; + } + } + + return empty($sortableColumns) && $this->isAllowedToMoveTask($projectId, $role); + } + + return true; + } + + /** + * Return true if the user can create a task for the given column + * + * @param int $projectId + * @param int $columnId + * @return bool + */ + public function canCreateTaskInColumn($projectId, $columnId) + { + $role = $this->getProjectUserRole($projectId); + + if ($this->role->isCustomProjectRole($role)) { + if (! $this->isAllowedToCreateTask($projectId, $columnId, $role)) { + return false; + } + } + + return $this->helper->user->hasProjectAccess('TaskCreationController', 'show', $projectId); + } + + /** + * Return true if the user can create a task for the given column + * + * @param int $projectId + * @param int $columnId + * @return bool + */ + public function canChangeTaskStatusInColumn($projectId, $columnId) + { + $role = $this->getProjectUserRole($projectId); + + if ($this->role->isCustomProjectRole($role)) { + if (! $this->isAllowedToChangeTaskStatus($projectId, $columnId, $role)) { + return false; + } + } + + return $this->helper->user->hasProjectAccess('TaskStatusController', 'close', $projectId); + } + + /** + * 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) + { + $role = $this->getProjectUserRole($task['project_id']); + + if ($this->hasRestriction($task['project_id'], $role, ProjectRoleRestrictionModel::RULE_TASK_SUPPRESSION)) { + return false; + } + + 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; + } + + /** + * Return true if the user can change assignee + * + * @public + * @param array $task + * @return bool + */ + public function canChangeAssignee(array $task) + { + $role = $this->getProjectUserRole($task['project_id']); + + if ($this->role->isCustomProjectRole($role) && $this->hasRestriction($task['project_id'], $role, ProjectRoleRestrictionModel::RULE_TASK_CHANGE_ASSIGNEE)) { + return false; + } + + return true; + } + + /** + * Return true if the user can update a task + * + * @public + * @param array $task + * @return bool + */ + public function canUpdateTask(array $task) + { + $role = $this->getProjectUserRole($task['project_id']); + + if ($this->role->isCustomProjectRole($role) && $task['owner_id'] != $this->userSession->getId() && $this->hasRestriction($task['project_id'], $role, ProjectRoleRestrictionModel::RULE_TASK_UPDATE_ASSIGNED)) { + return false; + } + + return true; + } + + /** + * Check project access + * + * @param string $controller + * @param string $action + * @param integer $projectId + * @return bool + */ + public function checkProjectAccess($controller, $action, $projectId) + { + if (! $this->userSession->isLogged()) { + return false; + } + + if ($this->userSession->isAdmin()) { + return true; + } + + if (! $this->helper->user->hasAccess($controller, $action)) { + return false; + } + + $role = $this->getProjectUserRole($projectId); + + 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 $projectId + * @param int $columnId + * @param string $role + * @return bool + */ + protected function isAllowedToChangeTaskStatus($projectId, $columnId, $role) + { + $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($projectId, $role); + + foreach ($columnRestrictions as $restriction) { + if ($restriction['column_id'] == $columnId) { + if ($restriction['rule'] == ColumnRestrictionModel::RULE_ALLOW_TASK_OPEN_CLOSE) { + return true; + } elseif ($restriction['rule'] == ColumnRestrictionModel::RULE_BLOCK_TASK_OPEN_CLOSE) { + return false; + } + } + } + + return ! $this->hasRestriction($projectId, $role, ProjectRoleRestrictionModel::RULE_TASK_OPEN_CLOSE); + } + + /** + * Check authorization for a custom project role to create a task + * + * @param int $projectId + * @param int $columnId + * @param string $role + * @return bool + */ + protected function isAllowedToCreateTask($projectId, $columnId, $role) + { + $columnRestrictions = $this->columnRestrictionCacheDecorator->getAllByRole($projectId, $role); + + foreach ($columnRestrictions as $restriction) { + if ($restriction['column_id'] == $columnId) { + if ($restriction['rule'] == ColumnRestrictionModel::RULE_ALLOW_TASK_CREATION) { + return true; + } elseif ($restriction['rule'] == ColumnRestrictionModel::RULE_BLOCK_TASK_CREATION) { + return false; + } + } + } + + return ! $this->hasRestriction($projectId, $role, ProjectRoleRestrictionModel::RULE_TASK_CREATION); + } + + /** + * Check if the role can move task in the given project + * + * @param int $projectId + * @param string $role + * @return bool + */ + protected function isAllowedToMoveTask($projectId, $role) + { + $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($projectId, $role); + + foreach ($projectRestrictions as $restriction) { + if ($restriction['rule'] == ProjectRoleRestrictionModel::RULE_TASK_MOVE) { + return false; + } + } + + return true; + } + + /** + * Check if given role has a restriction + * + * @param integer $projectId + * @param string $role + * @param string $rule + * @return bool + */ + protected function hasRestriction($projectId, $role, $rule) + { + $projectRestrictions = $this->projectRoleRestrictionCacheDecorator->getAllByRole($projectId, $role); + + foreach ($projectRestrictions as $restriction) { + if ($restriction['rule'] == $rule) { + return true; + } + } + + return false; + } +} diff --git a/app/Helper/SubtaskHelper.php b/app/Helper/SubtaskHelper.php new file mode 100644 index 0000000..318ab1e --- /dev/null +++ b/app/Helper/SubtaskHelper.php @@ -0,0 +1,174 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; +use Kanboard\Model\SubtaskModel; + +/** + * Subtask helpers + * + * @package helper + * @author Frederic Guillot + */ +class SubtaskHelper extends Base +{ + /** + * Return if the current user has a subtask in progress + * + * @return bool + */ + public function hasSubtaskInProgress() + { + return session_is_true('hasSubtaskInProgress'); + } + + /** + * Render subtask title + * + * @param array $subtask + * @return string + */ + public function renderTitle(array $subtask) + { + if ($subtask['status'] == 0) { + $html = '<i class="fa fa-square-o fa-fw ' . ($this->hasSubtaskInProgress() ? 'js-modal-confirm' : '') . '"></i>'; + } elseif ($subtask['status'] == 1) { + $html = '<i class="fa fa-gears fa-fw"></i>'; + } else { + $html = '<i class="fa fa-check-square-o fa-fw"></i>'; + } + + return $html.$this->helper->text->e($subtask['title']); + } + + /** + * Get the link to toggle subtask status + * + * @access public + * @param array $task + * @param array $subtask + * @param string $fragment + * @param int $userId + * @return string + */ + public function renderToggleStatus(array $task, array $subtask, $fragment = '', $userId = 0) + { + if (! $this->helper->user->hasProjectAccess('SubtaskController', 'edit', $task['project_id'])) { + $html = $this->renderTitle($subtask); + } else { + $title = $this->renderTitle($subtask); + $params = array( + 'project_id' => $task['project_id'], + 'task_id' => $subtask['task_id'], + 'subtask_id' => $subtask['id'], + 'user_id' => $userId, + 'fragment' => $fragment, + 'csrf_token' => $this->token->getReusableCSRFToken(), + ); + + if ($subtask['status'] == 0 && $this->hasSubtaskInProgress()) { + $html = $this->helper->url->link($title, 'SubtaskRestrictionController', 'show', $params, false, 'js-modal-confirm', $this->getSubtaskTooltip($subtask)); + } else { + $html = $this->helper->url->link($title, 'SubtaskStatusController', 'change', $params, false, 'js-subtask-toggle-status', $this->getSubtaskTooltip($subtask)); + } + } + + return '<span class="subtask-title">'.$html.'</span>'; + } + + public function renderTimer(array $task, array $subtask) + { + $html = '<span class="subtask-timer-toggle">'; + $params = array( + 'task_id' => $subtask['task_id'], + 'subtask_id' => $subtask['id'], + 'timer' => '', + 'csrf_token' => $this->token->getReusableCSRFToken(), + ); + + if ($subtask['is_timer_started']) { + $params['timer'] = 'stop'; + $html .= $this->helper->url->icon('pause', t('Stop timer'), 'SubtaskStatusController', 'timer', $params, false, 'js-subtask-toggle-timer'); + $html .= ' (' . $this->helper->dt->age($subtask['timer_start_date']) .')'; + } else { + $params['timer'] = 'start'; + $html .= $this->helper->url->icon('play-circle-o', t('Start timer'), 'SubtaskStatusController', 'timer', $params, false, 'js-subtask-toggle-timer'); + } + + $html .= '</span>'; + + return $html; + } + + public function renderBulkTitleField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="1"', 'required'), $attributes); + + $html = $this->helper->form->label(t('Title'), 'title'); + $html .= $this->helper->form->textarea('title', $values, $errors, $attributes); + $html .= '<p class="form-help">'.t('Enter one subtask by line.').'</p>'; + + return $html; + } + + public function renderTitleField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="1"', 'required'), $attributes); + + $html = $this->helper->form->label(t('Title'), 'title'); + $html .= $this->helper->form->text('title', $values, $errors, $attributes, 'form-max-width'); + + return $html; + } + + 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 .= ' '; + $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').'" aria-label="'.t('Assign to me').'">'.t('Me').'</a>'; + $html .= '</small>'; + + return $html; + } + + public function renderTimeEstimatedField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="3"'), $attributes); + + $html = $this->helper->form->label(t('Original estimate'), 'time_estimated'); + $html .= $this->helper->form->numeric('time_estimated', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; + } + + public function renderTimeSpentField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="4"'), $attributes); + + $html = $this->helper->form->label(t('Time spent'), 'time_spent'); + $html .= $this->helper->form->numeric('time_spent', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; + } + + public function getSubtaskTooltip(array $subtask) + { + switch ($subtask['status']) { + case SubtaskModel::STATUS_TODO: + return t('Subtask not started'); + case SubtaskModel::STATUS_INPROGRESS: + return t('Subtask currently in progress'); + case SubtaskModel::STATUS_DONE: + return t('Subtask completed'); + } + + return ''; + } +} diff --git a/app/Helper/TaskHelper.php b/app/Helper/TaskHelper.php new file mode 100644 index 0000000..6c6c823 --- /dev/null +++ b/app/Helper/TaskHelper.php @@ -0,0 +1,380 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Task helpers + * + * @package helper + * @author Frederic Guillot + */ +class TaskHelper extends Base +{ + /** + * Local cache for project columns + * + * @access private + * @var array + */ + private $columns = array(); + + public function getColors() + { + return $this->colorModel->getList(); + } + + public function recurrenceTriggers() + { + return $this->taskRecurrenceModel->getRecurrenceTriggerList(); + } + + public function recurrenceTimeframes() + { + return $this->taskRecurrenceModel->getRecurrenceTimeframeList(); + } + + public function recurrenceBasedates() + { + return $this->taskRecurrenceModel->getRecurrenceBasedateList(); + } + + public function renderTitleField(array $values, array $errors) + { + $html = $this->helper->form->label(t('Title'), 'title', ['class="ui-helper-hidden-accessible"']); + $html .= $this->helper->form->text( + 'title', + $values, + $errors, + array( + 'autofocus', + 'required', + '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, 'aria-label' => t('Description'))); + } + + public function renderDescriptionTemplateDropdown($projectId) + { + $templates = $this->predefinedTaskDescriptionModel->getAll($projectId); + + if (! empty($templates)) { + $html = '<div class="dropdown dropdown-smaller">'; + $html .= '<a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-floppy-o fa-fw" aria-hidden="true"></i>'.t('Template for the task description').' <i class="fa fa-caret-down" aria-hidden="true"></i></a>'; + $html .= '<ul>'; + + foreach ($templates as $template) { + $html .= '<li>'; + $html .= '<a href="#" data-template-target="textarea[name=description]" data-template="'.$this->helper->text->e($template['description']).'" class="js-template">'; + $html .= $this->helper->text->e($template['title']); + $html .= '</a>'; + $html .= '</li>'; + } + + $html .= '</ul></div>'; + return $html; + } + + return ''; + } + + public function renderTagField(array $project, array $tags = array()) + { + $options = $this->tagModel->getAssignableList($project['id'], $project['enable_global_tags']); + + $html = $this->helper->form->label(t('Tags'), 'tags[]'); + $html .= '<input type="hidden" name="tags[]" value="">'; + $html .= '<select name="tags[]" id="form-tags" class="tag-autocomplete" multiple tabindex="3">'; + + foreach ($options as $tag) { + $html .= sprintf( + '<option value="%s" %s>%s</option>', + $this->helper->text->e($tag), + in_array($tag, $tags) ? 'selected="selected"' : '', + $this->helper->text->e($tag) + ); + } + + $html .= '</select>'; + + return $html; + } + + public function renderColorField(array $values) + { + $html = $this->helper->form->colorSelect('color_id', $values); + return $html; + } + + public function renderAssigneeField(array $users, array $values, array $errors = array(), array $attributes = array()) + { + if (isset($values['project_id']) && ! $this->helper->projectRole->canChangeAssignee($values)) { + return ''; + } + + $attributes = array_merge(array('tabindex="5"'), $attributes); + + $html = $this->helper->form->label(t('Assignee'), 'owner_id'); + $html .= $this->helper->form->select('owner_id', $users, $values, $errors, $attributes); + $html .= ' '; + $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').'" aria-label="'.t('Assign to me').'">'.t('Me').'</a>'; + $html .= '</small>'; + + return $html; + } + + public function renderCategoryField(array $categories, array $values, array $errors = array(), array $attributes = array(), $allow_one_item = false) + { + $attributes = array_merge(array('tabindex="6"'), $attributes); + $html = ''; + + if (! (! $allow_one_item && count($categories) === 1 && key($categories) == 0)) { + $html .= $this->helper->form->label(t('Category'), 'category_id'); + $html .= $this->helper->form->select('category_id', $categories, $values, $errors, $attributes); + } + + return $html; + } + + public function renderSwimlaneField(array $swimlanes, array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="7"'), $attributes); + $html = ''; + + if (count($swimlanes) > 1) { + $html .= $this->helper->form->label(t('Swimlane'), 'swimlane_id'); + $html .= $this->helper->form->select('swimlane_id', $swimlanes, $values, $errors, $attributes); + } + + return $html; + } + + public function renderColumnField(array $columns, array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="8"'), $attributes); + + $html = $this->helper->form->label(t('Column'), 'column_id'); + $html .= $this->helper->form->select('column_id', $columns, $values, $errors, $attributes); + + return $html; + } + + public function renderPriorityField(array $project, array $values) + { + $range = range($project['priority_start'], $project['priority_end']); + $options = array_combine($range, $range); + $values += array('priority' => $project['priority_default']); + + $html = $this->helper->form->label(t('Priority'), 'priority'); + $html .= $this->helper->form->select('priority', $options, $values, array(), array('tabindex="9"')); + + return $html; + } + + public function renderScoreField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="14"'), $attributes); + + $html = $this->helper->form->label(t('Complexity'), 'score'); + $html .= $this->helper->form->number('score', $values, $errors, $attributes); + + return $html; + } + + public function renderReferenceField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="15"'), $attributes); + + $html = $this->helper->form->label(t('Reference'), 'reference'); + $html .= $this->helper->form->text('reference', $values, $errors, $attributes, 'form-input-small'); + + return $html; + } + + public function renderTimeEstimatedField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="12"'), $attributes); + + $html = $this->helper->form->label(t('Original estimate'), 'time_estimated'); + $html .= $this->helper->form->numeric('time_estimated', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; + } + + public function renderTimeSpentField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="13"'), $attributes); + + $html = $this->helper->form->label(t('Time spent'), 'time_spent'); + $html .= $this->helper->form->numeric('time_spent', $values, $errors, $attributes); + $html .= ' '.t('hours'); + + return $html; + } + + public function renderStartDateField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="11"'), $attributes); + return $this->helper->form->datetime(t('Start Date'), 'date_started', $values, $errors, $attributes); + } + + public function renderDueDateField(array $values, array $errors = array(), array $attributes = array()) + { + $attributes = array_merge(array('tabindex="10"'), $attributes); + return $this->helper->form->datetime(t('Due Date'), 'date_due', $values, $errors, $attributes); + } + + public function renderPriority($priority) + { + $html = '<span class="task-priority" title="'.t('Task priority').'">'; + $html .= '<span class="ui-helper-hidden-accessible">'.t('Task priority').' </span>'; + $html .= $this->helper->text->e($priority >= 0 ? 'P'.$priority : '-P'.abs($priority)); + $html .= '</span>'; + + return $html; + } + + public function renderReference(array $task) + { + if (! empty($task['reference'])) { + $reference = $this->helper->text->e($task['reference']); + + if (filter_var($task['reference'], FILTER_VALIDATE_URL) !== false) { + return sprintf('<a href="%s" target=_blank">%s</a>', $reference, $reference); + } + + return $reference; + } + + return ''; + } + + public function renderInternalLinkField(array $internallinks, array $values, array $errors = array(), array $attributes = array()) + { + $html = ''; + + $html .= $this->helper->form->hidden('opposite_task_id', $values); + $html .= '<span class="task-internallink" title="'.t('Add internal link').'">'.t('Add internal link').'</span>'; + $html .= '<br><br>'; + $html .= $this->helper->form->label(t('Label'), 'link_id'); + $html .= $this->helper->form->select('link_id', $internallinks, $values, $errors, $attributes); + + $html .= $this->helper->form->label(t('Task'), 'title'); + $html .= $this->helper->form->text( + 'title', + $values, + $errors, + array( + 'required', + 'placeholder="'.t('Start to type task title...').'"', + 'title="'.t('Start to type task title...').'"', + 'data-dst-field="opposite_task_id"', + 'data-search-url="'.$this->helper->url->href('TaskAjaxController', 'autocomplete', array('exclude_task_ids' => $values['task_ids'])).'"', + ), + 'autocomplete' + ); + + return $html; + } + + public function renderFileUpload($screenshot = '', array $files = array()) + { + $upload_max_size = get_upload_max_size()*0.90; // 10% margin for the orther part of request + conversion in base64 + $html = '<div class="task-form-bottom-column">'; + $html .= ' <div id="screenshot-zone">'; + $html .= ' <p id="screenshot-inner">'.t('Take a screenshot and press CTRL+V or ⌘+V to paste here.').'</p>'; + $html .= ' </div>'; + $html .= '</div>'; + $html .= '<div class="task-form-bottom-column">'; + $html .= $this->helper->app->component('file-upload-task-create', array( + 'maxSize' => $upload_max_size, + 'labelDropzone' => t('Drag and drop your files here'), + 'labelOr' => t('or'), + 'labelChooseFiles' => t('choose files'), + 'labelOversize' => t('The total maximum allowed attachments size is %sB.', $this->helper->text->bytes($upload_max_size)), + 'labelSuccess' => t('All files have been uploaded successfully.'), + 'labelCloseSuccess' => t('Close this window'), + 'labelUploadError' => t('Unable to upload this file.'), + 'screenshot' => $screenshot, + 'files' => $files, + )); + $html .= '</div>'; + return $html; + } + + public function getProgress($task) + { + if (! isset($this->columns[$task['project_id']])) { + $this->columns[$task['project_id']] = $this->columnModel->getList($task['project_id']); + } + + return $this->taskModel->getProgress($task, $this->columns[$task['project_id']]); + } + + public function getNewBoardTaskButton(array $swimlane, array $column) + { + $html = '<div class="board-add-icon">'; + $providers = $this->externalTaskManager->getProviders(); + + if (empty($providers)) { + $html .= $this->helper->modal->largeIcon( + 'plus', + t('Add a new task'), + 'TaskCreationController', + 'show', + array( + 'project_id' => $column['project_id'], + 'column_id' => $column['id'], + 'swimlane_id' => $swimlane['id'], + ) + ); + } else { + $html .= '<div class="dropdown">'; + $html .= '<a href="#" class="dropdown-menu"><i class="fa fa-plus" aria-hidden="true"></i></a><ul>'; + + $link = $this->helper->modal->large( + 'plus', + t('Add a new Kanboard task'), + 'TaskCreationController', + 'show', + array( + 'project_id' => $column['project_id'], + 'column_id' => $column['id'], + 'swimlane_id' => $swimlane['id'], + ) + ); + + $html .= '<li>'.$link.'</li>'; + + foreach ($providers as $provider) { + $link = $this->helper->url->link( + $provider->getMenuAddLabel(), + 'ExternalTaskCreationController', + 'step1', + array('project_id' => $column['project_id'], 'swimlane_id' => $swimlane['id'], 'column_id' => $column['id'], 'provider_name' => $provider->getName()), + false, + 'js-modal-large' + ); + + $html .= '<li>'.$provider->getIcon().' '.$link.'</li>'; + } + + $html .= '</ul></div>'; + } + + $html .= '</div>'; + + return $html; + } +} diff --git a/app/Helper/TextHelper.php b/app/Helper/TextHelper.php new file mode 100644 index 0000000..f84c4c2 --- /dev/null +++ b/app/Helper/TextHelper.php @@ -0,0 +1,121 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Markdown; +use Kanboard\Core\Base; + +/** + * Text Helpers + * + * @package helper + * @author Frederic Guillot + */ +class TextHelper extends Base +{ + /** + * HTML escaping + * + * @param string $value Value to escape + * @return string + */ + public function e($value) + { + return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8', false); + } + + /** + * Join with HTML escaping + * + * @param $glue + * @param array $list + * @return string + */ + public function implode($glue, array $list) + { + array_walk($list, function (&$value) { + $value = htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false); + }); + return implode($glue, $list); + } + + /** + * Markdown transformation + * + * @param string $text + * @param boolean $isPublicLink + * @return string + */ + public function markdown($text, $isPublicLink = false) + { + $parser = new Markdown($this->container, $isPublicLink); + $parser->setSafeMode(true); + $parser->setMarkupEscaped(MARKDOWN_ESCAPE_HTML); + $parser->setBreaksEnabled(true); + return $parser->text($text ?: ''); + } + + /** + * Reply transformation + * + * @param string $username + * @param string $text + * @return string + */ + public function reply($username, $text) + { + $res = t('%s wrote: ', $username).PHP_EOL.'> '; + + $lines = preg_split("/\r\n|\n|\r/", $text); + + return $res.implode(PHP_EOL.'> ', $lines); + } + + /** + * Format a file size + * + * @param integer $size Size in bytes + * @param integer $precision Precision + * @return string + */ + public function bytes($size, $precision = 2) + { + if ($size == 0) { + return 0; + } + + $base = log($size) / log(1024); + $suffixes = array('', 'k', 'M', 'G', 'T'); + + return round(pow(1024, $base - floor($base)), $precision).$suffixes[(int)floor($base)]; + } + + /** + * Return true if needle is contained in the haystack + * + * @param string $haystack Haystack + * @param string $needle Needle + * @return boolean + */ + public function contains($haystack, $needle) + { + return strpos($haystack, $needle) !== false; + } + + /** + * Return a value from a dictionary + * + * @param mixed $id Key + * @param array $listing Dictionary + * @param string $default_value Value displayed when the key doesn't exists + * @return string + */ + public function in($id, array $listing, $default_value = '?') + { + if (isset($listing[$id])) { + return $this->helper->text->e($listing[$id]); + } + + return $default_value; + } +} diff --git a/app/Helper/UrlHelper.php b/app/Helper/UrlHelper.php new file mode 100644 index 0000000..08bd2b9 --- /dev/null +++ b/app/Helper/UrlHelper.php @@ -0,0 +1,214 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * Url Helper + * + * @package helper + * @author Frederic Guillot + */ +class UrlHelper extends Base +{ + private $base = ''; + private $directory = ''; + + /** + * Helper to generate a link to the documentation + * + * @access public + * @param string $label + * @param string $file + * @return string + */ + public function doc($label, $file = '') + { + $url = sprintf(DOCUMENTATION_URL_PATTERN, $file); + return sprintf('<a href="%s" target="_blank">%s</a>', $url, $label); + } + + /** + * Button Link Element + * + * @access public + * @param string $icon Font-Awesome icon + * @param string $label Link label + * @param string $controller Controller name + * @param string $action Action name + * @param array $params Url parameters + * @param string $class CSS class attribute + * @return string + */ + public function button($icon, $label, $controller, $action, array $params = array(), $class = '') + { + $html = '<i class="fa fa-'.$icon.' fa-fw"></i> '.$label; + $class = 'btn '.$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 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 absoluteLink($label, $controller, $action, array $params = array()) + { + return $this->link($label, $controller, $action, $params, false, '', '', true, '', true); + } + + /** + * HTML Hyperlink + * + * @access public + * @param string $controller Controller name + * @param string $action Action name + * @param array $params Url parameters + * @param boolean $csrf Add a CSRF token + * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link + * @return string + */ + public function href($controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false) + { + return $this->build('&', $controller, $action, $params, $csrf, $anchor, $absolute); + } + + /** + * Generate controller/action url + * + * @access public + * @param string $controller Controller name + * @param string $action Action name + * @param array $params Url parameters + * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link + * @return string + */ + public function to($controller, $action, array $params = array(), $anchor = '', $absolute = false) + { + return $this->build('&', $controller, $action, $params, false, $anchor, $absolute); + } + + /** + * Get application base url + * + * @access public + * @return string + */ + public function base() + { + if (empty($this->base)) { + $this->base = $this->configModel->get('application_url') ?: 'http://localhost/'; + } + + return $this->base; + } + + /** + * Get application base directory + * + * @access public + * @return string + */ + public function dir() + { + if ($this->directory === '' && $this->request->getMethod() !== '') { + if (defined('KANBOARD_URL') && strlen(KANBOARD_URL) > 0) { + $this->directory = parse_url(KANBOARD_URL, PHP_URL_PATH); + } else { + $this->directory = str_replace('\\', '/', dirname($this->request->getServerVariable('PHP_SELF'))); + $this->directory = $this->directory !== '/' ? $this->directory.'/' : '/'; + $this->directory = str_replace('//', '/', $this->directory); + } + } + + return $this->directory; + } + + /** + * Build relative url + * + * @access protected + * @param string $separator Querystring argument separator + * @param string $controller Controller name + * @param string $action Action name + * @param array $params Url parameters + * @param boolean $csrf Add a CSRF token + * @param string $anchor Link Anchor + * @param boolean $absolute Absolute or relative link + * @return string + */ + protected function build($separator, $controller, $action, array $params = array(), $csrf = false, $anchor = '', $absolute = false) + { + $path = $this->route->findUrl($controller, $action, $params); + $qs = array(); + + if (empty($path)) { + $qs['controller'] = $controller; + $qs['action'] = $action; + $qs += $params; + } else { + unset($params['plugin']); + } + + if ($csrf) { + $qs['csrf_token'] = $this->token->getCSRFToken(); + } + + if (! empty($qs)) { + $path .= '?'.http_build_query($qs, '', $separator); + } + + return ($absolute ? $this->base() : $this->dir()).$path.(empty($anchor) ? '' : '#'.$anchor); + } +} diff --git a/app/Helper/UserHelper.php b/app/Helper/UserHelper.php new file mode 100644 index 0000000..fbf1235 --- /dev/null +++ b/app/Helper/UserHelper.php @@ -0,0 +1,198 @@ +<?php + +namespace Kanboard\Helper; + +use Kanboard\Core\Base; + +/** + * User helpers + * + * @package helper + * @author Frederic Guillot + */ +class UserHelper extends Base +{ + public function getTheme() + { + return $this->userSession->getTheme(); + } + + /** + * Return subtask list toggle value + * + * @access public + * @return boolean + */ + public function hasSubtaskListActivated() + { + return $this->userSession->hasSubtaskListActivated(); + } + + /** + * Return true if the logged user has unread notifications + * + * @access public + * @return boolean + */ + public function hasNotifications() + { + return $this->userUnreadNotificationModel->hasNotifications($this->userSession->getId()); + } + + /** + * Get initials from a user + * + * @access public + * @param string $name + * @return string + */ + public function getInitials($name) + { + $initials = ''; + + foreach (explode(' ', $name, 2) as $string) { + $initials .= mb_substr($string, 0, 1, 'UTF-8'); + } + + return mb_strtoupper($initials, 'UTF-8'); + } + + /** + * Return the user full name + * + * @param array $user User properties + * @return string + */ + public function getFullname(array $user = array()) + { + $user = empty($user) ? $this->userSession->getAll() : $user; + return $user['name'] ?: $user['username']; + } + + /** + * Get user id + * + * @access public + * @return integer + */ + public function getId() + { + return $this->userSession->getId(); + } + + /** + * Check if the given user_id is the connected user + * + * @param integer $user_id User id + * @return boolean + */ + public function isCurrentUser($user_id) + { + return $this->userSession->getId() == $user_id; + } + + /** + * Return if the logged user is admin + * + * @access public + * @return boolean + */ + public function isAdmin() + { + return $this->userSession->isAdmin(); + } + + /** + * Get role + * + * @access public + * @return string + */ + public function getRole() + { + return $this->userSession->getRole(); + } + + /** + * Get role name + * + * @access public + * @param string $role + * @return string + */ + public function getRoleName($role = '') + { + return $this->role->getRoleName($role ?: $this->userSession->getRole()); + } + + /** + * Get group names for a given user and return an associative array: + * + * @access public + * @param integer $userID User id + * @return array + */ + public function getUsersGroupNames($userID) + { + $groupsList = array_column($this->groupMemberModel->getGroups($userID), 'name'); + $limitedList = $groupsList; + $total = count($groupsList); + + if ($total > 0 && SHOW_GROUP_MEMBERSHIPS_IN_USERLIST_WITH_LIMIT > 0) { + $limitedList = array_slice($groupsList, 0, SHOW_GROUP_MEMBERSHIPS_IN_USERLIST_WITH_LIMIT); + } + + return [ + 'full_list' => $groupsList, + 'limited_list' => $limitedList, + 'has_groups' => $total > 0, + 'total' => $total, + 'shown' => count($limitedList), + ]; + } + + /** + * Check application access + * + * @param string $controller + * @param string $action + * @return bool + */ + public function hasAccess($controller, $action) + { + if (! $this->userSession->isLogged()) { + return false; + } + + $key = 'app_access:'.$controller.$action; + $result = $this->memoryCache->get($key); + + if ($result === null) { + $result = $this->applicationAuthorization->isAllowed($controller, $action, $this->userSession->getRole()); + $this->memoryCache->set($key, $result); + } + + return $result; + } + + /** + * Check project access + * + * @param string $controller + * @param string $action + * @param integer $project_id + * @return bool + */ + public function hasProjectAccess($controller, $action, $project_id) + { + $key = 'project_access:'.$controller.$action.$project_id; + $result = $this->memoryCache->get($key); + + if ($result === null) { + $result = $this->helper->projectRole->checkProjectAccess($controller, $action, $project_id); + $this->memoryCache->set($key, $result); + } + + return $result; + } +} diff --git a/app/Import/TaskImport.php b/app/Import/TaskImport.php new file mode 100644 index 0000000..9ed351c --- /dev/null +++ b/app/Import/TaskImport.php @@ -0,0 +1,179 @@ +<?php + +namespace Kanboard\Import; + +use Kanboard\Core\Base; +use Kanboard\Core\Csv; +use Kanboard\Core\ExternalLink\ExternalLinkManager; +use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound; +use SimpleValidator\Validator; +use SimpleValidator\Validators; + +/** + * Task CSV Import + * + * @package Kanboard\Import + * @author Frederic Guillot + */ +class TaskImport extends Base +{ + protected $nbImportedTasks = 0; + protected $projectId = 0; + + public function setProjectId($projectId) + { + $this->projectId = $projectId; + return $this; + } + + public function getNumberOfImportedTasks() + { + return $this->nbImportedTasks; + } + + public function getColumnMapping() + { + return array( + 'reference' => e('Reference'), + 'title' => e('Title'), + 'description' => e('Description'), + 'assignee' => e('Assignee Username'), + 'creator' => e('Creator Username'), + 'color' => e('Color Name'), + 'column' => e('Column Name'), + 'category' => e('Category Name'), + 'swimlane' => e('Swimlane Name'), + 'score' => e('Complexity'), + 'time_estimated' => e('Time Estimated'), + 'time_spent' => e('Time Spent'), + 'date_started' => e('Start Date'), + 'date_due' => e('Due Date'), + 'priority' => e('Priority'), + 'is_active' => e('Status'), + 'tags' => e('Tags'), + 'external_link' => e('External Link'), + ); + } + + public function importTask(array $row, $lineNumber) + { + $task = $this->prepareTask($row); + + if ($this->validateCreation($task)) { + $taskId = $this->taskCreationModel->create($task); + + if ($taskId > 0) { + $this->logger->debug(__METHOD__.': imported successfully line '.$lineNumber); + $this->nbImportedTasks++; + + if (! empty($row['tags'])) { + $this->taskTagModel->save($this->projectId, $taskId, explode_csv_field($row['tags'])); + } + + if (! empty($row['external_link'])) { + $this->createExternalLink($taskId, $row['external_link']); + } + } else { + $this->logger->error(__METHOD__.': creation error at line '.$lineNumber); + } + } else { + $this->logger->error(__METHOD__.': validation error at line '.$lineNumber); + } + } + + public function prepareTask(array $row) + { + $values = array(); + $values['project_id'] = $this->projectId; + $values['reference'] = $row['reference']; + $values['title'] = $row['title']; + $values['description'] = $row['description']; + $values['is_active'] = Csv::getBooleanValue($row['is_active']) == 1 ? 0 : 1; + $values['score'] = (int) $row['score']; + $values['priority'] = (int) $row['priority']; + $values['time_estimated'] = (float) $row['time_estimated']; + $values['time_spent'] = (float) $row['time_spent']; + + if (! empty($row['assignee'])) { + $values['owner_id'] = $this->userModel->getIdByUsername($row['assignee']); + } + + if (! empty($row['creator'])) { + $values['creator_id'] = $this->userModel->getIdByUsername($row['creator']); + } + + if (! empty($row['color'])) { + $values['color_id'] = $this->colorModel->find($row['color']); + } + + if (! empty($row['column'])) { + $values['column_id'] = $this->columnModel->getColumnIdByTitle($this->projectId, $row['column']); + } + + if (! empty($row['category'])) { + $values['category_id'] = $this->categoryModel->getIdByName($this->projectId, $row['category']); + } + + if (! empty($row['swimlane'])) { + $values['swimlane_id'] = $this->swimlaneModel->getIdByName($this->projectId, $row['swimlane']); + } + + if (! empty($row['date_due'])) { + $values['date_due'] = $this->dateParser->getTimestamp($row['date_due']); + } + + if (! empty($row['date_started'])) { + $values['date_started'] = $this->dateParser->getTimestamp($row['date_started']); + } + + $this->helper->model->removeEmptyFields( + $values, + array('owner_id', 'creator_id', 'color_id', 'column_id', 'category_id', 'swimlane_id', 'date_due', 'date_started', 'priority') + ); + + return $values; + } + + protected function validateCreation(array $values) + { + $v = new Validator($values, array( + new Validators\Integer('project_id', t('This value must be an integer')), + new Validators\Required('project_id', t('The project is required')), + new Validators\Required('title', t('The title is required')), + new Validators\MaxLength('title', t('The maximum length is %d characters', 65535), 65535), + new Validators\MaxLength('reference', t('The maximum length is %d characters', 255), 255), + )); + + return $v->execute(); + } + + protected function createExternalLink($taskId, $externalLink) + { + try { + $provider = $this->externalLinkManager + ->setUserInputText($externalLink) + ->setUserInputType(ExternalLinkManager::TYPE_AUTO) + ->find(); + + $link = $provider->getLink(); + $dependencies = $provider->getDependencies(); + $values = array( + 'task_id' => $taskId, + 'title' => $link->getTitle() ?: $link->getUrl(), + 'url' => $link->getUrl(), + 'link_type' => $provider->getType(), + 'dependency' => key($dependencies), + ); + + list($valid, $errors) = $this->externalLinkValidator->validateCreation($values); + + if ($valid) { + $this->taskExternalLinkModel->create($values); + } else { + $this->logger->error(__METHOD__.': '.var_export($errors, true)); + } + } catch (ExternalLinkProviderNotFound $e) { + $this->logger->error(__METHOD__.': '.$e->getMessage()); + } + } +} diff --git a/app/Import/UserImport.php b/app/Import/UserImport.php new file mode 100644 index 0000000..004d20b --- /dev/null +++ b/app/Import/UserImport.php @@ -0,0 +1,126 @@ +<?php + +namespace Kanboard\Import; + +use Kanboard\Model\UserModel; +use SimpleValidator\Validator; +use SimpleValidator\Validators; +use Kanboard\Core\Security\Role; +use Kanboard\Core\Base; +use Kanboard\Core\Csv; +use Kanboard\Notification\MailNotification; +use Kanboard\Notification\WebNotification; + +/** + * User Import + * + * @package import + * @author Frederic Guillot + */ +class UserImport extends Base +{ + /** + * Number of successful import + * + * @access public + * @var integer + */ + public $counter = 0; + + /** + * Get mapping between CSV header and SQL columns + * + * @access public + * @return array + */ + public function getColumnMapping() + { + return array( + 'username' => 'Username', + 'password' => 'Password', + 'email' => 'Email', + 'name' => 'Full Name', + 'is_admin' => 'Administrator', + 'is_manager' => 'Manager', + 'is_ldap_user' => 'Remote User', + ); + } + + /** + * Import a single row + * + * @access public + * @param array $row + * @param integer $line_number + */ + public function import(array $row, $line_number) + { + $row = $this->prepare($row); + + if ($this->validateCreation($row)) { + if (($user_id = $this->userModel->create($row)) !== false) { + $this->logger->debug('UserImport: imported successfully line '.$line_number); + $this->counter++; + + if ($this->configModel->get('notifications_enabled', 0) == 1) { + $this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]); + } + } else { + $this->logger->error('UserImport: creation error at line '.$line_number); + } + } else { + $this->logger->error('UserImport: validation error at line '.$line_number); + } + } + + /** + * Format row before validation + * + * @access public + * @param array $row + * @return array + */ + public function prepare(array $row) + { + $row['username'] = strtolower($row['username']); + + foreach (array('is_admin', 'is_manager', 'is_ldap_user') as $field) { + $row[$field] = Csv::getBooleanValue($row[$field]); + } + + if ($row['is_admin'] == 1) { + $row['role'] = Role::APP_ADMIN; + } elseif ($row['is_manager'] == 1) { + $row['role'] = Role::APP_MANAGER; + } else { + $row['role'] = Role::APP_USER; + } + + unset($row['is_admin']); + unset($row['is_manager']); + + $this->helper->model->removeEmptyFields($row, array('password', 'email', 'name')); + + return $row; + } + + /** + * Validate user creation + * + * @access public + * @param array $values + * @return boolean + */ + public function validateCreation(array $values) + { + $v = new Validator($values, array( + new Validators\MaxLength('username', t('The maximum length is %d characters', 255), 255), + new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), UserModel::TABLE, 'id'), + new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6), + new Validators\Email('email', t('Email address invalid')), + new Validators\Integer('is_ldap_user', t('This value must be an integer')), + )); + + return $v->execute(); + } +} diff --git a/app/Job/BaseJob.php b/app/Job/BaseJob.php new file mode 100644 index 0000000..60522ac --- /dev/null +++ b/app/Job/BaseJob.php @@ -0,0 +1,33 @@ +<?php + +namespace Kanboard\Job; + +use Kanboard\Core\Base; + +/** + * Class BaseJob + * + * @package Kanboard\Job + * @author Frederic Guillot + */ +abstract class BaseJob extends Base +{ + /** + * Job parameters + * + * @access protected + * @var array + */ + protected $jobParams = array(); + + /** + * Get job parameters + * + * @access public + * @return array + */ + public function getJobParams() + { + return $this->jobParams; + } +} diff --git a/app/Job/CommentEventJob.php b/app/Job/CommentEventJob.php new file mode 100644 index 0000000..4faec20 --- /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($event, $eventName); + + 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/EmailJob.php b/app/Job/EmailJob.php new file mode 100644 index 0000000..c6eb500 --- /dev/null +++ b/app/Job/EmailJob.php @@ -0,0 +1,55 @@ +<?php + +namespace Kanboard\Job; + +/** + * Class EmailJob + * + * @package Kanboard\Job + * @author Frederic Guillot + */ +class EmailJob extends BaseJob +{ + /** + * Set job parameters + * + * @access public + * @param string $recipientEmail + * @param string $recipientName + * @param string $subject + * @param string $html + * @param string $authorName + * @param string $authorEmail + * @return $this + */ + public function withParams($recipientEmail, $recipientName, $subject, $html, $authorName, $authorEmail) + { + $this->jobParams = array($recipientEmail, $recipientName, $subject, $html, $authorName, $authorEmail); + return $this; + } + + /** + * Execute job + * + * @access public + * @param string $recipientEmail + * @param string $recipientName + * @param string $subject + * @param string $html + * @param string $authorName + * @param string $authorEmail + */ + public function execute($recipientEmail, $recipientName, $subject, $html, $authorName, $authorEmail) + { + $transport = $this->helper->mail->getMailTransport(); + $startTime = microtime(true); + + $this->logger->debug(__METHOD__.' Sending email to: '.$recipientEmail.' using transport: '.$transport); + + $this->emailClient + ->getTransport($transport) + ->sendEmail($recipientEmail, $recipientName, $subject, $html, $authorName, $authorEmail); + + $this->logger->debug(__METHOD__.' Email sent in '.round(microtime(true) - $startTime, 6).' seconds'); + } +} diff --git a/app/Job/HttpAsyncJob.php b/app/Job/HttpAsyncJob.php new file mode 100644 index 0000000..9e1ffb6 --- /dev/null +++ b/app/Job/HttpAsyncJob.php @@ -0,0 +1,44 @@ +<?php + +namespace Kanboard\Job; + +/** + * Async HTTP Client (fire and forget) + * + * @package Kanboard\Job + * @author Frederic Guillot + */ +class HttpAsyncJob extends BaseJob +{ + /** + * Set job parameters + * + * @access public + * @param string $method + * @param string $url + * @param string $content + * @param array $headers + * @param bool $raiseForErrors + * @return $this + */ + public function withParams($method, $url, $content, array $headers, $raiseForErrors = false) + { + $this->jobParams = array($method, $url, $content, $headers, $raiseForErrors); + return $this; + } + + /** + * Set job parameters + * + * @access public + * @param string $method + * @param string $url + * @param string $content + * @param array $headers + * @param bool $raiseForErrors + */ + public function execute($method, $url, $content, array $headers, $raiseForErrors = false) + { + $this->httpClient->doRequest($method, $url, $content, $headers, $raiseForErrors); + } +} diff --git a/app/Job/NotificationJob.php b/app/Job/NotificationJob.php new file mode 100644 index 0000000..8fb260e --- /dev/null +++ b/app/Job/NotificationJob.php @@ -0,0 +1,43 @@ +<?php + +namespace Kanboard\Job; + +use Kanboard\Event\GenericEvent; + +/** + * Class NotificationJob + * + * @package Kanboard\Job + * @author Frederic Guillot + */ +class NotificationJob extends BaseJob +{ + /** + * Set job parameters + * + * @param GenericEvent $event + * @param string $eventName + * @return $this + */ + public function withParams(GenericEvent $event, $eventName) + { + $this->jobParams = array($event->getAll(), $eventName); + return $this; + } + + /** + * Execute job + * + * @param array $eventData + * @param string $eventName + */ + public function execute(array $eventData, $eventName) + { + 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); + } + } +} diff --git a/app/Job/ProjectFileEventJob.php b/app/Job/ProjectFileEventJob.php new file mode 100644 index 0000000..c75dc98 --- /dev/null +++ b/app/Job/ProjectFileEventJob.php @@ -0,0 +1,44 @@ +<?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 + */ + public function execute($fileId, $eventName) + { + $event = ProjectFileEventBuilder::getInstance($this->container) + ->withFileId($fileId) + ->buildEvent(); + + if ($event !== null) { + $this->dispatcher->dispatch($event, $eventName); + } + } +} diff --git a/app/Job/ProjectMetricJob.php b/app/Job/ProjectMetricJob.php new file mode 100644 index 0000000..6330bd4 --- /dev/null +++ b/app/Job/ProjectMetricJob.php @@ -0,0 +1,40 @@ +<?php + +namespace Kanboard\Job; + +/** + * Class ProjectMetricJob + * + * @package Kanboard\Job + * @author Frederic Guillot + */ +class ProjectMetricJob extends BaseJob +{ + /** + * Set job parameters + * + * @access public + * @param integer $projectId + * @return $this + */ + public function withParams($projectId) + { + $this->jobParams = array($projectId); + return $this; + } + + /** + * Execute job + * + * @access public + * @param integer $projectId + */ + public function execute($projectId) + { + $this->logger->debug(__METHOD__.' Run project metrics calculation'); + $now = date('Y-m-d'); + + $this->projectDailyColumnStatsModel->updateTotals($projectId, $now); + $this->projectDailyStatsModel->updateTotals($projectId, $now); + } +} diff --git a/app/Job/SubtaskEventJob.php b/app/Job/SubtaskEventJob.php new file mode 100644 index 0000000..340ffcd --- /dev/null +++ b/app/Job/SubtaskEventJob.php @@ -0,0 +1,49 @@ +<?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 array $eventNames + * @param array $values + * @return $this + */ + public function withParams($subtaskId, array $eventNames, array $values = array()) + { + $this->jobParams = array($subtaskId, $eventNames, $values); + return $this; + } + + /** + * Execute job + * + * @param int $subtaskId + * @param array $eventNames + * @param array $values + */ + public function execute($subtaskId, array $eventNames, array $values = array()) + { + $event = SubtaskEventBuilder::getInstance($this->container) + ->withSubtaskId($subtaskId) + ->withValues($values) + ->buildEvent(); + + if ($event !== null) { + foreach ($eventNames as $eventName) { + $this->dispatcher->dispatch($event, $eventName); + } + } + } +} diff --git a/app/Job/TaskEventJob.php b/app/Job/TaskEventJob.php new file mode 100644 index 0000000..fd15847 --- /dev/null +++ b/app/Job/TaskEventJob.php @@ -0,0 +1,75 @@ +<?php + +namespace Kanboard\Job; + +use Kanboard\Event\TaskEvent; +use Kanboard\EventBuilder\TaskEventBuilder; +use Kanboard\Model\TaskModel; + +/** + * Class TaskEventJob + * + * @package Kanboard\Job + * @author Frederic Guillot + */ +class TaskEventJob extends BaseJob +{ + /** + * Set job params + * + * @param int $taskId + * @param array $eventNames + * @param array $changes + * @param array $values + * @param array $task + * @return $this + */ + public function withParams($taskId, array $eventNames, array $changes = array(), array $values = array(), array $task = array()) + { + $this->jobParams = array($taskId, $eventNames, $changes, $values, $task); + return $this; + } + + /** + * Execute job + * + * @param int $taskId + * @param array $eventNames + * @param array $changes + * @param array $values + * @param array $task + */ + 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($event, $eventName); + + 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 0000000..9b3cf82 --- /dev/null +++ b/app/Job/TaskFileEventJob.php @@ -0,0 +1,44 @@ +<?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 + */ + public function execute($fileId, $eventName) + { + $event = TaskFileEventBuilder::getInstance($this->container) + ->withFileId($fileId) + ->buildEvent(); + + if ($event !== null) { + $this->dispatcher->dispatch($event, $eventName); + } + } +} diff --git a/app/Job/TaskLinkEventJob.php b/app/Job/TaskLinkEventJob.php new file mode 100644 index 0000000..55be819 --- /dev/null +++ b/app/Job/TaskLinkEventJob.php @@ -0,0 +1,44 @@ +<?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 + */ + public function execute($taskLinkId, $eventName) + { + $event = TaskLinkEventBuilder::getInstance($this->container) + ->withTaskLinkId($taskLinkId) + ->buildEvent(); + + if ($event !== null) { + $this->dispatcher->dispatch($event, $eventName); + } + } +} diff --git a/app/Job/UserMentionJob.php b/app/Job/UserMentionJob.php new file mode 100644 index 0000000..a60ee24 --- /dev/null +++ b/app/Job/UserMentionJob.php @@ -0,0 +1,75 @@ +<?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($event, $eventName); + } + } + } + + /** + * Get list of mentioned users + * + * @access public + * @param string $text + * @return array + */ + public function getMentionedUsers($text) + { + $users = array(); + + if ($text !== null && 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/ar_SY/translations.php b/app/Locale/ar_SY/translations.php new file mode 100644 index 0000000..8b018ad --- /dev/null +++ b/app/Locale/ar_SY/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => 'Ù«', + 'number.thousands_separator' => 'Ù¬', + 'None' => 'لا شيء', + 'Edit' => 'تعديل', + 'Remove' => 'إزالة', + 'Yes' => 'نعم', + 'No' => 'لا', + 'cancel' => 'إلغاء', + 'or' => 'أو', + 'Yellow' => 'Ø£ØµÙØ±', + 'Blue' => 'أزرق', + 'Green' => 'أخضر', + 'Purple' => 'أرجواني', + 'Red' => 'أحمر', + 'Orange' => 'برتقالي', + 'Grey' => 'رمادي', + 'Brown' => 'بني', + 'Deep Orange' => 'برتقالي داكن', + 'Dark Grey' => 'رمادي داكن', + 'Pink' => 'وردي', + 'Teal' => 'تركوازي', + 'Cyan' => 'سماوي', + 'Lime' => 'أخضر ليموني', + 'Light Green' => 'أخضر ÙØ§ØªØ­', + 'Amber' => 'كهرماني', + 'Save' => 'Ø­ÙØ¸', + 'Login' => 'تسجيل الدخول', + 'Official website:' => 'الموقع الرسمي:', + 'Unassigned' => 'غير معيّن', + 'View this task' => 'عرض هذه المهمة', + 'Remove user' => 'إزالة المستخدم', + 'Do you really want to remove this user: "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة هذا المستخدم: "%s"ØŸ', + 'All users' => 'جميع المستخدمين', + 'Username' => 'اسم المستخدم', + 'Password' => 'كلمة المرور', + 'Administrator' => 'مدير النظام', + 'Sign in' => 'تسجيل الدخول', + 'Users' => 'المستخدمون', + 'Forbidden' => 'ممنوع', + 'Access Forbidden' => 'الوصول ممنوع', + 'Edit user' => 'تعديل المستخدم', + 'Logout' => 'تسجيل الخروج', + 'Bad username or password' => 'اسم المستخدم أو كلمة المرور غير صحيحة', + 'Edit project' => 'تعديل المشروع', + 'Name' => 'الاسم', + 'Projects' => 'المشاريع', + 'No project' => 'لا يوجد مشروع', + 'Project' => 'مشروع', + 'Status' => 'الحالة', + 'Tasks' => 'المهام', + 'Board' => 'اللوحة', + 'Actions' => 'الإجراءات', + 'Inactive' => 'غير نشط', + 'Active' => 'نشط', + 'Unable to update this board.' => 'تعذّر تحديث هذه اللوحة.', + 'Disable' => 'تعطيل', + 'Enable' => 'تمكين', + 'New project' => 'مشروع جديد', + 'Do you really want to remove this project: "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة هذا المشروع: "%s"ØŸ', + 'Remove project' => 'إزالة المشروع', + 'Edit the board for "%s"' => 'تعديل اللوحة لـ "%s"', + 'Add a new column' => 'Ø¥Ø¶Ø§ÙØ© عمود جديد', + 'Title' => 'العنوان', + 'Assigned to %s' => 'Ù…ÙØ³Ù†Ø¯ إلى %s', + 'Remove a column' => 'إزالة عمود', + 'Unable to remove this column.' => 'تعذّر إزالة هذا العمود.', + 'Do you really want to remove this column: "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة هذا العمود: "%s"ØŸ', + 'Settings' => 'الإعدادات', + 'Application settings' => 'إعدادات التطبيق', + 'Language' => 'اللغة', + 'Webhook token:' => 'رمز Webhook:', + 'API token:' => 'رمز الواجهة البرمجية API:', + 'Database size:' => 'حجم قاعدة البيانات:', + 'Download the database' => 'تنزيل قاعدة البيانات', + 'Optimize the database' => 'تحسين قاعدة البيانات', + '(VACUUM command)' => '(أمر VACUUM)', + '(Gzip compressed Sqlite file)' => '(مل٠Sqlite مضغوط بـ Gzip)', + 'Close a task' => 'إغلاق مهمة', + 'Column' => 'العمود', + 'Color' => 'اللون', + 'Assignee' => 'المكلّÙ', + 'Create another task' => 'إنشاء مهمة أخرى', + 'New task' => 'مهمة جديدة', + 'Open a task' => 'ÙØªØ­ مهمة', + 'Do you really want to open this task: "%s"?' => 'هل تريد ÙØ¹Ù„اً ÙØªØ­ هذه المهمة: "%s"ØŸ', + 'Back to the board' => 'العودة إلى اللوحة', + 'There is nobody assigned' => 'لا يوجد أي شخص معيّن', + 'Column on the board:' => 'العمود على اللوحة:', + 'Close this 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 أحرÙ', + 'The password is required' => 'كلمة المرور مطلوبة', + 'This value must be an integer' => 'يجب أن تكون هذه القيمة عددًا صحيحًا', + 'The username must be unique' => 'يجب أن يكون اسم المستخدم ÙØ±ÙŠØ¯Ù‹Ø§', + 'The user id is required' => 'معرّ٠المستخدم مطلوب', + 'Passwords don\'t match' => 'كلمتا المرور غير متطابقتين', + 'The confirmation is required' => 'التأكيد مطلوب', + 'The project is required' => 'المشروع مطلوب', + 'The id is required' => 'المعرّ٠مطلوب', + 'The project id is required' => 'معرّ٠المشروع مطلوب', + 'The project name is required' => 'اسم المشروع مطلوب', + 'The title is required' => 'العنوان مطلوب', + 'Settings saved successfully.' => 'تم Ø­ÙØ¸ الإعدادات بنجاح.', + 'Unable to save your settings.' => 'تعذّر Ø­ÙØ¸ إعداداتك.', + 'Database optimization done.' => 'تم تحسين قاعدة البيانات.', + 'Your project has been created successfully.' => 'تم إنشاء مشروعك بنجاح.', + 'Unable to create your project.' => 'تعذّر إنشاء مشروعك.', + 'Project updated successfully.' => 'تم تحديث المشروع بنجاح.', + 'Unable to update this project.' => 'تعذّر تحديث هذا المشروع.', + 'Unable to remove this project.' => 'تعذّر إزالة هذا المشروع.', + 'Project removed successfully.' => 'تمت إزالة المشروع بنجاح.', + 'Project activated successfully.' => 'تم ØªÙØ¹ÙŠÙ„ المشروع بنجاح.', + 'Unable to activate this project.' => 'تعذّر ØªÙØ¹ÙŠÙ„ هذا المشروع.', + 'Project disabled successfully.' => 'تم تعطيل المشروع بنجاح.', + 'Unable to disable this project.' => 'تعذّر تعطيل هذا المشروع.', + 'Unable to open this task.' => 'تعذّر ÙØªØ­ هذه المهمة.', + 'Task opened successfully.' => 'تم ÙØªØ­ المهمة بنجاح.', + 'Unable to close this task.' => 'تعذّر إغلاق هذه المهمة.', + 'Task closed successfully.' => 'تم إغلاق المهمة بنجاح.', + 'Unable to update your task.' => 'تعذّر تحديث مهمتك.', + 'Task updated successfully.' => 'تم تحديث المهمة بنجاح.', + 'Unable to create your task.' => 'تعذّر إنشاء مهمتك.', + 'Task created successfully.' => 'تم إنشاء المهمة بنجاح.', + 'User created successfully.' => 'تم إنشاء المستخدم بنجاح.', + 'Unable to create your user.' => 'تعذّر إنشاء المستخدم.', + 'User updated successfully.' => 'تم تحديث المستخدم بنجاح.', + 'User removed successfully.' => 'تمت إزالة المستخدم بنجاح.', + 'Unable to remove this user.' => 'تعذّر إزالة هذا المستخدم.', + 'Board updated successfully.' => 'تم تحديث اللوحة بنجاح.', + 'Ready' => 'جاهزة', + 'Backlog' => 'المتراكم', + 'Work in progress' => 'العمل جارÙ', + 'Done' => 'منجز', + 'Application version:' => 'إصدار التطبيق:', + 'Id' => 'المعرّÙ', + 'Public link' => 'رابط عام', + 'Timezone' => 'المنطقة الزمنية', + 'Sorry, I didn\'t find this information in my database!' => 'عذرًا، لم أجد هذه المعلومات ÙÙŠ قاعدة البيانات!', + 'Page not found' => 'Ø§Ù„ØµÙØ­Ø© غير موجودة', + 'Complexity' => 'التعقيد', + 'Task limit' => 'حد المهام', + 'Task count' => 'عدد المهام', + 'User' => 'المستخدم', + 'Comments' => 'التعليقات', + 'Comment is required' => 'التعليق مطلوب', + 'Comment added successfully.' => 'تمت Ø¥Ø¶Ø§ÙØ© التعليق بنجاح.', + 'Unable to create your comment.' => 'تعذّر إنشاء تعليقك.', + 'Due Date' => 'تاريخ الاستحقاق', + 'Invalid date' => 'تاريخ غير صالح', + 'Automatic actions' => 'إجراءات تلقائية', + 'Your automatic action has been created successfully.' => 'تم إنشاء الإجراء التلقائي بنجاح.', + 'Unable to create your automatic action.' => 'تعذّر إنشاء الإجراء التلقائي.', + 'Remove an action' => 'إزالة إجراء', + 'Unable to remove this action.' => 'تعذّر إزالة هذا الإجراء.', + 'Action removed successfully.' => 'تمت إزالة الإجراء بنجاح.', + 'Automatic actions for the project "%s"' => 'الإجراءات التلقائية للمشروع "%s"', + 'Add an action' => 'Ø¥Ø¶Ø§ÙØ© إجراء', + 'Event name' => 'اسم الحدث', + 'Action' => 'الإجراء', + 'Event' => 'الحدث', + 'When the selected event occurs execute the corresponding action.' => 'عند حدوث الحدث المحدّد تÙÙ†Ùَّذ الإجراء المواÙÙ‚.', + 'Next step' => 'الخطوة التالية', + '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 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' => 'تعيين لون لمستخدم محدد', + 'Position' => 'الموضع', + 'Duplicate to project' => 'استنساخ إلى مشروع', + 'Duplicate' => 'استنساخ', + 'Link' => 'رابط', + 'Comment updated successfully.' => 'تم تحديث التعليق بنجاح.', + 'Unable to update your comment.' => 'تعذّر تحديث تعليقك.', + 'Remove a comment' => 'إزالة تعليق', + 'Comment removed successfully.' => 'تمت إزالة التعليق بنجاح.', + 'Unable to remove this comment.' => 'تعذّر إزالة هذا التعليق.', + 'Do you really want to remove this comment?' => 'هل تريد ÙØ¹Ù„اً إزالة هذا التعليق؟', + 'Current password for the user "%s"' => 'كلمة المرور الحالية للمستخدم "%s"', + 'The current password is required' => 'كلمة المرور الحالية مطلوبة', + 'Wrong password' => 'كلمة مرور غير صحيحة', + 'Unknown' => 'غير معروÙ', + 'Last logins' => 'آخر عمليات تسجيل الدخول', + 'Login date' => 'تاريخ تسجيل الدخول', + 'Authentication method' => 'طريقة المصادقة', + 'IP address' => 'عنوان IP', + 'User agent' => 'وكيل المستخدم', + 'Persistent connections' => 'جلسات دائمة', + 'No session.' => 'لا توجد جلسة.', + 'Expiration date' => 'تاريخ الانتهاء', + 'Remember Me' => 'تذكرني', + 'Creation date' => 'تاريخ الإنشاء', + 'Everybody' => 'الجميع', + 'Open' => 'Ù…ÙØªÙˆØ­', + 'Closed' => 'مغلق', + 'Search' => 'بحث', + 'Nothing found.' => 'لم يتم العثور على شيء.', + 'Due date' => 'تاريخ الاستحقاق', + 'Description' => 'الوصÙ', + '%d comments' => '%d تعليقات', + '%d comment' => '%d تعليق', + '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.' => 'تم ربط حسابك الخارجي بملÙÙƒ بنجاح.', + 'Email' => 'البريد الإلكتروني', + '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' => 'تعيين ÙØ¦Ø© تلقائيًا بناءً على اللون', + 'Task creation or modification' => 'إنشاء أو تعديل مهمة', + 'Category' => 'Ø§Ù„ÙØ¦Ø©', + 'Category:' => 'Ø§Ù„ÙØ¦Ø©:', + 'Categories' => 'Ø§Ù„ÙØ¦Ø§Øª', + 'Your category has been created successfully.' => 'تم إنشاء Ø§Ù„ÙØ¦Ø© بنجاح.', + 'This category has been updated successfully.' => 'تم تحديث هذه Ø§Ù„ÙØ¦Ø© بنجاح.', + 'Unable to update this 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' => 'بلا ÙØ¦Ø©', + 'The name is required' => 'الاسم مطلوب', + 'Remove a file' => 'إزالة ملÙ', + 'Unable to remove this file.' => 'تعذّر إزالة هذا الملÙ.', + 'File removed successfully.' => 'تمت إزالة المل٠بنجاح.', + 'Attach a document' => 'Ø¥Ø±ÙØ§Ù‚ مستند', + 'Do you really want to remove this file: "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة هذا الملÙ: "%s"ØŸ', + 'Attachments' => 'المرÙقات', + 'Edit the task' => 'تعديل المهمة', + 'Add a comment' => 'Ø¥Ø¶Ø§ÙØ© تعليق', + 'Edit a comment' => 'تعديل تعليق', + 'Summary' => 'ملخص', + 'Time tracking' => 'تتبع الوقت', + 'Estimate:' => 'التقدير:', + 'Spent:' => 'المستغرق:', + 'Do you really want to remove this sub-task?' => 'هل تريد ÙØ¹Ù„اً إزالة هذه المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ©ØŸ', + 'Remaining:' => 'المتبقي:', + 'hours' => 'ساعات', + 'estimated' => 'مقدّر', + 'Sub-Tasks' => 'المهام Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Add a sub-task' => 'Ø¥Ø¶Ø§ÙØ© مهمة ÙØ±Ø¹ÙŠØ©', + 'Original estimate' => 'التقدير الأصلي', + 'Create another sub-task' => 'إنشاء مهمة ÙØ±Ø¹ÙŠØ© أخرى', + 'Time spent' => 'الوقت المستغرق', + 'Edit a sub-task' => 'تعديل مهمة ÙØ±Ø¹ÙŠØ©', + 'Remove a sub-task' => 'إزالة مهمة ÙØ±Ø¹ÙŠØ©', + 'The time must be a numeric value' => 'يجب أن يكون الوقت قيمة رقمية', + 'Todo' => 'للإنجاز', + 'In progress' => 'قيد التنÙيذ', + 'Sub-task removed successfully.' => 'تمت إزالة المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ© بنجاح.', + 'Unable to remove this sub-task.' => 'تعذّر إزالة هذه المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ©.', + 'Sub-task updated successfully.' => 'تم تحديث المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ© بنجاح.', + 'Unable to update your sub-task.' => 'تعذّر تحديث مهمتك Ø§Ù„ÙØ±Ø¹ÙŠØ©.', + 'Unable to create your sub-task.' => 'تعذّر إنشاء مهمتك Ø§Ù„ÙØ±Ø¹ÙŠØ©.', + 'Maximum size: ' => 'الحجم الأقصى: ', + 'Display another project' => 'عرض مشروع آخر', + 'Created by %s' => 'أنشأه %s', + 'Tasks Export' => 'تصدير المهام', + 'Start Date' => 'تاريخ البدء', + 'Execute' => 'تنÙيذ', + 'Task Id' => 'معرّ٠المهمة', + 'Creator' => 'المنشئ', + 'Modification date' => 'تاريخ التعديل', + 'Completion date' => 'تاريخ الإكمال', + 'Clone' => 'استنساخ', + 'Project cloned successfully.' => 'تم استنساخ المشروع بنجاح.', + 'Unable to clone this project.' => 'تعذّر استنساخ هذا المشروع.', + 'Enable email notifications' => 'تمكين إشعارات البريد الإلكتروني', + 'Task position:' => 'موضع المهمة:', + 'The task #%d has been opened.' => 'تم ÙØªØ­ المهمة #%d.', + 'The task #%d has been closed.' => 'تم إغلاق المهمة #%d.', + 'Sub-task updated' => 'تم تحديث المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Title:' => 'العنوان:', + 'Status:' => 'الحالة:', + 'Assignee:' => 'المكلّÙ:', + 'Time tracking:' => 'تتبع الوقت:', + 'New sub-task' => 'مهمة ÙØ±Ø¹ÙŠØ© جديدة', + 'New attachment added "%s"' => 'تمت Ø¥Ø¶Ø§ÙØ© مرÙÙ‚ جديد "%s"', + 'New comment posted by %s' => 'تم نشر تعليق جديد بواسطة %s', + 'New comment' => 'تعليق جديد', + 'Comment updated' => 'تم تحديث التعليق', + 'New subtask' => 'مهمة ÙØ±Ø¹ÙŠØ© جديدة', + 'I only want to receive notifications for these projects:' => 'أرغب ÙÙŠ تلقي الإشعارات لهذه المشاريع Ùقط:', + 'view the task on Kanboard' => 'عرض المهمة على Kanboard', + 'Public access' => 'وصول عام', + 'Disable public access' => 'تعطيل الوصول العام', + 'Enable public access' => 'تمكين الوصول العام', + 'Public access disabled' => 'تم تعطيل الوصول العام', + 'Move the task to another project' => 'نقل المهمة إلى مشروع آخر', + 'Move to project' => 'نقل إلى مشروع', + 'Do you really want to duplicate this task?' => 'هل تريد ÙØ¹Ù„اً استنساخ هذه المهمة؟', + 'Duplicate a task' => 'استنساخ مهمة', + 'External accounts' => 'حسابات خارجية', + 'Account type' => 'نوع الحساب', + 'Local' => 'محلي', + 'Remote' => 'بعيد', + 'Enabled' => 'Ù…Ùمكّن', + 'Disabled' => 'معطّل', + 'Login:' => 'اسم المستخدم:', + 'Full Name:' => 'الاسم الكامل:', + 'Email:' => 'البريد الإلكتروني:', + 'Notifications:' => 'الإشعارات:', + 'Notifications' => 'الإشعارات', + 'Account type:' => 'نوع الحساب:', + 'Edit profile' => 'تعديل المل٠الشخصي', + 'Change password' => 'تغيير كلمة المرور', + 'Password modification' => 'تعديل كلمة المرور', + 'External authentications' => 'مصادقات خارجية', + 'Never connected.' => 'لم يتصل مطلقًا.', + '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/%sس', + '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', + '%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)' => 'الأعمدة Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠØ© للمشاريع الجديدة (Ù…ÙØµÙˆÙ„Ø© بÙواصل)', + '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' => 'اختر حدثًا', + 'Create a task from an external provider' => 'إنشاء مهمة من مزوّد خارجي', + 'Change the assignee based on an external username' => 'تغيير المكلّ٠بناءً على اسم مستخدم خارجي', + 'Change the category based on an external label' => 'تغيير Ø§Ù„ÙØ¦Ø© بناءً على وسم خارجي', + 'Reference' => 'مرجع', + 'Label' => 'وسم', + 'Database' => 'قاعدة البيانات', + 'About' => 'حول', + 'Database driver:' => 'مشغّل قاعدة البيانات:', + 'Board settings' => 'إعدادات اللوحة', + 'Webhook settings' => 'إعدادات Webhook', + 'Reset token' => 'إعادة ضبط الرمز', + 'API endpoint:' => 'عنوان واجهة API:', + 'Refresh interval for personal 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 للتعطيل، Ø§ÙØªØ±Ø§Ø¶ÙŠÙ‹Ø§ يومان)', + 'Frequency in second (60 seconds by default)' => 'التردد بالثواني (Ø§ÙØªØ±Ø§Ø¶ÙŠÙ‹Ø§ 60 ثانية)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'التردد بالثواني (0 لتعطيل هذه الميزة، Ø§ÙØªØ±Ø§Ø¶ÙŠÙ‹Ø§ 10 ثوانÙ)', + 'Application URL' => 'رابط التطبيق', + 'Token regenerated.' => 'تمت إعادة توليد الرمز.', + 'Date format' => 'تنسيق التاريخ', + 'ISO format is always accepted, example: "%s" and "%s"' => 'تنسيق ISO مقبول دائمًا، مثال: "%s" Ùˆ "%s"', + 'New personal project' => 'مشروع شخصي جديد', + 'This project is personal' => 'هذا المشروع شخصي', + 'Add' => 'Ø¥Ø¶Ø§ÙØ©', + 'Start date' => 'تاريخ البدء', + 'Time estimated' => 'الوقت المقدّر', + 'There is nothing assigned to you.' => 'لا شيء Ù…ÙØ³Ù†Ø¯ إليك.', + 'My tasks' => 'مهامي', + 'Activity stream' => 'سجل النشاط', + 'Dashboard' => 'لوحة المعلومات', + 'Confirmation' => 'تأكيد', + 'Webhooks' => 'Ø®Ø·Ø§ÙØ§Øª الويب', + 'API' => 'واجهة برمجية (API)', + 'Create a comment from an external provider' => 'إنشاء تعليق من مزوّد خارجي', + 'Project management' => 'إدارة المشروع', + 'Columns' => 'الأعمدة', + 'Task' => 'مهمة', + 'Percentage' => 'النسبة المئوية', + 'Number of tasks' => 'عدد المهام', + 'Task distribution' => 'توزيع المهام', + 'Analytics' => 'التحليلات', + 'Subtask' => 'مهمة ÙØ±Ø¹ÙŠØ©', + 'User repartition' => 'توزيع المستخدمين', + 'Clone this project' => 'استنساخ هذا المشروع', + 'Column removed successfully.' => 'تم إزالة العمود بنجاح.', + '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 subtask id is required' => 'معرّ٠المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ© مطلوب', + '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' => 'يجب أن يكون معرّ٠المستخدم عددًا صحيحًا', + 'This value is required' => 'هذه القيمة مطلوبة', + 'This value must be numeric' => 'يجب أن تكون هذه القيمة رقمية', + 'Unable to create this task.' => 'تعذّر إنشاء هذه المهمة.', + 'Cumulative flow diagram' => 'مخطط التدÙÙ‚ التراكمي', + 'Daily project summary' => 'ملخص المشروع اليومي', + 'Daily project summary export' => 'تصدير الملخص اليومي للمشروع', + 'Exports' => 'عمليات التصدير', + 'This export contains the number of tasks per column grouped per day.' => 'يحتوي هذا التصدير على عدد المهام لكل عمود، مجمعة حسب اليوم.', + 'Active swimlanes' => 'مسارات السباحة النشطة', + 'Add a new swimlane' => 'Ø¥Ø¶Ø§ÙØ© مسار سباحة جديد', + 'Default swimlane' => 'مسار السباحة Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠ', + 'Do you really want to remove this swimlane: "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة مسار السباحة هذا: "%s"ØŸ', + 'Inactive swimlanes' => 'مسارات السباحة غير النشطة', + 'Remove a swimlane' => 'إزالة مسار سباحة', + 'Swimlane modification for the project "%s"' => 'تعديل مسار السباحة للمشروع "%s"', + 'Swimlane removed successfully.' => 'تم إزالة مسار السباحة بنجاح.', + 'Swimlanes' => 'مسارات السباحة', + 'Swimlane updated successfully.' => 'تم تحديث مسار السباحة بنجاح.', + 'Unable to remove this swimlane.' => 'تعذّر إزالة مسار السباحة هذا.', + 'Unable to update this swimlane.' => 'تعذّر تحديث مسار السباحة هذا.', + 'Your swimlane has been created successfully.' => 'تم إنشاء مسار السباحة بنجاح.', + 'Example: "Bug, Feature Request, Improvement"' => 'مثال: "عطل، طلب ميزة، تحسين"', + 'Default categories for new projects (Comma-separated)' => 'Ø§Ù„ÙØ¦Ø§Øª Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠØ© للمشاريع الجديدة (Ù…ÙØµÙˆÙ„Ø© بÙواصل)', + 'Integrations' => 'تكاملات', + 'Integration with third-party services' => 'التكامل مع خدمات طر٠ثالث', + 'Subtask Id' => 'معرّ٠المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Subtasks' => 'المهام Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Subtasks Export' => 'تصدير المهام Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Task Title' => 'عنوان المهمة', + 'Untitled' => 'بدون عنوان', + 'Application default' => 'Ø§ÙØªØ±Ø§Ø¶ÙŠ Ø§Ù„ØªØ·Ø¨ÙŠÙ‚', + 'Language:' => 'اللغة:', + 'Timezone:' => 'المنطقة الزمنية:', + 'All columns' => 'كل الأعمدة', + 'Next' => 'التالي', + '#%d' => '#%d', + 'All swimlanes' => 'كل مسارات السباحة', + 'All colors' => 'جميع الألوان', + 'Moved to column %s' => 'Ù†Ùقلت إلى العمود %s', + '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' => 'تتبع الوقت', + 'You already have one subtask in progress' => 'لديك Ø¨Ø§Ù„ÙØ¹Ù„ مهمة ÙØ±Ø¹ÙŠØ© قيد التنÙيذ', + 'Which parts of the project do you want to duplicate?' => 'أي أجزاء من المشروع تريد استنساخها؟', + 'Disallow login form' => 'منع نموذج تسجيل الدخول', + 'Start' => 'البدء', + 'End' => 'الانتهاء', + 'Task age in days' => 'عمر المهمة بالأيام', + 'Days in this column' => 'الأيام ÙÙŠ هذا العمود', + '%dd' => '%dd', + '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' => 'الحقل مطلوب', + 'Link added successfully.' => 'تمت Ø¥Ø¶Ø§ÙØ© الرابط بنجاح.', + 'Link updated successfully.' => 'تم تحديث الرابط بنجاح.', + 'Link removed successfully.' => 'تمت إزالة الرابط بنجاح.', + 'Link labels' => 'تسميات الروابط', + 'Link modification' => 'تعديل الرابط', + 'Opposite label' => 'التسمية المقابلة', + 'Remove a link' => 'إزالة رابط', + '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.' => 'تعذّر إزالة هذا الرابط.', + 'relates to' => 'يرتبط بـ', + 'blocks' => 'يحظر', + 'is blocked by' => 'محظور بواسطة', + 'duplicates' => 'يكرّر', + 'is duplicated by' => 'مكرّر بواسطة', + 'is a child of' => 'تابع لـ', + 'is a parent of' => 'أصل لـ', + 'targets milestone' => 'يستهد٠معلماً', + 'is a milestone of' => 'معلَم لـ', + 'fixes' => 'ÙŠÙØµÙ„Ø­', + 'is fixed by' => 'Ø£ÙØµÙ„Ø­ بواسطة', + 'This task' => 'هذه المهمة', + '<1h' => '<1س', + '%dh' => '%dس', + 'Expand tasks' => 'توسيع المهام', + 'Collapse tasks' => 'طيّ المهام', + 'Expand/collapse tasks' => 'توسيع/طيّ المهام', + 'Close dialog box' => 'إغلاق Ù†Ø§ÙØ°Ø© الحوار', + 'Submit a form' => 'إرسال نموذج', + 'Board view' => 'عرض اللوحة', + 'Keyboard shortcuts' => 'اختصارات لوحة Ø§Ù„Ù…ÙØ§ØªÙŠØ­', + 'Open board switcher' => 'ÙØªØ­ مبدّل اللوحات', + 'Application' => 'التطبيق', + 'Compact view' => 'عرض مضغوط', + 'Horizontal scrolling' => 'تمرير Ø£Ùقي', + 'Compact/wide view' => 'عرض مضغوط/واسع', + 'Currency' => 'العملة', + 'Personal project' => 'مشروع شخصي', + 'AUD - Australian Dollar' => 'AUD - دولار أسترالي', + 'CAD - Canadian Dollar' => 'CAD - دولار كندي', + 'CHF - Swiss Francs' => 'CHF - ÙØ±Ù†Ùƒ سويسري', + 'Custom Stylesheet' => 'ورقة أنماط مخصصة', + 'EUR - Euro' => 'EUR - يورو', + 'GBP - British Pound' => 'GBP - جنيه إسترليني', + 'INR - Indian Rupee' => 'INR - روبية هندية', + 'JPY - Japanese Yen' => 'JPY - ين ياباني', + 'NZD - New Zealand Dollar' => 'NZD - دولار نيوزيلندي', + 'PEN - Peruvian Sol' => 'PEN - سول بيروÙÙŠ', + 'RSD - Serbian dinar' => 'RSD - دينار صربي', + 'CNY - Chinese Yuan' => 'CNY - يوان صيني', + 'USD - US Dollar' => 'USD - دولار أمريكي', + 'VES - Venezuelan Bolívar' => 'VES - Ø¨ÙˆÙ„ÙŠÙØ§Ø± Ùنزويلي', + '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' => 'الوقت المستغرق ÙÙŠ العمود', + '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.' => 'يتضمن هذا التقرير جميع عمليات نقل الأعمدة لكل مهمة مع التاريخ والمستخدم والوقت المستغرق لكل انتقال.', + 'Currency rates' => 'أسعار العملات', + 'Rate' => 'السعر', + 'Change reference currency' => 'تغيير عملة المرجع', + 'Reference currency' => 'عملة المرجع', + 'The currency rate has been added successfully.' => 'تمت Ø¥Ø¶Ø§ÙØ© سعر الصر٠بنجاح.', + 'Unable to add this currency rate.' => 'تعذّر Ø¥Ø¶Ø§ÙØ© سعر الصر٠هذا.', + 'Webhook URL' => 'عنوان URL للـ Webhook', + '%s removed the assignee of the task %s' => 'قام %s بإزالة المكلّ٠بالمهمة %s', + 'Information' => 'معلومات', + 'Check two factor authentication code' => 'تحقق من رمز المصادقة الثنائية', + 'The two factor authentication code is not valid.' => 'رمز المصادقة الثنائية غير صالح.', + 'The two factor authentication code is valid.' => 'رمز المصادقة الثنائية صالح.', + 'Code' => 'رمز', + 'Two factor authentication' => 'المصادقة الثنائية', + 'This QR code contains the key URI: ' => 'يحتوي رمز QR هذا على Ù…ÙØªØ§Ø­ URI: ', + 'Check my code' => 'تحقق من الرمز', + 'Secret key: ' => 'Ø§Ù„Ù…ÙØªØ§Ø­ السري: ', + 'Test your device' => 'اختبر جهازك', + 'Assign a color when the task is moved to a specific column' => 'تعيين لون عند نقل المهمة إلى عمود محدد', + '%s via Kanboard' => '%s عبر Kanboard', + 'Burndown chart' => 'مخطط الاحتراق', + '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 - كرونا سويدية', + 'Identifier' => 'المعرّÙ', + 'Disable two factor authentication' => 'تعطيل المصادقة الثنائية', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'هل تريد ÙØ¹Ù„اً تعطيل المصادقة الثنائية لهذا المستخدم: "%s"ØŸ', + 'Edit link' => 'تعديل الرابط', + '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' => 'تمت جدولة إنشاء مهمة متكررة', + 'Score' => 'الدرجة', + 'The identifier must be unique' => 'يجب أن يكون Ø§Ù„Ù…Ø¹Ø±Ù‘Ù ÙØ±ÙŠØ¯Ù‹Ø§', + 'This linked task id doesn\'t exists' => 'معرّ٠المهمة المرتبطة غير موجود', + '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' => 'الإطار الزمني لاحتساب تاريخ الاستحقاق الجديد', + '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: ' => 'عامل احتساب تاريخ الاستحقاق الجديد: ', + 'Month(s)' => 'شهر/أشهر', + 'This task has been created by: ' => 'تم إنشاء هذه المهمة بواسطة: ', + 'Recurrent task has been generated:' => 'تم إنشاء مهمة متكررة:', + 'Timeframe to calculate new due date: ' => 'الإطار الزمني لاحتساب تاريخ الاستحقاق الجديد: ', + 'Trigger to generate recurrent task: ' => 'مشغّل إنشاء مهمة متكررة: ', + 'When task is closed' => 'عند إغلاق المهمة', + 'When task is moved from first column' => 'عند نقل المهمة من العمود الأول', + 'When task is moved to last column' => 'عند نقل المهمة إلى العمود الأخير', + 'Year(s)' => 'سنة/سنوات', + 'Project settings' => 'إعدادات المشروع', + 'Automatically update the start date' => 'تحديث تاريخ البدء تلقائيًا', + 'iCal feed' => 'خلاصة iCal', + 'Preferences' => 'ØªÙØ¶ÙŠÙ„ات', + 'Security' => 'أمان', + 'Two factor authentication disabled' => 'تم تعطيل المصادقة الثنائية', + 'Two factor authentication enabled' => 'تم تمكين المصادقة الثنائية', + 'Unable to update this user.' => 'تعذّر تحديث هذا المستخدم.', + 'There is no user management for personal 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' => 'نقل المهمة إلى عمود آخر عند تغيير Ø§Ù„ÙØ¦Ø©', + 'Send a task by email to someone' => 'إرسال مهمة عبر البريد الإلكتروني لشخص ما', + 'Reopen a task' => 'إعادة ÙØªØ­ مهمة', + 'Notification' => 'إشعار', + '%s moved the task #%d to the first swimlane' => 'قام %s بنقل المهمة #%d إلى مسار السباحة الأول', + 'Swimlane' => 'مسار السباحة', + '%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.' => 'يحتوي هذا التقرير على معلومات جميع المهام للنطاق الزمني المحدد.', + 'Project activities for %s' => 'أنشطة المشروع لـ %s', + 'view the board on Kanboard' => 'عرض اللوحة على Kanboard', + 'The task has been moved to the first swimlane' => 'تم نقل المهمة إلى مسار السباحة الأول', + 'The task has been moved to another swimlane:' => 'تم نقل المهمة إلى مسار سباحة آخر:', + 'New title: %s' => 'عنوان جديد: %s', + 'The task is not assigned anymore' => 'لم تعد المهمة Ù…ÙØ³Ù†Ø¯Ø©', + 'New assignee: %s' => 'المكلّ٠الجديد: %s', + 'There is no category now' => 'لا توجد ÙØ¦Ø© الآن', + 'New category: %s' => 'Ø§Ù„ÙØ¦Ø© الجديدة: %s', + 'New color: %s' => 'اللون الجديد: %s', + 'New complexity: %d' => 'التعقيد الجديد: %d', + 'The due date has been removed' => 'تمت إزالة تاريخ الاستحقاق', + 'There is no description anymore' => 'لم يعد هناك وصÙ', + 'Recurrence settings has been modified' => 'تم تعديل إعدادات التكرار', + 'Time spent changed: %sh' => 'تم تغيير الوقت المستغرق: %sس', + 'Time estimated changed: %sh' => 'تم تغيير الوقت المقدّر: %sس', + 'The field "%s" has 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:' => 'أريد تلقي إشعارات بشأن:', + 'All tasks' => 'كل المهام', + 'Only for tasks assigned to me' => 'Ùقط للمهام المسندة إليّ', + 'Only for tasks created by me' => 'Ùقط للمهام التي أنشأتها', + 'Only for tasks created by me and tasks assigned to me' => 'Ùقط للمهام التي أنشأتها والمهام المسندة إليّ', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'الإجمالي لجميع الأعمدة', + 'You need at least 2 days of data to show the chart.' => 'تحتاج إلى ما لا يقل عن يومين من البيانات لعرض المخطط.', + '<15m' => '<15د', + '<30m' => '<30د', + 'Stop timer' => 'إيقا٠المؤقت', + 'Start timer' => 'بدء المؤقت', + 'My activity stream' => 'سجل نشاطي', + 'Search tasks' => 'البحث عن مهام', + 'Reset filters' => 'إعادة تعيين المرشّحات', + 'My tasks due tomorrow' => 'مهامي المستحقة غدًا', + 'Tasks due today' => 'المهام المستحقة اليوم', + 'Tasks due tomorrow' => 'المهام المستحقة غدًا', + 'Tasks due yesterday' => 'المهام المستحقة أمس', + 'Closed tasks' => 'المهام المغلقة', + 'Open tasks' => 'المهام Ø§Ù„Ù…ÙØªÙˆØ­Ø©', + 'Not assigned' => 'غير Ù…ÙØ³Ù†Ø¯Ø©', + 'View advanced search syntax' => 'عرض صيغة البحث المتقدم', + 'Overview' => 'نظرة عامة', + 'Board/Calendar/List view' => 'عرض لوحة/تقويم/قائمة', + 'Switch to the board view' => 'التبديل إلى عرض اللوحة', + 'Switch to the list view' => 'التبديل إلى عرض القائمة', + 'Go to the search/filter box' => 'الانتقال إلى مربع البحث/الترشيح', + 'There is no activity yet.' => 'لا يوجد نشاط حتى الآن.', + 'No tasks found.' => 'لم يتم العثور على مهام.', + 'Keyboard shortcut: "%s"' => 'اختصار لوحة Ø§Ù„Ù…ÙØ§ØªÙŠØ­: "%s"', + 'List' => 'قائمة', + 'Filter' => 'مرشّح', + 'Advanced search' => 'بحث متقدم', + 'Example of query: ' => 'مثال على الاستعلام: ', + 'Search by project: ' => 'بحث حسب المشروع: ', + 'Search by column: ' => 'بحث حسب العمود: ', + 'Search by assignee: ' => 'بحث حسب المكلّÙ: ', + 'Search by color: ' => 'بحث حسب اللون: ', + 'Search by category: ' => 'بحث حسب Ø§Ù„ÙØ¦Ø©: ', + 'Search by description: ' => 'بحث حسب الوصÙ: ', + 'Search by due date: ' => 'بحث حسب تاريخ الاستحقاق: ', + 'Average time spent in each column' => 'متوسط الوقت المستغرق ÙÙŠ كل عمود', + 'Average time spent' => 'متوسط الوقت المستغرق', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'يعرض هذا المخطط متوسط الوقت المستغرق ÙÙŠ كل عمود لآخر %d مهمة.', + 'Average Lead and Cycle time' => 'متوسط زمن القيادة والدورة', + 'Average lead time: ' => 'متوسط زمن القيادة: ', + 'Average cycle time: ' => 'متوسط زمن الدورة: ', + 'Cycle Time' => 'زمن الدورة', + 'Lead Time' => 'زمن القيادة', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'يعرض هذا المخطط متوسط زمن القيادة والدورة لآخر %d مهمة عبر الزمن.', + 'Average time into each column' => 'متوسط الوقت ÙÙŠ كل عمود', + 'Lead and cycle time' => 'زمن القيادة والدورة', + 'Lead time: ' => 'زمن القيادة: ', + 'Cycle time: ' => 'زمن الدورة: ', + 'Time spent in 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 the start date automatically' => 'تعيين تاريخ البدء تلقائيًا', + 'Edit Authentication' => 'تحرير المصادقة', + 'Remote user' => 'مستخدم بعيد', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'لا يخزن المستخدمون البعيدون كلمات المرور ÙÙŠ قاعدة بيانات KanboardØŒ أمثلة: حسابات LDAP ÙˆGoogle ÙˆGithub.', + '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.' => 'لا يوجد مشروع وجهة متاح.', + 'Trigger automatically subtask time tracking' => 'تشغيل تتبّع وقت المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ© تلقائيًا', + 'Include closed tasks in the cumulative flow diagram' => 'تضمين المهام المغلقة ÙÙŠ مخطط التدÙÙ‚ التراكمي', + 'Current swimlane: %s' => 'مسار السباحة الحالي: %s', + 'Current column: %s' => 'العمود الحالي: %s', + 'Current category: %s' => 'Ø§Ù„ÙØ¦Ø© الحالية: %s', + 'no category' => 'بدون ÙØ¦Ø©', + 'Current assignee: %s' => 'المكلّ٠الحالي: %s', + 'not assigned' => 'غير Ù…ÙØ³Ù†Ø¯', + 'Author:' => 'المؤلÙ:', + 'contributors' => 'المساهمون', + 'License:' => 'الترخيص:', + 'License' => 'الترخيص', + 'Enter the text below' => 'أدخل النص أدناه', + 'Start date:' => 'تاريخ البدء:', + 'Due date:' => 'تاريخ الاستحقاق:', + 'People who are project managers' => 'الأشخاص الذين هم مدراء المشروع', + 'People who are project members' => 'الأشخاص الذين هم أعضاء المشروع', + 'NOK - Norwegian Krone' => 'NOK - كرونة نرويجية', + 'Show this column' => 'إظهار هذا العمود', + 'Hide this column' => 'Ø¥Ø®ÙØ§Ø¡ هذا العمود', + 'End date' => 'تاريخ الانتهاء', + 'Users overview' => 'نظرة عامة على المستخدمين', + 'Members' => 'الأعضاء', + 'Shared project' => 'مشروع مشترك', + 'Project managers' => 'مدراء المشروع', + 'Projects list' => 'قائمة المشاريع', + 'End date:' => 'تاريخ الانتهاء:', + 'Change task color when using a specific task link' => 'تغيير لون المهمة عند استخدام رابط مهمة محدد', + 'Task link creation or modification' => 'إنشاء أو تعديل رابط مهمة', + 'Milestone' => 'معلَم', + 'Reset the search/filter box' => 'إعادة تعيين مربع البحث/الترشيح', + 'Documentation' => 'التوثيق', + 'Author' => 'المؤلÙ', + 'Version' => 'الإصدار', + 'Plugins' => 'Ø§Ù„Ø¥Ø¶Ø§ÙØ§Øª', + 'There is no plugin loaded.' => 'لا توجد أي Ø¥Ø¶Ø§ÙØ© محمّلة.', + 'My notifications' => 'إشعاراتي', + 'Custom filters' => 'مرشّحات مخصصة', + 'Your custom filter has 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 has been updated successfully.' => 'تم تحديث المرشّح المخصص بنجاح.', + 'Unable to update custom filter.' => 'تعذّر تحديث المرشّح المخصص.', + 'Web' => 'الويب', + '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', + 'New task #%d: %s' => 'مهمة جديدة #%d: %s', + 'Task updated #%d' => 'تم تحديث المهمة #%d', + 'Task #%d closed' => 'Ø£ÙØºÙ„قت المهمة #%d', + 'Task #%d opened' => 'ÙÙØªØ­Øª المهمة #%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', + '%d overdue tasks' => '%d مهام متأخرة', + 'No notification.' => 'لا توجد إشعارات.', + '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:' => 'طرق الإشعارات:', + 'Unable to read your file' => 'تعذّر قراءة ملÙÙƒ', + '%d task(s) have been imported successfully.' => 'تم استيراد %d مهمة بنجاح.', + 'Nothing has been imported!' => 'لم يتم استيراد أي شيء!', + 'Import users from CSV file' => 'استيراد مستخدمين من مل٠CSV', + '%d user(s) have been imported successfully.' => 'تم استيراد %d مستخدمًا بنجاح.', + 'Comma' => 'ÙØ§ØµÙ„Ø©', + 'Semi-colon' => 'ÙØ§ØµÙ„Ø© منقوطة', + 'Tab' => 'علامة تبويب', + 'Vertical bar' => 'شريط عمودي', + '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)' => 'إلحاق المرشّح (بدلاً من الاستبدال)', + 'Append/Replace' => 'إلحاق/استبدال', + 'Append' => 'إلحاق', + 'Replace' => 'استبدال', + 'Import' => 'استيراد', + 'Change sorting' => 'تغيير Ø§Ù„ÙØ±Ø²', + 'Tasks Importation' => 'استيراد المهام', + 'Delimiter' => 'Ø§Ù„ÙØ§ØµÙ„', + 'Enclosure' => 'المغلّÙÙ', + 'CSV File' => 'مل٠CSV', + 'Instructions' => 'تعليمات', + 'Your file must use the predefined CSV format' => 'يجب أن يستخدم ملÙÙƒ تنسيق CSV المحدّد مسبقًا', + 'Your file must be encoded in UTF-8' => 'يجب أن يكون ملÙÙƒ Ù…ÙØ±Ù…َّزًا بترميز 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' => 'يجب أن يكون تاريخ الاستحقاق بتنسيق ISO: YYYY-MM-DD', + 'Download CSV template' => 'تنزيل قالب CSV', + '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' => 'أرÙÙ‚ %s ملÙًا جديدًا بالمهمة %s', + 'Link type' => 'نوع الرابط', + 'Assign automatically a category based on a link' => 'تعيين ÙØ¦Ø© تلقائيًا استنادًا إلى رابط', + 'BAM - Konvertible Mark' => 'BAM - مارك قابل للتحويل', + 'Assignee Username' => 'اسم مستخدم المكلّÙ', + 'Assignee Name' => 'اسم المكلّÙ', + 'Groups' => 'المجموعات', + 'Members of %s' => 'أعضاء %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"' => 'Ø¥Ø¶Ø§ÙØ© عضو مجموعة إلى "%s"', + 'Group member added successfully.' => 'تمت Ø¥Ø¶Ø§ÙØ© عضو المجموعة بنجاح.', + 'Unable to add group member.' => 'تعذّر Ø¥Ø¶Ø§ÙØ© عضو المجموعة.', + 'Remove user from group "%s"' => 'إزالة مستخدم من المجموعة "%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' => 'تم Ù‚ÙÙ„ حسابك لمدة %d دقيقة', + 'Invalid captcha' => 'رمز التحقق غير صالح', + 'The name must be unique' => 'يجب أن يكون الاسم ÙØ±ÙŠØ¯Ù‹Ø§', + 'View all groups' => 'عرض جميع المجموعات', + 'There is no user available.' => 'لا يوجد مستخدم متاح.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة المستخدم "%s" من المجموعة "%s"ØŸ', + 'There is no group.' => 'لا توجد مجموعة.', + 'Add group member' => 'Ø¥Ø¶Ø§ÙØ© عضو مجموعة', + 'Do you really want to remove this group: "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة هذه المجموعة: "%s"ØŸ', + 'There is no user in this group.' => 'لا يوجد مستخدم ÙÙŠ هذه المجموعة.', + 'Permissions' => 'الأذونات', + 'Allowed Users' => 'المستخدمون المسموح لهم', + 'No specific user has been allowed.' => 'لم ÙŠÙØ³Ù…Ø­ بأي مستخدم محدد.', + 'Role' => 'الدور', + 'Enter user name...' => 'أدخل اسم المستخدم...', + 'Allowed Groups' => 'المجموعات المسموح لها', + 'No group has been allowed.' => 'لم ÙŠÙØ³Ù…Ø­ بأي مجموعة.', + 'Group' => 'المجموعة', + 'Group Name' => 'اسم المجموعة', + 'Enter group name...' => 'أدخل اسم المجموعة...', + 'Role:' => 'الدور:', + 'Project members' => 'أعضاء المشروع', + '%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', + 'Estimated hours: ' => 'ساعات مقدّرة: ', + 'Actual hours: ' => 'ساعات ÙØ¹Ù„ية: ', + 'Hours Spent' => 'الساعات المستغرقة', + 'Hours Estimated' => 'الساعات المقدّرة', + 'Estimated Time' => 'الوقت المقدّر', + 'Actual Time' => 'الوقت Ø§Ù„ÙØ¹Ù„ÙŠ', + 'Estimated vs actual time' => 'المقدّر مقابل Ø§Ù„ÙØ¹Ù„ÙŠ', + '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' => 'خوارزمية كلمة المرور ذات الاستخدام الواحد المبنية على الوقت', + '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?' => 'هل نسيت كلمة المرور؟', + 'Enable "Forget Password"' => 'تمكين "نسيت كلمة المرور"', + 'Password Reset' => 'إعادة تعيين كلمة المرور', + 'New password' => 'كلمة مرور جديدة', + 'Change Password' => 'تغيير كلمة المرور', + 'To reset your password click on this link:' => 'لإعادة تعيين كلمة المرور انقر على هذا الرابط:', + 'Last Password Reset' => 'آخر إعادة تعيين لكلمة المرور', + 'The password has never been reinitialized.' => 'لم تتم إعادة تعيين كلمة المرور من قبل.', + 'Creation' => 'الإنشاء', + 'Expiration' => 'الانتهاء', + 'Password reset history' => 'سجل إعادة تعيين كلمة المرور', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'تم إغلاق جميع مهام العمود "%s" ومسار السباحة "%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.' => 'سيتم إغلاق %d مهمة ÙÙŠ العمود "%s" ومسار السباحة "%s".', + 'Close all tasks in this column and this swimlane' => 'إغلاق جميع المهام ÙÙŠ هذا العمود ومسار السباحة هذا', + '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.' => 'معرّ٠المشروع اختياري ويجب أن يكون أبجديًا رقميًا، مثال: MYPROJECT.', + 'Project owner' => 'مالك المشروع', + 'Personal projects do not have users and groups management.' => 'المشاريع الشخصية لا تحتوي على إدارة مستخدمين ومجموعات.', + 'There is no project member.' => 'لا يوجد عضو ÙÙŠ المشروع.', + 'Priority' => 'الأولوية', + 'Task priority' => 'أولوية المهمة', + 'General' => 'عام', + 'Dates' => 'التواريخ', + 'Default priority' => 'الأولوية Ø§Ù„Ø§ÙØªØ±Ø§Ø¶ÙŠØ©', + 'Lowest priority' => 'أدنى أولوية', + 'Highest priority' => 'أعلى أولوية', + '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' => 'مرÙÙ‚', + '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' => '%s ساعات', + 'Time spent:' => 'الوقت المستغرق:', + 'Created:' => 'Ø£Ùنشئت:', + 'Modified:' => 'Ø¹ÙØ¯Ù‘لت:', + 'Completed:' => 'Ø£ÙكمÙلت:', + 'Started:' => 'بدأت:', + 'Moved:' => 'Ù†Ùقلت:', + 'Task #%d' => 'المهمة #%d', + 'Time format' => 'تنسيق الوقت', + 'Start date: ' => 'تاريخ البدء: ', + 'End date: ' => 'تاريخ الانتهاء: ', + 'New due date: ' => 'تاريخ استحقاق جديد: ', + 'Start date changed: ' => 'تم تغيير تاريخ البدء: ', + 'Disable personal projects' => 'تعطيل المشاريع الشخصية', + 'Do you really want to remove this custom filter: "%s"?' => 'هل تريد ÙØ¹Ù„اً إزالة هذا المرشّح المخصص: "%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.' => 'تم Ø±ÙØ¹ جميع Ø§Ù„Ù…Ù„ÙØ§Øª بنجاح.', + 'The maximum allowed file size is %sB.' => 'أقصى حجم مل٠مسموح به هو %sB.', + 'Drag and drop your files here' => 'اسحب وأÙÙ„ÙØª Ù…Ù„ÙØ§ØªÙƒ هنا', + 'choose files' => 'اختر Ù…Ù„ÙØ§Øª', + 'View profile' => 'عرض المل٠الشخصي', + 'Two Factor' => 'عاملان', + 'Disable user' => 'تعطيل المستخدم', + 'Do you really want to disable this user: "%s"?' => 'هل تريد ÙØ¹Ù„اً تعطيل هذا المستخدم: "%s"ØŸ', + 'Enable user' => 'تمكين المستخدم', + 'Do you really want to enable this user: "%s"?' => 'هل تريد ÙØ¹Ù„اً تمكين هذا المستخدم: "%s"ØŸ', + 'Download' => 'تنزيل', + 'Uploaded: %s' => 'تم Ø§Ù„Ø±ÙØ¹: %s', + 'Size: %s' => 'الحجم: %s', + 'Uploaded by %s' => 'Ø±ÙØ¹Ù‡Ø§ %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' => 'يجب أن تكون هذه القيمة أكبر من %d', + 'Another swimlane with the same name exists in the project' => 'يوجد مسار سباحة آخر Ø¨Ù†ÙØ³ الاسم ÙÙŠ المشروع', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'مثال: https://example.kanboard.org/ (ÙŠÙØ³ØªØ®Ø¯Ù… لإنشاء روابط مطلقة)', + '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:', + 'PHP SAPI:' => 'واجهة PHP SAPI:', + 'OS version:' => 'إصدار نظام التشغيل:', + 'Database version:' => 'إصدار قاعدة البيانات:', + 'Browser:' => 'Ø§Ù„Ù…ØªØµÙØ­:', + 'Task view' => 'عرض المهمة', + 'Edit task' => 'تعديل المهمة', + 'Edit description' => 'تعديل الوصÙ', + 'New internal link' => 'رابط داخلي جديد', + 'Display list of keyboard shortcuts' => 'عرض قائمة اختصارات لوحة Ø§Ù„Ù…ÙØ§ØªÙŠØ­', + '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' => 'رمز 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 a 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 has 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', + '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', + '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' => 'رسائل البريد الصادرة', + '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 دعوة.', + '%d invitation was sent.' => 'تم إرسال %d دعوة.', + 'Unable to create this user.' => 'تعذّر إنشاء هذا المستخدم.', + 'Kanboard Invitation' => 'دعوة Kanboard', + '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.' => 'لقد تمت دعوتك للتسجيل ÙÙŠ 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' => 'اسم المستخدم هذا محجوز مسبقًا', + 'Your profile must have a valid email address.' => 'يجب أن يحتوي ملÙÙƒ الشخصي على عنوان بريد إلكتروني صالح.', + 'TRL - Turkish Lira' => 'TRL - ليرة تركية', + 'The project email is optional and could be used by several plugins.' => 'بريد المشروع اختياري ويمكن أن تستخدمه عدة Ø¥Ø¶Ø§ÙØ§Øª.', + 'The project email must be unique across all projects' => 'يجب أن يكون بريد المشروع ÙØ±ÙŠØ¯Ù‹Ø§ عبر جميع المشاريع', + 'The email configuration has been disabled by the administrator.' => 'تم تعطيل إعدادات البريد الإلكتروني بواسطة المدير.', + 'Close this project' => 'إغلاق هذا المشروع', + 'Open this project' => 'ÙØªØ­ هذا المشروع', + 'Close a project' => 'إغلاق مشروع', + 'Do you really want to close this project: "%s"?' => 'هل تريد ÙØ¹Ù„اً إغلاق هذا المشروع: "%s"ØŸ', + 'Reopen a project' => 'إعادة ÙØªØ­ مشروع', + 'Do you really want to reopen this project: "%s"?' => 'هل تريد ÙØ¹Ù„اً إعادة ÙØªØ­ هذا المشروع: "%s"ØŸ', + 'This project is open' => 'هذا المشروع Ù…ÙØªÙˆØ­', + 'This project is closed' => 'هذا المشروع مغلق', + 'Unable to upload files, check the permissions of your data folder.' => 'تعذّر Ø±ÙØ¹ Ø§Ù„Ù…Ù„ÙØ§ØªØŒ تحقّق من أذونات مجلد البيانات.', + 'Another category with the same name exists in this project' => 'توجد ÙØ¦Ø© أخرى Ø¨Ù†ÙØ³ الاسم ÙÙŠ هذا المشروع', + 'Comment sent by email successfully.' => 'تم إرسال التعليق عبر البريد بنجاح.', + 'Sent by email to "%s" (%s)' => 'Ø£ÙØ±Ø³Ù„ عبر البريد إلى "%s" (%s)', + 'Unable to read uploaded file.' => 'تعذّر قراءة المل٠المرÙوع.', + 'Database uploaded successfully.' => 'تم Ø±ÙØ¹ قاعدة البيانات بنجاح.', + 'Task sent by email successfully.' => 'تم إرسال المهمة عبر البريد بنجاح.', + 'There is no category in this project.' => 'لا توجد ÙØ¦Ø© ÙÙŠ هذا المشروع.', + 'Send by email' => 'إرسال عبر البريد', + 'Create and send a comment by email' => 'إنشاء وإرسال تعليق عبر البريد', + 'Subject' => 'الموضوع', + 'Upload the database' => 'Ø±ÙØ¹ قاعدة البيانات', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'يمكنك Ø±ÙØ¹ قاعدة بيانات Sqlite التي سبق تنزيلها (تنسيق Gzip).', + 'Database file' => 'مل٠قاعدة البيانات', + 'Upload' => 'Ø±ÙØ¹', + 'Your project must have at least one active swimlane.' => 'يجب أن يحتوي مشروعك على مسار سباحة واحد نشط على الأقل.', + 'Project: %s' => 'المشروع: %s', + 'Automatic action not found: "%s"' => 'لم يتم العثور على إجراء تلقائي: "%s"', + '%d projects' => '%d مشاريع', + '%d project' => '%d مشروع', + 'There is no project.' => 'لا يوجد مشروع.', + 'Sort' => 'ÙØ±Ø²', + 'Project ID' => 'معرّ٠المشروع', + 'Project name' => 'اسم المشروع', + 'Public' => 'عام', + 'Personal' => 'شخصي', + '%d tasks' => '%d مهام', + '%d task' => '%d مهمة', + 'Task ID' => 'معرّ٠المهمة', + 'Assign automatically a color when due date is expired' => 'تعيين لون تلقائيًا عند انقضاء تاريخ الاستحقاق', + 'Total score in this column across all swimlanes' => 'إجمالي النقاط ÙÙŠ هذا العمود عبر جميع مسارات السباحة', + 'HRK - Kuna' => 'HRK - كونا', + 'ARS - Argentine Peso' => 'ARS - بيزو أرجنتيني', + 'COP - Colombian Peso' => 'COP - بيزو كولومبي', + '%d groups' => '%d مجموعات', + '%d group' => '%d مجموعة', + 'Group ID' => 'معرّ٠المجموعة', + 'External ID' => 'معرّ٠خارجي', + '%d users' => '%d مستخدمين', + '%d user' => '%d مستخدم', + 'Hide subtasks' => 'Ø¥Ø®ÙØ§Ø¡ المهام Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Show subtasks' => 'إظهار المهام Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Authentication Parameters' => 'معاملات المصادقة', + 'API Access' => 'وصول API', + 'No users found.' => 'لم يتم العثور على مستخدمين.', + 'User ID' => 'معرّ٠المستخدم', + 'Notifications are activated' => 'الإشعارات Ù…ÙØ¹Ù‘لة', + 'Notifications are disabled' => 'الإشعارات معطّلة', + 'User disabled' => 'المستخدم معطّل', + '%d notifications' => '%d إشعارات', + '%d notification' => '%d إشعار', + 'There is no external integration installed.' => 'لا توجد أي تكاملات خارجية مثبتة.', + 'You are not allowed to update tasks assigned to someone else.' => 'غير مسموح لك بتحديث المهام المسندة إلى شخص آخر.', + 'You are not allowed to change the assignee.' => 'غير مسموح لك بتغيير المكلّÙ.', + 'Task suppression is not permitted' => 'حذ٠المهام غير مسموح', + 'Changing assignee is not permitted' => 'تغيير المكلّ٠غير مسموح', + 'Update only assigned tasks is permitted' => 'مسموح تحديث المهام المسندة Ùقط', + 'Only for tasks assigned to the current user' => 'Ùقط للمهام المسندة إلى المستخدم الحالي', + 'My projects' => 'مشاريعي', + 'You are not a member of any project.' => 'لست عضوًا ÙÙŠ أي مشروع.', + 'My subtasks' => 'مهامي Ø§Ù„ÙØ±Ø¹ÙŠØ©', + '%d subtasks' => '%d مهام ÙØ±Ø¹ÙŠØ©', + '%d subtask' => '%d مهمة ÙØ±Ø¹ÙŠØ©', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'ÙŠÙØ³Ù…Ø­ بنقل المهمة بين هذه الأعمدة Ùقط للمهام المسندة إلى المستخدم الحالي', + '[DUPLICATE]' => '[Ù…Ùكرّر]', + 'DKK - Danish Krona' => 'DKK - كرونة دنماركية', + 'Remove user from group' => 'إزالة مستخدم من المجموعة', + 'Assign the task to its creator' => 'إسناد المهمة إلى منشئها', + 'This task was sent by email to "%s" with subject "%s".' => 'Ø£ÙØ±Ø³Ù„ت هذه المهمة عبر البريد إلى "%s" بالموضوع "%s".', + 'Predefined Email Subjects' => 'عناوين بريد محددة مسبقًا', + 'Write one subject by line.' => 'اكتب موضوعًا واحدًا ÙÙŠ كل سطر.', + 'Create another link' => 'إنشاء رابط آخر', + 'BRL - Brazilian Real' => 'BRL - ريال برازيلي', + 'Add a new Kanboard task' => 'Ø¥Ø¶Ø§ÙØ© مهمة Kanboard جديدة', + 'Subtask not started' => 'مهمة ÙØ±Ø¹ÙŠØ© غير Ù…ÙØ¨Ø¯ÙˆØ¡Ø©', + 'Subtask currently in progress' => 'مهمة ÙØ±Ø¹ÙŠØ© قيد التنÙيذ', + 'Subtask completed' => 'مهمة ÙØ±Ø¹ÙŠØ© مكتملة', + 'Subtask added successfully.' => 'تمت Ø¥Ø¶Ø§ÙØ© المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ© بنجاح.', + '%d subtasks added successfully.' => 'تمت Ø¥Ø¶Ø§ÙØ© %d مهمة ÙØ±Ø¹ÙŠØ© بنجاح.', + 'Enter one subtask by line.' => 'أدخل مهمة ÙØ±Ø¹ÙŠØ© واحدة ÙÙŠ كل سطر.', + 'Predefined Contents' => 'محتويات محددة مسبقًا', + 'Predefined contents' => 'محتويات محددة مسبقًا', + 'Predefined Task Description' => 'وص٠مهمة محدد مسبقًا', + 'Do you really want to remove this template? "%s"' => 'هل تريد ÙØ¹Ù„اً إزالة هذا القالب؟ "%s"', + 'Add predefined task description' => 'Ø¥Ø¶Ø§ÙØ© وص٠مهمة محدد مسبقًا', + 'Predefined Task Descriptions' => 'أوصا٠مهام محددة مسبقًا', + 'Template created successfully.' => 'تم إنشاء القالب بنجاح.', + 'Unable to create this template.' => 'تعذّر إنشاء هذا القالب.', + 'Template updated successfully.' => 'تم تحديث القالب بنجاح.', + 'Unable to update this template.' => 'تعذّر تحديث هذا القالب.', + 'Template removed successfully.' => 'تمت إزالة القالب بنجاح.', + 'Unable to remove this template.' => 'تعذّر إزالة هذا القالب.', + 'Template for the task description' => 'قالب لوص٠المهمة', + 'The start date is greater than the end date' => 'تاريخ البدء أكبر من تاريخ الانتهاء', + 'Tags must be separated by a comma' => 'يجب ÙØµÙ„ الوسوم Ø¨ÙØ§ØµÙ„Ø©', + 'Only the task title is required' => 'مطلوب Ùقط عنوان المهمة', + 'Creator Username' => 'اسم مستخدم المنشئ', + 'Color Name' => 'اسم اللون', + 'Column Name' => 'اسم العمود', + 'Swimlane Name' => 'اسم مسار السباحة', + 'Time Estimated' => 'الوقت المقدّر', + 'Time Spent' => 'الوقت المستغرق', + 'External Link' => 'رابط خارجي', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'تتيح هذه الميزة خلاصة iCal وتلقيم RSS وعرض اللوحة العامة.', + 'Stop the timer of all subtasks when moving a task to another column' => 'إيقا٠مؤقت جميع المهام Ø§Ù„ÙØ±Ø¹ÙŠØ© عند نقل مهمة إلى عمود آخر', + 'Subtask Title' => 'عنوان المهمة Ø§Ù„ÙØ±Ø¹ÙŠØ©', + 'Add a subtask and activate the timer when moving a task to another column' => 'Ø¥Ø¶Ø§ÙØ© مهمة ÙØ±Ø¹ÙŠØ© ÙˆØªÙØ¹ÙŠÙ„ المؤقت عند نقل مهمة إلى عمود آخر', + 'days' => 'أيام', + 'minutes' => 'دقائق', + 'seconds' => 'ثوانÙ', + 'Assign automatically a color when preset start date is reached' => 'تعيين لون تلقائيًا عند بلوغ تاريخ البدء المحدّد مسبقًا', + 'Move the task to another column once a predefined start date is reached' => 'نقل المهمة إلى عمود آخر عند بلوغ تاريخ بدء محدد مسبقًا', + 'This task is now linked to the task %s with the relation "%s"' => 'تم ربط هذه المهمة الآن بالمهمة %s بالعلاقة "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'تمت إزالة الرابط بالعلاقة "%s" إلى المهمة %s', + 'Custom Filter:' => 'مرشّح مخصص:', + 'Unable to find this group.' => 'تعذّر العثور على هذه المجموعة.', + '%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 moved the task #%d to the swimlane "%s"' => 'قام %s بنقل المهمة #%d إلى مسار السباحة "%s"', + '%sh spent' => '%sس مستغرق', + '%sh estimated' => '%sس مقدّر', + 'Select All' => 'تحديد الكل', + 'Unselect All' => 'إلغاء تحديد الكل', + 'Apply action' => 'تطبيق الإجراء', + 'Move selected tasks to another column or swimlane' => 'نقل المهام المحددة إلى عمود أو مسار سباحة آخر', + 'Edit tasks in bulk' => 'تحرير المهام بالجملة', + 'Choose the properties that you would like to change for the selected tasks.' => 'اختر الخصائص التي ترغب ÙÙŠ تغييرها للمهام المحددة.', + 'Configure this project' => 'تهيئة هذا المشروع', + 'Start now' => 'ابدأ الآن', + '%s removed a file from the task #%d' => 'قام %s بإزالة مل٠من المهمة #%d', + 'Attachment removed from task #%d: %s' => 'Ø£ÙØ²ÙŠÙ„ المرÙÙ‚ من المهمة #%d: %s', + 'No color' => 'بدون لون', + 'Attachment removed "%s"' => 'Ø£ÙØ²ÙŠÙ„ المرÙÙ‚ "%s"', + '%s removed a file from the task %s' => 'قام %s بإزالة مل٠من المهمة %s', + 'Move the task to another swimlane when assigned to a user' => 'نقل المهمة إلى مسار سباحة آخر عند إسنادها لمستخدم', + 'Destination swimlane' => 'مسار السباحة الوجهة', + 'Assign a category when the task is moved to a specific swimlane' => 'تعيين ÙØ¦Ø© عند نقل المهمة إلى مسار سباحة محدد', + 'Move the task to another swimlane when the category is changed' => 'نقل المهمة إلى مسار سباحة آخر عند تغيير Ø§Ù„ÙØ¦Ø©', + 'Reorder this column by priority (ASC)' => 'إعادة ترتيب هذا العمود حسب الأولوية (تصاعدي)', + 'Reorder this column by priority (DESC)' => 'إعادة ترتيب هذا العمود حسب الأولوية (تنازلي)', + 'Reorder this column by assignee and priority (ASC)' => 'إعادة ترتيب هذا العمود حسب المكلّ٠والأولوية (تصاعدي)', + 'Reorder this column by assignee and priority (DESC)' => 'إعادة ترتيب هذا العمود حسب المكلّ٠والأولوية (تنازلي)', + 'Reorder this column by assignee (A-Z)' => 'إعادة ترتيب هذا العمود حسب المكلّ٠(Ø£-ÙŠ)', + 'Reorder this column by assignee (Z-A)' => 'إعادة ترتيب هذا العمود حسب المكلّ٠(ÙŠ-Ø£)', + 'Reorder this column by due date (ASC)' => 'إعادة ترتيب هذا العمود حسب تاريخ الاستحقاق (تصاعدي)', + 'Reorder this column by due date (DESC)' => 'إعادة ترتيب هذا العمود حسب تاريخ الاستحقاق (تنازلي)', + 'Reorder this column by id (ASC)' => 'إعادة ترتيب هذا العمود حسب المعرّ٠(تصاعدي)', + 'Reorder this column by id (DESC)' => 'إعادة ترتيب هذا العمود حسب المعرّ٠(تنازلي)', + '%s moved the task #%d "%s" to the project "%s"' => 'قام %s بنقل المهمة #%d "%s" إلى المشروع "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'تم نقل المهمة #%d "%s" إلى المشروع "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'نقل المهمة إلى عمود آخر عندما يكون تاريخ الاستحقاق أقل من عدد معيّن من الأيام', + 'Automatically update the start date when the task is moved away from a specific column' => 'تحديث تاريخ البدء تلقائيًا عند نقل المهمة بعيدًا عن عمود محدد', + 'HTTP Client:' => 'عميل HTTP:', + 'Assigned' => 'Ù…ÙØ³Ù†Ø¯', + 'Task limits apply to each swimlane individually' => 'تنطبق حدود المهام على كل مسار سباحة على حدة', + 'Column task limits apply to each swimlane individually' => 'تنطبق حدود مهام العمود على كل مسار سباحة على حدة', + 'Column task limits are applied to each swimlane individually' => 'ØªÙØ·Ø¨Ù‘Ù‚ حدود مهام العمود على كل مسار سباحة على حدة', + 'Column task limits are applied across swimlanes' => 'ØªÙØ·Ø¨Ù‘Ù‚ حدود مهام العمود عبر مسارات السباحة', + 'Task limit: ' => 'حد المهام: ', + 'Change to global tag' => 'تغيير إلى وسم عام', + 'Do you really want to make the tag "%s" global?' => 'هل تريد ÙØ¹Ù„اً جعل الوسم "%s" عامًا؟', + 'Enable global tags for this project' => 'تمكين الوسوم العامة لهذا المشروع', + 'Group membership(s):' => 'عضوية المجموعات:', + '%s is a member of the following group(s): %s' => '%s عضو ÙÙŠ المجموعات التالية: %s', + '%d/%d group(s) shown' => 'Ø¹ÙØ±Ø¶ %d/%d مجموعة', + 'Subtask creation or modification' => 'إنشاء أو تعديل مهمة ÙØ±Ø¹ÙŠØ©', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'إسناد المهمة إلى مستخدم محدد عند نقلها إلى مسار سباحة معين', + 'Comment' => 'تعليق', + 'Collapse vertically' => 'طيّ رأسيًا', + 'Expand vertically' => 'توسيع رأسيًا', + 'MXN - Mexican Peso' => 'MXN - بيزو مكسيكي', + 'Estimated vs actual time per column' => 'الوقت المقدّر مقابل Ø§Ù„ÙØ¹Ù„ÙŠ لكل عمود', + 'HUF - Hungarian Forint' => 'HUF - Ùورنت مجري', + 'XBT - Bitcoin' => 'XBT - بيتكوين', + 'You must select a file to upload as your avatar!' => 'يجب تحديد Ù…Ù„Ù Ù„Ø±ÙØ¹Ù‡ كصورتك الرمزية!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'المل٠الذي Ø±ÙØ¹ØªÙ‡ ليس صورة صالحة! (يسمح Ùقط بـ *.gif Ùˆ *.jpg Ùˆ *.jpeg Ùˆ *.png)', + 'Automatically set the due date when the task is moved away from a specific column' => 'تعيين تاريخ الاستحقاق تلقائيًا عند نقل المهمة بعيدًا عن عمود محدد', + 'No other projects found.' => 'لم يتم العثور على مشاريع أخرى.', + 'Tasks copied successfully.' => 'تم نسخ المهام بنجاح.', + 'Unable to copy tasks.' => 'تعذّر نسخ المهام.', + 'Theme' => 'السÙمة', + 'Theme:' => 'السÙمة:', + 'Light theme' => 'سÙمة ÙØ§ØªØ­Ø©', + 'Dark theme' => 'سÙمة داكنة', + 'Automatic theme - Sync with system' => 'سÙمة تلقائية - مزامنة مع النظام', + 'Application managers or more' => 'مدراء التطبيق أو أعلى', + 'Administrators' => 'المديرون', + 'Visibility:' => 'الظهور:', + 'Standard users' => 'المستخدمون العاديون', + 'Visibility is required' => 'الظهور مطلوب', + 'The visibility should be an app role' => 'يجب أن تكون درجة الظهور دورًا ÙÙŠ التطبيق', + 'Reply' => 'ردّ', + '%s wrote: ' => 'كتب %s: ', + 'Number of visible tasks in this column and swimlane' => 'عدد المهام المرئية ÙÙŠ هذا العمود ومسار السباحة', + 'Number of tasks in this swimlane' => 'عدد المهام ÙÙŠ هذا مسار السباحة', + 'Unable to find another subtask in progress, you can close this window.' => 'تعذّر العثور على مهمة ÙØ±Ø¹ÙŠØ© أخرى قيد التنÙيذ، يمكنك إغلاق هذه Ø§Ù„Ù†Ø§ÙØ°Ø©.', + 'This theme is invalid' => 'هذه السÙمة غير صالحة', + 'This role is invalid' => 'هذا الدور غير صالح', + 'This timezone is invalid' => 'هذه المنطقة الزمنية غير صالحة', + 'This language is invalid' => 'هذه اللغة غير صالحة', + 'This URL is invalid' => 'هذا الرابط غير صالح', + 'Date format invalid' => 'تنسيق التاريخ غير صالح', + 'Time format invalid' => 'تنسيق الوقت غير صالح', + 'Invalid Mail transport' => 'وسيلة نقل بريد غير صالحة', + 'Color invalid' => 'لون غير صالح', + 'This value must be greater or equal to %d' => 'يجب أن تكون هذه القيمة أكبر من أو تساوي %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'أض٠BOM ÙÙŠ بداية المل٠(مطلوب لبرنامج Microsoft Excel)', + 'Just add these tag(s)' => 'أض٠هذه الوسوم Ùقط', + 'Remove internal link(s)' => 'إزالة روابط داخلية', + 'Import tasks from another project' => 'استيراد مهام من مشروع آخر', + 'Select the project to copy tasks from' => 'حدد المشروع لنسخ المهام منه', + 'The total maximum allowed attachments size is %sB.' => 'إجمالي الحد الأقصى المسموح به للمرÙقات هو %sB.', + 'Add attachments' => 'Ø¥Ø¶Ø§ÙØ© مرÙقات', + 'Task #%d "%s" is overdue' => 'المهمة #%d "%s" متأخرة', + 'Enable notifications by default for all new users' => 'تمكين الإشعارات Ø§ÙØªØ±Ø§Ø¶ÙŠÙ‹Ø§ لجميع المستخدمين الجدد', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'إسناد المهمة إلى منشئها لأعمدة محددة إذا لم يتم تعيين مكلّ٠يدويًا', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'إسناد المهمة إلى المستخدم المسجّل عند تغيير العمود إلى العمود المحدد إذا لم يكن هناك مستخدم معيّن', +]; diff --git a/app/Locale/bg_BG/translations.php b/app/Locale/bg_BG/translations.php new file mode 100644 index 0000000..793325c --- /dev/null +++ b/app/Locale/bg_BG/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'ÐÑма', + 'Edit' => 'Редактиране', + 'Remove' => 'Премахни', + 'Yes' => 'Да', + 'No' => 'Ðе', + 'cancel' => 'Отказ', + 'or' => 'или', + 'Yellow' => 'Жълто', + 'Blue' => 'Синьо', + 'Green' => 'Зелено', + 'Purple' => 'Лилаво', + 'Red' => 'Червено', + 'Orange' => 'Оранжево', + 'Grey' => 'Сиво', + 'Brown' => 'КафÑво', + 'Deep Orange' => 'Тъмнооранжево', + 'Dark Grey' => 'ТъмноÑиво', + 'Pink' => 'Розово', + 'Teal' => 'Тил', + 'Cyan' => 'Циан', + 'Lime' => 'Лайм', + 'Light Green' => 'Светлозелено', + 'Amber' => 'Кехлибаренo', + 'Save' => 'Запиши', + 'Login' => ' Вход', + 'Official website:' => 'Официален Ñайт:', + 'Unassigned' => 'ÐеприÑвоен', + 'View this task' => 'Преглед на тази задача', + 'Remove user' => 'Премахване на потребител', + 'Do you really want to remove this user: "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете този потребител: "%s"?', + 'All users' => 'Ð’Ñички потребители', + 'Username' => 'ПотребителÑко име', + 'Password' => 'Парола', + 'Administrator' => 'ÐдминиÑтратор', + 'Sign in' => 'Вход', + 'Users' => 'Потребители', + 'Forbidden' => 'Забранен', + 'Access Forbidden' => 'ДоÑтъпът е забранен', + 'Edit user' => 'Редактиране на потребител', + 'Logout' => 'Изход', + 'Bad username or password' => 'Грешно потребителÑко име или парола', + 'Edit project' => 'Редактирай проект', + 'Name' => 'Име', + 'Projects' => 'Проекти', + 'No project' => 'ÐÑма проект', + 'Project' => 'Проект', + 'Status' => 'СтатуÑ', + 'Tasks' => 'Задачи', + 'Board' => 'ДъÑка', + 'Actions' => 'ДейÑтвиÑ', + 'Inactive' => 'Ðеактивен', + 'Active' => 'Ðктивен', + 'Unable to update this board.' => 'Ðе може да Ñе актуализира тази дъÑка.', + 'Disable' => 'Изключи', + 'Enable' => 'Включи', + 'New project' => 'Ðов проект', + 'Do you really want to remove this project: "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете този проект: "%s"?', + 'Remove project' => 'Премахване на проект', + 'Edit the board for "%s"' => 'Редактиране на дъÑката за "%s"', + 'Add a new column' => 'ДобавÑне на нова колона', + 'Title' => 'Заглавие', + 'Assigned to %s' => 'Възложено на %s', + 'Remove a column' => 'Премахване на колона', + 'Unable to remove this column.' => 'Тази колона не може да бъде премахната.', + 'Do you really want to remove this column: "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете тази колона: "%s"?', + 'Settings' => 'ÐаÑтройки', + 'Application settings' => 'ÐаÑтройки на приложението', + 'Language' => 'Език', + 'Webhook token:' => 'Webhook токен:', + 'API token:' => 'API токен:', + 'Database size:' => 'Размер на базата данни:', + 'Download the database' => 'ИзтеглÑне на базата данни', + 'Optimize the database' => 'Оптимизиране на базата данни', + '(VACUUM command)' => '(команда за VACUUM)', + '(Gzip compressed Sqlite file)' => '(Gzip компреÑиран Sqlite файл)', + 'Close a task' => 'ЗатварÑне на задача', + 'Column' => 'Колона', + 'Color' => 'ЦвÑÑ‚', + 'Assignee' => 'Изпълнител', + 'Create another task' => 'Създаване на друга задача', + 'New task' => 'Ðова задача', + 'Open a task' => 'ОтварÑне на задача', + 'Do you really want to open this task: "%s"?' => 'ÐаиÑтина ли иÑкате да отворите тази задача: "%s"?', + 'Back to the board' => 'Обратно към дъÑката', + 'There is nobody assigned' => 'Ðикой не е назначен', + 'Column on the board:' => 'Колона на дъÑката:', + 'Close this 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 знака', + 'The password is required' => 'ИзиÑква Ñе парола', + 'This value must be an integer' => 'Тази ÑтойноÑÑ‚ трÑбва да бъде цÑло чиÑло', + 'The username must be unique' => 'ПотребителÑкото име трÑбва да е уникално', + 'The user id is required' => 'ПотребителÑкото ID е задължителен', + 'Passwords don\'t match' => 'Паролите не Ñъвпадат', + 'The confirmation is required' => 'Потвърждението е задължително', + 'The project is required' => 'Проекта е задължителен', + 'The id is required' => 'ID е задължително', + 'The project id is required' => 'ID на проекта е задължителен', + 'The project name is required' => 'Името на проекта е задължително', + 'The title is required' => 'ИзиÑква Ñе заглавие', + 'Settings saved successfully.' => 'ÐаÑтройките Ñа запиÑани уÑпешно.', + 'Unable to save your settings.' => 'ÐеуÑпешно запазване на вашите наÑтройки.', + 'Database optimization done.' => 'Оптимизирането на базата данни е направено.', + 'Your project has been created successfully.' => 'Ð’Ð°ÑˆÐ¸Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚ е Ñъздаден уÑпешно.', + 'Unable to create your project.' => 'ÐеуÑпешно Ñъздаване на Ð²Ð°ÑˆÐ¸Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚.', + 'Project updated successfully.' => 'Проекта е актуализиран уÑпешно.', + 'Unable to update this project.' => 'ÐеуÑпешно актуализиране на този проект.', + 'Unable to remove this project.' => 'Този проект не може да бъде премахнат.', + 'Project removed successfully.' => 'Проекта е премахнат уÑпешно.', + 'Project activated successfully.' => 'Проекта е активиран уÑпешно.', + 'Unable to activate this project.' => 'ÐеуÑпешно активиране на този проект.', + 'Project disabled successfully.' => 'Проекта е деактивиран уÑпешно.', + 'Unable to disable this project.' => 'Този проект не може да бъде деактивиран.', + 'Unable to open this task.' => 'Тази задача не може да бъде отворена.', + 'Task opened successfully.' => 'Задачата е отворена уÑпешно.', + 'Unable to close this task.' => 'Тази задача не може да бъде затворена.', + 'Task closed successfully.' => 'Задачата е приключена уÑпешно.', + 'Unable to update your task.' => 'Вашата задача не може да бъде актуализирана.', + 'Task updated successfully.' => 'Задачата е актуализирана уÑпешно.', + 'Unable to create your task.' => 'Вашата задача не може да бъде Ñъздадена.', + 'Task created successfully.' => 'Задачата е Ñъздадена уÑпешно.', + 'User created successfully.' => 'Създаването на потребител е уÑпешно', + 'Unable to create your user.' => 'ÐеуÑпешно Ñъздаване на Ð²Ð°ÑˆÐ¸Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ».', + 'User updated successfully.' => 'ÐŸÐ¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ Ðµ актуализиран уÑпешно', + 'User removed successfully.' => 'ÐŸÐ¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ Ðµ премахнат уÑпешно.', + 'Unable to remove this user.' => 'Този потребител не може да бъде премахнат.', + 'Board updated successfully.' => 'ДъÑката е актуализиран уÑпешно.', + 'Ready' => 'Ð’ готовноÑÑ‚', + 'Backlog' => 'Изчакващи', + 'Work in progress' => 'Работа в прогреÑ', + 'Done' => 'Готови', + 'Application version:' => 'ВерÑÐ¸Ñ Ð½Ð° приложението:', + 'Id' => 'Id', + 'Public link' => 'Публична връзка', + 'Timezone' => 'ЧаÑова зона', + 'Sorry, I didn\'t find this information in my database!' => 'За Ñъжаление, не намерих тази Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð² базата данни!', + 'Page not found' => 'Страницата не е намерена', + 'Complexity' => 'СложноÑÑ‚', + 'Task limit' => 'Лимит на задачите', + 'Task count' => 'Брой задачи', + 'User' => 'Потребител', + 'Comments' => 'Забележки', + 'Comment is required' => 'Ðеобходима е забележка', + 'Comment added successfully.' => 'Забележката е добавена уÑпешно.', + 'Unable to create your comment.' => 'Ð’Ð°ÑˆÐ¸Ñ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€ не може да бъде Ñъздаден.', + 'Due Date' => 'Краен Ñрок', + 'Invalid date' => 'Ðевалидна дата', + 'Automatic actions' => 'Ðвтоматични дейÑтвиÑ', + 'Your automatic action has been created successfully.' => 'Вашето автоматичното дейÑтвие е Ñъздадено уÑпешно.', + 'Unable to create your automatic action.' => 'ÐеуÑпешно Ñъздаване на вашето автоматично дейÑтвие.', + 'Remove an action' => 'Премахване на дейÑтвие', + 'Unable to remove this action.' => 'Това дейÑтвие не може да бъде премахнато.', + 'Action removed successfully.' => 'ДейÑтвието е премахнато уÑпешно.', + 'Automatic actions for the project "%s"' => 'Ðвтоматични дейÑÑ‚Ð²Ð¸Ñ Ð½Ð° проекта "%s"', + 'Add an action' => 'ДобавÑне на дейÑтвие', + 'Event name' => 'Име на Ñъбитието', + 'Action' => 'ДейÑтвие', + 'Event' => 'Събитие', + 'When the selected event occurs execute the corresponding action.' => 'Когато Ñе Ñлучи избраното Ñъбитие, изпълнете Ñъответното дейÑтвие.', + 'Next step' => 'Следваща Ñтъпка', + '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 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' => 'ПриÑвоÑване на цвÑÑ‚ към конкретен потребител', + 'Position' => 'позициÑ', + 'Duplicate to project' => 'Дублиране на проект', + 'Duplicate' => 'Дублиране', + 'Link' => 'Линк', + 'Comment updated successfully.' => 'Забележката е актуализирана уÑпешно.', + 'Unable to update your comment.' => 'Вашата забележка не може да бъде актуализирана.', + 'Remove a comment' => 'Премахване на забележка', + 'Comment removed successfully.' => 'Забележката е премахната уÑпешно.', + 'Unable to remove this comment.' => 'Тази забележка не може да бъде премахната.', + 'Do you really want to remove this comment?' => 'ÐаиÑтина ли иÑкате да премахнете тази забележката?', + 'Current password for the user "%s"' => 'Текуща парола на Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ "%s"', + 'The current password is required' => 'ИзиÑква Ñе текущата парола', + 'Wrong password' => 'Грешна парола', + 'Unknown' => 'ÐеизвеÑтно', + 'Last logins' => 'ПоÑледни влизаниÑ', + 'Login date' => 'Дата на влизане', + 'Authentication method' => 'УдоÑтоверÑване', + 'IP address' => 'IP адреÑи', + 'User agent' => 'ПотребителÑки агент', + 'Persistent connections' => 'УÑтойчиви връзки', + 'No session.' => 'ÐÑма ÑеÑиÑ', + 'Expiration date' => ' Изтича на', + 'Remember Me' => ' Запомни ме', + 'Creation date' => 'Дата на Ñъздаване', + 'Everybody' => 'Ð’Ñеки', + 'Open' => 'Отворено', + 'Closed' => 'Затворено', + 'Search' => 'ТърÑене', + 'Nothing found.' => 'ÐÑма намерени.', + 'Due date' => 'Краен Ñрок', + 'Description' => 'ОпиÑание', + '%d comments' => '%d забележки', + '%d comment' => '%d забележка', + '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.' => 'Ð’Ð°ÑˆÐ¸Ñ Ð²ÑŠÐ½ÑˆÐµÐ½ акаунт е Ñвързан уÑпешно Ñ Ð²Ð°ÑˆÐ¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð».', + 'Email' => 'Имейл', + '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' => 'Ðвтоматично задаване на ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ð²ÑŠÐ· оÑнова на цвÑÑ‚', + 'Task creation or modification' => 'Създаване или промÑна на задача', + 'Category' => 'КатегориÑ', + 'Category:' => 'КатегориÑ:', + 'Categories' => ' Категории', + 'Your category has been created successfully.' => 'Вашата ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ðµ Ñъздадена уÑпешно.', + 'This category has been updated successfully.' => 'Тази ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ðµ актуализирана уÑпешно.', + 'Unable to update this 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' => 'Без категориÑ', + 'The name is required' => 'Името е задължително', + 'Remove a file' => 'Премахване на файл', + 'Unable to remove this file.' => 'Този файл не може да бъде премахнат.', + 'File removed successfully.' => 'Файла е уÑпешно премахнат', + 'Attach a document' => 'Прикачване на документ', + 'Do you really want to remove this file: "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете този файл: "%s"?', + 'Attachments' => 'Прикачени файлове', + 'Edit the task' => 'Редактиране на задачата', + 'Add a comment' => 'Добавете забележка', + 'Edit a comment' => 'Редактиране на забележка', + 'Summary' => 'Обобщение', + 'Time tracking' => 'ПроÑледÑване на времето', + 'Estimate:' => 'Приблизително:', + 'Spent:' => 'Прекарано:', + 'Do you really want to remove this sub-task?' => 'ÐаиÑтина ли иÑкате да премахнете тази подзадача ?', + 'Remaining:' => 'ОÑтават:', + 'hours' => 'чаÑа', + 'estimated' => 'Очаквано', + 'Sub-Tasks' => 'Подзадачи', + 'Add a sub-task' => 'ДобавÑне на подзадача', + 'Original estimate' => 'Първоначална оценка', + 'Create another sub-task' => 'Създаване на друга подзадача', + 'Time spent' => 'Прекарано време', + 'Edit a sub-task' => 'Редактиране на подзадача', + 'Remove a sub-task' => 'Премахване на подзадача', + 'The time must be a numeric value' => 'Времето трÑбва да бъде цифрова ÑтойноÑÑ‚', + 'Todo' => 'Todo', + 'In progress' => 'Ð’ ход', + 'Sub-task removed successfully.' => 'Подзадачата е премахната уÑпешно.', + 'Unable to remove this sub-task.' => 'Тази подзадача не може да бъде премахната.', + 'Sub-task updated successfully.' => 'Подзадачата е актуализирана уÑпешно.', + 'Unable to update your sub-task.' => 'ÐеуÑпешно актуализиране на подзадачата.', + 'Unable to create your sub-task.' => 'ÐеуÑпешно Ñъздаване на подзадачата.', + 'Maximum size: ' => 'МакÑимален размер:', + 'Display another project' => 'Показване на друг проект', + 'Created by %s' => 'Създадена от %s', + 'Tasks Export' => 'ЕкÑпортиране на задачи', + 'Start Date' => 'Ðачална дата', + 'Execute' => 'Изпълни', + 'Task Id' => 'ID на задачата', + 'Creator' => 'Създател', + 'Modification date' => 'Дата на промÑна', + 'Completion date' => 'Крайна дата', + 'Clone' => 'Клониране', + 'Project cloned successfully.' => 'Проекта е клониран уÑпешно.', + 'Unable to clone this project.' => 'Ðе може да Ñе клонира този проект.', + 'Enable email notifications' => 'Ðктивиране на извеÑÑ‚Ð¸Ñ Ð¿Ð¾ имейл', + 'Task position:' => 'ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð½Ð° задачата:', + 'The task #%d has been opened.' => 'Задача #%d е отворена.', + 'The task #%d has been closed.' => 'Задача #%d е затворена.', + 'Sub-task updated' => 'Подзадачата е актуализирана', + 'Title:' => 'Заглавие:', + 'Status:' => 'СтатуÑ:', + 'Assignee:' => 'Изпълнител:', + 'Time tracking:' => 'ПроÑледÑване на времето:', + 'New sub-task' => 'Ðова подзадача', + 'New attachment added "%s"' => 'Добавен е нов прикачен файл "%s"', + 'New comment posted by %s' => 'Ðова забележка, публикуван от %s', + 'New comment' => 'Ðова забележка', + 'Comment updated' => 'Коментара е актуализиран', + 'New subtask' => 'Ðова подзадача', + 'I only want to receive notifications for these projects:' => 'ИÑкам да получавам извеÑÑ‚Ð¸Ñ Ñамо за тези проекти:', + 'view the task on Kanboard' => 'преглед на задачата в Kanboard', + 'Public access' => 'ПубличноÑÑ‚', + 'Disable public access' => 'Деактивиране на Ð¿ÑƒÐ±Ð»Ð¸Ñ‡Ð½Ð¸Ñ Ð´Ð¾Ñтъп', + 'Enable public access' => 'Ðктивиране на публичен доÑтъп', + 'Public access disabled' => 'ОбщеÑÑ‚Ð²ÐµÐ½Ð¸Ñ Ð´Ð¾Ñтъп е деактивиран', + 'Move the task to another project' => 'ПремеÑтване на задачата в друг проект', + 'Move to project' => 'ПремеÑтване в проект', + 'Do you really want to duplicate this task?' => 'ÐаиÑтина ли иÑкате да дублирате тази задача ?', + 'Duplicate a task' => 'Дублиране на задача', + 'External accounts' => 'Външни акаунти', + 'Account type' => 'Вид акаунт', + 'Local' => 'Локален', + 'Remote' => 'Отдалечен', + 'Enabled' => 'Ðктивирано', + 'Disabled' => 'Деактивирано', + 'Login:' => ' Вход:', + 'Full Name:' => 'Пълно име:', + 'Email:' => 'Имейл:', + 'Notifications:' => 'УведомлениÑ:', + 'Notifications' => 'УведомлениÑ', + 'Account type:' => 'Вид акаунт:', + 'Edit profile' => 'ПромÑна на профил', + 'Change password' => 'СмÑна на паролата', + 'Password modification' => 'ПромÑна на паролата', + 'External authentications' => 'Външни удоÑтоверÑваниÑ', + 'Never connected.' => '- Ðикога не Ñа Ñе Ñвързвали.', + '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/%sч', + '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 канал', + '%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)' => 'Колони по подразбиране за нови проекти (разделени ÑÑŠÑ Ð·Ð°Ð¿ÐµÑ‚Ð°Ñ)', + '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' => 'Избор на Ñъбитие', + 'Create a task from an external provider' => 'Създаване на задача от външен доÑтавчик', + 'Change the assignee based on an external username' => 'ПромÑна на Ð¸Ð·Ð¿ÑŠÐ»Ð½Ð¸Ñ‚ÐµÐ»Ñ Ð²ÑŠÐ· оÑнова на външно потребителÑко име', + 'Change the category based on an external label' => 'ПромÑна на категориÑта въз оÑнова на външен етикет', + 'Reference' => 'РеференциÑ', + 'Label' => 'Етикет', + 'Database' => 'База данни', + 'About' => 'ОтноÑно', + 'Database driver:' => 'Драйвер на база данни:', + 'Board settings' => 'ÐаÑтройки на дъÑката', + 'Webhook settings' => 'ÐаÑтройка на Webhook', + 'Reset token' => 'Ðулиране на токена', + 'API endpoint:' => 'Крайна точка на API:', + 'Refresh interval for personal 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 Ñекунди по подразбиране)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'ЧеÑтота в Ñекунди (0 за деактивиране на тази функциÑ, 10 Ñекунди по подразбиране)', + 'Application URL' => 'Url Ð°Ð´Ñ€ÐµÑ Ð½Ð° приложението', + 'Token regenerated.' => '- Токена е регенериран.', + 'Date format' => 'Формат на дата', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Формат ISO винаги Ñе приема, например: "%s" и "%s"', + 'New personal project' => 'Ðов личен проект', + 'This project is personal' => 'Този проект е личен', + 'Add' => 'Добави', + 'Start date' => 'Ðачална дата', + 'Time estimated' => 'Очаквано време', + 'There is nothing assigned to you.' => 'Ðищо не Ви е възложено.', + 'My tasks' => 'Моите задачи', + 'Activity stream' => 'Поток на активноÑтта', + 'Dashboard' => 'Табло', + 'Confirmation' => 'Потвърждение', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Създаване на забележка от външен доÑтавчик', + 'Project management' => 'Управление на проекти', + 'Columns' => 'Колони ', + 'Task' => 'Задача', + 'Percentage' => 'Процент', + 'Number of tasks' => 'Брой задачи', + 'Task distribution' => 'Разпределение на задачите', + 'Analytics' => 'Ðнализ', + 'Subtask' => 'Подзадача', + 'User repartition' => 'Разпределение на потребителите', + 'Clone this project' => 'Клониране на този проект', + 'Column removed successfully.' => 'Колоната е премахната уÑпешно.', + '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 subtask id is required' => 'ИзиÑква Ñе идентификатор на подзадачата', + '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' => 'ПотребителÑÐºÐ¸Ñ Ð¸Ð´ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ‚Ð¾Ñ€ трÑбва да бъде цÑло чиÑло', + 'This value is required' => 'Тази ÑтойноÑÑ‚ е задължителна', + 'This value must be numeric' => 'Тази ÑтойноÑÑ‚ трÑбва да е цифрова', + 'Unable to create this task.' => 'Тази задача не може да бъде Ñъздадена.', + 'Cumulative flow diagram' => 'Схема на ÐºÑƒÐ¼ÑƒÐ»Ð°Ñ‚Ð¸Ð²Ð½Ð¸Ñ Ð¿Ð¾Ñ‚Ð¾Ðº', + 'Daily project summary' => 'Ежедневно резюме на проекта', + 'Daily project summary export' => 'Ежедневен Ð¸Ð·Ð½Ð¾Ñ Ð½Ð° обобщена Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° проекта', + 'Exports' => 'ЕкÑпорти', + 'This export contains the number of tasks per column grouped per day.' => 'Този екÑпорт Ñъдържа Ð±Ñ€Ð¾Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸ на колона, групирани на ден.', + 'Active swimlanes' => 'Ðктивни коридори', + 'Add a new swimlane' => 'ДобавÑне на нов коридор', + 'Default swimlane' => 'Коридор по подразбиране', + 'Do you really want to remove this swimlane: "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете този коридор: "%s"?', + 'Inactive swimlanes' => 'Ðеактивни коридори', + 'Remove a swimlane' => 'Премахване на коридор', + 'Swimlane modification for the project "%s"' => 'ÐœÐ¾Ð´Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð½Ð° коридор за проекта "%s"', + 'Swimlane removed successfully.' => 'Коридора е премахнато уÑпешно.', + 'Swimlanes' => 'Коридори', + 'Swimlane updated successfully.' => 'Коридора е актуализиран уÑпешно.', + 'Unable to remove this swimlane.' => 'Ðевъзможно е да Ñе премахне този коридор.', + 'Unable to update this swimlane.' => 'ÐеуÑпешно актуализиране на този коридор.', + 'Your swimlane has been created successfully.' => 'Ð’Ð°ÑˆÐ¸Ñ ÐºÐ¾Ñ€Ð¸Ð´Ð¾Ñ€ е Ñъздадена уÑпешно.', + 'Example: "Bug, Feature Request, Improvement"' => 'Пример: „Грешка, ЗаÑвка за функционалноÑÑ‚, Подобрение“', + 'Default categories for new projects (Comma-separated)' => 'Категории по подразбиране за нови проекти (разделени ÑÑŠÑ Ð·Ð°Ð¿ÐµÑ‚Ð°Ñ)', + 'Integrations' => 'Интеграции', + 'Integration with third-party services' => 'Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ Ñ ÑƒÑлуги на трети Ñтрани', + 'Subtask Id' => 'ID на подзадачата', + 'Subtasks' => 'Подзадачи', + 'Subtasks Export' => 'ЕкÑпортиране на подзадачи', + 'Task Title' => 'Заглавие на задача', + 'Untitled' => 'Ðеозаглавено', + 'Application default' => 'По подразбиране на приложението', + 'Language:' => 'Език:', + 'Timezone:' => 'ЧаÑова зона:', + 'All columns' => 'Ð’Ñички колони', + 'Next' => 'Ðапред', + '#%d' => '#%d', + 'All swimlanes' => 'Ð’Ñички коридори', + 'All colors' => 'Ð’Ñички цветове', + 'Moved to column %s' => 'ПремеÑтено в колона %s', + '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' => 'ПроÑледÑване на времето', + 'You already have one subtask in progress' => 'Вече имате една подзадача в Ð¿Ñ€Ð¾Ñ†ÐµÑ Ð½Ð° изпълнение', + 'Which parts of the project do you want to duplicate?' => 'ÐšÐ¾Ñ Ñ‡Ð°ÑÑ‚ от проекта иÑкате да дублирате?', + 'Disallow login form' => 'ЗабранÑване на формулÑра за вход', + 'Start' => 'Старт', + 'End' => 'Край', + 'Task age in days' => 'ВъзраÑÑ‚ на задачата в дни', + 'Days in this column' => 'Дни в тази колона', + '%dd' => '%dd', + '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' => 'Полето е задължително', + 'Link added successfully.' => 'Връзката е добавена уÑпешно.', + 'Link updated successfully.' => 'Връзката е актуализирана уÑпешно.', + 'Link removed successfully.' => 'Връзката е премахната уÑпешно.', + 'Link labels' => 'Свързване на етикети', + 'Link modification' => 'ПромÑна на връзката', + 'Opposite label' => 'Противоположен етикет', + 'Remove a link' => 'Изтриване на връзка.', + '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.' => 'Тази връзка не може да бъде премахната.', + 'relates to' => 'Ñе отнаÑÑ Ð´Ð¾', + 'blocks' => 'блокира', + 'is blocked by' => 'е блокиран от', + 'duplicates' => 'дублира', + 'is duplicated by' => 'Ñе дублира от', + 'is a child of' => 'е дете на', + 'is a parent of' => 'е родител на', + 'targets milestone' => 'цели', + 'is a milestone of' => 'е цел на', + 'fixes' => 'определÑ', + 'is fixed by' => 'Ñе Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñ Ð¾Ñ‚', + 'This task' => 'Тази задача', + '<1h' => '<1ч', + '%dh' => '%dч', + 'Expand tasks' => 'Разгъване на задачите', + 'Collapse tasks' => 'Свиване на задачите', + 'Expand/collapse tasks' => 'Разгъване/Ñвиване на задачи', + 'Close dialog box' => 'ЗатварÑне на Ð´Ð¸Ð°Ð»Ð¾Ð³Ð¾Ð²Ð¸Ñ Ð¿Ñ€Ð¾Ð·Ð¾Ñ€ÐµÑ†', + 'Submit a form' => 'Подаване на формулÑÑ€', + 'Board view' => 'Изглед на дъÑката', + 'Keyboard shortcuts' => 'Преки пътища от клавиатурата', + 'Open board switcher' => 'Превключвател на дъÑки', + 'Application' => 'Приложение', + 'Compact view' => 'Компактен изглед', + 'Horizontal scrolling' => 'Хоризонтално превъртане', + 'Compact/wide view' => 'Компактен/широк изглед', + 'Currency' => 'Валута', + 'Personal project' => 'ПерÑонален проект', + 'AUD - Australian Dollar' => 'AUD - авÑтралийÑки долар', + 'CAD - Canadian Dollar' => 'CAD - канадÑки долар', + 'CHF - Swiss Francs' => 'CHF - швейцарÑки франкове', + 'Custom Stylesheet' => 'ПерÑонализиран Ñтилов лиÑÑ‚', + 'EUR - Euro' => 'EUR - евро ', + 'GBP - British Pound' => 'GBP - британÑки паунд', + 'INR - Indian Rupee' => 'INR - индийÑка рупиÑ', + 'JPY - Japanese Yen' => 'JPY - ÑпонÑка йена', + 'NZD - New Zealand Dollar' => 'NZD - Ðова Ð—ÐµÐ»Ð°Ð½Ð´Ð¸Ñ Ð´Ð¾Ð»Ð°Ñ€', + 'PEN - Peruvian Sol' => 'PEN - ПеруанÑки Ñол', + 'RSD - Serbian dinar' => 'RSD - ÑръбÑки динар', + 'CNY - Chinese Yuan' => 'CNY - китайÑки юан', + 'USD - US Dollar' => 'USD - щатÑки долар', + 'VES - Venezuelan Bolívar' => 'VES - ВенецуелÑки боливар', + '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' => 'Време, прекарано в колоната', + '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.' => 'Този отчет Ñъдържа вÑички Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð½Ð° колоните за вÑÑка задача Ñ Ð´Ð°Ñ‚Ð°Ñ‚Ð°, Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ Ð¸ времето, прекарано за вÑеки преход.', + 'Currency rates' => 'Валутни курÑове', + 'Rate' => 'Оценка', + 'Change reference currency' => 'ПромÑна на референтната валута', + 'Reference currency' => 'Референтна валута', + 'The currency rate has been added successfully.' => 'Ð’Ð°Ð»ÑƒÑ‚Ð½Ð¸Ñ ÐºÑƒÑ€Ñ Ðµ добавен уÑпешно.', + 'Unable to add this currency rate.' => 'Ðе може да Ñе добави този валутен курÑ.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s премахна Ð¸Ð·Ð¿ÑŠÐ»Ð½Ð¸Ñ‚ÐµÐ»Ñ Ð½Ð° задачата %s', + 'Information' => 'ИнформациÑ', + 'Check two factor authentication code' => 'Проверете Ð´Ð²ÑƒÑ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¸Ñ ÐºÐ¾Ð´ за удоÑтоверÑване', + 'The two factor authentication code is not valid.' => 'Ð”Ð²ÑƒÑ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¸Ñ ÐºÐ¾Ð´ за удоÑтоверÑване не е валиден.', + 'The two factor authentication code is valid.' => 'Ð”Ð²ÑƒÑ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð¸Ñ ÐºÐ¾Ð´ за удоÑтоверÑване е валиден.', + 'Code' => 'Код', + 'Two factor authentication' => 'ДвуÑтепенно удоÑтоверÑване', + 'This QR code contains the key URI: ' => 'Този QR код Ñъдържа ÐºÐ»ÑŽÑ‡Ð¾Ð²Ð¸Ñ URI адреÑ:', + 'Check my code' => 'Проверка на кода ми', + 'Secret key: ' => 'Таен ключ: ', + 'Test your device' => 'ТеÑтвайте уÑтройÑтвото Ñи', + 'Assign a color when the task is moved to a specific column' => 'Задаване на цвÑÑ‚ при премеÑтване на задачата в конкретна колона', + '%s via Kanboard' => '%s чрез Kanboard', + 'Burndown chart' => 'Burndown chart', + '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 - шведÑка крона', + 'Identifier' => 'Идентификатор', + 'Disable two factor authentication' => 'Деактивиране на двуÑтепенно удоÑтоверÑване', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'ÐаиÑтина ли иÑкате да деактивирате двуфакторното удоÑтоверÑване за този потребител: "%s"?', + 'Edit link' => 'Редактиране на връзка', + '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' => 'Планирано е генериране на повтарÑща Ñе задача', + 'Score' => 'Резултат', + 'The identifier must be unique' => 'Идентификатора трÑбва да бъде уникален', + 'This linked task id doesn\'t exists' => 'Този Ñвързан идентификатор на задача не ÑъщеÑтвува', + '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' => 'Срок за изчиÑлÑване на нов краен Ñрок', + '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: ' => 'Фактор за изчиÑлÑване на нов краен Ñрок: ', + 'Month(s)' => 'МеÑец(и)', + 'This task has been created by: ' => 'Тази задача е Ñъздадена от: ', + 'Recurrent task has been generated:' => 'Генерирана е повтарÑща Ñе задача:', + 'Timeframe to calculate new due date: ' => 'Срок за изчиÑлÑване на нов краен Ñрок: ', + 'Trigger to generate recurrent task: ' => 'ДейÑтвие за генериране на повтарÑща Ñе задача:', + 'When task is closed' => 'Когато задачата е затворена', + 'When task is moved from first column' => 'Когато задачата е премеÑтена от първата колона', + 'When task is moved to last column' => 'Когато задачата е премеÑтена в поÑледната колона', + 'Year(s)' => 'година(и)', + 'Project settings' => 'ÐаÑтройки на проекта', + 'Automatically update the start date' => 'Ðвтоматично актуализиране на началната дата', + 'iCal feed' => 'iCal Feed', + 'Preferences' => 'ПредпочитаниÑ', + 'Security' => 'Защита', + 'Two factor authentication disabled' => 'Двуфакторното удоÑтоверÑване е деактивирано', + 'Two factor authentication enabled' => 'Ðктивирано е двуфакторно удоÑтоверÑване', + 'Unable to update this user.' => 'ÐеуÑпешно актуализиране на този потребител.', + 'There is no user management for personal 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' => 'ПремеÑтване на задачата в друга колона при промÑна на категориÑ', + 'Send a task by email to someone' => 'Изпращане на задача по имейл до нÑкого', + 'Reopen a task' => 'Повторно отварÑне на задача', + 'Notification' => 'УведомÑване', + '%s moved the task #%d to the first swimlane' => '%s премеÑти задачата #%d на първи коридор', + 'Swimlane' => 'Коридор', + '%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.' => 'Този отчет Ñъдържа цÑлата Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð·Ð° задачите за Ð´Ð°Ð´ÐµÐ½Ð¸Ñ Ð¿ÐµÑ€Ð¸Ð¾Ð´ от време.', + 'Project activities for %s' => 'Проектни дейноÑти за %s', + 'view the board on Kanboard' => 'преглед на дъÑката на Kanboard', + 'The task has been moved to the first swimlane' => 'Задачата е премеÑтена на първи коридор', + 'The task has been moved to another swimlane:' => '- Задачата е премеÑтена в друг коридор:', + 'New title: %s' => 'Ðово заглавие: %s', + 'The task is not assigned anymore' => 'Задачата вече не е възложена', + 'New assignee: %s' => 'Ðов изпълнител: %s', + 'There is no category now' => 'Вече нÑма категориÑ', + 'New category: %s' => 'Ðова категориÑ: %s', + 'New color: %s' => 'Ðов цвÑÑ‚: %s', + 'New complexity: %d' => 'Ðова ÑложноÑÑ‚: %d', + 'The due date has been removed' => 'ÐšÑ€Ð°Ð¹Ð½Ð¸Ñ Ñрок е премахнат', + 'There is no description anymore' => 'Вече нÑма опиÑание', + 'Recurrence settings has been modified' => 'ÐаÑтройките за повторение Ñа променени', + 'Time spent changed: %sh' => 'Времето, прекарано в работа, Ñе промени: %sч', + 'Time estimated changed: %sh' => 'Променено очаквано време: %sч', + 'The field "%s" has 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:' => 'ИÑкам да получавам извеÑÑ‚Ð¸Ñ Ð·Ð°:', + 'All tasks' => 'Ð’Ñички задачи', + 'Only for tasks assigned to me' => 'Само за задачи, които Ñа ми възложени', + 'Only for tasks created by me' => 'Само за задачи, Ñъздадени от мен', + 'Only for tasks created by me and tasks assigned to me' => 'Само за задачи, Ñъздадени от мен, и задачи, възложени на мен', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Общо за вÑички колони', + 'You need at least 2 days of data to show the chart.' => 'Ðеобходими Ñа данни за поне два дена, за да Ñе покаже тази диаграмата.', + '<15m' => '<15м', + '<30m' => '30m', + 'Stop timer' => 'Таймер за Ñпиране', + 'Start timer' => 'Старт таймер', + 'My activity stream' => 'Мой поток от дейноÑти', + 'Search tasks' => 'Задачи за търÑене', + 'Reset filters' => 'Ðулиране на филтрите', + 'My tasks due tomorrow' => 'Моите задачи, които изтичат утре', + 'Tasks due today' => 'Задачи, изтичащи днеÑ', + 'Tasks due tomorrow' => 'Задачи, изтичащи утре', + 'Tasks due yesterday' => 'Задачи, изтекли вчера', + 'Closed tasks' => 'Приключени задачи', + 'Open tasks' => 'Отворени задачи', + 'Not assigned' => 'Ðе е възложено на никого', + 'View advanced search syntax' => 'Преглед на ÑинтакÑиÑа за разширено търÑене', + 'Overview' => 'Преглед', + 'Board/Calendar/List view' => 'Изглед на табло/календар/ÑпиÑък', + 'Switch to the board view' => 'Превключване към изгледа на дъÑката', + 'Switch to the list view' => 'Превключване към изглед на ÑпиÑък', + 'Go to the search/filter box' => 'Към полето за търÑене/филтриране', + 'There is no activity yet.' => 'Ð’Ñе още нÑма активноÑÑ‚.', + 'No tasks found.' => 'ÐÑма намерени задачи', + 'Keyboard shortcut: "%s"' => 'Клавишна комбинациÑ: "%s"', + 'List' => 'СпиÑък', + 'Filter' => 'Филтър', + 'Advanced search' => 'Разширено търÑене', + 'Example of query: ' => 'Пример за заÑвка:', + 'Search by project: ' => 'ТърÑене по проект:', + 'Search by column: ' => 'ТърÑене по колона:', + 'Search by assignee: ' => 'ТърÑене по изпълнител:', + 'Search by color: ' => 'ТърÑене по цвÑÑ‚:', + 'Search by category: ' => 'ТърÑене по категориÑ', + 'Search by description: ' => 'ТърÑене по опиÑание:', + 'Search by due date: ' => 'ТърÑене по краен Ñрок:', + 'Average time spent in each column' => 'Средно време, прекарано във вÑÑка колона', + 'Average time spent' => 'Средно прекарано време', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Тази диаграма показва Ñредното време, прекарано във вÑÑка колона за поÑледните %d задачи.', + 'Average Lead and Cycle time' => 'Средно време за изпълнение и цикъл', + 'Average lead time: ' => 'Средно време за изпълнение:', + 'Average cycle time: ' => 'Средно време на цикъла:', + 'Cycle Time' => 'Време на цикъла', + 'Lead Time' => 'Време за изпълнение', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Тази диаграма показва Ñредното време за изпълнение и цикъл за поÑледните %d задачи във времето.', + 'Average time into each column' => 'Средно време във вÑÑка колона', + 'Lead and cycle time' => 'Време за изпълнение и цикъл', + 'Lead time: ' => 'Време за изпълнение:', + 'Cycle time: ' => 'Времетраене на цикъла:', + 'Time spent in 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 the start date automatically' => 'Ðвтоматично задаване на началната дата', + 'Edit Authentication' => 'Редактиране на удоÑтоверÑване', + 'Remote user' => 'Отдалечен потребител', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Отдалечените потребители не ÑъхранÑват паролата Ñи в базата данни на Kanboard, примери: LDAP, Google и Github акаунти.', + '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.' => 'ÐÑма наличен проект за меÑтоназначение.', + 'Trigger automatically subtask time tracking' => 'ЗадейÑтване на автоматично проÑледÑване на времето за подзадачи', + 'Include closed tasks in the cumulative flow diagram' => 'Включване на затворени задачи в диаграмата на ÐºÑƒÐ¼ÑƒÐ»Ð°Ñ‚Ð¸Ð²Ð½Ð¸Ñ Ð¿Ð¾Ñ‚Ð¾Ðº', + 'Current swimlane: %s' => 'Текущ коридор: %s', + 'Current column: %s' => 'Текуща колона: %s', + 'Current category: %s' => 'Текуща категориÑ: %s', + 'no category' => 'без категориÑ', + 'Current assignee: %s' => 'Текущ изпълнител: %s', + 'not assigned' => 'Ðе е зададено', + 'Author:' => 'Ðвтор', + 'contributors' => 'Сътрудници', + 'License:' => 'Лиценз:', + 'License' => 'Лиценз', + 'Enter the text below' => 'Въведете текÑта по-долу', + 'Start date:' => 'Ðачална дата:', + 'Due date:' => 'Краен Ñрок:', + 'People who are project managers' => 'Хора, които Ñа ръководители на проекти', + 'People who are project members' => 'Хора, които Ñа членове на проекта', + 'NOK - Norwegian Krone' => 'NOK - норвежка крона ', + 'Show this column' => 'Показване на тази колона', + 'Hide this column' => 'Скриване на тази колона', + 'End date' => 'Крайна дата', + 'Users overview' => 'Общ преглед на потребителите', + 'Members' => 'Членове', + 'Shared project' => 'Споделен проект', + 'Project managers' => 'Мениджъри на проекти', + 'Projects list' => 'СпиÑък Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð¸', + 'End date:' => 'Крайна дата:', + 'Change task color when using a specific task link' => 'ПромÑна на цвета на задачата при използване на конкретна връзка към задача', + 'Task link creation or modification' => 'Създаване или модифициране на връзка към задача', + 'Milestone' => 'Milestone', + 'Reset the search/filter box' => 'Ðулиране на полето за търÑене/филтриране', + 'Documentation' => 'ДокументациÑ', + 'Author' => 'Ðвтор', + 'Version' => 'ВерÑиÑ', + 'Plugins' => 'Плъгини', + 'There is no plugin loaded.' => 'ÐÑма зареден плъгин.', + 'My notifications' => 'Моите извеÑтиÑ', + 'Custom filters' => 'ПотребителÑки филтри', + 'Your custom filter has 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 has been updated successfully.' => 'Ð’Ð°ÑˆÐ¸Ñ Ð¿ÐµÑ€Ñонализиран филтър е актуализиран уÑпешно.', + 'Unable to update custom filter.' => 'ÐеуÑпешно актуализиране на перÑÐ¾Ð½Ð°Ð»Ð¸Ð·Ð¸Ñ€Ð°Ð½Ð¸Ñ Ñ„Ð¸Ð»Ñ‚ÑŠÑ€.', + 'Web' => 'Интернет Ñтраница', + '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', + 'New task #%d: %s' => 'Ðова задача #%d: %s', + 'Task updated #%d' => 'Задача #%d е актуализирана', + 'Task #%d closed' => 'Задача #%d е затворена', + 'Task #%d opened' => 'Задача #%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', + '%d overdue tasks' => '%d проÑрочени задачи', + 'No notification.' => 'ÐÑма извеÑтиÑ.', + '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:' => 'Ðачини за уведомÑване:', + 'Unable to read your file' => 'Файла не може да бъде прочетен.', + '%d task(s) have been imported successfully.' => '%d задача(и) Ñа импортирани уÑпешно.', + 'Nothing has been imported!' => 'Ðищо не е импортирано!', + 'Import users from CSV file' => 'Импортиране на потребители от CSV', + '%d user(s) have been imported successfully.' => '%d потребител(и) Ñа импортирани уÑпешно.', + 'Comma' => 'Запетайка', + 'Semi-colon' => 'Точка и запетаÑ', + 'Tab' => 'Tab', + 'Vertical bar' => 'Вертикална лента', + '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)' => 'ДобавÑне на филтър (вмеÑто замÑна)', + 'Append/Replace' => 'ДобавÑне/замÑна', + 'Append' => 'ДобавÑне', + 'Replace' => 'ЗамÑна', + 'Import' => 'Импорт', + 'Change sorting' => 'ПромÑна на Ñортирането', + 'Tasks Importation' => 'Импортиране на задачи', + 'Delimiter' => 'Разделител', + 'Enclosure' => 'ЗатварÑщ Ñимвол', + 'CSV File' => 'CSV файл', + 'Instructions' => 'ИнÑтрукции', + 'Your file must use the predefined CSV format' => 'Ð’Ð°ÑˆÐ¸Ñ Ñ„Ð°Ð¹Ð» трÑбва да използва предварително дефиниран CSV формат', + 'Your file must be encoded in UTF-8' => 'Ð’Ð°ÑˆÐ¸Ñ Ñ„Ð°Ð¹Ð» трÑбва да бъде кодиран в 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' => 'ÐšÑ€Ð°Ð¹Ð½Ð¸Ñ Ñрок трÑбва да използва ISO формат: ГГГГ-ММ-ДД', + 'Download CSV template' => 'ИзтеглÑне на CSV шаблон', + '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' => '%s прикачи нов файл към задачата %s', + 'Link type' => 'Вид на връзката', + 'Assign automatically a category based on a link' => 'Ðвтоматично приÑвоÑване на ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ð²ÑŠÐ· оÑнова на връзка', + 'BAM - Konvertible Mark' => 'BAM - Конвертируема марка', + 'Assignee Username' => 'ПотребителÑко име на изпълнител', + 'Assignee Name' => 'Име на изпълнител', + 'Groups' => 'Групи', + 'Members of %s' => 'Членове на %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"' => 'ДобавÑне на групов член към "%s"', + 'Group member added successfully.' => 'УÑпешно добавен член на група.', + 'Unable to add group member.' => 'ÐеуÑпешно добавÑне на групов член.', + 'Remove user from group "%s"' => 'Премахни Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ Ð¾Ñ‚ група "%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' => 'Ð’Ð°ÑˆÐ¸Ñ Ð°ÐºÐ°ÑƒÐ½Ñ‚ е заключен за %d минути', + 'Invalid captcha' => 'Ðевалиден Captcha', + 'The name must be unique' => 'Името трÑбва да е уникално', + 'View all groups' => 'Преглед на вÑички групи', + 'There is no user available.' => 'ÐÑма наличен потребител.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ "%s" от групата "%s"?', + 'There is no group.' => 'ÐÑма група.', + 'Add group member' => 'ДобавÑне на член на групата', + 'Do you really want to remove this group: "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете тази група: "%s"?', + 'There is no user in this group.' => 'ÐÑма потребител в тази група.', + 'Permissions' => 'Права', + 'Allowed Users' => 'Разрешени потребители', + 'No specific user has been allowed.' => 'Ðе е разрешен конкретен потребител.', + 'Role' => 'РолÑ', + 'Enter user name...' => 'Въведи потребителÑко име', + 'Allowed Groups' => 'Разрешени групи', + 'No group has been allowed.' => 'ÐÐ¸ÐºÐ¾Ñ Ð³Ñ€ÑƒÐ¿Ð° не е разрешена.', + 'Group' => 'Група', + 'Group Name' => 'Име на група', + 'Enter group name...' => 'Въведете име на групата...', + 'Role:' => 'РолÑ:', + 'Project members' => 'Членове на проекта', + '%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', + 'Estimated hours: ' => 'Очаквани чаÑове:', + 'Actual hours: ' => 'ДейÑтвително работно време:', + 'Hours Spent' => 'Прекарани чаÑове', + 'Hours Estimated' => 'Очаквани чаÑове', + 'Estimated Time' => 'Прогнозен чаÑ', + 'Actual Time' => 'ДейÑтвително време', + 'Estimated vs actual time' => 'Очаквано ÑпрÑмо дейÑтвително време', + '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' => 'Ðлгоритъм за еднократна парола, базиран на време', + '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?' => 'Забравена парола', + 'Enable "Forget Password"' => 'Ðктивиране на "Забравена парола"', + 'Password Reset' => ' ВъзÑтановÑване на парола', + 'New password' => 'Ðова парола', + 'Change Password' => 'СмÑна на паролата', + 'To reset your password click on this link:' => 'За да нулирате паролата Ñи, щракнете върху тази връзка:', + 'Last Password Reset' => 'Ðулиране на поÑледната парола', + 'The password has never been reinitialized.' => 'Паролата никога не е била инициализирана отново.', + 'Creation' => 'Създаване', + 'Expiration' => 'Изтича на', + 'Password reset history' => 'Ð¥Ñ€Ð¾Ð½Ð¾Ð»Ð¾Ð³Ð¸Ñ Ð½Ð° нулирането на паролата', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Ð’Ñички задачи на колоната "%s" и коридор "%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.' => '%d задача(и) в колоната "%s" и коридор "%s" ще бъдат затворени.', + 'Close all tasks in this column and this swimlane' => 'Затворете вÑички задачи в тази колона и този коридор', + '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.' => 'Идентификатора на проекта не е задължителен и трÑбва да бъде буквено-цифров, например: MYPROJECT.', + 'Project owner' => 'СобÑтвеник на проекта', + 'Personal projects do not have users and groups management.' => 'Личните проекти нÑмат управление на потребители и групи.', + 'There is no project member.' => 'ÐÑма член на проекта.', + 'Priority' => 'Priority', + 'Task priority' => 'Приоритет на задачата', + 'General' => 'Общи', + 'Dates' => 'Дати', + 'Default priority' => 'Приоритет по подразбиране', + 'Lowest priority' => 'Ðай-ниÑÐºÐ¸Ñ Ð¿Ñ€Ð¸Ð¾Ñ€Ð¸Ñ‚ÐµÑ‚', + 'Highest priority' => 'Ðай-виÑÐ¾ÐºÐ¸Ñ Ð¿Ñ€Ð¸Ð¾Ñ€Ð¸Ñ‚ÐµÑ‚', + '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' => 'Прикачен файл', + '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' => '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' => '%s чаÑове', + 'Time spent:' => 'Прекарано време:', + 'Created:' => 'Създадено:', + 'Modified:' => 'Модифицирано:', + 'Completed:' => 'Завършено:', + 'Started:' => 'Стартирано:', + 'Moved:' => 'ПремеÑтено:', + 'Task #%d' => 'Задача #%d', + 'Time format' => 'Времеви формат', + 'Start date: ' => 'Ðачална дата:', + 'End date: ' => 'Крайна дата:', + 'New due date: ' => 'Ðов краен Ñрок:', + 'Start date changed: ' => 'Ðачалната дата е променена:', + 'Disable personal projects' => 'Деактивиране на лични проекти', + 'Do you really want to remove this custom filter: "%s"?' => 'ÐаиÑтина ли иÑкате да премахнете този перÑонализиран филтър: "%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.' => 'Ð’Ñички файлове Ñа качени уÑпешно.', + 'The maximum allowed file size is %sB.' => 'МакÑÐ¸Ð¼Ð°Ð»Ð½Ð¸Ñ Ñ€Ð°Ð·Ñ€ÐµÑˆÐµÐ½ размер на файла е %sB.', + 'Drag and drop your files here' => 'Плъзнете и пуÑнете файловете Ñи тук', + 'choose files' => 'Изберете файлове', + 'View profile' => 'Виж профил', + 'Two Factor' => 'Двуфакторен', + 'Disable user' => 'Деактивиране на потребител', + 'Do you really want to disable this user: "%s"?' => 'ÐаиÑтина ли иÑкате да деактивирате този потребител: "%s"?', + 'Enable user' => 'Ðктивиране на потребител', + 'Do you really want to enable this user: "%s"?' => 'ÐаиÑтина ли иÑкате да активирате този потребител: "%s"?', + 'Download' => 'Изтегли', + 'Uploaded: %s' => 'Качено: %s', + 'Size: %s' => 'Размер: %s', + 'Uploaded by %s' => 'Качено от %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' => 'Тази ÑтойноÑÑ‚ трÑбва да е по-голÑма от %d', + 'Another swimlane with the same name exists in the project' => 'Ð’ проекта ÑъщеÑтвува друг коридор ÑÑŠÑ Ñъщото име', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Пример: https://example.kanboard.org/ (използва Ñе за генериране на абÑолютни URL адреÑи)', + '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:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'ВерÑÐ¸Ñ Ð½Ð° операционната ÑиÑтема:', + 'Database version:' => 'ВерÑÐ¸Ñ Ð½Ð° базата данни:', + 'Browser:' => 'Браузър:', + 'Task view' => 'Изглед на задачите', + 'Edit task' => 'Редактиране на задача', + 'Edit description' => 'Редактиране на опиÑанието', + 'New internal link' => 'Ðова вътрешна връзка', + 'Display list of keyboard shortcuts' => 'Показване на ÑпиÑък Ñ ÐºÐ»Ð°Ð²Ð¸ÑˆÐ½Ð¸ комбинации', + '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' => 'Маркер за уебкука', + '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 a 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 has 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', + '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', + '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' => 'ИзходÑщи имейли', + '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 покани.', + '%d invitation was sent.' => 'Изпратена е %d покана.', + '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.' => 'Поканени Ñте да Ñе региÑтрирате в 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' => 'Това потребителÑко име вече е заето', + 'Your profile must have a valid email address.' => 'Ð’Ð°ÑˆÐ¸Ñ Ð¿Ñ€Ð¾Ñ„Ð¸Ð» трÑбва да има валиден имейл адреÑ.', + 'TRL - Turkish Lira' => 'TRL - ТурÑка лира', + 'The project email is optional and could be used by several plugins.' => 'Имейла на проекта не е задължителен и може да Ñе използва от нÑколко плъгина.', + 'The project email must be unique across all projects' => 'Имейла на проекта трÑбва да бъде уникален за вÑички проекти', + 'The email configuration has been disabled by the administrator.' => 'КонфигурациÑта на имейла е деактивирана от админиÑтратора.', + 'Close this project' => 'ЗатварÑне на този проект', + 'Open this project' => 'ОтварÑне на този проект', + 'Close a project' => 'ЗатварÑне на проект', + 'Do you really want to close this project: "%s"?' => 'ÐаиÑтина ли иÑкате да затворите този проект: "%s"?', + 'Reopen a project' => 'Повторно отварÑне на проект', + 'Do you really want to reopen this project: "%s"?' => 'ÐаиÑтина ли иÑкате да отворите отново този проект: "%s"?', + 'This project is open' => 'Този проект е отворен', + 'This project is closed' => 'Този проект е затворен', + 'Unable to upload files, check the permissions of your data folder.' => 'Файловете не могат да бъдат качени, проверете правата за вашата папка Ñ Ð´Ð°Ð½Ð½Ð¸.', + 'Another category with the same name exists in this project' => 'Друга ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ ÑÑŠÑ Ñъщото име ÑъщеÑтвува в този проект', + 'Comment sent by email successfully.' => 'Коментара е изпратен уÑпешно по имейл.', + 'Sent by email to "%s" (%s)' => 'Изпратено по имейл на "%s" (%s)', + 'Unable to read uploaded file.' => 'ÐšÐ°Ñ‡ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð» не може да бъде прочетен.', + 'Database uploaded successfully.' => 'Базата данни е качена уÑпешно.', + 'Task sent by email successfully.' => 'Задачата е изпратена уÑпешно по имейл.', + 'There is no category in this project.' => 'Ð’ този проект нÑма категориÑ.', + 'Send by email' => 'Изпращане по поща', + 'Create and send a comment by email' => 'Създаване и изпращане на коментар по имейл', + 'Subject' => 'Тема', + 'Upload the database' => 'Качване на базата данни', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Можете да качите изтеглената по-рано база данни на Sqlite (формат gzip).', + 'Database file' => 'Файл бази данни', + 'Upload' => 'Качване', + 'Your project must have at least one active swimlane.' => 'Ð’Ð°ÑˆÐ¸Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚ трÑбва да има поне един активен коридор.', + 'Project: %s' => 'Проект: %s', + 'Automatic action not found: "%s"' => 'Ðвтоматичното дейÑтвие не е намерено: "%s"', + '%d projects' => '%d проекта', + '%d project' => '%d проект', + 'There is no project.' => 'ÐÑма проект.', + 'Sort' => 'Сортиране', + 'Project ID' => 'ID на проект', + 'Project name' => 'Име на проект', + 'Public' => 'Публичен', + 'Personal' => 'Личен', + '%d tasks' => '%d задачи', + '%d task' => '%d задача', + 'Task ID' => 'ID на задачата', + 'Assign automatically a color when due date is expired' => 'Ðвтоматично задаване на цвÑÑ‚, когато ÐºÑ€Ð°Ð¹Ð½Ð¸Ñ Ñрок е изтекъл', + 'Total score in this column across all swimlanes' => 'Общ резултат в тази колона за вÑички коридори', + 'HRK - Kuna' => 'HRK - Куна', + 'ARS - Argentine Peso' => 'ARS - аржентинÑко пеÑо', + 'COP - Colombian Peso' => 'COP - колумбийÑко пеÑо', + '%d groups' => '%d групи', + '%d group' => '%d група', + 'Group ID' => 'Групово ID', + 'External ID' => 'Външно ID', + '%d users' => '%d потребители', + '%d user' => '%d потребител', + 'Hide subtasks' => 'Скриване на подзадачите', + 'Show subtasks' => 'Показване на подзадачи', + 'Authentication Parameters' => 'Параметри за удоÑтоверÑване', + 'API Access' => 'API доÑтъп ', + 'No users found.' => 'Ðе Ñа намерени потребители.', + 'User ID' => 'ID на потребителÑ', + 'Notifications are activated' => 'ИзвеÑтиÑта Ñа активирани', + 'Notifications are disabled' => 'ИзвеÑтиÑта Ñа деактивирани', + 'User disabled' => 'ÐŸÐ¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ Ðµ деактивиран', + '%d notifications' => '%d извеÑтиÑ', + '%d notification' => '%d извеÑтие', + 'There is no external integration installed.' => 'ÐÑма инÑталирана външна интеграциÑ.', + 'You are not allowed to update tasks assigned to someone else.' => 'ÐÑмате право да актуализирате задачите, възложени на нÑкой друг.', + 'You are not allowed to change the assignee.' => 'ÐÑмате право да променÑте изпълнителÑ.', + 'Task suppression is not permitted' => 'ПотиÑкането на задачи не е разрешено', + 'Changing assignee is not permitted' => 'ПромÑната на изпълнител не е разрешена', + 'Update only assigned tasks is permitted' => 'Ðктуализирането Ñамо на възложените задачи е разрешено', + 'Only for tasks assigned to the current user' => 'Само за задачи, възложени на Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»', + 'My projects' => 'Моите проекти', + 'You are not a member of any project.' => 'Вие не Ñте член на нито един проект.', + 'My subtasks' => 'Моите подзадачи', + '%d subtasks' => '%d подзадачи', + '%d subtask' => '%d подзадача', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Разрешено е Ñамо премеÑтване на задача между тези колони за задачи, възложени на Ñ‚ÐµÐºÑƒÑ‰Ð¸Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»', + '[DUPLICATE]' => '[ДУБЛИРÐÐО]', + 'DKK - Danish Krona' => 'DKK - датÑка крона', + 'Remove user from group' => 'Премахни Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ»Ñ Ð¾Ñ‚ групата', + 'Assign the task to its creator' => 'Възлагане на задачата на Ð½ÐµÐ¹Ð½Ð¸Ñ Ñъздател', + 'This task was sent by email to "%s" with subject "%s".' => 'Тази задача беше изпратена по имейл на "%s" Ñ Ñ‚ÐµÐ¼Ð° "%s".', + 'Predefined Email Subjects' => 'Предварително дефинирани теми на имейл', + 'Write one subject by line.' => 'Ðапишете по една тема на ред.', + 'Create another link' => 'Създаване на друга връзка', + 'BRL - Brazilian Real' => 'BRL - БразилÑки Реал', + 'Add a new Kanboard task' => 'ДобавÑне на нова задача в Kanboard', + 'Subtask not started' => 'Подзадачата не е Ñтартирана', + 'Subtask currently in progress' => 'Подзадачата Ñе изпълнÑва в момента', + 'Subtask completed' => 'Подзадачата е изпълнена', + 'Subtask added successfully.' => 'Подзадачата е добавена уÑпешно.', + '%d subtasks added successfully.' => '%d подзадачи Ñа добавени уÑпешно.', + 'Enter one subtask by line.' => 'Въведете една подзадача по ред.', + 'Predefined Contents' => 'Предварително определено Ñъдържание', + 'Predefined contents' => 'Предварително определено Ñъдържание', + 'Predefined Task Description' => 'Предварително дефинирано опиÑание на задачата', + 'Do you really want to remove this template? "%s"' => 'ÐаиÑтина ли иÑкате да премахнете този шаблон? "%s"', + 'Add predefined task description' => 'ДобавÑне на предварително дефинирано опиÑание на задачата', + 'Predefined Task Descriptions' => 'Предварително дефинирани опиÑÐ°Ð½Ð¸Ñ Ð½Ð° задачите', + 'Template created successfully.' => 'Шаблона е Ñъздаден уÑпешно.', + 'Unable to create this template.' => 'Този шаблон не може да бъде Ñъздаден.', + 'Template updated successfully.' => 'Шаблона е актуализиран уÑпешно.', + 'Unable to update this template.' => 'Този шаблон не може да бъде актуализиран.', + 'Template removed successfully.' => 'Шаблона е премахнат уÑпешно.', + 'Unable to remove this template.' => 'Този шаблон не може да бъде премахнат.', + 'Template for the task description' => 'Образец за опиÑание на задачата', + 'The start date is greater than the end date' => 'Ðачалната дата е по-голÑма от крайната дата', + 'Tags must be separated by a comma' => 'Етикетите трÑбва да бъдат разделени ÑÑŠÑ Ð·Ð°Ð¿ÐµÑ‚Ð°Ñ', + 'Only the task title is required' => 'ИзиÑква Ñе Ñамо заглавието на задачата', + 'Creator Username' => 'ПотребителÑко име на ÑъздателÑ', + 'Color Name' => 'Име на цвета', + 'Column Name' => 'Име на колона', + 'Swimlane Name' => 'Име на коридор', + 'Time Estimated' => 'Очаквано време', + 'Time Spent' => 'Прекарано време', + 'External Link' => 'Външен линк', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Тази Ñ„ÑƒÐ½ÐºÑ†Ð¸Ñ Ð¿Ð¾Ð·Ð²Ð¾Ð»Ñва емиÑÐ¸Ñ iCal, RSS емиÑÐ¸Ñ Ð¸ Ð¿ÑƒÐ±Ð»Ð¸Ñ‡Ð½Ð¸Ñ Ð¸Ð·Ð³Ð»ÐµÐ´ на дъÑката.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Спиране на таймера на вÑички подзадачи, когато премеÑтвате задача в друга колона', + 'Subtask Title' => 'Заглавие на подзадача', + 'Add a subtask and activate the timer when moving a task to another column' => 'ДобавÑне на подзадача и активиране на таймера при премеÑтване на задача в друга колона', + 'days' => 'дни', + 'minutes' => 'минути', + 'seconds' => 'Ñекунди', + 'Assign automatically a color when preset start date is reached' => 'Ðвтоматично задаване на цвÑÑ‚ при доÑтигане на предварително зададена начална дата', + 'Move the task to another column once a predefined start date is reached' => 'ПремеÑтване на задачата в друга колона Ñлед доÑтигане на предварително определена начална дата', + 'This task is now linked to the task %s with the relation "%s"' => 'Тази задача Ñега е Ñвързана ÑÑŠÑ Ð·Ð°Ð´Ð°Ñ‡Ð°Ñ‚Ð° %s Ñ Ð²Ñ€ÑŠÐ·ÐºÐ° "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Връзката Ñ Ñ€ÐµÐ»Ð°Ñ†Ð¸Ñта "%s" към задачата %s е премахната', + 'Custom Filter:' => 'ПерÑонализиран филтър:', + 'Unable to find this group.' => 'Тази група не може да бъде намерена.', + '%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 moved the task #%d to the swimlane "%s"' => '%s премеÑти задачата #%d в коридор "%s"', + '%sh spent' => '%sч използвано', + '%sh estimated' => '%sч очаквано', + 'Select All' => 'Избери вÑички', + 'Unselect All' => 'Отмаркирай вÑички', + 'Apply action' => 'Приложи', + 'Move selected tasks to another column or swimlane' => 'ПремеÑтване на избраните задачи в друг коридор', + 'Edit tasks in bulk' => 'Групово редактиране на задачи', + 'Choose the properties that you would like to change for the selected tasks.' => 'Изберете ÑвойÑтвата, които иÑкате да промените за избраните задачи.', + 'Configure this project' => 'Конфигуриране на този проект', + 'Start now' => 'Започнете Ñега', + '%s removed a file from the task #%d' => '%s премахна файл от задачата #%d', + 'Attachment removed from task #%d: %s' => 'ÐŸÑ€Ð¸ÐºÐ°Ñ‡ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð» е премахнат от задача #%d: %s', + 'No color' => 'Без цвÑÑ‚', + 'Attachment removed "%s"' => 'ÐŸÑ€Ð¸ÐºÐ°Ñ‡ÐµÐ½Ð¸Ñ Ñ„Ð°Ð¹Ð» е премахнат "%s"', + '%s removed a file from the task %s' => '%s премахна файл от задачата %s', + 'Move the task to another swimlane when assigned to a user' => 'ПремеÑтване на задачата в друг коридор, когато е възложена на потребител', + 'Destination swimlane' => 'ДеÑтинационен коридор', + 'Assign a category when the task is moved to a specific swimlane' => 'Задаване на категориÑ, когато задачата бъде премеÑтена в конкретен коридор', + 'Move the task to another swimlane when the category is changed' => 'ПремеÑтване на задачата в друг коридор при промÑна на категориÑта', + 'Reorder this column by priority (ASC)' => 'Пренареждане на тази колона по приоритет (ВЪЗХОДЯЩ)', + 'Reorder this column by priority (DESC)' => 'Пренареждане на тази колона по приоритет (ÐИЗХОДЯЩ)', + 'Reorder this column by assignee and priority (ASC)' => 'Пренареждане на тази колона по изпълнител и приоритет (ВЪЗХОДЯЩ)', + 'Reorder this column by assignee and priority (DESC)' => 'Пренареждане на тази колона по изпълнител и приоритет (ÐИЗХОДЯЩ)', + 'Reorder this column by assignee (A-Z)' => 'Пренареждане на тази колона по изпълнител (Ð-Я)', + 'Reorder this column by assignee (Z-A)' => 'Пренареждане на тази колона по изпълнител (Я-Ð)', + 'Reorder this column by due date (ASC)' => 'Пренареждане на тази колона по краен Ñрок (ВЪЗХОДЯЩ)', + 'Reorder this column by due date (DESC)' => 'Пренареждане на тази колона по краен Ñрок (ÐИЗХОДЯЩ)', + 'Reorder this column by id (ASC)' => 'Пренареждане на тази колона по ID (ВЪЗХОДЯЩ)', + 'Reorder this column by id (DESC)' => 'Пренареждане на тази колона по ID (ÐИЗХОДЯЩ)', + '%s moved the task #%d "%s" to the project "%s"' => '%s премеÑти задачата #%d "%s" в проекта "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Задача #%d "%s" е премеÑтена в проекта "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'ПремеÑтване на задачата в друга колона, когато ÐºÑ€Ð°Ð¹Ð½Ð¸Ñ Ñрок изтича Ñлед по-малко от определен брой дни', + 'Automatically update the start date when the task is moved away from a specific column' => 'Ðвтоматично актуализиране на началната дата, когато задачата Ñе премеÑтва от определена колона', + 'HTTP Client:' => 'HTTP клиент:', + 'Assigned' => 'Възложено', + 'Task limits apply to each swimlane individually' => 'ОграничениÑта на задачите Ñе прилагат за вÑеки коридор поотделно', + 'Column task limits apply to each swimlane individually' => 'ОграничениÑта на задачите в колоните да Ñе прилагат за вÑеки коридор поотделно', + 'Column task limits are applied to each swimlane individually' => 'ОграничениÑта на задачите в колоните Ñе прилагат за вÑеки коридор поотделно', + 'Column task limits are applied across swimlanes' => 'ОграничениÑта на задачите в колони Ñе прилагат върху вÑички коридори', + 'Task limit: ' => 'Лимит на задачите:', + 'Change to global tag' => 'ПромÑна на глобален етикет', + 'Do you really want to make the tag "%s" global?' => 'ÐаиÑтина ли иÑкате да направите етикета "%s" глобален?', + 'Enable global tags for this project' => 'Ðктивиране на глобални етикети за този проект', + 'Group membership(s):' => 'ЧленÑтво(а) в група(и):', + '%s is a member of the following group(s): %s' => '%s е член на Ñледната(ите) група(и): %s', + '%d/%d group(s) shown' => 'Показани Ñа %d/%d групи', + 'Subtask creation or modification' => 'Създаване или модифициране на подзадачи', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Задайте задачата на конкретен потребител, когато задачата Ñе премеÑти на конкретен коридор', + 'Comment' => 'Забележка', + 'Collapse vertically' => 'Свиване вертикално', + 'Expand vertically' => 'Разгъване вертикално', + 'MXN - Mexican Peso' => 'MXN - МекÑиканÑко пеÑо', + 'Estimated vs actual time per column' => 'Очаквано ÑпрÑмо дейÑтвително време по колона', + 'HUF - Hungarian Forint' => 'HUF - унгарÑки форинт', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'ТрÑбва да изберете файл за качване като ваша профилна Ñнимка!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'ÐšÐ°Ñ‡ÐµÐ½Ð¸Ñ Ð¾Ñ‚ Ð’Ð°Ñ Ñ„Ð°Ð¹Ð» не е валидно изображение! (Разрешени Ñа Ñамо *.gif, *.jpg, *.jpeg и *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Ðвтоматично задаване на краен Ñрок, когато задачата Ñе премеÑтва от конкретна колона', + 'No other projects found.' => 'Ðе Ñа намерени други проекти.', + 'Tasks copied successfully.' => 'Задачите Ñа копирани уÑпешно.', + 'Unable to copy tasks.' => 'Задачите не могат да бъдат копирани.', + 'Theme' => 'Тема', + 'Theme:' => 'Тема:', + 'Light theme' => 'Светла тема', + 'Dark theme' => 'Тъмна тема', + 'Automatic theme - Sync with system' => 'Ðвтоматична тема - Синхронизиране ÑÑŠÑ ÑиÑтемата', + 'Application managers or more' => 'Мениджъри на приложението или повече', + 'Administrators' => 'ÐдминиÑтратори', + 'Visibility:' => 'ВидимоÑÑ‚:', + 'Standard users' => 'Стандартни потребители', + 'Visibility is required' => 'ВидимоÑтта е задължителна', + 'The visibility should be an app role' => 'ВидимоÑтта трÑбва да бъде Ñ€Ð¾Ð»Ñ Ð½Ð° приложението', + 'Reply' => 'Отговор', + '%s wrote: ' => '%s напиÑа: ', + 'Number of visible tasks in this column and swimlane' => 'Брой видими задачи в тази колона и лента', + 'Number of tasks in this swimlane' => 'Брой задачи в тази лента', + 'Unable to find another subtask in progress, you can close this window.' => 'Ðе може да бъде намерена друга подзадача в процеÑ, можете да затворите този прозорец.', + 'This theme is invalid' => 'Тази тема е невалидна', + 'This role is invalid' => 'Тази Ñ€Ð¾Ð»Ñ Ðµ невалидна', + 'This timezone is invalid' => 'Тази чаÑова зона е невалидна', + 'This language is invalid' => 'Този език е невалиден', + 'This URL is invalid' => 'Този URL е невалиден', + 'Date format invalid' => 'Ðевалиден формат на дата', + 'Time format invalid' => 'Ðевалиден формат на чаÑ', + 'Invalid Mail transport' => 'Ðевалиден пощенÑки транÑпорт', + 'Color invalid' => 'Ðевалиден цвÑÑ‚', + 'This value must be greater or equal to %d' => 'Тази ÑтойноÑÑ‚ трÑбва да бъде по-голÑма или равна на %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Добавете BOM в началото на файла (необходимо за Microsoft Excel)', + 'Just add these tag(s)' => 'Добавете Ñамо тези етикети', + 'Remove internal link(s)' => 'Премахнете вътрешните връзки', + 'Import tasks from another project' => 'Импортирайте задачи от друг проект', + 'Select the project to copy tasks from' => 'Изберете проект, от който да копирате задачи', + 'The total maximum allowed attachments size is %sB.' => 'ОбщиÑÑ‚ макÑимален разрешен размер на прикачените файлове е %sB.', + 'Add attachments' => 'Добавете прикачени файлове', + 'Task #%d "%s" is overdue' => 'Задача #%d "%s" е Ñ Ð¸Ð·Ñ‚ÐµÐºÑŠÐ» Ñрок', + 'Enable notifications by default for all new users' => 'Ðктивиране на извеÑÑ‚Ð¸Ñ Ð¿Ð¾ подразбиране за вÑички нови потребители', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Ðазначаване на задачата на Ð½ÐµÐ¹Ð½Ð¸Ñ Ñъздател за определени колони, ако нÑма ръчно зададен изпълнител', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Ðазначаване на задачата на Ð²Ð»ÐµÐ·Ð»Ð¸Ñ Ð¿Ð¾Ñ‚Ñ€ÐµÐ±Ð¸Ñ‚ÐµÐ» при премеÑтване в определена колона, ако нÑма назначен потребител', +]; diff --git a/app/Locale/bs_BA/translations.php b/app/Locale/bs_BA/translations.php new file mode 100644 index 0000000..7714ca7 --- /dev/null +++ b/app/Locale/bs_BA/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'None', + 'Edit' => 'Uredi', + 'Remove' => 'Ukloni', + 'Yes' => 'Da', + 'No' => 'Ne', + 'cancel' => 'odustani', + 'or' => 'ili', + 'Yellow' => 'Žuta', + 'Blue' => 'Plava', + 'Green' => 'Zelena', + 'Purple' => 'LjubiÄasta', + 'Red' => 'Crvena', + 'Orange' => 'Narandžasta', + 'Grey' => 'Siva', + 'Brown' => 'SmeÄ‘a', + 'Deep Orange' => 'Tamno narandžasta', + 'Dark Grey' => 'Tamno siva', + 'Pink' => 'Roze', + 'Teal' => 'Tirkizna', + 'Cyan' => 'Zelenkasto plava', + 'Lime' => 'Žućkasto zelena', + 'Light Green' => 'Svijetlo zelena', + 'Amber' => 'Ćilibarska', + 'Save' => 'SaÄuvaj', + 'Login' => 'Prijava', + 'Official website:' => 'ZvaniÄna stranica:', + 'Unassigned' => 'Nedodijeljen', + 'View this task' => 'Pregledaj zadatak', + 'Remove user' => 'Ukloni korisnika', + 'Do you really want to remove this user: "%s"?' => 'Da li zaista želiÅ¡ da ukloniÅ¡ korisnika: "%s"?', + 'All users' => 'Svi korisnici', + 'Username' => 'KorisniÄko ime', + 'Password' => 'Å ifra', + 'Administrator' => 'Administrator', + 'Sign in' => 'Prijava', + 'Users' => 'Korisnici', + 'Forbidden' => 'Zabranjeno', + 'Access Forbidden' => 'Pristup zabranjen', + 'Edit user' => 'Uredi korisnika', + 'Logout' => 'Odjava', + 'Bad username or password' => 'PogreÅ¡no korisniÄko ime ili Å¡ifra', + 'Edit project' => 'Uredi projekat', + 'Name' => 'Ime', + 'Projects' => 'Projekti', + 'No project' => 'Bez projekta', + 'Project' => 'Projekat', + 'Status' => 'Status', + 'Tasks' => 'Zadaci', + 'Board' => 'PloÄa', + 'Actions' => 'Akcije', + 'Inactive' => 'Neaktivan', + 'Active' => 'Aktivan', + 'Unable to update this board.' => 'Nemogu da ažuriram ovu ploÄu.', + 'Disable' => 'Onemogući', + 'Enable' => 'Omogući', + 'New project' => 'Novi projekat', + 'Do you really want to remove this project: "%s"?' => 'Da li zaista želiÅ¡ ukloniti projekat: "%s"?', + 'Remove project' => 'Ukloni projekat', + 'Edit the board for "%s"' => 'Uredi ploÄu za "%s"', + 'Add a new column' => 'Dodaj novu kolonu', + 'Title' => 'Naslov', + 'Assigned to %s' => 'Dodijeljen korisniku %s', + 'Remove a column' => 'Ukloni kolonu', + '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"?', + 'Settings' => 'PodeÅ¡avanja', + 'Application settings' => 'PodeÅ¡avanja aplikacije', + 'Language' => 'Jezik', + 'Webhook token:' => 'Token:', + 'API token:' => 'Token za API', + 'Database size:' => 'VeliÄina baze:', + 'Download the database' => 'Preuzmi bazu', + 'Optimize the database' => 'Optimizuj bazu', + '(VACUUM command)' => '(Naredba VACUUM)', + '(Gzip compressed Sqlite file)' => '(Sqlite baza spakovana Gzip-om)', + 'Close a task' => 'Zatvori zadatak', + 'Column' => 'Kolona', + 'Color' => 'Boja', + 'Assignee' => 'IzvrÅ¡ilac', + 'Create another task' => 'Poslije ovoga autoamtski otvori formu za dodavanje novog zadatka', + 'New task' => 'Novi zadatak', + 'Open a task' => 'Otvori zadatak', + 'Do you really want to open this task: "%s"?' => 'Da li zaista želiÅ¡ da otvoriÅ¡ zadatak: "%s"?', + 'Back to the board' => 'Nazad na ploÄu', + 'There is nobody assigned' => 'Niko nije dodijeljen!', + 'Column on the board:' => 'Kolona na tabli:', + 'Close this task' => 'Zatvori ovaj zadatak', + 'Open this task' => 'Otvori ovaj zadatak', + 'There is no description.' => 'Bez opisa.', + 'Add a new task' => 'Dodaj novi zadatak', + 'The username is required' => 'KorisniÄko ime je obavezno', + 'The maximum length is %d characters' => 'Maksimalna dužina je %d znakova', + 'The minimum length is %d characters' => 'Minimalna dužina je %d znakova', + 'The password is required' => 'Å ifra je obavezna', + 'This value must be an integer' => 'Mora biti cio broj', + 'The username must be unique' => 'KorisniÄko ime mora biti jedinstveno', + 'The user id is required' => 'ID korisnika je obavezan', + 'Passwords don\'t match' => 'Å ifre se ne podudaraju', + 'The confirmation is required' => 'Potvrda je obavezna', + 'The project is required' => 'Projekat je obavezan', + 'The id is required' => 'ID je obavezan', + 'The project id is required' => 'ID projekta je obavezan', + 'The project name is required' => 'Naziv projekta je obavezan', + 'The title is required' => 'Naslov je obavezan', + 'Settings saved successfully.' => 'PodeÅ¡avanja uspjeÅ¡no saÄuvana.', + 'Unable to save your settings.' => 'Nemoguće saÄuvati podeÅ¡avanja.', + 'Database optimization done.' => 'Optimizacija baze je zavrÅ¡ena.', + 'Your project has been created successfully.' => 'Projekat je uspjeÅ¡no napravljen.', + 'Unable to create your project.' => 'Nemoguće kreiranje projekta.', + 'Project updated successfully.' => 'Projekat je uspjeÅ¡no ažuriran.', + 'Unable to update this project.' => 'Nemoguće ažuriranje projekta.', + 'Unable to remove this project.' => 'Nemoguće uklanjanje projekta.', + 'Project removed successfully.' => 'Projekat uspjeÅ¡no uklonjen.', + 'Project activated successfully.' => 'Projekt uspjeÅ¡no aktiviran.', + 'Unable to activate this project.' => 'Nemoguće aktiviranje projekta.', + 'Project disabled successfully.' => 'Projekat uspjeÅ¡no deaktiviran.', + 'Unable to disable this project.' => 'nemoguće deaktiviranje projekta.', + 'Unable to open this task.' => 'Nemoguće otvaranje zadatka.', + 'Task opened successfully.' => 'Zadatak uspjeÅ¡no otvoren.', + 'Unable to close this task.' => 'Nije moguće zatvaranje ovog zadatka.', + 'Task closed successfully.' => 'Zadatak uspjeÅ¡no zatvoren.', + 'Unable to update your task.' => 'Nije moguće ažuriranje zadatka.', + 'Task updated successfully.' => 'Zadatak uspjeÅ¡no ažuriran.', + 'Unable to create your task.' => 'Nije moguće kreiranje zadatka.', + 'Task created successfully.' => 'Zadatak uspjeÅ¡no kreiran.', + 'User created successfully.' => 'Korisnik uspjeÅ¡no kreiran', + 'Unable to create your user.' => 'Nije uspjelo kreiranje korisnika.', + 'User updated successfully.' => 'Korisnik uspjeÅ¡no ažuriran.', + 'User removed successfully.' => 'Korisnik uspjeÅ¡no uklonjen.', + 'Unable to remove this user.' => 'Nije moguće uklanjanje korisnika.', + 'Board updated successfully.' => 'PloÄa je uspjeÅ¡no ažurirana.', + 'Ready' => 'Spreman', + 'Backlog' => 'Zaliha', + 'Work in progress' => 'U izradi', + 'Done' => 'Gotovo', + 'Application version:' => 'Verzija aplikacije:', + 'Id' => 'Id', + 'Public link' => 'Javni link', + 'Timezone' => 'Vremenska zona', + 'Sorry, I didn\'t find this information in my database!' => 'Na žalost, nije pronaÄ‘ena informacija u bazi', + 'Page not found' => 'Strana nije pronaÄ‘ena', + 'Complexity' => 'Složenost', + 'Task limit' => 'NajviÅ¡e zadataka', + 'Task count' => 'Broj zadataka', + 'User' => 'Korisnik', + 'Comments' => 'Komentari', + 'Comment is required' => 'Komentar je obavezan', + 'Comment added successfully.' => 'Komentar uspjeÅ¡no dodan', + 'Unable to create your comment.' => 'Nemoguće kreiranje komentara', + 'Due Date' => 'Treba biti gotovo do dana', + 'Invalid date' => 'PogreÅ¡an datum', + 'Automatic actions' => 'Automatske akcije', + 'Your automatic action has been created successfully.' => 'UspjeÅ¡no kreirana automatska akcija', + 'Unable to create your automatic action.' => 'Nemoguće kreiranje automatske akcije', + 'Remove an action' => 'ObriÅ¡i akciju', + 'Unable to remove this action.' => 'Nije moguće obrisati akciju', + 'Action removed successfully.' => 'Akcija obrisana', + 'Automatic actions for the project "%s"' => 'Akcije za automatizaciju projekta "%s"', + 'Add an action' => 'dodaj akcju', + 'Event name' => 'Naziv dogaÄ‘aja', + 'Action' => 'Akcija', + 'Event' => 'DogaÄ‘aj', + 'When the selected event occurs execute the corresponding action.' => 'Na izabrani dogaÄ‘aj izvrÅ¡i odgovarajuću akciju', + 'Next step' => 'Slijedeći korak', + 'Define action parameters' => 'DefiniÅ¡i parametre akcije', + 'Do you really want to remove this action: "%s"?' => 'Da li da obriÅ¡em akciju "%s"?', + 'Remove an automatic action' => 'ObriÅ¡i automatsku akciju', + 'Assign the task to a specific user' => 'Dodijeli zadatak odreÄ‘enom korisniku', + 'Assign the task to the person who does the action' => 'Dodeli zadatak korisniku koji je izvrÅ¡io akciju', + 'Duplicate the task to another project' => 'Kopiraj akciju u drugi projekat', + 'Move a task to another column' => 'Premjesti zadatak u drugu kolonu', + 'Task modification' => 'Izmjene zadatka', + 'Task creation' => 'Kreiranje zadatka', + 'Closing a task' => 'Zatvaranja zadatka', + 'Assign a color to a specific user' => 'Dodeli boju korisniku', + 'Position' => 'Pozicija', + 'Duplicate to project' => 'Dupliciraj u drugi projekat', + 'Duplicate' => 'Dupliciraj', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Komentar uspjeÅ¡no ažuriran.', + 'Unable to update your comment.' => 'NeuspjeÅ¡no ažuriranje komentara.', + 'Remove a comment' => 'ObriÅ¡i komentar', + 'Comment removed successfully.' => 'Komentar je uspjeÅ¡no obrisan.', + 'Unable to remove this comment.' => 'NeuspjeÅ¡no brisanje komentara.', + 'Do you really want to remove this comment?' => 'Da li zaista želiÅ¡ obrisati ovaj komentar?', + 'Current password for the user "%s"' => 'Trenutna Å¡ifra korisnika "%s"', + 'The current password is required' => 'Trenutna Å¡ifra je obavezna', + 'Wrong password' => 'PogreÅ¡na Å¡ifra', + 'Unknown' => 'Nepoznat', + 'Last logins' => 'Posljednje prijave', + 'Login date' => 'Datum prijave', + 'Authentication method' => 'Metod autentikacije', + 'IP address' => 'IP adresa', + 'User agent' => 'Browser', + 'Persistent connections' => 'Stalna konekcija', + 'No session.' => 'Bez sesije', + 'Expiration date' => 'IstiÄe', + 'Remember Me' => 'Zapamti me', + 'Creation date' => 'Datum kreiranja', + 'Everybody' => 'Svi', + 'Open' => 'Otvoreni', + 'Closed' => 'Zatvoreni', + 'Search' => 'Pretraga', + 'Nothing found.' => 'NiÅ¡ta nije pronaÄ‘eno', + 'Due date' => 'Treba biti gotovo do dana', + 'Description' => 'Opis', + '%d comments' => '%d Komentara', + '%d comment' => '%d Komentar', + 'Email address invalid' => 'PogreÅ¡an e-mail', + 'Your external account is not linked anymore to your profile.' => 'Tvoj vanjski korisniÄki profil nije viÅ¡e povezan.', + 'Unable to unlink your external account.' => 'Nemoguće ukloniti vezu s vanjskim korisniÄkim profilom', + 'External authentication failed' => 'Vanjska autentikacija nije uspostavljena', + 'Your external account is linked to your profile successfully.' => 'UspjeÅ¡no uspostavljena vanjska autentikacija', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Zadatak uspjeÅ¡no uklonjen.', + 'Unable to remove this task.' => 'Nemoguće uklanjanje zadatka.', + 'Remove a task' => 'Ukloni zadatak', + 'Do you really want to remove this task: "%s"?' => 'Da li zaista želiÅ¡ ukloniti zadatak "%s"?', + 'Assign automatically a color based on a category' => 'Automatski dodijeli boju po kategoriji', + 'Assign automatically a category based on a color' => 'Automatski dodijeli kategoriju po boji', + 'Task creation or modification' => 'Kreiranje ili izmjena zadatka', + 'Category' => 'Kategorija', + 'Category:' => 'Kategorija:', + 'Categories' => 'Kategorije', + 'Your category has been created successfully.' => 'UspjeÅ¡no kreirana kategorija.', + 'This category has been updated successfully.' => 'Kategorija je uspjeÅ¡no ažurirana', + 'Unable to update this category.' => 'Nemoguće izmijeniti kategoriju', + 'Remove a category' => 'Ukloni kategoriju', + 'Category removed successfully.' => 'Kategorija uspjeÅ¡no uklonjena.', + 'Unable to remove this category.' => 'Nije moguće ukloniti kategoriju.', + 'Category modification for the project "%s"' => 'Izmjena kategorije za projekat "%s"', + 'Category Name' => 'Naziv kategorije', + 'Add a new category' => 'Dodaj novu kategoriju', + 'Do you really want to remove this category: "%s"?' => 'Da li zaista želiÅ¡ ukloniti kategoriju: "%s"?', + 'All categories' => 'Sve kategorije', + 'No category' => 'Bez kategorije', + 'The name is required' => 'Naziv je obavezan', + 'Remove a file' => 'Ukloni datoteku', + 'Unable to remove this file.' => 'Fajl nije moguće ukloniti.', + 'File removed successfully.' => 'UspjeÅ¡no uklonjena datoteka.', + 'Attach a document' => 'PrikaÄi dokument', + 'Do you really want to remove this file: "%s"?' => 'Da li zaista želiÅ¡ ukloniti datoteku: "%s"?', + 'Attachments' => 'Prilozi', + 'Edit the task' => 'Uredi zadatak', + 'Add a comment' => 'Dodaj komentar', + 'Edit a comment' => 'Izmijeni komentar', + 'Summary' => 'Pregled', + 'Time tracking' => 'Praćenje vremena', + 'Estimate:' => 'Procjena:', + 'Spent:' => 'PotroÅ¡eno:', + 'Do you really want to remove this sub-task?' => 'Da li da zaista želiÅ¡ ukloniti pod-zdadatak?', + 'Remaining:' => 'Preostalo:', + 'hours' => 'sati', + 'estimated' => 'procijenjeno', + 'Sub-Tasks' => 'Pod-zadaci', + 'Add a sub-task' => 'Dodaj pod-zadatak', + 'Original estimate' => 'Originalna procjena', + 'Create another sub-task' => 'Dodaj novi pod-zadatak', + 'Time spent' => 'UtroÅ¡eno vrijeme', + 'Edit a sub-task' => 'Izmijeni pod-zadatak', + 'Remove a sub-task' => 'Ukloni pod-zadatak', + 'The time must be a numeric value' => 'Vrijeme mora biti broj', + 'Todo' => 'Za uraditi', + 'In progress' => 'U radu', + 'Sub-task removed successfully.' => 'Pod-zadatak uspjeÅ¡no uklonjen.', + 'Unable to remove this sub-task.' => 'Nemoguće ukloniti pod-zadatak.', + 'Sub-task updated successfully.' => 'Pod-zadatak uspjeÅ¡no ažuriran.', + 'Unable to update your sub-task.' => 'Nemoguće ažurirati pod-zadatak.', + 'Unable to create your sub-task.' => 'Nemoguće dodati pod-zadatak.', + 'Maximum size: ' => 'Maksimalna veliÄina: ', + 'Display another project' => 'Prikaži drugi projekat', + 'Created by %s' => 'Kreirao %s', + 'Tasks Export' => 'Izvoz zadataka', + 'Start Date' => 'PoÄetni datum', + 'Execute' => 'IzvrÅ¡i', + 'Task Id' => 'Identifikator zadatka', + 'Creator' => 'Autor', + 'Modification date' => 'Datum izmjene', + 'Completion date' => 'Datum zavrÅ¡etka', + 'Clone' => 'Kloniraj', + 'Project cloned successfully.' => 'Projekat uspjeÅ¡no kloniran.', + 'Unable to clone this project.' => 'Nije moguće klonirati projekat.', + 'Enable email notifications' => 'Omogući obavjeÅ¡tenja e-mailom', + 'Task position:' => 'Pozicija zadatka:', + 'The task #%d has been opened.' => 'Zadatak #%d je otvoren.', + 'The task #%d has been closed.' => 'Zadatak #%d je zatvoren.', + 'Sub-task updated' => 'Pod-zadatak izmijenjen', + 'Title:' => 'Naslov:', + 'Status:' => 'Status:', + 'Assignee:' => 'IzvrÅ¡ilac:', + 'Time tracking:' => 'Praćenje vremena:', + '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 comment' => 'Novi komentar', + 'Comment updated' => 'Komentar ažuriran', + 'New subtask' => 'Novi pod-zadatak', + 'I only want to receive notifications for these projects:' => 'Želim obavjeÅ¡tenja samo za ove projekte:', + 'view the task on Kanboard' => 'Pregledaj zadatke', + 'Public access' => 'Javni pristup', + 'Disable public access' => 'Zabrani javni pristup', + 'Enable public access' => 'Dozvoli javni pristup', + 'Public access disabled' => 'Javni pristup onemogućen!', + 'Move the task to another project' => 'Premjesti zadatak u drugi projekat', + 'Move to project' => 'Premjesti u drugi projekat', + 'Do you really want to duplicate this task?' => 'Da li zaista želiÅ¡ duplicirati ovaj zadatak?', + 'Duplicate a task' => 'Dupliciraj zadatak', + 'External accounts' => 'Vanjski korisniÄki raÄuni', + 'Account type' => 'Tip korisniÄkog raÄuna', + 'Local' => 'Lokalno', + 'Remote' => 'Udaljeno', + 'Enabled' => 'Omogućeno', + 'Disabled' => 'Onemogućeno', + 'Login:' => 'KorisniÄko ime:', + 'Full Name:' => 'Ime i Prezime', + 'Email:' => 'Email: ', + 'Notifications:' => 'ObavjeÅ¡tenja: ', + 'Notifications' => 'ObavjeÅ¡tenja', + 'Account type:' => 'Vrsta korisniÄkog raÄuna:', + 'Edit profile' => 'Uredi profil', + 'Change password' => 'Promijeni Å¡ifru', + 'Password modification' => 'Izmjena Å¡ifre', + 'External authentications' => 'Vanjske autentikacije', + 'Never connected.' => 'Bez konekcija.', + 'No external authentication enabled.' => 'Bez omogućenih vanjskih autentikacija.', + 'Password modified successfully.' => 'UspjeÅ¡na izmjena Å¡ifre.', + 'Unable to change the password.' => 'Nije moguće izmijeniti Å¡ifru.', + 'Change category' => 'Izmijeni kategoriju', + '%s updated the task %s' => '%s izmijenio zadatak %s', + '%s opened the task %s' => '%s otvorio zadatak %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s premjestio zadatak %s na poziciju #%d u koloni "%s"', + '%s moved the task %s to the column "%s"' => '%s premjestio zadatak %s u kolonu "%s"', + '%s created the task %s' => '%s kreirao zadatak %s', + '%s closed the task %s' => '%s zatvorio zadatak %s', + '%s created a subtask for the task %s' => '%s kreirao pod-zadatak zadatka %s', + '%s updated a subtask for the task %s' => '%s izmijenio pod-zadatak zadatka %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Dodijeljen korisniku %s uz procjenu vremena %s/%sh', + 'Not assigned, estimate of %sh' => 'Ne dodijeljen, procijenjeno vrijeme %sh', + '%s updated a comment on the task %s' => '%s izmijenio komentar zadatka %s', + '%s commented the task %s' => '%s komentarisao zadatak %s', + '%s\'s activity' => 'Aktivnosti %s', + 'RSS feed' => 'RSS kanal', + '%s updated a comment on the task #%d' => '%s izmijenio komentar zadatka #%d', + '%s commented on the task #%d' => '%s komentarisao zadatak #%d', + '%s updated a subtask for the task #%d' => '%s izmijenio pod-zadatak zadatka #%d', + '%s created a subtask for the task #%d' => '%s kreirao pod-zadatak zadatka #%d', + '%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 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 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', + 'Create a task from an external provider' => 'Kreiraj zadatak preko posrednika', + 'Change the assignee based on an external username' => 'Izmijene izvrÅ¡ioca bazirano na vanjskom korisniÄkom imenu', + 'Change the category based on an external label' => 'Izmijene kategorije bazirano na vanjskoj etiketi', + 'Reference' => 'Referenca', + 'Label' => 'Etiketa', + 'Database' => 'Baza', + 'About' => 'O Kanboardu', + 'Database driver:' => 'Database driver:', + 'Board settings' => 'Postavke table', + 'Webhook settings' => 'Postavke za webhook', + 'Reset token' => 'Resetuj token', + 'API endpoint:' => 'API endpoint', + 'Refresh interval for personal board' => 'Interval osvježavanja privatnih ploÄa', + 'Refresh interval for public board' => 'Interval osvježavanja javnih ploÄa', + 'Task highlight period' => 'Period naznaÄavanja zadatka', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Period (u sekundama) u kom su se dogaÄ‘ale promjene na zadatku (0 je onemogućeno, 2 dana je uobiÄajeno)', + 'Frequency in second (60 seconds by default)' => 'Frekvencija u sekundama (60 sekundi je uobiÄajeno)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvencija u sekundama (0 je onemogućeno u budućnosti, 10 sekundi je uobiÄajeno)', + 'Application URL' => 'URL aplikacije', + 'Token regenerated.' => 'Token regenerisan.', + 'Date format' => 'Format datuma', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO je uvijek prihvatljiv, primjer: "%s", "%s"', + 'New personal project' => 'Novi privatni projekat', + 'This project is personal' => 'Ovaj projekat je privatan', + 'Add' => 'Dodaj', + 'Start date' => 'Datum poÄetka', + 'Time estimated' => 'Procijenjeno vrijeme', + 'There is nothing assigned to you.' => 'NiÅ¡ta ti nije dodijeljeno', + 'My tasks' => 'Moji zadaci', + 'Activity stream' => 'Spisak aktivnosti', + 'Dashboard' => 'Panel', + 'Confirmation' => 'Potvrda', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Napravi komentar preko vanjskog posrednika', + 'Project management' => 'Upravljanje projektima', + 'Columns' => 'Kolone', + 'Task' => 'Zadatak', + 'Percentage' => 'Procenat', + 'Number of tasks' => 'Broj zadataka', + 'Task distribution' => 'Podjela zadataka', + 'Analytics' => 'Analiza', + 'Subtask' => 'Pod-zadatak', + 'User repartition' => 'Zaduženja korisnika', + '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.', + 'Previous' => 'Prethodni', + 'The id must be an integer' => 'ID mora biti cjeloviti broj', + 'The project id must be an integer' => 'ID projekta mora biti cjeloviti broj', + 'The status must be an integer' => 'Status mora biti cjeloviti broj', + 'The subtask id is required' => 'ID pod-zadataka je obavezan', + 'The subtask id must be an integer' => 'ID pod-zadatka mora biti cjeloviti broj', + 'The task id is required' => 'ID zadatka je obavezan', + 'The task id must be an integer' => 'ID zadatka mora biti cjeloviti broj', + 'The user id must be an integer' => 'ID korisnika mora biti cjeloviti broj', + 'This value is required' => 'Vrijednost je obavezna', + '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', + 'Daily project summary' => 'Zbirni pregled po danima', + 'Daily project summary export' => 'Izvoz zbirnog pregleda po danima', + '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 swimlane trake', + 'Add a new swimlane' => 'Dodaj novu swimlane traku', + 'Default swimlane' => 'Podrazumijevana swimlane traka', + 'Do you really want to remove this swimlane: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovu swimlane traku: "%s"?', + 'Inactive swimlanes' => 'Neaktivne swimlane trake', + 'Remove a swimlane' => 'Ukloni swimlane traku', + 'Swimlane modification for the project "%s"' => 'Izmjene swimlane trake za projekat "%s"', + 'Swimlane removed successfully.' => 'Swimlane traka uspjeÅ¡no uklonjena.', + 'Swimlanes' => 'Swimlane trake', + 'Swimlane updated successfully.' => 'Swimlane traka uspjeno ažurirana.', + 'Unable to remove this swimlane.' => 'Nemoguće ukloniti swimlane traku.', + 'Unable to update this swimlane.' => 'Nemoguće ažurirati swimlane traku.', + 'Your swimlane has been created successfully.' => 'Swimlane traka je uspjeÅ¡no kreirana.', + 'Example: "Bug, Feature Request, Improvement"' => 'Npr: "GreÅ¡ka, Zahtjev za izmjenama, PoboljÅ¡anje"', + 'Default categories for new projects (Comma-separated)' => 'Podrazumijevane kategorije za novi projekat', + 'Integrations' => 'Integracije', + 'Integration with third-party services' => 'Integracija sa uslugama vanjskih servisa', + 'Subtask Id' => 'ID pod-zadatka', + 'Subtasks' => 'Pod-zadaci', + 'Subtasks Export' => 'Izvoz pod-zadataka', + 'Task Title' => 'Naslov zadatka', + 'Untitled' => 'Bez naslova', + 'Application default' => 'Podrazumijevano od aplikacije', + 'Language:' => 'Jezik:', + 'Timezone:' => 'Vremenska zona:', + 'All columns' => 'Sve kolone', + 'Next' => 'Slijedeći', + '#%d' => '#%d', + 'All swimlanes' => 'Sve swimlane trake', + 'All colors' => 'Sve boje', + 'Moved to column %s' => 'PremjeÅ¡ten u kolonu %s', + 'User dashboard' => 'KorisniÄki panel', + 'Allow only one subtask in progress at the same time for a user' => 'Dozvoli samo jedan pod-zadatak "u radu" po korisniku', + 'Edit column "%s"' => 'Uredi kolonu "%s"', + 'Select the new status of the subtask: "%s"' => 'Izaberi novi status za pod-zadatak: "%s"', + 'Subtask timesheet' => 'Vremenska tabela za pod-zadatak', + 'There is nothing to show.' => 'Nema niÅ¡ta za pokazati', + 'Time Tracking' => 'Praćenje vremena', + 'You already have one subtask in progress' => 'Već imaÅ¡ jedan pod-zadatak "u radu"', + 'Which parts of the project do you want to duplicate?' => 'Koje delove projekta želiÅ¡ duplicirati?', + 'Disallow login form' => 'Zabrani prijavnu formu', + 'Start' => 'PoÄetak', + 'End' => 'Kraj', + 'Task age in days' => 'Trajanje zadatka u danima', + 'Days in this column' => 'Dani u ovoj koloni', + '%dd' => '%dd', + 'Add a new link' => 'Dodaj novu vezu', + 'Do you really want to remove this link: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovu vezu: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Da li zaista želiÅ¡ ukloniti ovu vezu sa zadatkom #%d?', + 'Field required' => 'Polje je obavezno', + 'Link added successfully.' => 'Veza je uspjeÅ¡no dodana.', + 'Link updated successfully.' => 'Veza je uspjeÅ¡no ažurirana.', + 'Link removed successfully.' => 'Veza je uspjeÅ¡no uklonjena.', + 'Link labels' => 'Veza s etiketama', + 'Link modification' => 'Veza modifikacija', + 'Opposite label' => 'Suprotna etiketa', + 'Remove a link' => 'Ukloni vezu', + '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', + 'Unable to create your link.' => 'Nemoguće napraviti vezu.', + 'Unable to update your link.' => 'Nemoguće ažurirati vezu.', + 'Unable to remove this link.' => 'Nemoguće ukloniti vezu.', + 'relates to' => 'relacija sa', + 'blocks' => 'blokira', + 'is blocked by' => 'je blokiran od', + 'duplicates' => 'duplicira', + 'is duplicated by' => 'je dupliciran od', + 'is a child of' => 'je dijete od', + 'is a parent of' => 'je roditelj od', + 'targets milestone' => 'cilj prekretnice', + 'is a milestone of' => 'je od prekretnice', + 'fixes' => 'popravlja', + 'is fixed by' => 'je popravljen od', + 'This task' => 'Ovaj zadatak', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'ProÅ¡iri zadatke', + 'Collapse tasks' => 'Skupi zadatke', + 'Expand/collapse tasks' => 'ProÅ¡iri/skupi zadatke', + 'Close dialog box' => 'Skupi dialog', + 'Submit a form' => 'PoÅ¡alji obrazac', + 'Board view' => 'Pregled ploÄe', + 'Keyboard shortcuts' => 'PreÄice tastature', + 'Open board switcher' => 'Otvori prekidaÄe ploÄe', + 'Application' => 'Aplikacija', + 'Compact view' => 'Kompaktan pregled', + 'Horizontal scrolling' => 'Horizontalno listanje', + 'Compact/wide view' => 'Skupi/raÅ¡iri pregled', + 'Currency' => 'Valuta', + 'Personal project' => 'Privatni projekat', + 'AUD - Australian Dollar' => 'AUD - Australijski dolar', + 'CAD - Canadian Dollar' => 'CAD - Kanadski dolar', + 'CHF - Swiss Francs' => 'CHF - Å vicarski franak', + 'Custom Stylesheet' => 'PrilagoÄ‘eni stil', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britanska funta', + 'INR - Indian Rupee' => 'INR - Indijski rupi', + 'JPY - Japanese Yen' => 'JPY - Japanski jen', + 'NZD - New Zealand Dollar' => 'NZD - Novozelandski dolar', + 'PEN - Peruvian Sol' => 'PEN - Peruanski sol', + 'RSD - Serbian dinar' => 'RSD - Srpski dinar', + 'CNY - Chinese Yuan' => 'CNY - Kineski juan', + 'USD - US Dollar' => 'USD - AmeriÄki dolar', + 'VES - Venezuelan Bolívar' => 'VES - Venecuelanski bolivar', + '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', + 'Move the task to another column when assignee is cleared' => 'Premjesti zadatak u neku drugu kolonu kada se ukloni izvrÅ¡ilac', + 'Source column' => 'Izvorna kolona', + 'Transitions' => 'Prelaz', + 'Executer' => 'IzvrÅ¡ilac', + 'Time spent in the column' => 'Vrijeme provedeno u koloni', + 'Task transitions' => 'Prelazi zadatka', + 'Task transitions export' => 'Izvezi prelaze zadatka', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ovaj izvjeÅ¡taj sadržava sve kolone premjeÅ¡tanja za svaki zadatak s datumom, te korisnikom i utroÅ¡enim vremenom za svaki premjeÅ¡taj.', + 'Currency rates' => 'Stopa valute', + 'Rate' => 'Stopa', + 'Change reference currency' => 'Promijeni referencu valute', + 'Reference currency' => 'Referentna valuta', + 'The currency rate has 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 removed the assignee of the task %s' => '%s je uklonio izvrÅ¡ioca zadatka %s', + 'Information' => 'Informacije', + 'Check two factor authentication code' => 'Provjera "Dva faktora" autentifikacionog koda', + 'The two factor authentication code is not valid.' => '"Dva faktora" autentifikacionog koda nije validan.', + 'The two factor authentication code is valid.' => '"Dva faktora" autentifikacionog koda je validan.', + 'Code' => 'Kod', + 'Two factor authentication' => '"Dva faktora" autentifikacija', + 'This QR code contains the key URI: ' => 'Ovaj QR kod sadržava kljuÄni URL: ', + 'Check my code' => 'Provjeri moj kod', + 'Secret key: ' => 'Tajni kljuÄ: ', + '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' => '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', + 'Add a screenshot' => 'Dodaj sliku ekrana', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Uzmi sliku ekrana i pritisni CTRL+V ili ⌘+V da zalijepiÅ¡ ovdje.', + 'Screenshot uploaded successfully.' => 'Slika ekrana uspjeÅ¡no dodana.', + 'SEK - Swedish Krona' => 'SEK - Å vedska kruna', + 'Identifier' => 'Identifikator', + 'Disable two factor authentication' => 'Onemogući "Dva faktora" autentifikaciju', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Da li zaista želiÅ¡ onemogućiti "Dva faktora" autentifikaciju: "%s"?', + 'Edit link' => 'Uredi vezu', + 'Start to type task title...' => 'PoÄni pisati naslov zadatka...', + 'A task cannot be linked to itself' => 'Zadatak ne može biti povezan sa samim sobom', + 'The exact same link already exists' => 'Ista veza već postoji', + 'Recurrent task is scheduled to be generated' => 'Ponavljajući zadatak je pripremljen da bude kreiran', + 'Score' => 'Uspjeh', + 'The identifier must be unique' => 'Identifikator mora biti jedinstven', + 'This linked task id doesn\'t exists' => 'Povezani ID zadatka ne postoji', + 'This value must be alphanumeric' => 'Ova vrijednost mora biti alfanumeriÄka', + 'Edit recurrence' => 'Uredi ponavljanje', + 'Generate recurrent task' => 'Napravi ponavljajući zadatak', + 'Trigger to generate recurrent task' => 'OkidaÄ koji pravi ponavljajući zadatak', + 'Factor to calculate new due date' => 'Faktor za raÄunanje novog datuma zavrÅ¡etka', + 'Timeframe to calculate new due date' => 'Vremenski okvir za raÄunanje novog datuma zavrÅ¡etka', + 'Base date to calculate new due date' => 'PoÄetni datum za raÄunanje novog datuma zavrÅ¡etka', + 'Action date' => 'Datum akcije', + 'Base date to calculate new due date: ' => 'PoÄetni datum za raÄunanje novog datuma zavrÅ¡etka: ', + 'This task has created this child task: ' => 'Ovaj zadatak će napraviti zadatak-dijete: ', + 'Day(s)' => 'Dan(i)', + 'Existing due date' => 'Postojeći datum zavrÅ¡etka', + 'Factor to calculate new due date: ' => 'Faktor za raÄunanje novog datuma zavrÅ¡etka: ', + 'Month(s)' => 'Mjesec(i)', + 'This task has been created by: ' => 'Ovaj zadatak je napravio: ', + 'Recurrent task has been generated:' => 'Ponavljajući zadatak je napravio:', + 'Timeframe to calculate new due date: ' => 'Vremenski okvir za raÄunanje novog datuma zavrÅ¡etka:', + 'Trigger to generate recurrent task: ' => 'OkidaÄ za pravljenje ponavljajućeg zadatka', + 'When task is closed' => 'Kada je zadatak zatvoren', + 'When task is moved from first column' => 'Kada je zadatak premjeÅ¡ten iz prve kolone', + 'When task is moved to last column' => 'Kada je zadatak premjeÅ¡ten u posljednju kolonu', + 'Year(s)' => 'Godina/e', + 'Project settings' => 'Postavke projekta', + 'Automatically update the start date' => 'Automatski ažuriraj poÄetni datum', + 'iCal feed' => 'iCal kanal', + 'Preferences' => 'Postavke', + 'Security' => 'Sigurnost', + 'Two factor authentication disabled' => '"Dva faktora" autentifikacija onemogućena', + 'Two factor authentication enabled' => '"Dva faktora" autentifikacija omogućena', + 'Unable to update this user.' => 'Nemoguće ažurirati ovog korisnika', + 'There is no user management for personal projects.' => 'Nema mehanizma za upravljanje korisnicima kod privatnih projekata.', + 'User that will receive the email' => 'Korisnik će dobiti email', + 'Email subject' => 'Predmet email-a', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'Dodaj komentar u dnevnik kada se pomjeri zadatak izmeÄ‘u kolona', + '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', + 'Notification' => 'ObavjeÅ¡tenja', + '%s moved the task #%d to the first swimlane' => '%s je premjestio zadatak #%d u prvu swimlane traku', + 'Swimlane' => 'Swimlane traka', + '%s moved the task %s to the first swimlane' => '%s je premjestio zadatak %s u prvi swimlane traku', + '%s moved the task %s to the swimlane "%s"' => '%s je premjestio zadatak %s u swimlane traku "%s"', + 'This report contains all subtasks information for the given date range.' => 'Ovaj izvjeÅ¡taj sadržava sve informacije o pod-zadacima za dati period', + 'This report contains all tasks information for the given date range.' => 'Ovaj izvjeÅ¡taj sadržava sve informacije o zadacima u datom periodu', + 'Project activities for %s' => 'Aktivnosti projekta za %s', + 'view the board on Kanboard' => 'pregled ploÄe na Kanboard-u', + 'The task has been moved to the first swimlane' => 'Zadatak je premjeÅ¡ten u prvu swimlane traku', + 'The task has been moved to another swimlane:' => 'Zadatak je premjeÅ¡ten u drugu swimlane traku', + 'New title: %s' => 'Novi naslov: %s', + 'The task is not assigned anymore' => 'Zadatak nema viÅ¡e izvrÅ¡ioca', + 'New assignee: %s' => 'Novi izvrÅ¡ilac: %s', + 'There is no category now' => 'Sada nema kategorije', + 'New category: %s' => 'Nova kategorija: %s', + 'New color: %s' => 'Nova boja: %s', + 'New complexity: %d' => 'Nova složenost: %d', + 'The due date has been removed' => 'Datum zavrÅ¡etka je ukloljen', + 'There is no description anymore' => 'Nema viÅ¡e opisa', + 'Recurrence settings has been modified' => 'Promijenjene postavke za ponavljajuće zadatke', + 'Time spent changed: %sh' => 'UtroÅ¡eno vrijeme je promijenjeno: %sh', + 'Time estimated changed: %sh' => 'OÄekivano vrijeme je promijenjeno: %sh', + 'The field "%s" has been updated' => 'Polje "%s" je ažurirano', + 'The description has been modified:' => 'Promijenjen opis:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Da li zaista želiÅ¡ zatvoriti zadatak "%s" kao i sve pod-zadatke?', + 'I want to receive notifications for:' => 'Želim dobijati obavjeÅ¡tenja za:', + 'All tasks' => 'Sve zadatke', + 'Only for tasks assigned to me' => 'Samo za zadatke na kojima sam izvrÅ¡ilac', + 'Only for tasks created by me' => 'Samo za zadatke koje sam ja napravio', + 'Only for tasks created by me and tasks assigned to me' => 'Samo za zadatke koje sam ja napravio i na kojima sam izvrÅ¡ilac', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Ukupno za sve kolone', + 'You need at least 2 days of data to show the chart.' => 'Da bi se prikazao ovaj grafik potrebni su podaci iz najmanje posljednja dva dana.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Zaustavi tajmer', + 'Start timer' => 'Pokreni tajmer', + 'My activity stream' => 'Tok mojih aktivnosti', + 'Search tasks' => 'Pretraga zadataka', + 'Reset filters' => 'Vrati filtere na poÄetno', + 'My tasks due tomorrow' => 'Moji zadaci koje treba zavrÅ¡iti sutra', + 'Tasks due today' => 'Zadaci koje treba zavrÅ¡iti danas', + 'Tasks due tomorrow' => 'Zadaci koje treba zavrÅ¡iti sutra', + 'Tasks due yesterday' => 'Zadaci koje je trebalo zavrÅ¡iti juÄer', + 'Closed tasks' => 'Zatvoreni zadaci', + 'Open tasks' => 'Otvoreni zadaci', + 'Not assigned' => 'Bez izvrÅ¡ioca', + 'View advanced search syntax' => 'Vidi naprednu sintaksu pretrage', + 'Overview' => 'OpÅ¡ti pregled', + 'Board/Calendar/List view' => 'Pregled PloÄe/Kalendara/Liste', + 'Switch to the board view' => 'Promijeni da vidim ploÄu', + 'Switch to the list view' => 'Promijeni da vidim listu', + 'Go to the search/filter box' => 'Idi na kutiju s pretragom/filterima', + 'There is no activity yet.' => 'JoÅ¡ uvijek nema aktivnosti.', + 'No tasks found.' => 'Zadaci nisu pronaÄ‘eni.', + 'Keyboard shortcut: "%s"' => 'PreÄica tastature: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filter', + 'Advanced search' => 'Napredna pretraga', + 'Example of query: ' => 'Primjer za upit: ', + 'Search by project: ' => 'Pretraga po projektu: ', + 'Search by column: ' => 'Pretraga po koloni: ', + 'Search by assignee: ' => 'Pretraga po izvrÅ¡iocu: ', + 'Search by color: ' => 'Pretraga po boji: ', + 'Search by category: ' => 'Pretraga po kategoriji: ', + 'Search by description: ' => 'Pretraga po opisu: ', + 'Search by due date: ' => 'Pretraga po datumu zavrÅ¡etka: ', + 'Average time spent in each column' => 'Prosjek utroÅ¡enog vrmena u svakoj koloni', + 'Average time spent' => 'Prosjek utroÅ¡enog vremena', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Ovaj grafik pokazuje prosjek utroÅ¡enog vremena u svakoj koloni za posljednjih %d zadataka.', + 'Average Lead and Cycle time' => 'Prosjek vremena upravljanja i vremenskog ciklusa', + 'Average lead time: ' => 'Prosjek vremena upravljanja', + 'Average cycle time: ' => 'Prosjek vremenskog ciklusa', + 'Cycle Time' => 'Vremenski ciklus', + 'Lead Time' => 'Vrijeme upravljanja', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Ovaj grafik pokazuje prosjek vremena voÄ‘enja i vremenskog ciklusa za posljednjih %d zadataka tokom vremena.', + 'Average time into each column' => 'ProsjeÄno vrijeme u svakoj koloni', + 'Lead and cycle time' => 'Vrijeme voÄ‘enja i vremenski ciklus', + 'Lead time: ' => 'Vrijeme voÄ‘enja: ', + 'Cycle time: ' => 'Vremenski ciklus: ', + 'Time spent in each column' => 'UtroÅ¡eno vrijeme u svakoj koloni', + 'The lead time is the duration between the task creation and the completion.' => 'Vrijeme voÄ‘enja je vrijeme koje je proteklo izmeÄ‘u otvaranja i zatvaranja zadatka.', + 'The cycle time is the duration between the start date and the completion.' => 'Vremenski ciklus je vrijeme koje je proteklo izmeÄ‘u poÄetka i zavrÅ¡etka rada na zadatku.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Ako zadatak nije zatvoren trenutno vrijeme je iskoriÅ¡teno umjesto datuma zavrÅ¡etka.', + 'Set the start date automatically' => 'Automatski postavi poÄetno vrijeme', + 'Edit Authentication' => 'Uredi autentifikaciju', + '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.', + '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.', + 'Trigger automatically subtask time tracking' => 'OkidaÄ za automatsko vremensko praćenje za pod-zadatke', + 'Include closed tasks in the cumulative flow diagram' => 'Obuhvati zatvorene zadatke u kumulativnom dijagramu toka', + 'Current swimlane: %s' => 'Trenutna swimlane traka: %s', + 'Current column: %s' => 'Trenutna kolona: %s', + 'Current category: %s' => 'Trenutna kategorija: %s', + 'no category' => 'bez kategorije', + 'Current assignee: %s' => 'Trenutni izvrÅ¡ilac: %s', + 'not assigned' => 'bez ivrÅ¡ioca', + 'Author:' => 'Autor:', + 'contributors' => 'saradnici', + 'License:' => 'Licenca:', + 'License' => 'Licenca', + 'Enter the text below' => 'Unesi tekst ispod', + 'Start date:' => 'PoÄetno vrijeme:', + 'Due date:' => 'Vrijeme do kada treba zavrÅ¡iti:', + 'People who are project managers' => 'Osobe koji su menadžeri projekta', + 'People who are project members' => 'Osobe koje su Älanovi projekta', + 'NOK - Norwegian Krone' => 'NOK - NorveÅ¡ka kruna', + 'Show this column' => 'Prikaži ovu kolonu', + 'Hide this column' => 'Sakrij ovu kolonu', + 'End date' => 'Datum zavrÅ¡etka', + 'Users overview' => 'OpÅ¡ti pregled korisnika', + 'Members' => 'ÄŒlanovi', + 'Shared project' => 'Dijeljeni projekti', + 'Project managers' => 'Menadžeri projekta', + 'Projects list' => 'Lista projekata', + 'End date:' => 'Datum zavrÅ¡etka:', + 'Change task color when using a specific task link' => 'Promijeni boju zadatka kada se koristi odreÄ‘ena veza na zadatku', + 'Task link creation or modification' => 'Veza na zadatku je napravljena ili izmijenjena', + 'Milestone' => 'Prekretnica', + 'Reset the search/filter box' => 'Vrati na poÄetno pretragu/filtere', + 'Documentation' => 'Dokumentacija', + 'Author' => 'Autor', + 'Version' => 'Verzija', + 'Plugins' => 'Dodaci', + 'There is no plugin loaded.' => 'Nema uÄitanih dodataka.', + 'My notifications' => 'Moja obavjeÅ¡tenja', + 'Custom filters' => 'PrilagoÄ‘eni filteri', + 'Your custom filter has been created successfully.' => 'Tvoj prilagoÄ‘eni filter je uspjeÅ¡no napravljen.', + 'Unable to create your custom filter.' => 'Nemoguće napraviti prilagoÄ‘eni filter.', + 'Custom filter removed successfully.' => 'PrilagoÄ‘eni filter uspjeÅ¡no uklonjen.', + 'Unable to remove this custom filter.' => 'Nemoguće ukloniti prilagoÄ‘eni filter.', + 'Edit custom filter' => 'Uredi prilagoÄ‘eni filter', + 'Your custom filter has been updated successfully.' => 'PrilagoÄ‘eni filter uspjeÅ¡no ažuriran.', + 'Unable to update custom filter.' => 'Nemoguće ažurirati prilagoÄ‘eni filter', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Novi priložak na zadatku #%d: %s', + 'New comment on task #%d' => 'Novi komentar na zadatku #%d', + 'Comment updated on task #%d' => 'Ažuriran komentar na zadatku #%d', + 'New subtask on task #%d' => 'Novi pod-zadatak na zadatku #%d', + 'Subtask updated on task #%d' => 'Pod-zadatak ažuriran na zadatku #%d', + 'New task #%d: %s' => 'Novi zadatak #%d: %s', + 'Task updated #%d' => 'Zadatak ažuriran #%d', + 'Task #%d closed' => 'Zadatak #%d zatvoren', + 'Task #%d opened' => 'Zadatak #%d otvoren', + 'Column changed for task #%d' => 'Promijenjena kolona za zadatak #%d', + 'New position for task #%d' => 'Nova pozicija za zadatak #%d', + 'Swimlane changed for task #%d' => 'Swimlane traka promijenjena za zadatak #%d', + 'Assignee changed on task #%d' => 'Promijenjen izvrÅ¡ilac na zadatku #%d', + '%d overdue tasks' => '%d zadataka kasni', + 'No notification.' => 'Nema novih obavjeÅ¡tenja.', + 'Mark all as read' => 'OznaÄi sve kao proÄitano', + 'Mark as read' => 'OznaÄi kao proÄitano', + 'Total number of tasks in this column across all swimlanes' => 'Ukupan broj zadataka u ovoj koloni u svim swimlane trakama', + 'Collapse swimlane' => 'Skupi swimlane trake', + 'Expand swimlane' => 'ProÅ¡iri swimlane trake', + 'Add a new filter' => 'Dodaj novi filter', + 'Share with all project members' => 'Podijeli sa svim Älanovima projekta', + 'Shared' => 'Podijeljeno', + 'Owner' => 'Vlasnik', + 'Unread notifications' => 'NeproÄitana obavjeÅ¡tenja', + 'Notification methods:' => 'Metode obavjeÅ¡tenja:', + 'Unable to read your file' => 'Nemoguće proÄitati datoteku', + '%d task(s) have been imported successfully.' => '%d zadataka uspjeÅ¡no uvezeno.', + 'Nothing has been imported!' => 'NiÅ¡ta nije uvezeno!', + 'Import users from CSV file' => 'Uvezi korisnike putem CSV datoteke', + '%d user(s) have been imported successfully.' => '%d korisnika uspjeÅ¡no uvezeno.', + 'Comma' => 'Zarez', + 'Semi-colon' => 'TaÄka-zarez', + 'Tab' => 'Tab', + 'Vertical bar' => 'Vertikalna traka', + 'Double Quote' => 'Dvostruki navodnici', + 'Single Quote' => 'Jednostruki navodnici', + '%s attached a file to the task #%d' => '%s je dodao novu datoteku u zadatak %d', + 'There is no column or swimlane activated in your project!' => 'Nema kolone ili swimlane trake aktivirane za ovaj projekat!', + 'Append filter (instead of replacement)' => 'Dodaj filter (umjesto zamjene postojećeg)', + 'Append/Replace' => 'Dodaj/Zamijeni', + 'Append' => 'Dodaj', + 'Replace' => 'Zamijeni', + 'Import' => 'Uvoz', + 'Change sorting' => 'Promijeni sortiranje', + 'Tasks Importation' => 'Uvoz zadataka', + 'Delimiter' => 'Djelilac', + 'Enclosure' => 'Prilog', + 'CSV File' => 'CSV File', + 'Instructions' => 'Uputstva', + 'Your file must use the predefined CSV format' => 'File mora biti predefinisani CSV format', + 'Your file must be encoded in UTF-8' => 'File mora biti u UTF-8 kodu', + 'The first row must be the header' => 'Prvi red mora biti zaglavlje', + 'Duplicates are not verified for you' => 'Dupliciranja neće biti provjeravana (to ćeÅ¡ morati uraditi ruÄno)', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Datum do kog se treba izvrÅ¡iti mora biti u ISO formatu: GGGG-MM-DD', + 'Download CSV template' => 'Preuzmi CSV Å¡ablon', + 'No external integration registered.' => 'Nema registrovanih vanjskih integracija.', + 'Duplicates are not imported' => 'Duplikati nisu uvezeni', + 'Usernames must be lowercase and unique' => 'KorisniÄko ime mora biti malim slovima i jedinstveno', + 'Passwords will be encrypted if present' => 'Å ifra će biti kriptovana', + '%s attached a new file to the task %s' => '%s je dodao novu datoteku u zadatak %s', + 'Link type' => 'Tip veze', + 'Assign automatically a category based on a link' => 'Automatsko pridruživanje kategorije bazirano na vezi', + 'BAM - Konvertible Mark' => 'BAM - Konvertibilna marka', + 'Assignee Username' => 'Pridruži korisniÄko ime', + 'Assignee Name' => 'Pridruži ime', + 'Groups' => 'Grupe', + 'Members of %s' => 'ÄŒlanovi %s', + 'New group' => 'Nova grupa', + 'Group created successfully.' => 'Grupa uspjeÅ¡no kreirana.', + 'Unable to create your group.' => 'Nemoguće kreirati grupu.', + 'Edit group' => 'Uredi grupu', + 'Group updated successfully.' => 'Grupa uspjeÅ¡no ažurirana.', + 'Unable to update your group.' => 'Nemoguće ažurirati grupu', + 'Add group member to "%s"' => 'Dodaj Älana grupe u ""%s"', + 'Group member added successfully.' => 'UspjeÅ¡no dodan Älan grupe.', + 'Unable to add group member.' => 'Nemoguće dodati Älana grupe.', + 'Remove user from group "%s"' => 'Ukloni korisnika iz grupe "%s"', + 'User removed successfully from this group.' => 'Korisnik uspjeÅ¡no uklonjen iz grupe.', + 'Unable to remove this user from the group.' => 'Nemoguće ukloniti korisnika iz grupe.', + 'Remove group' => 'Ukloni grupu', + 'Group removed successfully.' => 'Grupa uspjeÅ¡no uklonjena.', + 'Unable to remove this group.' => 'Nemoguće ukloniti grupu.', + 'Project Permissions' => 'Prava na projektu', + 'Manager' => 'Menadžer', + 'Project Manager' => 'Menadžer projekta', + 'Project Member' => 'ÄŒlan projekta', + 'Project Viewer' => 'Preglednik projekta', + 'Your account is locked for %d minutes' => 'Tvoj korisniÄki raÄun je zakljuÄan za narednih %d minuta', + 'Invalid captcha' => 'PogreÅ¡na captcha', + 'The name must be unique' => 'Ime mora biti jedinstveno', + 'View all groups' => 'Pregledaj sve 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.', + 'Add group member' => 'Dodaj Älana grupe', + 'Do you really want to remove this group: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovu grupu: "%s"?', + 'There is no user in this group.' => 'Trenutno nema korisnika u grupi.', + 'Permissions' => 'Prava', + 'Allowed Users' => 'Dozvoljeni korisnici', + 'No specific user has been allowed.' => 'Nema korisnika sa specijalnim dozvolama.', + 'Role' => 'Uloge', + 'Enter user name...' => 'Unesi korisniÄko ime...', + 'Allowed Groups' => 'Dozvoljene grupe', + 'No group has been allowed.' => 'Nema grupa sa specijalnim dozvolama.', + 'Group' => 'Grupa', + 'Group Name' => 'Ime grupe', + 'Enter group name...' => 'Unesi ime grupe...', + 'Role:' => 'Uloga:', + 'Project members' => 'ÄŒlanovi projekta', + '%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', + 'Estimated hours: ' => 'OÄekivani sati:', + 'Actual hours: ' => 'Aktuelni sati:', + 'Hours Spent' => 'UtroÅ¡eni sati:', + 'Hours Estimated' => 'OÄekivani sati', + 'Estimated Time' => 'OÄekivano vrijeme', + 'Actual Time' => 'Aktuelno vrijeme', + 'Estimated vs actual time' => 'OÄekivano nasuprot aktuelnog vremena', + 'RUB - Russian Ruble' => 'RUB - Ruski rubij', + 'Assign the task to the person who does the action when the column is changed' => 'Dodijeli zadatak osobi koja izvrÅ¡i akciju promjene kolone', + 'Close a task in a specific column' => 'Zatvori zadatak u odreÄ‘enoj koloni', + 'Time-based One-time Password Algorithm' => 'Vremenski bazirani One-time Algoritam Å¡ifri', + 'Two-Factor Provider: ' => '"Dva-faktora" provajder: ', + 'Disable two-factor authentication' => 'Onemogući "Dva-faktora" autentifikaciju', + 'Enable two-factor authentication' => 'Omogući "Dva-faktora" autentifikaciju', + 'There is no integration registered at the moment.' => 'Trenutno nema registrovanih integracija.', + 'Password Reset for Kanboard' => 'Promjena Å¡ifre za Kanboard', + 'Forgot password?' => 'Ne mogu da se sjetim Å¡ifre?', + 'Enable "Forget Password"' => 'Omogući "Ne mogu da se sjetim Å¡ifre"', + 'Password Reset' => 'Promijena Å¡ifre', + 'New password' => 'Nova Å¡ifra', + 'Change Password' => 'Promijeni Å¡ifru', + 'To reset your password click on this link:' => 'Da bi promijenio Å¡ifru klikni na ovaj link:', + 'Last Password Reset' => 'Posljednja promjena Å¡ifre', + 'The password has never been reinitialized.' => 'Å ifra nije nikada mijenjena.', + 'Creation' => 'Napravljena', + 'Expiration' => 'IstiÄe', + 'Password reset history' => 'Hitorija promjena Å¡ifri', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Svi zadaci kolone "%s" i swimlane-a "%s" uspjeÅ¡no su zatvoreni.', + 'Do you really want to close all tasks of this column?' => 'Da li zaista želiÅ¡ zatvoriti sve zadatke ove kolone?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d zadatak(ci) u koloni "%s" i swimlane-u "%s" će biti zatvoreni.', + 'Close all tasks in this column and this swimlane' => 'Zatvori sve zadatke ove kolone', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nema posebnog Dodatka za obavjeÅ¡tenja. JoÅ¡ uvijek možeÅ¡ koristiti individualne postavke obavjeÅ¡tenja na svom profilu.', + 'My dashboard' => 'Moj panel', + 'My profile' => 'Moj profil', + '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', + 'Personal 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', + 'Task priority' => 'Prioritet zadatka', + 'General' => 'OpÅ¡te', + 'Dates' => 'Datumi', + 'Default priority' => 'Podrazumijevani prioritet', + 'Lowest priority' => 'Najmanji prioritet', + 'Highest priority' => 'Najveći prioritet', + 'Close a task when there is no activity' => 'Zatvori zadatak kada nema aktivnosti', + 'Duration in days' => 'Dužina trajanja u danima', + 'Send email when there is no activity on a task' => 'PoÅ¡alji email kada nema aktivnosti na zadatku', + 'Unable to fetch link information.' => 'Ne mogu da pribavim informacije o vezi.', + 'Daily background job for tasks' => 'Dnevni pozadinski poslovi na zadacima', + 'Auto' => 'Automatski', + 'Related' => 'Povezani', + 'Attachment' => 'Dodijeljeni', + 'Web Link' => 'Web veza', + 'External links' => 'Vanjska veza', + 'Add external link' => 'Dodaj vanjsku vezu', + 'Type' => 'Vrsta', + 'Dependency' => 'Zavisnost', + 'Add internal link' => 'Dodaj unutraÅ¡nju vezu', + 'Add a new external link' => 'Dodaj novu vanjsku vezu', + 'Edit external link' => 'Uredi vanjsku vezu', + 'External link' => 'Vanjska veza', + 'Copy and paste your link here...' => 'Kopiraj i zalijepi svoju vezu ovdje...', + 'URL' => 'URL', + 'Internal links' => 'UnutraÅ¡nje veze', + 'Assign to me' => 'Dodijeli meni', + 'Me' => 'Za mene', + 'Do not duplicate anything' => 'NiÅ¡ta ne dupliciraj', + 'Projects management' => 'Menadžment projekata', + 'Users management' => 'Menadžment korisnika', + 'Groups management' => 'Menadžment grupa', + 'Create from another project' => 'Napravi iz drugog projekta', + 'open' => 'otvoreno', + 'closed' => 'zatvoreno', + 'Priority:' => 'Prioritet:', + 'Reference:' => 'Preporuka:', + 'Complexity:' => 'Složenost:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Kolona:', + 'Position:' => 'Pozicija:', + 'Creator:' => 'Kreator:', + 'Time estimated:' => 'OÄekivano vrijeme:', + '%s hours' => '%s sati', + 'Time spent:' => 'UtroÅ¡eno vrijeme:', + 'Created:' => 'Kreirao:', + 'Modified:' => 'Uredio:', + 'Completed:' => 'ZavrÅ¡io:', + 'Started:' => 'PoÄeto:', + 'Moved:' => 'Pomjereno:', + 'Task #%d' => 'Zadatak #%d', + 'Time format' => 'Format za vrijeme', + 'Start date: ' => 'PoÄetni datum:', + 'End date: ' => 'Krajnji datum:', + 'New due date: ' => 'Novi datum oÄekivanja:', + 'Start date changed: ' => 'PoÄetni datum promijenjen:', + 'Disable personal projects' => 'Onemogući privatne projekte', + 'Do you really want to remove this custom filter: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovaj prilagoÄ‘eni filter "%s"?', + 'Remove a custom filter' => 'Ukloni prilagoÄ‘eni filter', + 'User activated successfully.' => 'Korisnik uspjeÅ¡no aktiviran.', + 'Unable to enable this user.' => 'Nemoguće omogućiti ovog korisnika.', + '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.' => 'Sve datoteke su uspjeÅ¡no dodane.', + 'The maximum allowed file size is %sB.' => 'Maksimalna dozvoljena veliÄina datoteke je %sB.', + 'Drag and drop your files here' => 'Povuci i spusti svoje datoteke ovdje', + 'choose files' => 'izaberi datoteke', + 'View profile' => 'Pregledaj profil', + 'Two Factor' => 'Dva faktora', + 'Disable user' => 'Onemogući korisnika', + 'Do you really want to disable this user: "%s"?' => 'Da li zaista želiÅ¡ onemogućiti ovog korisnika: "%s"?', + 'Enable user' => 'Omogući korisnika', + 'Do you really want to enable this user: "%s"?' => 'Da li zaista želiÅ¡ omogućiti ovog korisnika: "%s"?', + 'Download' => 'Preuzeto', + 'Uploaded: %s' => 'Dodano: %s', + 'Size: %s' => 'VeliÄina: %s', + 'Uploaded by %s' => 'Dodao %s', + 'Filename' => 'Ime datoteke', + 'Size' => 'VeliÄina', + 'Column created successfully.' => 'Kolona uspjeÅ¡no napravljena.', + 'Another column with the same name exists in the project' => 'Već postoji kolona s istim imenom u ovom projektu.', + 'Default filters' => 'Podrazumijevani filteri', + 'Your board doesn\'t have any columns!' => 'Tvoj panel nema ni jednu kolonu!', + 'Change column position' => 'Promijeni poziciju kolone', + 'Switch to the project overview' => 'Promijeni u pregled projekta', + 'User filters' => 'KorisniÄki filteri', + 'Category filters' => 'Kategorija filtera', + 'Upload a file' => 'Priloži datoteku', + 'View file' => 'Pregled datoteke', + 'Last activity' => 'Posljednja aktivnost', + 'Change subtask position' => 'Promijeni poziciju pod-zadatka', + 'This value must be greater than %d' => 'Ova vrijednost mora biti veća od %d', + 'Another swimlane with the same name exists in the project' => 'Već postoji swimlane sa istim imenom u ovom projektu', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Na primjer: https://example.kanboard.org/ (koristi se za pravljenje apsolutnog URL-a)', + 'Actions duplicated successfully.' => 'Akcije uspjeÅ¡no duplicirane.', + 'Unable to duplicate actions.' => 'Nemoguće duplicirati akcije.', + 'Add a new action' => 'Dodaj novu akciju', + 'Import from another project' => 'Uvezi iz drugog projekta', + 'There is no action at the moment.' => 'Trenutno nema akcija.', + 'Import actions from another project' => 'Uvezi akcije iz drugog projekta', + 'There is no available project.' => 'Trenutno nema dostupnih projekata.', + 'Local File' => 'Lokalna datoteka', + 'Configuration' => 'Konfiguracija', + 'PHP version:' => 'Verzija PHP-a:', + 'PHP SAPI:' => 'Verzija SAPI-a:', + 'OS version:' => 'Verzija OS-a:', + 'Database version:' => 'Verzija baze podataka:', + 'Browser:' => 'PretraživaÄ:', + 'Task view' => 'Pregled zadatka', + 'Edit task' => 'Uredi zadatak', + 'Edit description' => 'Uredi opis', + 'New internal link' => 'Nova unutraÅ¡nja veza', + 'Display list of keyboard shortcuts' => 'Prikaži listu preÄica na tastaturi', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Dodaj sliku za moj avatar', + 'Remove my image' => 'Ukloni moju sliku', + 'The OAuth2 state parameter is invalid' => 'OAuth2 status parametar nije validan', + 'User not found.' => 'Korisnik nije pronaÄ‘en.', + 'Search in activity stream' => 'Pretraži aktivnosti', + 'My activities' => 'Moje aktivnosti', + 'Activity until yesterday' => 'Aktivnosti do juÄer', + 'Activity until today' => 'Aktivnosti do danas', + 'Search by creator: ' => 'Pretraga po kreatoru: ', + 'Search by creation date: ' => 'Pretraga po datumu kreiranja: ', + 'Search by task status: ' => 'Pretraga po statusu zadatka: ', + 'Search by task title: ' => 'Pretraga po naslovu zadatka: ', + 'Activity stream search' => 'Pretraga aktivnosti', + 'Projects where "%s" is manager' => 'Projekti gdje je "%s" menadžer', + 'Projects where "%s" is member' => 'Projekti gdje je "%s" Älan', + 'Open tasks assigned to "%s"' => 'Otvoreni zadaci dodijeljeni "%s"', + 'Closed tasks assigned to "%s"' => 'Zatvoreni zadaci dodijeljeni "%s"', + 'Assign automatically a color based on a priority' => 'Dodijeli boju automatski bazirano na prioritetu', + 'Overdue tasks for the project(s) "%s"' => 'Zadaci u kaÅ¡njenju za projekat(te) "%s"', + 'Upload files' => 'Priloži dokumente', + 'Installed Plugins' => 'Instalirani dodaci', + 'Plugin Directory' => 'Direktorij dodataka', + 'Plugin installed successfully.' => 'Dodatak je uspjeÅ¡no instaliran.', + 'Plugin updated successfully.' => 'Dodatak je uspjeÅ¡no ažuriran.', + 'Plugin removed successfully.' => 'Dodatak uspjeÅ¡no uklonjen.', + 'Subtask converted to task successfully.' => 'Pod-zadatak uspjeÅ¡no pretvoren u zadatak.', + 'Unable to convert the subtask.' => 'Nemoguće pretvoriti pod-zadatak.', + 'Unable to extract plugin archive.' => 'Nemoguće otpakovati arhivu dodatka.', + 'Plugin not found.' => 'Dodatak nije pronaÄ‘en.', + 'You don\'t have the permission to remove this plugin.' => 'Nemate prava za uklanjanje ovog dodatka.', + 'Unable to download plugin archive.' => 'Nemoguće preuzeti arhivu dodatka.', + 'Unable to write temporary file for plugin.' => 'Nemoguće zapisati privremenu datoteku za dodatak.', + 'Unable to open plugin archive.' => 'Nemoguće otvoriti arhivu dodatka.', + 'There is no file in the plugin archive.' => 'Nema datoteke u arhivi dodatka.', + 'Create tasks in bulk' => 'Kreiranje zadataka u rinfuzi', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Ova Kanboard nije konfigurisan tako da podržava instalaciju dodataka kroz korisniÄko okruženje.', + 'There is no plugin available.' => 'Dodaci nisu dostupni.', + 'Install' => 'Instaliraj', + 'Update' => 'Ažuriraj', + 'Up to date' => 'Ažurirano', + 'Not available' => 'Nije dostupno', + 'Remove plugin' => 'Ukloni dodatak', + 'Do you really want to remove this plugin: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovaj dodatak: "%s"?', + 'Uninstall' => 'Deinstaliraj', + 'Listing' => 'Spisak', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Upravljanje projektima', + 'Convert to task' => 'Pretvori u zadatak', + 'Convert sub-task to task' => 'Pretvori pod-zadatak u zadatak', + 'Do you really want to convert this sub-task to a task?' => 'Da li zaista želiÅ¡ pretvoriti pod-zadatak u zadatak?', + 'My task title' => 'Naslov zadatka', + 'Enter one task by line.' => 'Unesi jedan zadatak po liniji.', + 'Number of failed login:' => 'Broj neuspjeÅ¡nih prijava:', + 'Account locked until:' => 'KorisniÄki raÄun zakljuÄan do:', + 'Email settings' => 'Postavke email-a', + 'Email sender address' => 'Email adresa poÅ¡iljaoca', + 'Email transport' => 'Saobraćaj emil-a', + 'Webhook token' => 'Webhook token', + 'Project tags management' => 'Upravljanje oznakama projekta', + 'Tag created successfully.' => 'Oznaka uspjeÅ¡no kreirana.', + 'Unable to create this tag.' => 'Nemoguće kreirati oznaku.', + 'Tag updated successfully.' => 'Oznaka uspjeÅ¡no ažurirana.', + 'Unable to update this tag.' => 'Nemoguće ažurirati ovu oznaku.', + 'Tag removed successfully.' => 'Oznaka uspjeÅ¡no uklonjena.', + 'Unable to remove this tag.' => 'Nemoguće ukloniti ovu oznaku.', + 'Global tags management' => 'Upravljanje opÅ¡tih oznaka.', + 'Tags' => 'Oznake', + 'Tags management' => 'Upravljanje oznakama', + 'Add new tag' => 'Dodaj novu oznaku', + 'Edit a tag' => 'Uredi oznaku', + 'Project tags' => 'Oznake projekta', + 'There is no specific tag for this project at the moment.' => 'Trenutno nema oznaka za ovaj projekat.', + 'Tag' => 'Oznaka', + 'Remove a tag' => 'Ukloni oznaku', + 'Do you really want to remove this tag: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovu oznaku: "%s"?', + 'Global tags' => 'OpÅ¡te oznake', + 'There is no global tag at the moment.' => 'Trenutno nema opÅ¡tih oznaka.', + 'This field cannot be empty' => 'Ovo polje ne može biti prazno', + 'Close a task when there is no activity in a specific column' => 'Zatvori zadatak kada nema aktivnosti u odabranoj koloni', + '%s removed a subtask for the task #%d' => '%s je uklonio pod-zadatak za zadatak #%d', + '%s removed a comment on the task #%d' => '%s je uklonio komentar na zadatku #%d', + 'Comment removed on task #%d' => 'Komentar ukloljen na zadatku #%d', + 'Subtask removed on task #%d' => 'Pod-zadatak ukloljn na zadatku #%d', + 'Hide tasks in this column in the dashboard' => 'Skrij zadatke u koloni na radnoj ploÄi', + '%s removed a comment on the task %s' => '%s je uklonio komentar za zadatak %s', + '%s removed a subtask for the task %s' => '%s je uklonio pod-zadatak za zadatak %s', + 'Comment removed' => 'Komentar uklonjen', + 'Subtask removed' => 'Pod-zadatak uklonjen', + '%s set a new internal link for the task #%d' => '%s je dodao novu unutraÅ¡nju vezu za zadatak #%d', + '%s removed an internal link for the task #%d' => '%s je uklonio unutraÅ¡nju vezu za zadatak #%d', + 'A new internal link for the task #%d has been defined' => 'Nova unutraÅ¡nja veza za zadatak #%d je definisana', + 'Internal link removed for the task #%d' => 'UnutraÅ¡nja veza je uklonjena za zadatak #%d', + '%s set a new internal link for the task %s' => '%s je dodao novu unutraÅ¡nju vezu za zadatak %s', + '%s removed an internal link for the task %s' => '%s je uklonio unutraÅ¡nju vezu za zadatak %s', + 'Automatically set the due date on task creation' => 'Automatski dodaj datum roka izvrÅ¡enja na kreiranom zadatku', + 'Move the task to another column when closed' => 'Pomjeri zadatak u drugu kolonu kada je zatvoren', + 'Move the task to another column when not moved during a given period' => 'Pomjeri zadatak u drugu kolonu kada nema pomijeranja za zadati period', + 'Dashboard for %s' => 'Radna ploÄa za %s', + 'Tasks overview for %s' => 'Pregled zadataka za %s', + 'Subtasks overview for %s' => 'Pregled pod-zadataka za %s', + 'Projects overview for %s' => 'Pregled projekata za %s', + 'Activity stream for %s' => 'Tok aktivnosti za %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Dodijeli boju kada je zadatak pomjeren u odreÄ‘enu swimlane traku', + 'Assign a priority when the task is moved to a specific swimlane' => 'Dodijeli prioritet kada je zadatak pomjeren u odreÄ‘enu swimlane traku', + 'User unlocked successfully.' => 'Korisnik je uspjeÅ¡no otkljuÄan.', + 'Unable to unlock the user.' => 'Nemoguće otkljuÄati korisnika.', + 'Move a task to another swimlane' => 'Pomjeri zadatak u drugu swimlane traku', + 'Creator Name' => 'Ime kreatora', + 'Time spent and estimated' => 'UtroÅ¡eno i oÄekivano vrijeme', + 'Move position' => 'Mjesto pomijeranja', + 'Move task to another position on the board' => 'Pomjeri zadataka na drugo mjesto na radnoj ploÄi', + 'Insert before this task' => 'Umetni prije ovog zadatka', + 'Insert after this task' => 'Umetni poslije ovog zadatka', + 'Unlock this user' => 'OtkljuÄaj ovog korisnika', + 'Custom Project Roles' => 'PrilagoÄ‘ne uloge projekta', + 'Add a new custom role' => 'Dodaj novu prilagoÄ‘enu ulogu', + 'Restrictions for the role "%s"' => 'OgraniÄenja za ulogu "%s"', + 'Add a new project restriction' => 'Dodaj nova ograniÄenja na projektu', + 'Add a new drag and drop restriction' => 'Dodaj nova "prevuci i spusti" ograniÄenja', + 'Add a new column restriction' => 'Dodaj nova ograniÄenja za kolonu', + 'Edit this role' => 'Uredi ovu ulogu', + 'Remove this role' => 'Ukloni ovu ulogu', + 'There is no restriction for this role.' => 'Nema ograniÄenja za ovu ulogu.', + 'Only moving task between those columns is permitted' => 'Samo izmeÄ‘u ovih kolona je dozvoljeno pomijeranje', + 'Close a task in a specific column when not moved during a given period' => 'Zatvori zadatak u odabranoj koloni kada nema pomijeranja za zadati period', + 'Edit columns' => 'Uredi kolone', + 'The column restriction has been created successfully.' => 'OgraniÄenja za kolonu su uspjeÅ¡no kreirana.', + 'Unable to create this column restriction.' => 'Nemoguće kreirati ograniÄenja za kolonu.', + 'Column restriction removed successfully.' => 'OgraniÄenja za kolonu su uspjeÅ¡no uklonjena.', + 'Unable to remove this restriction.' => 'Nemoguće ukloniti ovo ograniÄenje.', + 'Your custom project role has been created successfully.' => 'Tvoja prilagoÄ‘ena uloga na projekatu je uspjeÅ¡no kreirana.', + 'Unable to create custom project role.' => 'Nemoguće kreirati prilagoÄ‘enu ulogu na projektu.', + 'Your custom project role has been updated successfully.' => 'Tvoja prilagoÄ‘ena uloga na projekatu je uspjeÅ¡no ažurirana.', + 'Unable to update custom project role.' => 'Nemoguće ažurirati prilagoÄ‘enu ulogu na projektu.', + 'Custom project role removed successfully.' => 'PrilagoÄ‘ena uloga na projektu uspjeÅ¡no uklonjena.', + 'Unable to remove this project role.' => 'Nemoguće ukloniti ovu ulogu na projektu.', + 'The project restriction has been created successfully.' => 'OgraniÄenja na projektu uspjeÅ¡no kreirana.', + 'Unable to create this project restriction.' => 'Nemoguće kreirati ovo ograniÄenje na projektu.', + 'Project restriction removed successfully.' => 'OgraniÄenje na projektu uspjeÅ¡no uklonjeno.', + 'You cannot create tasks in this column.' => 'Ne možete kreirati zadatak u ovoj koloni.', + 'Task creation is permitted for this column' => 'Kreiranje zadatka u ovoj koloni je zabranjeno', + 'Closing or opening a task is permitted for this column' => 'Zatvaranje ili otvaranje zadatka u ovoj koloni je zabranjeno', + 'Task creation is blocked for this column' => 'Kreiranje zadatka je onemogućeno za ovu kolonu', + 'Closing or opening a task is blocked for this column' => 'Zatvaranje ili otvaranje zadatka u ovoj koloni je onemogućeno', + 'Task creation is not permitted' => 'Kreiranje zadatka nije dozvoljeno', + 'Closing or opening a task is not permitted' => 'Zatvarnaje ili otvaranje zadatka nije dozvoljeno', + 'New drag and drop restriction for the role "%s"' => 'Novo "prevuci i spusti" ograniÄenje za ulogu "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Osobe kojima se dodijeli ova uloga će biti u mogućnosti pomijerati zadatke samo imeÄ‘u izvorne i odrediÅ¡ne kolone.', + 'Remove a column restriction' => 'Ukloni ograniÄenje za kolonu', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Da li zaista želiÅ¡ uklonit ovo ograniÄenje za kolonu: "%s" u "%s"?', + 'New column restriction for the role "%s"' => 'Novo ograniÄenje za kolonu za ulogu "%s"', + 'Rule' => 'Uloga', + 'Do you really want to remove this column restriction?' => 'Da li zaista želiÅ¡ ukloniti ovo ograniÄenje za kolonu?', + 'Custom roles' => 'PrilagoÄ‘ene uloge', + 'New custom project role' => 'Nova prilagoÄ‘ena uloga za projekat', + 'Edit custom project role' => 'Uredi prilagoÄ‘enu ulogu za projekat', + 'Remove a custom role' => 'Ukloni prilagoÄ‘enu ulogu', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Da li zaista želiÅ¡ ukloniti ovu prilagoÄ‘enu ulogu: "%s"? Sve osobe kojima je dodijeljena ova uloga će postati Älanovi projekta.', + 'There is no custom role for this project.' => 'Nema prilagoÄ‘enih uloga za ovaj projekat.', + 'New project restriction for the role "%s"' => 'Novo ograniÄenje na projektu za ulogu "%s"', + 'Restriction' => 'OgraniÄenje', + 'Remove a project restriction' => 'Ukloni ograniÄenje na projektu', + 'Do you really want to remove this project restriction: "%s"?' => 'Da li zaista želiÅ¡ ukloniti ovo ograniÄenje na projektu: "%s"?', + 'Duplicate to multiple projects' => 'Dupliciraj u viÅ¡e projekata', + 'This field is required' => 'Ovo polje je obavezno', + 'Moving a task is not permitted' => 'Pomijeranje zadatka nije dozvoljeno', + 'This value must be in the range %d to %d' => 'Ova vrijednost mora biti u rasponu od %d do %d', + 'You are not allowed to move this task.' => 'NemaÅ¡ dozvolu za pomijeranje ovog zadatka.', + 'API User Access' => 'API User Access', + 'Preview' => 'Pregled', + 'Write' => 'PiÅ¡i', + 'Write your text in Markdown' => 'UpiÅ¡i tekst u Markdown', + 'No personal API access token registered.' => 'Nema registrovanog liÄnog pristupnog API tokena', + 'Your personal API access token is "%s"' => 'Tvoj liÄni pristupni API token je: "%s"', + 'Remove your token' => 'Ukloni svoj token', + 'Generate a new token' => 'GeneriÅ¡i novi token', + 'Showing %d-%d of %d' => 'Prikaži %d-%d od %d', + 'Outgoing Emails' => 'Izlazni email-ovi', + 'Add or change currency rate' => 'Dodaj ili izmijeni odnos valuta', + 'Reference currency: %s' => 'Referentna valuta: %s', + 'Add custom filters' => 'Dodaj prilagoÄ‘ene filtere', + 'Export' => 'Izvezi', + 'Add link label' => 'Dodaj etiketu na vezu', + 'Incompatible Plugins' => 'Nekompatibilni dodaci', + 'Compatibility' => 'Kompatibilnost', + 'Permissions and ownership' => 'Dozvole i vlasniÅ¡tvo', + 'Priorities' => 'Prioriteti', + 'Close this window' => 'Zatvori ovaj pendžer', + 'Unable to upload this file.' => 'Nemoguće dodati ovu datoteku.', + 'Import tasks' => 'Uvezi zadatke.', + 'Choose a project' => 'Izaberi projekat', + 'Profile' => 'Profil', + 'Application role' => 'Uloga na aplikaciji', + '%d invitations were sent.' => 'Poslano %d pozivnica.', + '%d invitation was sent.' => 'Poslana %d pozivnica.', + 'Unable to create this user.' => 'Nemoguće kreirati ovog korisnika.', + 'Kanboard Invitation' => 'Kanboard Pozivnica', + 'Visible on dashboard' => 'Vidljivo na radnoj ploÄi', + 'Created at:' => 'Kreirao:', + 'Updated at:' => 'Ažurirao:', + 'There is no custom filter.' => 'Nema prilagoÄ‘enih filtera.', + 'New User' => 'Novi korisnik', + 'Authentication' => 'Autentikacija', + 'If checked, this user will use a third-party system for authentication.' => 'Ako je oznaÄeno, ovaj korisnik će koristiti "treću stranu" za autentikaciju.', + 'The password is necessary only for local users.' => 'Å ifra je neophodna samo za lokalne korisnike.', + 'You have been invited to register on Kanboard.' => 'Pozvan si da se registrujeÅ¡ na Kanboard.', + 'Click here to join your team' => 'Klikni ovdje da se pridružiÅ¡ timu', + 'Invite people' => 'Pozovi ljude', + 'Emails' => 'Email-ovi', + 'Enter one email address by line.' => 'Unesi jednu email adresu po redu.', + 'Add these people to this project' => 'Dodaj ove ljude u ovaj projekat', + 'Add this person to this project' => 'Dodaj ovu osobu u ovaj projekat', + 'Sign-up' => 'Registracija', + 'Credentials' => 'Pristupni podaci', + 'New user' => 'Novi korisnik', + 'This username is already taken' => 'Ovo korisniÄko ime je zauzeto', + 'Your profile must have a valid email address.' => 'Profil mora imati validnu email adresu.', + 'TRL - Turkish Lira' => 'TRL - Turska Lira', + 'The project email is optional and could be used by several plugins.' => 'Email projekta je prizvoljan i može biti koriÅ¡ten od strane dodataka.', + 'The project email must be unique across all projects' => 'Email projekta mora biti jedinstven za svaki projekat', + 'The email configuration has been disabled by the administrator.' => 'UreÄ‘ivanje email-a je onemogućeno od strane administratora.', + 'Close this project' => 'Zatvori ovaj projekat', + 'Open this project' => 'Otvori ovaj projekat', + 'Close a project' => 'Zatvori projekat', + 'Do you really want to close this project: "%s"?' => 'Da li zaista želiÅ¡ zatvoriti projekat: "%s"?', + 'Reopen a project' => 'Ponovo otvori projekat', + 'Do you really want to reopen this project: "%s"?' => 'Da li zaista želiÅ¡ ponovo otvoriti projekat: "%s"?', + 'This project is open' => 'Ovaj projekat je otvoren', + 'This project is closed' => 'Ovaj projekat je zatvoren', + 'Unable to upload files, check the permissions of your data folder.' => 'Nemoguće priložiti datoteke, provjeri prava na "data" direktoriju na serveru.', + 'Another category with the same name exists in this project' => 'Kategorija sa ovim imenom već postoji na ovom projektu', + 'Comment sent by email successfully.' => 'Komentar je uspjeÅ¡no poslan email-om.', + 'Sent by email to "%s" (%s)' => 'PoÅ¡alji email-om "%s" (%s)', + 'Unable to read uploaded file.' => 'Nemoguće proÄitati dodanu datoteku.', + 'Database uploaded successfully.' => 'Baza podataka je uspjeÅ¡no dodana.', + 'Task sent by email successfully.' => 'Zadatak je uspjeÅ¡no poslan email-om.', + 'There is no category in this project.' => 'Nema kategorija u ovom projektu.', + 'Send by email' => 'PoÅ¡alji email-om', + 'Create and send a comment by email' => 'Kreiraj i poÅ¡alji komentar email-om', + 'Subject' => 'Predmet', + 'Upload the database' => 'Priloži u bazu podataka', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'MožeÅ¡ priložiti prethodno preuzetu Sqlite bazu podataka (Gzip format).', + 'Database file' => 'Datoteka baze podataka', + 'Upload' => 'Priloži', + 'Your project must have at least one active swimlane.' => 'Projekat mora imati barem jednu aktivnu swimlane traku.', + 'Project: %s' => 'Projekat: %s', + 'Automatic action not found: "%s"' => 'Automatska akcija nije pronaÄ‘ena: "%s"', + '%d projects' => '%d projekata', + '%d project' => '%d projekat', + 'There is no project.' => 'Ovo nije projekat.', + 'Sort' => 'Sortiraj', + 'Project ID' => 'ID projekta', + 'Project name' => 'Naziv projekta', + 'Public' => 'Javno', + 'Personal' => 'Privatno', + '%d tasks' => '%d zadataka', + '%d task' => '%d zadatak', + 'Task ID' => 'ID zadatka', + 'Assign automatically a color when due date is expired' => 'Automatski dodaj boju kada je krajnje vrijeme izvrÅ¡enja isteklo', + 'Total score in this column across all swimlanes' => 'Ukupni rezultat u ovoj koloni za sve swimlane trake', + 'HRK - Kuna' => 'HRK - Hrvatska Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentinski pezo', + 'COP - Colombian Peso' => 'COP - Kolumbijski pezo', + '%d groups' => '%d grupa', + '%d group' => '%d grupa', + 'Group ID' => 'ID grupe', + 'External ID' => 'Vanjski ID', + '%d users' => '%d korisnika', + '%d user' => '%d korisnik', + 'Hide subtasks' => 'Sakrij pod-zadatke', + 'Show subtasks' => 'Prikaži pod-zadatke', + 'Authentication Parameters' => 'Parametri autentifikacije', + 'API Access' => 'API pristup', + 'No users found.' => 'Nema pronaÄ‘enih korisnika.', + 'User ID' => 'ID korisnika', + 'Notifications are activated' => 'ObavjeÅ¡tenja su aktivirana', + 'Notifications are disabled' => 'ObavjeÅ¡tenja su onemogućena', + 'User disabled' => 'Korisnik onemogućen', + '%d notifications' => '%d obavjeÅ¡tenja', + '%d notification' => '%d obavjeÅ¡tenje', + 'There is no external integration installed.' => 'Nema instaliranih vanjskih integracija.', + 'You are not allowed to update tasks assigned to someone else.' => 'NemaÅ¡ dozvolu za ažuriranje zadataka dodijeljenih drugima.', + 'You are not allowed to change the assignee.' => 'NemaÅ¡ dozvolu za promjenu izvrÅ¡ioca.', + 'Task suppression is not permitted' => 'Brisanje zadatka nije dozvoljeno', + 'Changing assignee is not permitted' => 'Promjena izvrÅ¡ioca nije dozvoljena', + 'Update only assigned tasks is permitted' => 'Ažuriranje samo dodijeljenih zadataka je dozvoljeno', + 'Only for tasks assigned to the current user' => 'Samo za zadatke dodijeljene trenutnom korisniku', + 'My projects' => 'Moji projekti', + 'You are not a member of any project.' => 'Nisi Älan ni jednog projekta.', + 'My subtasks' => 'Moji pod-zadaci', + '%d subtasks' => '%d pod-zadataka', + '%d subtask' => '%d pod-zadatak', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Pomijeranje zadataka izmeÄ‘u ovih kolona je dozvoljeno samo za zadatke dodijeljene trenutnom korisniku', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Danska kruna', + 'Remove user from group' => 'Ukloni korisnika iz grupe', + 'Assign the task to its creator' => 'Dodijeli zadatak njegovom kreatoru', + 'This task was sent by email to "%s" with subject "%s".' => 'Ovaj zadatak je poslan email-om "%s" sa predmetom "%s".', + 'Predefined Email Subjects' => 'Predefinisani predmeti email-a', + 'Write one subject by line.' => 'NapiÅ¡i jedan predmet po liniji.', + 'Create another link' => 'Kreiraj drugu vezu', + 'BRL - Brazilian Real' => 'BRL - Brazilski real', + 'Add a new Kanboard task' => 'Dodaj novi Kanboard zadatak', + 'Subtask not started' => 'Pod-zadatak nije zapoÄet', + 'Subtask currently in progress' => 'Pod-zadatak trenutno u toku', + 'Subtask completed' => 'Pod-zadatak zavrÅ¡en', + 'Subtask added successfully.' => 'Pod-zadatak uspjeÅ¡no dodan.', + '%d subtasks added successfully.' => '%d pod-zadataka uspjeÅ¡no dodano.', + 'Enter one subtask by line.' => 'Unesi jedan pod-zadatak po liniji.', + 'Predefined Contents' => 'Predefinisani sadržaji', + 'Predefined contents' => 'Predefinisani sadržaji', + 'Predefined Task Description' => 'Predefinisani opis zadatka', + 'Do you really want to remove this template? "%s"' => 'Da li zaista želiÅ¡ ukloniti ovaj Å¡ablon? "%s"', + 'Add predefined task description' => 'Dodaj predefinisani opis zadatka', + 'Predefined Task Descriptions' => 'Predefinisani opisi zadataka', + 'Template created successfully.' => 'Å ablon uspjeÅ¡no kreiran.', + 'Unable to create this template.' => 'Nemoguće kreirati ovaj Å¡ablon.', + 'Template updated successfully.' => 'Å ablon uspjeÅ¡no ažuriran.', + 'Unable to update this template.' => 'Nemoguće ažurirati ovaj Å¡ablon.', + 'Template removed successfully.' => 'Å ablon uspjeÅ¡no uklonjen.', + 'Unable to remove this template.' => 'Nemoguće ukloniti ovaj Å¡ablon.', + 'Template for the task description' => 'Å ablon za opis zadatka', + 'The start date is greater than the end date' => 'PoÄetni datum je veći od krajnjeg datuma', + 'Tags must be separated by a comma' => 'Oznake moraju biti odvojene zarezom', + 'Only the task title is required' => 'Samo je naslov zadatka obavezan', + 'Creator Username' => 'KorisniÄko ime kreatora', + 'Color Name' => 'Naziv boje', + 'Column Name' => 'Naziv kolone', + 'Swimlane Name' => 'Naziv swimlane trake', + 'Time Estimated' => 'Procijenjeno vrijeme', + 'Time Spent' => 'UtroÅ¡eno vrijeme', + 'External Link' => 'Vanjska veza', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Ova funkcionalnost omogućava iCal feed, RSS feed i javni pregled ploÄe.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Zaustavi tajmer svih pod-zadataka kada se zadatak premjesti u drugu kolonu', + 'Subtask Title' => 'Naslov pod-zadatka', + 'Add a subtask and activate the timer when moving a task to another column' => 'Dodaj pod-zadatak i aktiviraj tajmer kada se zadatak premjesti u drugu kolonu', + 'days' => 'dana', + 'minutes' => 'minuta', + 'seconds' => 'sekundi', + 'Assign automatically a color when preset start date is reached' => 'Automatski dodijeli boju kada se dostigne unaprijed postavljeni poÄetni datum', + 'Move the task to another column once a predefined start date is reached' => 'Premjesti zadatak u drugu kolonu kada se dostigne unaprijed postavljeni poÄetni datum', + 'This task is now linked to the task %s with the relation "%s"' => 'Ovaj zadatak je sada povezan sa zadatkom %s sa relacijom "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Veza sa relacijom "%s" prema zadatku %s je uklonjena', + 'Custom Filter:' => 'PrilagoÄ‘eni filter:', + 'Unable to find this group.' => 'Nemoguće pronaći ovu grupu.', + '%s moved the task #%d to the column "%s"' => '%s je premjestio zadatak #%d u kolonu "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s je premjestio zadatak #%d na poziciju %d u koloni "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s je premjestio zadatak #%d u swimlane traku "%s"', + '%sh spent' => '%sh utroÅ¡eno', + '%sh estimated' => '%sh procijenjeno', + 'Select All' => 'OznaÄi sve', + 'Unselect All' => 'PoniÅ¡ti sve', + 'Apply action' => 'Primijeni akciju', + 'Move selected tasks to another column or swimlane' => 'Premjesti odabrane zadatke u drugu kolonu ili swimlane traku', + 'Edit tasks in bulk' => 'Uredi zadatke u rinfuzi', + 'Choose the properties that you would like to change for the selected tasks.' => 'Izaberi svojstva koja želiÅ¡ promijeniti za odabrane zadatke.', + 'Configure this project' => 'KonfiguriÅ¡i ovaj projekat', + 'Start now' => 'PoÄni sada', + '%s removed a file from the task #%d' => '%s je uklonio datoteku sa zadatka #%d', + 'Attachment removed from task #%d: %s' => 'Prilog uklonjen sa zadatka #%d: %s', + 'No color' => 'Bez boje', + 'Attachment removed "%s"' => 'Prilog uklonjen "%s"', + '%s removed a file from the task %s' => '%s je uklonio datoteku sa zadatka %s', + 'Move the task to another swimlane when assigned to a user' => 'Premjesti zadatak u drugu swimlane traku kada je dodijeljen korisniku', + 'Destination swimlane' => 'OdrediÅ¡na swimlane traka', + 'Assign a category when the task is moved to a specific swimlane' => 'Dodijeli kategoriju kada je zadatak premjeÅ¡ten u odreÄ‘enu swimlane traku', + 'Move the task to another swimlane when the category is changed' => 'Premjesti zadatak u drugu swimlane traku kada je kategorija promijenjena', + 'Reorder this column by priority (ASC)' => 'Presortiraj ovu kolonu po prioritetu (ASC)', + 'Reorder this column by priority (DESC)' => 'Presortiraj ovu kolonu po prioritetu (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Presortiraj ovu kolonu po izvrÅ¡iocu i prioritetu (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Presortiraj ovu kolonu po izvrÅ¡iocu i prioritetu (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Presortiraj ovu kolonu po izvrÅ¡iocu (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Presortiraj ovu kolonu po izvrÅ¡iocu (Z-A)', + 'Reorder this column by due date (ASC)' => 'Presortiraj ovu kolonu po datumu zavrÅ¡etka (ASC)', + 'Reorder this column by due date (DESC)' => 'Presortiraj ovu kolonu po datumu zavrÅ¡etka (DESC)', + 'Reorder this column by id (ASC)' => 'Presortiraj ovu kolonu po ID-u (ASC)', + 'Reorder this column by id (DESC)' => 'Presortiraj ovu kolonu po ID-u (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s je premjestio zadatak #%d "%s" u projekat "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Zadatak #%d "%s" je premjeÅ¡ten u projekat "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Premjesti zadatak u drugu kolonu kada je datum zavrÅ¡etka manji od odreÄ‘enog broja dana', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automatski ažuriraj poÄetni datum kada je zadatak premjeÅ¡ten iz odreÄ‘ene kolone', + 'HTTP Client:' => 'HTTP klijent:', + 'Assigned' => 'Dodijeljen', + 'Task limits apply to each swimlane individually' => 'OgraniÄenja zadataka se primjenjuju na svaku swimlane traku pojedinaÄno', + 'Column task limits apply to each swimlane individually' => 'OgraniÄenja zadataka u koloni se primjenjuju na svaku swimlane traku pojedinaÄno', + 'Column task limits are applied to each swimlane individually' => 'OgraniÄenja zadataka u koloni se primjenjuju na svaku swimlane traku pojedinaÄno', + 'Column task limits are applied across swimlanes' => 'OgraniÄenja zadataka u koloni se primjenjuju na sve swimlane trake', + 'Task limit: ' => 'OgraniÄenje zadatka: ', + 'Change to global tag' => 'Promijeni u globalnu oznaku', + 'Do you really want to make the tag "%s" global?' => 'Da li zaista želiÅ¡ uÄiniti oznaku "%s" globalnom?', + 'Enable global tags for this project' => 'Omogući globalne oznake za ovaj projekat', + 'Group membership(s):' => 'ÄŒlanstvo u grupi:', + '%s is a member of the following group(s): %s' => '%s je Älan sljedećih grupa: %s', + '%d/%d group(s) shown' => '%d/%d grupa prikazano', + 'Subtask creation or modification' => 'Kreiranje ili izmjena pod-zadatka', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Dodijeli zadatak odreÄ‘enom korisniku kada je zadatak premjeÅ¡ten u odreÄ‘enu swimlane traku', + 'Comment' => 'Komentar', + 'Collapse vertically' => 'Skupi vertikalno', + 'Expand vertically' => 'ProÅ¡iri vertikalno', + 'MXN - Mexican Peso' => 'MXN - MeksiÄki pezo', + 'Estimated vs actual time per column' => 'Procijenjeno naspram stvarnog vremena po koloni', + 'HUF - Hungarian Forint' => 'HUF - MaÄ‘arski forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Morate odabrati datoteku za otpremanje kao svoj avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Datoteka koju ste otpremili nije važeća slika! (Dozvoljeni su samo *.gif, *.jpg, *.jpeg i *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Automatski postavi rok kada se zadatak premjesti iz odreÄ‘ene kolone', + 'No other projects found.' => 'Nema pronaÄ‘enih drugih projekata.', + 'Tasks copied successfully.' => 'Zadaci su uspjeÅ¡no kopirani.', + 'Unable to copy tasks.' => 'Nije moguće kopirati zadatke.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Svijetla tema', + 'Dark theme' => 'Tamna tema', + 'Automatic theme - Sync with system' => 'Automatska tema - Sinhronizacija sa sistemom', + 'Application managers or more' => 'Menadžeri aplikacije ili viÅ¡e', + 'Administrators' => 'Administratori', + 'Visibility:' => 'Vidljivost:', + 'Standard users' => 'Standardni korisnici', + 'Visibility is required' => 'Vidljivost je obavezna', + 'The visibility should be an app role' => 'Vidljivost treba biti uloga aplikacije', + 'Reply' => 'Odgovori', + '%s wrote: ' => '%s je napisao: ', + 'Number of visible tasks in this column and swimlane' => 'Broj vidljivih zadataka u ovoj koloni i plivaÄkoj stazi', + 'Number of tasks in this swimlane' => 'Broj zadataka u ovoj plivaÄkoj stazi', + 'Unable to find another subtask in progress, you can close this window.' => 'Nije moguće pronaći drugu podzadatak u toku, možete zatvoriti ovaj prozor.', + 'This theme is invalid' => 'Ova tema nije važeća', + 'This role is invalid' => 'Ova uloga nije važeća', + 'This timezone is invalid' => 'Ova vremenska zona nije važeća', + 'This language is invalid' => 'Ovaj jezik nije važeći', + 'This URL is invalid' => 'Ovaj URL nije važeći', + 'Date format invalid' => 'Nevažeći format datuma', + 'Time format invalid' => 'Nevažeći format vremena', + 'Invalid Mail transport' => 'Nevažeći mail transport', + 'Color invalid' => 'Nevažeća boja', + 'This value must be greater or equal to %d' => 'Ova vrijednost mora biti veća ili jednaka %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Dodajte BOM na poÄetak datoteke (potrebno za Microsoft Excel)', + 'Just add these tag(s)' => 'Dodajte samo ove oznake', + 'Remove internal link(s)' => 'Uklonite interne veze', + 'Import tasks from another project' => 'Uvezite zadatke iz drugog projekta', + 'Select the project to copy tasks from' => 'Odaberite projekat iz kojeg želite kopirati zadatke', + 'The total maximum allowed attachments size is %sB.' => 'Ukupna maksimalna dozvoljena veliÄina priloga je %sB.', + 'Add attachments' => 'Dodajte priloge', + 'Task #%d "%s" is overdue' => 'Zadatak #%d "%s" je s izteÄen Ñроком', + 'Enable notifications by default for all new users' => 'Omogući obavjeÅ¡tenja po difoltu za sve nove korisnike', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Dodijeli zadatak njegovom kreatoru za odreÄ‘ene kolone ako izvrÅ¡ilac nije ruÄno postavljen', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Dodijeli zadatak prijavljenom korisniku pri promjeni kolone na odabranu kolonu ako niko nije dodijeljen', +]; diff --git a/app/Locale/ca_ES/translations.php b/app/Locale/ca_ES/translations.php new file mode 100644 index 0000000..dbbdbd2 --- /dev/null +++ b/app/Locale/ca_ES/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Cap', + 'Edit' => 'Edita', + 'Remove' => 'Elimina', + 'Yes' => 'Si', + 'No' => 'No', + 'cancel' => 'cancel·la', + 'or' => 'o', + 'Yellow' => 'Groc', + 'Blue' => 'Blau', + 'Green' => 'Verd', + 'Purple' => 'Lil·la', + 'Red' => 'Vermell', + 'Orange' => 'Taronja', + 'Grey' => 'Gris', + 'Brown' => 'Marró', + 'Deep Orange' => 'Taronja fosc', + 'Dark Grey' => 'Gris fosc', + 'Pink' => 'Rosa', + 'Teal' => 'Turquessa', + 'Cyan' => 'Cian', + 'Lime' => 'Lima', + 'Light Green' => 'Verd clar', + 'Amber' => 'Ambre', + 'Save' => 'Desa', + 'Login' => 'Inicia la sessió', + 'Official website:' => 'Pàgina web oficial:', + 'Unassigned' => 'Sense assignar', + 'View this task' => 'Veure aquesta tasca', + 'Remove user' => 'Eliminar usuari', + 'Do you really want to remove this user: "%s"?' => 'Vols eliminar aquest usuari: "%s"?', + 'All users' => 'Tots els usuaris', + 'Username' => 'Nom d\'usuari', + 'Password' => 'Contrasenya', + 'Administrator' => 'Administrador', + 'Sign in' => 'Accedeix', + 'Users' => 'Usuaris', + 'Forbidden' => 'Prohibit', + 'Access Forbidden' => 'Accés prohibit', + 'Edit user' => 'Edita usuaris', + 'Logout' => 'Tanca sessió', + 'Bad username or password' => 'Usuari o contrasenya no existeix', + 'Edit project' => 'Edita el projecte', + 'Name' => 'Nom', + 'Projects' => 'Projectes', + 'No project' => 'Cap projecte', + 'Project' => 'Projecte', + 'Status' => 'Estat', + 'Tasks' => 'Tasques', + 'Board' => 'Tauler', + 'Actions' => 'Accions', + 'Inactive' => 'Inactiu', + 'Active' => 'Actiu', + 'Unable to update this board.' => 'No es pot actualitzar aquest tauler.', + 'Disable' => 'Desactivar', + 'Enable' => 'Habilita', + 'New project' => 'Nou projecte', + 'Do you really want to remove this project: "%s"?' => 'Vols eliminar aquest projecte: "%s"?', + 'Remove project' => 'Elimina projecte', + 'Edit the board for "%s"' => 'Edita el tauler de "%s"', + 'Add a new column' => 'Afegeix una nova columna', + 'Title' => 'Títol', + 'Assigned to %s' => 'Assignat a %s', + 'Remove a column' => 'Eliminar una columna', + 'Unable to remove this column.' => 'No es pot eliminar aquesta columna.', + 'Do you really want to remove this column: "%s"?' => 'Vols eliminar aquesta columna: "%s"?', + 'Settings' => 'Configuració', + 'Application settings' => 'Configuració de l\'aplicació', + 'Language' => 'Idioma', + 'Webhook token:' => 'Web hook token:', + 'API token:' => 'Token de l\'API:', + 'Database size:' => 'Mida de la base de dades:', + 'Download the database' => 'Descarregar la base de dades', + 'Optimize the database' => 'Optimitzar la base de dades', + '(VACUUM command)' => '(Comanda VACUUM)', + '(Gzip compressed Sqlite file)' => '(Arxiu comprimit gzip SQLite)', + 'Close a task' => 'Tancar una tasca', + 'Column' => 'Columna', + 'Color' => 'Color', + 'Assignee' => 'Assignat a', + 'Create another task' => 'Crear una altra tasca', + 'New task' => 'Nova tasca', + 'Open a task' => 'Obrir una tasca', + 'Do you really want to open this task: "%s"?' => 'Vols obrir aquesta tasca: "%s"?', + 'Back to the board' => 'Torna al tauler', + 'There is nobody assigned' => 'No hi ha ningú assignat', + 'Column on the board:' => 'Columna al tauler:', + 'Close this task' => 'Tanca aquesta tasca', + 'Open this task' => 'Obre aquesta tasca', + 'There is no description.' => 'No hi ha cap descripció.', + 'Add a new task' => 'Afegeix una nova tasca', + 'The username is required' => 'Es requereix el nom d\'usuari', + 'The maximum length is %d characters' => 'La longitud màxima és de %d caràcters', + 'The minimum length is %d characters' => 'La longitud mínima és %d caràcters', + 'The password is required' => 'La contrasenya és necessària', + 'This value must be an integer' => 'Aquest valor ha de ser un nombre enter', + 'The username must be unique' => 'El nom d\'usuari ha de ser únic', + 'The user id is required' => 'Es requereix que l\'ID d\'usuari', + 'Passwords don\'t match' => 'Les contrasenyes no coincideixen', + 'The confirmation is required' => 'Es requereix la confirmació', + 'The project is required' => 'Es requereix el projecte', + 'The id is required' => 'Es requereix que l\'ID', + 'The project id is required' => 'Es requereix que l\'identificador del projecte', + 'The project name is required' => 'Es requereix el nom del projecte', + 'The title is required' => 'Es requereix el títol', + 'Settings saved successfully.' => 'La configuració es va guardar amb èxit.', + 'Unable to save your settings.' => 'No es pot desar la configuració.', + 'Database optimization done.' => 'Optimització de bases de dades realitza.', + 'Your project has been created successfully.' => 'El seu projecte s\'han creat amb èxit.', + 'Unable to create your project.' => 'No es pot crear el projecte.', + 'Project updated successfully.' => 'Projecte actualitzat correctament.', + 'Unable to update this project.' => 'No es pot actualitzar aquest projecte.', + 'Unable to remove this project.' => 'No es pot eliminar aquest projecte.', + 'Project removed successfully.' => 'Projecte eliminat correctament.', + 'Project activated successfully.' => 'Projecte activat correctament.', + 'Unable to activate this project.' => 'No es pot activar aquest projecte.', + 'Project disabled successfully.' => 'Projecte deshabilitat amb èxit.', + 'Unable to disable this project.' => 'No és possible desactivar aquest projecte.', + 'Unable to open this task.' => 'No es pot obrir aquesta tasca.', + 'Task opened successfully.' => 'Tasca oberta amb èxit.', + 'Unable to close this task.' => 'No es pot tancar aquesta tasca.', + 'Task closed successfully.' => 'Tasca tancada amb èxit.', + 'Unable to update your task.' => 'No es pot actualitzar la seva tasca.', + 'Task updated successfully.' => 'Tasca actualitzada correctament.', + 'Unable to create your task.' => 'No es pot crear la tasca.', + 'Task created successfully.' => 'Tasca creada correctament.', + 'User created successfully.' => 'L\'usuari ha creat correctament.', + 'Unable to create your user.' => 'No es pot crear l\'usuari.', + 'User updated successfully.' => 'Usuari actualitzat correctament.', + 'User removed successfully.' => 'Usuari eliminat correctament.', + 'Unable to remove this user.' => 'No es pot eliminar aquest usuari.', + 'Board updated successfully.' => 'Tauler actualitzat correctament.', + 'Ready' => 'Preparat', + 'Backlog' => 'Pendent', + 'Work in progress' => 'En curs', + 'Done' => 'Fet', + 'Application version:' => 'Versió de l\'aplicació:', + 'Id' => 'Identificació', + 'Public link' => 'Enllaç públic', + 'Timezone' => 'Zona horària', + 'Sorry, I didn\'t find this information in my database!' => 'No s\'ha trobat la informació a la base de dades!', + 'Page not found' => 'Pàgina no trobada', + 'Complexity' => 'Dificultat', + 'Task limit' => 'Límit de tasques', + 'Task count' => 'Recompte de tasques', + 'User' => 'Usuari', + 'Comments' => 'Comentaris', + 'Comment is required' => 'Es requereix comentari', + 'Comment added successfully.' => 'Comentari afegit amb èxit.', + 'Unable to create your comment.' => 'No es pot crear el seu comentari.', + 'Due Date' => 'Data de venciment', + 'Invalid date' => 'Data no vàlida', + 'Automatic actions' => 'Accions automàtiques', + 'Your automatic action has been created successfully.' => 'La seva acció automàtica s\'han creat amb èxit.', + 'Unable to create your automatic action.' => 'No es pot crear la seva acció automàtica.', + 'Remove an action' => 'Eliminar una acció', + 'Unable to remove this action.' => 'No es pot eliminar aquesta acció.', + 'Action removed successfully.' => 'Acció eliminat correctament.', + 'Automatic actions for the project "%s"' => 'Accions automàtiques per al projecte "%s"', + 'Add an action' => 'Afegeix una acció', + 'Event name' => 'Nom de l\'esdeveniment', + 'Action' => 'Acció', + 'Event' => 'Esdeveniment', + 'When the selected event occurs execute the corresponding action.' => 'Quan es produeix l\'esdeveniment seleccionat executar l\'acció corresponent.', + 'Next step' => 'Següent pas', + 'Define action parameters' => 'Definir paràmetres d\'acció', + 'Do you really want to remove this action: "%s"?' => 'Vols eliminar aquesta acció: "%s"?', + 'Remove an automatic action' => 'Eliminar una acció automàtica', + 'Assign the task to a specific user' => 'Assignar la tasca a un usuari específic', + 'Assign the task to the person who does the action' => 'Assignar la tasca a la persona que fa l\'acció', + 'Duplicate the task to another project' => 'Duplica la tasca a un altre projecte', + 'Move a task to another column' => 'Mou una tasca a una altra columna', + 'Task modification' => 'Modificació de tasques', + 'Task creation' => 'Creació de tasques', + 'Closing a task' => 'El tancament d\'una tasca', + 'Assign a color to a specific user' => 'Assignar un color a un usuari específic', + 'Position' => 'Posició', + 'Duplicate to project' => 'Duplica a un altre projecte', + 'Duplicate' => 'Duplica', + 'Link' => 'Enllaç', + 'Comment updated successfully.' => 'Comentari actualitzat correctament.', + 'Unable to update your comment.' => 'No es pot actualitzar el seu comentari.', + 'Remove a comment' => 'Retirar un comentari', + 'Comment removed successfully.' => 'Comentari eliminat correctament.', + 'Unable to remove this comment.' => 'No es pot eliminar aquest comentari.', + 'Do you really want to remove this comment?' => 'Vols eliminar aquest comentari?', + 'Current password for the user "%s"' => 'Contrasenya actual per a l\'usuari "%s"', + 'The current password is required' => 'Es requereix la contrasenya actual', + 'Wrong password' => 'Contrasenya incorrecta', + 'Unknown' => 'Desconegut', + 'Last logins' => 'Últims inicis de sessió', + 'Login date' => 'Data d\'inici de sessió', + 'Authentication method' => 'Mètode d\'autenticació', + 'IP address' => 'Adreça IP', + 'User agent' => 'Agent d\'usuari', + 'Persistent connections' => 'Les connexions persistents', + 'No session.' => 'Cap sessió.', + 'Expiration date' => 'Data de caducitat', + 'Remember Me' => 'Recorda\'m', + 'Creation date' => 'Data de creació', + 'Everybody' => 'Tothom', + 'Open' => 'Obert', + 'Closed' => 'Tancat', + 'Search' => 'Cerca', + 'Nothing found.' => 'No s\'ha trobat res.', + 'Due date' => 'Data de venciment', + 'Description' => 'Descripció', + '%d comments' => '%d comentaris', + '%d comment' => '%d comentari', + 'Email address invalid' => 'Adreça de correu electrònic no vàlida', + 'Your external account is not linked anymore to your profile.' => 'El seu compte extern no està vinculada més al seu perfil.', + 'Unable to unlink your external account.' => 'No és possible desvincular el compte extern.', + 'External authentication failed' => 'L\'autenticació externa va fallar', + 'Your external account is linked to your profile successfully.' => 'El seu compte extern està vinculada al seu perfil correctament.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Tasca eliminat correctament.', + 'Unable to remove this task.' => 'No es pot eliminar aquesta tasca.', + 'Remove a task' => 'Treure una tasca', + 'Do you really want to remove this task: "%s"?' => 'Vols eliminar aquesta tasca: "%s"?', + 'Assign automatically a color based on a category' => 'Assignar automàticament un color basat en una categoria', + 'Assign automatically a category based on a color' => 'Assignar automàticament una categoria basada en un color', + 'Task creation or modification' => 'Creació o modificació de tasques', + 'Category' => 'Categoria', + 'Category:' => 'Categoria:', + 'Categories' => 'Categories', + 'Your category has been created successfully.' => 'La seva categoria s\'han creat amb èxit.', + 'This category has been updated successfully.' => 'Aquesta categoria s\'ha actualitzat correctament.', + 'Unable to update this category.' => 'No es pot actualitzar aquesta categoria.', + 'Remove a category' => 'Eliminar una categoria', + 'Category removed successfully.' => 'Categoria eliminat correctament.', + 'Unable to remove this category.' => 'No es pot eliminar aquesta categoria.', + 'Category modification for the project "%s"' => 'Modificació de la categoria pel projecte "%s"', + 'Category Name' => 'Nom de categoria', + 'Add a new category' => 'Afegeix una nova categoria', + 'Do you really want to remove this category: "%s"?' => 'Vols eliminar aquesta categoria: "%s"?', + 'All categories' => 'Totes les categories', + 'No category' => 'sense categoria', + 'The name is required' => 'Es requereix el nom', + 'Remove a file' => 'Suprimir un fitxer', + 'Unable to remove this file.' => 'No es pot eliminar aquest arxiu.', + 'File removed successfully.' => 'Arxiu eliminat amb èxit.', + 'Attach a document' => 'Adjunta un document', + 'Do you really want to remove this file: "%s"?' => 'Vols eliminar aquesta imatge: "%s"?', + 'Attachments' => 'Adjunts', + 'Edit the task' => 'Edita la tasca', + 'Add a comment' => 'Afegeix un comentari', + 'Edit a comment' => 'Edita un comentari', + 'Summary' => 'Resum', + 'Time tracking' => 'Control d\'hores', + 'Estimate:' => 'Estimació:', + 'Spent:' => 'Utilitzat:', + 'Do you really want to remove this sub-task?' => 'Realment voleu eliminar aquesta sub-tasca?', + 'Remaining:' => 'Restant:', + 'hours' => 'hores', + 'estimated' => 'estimat', + 'Sub-Tasks' => 'Subtasques', + 'Add a sub-task' => 'Afegeix una subtasca', + 'Original estimate' => 'Estimació original', + 'Create another sub-task' => 'Crear una altra subtasca', + 'Time spent' => 'El temps dedicat', + 'Edit a sub-task' => 'Editar una subtasca', + 'Remove a sub-task' => 'Suprimir una subtasca', + 'The time must be a numeric value' => 'El temps ha de ser un valor numèric', + 'Todo' => 'Fer', + 'In progress' => 'En progrés', + 'Sub-task removed successfully.' => 'Subtasca eliminat correctament.', + 'Unable to remove this sub-task.' => 'No es pot eliminar aquesta sub-tasques.', + 'Sub-task updated successfully.' => 'Subtasca actualitzat correctament.', + 'Unable to update your sub-task.' => 'No es pot actualitzar el seu sub-tasques.', + 'Unable to create your sub-task.' => 'No es pot crear el sub-tasques.', + 'Maximum size: ' => 'Mida màxima: ', + 'Display another project' => 'Mostra un altre projecte', + 'Created by %s' => 'Creat per %s', + 'Tasks Export' => 'Exportació de tasques', + 'Start Date' => 'Data d\'inici', + 'Execute' => 'Executar', + 'Task Id' => 'ID de tasca', + 'Creator' => 'Creador', + 'Modification date' => 'Data de modificació', + 'Completion date' => 'Data de finalització', + 'Clone' => 'Clon', + 'Project cloned successfully.' => 'Projecte clonat amb èxit.', + 'Unable to clone this project.' => 'No és possible clonar aquest projecte.', + 'Enable email notifications' => 'Activa notificacions per correu electrònic', + 'Task position:' => 'Posició de la tasca:', + 'The task #%d has been opened.' => 'La tasca #%d s\'ha obert.', + 'The task #%d has been closed.' => 'La tasca #%d s\'ha tancat.', + 'Sub-task updated' => 'Subtasca actualitza', + 'Title:' => 'Títol:', + 'Status:' => 'Estat:', + 'Assignee:' => 'Assignat a:', + 'Time tracking:' => 'Control d\'hores:', + 'New sub-task' => 'Nova subtasca', + 'New attachment added "%s"' => 'Nou adjunt afegit "%s"', + 'New comment posted by %s' => 'Nou comentari Publicat per %s', + 'New comment' => 'Nou comentari', + 'Comment updated' => 'Comentari actualitzat', + 'New subtask' => 'Nova subtasca', + 'I only want to receive notifications for these projects:' => 'Vull rebre notificacions només per a aquells projectes:', + 'view the task on Kanboard' => 'Veure la tasca en Kanboard', + 'Public access' => 'Accés públic', + 'Disable public access' => 'Deshabilitar l\'accés del públic', + 'Enable public access' => 'Permetre l\'accés del públic', + 'Public access disabled' => 'L\'accés públic deshabilitat', + 'Move the task to another project' => 'Mou la tasca a un altre projecte', + 'Move to project' => 'Mou a un altre projecte', + 'Do you really want to duplicate this task?' => 'És el que realment desitja duplicar aquesta tasca?', + 'Duplicate a task' => 'Duplica una tasca', + 'External accounts' => 'Els comptes externes', + 'Account type' => 'Tipus de compte', + 'Local' => 'Local', + 'Remote' => 'Remot', + 'Enabled' => 'Habilitat', + 'Disabled' => 'Inhabilitat', + 'Login:' => 'Iniciar Sessió:', + 'Full Name:' => 'Nom complet:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Notificacions:', + 'Notifications' => 'Notificacions', + 'Account type:' => 'Tipus de compte:', + 'Edit profile' => 'Editar el perfil', + 'Change password' => 'Canvia la contrasenya', + 'Password modification' => 'Modificació de la contrasenya', + 'External authentications' => 'Autenticacions externes', + 'Never connected.' => 'Mai connectada.', + 'No external authentication enabled.' => 'No s\'habilita l\'autenticació externa.', + 'Password modified successfully.' => 'Contrasenya modificat correctament.', + 'Unable to change the password.' => 'No es pot canviar la contrasenya.', + 'Change category' => 'Canvia la categoria', + '%s updated the task %s' => '%s ha actualitzat la tasca %s', + '%s opened the task %s' => '%s ha obert la tasca %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s ha mogut la tasca %s a la posició #%d de la columna "%s"', + '%s moved the task %s to the column "%s"' => '%s ha mogut la tasca %s a la columna "%s"', + '%s created the task %s' => '%s creat la tasca %s', + '%s closed the task %s' => '%s tancada la tasca %s', + '%s created a subtask for the task %s' => '%s creat una subtasca de la tasca %s', + '%s updated a subtask for the task %s' => '%s actualitzada una subtasca de la tasca %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Assignat a %s amb un temps estimat de %s/%sh', + 'Not assigned, estimate of %sh' => 'No assignat, estimat de %sh', + '%s updated a comment on the task %s' => '%s ha actualitzat un comentari a la tasca %s', + '%s commented the task %s' => '%s ha comentat la tasca %s', + '%s\'s activity' => 'L\'activitat de %s', + 'RSS feed' => 'RSS feed', + '%s updated a comment on the task #%d' => '%s s\'actualitzen un comentari a la tasca #%d', + '%s commented on the task #%d' => '%s comentat a la tasca #%d', + '%s updated a subtask for the task #%d' => '%s s\'actualitzen un subtasca de la tasca #%d', + '%s created a subtask for the task #%d' => '%s creat una subtasca de la tasca #%d', + '%s updated the task #%d' => '%s actualitzada la tasca #%d', + '%s created the task #%d' => '%s creat la tasca #%d', + '%s closed the task #%d' => '%s van tancar la tasca #%d', + '%s opened the task #%d' => '%s va obrir la tasca #%d', + 'Activity' => 'Activitat', + 'Default values are "%s"' => 'Els valors per defecte son "%s"', + 'Default columns for new projects (Comma-separated)' => 'Columnes predeterminades per a nous projectes (separats per comes)', + 'Task assignee change' => 'Tasca de canvi del assignat', + '%s changed the assignee of the task #%d to %s' => '%s va canviar el assignat de la tasca #%d de %s', + '%s changed the assignee of the task %s to %s' => '%s va canviar el assignat de la tasca %s %s', + 'New password for the user "%s"' => 'Nova contrasenya per a l\'usuari "%s"', + 'Choose an event' => 'Tria un esdeveniment', + 'Create a task from an external provider' => 'Crear una tasca d\'un proveïdor extern', + 'Change the assignee based on an external username' => 'Canviar el assignat basat en un nom d\'usuari extern', + 'Change the category based on an external label' => 'Canviar la categoria en funció d\'una etiqueta externa', + 'Reference' => 'Referència', + 'Label' => 'Etiqueta', + 'Database' => 'Base de dades', + 'About' => 'Quant a', + 'Database driver:' => 'Controlador de base de dades:', + 'Board settings' => 'Paràmetres del tauler', + 'Webhook settings' => 'Configuració web hook', + 'Reset token' => 'Reinicia el token', + 'API endpoint:' => 'API de punt final:', + 'Refresh interval for personal board' => 'Interval d\'actualització per al tauler privada', + 'Refresh interval for public board' => 'Interval d\'actualització per al tauler pública', + 'Task highlight period' => 'Període culminant de tasques', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Període (en segons) per considerar una tasca es va modificar recentment (0 per desactivar, 2 dies de defecte)', + 'Frequency in second (60 seconds by default)' => 'Freqüència en segons (60 segons per defecte)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Freqüència en segons (0 per desactivar aquesta característica, 10 segons per defecte)', + 'Application URL' => 'URL de l\'aplicació', + 'Token regenerated.' => 'Token regenerat.', + 'Date format' => 'Format de dates', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Sempre s\'accepta el format ISO, exemple "%s" i "%s"', + 'New personal project' => 'Nou projecte privat', + 'This project is personal' => 'Aquest projecte és privat', + 'Add' => 'Afegeix', + 'Start date' => 'Data d\'inici', + 'Time estimated' => 'Temps estimat', + 'There is nothing assigned to you.' => 'No tens res assignat.', + 'My tasks' => 'Les meves tasques', + 'Activity stream' => 'Fluxe d\'activitat', + 'Dashboard' => 'Panell', + 'Confirmation' => 'Confirmació', + 'Webhooks' => 'WebHooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Crear un comentari d\'un proveïdor extern', + 'Project management' => 'Gestió de projectes', + 'Columns' => 'Columnes', + 'Task' => 'Tasca', + 'Percentage' => 'Percentatge', + 'Number of tasks' => 'Nombre de tasques', + 'Task distribution' => 'Distribució de tasques', + 'Analytics' => 'Analítica', + 'Subtask' => 'Subtasca', + 'User repartition' => 'Repartiment d\'usuari', + 'Clone this project' => 'Clonar aquest projecte', + 'Column removed successfully.' => 'Columna eliminat correctament.', + 'Not enough data to show the graph.' => 'No hi ha dades suficients per mostrar el gràfic.', + 'Previous' => 'Anterior', + 'The id must be an integer' => 'L\'identificador ha de ser un nombre enter', + 'The project id must be an integer' => 'L\'identificador de projecte ha de ser un enter', + 'The status must be an integer' => 'L\'estat ha de ser un enter', + 'The subtask id is required' => 'Es requereix que l\'ID subtasca', + 'The subtask id must be an integer' => 'L\'identificador de subtasca ha de ser un enter', + 'The task id is required' => 'Es requereix que l\'ID de tasca', + 'The task id must be an integer' => 'L\'identificador de tasca ha de ser un enter', + 'The user id must be an integer' => 'L\'identificador d\'usuari ha de ser un nombre enter', + 'This value is required' => 'Aquest valor és necessari', + 'This value must be numeric' => 'Aquest valor ha de ser numèric', + 'Unable to create this task.' => 'No es pot crear aquesta tasca.', + 'Cumulative flow diagram' => 'Diagrama de flux acumulat', + 'Daily project summary' => 'Resum diari projecte', + 'Daily project summary export' => 'Exportació del resum diari del projecte', + 'Exports' => 'Exportacions', + 'This export contains the number of tasks per column grouped per day.' => 'Aquesta exportació conté el nombre de tasques per columna agrupada per dia.', + 'Active swimlanes' => 'Swimlanes actius', + 'Add a new swimlane' => 'Afegeix un nou swimlane', + 'Default swimlane' => 'Swimlane per defecte', + 'Do you really want to remove this swimlane: "%s"?' => 'Segur que vols eliminar la swimlane: "%s"', + 'Inactive swimlanes' => 'Swimlanes inactius', + 'Remove a swimlane' => 'Traieu un carril', + 'Swimlane modification for the project "%s"' => 'Modificació del swimlane pel projecte "%s"', + 'Swimlane removed successfully.' => 'Swimlane eliminat correctament.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane actualitzat correctament.', + 'Unable to remove this swimlane.' => 'No es pot eliminar aquest swimlane.', + 'Unable to update this swimlane.' => 'No es pot actualitzar aquest swimlane.', + 'Your swimlane has been created successfully.' => 'La seva swimlane s\'han creat amb èxit.', + 'Example: "Bug, Feature Request, Improvement"' => 'Exemple: "Bug, Comanda de funcions, Millora"', + 'Default categories for new projects (Comma-separated)' => 'Categories per defecte per a nous projectes (separats per comes)', + 'Integrations' => 'Integracions', + 'Integration with third-party services' => 'Integració amb els serveis de tercers', + 'Subtask Id' => 'ID de subtasca', + 'Subtasks' => 'Subtasques', + 'Subtasks Export' => 'Exportació de subtasques', + 'Task Title' => 'Títol de la tasca', + 'Untitled' => 'Sense títol', + 'Application default' => 'Per defecte de l\'aplicació', + 'Language:' => 'Llengua:', + 'Timezone:' => 'Zona horària:', + 'All columns' => 'Totes les columnes', + 'Next' => 'Pròxim', + '#%d' => '#%d', + 'All swimlanes' => 'Tots swimlanes', + 'All colors' => 'Tots els colors', + 'Moved to column %s' => 'Traslladat a la columna %s', + 'User dashboard' => 'Tauler d\'usuari', + 'Allow only one subtask in progress at the same time for a user' => 'Permetre només una subtasca en curs al mateix temps per a un usuari', + 'Edit column "%s"' => 'Modifica la columna "%s"', + 'Select the new status of the subtask: "%s"' => 'Sel·lecciona el nou estat per la subtasca: "%s"', + 'Subtask timesheet' => 'Part d\'hores de subtasca', + 'There is nothing to show.' => 'Res per mostrar.', + 'Time Tracking' => 'Control d\'hores', + 'You already have one subtask in progress' => 'Ja tens una subtasca en curs', + 'Which parts of the project do you want to duplicate?' => 'Quines parts del projecte vols duplicar?', + 'Disallow login form' => 'No permetre formulari d\'accés', + 'Start' => 'Començar', + 'End' => 'Final', + 'Task age in days' => 'L\'edat de tasques en dies', + 'Days in this column' => 'Dies en aquesta columna', + '%dd' => '%dd', + 'Add a new link' => 'Afegeix un nou enllaç', + 'Do you really want to remove this link: "%s"?' => 'Segur que vols eliminar aquest enllaç: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Segur que vols eliminar aquest enllaç amb la tasca #%d?', + 'Field required' => 'Camp requerit', + 'Link added successfully.' => 'Enllaç afegit correctament.', + 'Link updated successfully.' => 'Enllaç actualitzat correctament.', + 'Link removed successfully.' => 'Enllaç eliminat amb èxit.', + 'Link labels' => 'Etiquetes d\'enllaç', + 'Link modification' => 'Modificació d\'enllaç', + 'Opposite label' => 'Etiqueta oposada', + 'Remove a link' => 'Suprimir un enllaç', + 'The labels must be different' => 'Les etiquetes han de ser diferents', + 'There is no link.' => 'No hi ha cap relació.', + 'This label must be unique' => 'Aquest avís ha de ser únic', + 'Unable to create your link.' => 'No es pot crear l\'enllaç.', + 'Unable to update your link.' => 'No es pot actualitzar el seu enllaç.', + 'Unable to remove this link.' => 'No es pot eliminar aquest enllaç.', + 'relates to' => 'es refereix a', + 'blocks' => 'bloqueja', + 'is blocked by' => 'està bloquejada per', + 'duplicates' => 'duplica', + 'is duplicated by' => 'es duplica per', + 'is a child of' => 'és fill de', + 'is a parent of' => 'és pare de', + 'targets milestone' => 'objectiu de la fita', + 'is a milestone of' => 'és una fita de', + 'fixes' => 'corregeix', + 'is fixed by' => 'corregit per', + 'This task' => 'Aquesta tasca', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Amplia les tasques', + 'Collapse tasks' => 'Col·lapsa les tasques', + 'Expand/collapse tasks' => 'Amplia les tasques / col·lapsa', + 'Close dialog box' => 'Quadre de diàleg tancar', + 'Submit a form' => 'Envia un formulari', + 'Board view' => 'Mostra el tauler', + 'Keyboard shortcuts' => 'Dreceres de teclat', + 'Open board switcher' => 'Obre commutador de tauler', + 'Application' => 'Aplicació', + 'Compact view' => 'Vista compacta', + 'Horizontal scrolling' => 'Desplaçament horitzontal', + 'Compact/wide view' => 'Vista compacta / ample', + 'Currency' => 'Moneda', + 'Personal project' => 'Projecte privat', + 'AUD - Australian Dollar' => 'MXN - Dòlar australià', + 'CAD - Canadian Dollar' => 'CAD - Dòlar canadenc', + 'CHF - Swiss Francs' => 'CHF - Franc suís', + 'Custom Stylesheet' => 'Estil personalitzat', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Lliura britànica', + 'INR - Indian Rupee' => 'INR - Rupia índia', + 'JPY - Japanese Yen' => 'JPY - El ien japonès', + 'NZD - New Zealand Dollar' => 'NZD - Dòlar de Nova Zelanda', + 'PEN - Peruvian Sol' => 'PEN - Sol Peruà', + 'RSD - Serbian dinar' => 'RSD - Dinar serbi', + 'CNY - Chinese Yuan' => 'CNY - Yuan xinès', + 'USD - US Dollar' => 'USD - El dòlar dels EUA', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar Veneçolà', + 'Destination column' => 'Columna de destinació', + 'Move the task to another column when assigned to a user' => 'Mou la tasca a una altra columna quan s\'assigna a un usuari', + 'Move the task to another column when assignee is cleared' => 'Mou la tasca a una altra columna quan s\'esborra assignat', + 'Source column' => 'Columna d\'origen', + 'Transitions' => 'Transicions', + 'Executer' => 'Executor', + 'Time spent in the column' => 'El temps emprat a la columna', + 'Task transitions' => 'transicions de tasques', + 'Task transitions export' => 'Tasca transicions d\'exportació', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Aquest informe conté tots els moviments de columna per a cada tasca amb la data, l\'usuari i el temps per a cada transició.', + 'Currency rates' => 'Monedes', + 'Rate' => 'Preu', + 'Change reference currency' => 'Moneda de canvi de referència', + 'Reference currency' => 'Moneda de referència', + 'The currency rate has been added successfully.' => 'El tipus de canvi s\'ha afegit amb èxit.', + 'Unable to add this currency rate.' => 'No es pot afegeix aquest tipus de moneda.', + 'Webhook URL' => 'URL web hook', + '%s removed the assignee of the task %s' => '%s ha eliminat l\'assignat de la tasca %s', + 'Information' => 'Informació', + 'Check two factor authentication code' => 'Comprovar el codi d\'autenticació de dos factors', + 'The two factor authentication code is not valid.' => 'El codi d\'autenticació de dos factors no és vàlid.', + 'The two factor authentication code is valid.' => 'El codi d\'autenticació de dos factors és vàlid.', + 'Code' => 'Codi', + 'Two factor authentication' => 'Autenticació de dos factors', + 'This QR code contains the key URI: ' => 'Aquest codi QR conté la URI de la clau: ', + 'Check my code' => 'Comprovar el meu codi', + 'Secret key: ' => 'Clau secreta: ', + 'Test your device' => 'Prova el teu dispositiu', + 'Assign a color when the task is moved to a specific column' => 'Assignar un color quan la tasca es mou a una columna específica', + '%s via Kanboard' => '%s a través del Kanboard', + 'Burndown chart' => 'Burndown chart', + 'This chart show the task complexity over the time (Work Remaining).' => 'Aquesta gràfica mostra la dificultat de la tasca en el temps (Treball restant).', + 'Screenshot taken %s' => 'Imatge presa %s', + 'Add a screenshot' => 'Afegeix una captura de pantalla', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Prengui una captura de pantalla i premeu CTRL + V o ⌘ + V per enganxar aquí.', + 'Screenshot uploaded successfully.' => 'Captura carregat correctament.', + 'SEK - Swedish Krona' => 'SEK - Corona sueca', + 'Identifier' => 'Identificador', + 'Disable two factor authentication' => 'Desactiva l\'autenticació de dos factors', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Realment vols desactivar l\'autenticació de doble factor per a aquest usuari: "%s"?', + 'Edit link' => 'Edita enllaç', + 'Start to type task title...' => 'Comenceu a títol de la tasca de tipus ...', + 'A task cannot be linked to itself' => 'Una tasca no pot vincular-se a si mateix', + 'The exact same link already exists' => 'La mateixa relació exacta ja existeix', + 'Recurrent task is scheduled to be generated' => 'La tasca recurrent està programada per ser generada', + 'Score' => 'Puntuació', + 'The identifier must be unique' => 'L\'identificador ha de ser únic', + 'This linked task id doesn\'t exists' => 'Aquest ID de tasca enllaçada no existeix', + 'This value must be alphanumeric' => 'Aquest valor ha de ser alfanumèric', + 'Edit recurrence' => 'Edita recurrència', + 'Generate recurrent task' => 'Generar tasca recurrent', + 'Trigger to generate recurrent task' => 'Desencadenar per generar tasca recurrent', + 'Factor to calculate new due date' => 'Factor per calcular la nova data de venciment', + 'Timeframe to calculate new due date' => 'Marc de temps per calcular la nova data de venciment', + 'Base date to calculate new due date' => 'Data de referència per al càlcul de la nova data de venciment', + 'Action date' => 'data d\'acció', + 'Base date to calculate new due date: ' => 'Data base per a calcular nova data de venciment: ', + 'This task has created this child task: ' => 'Aquesta tasca ha creat aquesta tasca filla: ', + 'Day(s)' => 'Dia (Dies)', + 'Existing due date' => 'Data de venciment existent', + 'Factor to calculate new due date: ' => 'Factor per calcular la nova data de venciment: ', + 'Month(s)' => 'Mes (Mesos)', + 'This task has been created by: ' => 'Tasca creada per: ', + 'Recurrent task has been generated:' => 'La tasca recurrent s\'ha generat:', + 'Timeframe to calculate new due date: ' => 'Període de temps per calcular la nova data de venciment: ', + 'Trigger to generate recurrent task: ' => 'Activador per generar tasques recurrents: ', + 'When task is closed' => 'Quan la tasca està tancada', + 'When task is moved from first column' => 'Quan la tasca es mou de la primera columna', + 'When task is moved to last column' => 'Quan la tasca es mou a última columna', + 'Year(s)' => 'Any (s)', + 'Project settings' => 'Configuració del projecte', + 'Automatically update the start date' => 'Actualitzar automàticament la data d\'inici', + 'iCal feed' => 'Origen iCal', + 'Preferences' => 'Configuració', + 'Security' => 'Seguretat', + 'Two factor authentication disabled' => 'L\'autenticació de dos factors deshabilitat', + 'Two factor authentication enabled' => 'Autenticació de dos factors activada', + 'Unable to update this user.' => 'No es pot actualitzar aquest usuari.', + 'There is no user management for personal projects.' => 'No hi ha una gestió d\'usuaris per a projectes privats.', + 'User that will receive the email' => 'L\'usuari que rebrà el correu electrònic', + 'Email subject' => 'assumpte del correu electrònic', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'Afegeix un registre comentari en moure la tasca entre les columnes', + 'Move the task to another column when the category is changed' => 'Mou la tasca a una altra columna quan es canvia la categoria', + 'Send a task by email to someone' => 'Envia una tasca per correu electrònic a algú', + 'Reopen a task' => 'Torneu a obrir una tasca', + 'Notification' => 'Notificació', + '%s moved the task #%d to the first swimlane' => '%s van moure la tasca #%d per al primer swimlane', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => '%s es va traslladar la tasca %s per al primer swimlane', + '%s moved the task %s to the swimlane "%s"' => '%s va moure la tasca %s al carril "%s"', + 'This report contains all subtasks information for the given date range.' => 'Aquest informe conté tota la informació subtasques per al període de temps.', + 'This report contains all tasks information for the given date range.' => 'Aquest informe conté tota la informació de tasques per al període de temps.', + 'Project activities for %s' => 'Les activitats del projecte per a %s', + 'view the board on Kanboard' => 'Veure el tauler en Kanboard', + 'The task has been moved to the first swimlane' => 'La tasca ha estat mogut a la primera swimlane', + 'The task has been moved to another swimlane:' => 'La tasca s\'han traslladat a un altre swimlane:', + 'New title: %s' => 'Nou títol: %s', + 'The task is not assigned anymore' => 'La tasca no s\'ha assignat més', + 'New assignee: %s' => 'Nova assignat:%s', + 'There is no category now' => 'No hi ha una categoria ara', + 'New category: %s' => 'Nova categoria:%s', + 'New color: %s' => 'Nou color: %s', + 'New complexity: %d' => 'Nova dificultat: %d', + 'The due date has been removed' => 'La data de venciment s\'han eliminat', + 'There is no description anymore' => 'No hi ha una descripció més', + 'Recurrence settings has been modified' => 'ajustos de recurrència s\'han modificat', + 'Time spent changed: %sh' => 'Temps invertit canviat: %sh', + 'Time estimated changed: %sh' => 'Temps estimat canviat: %sh', + 'The field "%s" has been updated' => 'El camp "%s" ha estat actualitzat', + 'The description has been modified:' => 'La descripció s\'ha modificat:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vols tancar la tasca "%s", així com totes les subtasques?', + 'I want to receive notifications for:' => 'Vull rebre notificacions per:', + 'All tasks' => 'totes les tasques', + 'Only for tasks assigned to me' => 'Només per a les tasques que se m\'ha s\'assignin', + 'Only for tasks created by me' => 'Només per a les tasques creades per mi', + 'Only for tasks created by me and tasks assigned to me' => 'Només per a tasques creades per mi i em assignin', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Total per a totes les columnes', + 'You need at least 2 days of data to show the chart.' => 'Es necessita com a mínim 2 dies de dades per mostrar el gràfic.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Atura el temporitzador', + 'Start timer' => 'Inicia el temporitzador', + 'My activity stream' => 'El meu fluxe d\'activitat', + 'Search tasks' => 'Cerca de tasques', + 'Reset filters' => 'Restableix filtres', + 'My tasks due tomorrow' => 'Les meves tasques per demà', + 'Tasks due today' => 'Tasques que vencen avui', + 'Tasks due tomorrow' => 'Tasques per demà', + 'Tasks due yesterday' => 'Tasques a causa ahir', + 'Closed tasks' => 'Tasques tancades', + 'Open tasks' => 'Tasques obertes', + 'Not assigned' => 'No assignat', + 'View advanced search syntax' => 'Veure sintaxi de cerca avançada', + 'Overview' => 'Visió de conjunt', + 'Board/Calendar/List view' => 'Tauler / Calendari / Vista de llista', + 'Switch to the board view' => 'Canviar a la vista de tauler', + 'Switch to the list view' => 'Canviar a la vista de llista', + 'Go to the search/filter box' => 'Anar al quadre de cerca / filtre', + 'There is no activity yet.' => 'No hi ha cap activitat encara.', + 'No tasks found.' => 'No s\'han trobat tasques.', + 'Keyboard shortcut: "%s"' => 'Drecera de teclat: "%s"', + 'List' => 'Llista', + 'Filter' => 'Filtre', + 'Advanced search' => 'Cerca avançada', + 'Example of query: ' => 'Exemple de consulta: ', + 'Search by project: ' => 'Cerca per projecte: ', + 'Search by column: ' => 'Cerca per columna: ', + 'Search by assignee: ' => 'Cerca per assignat: ', + 'Search by color: ' => 'Cerca per color: ', + 'Search by category: ' => 'Cerca per categoria: ', + 'Search by description: ' => 'Cerca per descripció: ', + 'Search by due date: ' => 'Cerca per data de venciment: ', + 'Average time spent in each column' => 'Temps mitjà de permanència en cada columna', + 'Average time spent' => 'Temps mitjà de permanència', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Aquest gràfic mostra el temps mitjà de permanència en cada columna per a les últimes %d tasques.', + 'Average Lead and Cycle time' => 'El plom i la mitjana del temps de cicle', + 'Average lead time: ' => 'Mitjana de temps de lliurament: ', + 'Average cycle time: ' => 'Mitjana de temps de cicle: ', + 'Cycle Time' => 'Temps de cicle', + 'Lead Time' => 'Temps de lliurament', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Aquesta taula mostra la mitjana de plom i el temps de cicle per a les últimes tasques %d respecte al temps.', + 'Average time into each column' => 'Temps mitjà en cada columna', + 'Lead and cycle time' => 'Temps de lliurament i cicle', + 'Lead time: ' => 'Temps de lliurament: ', + 'Cycle time: ' => 'Temps del cicle: ', + 'Time spent in each column' => 'El temps invertit en cada columna', + 'The lead time is the duration between the task creation and the completion.' => 'El termini d\'execució és la durada entre la creació de la tasca i la finalització.', + 'The cycle time is the duration between the start date and the completion.' => 'El temps de cicle és la durada entre la data d\'inici i la finalització.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tasca no està tancat el moment actual s\'utilitza en lloc de la data de finalització.', + 'Set the start date automatically' => 'S\'ajusta automàticament la data d\'inici', + 'Edit Authentication' => 'Edita autenticació', + 'Remote user' => 'Usuari remot', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Els usuaris remots no emmagatzemen la contrasenya a la base de dades Kanboard, exemples: els comptes LDAP, Google i Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marca la casella "No permetre formulari d\'accés", s\'ignoraran les credencials introduïdes al formulari d\'inici de sessió.', + 'Default task color' => 'Color per defecte de les tasques', + 'This feature does not work with all browsers.' => 'Aquesta característica no funciona amb tots els navegadors.', + 'There is no destination project available.' => 'No hi ha cap projecte de destinació disponibles.', + 'Trigger automatically subtask time tracking' => 'Desencadenar automàticament control d\'hores de subtasca', + 'Include closed tasks in the cumulative flow diagram' => 'Incloure tasques tancades en el diagrama de flux acumulat', + 'Current swimlane: %s' => 'Swimlane actual: %s', + 'Current column: %s' => 'Columna actual: %s', + 'Current category: %s' => 'Categoria actual: %s', + 'no category' => 'cap categoria', + 'Current assignee: %s' => 'Assignat actual: %s', + 'not assigned' => 'no assignat', + 'Author:' => 'Autor:', + 'contributors' => 'contribuents', + 'License:' => 'Llicència:', + 'License' => 'Llicència', + 'Enter the text below' => 'Introduïu el text a continuació', + 'Start date:' => 'Data d\'inici:', + 'Due date:' => 'Data de venciment:', + 'People who are project managers' => 'Les persones que són caps de projecte', + 'People who are project members' => 'Les persones que són membres del projecte', + 'NOK - Norwegian Krone' => 'NOK - Corona noruega', + 'Show this column' => 'Mostra aquesta columna', + 'Hide this column' => 'Amaga aquesta columna', + 'End date' => 'Data de finalització', + 'Users overview' => 'Visió general dels usuaris', + 'Members' => 'Membres', + 'Shared project' => 'Projecte compartit', + 'Project managers' => 'Els gerents de projecte', + 'Projects list' => 'Llista de projectes', + 'End date:' => 'Data de finalització:', + 'Change task color when using a specific task link' => 'Canviar de color quan la tasca utilitzant un enllaç tasca específica', + 'Task link creation or modification' => 'La creació o modificació de l\'enllaç de tasques', + 'Milestone' => 'Fita', + 'Reset the search/filter box' => 'Restablir el quadre de cerca / filtre', + 'Documentation' => 'Documentació', + 'Author' => 'Autor', + 'Version' => 'Versió', + 'Plugins' => 'Connectors', + 'There is no plugin loaded.' => 'No hi ha cap complement carregat.', + 'My notifications' => 'Les meves notificacions', + 'Custom filters' => 'Filtres personalitzats', + 'Your custom filter has been created successfully.' => 'El seu filtre a mida s\'han creat amb èxit.', + 'Unable to create your custom filter.' => 'No es pot crear el filtre personalitzat.', + 'Custom filter removed successfully.' => 'Filtre a mida eliminat correctament.', + 'Unable to remove this custom filter.' => 'No es pot eliminar aquest filtre personalitzat.', + 'Edit custom filter' => 'Edita filtre a mida', + 'Your custom filter has been updated successfully.' => 'El seu filtre a mida s\'han actualitzat correctament.', + 'Unable to update custom filter.' => 'No es pot actualitzar filtre a mida.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nou adjunt a la tasca #%d: %s', + 'New comment on task #%d' => 'Nou comentari a la tasca #%d', + 'Comment updated on task #%d' => 'Comentari actualitzat a la tasca #%d', + 'New subtask on task #%d' => 'Nova subtasca a la tasca #%d', + 'Subtask updated on task #%d' => 'Subtasca s\'actualitza a la tasca #%d', + 'New task #%d: %s' => 'Nova tasca #%d: %s', + 'Task updated #%d' => 'Tasca actualitzada #%d', + 'Task #%d closed' => 'Tasca #%d tancada', + 'Task #%d opened' => 'Tasca #%d obrir', + 'Column changed for task #%d' => 'Columna canviada per la tasca #%d', + 'New position for task #%d' => 'Nova posició per la tasca #%d', + 'Swimlane changed for task #%d' => 'El swimlane va canviat per la tasca #%d', + 'Assignee changed on task #%d' => 'L\'assignat va canviar a la tasca #%d', + '%d overdue tasks' => '%d tasques endarrerides', + 'No notification.' => 'Sense notificacions.', + 'Mark all as read' => 'Marca totes com llegides', + 'Mark as read' => 'Marca com llegida', + 'Total number of tasks in this column across all swimlanes' => 'Nombre total de tasques en aquesta columna a través de tots swimlanes', + 'Collapse swimlane' => 'Col·lapsa swimlane', + 'Expand swimlane' => 'Ampliar swimlane', + 'Add a new filter' => 'Afegeix un nou filtre', + 'Share with all project members' => 'Compartir amb tots els membres del projecte', + 'Shared' => 'Compartit', + 'Owner' => 'Propietari', + 'Unread notifications' => 'Notificacions no llegides', + 'Notification methods:' => 'Els mètodes de notificació:', + 'Unable to read your file' => 'No es pot llegir el fitxer', + '%d task(s) have been imported successfully.' => '%d tasca(s) s\'han importat correctament.', + 'Nothing has been imported!' => 'Res s\'han importat!', + 'Import users from CSV file' => 'Importa usuaris des d\'arxiu CSV', + '%d user(s) have been imported successfully.' => '%d usuari(s) s\'han importat correctament.', + 'Comma' => 'Coma', + 'Semi-colon' => 'Punt i coma', + 'Tab' => 'Llengüeta', + 'Vertical bar' => 'Barra vertical', + 'Double Quote' => 'Cometes dobles', + 'Single Quote' => 'Cometes simples', + '%s attached a file to the task #%d' => '%s ha adjuntat un arxiu a la tasca #%d', + 'There is no column or swimlane activated in your project!' => 'No hi ha cap columna o swimlane activat en el seu projecte!', + 'Append filter (instead of replacement)' => 'Annexar filtre (en lloc de reemplaçament)', + 'Append/Replace' => 'Annexar / Reemplaçar', + 'Append' => 'Annexar', + 'Replace' => 'Reemplaçar', + 'Import' => 'Importació', + 'Change sorting' => 'Canvia la ordenació', + 'Tasks Importation' => 'Importació de tasques', + 'Delimiter' => 'Delimitador', + 'Enclosure' => 'Recinte', + 'CSV File' => 'Fitxer CSV', + 'Instructions' => 'Instruccions', + 'Your file must use the predefined CSV format' => 'El seu arxiu ha d\'utilitzar el format CSV predefinit', + 'Your file must be encoded in UTF-8' => 'El seu arxiu ha d\'estar codificat en UTF-8', + 'The first row must be the header' => 'La primera fila ha de ser la capçalera', + 'Duplicates are not verified for you' => 'Els duplicats no es verifiquen per a vostè', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La data de venciment ha d\'utilitzar el format ISO: AAAA-MM-DD', + 'Download CSV template' => 'Descarregar plantilla CSV', + 'No external integration registered.' => 'No es va registrar la integració externa.', + 'Duplicates are not imported' => 'Els duplicats no s\'importen', + 'Usernames must be lowercase and unique' => 'Els noms d\'usuari han d\'estar en minúscules i únic', + 'Passwords will be encrypted if present' => 'Les contrasenyes es xifraran si està present', + '%s attached a new file to the task %s' => '%s adjunta un arxiu nou a la tasca %s', + 'Link type' => 'Tipus d\'enllaç', + 'Assign automatically a category based on a link' => 'Assignar automàticament una categoria basada en un enllaç', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Nom d\'usuari assignat', + 'Assignee Name' => 'Nom assignat', + 'Groups' => 'Grups', + 'Members of %s' => 'Els membres de %s', + 'New group' => 'Nou grup', + 'Group created successfully.' => 'Grup creat correctament.', + 'Unable to create your group.' => 'No es pot crear el grup.', + 'Edit group' => 'Edita grup', + 'Group updated successfully.' => 'Grup actualitzat correctament.', + 'Unable to update your group.' => 'No es pot actualitzar el seu grup.', + 'Add group member to "%s"' => 'Afegir membre del grup a "%s"', + 'Group member added successfully.' => 'Membre del grup afegit correctament.', + 'Unable to add group member.' => 'No es pot afegeix membre del grup.', + 'Remove user from group "%s"' => 'Eliminar usuari del grup "%s"', + 'User removed successfully from this group.' => 'Usuari eliminat amb èxit d\'aquest grup.', + 'Unable to remove this user from the group.' => 'No es pot eliminar aquest usuari del grup.', + 'Remove group' => 'Elimina el grup', + 'Group removed successfully.' => 'Grup eliminat correctament.', + 'Unable to remove this group.' => 'No es pot eliminar aquest grup.', + 'Project Permissions' => 'Permisos de projectes', + 'Manager' => 'Gerent', + 'Project Manager' => 'Gerent de projectes', + 'Project Member' => 'Membres del projecte', + 'Project Viewer' => 'Visor de projectes', + 'Your account is locked for %d minutes' => 'El seu compte està bloquejada per %d minuts', + 'Invalid captcha' => 'codi d\'imatge no vàlida', + 'The name must be unique' => 'El nom ha de ser únic', + 'View all groups' => 'Veure tots els grups', + 'There is no user available.' => 'No hi ha cap usuari disponible.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Realment vols eliminar l\'usuari "%s" del grup "%s"?', + 'There is no group.' => 'No hi ha cap grup.', + 'Add group member' => 'Afegeix membre del grup', + 'Do you really want to remove this group: "%s"?' => 'Realment vols eliminar aquest grup: "%s"?', + 'There is no user in this group.' => 'No hi ha cap usuari en aquest grup.', + 'Permissions' => 'Permisos', + 'Allowed Users' => 'Els usuaris autoritzats', + 'No specific user has been allowed.' => 'Cap usuari s\'hagi establert.', + 'Role' => 'Rol', + 'Enter user name...' => 'Introdueix el nom d\'usuari ...', + 'Allowed Groups' => 'Grups permesos', + 'No group has been allowed.' => 'Cap grup s\'hagi establert.', + 'Group' => 'Grup', + 'Group Name' => 'Nom del grup', + 'Enter group name...' => 'Introduïu el nom del grup ...', + 'Role:' => 'Rol:', + 'Project members' => 'Els membres del projecte', + '%s mentioned you in the task #%d' => '%s t\'ha esmentat a la tasca #%d', + '%s mentioned you in a comment on the task #%d' => '%s t\'ha mencionat en un comentari a la tasca #%d', + 'You were mentioned in the task #%d' => 'Se t\'ha esmentat a la tasca #%d', + 'You were mentioned in a comment on the task #%d' => 'Se t\'ha mencionat en un comentari sobre la tasca #%d', + 'Estimated hours: ' => 'Hores estimades: ', + 'Actual hours: ' => 'Hores reals: ', + 'Hours Spent' => 'Les hores invertides', + 'Hours Estimated' => 'hores estimat', + 'Estimated Time' => 'Temps estimat', + 'Actual Time' => 'temps real', + 'Estimated vs actual time' => 'Estimat en funció del temps real', + 'RUB - Russian Ruble' => 'RUB - Russia rus', + 'Assign the task to the person who does the action when the column is changed' => 'Assignar la tasca a la persona que fa l\'acció quan es canvia la columna', + 'Close a task in a specific column' => 'Tancar una tasca en una columna específica', + 'Time-based One-time Password Algorithm' => 'Algoritme de contrasenya basat en el temps d\'una sola vegada', + 'Two-Factor Provider: ' => 'Proveïdor de doble factor: ', + 'Disable two-factor authentication' => 'Deshabilita l\'autenticació de dos factors', + 'Enable two-factor authentication' => 'Habilitar l\'autenticació de dos factors', + 'There is no integration registered at the moment.' => 'No hi ha integració registrada en el moment.', + 'Password Reset for Kanboard' => 'Restableix contrasenya per Kanboard', + 'Forgot password?' => 'Has oblidat la contrasenya?', + 'Enable "Forget Password"' => 'Habilita "he oblidat la contrasenya"', + 'Password Reset' => 'Restablir contrasenya', + 'New password' => 'Nova contrasenya', + 'Change Password' => 'Canvia la contrasenya', + 'To reset your password click on this link:' => 'Per restablir la contrasenya, feu clic en aquest enllaç:', + 'Last Password Reset' => 'Darrer restabliment de contrasenya', + 'The password has never been reinitialized.' => 'La contrasenya mai ha estat reiniciada.', + 'Creation' => 'Creació', + 'Expiration' => 'Caducitat', + 'Password reset history' => 'Històric de restabliment de contrasenya', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Totes les tasques de la columna "%s" i del carril "%s" s\'han tancat correctament.', + 'Do you really want to close all tasks of this column?' => 'Vols tancar totes les tasques d\'aquesta columna?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tasca(es) a la columna "%s" i al carril "%s" es tancarà(n).', + 'Close all tasks in this column and this swimlane' => 'Tanqueu totes les tasques d\'aquesta columna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'No s\'ha registrat plugin d\'un mètode de notificació del projecte. Encara es pot configurar les notificacions individuals en el seu perfil d\'usuari.', + 'My dashboard' => 'El meu tauler', + 'My profile' => 'El meu perfil', + 'Project owner: ' => 'Propietari del projecte: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'L\'identificador de projecte és opcional i ha de ser alfanumèric, exemple: MYPROJECT.', + 'Project owner' => 'Propietari del projecte', + 'Personal projects do not have users and groups management.' => 'projectes privats no tenen els usuaris i grups de gestió.', + 'There is no project member.' => 'No hi ha cap membre del projecte.', + 'Priority' => 'Prioritat', + 'Task priority' => 'Prioritat de la tasca', + 'General' => 'General', + 'Dates' => 'Dates', + 'Default priority' => 'Prioritat per defecte', + 'Lowest priority' => 'La prioritat més baixa', + 'Highest priority' => 'La prioritat més alta', + 'Close a task when there is no activity' => 'Tancar una tasca quan no hi ha activitat', + 'Duration in days' => 'Durada en dies', + 'Send email when there is no activity on a task' => 'Envia correu electrònic quan no hi ha activitat en una tasca', + 'Unable to fetch link information.' => 'No és possible obtenir informació d\'enllaç.', + 'Daily background job for tasks' => 'Feina en segon pla diaria per a les tasques', + 'Auto' => 'Auto', + 'Related' => 'Relacionat', + 'Attachment' => 'Adjunt', + 'Web Link' => 'Enllaç web', + 'External links' => 'Enllaços externs', + 'Add external link' => 'Afegeix un enllaç extern', + 'Type' => 'Tipus', + 'Dependency' => 'Dependència', + 'Add internal link' => 'Afegeix un enllaç intern', + 'Add a new external link' => 'Afegeix un nou enllaç extern', + 'Edit external link' => 'Edita enllaç extern', + 'External link' => 'URL', + 'Copy and paste your link here...' => 'Copia i enganxa l\'enllaç aquí ...', + 'URL' => 'URL', + 'Internal links' => 'Els enllaços interns', + 'Assign to me' => 'Assignar a mi', + 'Me' => 'jo', + 'Do not duplicate anything' => 'No dupliquis res', + 'Projects management' => 'Gestió de projectes', + 'Users management' => 'Gestió d\'usuaris', + 'Groups management' => 'Gestió de grups', + 'Create from another project' => 'Crea a partir d\'un altre projecte', + 'open' => 'obert', + 'closed' => 'tancat', + 'Priority:' => 'Prioritat:', + 'Reference:' => 'Referència:', + 'Complexity:' => 'Dificultat:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Columna:', + 'Position:' => 'Posició:', + 'Creator:' => 'Creador:', + 'Time estimated:' => 'Temps estimat:', + '%s hours' => '%s hora', + 'Time spent:' => 'El temps passat:', + 'Created:' => 'Creat:', + 'Modified:' => 'Modificat:', + 'Completed:' => 'Completat:', + 'Started:' => 'Començat:', + 'Moved:' => 'Mogut:', + 'Task #%d' => 'Tasca #%d', + 'Time format' => 'Format d\'hora', + 'Start date: ' => 'Data d\'inici: ', + 'End date: ' => 'Data de fi: ', + 'New due date: ' => 'Nova data de venciment: ', + 'Start date changed: ' => 'Data d\'inici canviada: ', + 'Disable personal projects' => 'Desactivar els projectes privats', + 'Do you really want to remove this custom filter: "%s"?' => 'Realment vols eliminar aquest filtre personalitzat: "%s"?', + 'Remove a custom filter' => 'Treure un filtre a mida', + 'User activated successfully.' => 'L\'usuari ha activat correctament.', + 'Unable to enable this user.' => 'No és possible permetre que aquest usuari.', + 'User disabled successfully.' => 'L\'usuari deshabilitat amb èxit.', + 'Unable to disable this user.' => 'No és possible desactivar aquest usuari.', + 'All files have been uploaded successfully.' => 'Tots els arxius s\'hagin carregat correctament.', + 'The maximum allowed file size is %sB.' => 'La mida màxima de fitxer permesa és %sB.', + 'Drag and drop your files here' => 'Arrossegar i deixar anar els arxius aquí', + 'choose files' => 'triar els arxius', + 'View profile' => 'Veure el perfil', + 'Two Factor' => 'dos factors', + 'Disable user' => 'desactivar usuari', + 'Do you really want to disable this user: "%s"?' => 'Realment vols desactivar aquest usuari: "%s"?', + 'Enable user' => 'Permetre a l\'usuari', + 'Do you really want to enable this user: "%s"?' => 'Realment vols habilitar aquest usuari: "%s"?', + 'Download' => 'Descarregar', + 'Uploaded: %s' => 'Pujat: %s', + 'Size: %s' => 'Mida: %s', + 'Uploaded by %s' => 'Pujada per %s', + 'Filename' => 'Nom de l\'arxiu', + 'Size' => 'Mida', + 'Column created successfully.' => 'Columna creat correctament.', + 'Another column with the same name exists in the project' => 'Una altra columna amb el mateix nom existeix en el projecte', + 'Default filters' => 'filtres predeterminats', + 'Your board doesn\'t have any columns!' => 'El teu tauler no té columnes!', + 'Change column position' => 'Canvi de posició de columna', + 'Switch to the project overview' => 'Canviar a la visió general del projecte', + 'User filters' => 'Els filtres d\'usuari', + 'Category filters' => 'filtres de categories', + 'Upload a file' => 'Pujar un arxiu', + 'View file' => 'Veure arxiu', + 'Last activity' => 'Última activitat', + 'Change subtask position' => 'Subtasca posició de canvi', + 'This value must be greater than %d' => 'Aquest valor ha de ser més gran que %d', + 'Another swimlane with the same name exists in the project' => 'Una altra swimlane amb el mateix nom existeix en el projecte', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Exemple: https://example.kanboard.org/ (utilitzat per generar URL absoluta)', + 'Actions duplicated successfully.' => 'Accions dupliquen amb èxit.', + 'Unable to duplicate actions.' => 'No s\'ha pogut duplicar accions.', + 'Add a new action' => 'Afegeix una nova acció', + 'Import from another project' => 'Importa d\'un altre projecte', + 'There is no action at the moment.' => 'No hi ha cap acció en aquest moment.', + 'Import actions from another project' => 'accions d\'importació d\'un altre projecte', + 'There is no available project.' => 'No hi ha un projecte disponible.', + 'Local File' => 'Fitxer local', + 'Configuration' => 'Configuració', + 'PHP version:' => 'Versió de PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versió del sistema operatiu:', + 'Database version:' => 'Versió de la base de dades:', + 'Browser:' => 'Navegador:', + 'Task view' => 'Vista de tasques', + 'Edit task' => 'Edició de tasques', + 'Edit description' => 'Modifica la descripció', + 'New internal link' => 'Nou enllaç intern', + 'Display list of keyboard shortcuts' => 'Visualització de la llista de dreceres de teclat', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Puja la imatge meu avatar', + 'Remove my image' => 'Traieu la meva imatge', + 'The OAuth2 state parameter is invalid' => 'El paràmetre d\'estat OAuth2 no és vàlid', + 'User not found.' => 'Usuari no trobat.', + 'Search in activity stream' => 'Buscar en el Tràfic d\'activitat', + 'My activities' => 'Les meves activitats', + 'Activity until yesterday' => 'Activitat fins ahir', + 'Activity until today' => 'Activitat fins avui', + 'Search by creator: ' => 'Cerca per creador: ', + 'Search by creation date: ' => 'Cerca per data de creació: ', + 'Search by task status: ' => 'Cerca per estat de la tasca: ', + 'Search by task title: ' => 'Cerca per títol de la tasca: ', + 'Activity stream search' => 'Cerca al fluxe d\'activitat', + 'Projects where "%s" is manager' => 'Projectes on "%s" és gestor', + 'Projects where "%s" is member' => 'Projectes on "%s" és membre', + 'Open tasks assigned to "%s"' => 'Tasques obertes assignades a "%s"', + 'Closed tasks assigned to "%s"' => 'Tasques tancades assignades a "%s"', + 'Assign automatically a color based on a priority' => 'Assignar automàticament un color basat en una prioritat', + 'Overdue tasks for the project(s) "%s"' => 'Tasques endarrerides per al(s) projecte(s) "%s"', + 'Upload files' => 'Pujar arxius', + 'Installed Plugins' => 'Els connectors instal·lats', + 'Plugin Directory' => 'Directori de complements', + 'Plugin installed successfully.' => 'Plug-in instal·lat correctament.', + 'Plugin updated successfully.' => 'Plugin actualitzat correctament.', + 'Plugin removed successfully.' => 'Plugin eliminat correctament.', + 'Subtask converted to task successfully.' => 'Subtasca converteix en tasca amb èxit.', + 'Unable to convert the subtask.' => 'No es pot convertir la subtasca.', + 'Unable to extract plugin archive.' => 'No es pot extreure arxiu connector.', + 'Plugin not found.' => 'Plugin no trobat.', + 'You don\'t have the permission to remove this plugin.' => 'No tens permís per eliminar aquest connector.', + 'Unable to download plugin archive.' => 'No es pot descarregar arxiu connector.', + 'Unable to write temporary file for plugin.' => 'No es pot escriure el fitxer temporal per al connector.', + 'Unable to open plugin archive.' => 'No es pot obrir arxiu de plugin.', + 'There is no file in the plugin archive.' => 'No hi ha cap arxiu a l\'arxiu connector.', + 'Create tasks in bulk' => 'Crear tasques a granel', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'La instància Kanboard no està configurat per a instal·lar plugins des de la interfície d\'usuari.', + 'There is no plugin available.' => 'No hi ha cap complement disponible.', + 'Install' => 'Instal·la', + 'Update' => 'Actualitza', + 'Up to date' => 'Actualitzat', + 'Not available' => 'No disponible', + 'Remove plugin' => 'Eliminar connector', + 'Do you really want to remove this plugin: "%s"?' => 'Realment vols eliminar aquest connector: "%s"?', + 'Uninstall' => 'Suprimeix', + 'Listing' => 'Llistat', + 'Metadata' => 'Metadades', + 'Manage projects' => 'Gestió de projectes', + 'Convert to task' => 'Convertir la tasca', + 'Convert sub-task to task' => 'Converteix sub-tasca a una altra', + 'Do you really want to convert this sub-task to a task?' => 'Vols convertir aquesta sub-tasca a una tasca?', + 'My task title' => 'Títol de la tasca', + 'Enter one task by line.' => 'Introduïu una tasca per línia.', + 'Number of failed login:' => 'Nombre d\'inici de sessió fallit:', + 'Account locked until:' => 'Compte bloquejada fins que:', + 'Email settings' => 'Configuració de correu electrònic', + 'Email sender address' => 'Adreça de correu electrònic del remitent', + 'Email transport' => 'Transport de correu electrònic', + 'Webhook token' => 'Símbol web hook', + 'Project tags management' => 'Gestió de projectes etiquetes', + 'Tag created successfully.' => 'Tag creat correctament.', + 'Unable to create this tag.' => 'No es pot crear aquesta etiqueta.', + 'Tag updated successfully.' => 'Tag actualitzat correctament.', + 'Unable to update this tag.' => 'No es pot actualitzar aquesta etiqueta.', + 'Tag removed successfully.' => 'Tag eliminat correctament.', + 'Unable to remove this tag.' => 'No es pot eliminar aquesta etiqueta.', + 'Global tags management' => 'Gestió global d\'etiquetes', + 'Tags' => 'Etiquetes', + 'Tags management' => 'Gestió d\'etiquetes', + 'Add new tag' => 'Afegeix nova etiqueta', + 'Edit a tag' => 'Edita una etiqueta', + 'Project tags' => 'Variables del projecte', + 'There is no specific tag for this project at the moment.' => 'No hi ha una etiqueta específica per a aquest projecte en aquest moment.', + 'Tag' => 'Etiqueta', + 'Remove a tag' => 'Elimina una etiqueta', + 'Do you really want to remove this tag: "%s"?' => 'Realment vols eliminar aquesta etiqueta: "%s"?', + 'Global tags' => 'etiquetes globals', + 'There is no global tag at the moment.' => 'No hi ha cap variable global en el moment.', + 'This field cannot be empty' => 'Aquest camp no pot estar buit', + 'Close a task when there is no activity in a specific column' => 'Tancar una tasca quan no hi ha activitat en una columna específica', + '%s removed a subtask for the task #%d' => '%s extret una subtasca per a la tasca #%d', + '%s removed a comment on the task #%d' => '%s remogut un comentari a la tasca #%d', + 'Comment removed on task #%d' => 'Comentari eliminat a la tasca #%d', + 'Subtask removed on task #%d' => 'Subtasca eliminat a la tasca #%d', + 'Hide tasks in this column in the dashboard' => 'Amaga tasques en aquesta columna al tauler d\'instruments', + '%s removed a comment on the task %s' => '%s remogut un comentari a la tasca %s', + '%s removed a subtask for the task %s' => '%s extret una subtasca per a la tasca %s', + 'Comment removed' => 'Es va eliminar el comentari', + 'Subtask removed' => 'subtasca retira', + '%s set a new internal link for the task #%d' => '%s va establir un nou enllaç intern per a la tasca #%d', + '%s removed an internal link for the task #%d' => '%s eliminat un enllaç intern per a la tasca #%d', + 'A new internal link for the task #%d has been defined' => 'Un nou enllaç intern per a la tasca #%d s\'han definit', + 'Internal link removed for the task #%d' => 'Enllaç intern eliminat la tasca #%d', + '%s set a new internal link for the task %s' => '%s va establir un nou enllaç intern per a la tasca %s', + '%s removed an internal link for the task %s' => '%s eliminat un enllaç intern per a la tasca %s', + 'Automatically set the due date on task creation' => 'S\'ajusta automàticament la data de venciment a la creació de tasques', + 'Move the task to another column when closed' => 'Mou la tasca a una altra columna quan es tanca', + 'Move the task to another column when not moved during a given period' => 'Mou la tasca a una altra columna quan no es va moure durant un període determinat', + 'Dashboard for %s' => 'Tauler de %s', + 'Tasks overview for %s' => 'Resum de tasques de %s', + 'Subtasks overview for %s' => 'Visió general de subtasques de %s', + 'Projects overview for %s' => 'Visió general del projecte de %s', + 'Activity stream for %s' => 'Fluxe d\'activitat per a %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Assignar un color quan la tasca es mou a un carril específic', + 'Assign a priority when the task is moved to a specific swimlane' => 'Assignar una prioritat quan la tasca es mou a un carril específic', + 'User unlocked successfully.' => 'Usuari desbloquejat amb èxit.', + 'Unable to unlock the user.' => 'No és possible desbloquejar l\'usuari.', + 'Move a task to another swimlane' => 'Mou una tasca a una altra swimlane', + 'Creator Name' => 'nom creador', + 'Time spent and estimated' => 'El temps dedicat i estima', + 'Move position' => 'Mou la posició', + 'Move task to another position on the board' => 'Mou tasca a una altra posició al tauler', + 'Insert before this task' => 'Insereix abans aquesta tasca', + 'Insert after this task' => 'Inserir, després d\'aquesta tasca', + 'Unlock this user' => 'Desbloquejar aquest usuari', + 'Custom Project Roles' => 'Rols d\'encàrrec del projecte', + 'Add a new custom role' => 'Afegeix un nou rol', + 'Restrictions for the role "%s"' => 'Restriccions per al rol "%s"', + 'Add a new project restriction' => 'Afegeix una nova restricció projecte', + 'Add a new drag and drop restriction' => 'Afegeix un nou arrossegar i deixar anar restricció', + 'Add a new column restriction' => 'Afegeix una nova restricció de columna', + 'Edit this role' => 'Edita aquest rol', + 'Remove this role' => 'Elimina aquest rol', + 'There is no restriction for this role.' => 'No hi ha cap restricció per a aquest rol.', + 'Only moving task between those columns is permitted' => 'Només es mou tasques entre aquestes columnes es permet', + 'Close a task in a specific column when not moved during a given period' => 'Tancar una tasca en una columna específica quan no es va moure durant un període determinat', + 'Edit columns' => 'Edita columnes', + 'The column restriction has been created successfully.' => 'La restricció de la columna s\'ha creat correctament.', + 'Unable to create this column restriction.' => 'No es pot crear aquesta restricció columna.', + 'Column restriction removed successfully.' => 'restricció Columna eliminat correctament.', + 'Unable to remove this restriction.' => 'No es pot eliminar aquesta restricció.', + 'Your custom project role has been created successfully.' => 'El seu rol projecte personalitzat s\'ha creat correctament.', + 'Unable to create custom project role.' => 'No es pot crear el rol de projecte personalitzat.', + 'Your custom project role has been updated successfully.' => 'El seu rol projecte personalitzat s\'ha actualitzat correctament.', + 'Unable to update custom project role.' => 'No es pot actualitzar rol projecte personalitzat.', + 'Custom project role removed successfully.' => 'rol projecte personalitzat eliminat correctament.', + 'Unable to remove this project role.' => 'No es pot treure aquest rol projecte.', + 'The project restriction has been created successfully.' => 'La restricció projecte ha estat creat amb èxit.', + 'Unable to create this project restriction.' => 'No es pot crear aquesta restricció projecte.', + 'Project restriction removed successfully.' => 'Projecte restricció eliminat correctament.', + 'You cannot create tasks in this column.' => 'No es poden crear tasques en aquesta columna.', + 'Task creation is permitted for this column' => 'Es permet la creació de tasques per a aquesta columna', + 'Closing or opening a task is permitted for this column' => 'Tancant o obrint una tasca està permesa per a aquesta columna', + 'Task creation is blocked for this column' => 'creació de la tasca està bloquejat per a aquesta columna', + 'Closing or opening a task is blocked for this column' => 'Tancant o obrint una tasca està bloquejat per a aquesta columna', + 'Task creation is not permitted' => 'No es permet la creació de tasques', + 'Closing or opening a task is not permitted' => 'No es permet tancar o obrir una tasca', + 'New drag and drop restriction for the role "%s"' => 'Nova restricció d\'arrossegar i deixar anar per al rol "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Les persones que pertanyen a aquest rol seran capaços de moure les tasques només entre la font i la columna de destinació.', + 'Remove a column restriction' => 'Eliminar una restricció columna', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Realment vols eliminar aquesta restricció de columna: "%s" a "%s"?', + 'New column restriction for the role "%s"' => 'Nova restricció de columna per al rol "%s"', + 'Rule' => 'regla', + 'Do you really want to remove this column restriction?' => 'Vols eliminar aquest criteri de la columna?', + 'Custom roles' => 'Rols personalitzats', + 'New custom project role' => 'Nou rol projecte personalitzat', + 'Edit custom project role' => 'Edita rol projecte personalitzat', + 'Remove a custom role' => 'Eliminar una funció personalitzada', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Realment vols eliminar aquest rol personalitzat: "%s"? Totes les persones assignades a aquest rol es convertiran en membres del projecte.', + 'There is no custom role for this project.' => 'No hi ha una funció personalitzada per a aquest projecte.', + 'New project restriction for the role "%s"' => 'Nova restricció de projecte per al rol "%s"', + 'Restriction' => 'Restricció', + 'Remove a project restriction' => 'Eliminar una restricció projecte', + 'Do you really want to remove this project restriction: "%s"?' => 'Realment vols eliminar aquesta restricció de projecte: "%s"?', + 'Duplicate to multiple projects' => 'Duplica a múltiples projectes', + 'This field is required' => 'Aquest camp és obligatori', + 'Moving a task is not permitted' => 'No està permès moure una tasca', + 'This value must be in the range %d to %d' => 'Aquest valor ha d\'estar en el rang %d a %d', + 'You are not allowed to move this task.' => 'No se li permet moure a aquesta tasca.', + 'API User Access' => 'L\'accés d\'usuari d\'API', + 'Preview' => 'Vista prèvia', + 'Write' => 'escriure', + 'Write your text in Markdown' => 'Escriure el text en Markdown', + 'No personal API access token registered.' => 'Sense accés a l\'API personals símbol registrat.', + 'Your personal API access token is "%s"' => 'La teva clau d\'accés API personal és "%s"', + 'Remove your token' => 'Traieu el testimoni', + 'Generate a new token' => 'Generar un nou token', + 'Showing %d-%d of %d' => 'Mostrant %d-%d de %d', + 'Outgoing Emails' => 'Els correus electrònics sortints', + 'Add or change currency rate' => 'Afegeix o canviar el valor de moneda', + 'Reference currency: %s' => 'moneda de referència: %s', + 'Add custom filters' => 'Afegeix filtres personalitzats', + 'Export' => 'exportació', + 'Add link label' => 'Afegeix etiqueta d\'enllaç', + 'Incompatible Plugins' => 'Connectors incompatibles', + 'Compatibility' => 'Compatibilitat', + 'Permissions and ownership' => 'Permisos i propietat', + 'Priorities' => 'Prioritats', + 'Close this window' => 'Tanca aquesta finestra', + 'Unable to upload this file.' => 'No es pot carregar aquest arxiu.', + 'Import tasks' => 'Importació de tasques', + 'Choose a project' => 'Tria un projecte', + 'Profile' => 'Perfil', + 'Application role' => 'Rol d\'aplicació', + '%d invitations were sent.' => 'S\'han enviat %d invitacions.', + '%d invitation was sent.' => 'S\'han enviat %d invitacions.', + 'Unable to create this user.' => 'No es pot crear aquest usuari.', + 'Kanboard Invitation' => 'Invitació Kanboard', + 'Visible on dashboard' => 'Visible al tauler', + 'Created at:' => 'Creat el:', + 'Updated at:' => 'Actualitzat a:', + 'There is no custom filter.' => 'No hi ha cap filtre personal.', + 'New User' => 'Nou usuari', + 'Authentication' => 'Autenticació', + 'If checked, this user will use a third-party system for authentication.' => 'Si s\'activa, aquest usuari utilitzarà un sistema de tercers per a l\'autenticació.', + 'The password is necessary only for local users.' => 'La contrasenya és necessària només per als usuaris locals.', + 'You have been invited to register on Kanboard.' => 'T\'han convidat a inscriure\'t al Kanboard.', + 'Click here to join your team' => 'Fes clic aquí per unir-te a l\'equip', + 'Invite people' => 'Convidar a més gent', + 'Emails' => 'Correus electrònics', + 'Enter one email address by line.' => 'Introdueix una adreça de correu electrònic per línia.', + 'Add these people to this project' => 'Afegeix a aquestes persones a aquest projecte', + 'Add this person to this project' => 'Afegeix aquesta persona a aquest projecte', + 'Sign-up' => 'Registra\'t', + 'Credentials' => 'Credencials', + 'New user' => 'Nou usuari', + 'This username is already taken' => 'Aquest nom d\'usuari ja està en ús', + 'Your profile must have a valid email address.' => 'El teu perfil ha de tenir una adreça de correu electrònic vàlida.', + 'TRL - Turkish Lira' => 'TRL - Lira turca', + 'The project email is optional and could be used by several plugins.' => 'El correu electrònic del projecte és opcional i pot ser utilitzat per diversos connectors.', + 'The project email must be unique across all projects' => 'El correu electrònic del projecte ha de ser únic en tots els projectes', + 'The email configuration has been disabled by the administrator.' => 'La configuració de correu electrònic ha estat desactivat per l\'administrador.', + 'Close this project' => 'Tanca aquest projecte', + 'Open this project' => 'Obre aquest projecte', + 'Close a project' => 'Tanca un projecte', + 'Do you really want to close this project: "%s"?' => 'Realment vols tancar aquest projecte: "%s"?', + 'Reopen a project' => 'Torna a obrir un projecte', + 'Do you really want to reopen this project: "%s"?' => 'Realment vols reobrir aquest projecte: "%s"?', + 'This project is open' => 'Aquest projecte està obert', + 'This project is closed' => 'Aquest projecte està tancat', + 'Unable to upload files, check the permissions of your data folder.' => 'No es pot pujar arxius, comprovar els permisos de la carpeta de dades.', + 'Another category with the same name exists in this project' => 'Una altra categoria amb el mateix nom existeix en aquest projecte', + 'Comment sent by email successfully.' => 'Comentar enviat per correu electrònic amb èxit.', + 'Sent by email to "%s" (%s)' => 'Enviat per correu electrònic a "%s" (%s)', + 'Unable to read uploaded file.' => 'No es pot llegir el fitxer pujat.', + 'Database uploaded successfully.' => 'Base de dades carregat correctament.', + 'Task sent by email successfully.' => 'Tasca enviat per correu electrònic amb èxit.', + 'There is no category in this project.' => 'No hi ha una categoria en aquest projecte.', + 'Send by email' => 'Envia per correu electrònic', + 'Create and send a comment by email' => 'Per crear i enviar un comentari per correu electrònic', + 'Subject' => 'Tema', + 'Upload the database' => 'Puja la base de dades', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Es podia pujar la base de dades SQLite prèviament descarregat (format gzip).', + 'Database file' => 'arxiu de base', + 'Upload' => 'Pujar', + 'Your project must have at least one active swimlane.' => 'El seu projecte ha de tenir com a mínim un swimlane actiu.', + 'Project: %s' => 'Projecte: %s', + 'Automatic action not found: "%s"' => 'Acció automàtica no trobada: "%s"', + '%d projects' => '%d projectes', + '%d project' => '%d projecte', + 'There is no project.' => 'No hi ha cap projecte.', + 'Sort' => 'Ordena', + 'Project ID' => 'identificació del projecte', + 'Project name' => 'Nom del projecte', + 'Public' => 'Públic', + 'Personal' => 'Privat', + '%d tasks' => '%d tasques', + '%d task' => '%d tasca', + 'Task ID' => 'ID de tasca', + 'Assign automatically a color when due date is expired' => 'Assignar automàticament un color quan ha caducat la data de venciment', + 'Total score in this column across all swimlanes' => 'Puntuació total d\'aquesta columna a través de tots swimlanes', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Pes Argentí', + 'COP - Colombian Peso' => 'COP - Pes colombià', + '%d groups' => '%d grups', + '%d group' => '%d grup', + 'Group ID' => 'ID de grup', + 'External ID' => 'Identificació externa', + '%d users' => '%d usuaris', + '%d user' => '%d usuari', + 'Hide subtasks' => 'Amaga subtasques', + 'Show subtasks' => 'Mostra subtasques', + 'Authentication Parameters' => 'Paràmetres d\'autenticació', + 'API Access' => 'Accés API', + 'No users found.' => 'No es van trobar usuaris.', + 'User ID' => 'ID d\'usuari', + 'Notifications are activated' => 'Notificacions habilitades', + 'Notifications are disabled' => 'Notificacions inhabilitades', + 'User disabled' => 'Usuari deshabilitat', + '%d notifications' => '%d notificacions', + '%d notification' => '%d notificació', + 'There is no external integration installed.' => 'No hi ha integració externa instal·lada.', + 'You are not allowed to update tasks assigned to someone else.' => 'No se li permet posar al dia les tasques assignades a una altra persona.', + 'You are not allowed to change the assignee.' => 'No se li permet canviar el assignat.', + 'Task suppression is not permitted' => 'No es permet la supressió de tasques', + 'Changing assignee is not permitted' => 'No es permet el canvi de assignatari', + 'Update only assigned tasks is permitted' => 'Actualitzar les tasques assignades només es permet', + 'Only for tasks assigned to the current user' => 'Només per a les tasques assignades a l\'usuari actual', + 'My projects' => 'Els meus projectes', + 'You are not a member of any project.' => 'No és membre de cap projecte.', + 'My subtasks' => 'Les meves subtasques', + '%d subtasks' => '%d subtasques', + '%d subtask' => '%d subtasca', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Only moving task between those columns is permitted for tasks assigned to the current user', + '[DUPLICATE]' => '[DUPLICATE]', + 'DKK - Danish Krona' => 'DKK - Corona Danesa', + 'Remove user from group' => 'Eliminar usuari del grup', + 'Assign the task to its creator' => 'Assignar la tasca al seu creador', + 'This task was sent by email to "%s" with subject "%s".' => 'Aquesta tasca s\'ha enviat per correu electrònic a "%s" amb l\'assumpte "%s".', + 'Predefined Email Subjects' => 'Assumptes de correu electrònic predefinits', + 'Write one subject by line.' => 'Escriu un assumpte per línia.', + 'Create another link' => 'Crear un altre enllaç', + 'BRL - Brazilian Real' => 'BRL - Real Brasiler', + 'Add a new Kanboard task' => 'Afegir una nova tasca de Kanboard', + 'Subtask not started' => 'Subtasca no iniciada', + 'Subtask currently in progress' => 'Subtasca actualment en curs', + 'Subtask completed' => 'Subtasca completada', + 'Subtask added successfully.' => 'Subtasca afegida correctament.', + '%d subtasks added successfully.' => '%d subtasques afegides correctament.', + 'Enter one subtask by line.' => 'Introdueix una subtasca per línia.', + 'Predefined Contents' => 'Continguts predefinits', + 'Predefined contents' => 'Continguts predefinits', + 'Predefined Task Description' => 'Descripció de la tasca predefinida', + 'Do you really want to remove this template? "%s"' => 'Realment vols eliminar aquesta plantilla? "%s"', + 'Add predefined task description' => 'Afegir descripció de tasca predefinida', + 'Predefined Task Descriptions' => 'Descripcions de tasques predefinides', + 'Template created successfully.' => 'Plantilla creada correctament.', + 'Unable to create this template.' => 'No s\'ha pogut crear aquesta plantilla.', + 'Template updated successfully.' => 'Plantilla actualitzada correctament.', + 'Unable to update this template.' => 'No s\'ha pogut actualitzar aquesta plantilla.', + 'Template removed successfully.' => 'Plantilla eliminada correctament.', + 'Unable to remove this template.' => 'No s\'ha pogut eliminar aquesta plantilla.', + 'Template for the task description' => 'Plantilla per a la descripció de la tasca', + 'The start date is greater than the end date' => 'La data d\'inici és posterior a la data de finalització', + 'Tags must be separated by a comma' => 'Les etiquetes s\'han de separar per una coma', + 'Only the task title is required' => 'Només el títol de la tasca és obligatori', + 'Creator Username' => 'Nom d\'usuari del creador', + 'Color Name' => 'Nom del color', + 'Column Name' => 'Nom de la columna', + 'Swimlane Name' => 'Nom del carril', + 'Time Estimated' => 'Temps estimat', + 'Time Spent' => 'Temps dedicat', + 'External Link' => 'Enllaç extern', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Aquesta funció habilita el canal iCal, el canal RSS i la vista pública del tauler.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Atura el temporitzador de totes les subtasques quan es mou una tasca a una altra columna', + 'Subtask Title' => 'Títol de la subtasca', + 'Add a subtask and activate the timer when moving a task to another column' => 'Afegir una subtasca i activar el temporitzador quan es mou una tasca a una altra columna', + 'days' => 'dies', + 'minutes' => 'minuts', + 'seconds' => 'segons', + 'Assign automatically a color when preset start date is reached' => 'Assigna automàticament un color quan s\'arriba a la data d\'inici preestablerta', + 'Move the task to another column once a predefined start date is reached' => 'Mou la tasca a una altra columna un cop s\'arriba a una data d\'inici predefinida', + 'This task is now linked to the task %s with the relation "%s"' => 'Aquesta tasca ara està enllaçada a la tasca %s amb la relació "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'S\'ha eliminat l\'enllaç amb la relació "%s" a la tasca %s', + 'Custom Filter:' => 'Filtre personalitzat:', + 'Unable to find this group.' => 'No s\'ha pogut trobar aquest grup.', + '%s moved the task #%d to the column "%s"' => '%s va moure la tasca #%d a la columna "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s va moure la tasca #%d a la posició %d de la columna "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s va moure la tasca #%d al carril "%s"', + '%sh spent' => '%sh dedicades', + '%sh estimated' => '%sh estimades', + 'Select All' => 'Seleccionar tot', + 'Unselect All' => 'Deseleccionar tot', + 'Apply action' => 'Aplicar acció', + 'Move selected tasks to another column or swimlane' => 'Mou les tasques seleccionades a una altra columna o carril', + 'Edit tasks in bulk' => 'Edita tasques en massa', + 'Choose the properties that you would like to change for the selected tasks.' => 'Trieu les propietats que voleu canviar per a les tasques seleccionades.', + 'Configure this project' => 'Configura aquest projecte', + 'Start now' => 'Comença ara', + '%s removed a file from the task #%d' => '%s ha eliminat un fitxer de la tasca #%d', + 'Attachment removed from task #%d: %s' => 'Adjunt eliminat de la tasca #%d: %s', + 'No color' => 'Sense color', + 'Attachment removed "%s"' => 'Adjunt eliminat "%s"', + '%s removed a file from the task %s' => '%s ha eliminat un fitxer de la tasca %s', + 'Move the task to another swimlane when assigned to a user' => 'Mou la tasca a un altre carril quan se l\'assigna a un usuari', + 'Destination swimlane' => 'Carril de destí', + 'Assign a category when the task is moved to a specific swimlane' => 'Assigna una categoria quan la tasca es mou a un carril específic', + 'Move the task to another swimlane when the category is changed' => 'Mou la tasca a un altre carril quan es canvia la categoria', + 'Reorder this column by priority (ASC)' => 'Reordena aquesta columna per prioritat (ASC)', + 'Reorder this column by priority (DESC)' => 'Reordena aquesta columna per prioritat (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Reordena aquesta columna per assignat i prioritat (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Reordena aquesta columna per assignat i prioritat (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Reordena aquesta columna per assignat (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Reordena aquesta columna per assignat (Z-A)', + 'Reorder this column by due date (ASC)' => 'Reordena aquesta columna per data de venciment (ASC)', + 'Reorder this column by due date (DESC)' => 'Reordena aquesta columna per data de venciment (DESC)', + 'Reorder this column by id (ASC)' => 'Reordena aquesta columna per id (ASC)', + 'Reorder this column by id (DESC)' => 'Reordena aquesta columna per id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s va moure la tasca #%d "%s" al projecte "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'La tasca #%d "%s" s\'ha mogut al projecte "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Mou la tasca a una altra columna quan la data de venciment sigui inferior a un cert nombre de dies', + 'Automatically update the start date when the task is moved away from a specific column' => 'Actualitza automàticament la data d\'inici quan la tasca es mou fora d\'una columna específica', + 'HTTP Client:' => 'Client HTTP:', + 'Assigned' => 'Assignat', + 'Task limits apply to each swimlane individually' => 'Els límits de tasques s\'apliquen a cada carril individualment', + 'Column task limits apply to each swimlane individually' => 'Els límits de tasques de columna s\'apliquen a cada carril individualment', + 'Column task limits are applied to each swimlane individually' => 'Els límits de tasques de columna s\'apliquen a cada carril individualment', + 'Column task limits are applied across swimlanes' => 'Els límits de tasques de columna s\'apliquen a tots els carrils', + 'Task limit: ' => 'Límit de tasques:', + 'Change to global tag' => 'Canvia a etiqueta global', + 'Do you really want to make the tag "%s" global?' => 'Realment vols que l\'etiqueta "%s" sigui global?', + 'Enable global tags for this project' => 'Habilita etiquetes globals per a aquest projecte', + 'Group membership(s):' => 'Membre(s) del grup:', + '%s is a member of the following group(s): %s' => '%s és membre del(s) següent(s) grup(s): %s', + '%d/%d group(s) shown' => '%d/%d grup(s) mostrat(s)', + 'Subtask creation or modification' => 'Creació o modificació de subtasques', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Assigna la tasca a un usuari concret quan es mou a un carril específic', + 'Comment' => 'Comentari', + 'Collapse vertically' => 'Replega verticalment', + 'Expand vertically' => 'Expandeix verticalment', + 'MXN - Mexican Peso' => 'MXN - Peso Mexicà', + 'Estimated vs actual time per column' => 'Temps estimat vs temps real per columna', + 'HUF - Hungarian Forint' => 'HUF - Fòrint Hongarès', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Has de seleccionar un fitxer per pujar com a avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'El fitxer que has pujat no és una imatge vàlida! (Només es permeten *.gif, *.jpg, *.jpeg i *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Estableix automàticament la data de venciment quan la tasca es mou fora d\'una columna específica', + 'No other projects found.' => 'No s\'han trobat altres projectes.', + 'Tasks copied successfully.' => 'Tasques copiades correctament.', + 'Unable to copy tasks.' => 'No s\'han pogut copiar les tasques.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema clar', + 'Dark theme' => 'Tema fosc', + 'Automatic theme - Sync with system' => 'Tema automàtic - Sincronitza amb el sistema', + 'Application managers or more' => 'Gestors d\'aplicació o més', + 'Administrators' => 'Administradors', + 'Visibility:' => 'Visibilitat:', + 'Standard users' => 'Usuaris estàndard', + 'Visibility is required' => 'La visibilitat és obligatòria', + 'The visibility should be an app role' => 'La visibilitat hauria de ser una funció de l\'aplicació', + 'Reply' => 'Respondre', + '%s wrote: ' => '%s va escriure: ', + 'Number of visible tasks in this column and swimlane' => 'Nombre de tasques visibles en aquesta columna i carril', + 'Number of tasks in this swimlane' => 'Nombre de tasques en aquest carril', + 'Unable to find another subtask in progress, you can close this window.' => 'No es pot trobar una altra subtasca en curs, podeu tancar aquesta finestra.', + 'This theme is invalid' => 'Aquest tema no és vàlid', + 'This role is invalid' => 'Aquest rol no és vàlid', + 'This timezone is invalid' => 'Aquesta zona horària no és vàlida', + 'This language is invalid' => 'Aquesta llengua no és vàlida', + 'This URL is invalid' => 'Aquesta URL no és vàlida', + 'Date format invalid' => 'Format de data no vàlid', + 'Time format invalid' => 'Format d\'hora no vàlid', + 'Invalid Mail transport' => 'Transport de correu no vàlid', + 'Color invalid' => 'Color no vàlid', + 'This value must be greater or equal to %d' => 'Aquest valor ha de ser major o igual que %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Afegeix un BOM al principi del fitxer (requerit per a Microsoft Excel)', + 'Just add these tag(s)' => 'Només afegeix aquestes etiqueta(es)', + 'Remove internal link(s)' => 'Eliminar enllaç(os) intern(s)', + 'Import tasks from another project' => 'Importa tasques d\'un altre projecte', + 'Select the project to copy tasks from' => 'Selecciona el projecte del qual copiar les tasques', + 'The total maximum allowed attachments size is %sB.' => 'La mida màxima total permesa per als adjunts és %sB.', + 'Add attachments' => 'Afegir adjunts', + 'Task #%d "%s" is overdue' => 'Tasca #%d "%s" ha caducat', + 'Enable notifications by default for all new users' => 'Habilitar notificacions per defecte per a tots els nous usuaris', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Assigna la tasca al seu creador per a columnes específiques si no s\'ha definit cap responsable manualment', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Assigna una tasca a l\'usuari connectat en canviar de columna a la columna especificada si no hi ha cap usuari assignat', +]; diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php new file mode 100644 index 0000000..bef3c46 --- /dev/null +++ b/app/Locale/cs_CZ/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Žádné', + 'Edit' => 'Editovat', + 'Remove' => 'Odstranit', + 'Yes' => 'Ano', + 'No' => 'Ne', + 'cancel' => 'ZruÅ¡it', + 'or' => 'nebo', + 'Yellow' => 'Žlutá', + 'Blue' => 'Modrá', + 'Green' => 'Zelená', + 'Purple' => 'Fialová', + 'Red' => 'ÄŒervená', + 'Orange' => 'Oranžová', + 'Grey' => 'Å edá', + 'Brown' => 'HnÄ›dá', + 'Deep Orange' => 'TmavÄ› oranžová', + 'Dark Grey' => 'TmavÄ› Å¡edá', + 'Pink' => 'Růžová', + 'Teal' => 'Modrozelená', + 'Cyan' => 'Tyrkysová', + 'Lime' => 'Limetková', + 'Light Green' => 'SvÄ›tle zelená', + 'Amber' => 'Jantarová', + 'Save' => 'Uložit', + 'Login' => 'PÅ™ihlásit se', + 'Official website:' => 'Oficiální stránky:', + 'Unassigned' => 'NepÅ™iÅ™azeno', + 'View this task' => 'Zobrazit úkol', + 'Remove user' => 'Odebrat uživatele', + 'Do you really want to remove this user: "%s"?' => 'Opravdu chcete odebrat uživatele: "%s"?', + 'All users' => 'VÅ¡ichni uživatelé', + 'Username' => 'Uživatelské jméno', + 'Password' => 'Heslo', + 'Administrator' => 'Administrátor', + 'Sign in' => 'PÅ™ihlásit', + 'Users' => 'Uživatelé', + 'Forbidden' => 'Zakázáno', + 'Access Forbidden' => 'Přístup zakázán', + 'Edit user' => 'Upravit uživatele', + 'Logout' => 'Odhlásit', + 'Bad username or password' => 'Chybné uživatelské jméno nebo heslo', + 'Edit project' => 'Editovat projekt', + 'Name' => 'Jméno', + 'Projects' => 'Projekty', + 'No project' => 'Žádný projekt', + 'Project' => 'Projekt', + 'Status' => 'Stav', + 'Tasks' => 'Úkoly', + 'Board' => 'NástÄ›nka', + 'Actions' => 'Akce', + 'Inactive' => 'Neaktivní', + 'Active' => 'Aktivní', + 'Unable to update this board.' => 'NástÄ›nku není možné aktualizovat', + 'Disable' => 'Zakázat', + 'Enable' => 'Povolit', + 'New project' => 'Nový projekt', + 'Do you really want to remove this project: "%s"?' => 'Opravdu chcete odstranit projekt: "%s"?', + 'Remove project' => 'Odstranit projekt', + 'Edit the board for "%s"' => 'Editace nástÄ›nky pro "%s" ', + 'Add a new column' => 'PÅ™idat nový sloupec', + 'Title' => 'Název', + 'Assigned to %s' => 'PÅ™iÅ™azeno uživateli: %s', + 'Remove a column' => 'Odstranit sloupec', + 'Unable to remove this column.' => 'Tento sloupec nelze odstranit', + 'Do you really want to remove this column: "%s"?' => 'Opravdu chcete odstranit tento sloupec: "%s"?', + 'Settings' => 'Nastavení', + 'Application settings' => 'Nastavení aplikace', + 'Language' => 'ÄŒeÅ¡tina', + 'Webhook token:' => 'Webhook token:', + 'API token:' => 'API token:', + 'Database size:' => 'Velikost databáze:', + 'Download the database' => 'Stáhnout databázi', + 'Optimize the database' => 'Optimalizovat databázi', + '(VACUUM command)' => '(příkaz VACUUM)', + '(Gzip compressed Sqlite file)' => '(Gzip komprimovaný soubor Sqlite)', + 'Close a task' => 'Zavřít úkol', + 'Column' => 'Sloupec', + 'Color' => 'Barva', + 'Assignee' => 'PÅ™iÅ™azeno uživateli', + 'Create another task' => 'VytvoÅ™it další úkol', + 'New task' => 'Nový úkol', + 'Open a task' => 'Otevřít úkol', + 'Do you really want to open this task: "%s"?' => 'Opravdu chcete znovuotevřít tento úkol: "%s"?', + 'Back to the board' => 'ZpÄ›t na nástÄ›nku', + 'There is nobody assigned' => 'Není pÅ™iÅ™azeno žádnému uživateli', + 'Column on the board:' => 'Sloupec na tabuli:', + 'Close this task' => 'Uzavřít úkol', + 'Open this task' => 'Otevřít tento úkol', + 'There is no description.' => 'Bez popisu', + 'Add a new task' => 'PÅ™idat nový úkol', + 'The username is required' => 'Uživatelské jméno je vyžadováno', + 'The maximum length is %d characters' => 'Maximální délka je %d znaků', + 'The minimum length is %d characters' => 'Minimální délka je %d znaků', + 'The password is required' => 'Heslo je vyžadováno', + 'This value must be an integer' => 'Je vyžadována Äíselná hodnota', + 'The username must be unique' => 'Uživatelské jméno musí být jedineÄné', + 'The user id is required' => 'Uživatelské ID je vyžadováno', + 'Passwords don\'t match' => 'Heslo se neshoduje', + 'The confirmation is required' => 'Je vyžadováno potvrzení', + 'The project is required' => 'Projekt je vyžadován', + 'The id is required' => 'ID je vyžadováno', + 'The project id is required' => 'ID projektu je vyžadováno', + 'The project name is required' => 'Jméno projektu je vyžadováno', + 'The title is required' => 'Nadpis je vyžadován', + 'Settings saved successfully.' => 'Nastavení bylo úspěšnÄ› uloženo', + 'Unable to save your settings.' => 'VaÅ¡e nastavení nelze uložit.', + 'Database optimization done.' => 'Optimalizace databáze byla provedena.', + 'Your project has been created successfully.' => 'Projekt byl úspěšnÄ› vytvoÅ™en.', + 'Unable to create your project.' => 'Projekt nelze vytvoÅ™it.', + 'Project updated successfully.' => 'Projekt byl úspěšnÄ› aktualizován', + 'Unable to update this project.' => 'Projekt nebylo možné aktualizovat.', + 'Unable to remove this project.' => 'Projekt nebylo možné odstranit.', + 'Project removed successfully.' => 'Projekt byl odstranÄ›n.', + 'Project activated successfully.' => 'Projekt byl povolen.', + 'Unable to activate this project.' => 'Aktivace projektu selhala.', + 'Project disabled successfully.' => 'Projekt byl zakázán.', + 'Unable to disable this project.' => 'Zakázání projektu selhalo.', + 'Unable to open this task.' => 'Nelze otevÅ™ení tento úkol.', + 'Task opened successfully.' => 'Úkol byl úspěšnÄ› otevÅ™en.', + 'Unable to close this task.' => 'Nelze uzavřít tento úkol.', + 'Task closed successfully.' => 'Úkol byl úspěšnÄ› uzavÅ™en.', + 'Unable to update your task.' => 'Aktualizace úkolu se nezdaÅ™ila.', + 'Task updated successfully.' => 'Úkol byl úspěšnÄ› aktualizován.', + 'Unable to create your task.' => 'Úkol nelze vytvoÅ™it.', + 'Task created successfully.' => 'Úkol byl úspěšnÄ› vytvoÅ™en.', + 'User created successfully.' => 'Uživatel byl úspěšnÄ› vytvoÅ™en.', + 'Unable to create your user.' => 'Uživatele nebylo možné vytvoÅ™it.', + 'User updated successfully.' => 'Uživatel byl úspěšnÄ› aktualizován.', + 'User removed successfully.' => 'Uživatel byl vymazán.', + 'Unable to remove this user.' => 'Uživatele nebylo možné odebrat.', + 'Board updated successfully.' => 'NástÄ›nka byla úspěšnÄ› aktualizována.', + 'Ready' => 'PÅ™ipraveno', + 'Backlog' => 'Nevyřízené', + 'Work in progress' => 'V Å™eÅ¡ení', + 'Done' => 'DokonÄeno', + 'Application version:' => 'Verze aplikace:', + 'Id' => 'ID', + 'Public link' => 'VeÅ™ejný odkaz', + 'Timezone' => 'ÄŒasová zóna', + 'Sorry, I didn\'t find this information in my database!' => 'Omlouváme se, tuto informaci nelze nalézt!', + 'Page not found' => 'Stránka nenalezena', + 'Complexity' => 'Složitost', + 'Task limit' => 'Maximální poÄet úkolů', + 'Task count' => 'PoÄet úkolů', + 'User' => 'Uživatel', + 'Comments' => 'Komentáře', + 'Comment is required' => 'Komentář je vyžadován', + 'Comment added successfully.' => 'Komentář byl úspěšnÄ› pÅ™idán.', + 'Unable to create your comment.' => 'Komentář nelze vytvoÅ™it.', + 'Due Date' => 'Datum splnÄ›ní', + 'Invalid date' => 'Neplatné datum', + 'Automatic actions' => 'Automaticky vykonávané akce', + 'Your automatic action has been created successfully.' => 'VaÅ¡e akce byla úspěšnÄ› vytvoÅ™ena.', + 'Unable to create your automatic action.' => 'Vaší akci nebylo možné vytvoÅ™it.', + 'Remove an action' => 'Odstranit akci', + 'Unable to remove this action.' => 'Tuto akci nelze odstranit.', + 'Action removed successfully.' => 'Akce byla úspěšnÄ› odstranÄ›na.', + 'Automatic actions for the project "%s"' => 'Automaticky vykonávané akce pro projekt "%s"', + 'Add an action' => 'PÅ™idat akci', + 'Event name' => 'Název události', + 'Action' => 'Akce', + 'Event' => 'Událost', + 'When the selected event occurs execute the corresponding action.' => 'Kdykoliv se vybraná událost objeví, vykonat odpovídající akci.', + 'Next step' => 'Další krok', + 'Define action parameters' => 'Definovat parametry akce', + 'Do you really want to remove this action: "%s"?' => 'SkuteÄnÄ› chcete odebrat tuto akci: "%s"?', + 'Remove an automatic action' => 'Odebrat automaticky provádÄ›nou akci', + 'Assign the task to a specific user' => 'PÅ™iÅ™adit tento úkol konkrétnímu uživateli', + 'Assign the task to the person who does the action' => 'PÅ™iÅ™adit úkol osobÄ›, která akci provádí', + 'Duplicate the task to another project' => 'Duplikovat úkol do jiného projektu', + 'Move a task to another column' => 'PÅ™esun úkolu do jiného sloupce', + 'Task modification' => 'Modifikace úkolu', + 'Task creation' => 'Vytváření úkolu', + 'Closing a task' => 'UzavÅ™ení úkolu', + 'Assign a color to a specific user' => 'PÅ™iÅ™adit barvu konkrétnímu uživateli', + 'Position' => 'Pozice', + 'Duplicate to project' => 'VytvoÅ™it kopii v jiném projektu', + 'Duplicate' => 'VytvoÅ™it kopii', + 'Link' => 'Odkaz', + 'Comment updated successfully.' => 'Komentář byl úspěšnÄ› aktualizován.', + 'Unable to update your comment.' => 'Nelze upravit Váš komentář.', + 'Remove a comment' => 'Odebrat komentář', + 'Comment removed successfully.' => 'Komentář byl smazán.', + 'Unable to remove this comment.' => 'Komentář nelze odebrat.', + 'Do you really want to remove this comment?' => 'SkuteÄnÄ› chcete odebrat tento komentář?', + 'Current password for the user "%s"' => 'Aktuální heslo pro uživatele "%s"', + 'The current password is required' => 'Heslo je vyžadováno', + 'Wrong password' => 'Neplatné heslo', + 'Unknown' => 'Neznámý', + 'Last logins' => 'Poslední pÅ™ihlášení', + 'Login date' => 'Datum pÅ™ihlášení', + 'Authentication method' => 'AutentifikaÄní metoda', + 'IP address' => 'IP adresa', + 'User agent' => 'User Agent', + 'Persistent connections' => 'Trvalé pÅ™ipojení', + 'No session.' => 'doposud žádná relace.', + 'Expiration date' => 'Datum expirace', + 'Remember Me' => 'Zapamatovat si', + 'Creation date' => 'Datum vytvoÅ™ení', + 'Everybody' => 'Kdokoliv', + 'Open' => 'OtevÅ™ené', + 'Closed' => 'UzavÅ™ené', + 'Search' => 'Vyhledat', + 'Nothing found.' => 'Nenalezena žádná položka.', + 'Due date' => 'Plánovaný termín', + 'Description' => 'Podrobný popis', + '%d comments' => '%d komentářů', + '%d comment' => '%d komentář', + 'Email address invalid' => 'Neplatná e-mailová adresa', + 'Your external account is not linked anymore to your profile.' => 'Váš externí úÄet již není propojen s vaším profilem.', + 'Unable to unlink your external account.' => 'Nelze odpojit externí úÄet.', + 'External authentication failed' => 'Selhalo externí ověřování', + 'Your external account is linked to your profile successfully.' => 'Váš externí úÄet je úspěšnÄ› propojen s vaším profilem.', + 'Email' => 'E-Mail', + 'Task removed successfully.' => 'Úkol byl úspěšnÄ› odebrán.', + 'Unable to remove this task.' => 'Tento úkol nelze odebrat.', + 'Remove a task' => 'Odebrat úkol', + 'Do you really want to remove this task: "%s"?' => 'Opravdu chcete odebrat úkol: "%s"?', + 'Assign automatically a color based on a category' => 'Automaticky pÅ™iÅ™adit barvu v závislosti na kategorii', + 'Assign automatically a category based on a color' => 'Automaticky pÅ™iÅ™adit kategorii v závislosti na barvÄ›', + 'Task creation or modification' => 'Vytváření nebo úprava úkolu', + 'Category' => 'Kategorie', + 'Category:' => 'Kategorie:', + 'Categories' => 'Kategorie', + 'Your category has been created successfully.' => 'Kategorie byla úspěšnÄ› vytvoÅ™ena.', + 'This category has been updated successfully.' => 'Kategorie byla úspěšnÄ› aktualizována.', + 'Unable to update this category.' => 'Kategorii nelze aktualizovat.', + 'Remove a category' => 'Odstranit kategorii', + 'Category removed successfully.' => 'Kategorie byla odstranÄ›na.', + 'Unable to remove this category.' => 'Kategorie nemhla být odstranÄ›na.', + 'Category modification for the project "%s"' => 'Aktualizace kategoire pro projekt "%s" ', + 'Category Name' => 'Název kategorie', + 'Add a new category' => 'PÅ™idat kategorii', + 'Do you really want to remove this category: "%s"?' => 'SkuteÄnÄ› chcete odebrat kategorii: "%s"?', + 'All categories' => 'VÅ¡echny kategorie', + 'No category' => 'Žádná kategorie', + 'The name is required' => 'Název je vyžadován', + 'Remove a file' => 'Odstranit sougor', + 'Unable to remove this file.' => 'Soubor nelze odebrat.', + 'File removed successfully.' => 'Soubor byl úspěšnÄ› odebrán.', + 'Attach a document' => 'Vložit dokument', + 'Do you really want to remove this file: "%s"?' => 'SkuteÄnÄ› chcete odebrat soubor: "%s"?', + 'Attachments' => 'Přílohy', + 'Edit the task' => 'Upravit úkol', + 'Add a comment' => 'PÅ™idat komentář', + 'Edit a comment' => 'Upravit komentář', + 'Summary' => 'Souhrn', + 'Time tracking' => 'Sledování Äasu', + 'Estimate:' => 'Odhad:', + 'Spent:' => 'Stráveno:', + 'Do you really want to remove this sub-task?' => 'SkuteÄnÄ› chcete odebrat dílÄí úkol?', + 'Remaining:' => 'Zbývá:', + 'hours' => 'hodin', + 'estimated' => 'odhadnuto', + 'Sub-Tasks' => 'DílÄí úkoly', + 'Add a sub-task' => 'PÅ™idat dílÄí úkol', + 'Original estimate' => 'ÄŒasový odhad', + 'Create another sub-task' => 'VytvoÅ™it další dílÄí úkol', + 'Time spent' => 'Strávený Äas', + 'Edit a sub-task' => 'Upravid dílÄí úkol', + 'Remove a sub-task' => 'Odstranit dílÄí úkol', + 'The time must be a numeric value' => 'Zadejte numerickou hodnotu Äasu', + 'Todo' => 'Seznam úkolů', + 'In progress' => 'Zpracováváme', + 'Sub-task removed successfully.' => 'DílÄí úkol byl smazán.', + 'Unable to remove this sub-task.' => 'Tento dílÄí úkol nelze odebrat.', + 'Sub-task updated successfully.' => 'DílÄí úkol byl aktualizován.', + 'Unable to update your sub-task.' => 'Nelze aktualizovat dílÄí úkol.', + 'Unable to create your sub-task.' => 'Nelze vytvoÅ™it dílÄí úkol.', + 'Maximum size: ' => 'Maximální velikost: ', + 'Display another project' => 'Zobrazit jiný projekt', + 'Created by %s' => 'VytvoÅ™eno uživatelem %s', + 'Tasks Export' => 'Export úkolů', + 'Start Date' => 'PoÄáteÄní datum', + 'Execute' => 'Spustit', + 'Task Id' => 'Úkol ID', + 'Creator' => 'Vlastník', + 'Modification date' => 'Datum úpravy', + 'Completion date' => 'Datum dokonÄení', + 'Clone' => 'Kopie', + 'Project cloned successfully.' => 'Kopie projektu byla úspěšnÄ› vytvoÅ™ena.', + 'Unable to clone this project.' => 'Kopii projektu nelze vytvoÅ™it.', + 'Enable email notifications' => 'Povolit upozornÄ›ní pomocí e-mailů', + 'Task position:' => 'PoÅ™adí úkolu:', + 'The task #%d has been opened.' => 'Úkol #%d byl znovu otevÅ™en.', + 'The task #%d has been closed.' => 'Úkol #%d byl uzavÅ™en.', + 'Sub-task updated' => 'DílÄí úkol byl aktualizován', + 'Title:' => 'Nadpis:', + 'Status:' => 'Stav:', + 'Assignee:' => 'PÅ™iÅ™azeno:', + 'Time tracking:' => 'Sledování Äasu:', + '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 comment' => 'Nový komentář', + 'Comment updated' => 'Komentář byl aktualizován.', + 'New subtask' => 'Nový dílÄí úkol', + 'I only want to receive notifications for these 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', + 'Disable public access' => 'Zakázat veÅ™ejný přístup', + 'Enable public access' => 'Povolit veÅ™ejný přístup', + 'Public access disabled' => 'VeÅ™ejný přístup zakázán', + 'Move the task to another project' => 'PÅ™esunout úkol do jiného projektu', + 'Move to project' => 'PÅ™esunout do jiného projektu', + 'Do you really want to duplicate this task?' => 'Opravdu chcete vytoÅ™it kopii tohoto úkolu?', + 'Duplicate a task' => 'VytvoÅ™it kopii úkolu', + 'External accounts' => 'Externí úÄty', + 'Account type' => 'typ úÄtu', + 'Local' => 'Lokální', + 'Remote' => 'Vzdálený', + 'Enabled' => 'Povoleno', + 'Disabled' => 'Zakázáno', + 'Login:' => 'Uživatelské jméno:', + 'Full Name:' => 'Jméno:', + 'Email:' => 'e-mail', + 'Notifications:' => 'UpozornÄ›ní:', + 'Notifications' => 'UpozornÄ›ní', + 'Account type:' => 'Typ úÄtu:', + 'Edit profile' => 'Upravit profil', + 'Change password' => 'ZmÄ›nit heslo', + 'Password modification' => 'ZmÄ›na hesla', + 'External authentications' => 'Vzdálená autorizace', + 'Never connected.' => 'Zatím nikdy nespojen.', + 'No external authentication enabled.' => 'Není povolena žádná vzdálená autorizace.', + 'Password modified successfully.' => 'Heslo bylo úspěšnÄ› zmÄ›nÄ›no.', + 'Unable to change the password.' => 'Nelze zmÄ›nit heslo.', + 'Change category' => 'ZmÄ›nit kategorii', + '%s updated the task %s' => '%s aktualizoval úkol %s ', + '%s opened the task %s' => '%s znovu otevÅ™el úkol %s ', + '%s moved the task %s to the position #%d in the column "%s"' => '%s pÅ™esunul úkol %s na pozici #%d ve sloupci "%s" ', + '%s moved the task %s to the column "%s"' => '%s pÅ™esunul úkol %s do sloupce "%s" ', + '%s created the task %s' => '%s vytvoÅ™il úkol %s ', + '%s closed the task %s' => '%s uzavÅ™el úkol %s ', + '%s created a subtask for the task %s' => '%s vytvoÅ™il dílÄí úkol pro úkol %s ', + '%s updated a subtask for the task %s' => '%s aktualizoval dílÄí úkol pro úkol %s ', + 'Assigned to %s with an estimate of %s/%sh' => 'PÅ™iÅ™azeno uživateli %s s Äasovým odhadem práce %s/%s dní', + 'Not assigned, estimate of %sh' => 'NepÅ™iÅ™azeno, Äasový odhad práce je %s dní', + '%s updated a comment on the task %s' => '%s aktualizoval komentář k úkolu %s ', + '%s commented the task %s' => '%s pÅ™idal komentář k úkolu %s ', + '%s\'s activity' => 'Aktivity projektu %s', + 'RSS feed' => 'RSS kanál', + '%s updated a comment on the task #%d' => '%s aktualizoval komnetář k úkolu #%d ', + '%s commented on the task #%d' => '%s pÅ™idal komentář k úkolu #%d ', + '%s updated a subtask for the task #%d' => '%s aktualizoval dílÄí úkol úkolu #%d ', + '%s created a subtask for the task #%d' => '%s vytvoÅ™il dílÄí úkol úkolu #%d ', + '%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 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 changed the assignee of the task #%d to %s' => '%s zmÄ›nil pÅ™idÄ›lení úkolu #%d na uživatele %s', + '%s changed the assignee of the task %s to %s' => '%s zmÄ›nil pÅ™idÄ›lení úkolu %s na uživatele %s', + 'New password for the user "%s"' => 'Nové heslo pro uživatele "%s"', + 'Choose an event' => 'Vybrat událost', + 'Create a task from an external provider' => 'VytvoÅ™it úkol externím poskytovatelem', + 'Change the assignee based on an external username' => 'ZmÄ›nit pÅ™iÅ™azení uživatele závislé na externím uživateli', + 'Change the category based on an external label' => 'ZmÄ›nit kategorii závislou na externím popisku', + 'Reference' => 'Reference', + 'Label' => 'ZnaÄka', + 'Database' => 'Databáze', + 'About' => 'O projektu', + 'Database driver:' => 'Databázový ovladaÄ:', + 'Board settings' => 'Nastavení nástÄ›nky', + 'Webhook settings' => 'Webhook nastavení', + 'Reset token' => 'Token reset', + 'API endpoint:' => 'API endpoint', + 'Refresh interval for personal board' => 'Interval automatického obnovování pro osobní nástÄ›nky', + 'Refresh interval for public board' => 'Interval automatického obnovování pro veÅ™ejné nástÄ›nky', + 'Task highlight period' => 'Úkol zvýraznÄ›ného období', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Interval (v sekundách), ve kterém je považovány úpravy úkolů za aktuální (0 pro zakázání, 2 dny ve výchozím nastavení)', + 'Frequency in second (60 seconds by default)' => 'Frekvence v sekundách (60 sekund ve výchozím nastavení)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvence v sekundách (0 pro zákaz této vlastnosti, 10 sekund ve výchozím nastavení)', + 'Application URL' => 'URL aplikace', + 'Token regenerated.' => 'Token byl opÄ›tovnÄ› generován.', + 'Date format' => 'Formát data', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formát je vždy akceptován, například: "%s" a "%s"', + 'New personal project' => 'Nový osobní projekt', + 'This project is personal' => 'Tento projekt je osobní', + 'Add' => 'PÅ™idat', + 'Start date' => 'PoÄáteÄní datum', + 'Time estimated' => 'Odhadovaný Äas', + 'There is nothing assigned to you.' => 'Nemáte pÅ™iÅ™azenou žádnou položku.', + 'My tasks' => 'Moje úkoly', + 'Activity stream' => 'PÅ™ehled aktivit', + 'Dashboard' => 'NástÄ›nka', + 'Confirmation' => 'Potvrzení', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'VytvoÅ™it komentář pomocí externího poskytovatele', + 'Project management' => 'Správa projektů', + 'Columns' => 'Sloupce', + 'Task' => 'Úkol', + 'Percentage' => 'Procenta', + 'Number of tasks' => 'PoÄet úkolů', + 'Task distribution' => 'RozdÄ›lení úkolů', + 'Analytics' => 'Analýza', + 'Subtask' => 'DílÄí úkoly', + 'User repartition' => 'RozdÄ›lení podle uživatelů', + '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.', + 'Previous' => 'PÅ™edchozí', + 'The id must be an integer' => 'ID musí být celé Äíslo', + 'The project id must be an integer' => 'ID projektu musí být celé Äíslo', + 'The status must be an integer' => 'Status musí být celé Äíslo', + 'The subtask id is required' => 'Je požadováno id dílÄího úkolu', + 'The subtask id must be an integer' => 'ID dílÄího úkolu musí být Äíslo', + 'The task id is required' => 'ID úkolu je povinné', + 'The task id must be an integer' => 'ID úkolu musí být Äíslo', + 'The user id must be an integer' => 'ID uživatele musí být Äíslo', + 'This value is required' => 'Hodnota je povinná', + 'This value must be numeric' => 'Hodnota musí být Äíselná', + 'Unable to create this task.' => 'Nelze vytvoÅ™it tento úkol', + 'Cumulative flow diagram' => 'Kumulativní diagram', + 'Daily project summary' => 'Denní pÅ™ehledy', + 'Daily project summary export' => 'Export denních pÅ™ehledů', + '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', + 'Add a new swimlane' => 'PÅ™idat novou dráhu', + 'Default swimlane' => 'Výchozí dráha', + 'Do you really want to remove this swimlane: "%s"?' => 'Opravdu si pÅ™ejete odstranit tuto dráhu: "%s"?', + 'Inactive swimlanes' => 'Neaktivní dráha', + 'Remove a swimlane' => 'Odstranit dráhu', + 'Swimlane modification for the project "%s"' => 'ZmÄ›ny dráhy pro projekt "%s"', + 'Swimlane removed successfully.' => 'Dráha byla odstranÄ›na.', + 'Swimlanes' => 'Dráhy', + 'Swimlane updated successfully.' => 'Dráha byla upravena.', + 'Unable to remove this swimlane.' => 'Tuto dráhu nelze odstranit.', + 'Unable to update this swimlane.' => 'Tuto dráhu nelze upravit.', + 'Your swimlane has been created successfully.' => 'Dráha byla vytvoÅ™ena.', + 'Example: "Bug, Feature Request, Improvement"' => 'Například: "Chyba", "Nápad", "Požadavek"...', + 'Default categories for new projects (Comma-separated)' => 'Výchozí kategorie pro nové projekty (oddÄ›lené Äárkou)', + 'Integrations' => 'Integrace', + 'Integration with third-party services' => 'Integrace se službami tÅ™etích stran', + 'Subtask Id' => 'DílÄí úkol Id', + 'Subtasks' => 'DílÄí úkoly', + 'Subtasks Export' => 'Export dílÄích úkolů', + 'Task Title' => 'Název úkolu', + 'Untitled' => 'bez názvu', + 'Application default' => 'Standardní hodnoty', + 'Language:' => 'Jazyk:', + 'Timezone:' => 'ÄŒasová zóna:', + 'All columns' => 'VÅ¡echny sloupce', + 'Next' => 'Další', + '#%d' => '#%d', + 'All swimlanes' => 'Alle Swimlanes', + 'All colors' => 'VÅ¡echny barvy', + 'Moved to column %s' => 'PÅ™esunuto do sloupce %s ', + 'User dashboard' => 'NástÄ›nka uživatele', + 'Allow only one subtask in progress at the same time for a user' => 'Umožnit uživateli práci pouze na jednom dílÄím úkolu ve stejném Äase', + 'Edit column "%s"' => 'Upravit sloupec "%s" ', + 'Select the new status of the subtask: "%s"' => 'Vyberte nový stav pro dílÄí úkol: "%s"', + 'Subtask timesheet' => 'ÄŒasový rozvrh dílÄích úkolů', + 'There is nothing to show.' => 'Žádná položka k zobrazení', + 'Time Tracking' => 'Sledování Äasu', + 'You already have one subtask in progress' => 'Jeden dílÄí úkol již aktuálnÄ› Å™ešíte', + 'Which parts of the project do you want to duplicate?' => 'Které Äásti projektu chcete duplikovat?', + 'Disallow login form' => 'Zakázat pÅ™ihlaÅ¡ovací formulář', + 'Start' => 'ZaÄátek', + 'End' => 'Konec', + 'Task age in days' => 'Doba trvání úkolu ve dnech', + 'Days in this column' => 'Dní v tomto sloupci', + '%dd' => '%dd', + 'Add a new link' => 'PÅ™idat nový odkaz', + 'Do you really want to remove this link: "%s"?' => 'Opravdu chcete odstranit odkaz "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Opravdu chcete odstranit odkaz na úkol #%d ?', + 'Field required' => 'Povinné pole', + 'Link added successfully.' => 'Propojení bylo úspěšnÄ› pÅ™idáno.', + 'Link updated successfully.' => 'Propojení bylo úspěšnÄ› aktualizováno.', + 'Link removed successfully.' => 'Propojení bylo úspěšnÄ› odebráno.', + 'Link labels' => 'Seznam odkazů', + 'Link modification' => 'Úpravy odkazů', + 'Opposite label' => 'OpaÄný text', + 'Remove a link' => 'Odstranit odkaz', + '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ý', + 'Unable to create your link.' => 'Nelze vytvoÅ™it toto propojení.', + 'Unable to update your link.' => 'Nelze aktualizovat toto propojení.', + 'Unable to remove this link.' => 'Nelze odstranit toto propojení', + 'relates to' => 'souvisí s', + 'blocks' => 'blokuje', + 'is blocked by' => 'je blokován', + 'duplicates' => 'duplikuje', + 'is duplicated by' => 'je duplikován', + 'is a child of' => 'je podřízený', + 'is a parent of' => 'je nadřízený', + 'targets milestone' => 'patří k milníku', + 'is a milestone of' => 'je milníkem', + 'fixes' => 'nahrazuje', + 'is fixed by' => 'je nahrazen', + 'This task' => 'Tento úkol', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Rozbalit úkoly', + 'Collapse tasks' => 'Sbalit úkoly', + 'Expand/collapse tasks' => 'Rozbalit / sbalit úkoly', + 'Close dialog box' => 'Zavřít dialogové okno', + 'Submit a form' => 'Odeslat formulář', + 'Board view' => 'Zobrazení nástÄ›nky', + 'Keyboard shortcuts' => 'Klávesnicové zkratky', + 'Open board switcher' => 'Otevřít pÅ™epínaÄ nástÄ›nek', + 'Application' => 'Aplikace', + 'Compact view' => 'Kompaktní zobrazení', + 'Horizontal scrolling' => 'Horizontální rolování', + 'Compact/wide view' => 'Kompaktní/plné zobrazení', + 'Currency' => 'MÄ›na', + 'Personal project' => 'Osobní projekt', + 'AUD - Australian Dollar' => 'AUD - Australský dolar', + 'CAD - Canadian Dollar' => 'CAD - kanadský dolar', + 'CHF - Swiss Francs' => 'CHF - Å¡výcarský frank', + 'Custom Stylesheet' => 'Vlastní Å¡ablony stylů', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britská Libra', + 'INR - Indian Rupee' => 'INR - Indická rupie', + 'JPY - Japanese Yen' => 'JPY - Japonský jen', + 'NZD - New Zealand Dollar' => 'NZD - Novozélandský dolar', + 'PEN - Peruvian Sol' => 'PEN - Peruánský sol', + 'RSD - Serbian dinar' => 'RSD - Srbský dinár', + 'CNY - Chinese Yuan' => 'CNY - Äínský jüan', + 'USD - US Dollar' => 'USD - Americký dolar', + 'VES - Venezuelan Bolívar' => 'VES - Venezuelský bolívar', + '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.', + 'Move the task to another column when assignee is cleared' => 'PÅ™esunout úkol do jiného sloupce, když je pověření uživatele vymazáno.', + 'Source column' => 'Zdrojový sloupec', + 'Transitions' => 'ZmÄ›ny etap', + 'Executer' => 'Vykonavatel', + 'Time spent in the column' => 'Trvání jednotlivých etap', + 'Task transitions' => 'PÅ™esuny úkolů', + 'Task transitions export' => 'Export pÅ™esunů mezi sloupci', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Tento seznam obsahuje vÅ¡echny pohyby úkolů s daty, uživateli a Äasy strávenými na úkolu.', + 'Currency rates' => 'Aktuální kurzy', + 'Rate' => 'Kurz', + 'Change reference currency' => 'ZmÄ›nit referenÄní mÄ›nu', + 'Reference currency' => 'ReferenÄní mÄ›na', + 'The currency rate has 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 removed the assignee of the task %s' => '%s odstranil pÅ™iÅ™azení úkolu %s ', + 'Information' => 'Informace', + 'Check two factor authentication code' => 'Zkontrolujte dvouúrovňový autentifikaÄní klíÄ', + 'The two factor authentication code is not valid.' => 'Dvouúrovňový autentifikaÄní klÃ­Ä není platný.', + 'The two factor authentication code is valid.' => 'Dvouúrovňový autentifikaÄní klÃ­Ä je platný.', + 'Code' => 'KlíÄ', + 'Two factor authentication' => 'Dvouúrovňová autorizace', + 'This QR code contains the key URI: ' => 'Tento QR kód obsahuje adresu s klíÄem: ', + 'Check my code' => 'Kontrola mého kódu', + 'Secret key: ' => 'Tajný klíÄ: ', + 'Test your device' => 'Test VaÅ¡eho zařízení', + 'Assign a color when the task is moved to a specific column' => 'PÅ™iÅ™adit barvu, když je úkol pÅ™esunut do konkrétního sloupce', + '%s via Kanboard' => '%s via Kanboard', + 'Burndown chart' => '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' => 'Snímek obrazovky pořízen %s ', + 'Add a screenshot' => 'PÅ™idat snímek obrazovky', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'PoÅ™iÄte snímek obrazovky a v tomto poli stisknÄ›te Ctrl+V nebo ⌘+V ', + 'Screenshot uploaded successfully.' => 'Snímek obrazovky byl úspěšnÄ› nahrán.', + 'SEK - Swedish Krona' => 'SEK - Schwedische Kronen', + 'Identifier' => 'Identifikátor', + 'Disable two factor authentication' => 'ZruÅ¡it dvouúrovňovou autorizaci', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Opravdu chcete vypnout dvouúrovňovou autentifikaci pro uživatele: "%s"?', + 'Edit link' => 'Upravit odkaz', + 'Start to type task title...' => 'ZaÄnÄ›te zadávat název úkolu ...', + 'A task cannot be linked to itself' => 'Úkol nemůže být napojen na sebe', + 'The exact same link already exists' => 'Stejný odkaz již existuje', + 'Recurrent task is scheduled to be generated' => 'Plánované generování opakovaného úkolu', + 'Score' => 'Skóre', + 'The identifier must be unique' => 'Identifikátor musí být unikátní', + 'This linked task id doesn\'t exists' => 'Tento odkazovaný úkol neexistuje', + 'This value must be alphanumeric' => 'Tato hodnota musí být alfanumerická', + 'Edit recurrence' => 'Upravit opakování', + 'Generate recurrent task' => 'Generovat opakující se úkol', + 'Trigger to generate recurrent task' => 'SpouÅ¡tÄ›Ä pro generování opakujícího se úkolu', + 'Factor to calculate new due date' => 'Faktor pro výpoÄet nového data splnÄ›ní', + 'Timeframe to calculate new due date' => 'ÄŒasové okno pro výpoÄet nového data splnÄ›ní', + 'Base date to calculate new due date' => 'Výchozí datum pro výpoÄet nového data splnÄ›ní', + 'Action date' => 'Datum akce', + 'Base date to calculate new due date: ' => 'Výchozí datum pro výpoÄet nového data splnÄ›ní: ', + 'This task has created this child task: ' => 'Tento úkol vygeneroval následující dílÄí úkol: ', + 'Day(s)' => 'Dny', + 'Existing due date' => 'Existující datum splnÄ›ní', + 'Factor to calculate new due date: ' => 'Faktor pro výpoÄet nového data splnÄ›ní: ', + 'Month(s)' => 'MÄ›síce', + 'This task has been created by: ' => 'Tento úkol vytvoÅ™il: ', + 'Recurrent task has been generated:' => 'Opakující se úkol vygeneroval: ', + 'Timeframe to calculate new due date: ' => 'ÄŒasové okno pro výpoÄet nového data splnÄ›ní: ', + 'Trigger to generate recurrent task: ' => 'SpouÅ¡tÄ›Ä pro generování opakujícího se úkolu: ', + 'When task is closed' => 'Když je úkol uzavÅ™en', + 'When task is moved from first column' => 'Když je úkol pÅ™esunut z prvního sloupce', + 'When task is moved to last column' => 'Když je úkol pÅ™esunut do posleního sloupce', + 'Year(s)' => 'Rok(y)', + 'Project settings' => 'Nastavení projektu', + 'Automatically update the start date' => 'Automaticky aktualizovat poÄáteÄní datum', + 'iCal feed' => 'iCal feed', + 'Preferences' => 'PÅ™edvolby', + 'Security' => 'ZabezpeÄení ', + 'Two factor authentication disabled' => 'Dvouúrovňová autorizace zakázána.', + 'Two factor authentication enabled' => 'Dvouúrovňová autorizace povolena.', + 'Unable to update this user.' => 'Uživatele nelze aktualizovat.', + 'There is no user management for personal projects.' => 'Pro osobní projekty není aplikována správa uživatelů.', + 'User that will receive the email' => 'Uživatel, který dostane E-mail', + 'Email subject' => 'E-mail PÅ™edmÄ›t', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'PÅ™idat komentář když je úkol pÅ™esouván mezi sloupci', + 'Move the task to another column when the category is changed' => 'PÅ™esunout úkol 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', + 'Notification' => 'UpozornÄ›ní', + '%s moved the task #%d to the first swimlane' => '%s pÅ™esunul úkol #%d do první dráhy', + 'Swimlane' => 'Dráha', + '%s moved the task %s to the first swimlane' => '%s pÅ™esunul úkol %s do první dráhy', + '%s moved the task %s to the swimlane "%s"' => '%s pÅ™esunul úkol %s do dráhy "%s"', + 'This report contains all subtasks information for the given date range.' => 'Report obsahuje vÅ¡echny informace o dílÄích úkolech pro daný Äasový úsek', + 'This report contains all tasks information for the given date range.' => 'Report obsahuje informace o vÅ¡ech úkolech pro daný Äasový úsek.', + 'Project activities for %s' => 'Aktivity projektu %s', + 'view the board on Kanboard' => 'Zobrazit nástÄ›nku', + 'The task has been moved to the first swimlane' => 'Úkol byl pÅ™esunut do první dráhy', + 'The task has been moved to another swimlane:' => 'Úkol byl pÅ™esunut do další dráhy', + 'New title: %s' => 'Nový název: %s', + 'The task is not assigned anymore' => 'Úkol již není pÅ™idÄ›len', + 'New assignee: %s' => 'pÅ™idÄ›lení: %s', + 'There is no category now' => 'Nyní neexistuje žádná kategorie', + 'New category: %s' => 'Nová kategorie: %s', + 'New color: %s' => 'Nová barva: %s', + 'New complexity: %d' => 'Nová složitost: %d', + 'The due date has been removed' => 'Datum dokonÄení byl odstranÄ›n', + 'There is no description anymore' => 'JeÅ¡tÄ› neexistuje žádný popis', + 'Recurrence settings has been modified' => 'Nastavení opakování bylo zmÄ›nÄ›no', + 'Time spent changed: %sh' => 'Strávený Äas se zmÄ›nil: %sh', + 'Time estimated changed: %sh' => 'Odhadovaný Äas se zmÄ›nil: %sh', + 'The field "%s" has been updated' => 'Sloupec "%s" byl upraven', + 'The description has been modified:' => 'Popis byl upraven:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Opravdu si pÅ™ejete úkol "%s" uzavřít? (stejnÄ› jako vÅ¡echny dílÄí úkoly)', + 'I want to receive notifications for:' => 'Chci dostávat upozornÄ›ní na:', + 'All tasks' => 'VÅ¡echny úkoly', + 'Only for tasks assigned to me' => 'pouze pro moje úkoly', + 'Only for tasks created by me' => 'pouze pro mnou vytvoÅ™ené úkoly', + 'Only for tasks created by me and tasks assigned to me' => 'pouze pro mnou vytvoÅ™ené a mÄ› pÅ™iÅ™azené úkoly', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Celkem pro vÅ¡echny sloupce', + 'You need at least 2 days of data to show the chart.' => 'PotÅ™ebujete nejménÄ› data ze dvou dnů pro zobrazení grafu', + '<15m' => '<15min.', + '<30m' => '<30min.', + 'Stop timer' => 'Zastavit ÄasovaÄ', + 'Start timer' => 'Spustit ÄasovaÄ', + 'My activity stream' => 'PÅ™ehled mých aktivit', + 'Search tasks' => 'Hledání úkolů', + 'Reset filters' => 'Resetovat filtry', + 'My tasks due tomorrow' => 'Moje zítÅ™ejší úkoly', + 'Tasks due today' => 'DneÅ¡ní úkoly', + 'Tasks due tomorrow' => 'ZítÅ™ejší úkoly', + 'Tasks due yesterday' => 'VÄerejší úkoly', + 'Closed tasks' => 'UzavÅ™ené úkoly', + 'Open tasks' => 'OtevÅ™ené úkoly', + 'Not assigned' => 'NepÅ™iÅ™azené', + 'View advanced search syntax' => 'Zobrazit syntaxi rozšířeného vyhledávání', + 'Overview' => 'PÅ™ehled', + 'Board/Calendar/List view' => 'NástÄ›nka/Kalendář/Zobrazení seznamu', + 'Switch to the board view' => 'PÅ™epnout na nástÄ›nku', + 'Switch to the list view' => 'PÅ™epnout na seznam zobrazení', + 'Go to the search/filter box' => 'Zobrazit vyhledávání/filtrování', + 'There is no activity yet.' => 'Doposud nejsou žádné aktivity.', + 'No tasks found.' => 'Nenalezen žádný úkol.', + 'Keyboard shortcut: "%s"' => 'Klávesová zkratka: "%s"', + 'List' => 'Seznam', + 'Filter' => 'Filtr', + 'Advanced search' => 'Rozšířené hledání', + 'Example of query: ' => 'Příklad dotazu: ', + 'Search by project: ' => 'Hledat podle projektu: ', + 'Search by column: ' => 'Hledat podle sloupce: ', + 'Search by assignee: ' => 'Hledat podle pÅ™iÅ™azené osoby: ', + 'Search by color: ' => 'Hledat podle barvy: ', + 'Search by category: ' => 'Hledat podle kategorie: ', + 'Search by description: ' => 'Hledat podle popisu: ', + 'Search by due date: ' => 'Hledat podle termínu: ', + 'Average time spent in each column' => 'PrůmÄ›rná doba strávená v každé fázi', + 'Average time spent' => 'PrůmÄ›rná strávená doba', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Tento graf ukazuje průmÄ›rný Äas strávený v každém sloupci pro poslední úkoly %d.', + 'Average Lead and Cycle time' => 'PrůmÄ›rná dodací lhůta a doba cyklu', + 'Average lead time: ' => 'PrůmÄ›rná dodací lhůta: ', + 'Average cycle time: ' => 'PrůmÄ›rná doba cyklu: ', + 'Cycle Time' => 'Doba cyklu', + 'Lead Time' => 'Dodací lhůta', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Graf ukazuje průmÄ›rnou dodací lhůtu a dobu cyklu pro posledních %d úkolů v průbÄ›hu Äasu', + 'Average time into each column' => 'PrůmÄ›rná doba v každé fázi', + 'Lead and cycle time' => 'Dodací lhůta a doba cyklu', + 'Lead time: ' => 'Dodací lhůta: ', + 'Cycle time: ' => 'Doba cyklu: ', + 'Time spent in each column' => 'ÄŒas strávený v každé fázi', + 'The lead time is the duration between the task creation and the completion.' => 'Lead time (dodací lhůta) je Äas od založení úkolu do jeho dokonÄení.', + 'The cycle time is the duration between the start date and the completion.' => 'Doba cyklu je doba trvání mezi zahájením a dokonÄením úkolu.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Jestliže není úkol uzavÅ™en, místo termínu dokonÄení je použit aktuální Äas.', + 'Set the start date automatically' => 'Nastavit automaticky poÄáteÄní datum', + 'Edit Authentication' => 'Upravit ověřování', + '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.', + '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.', + 'Trigger automatically subtask time tracking' => 'SpouÅ¡tÄ›t automaticky dílÄí úkol sledování Äasu', + 'Include closed tasks in the cumulative flow diagram' => 'zaÄlenit dokonÄené úkoly do kumulativního flow diagramu', + 'Current swimlane: %s' => 'Aktuální swimlane: %s', + 'Current column: %s' => 'Aktuální fáze: %s', + 'Current category: %s' => 'Aktuální kategorie: %s', + 'no category' => 'kategorie nenastavena', + 'Current assignee: %s' => 'AktuálnÄ› pÅ™iÅ™azený uživatel: %s', + 'not assigned' => 'nepÅ™iÅ™azeno', + 'Author:' => 'Autor:', + 'contributors' => 'pÅ™ispÄ›vatelé', + 'License:' => 'Licence:', + 'License' => 'Licence', + 'Enter the text below' => 'Zadejte text níže', + 'Start date:' => 'Termín zahájení:', + 'Due date:' => 'Termín dokonÄení:', + 'People who are project managers' => 'Lidé, kteří jsou správci projektu', + 'People who are project members' => 'Lidé, kteří jsou Älenové projektu', + 'NOK - Norwegian Krone' => 'NOK - Norská koruna', + 'Show this column' => 'Zobrazit tento sloupec', + 'Hide this column' => 'Skrýt tento sloupec', + 'End date' => 'Datum konce', + 'Users overview' => 'PÅ™ehled uživatelů', + 'Members' => 'ÄŒlenové', + 'Shared project' => 'Sdílený projekt', + 'Project managers' => 'Správci projektu', + 'Projects list' => 'Seznam projektů', + 'End date:' => 'Datum konce: ', + 'Change task color when using a specific task link' => 'ZmÄ›nit barvu úkolu pÅ™i použití konkrétního odkazu na úkol', + 'Task link creation or modification' => 'VytvoÅ™ení, nebo zmÄ›na odkazu na úkol', + 'Milestone' => 'Milník', + 'Reset the search/filter box' => 'Vyresetovat pole pro vyhledávání / filtrování', + 'Documentation' => 'Dokumentace', + 'Author' => 'Autor', + 'Version' => 'Verze', + 'Plugins' => 'Pluginy', + 'There is no plugin loaded.' => 'Není nainstalován plugin.', + 'My notifications' => 'Moje oznámení', + 'Custom filters' => 'Vlastní filtry', + 'Your custom filter has been created successfully.' => 'Vlastní filtr byl úspěšnÄ› vytvoÅ™en.', + 'Unable to create your custom filter.' => 'Nelze vytvoÅ™it vlastní filtr.', + 'Custom filter removed successfully.' => 'Vlastní filtr byl úspěšnÄ› odebrán.', + 'Unable to remove this custom filter.' => 'Nelze odebrat tento vlastní filtr.', + 'Edit custom filter' => 'Upravit vlastní filtr', + 'Your custom filter has been updated successfully.' => 'Vlastní filtr byl úspěšnÄ› aktualizován.', + 'Unable to update custom filter.' => 'Nelze aktualizovat vlastní filtr.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nová příloha u úkolu #%d: %s', + 'New comment on task #%d' => 'Nový komentář u úkolu #%d', + 'Comment updated on task #%d' => 'Komentář u úkolu #%d byl aktualizován', + 'New subtask on task #%d' => 'Nový dílÄí úkol u úkolu #%d', + 'Subtask updated on task #%d' => 'DílÄí úkol u úkolu #%d byl aktualizován', + 'New task #%d: %s' => 'Nový úkol #%d: %s', + 'Task updated #%d' => 'Úkol #%d byl aktualizován', + 'Task #%d closed' => 'Úkol #%d byl uzavÅ™en', + 'Task #%d opened' => 'Úkol #%d byl otevÅ™en', + 'Column changed for task #%d' => 'Sloupec úkolu #%d byl zmÄ›nÄ›n', + 'New position for task #%d' => 'Nová pozice úkolu #%d', + 'Swimlane changed for task #%d' => 'Dráha úkolu #%d byla zmÄ›nÄ›na', + 'Assignee changed on task #%d' => 'Vlastník úkolu #%d byl zmÄ›nÄ›n', + '%d overdue tasks' => '%d úkolů po datu splnÄ›ní', + 'No notification.' => 'Žádná notifikace.', + 'Mark all as read' => 'OznaÄit vÅ¡e jako pÅ™eÄtené', + 'Mark as read' => 'OznaÄit jako pÅ™eÄtené', + 'Total number of tasks in this column across all swimlanes' => 'Celkový poÄet úkolů v tomto sloupci napÅ™Ã­Ä vÅ¡emi dráhami', + 'Collapse swimlane' => 'Sbalit dráhu', + 'Expand swimlane' => 'Rozbalit dráhu', + 'Add a new filter' => 'PÅ™idat nový filtr', + 'Share with all project members' => 'Sdílet se vÅ¡emi Äleny projektu', + 'Shared' => 'Sdíleno', + 'Owner' => 'Vlastník', + 'Unread notifications' => 'NepÅ™eÄtené notifikace', + 'Notification methods:' => 'Způsoby notifikací:', + 'Unable to read your file' => 'Nelze pÅ™eÄíst soubor', + '%d task(s) have been imported successfully.' => '%d úkol(y) byly úspěšnÄ› importovány.', + 'Nothing has been imported!' => 'Nic nebylo importováno!', + 'Import users from CSV file' => 'Import uživatele ze souboru CSV', + '%d user(s) have been imported successfully.' => '%d uživatel(é) byl úspěšnÄ› importován.', + 'Comma' => 'Čárka', + 'Semi-colon' => 'StÅ™edník', + 'Tab' => 'Záložka', + 'Vertical bar' => 'Vertikální pruh', + 'Double Quote' => 'Dvojitá nabídka', + 'Single Quote' => 'Jediná nabídka', + '%s attached a file to the task #%d' => '%s pÅ™ipojil soubor k úkolu #%d', + 'There is no column or swimlane activated in your project!' => 'Ve vaÅ¡em projektu není aktivován žádný sloupec ani dráha!', + 'Append filter (instead of replacement)' => 'PÅ™idat filtr (místo nahrazení)', + 'Append/Replace' => 'PÅ™idat / Nahradit', + 'Append' => 'PÅ™idat', + 'Replace' => 'Nahradit', + 'Import' => 'Import', + 'Change sorting' => 'ZmÄ›nit třídÄ›ní', + 'Tasks Importation' => 'Import úkolů', + 'Delimiter' => 'OddÄ›lovaÄ', + 'Enclosure' => 'Příloha', + 'CSV File' => 'Soubor CSV', + 'Instructions' => 'Instrukce', + 'Your file must use the predefined CSV format' => 'Váš soubor musí používat pÅ™eddefinovaný formát CSV', + 'Your file must be encoded in UTF-8' => 'Soubor musí být zakódován v UTF-8', + 'The first row must be the header' => 'První řádek musí být záhlaví', + 'Duplicates are not verified for you' => 'Duplikáty pro vás nejsou ověřeny', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Datum vyprÅ¡ení musí být ve formátu ISO: RRRR-MM-DD', + 'Download CSV template' => 'Stáhnout Å¡ablonu CSV', + 'No external integration registered.' => 'Není registrována žádná externí integrace.', + 'Duplicates are not imported' => 'Duplikáty nejsou importovány', + 'Usernames must be lowercase and unique' => 'Uživatelská jména musí být malá a jedineÄná', + 'Passwords will be encrypted if present' => 'Hesla budou Å¡ifrována, pokud jsou přítomna', + '%s attached a new file to the task %s' => '%s pÅ™ipojil nový úkol k úkolu %s', + 'Link type' => 'Typ odkazu', + 'Assign automatically a category based on a link' => 'Automaticky pÅ™iÅ™adit kategorii na základÄ› odkazu', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Uživatelské jméno postupníka', + 'Assignee Name' => 'Jméno postupníka', + 'Groups' => 'Skupiny', + 'Members of %s' => 'ÄŒlenové %s', + 'New group' => 'Nová skupina', + 'Group created successfully.' => 'Skupina byla úspěšnÄ› vytvoÅ™ena.', + 'Unable to create your group.' => 'Nelze vytvoÅ™it vaÅ¡i skupinu.', + 'Edit group' => 'Upravit skupinu', + 'Group updated successfully.' => 'Skupina byla úspěšnÄ› aktualizována.', + 'Unable to update your group.' => 'Skupinu nelze aktualizovat.', + 'Add group member to "%s"' => 'PÅ™idat Älena skupiny do "%s"', + 'Group member added successfully.' => 'ÄŒlen skupiny byl úspěšnÄ› pÅ™idán.', + 'Unable to add group member.' => 'Nelze pÅ™idat Älena skupiny.', + 'Remove user from group "%s"' => 'Odebrat uživatele ze skupiny "%s"', + 'User removed successfully from this group.' => 'Uživatel byl úspěšnÄ› odebrán z této skupiny.', + 'Unable to remove this user from the group.' => 'Nelze odebrat tohoto uživatele ze skupiny.', + 'Remove group' => 'Odebrat skupinu', + 'Group removed successfully.' => 'Skupina byla úspěšnÄ› odebrána.', + 'Unable to remove this group.' => 'Tuto skupinu nelze odebrat.', + 'Project Permissions' => 'OprávnÄ›ní projektu', + 'Manager' => 'Správce', + 'Project Manager' => 'Správce projektu', + 'Project Member' => 'ÄŒlen projektu', + 'Project Viewer' => 'ÄŒtenář projektu', + 'Your account is locked for %d minutes' => 'Váš úÄet je uzamÄen na %d minut', + 'Invalid captcha' => 'Neplatná captcha', + 'The name must be unique' => 'Název musí být jedineÄný', + 'View all groups' => 'Zobrazit vÅ¡echny skupiny', + 'There is no user available.' => 'Uživatel není k dispozici.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Opravdu chcete odstranit uživatele "%s" ze skupiny "%s"?', + 'There is no group.' => 'Neexistuje žádná skupina.', + 'Add group member' => 'PÅ™idat Älena skupiny', + 'Do you really want to remove this group: "%s"?' => 'Opravdu chcete tuto skupinu odstranit: "%s"?', + 'There is no user in this group.' => 'V této skupinÄ› není žádný uživatel.', + 'Permissions' => 'OprávnÄ›ní', + 'Allowed Users' => 'Povolení uživatelé', + 'No specific user has been allowed.' => 'Žádný uživatel nebyl výslovnÄ› povolen.', + 'Role' => 'Role', + 'Enter user name...' => 'Zadejte uživatelské jméno...', + 'Allowed Groups' => 'Povolené skupiny', + 'No group has been allowed.' => 'Žádná skupina nebyla povolena.', + 'Group' => 'Skupina', + 'Group Name' => 'Jméno skupiny', + 'Enter group name...' => 'Zadejte název skupiny ...', + 'Role:' => 'Role:', + 'Project members' => 'ÄŒlenové projektu', + '%s mentioned you in the task #%d' => '%s vás uvedl v úkolu #%d', + '%s mentioned you in a comment on the task #%d' => '%s vás uvedl v komentáři k úkolu #%d', + 'You were mentioned in the task #%d' => 'Byli jste zmínÄ›ni v úkolu #%d', + 'You were mentioned in a comment on the task #%d' => 'Byli jste zmínÄ›ni v komentáři k úkolu #%d', + 'Estimated hours: ' => 'Odhad hodin: ', + 'Actual hours: ' => 'Aktuální hodiny: ', + 'Hours Spent' => 'Hodin stráveno', + 'Hours Estimated' => 'Hodin odhadováno', + 'Estimated Time' => 'Odhadovaný Äas', + 'Actual Time' => 'Aktuální Äas', + 'Estimated vs actual time' => 'Rozdíl mezi odhadovaným a aktuálním Äasem', + 'RUB - Russian Ruble' => 'RUB - ruský rubl', + 'Assign the task to the person who does the action when the column is changed' => 'Úkol pÅ™iÅ™adit osobÄ›, která provede akci pÅ™i zmÄ›nÄ› sloupce', + 'Close a task in a specific column' => 'UkonÄete úlohu ve specifickém sloupci', + 'Time-based One-time Password Algorithm' => 'Algoritmus na ÄasovÄ› závislé jednorázové heslo', + 'Two-Factor Provider: ' => 'Poskytovatel dvou faktorů: ', + 'Disable two-factor authentication' => 'Zakázat dvoufaktorovou autentizaci', + 'Enable two-factor authentication' => 'Povolit dvoufaktorovou autentizaci', + 'There is no integration registered at the moment.' => 'V tuto chvíli není registrována žádná integrace.', + 'Password Reset for Kanboard' => 'Reset hesla pro Kanboard', + 'Forgot password?' => 'Zapomenuté heslo?', + 'Enable "Forget Password"' => 'Povolit zapomenuté heslo', + 'Password Reset' => 'Resetovat heslo', + 'New password' => 'Nové heslo', + 'Change Password' => 'ZmÄ›nit heslo', + 'To reset your password click on this link:' => 'Chcete-li heslo obnovit, kliknÄ›te na tento odkaz:', + 'Last Password Reset' => 'Poslední reset hesla', + 'The password has never been reinitialized.' => 'Heslo nebylo nikdy znovu inicializováno.', + 'Creation' => 'VytvoÅ™ení', + 'Expiration' => 'VyprÅ¡ení', + 'Password reset history' => 'Historie resetování hesla', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'VÅ¡echny úkoly ve sloupci "%s" a dráze "%s" byly úspěšnÄ› uzavÅ™eny.', + 'Do you really want to close all tasks of this column?' => 'Opravdu chcete zavřít vÅ¡echny úkoly tohoto sloupce?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d úkol(y) ve sloupci "%s" a dráha "%s" bude uzavÅ™ena.', + 'Close all tasks in this column and this swimlane' => 'Uzavřít vÅ¡echny úkoly v tomto sloupci', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Žádný plugin není registrován na metodu oznamování projektu. Stále můžete konfigurovat jednotlivá oznámení v profilu uživatele.', + 'My dashboard' => 'Moje nástÄ›nka', + 'My profile' => 'Můj profil', + 'Project owner: ' => 'Vlastník projektu: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identifikátor projektu je nepovinný a musí být alfanumerický, například: MYPROJECT.', + 'Project owner' => 'Vlastník projektu', + 'Personal projects do not have users and groups management.' => 'Osobní projekty nemají správu uživatelů a skupin', + 'There is no project member.' => 'Projekt nemá žádného Älena', + 'Priority' => 'Priorita', + 'Task priority' => 'Priorita úkolu', + 'General' => 'VÅ¡eobecné', + 'Dates' => 'Termíny', + 'Default priority' => 'Výchozí priorita', + 'Lowest priority' => 'Nejnižší priorita', + 'Highest priority' => 'Nejvyšší priorita', + 'Close a task when there is no activity' => 'Uzavřít úkol pokud není žádná aktivita', + 'Duration in days' => 'Trvání ve dnech', + 'Send email when there is no activity on a task' => 'Odeslat mail pokud není žádná aktivita u úkolu', + 'Unable to fetch link information.' => 'Nelze naÄíst informace o odkazu.', + 'Daily background job for tasks' => 'Denní práce na pozadí pro úkoly', + 'Auto' => 'Auto', + 'Related' => 'Související', + 'Attachment' => 'Příloha', + 'Web Link' => 'Webový odkaz', + 'External links' => 'Externí odkazy', + 'Add external link' => 'PÅ™idat externí odkaz', + 'Type' => 'Typ', + 'Dependency' => 'Závislost', + 'Add internal link' => 'PÅ™idat interní odkaz', + 'Add a new external link' => 'PÅ™idat nový externí odkaz', + 'Edit external link' => 'Upravit externí odkaz', + 'External link' => 'Externí odkaz', + 'Copy and paste your link here...' => 'Zde vložte váš odkaz...', + 'URL' => 'URL', + 'Internal links' => 'Interní odkazy', + 'Assign to me' => 'PÅ™iÅ™adit to mnÄ›', + 'Me' => 'MnÄ›', + 'Do not duplicate anything' => 'Nekopírovat nic', + 'Projects management' => 'Správa projektů', + 'Users management' => 'Správa uživatelů', + 'Groups management' => 'Správa skupin', + 'Create from another project' => 'VytvoÅ™it z jiného projektu', + 'open' => 'otevÅ™eno', + 'closed' => 'uzavÅ™eno', + 'Priority:' => 'Priorita:', + 'Reference:' => 'Odkaz:', + 'Complexity:' => 'Složitost:', + 'Swimlane:' => 'Dráha:', + 'Column:' => 'Sloupec:', + 'Position:' => 'Pozice:', + 'Creator:' => 'Autor:', + 'Time estimated:' => 'Odhadovaný Äas:', + '%s hours' => '%s hodin', + 'Time spent:' => 'Strávený Äas:', + 'Created:' => 'VytvoÅ™eno:', + 'Modified:' => 'ZmÄ›nÄ›no:', + 'Completed:' => 'DokonÄeno:', + 'Started:' => 'ZapoÄato:', + 'Moved:' => 'PÅ™esunuto', + 'Task #%d' => 'Úkol #%d', + 'Time format' => 'ÄŒasový formát', + 'Start date: ' => 'PoÄáteÄní datum', + 'End date: ' => 'Koncové datum: ', + 'New due date: ' => 'Nové datum splnÄ›ní: ', + 'Start date changed: ' => 'PoÄáteÄní datum zmÄ›nÄ›no: ', + 'Disable personal projects' => 'Zakázat osobní projekty', + 'Do you really want to remove this custom filter: "%s"?' => 'Opravdu chcete odstranit tento vlastní filtr: "%s"?', + 'Remove a custom filter' => 'Odebrat vlastní filtr', + 'User activated successfully.' => 'Uživatel byl úspěšnÄ› aktivován.', + 'Unable to enable this user.' => 'Tohoto uživatele nelze aktivovat.', + 'User disabled successfully.' => 'Uživatel byl úspěšnÄ› zakázán.', + 'Unable to disable this user.' => 'Tohoto uživatele nelze vypnout.', + 'All files have been uploaded successfully.' => 'VÅ¡echny soubory byly úspěšnÄ› nahrány.', + 'The maximum allowed file size is %sB.' => 'Maximální povolená velikost souboru je %sB.', + 'Drag and drop your files here' => 'PÅ™etáhnout soubory sem', + 'choose files' => 'zvolit soubory', + 'View profile' => 'Prohlédnout profil', + 'Two Factor' => 'Dva faktory', + 'Disable user' => 'Zakázat uživatele', + 'Do you really want to disable this user: "%s"?' => 'Opravdu chcete tohoto uživatele zakázat: "%s"?', + 'Enable user' => 'Povolit uživatele', + 'Do you really want to enable this user: "%s"?' => 'Opravdu chcete povolit tohoto uživatele: "%s"?', + 'Download' => 'Stáhnout', + 'Uploaded: %s' => 'Nahráno: %s', + 'Size: %s' => 'Velikost: %s', + 'Uploaded by %s' => 'Nahráno uživatelem %s', + 'Filename' => 'Název souboru', + 'Size' => 'Velikost', + 'Column created successfully.' => 'Sloupec byl úspěšnÄ› vytvoÅ™en.', + 'Another column with the same name exists in the project' => 'V projektu existuje další sloupec se stejným názvem', + 'Default filters' => 'Výchozí filtry', + 'Your board doesn\'t have any columns!' => 'VaÅ¡e deska nemá žádné sloupce', + 'Change column position' => 'ZmÄ›nit polohu sloupce', + 'Switch to the project overview' => 'PÅ™epnout do pÅ™ehledu projektu', + 'User filters' => 'Uživatelské filtry', + 'Category filters' => 'Filtry kategorie', + 'Upload a file' => 'Nahrát soubor', + 'View file' => 'Prohlédnout soubor', + 'Last activity' => 'Poslední aktivita', + 'Change subtask position' => 'ZmÄ›nit pozici dílÄí úlohy', + 'This value must be greater than %d' => 'Tato hodnota musí být vÄ›tší než %d', + 'Another swimlane with the same name exists in the project' => 'V projektu existuje další dráha se stejným názvem', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Příklad: https://example.kanboard.org/ (slouží k generování absolutních adres URL)', + 'Actions duplicated successfully.' => 'Akce byly úspěšnÄ› duplikovány.', + 'Unable to duplicate actions.' => 'Nelze duplikovat akce.', + 'Add a new action' => 'PÅ™idat novou akci', + 'Import from another project' => 'Import z jiného projektu', + 'There is no action at the moment.' => 'V tuto chvíli neexistuje žádná akce.', + 'Import actions from another project' => 'Import akcí z jiného projektu', + 'There is no available project.' => 'Projekt není k dispozici.', + 'Local File' => 'Místní soubor', + 'Configuration' => 'Konfigurace', + 'PHP version:' => 'Verze PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Verze operaÄního systému:', + 'Database version:' => 'Verze databáze:', + 'Browser:' => 'ProhlížeÄ:', + 'Task view' => 'Zobrazení úkolů', + 'Edit task' => 'Upravit úkol', + 'Edit description' => 'Upravit popis', + 'New internal link' => 'Nový interní odkaz', + 'Display list of keyboard shortcuts' => 'Zobrazí seznam klávesových zkratek', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Nahrát obrázek mého avatara', + 'Remove my image' => 'Odebrat můj obrázek', + 'The OAuth2 state parameter is invalid' => 'Parametr stavu OAuth2 je neplatný', + 'User not found.' => 'Uživatel nenalezen.', + 'Search in activity stream' => 'Vyhledávání v toku Äinností', + 'My activities' => 'Moje aktivity', + 'Activity until yesterday' => 'Aktivita do vÄerejÅ¡ka', + 'Activity until today' => 'Aktivita dodnes', + 'Search by creator: ' => 'Vyhledávání podle autora: ', + 'Search by creation date: ' => 'Vyhledávání podle data vytvoÅ™ení: ', + 'Search by task status: ' => 'Vyhledávání podle stavu úkolu: ', + 'Search by task title: ' => 'Vyhledávání podle názvu úkolu: ', + 'Activity stream search' => 'Vyhledávání aktivity', + 'Projects where "%s" is manager' => 'Projekty, kde "%s" je manažer', + 'Projects where "%s" is member' => 'Projekty, kde "%s" je Älen', + 'Open tasks assigned to "%s"' => 'Otevřít úkoly pÅ™iÅ™azené k "%s"', + 'Closed tasks assigned to "%s"' => 'UzavÅ™ené úkoly pÅ™iÅ™azené k "%s"', + 'Assign automatically a color based on a priority' => 'Automaticky pÅ™iÅ™adit barvu podle priority', + 'Overdue tasks for the project(s) "%s"' => 'ZpoždÄ›né úkoly pro projekt(y) "%s"', + 'Upload files' => 'Nahrát soubory', + 'Installed Plugins' => 'Instalované moduly', + 'Plugin Directory' => 'Plugin adresář', + 'Plugin installed successfully.' => 'Plugin byl úspěšnÄ› nainstalován.', + 'Plugin updated successfully.' => 'Plugin byl úspěšnÄ› aktualizován.', + 'Plugin removed successfully.' => 'Plugin byl úspěšnÄ› odstranÄ›n.', + 'Subtask converted to task successfully.' => 'DílÄí úkol úspěšnÄ› pÅ™eveden na úkol.', + 'Unable to convert the subtask.' => 'Nelze pÅ™evést dílÄí úkol.', + 'Unable to extract plugin archive.' => 'Nelze extrahovat archiv pluginů.', + 'Plugin not found.' => 'Plugin nebyl nalezen.', + 'You don\'t have the permission to remove this plugin.' => 'Nemáte povolení odstranit tento plugin.', + 'Unable to download plugin archive.' => 'Nelze stáhnout archiv pluginů.', + 'Unable to write temporary file for plugin.' => 'Nelze zapsat doÄasný soubor pro plugin.', + 'Unable to open plugin archive.' => 'Nelze otevřít archiv pluginu.', + 'There is no file in the plugin archive.' => 'V archivu pluginů není žádný soubor.', + 'Create tasks in bulk' => 'VytvoÅ™it úkoly hromadnÄ›', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Instance Kanboard není nakonfigurována pro instalaci pluginů z uživatelského rozhraní.', + 'There is no plugin available.' => 'Není k dispozici žádný plugin.', + 'Install' => 'Nainstalovat', + 'Update' => 'Aktualizace', + 'Up to date' => 'Aktuální', + 'Not available' => 'Není dostupný', + 'Remove plugin' => 'Odebrat plugin', + 'Do you really want to remove this plugin: "%s"?' => 'Opravdu chcete tento plugin odstranit: "%s"?', + 'Uninstall' => 'Odinstalovat', + 'Listing' => 'Výpis', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Správa projektů', + 'Convert to task' => 'PÅ™evést na úkol', + 'Convert sub-task to task' => 'PÅ™evést dílÄí úkol na úkol', + 'Do you really want to convert this sub-task to a task?' => 'Opravdu chcete pÅ™evést tento dílÄí úkol na úkol?', + 'My task title' => 'Můj název úkolu', + 'Enter one task by line.' => 'Zadejte jeden úkol na řádek.', + 'Number of failed login:' => 'PoÄet neúspěšných pÅ™ihlášení:', + 'Account locked until:' => 'ÚÄet uzamÄen do:', + 'Email settings' => 'Nastavení e-mailu', + 'Email sender address' => 'Adresa odesílatele e-mailu', + 'Email transport' => 'E-mail transport', + 'Webhook token' => 'Token Webhook', + 'Project tags management' => 'Správa tagů projektu', + 'Tag created successfully.' => 'ZnaÄka byla úspěšnÄ› vytvoÅ™ena.', + 'Unable to create this tag.' => 'Tuto znaÄku nelze vytvoÅ™it.', + 'Tag updated successfully.' => 'ZnaÄka byla úspěšnÄ› aktualizována.', + 'Unable to update this tag.' => 'Tuto znaÄku nelze aktualizovat.', + 'Tag removed successfully.' => 'ZnaÄka byla úspěšnÄ› odebrána.', + 'Unable to remove this tag.' => 'Tuto znaÄku nelze odstranit.', + 'Global tags management' => 'Správa globálních znaÄek', + 'Tags' => 'Å títky', + 'Tags management' => 'Správa Å¡títků', + 'Add new tag' => 'PÅ™idat nový Å¡títek', + 'Edit a tag' => 'Upravit Å¡títek', + 'Project tags' => 'Å títky projektu', + 'There is no specific tag for this project at the moment.' => 'MomentálnÄ› není pro tento projekt žádný specifický tag.', + 'Tag' => 'Å títek', + 'Remove a tag' => 'Odebrat znaÄku', + 'Do you really want to remove this tag: "%s"?' => 'Opravdu chcete tuto znaÄku odstranit: "%s"?', + 'Global tags' => 'Globální znaÄky', + 'There is no global tag at the moment.' => 'MomentálnÄ› neexistuje globální znaÄka.', + 'This field cannot be empty' => 'Toto pole nemůže být prázdné', + 'Close a task when there is no activity in a specific column' => 'UkonÄit úlohu, pokud ve specifickém sloupci neexistuje žádná aktivita', + '%s removed a subtask for the task #%d' => '%s odstranil dílÄí úkol pro úkol #%d', + '%s removed a comment on the task #%d' => '%s odstranil komentář k úkolu #%d', + 'Comment removed on task #%d' => 'Komentář byl odebrán na úkolu #%d', + 'Subtask removed on task #%d' => 'DílÄí úkol odstranÄ›n na úkolu #%d', + 'Hide tasks in this column in the dashboard' => 'Skrýt úkoly v tomto sloupci v řídicím panelu', + '%s removed a comment on the task %s' => '%s odstranil komentář k úkolu %s', + '%s removed a subtask for the task %s' => '%s odstranil dílÄí úkol pro úkol %s', + 'Comment removed' => 'Komentář byl odebrán', + 'Subtask removed' => 'OdstranÄ›n dílÄí úkol', + '%s set a new internal link for the task #%d' => '%s nastavit nový interní odkaz pro úkol #%d', + '%s removed an internal link for the task #%d' => '%s odstranil interní odkaz pro úkol #%d', + 'A new internal link for the task #%d has been defined' => 'Byl definován nový interní odkaz úkolu #%d', + 'Internal link removed for the task #%d' => 'Interní odkaz odstranÄ›n pro úkol #%d', + '%s set a new internal link for the task %s' => '%s nastavit nový interní odkaz pro úkol %s', + '%s removed an internal link for the task %s' => '%s odstranil interní odkaz pro úkol %s', + 'Automatically set the due date on task creation' => 'Automaticky nastaví datum splatnosti úlohy', + 'Move the task to another column when closed' => 'PÅ™i zavÅ™ení pÅ™esunout úkol do jiného sloupce', + 'Move the task to another column when not moved during a given period' => 'PÅ™esunout úkol do jiného sloupce, pokud není pÅ™esunut bÄ›hem daného období', + 'Dashboard for %s' => 'Ovládací panel pro %s', + 'Tasks overview for %s' => 'PÅ™ehled úkolů pro %s', + 'Subtasks overview for %s' => 'PÅ™ehled dílÄích úkolů pro%s', + 'Projects overview for %s' => 'PÅ™ehled projektů pro %s', + 'Activity stream for %s' => 'Proud aktivity pro %s', + 'Assign a color when the task is moved to a specific swimlane' => 'PÅ™i pÅ™esunu úkolu na konkrétní dráhu pÅ™iÅ™aÄte barvu', + 'Assign a priority when the task is moved to a specific swimlane' => 'PÅ™i pÅ™esunu úkolu na konkrétní dráhu pÅ™iÅ™aÄte prioritu', + 'User unlocked successfully.' => 'Uživatel byl úspěšnÄ› odemÄen.', + 'Unable to unlock the user.' => 'Nelze uživatele odemknout.', + 'Move a task to another swimlane' => 'PÅ™emístÄ›te úkol na jinou dráhu', + 'Creator Name' => 'Jméno tvůrce', + 'Time spent and estimated' => 'ÄŒas strávený a odhadovaný', + 'Move position' => 'PÅ™esuňte polohu', + 'Move task to another position on the board' => 'PÅ™esuňte úkol na jiné místo na tabuli', + 'Insert before this task' => 'Vložit pÅ™ed tento úkol', + 'Insert after this task' => 'Vložit po tomto úkolu', + 'Unlock this user' => 'Odemknout tohoto uživatele', + 'Custom Project Roles' => 'Role vlastního projektu', + 'Add a new custom role' => 'PÅ™idat novou vlastní roli', + 'Restrictions for the role "%s"' => 'Omezení role "%s"', + 'Add a new project restriction' => 'PÅ™idat nové omezení projektu', + 'Add a new drag and drop restriction' => 'PÅ™idat nové omezení pÅ™etažení', + 'Add a new column restriction' => 'PÅ™idat nové omezení sloupce', + 'Edit this role' => 'Upravit tuto roli', + 'Remove this role' => 'Odebrat tuto roli', + 'There is no restriction for this role.' => 'Pro tuto roli neexistuje žádné omezení.', + 'Only moving task between those columns is permitted' => 'Povoleno je pouze pÅ™esunutí úkolu mezi tÄ›mito sloupci', + 'Close a task in a specific column when not moved during a given period' => 'Pokud není úkol bÄ›hem urÄitého období pÅ™esunut, zavÅ™ete úlohu ve specifickém sloupci', + 'Edit columns' => 'Upravit sloupce', + 'The column restriction has been created successfully.' => 'Omezení sloupce bylo úspěšnÄ› vytvoÅ™eno.', + 'Unable to create this column restriction.' => 'Toto omezení sloupce nelze vytvoÅ™it.', + 'Column restriction removed successfully.' => 'Omezení sloupců bylo úspěšnÄ› odebráno.', + 'Unable to remove this restriction.' => 'Toto omezení nelze odstranit.', + 'Your custom project role has been created successfully.' => 'VaÅ¡e vlastní role projektu byla úspěšnÄ› vytvoÅ™ena.', + 'Unable to create custom project role.' => 'Nelze vytvoÅ™it vlastní roli projektu.', + 'Your custom project role has been updated successfully.' => 'VaÅ¡e vlastní role projektu byla úspěšnÄ› aktualizována.', + 'Unable to update custom project role.' => 'Nelze aktualizovat vlastní roli projektu.', + 'Custom project role removed successfully.' => 'Úkol vlastního projektu byl úspěšnÄ› odebrán.', + 'Unable to remove this project role.' => 'Tuto roli projektu nelze odstranit.', + 'The project restriction has been created successfully.' => 'Omezení projektu bylo úspěšnÄ› vytvoÅ™eno.', + 'Unable to create this project restriction.' => 'Toto omezení projektu nelze vytvoÅ™it.', + 'Project restriction removed successfully.' => 'Omezení projektu bylo úspěšnÄ› odstranÄ›no.', + 'You cannot create tasks in this column.' => 'V tomto sloupci nelze vytvářet úkoly.', + 'Task creation is permitted for this column' => 'Pro tento sloupec je povoleno vytvoÅ™ení úkolu', + 'Closing or opening a task is permitted for this column' => 'Pro tento sloupec je povoleno zavÅ™ení nebo otevÅ™ení úkolu ', + 'Task creation is blocked for this column' => 'VytvoÅ™ení úkolu je pro tento sloupec blokováno', + 'Closing or opening a task is blocked for this column' => 'UzavÅ™ení nebo otevÅ™ení úkolu je pro tento sloupec blokováno', + 'Task creation is not permitted' => 'Vytváření úkolů není povoleno', + 'Closing or opening a task is not permitted' => 'UzavÅ™ení nebo otevÅ™ení úkolu není povoleno', + 'New drag and drop restriction for the role "%s"' => 'Nové omezení pÅ™etažení pro roli "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Osoby patřící do této role budou moci pÅ™esouvat úkoly pouze mezi zdrojovým a cílovým sloupcem.', + 'Remove a column restriction' => 'Odebrat omezení sloupce', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Opravdu chcete odstranit toto omezení sloupce: "%s" na "%s"?', + 'New column restriction for the role "%s"' => 'Nové omezení sloupce pro roli "%s"', + 'Rule' => 'Pravidlo', + 'Do you really want to remove this column restriction?' => 'Opravdu chcete toto omezení sloupce odstranit?', + 'Custom roles' => 'Vlastní role', + 'New custom project role' => 'Nová role vlastního projektu', + 'Edit custom project role' => 'Upravit roli vlastního projektu', + 'Remove a custom role' => 'Odebrat vlastní roli', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Opravdu chcete tuto vlastní roli odebrat: "%s"? VÅ¡ichni Älenové této role se stanou Älenem projektu.', + 'There is no custom role for this project.' => 'Pro tento projekt neexistuje žádná vlastní role.', + 'New project restriction for the role "%s"' => 'Nové omezení projektu pro roli "%s"', + 'Restriction' => 'Omezení', + 'Remove a project restriction' => 'Odebrat omezení projektu', + 'Do you really want to remove this project restriction: "%s"?' => 'Opravdu chcete toto omezení projektu odstranit: "%s"?', + 'Duplicate to multiple projects' => 'Duplikovat na více projektů', + 'This field is required' => 'Toto pole je povinné', + 'Moving a task is not permitted' => 'PÅ™esunutí úkolu není dovoleno', + 'This value must be in the range %d to %d' => 'Tato hodnota musí být v rozsahu %d až %d', + 'You are not allowed to move this task.' => 'Tento úkol nesmíte pÅ™esouvat.', + 'API User Access' => 'API Uživatelský přístup', + 'Preview' => 'Náhled', + 'Write' => 'Napsat', + 'Write your text in Markdown' => 'Napsat svůj text v Markdown', + 'No personal API access token registered.' => 'Nebyl zaregistrován žádný token přístupu pro osobní rozhraní API.', + 'Your personal API access token is "%s"' => 'Osobní token přístupu API je "%s"', + 'Remove your token' => 'Odebrat token', + 'Generate a new token' => 'VytvoÅ™it nový token', + 'Showing %d-%d of %d' => 'Zobrazeno %d-%d z %d', + 'Outgoing Emails' => 'Odchozí e-maily', + 'Add or change currency rate' => 'PÅ™idat nebo zmÄ›nit mÄ›nový kurz', + 'Reference currency: %s' => 'ReferenÄní mÄ›na: %s', + 'Add custom filters' => 'PÅ™idat vlastní filtry', + 'Export' => 'Export', + 'Add link label' => 'PÅ™idat popisek na odkaz', + 'Incompatible Plugins' => 'Nekompatibilní zásuvné moduly', + 'Compatibility' => 'Kompatibilita', + 'Permissions and ownership' => 'OprávnÄ›ní a vlastnictví', + 'Priorities' => 'Priority', + 'Close this window' => 'Zavřít toto okno', + 'Unable to upload this file.' => 'Tento soubor nelze nahrát.', + 'Import tasks' => 'Import úkolů', + 'Choose a project' => 'Vybrat projekt', + 'Profile' => 'Profil', + 'Application role' => 'Role aplikace', + '%d invitations were sent.' => 'Bylo odesláno %d pozvánek.', + '%d invitation was sent.' => '%d pozvání bylo odesláno.', + 'Unable to create this user.' => 'Tohoto uživatele nelze vytvoÅ™it.', + 'Kanboard Invitation' => 'Pozvánka Kanboard', + 'Visible on dashboard' => 'Viditelné na přístrojové desce', + 'Created at:' => 'VytvoÅ™eno:', + 'Updated at:' => 'Aktualizováno dne:', + 'There is no custom filter.' => 'Neexistuje žádný vlastní filtr.', + 'New User' => 'Nový uživatel', + 'Authentication' => 'Ověřování', + 'If checked, this user will use a third-party system for authentication.' => 'Pokud je tato volba zaÅ¡krtnuta, bude tento uživatel používat pro ověřování systém tÅ™etích stran.', + 'The password is necessary only for local users.' => 'Heslo je nutné pouze pro lokální uživatele.', + 'You have been invited to register on Kanboard.' => 'Byli jste pozváni k registraci na Kanboard.', + 'Click here to join your team' => 'KliknÄ›te sem a pÅ™ipojte se ke svému týmu', + 'Invite people' => 'Pozvat lidi', + 'Emails' => 'E-maily', + 'Enter one email address by line.' => 'Zadejte jednu e-mailovou adresu na řádek.', + 'Add these people to this project' => 'PÅ™idat tyto osoby do tohoto projektu', + 'Add this person to this project' => 'PÅ™idat tuto osobu do tohoto projektu', + 'Sign-up' => 'PÅ™ihlásit se', + 'Credentials' => 'Pověření', + 'New user' => 'Nový uživatel', + 'This username is already taken' => 'Toto uživatelské jméno je již pÅ™ijato', + 'Your profile must have a valid email address.' => 'Váš profil musí mít platnou e-mailovou adresu.', + 'TRL - Turkish Lira' => 'TRL - turecká lira', + 'The project email is optional and could be used by several plugins.' => 'E-mail projektu je nepovinný a může být použit nÄ›kolika pluginy.', + 'The project email must be unique across all projects' => 'E-mail projektu musí být jedineÄný ve vÅ¡ech projektech', + 'The email configuration has been disabled by the administrator.' => 'Konfigurace e-mailu byla správcem zakázána.', + 'Close this project' => 'Uzavřít tento projekt', + 'Open this project' => 'Otevřít tento projekt', + 'Close a project' => 'Uzavřít projekt', + 'Do you really want to close this project: "%s"?' => 'Opravdu chcete zavřít tento projekt: "%s"?', + 'Reopen a project' => 'Znovu otevřít projekt', + 'Do you really want to reopen this project: "%s"?' => 'Opravdu chcete tento projekt znovu otevřít: "%s"?', + 'This project is open' => 'Tento projekt je otevÅ™en', + 'This project is closed' => 'Tento projekt je ukonÄen', + 'Unable to upload files, check the permissions of your data folder.' => 'Nelze nahrát soubory, zkontrolujte oprávnÄ›ní ke složce dat.', + 'Another category with the same name exists in this project' => 'V tomto projektu existuje další kategorie se stejným názvem', + 'Comment sent by email successfully.' => 'Komentář byl úspěšnÄ› odeslán e-mailem.', + 'Sent by email to "%s" (%s)' => 'Odesláno e-mailem na adresu "%s" (%s)', + 'Unable to read uploaded file.' => 'Nelze Äíst nahraný soubor.', + 'Database uploaded successfully.' => 'Databáze byla úspěšnÄ› nahrána.', + 'Task sent by email successfully.' => 'Úkol byl úspěšnÄ› odeslán e-mailem.', + 'There is no category in this project.' => 'V tomto projektu není žádná kategorie.', + 'Send by email' => 'Poslat e-mailem', + 'Create and send a comment by email' => 'VytvoÅ™it a odeslat komentář e-mailem', + 'Subject' => 'PÅ™edmÄ›t', + 'Upload the database' => 'Nahrát databázi', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Můžete nahrát dříve staženou databázi Sqlite (formát Gzip).', + 'Database file' => 'Databázový soubor', + 'Upload' => 'Nahrát', + 'Your project must have at least one active swimlane.' => 'Váš projekt musí mít alespoň jednu aktivní dráhu.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatická akce nebyla nalezena: "%s"', + '%d projects' => '%d projektů', + '%d project' => '%d projekt', + 'There is no project.' => 'Neexistuje žádný projekt.', + 'Sort' => 'SeÅ™adit', + 'Project ID' => 'ID projektu', + 'Project name' => 'Název projektu', + 'Public' => 'VeÅ™ejnost', + 'Personal' => 'Osobní', + '%d tasks' => '%d úkoly', + '%d task' => '%d úkol', + 'Task ID' => 'ID úkolu', + 'Assign automatically a color when due date is expired' => 'Pokud datum splatnosti vyprší, pÅ™iÅ™adit automaticky barvu', + 'Total score in this column across all swimlanes' => 'Celkové skóre v tomto sloupci napÅ™Ã­Ä vÅ¡emi drahami', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentinské Peso', + 'COP - Colombian Peso' => 'COP - Kolumbijské Peso', + '%d groups' => '%d skupin', + '%d group' => '%d skupina', + 'Group ID' => 'ID skupiny', + 'External ID' => 'Externí ID', + '%d users' => '%d uživatelů', + '%d user' => '%d uživatel', + 'Hide subtasks' => 'Skrýt dílÄí úkoly', + 'Show subtasks' => 'Zobrazit dílÄí úkoly', + 'Authentication Parameters' => 'Parametry ověřování', + 'API Access' => 'API Přístup', + 'No users found.' => 'Nenalezeni žádní uživatelé.', + 'User ID' => 'Uživatelské ID', + 'Notifications are activated' => 'Oznámení jsou aktivována', + 'Notifications are disabled' => 'Oznámení jsou zakázána', + 'User disabled' => 'Uživatel je zakázán', + '%d notifications' => '%d oznámení', + '%d notification' => '%d oznámení', + 'There is no external integration installed.' => 'Není nainstalovaná žádná externí integrace.', + 'You are not allowed to update tasks assigned to someone else.' => 'Není dovoleno aktualizovat úkoly pÅ™iÅ™azené nÄ›komu jinému.', + 'You are not allowed to change the assignee.' => 'Není dovoleno mÄ›nit postupníka.', + 'Task suppression is not permitted' => 'PotlaÄení úkolů není povoleno', + 'Changing assignee is not permitted' => 'ZmÄ›na postupníka není povolena', + 'Update only assigned tasks is permitted' => 'Povolené jsou pouze úkoly pÅ™iÅ™azené aktualizací', + 'Only for tasks assigned to the current user' => 'Pouze pro úkoly pÅ™iÅ™azené aktuálnímu uživateli', + 'My projects' => 'Moje projekty', + 'You are not a member of any project.' => 'Nejste Älenem žádného projektu.', + 'My subtasks' => 'Moje dílÄí úkoly', + '%d subtasks' => '%d dílÄí úkoly', + '%d subtask' => '%d dílÄí úkol', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Pro úkoly pÅ™iÅ™azené aktuálnímu uživateli je povoleno pouze pÅ™esunutí úlohy mezi tÄ›mito sloupci', + '[DUPLICATE]' => '[KOPIE]', + 'DKK - Danish Krona' => 'DKK - dánská koruna', + 'Remove user from group' => 'Odebrat uživatele ze skupiny', + 'Assign the task to its creator' => 'PÅ™iÅ™adit úkol svému tvůrci', + 'This task was sent by email to "%s" with subject "%s".' => 'Tento úkol byl zaslán e-mailem na adresu "%s" s pÅ™edmÄ›tem "%s".', + 'Predefined Email Subjects' => 'PÅ™eddefinované e-mailové objekty', + 'Write one subject by line.' => 'Napsat jeden pÅ™edmÄ›t na řádek.', + 'Create another link' => 'VytvoÅ™it další odkaz', + 'BRL - Brazilian Real' => 'BRL - Brazilský Real', + 'Add a new Kanboard task' => 'PÅ™idat nový úkol Kanboard', + 'Subtask not started' => 'DílÄí úkol nebyl spuÅ¡tÄ›n', + 'Subtask currently in progress' => 'Probíhající dílÄí úkol', + 'Subtask completed' => 'DílÄí úkol byl dokonÄen', + 'Subtask added successfully.' => 'DílÄí úkol byl úspěšnÄ› pÅ™idán.', + '%d subtasks added successfully.' => 'Úkoly %d byly úspěšnÄ› pÅ™idány.', + 'Enter one subtask by line.' => 'Zadejte jeden dílÄí úkol na řádek.', + 'Predefined Contents' => 'PÅ™eddefinovaný obsah', + 'Predefined contents' => 'PÅ™eddefinovaný obsah', + 'Predefined Task Description' => 'Popis pÅ™eddefinovaného úkolu', + 'Do you really want to remove this template? "%s"' => 'Opravdu chcete tuto Å¡ablonu odstranit? "%s"', + 'Add predefined task description' => 'PÅ™idat pÅ™eddefinovaný popis úkolu', + 'Predefined Task Descriptions' => 'PÅ™eddefinované popisy úkolů', + 'Template created successfully.' => 'Å ablona byla úspěšnÄ› vytvoÅ™ena.', + 'Unable to create this template.' => 'Tuto Å¡ablonu nelze vytvoÅ™it.', + 'Template updated successfully.' => 'Å ablona byla úspěšnÄ› aktualizována.', + 'Unable to update this template.' => 'Tuto Å¡ablonu nelze aktualizovat.', + 'Template removed successfully.' => 'Å ablona byla úspěšnÄ› odebrána.', + 'Unable to remove this template.' => 'Tuto Å¡ablonu nelze odebrat.', + 'Template for the task description' => 'Å ablona pro popis úkolu', + 'The start date is greater than the end date' => 'Datum zahájení je vÄ›tší než datum ukonÄení', + 'Tags must be separated by a comma' => 'ZnaÄky musí být oddÄ›leny Äárkou', + 'Only the task title is required' => 'Je vyžadován pouze název úkolu', + 'Creator Username' => 'Uživatelské jméno tvůrce', + 'Color Name' => 'Název barvy', + 'Column Name' => 'Název sloupce', + 'Swimlane Name' => 'Název dráhy', + 'Time Estimated' => 'Odhadovaný Äas', + 'Time Spent' => 'Strávený Äas', + 'External Link' => 'Externí odkaz', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Tato funkce umožňuje iCal feed, RSS kanál a zobrazení veÅ™ejné rady.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Zastavení ÄasovaÄe vÅ¡ech dílÄích úkolů pÅ™i pÅ™esunutí úkolu do jiného sloupce', + 'Subtask Title' => 'Náázev dílÄí úkolu', + 'Add a subtask and activate the timer when moving a task to another column' => 'PÅ™i pÅ™esunu úlohy do jiného sloupce pÅ™idat dílÄí úkol a aktivovat ÄasovaÄ', + 'days' => 'dnů', + 'minutes' => 'minut', + 'seconds' => 'sekundy', + 'Assign automatically a color when preset start date is reached' => 'PÅ™i dosažení pÅ™ednastaveného poÄáteÄního data automaticky pÅ™iÅ™adit barvu', + 'Move the task to another column once a predefined start date is reached' => 'Po dosažení pÅ™eddefinovaného data zahájení pÅ™esunout úkol do jiného sloupce', + 'This task is now linked to the task %s with the relation "%s"' => 'Tento úkol je nyní spojen s úkolem %s s relací "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Odkaz se vztahem "%s" na úkol %s byl odstranÄ›n', + 'Custom Filter:' => 'Vlastní filtr:', + 'Unable to find this group.' => 'Tuto skupinu nelze najít.', + '%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 do pozice %d ve sloupci "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s pÅ™esunul úkol #%d do dráhy "%s"', + '%sh spent' => '%sh strávil', + '%sh estimated' => '%sh odhadnuto', + 'Select All' => 'Vybrat vÅ¡e', + 'Unselect All' => 'OdznaÄit vÅ¡e', + 'Apply action' => 'Použít akci', + 'Move selected tasks to another column or swimlane' => 'PÅ™esuňte vybrané úkoly do jiného sloupce', + 'Edit tasks in bulk' => 'Úlohy lze upravovat hromadnÄ›', + 'Choose the properties that you would like to change for the selected tasks.' => 'Vyberte vlastnosti, které chcete zmÄ›nit pro vybrané úkoly.', + 'Configure this project' => 'Nakonfigurovat tento projekt', + 'Start now' => 'ZaÄnÄ›te hned', + '%s removed a file from the task #%d' => '%s odstranil soubor z úkolu #%d', + 'Attachment removed from task #%d: %s' => 'Příloha byla odstranÄ›na z úlohy #%d: %s', + 'No color' => 'Bez barvy', + 'Attachment removed "%s"' => 'Příloha byla odebrána "%s"', + '%s removed a file from the task %s' => '%s odstranil soubor z úkolu %s', + 'Move the task to another swimlane when assigned to a user' => 'Když je úkol pÅ™iÅ™azen uživateli, pÅ™esunout úkol na jinou dráhu', + 'Destination swimlane' => 'Cílová dráha', + 'Assign a category when the task is moved to a specific swimlane' => 'PÅ™iÅ™adit kategorii pÅ™i pÅ™esunu úkolu na urÄitou dráhu', + 'Move the task to another swimlane when the category is changed' => 'PÅ™i zmÄ›nÄ› kategorie pÅ™esunout úkol na jinou dráhu', + 'Reorder this column by priority (ASC)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle priority (vzestupnÄ›)', + 'Reorder this column by priority (DESC)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle priority (sestupnÄ›)', + 'Reorder this column by assignee and priority (ASC)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle zadavatele a priority (vzestupnÄ›)', + 'Reorder this column by assignee and priority (DESC)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle zadavatele a priority (sestupnÄ›)', + 'Reorder this column by assignee (A-Z)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle zadavatele (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle zadavatele (Z-A)', + 'Reorder this column by due date (ASC)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle data vyprÅ¡ení platnosti (vzestupnÄ›)', + 'Reorder this column by due date (DESC)' => 'ZmÄ›nit poÅ™adí tohoto sloupce podle data vyprÅ¡ení platnosti (sestupnÄ›)', + 'Reorder this column by id (ASC)' => 'PÅ™erovnat tento sloupec podle ID (vzestupnÄ›)', + 'Reorder this column by id (DESC)' => 'PÅ™erovnat tento sloupec podle ID (sestupnÄ›)', + '%s moved the task #%d "%s" to the project "%s"' => '%s pÅ™esunul úkol #%d "%s" do projektu "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Úkol #%d "%s" byl pÅ™esunut do projektu "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Pokud je datum vyprÅ¡ení platnosti menší než urÄitý poÄet dní, pÅ™esuňte úlohu do jiného sloupce', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automaticky aktualizuje datum zahájení, když je úkol pÅ™esunut z urÄitého sloupce', + 'HTTP Client:' => 'HTTP klient:', + 'Assigned' => 'PÅ™iÅ™azen', + 'Task limits apply to each swimlane individually' => 'Omezení úkolů platí pro každou dráhu samostatnÄ›', + 'Column task limits apply to each swimlane individually' => 'Omezení úkolů ve sloupcích platí pro každou dráhu samostatnÄ›', + 'Column task limits are applied to each swimlane individually' => 'Omezení úkolů ve sloupcích jsou aplikovány na každou dráhu samostatnÄ›', + 'Column task limits are applied across swimlanes' => 'Omezení úkolů ve sloupcích jsou aplikovány napÅ™Ã­Ä drahami', + 'Task limit: ' => 'Limit úkolu:', + 'Change to global tag' => 'ZmÄ›nit na globální znaÄku', + 'Do you really want to make the tag "%s" global?' => 'Opravdu chcete, aby se znaÄka "%s" stala globální?', + 'Enable global tags for this project' => 'Povolit pro tento projekt globální znaÄky', + 'Group membership(s):' => 'ÄŒlenství ve skupinÄ›:', + '%s is a member of the following group(s): %s' => '%s je Älenem následující skupiny (skupin): %s', + '%d/%d group(s) shown' => 'zobrazeno(a) %d/%d skupin(a)', + 'Subtask creation or modification' => 'VytvoÅ™ení nebo úprava dílÄího úkolu', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'PÅ™iÅ™adit úkol urÄitému uživateli pÅ™i pÅ™esunu úkolu na urÄitou dráhu', + 'Comment' => 'Komentář', + 'Collapse vertically' => 'Rozbalit svisle', + 'Expand vertically' => 'Sbalit svisle', + 'MXN - Mexican Peso' => 'MXN - Mexické peso', + 'Estimated vs actual time per column' => 'Odhadovaný vs. skuteÄný Äas na sloupec', + 'HUF - Hungarian Forint' => 'HUF - MaÄarský forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Musíte vybrat soubor, který nahrajete jako svého avatara!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Soubor, který jste nahráli, není platný obrázek! (Povoleny jsou pouze *.gif, *.jpg, *.jpeg a *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Automaticky nastavit datum splatnosti, když je úkol pÅ™esunut z konkrétního sloupce', + 'No other projects found.' => 'Nebyly nalezeny žádné další projekty.', + 'Tasks copied successfully.' => 'Úkoly úspěšnÄ› zkopírovány.', + 'Unable to copy tasks.' => 'Nelze kopírovat úkoly.', + 'Theme' => 'Téma', + 'Theme:' => 'Téma:', + 'Light theme' => 'SvÄ›tlé téma', + 'Dark theme' => 'Tmavé téma', + 'Automatic theme - Sync with system' => 'Automatické téma - Synchronizace se systémem', + 'Application managers or more' => 'Správci aplikace a vyšší', + 'Administrators' => 'AdministrátoÅ™i', + 'Visibility:' => 'Viditelnost:', + 'Standard users' => 'Standardní uživatelé', + 'Visibility is required' => 'Viditelnost je povinná', + 'The visibility should be an app role' => 'Viditelnost by mÄ›la být rolí aplikace', + 'Reply' => 'OdpovÄ›dÄ›t', + '%s wrote: ' => '%s napsal:', + 'Number of visible tasks in this column and swimlane' => 'PoÄet viditelných úkolů v tomto sloupci a plavecké dráze', + 'Number of tasks in this swimlane' => 'PoÄet úkolů v této plavecké dráze', + 'Unable to find another subtask in progress, you can close this window.' => 'Nelze najít další probíhající dílÄí úkol, můžete zavřít toto okno.', + 'This theme is invalid' => 'Toto téma je neplatné', + 'This role is invalid' => 'Tato role je neplatná', + 'This timezone is invalid' => 'Toto Äasové pásmo je neplatné', + 'This language is invalid' => 'Tento jazyk je neplatný', + 'This URL is invalid' => 'Tato URL adresa je neplatná', + 'Date format invalid' => 'Neplatný formát data', + 'Time format invalid' => 'Neplatný formát Äasu', + 'Invalid Mail transport' => 'Neplatný pÅ™enos poÅ¡ty', + 'Color invalid' => 'Neplatná barva', + 'This value must be greater or equal to %d' => 'Tato hodnota musí být vÄ›tší nebo rovna %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'PÅ™idat BOM na zaÄátek souboru (vyžadováno pro Microsoft Excel)', + 'Just add these tag(s)' => 'StaÄí pÅ™idat tyto Å¡títky', + 'Remove internal link(s)' => 'Odebrat interní odkaz(y)', + 'Import tasks from another project' => 'Importovat úkoly z jiného projektu', + 'Select the project to copy tasks from' => 'Vyberte projekt, ze kterého chcete úkoly kopírovat', + 'The total maximum allowed attachments size is %sB.' => 'Celková maximální povolená velikost příloh je %sB.', + 'Add attachments' => 'PÅ™idat přílohy', + 'Task #%d "%s" is overdue' => 'Úkol #%d "%s" je po termínu', + 'Enable notifications by default for all new users' => 'Ve výchozím nastavení povolit oznámení pro vÅ¡echny nové uživatele', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'PÅ™iÅ™adit úkol jeho tvůrci pro vybrané sloupce, pokud není ruÄnÄ› nastaven Å™eÅ¡itel', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'PÅ™iÅ™adit úkol pÅ™ihlášenému uživateli pÅ™i pÅ™esunu do zadaného sloupce, pokud není pÅ™iÅ™azen žádný uživatel', +]; diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php new file mode 100644 index 0000000..97c154b --- /dev/null +++ b/app/Locale/da_DK/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Ingen', + 'Edit' => 'Redigere', + 'Remove' => 'Fjerne', + 'Yes' => 'Ja', + 'No' => 'Nej', + 'cancel' => 'annullere', + 'or' => 'eller', + 'Yellow' => 'Gul', + 'Blue' => 'BlÃ¥', + 'Green' => 'Grøn', + 'Purple' => 'Lilla', + 'Red' => 'Rød', + 'Orange' => 'Orange', + 'Grey' => 'GrÃ¥', + 'Brown' => 'Brun', + 'Deep Orange' => 'Dyb orange', + 'Dark Grey' => 'MørkegrÃ¥', + 'Pink' => 'Lyserød', + 'Teal' => 'BlÃ¥grøn', + 'Cyan' => 'Turkis', + 'Lime' => 'Citron', + 'Light Green' => 'Lysegrøn', + 'Amber' => 'Rav', + 'Save' => 'Gemme', + 'Login' => 'Logge ind', + 'Official website:' => 'Officielt netsted:', + 'Unassigned' => 'Ikke tildelt', + 'View this task' => 'Vis opgave', + 'Remove user' => 'Fjern bruger', + 'Do you really want to remove this user: "%s"?' => 'Fjern bruger: "%s"?', + 'All users' => 'Alle brugere', + 'Username' => 'Brugernavn', + 'Password' => 'Adgangskode', + 'Administrator' => 'Administrator', + 'Sign in' => 'Logge ind', + 'Users' => 'Brugere', + 'Forbidden' => 'Nægtet', + 'Access Forbidden' => 'Adgang nægtet', + 'Edit user' => 'Redigere bruger', + 'Logout' => 'Logge ud', + 'Bad username or password' => 'Forkert brugernavn eller adgangskode', + 'Edit project' => 'Redigere projekt', + 'Name' => 'Navn', + 'Projects' => 'Projekter', + 'No project' => 'Ingen projekt', + 'Project' => 'Projekt', + 'Status' => 'Status', + 'Tasks' => 'Opgaver', + 'Board' => 'Tavle', + 'Actions' => 'Handlinger', + 'Inactive' => 'Inaktiv', + 'Active' => 'Aktiv', + 'Unable to update this board.' => 'Kan ikke opdatere tavlen.', + 'Disable' => 'Deaktivere', + 'Enable' => 'Aktivere', + 'New project' => 'Nyt projekt', + 'Do you really want to remove this project: "%s"?' => 'Fjern projekt: "%s"?', + 'Remove project' => 'Fjern projekt', + 'Edit the board for "%s"' => 'Redigere tavlen for "%s"', + 'Add a new column' => 'Tilføj ny kolonne', + 'Title' => 'Titel', + 'Assigned to %s' => 'Tildelt %s', + 'Remove a column' => 'Fjern kolonne', + 'Unable to remove this column.' => 'Kan ikke fjerne kolonne.', + 'Do you really want to remove this column: "%s"?' => 'Fjern kolonne: "%s"?', + 'Settings' => 'Opsætning', + 'Application settings' => 'Program opsætning', + 'Language' => 'Sprog', + 'Webhook token:' => 'Net-hægte symbol:', + 'API token:' => 'API symbol:', + 'Database size:' => 'Database størrelse:', + 'Download the database' => 'Hente databasen', + 'Optimize the database' => 'Optimere databasen', + '(VACUUM command)' => '(VACUUM kommando)', + '(Gzip compressed Sqlite file)' => '(Gzip komprimeret SQLite fil)', + 'Close a task' => 'Lukke opgave', + 'Column' => 'Kolonne', + 'Color' => 'Farve', + 'Assignee' => 'Ansvarlig', + 'Create another task' => 'Opret anden opgave', + 'New task' => 'Ny opgave', + 'Open a task' => 'Ã…bne opgave', + 'Do you really want to open this task: "%s"?' => 'Ã…bne opgave: "%s"?', + 'Back to the board' => 'Tilbage til tavlen', + 'There is nobody assigned' => 'Ingen tildelt', + 'Column on the board:' => 'Kolonne pÃ¥ tavlen:', + 'Close this task' => 'Lukke opgave', + 'Open this task' => 'Ã…bne opgave', + 'There is no description.' => 'Ingen beskrivelse.', + 'Add a new task' => 'Tilføj ny opgave', + 'The username is required' => 'Brugernavn pÃ¥krævet', + 'The maximum length is %d characters' => 'Maksimale længde er %d tegn', + 'The minimum length is %d characters' => 'Mindste længde er %d tegn', + 'The password is required' => 'Adgangskode pÃ¥krævet', + 'This value must be an integer' => 'Værdien skal være et heltal', + 'The username must be unique' => 'Brugernavn skal være unikt', + 'The user id is required' => 'Bruger ID pÃ¥krævet', + 'Passwords don\'t match' => 'Adgangskoder stemmer ikke overens', + 'The confirmation is required' => 'Verifikation pÃ¥krævet', + 'The project is required' => 'Projekt pÃ¥krævet', + 'The id is required' => 'ID pÃ¥krævet', + 'The project id is required' => 'Projekt ID pÃ¥krævet', + 'The project name is required' => 'Projekt navn pÃ¥krævet', + 'The title is required' => 'Titel pÃ¥krævet', + 'Settings saved successfully.' => 'Opsætning gemt.', + 'Unable to save your settings.' => 'Kan ikke gemme opsætning.', + 'Database optimization done.' => 'Database optimering færdig.', + 'Your project has been created successfully.' => 'Projekt oprettet.', + 'Unable to create your project.' => 'Kan ikke oprette projekt.', + 'Project updated successfully.' => 'Projekt opdateret.', + 'Unable to update this project.' => 'Kan ikke opdatere projekt.', + 'Unable to remove this project.' => 'Kan ikke fjerne projekt.', + 'Project removed successfully.' => 'Projekt fjernet.', + 'Project activated successfully.' => 'Projekt aktiveret.', + 'Unable to activate this project.' => 'Kan ikke aktivere projekt.', + 'Project disabled successfully.' => 'Projekt deaktiveret.', + 'Unable to disable this project.' => 'Kan ikke deaktivere projekt.', + 'Unable to open this task.' => 'Kan ikke Ã¥bne opgave.', + 'Task opened successfully.' => 'Opgave Ã¥bnet.', + 'Unable to close this task.' => 'Kan ikke lukke opgave.', + 'Task closed successfully.' => 'Opgave lukket.', + 'Unable to update your task.' => 'Kan ikke opdatere opgave.', + 'Task updated successfully.' => 'Opgave opdateret.', + 'Unable to create your task.' => 'Kan ikke oprette opgave.', + 'Task created successfully.' => 'Opgave oprettet.', + 'User created successfully.' => 'Bruger oprettet.', + 'Unable to create your user.' => 'Kan ikke oprette bruger.', + 'User updated successfully.' => 'Bruger opdateret.', + 'User removed successfully.' => 'Bruger fjernet.', + 'Unable to remove this user.' => 'Kan ikke fjerne bruger.', + 'Board updated successfully.' => 'Tavle opdateret.', + 'Ready' => 'Klar', + 'Backlog' => 'Efterslæb', + 'Work in progress' => 'Igangværende', + 'Done' => 'Færdig', + 'Application version:' => 'Program version:', + 'Id' => 'ID', + 'Public link' => 'Offentlig henvisning', + 'Timezone' => 'Tidszone', + 'Sorry, I didn\'t find this information in my database!' => 'Kan ikke finde information i database!', + 'Page not found' => 'Side ikke fundet', + 'Complexity' => 'Kompleksitet', + 'Task limit' => 'Opgave begrænsning', + 'Task count' => 'Opgave tæller', + 'User' => 'Bruger', + 'Comments' => 'Kommentarer', + 'Comment is required' => 'Kommentar pÃ¥krævet', + 'Comment added successfully.' => 'Kommentar tilføjet.', + 'Unable to create your comment.' => 'Kan ikke oprette kommentar.', + 'Due Date' => 'Forfaldsdato', + 'Invalid date' => 'Ugyldig dato', + 'Automatic actions' => 'Automatiske handlinger', + 'Your automatic action has been created successfully.' => 'Automatiske handling oprettet.', + 'Unable to create your automatic action.' => 'Kan ikke oprette automatisk handling.', + 'Remove an action' => 'Fjern handling', + 'Unable to remove this action.' => 'Kan ikke fjerne handling.', + 'Action removed successfully.' => 'Handling fjernet.', + 'Automatic actions for the project "%s"' => 'Automatiske handlinger for projekt "%s"', + 'Add an action' => 'Tilføj handling', + 'Event name' => 'Begivenheds-navn', + 'Action' => 'Handling', + 'Event' => 'Begivenhed', + 'When the selected event occurs execute the corresponding action.' => 'NÃ¥r valgt begivenhed opstÃ¥r udføres tilsvarende handling.', + 'Next step' => 'Næste trin', + 'Define action parameters' => 'Bestemme handlings-parametre', + 'Do you really want to remove this action: "%s"?' => 'Fjern handling: "%s"?', + 'Remove an automatic action' => 'Fjern automatisk handling', + 'Assign the task to a specific user' => 'Tildel opgave til bestemt bruger', + 'Assign the task to the person who does the action' => 'Tildel opgave til bruger der udfører handling', + 'Duplicate the task to another project' => 'Kopiere opgave til andet projekt', + 'Move a task to another column' => 'Flyt opgave til anden kolonne', + 'Task modification' => 'Opgave ændring', + 'Task creation' => 'Opgave oprettelse', + 'Closing a task' => 'Lukke opgave', + 'Assign a color to a specific user' => 'Tildel farve til bestemt bruger', + 'Position' => 'Placering', + 'Duplicate to project' => 'Kopiere til projekt', + 'Duplicate' => 'Kopiere', + 'Link' => 'Henvis', + 'Comment updated successfully.' => 'Kommentar opdateret.', + 'Unable to update your comment.' => 'Kan ikke opdatere kommentar.', + 'Remove a comment' => 'Fjern kommentar', + 'Comment removed successfully.' => 'Kommentar fjernet.', + 'Unable to remove this comment.' => 'Kan ikke fjerne kommentar.', + 'Do you really want to remove this comment?' => 'Fjern kommentar?', + 'Current password for the user "%s"' => 'Nuværende adgangskode for bruger "%s"', + 'The current password is required' => 'Nuværende adgangskode pÃ¥krævet', + 'Wrong password' => 'Forkert adgangskode', + 'Unknown' => 'Ukendt', + 'Last logins' => 'Seneste log ind', + 'Login date' => 'Log ind dato', + 'Authentication method' => 'Godkendelses-metode', + 'IP address' => 'IP adresse', + 'User agent' => 'Bruger agent', + 'Persistent connections' => 'Vedvarende forbindelser', + 'No session.' => 'Ingen blivende forbindelse.', + 'Expiration date' => 'Udløbsdato', + 'Remember Me' => 'Husk mig', + 'Creation date' => 'Oprettelsesdato', + 'Everybody' => 'Alle', + 'Open' => 'Ã…ben', + 'Closed' => 'Lukket', + 'Search' => 'Søg', + 'Nothing found.' => 'Intet fundet.', + 'Due date' => 'Forfaldsdato', + 'Description' => 'Beskrivelse', + '%d comments' => '%d kommentarer', + '%d comment' => '%d kommentar', + 'Email address invalid' => 'Ugyldig e-post adresse', + 'Your external account is not linked anymore to your profile.' => 'Ekstern konto ikke forbundet til profil.', + 'Unable to unlink your external account.' => 'Kan ikke fjerne henvisning til ekstern konto.', + 'External authentication failed' => 'Ekstern godkendelse mislykkedes', + 'Your external account is linked to your profile successfully.' => 'Ekstern konto forbundet til profil.', + 'Email' => 'E-post', + 'Task removed successfully.' => 'Opgave fjernet.', + 'Unable to remove this task.' => 'Kan ikke fjerne opgave.', + 'Remove a task' => 'Fjern opgave', + 'Do you really want to remove this task: "%s"?' => 'Fjern opgave: "%s"?', + 'Assign automatically a color based on a category' => 'Tildele automatisk farve baseret pÃ¥ kategori', + 'Assign automatically a category based on a color' => 'Tildele automatisk kategori baseret pÃ¥ farve', + 'Task creation or modification' => 'Opgave oprettelse eller ændring', + 'Category' => 'Kategori', + 'Category:' => 'Kategori:', + 'Categories' => 'Kategorier', + 'Your category has been created successfully.' => 'Kategori oprettet.', + 'This category has been updated successfully.' => 'Kategori opdateret.', + 'Unable to update this category.' => 'Kan ikke opdatere kategori.', + 'Remove a category' => 'Fjern kategori', + 'Category removed successfully.' => 'Kategori fjernet.', + 'Unable to remove this category.' => 'Kan ikke fjerne kategori.', + 'Category modification for the project "%s"' => 'Kategori ændring for projekt "%s"', + 'Category Name' => 'Kategori navn', + 'Add a new category' => 'Tilføj ny kategori', + 'Do you really want to remove this category: "%s"?' => 'Fjern kategori: "%s"?', + 'All categories' => 'Alle kategorier', + 'No category' => 'Ingen kategori', + 'The name is required' => 'Navn er pÃ¥krævet', + 'Remove a file' => 'Fjern fil', + 'Unable to remove this file.' => 'Kan ikke fjerne fil.', + 'File removed successfully.' => 'Fil fjernet.', + 'Attach a document' => 'Vedhæfte dokument', + 'Do you really want to remove this file: "%s"?' => 'Fjern fil: "%s"?', + 'Attachments' => 'Vedhæftninger', + 'Edit the task' => 'Redigere opgave', + 'Add a comment' => 'Tilføj kommentar', + 'Edit a comment' => 'Redigere kommentar', + 'Summary' => 'Oversigt', + 'Time tracking' => 'Tidsregistrering', + 'Estimate:' => 'AnslÃ¥:', + 'Spent:' => 'Brugt:', + 'Do you really want to remove this sub-task?' => 'Fjern delopgave?', + 'Remaining:' => 'Tilbageværende:', + 'hours' => 'timer', + 'estimated' => 'anslÃ¥et', + 'Sub-Tasks' => 'Delopgaver', + 'Add a sub-task' => 'Tilføj delopgave', + 'Original estimate' => 'Original anslÃ¥et', + 'Create another sub-task' => 'Opret anden delopgave', + 'Time spent' => 'Tidsforbrug', + 'Edit a sub-task' => 'Redigere delopgave', + 'Remove a sub-task' => 'Fjern delopgave', + 'The time must be a numeric value' => 'Tiden skal være numerisk værdi', + 'Todo' => 'At gøre', + 'In progress' => 'I gang', + 'Sub-task removed successfully.' => 'Delopgave fjernet.', + 'Unable to remove this sub-task.' => 'Kan ikke fjerne delopgave.', + 'Sub-task updated successfully.' => 'Delopgave opdateret.', + 'Unable to update your sub-task.' => 'Kan ikke opdatere delopgave.', + 'Unable to create your sub-task.' => 'Kan ikke oprette delopgave.', + 'Maximum size: ' => 'Maksimum størrelse: ', + 'Display another project' => 'Vis andet projekt', + 'Created by %s' => 'Oprettet af %s', + 'Tasks Export' => 'Opgave eksport', + 'Start Date' => 'Start dato', + 'Execute' => 'Udføre', + 'Task Id' => 'Opgave ID', + 'Creator' => 'Opretter', + 'Modification date' => 'Ændrings-dato', + 'Completion date' => 'Afslutningsdato', + 'Clone' => 'Klone', + 'Project cloned successfully.' => 'Projekt klonet..', + 'Unable to clone this project.' => 'Kan ikke klone projekt.', + 'Enable email notifications' => 'Aktivere e-post pÃ¥mindelser', + 'Task position:' => 'Opgave placering:', + 'The task #%d has been opened.' => 'Opgave #%d Ã¥bnet.', + 'The task #%d has been closed.' => 'Opgave #%d lukket.', + 'Sub-task updated' => 'Delopgave opdateret', + 'Title:' => 'Titel:', + 'Status:' => 'Status:', + 'Assignee:' => 'Ansvarlig:', + 'Time tracking:' => 'Tidsregistrering:', + 'New sub-task' => 'Ny delopgave', + 'New attachment added "%s"' => 'Ny vedhæftning tilføjet "%s"', + 'New comment posted by %s' => 'Ny kommentar af %s', + 'New comment' => 'Ny kommentar', + 'Comment updated' => 'Kommentar opdateret', + 'New subtask' => 'Ny delopgave', + 'I only want to receive notifications for these projects:' => 'Modtage pÃ¥mindelser for disse projekter:', + 'view the task on Kanboard' => 'vis opgave pÃ¥ Kanboard', + 'Public access' => 'Offentlig adgang', + 'Disable public access' => 'Deaktivere offentlig adgang', + 'Enable public access' => 'Aktivere offentlig adgang', + 'Public access disabled' => 'Offentlig adgang deaktiveret', + 'Move the task to another project' => 'Flyt opgave til andet projekt', + 'Move to project' => 'Flyt til projekt', + 'Do you really want to duplicate this task?' => 'Kopiere opgave?', + 'Duplicate a task' => 'Kopiere opgave', + 'External accounts' => 'Eksterne konti', + 'Account type' => 'Konto type', + 'Local' => 'Lokal', + 'Remote' => 'Fjern', + 'Enabled' => 'Aktiveret', + 'Disabled' => 'Deaktiveret', + 'Login:' => 'Log ind:', + 'Full Name:' => 'Fuldt navn:', + 'Email:' => 'E-post:', + 'Notifications:' => 'PÃ¥mindelser:', + 'Notifications' => 'PÃ¥mindelser', + 'Account type:' => 'Konto type:', + 'Edit profile' => 'Redigere profil', + 'Change password' => 'Ændre adgangskode', + 'Password modification' => 'Adgangskode ændring', + 'External authentications' => 'Ekstern godkendelse', + 'Never connected.' => 'Aldrig forbundet.', + 'No external authentication enabled.' => 'Ingen ekstern godkendelse aktiveret.', + 'Password modified successfully.' => 'Adgangskode ændret.', + 'Unable to change the password.' => 'Kan ikke ændre adgangskode.', + 'Change category' => 'Ændre kategori', + '%s updated the task %s' => '%s opdatere opgave %s', + '%s opened the task %s' => '%s Ã¥bne opgave %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s flytte opgave %s til placering #%d i kolonne "%s"', + '%s moved the task %s to the column "%s"' => '%s flyttet opgave %s til kolonne "%s"', + '%s created the task %s' => '%s oprettet opgave %s', + '%s closed the task %s' => '%s lukket opgave %s', + '%s created a subtask for the task %s' => '%s oprettet delopgave for opgave %s', + '%s updated a subtask for the task %s' => '%s opdateret delopgave for opgave %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Tildelt %s med anslÃ¥ning pÃ¥ %s/%st', + 'Not assigned, estimate of %sh' => 'Ikke tildelt, anslÃ¥et til %st', + '%s updated a comment on the task %s' => '%s opdateret kommentar pÃ¥ opgave %s', + '%s commented the task %s' => '%s har kommenteret opgave %s', + '%s\'s activity' => '%s\'s aktivitet', + 'RSS feed' => 'RSS nyheds-kilde', + '%s updated a comment on the task #%d' => '%s opdaterede kommentar pÃ¥ opgave #%d', + '%s commented on the task #%d' => '%s kommenterede pÃ¥ opgave #%d', + '%s updated a subtask for the task #%d' => '%s opdaterede delopgave for opgave #%d', + '%s created a subtask for the task #%d' => '%s oprettede delopgave for opgave #%d', + '%s updated the task #%d' => '%s opdaterede opgave #%d', + '%s created the task #%d' => '%s oprettede opgave #%d', + '%s closed the task #%d' => '%s lukkede opgave #%d', + '%s opened the task #%d' => '%s Ã¥bnede opgave #%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' => 'Ændre opgave ansvarlig', + '%s changed the assignee of the task #%d to %s' => '%s ændre ansvarlig for opgave #%d til %s', + '%s changed the assignee of the task %s to %s' => '%s ændre ansvarlig for opgave %s til %s', + 'New password for the user "%s"' => 'Ny adgangskode til bruger "%s"', + 'Choose an event' => 'Vælge hændelse', + 'Create a task from an external provider' => 'Opret opgave fra ekstern udbyder', + 'Change the assignee based on an external username' => 'Ændre tildelt baseret pÃ¥ eksternt brugernavn', + 'Change the category based on an external label' => 'Ændre kategori baseret pÃ¥ ekstern etiket', + 'Reference' => 'Henvisning', + 'Label' => 'Etiket', + 'Database' => 'Database', + 'About' => 'Om', + 'Database driver:' => 'Database drivprogram:', + 'Board settings' => 'Tavle indstillinger', + 'Webhook settings' => 'Net-hægte indstillinger', + 'Reset token' => 'Nulstille symbol', + 'API endpoint:' => 'API slutpunkt:', + 'Refresh interval for personal board' => 'Opdaterings-frekvens for privat tavle', + 'Refresh interval for public board' => 'Opdaterings-frekvens for offentlig tavle', + 'Task highlight period' => 'Fremhæve opgave periode', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode (sekunder) for at betragte opgave nylig ændret (0 deaktiverer, standard 2 dage)', + 'Frequency in second (60 seconds by default)' => 'Frekvens i sekunder (standard 60 sekunder)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvens i sekunder (0 deaktiverer funktion, standard 10 sekunder)', + 'Application URL' => 'Program URL', + 'Token regenerated.' => 'Symbol gendannet.', + 'Date format' => 'Dato format', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format accepteres altid, eksempelvis: "%s" og "%s"', + 'New personal project' => 'Nyt privat projekt', + 'This project is personal' => 'Privat projekt', + 'Add' => 'Tilføje', + 'Start date' => 'Start dato', + 'Time estimated' => 'Tid anslÃ¥et', + 'There is nothing assigned to you.' => 'Intet tildelt.', + 'My tasks' => 'Tildelte opgaver', + 'Activity stream' => 'Aktivitet strøm', + 'Dashboard' => 'Instrumentbræt', + 'Confirmation' => 'Bekræftelse', + 'Webhooks' => 'Net-hægter', + 'API' => 'API', + 'Create a comment from an external provider' => 'Opret kommentar fra ekstern udbyder', + 'Project management' => 'Projekt styring', + 'Columns' => 'Kolonner', + 'Task' => 'Opgave', + 'Percentage' => 'Procent', + 'Number of tasks' => 'Antal opgaver', + 'Task distribution' => 'Opgave fordeling', + 'Analytics' => 'Analyse', + 'Subtask' => 'Delopgave', + 'User repartition' => 'Bruger fordeling', + 'Clone this project' => 'Kopiere projekt', + 'Column removed successfully.' => 'Kolonne fjernet.', + 'Not enough data to show the graph.' => 'Ikke nok data for at vise graf.', + 'Previous' => 'Forrige', + 'The id must be an integer' => 'ID\'et skal være heltal', + 'The project id must be an integer' => 'Projekt ID skal være heltal', + 'The status must be an integer' => 'Status skal være heltal', + 'The subtask id is required' => 'Delopgave ID pÃ¥krævet', + 'The subtask id must be an integer' => 'Delopgave ID skal være heltal', + 'The task id is required' => 'Opgave ID pÃ¥krævet', + 'The task id must be an integer' => 'Opgave ID skal være heltal', + 'The user id must be an integer' => 'Bruger ID skal være heltal', + 'This value is required' => 'Værdi pÃ¥krævet', + 'This value must be numeric' => 'Værdi skal være numerisk', + 'Unable to create this task.' => 'Kan ikke oprette opgave.', + 'Cumulative flow diagram' => 'Akkumuleret rutediagram', + 'Daily project summary' => 'Daglig projektoversigt', + 'Daily project summary export' => 'Daglig projektoversigt eksport', + 'Exports' => 'Eksportere', + 'This export contains the number of tasks per column grouped per day.' => 'Eksport indeholder antal opgaver pr. kolonne grupperet pr. dag.', + 'Active swimlanes' => 'Aktive spor', + 'Add a new swimlane' => 'Tilføj nyt spor', + 'Default swimlane' => 'Standard spor', + 'Do you really want to remove this swimlane: "%s"?' => 'Fjern spor: "%s"?', + 'Inactive swimlanes' => 'Inaktive spor', + 'Remove a swimlane' => 'Fjern spor', + 'Swimlane modification for the project "%s"' => 'Ændre spor for projekt "%s"', + 'Swimlane removed successfully.' => 'Spor fjernet.', + 'Swimlanes' => 'Spor', + 'Swimlane updated successfully.' => 'Spor opdateret.', + 'Unable to remove this swimlane.' => 'Kan ikke fjerne spor.', + 'Unable to update this swimlane.' => 'Kan ikke opdatere spor.', + 'Your swimlane has been created successfully.' => 'Spor oprettet.', + 'Example: "Bug, Feature Request, Improvement"' => 'Eksempel: "fejl, egenskab forespørgsel, forbedring"', + 'Default categories for new projects (Comma-separated)' => 'Standard kategorier for nye projekter (komma separeret)', + 'Integrations' => 'Integrationer', + 'Integration with third-party services' => 'Integration med 3. part tjenester', + 'Subtask Id' => 'Delopgave ID', + 'Subtasks' => 'Delopgaver', + 'Subtasks Export' => 'Delopgave eksport', + 'Task Title' => 'Opgave titel', + 'Untitled' => 'Intet navn', + 'Application default' => 'Program standard', + 'Language:' => 'Sprog:', + 'Timezone:' => 'Tidszone:', + 'All columns' => 'Alle kolonner', + 'Next' => 'Næste', + '#%d' => '#%d', + 'All swimlanes' => 'Alle spor', + 'All colors' => 'Alle farver', + 'Moved to column %s' => 'Flyttet til kolonne %s', + 'User dashboard' => 'Bruger instrumentbræt', + 'Allow only one subtask in progress at the same time for a user' => 'Tillad én aktiv delopgave i gang pÃ¥ samme tid for bruger', + 'Edit column "%s"' => 'Redigere kolonne "%s"', + 'Select the new status of the subtask: "%s"' => 'Vælge ny status for delopgave: "%s"', + 'Subtask timesheet' => 'Delopgave tidsregistrering', + 'There is nothing to show.' => 'Ikke noget at vise.', + 'Time Tracking' => 'Tidsporing', + 'You already have one subtask in progress' => 'En delopgave er i gang', + 'Which parts of the project do you want to duplicate?' => 'Hvilke dele af projekt skal kopieres?', + 'Disallow login form' => 'Afvis log ind formular', + 'Start' => 'Start', + 'End' => 'Slut', + 'Task age in days' => 'Opgave tid i dage', + 'Days in this column' => 'Dage i kolonne', + '%dd' => '%dd', + 'Add a new link' => 'Tilføj ny henvisning', + 'Do you really want to remove this link: "%s"?' => 'Fjern henvisning: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Fjern henvisning med opgave #%d?', + 'Field required' => 'Felt pÃ¥krævet', + 'Link added successfully.' => 'Henvisning tilføjet.', + 'Link updated successfully.' => 'Henvisning opdateret.', + 'Link removed successfully.' => 'Henvisning fjernet.', + 'Link labels' => 'Henvisning etiketter', + 'Link modification' => 'Henvisning ændring', + 'Opposite label' => 'Modsatte etiket', + 'Remove a link' => 'Fjern henvisning', + 'The labels must be different' => 'Etiketter skal være forskellige', + 'There is no link.' => 'Ingen henvisning.', + 'This label must be unique' => 'Etiket skal være unik', + 'Unable to create your link.' => 'Kan ikke oprette henvisning.', + 'Unable to update your link.' => 'Kan ikke opdatere henvisning.', + 'Unable to remove this link.' => 'Kan ikke fjerne henvisning.', + 'relates to' => 'vedrører', + 'blocks' => 'blokerer', + 'is blocked by' => 'er blokeret af', + 'duplicates' => 'dubletter', + 'is duplicated by' => 'er kopieret af', + 'is a child of' => 'er barn af', + 'is a parent of' => 'er forælder til', + 'targets milestone' => 'mÃ¥l milepæl', + 'is a milestone of' => 'er milepæl af', + 'fixes' => 'rettelser', + 'is fixed by' => 'er rettet af', + 'This task' => 'Opgave', + '<1h' => '<1t', + '%dh' => '%dt', + 'Expand tasks' => 'Udfolde opgaver', + 'Collapse tasks' => 'Sammenfolde opgaver', + 'Expand/collapse tasks' => 'Udfolde/sammenfolde opgaver', + 'Close dialog box' => 'Lukke dialogboks', + 'Submit a form' => 'Indsende formular', + 'Board view' => 'Tavle visning', + 'Keyboard shortcuts' => 'Tastaturgenveje', + 'Open board switcher' => 'Ã…bne tavle skifter', + 'Application' => 'Program', + 'Compact view' => 'Kompakt visning', + 'Horizontal scrolling' => 'Vandret rulning', + 'Compact/wide view' => 'Kompakt/bred visning', + 'Currency' => 'Valuta', + 'Personal project' => 'Privat projekt', + 'AUD - Australian Dollar' => 'AUD - australsk dollar', + 'CAD - Canadian Dollar' => 'CAD - canadisk dollar', + 'CHF - Swiss Francs' => 'CHF - schweizisk franc', + 'Custom Stylesheet' => 'Brugertilpasset stilark', + 'EUR - Euro' => 'EUR - euro', + 'GBP - British Pound' => 'GBP - britisk pund', + 'INR - Indian Rupee' => 'INR - indisk rupee', + 'JPY - Japanese Yen' => 'JPY - japansk yen', + 'NZD - New Zealand Dollar' => 'NZD - new zealandsk dollar', + 'PEN - Peruvian Sol' => 'PEN – peruviansk sol', + 'RSD - Serbian dinar' => 'RSD - serbisk dinar', + 'CNY - Chinese Yuan' => 'CNY - kinesisk yuan', + 'USD - US Dollar' => 'USD - amerikansk dollar', + 'VES - Venezuelan Bolívar' => 'VES – venezuelansk bolívar', + 'Destination column' => 'MÃ¥l kolonne', + 'Move the task to another column when assigned to a user' => 'Flyt opgave til anden kolonne nÃ¥r den er tildelt bruger', + 'Move the task to another column when assignee is cleared' => 'Flyt opgave til anden kolonne nÃ¥r ansvarlig er ryddet', + 'Source column' => 'Kilde kolonne', + 'Transitions' => 'Overgange', + 'Executer' => 'Udfører', + 'Time spent in the column' => 'Tid brugt i kolonne', + 'Task transitions' => 'Opgave overgange', + 'Task transitions export' => 'Opgave overgange eksport', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Rapport indeholder alle kolonne bevægelser for hver opgave med dato, bruger og tid der bruges til hver overgang.', + 'Currency rates' => 'Valutakurser', + 'Rate' => 'Kurs', + 'Change reference currency' => 'Ændre referencevaluta', + 'Reference currency' => 'Referencevaluta', + 'The currency rate has been added successfully.' => 'Valutakursen tilføjet.', + 'Unable to add this currency rate.' => 'Kan ikke tilføje valutakurs.', + 'Webhook URL' => 'Net-hægte URL', + '%s removed the assignee of the task %s' => '%s fjernede ansvarlig for opgave %s', + 'Information' => 'Information', + 'Check two factor authentication code' => 'Kontrollere to-faktor godkendelses-kode', + 'The two factor authentication code is not valid.' => 'To-faktor godkendelses-kode ugyldig.', + 'The two factor authentication code is valid.' => 'To-faktor godkendelses-koden gyldig.', + 'Code' => 'Kode', + 'Two factor authentication' => 'To-faktor godkendelse', + 'This QR code contains the key URI: ' => 'QR kode indeholder nøgle URI: ', + 'Check my code' => 'Kontrollere kode', + 'Secret key: ' => 'Hemmelig nøgle: ', + 'Test your device' => 'Test enhed', + 'Assign a color when the task is moved to a specific column' => 'Tildele farve nÃ¥r opgave flyttes til bestemt kolonne', + '%s via Kanboard' => '%s via Kanboard', + 'Burndown chart' => 'Burn-down diagram', + 'This chart show the task complexity over the time (Work Remaining).' => 'Diagram viser opgavekompleksitet over tid (resterende arbejde).', + 'Screenshot taken %s' => 'Skærmbillede taget %s', + 'Add a screenshot' => 'Tilføj skærmbillede', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tage skærmbillede og tryk pÃ¥ CTRL + V eller ⌘ + V for at indsætte her.', + 'Screenshot uploaded successfully.' => 'Skærmbillede overført.', + 'SEK - Swedish Krona' => 'SEK - svensk krone', + 'Identifier' => 'ID', + 'Disable two factor authentication' => 'Deaktivere to-faktor godkendelse', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Deaktivere to-faktor godkendelse for bruger: "%s"?', + 'Edit link' => 'Redigere henvisning', + 'Start to type task title...' => 'Start med at skrive opgave titel...', + 'A task cannot be linked to itself' => 'En opgave kan ikke henvise til sig selv', + 'The exact same link already exists' => 'Henvisning eksisterer allerede', + 'Recurrent task is scheduled to be generated' => 'Gentagende opgave planlagt at blive oprettet', + 'Score' => 'Bedømmelse', + 'The identifier must be unique' => 'ID skal være unik', + 'This linked task id doesn\'t exists' => 'Opgave ID henvisning eksisterer ikke', + 'This value must be alphanumeric' => 'Værdi skal være alfanumerisk', + 'Edit recurrence' => 'Redigere gentagelse', + 'Generate recurrent task' => 'Generere gentagende opgave', + 'Trigger to generate recurrent task' => 'Udløser for at generere gentagende opgave', + 'Factor to calculate new due date' => 'Faktor til beregning af ny forfaldsdato', + 'Timeframe to calculate new due date' => 'Tidsramme til beregning af ny forfaldsdato', + 'Base date to calculate new due date' => 'Grund dato til beregning af ny forfaldsdato', + 'Action date' => 'Handlings-dato', + 'Base date to calculate new due date: ' => 'Grund dato til beregning af ny forfaldsdato: ', + 'This task has created this child task: ' => 'Opgave har oprettet barne opgave: ', + 'Day(s)' => 'Dag(e)', + 'Existing due date' => 'Eksisterende forfaldsdato', + 'Factor to calculate new due date: ' => 'Faktor til beregning af ny forfaldsdato: ', + 'Month(s)' => 'MÃ¥ned(er)', + 'This task has been created by: ' => 'Opgave oprettet af: ', + 'Recurrent task has been generated:' => 'Gentagende opgave oprettet:', + 'Timeframe to calculate new due date: ' => 'Tidsramme til beregning af ny forfaldsdato: ', + 'Trigger to generate recurrent task: ' => 'Udløser for at generere gentagende opgave: ', + 'When task is closed' => 'NÃ¥r opgave er lukket', + 'When task is moved from first column' => 'NÃ¥r opgave flyttes fra første kolonne', + 'When task is moved to last column' => 'NÃ¥r opgave flyttes til sidste kolonne', + 'Year(s)' => 'Ã…r', + 'Project settings' => 'Projekt indstillinger', + 'Automatically update the start date' => 'Opdatere automatisk startdato', + 'iCal feed' => 'iCal nyheds-kilde', + 'Preferences' => 'Indstillinger', + 'Security' => 'Sikkerhed', + 'Two factor authentication disabled' => 'To-faktor godkendelse deaktiveret', + 'Two factor authentication enabled' => 'To-faktor godkendelse aktiveret', + 'Unable to update this user.' => 'Kan ikke opdatere bruger.', + 'There is no user management for personal projects.' => 'Ingen bruger styring til private projekter.', + 'User that will receive the email' => 'Bruger der modtager e-post', + 'Email subject' => 'E-post emne', + 'Date' => 'Dato', + 'Add a comment log when moving the task between columns' => 'Tilføj kommentar log nÃ¥r opgave flyttes mellem kolonner', + 'Move the task to another column when the category is changed' => 'Flyt opgave til anden kolonne nÃ¥r kategori ændres', + 'Send a task by email to someone' => 'Sende opgave som e-post til nogen', + 'Reopen a task' => 'GenÃ¥bne opgave', + 'Notification' => 'PÃ¥mindelse', + '%s moved the task #%d to the first swimlane' => '%s flyttet opgave #%d til første spor', + 'Swimlane' => 'Spor', + '%s moved the task %s to the first swimlane' => '%s flyttet opgave %s til første spor', + '%s moved the task %s to the swimlane "%s"' => '%s flyttet opgave %s til spor "%s"', + 'This report contains all subtasks information for the given date range.' => 'Rapport indeholder alle delopgave oplysninger for dato periode.', + 'This report contains all tasks information for the given date range.' => 'Rapport indeholder alle opgave oplysninger for dato periode.', + 'Project activities for %s' => 'Projekt aktiviteter for %s', + 'view the board on Kanboard' => 'vis tavle pÃ¥ Kanboard', + 'The task has been moved to the first swimlane' => 'Opgave flyttet til første spor', + 'The task has been moved to another swimlane:' => 'Opgave flyttet til andet spor:', + 'New title: %s' => 'Ny titel: %s', + 'The task is not assigned anymore' => 'Opgave ikke tildelt længere', + 'New assignee: %s' => 'Ny ansvarlig: %s', + 'There is no category now' => 'Ingen kategori nu', + 'New category: %s' => 'Ny kategori: %s', + 'New color: %s' => 'Ny farve: %s', + 'New complexity: %d' => 'Ny kompleksitet: %d', + 'The due date has been removed' => 'Forfaldsdato fjernet', + 'There is no description anymore' => 'Ingen beskrivelse mere', + 'Recurrence settings has been modified' => 'Gentagelse indstillinger ændret', + 'Time spent changed: %sh' => 'Tid forbrugt ændret: %st', + 'Time estimated changed: %sh' => 'Tid anslÃ¥et ændret: %st', + 'The field "%s" has been updated' => 'Felt "%s" opdateret', + 'The description has been modified:' => 'Beskrivelse ændret:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Lukke opgave "%s" og alle delopgaver?', + 'I want to receive notifications for:' => 'Bruger modtager underretninger for:', + 'All tasks' => 'Alle opgaver', + 'Only for tasks assigned to me' => 'Kun tildelte opgaver', + 'Only for tasks created by me' => 'Kun opgaver oprettet af bruger', + 'Only for tasks created by me and tasks assigned to me' => 'Kun opgaver oprettet og tildelt bruger', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Total for alle kolonner', + 'You need at least 2 days of data to show the chart.' => 'Der skal mindst 2 dages data for at vise diagram.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Stop stopur', + 'Start timer' => 'Start stopur', + 'My activity stream' => 'Aktivitet strøm', + 'Search tasks' => 'Søg opgaver', + 'Reset filters' => 'Nulstille filtre', + 'My tasks due tomorrow' => 'Tildelte opgaver i morgen', + 'Tasks due today' => 'Opgaver i dag', + 'Tasks due tomorrow' => 'Opgaver i morgen', + 'Tasks due yesterday' => 'Opgaver i gÃ¥r', + 'Closed tasks' => 'Lukket opgaver', + 'Open tasks' => 'Ã…bne opgaver', + 'Not assigned' => 'Ikke tildelt', + 'View advanced search syntax' => 'Vis avanceret søge syntaks', + 'Overview' => 'Oversigt', + 'Board/Calendar/List view' => 'Tavle / kalender / liste visning', + 'Switch to the board view' => 'Skift til tavle visning', + 'Switch to the list view' => 'Skift til liste visning', + 'Go to the search/filter box' => 'GÃ¥ til søg- / filter-boks', + 'There is no activity yet.' => 'Endnu ingen aktivitet.', + 'No tasks found.' => 'Ingen opgave fundet.', + 'Keyboard shortcut: "%s"' => 'Tastaturgenvej: â€%sâ€', + 'List' => 'Liste', + 'Filter' => 'Filter', + 'Advanced search' => 'Avanceret søgning', + 'Example of query: ' => 'Forespørgsel eksempel: ', + 'Search by project: ' => 'Søg efter projekt: ', + 'Search by column: ' => 'Søg efter kolonne: ', + 'Search by assignee: ' => 'Søg ved ansvarlig: ', + 'Search by color: ' => 'Søg efter farve: ', + 'Search by category: ' => 'Søg efter kategori: ', + 'Search by description: ' => 'Søg efter beskrivelse: ', + 'Search by due date: ' => 'Søg efter forfaldsdato: ', + 'Average time spent in each column' => 'Gennemsnitlig tid forbrugt i hver kolonne', + 'Average time spent' => 'Gennemsnitlig tid forbrugt', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Diagram viser gennemsnitlig tid i hver kolonne for de seneste %d opgaver.', + 'Average Lead and Cycle time' => 'Gennemsnitlig hoved- og gennemløbstid', + 'Average lead time: ' => 'Gennemsnitlig hoved tid: ', + 'Average cycle time: ' => 'Gennemsnitlig gennemløbstid: ', + 'Cycle Time' => 'Gennemløbstid', + 'Lead Time' => 'Hoved tid', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Diagram viser gennemsnitlig hoved- og gennemløbstid for de seneste %d opgaver over tid.', + 'Average time into each column' => 'Gennemsnitlig tid i hver kolonne', + 'Lead and cycle time' => 'Hoved- og gennemløbstid', + 'Lead time: ' => 'Hoved tid: ', + 'Cycle time: ' => 'Gennemløbstid: ', + 'Time spent in each column' => 'Tid forbrugt i hver kolonne', + 'The lead time is the duration between the task creation and the completion.' => 'Hoved tid er varighed mellem opgaveoprettelse og færdiggørelse.', + 'The cycle time is the duration between the start date and the completion.' => 'Gennemløbstid er varighed mellem startdato og færdiggørelse.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Hvis opgave ikke er lukket bruges nuværende tid i stedet for færdiggørelses-dato.', + 'Set the start date automatically' => 'Angive automatisk startdato', + 'Edit Authentication' => 'Redigere godkendelse', + 'Remote user' => 'Fjern bruger', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Fjern brugere gemmer ikke adgangskode i Kanboard databasen; eksempler: LDAP, Google og Github konti.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ved at markere afkrydsnings-felt "Tillad ikke log ind formular" ignoreres legitimations-oplysninger, der er indtastet i log ind formular.', + 'Default task color' => 'Standard opgave farve', + 'This feature does not work with all browsers.' => 'Funktion virker ikke med alle netlæsere.', + 'There is no destination project available.' => 'Ingen mÃ¥l projekt tilgængelig.', + 'Trigger automatically subtask time tracking' => 'Udløser automatisk delopgave tidsporing', + 'Include closed tasks in the cumulative flow diagram' => 'Inkludere lukkede opgaver i kumulativ rutediagram', + 'Current swimlane: %s' => 'Nuværende spor: %s', + 'Current column: %s' => 'Nuværende kolonne: %s', + 'Current category: %s' => 'Nuværende kategori: %s', + 'no category' => 'ingen kategori', + 'Current assignee: %s' => 'Nuværende ansvarlig: %s', + 'not assigned' => 'ikke tildelt', + 'Author:' => 'Forfatter:', + 'contributors' => 'bidragsydere', + 'License:' => 'Licens:', + 'License' => 'Licens', + 'Enter the text below' => 'Indtast tekst nedenfor', + 'Start date:' => 'Start dato:', + 'Due date:' => 'Forfaldsdato:', + 'People who are project managers' => 'Personer der er projektledere', + 'People who are project members' => 'Personer der er projektmedlemmer', + 'NOK - Norwegian Krone' => 'NOK - norsk krone', + 'Show this column' => 'Vis kolonne', + 'Hide this column' => 'Skjul kolonne', + 'End date' => 'Slut dato', + 'Users overview' => 'Bruger oversigt', + 'Members' => 'Medlemmer', + 'Shared project' => 'Delt projekt', + 'Project managers' => 'Projektledere', + 'Projects list' => 'Projekt liste', + 'End date:' => 'Slut dato:', + 'Change task color when using a specific task link' => 'Ændre opgave farve nÃ¥r der bruges bestemt opgave henvisning', + 'Task link creation or modification' => 'Opgave henvisning oprettelse eller ændring', + 'Milestone' => 'Milepæl', + 'Reset the search/filter box' => 'Nulstil søg- / filter-boks', + 'Documentation' => 'Dokumentation', + 'Author' => 'Forfatter', + 'Version' => 'Version', + 'Plugins' => 'Udvidelses-moduler', + 'There is no plugin loaded.' => 'Ingen udvidelses-modul indlæst.', + 'My notifications' => 'Tildelte pÃ¥mindelser', + 'Custom filters' => 'Brugertilpasset filtre', + 'Your custom filter has been created successfully.' => 'Brugertilpasset filter oprettet.', + 'Unable to create your custom filter.' => 'Kan ikke oprette brugertilpasset filter.', + 'Custom filter removed successfully.' => 'Brugertilpasset filter fjernet.', + 'Unable to remove this custom filter.' => 'Kan ikke fjerne brugertilpasset filter.', + 'Edit custom filter' => 'Redigere brugertilpasset filter', + 'Your custom filter has been updated successfully.' => 'Brugertilpasset filter opdateret.', + 'Unable to update custom filter.' => 'Kan ikke opdatere brugertilpasset filter.', + 'Web' => 'Net', + 'New attachment on task #%d: %s' => 'Ny vedhæftet fil til opgave #%d: %s', + 'New comment on task #%d' => 'Ny kommentar til opgave #%d', + 'Comment updated on task #%d' => 'Kommentar opdateret til opgave #%d', + 'New subtask on task #%d' => 'Ny delopgave til opgave #%d', + 'Subtask updated on task #%d' => 'Delopgave opdateret til opgave #%d', + 'New task #%d: %s' => 'Ny opgave #%d: %s', + 'Task updated #%d' => 'Opgave opdateret #%d', + 'Task #%d closed' => 'Opgave #%d lukket', + 'Task #%d opened' => 'Opgave #%d Ã¥bnet', + 'Column changed for task #%d' => 'Kolonne ændret for opgave #%d', + 'New position for task #%d' => 'Ny placering for opgave #%d', + 'Swimlane changed for task #%d' => 'Spor ændret for opgave #%d', + 'Assignee changed on task #%d' => 'Tildelte ændret til opgave #%d', + '%d overdue tasks' => '%d forsinkede opgaver', + 'No notification.' => 'Ingen pÃ¥mindelse.', + 'Mark all as read' => 'Markere alle som læst', + 'Mark as read' => 'Markere som læst', + 'Total number of tasks in this column across all swimlanes' => 'Samlet antal opgaver i kolonne pÃ¥ tværs af alle spor', + 'Collapse swimlane' => 'Sammenfolde spor', + 'Expand swimlane' => 'Udfolde spor', + 'Add a new filter' => 'Tilføj nyt filter', + 'Share with all project members' => 'Del med alle projektmedlemmer', + 'Shared' => 'Delt', + 'Owner' => 'Ejer', + 'Unread notifications' => 'Ulæste pÃ¥mindelser', + 'Notification methods:' => 'PÃ¥mindelse metoder:', + 'Unable to read your file' => 'Kan ikke læse fil', + '%d task(s) have been imported successfully.' => '%d opgave(er) blev importeret.', + 'Nothing has been imported!' => 'Intet blev importeret!', + 'Import users from CSV file' => 'Importere brugere fra CSV fil', + '%d user(s) have been imported successfully.' => '%d bruger(e) blev importeret.', + 'Comma' => 'Komma', + 'Semi-colon' => 'Semikolon', + 'Tab' => 'Fane', + 'Vertical bar' => 'Lodret bjælke', + 'Double Quote' => 'Dobbelt citat', + 'Single Quote' => 'Enkelt citat', + '%s attached a file to the task #%d' => '%s vedhæftet fil til opgave #%d', + 'There is no column or swimlane activated in your project!' => 'Ingen kolonne eller spor aktiveret i projekt!', + 'Append filter (instead of replacement)' => 'Tilføj filter (i stedet for udskiftning)', + 'Append/Replace' => 'Tilføj / erstat', + 'Append' => 'Tilføj', + 'Replace' => 'Erstat', + 'Import' => 'Importer', + 'Change sorting' => 'Ændre sortering', + 'Tasks Importation' => 'Opgaver Import', + 'Delimiter' => 'Afgrænse', + 'Enclosure' => 'Indeluk', + 'CSV File' => 'CSV fil', + 'Instructions' => 'Instruktioner', + 'Your file must use the predefined CSV format' => 'Filen skal bruge forudbestemt CSV format', + 'Your file must be encoded in UTF-8' => 'Fil skal være indkodet i UTF-8', + 'The first row must be the header' => 'Første række skal være overskrift', + 'Duplicates are not verified for you' => 'Dubletter er ikke efterprøvet', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Forfaldsdato skal bruge ISO-format: Ã…Ã…Ã…Ã…-MM-DD', + 'Download CSV template' => 'Hente CSV skabelon', + 'No external integration registered.' => 'Ingen ekstern integration registreret.', + 'Duplicates are not imported' => 'Dubletter importeres ikke', + 'Usernames must be lowercase and unique' => 'Brugernavn skal være smÃ¥ bogstaver og unik', + 'Passwords will be encrypted if present' => 'Adgangskoder krypteres hvis tilstede', + '%s attached a new file to the task %s' => '%s knytte ny fil til opgave %s', + 'Link type' => 'Henvisning type', + 'Assign automatically a category based on a link' => 'Tildele automatisk kategori baseret pÃ¥ henvisning', + 'BAM - Konvertible Mark' => 'BAM - mærke kan konverteres', + 'Assignee Username' => 'Ansvarligs brugernavn', + 'Assignee Name' => 'Ansvarligs navn', + 'Groups' => 'Grupper', + 'Members of %s' => 'Medlemmer af %s', + 'New group' => 'Ny gruppe', + 'Group created successfully.' => 'Gruppe oprettet.', + 'Unable to create your group.' => 'Kan ikke oprette gruppe.', + 'Edit group' => 'Redigere gruppe', + 'Group updated successfully.' => 'Gruppe opdateret.', + 'Unable to update your group.' => 'Kan ikke opdatere gruppe.', + 'Add group member to "%s"' => 'Tilføj gruppemedlem til "%s"', + 'Group member added successfully.' => 'Gruppemedlem tilføjet.', + 'Unable to add group member.' => 'Kan ikke tilføje gruppemedlem.', + 'Remove user from group "%s"' => 'Fjern bruger fra gruppe "%s"', + 'User removed successfully from this group.' => 'Fjernet bruger fra gruppe.', + 'Unable to remove this user from the group.' => 'Kan ikke fjerne bruger fra gruppe.', + 'Remove group' => 'Fjern gruppe', + 'Group removed successfully.' => 'Gruppe fjernet.', + 'Unable to remove this group.' => 'Kan ikke fjerne gruppe.', + 'Project Permissions' => 'Projekt tilladelser', + 'Manager' => 'Leder', + 'Project Manager' => 'Projektleder', + 'Project Member' => 'Projektmedlem', + 'Project Viewer' => 'Projekt betragter', + 'Your account is locked for %d minutes' => 'Konto lÃ¥st i %d minutter', + 'Invalid captcha' => 'Ugyldig CAPTCHA', + 'The name must be unique' => 'Navn skal være unik', + 'View all groups' => 'Vis alle grupper', + 'There is no user available.' => 'Ingen bruger tilgængelig.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Fjern bruger "%s" fra gruppe "%s"?', + 'There is no group.' => 'Ingen gruppe.', + 'Add group member' => 'Tilføj gruppe medlem', + 'Do you really want to remove this group: "%s"?' => 'Fjern gruppe: "%s"?', + 'There is no user in this group.' => 'Ingen bruger i gruppe.', + 'Permissions' => 'Tilladelser', + 'Allowed Users' => 'Tillad brugere', + 'No specific user has been allowed.' => 'Ingen bruger specifik tilladt.', + 'Role' => 'Rolle', + 'Enter user name...' => 'Indtast brugernavn...', + 'Allowed Groups' => 'Tillad grupper', + 'No group has been allowed.' => 'Ingen gruppe specifik tilladt.', + 'Group' => 'Gruppe', + 'Group Name' => 'Gruppenavn', + 'Enter group name...' => 'Indtast gruppenavn...', + 'Role:' => 'Rolle:', + 'Project members' => 'Projektmedlemmer', + '%s mentioned you in the task #%d' => '%s nævnte bruger i opgave #%d', + '%s mentioned you in a comment on the task #%d' => '%s nævnte bruger i kommentar til opgave #%d', + 'You were mentioned in the task #%d' => 'Bruger nævnt i opgave #%d', + 'You were mentioned in a comment on the task #%d' => 'Bruger nævnt i kommentar til opgave #%d', + 'Estimated hours: ' => 'AnslÃ¥ede timer: ', + 'Actual hours: ' => 'Faktiske timer: ', + 'Hours Spent' => 'Forbrugte timer:', + 'Hours Estimated' => 'AnslÃ¥ede timer', + 'Estimated Time' => 'AnslÃ¥et tid', + 'Actual Time' => 'Faktiske tid', + 'Estimated vs actual time' => 'AnslÃ¥et mod faktisk tid', + 'RUB - Russian Ruble' => 'RUB - russisk rubel', + 'Assign the task to the person who does the action when the column is changed' => 'Tildele opgave til person som begÃ¥r handlingen nÃ¥r kolonnen ændres', + 'Close a task in a specific column' => 'Lukke opgave i bestemt kolonne', + 'Time-based One-time Password Algorithm' => 'Tidsbaseret engangs-adgangskode algoritme', + 'Two-Factor Provider: ' => 'To-faktor udbyder: ', + 'Disable two-factor authentication' => 'Deaktivere to-faktor godkendelse', + 'Enable two-factor authentication' => 'Aktivere to-faktor godkendelse', + 'There is no integration registered at the moment.' => 'Ingen integration registreret i øjeblikket.', + 'Password Reset for Kanboard' => 'Nulstille adgangskode for Kanboard', + 'Forgot password?' => 'Glemt adgangskode?', + 'Enable "Forget Password"' => 'Aktivere "Glemme adgangskode"', + 'Password Reset' => 'Nulstille adgangskode', + 'New password' => 'Ny adgangskode', + 'Change Password' => 'Ændre adgangskode', + 'To reset your password click on this link:' => 'For at nulstille adgangskode klik pÃ¥ henvisning:', + 'Last Password Reset' => 'Adgangskode senest nulstillet', + 'The password has never been reinitialized.' => 'Adgangskode aldrig nulstillet.', + 'Creation' => 'Oprettelse', + 'Expiration' => 'Udløb', + 'Password reset history' => 'Adgangskode nulstille historik', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Alle opgaver i kolonne "%s" og spor "%s" blev lukket.', + 'Do you really want to close all tasks of this column?' => 'Lukke alle opgaver i kolonnen?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d opgave(r) i kolonne "%s" og spor "%s" lukkes.', + 'Close all tasks in this column and this swimlane' => 'Lukke alle opgaver i kolonne', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Ingen udvidelses-modul har registreret projekt pÃ¥mindelses-metode Brugeren kan stadig konfigurere individuelle pÃ¥mindelser i brugerprofil.', + 'My dashboard' => 'Instrumentbræt', + 'My profile' => 'Profil', + 'Project owner: ' => 'Projekt ejer: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Projekt ID valgfri og skal være alfanumerisk; eksempel: MitProjekt.', + 'Project owner' => 'Projekt ejer', + 'Personal projects do not have users and groups management.' => 'Private projekter har ikke bruger og gruppe styring.', + 'There is no project member.' => 'Ingen projekt medlem.', + 'Priority' => 'Prioritet', + 'Task priority' => 'Opgave prioritet', + 'General' => 'Generelt', + 'Dates' => 'Datoer', + 'Default priority' => 'Standard prioritet', + 'Lowest priority' => 'Laveste prioritet', + 'Highest priority' => 'Højeste prioritet', + 'Close a task when there is no activity' => 'Lukke opgave nÃ¥r der ikke er aktivitet', + 'Duration in days' => 'Varighed i dage', + 'Send email when there is no activity on a task' => 'Sende e-post nÃ¥r der ikke er aktivitet pÃ¥ opgave', + 'Unable to fetch link information.' => 'Kan ikke hente henvisning informationer.', + 'Daily background job for tasks' => 'Daglige baggrundsjob til opgaver', + 'Auto' => 'Auto', + 'Related' => 'Relateret', + 'Attachment' => 'Vedhæftning', + 'Web Link' => 'Net henvisning', + 'External links' => 'Eksterne henvisninger', + 'Add external link' => 'Tilføj ekstern henvisning', + 'Type' => 'Type', + 'Dependency' => 'Afhængighed', + 'Add internal link' => 'Tilføj intern henvisning', + 'Add a new external link' => 'Tilføj ny ekstern henvisning', + 'Edit external link' => 'Rette ekstern henvisning', + 'External link' => 'Ekstern henvisning', + 'Copy and paste your link here...' => 'Kopiere og indsætte henvisning her...', + 'URL' => 'URL', + 'Internal links' => 'Intern henvisning', + 'Assign to me' => 'Tildele bruger', + 'Me' => 'Bruger', + 'Do not duplicate anything' => 'Kopiere ikke noget', + 'Projects management' => 'Projekt styring', + 'Users management' => 'Bruger styring', + 'Groups management' => 'Gruppe styring', + 'Create from another project' => 'Opret fra andet projekt', + 'open' => 'Ã¥ben', + 'closed' => 'lukket', + 'Priority:' => 'Prioritet:', + 'Reference:' => 'Reference:', + 'Complexity:' => 'Kompleksitet:', + 'Swimlane:' => 'Spor:', + 'Column:' => 'Kolonne:', + 'Position:' => 'Placering:', + 'Creator:' => 'Opretter:', + 'Time estimated:' => 'Tid anslÃ¥et:', + '%s hours' => '%s timer', + 'Time spent:' => 'Tid brugt:', + 'Created:' => 'Oprettet:', + 'Modified:' => 'Ændret:', + 'Completed:' => 'Afsluttet:', + 'Started:' => 'Startet:', + 'Moved:' => 'Flyttet:', + 'Task #%d' => 'Opgave #%d', + 'Time format' => 'Tidsformat', + 'Start date: ' => 'Start dato: ', + 'End date: ' => 'Slut dato: ', + 'New due date: ' => 'Ny forfalds-dato: ', + 'Start date changed: ' => 'Start dato ændret: ', + 'Disable personal projects' => 'Deaktivere private projekter', + 'Do you really want to remove this custom filter: "%s"?' => 'Fjern brugertilpasset filter: "%s"?', + 'Remove a custom filter' => 'Fjern brugertilpasset filter', + 'User activated successfully.' => 'Bruger aktiveret.', + 'Unable to enable this user.' => 'Kan ikke aktivere bruger.', + 'User disabled successfully.' => 'Bruger deaktiveret.', + 'Unable to disable this user.' => 'Kan ikke deaktivere bruger.', + 'All files have been uploaded successfully.' => 'Alle filer overført.', + 'The maximum allowed file size is %sB.' => 'Den maksimale filstørrelse er %sB.', + 'Drag and drop your files here' => 'Træk og slip filer her', + 'choose files' => 'vælg filer', + 'View profile' => 'Vis profil', + 'Two Factor' => 'To-faktor', + 'Disable user' => 'Deaktivere bruger', + 'Do you really want to disable this user: "%s"?' => 'Deaktivere bruger: "%s"?', + 'Enable user' => 'Aktivere bruger', + 'Do you really want to enable this user: "%s"?' => 'Aktivere bruger: "%s"?', + 'Download' => 'Hente', + 'Uploaded: %s' => 'Overført: %s', + 'Size: %s' => 'Størrelse: %s', + 'Uploaded by %s' => 'Overført af %s', + 'Filename' => 'Filnavn', + 'Size' => 'Størrelse', + 'Column created successfully.' => 'Kolonne oprettet.', + 'Another column with the same name exists in the project' => 'Der findes anden kolonne med samme navn i projekt', + 'Default filters' => 'Standard filtre', + 'Your board doesn\'t have any columns!' => 'Tavle har ingen kolonner!', + 'Change column position' => 'Ændre kolonne placering', + 'Switch to the project overview' => 'Skifte til projekt oversigt', + 'User filters' => 'Bruger filtre', + 'Category filters' => 'Kategori filtre', + 'Upload a file' => 'Overføre fil', + 'View file' => 'Vis fil', + 'Last activity' => 'Seneste aktivitet', + 'Change subtask position' => 'Ændre delopgave placering', + 'This value must be greater than %d' => 'Værdi skal være større end %d', + 'Another swimlane with the same name exists in the project' => 'Der findes andet spor med samme navn i projekt', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Eksempel: https://example.kanboard.org/ (bruges til at generere absolutte URLer)', + 'Actions duplicated successfully.' => 'Handlinger kopieret.', + 'Unable to duplicate actions.' => 'Kan ikke kopiere handlinger.', + 'Add a new action' => 'Tilføj ny handling', + 'Import from another project' => 'Importere fra andet projekt', + 'There is no action at the moment.' => 'Ingen handling i øjeblikket.', + 'Import actions from another project' => 'Importere handlinger fra andet projekt', + 'There is no available project.' => 'Ingen projekt tilgængelig.', + 'Local File' => 'Lokal fil', + 'Configuration' => 'Indstillinger', + 'PHP version:' => 'PHP version:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'OS version:', + 'Database version:' => 'Database version:', + 'Browser:' => 'Netlæser:', + 'Task view' => 'Opgave oversigt', + 'Edit task' => 'Redigere opgave', + 'Edit description' => 'Redigere beskrivelse', + 'New internal link' => 'Ny intern henvisning', + 'Display list of keyboard shortcuts' => 'Vis liste over tastaturgenveje', + 'Avatar' => 'Ikon', + 'Upload my avatar image' => 'Overføre ikon billede', + 'Remove my image' => 'Fjern billede', + 'The OAuth2 state parameter is invalid' => 'Ugyldig OAuth2 tilstands-parameter', + 'User not found.' => 'Bruger ikke fundet.', + 'Search in activity stream' => 'Søg i aktivitet strøm', + 'My activities' => 'Tildelte aktiviteter', + 'Activity until yesterday' => 'Aktivitet indtil i gÃ¥r', + 'Activity until today' => 'Aktivitet indtil i dag', + 'Search by creator: ' => 'Søg efter opretter: ', + 'Search by creation date: ' => 'Søg efter oprettelsesdato: ', + 'Search by task status: ' => 'Søg efter opgave status: ', + 'Search by task title: ' => 'Søg efter opgavetitel: ', + 'Activity stream search' => 'Aktivitet strøm søgning', + 'Projects where "%s" is manager' => 'Projekter hvor "%s" er leder', + 'Projects where "%s" is member' => 'Projekter, hvor "%s" er medlem', + 'Open tasks assigned to "%s"' => 'Ã…bne opgaver tildelt "%s"', + 'Closed tasks assigned to "%s"' => 'Lukkede opgaver tildelt "%s"', + 'Assign automatically a color based on a priority' => 'Tildele automatisk farve baseret pÃ¥ prioritet', + 'Overdue tasks for the project(s) "%s"' => 'Forfaldne opgaver for projekt(er) "%s"', + 'Upload files' => 'Overføre filer', + 'Installed Plugins' => 'Installerede udvidelses-moduler', + 'Plugin Directory' => 'Udvidelses-modul katalog', + 'Plugin installed successfully.' => 'Udvidelses-modul installeret.', + 'Plugin updated successfully.' => 'Udvidelses-modul opdateret.', + 'Plugin removed successfully.' => 'Udvidelses-modul fjernet.', + 'Subtask converted to task successfully.' => 'Delopgave konverteret til opgave.', + 'Unable to convert the subtask.' => 'Kan ikke konvertere delopgave.', + 'Unable to extract plugin archive.' => 'Kan ikke udpakke udvidelses-modul arkiv.', + 'Plugin not found.' => 'Udvidelses-modul ikke fundet.', + 'You don\'t have the permission to remove this plugin.' => 'Ikke tilladelse til at fjerne udvidelses-modul.', + 'Unable to download plugin archive.' => 'Kan ikke hente udvidelses-modul arkiv.', + 'Unable to write temporary file for plugin.' => 'Kan ikke skrive midlertidig fil til udvidelses-modul.', + 'Unable to open plugin archive.' => 'Kan ikke Ã¥bne udvidelses-modul arkiv.', + 'There is no file in the plugin archive.' => 'Ingen fil i udvidelses-modul arkivet.', + 'Create tasks in bulk' => 'Opret flere opgaver', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Kanboard forekomst er ikke indstillettil at installere udvidelses-moduler fra brugergrænsefladen.', + 'There is no plugin available.' => 'Ingen udvidelses-modul til rÃ¥dighed.', + 'Install' => 'Installere', + 'Update' => 'Opdatere', + 'Up to date' => 'Opdateret', + 'Not available' => 'Ikke tilgængelig', + 'Remove plugin' => 'Fjern udvidelses-modul', + 'Do you really want to remove this plugin: "%s"?' => 'Fjern udvidelses-modul: "%s"?', + 'Uninstall' => 'Afinstallere', + 'Listing' => 'Kategorisere', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Projekt styring', + 'Convert to task' => 'Konvertere til opgave', + 'Convert sub-task to task' => 'Konvertere delopgave til opgave', + 'Do you really want to convert this sub-task to a task?' => 'Konvertere delopgave til opgave?', + 'My task title' => 'Tildelt opgave titel', + 'Enter one task by line.' => 'Indtast én opgave pr. linje.', + 'Number of failed login:' => 'Antal mislykkede log ind:', + 'Account locked until:' => 'Konto lÃ¥st indtil:', + 'Email settings' => 'E-post indstillinger', + 'Email sender address' => 'E-post afsender adresse', + 'Email transport' => 'E-post transport', + 'Webhook token' => 'Net-hægte symbol', + 'Project tags management' => 'Projekt mærke styring', + 'Tag created successfully.' => 'Mærke oprettet.', + 'Unable to create this tag.' => 'Kunne ikke oprette mærke.', + 'Tag updated successfully.' => 'Mærke opdateret.', + 'Unable to update this tag.' => 'Kan ikke opdatere mærke.', + 'Tag removed successfully.' => 'Mærke fjernet.', + 'Unable to remove this tag.' => 'Kan ikke fjerne mærke.', + 'Global tags management' => 'Global mærke styring', + 'Tags' => 'Mærker', + 'Tags management' => 'Mærke styring', + 'Add new tag' => 'Tilføj nyt mærke', + 'Edit a tag' => 'Redigere mærke', + 'Project tags' => 'Projekt mærker', + 'There is no specific tag for this project at the moment.' => 'Ingen specifik projekt mærke i øjeblikket.', + 'Tag' => 'Mærke', + 'Remove a tag' => 'Fjern et mærke', + 'Do you really want to remove this tag: "%s"?' => 'Fjern mærke: "%s"?', + 'Global tags' => 'Globale mærker', + 'There is no global tag at the moment.' => 'Ingen global mærke i øjeblikket.', + 'This field cannot be empty' => 'Feltet kan ikke være tomt', + 'Close a task when there is no activity in a specific column' => 'Lukke opgave nÃ¥r der ikke er nogen aktivitet i bestemt kolonne', + '%s removed a subtask for the task #%d' => '%s fjernet delopgave til opgave #%d', + '%s removed a comment on the task #%d' => '%s fjernet kommentar til opgave #%d', + 'Comment removed on task #%d' => 'Kommentar fjernet til opgave #%d', + 'Subtask removed on task #%d' => 'Delopgave fjernet til opgave #%d', + 'Hide tasks in this column in the dashboard' => 'Skjul opgaver i kolonne i instrumentbrættet', + '%s removed a comment on the task %s' => '%s fjernet kommentar til opgave %s', + '%s removed a subtask for the task %s' => '%s fjernet delopgave til opgave %s', + 'Comment removed' => 'Kommentar fjernet', + 'Subtask removed' => 'Delopgave fjernet', + '%s set a new internal link for the task #%d' => '%s angive ny intern henvisning til opgave #%d', + '%s removed an internal link for the task #%d' => '%s fjernet intern henvisning til opgave #%d', + 'A new internal link for the task #%d has been defined' => 'Ny intern henvisning til opgave #%d er bestemt', + 'Internal link removed for the task #%d' => 'Intern henvisning fjernet til opgave #%d', + '%s set a new internal link for the task %s' => '%s angive ny intern henvisning til opgave %s', + '%s removed an internal link for the task %s' => '%s fjernet intern henvisning til opgave %s', + 'Automatically set the due date on task creation' => 'Angive forfaldsdato automatisk ved oprettelse af opgave', + 'Move the task to another column when closed' => 'Flyt opgave til anden kolonne nÃ¥r den er lukket', + 'Move the task to another column when not moved during a given period' => 'Flyt opgave til anden kolonne nÃ¥r den ikke er flyttet i given periode', + 'Dashboard for %s' => 'Instrumentbræt for %s', + 'Tasks overview for %s' => 'Opgave oversigt for %s', + 'Subtasks overview for %s' => 'Delopgave oversigt for %s', + 'Projects overview for %s' => 'Projektoversigt for %s', + 'Activity stream for %s' => 'Aktivitet strøm for %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Tildele farve nÃ¥r opgave flyttes til bestemt spor', + 'Assign a priority when the task is moved to a specific swimlane' => 'Tildele prioritet nÃ¥r opgave flyttes til bestemt spor', + 'User unlocked successfully.' => 'Bruger frigjort.', + 'Unable to unlock the user.' => 'Kan ikke frigøre bruger.', + 'Move a task to another swimlane' => 'Flyt opgave til andet spor', + 'Creator Name' => 'Opretter navn', + 'Time spent and estimated' => 'Tid brugt og anslÃ¥et', + 'Move position' => 'Flyt placering', + 'Move task to another position on the board' => 'Flyt opgave til anden placering pÃ¥ tavle', + 'Insert before this task' => 'Indsæt før opgave', + 'Insert after this task' => 'Indsæt efter opgave', + 'Unlock this user' => 'Frigøre bruger', + 'Custom Project Roles' => 'Brugertilpasset projektroller', + 'Add a new custom role' => 'Tilføj ny brugertilpasset rolle', + 'Restrictions for the role "%s"' => 'Begrænsninger for rolle "%s"', + 'Add a new project restriction' => 'Tilføj ny projektbegrænsning', + 'Add a new drag and drop restriction' => 'Tilføj ny træk og slip begrænsning', + 'Add a new column restriction' => 'Tilføj ny kolonne begrænsning', + 'Edit this role' => 'Redigere rolle', + 'Remove this role' => 'Fjern rolle', + 'There is no restriction for this role.' => 'Ingen begrænsninger for rolle.', + 'Only moving task between those columns is permitted' => 'Det er kun tilladt at flytte opgave mellem disse kolonner', + 'Close a task in a specific column when not moved during a given period' => 'Lukke opgave i bestemt kolonne nÃ¥r den ikke er flyttet i given periode', + 'Edit columns' => 'Redigere kolonner', + 'The column restriction has been created successfully.' => 'Kolonne begrænsning oprettet.', + 'Unable to create this column restriction.' => 'Kan ikke oprette kolonne begrænsning.', + 'Column restriction removed successfully.' => 'Kolonne begrænsning fjernet.', + 'Unable to remove this restriction.' => 'Kan ikke fjerne begrænsning.', + 'Your custom project role has been created successfully.' => 'Brugertilpasset projektrolle oprettet.', + 'Unable to create custom project role.' => 'Kan ikke oprette brugertilpasset projekt rolle.', + 'Your custom project role has been updated successfully.' => 'Brugertilpasset projektrolle opdateret.', + 'Unable to update custom project role.' => 'Kan ikke opdatere brugertilpasset projekt rolle.', + 'Custom project role removed successfully.' => 'Brugertilpasset projektrolle fjernet.', + 'Unable to remove this project role.' => 'Kan ikke fjerne projekt rolle.', + 'The project restriction has been created successfully.' => 'Projektbegrænsningen oprettet.', + 'Unable to create this project restriction.' => 'Kan ikke oprette projekt begrænsning.', + 'Project restriction removed successfully.' => 'Projektbegrænsningen fjernet.', + 'You cannot create tasks in this column.' => 'Kan ikke oprette opgaver i kolonne.', + 'Task creation is permitted for this column' => 'Opgaveoprettelse tilladt for kolonne', + 'Closing or opening a task is permitted for this column' => 'Lukning eller Ã¥bning af opgave tilladt for kolonne', + 'Task creation is blocked for this column' => 'Opgaveoprettelse blokeret for kolonne', + 'Closing or opening a task is blocked for this column' => 'Lukning eller Ã¥bning af opgave blokeret for kolonne', + 'Task creation is not permitted' => 'Opgaveoprettelse ikke tilladt', + 'Closing or opening a task is not permitted' => 'Lukning eller Ã¥bning af opgave ikke tilladt', + 'New drag and drop restriction for the role "%s"' => 'Ny træk og slip begrænsning for rolle "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Personer med denne rolle kan flytte opgaver mellem kilde og destinations kolonne.', + 'Remove a column restriction' => 'Fjern kolonne begrænsning', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Fjern kolonne begrænsning: "%s" til "%s"?', + 'New column restriction for the role "%s"' => 'Ny kolonne begrænsning for rolle "%s"', + 'Rule' => 'Regel', + 'Do you really want to remove this column restriction?' => 'Fjern kolonne begrænsning?', + 'Custom roles' => 'Brugertilpassede roller', + 'New custom project role' => 'Ny brugertilpasset projektrolle', + 'Edit custom project role' => 'Redigere brugertilpasset projektrolle', + 'Remove a custom role' => 'Fjern brugertilpasset rolle', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Fjern brugertilpasset rolle: "%s"? Alle personer tildelt rolle bliver projektmedlem.', + 'There is no custom role for this project.' => 'Ingen brugertilpasset rolle for projekt.', + 'New project restriction for the role "%s"' => 'Ny projektbegrænsning for rolle "%s"', + 'Restriction' => 'Begrænsning', + 'Remove a project restriction' => 'Fjern projektbegrænsning', + 'Do you really want to remove this project restriction: "%s"?' => 'Fjern projektbegrænsning: "%s"?', + 'Duplicate to multiple projects' => 'Kopiere til flere projekter', + 'This field is required' => 'Felt er pÃ¥krævet', + 'Moving a task is not permitted' => 'Ikke tilladt at flytte opgave', + 'This value must be in the range %d to %d' => 'Værdi skal ligge i omrÃ¥de %d til %d', + 'You are not allowed to move this task.' => 'Ikke tilladelse til at flytte opgave.', + 'API User Access' => 'API bruger adgang', + 'Preview' => 'ForhÃ¥nds-visning', + 'Write' => 'Skriv', + 'Write your text in Markdown' => 'Skriv tekst i mark-down format', + 'No personal API access token registered.' => 'Ingen personlig API adgangsymbol registreret.', + 'Your personal API access token is "%s"' => 'Personlig API adgangsymbol er "%s"', + 'Remove your token' => 'Fjern symbol', + 'Generate a new token' => 'Generere nyt symbol', + 'Showing %d-%d of %d' => 'Viser %d-%d af %d', + 'Outgoing Emails' => 'UdgÃ¥ende e-post', + 'Add or change currency rate' => 'Tilføj eller ændre valutakurs', + 'Reference currency: %s' => 'Referencevaluta: %s', + 'Add custom filters' => 'Tilføj brugertilpasset filtre', + 'Export' => 'Eksport', + 'Add link label' => 'Tilføj henvisnings etiket', + 'Incompatible Plugins' => 'Inkompatibel udvidelses-moduler', + 'Compatibility' => 'Kompatibilitet', + 'Permissions and ownership' => 'Tilladelser og ejerskab', + 'Priorities' => 'Prioriteter', + 'Close this window' => 'Lukke vindue', + 'Unable to upload this file.' => 'Kan ikke overføre fil.', + 'Import tasks' => 'Importere opgaver', + 'Choose a project' => 'Vælge projekt', + 'Profile' => 'Profil', + 'Application role' => 'Program rolle', + '%d invitations were sent.' => '%d invitationer blev sendt.', + '%d invitation was sent.' => '%d invitation blev sendt.', + 'Unable to create this user.' => 'Kan ikke oprette bruger.', + 'Kanboard Invitation' => 'Kanboard invitation', + 'Visible on dashboard' => 'Synlig pÃ¥ instrumentbræt', + 'Created at:' => 'Oprettet pÃ¥:', + 'Updated at:' => 'Opdateret pÃ¥:', + 'There is no custom filter.' => 'Ingen brugertilpasset filter.', + 'New User' => 'Ny Bruger', + 'Authentication' => 'Godkendelse', + 'If checked, this user will use a third-party system for authentication.' => 'Hvis markeret vil bruger bruge et tredjeparts system til godkendelse.', + 'The password is necessary only for local users.' => 'Adgangskode er kun nødvendig for lokale brugere.', + 'You have been invited to register on Kanboard.' => 'Invitation til at registrere pÃ¥ Kanboard.', + 'Click here to join your team' => 'Klik her for at deltage pÃ¥ holdet', + 'Invite people' => 'Invitere personer', + 'Emails' => 'E-post', + 'Enter one email address by line.' => 'Indtaste e-post adresse pr. linje.', + 'Add these people to this project' => 'Tilføj disse personer til projekt', + 'Add this person to this project' => 'Tilføj person til projekt', + 'Sign-up' => 'Tilmelde', + 'Credentials' => 'Legitimations-oplysninger', + 'New user' => 'Ny bruger', + 'This username is already taken' => 'Brugernavn optaget', + 'Your profile must have a valid email address.' => 'Profil skal have gyldig e-post adresse.', + 'TRL - Turkish Lira' => 'TRL - tyrkisk lire', + 'The project email is optional and could be used by several plugins.' => 'Projekt e-post adresse valgfri og kan bruges af flere udvidelses-moduler.', + 'The project email must be unique across all projects' => 'Projekt e-post adresse skal være unik pÃ¥ tværs af projekter', + 'The email configuration has been disabled by the administrator.' => 'E-post indstilling deaktiveret af administrator.', + 'Close this project' => 'Lukke projekt', + 'Open this project' => 'Ã…bne projekt', + 'Close a project' => 'Lukke projekt', + 'Do you really want to close this project: "%s"?' => 'Lukke projekt: "%s"?', + 'Reopen a project' => 'GenÃ¥bne projekt', + 'Do you really want to reopen this project: "%s"?' => 'GenÃ¥bne projekt: "%s"?', + 'This project is open' => 'Projekt Ã¥ben', + 'This project is closed' => 'Projekt lukket', + 'Unable to upload files, check the permissions of your data folder.' => 'Kan ikke overføre filer, kontrollere mappe tilladelser.', + 'Another category with the same name exists in this project' => 'Anden kategori med samme navn i projekt', + 'Comment sent by email successfully.' => 'Kommentar sendt via e-post.', + 'Sent by email to "%s" (%s)' => 'Sendt via e-post til "%s" (%s)', + 'Unable to read uploaded file.' => 'Kan ikke læse overført fil.', + 'Database uploaded successfully.' => 'Database overført.', + 'Task sent by email successfully.' => 'Opgave sendt via e-post.', + 'There is no category in this project.' => 'Ingen kategori i projekt.', + 'Send by email' => 'Send via e-post', + 'Create and send a comment by email' => 'Opret og send kommentar via e-post', + 'Subject' => 'Emne', + 'Upload the database' => 'Overføre database', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Overføre tidligere hentede SQL-database (Gzip format).', + 'Database file' => 'Databasefil', + 'Upload' => 'Overføre', + 'Your project must have at least one active swimlane.' => 'Projekt skal have mindst et aktivt spor.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatisk handling ikke fundet: "%s"', + '%d projects' => '%d projekter', + '%d project' => '%d projekt', + 'There is no project.' => 'Ikke noget projekt.', + 'Sort' => 'Sortere', + 'Project ID' => 'Projekt ID', + 'Project name' => 'Projekt navn', + 'Public' => 'Offentlig', + 'Personal' => 'Privat', + '%d tasks' => '%d opgaver', + '%d task' => '%d opgave', + 'Task ID' => 'Opgave ID', + 'Assign automatically a color when due date is expired' => 'Tildele automatisk farve nÃ¥r forfaldsdato er udløbet', + 'Total score in this column across all swimlanes' => 'Samlet bedømmelse i kolonne pÃ¥ tværs af alle spor', + 'HRK - Kuna' => 'HRK - kroatisk kuna', + 'ARS - Argentine Peso' => 'ARS - argentinsk peso', + 'COP - Colombian Peso' => 'COP - colombiansk peso', + '%d groups' => '%d grupper', + '%d group' => '%d gruppe', + 'Group ID' => 'Gruppe ID', + 'External ID' => 'Ekstern ID', + '%d users' => '%d brugere', + '%d user' => '%d bruger', + 'Hide subtasks' => 'Skjul delopgaver', + 'Show subtasks' => 'Vis delopgaver', + 'Authentication Parameters' => 'Godkendelses-parametre', + 'API Access' => 'API adgang', + 'No users found.' => 'Ingen brugere fundet.', + 'User ID' => 'Bruger ID', + 'Notifications are activated' => 'PÃ¥mindelser aktiveret', + 'Notifications are disabled' => 'PÃ¥mindelser deaktiveret', + 'User disabled' => 'Bruger deaktiveret', + '%d notifications' => '%d pÃ¥mindelser', + '%d notification' => '%d pÃ¥mindelse', + 'There is no external integration installed.' => 'Ingen ekstern integration installeret.', + 'You are not allowed to update tasks assigned to someone else.' => 'Kan ikke opdatere opgaver tildelt anden bruger.', + 'You are not allowed to change the assignee.' => 'Kan ikke ændre ejer.', + 'Task suppression is not permitted' => 'Opgave undertrykkelse er ikke tilladt', + 'Changing assignee is not permitted' => 'Ændring af ansvarlig er ikke tilladt', + 'Update only assigned tasks is permitted' => 'Opdatere kun tilladt tildelte opgaver', + 'Only for tasks assigned to the current user' => 'Kun opgaver tildelt nuværende bruger', + 'My projects' => 'Tildelte projekter', + 'You are not a member of any project.' => 'Ikke medlem af noget projekt.', + 'My subtasks' => 'Tildelte delopgaver', + '%d subtasks' => '%d delopgaver', + '%d subtask' => '%d delopgave', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Kun opgave flyt mellem disse kolonner er tilladt for opgaver tildelt nuværende bruger', + '[DUPLICATE]' => '[KOPIERE]', + 'DKK - Danish Krona' => 'DKK - dansk krone', + 'Remove user from group' => 'Fjern bruger fra gruppe', + 'Assign the task to its creator' => 'Tildele opgave til opretter', + 'This task was sent by email to "%s" with subject "%s".' => 'Opgave sendt via e-post til "%s" med emne "%s".', + 'Predefined Email Subjects' => 'Forudbestemte e-post emner', + 'Write one subject by line.' => 'Skriv ét emne pr. linje.', + 'Create another link' => 'Opret anden henvisning', + 'BRL - Brazilian Real' => 'BRL - brasiliansk real', + 'Add a new Kanboard task' => 'Tilføj ny Kanboard opgave', + 'Subtask not started' => 'Delopgave ikke startet', + 'Subtask currently in progress' => 'Delopgave i gang', + 'Subtask completed' => 'Delopgave afsluttet', + 'Subtask added successfully.' => 'Delopgave tilføjet.', + '%d subtasks added successfully.' => '%d delopgaver tilføjet.', + 'Enter one subtask by line.' => 'Indtaste én delopgave pr. linje.', + 'Predefined Contents' => 'Forudbestemt indhold', + 'Predefined contents' => 'Forudbestemt indhold', + 'Predefined Task Description' => 'Forudbestemt opgavebeskrivelse', + 'Do you really want to remove this template? "%s"' => 'Fjern skabelon? "%s"', + 'Add predefined task description' => 'Tilføj forudbestemt opgavebeskrivelse', + 'Predefined Task Descriptions' => 'Forudbestemte opgavebeskrivelser', + 'Template created successfully.' => 'Skabelonen oprettet.', + 'Unable to create this template.' => 'Kan ikke oprette skabelon.', + 'Template updated successfully.' => 'Skabelon opdateret.', + 'Unable to update this template.' => 'Kan ikke opdatere skabelon.', + 'Template removed successfully.' => 'Skabelon fjernet.', + 'Unable to remove this template.' => 'Kan ikke fjerne skabelon.', + 'Template for the task description' => 'Skabelon til opgavebeskrivelse', + 'The start date is greater than the end date' => 'Startdato er større end slutdato', + 'Tags must be separated by a comma' => 'Mærker skal adskilles med komma', + 'Only the task title is required' => 'Kun opgave titel pÃ¥krævet', + 'Creator Username' => 'Opretters brugernavn', + 'Color Name' => 'Farve navn', + 'Column Name' => 'Kolonne navn', + 'Swimlane Name' => 'Spor navn', + 'Time Estimated' => 'Tid anslÃ¥et', + 'Time Spent' => 'Tid brugt', + 'External Link' => 'Ekstern henvisning', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Funktionen aktiverer iCal og RSS nyheds-kilde samt offentlig tavle visning.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Stop tidsuret for alle delopgaver nÃ¥r opgave flyttes til anden kolonne', + 'Subtask Title' => 'Delopgave titel', + 'Add a subtask and activate the timer when moving a task to another column' => 'Tilføj delopgave og aktivere tidsuret nÃ¥r opgave flyttes til anden kolonne', + 'days' => 'dage', + 'minutes' => 'minutter', + 'seconds' => 'sekunder', + 'Assign automatically a color when preset start date is reached' => 'Tildel automatisk farve nÃ¥r forudindstillet startdato er nÃ¥et', + 'Move the task to another column once a predefined start date is reached' => 'Flyt opgave til anden kolonne nÃ¥r forudbestemt startdato er nÃ¥et', + 'This task is now linked to the task %s with the relation "%s"' => 'Opgave henviser til opgave %s med relation "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Henvisning med relation "%s" til opgave %s er fjernet', + 'Custom Filter:' => 'Brugertilpasset filter:', + 'Unable to find this group.' => 'Kan ikke finde gruppe.', + '%s moved the task #%d to the column "%s"' => '%s flyttet opgave #%d til kolonne "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s flyttet opgave #%d til placering %d i kolonne "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s flyttet opgave #%d til spor "%s"', + '%sh spent' => '%sh brugt', + '%sh estimated' => '%sh anslÃ¥et', + 'Select All' => 'Vælge alle', + 'Unselect All' => 'Fravælge alle', + 'Apply action' => 'Anvende handling', + 'Move selected tasks to another column or swimlane' => 'Flyt valgte opgaver til anden kolonne', + 'Edit tasks in bulk' => 'Masse redigere opgaver', + 'Choose the properties that you would like to change for the selected tasks.' => 'Vælg egenskaber som skal ændres for de valgte opgaver.', + 'Configure this project' => 'Indstille projekt', + 'Start now' => 'Start nu', + '%s removed a file from the task #%d' => '%s fjernede en fil fra opgave #%d', + 'Attachment removed from task #%d: %s' => 'Vedhæftet fjernet fra opgave #%d: %s', + 'No color' => 'Ingen farve', + 'Attachment removed "%s"' => 'Vedhæftet fjernet "%s"', + '%s removed a file from the task %s' => '%s fjernede en fil fra opgave %s', + 'Move the task to another swimlane when assigned to a user' => 'Flyt opgave til andet spor nÃ¥r tildelt anden bruger', + 'Destination swimlane' => 'MÃ¥l spor', + 'Assign a category when the task is moved to a specific swimlane' => 'Tildele kategori nÃ¥r opgave flyttes til anført spor', + 'Move the task to another swimlane when the category is changed' => 'Flyt opgave til andet spor nÃ¥r kategori ændres', + 'Reorder this column by priority (ASC)' => 'Sortere kolonne efter prioritet (ASC)', + 'Reorder this column by priority (DESC)' => 'Sortere kolonne efter prioritet (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Sortere kolonne efter ansvarlig og prioritet (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Sortere kolonne efter ansvarlig og prioritet (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Sorter kolonne efter ansvarlig (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Sorter kolonne efter ansvarlig (Z-A)', + 'Reorder this column by due date (ASC)' => 'Sorter kolonne efter udløbstid (ASC)', + 'Reorder this column by due date (DESC)' => 'Sorter kolonne efter udløbstid (DESC)', + 'Reorder this column by id (ASC)' => 'Sortér denne kolonne efter id (stigende)', + 'Reorder this column by id (DESC)' => 'Sortér denne kolonne efter id (faldende)', + '%s moved the task #%d "%s" to the project "%s"' => '%s flyttet opgave #%d "%s" til projekt "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Opgave #%d "%s" er flyttet til projekt "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Flyt opgave til anden kolonne nÃ¥r afleveringsdato er mindre end et bestemt antal dage', + 'Automatically update the start date when the task is moved away from a specific column' => 'Opdatere automatisk startdato nÃ¥r opgaven flyttes væk fra bestemt kolonne', + 'HTTP Client:' => 'HTTP klient:', + 'Assigned' => 'Tildelt', + 'Task limits apply to each swimlane individually' => 'Opgavegrænser gælder individuelt for hvert sport', + 'Column task limits apply to each swimlane individually' => 'Kolonne opgave grænser gælder individuelt for hvert spor', + 'Column task limits are applied to each swimlane individually' => 'Kolonne opgave grænser anvendes individuelt pÃ¥ hvert spor', + 'Column task limits are applied across swimlanes' => 'Kolonne opgave grænser anvendes pÃ¥ tværs af spor', + 'Task limit: ' => 'Opgave grænser:', + 'Change to global tag' => 'Ændre til globalt mærke', + 'Do you really want to make the tag "%s" global?' => 'Skal mærket "%s" gøres globalt?', + 'Enable global tags for this project' => 'Aktivere globale mærker for dette projekt', + 'Group membership(s):' => 'Gruppe medlemskab', + '%s is a member of the following group(s): %s' => '%s er medlem af følgende gruppe(r): %s', + '%d/%d group(s) shown' => '%d/%d gruppe(r) vist', + 'Subtask creation or modification' => 'Underopgave oprettelse eller ændring', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Tildele opgave til bestemt bruger nÃ¥r opgave flyttes til anført spor', + 'Comment' => 'Kommentar', + 'Collapse vertically' => 'Sammenfolde lodret', + 'Expand vertically' => 'Udvide lodret', + 'MXN - Mexican Peso' => 'Mexicansk Peso', + 'Estimated vs actual time per column' => 'AnslÃ¥et mod faktisk tid pr. kolonne', + 'HUF - Hungarian Forint' => 'HUF - Ungarsk forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Vælg fil som overføres som profil billede!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Overført fil er ikke et gyldigt billede! (*.gif, *.jpg, *.jpeg eller *.png)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Sæt automatisk afleveringsdato nÃ¥r opgave er flyttet væk fra en bestemt kolonne', + 'No other projects found.' => 'Ingen andre projekter fundet.', + 'Tasks copied successfully.' => 'Opgaver blev kopieret med succes.', + 'Unable to copy tasks.' => 'Kunne ikke kopiere opgaver.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Lyst tema', + 'Dark theme' => 'Mørkt tema', + 'Automatic theme - Sync with system' => 'Automatisk tema – synkroniser med systemet', + 'Application managers or more' => 'Applikationsansvarlige eller højere', + 'Administrators' => 'Administratorer', + 'Visibility:' => 'Synlighed:', + 'Standard users' => 'Standardbrugere', + 'Visibility is required' => 'Synlighed er pÃ¥krævet', + 'The visibility should be an app role' => 'Synligheden skal være en app-rolle', + 'Reply' => 'Svar', + '%s wrote: ' => '%s skrev: ', + 'Number of visible tasks in this column and swimlane' => 'Antal synlige opgaver i denne kolonne og bane', + 'Number of tasks in this swimlane' => 'Antal opgaver i denne bane', + 'Unable to find another subtask in progress, you can close this window.' => 'Kunne ikke finde en anden underopgave i gang, du kan lukke dette vindue.', + 'This theme is invalid' => 'Dette tema er ugyldigt', + 'This role is invalid' => 'Denne rolle er ugyldig', + 'This timezone is invalid' => 'Denne tidszone er ugyldig', + 'This language is invalid' => 'Dette sprog er ugyldigt', + 'This URL is invalid' => 'Denne URL er ugyldig', + 'Date format invalid' => 'Ugyldigt datoformat', + 'Time format invalid' => 'Ugyldigt tidsformat', + 'Invalid Mail transport' => 'Ugyldig mailtransport', + 'Color invalid' => 'Ugyldig farve', + 'This value must be greater or equal to %d' => 'Værdien skal være større end eller lig med %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Tilføj en BOM i begyndelsen af filen (krævet af Microsoft Excel)', + 'Just add these tag(s)' => 'Tilføj blot disse mærker', + 'Remove internal link(s)' => 'Fjern interne links', + 'Import tasks from another project' => 'Importer opgaver fra et andet projekt', + 'Select the project to copy tasks from' => 'Vælg projektet, du vil kopiere opgaver fra', + 'The total maximum allowed attachments size is %sB.' => 'Den samlede maksimalt tilladte størrelse for vedhæftede filer er %sB.', + 'Add attachments' => 'Tilføj vedhæftede filer', + 'Task #%d "%s" is overdue' => 'Opgave #%d "%s" er forfalden', + 'Enable notifications by default for all new users' => 'Aktiver pÃ¥mindelser som standard for alle nye brugere', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Tildel opgaven til dens opretter for bestemte kolonner, hvis ingen ansvarlig er sat manuelt', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Tildel en opgave til den loggede bruger ved kolonneændring til den angivne kolonne, hvis ingen bruger er tildelt', +]; diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php new file mode 100644 index 0000000..be33474 --- /dev/null +++ b/app/Locale/de_DE/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Keines', + 'Edit' => 'Bearbeiten', + 'Remove' => 'Entfernen', + 'Yes' => 'Ja', + 'No' => 'Nein', + 'cancel' => 'Abbrechen', + 'or' => 'oder', + 'Yellow' => 'Gelb', + 'Blue' => 'Blau', + 'Green' => 'Grün', + 'Purple' => 'Violett', + 'Red' => 'Rot', + 'Orange' => 'Orange', + 'Grey' => 'Grau', + 'Brown' => 'Braun', + 'Deep Orange' => 'Dunkelorange', + 'Dark Grey' => 'Dunkelgrau', + 'Pink' => 'Pink', + 'Teal' => 'Türkis', + 'Cyan' => 'Cyan', + 'Lime' => 'Limette', + 'Light Green' => 'Hellgrün', + 'Amber' => 'Bernstein', + 'Save' => 'Speichern', + 'Login' => 'Anmelden', + 'Official website:' => 'Offizielle Webseite:', + 'Unassigned' => 'Nicht zugeordnet', + 'View this task' => 'Aufgabe ansehen', + 'Remove user' => 'Benutzer löschen', + 'Do you really want to remove this user: "%s"?' => 'Soll dieser Benutzer wirklich gelöscht werden: "%s"?', + 'All users' => 'Alle Benutzer', + 'Username' => 'Benutzername', + 'Password' => 'Passwort', + 'Administrator' => 'Administrator', + 'Sign in' => 'Anmelden', + 'Users' => 'Benutzer', + 'Forbidden' => 'Verboten', + 'Access Forbidden' => 'Zugriff verboten', + 'Edit user' => 'Benutzer bearbeiten', + 'Logout' => 'Abmelden', + 'Bad username or password' => 'Falscher Benutzername oder Passwort', + 'Edit project' => 'Projekt bearbeiten', + 'Name' => 'Name', + 'Projects' => 'Projekte', + 'No project' => 'Keine Projekte', + 'Project' => 'Projekt', + 'Status' => 'Status', + 'Tasks' => 'Aufgaben', + 'Board' => 'Pinnwand', + 'Actions' => 'Aktionen', + 'Inactive' => 'Inaktiv', + 'Active' => 'Aktiv', + 'Unable to update this board.' => 'Ändern dieser Pinnwand nicht möglich.', + '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', + 'Add a new column' => 'Neue Spalte hinzufügen', + 'Title' => 'Titel', + 'Assigned to %s' => 'Zuständig: %s', + 'Remove a column' => 'Spalte 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"?', + 'Settings' => 'Einstellungen', + 'Application settings' => 'Anwendungskonfiguration', + 'Language' => 'Sprache', + 'Webhook token:' => 'Webhook Token:', + 'API token:' => 'API Token:', + 'Database size:' => 'Datenbankgröße:', + 'Download the database' => 'Datenbank herunterladen', + 'Optimize the database' => 'Datenbank optimieren', + '(VACUUM command)' => '(VACUUM Befehl)', + '(Gzip compressed Sqlite file)' => '(Gzip-komprimierte SQLite-Datei)', + 'Close a task' => 'Aufgabe abschließen', + 'Column' => 'Spalte', + 'Color' => 'Farbe', + 'Assignee' => 'Zuständiger', + 'Create another task' => 'Weitere Aufgabe erstellen', + 'New task' => 'Neue Aufgabe', + 'Open a task' => 'Öffne eine Aufgabe', + 'Do you really want to open this task: "%s"?' => 'Soll diese Aufgabe wirklich wieder geöffnet werden: "%s"?', + 'Back to the board' => 'Zurück zur Pinnwand', + 'There is nobody assigned' => 'Die Aufgabe wurde niemandem zugewiesen', + 'Column on the board:' => 'Spalte:', + 'Close this task' => 'Aufgabe schließen', + 'Open this task' => 'Aufgabe wieder öffnen', + 'There is no description.' => 'Keine Beschreibung vorhanden.', + 'Add a new task' => 'Neue Aufgabe hinzufügen', + 'The username is required' => 'Der Benutzername wird benötigt', + 'The maximum length is %d characters' => 'Die maximale Länge beträgt %d Zeichen', + 'The minimum length is %d characters' => 'Die minimale Länge beträgt %d Zeichen', + 'The password is required' => 'Das Passwort wird benötigt', + 'This value must be an integer' => 'Dieser Wert muss eine ganze Zahl sein', + 'The username must be unique' => 'Der Benutzername muss eindeutig sein', + 'The user id is required' => 'Die Benutzer-ID ist anzugeben', + 'Passwords don\'t match' => 'Passwörter nicht gleich', + 'The confirmation is required' => 'Die Bestätigung ist erforderlich', + 'The project is required' => 'Das Projekt ist anzugeben', + 'The id is required' => 'Die ID ist anzugeben', + 'The project id is required' => 'Die Projekt-ID ist anzugeben', + 'The project name is required' => 'Der Projektname ist anzugeben', + 'The title is required' => 'Der Titel ist anzugeben', + 'Settings saved successfully.' => 'Einstellungen erfolgreich gespeichert.', + 'Unable to save your settings.' => 'Speichern der Einstellungen nicht möglich.', + 'Database optimization done.' => 'Optimieren der Datenbank abgeschlossen.', + 'Your project has been created successfully.' => 'Das Projekt wurde erfolgreich erstellt.', + 'Unable to create your project.' => 'Erstellen des Projekts nicht möglich.', + 'Project updated successfully.' => 'Projekt erfolgreich geändert.', + 'Unable to update this project.' => 'Änderung des Projekts nicht möglich.', + 'Unable to remove this project.' => 'Löschen des Projekts nicht möglich.', + 'Project removed successfully.' => 'Projekt erfolgreich gelöscht.', + 'Project activated successfully.' => 'Projekt erfolgreich aktiviert.', + 'Unable to activate this project.' => 'Aktivieren des Projekts nicht möglich.', + 'Project disabled successfully.' => 'Projekt erfolgreich deaktiviert.', + 'Unable to disable this project.' => 'Deaktivieren des Projekts nicht möglich.', + 'Unable to open this task.' => 'Wiedereröffnung der Aufgabe nicht möglich.', + 'Task opened successfully.' => 'Aufgabe erfolgreich wieder geöffnet.', + 'Unable to close this task.' => 'Abschließen der Aufgabe nicht möglich.', + 'Task closed successfully.' => 'Aufgabe erfolgreich geschlossen.', + 'Unable to update your task.' => 'Aktualisieren der Aufgabe nicht möglich.', + 'Task updated successfully.' => 'Aufgabe erfolgreich aktualisiert.', + 'Unable to create your task.' => 'Erstellen der Aufgabe nicht möglich.', + 'Task created successfully.' => 'Aufgabe erfolgreich erstellt.', + 'User created successfully.' => 'Benutzer erfolgreich erstellt.', + 'Unable to create your user.' => 'Erstellen des Benutzers nicht möglich.', + 'User updated successfully.' => 'Benutzer erfolgreich geändert.', + 'User removed successfully.' => 'Benutzer erfolgreich gelöscht.', + 'Unable to remove this user.' => 'Löschen des Benutzers nicht möglich.', + 'Board updated successfully.' => 'Pinnwand erfolgreich geändert.', + 'Ready' => 'Bereit', + 'Backlog' => 'Ideen', + 'Work in progress' => 'In Arbeit', + 'Done' => 'Erledigt', + 'Application version:' => 'Version:', + 'Id' => 'ID', + 'Public link' => 'Öffentlicher Link', + 'Timezone' => 'Zeitzone', + 'Sorry, I didn\'t find this information in my database!' => 'Diese Information wurde in der Datenbank nicht gefunden!', + 'Page not found' => 'Seite nicht gefunden', + 'Complexity' => 'Komplexität', + 'Task limit' => 'Maximale Anzahl von Aufgaben', + 'Task count' => 'Aufgabenanzahl', + 'User' => 'Benutzer', + 'Comments' => 'Kommentare', + 'Comment is required' => 'Ein Kommentar wird benötigt', + 'Comment added successfully.' => 'Kommentar erfolgreich hinzugefügt.', + 'Unable to create your comment.' => 'Hinzufügen eines Kommentars nicht möglich.', + 'Due Date' => 'Fällig am', + 'Invalid date' => 'Ungültiges Datum', + 'Automatic actions' => 'Automatische Aktionen', + 'Your automatic action has been created successfully.' => 'Die automatische Aktion wurde erfolgreich erstellt.', + 'Unable to create your automatic action.' => 'Erstellen der automatischen Aktion nicht möglich.', + 'Remove an action' => 'Aktion löschen', + 'Unable to remove this action.' => 'Löschen der Aktion nicht möglich.', + 'Action removed successfully.' => 'Aktion erfolgreich gelöscht.', + 'Automatic actions for the project "%s"' => 'Automatische Aktionen für das Projekt "%s"', + 'Add an action' => 'Aktion hinzufügen', + 'Event name' => 'Ereignisname', + 'Action' => 'Aktion', + 'Event' => 'Ereignis', + 'When the selected event occurs execute the corresponding action.' => 'Wenn das gewählte Ereignis eintritt, führe die zugehörige Aktion aus.', + 'Next step' => 'Weiter', + 'Define action parameters' => 'Aktionsparameter definieren', + 'Do you really want to remove this action: "%s"?' => 'Soll diese Aktion wirklich gelöscht werden: "%s"?', + 'Remove an automatic action' => 'Löschen einer automatischen Aktion', + 'Assign the task to a specific user' => 'Aufgabe einem Benutzer zuordnen', + 'Assign the task to the person who does the action' => 'Aufgabe dem Benutzer zuordnen, der die Aktion ausgeführt hat', + 'Duplicate the task to another project' => 'Aufgabe in ein anderes Projekt kopieren', + 'Move a task to another column' => 'Aufgabe in andere Spalte verschieben', + 'Task modification' => 'Aufgabe ändern', + 'Task creation' => 'Aufgabe erstellen', + 'Closing a task' => 'Aufgabe abschließen', + 'Assign a color to a specific user' => 'Einem Benutzer eine Farbe zuordnen', + 'Position' => 'Position', + 'Duplicate to project' => 'In ein anderes Projekt duplizieren', + 'Duplicate' => 'Duplizieren', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Kommentar erfolgreich aktualisiert.', + 'Unable to update your comment.' => 'Aktualisierung des Kommentars nicht möglich.', + 'Remove a comment' => 'Kommentar löschen', + 'Comment removed successfully.' => 'Kommentar erfolgreich gelöscht.', + 'Unable to remove this comment.' => 'Löschen des Kommentars nicht möglich.', + 'Do you really want to remove this comment?' => 'Soll dieser Kommentar wirklich gelöscht werden?', + 'Current password for the user "%s"' => 'Aktuelles Passwort des Benutzers "%s"', + 'The current password is required' => 'Das aktuelle Passwort wird benötigt', + 'Wrong password' => 'Falsches Passwort', + 'Unknown' => 'Unbekannt', + 'Last logins' => 'Letzte Anmeldungen', + 'Login date' => 'Anmeldedatum', + 'Authentication method' => 'Authentisierungsmethode', + 'IP address' => 'IP-Adresse', + 'User agent' => 'User-Agent', + 'Persistent connections' => 'Bestehende Verbindungen', + 'No session.' => 'Keine Sitzung.', + 'Expiration date' => 'Ablaufdatum', + 'Remember Me' => 'Angemeldet bleiben', + 'Creation date' => 'Erstellungsdatum', + 'Everybody' => 'Alle', + 'Open' => 'Offen', + 'Closed' => 'Abgeschlossen', + 'Search' => 'Suchen', + 'Nothing found.' => 'Nichts gefunden.', + 'Due date' => 'Fälligkeitsdatum', + 'Description' => 'Beschreibung', + '%d comments' => '%d Kommentare', + '%d comment' => '%d Kommentar', + 'Email address invalid' => 'Ungültige E-Mail-Adresse', + 'Your external account is not linked anymore to your profile.' => 'Ihr externer Account ist nicht mehr mit Ihrem Profil verbunden.', + 'Unable to unlink your external account.' => 'Externer Account konnte nicht getrennt werden.', + 'External authentication failed' => 'Externe Authentifizierung fehlgeschlagen', + 'Your external account is linked to your profile successfully.' => 'Ihr externer Account wurde erfolgreich mit Ihrem Profil verbunden', + 'Email' => 'E-Mail', + 'Task removed successfully.' => 'Aufgabe erfolgreich gelöscht.', + 'Unable to remove this task.' => 'Löschen der Aufgabe nicht möglich.', + 'Remove a task' => 'Aufgabe löschen', + 'Do you really want to remove this task: "%s"?' => 'Soll diese Aufgabe wirklich gelöscht werden: "%s"?', + 'Assign automatically a color based on a category' => 'Automatisch eine Farbe anhand der Kategorie zuweisen', + 'Assign automatically a category based on a color' => 'Automatisch eine Kategorie anhand der Farbe zuweisen', + 'Task creation or modification' => 'Aufgabe erstellen oder ändern', + 'Category' => 'Kategorie', + 'Category:' => 'Kategorie:', + 'Categories' => 'Kategorien', + 'Your category has been created successfully.' => 'Kategorie erfolgreich erstellt.', + 'This category has been updated successfully.' => 'Kategorie erfolgreich aktualisiert.', + 'Unable to update this category.' => 'Änderung der Kategorie nicht möglich.', + 'Remove a category' => 'Kategorie löschen', + 'Category removed successfully.' => 'Kategorie erfolgreich gelöscht.', + 'Unable to remove this category.' => 'Löschen der Kategorie nicht möglich.', + 'Category modification for the project "%s"' => 'Kategorie für das Projekt "%s" bearbeiten', + 'Category Name' => 'Kategoriename', + 'Add a new category' => 'Neue Kategorie', + 'Do you really want to remove this category: "%s"?' => 'Soll diese Kategorie wirklich gelöscht werden: "%s"?', + 'All categories' => 'Alle Kategorien', + 'No category' => 'Keine Kategorie', + 'The name is required' => 'Der Name ist erforderlich', + 'Remove a file' => 'Datei löschen', + 'Unable to remove this file.' => 'Löschen der Datei nicht möglich.', + 'File removed successfully.' => 'Datei erfolgreich gelöscht.', + 'Attach a document' => 'Dokument anhängen', + 'Do you really want to remove this file: "%s"?' => 'Soll diese Datei wirklich gelöscht werden: "%s"?', + 'Attachments' => 'Anhänge', + 'Edit the task' => 'Aufgabe bearbeiten', + 'Add a comment' => 'Kommentar hinzufügen', + 'Edit a comment' => 'Kommentar bearbeiten', + 'Summary' => 'Zusammenfassung', + 'Time tracking' => 'Zeiterfassung', + 'Estimate:' => 'Geschätzt:', + 'Spent:' => 'Aufgewendet:', + 'Do you really want to remove this sub-task?' => 'Soll diese Teilaufgabe wirklich gelöscht werden?', + 'Remaining:' => 'Verbleibend:', + 'hours' => 'Stunden', + 'estimated' => 'geschätzt', + 'Sub-Tasks' => 'Teilaufgaben', + 'Add a sub-task' => 'Teilaufgabe anlegen', + 'Original estimate' => 'Geschätzter Aufwand', + 'Create another sub-task' => 'Weitere Teilaufgabe anlegen', + 'Time spent' => 'Aufgewendete Zeit', + 'Edit a sub-task' => 'Teilaufgabe bearbeiten', + 'Remove a sub-task' => 'Teilaufgabe löschen', + 'The time must be a numeric value' => 'Zeit nur als nummerische Angabe', + 'Todo' => 'Nicht gestartet', + 'In progress' => 'In Bearbeitung', + 'Sub-task removed successfully.' => 'Teilaufgabe erfolgreich gelöscht.', + 'Unable to remove this sub-task.' => 'Löschen der Teilaufgabe nicht möglich.', + 'Sub-task updated successfully.' => 'Teilaufgabe erfolgreich aktualisiert.', + 'Unable to update your sub-task.' => 'Aktualisieren der Teilaufgabe nicht möglich.', + 'Unable to create your sub-task.' => 'Erstellen der Teilaufgabe nicht möglich.', + 'Maximum size: ' => 'Maximalgröße: ', + 'Display another project' => 'Zu Projekt wechseln', + 'Created by %s' => 'Erstellt durch %s', + 'Tasks Export' => 'Aufgaben exportieren', + 'Start Date' => 'Anfangsdatum', + 'Execute' => 'Ausführen', + 'Task Id' => 'Aufgaben-ID', + 'Creator' => 'Erstellt von', + 'Modification date' => 'Änderungsdatum', + 'Completion date' => 'Abschlussdatum', + 'Clone' => 'duplizieren', + 'Project cloned successfully.' => 'Projekt wurde dupliziert.', + 'Unable to clone this project.' => 'Duplizieren dieses Projekts schlug fehl.', + 'Enable email notifications' => 'E-Mail-Benachrichtigungen einschalten', + 'Task position:' => 'Position der Aufgabe:', + 'The task #%d has been opened.' => 'Die Aufgabe #%d wurde geöffnet.', + 'The task #%d has been closed.' => 'Die Aufgabe #%d wurde geschlossen.', + 'Sub-task updated' => 'Teilaufgabe aktualisiert', + 'Title:' => 'Titel:', + 'Status:' => 'Status:', + 'Assignee:' => 'Zuständigkeit:', + 'Time tracking:' => 'Zeiterfassung:', + '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 comment' => 'Neuer Kommentar', + 'Comment updated' => 'Kommentar wurde aktualisiert', + 'New subtask' => 'Neue Teilaufgabe', + 'I only want to receive notifications for these 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', + 'Disable public access' => 'Öffentlichen Zugriff deaktivieren', + 'Enable public access' => 'Öffentlichen Zugriff aktivieren', + 'Public access disabled' => 'Öffentlicher Zugriff deaktiviert', + 'Move the task to another project' => 'Aufgabe in ein anderes Projekt verschieben', + 'Move to project' => 'In anderes Projekt verschieben', + 'Do you really want to duplicate this task?' => 'Möchten Sie diese Aufgabe wirklich duplizieren?', + 'Duplicate a task' => 'Aufgabe duplizieren', + 'External accounts' => 'Externe Accounts', + 'Account type' => 'Accounttyp', + 'Local' => 'Lokal', + 'Remote' => 'Remote', + 'Enabled' => 'angeschaltet', + 'Disabled' => 'abgeschaltet', + 'Login:' => 'Benutzername:', + 'Full Name:' => 'Vollständiger Name:', + 'Email:' => 'E-Mail:', + 'Notifications:' => 'Benachrichtigungen:', + 'Notifications' => 'Benachrichtigungen', + 'Account type:' => 'Accounttyp:', + 'Edit profile' => 'Profil bearbeiten', + 'Change password' => 'Passwort ändern', + 'Password modification' => 'Passwortänderung', + 'External authentications' => 'Externe Authentisierungsmethoden', + 'Never connected.' => 'Noch nie verbunden.', + 'No external authentication enabled.' => 'Es sind keine externen Authentisierungsmethoden aktiv.', + 'Password modified successfully.' => 'Passwort wurde erfolgreich geändert.', + 'Unable to change the password.' => 'Passwort konnte nicht geändert werden.', + 'Change category' => 'Kategorie ändern', + '%s updated the task %s' => '%s hat die Aufgabe %s aktualisiert', + '%s opened the task %s' => '%s hat die Aufgabe %s geöffnet', + '%s moved the task %s to the position #%d in the column "%s"' => '%s hat die Aufgabe %s auf die Position #%d in der Spalte "%s" verschoben', + '%s moved the task %s to the column "%s"' => '%s hat die Aufgabe %s in die Spalte "%s" verschoben', + '%s created the task %s' => '%s hat die Aufgabe %s angelegt', + '%s closed the task %s' => '%s hat die Aufgabe %s geschlossen', + '%s created a subtask for the task %s' => '%s hat eine Teilaufgabe für die Aufgabe %s angelegt', + '%s updated a subtask for the task %s' => '%s hat eine Teilaufgabe der Aufgabe %s verändert', + 'Assigned to %s with an estimate of %s/%sh' => 'An %s zugewiesen mit einer Schätzung von %s/%s Stunden', + 'Not assigned, estimate of %sh' => 'Nicht zugewiesen, Schätzung von %s Stunden', + '%s updated a comment on the task %s' => '%s hat einen Kommentar der Aufgabe %s aktualisiert', + '%s commented the task %s' => '%s hat die Aufgabe %s kommentiert', + '%s\'s activity' => '%s\'s Aktivität', + 'RSS feed' => 'RSS Feed', + '%s updated a comment on the task #%d' => '%s hat einen Kommentar der Aufgabe #%d aktualisiert', + '%s commented on the task #%d' => '%s hat die Aufgabe #%d kommentiert', + '%s updated a subtask for the task #%d' => '%s hat eine Teilaufgabe der Aufgabe #%d aktualisiert', + '%s created a subtask for the task #%d' => '%s hat eine Teilaufgabe der Aufgabe #%d angelegt', + '%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 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 changed the assignee of the task #%d to %s' => '%s hat die Zuständigkeit der Aufgabe #%d geändert zu %s', + '%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert zu %s', + 'New password for the user "%s"' => 'Neues Passwort des Benutzers "%s"', + 'Choose an event' => 'Aktion wählen', + 'Create a task from an external provider' => 'Eine Aufgabe durch einen externen Provider hinzufügen', + 'Change the assignee based on an external username' => 'Zuordnung ändern basierend auf externem Benutzernamen', + 'Change the category based on an external label' => 'Kategorie basierend auf einer externen Kennzeichnung ändern', + 'Reference' => 'Referenz', + 'Label' => 'Kennzeichnung', + 'Database' => 'Datenbank', + 'About' => 'Über', + 'Database driver:' => 'Datenbanktreiber:', + 'Board settings' => 'Pinnwandeinstellungen', + 'Webhook settings' => 'Webhook-Einstellungen', + 'Reset token' => 'Token zurücksetzen', + 'API endpoint:' => 'API-Endpunkt:', + 'Refresh interval for personal board' => 'Aktualisierungsintervall für persönliche Pinnwände', + 'Refresh interval for public board' => 'Aktualisierungsintervall für öffentliche Pinnwände', + 'Task highlight period' => 'Aufgaben-Hervorhebungsdauer', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Dauer (in Sekunden), wie lange eine Aufgabe als kürzlich verändert gilt (0 um diese Funktion zu deaktivieren, standardmäßig 2 Tage)', + 'Frequency in second (60 seconds by default)' => 'Frequenz in Sekunden (standardmäßig 60 Sekunden)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequenz in Sekunden (0 um diese Funktion zu deaktivieren, standardmäßig 10 Sekunden)', + 'Application URL' => 'Applikations-URL', + 'Token regenerated.' => 'Token wurde neu generiert.', + 'Date format' => 'Datumsformat', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO Format wird immer akzeptiert, z.B.: "%s" und "%s"', + 'New personal project' => 'Neues persönliches Projekt', + 'This project is personal' => 'Dies ist ein persönliches Projekt', + 'Add' => 'Hinzufügen', + 'Start date' => 'Startdatum', + 'Time estimated' => 'Geschätzte Zeit', + 'There is nothing assigned to you.' => 'Ihnen ist nichts zugewiesen.', + 'My tasks' => 'Meine Aufgaben', + 'Activity stream' => 'Letzte Aktivitäten', + 'Dashboard' => 'Dashboard', + 'Confirmation' => 'Wiederholung', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Kommentar eines externen Providers hinzufügen', + 'Project management' => 'Projektmanagement', + 'Columns' => 'Spalten', + 'Task' => 'Aufgabe', + 'Percentage' => 'Prozentsatz', + 'Number of tasks' => 'Anzahl an Aufgaben', + 'Task distribution' => 'Aufgabenverteilung', + 'Analytics' => 'Analyse', + 'Subtask' => 'Teilaufgabe', + 'User repartition' => 'Benutzerverteilung', + '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.', + 'Previous' => 'Vorherige', + 'The id must be an integer' => 'Die ID muss eine ganze Zahl sein', + 'The project id must be an integer' => 'Der Projekt-ID muss eine ganze Zahl sein', + 'The status must be an integer' => 'Der Status muss eine ganze Zahl sein', + 'The subtask id is required' => 'Die Teilaufgaben-ID ist benötigt', + 'The subtask id must be an integer' => 'Die Teilaufgaben-ID muss eine ganze Zahl sein', + 'The task id is required' => 'Die Aufgaben-ID ist benötigt', + 'The task id must be an integer' => 'Die Aufgaben-ID muss eine ganze Zahl sein', + 'The user id must be an integer' => 'Die Benutzer-ID muss eine ganze Zahl sein', + 'This value is required' => 'Dieser Wert ist erforderlich', + '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', + 'Daily project summary' => 'Tägliche Projektzusammenfassung', + 'Daily project summary export' => 'Export der täglichen Projektzusammenfassung', + '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', + 'Add a new swimlane' => 'Eine neue Swimlane hinzufügen', + 'Default swimlane' => 'Standard-Swimlane', + 'Do you really want to remove this swimlane: "%s"?' => 'Diese Swimlane wirklich entfernen: "%s"?', + 'Inactive swimlanes' => 'Inaktive Swimlane', + 'Remove a swimlane' => 'Swimlane entfernen', + 'Swimlane modification for the project "%s"' => 'Swimlane-Änderung für das Projekt "%s"', + 'Swimlane removed successfully.' => 'Swimlane erfolgreich entfernt.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane erfolgreich geändert.', + 'Unable to remove this swimlane.' => 'Es ist nicht möglich, die Swimlane zu entfernen.', + 'Unable to update this swimlane.' => 'Es ist nicht möglich, die Swimlane zu ändern.', + 'Your swimlane has been created successfully.' => 'Die Swimlane wurde erfolgreich angelegt.', + 'Example: "Bug, Feature Request, Improvement"' => 'Beispiel: "Bug, Funktionswünsche, Verbesserung"', + 'Default categories for new projects (Comma-separated)' => 'Standard-Kategorien für neue Projekte (Komma-getrennt)', + 'Integrations' => 'Integration', + 'Integration with third-party services' => 'Integration von externen Diensten', + 'Subtask Id' => 'Teilaufgaben-ID', + 'Subtasks' => 'Teilaufgaben', + 'Subtasks Export' => 'Export von Teilaufgaben', + 'Task Title' => 'Aufgaben-Titel', + 'Untitled' => 'unbetitelt', + 'Application default' => 'Anwendungsstandard', + 'Language:' => 'Sprache:', + 'Timezone:' => 'Zeitzone:', + 'All columns' => 'Alle Spalten', + 'Next' => 'Nächste', + '#%d' => 'Nr %d', + 'All swimlanes' => 'Alle Swimlanes', + 'All colors' => 'Alle Farben', + 'Moved to column %s' => 'In Spalte %s verschoben', + 'User dashboard' => 'Benutzer-Dashboard', + 'Allow only one subtask in progress at the same time for a user' => 'Erlaube nur eine Teilaufgabe pro Benutzer zu bearbeiten', + 'Edit column "%s"' => 'Spalte "%s" bearbeiten', + 'Select the new status of the subtask: "%s"' => 'Wähle einen neuen Status für Teilaufgabe: "%s"', + 'Subtask timesheet' => 'Teilaufgaben Zeiterfassung', + 'There is nothing to show.' => 'Es ist nichts zum Anzeigen vorhanden.', + 'Time Tracking' => 'Zeiterfassung', + 'You already have one subtask in progress' => 'Bereits eine Teilaufgabe in Bearbeitung', + 'Which parts of the project do you want to duplicate?' => 'Welcher Teil des Projekts soll kopiert werden?', + 'Disallow login form' => 'Verbiete Login-Formular', + 'Start' => 'Start', + 'End' => 'Ende', + 'Task age in days' => 'Aufgabenalter in Tagen', + 'Days in this column' => 'Tage in dieser Spalte', + '%dd' => '%dT', + 'Add a new link' => 'Neue Verbindung hinzufügen', + 'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?', + 'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?', + 'Field required' => 'Feld erforderlich', + 'Link added successfully.' => 'Verbindung erfolgreich hinzugefügt.', + 'Link updated successfully.' => 'Verbindung erfolgreich aktualisiert.', + 'Link removed successfully.' => 'Verbindung erfolgreich gelöscht.', + 'Link labels' => 'Verbindungsbeschriftung', + 'Link modification' => 'Verbindung ändern', + 'Opposite label' => 'Gegenteil', + 'Remove a link' => 'Verbindung entfernen', + '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', + 'Unable to create your link.' => 'Verbindung kann nicht erstellt werden.', + 'Unable to update your link.' => 'Verbindung kann nicht aktualisiert werden.', + 'Unable to remove this link.' => 'Verbindung kann nicht entfernt werden', + 'relates to' => 'gehört zu', + 'blocks' => 'blockiert', + 'is blocked by' => 'ist blockiert von', + 'duplicates' => 'doppelt', + 'is duplicated by' => 'ist gedoppelt von', + 'is a child of' => 'ist ein untergeordnetes Element von', + 'is a parent of' => 'ist ein übergeordnetes Element von', + 'targets milestone' => 'betrifft Meilenstein', + 'is a milestone of' => 'ist ein Meilenstein von', + 'fixes' => 'behebt', + 'is fixed by' => 'wird behoben von', + 'This task' => 'Diese Aufgabe', + '<1h' => '<1Std', + '%dh' => '%dStd', + 'Expand tasks' => 'Aufgaben aufklappen', + 'Collapse tasks' => 'Aufgaben zusammenklappen', + 'Expand/collapse tasks' => 'Aufgaben auf/zuklappen', + 'Close dialog box' => 'Dialog schließen', + 'Submit a form' => 'Formular abschicken', + 'Board view' => 'Pinnwand Ansicht', + 'Keyboard shortcuts' => 'Tastaturkürzel', + 'Open board switcher' => 'Pinnwandauswahl öffnen', + 'Application' => 'Anwendung', + 'Compact view' => 'Kompaktansicht', + 'Horizontal scrolling' => 'Horizontales Scrollen', + 'Compact/wide view' => 'Kompakt/Breite-Ansicht', + 'Currency' => 'Währung', + 'Personal project' => 'privates Projekt', + 'AUD - Australian Dollar' => 'AUD - Australische Dollar', + 'CAD - Canadian Dollar' => 'CAD - Kanadische Dollar', + 'CHF - Swiss Francs' => 'CHF - Schweizer Franken', + 'Custom Stylesheet' => 'benutzerdefiniertes Stylesheet', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britische Pfund', + 'INR - Indian Rupee' => 'INR - Indische Rupien', + 'JPY - Japanese Yen' => 'JPY - Japanische Yen', + 'NZD - New Zealand Dollar' => 'NZD - Neuseeland-Dollar', + 'PEN - Peruvian Sol' => 'PEN - Peruanischer Sol', + 'RSD - Serbian dinar' => 'RSD – Serbischer Dinar', + 'CNY - Chinese Yuan' => 'CNY - Chinesischer Yuan', + 'USD - US Dollar' => 'USD – US-Dollar', + 'VES - Venezuelan Bolívar' => 'VES - Venezolanischer Bolívar', + 'Destination column' => 'Zielspalte', + 'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein Benutzer zugeordnet wurde.', + 'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.', + 'Source column' => 'Quellspalte', + 'Transitions' => 'Übergänge', + 'Executer' => 'Ausführender', + 'Time spent in the column' => 'Zeit in Spalte verbracht', + 'Task transitions' => 'Aufgaben-Übergänge', + 'Task transitions export' => 'Aufgaben-Übergänge exportieren', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Diese Auswertung enthält alle Spaltenbewegungen für jede Aufgabe mit Datum, Benutzer und Zeit vor jedem Wechsel.', + 'Currency rates' => 'Währungskurse', + 'Rate' => 'Kurse', + 'Change reference currency' => 'Referenzwährung ändern', + 'Reference currency' => 'Referenzwährung', + 'The currency rate has 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 removed the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen', + 'Information' => 'Information', + 'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode', + 'The two factor authentication code is not valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist ungültig.', + 'The two factor authentication code is valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist gültig.', + 'Code' => 'Code', + 'Two factor authentication' => 'Zwei-Faktor-Authentifizierung', + 'This QR code contains the key URI: ' => 'Dieser QR-Code beinhaltet die Schlüssel-URI: ', + 'Check my code' => 'Überprüfe meinen Code', + 'Secret key: ' => 'Geheimer Schlüssel: ', + 'Test your device' => 'Testen Sie Ihr 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' => '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', + 'Add a screenshot' => 'Screenshot hinzufügen', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Nehmen Sie einen Screenshot auf und drücken STRG+V oder ⌘+V um ihn hier einzufügen.', + 'Screenshot uploaded successfully.' => 'Screenshot erfolgreich hochgeladen.', + 'SEK - Swedish Krona' => 'SEK - Schwedische Kronen', + 'Identifier' => 'Identifikator', + 'Disable two factor authentication' => 'Deaktiviere Zwei-Faktor-Authentifizierung', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Wollen Sie wirklich für folgenden Benutzer die Zwei-Faktor-Authentifizierung deaktivieren: "%s"?', + 'Edit link' => 'Verbindung bearbeiten', + 'Start to type task title...' => 'Beginne mit der Titeleingabe...', + 'A task cannot be linked to itself' => 'Eine Aufgabe kann nicht mit sich selber verbunden werden', + 'The exact same link already exists' => 'Diese Verbindung existiert bereits', + 'Recurrent task is scheduled to be generated' => 'Wiederkehrende Aufgabe ist zur Generierung eingeplant', + 'Score' => 'Wertung', + 'The identifier must be unique' => 'Der Schlüssel muss einzigartig sein', + 'This linked task id doesn\'t exists' => 'Die verbundene Aufgabe existiert nicht', + 'This value must be alphanumeric' => 'Der Wert muss alphanumerisch sein', + 'Edit recurrence' => 'Wiederholung bearbeiten', + 'Generate recurrent task' => 'Wiederkehrende Aufgabe generieren', + 'Trigger to generate recurrent task' => 'Auslöser für wiederkehrende Aufgabe', + 'Factor to calculate new due date' => 'Faktor zur Berechnung für neues Ablaufdatum', + 'Timeframe to calculate new due date' => 'Zeitfenster zur Berechnung für neues Ablaufdatum', + 'Base date to calculate new due date' => 'Basisdatum zur Berechnung für neues Ablaufdatum', + 'Action date' => 'Aktionsdatum', + 'Base date to calculate new due date: ' => 'Basisdatum zur Berechnung für neues Ablaufdatum: ', + 'This task has created this child task: ' => 'Diese Aufgabe hat diese Teilaufgabe erstellt: ', + 'Day(s)' => 'Tag(e)', + 'Existing due date' => 'Existierendes Ablaufdatum', + 'Factor to calculate new due date: ' => 'Faktor zur Berechnung für neues Ablaufdatum: ', + 'Month(s)' => 'Monat(e)', + '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 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)', + 'Project settings' => 'Projekteinstellungen', + 'Automatically update the start date' => 'Beginndatum automatisch aktualisieren', + 'iCal feed' => 'iCal Feed', + 'Preferences' => 'Einstellungen', + 'Security' => 'Sicherheit', + 'Two factor authentication disabled' => 'Zwei-Faktor-Authentifizierung deaktiviert', + 'Two factor authentication enabled' => 'Zwei-Faktor-Authentifizierung aktiviert', + 'Unable to update this user.' => 'Benutzer kann nicht bearbeitet werden', + 'There is no user management for personal projects.' => 'Es gibt keine Benutzerverwaltung für persönliche Projekte', + 'User that will receive the email' => 'Empfänger der E-Mail', + 'Email subject' => 'E-Mail-Betreff', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'Kommentar hinzufügen, wenn Aufgabe in andere Spalte verschoben wird', + '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', + 'Notification' => 'Benachrichtigungen', + '%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben', + '%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben', + 'This report contains all subtasks information for the given date range.' => 'Der Bericht beinhaltet alle Teilaufgaben im gewählten Zeitraum', + 'This report contains all tasks information for the given date range.' => 'Der Bericht beinhaltet alle Aufgaben im gewählten Zeitraum', + 'Project activities for %s' => 'Projektaktivitäten für %s', + 'view the board on Kanboard' => 'Pinnwand in Kanboard anzeigen', + 'The task has been moved to the first swimlane' => 'Die Aufgabe wurde in die erste Swimlane verschoben', + 'The task has been moved to another swimlane:' => 'Die Aufgaben wurde in eine andere Swimlane verschoben:', + 'New title: %s' => 'Neuer Titel: %s', + 'The task is not assigned anymore' => 'Die Aufgabe ist nicht mehr zugewiesen', + 'New assignee: %s' => 'Neue Zuordnung: %s', + 'There is no category now' => 'Es gibt keine Kategorie im Moment', + 'New category: %s' => 'Neue Kategorie: %s', + 'New color: %s' => 'Neue Farbe: %s', + 'New complexity: %d' => 'Neue Komplexität: %d', + 'The due date has been removed' => 'Das Ablaufdatum wurde entfernt', + 'There is no description anymore' => 'Es gibt keine Beschreibung mehr', + 'Recurrence settings has been modified' => 'Die Einstellungen für Wiederholung wurden geändert', + 'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh', + 'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh', + 'The field "%s" has been updated' => 'Das Feld "%s" wurde verändert', + 'The description has been modified:' => 'Die Beschreibung wurde geändert:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)', + 'I want to receive notifications for:' => 'Ich möchte Benachrichtigungen erhalten für:', + 'All tasks' => 'Alle Aufgaben', + 'Only for tasks assigned to me' => 'nur mir zugeordnete Aufgaben', + 'Only for tasks created by me' => 'nur von mir erstellte Aufgaben', + 'Only for tasks created by me and tasks assigned to me' => 'nur mir zugeordnete und von mir erstellte Aufgaben', + '%%Y-%%m-%%d' => '%%d.%%m.%%Y', + 'Total for all columns' => 'Gesamt für alle Spalten', + 'You need at least 2 days of data to show the chart.' => 'Es werden mindestens 2 Tage zur Darstellung benötigt', + '<15m' => '<15min', + '<30m' => '<30min', + 'Stop timer' => 'Stoppe Timer', + 'Start timer' => 'Starte Timer', + 'My activity stream' => 'Aktivitätsstream', + 'Search tasks' => 'Suche nach Aufgaben', + 'Reset filters' => 'Filter zurücksetzen', + 'My tasks due tomorrow' => 'Meine morgen fälligen Aufgaben', + 'Tasks due today' => 'Heute fällige Aufgaben', + 'Tasks due tomorrow' => 'Morgen fällige Aufgaben', + 'Tasks due yesterday' => 'Gestern fällige Aufgaben', + 'Closed tasks' => 'Abgeschlossene Aufgaben', + 'Open tasks' => 'Offene Aufgaben', + 'Not assigned' => 'Nicht zugewiesen', + 'View advanced search syntax' => 'Zur erweiterten Suchsyntax', + 'Overview' => 'Überblick', + 'Board/Calendar/List view' => 'Board-/Kalender-/Listen-Ansicht', + 'Switch to the board view' => 'Zur Board-Ansicht', + 'Switch to the list view' => 'Zur Listen-Ansicht', + 'Go to the search/filter box' => 'Zum Such- und Filterfeld', + 'There is no activity yet.' => 'Es gibt bislang keine Aktivitäten.', + 'No tasks found.' => 'Keine Aufgaben gefunden.', + 'Keyboard shortcut: "%s"' => 'Tastaturkürzel: "%s"', + 'List' => 'Liste', + 'Filter' => 'Filter', + 'Advanced search' => 'Fortgeschrittene Suche', + 'Example of query: ' => 'Beispiel einer Abfrage: ', + 'Search by project: ' => 'Suche nach Projekt: ', + 'Search by column: ' => 'Suche nach Spalte: ', + 'Search by assignee: ' => 'Suche nach zugeordnetem Benutzer: ', + 'Search by color: ' => 'Suche nach Farbe: ', + 'Search by category: ' => 'Suche nach Kategorie: ', + 'Search by description: ' => 'Suche nach Beschreibung: ', + 'Search by due date: ' => 'Suche nach Fälligkeitsdatum: ', + 'Average time spent in each column' => 'Durchschnittszeit in jeder Spalte', + 'Average time spent' => 'Durchschnittlicher Zeitverbrauch', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Dieses Diagramm zeigt die durchschnittliche Zeit in jeder Spalte der letzten %d Aufgaben.', + 'Average Lead and Cycle time' => 'Durchschnittliche Zyklus- und Durchlaufzeit', + 'Average lead time: ' => 'Durchschnittliche Durchlaufzeit: ', + 'Average cycle time: ' => 'Durchschnittliche Zykluszeit: ', + 'Cycle Time' => 'Zykluszeit', + 'Lead Time' => 'Durchlaufzeit', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Das Diagramm zeigt die durchschnittliche Durchlauf- und Zykluszeit der letzten %d Aufgaben über die Zeit an.', + 'Average time into each column' => 'Durchschnittszeit in jeder Spalte', + 'Lead and cycle time' => 'Durchlauf- und Zykluszeit', + 'Lead time: ' => 'Durchlaufzeit: ', + 'Cycle time: ' => 'Zykluszeit: ', + 'Time spent in each column' => 'zeit verbracht in jeder Spalte', + 'The lead time is the duration between the task creation and the completion.' => 'Die Durchlaufzeit ist die Dauer zwischen Erstellung und Fertigstellung.', + 'The cycle time is the duration between the start date and the completion.' => 'Die Zykluszeit ist die Dauer zwischen Start und Fertigstellung.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Wenn die Aufgabe nicht geschlossen ist, wird die aktuelle Zeit statt der Fertigstellung verwendet.', + 'Set the start date automatically' => 'Setze Startdatum automatisch', + 'Edit Authentication' => 'Authentifizierung bearbeiten', + '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.', + '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.', + 'Trigger automatically subtask time tracking' => 'Teilaufgaben Zeiterfassung automatisch starten', + 'Include closed tasks in the cumulative flow diagram' => 'Geschlossen Aufgaben ins kumulative Flussdiagramm einschließen', + 'Current swimlane: %s' => 'Aktuelle Swimlane: %s', + 'Current column: %s' => 'Aktuelle Spalte: %s', + 'Current category: %s' => 'Aktuelle Kategorie: %s', + 'no category' => 'keine Kategorie', + 'Current assignee: %s' => 'Aktuelle Zuordnung: %s', + 'not assigned' => 'nicht zugeordnet', + 'Author:' => 'Autor:', + 'contributors' => 'Mitwirkende', + 'License:' => 'Lizenz:', + 'License' => 'Lizenz', + 'Enter the text below' => 'Text unten eingeben', + 'Start date:' => 'Startdatum:', + 'Due date:' => 'Ablaufdatum:', + 'People who are project managers' => 'Benutzer die Projektmanager sind', + 'People who are project members' => 'Benutzer die Projektmitglieder sind', + 'NOK - Norwegian Krone' => 'NOK - Norwegische Kronen', + 'Show this column' => 'Spalte anzeigen', + 'Hide this column' => 'Spalte verstecken', + 'End date' => 'Endedatum', + 'Users overview' => 'Benutzerübersicht', + 'Members' => 'Mitglieder', + 'Shared project' => 'Geteiltes Projekt', + 'Project managers' => 'Projektmanager', + 'Projects list' => 'Projektliste', + 'End date:' => 'Endedatum:', + 'Change task color when using a specific task link' => 'Aufgabefarbe ändern bei bestimmter Aufgabenverbindung', + 'Task link creation or modification' => 'Aufgabenverbindung erstellen oder bearbeiten', + 'Milestone' => 'Meilenstein', + 'Reset the search/filter box' => 'Suche/Filter-Box zurücksetzen', + 'Documentation' => 'Dokumentation', + 'Author' => 'Autor', + 'Version' => 'Version', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Es ist kein Plugin geladen.', + 'My notifications' => 'Meine Benachrichtigungen', + 'Custom filters' => 'Benutzerdefinierte Filter', + 'Your custom filter has been created successfully.' => 'Benutzerdefinierten Filter erfolgreich erstellt.', + 'Unable to create your custom filter.' => 'Benutzerdefinierter Filter konnte nicht erstellt werden.', + 'Custom filter removed successfully.' => 'Benutzerdefinierten Filter erfolgreich entfernt.', + 'Unable to remove this custom filter.' => 'Benutzerdefinierten Filter konnte nicht entfernt werden.', + 'Edit custom filter' => 'Benutzerdefinierten Filter bearbeiten', + 'Your custom filter has been updated successfully.' => 'Benutzerdefinierten Filter erfolgreich bearbeitet.', + 'Unable to update custom filter.' => 'Benutzerdefinierter Filter konnte nicht geändert werden.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Neuer Anhang für Aufgabe #%d: %s', + 'New comment on task #%d' => 'Neuer Kommentar für Aufgabe #%d', + 'Comment updated on task #%d' => 'Kommentar geändert für Aufgabe #%d', + 'New subtask on task #%d' => 'Neue Teilaufgabe für Aufgabe #%d', + 'Subtask updated on task #%d' => 'Teilaufgabe geändert für Aufgabe #%d', + 'New task #%d: %s' => 'Neue Aufgabe #%d: %s', + 'Task updated #%d' => 'Aufgabe bearbeitet #%d', + 'Task #%d closed' => 'Aufgabe #%d geschlossen', + 'Task #%d opened' => 'Aufgabe #%d eröffnet', + 'Column changed for task #%d' => 'Spalte geändert von Aufgabe #%d', + 'New position for task #%d' => 'Neue Position für Aufgabe #%d', + 'Swimlane changed for task #%d' => 'Neue Swimlane für Aufgabe #%d', + 'Assignee changed on task #%d' => 'Neue Zuordnung für Aufgabe #%d ', + '%d overdue tasks' => '%d überfällige Aufgaben', + 'No notification.' => 'Keine neuen Benachrichtigungen', + 'Mark all as read' => 'Alles als gelesen markieren', + 'Mark as read' => 'Als gelesen markieren', + 'Total number of tasks in this column across all swimlanes' => 'Anzahl an Aufgaben in dieser Spalte über alle Swimlanes', + 'Collapse swimlane' => 'Swimlane einklappen', + 'Expand swimlane' => 'Swimlane ausklappen', + 'Add a new filter' => 'Neuen Filter hinzufügen', + 'Share with all project members' => 'Mit allen Projektmitgliedern teilen.', + 'Shared' => 'Geteilt', + 'Owner' => 'Eigentümer', + 'Unread notifications' => 'Ungelesene Benachrichtigungen', + 'Notification methods:' => 'Benachrichtigungs-Methoden:', + '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 has been imported!' => 'Es wurde nichts importiert!', + 'Import users from CSV file' => 'Importiere Benutzer aus CSV Datei', + '%d user(s) have been imported successfully.' => '%d Benutzer wurde(n) erfolgreich importiert.', + 'Comma' => 'Komma', + 'Semi-colon' => 'Semikolon', + 'Tab' => 'Tabulator', + 'Vertical bar' => 'senkrechter Strich', + 'Double Quote' => 'Doppelte Anführungszeichen', + 'Single Quote' => 'Einfache Anführungszeichen', + '%s attached a file to the task #%d' => '%s hat eine Datei zur Aufgabe #%d hinzugefügt', + 'There is no column or swimlane activated in your project!' => 'Es ist keine Spalte oder Swimlane in Ihrem Projekt aktiviert!', + 'Append filter (instead of replacement)' => 'Filter anhängen (statt zu ersetzen)', + 'Append/Replace' => 'Anhängen/Ersetzen', + 'Append' => 'Anhängen', + 'Replace' => 'Ersetzen', + 'Import' => 'Import', + 'Change sorting' => 'Sortierung ändern', + 'Tasks Importation' => 'Aufgaben Import', + 'Delimiter' => 'Trennzeichen', + 'Enclosure' => 'Textbegrenzer', + 'CSV File' => 'CSV Datei', + 'Instructions' => 'Anweisungen', + 'Your file must use the predefined CSV format' => 'Ihre Datei muss das vorgegebene CSV Format haben', + 'Your file must be encoded in UTF-8' => 'Ihre Datei muss UTF-8 kodiert sein', + 'The first row must be the header' => 'Die erste Zeile muss die Kopfzeile sein', + 'Duplicates are not verified for you' => 'Duplikate werden nicht für Sie geprüft', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Das Fälligkeitsdatum muss das ISO Format haben: YYYY-MM-DD', + 'Download CSV template' => 'CSV Vorlage herunterladen', + 'No external integration registered.' => 'Keine externe Integration registriert', + 'Duplicates are not imported' => 'Duplikate wurden nicht importiert', + 'Usernames must be lowercase and unique' => 'Benutzernamen müssen in Kleinbuchstaben und eindeutig sein', + 'Passwords will be encrypted if present' => 'Passwörter werden verschlüsselt wenn vorhanden', + '%s attached a new file to the task %s' => '%s hat eine neue Datei zur Aufgabe %s hinzugefügt', + 'Link type' => 'Verbindungstyp', + 'Assign automatically a category based on a link' => 'Linkbasiert eine Kategorie automatisch zuordnen', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Benutzername des Zuständigen', + 'Assignee Name' => 'Name des Zuständigen', + 'Groups' => 'Gruppen', + 'Members of %s' => 'Mitglied von %s', + 'New group' => 'Neue Gruppe', + 'Group created successfully.' => 'Gruppe erfolgreich angelegt.', + 'Unable to create your group.' => 'Gruppe konnte nicht angelegt werden', + 'Edit group' => 'Gruppe bearbeiten', + 'Group updated successfully.' => 'Gruppe erfolgreich aktualisiert', + 'Unable to update your group.' => 'Gruppe konnte nicht aktualisiert werden', + 'Add group member to "%s"' => 'Gruppenmitglied zu "%s" hinzufügen', + 'Group member added successfully.' => 'Gruppenmitglied erfolgreich hinzugefügt', + 'Unable to add group member.' => 'Gruppenmitglied konnte nicht hinzugefügt werden.', + 'Remove user from group "%s"' => 'Benutzer aus Gruppe "%s" löschen', + 'User removed successfully from this group.' => 'Benutzer erfolgreich aus dieser Gruppe gelöscht.', + 'Unable to remove this user from the group.' => 'Benutzer konnte nicht aus dieser Gruppe gelöscht werden.', + 'Remove group' => 'Gruppe löschen', + 'Group removed successfully.' => 'Gruppe erfolgreich gelöscht.', + 'Unable to remove this group.' => 'Gruppe konnte nicht gelöscht werden.', + 'Project Permissions' => 'Projekt Berechtigungen', + 'Manager' => 'Manager', + 'Project Manager' => 'Projekt Manager', + 'Project Member' => 'Projekt Mitglied', + 'Project Viewer' => 'Projekt Betrachter', + 'Your account is locked for %d minutes' => 'Ihr Zugang wurde für %d Minuten gesperrt', + 'Invalid captcha' => 'Ungültiges Captcha', + 'The name must be unique' => 'Der Name muss eindeutig sein', + 'View all groups' => 'Alle Gruppen 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.', + 'Add group member' => 'Gruppenmitglied hinzufügen', + 'Do you really want to remove this group: "%s"?' => 'Wollen Sie die Gruppe "%s" wirklich löschen?', + 'There is no user in this group.' => 'Es gibt keinen Benutzer in dieser Gruppe.', + 'Permissions' => 'Berechtigungen', + 'Allowed Users' => 'Berechtigte Benutzer', + 'No specific user has been allowed.' => 'Keine Benutzer mit ausdrücklicher Berechtigung.', + 'Role' => 'Rolle', + 'Enter user name...' => 'Geben Sie den Benutzernamen ein...', + 'Allowed Groups' => 'Berechtigte Gruppen', + 'No group has been allowed.' => 'Keine Gruppen mit ausdrücklicher Berechtigung.', + 'Group' => 'Gruppe', + 'Group Name' => 'Gruppenname', + 'Enter group name...' => 'Geben Sie den Gruppennamen ein...', + 'Role:' => 'Rolle:', + 'Project members' => 'Projektmitglieder', + '%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', + 'Estimated hours: ' => 'Erwarteter Zeitaufwand (Stunden): ', + 'Actual hours: ' => 'Tatsächlich aufgewendete Stunden: ', + 'Hours Spent' => 'Stunden aufgewendet', + 'Hours Estimated' => 'Stunden erwartet', + 'Estimated Time' => 'Erwartete Zeit', + 'Actual Time' => 'Aktuelle Zeit', + 'Estimated vs actual time' => 'Erwarteter vs. tatsächlicher Zeitaufwand', + 'RUB - Russian Ruble' => 'RUB - Russische Rubel', + 'Assign the task to the person who does the action when the column is changed' => 'Aufgabe der Person zuordnen, die die Aktion durchführt, wenn die Spalte geändert wird', + 'Close a task in a specific column' => 'Schließe eine Aufgabe in einer bestimmten Spalte', + 'Time-based One-time Password Algorithm' => 'Zeitbasierter Einmalpasswort Algorithmus', + 'Two-Factor Provider: ' => '2FA Anbieter: ', + 'Disable two-factor authentication' => 'Zwei-Faktor-Authentifizierung deaktivieren', + 'Enable two-factor authentication' => 'Zwei-Faktor-Authentifizierung aktivieren', + 'There is no integration registered at the moment.' => 'Derzeit ist kein externer Dienst registriert.', + 'Password Reset for Kanboard' => 'Zurücksetzen des Passwortes für Kanboard', + 'Forgot password?' => 'Passwort vergessen?', + 'Enable "Forget Password"' => 'Passwortrücksetzung aktivieren', + 'Password Reset' => 'Passwort zurücksetzen', + 'New password' => 'Neues Passwort', + 'Change Password' => 'Passwort ändern', + 'To reset your password click on this link:' => 'Bitte auf den Link klicken, um Ihr Passwort zurückzusetzen.', + 'Last Password Reset' => 'Verlauf der Passwortrücksetzung', + 'The password has never been reinitialized.' => 'Das Passwort wurde noch nie zurückgesetzt.', + 'Creation' => 'Erstellung', + 'Expiration' => 'Ablauf', + 'Password reset history' => 'Verlauf Passwortrücksetzung', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Alle Aufgaben der Spalte "%s" und der Swimlane "%s" wurden erfolgreich geschlossen', + 'Do you really want to close all tasks of this column?' => 'Wollen Sie wirklich alle Aufgaben in dieser Spalte schließen?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d Aufgabe(n) in der Spalte "%s" und in der Swimlane "%s" werden geschlossen.', + 'Close all tasks in this column and this swimlane' => 'Alle Aufgaben in dieser Spalte schließen', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Kein Plugin hat eine Projekt-Benachrichtigungsmethode registriert. Sie können individuelle Meldungen in Ihrem Benutzerprofil konfigurieren', + 'My dashboard' => 'Mein Dashboard', + 'My profile' => 'Mein Profil', + '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', + 'Personal 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', + 'Task priority' => 'Aufgaben-Priorität', + 'General' => 'Allgemein', + 'Dates' => 'Daten', + 'Default priority' => 'Standard-Priorität', + 'Lowest priority' => 'Niedrigste Priorität', + 'Highest priority' => 'Höchste Priorität', + 'Close a task when there is no activity' => 'Schließe eine Aufgabe, wenn keine Aktivitäten vorhanden sind', + 'Duration in days' => 'Dauer in Tagen', + 'Send email when there is no activity on a task' => 'Versende eine E-Mail, wenn keine Aktivitäten an einer Aufgabe vorhanden sind', + 'Unable to fetch link information.' => 'Kann keine Informationen über Verbindungen holen', + 'Daily background job for tasks' => 'Tägliche Hintergrundarbeit für Aufgaben', + 'Auto' => 'Auto', + 'Related' => 'Verbunden', + 'Attachment' => 'Anhang', + 'Web Link' => 'Weblink', + 'External links' => 'Externe Verbindungen', + 'Add external link' => 'Externe Verbindung hinzufügen', + 'Type' => 'Typ', + 'Dependency' => 'Abhängigkeit', + 'Add internal link' => 'Interne Verbindung hinzufügen', + '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 hierher...', + 'URL' => 'URL', + 'Internal links' => 'Interne Verbindungen', + 'Assign to me' => 'Mir zuweisen', + 'Me' => 'Mich', + 'Do not duplicate anything' => 'Nichts duplizieren', + 'Projects management' => 'Projektmanagement', + 'Users management' => 'Benutzermanagement', + 'Groups management' => 'Gruppenmanagement', + 'Create from another project' => 'Von einem anderen Projekt erstellen', + 'open' => 'offen', + 'closed' => 'geschlossen', + 'Priority:' => 'Priorität:', + 'Reference:' => 'Referenz:', + 'Complexity:' => 'Komplexität:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Spalte:', + 'Position:' => 'Position:', + 'Creator:' => 'Ersteller:', + 'Time estimated:' => 'Geschätzte Zeit:', + '%s hours' => '%s Stunden', + 'Time spent:' => 'Aufgewendete Zeit:', + 'Created:' => 'Erstellt:', + 'Modified:' => 'Geändert:', + 'Completed:' => 'Abgeschlossen:', + 'Started:' => 'Gestartet:', + 'Moved:' => 'Verschoben:', + 'Task #%d' => 'Aufgabe #%d', + 'Time format' => 'Zeitformat', + 'Start date: ' => 'Anfangsdatum: ', + 'End date: ' => 'Enddatum: ', + 'New due date: ' => 'Neues Fälligkeitsdatum: ', + 'Start date changed: ' => 'Anfangsdatum geändert: ', + 'Disable personal projects' => 'Persönliche Projekte deaktivieren', + 'Do you really want to remove this custom filter: "%s"?' => 'Wollen Sie diesen benutzerdefinierten Filter wirklich entfernen: "%s"?', + 'Remove a custom filter' => 'Benutzerdefinierten Filter entfernen', + 'User activated successfully.' => 'Benutzer erfolgreich aktiviert.', + 'Unable to enable this user.' => 'Dieser Benutzer kann nicht aktiviert werden.', + '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.', + 'The maximum allowed file size is %sB.' => 'Die maximal erlaubte Dateigröße ist %sB.', + 'Drag and drop your files here' => 'Ziehen Sie Ihre Dateien hier hin', + 'choose files' => 'Dateien auswählen', + 'View profile' => 'Profil ansehen', + 'Two Factor' => 'Zwei-Faktor', + 'Disable user' => 'Benutzer deaktivieren', + 'Do you really want to disable this user: "%s"?' => 'Wollen Sie diesen Benutzer wirklich deaktivieren: "%s"?', + 'Enable user' => 'Benutzer aktivieren', + 'Do you really want to enable this user: "%s"?' => 'Wollen Sie diesen Benutzer wirklich aktivieren: "%s"?', + 'Download' => 'Herunterladen', + 'Uploaded: %s' => 'Hochgeladen: %s', + 'Size: %s' => 'Größe: %s', + 'Uploaded by %s' => 'Hochgeladen von %s', + 'Filename' => 'Dateiname', + 'Size' => 'Größe', + 'Column created successfully.' => 'Spalte erfolgreich erstellt.', + 'Another column with the same name exists in the project' => 'Es gibt bereits eine Spalte mit demselben Namen im Projekt', + 'Default filters' => 'Standard-Filter', + 'Your board doesn\'t have any columns!' => 'Es gibt keine Spalten in diesem Projekt!', + 'Change column position' => 'Position der Spalte ändern', + 'Switch to the project overview' => 'Zur Projektübersicht wechseln', + 'User filters' => 'Benutzer-Filter', + 'Category filters' => 'Kategorie-Filter', + 'Upload a file' => 'Eine Datei hochladen', + 'View file' => 'Datei ansehen', + 'Last activity' => 'Letzte Aktivität', + 'Change subtask position' => 'Position der Teilaufgabe ändern', + 'This value must be greater than %d' => 'Dieser Wert muss größer als %d sein', + 'Another swimlane with the same name exists in the project' => 'Es gibt bereits eine Swimlane mit diesem Namen im Projekt', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Beispiel: https://example.kanboard.org/ (wird zum Erstellen absoluter URLs genutzt)', + 'Actions duplicated successfully.' => 'Aktionen erfolgreich dupliziert', + 'Unable to duplicate actions.' => 'Aktionen können nicht dupliziert werden.', + 'Add a new action' => 'Neue Aktion hinzufügen', + 'Import from another project' => 'Von einem anderen Projekt importieren', + 'There is no action at the moment.' => 'Es gibt zur Zeit keine Aktionen.', + 'Import actions from another project' => 'Aktionen von einem anderen Projekt importieren', + 'There is no available project.' => 'Es ist kein Projekt verfügbar.', + 'Local File' => 'Lokale Datei', + 'Configuration' => 'Konfiguration', + 'PHP version:' => 'PHP Version:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'OS Version:', + 'Database version:' => 'Datenbank Version:', + 'Browser:' => 'Browser:', + 'Task view' => 'Aufgaben Ansicht', + 'Edit task' => 'Aufgabe bearbeiten', + 'Edit description' => 'Beschreibung bearbeiten', + 'New internal link' => 'Neue interne Verbindung', + 'Display list of keyboard shortcuts' => 'Liste der Tastaturkürzel anzeigen', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Mein Avatar Bild hochladen', + 'Remove my image' => 'Mein Bild entfernen', + 'The OAuth2 state parameter is invalid' => 'Der OAuth2 Statusparameter ist ungültig', + 'User not found.' => 'Benutzer nicht gefunden', + 'Search in activity stream' => 'Im Aktivitätenstrom suchen', + 'My activities' => 'Meine Aktivitäten', + 'Activity until yesterday' => 'Aktivitäten bis gestern', + 'Activity until today' => 'Aktivitäten bis heute', + 'Search by creator: ' => 'nach Ersteller suchen:', + 'Search by creation date: ' => 'nach Datum suchen:', + 'Search by task status: ' => 'nach Aufgabenstatus suchen:', + 'Search by task title: ' => 'nach Titel suchen:', + 'Activity stream search' => 'Im Aktivitätenstrom suchen', + 'Projects where "%s" is manager' => 'Projekte in denen "%s" Manager ist', + 'Projects where "%s" is member' => 'Projekte in denen "%s" Mitglied ist', + 'Open tasks assigned to "%s"' => 'Offene Aufgaben, die "%s" zugeteilt sind', + 'Closed tasks assigned to "%s"' => 'Geschlossene Aufgaben, die "%s" zugeteilt sind', + 'Assign automatically a color based on a priority' => 'Eine Farbe basierend auf einer Priorität automatisch zuordnen', + 'Overdue tasks for the project(s) "%s"' => 'Überfällige Aufgaben des/der Projekt/e "%s"', + 'Upload files' => 'Dateien hochladen', + 'Installed Plugins' => 'Installierte Plugins', + 'Plugin Directory' => 'Plugin Verzeichnis', + 'Plugin installed successfully.' => 'Plugin erfolgreich installiert.', + 'Plugin updated successfully.' => 'Plugin erfolgreich aktualisiert.', + 'Plugin removed successfully.' => 'Plugin erfolgreich entfernt.', + 'Subtask converted to task successfully.' => 'Teilaufgabe erfolgreich in Aufgabe umgewandelt.', + 'Unable to convert the subtask.' => 'Teilaufgabe kann nicht umgewandelt werden.', + 'Unable to extract plugin archive.' => 'Plugin Archiv kann nicht entpackt werden.', + 'Plugin not found.' => 'Plugin nicht gefunden.', + 'You don\'t have the permission to remove this plugin.' => 'Sie dürfen dieses Plugin nicht entfernen.', + 'Unable to download plugin archive.' => 'Plugin Archiv kann nicht herunter geladen werden.', + 'Unable to write temporary file for plugin.' => 'Temporäre Dateien für das Plugin können nicht geschrieben werden.', + 'Unable to open plugin archive.' => 'Kann das Plugin Archiv nicht öffnen.', + 'There is no file in the plugin archive.' => 'Es gibt keine Datei im Plugin Archiv.', + 'Create tasks in bulk' => 'Viele Aufgaben auf einmal erstellen', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Ihre Kanboard Installation ist nicht dafür konfiguriert, Plugins mit dem Benutzerinterface zu installieren.', + 'There is no plugin available.' => 'Es gibt kein Plugin.', + 'Install' => 'Installieren', + 'Update' => 'Aktualisieren', + 'Up to date' => 'Aktuell', + 'Not available' => 'Nicht verfügbar', + 'Remove plugin' => 'Plugin entfernen', + 'Do you really want to remove this plugin: "%s"?' => 'Wollen Sie das Plugin "%s" wirklich entfernen?', + 'Uninstall' => 'Deinstallieren', + 'Listing' => 'Auflistung', + 'Metadata' => 'Metadaten', + 'Manage projects' => 'Projekte verwalten', + 'Convert to task' => 'In Aufgabe umwandeln', + 'Convert sub-task to task' => 'Teilaufgabe in Aufgabe umwandeln', + 'Do you really want to convert this sub-task to a task?' => 'Wollen Sie diese Teilaufgabe wirklich in eine Aufgabe umwandeln?', + 'My task title' => 'Mein Aufgabentitel', + 'Enter one task by line.' => 'Geben Sie eine Aufgabe pro Zeile ein.', + 'Number of failed login:' => 'Anzahl fehlgeschlagener Anmeldungen:', + 'Account locked until:' => 'Konto gesperrt bis:', + 'Email settings' => 'E-Mail Einstellungen', + 'Email sender address' => 'E-Mail Absender Adresse', + 'Email transport' => 'E-Mail Verkehr', + 'Webhook token' => 'Webhook Token', + 'Project tags management' => 'Projektbezogenes Schlagwort-Management', + 'Tag created successfully.' => 'Schlagwort erfolgreich erstellt.', + 'Unable to create this tag.' => 'Das Schlagwort kann nicht erstellt werden.', + 'Tag updated successfully.' => 'Schlagwort erfolgreich aktualisiert.', + 'Unable to update this tag.' => 'Das Schlagwort kann nicht aktualisiert werden.', + 'Tag removed successfully.' => 'Schlagwort erfolgreich entfernt.', + 'Unable to remove this tag.' => 'Das Schlagwort kann nicht entfernt werden.', + 'Global tags management' => 'Globales Schlagwort-Management', + 'Tags' => 'Schlagworte', + 'Tags management' => 'Schlagwort-Management', + 'Add new tag' => 'Neues Schlagwort hinzufügen', + 'Edit a tag' => 'Schlagwort bearbeiten', + 'Project tags' => 'Projektbezogene Schlagwörter', + 'There is no specific tag for this project at the moment.' => 'Es gibt zur Zeit kein spezifisches Schlagwort.', + 'Tag' => 'Schlagwort', + 'Remove a tag' => 'Schlagwort entfernen', + 'Do you really want to remove this tag: "%s"?' => 'Soll dieses Schlagwort wirklich entfernt werden: "%s"?', + 'Global tags' => 'Globale Schlagwörter', + 'There is no global tag at the moment.' => 'Es gibt zur Zeit kein globales Schlagwort', + 'This field cannot be empty' => 'Dieses Feld kann nicht leer sein', + 'Close a task when there is no activity in a 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 has 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', + '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 schließen, wenn sie im angegebenen Zeitraum nicht verschoben wurde', + 'Edit columns' => 'Spalten bearbeiten', + 'The column restriction has been created successfully.' => 'Die Spalteneinschränkung wurde erfolgreich erstellt.', + 'Unable to create this column restriction.' => 'Erstellen der Spalteneinschränkung fehlgeschlagen.', + 'Column restriction removed successfully.' => ' Spalteneinschränkung erfolgreich entfernt.', + 'Unable to remove this restriction.' => 'Entfernen der Spalteneinschrä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 Schließen 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 Schließen 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 Schließen 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 Spalteneinschrä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 Spalteneinschrä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', + '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 von %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 Person diesem Projekt hinzu', + 'Sign-up' => 'Anmelden', + 'Credentials' => 'Anmeldeinformationen', + 'New user' => 'Neuer Benutzer', + 'This username is already taken' => 'Dieser Benutzername ist bereits vergeben', + 'Your profile must have a valid email address.' => 'Ihr Profil muss eine gültige E-Mail-Adresse haben.', + 'TRL - Turkish Lira' => 'TRL - Türkische Lira', + 'The project email is optional and could be used by several plugins.' => 'Die Projekt-E-Mail ist optional und kann von mehreren Plugins verwendet werden.', + 'The project email must be unique across all projects' => 'Die Projekt-E-Mail muss für alle Projekte eindeutig sein', + 'The email configuration has been disabled by the administrator.' => 'Die E-Mail-Konfiguration wurde vom Administrator deaktiviert.', + 'Close this project' => 'Dieses Projekt schließen', + 'Open this project' => 'Dieses Projekt öffnen', + 'Close a project' => 'Ein Projekt schließen', + 'Do you really want to close this project: "%s"?' => 'Möchten Sie dieses Projekt wirklich schließen: "%s"?', + 'Reopen a project' => 'Ein Projekt wieder öffnen', + 'Do you really want to reopen this project: "%s"?' => 'Möchten Sie dieses Projekt wirklich wieder öffnen: "%s"?', + 'This project is open' => 'Dieses Projekt ist offen', + 'This project is closed' => 'Dieses Projekt ist geschlossen', + 'Unable to upload files, check the permissions of your data folder.' => 'Dateien können nicht hochgeladen werden, überprüfen Sie die Berechtigungen Ihres Datenordners.', + 'Another category with the same name exists in this project' => 'Eine weitere Kategorie mit demselben Namen existiert in diesem Projekt', + 'Comment sent by email successfully.' => 'Kommentar wurde erfolgreich per E-Mail gesendet.', + 'Sent by email to "%s" (%s)' => 'Wurde per E-Mail an [%s] gesendet "%s"', + 'Unable to read uploaded file.' => 'Die hochgeladene Datei konnte nicht gelesen werden.', + 'Database uploaded successfully.' => 'Die Datenbank wurde erfolgreich hochgeladen.', + 'Task sent by email successfully.' => 'Aufgabe wurde erfolgreich per E-Mail gesendet.', + 'There is no category in this project.' => 'Es gibt keine Kategorie in diesem Projekt', + 'Send by email' => 'Per E-Mail senden', + 'Create and send a comment by email' => 'Erstellen und senden Sie einen Kommentar per E-Mail', + 'Subject' => 'Betreff', + 'Upload the database' => 'Datenbank hochladen', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Sie können die zuvor heruntergeladene SQLite-Datenbank (Gzip-Format) hochladen.', + 'Database file' => 'Datenbankdatei', + 'Upload' => 'Hochladen', + 'Your project must have at least one active swimlane.' => 'Ihr Projekt muss mindestens eine aktive Swimlane haben.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatische Aktion nicht gefunden: "%s"', + '%d projects' => '%d Projekte', + '%d project' => '%d Projekt', + 'There is no project.' => 'Es gibt kein Projekt.', + 'Sort' => 'Sortieren', + 'Project ID' => 'Projekt-ID', + 'Project name' => 'Projekt Name', + 'Public' => 'Öffentlich', + 'Personal' => 'Persönlich', + '%d tasks' => '%d Aufgaben', + '%d task' => '%d Aufgabe', + 'Task ID' => 'Aufgaben-ID', + 'Assign automatically a color when due date is expired' => 'Automatisch eine Farbe zuweisen, wenn das Fälligkeitsdatum abgelaufen ist', + 'Total score in this column across all swimlanes' => 'Gesamtpunktzahl in dieser Spalte über alle Swimlanes', + 'HRK - Kuna' => 'HRK - Kroatische Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentinische Peso', + 'COP - Colombian Peso' => 'COP - Kolumbianische Peso', + '%d groups' => '%d Gruppen', + '%d group' => '%d Gruppe', + 'Group ID' => 'Gruppen-ID', + 'External ID' => 'Externe ID', + '%d users' => '%d Benutzer', + '%d user' => '%d Benutzer', + 'Hide subtasks' => 'Teilaufgaben verstecken', + 'Show subtasks' => 'Teilaufgaben anzeigen', + 'Authentication Parameters' => 'Authentifizierungsparameter', + 'API Access' => 'API-Zugriff', + 'No users found.' => 'Keine Benutzer gefunden.', + 'User ID' => 'Benutzer-ID', + 'Notifications are activated' => 'Benachrichtigungen sind aktiviert', + 'Notifications are disabled' => 'Benachrichtigungen sind deaktiviert', + 'User disabled' => 'Benutzer deaktiviert', + '%d notifications' => '%d Benachrichtigungen', + '%d notification' => '%d Benachrichtigung', + 'There is no external integration installed.' => 'Es ist keine externe Integration installiert.', + 'You are not allowed to update tasks assigned to someone else.' => 'Sie sind nicht berechtigt, Aufgaben zu aktualisieren, die jemand anderem zugewiesen wurden.', + 'You are not allowed to change the assignee.' => 'Sie dürfen den Zuständigen nicht ändern.', + 'Task suppression is not permitted' => 'Entfernen von Aufgaben ist nicht erlaubt.', + 'Changing assignee is not permitted' => 'Änderung des Zuständigen ist nicht zulässig', + 'Update only assigned tasks is permitted' => 'Nur zugeordnete Aufgaben dürfen aktualisiert werden', + 'Only for tasks assigned to the current user' => 'Nur für Aufgaben, die dem aktuellen Benutzer zugeordnet sind', + 'My projects' => 'Meine Projekte', + 'You are not a member of any project.' => 'Sie sind nicht Mitglied eines Projektes.', + 'My subtasks' => 'Meine Teilaufgaben', + '%d subtasks' => '%d Teilaufgaben', + '%d subtask' => '%d Teilaufgabe', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Das Bewegen einer Aufgabe zwischen diesen Spalten ist nur für Aufgaben zulässig, die dem aktuellen Benutzer zugewiesen sind', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Dänische Kronen', + 'Remove user from group' => 'Benutzer aus Gruppe löschen', + 'Assign the task to its creator' => 'Aufgabe dem Ersteller zuordnen', + 'This task was sent by email to "%s" with subject "%s".' => 'Diese Aufgabe wurde per Mail an "%s" mit dem Betreff "%s" gesendet.', + 'Predefined Email Subjects' => 'Vordefinierte E-Mail Betreffzeilen', + 'Write one subject by line.' => 'Schreibe ein Betreff pro Zeile.', + 'Create another link' => 'Einen weiteren Link erstellen', + 'BRL - Brazilian Real' => 'BRL - Brasilianische Real', + 'Add a new Kanboard task' => 'Eine neue Kanboard Aufgabe hinzufügen', + 'Subtask not started' => 'Teilaufgabe nicht gestartet', + 'Subtask currently in progress' => 'Teilaufgabe aktuell in Bearbeitung', + 'Subtask completed' => 'Teilaufgabe abgeschlossen', + 'Subtask added successfully.' => 'Teilaufgabe erfolgreich hinzugefügt.', + '%d subtasks added successfully.' => '%d Teilaufgaben erfolgreich hinzugefügt.', + 'Enter one subtask by line.' => 'Geben Sie eine Teilaufgabe pro Zeile ein.', + 'Predefined Contents' => 'Vordefinierte Inhalte', + 'Predefined contents' => 'Vordefinierte Inhalte', + 'Predefined Task Description' => 'Vordefinierte Aufgabenbeschreibung', + 'Do you really want to remove this template? "%s"' => 'Wollen Sie diese Vorlage wirklich löschen? "%s"', + 'Add predefined task description' => 'Vordefinierte Aufgabenbeschreibung hinzufügen', + 'Predefined Task Descriptions' => 'Vordefinierte Aufgabenbeschreibungen', + 'Template created successfully.' => 'Vorlage erfolgreich erstellt.', + 'Unable to create this template.' => 'Erstellen der Vorlage nicht möglich.', + 'Template updated successfully.' => 'Vorlage erfolgreich geändert.', + 'Unable to update this template.' => 'Aktualisierung der Vorlage nicht möglich.', + 'Template removed successfully.' => 'Vorlage erfolgreich gelöscht.', + 'Unable to remove this template.' => 'Löschen der Vorlage nicht möglich.', + 'Template for the task description' => 'Vorlage für die Aufgabenbeschreibung', + 'The start date is greater than the end date' => 'Das Startdatum ist größer als das Enddatum', + 'Tags must be separated by a comma' => 'Schlagworte müssen per Komma getrennt werden', + 'Only the task title is required' => 'Nur der Aufgaben-Titel wird benötigt', + 'Creator Username' => 'Ersteller Benutzername', + 'Color Name' => 'Farbname', + 'Column Name' => 'Spaltenname', + 'Swimlane Name' => 'Swimlane-Name', + 'Time Estimated' => 'geschätzte Zeit', + 'Time Spent' => 'Zeitaufwand', + 'External Link' => 'externer Link', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Diese Funktion aktiviert den iCal Feed, RSS Feed und die öffentliche Board-Ansicht', + 'Stop the timer of all subtasks when moving a task to another column' => 'Beenden des Timers für alle Unteraufgaben, wenn die Aufgabe in eine andere Spalte verschoben wird', + 'Subtask Title' => 'Titel der Teilaufgabe', + 'Add a subtask and activate the timer when moving a task to another column' => 'Teilaufgabe hinzufügen und den Timer aktivieren, wenn die Aufgabe in eine andere Spalte verschoben wird', + 'days' => 'Tage', + 'minutes' => 'Minuten', + 'seconds' => 'Sekunden', + 'Assign automatically a color when preset start date is reached' => 'Automatisch eine Farbe zuweisen, wenn das Startdatum erreicht ist', + 'Move the task to another column once a predefined start date is reached' => 'Verschieben des Tasks in eine andere Spalte, wenn das definierte Startdatum erreicht ist', + 'This task is now linked to the task %s with the relation "%s"' => 'Diese Aufgabe ist jetzt verknüpft mit der Aufgabe %s mit der Relation "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Die Verknüpfung mit der Relation "%s" zur Aufgabe %s wurde entfernt', + 'Custom Filter:' => 'Benutzerdefinierter Filter:', + 'Unable to find this group.' => 'Diese Gruppe konnte nicht gefunden werden', + '%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 auf die Position %d in der Spalte "%s" verschoben', + '%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben', + '%sh spent' => '%sh aufgewendet', + '%sh estimated' => '%sh angesetzt', + 'Select All' => 'Alle auswählen', + 'Unselect All' => 'Keine auswählen', + 'Apply action' => 'Aktion anwenden', + 'Move selected tasks to another column or swimlane' => 'Ausgewählte Aufgaben in andere Spalte verschieben', + 'Edit tasks in bulk' => 'Massenbearbeitung', + 'Choose the properties that you would like to change for the selected tasks.' => 'Wählen Sie die Eigenschaften aus, die Sie für die ausgewählten Aufgaben ändern möchten.', + 'Configure this project' => 'Projekteinstellungen', + 'Start now' => 'Jetzt starten', + '%s removed a file from the task #%d' => '%s hat eine Datei aus der Aufgabe #%d entfernt.', + 'Attachment removed from task #%d: %s' => 'Anhang aus Aufgabe #%d entfernt: %s', + 'No color' => 'Keine Farbe', + 'Attachment removed "%s"' => 'Anhang entfernt "%s"', + '%s removed a file from the task %s' => '%s hat eine Datei aus der Aufgabe entfernt %s', + 'Move the task to another swimlane when assigned to a user' => 'Verschieben der Aufgabe in eine andere Swimlane, wenn sie einem Benutzer zugewiesen wird', + 'Destination swimlane' => 'Ziel Swimlane', + 'Assign a category when the task is moved to a specific swimlane' => 'Kategorie zuweisen, wenn Aufgabe in eine bestimmte Swimlane verschoben wird', + 'Move the task to another swimlane when the category is changed' => 'Verschiebe die Aufgabe in eine andere Swimlane, wenn die Kategorie geändert wird', + 'Reorder this column by priority (ASC)' => 'Spalte nach Priorität ordnen (aufsteigend)', + 'Reorder this column by priority (DESC)' => 'Spalte nach Priorität ordnen (absteigend)', + 'Reorder this column by assignee and priority (ASC)' => 'Spalte nach Zuständigem und Priorität ordnen (aufsteigend)', + 'Reorder this column by assignee and priority (DESC)' => 'Spalte nach Zuständigem und Priorität ordnen (absteigend)', + 'Reorder this column by assignee (A-Z)' => 'Spalte nach Zuständigem ordnen (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Spalte nach Zuständigem ordnen (Z-A)', + 'Reorder this column by due date (ASC)' => 'Spalte nach Fälligkeitsdatum ordnen (aufsteigend)', + 'Reorder this column by due date (DESC)' => 'Spalte nach Fälligkeitsdatum ordnen (absteigend)', + 'Reorder this column by id (ASC)' => 'Diese Spalte nach ID aufsteigend sortieren', + 'Reorder this column by id (DESC)' => 'Diese Spalte nach ID absteigend sortieren', + '%s moved the task #%d "%s" to the project "%s"' => '%s hat die Aufgabe #%d "%s" in das Projekt "%s" verschoben', + 'Task #%d "%s" has been moved to the project "%s"' => 'Aufgabe #%d "%s" wurde in das Projekt "%s" verschoben', + 'Move the task to another column when the due date is less than a certain number of days' => 'Verschieben der Aufgabe in eine andere Spalte, wenn die Fälligkeit kleiner als eine bestimmte Anzahl von Tagen ist', + 'Automatically update the start date when the task is moved away from a specific column' => 'Aktualisiert automatisch das Startdatum, wenn die Aufgabe aus einer bestimmten Spalte genommen wird', + 'HTTP Client:' => 'HTTP-Client:', + 'Assigned' => 'Zugeordnet', + 'Task limits apply to each swimlane individually' => 'Aufgabenlimit gilt pro Swimlane', + 'Column task limits apply to each swimlane individually' => 'Spaltenaufgabenlimit für jede Swimlane einzeln anwenden', + 'Column task limits are applied to each swimlane individually' => 'Spaltenaufgabenlimit wird für jede Swimlane einzeln angewendet', + 'Column task limits are applied across swimlanes' => 'Spaltenaufgabenlimit wird swimlaneübergreifend angewendet', + 'Task limit: ' => 'Aufgabenlimit', + 'Change to global tag' => 'Zu globalem Schlagwort machen', + 'Do you really want to make the tag "%s" global?' => 'Das Schlagwort "%s" wirklich global machen?', + 'Enable global tags for this project' => 'Globale Schlagworte für dieses Projekt aktivieren', + 'Group membership(s):' => 'Gruppen-Mitgliedschaft(en):', + '%s is a member of the following group(s): %s' => '%s ist Mitglied in der/den folgenden Gruppe(n): %s', + '%d/%d group(s) shown' => '%d/%d Gruppe(n) angezeigt', + 'Subtask creation or modification' => 'Teilaufgabe erstellen oder ändern', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Aufgabe einem bestimmten Nutzer zuordnen, wenn die Aufgabe in eine bestimmte Swimlane verschoben wird', + 'Comment' => 'Kommentar', + 'Collapse vertically' => 'Vertikal zuklappen', + 'Expand vertically' => 'Vertikal erweitern', + 'MXN - Mexican Peso' => 'MXN - Mexikanischer Peso', + 'Estimated vs actual time per column' => 'Geschätzte vs. tatsächliche Zeit pro Spalte', + 'HUF - Hungarian Forint' => 'HUF - Ungarischer Forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Sie müssen eine Datei auswählen, um sie als Ihr Avatar hochzuladen!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Die hochgeladene Datei ist kein gültiges Bild! (Nur *.gif, *.jpg, *.jpeg und *.png sind erlaubt!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Fälligkeitsdatum automatisch setzen, wenn die Aufgabe aus einer bestimmten Spalte verschoben wird', + 'No other projects found.' => 'Keine weiteren Projekte gefunden.', + 'Tasks copied successfully.' => 'Aufgaben erfolgreich kopiert.', + 'Unable to copy tasks.' => 'Aufgaben können nicht kopiert werden.', + 'Theme' => 'Thema', + 'Theme:' => 'Thema:', + 'Light theme' => 'Helles Thema', + 'Dark theme' => 'Dunkles Thema', + 'Automatic theme - Sync with system' => 'Automatisches Thema - Mit System synchronisieren', + 'Application managers or more' => 'Anwendungsmanager oder mehr', + 'Administrators' => 'Administratoren', + 'Visibility:' => 'Sichtbarkeit:', + 'Standard users' => 'Standardbenutzer', + 'Visibility is required' => 'Sichtbarkeit ist erforderlich', + 'The visibility should be an app role' => 'Die Sichtbarkeit sollte eine App-Rolle sein', + 'Reply' => 'Antworten', + '%s wrote: ' => '%s schrieb: ', + 'Number of visible tasks in this column and swimlane' => 'Anzahl sichtbarer Aufgaben in dieser Spalte und Swimlane', + 'Number of tasks in this swimlane' => 'Anzahl der Aufgaben in dieser Swimlane', + 'Unable to find another subtask in progress, you can close this window.' => 'Es konnte keine weitere Teilaufgabe in Bearbeitung gefunden werden, Sie können dieses Fenster schließen.', + 'This theme is invalid' => 'Dieses Thema ist ungültig', + 'This role is invalid' => 'Diese Rolle ist ungültig', + 'This timezone is invalid' => 'Diese Zeitzone ist ungültig', + 'This language is invalid' => 'Diese Sprache ist ungültig', + 'This URL is invalid' => 'Diese URL ist ungültig', + 'Date format invalid' => 'Ungültiges Datumsformat', + 'Time format invalid' => 'Ungültiges Zeitformat', + 'Invalid Mail transport' => 'Ungültiger Mail-Transport', + 'Color invalid' => 'Ungültige Farbe', + 'This value must be greater or equal to %d' => 'Dieser Wert muss größer oder gleich %d sein', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Fügen Sie am Anfang der Datei ein BOM hinzu (erforderlich für Microsoft Excel)', + 'Just add these tag(s)' => 'Nur diese Tags hinzufügen', + 'Remove internal link(s)' => 'Interne Links entfernen', + 'Import tasks from another project' => 'Aufgaben aus einem anderen Projekt importieren', + 'Select the project to copy tasks from' => 'Wählen Sie das Projekt aus, aus dem Sie Aufgaben kopieren möchten', + 'The total maximum allowed attachments size is %sB.' => 'Die maximal zulässige Gesamtgröße für Anhänge beträgt %sB.', + 'Add attachments' => 'Anhänge hinzufügen', + 'Task #%d "%s" is overdue' => 'Aufgabe #%d "%s" ist überfällig', + 'Enable notifications by default for all new users' => 'Benachrichtigungen standardmäßig für alle neuen Benutzer aktivieren', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Aufgabe dem Ersteller zuweisen, wenn sie in einer ausgewählten Spalte ohne Bearbeiter ist.', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Aufgabe dem angemeldeten Nutzer bei Spaltenänderung zuweisen, wenn eine Aufgabe in die ausgewählte Spalte verschoben wird.', +]; diff --git a/app/Locale/de_DE_du/translations.php b/app/Locale/de_DE_du/translations.php new file mode 100644 index 0000000..640cf62 --- /dev/null +++ b/app/Locale/de_DE_du/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Keines', + 'Edit' => 'Bearbeiten', + 'Remove' => 'Entfernen', + 'Yes' => 'Ja', + 'No' => 'Nein', + 'cancel' => 'Abbrechen', + 'or' => 'oder', + 'Yellow' => 'Gelb', + 'Blue' => 'Blau', + 'Green' => 'Grün', + 'Purple' => 'Violett', + 'Red' => 'Rot', + 'Orange' => 'Orange', + 'Grey' => 'Grau', + 'Brown' => 'Braun', + 'Deep Orange' => 'Dunkelorange', + 'Dark Grey' => 'Dunkelgrau', + 'Pink' => 'Pink', + 'Teal' => 'Türkis', + 'Cyan' => 'Cyan', + 'Lime' => 'Limette', + 'Light Green' => 'Hellgrün', + 'Amber' => 'Bernstein', + 'Save' => 'Speichern', + 'Login' => 'Anmelden', + 'Official website:' => 'Offizielle Webseite:', + 'Unassigned' => 'Nicht zugeordnet', + 'View this task' => 'Aufgabe ansehen', + 'Remove user' => 'Benutzer löschen', + 'Do you really want to remove this user: "%s"?' => 'Soll dieser Benutzer wirklich gelöscht werden: "%s"?', + 'All users' => 'Alle Benutzer', + 'Username' => 'Benutzername', + 'Password' => 'Passwort', + 'Administrator' => 'Administrator', + 'Sign in' => 'Anmelden', + 'Users' => 'Benutzer', + 'Forbidden' => 'Verboten', + 'Access Forbidden' => 'Zugriff verboten', + 'Edit user' => 'Benutzer bearbeiten', + 'Logout' => 'Abmelden', + 'Bad username or password' => 'Falscher Benutzername oder Passwort', + 'Edit project' => 'Projekt bearbeiten', + 'Name' => 'Name', + 'Projects' => 'Projekte', + 'No project' => 'Keine Projekte', + 'Project' => 'Projekt', + 'Status' => 'Status', + 'Tasks' => 'Aufgaben', + 'Board' => 'Pinnwand', + 'Actions' => 'Aktionen', + 'Inactive' => 'Inaktiv', + 'Active' => 'Aktiv', + 'Unable to update this board.' => 'Ändern dieser Pinnwand nicht möglich.', + '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', + 'Add a new column' => 'Neue Spalte hinzufügen', + 'Title' => 'Titel', + 'Assigned to %s' => 'Zuständig: %s', + 'Remove a column' => 'Spalte 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"?', + 'Settings' => 'Einstellungen', + 'Application settings' => 'Anwendungskonfiguration', + 'Language' => 'Sprache', + 'Webhook token:' => 'Webhook Token:', + 'API token:' => 'API Token:', + 'Database size:' => 'Datenbankgröße:', + 'Download the database' => 'Datenbank herunterladen', + 'Optimize the database' => 'Datenbank optimieren', + '(VACUUM command)' => '(VACUUM Befehl)', + '(Gzip compressed Sqlite file)' => '(Gzip-komprimierte SQLite-Datei)', + 'Close a task' => 'Aufgabe abschließen', + 'Column' => 'Spalte', + 'Color' => 'Farbe', + 'Assignee' => 'Zuständiger', + 'Create another task' => 'Weitere Aufgabe erstellen', + 'New task' => 'Neue Aufgabe', + 'Open a task' => 'Öffne eine Aufgabe', + 'Do you really want to open this task: "%s"?' => 'Soll diese Aufgabe wirklich wieder geöffnet werden: "%s"?', + 'Back to the board' => 'Zurück zur Pinnwand', + 'There is nobody assigned' => 'Die Aufgabe wurde niemandem zugewiesen', + 'Column on the board:' => 'Spalte:', + 'Close this task' => 'Aufgabe schließen', + 'Open this task' => 'Aufgabe wieder öffnen', + 'There is no description.' => 'Keine Beschreibung vorhanden.', + 'Add a new task' => 'Neue Aufgabe hinzufügen', + 'The username is required' => 'Der Benutzername wird benötigt', + 'The maximum length is %d characters' => 'Die maximale Länge beträgt %d Zeichen', + 'The minimum length is %d characters' => 'Die minimale Länge beträgt %d Zeichen', + 'The password is required' => 'Das Passwort wird benötigt', + 'This value must be an integer' => 'Dieser Wert muss eine ganze Zahl sein', + 'The username must be unique' => 'Der Benutzername muss eindeutig sein', + 'The user id is required' => 'Die Benutzer-ID ist anzugeben', + 'Passwords don\'t match' => 'Passwörter nicht gleich', + 'The confirmation is required' => 'Die Bestätigung ist erforderlich', + 'The project is required' => 'Das Projekt ist anzugeben', + 'The id is required' => 'Die ID ist anzugeben', + 'The project id is required' => 'Die Projekt-ID ist anzugeben', + 'The project name is required' => 'Der Projektname ist anzugeben', + 'The title is required' => 'Der Titel ist anzugeben', + 'Settings saved successfully.' => 'Einstellungen erfolgreich gespeichert.', + 'Unable to save your settings.' => 'Speichern der Einstellungen nicht möglich.', + 'Database optimization done.' => 'Optimieren der Datenbank abgeschlossen.', + 'Your project has been created successfully.' => 'Das Projekt wurde erfolgreich erstellt.', + 'Unable to create your project.' => 'Erstellen des Projekts nicht möglich.', + 'Project updated successfully.' => 'Projekt erfolgreich geändert.', + 'Unable to update this project.' => 'Änderung des Projekts nicht möglich.', + 'Unable to remove this project.' => 'Löschen des Projekts nicht möglich.', + 'Project removed successfully.' => 'Projekt erfolgreich gelöscht.', + 'Project activated successfully.' => 'Projekt erfolgreich aktiviert.', + 'Unable to activate this project.' => 'Aktivieren des Projekts nicht möglich.', + 'Project disabled successfully.' => 'Projekt erfolgreich deaktiviert.', + 'Unable to disable this project.' => 'Deaktivieren des Projekts nicht möglich.', + 'Unable to open this task.' => 'Wiedereröffnung der Aufgabe nicht möglich.', + 'Task opened successfully.' => 'Aufgabe erfolgreich wieder geöffnet.', + 'Unable to close this task.' => 'Abschließen der Aufgabe nicht möglich.', + 'Task closed successfully.' => 'Aufgabe erfolgreich geschlossen.', + 'Unable to update your task.' => 'Aktualisieren der Aufgabe nicht möglich.', + 'Task updated successfully.' => 'Aufgabe erfolgreich aktualisiert.', + 'Unable to create your task.' => 'Erstellen der Aufgabe nicht möglich.', + 'Task created successfully.' => 'Aufgabe erfolgreich erstellt.', + 'User created successfully.' => 'Benutzer erfolgreich erstellt.', + 'Unable to create your user.' => 'Erstellen des Benutzers nicht möglich.', + 'User updated successfully.' => 'Benutzer erfolgreich geändert.', + 'User removed successfully.' => 'Benutzer erfolgreich gelöscht.', + 'Unable to remove this user.' => 'Löschen des Benutzers nicht möglich.', + 'Board updated successfully.' => 'Pinnwand erfolgreich geändert.', + 'Ready' => 'Bereit', + 'Backlog' => 'Ideen', + 'Work in progress' => 'In Arbeit', + 'Done' => 'Erledigt', + 'Application version:' => 'Version:', + 'Id' => 'ID', + 'Public link' => 'Öffentlicher Link', + 'Timezone' => 'Zeitzone', + 'Sorry, I didn\'t find this information in my database!' => 'Diese Information wurde in der Datenbank nicht gefunden!', + 'Page not found' => 'Seite nicht gefunden', + 'Complexity' => 'Komplexität', + 'Task limit' => 'Maximale Anzahl von Aufgaben', + 'Task count' => 'Aufgabenanzahl', + 'User' => 'Benutzer', + 'Comments' => 'Kommentare', + 'Comment is required' => 'Ein Kommentar wird benötigt', + 'Comment added successfully.' => 'Kommentar erfolgreich hinzugefügt.', + 'Unable to create your comment.' => 'Hinzufügen eines Kommentars nicht möglich.', + 'Due Date' => 'Fällig am', + 'Invalid date' => 'Ungültiges Datum', + 'Automatic actions' => 'Automatische Aktionen', + 'Your automatic action has been created successfully.' => 'Die automatische Aktion wurde erfolgreich erstellt.', + 'Unable to create your automatic action.' => 'Erstellen der automatischen Aktion nicht möglich.', + 'Remove an action' => 'Aktion löschen', + 'Unable to remove this action.' => 'Löschen der Aktion nicht möglich.', + 'Action removed successfully.' => 'Aktion erfolgreich gelöscht.', + 'Automatic actions for the project "%s"' => 'Automatische Aktionen für das Projekt "%s"', + 'Add an action' => 'Aktion hinzufügen', + 'Event name' => 'Ereignisname', + 'Action' => 'Aktion', + 'Event' => 'Ereignis', + 'When the selected event occurs execute the corresponding action.' => 'Wenn das gewählte Ereignis eintritt, führe die zugehörige Aktion aus.', + 'Next step' => 'Weiter', + 'Define action parameters' => 'Aktionsparameter definieren', + 'Do you really want to remove this action: "%s"?' => 'Soll diese Aktion wirklich gelöscht werden: "%s"?', + 'Remove an automatic action' => 'Löschen einer automatischen Aktion', + 'Assign the task to a specific user' => 'Aufgabe einem Benutzer zuordnen', + 'Assign the task to the person who does the action' => 'Aufgabe dem Benutzer zuordnen, der die Aktion ausgeführt hat', + 'Duplicate the task to another project' => 'Aufgabe in ein anderes Projekt kopieren', + 'Move a task to another column' => 'Aufgabe in andere Spalte verschieben', + 'Task modification' => 'Aufgabe ändern', + 'Task creation' => 'Aufgabe erstellen', + 'Closing a task' => 'Aufgabe abschließen', + 'Assign a color to a specific user' => 'Einem Benutzer eine Farbe zuordnen', + 'Position' => 'Position', + 'Duplicate to project' => 'In ein anderes Projekt duplizieren', + 'Duplicate' => 'Duplizieren', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Kommentar erfolgreich aktualisiert.', + 'Unable to update your comment.' => 'Aktualisierung des Kommentars nicht möglich.', + 'Remove a comment' => 'Kommentar löschen', + 'Comment removed successfully.' => 'Kommentar erfolgreich gelöscht.', + 'Unable to remove this comment.' => 'Löschen des Kommentars nicht möglich.', + 'Do you really want to remove this comment?' => 'Soll dieser Kommentar wirklich gelöscht werden?', + 'Current password for the user "%s"' => 'Aktuelles Passwort des Benutzers "%s"', + 'The current password is required' => 'Das aktuelle Passwort wird benötigt', + 'Wrong password' => 'Falsches Passwort', + 'Unknown' => 'Unbekannt', + 'Last logins' => 'Letzte Anmeldungen', + 'Login date' => 'Anmeldedatum', + 'Authentication method' => 'Authentisierungsmethode', + 'IP address' => 'IP-Adresse', + 'User agent' => 'User-Agent', + 'Persistent connections' => 'Bestehende Verbindungen', + 'No session.' => 'Keine Sitzung.', + 'Expiration date' => 'Ablaufdatum', + 'Remember Me' => 'Angemeldet bleiben', + 'Creation date' => 'Erstellungsdatum', + 'Everybody' => 'Alle', + 'Open' => 'Offen', + 'Closed' => 'Abgeschlossen', + 'Search' => 'Suchen', + 'Nothing found.' => 'Nichts gefunden.', + 'Due date' => 'Fälligkeitsdatum', + 'Description' => 'Beschreibung', + '%d comments' => '%d Kommentare', + '%d comment' => '%d Kommentar', + 'Email address invalid' => 'Ungültige E-Mail-Adresse', + 'Your external account is not linked anymore to your profile.' => 'Dein externer Account ist nicht mehr mit deinem Profil verbunden.', + 'Unable to unlink your external account.' => 'Externer Account konnte nicht getrennt werden.', + 'External authentication failed' => 'Externe Authentifizierung fehlgeschlagen', + 'Your external account is linked to your profile successfully.' => 'Dein externer Account wurde erfolgreich mit deinem Profil verbunden', + 'Email' => 'E-Mail', + 'Task removed successfully.' => 'Aufgabe erfolgreich gelöscht.', + 'Unable to remove this task.' => 'Löschen der Aufgabe nicht möglich.', + 'Remove a task' => 'Aufgabe löschen', + 'Do you really want to remove this task: "%s"?' => 'Soll diese Aufgabe wirklich gelöscht werden: "%s"?', + 'Assign automatically a color based on a category' => 'Automatisch eine Farbe anhand der Kategorie zuweisen', + 'Assign automatically a category based on a color' => 'Automatisch eine Kategorie anhand der Farbe zuweisen', + 'Task creation or modification' => 'Aufgabe erstellen oder ändern', + 'Category' => 'Kategorie', + 'Category:' => 'Kategorie:', + 'Categories' => 'Kategorien', + 'Your category has been created successfully.' => 'Kategorie erfolgreich erstellt.', + 'This category has been updated successfully.' => 'Kategorie erfolgreich aktualisiert.', + 'Unable to update this category.' => 'Änderung der Kategorie nicht möglich.', + 'Remove a category' => 'Kategorie löschen', + 'Category removed successfully.' => 'Kategorie erfolgreich gelöscht.', + 'Unable to remove this category.' => 'Löschen der Kategorie nicht möglich.', + 'Category modification for the project "%s"' => 'Kategorie für das Projekt "%s" bearbeiten', + 'Category Name' => 'Kategoriename', + 'Add a new category' => 'Neue Kategorie', + 'Do you really want to remove this category: "%s"?' => 'Soll diese Kategorie wirklich gelöscht werden: "%s"?', + 'All categories' => 'Alle Kategorien', + 'No category' => 'Keine Kategorie', + 'The name is required' => 'Der Name ist erforderlich', + 'Remove a file' => 'Datei löschen', + 'Unable to remove this file.' => 'Löschen der Datei nicht möglich.', + 'File removed successfully.' => 'Datei erfolgreich gelöscht.', + 'Attach a document' => 'Dokument anhängen', + 'Do you really want to remove this file: "%s"?' => 'Soll diese Datei wirklich gelöscht werden: "%s"?', + 'Attachments' => 'Anhänge', + 'Edit the task' => 'Aufgabe bearbeiten', + 'Add a comment' => 'Kommentar hinzufügen', + 'Edit a comment' => 'Kommentar bearbeiten', + 'Summary' => 'Zusammenfassung', + 'Time tracking' => 'Zeiterfassung', + 'Estimate:' => 'Geschätzt:', + 'Spent:' => 'Aufgewendet:', + 'Do you really want to remove this sub-task?' => 'Soll diese Teilaufgabe wirklich gelöscht werden?', + 'Remaining:' => 'Verbleibend:', + 'hours' => 'Stunden', + 'estimated' => 'geschätzt', + 'Sub-Tasks' => 'Teilaufgaben', + 'Add a sub-task' => 'Teilaufgabe anlegen', + 'Original estimate' => 'Geschätzter Aufwand', + 'Create another sub-task' => 'Weitere Teilaufgabe anlegen', + 'Time spent' => 'Aufgewendete Zeit', + 'Edit a sub-task' => 'Teilaufgabe bearbeiten', + 'Remove a sub-task' => 'Teilaufgabe löschen', + 'The time must be a numeric value' => 'Zeit nur als nummerische Angabe', + 'Todo' => 'Nicht gestartet', + 'In progress' => 'In Bearbeitung', + 'Sub-task removed successfully.' => 'Teilaufgabe erfolgreich gelöscht.', + 'Unable to remove this sub-task.' => 'Löschen der Teilaufgabe nicht möglich.', + 'Sub-task updated successfully.' => 'Teilaufgabe erfolgreich aktualisiert.', + 'Unable to update your sub-task.' => 'Aktualisieren der Teilaufgabe nicht möglich.', + 'Unable to create your sub-task.' => 'Erstellen der Teilaufgabe nicht möglich.', + 'Maximum size: ' => 'Maximalgröße: ', + 'Display another project' => 'Zu Projekt wechseln', + 'Created by %s' => 'Erstellt durch %s', + 'Tasks Export' => 'Aufgaben exportieren', + 'Start Date' => 'Anfangsdatum', + 'Execute' => 'Ausführen', + 'Task Id' => 'Aufgaben-ID', + 'Creator' => 'Erstellt von', + 'Modification date' => 'Änderungsdatum', + 'Completion date' => 'Abschlussdatum', + 'Clone' => 'duplizieren', + 'Project cloned successfully.' => 'Projekt wurde dupliziert.', + 'Unable to clone this project.' => 'Duplizieren dieses Projekts schlug fehl.', + 'Enable email notifications' => 'E-Mail-Benachrichtigungen einschalten', + 'Task position:' => 'Position der Aufgabe:', + 'The task #%d has been opened.' => 'Die Aufgabe #%d wurde geöffnet.', + 'The task #%d has been closed.' => 'Die Aufgabe #%d wurde geschlossen.', + 'Sub-task updated' => 'Teilaufgabe aktualisiert', + 'Title:' => 'Titel:', + 'Status:' => 'Status:', + 'Assignee:' => 'Zuständigkeit:', + 'Time tracking:' => 'Zeiterfassung:', + '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 comment' => 'Neuer Kommentar', + 'Comment updated' => 'Kommentar wurde aktualisiert', + 'New subtask' => 'Neue Teilaufgabe', + 'I only want to receive notifications for these 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', + 'Disable public access' => 'Öffentlichen Zugriff deaktivieren', + 'Enable public access' => 'Öffentlichen Zugriff aktivieren', + 'Public access disabled' => 'Öffentlicher Zugriff deaktiviert', + 'Move the task to another project' => 'Aufgabe in ein anderes Projekt verschieben', + 'Move to project' => 'In anderes Projekt verschieben', + 'Do you really want to duplicate this task?' => 'Möchtest du diese Aufgabe wirklich duplizieren?', + 'Duplicate a task' => 'Aufgabe duplizieren', + 'External accounts' => 'Externe Accounts', + 'Account type' => 'Accounttyp', + 'Local' => 'Lokal', + 'Remote' => 'Remote', + 'Enabled' => 'angeschaltet', + 'Disabled' => 'abgeschaltet', + 'Login:' => 'Benutzername:', + 'Full Name:' => 'Vollständiger Name:', + 'Email:' => 'E-Mail:', + 'Notifications:' => 'Benachrichtigungen:', + 'Notifications' => 'Benachrichtigungen', + 'Account type:' => 'Accounttyp:', + 'Edit profile' => 'Profil bearbeiten', + 'Change password' => 'Passwort ändern', + 'Password modification' => 'Passwortänderung', + 'External authentications' => 'Externe Authentisierungsmethoden', + 'Never connected.' => 'Noch nie verbunden.', + 'No external authentication enabled.' => 'Es sind keine externen Authentisierungsmethoden aktiv.', + 'Password modified successfully.' => 'Passwort wurde erfolgreich geändert.', + 'Unable to change the password.' => 'Passwort konnte nicht geändert werden.', + 'Change category' => 'Kategorie ändern', + '%s updated the task %s' => '%s hat die Aufgabe %s aktualisiert', + '%s opened the task %s' => '%s hat die Aufgabe %s geöffnet', + '%s moved the task %s to the position #%d in the column "%s"' => '%s hat die Aufgabe %s auf die Position #%d in der Spalte "%s" verschoben', + '%s moved the task %s to the column "%s"' => '%s hat die Aufgabe %s in die Spalte "%s" verschoben', + '%s created the task %s' => '%s hat die Aufgabe %s angelegt', + '%s closed the task %s' => '%s hat die Aufgabe %s geschlossen', + '%s created a subtask for the task %s' => '%s hat eine Teilaufgabe für die Aufgabe %s angelegt', + '%s updated a subtask for the task %s' => '%s hat eine Teilaufgabe der Aufgabe %s verändert', + 'Assigned to %s with an estimate of %s/%sh' => 'An %s zugewiesen mit einer Schätzung von %s/%s Stunden', + 'Not assigned, estimate of %sh' => 'Nicht zugewiesen, Schätzung von %s Stunden', + '%s updated a comment on the task %s' => '%s hat einen Kommentar der Aufgabe %s aktualisiert', + '%s commented the task %s' => '%s hat die Aufgabe %s kommentiert', + '%s\'s activity' => '%s\'s Aktivität', + 'RSS feed' => 'RSS Feed', + '%s updated a comment on the task #%d' => '%s hat einen Kommentar der Aufgabe #%d aktualisiert', + '%s commented on the task #%d' => '%s hat die Aufgabe #%d kommentiert', + '%s updated a subtask for the task #%d' => '%s hat eine Teilaufgabe der Aufgabe #%d aktualisiert', + '%s created a subtask for the task #%d' => '%s hat eine Teilaufgabe der Aufgabe #%d angelegt', + '%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 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 changed the assignee of the task #%d to %s' => '%s hat die Zuständigkeit der Aufgabe #%d geändert zu %s', + '%s changed the assignee of the task %s to %s' => '%s hat die Zuständigkeit der Aufgabe %s geändert zu %s', + 'New password for the user "%s"' => 'Neues Passwort des Benutzers "%s"', + 'Choose an event' => 'Aktion wählen', + 'Create a task from an external provider' => 'Eine Aufgabe durch einen externen Provider hinzufügen', + 'Change the assignee based on an external username' => 'Zuordnung ändern basierend auf externem Benutzernamen', + 'Change the category based on an external label' => 'Kategorie basierend auf einer externen Kennzeichnung ändern', + 'Reference' => 'Referenz', + 'Label' => 'Kennzeichnung', + 'Database' => 'Datenbank', + 'About' => 'Über', + 'Database driver:' => 'Datenbanktreiber:', + 'Board settings' => 'Pinnwandeinstellungen', + 'Webhook settings' => 'Webhook-Einstellungen', + 'Reset token' => 'Token zurücksetzen', + 'API endpoint:' => 'API-Endpunkt:', + 'Refresh interval for personal board' => 'Aktualisierungsintervall für persönliche Pinnwände', + 'Refresh interval for public board' => 'Aktualisierungsintervall für öffentliche Pinnwände', + 'Task highlight period' => 'Aufgaben-Hervorhebungsdauer', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Dauer (in Sekunden), wie lange eine Aufgabe als kürzlich verändert gilt (0 um diese Funktion zu deaktivieren, standardmäßig 2 Tage)', + 'Frequency in second (60 seconds by default)' => 'Frequenz in Sekunden (standardmäßig 60 Sekunden)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequenz in Sekunden (0 um diese Funktion zu deaktivieren, standardmäßig 10 Sekunden)', + 'Application URL' => 'Applikations-URL', + 'Token regenerated.' => 'Token wurde neu generiert.', + 'Date format' => 'Datumsformat', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO Format wird immer akzeptiert, z.B.: "%s" und "%s"', + 'New personal project' => 'Neues persönliches Projekt', + 'This project is personal' => 'Dies ist ein persönliches Projekt', + 'Add' => 'Hinzufügen', + 'Start date' => 'Startdatum', + 'Time estimated' => 'Geschätzte Zeit', + 'There is nothing assigned to you.' => 'Dir ist nichts zugewiesen.', + 'My tasks' => 'Meine Aufgaben', + 'Activity stream' => 'Letzte Aktivitäten', + 'Dashboard' => 'Dashboard', + 'Confirmation' => 'Wiederholung', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Kommentar eines externen Providers hinzufügen', + 'Project management' => 'Projektmanagement', + 'Columns' => 'Spalten', + 'Task' => 'Aufgabe', + 'Percentage' => 'Prozentsatz', + 'Number of tasks' => 'Anzahl an Aufgaben', + 'Task distribution' => 'Aufgabenverteilung', + 'Analytics' => 'Analyse', + 'Subtask' => 'Teilaufgabe', + 'User repartition' => 'Benutzerverteilung', + '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.', + 'Previous' => 'Vorherige', + 'The id must be an integer' => 'Die ID muss eine ganze Zahl sein', + 'The project id must be an integer' => 'Der Projekt-ID muss eine ganze Zahl sein', + 'The status must be an integer' => 'Der Status muss eine ganze Zahl sein', + 'The subtask id is required' => 'Die Teilaufgaben-ID ist benötigt', + 'The subtask id must be an integer' => 'Die Teilaufgaben-ID muss eine ganze Zahl sein', + 'The task id is required' => 'Die Aufgaben-ID ist benötigt', + 'The task id must be an integer' => 'Die Aufgaben-ID muss eine ganze Zahl sein', + 'The user id must be an integer' => 'Die Benutzer-ID muss eine ganze Zahl sein', + 'This value is required' => 'Dieser Wert ist erforderlich', + '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', + 'Daily project summary' => 'Tägliche Projektzusammenfassung', + 'Daily project summary export' => 'Export der täglichen Projektzusammenfassung', + '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', + 'Add a new swimlane' => 'Eine neue Swimlane hinzufügen', + 'Default swimlane' => 'Standard-Swimlane', + 'Do you really want to remove this swimlane: "%s"?' => 'Diese Swimlane wirklich entfernen: "%s"?', + 'Inactive swimlanes' => 'Inaktive Swimlane', + 'Remove a swimlane' => 'Swimlane entfernen', + 'Swimlane modification for the project "%s"' => 'Swimlane-Änderung für das Projekt "%s"', + 'Swimlane removed successfully.' => 'Swimlane erfolgreich entfernt.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane erfolgreich geändert.', + 'Unable to remove this swimlane.' => 'Es ist nicht möglich, die Swimlane zu entfernen.', + 'Unable to update this swimlane.' => 'Es ist nicht möglich, die Swimlane zu ändern.', + 'Your swimlane has been created successfully.' => 'Die Swimlane wurde erfolgreich angelegt.', + 'Example: "Bug, Feature Request, Improvement"' => 'Beispiel: "Bug, Funktionswünsche, Verbesserung"', + 'Default categories for new projects (Comma-separated)' => 'Standard-Kategorien für neue Projekte (Komma-getrennt)', + 'Integrations' => 'Integration', + 'Integration with third-party services' => 'Integration von externen Diensten', + 'Subtask Id' => 'Teilaufgaben-ID', + 'Subtasks' => 'Teilaufgaben', + 'Subtasks Export' => 'Export von Teilaufgaben', + 'Task Title' => 'Aufgaben-Titel', + 'Untitled' => 'unbetitelt', + 'Application default' => 'Anwendungsstandard', + 'Language:' => 'Sprache:', + 'Timezone:' => 'Zeitzone:', + 'All columns' => 'Alle Spalten', + 'Next' => 'Nächste', + '#%d' => 'Nr %d', + 'All swimlanes' => 'Alle Swimlanes', + 'All colors' => 'Alle Farben', + 'Moved to column %s' => 'In Spalte %s verschoben', + 'User dashboard' => 'Benutzer-Dashboard', + 'Allow only one subtask in progress at the same time for a user' => 'Erlaube nur eine Teilaufgabe pro Benutzer zu bearbeiten', + 'Edit column "%s"' => 'Spalte "%s" bearbeiten', + 'Select the new status of the subtask: "%s"' => 'Wähle einen neuen Status für Teilaufgabe: "%s"', + 'Subtask timesheet' => 'Teilaufgaben Zeiterfassung', + 'There is nothing to show.' => 'Es ist nichts zum Anzeigen vorhanden.', + 'Time Tracking' => 'Zeiterfassung', + 'You already have one subtask in progress' => 'Bereits eine Teilaufgabe in Bearbeitung', + 'Which parts of the project do you want to duplicate?' => 'Welcher Teil des Projekts soll kopiert werden?', + 'Disallow login form' => 'Verbiete Login-Formular', + 'Start' => 'Start', + 'End' => 'Ende', + 'Task age in days' => 'Aufgabenalter in Tagen', + 'Days in this column' => 'Tage in dieser Spalte', + '%dd' => '%dT', + 'Add a new link' => 'Neue Verbindung hinzufügen', + 'Do you really want to remove this link: "%s"?' => 'Die Verbindung "%s" wirklich löschen?', + 'Do you really want to remove this link with task #%d?' => 'Die Verbindung mit der Aufgabe #%d wirklich löschen?', + 'Field required' => 'Feld erforderlich', + 'Link added successfully.' => 'Verbindung erfolgreich hinzugefügt.', + 'Link updated successfully.' => 'Verbindung erfolgreich aktualisiert.', + 'Link removed successfully.' => 'Verbindung erfolgreich gelöscht.', + 'Link labels' => 'Verbindungsbeschriftung', + 'Link modification' => 'Verbindung ändern', + 'Opposite label' => 'Gegenteil', + 'Remove a link' => 'Verbindung entfernen', + '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', + 'Unable to create your link.' => 'Verbindung kann nicht erstellt werden.', + 'Unable to update your link.' => 'Verbindung kann nicht aktualisiert werden.', + 'Unable to remove this link.' => 'Verbindung kann nicht entfernt werden', + 'relates to' => 'gehört zu', + 'blocks' => 'blockiert', + 'is blocked by' => 'ist blockiert von', + 'duplicates' => 'doppelt', + 'is duplicated by' => 'ist gedoppelt von', + 'is a child of' => 'ist ein untergeordnetes Element von', + 'is a parent of' => 'ist ein übergeordnetes Element von', + 'targets milestone' => 'betrifft Meilenstein', + 'is a milestone of' => 'ist ein Meilenstein von', + 'fixes' => 'behebt', + 'is fixed by' => 'wird behoben von', + 'This task' => 'Diese Aufgabe', + '<1h' => '<1Std', + '%dh' => '%dStd', + 'Expand tasks' => 'Aufgaben aufklappen', + 'Collapse tasks' => 'Aufgaben zusammenklappen', + 'Expand/collapse tasks' => 'Aufgaben auf/zuklappen', + 'Close dialog box' => 'Dialog schließen', + 'Submit a form' => 'Formular abschicken', + 'Board view' => 'Pinnwand Ansicht', + 'Keyboard shortcuts' => 'Tastaturkürzel', + 'Open board switcher' => 'Pinnwandauswahl öffnen', + 'Application' => 'Anwendung', + 'Compact view' => 'Kompaktansicht', + 'Horizontal scrolling' => 'Horizontales Scrollen', + 'Compact/wide view' => 'Kompakt/Breite-Ansicht', + 'Currency' => 'Währung', + 'Personal project' => 'privates Projekt', + 'AUD - Australian Dollar' => 'AUD - Australische Dollar', + 'CAD - Canadian Dollar' => 'CAD - Kanadische Dollar', + 'CHF - Swiss Francs' => 'CHF - Schweizer Franken', + 'Custom Stylesheet' => 'benutzerdefiniertes Stylesheet', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britische Pfund', + 'INR - Indian Rupee' => 'INR - Indische Rupien', + 'JPY - Japanese Yen' => 'JPY - Japanische Yen', + 'NZD - New Zealand Dollar' => 'NZD - Neuseeland-Dollar', + 'PEN - Peruvian Sol' => 'PEN - Peruanischer Sol', + 'RSD - Serbian dinar' => 'RSD - Serbische Dinar', + 'CNY - Chinese Yuan' => 'CNY - Chinesische Renminbi', + 'USD - US Dollar' => 'USD - US-Dollar', + 'VES - Venezuelan Bolívar' => 'VES - Venezolanische Bolívar', + 'Destination column' => 'Zielspalte', + 'Move the task to another column when assigned to a user' => 'Aufgabe in eine andere Spalte verschieben, wenn ein Benutzer zugeordnet wurde.', + 'Move the task to another column when assignee is cleared' => 'Aufgabe in eine andere Spalte verschieben, wenn die Zuordnung gelöscht wurde.', + 'Source column' => 'Quellspalte', + 'Transitions' => 'Übergänge', + 'Executer' => 'Ausführender', + 'Time spent in the column' => 'Zeit in Spalte verbracht', + 'Task transitions' => 'Aufgaben-Übergänge', + 'Task transitions export' => 'Aufgaben-Übergänge exportieren', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Diese Auswertung enthält alle Spaltenbewegungen für jede Aufgabe mit Datum, Benutzer und Zeit vor jedem Wechsel.', + 'Currency rates' => 'Währungskurse', + 'Rate' => 'Kurse', + 'Change reference currency' => 'Referenzwährung ändern', + 'Reference currency' => 'Referenzwährung', + 'The currency rate has 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 removed the assignee of the task %s' => '%s Zuordnung für die Aufgabe %s entfernen', + 'Information' => 'Information', + 'Check two factor authentication code' => 'Prüfe Zwei-Faktor-Authentifizierungscode', + 'The two factor authentication code is not valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist ungültig.', + 'The two factor authentication code is valid.' => 'Der Zwei-Faktor-Authentifizierungscode ist gültig.', + 'Code' => 'Code', + 'Two factor authentication' => 'Zwei-Faktor-Authentifizierung', + 'This QR code contains the key URI: ' => 'Dieser QR-Code beinhaltet die Schlüssel-URI: ', + 'Check my code' => 'Überprüfe meinen Code', + 'Secret key: ' => 'Geheimer Schlüssel: ', + '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' => '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', + 'Add a screenshot' => 'Screenshot hinzufügen', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Nimm einen Screenshot auf und drücke STRG+V oder ⌘+V um ihn hier einzufügen.', + 'Screenshot uploaded successfully.' => 'Screenshot erfolgreich hochgeladen.', + 'SEK - Swedish Krona' => 'SEK - Schwedische Kronen', + 'Identifier' => 'Identifikator', + 'Disable two factor authentication' => 'Deaktiviere Zwei-Faktor-Authentifizierung', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Willst du wirklich für folgenden Benutzer die Zwei-Faktor-Authentifizierung deaktivieren: "%s"?', + 'Edit link' => 'Verbindung bearbeiten', + 'Start to type task title...' => 'Beginne mit der Titeleingabe...', + 'A task cannot be linked to itself' => 'Eine Aufgabe kann nicht mit sich selber verbunden werden', + 'The exact same link already exists' => 'Diese Verbindung existiert bereits', + 'Recurrent task is scheduled to be generated' => 'Wiederkehrende Aufgabe ist zur Generierung eingeplant', + 'Score' => 'Wertung', + 'The identifier must be unique' => 'Der Schlüssel muss einzigartig sein', + 'This linked task id doesn\'t exists' => 'Die verbundene Aufgabe existiert nicht', + 'This value must be alphanumeric' => 'Der Wert muss alphanumerisch sein', + 'Edit recurrence' => 'Wiederholung bearbeiten', + 'Generate recurrent task' => 'Wiederkehrende Aufgabe generieren', + 'Trigger to generate recurrent task' => 'Auslöser für wiederkehrende Aufgabe', + 'Factor to calculate new due date' => 'Faktor zur Berechnung für neues Ablaufdatum', + 'Timeframe to calculate new due date' => 'Zeitfenster zur Berechnung für neues Ablaufdatum', + 'Base date to calculate new due date' => 'Basisdatum zur Berechnung für neues Ablaufdatum', + 'Action date' => 'Aktionsdatum', + 'Base date to calculate new due date: ' => 'Basisdatum zur Berechnung für neues Ablaufdatum: ', + 'This task has created this child task: ' => 'Diese Aufgabe hat diese Teilaufgabe erstellt: ', + 'Day(s)' => 'Tag(e)', + 'Existing due date' => 'Existierendes Ablaufdatum', + 'Factor to calculate new due date: ' => 'Faktor zur Berechnung für neues Ablaufdatum: ', + 'Month(s)' => 'Monat(e)', + '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 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)', + 'Project settings' => 'Projekteinstellungen', + 'Automatically update the start date' => 'Beginndatum automatisch aktualisieren', + 'iCal feed' => 'iCal Feed', + 'Preferences' => 'Einstellungen', + 'Security' => 'Sicherheit', + 'Two factor authentication disabled' => 'Zwei-Faktor-Authentifizierung deaktiviert', + 'Two factor authentication enabled' => 'Zwei-Faktor-Authentifizierung aktiviert', + 'Unable to update this user.' => 'Benutzer kann nicht bearbeitet werden', + 'There is no user management for personal projects.' => 'Es gibt keine Benutzerverwaltung für persönliche Projekte', + 'User that will receive the email' => 'Empfänger der E-Mail', + 'Email subject' => 'E-Mail-Betreff', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'Kommentar hinzufügen, wenn Aufgabe in andere Spalte verschoben wird', + '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', + 'Notification' => 'Benachrichtigungen', + '%s moved the task #%d to the first swimlane' => '%s hat die Aufgabe #%d in die erste Swimlane verschoben', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => '%s hat die Aufgabe %s in die erste Swimlane verschoben', + '%s moved the task %s to the swimlane "%s"' => '%s hat die Aufgaben %s in die Swimlane "%s" verschoben', + 'This report contains all subtasks information for the given date range.' => 'Der Bericht beinhaltet alle Teilaufgaben im gewählten Zeitraum', + 'This report contains all tasks information for the given date range.' => 'Der Bericht beinhaltet alle Aufgaben im gewählten Zeitraum', + 'Project activities for %s' => 'Projektaktivitäten für %s', + 'view the board on Kanboard' => 'Pinnwand in Kanboard anzeigen', + 'The task has been moved to the first swimlane' => 'Die Aufgabe wurde in die erste Swimlane verschoben', + 'The task has been moved to another swimlane:' => 'Die Aufgaben wurde in eine andere Swimlane verschoben:', + 'New title: %s' => 'Neuer Titel: %s', + 'The task is not assigned anymore' => 'Die Aufgabe ist nicht mehr zugewiesen', + 'New assignee: %s' => 'Neue Zuordnung: %s', + 'There is no category now' => 'Es gibt keine Kategorie im Moment', + 'New category: %s' => 'Neue Kategorie: %s', + 'New color: %s' => 'Neue Farbe: %s', + 'New complexity: %d' => 'Neue Komplexität: %d', + 'The due date has been removed' => 'Das Ablaufdatum wurde entfernt', + 'There is no description anymore' => 'Es gibt keine Beschreibung mehr', + 'Recurrence settings has been modified' => 'Die Einstellungen für Wiederholung wurden geändert', + 'Time spent changed: %sh' => 'Verbrauchte Zeit geändert: %sh', + 'Time estimated changed: %sh' => 'Geschätzte Zeit geändert: %sh', + 'The field "%s" has been updated' => 'Das Feld "%s" wurde verändert', + 'The description has been modified:' => 'Die Beschreibung wurde geändert:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Soll die Aufgabe "%s" wirklich geschlossen werden? (einschließlich Teilaufgaben)', + 'I want to receive notifications for:' => 'Ich möchte Benachrichtigungen erhalten für:', + 'All tasks' => 'Alle Aufgaben', + 'Only for tasks assigned to me' => 'nur mir zugeordnete Aufgaben', + 'Only for tasks created by me' => 'nur von mir erstellte Aufgaben', + 'Only for tasks created by me and tasks assigned to me' => 'nur mir zugeordnete und von mir erstellte Aufgaben', + '%%Y-%%m-%%d' => '%%d.%%m.%%Y', + 'Total for all columns' => 'Gesamt für alle Spalten', + 'You need at least 2 days of data to show the chart.' => 'Es werden mindestens 2 Tage zur Darstellung benötigt', + '<15m' => '<15min', + '<30m' => '<30min', + 'Stop timer' => 'Stoppe Timer', + 'Start timer' => 'Starte Timer', + 'My activity stream' => 'Aktivitätsstream', + 'Search tasks' => 'Suche nach Aufgaben', + 'Reset filters' => 'Filter zurücksetzen', + 'My tasks due tomorrow' => 'Meine morgen fälligen Aufgaben', + 'Tasks due today' => 'Heute fällige Aufgaben', + 'Tasks due tomorrow' => 'Morgen fällige Aufgaben', + 'Tasks due yesterday' => 'Gestern fällige Aufgaben', + 'Closed tasks' => 'Abgeschlossene Aufgaben', + 'Open tasks' => 'Offene Aufgaben', + 'Not assigned' => 'Nicht zugewiesen', + 'View advanced search syntax' => 'Zur erweiterten Suchsyntax', + 'Overview' => 'Überblick', + 'Board/Calendar/List view' => 'Board-/Kalender-/Listen-Ansicht', + 'Switch to the board view' => 'Zur Board-Ansicht', + 'Switch to the list view' => 'Zur Listen-Ansicht', + 'Go to the search/filter box' => 'Zum Such- und Filterfeld', + 'There is no activity yet.' => 'Es gibt bislang keine Aktivitäten.', + 'No tasks found.' => 'Keine Aufgaben gefunden.', + 'Keyboard shortcut: "%s"' => 'Tastaturkürzel: "%s"', + 'List' => 'Liste', + 'Filter' => 'Filter', + 'Advanced search' => 'Fortgeschrittene Suche', + 'Example of query: ' => 'Beispiel einer Abfrage: ', + 'Search by project: ' => 'Suche nach Projekt: ', + 'Search by column: ' => 'Suche nach Spalte: ', + 'Search by assignee: ' => 'Suche nach zugeordnetem Benutzer: ', + 'Search by color: ' => 'Suche nach Farbe: ', + 'Search by category: ' => 'Suche nach Kategorie: ', + 'Search by description: ' => 'Suche nach Beschreibung: ', + 'Search by due date: ' => 'Suche nach Fälligkeitsdatum: ', + 'Average time spent in each column' => 'Durchschnittszeit in jeder Spalte', + 'Average time spent' => 'Durchschnittlicher Zeitverbrauch', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Dieses Diagramm zeigt die durchschnittliche Zeit in jeder Spalte der letzten %d Aufgaben.', + 'Average Lead and Cycle time' => 'Durchschnittliche Zyklus- und Durchlaufzeit', + 'Average lead time: ' => 'Durchschnittliche Durchlaufzeit: ', + 'Average cycle time: ' => 'Durchschnittliche Zykluszeit: ', + 'Cycle Time' => 'Zykluszeit', + 'Lead Time' => 'Durchlaufzeit', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Das Diagramm zeigt die durchschnittliche Durchlauf- und Zykluszeit der letzten %d Aufgaben über die Zeit an.', + 'Average time into each column' => 'Durchschnittszeit in jeder Spalte', + 'Lead and cycle time' => 'Durchlauf- und Zykluszeit', + 'Lead time: ' => 'Durchlaufzeit: ', + 'Cycle time: ' => 'Zykluszeit: ', + 'Time spent in each column' => 'zeit verbracht in jeder Spalte', + 'The lead time is the duration between the task creation and the completion.' => 'Die Durchlaufzeit ist die Dauer zwischen Erstellung und Fertigstellung.', + 'The cycle time is the duration between the start date and the completion.' => 'Die Zykluszeit ist die Dauer zwischen Start und Fertigstellung.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Wenn die Aufgabe nicht geschlossen ist, wird die aktuelle Zeit statt der Fertigstellung verwendet.', + 'Set the start date automatically' => 'Setze Startdatum automatisch', + 'Edit Authentication' => 'Authentifizierung bearbeiten', + '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.', + '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.', + 'Trigger automatically subtask time tracking' => 'Teilaufgaben Zeiterfassung automatisch starten', + 'Include closed tasks in the cumulative flow diagram' => 'Geschlossen Aufgaben ins kumulative Flussdiagramm einschließen', + 'Current swimlane: %s' => 'Aktuelle Swimlane: %s', + 'Current column: %s' => 'Aktuelle Spalte: %s', + 'Current category: %s' => 'Aktuelle Kategorie: %s', + 'no category' => 'keine Kategorie', + 'Current assignee: %s' => 'Aktuelle Zuordnung: %s', + 'not assigned' => 'nicht zugeordnet', + 'Author:' => 'Autor:', + 'contributors' => 'Mitwirkende', + 'License:' => 'Lizenz:', + 'License' => 'Lizenz', + 'Enter the text below' => 'Text unten eingeben', + 'Start date:' => 'Startdatum:', + 'Due date:' => 'Ablaufdatum:', + 'People who are project managers' => 'Benutzer die Projektmanager sind', + 'People who are project members' => 'Benutzer die Projektmitglieder sind', + 'NOK - Norwegian Krone' => 'NOK - Norwegische Kronen', + 'Show this column' => 'Spalte anzeigen', + 'Hide this column' => 'Spalte verstecken', + 'End date' => 'Endedatum', + 'Users overview' => 'Benutzerübersicht', + 'Members' => 'Mitglieder', + 'Shared project' => 'Geteiltes Projekt', + 'Project managers' => 'Projektmanager', + 'Projects list' => 'Projektliste', + 'End date:' => 'Endedatum:', + 'Change task color when using a specific task link' => 'Aufgabefarbe ändern bei bestimmter Aufgabenverbindung', + 'Task link creation or modification' => 'Aufgabenverbindung erstellen oder bearbeiten', + 'Milestone' => 'Meilenstein', + 'Reset the search/filter box' => 'Suche/Filter-Box zurücksetzen', + 'Documentation' => 'Dokumentation', + 'Author' => 'Autor', + 'Version' => 'Version', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Es ist kein Plugin geladen.', + 'My notifications' => 'Meine Benachrichtigungen', + 'Custom filters' => 'Benutzerdefinierte Filter', + 'Your custom filter has been created successfully.' => 'Benutzerdefinierten Filter erfolgreich erstellt.', + 'Unable to create your custom filter.' => 'Benutzerdefinierter Filter konnte nicht erstellt werden.', + 'Custom filter removed successfully.' => 'Benutzerdefinierten Filter erfolgreich entfernt.', + 'Unable to remove this custom filter.' => 'Benutzerdefinierten Filter konnte nicht entfernt werden.', + 'Edit custom filter' => 'Benutzerdefinierten Filter bearbeiten', + 'Your custom filter has been updated successfully.' => 'Benutzerdefinierten Filter erfolgreich bearbeitet.', + 'Unable to update custom filter.' => 'Benutzerdefinierter Filter konnte nicht geändert werden.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Neuer Anhang für Aufgabe #%d: %s', + 'New comment on task #%d' => 'Neuer Kommentar für Aufgabe #%d', + 'Comment updated on task #%d' => 'Kommentar geändert für Aufgabe #%d', + 'New subtask on task #%d' => 'Neue Teilaufgabe für Aufgabe #%d', + 'Subtask updated on task #%d' => 'Teilaufgabe geändert für Aufgabe #%d', + 'New task #%d: %s' => 'Neue Aufgabe #%d: %s', + 'Task updated #%d' => 'Aufgabe bearbeitet #%d', + 'Task #%d closed' => 'Aufgabe #%d geschlossen', + 'Task #%d opened' => 'Aufgabe #%d eröffnet', + 'Column changed for task #%d' => 'Spalte geändert von Aufgabe #%d', + 'New position for task #%d' => 'Neue Position für Aufgabe #%d', + 'Swimlane changed for task #%d' => 'Neue Swimlane für Aufgabe #%d', + 'Assignee changed on task #%d' => 'Neue Zuordnung für Aufgabe #%d ', + '%d overdue tasks' => '%d überfällige Aufgaben', + 'No notification.' => 'Keine neuen Benachrichtigungen', + 'Mark all as read' => 'Alles als gelesen markieren', + 'Mark as read' => 'Als gelesen markieren', + 'Total number of tasks in this column across all swimlanes' => 'Anzahl an Aufgaben in dieser Spalte über alle Swimlanes', + 'Collapse swimlane' => 'Swimlane einklappen', + 'Expand swimlane' => 'Swimlane ausklappen', + 'Add a new filter' => 'Neuen Filter hinzufügen', + 'Share with all project members' => 'Mit allen Projektmitgliedern teilen.', + 'Shared' => 'Geteilt', + 'Owner' => 'Eigentümer', + 'Unread notifications' => 'Ungelesene Benachrichtigungen', + 'Notification methods:' => 'Benachrichtigungs-Methoden:', + '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 has been imported!' => 'Es wurde nichts importiert!', + 'Import users from CSV file' => 'Importiere Benutzer aus CSV Datei', + '%d user(s) have been imported successfully.' => '%d Benutzer wurde(n) erfolgreich importiert.', + 'Comma' => 'Komma', + 'Semi-colon' => 'Semikolon', + 'Tab' => 'Tabulator', + 'Vertical bar' => 'senkrechter Strich', + 'Double Quote' => 'Doppelte Anführungszeichen', + 'Single Quote' => 'Einfache Anführungszeichen', + '%s attached a file to the task #%d' => '%s hat eine Datei zur Aufgabe #%d hinzugefügt', + 'There is no column or swimlane activated in your project!' => 'Es ist keine Spalte oder Swimlane in deinem Projekt aktiviert!', + 'Append filter (instead of replacement)' => 'Filter anhängen (statt zu ersetzen)', + 'Append/Replace' => 'Anhängen/Ersetzen', + 'Append' => 'Anhängen', + 'Replace' => 'Ersetzen', + 'Import' => 'Import', + 'Change sorting' => 'Sortierung ändern', + 'Tasks Importation' => 'Aufgaben Import', + 'Delimiter' => 'Trennzeichen', + 'Enclosure' => 'Textbegrenzer', + 'CSV File' => 'CSV Datei', + 'Instructions' => 'Anweisungen', + 'Your file must use the predefined CSV format' => 'Deine Datei muss das vorgegebene CSV Format haben', + 'Your file must be encoded in UTF-8' => 'Deine Datei muss UTF-8 kodiert sein', + 'The first row must be the header' => 'Die erste Zeile muss die Kopfzeile sein', + 'Duplicates are not verified for you' => 'Duplikate werden nicht für dich geprüft', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Das Fälligkeitsdatum muss das ISO Format haben: YYYY-MM-DD', + 'Download CSV template' => 'CSV Vorlage herunterladen', + 'No external integration registered.' => 'Keine externe Integration registriert', + 'Duplicates are not imported' => 'Duplikate wurden nicht importiert', + 'Usernames must be lowercase and unique' => 'Benutzernamen müssen in Kleinbuchstaben und eindeutig sein', + 'Passwords will be encrypted if present' => 'Passwörter werden verschlüsselt wenn vorhanden', + '%s attached a new file to the task %s' => '%s hat eine neue Datei zur Aufgabe %s hinzugefügt', + 'Link type' => 'Verbindungstyp', + 'Assign automatically a category based on a link' => 'Linkbasiert eine Kategorie automatisch zuordnen', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Benutzername des Zuständigen', + 'Assignee Name' => 'Name des Zuständigen', + 'Groups' => 'Gruppen', + 'Members of %s' => 'Mitglied von %s', + 'New group' => 'Neue Gruppe', + 'Group created successfully.' => 'Gruppe erfolgreich angelegt.', + 'Unable to create your group.' => 'Gruppe konnte nicht angelegt werden', + 'Edit group' => 'Gruppe bearbeiten', + 'Group updated successfully.' => 'Gruppe erfolgreich aktualisiert', + 'Unable to update your group.' => 'Gruppe konnte nicht aktualisiert werden', + 'Add group member to "%s"' => 'Gruppenmitglied zu "%s" hinzufügen', + 'Group member added successfully.' => 'Gruppenmitglied erfolgreich hinzugefügt', + 'Unable to add group member.' => 'Gruppenmitglied konnte nicht hinzugefügt werden.', + 'Remove user from group "%s"' => 'Benutzer aus Gruppe "%s" löschen', + 'User removed successfully from this group.' => 'Benutzer erfolgreich aus dieser Gruppe gelöscht.', + 'Unable to remove this user from the group.' => 'Benutzer konnte nicht aus dieser Gruppe gelöscht werden.', + 'Remove group' => 'Gruppe löschen', + 'Group removed successfully.' => 'Gruppe erfolgreich gelöscht.', + 'Unable to remove this group.' => 'Gruppe konnte nicht gelöscht werden.', + 'Project Permissions' => 'Projekt Berechtigungen', + 'Manager' => 'Manager', + 'Project Manager' => 'Projekt Manager', + 'Project Member' => 'Projekt Mitglied', + 'Project Viewer' => 'Projekt Betrachter', + 'Your account is locked for %d minutes' => 'Dein Zugang wurde für %d Minuten gesperrt', + 'Invalid captcha' => 'Ungültiges Captcha', + 'The name must be unique' => 'Der Name muss eindeutig sein', + 'View all groups' => 'Alle Gruppen 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"?' => 'Willst du den Benutzer "%s" wirklich aus der Gruppe "%s" löschen?', + 'There is no group.' => 'Es gibt keine Gruppe.', + 'Add group member' => 'Gruppenmitglied hinzufügen', + 'Do you really want to remove this group: "%s"?' => 'Willst du die Gruppe "%s" wirklich löschen?', + 'There is no user in this group.' => 'Es gibt keinen Benutzer in dieser Gruppe.', + 'Permissions' => 'Berechtigungen', + 'Allowed Users' => 'Berechtigte Benutzer', + 'No specific user has been allowed.' => 'Keine Benutzer mit ausdrücklicher Berechtigung.', + 'Role' => 'Rolle', + 'Enter user name...' => 'Gib den Benutzernamen ein...', + 'Allowed Groups' => 'Berechtigte Gruppen', + 'No group has been allowed.' => 'Keine Gruppen mit ausdrücklicher Berechtigung.', + 'Group' => 'Gruppe', + 'Group Name' => 'Gruppenname', + 'Enter group name...' => 'Gib den Gruppennamen ein...', + 'Role:' => 'Rolle:', + 'Project members' => 'Projektmitglieder', + '%s mentioned you in the task #%d' => '%s erwähnte dich in Aufgabe #%d', + '%s mentioned you in a comment on the task #%d' => '%s erwähnte dich in einem Kommentar zur Aufgabe #%d', + 'You were mentioned in the task #%d' => 'Du wurdest in der Aufgabe #%d erwähnt', + 'You were mentioned in a comment on the task #%d' => 'Du wurdest in einem Kommentar zur Aufgabe #%d erwähnt', + 'Estimated hours: ' => 'Erwarteter Zeitaufwand (Stunden): ', + 'Actual hours: ' => 'Tatsächlich aufgewendete Stunden: ', + 'Hours Spent' => 'Stunden aufgewendet', + 'Hours Estimated' => 'Stunden erwartet', + 'Estimated Time' => 'Erwartete Zeit', + 'Actual Time' => 'Aktuelle Zeit', + 'Estimated vs actual time' => 'Erwarteter vs. tatsächlicher Zeitaufwand', + 'RUB - Russian Ruble' => 'RUB - Russische Rubel', + 'Assign the task to the person who does the action when the column is changed' => 'Aufgabe der Person zuordnen, die die Aktion durchführt, wenn die Spalte geändert wird', + 'Close a task in a specific column' => 'Schließe eine Aufgabe in einer bestimmten Spalte', + 'Time-based One-time Password Algorithm' => 'Zeitbasierter Einmalpasswort Algorithmus', + 'Two-Factor Provider: ' => '2FA Anbieter: ', + 'Disable two-factor authentication' => 'Zwei-Faktor-Authentifizierung deaktivieren', + 'Enable two-factor authentication' => 'Zwei-Faktor-Authentifizierung aktivieren', + 'There is no integration registered at the moment.' => 'Derzeit ist kein externer Dienst registriert.', + 'Password Reset for Kanboard' => 'Zurücksetzen des Passwortes für Kanboard', + 'Forgot password?' => 'Passwort vergessen?', + 'Enable "Forget Password"' => 'Passwortrücksetzung aktivieren', + 'Password Reset' => 'Passwort zurücksetzen', + 'New password' => 'Neues Passwort', + 'Change Password' => 'Passwort ändern', + 'To reset your password click on this link:' => 'Bitte auf den Link klicken, um dein Passwort zurückzusetzen.', + 'Last Password Reset' => 'Verlauf der Passwortrücksetzung', + 'The password has never been reinitialized.' => 'Das Passwort wurde noch nie zurückgesetzt.', + 'Creation' => 'Erstellung', + 'Expiration' => 'Ablauf', + 'Password reset history' => 'Verlauf Passwortrücksetzung', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Alle Aufgaben der Spalte "%s" und der Swimlane "%s" wurden erfolgreich geschlossen', + 'Do you really want to close all tasks of this column?' => 'Willst du wirklich alle Aufgaben in dieser Spalte schließen?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d Aufgabe(n) in der Spalte "%s" und in der Swimlane "%s" werden geschlossen.', + 'Close all tasks in this column and this swimlane' => 'Alle Aufgaben in dieser Spalte schließen', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Kein Plugin hat eine Projekt-Benachrichtigungsmethode registriert. Du kannst individuelle Meldungen in deinem Benutzerprofil konfigurieren', + 'My dashboard' => 'Mein Dashboard', + 'My profile' => 'Mein Profil', + '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', + 'Personal 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', + 'Task priority' => 'Aufgaben-Priorität', + 'General' => 'Allgemein', + 'Dates' => 'Daten', + 'Default priority' => 'Standard-Priorität', + 'Lowest priority' => 'Niedrigste Priorität', + 'Highest priority' => 'Höchste Priorität', + 'Close a task when there is no activity' => 'Schließe eine Aufgabe, wenn keine Aktivitäten vorhanden sind', + 'Duration in days' => 'Dauer in Tagen', + 'Send email when there is no activity on a task' => 'Versende eine E-Mail, wenn keine Aktivitäten an einer Aufgabe vorhanden sind', + 'Unable to fetch link information.' => 'Kann keine Informationen über Verbindungen holen', + 'Daily background job for tasks' => 'Tägliche Hintergrundarbeit für Aufgaben', + 'Auto' => 'Auto', + 'Related' => 'Verbunden', + 'Attachment' => 'Anhang', + 'Web Link' => 'Weblink', + 'External links' => 'Externe Verbindungen', + 'Add external link' => 'Externe Verbindung hinzufügen', + 'Type' => 'Typ', + 'Dependency' => 'Abhängigkeit', + 'Add internal link' => 'Interne Verbindung hinzufügen', + '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...' => 'Kopiere deinen Link hierher...', + 'URL' => 'URL', + 'Internal links' => 'Interne Verbindungen', + 'Assign to me' => 'Mir zuweisen', + 'Me' => 'Mich', + 'Do not duplicate anything' => 'Nichts duplizieren', + 'Projects management' => 'Projektmanagement', + 'Users management' => 'Benutzermanagement', + 'Groups management' => 'Gruppenmanagement', + 'Create from another project' => 'Von einem anderen Projekt erstellen', + 'open' => 'offen', + 'closed' => 'geschlossen', + 'Priority:' => 'Priorität:', + 'Reference:' => 'Referenz:', + 'Complexity:' => 'Komplexität:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Spalte:', + 'Position:' => 'Position:', + 'Creator:' => 'Ersteller:', + 'Time estimated:' => 'Geschätzte Zeit:', + '%s hours' => '%s Stunden', + 'Time spent:' => 'Aufgewendete Zeit:', + 'Created:' => 'Erstellt:', + 'Modified:' => 'Geändert:', + 'Completed:' => 'Abgeschlossen:', + 'Started:' => 'Gestartet:', + 'Moved:' => 'Verschoben:', + 'Task #%d' => 'Aufgabe #%d', + 'Time format' => 'Zeitformat', + 'Start date: ' => 'Anfangsdatum: ', + 'End date: ' => 'Enddatum: ', + 'New due date: ' => 'Neues Fälligkeitsdatum: ', + 'Start date changed: ' => 'Anfangsdatum geändert: ', + 'Disable personal projects' => 'Persönliche Projekte deaktivieren', + 'Do you really want to remove this custom filter: "%s"?' => 'Willst du diesen benutzerdefinierten Filter wirklich entfernen: "%s"?', + 'Remove a custom filter' => 'Benutzerdefinierten Filter entfernen', + 'User activated successfully.' => 'Benutzer erfolgreich aktiviert.', + 'Unable to enable this user.' => 'Dieser Benutzer kann nicht aktiviert werden.', + '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.', + 'The maximum allowed file size is %sB.' => 'Die maximal erlaubte Dateigröße ist %sB.', + 'Drag and drop your files here' => 'Ziehe deine Dateien hier hin', + 'choose files' => 'Dateien auswählen', + 'View profile' => 'Profil ansehen', + 'Two Factor' => 'Zwei-Faktor', + 'Disable user' => 'Benutzer deaktivieren', + 'Do you really want to disable this user: "%s"?' => 'Willst du diesen Benutzer wirklich deaktivieren: "%s"?', + 'Enable user' => 'Benutzer aktivieren', + 'Do you really want to enable this user: "%s"?' => 'Willst du diesen Benutzer wirklich aktivieren: "%s"?', + 'Download' => 'Herunterladen', + 'Uploaded: %s' => 'Hochgeladen: %s', + 'Size: %s' => 'Größe: %s', + 'Uploaded by %s' => 'Hochgeladen von %s', + 'Filename' => 'Dateiname', + 'Size' => 'Größe', + 'Column created successfully.' => 'Spalte erfolgreich erstellt.', + 'Another column with the same name exists in the project' => 'Es gibt bereits eine Spalte mit demselben Namen im Projekt', + 'Default filters' => 'Standard-Filter', + 'Your board doesn\'t have any columns!' => 'Es gibt keine Spalten in diesem Projekt!', + 'Change column position' => 'Position der Spalte ändern', + 'Switch to the project overview' => 'Zur Projektübersicht wechseln', + 'User filters' => 'Benutzer-Filter', + 'Category filters' => 'Kategorie-Filter', + 'Upload a file' => 'Eine Datei hochladen', + 'View file' => 'Datei ansehen', + 'Last activity' => 'Letzte Aktivität', + 'Change subtask position' => 'Position der Teilaufgabe ändern', + 'This value must be greater than %d' => 'Dieser Wert muss größer als %d sein', + 'Another swimlane with the same name exists in the project' => 'Es gibt bereits eine Swimlane mit diesem Namen im Projekt', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Beispiel: https://example.kanboard.org/ (wird zum Erstellen absoluter URLs genutzt)', + 'Actions duplicated successfully.' => 'Aktionen erfolgreich dupliziert', + 'Unable to duplicate actions.' => 'Aktionen können nicht dupliziert werden.', + 'Add a new action' => 'Neue Aktion hinzufügen', + 'Import from another project' => 'Von einem anderen Projekt importieren', + 'There is no action at the moment.' => 'Es gibt zur Zeit keine Aktionen.', + 'Import actions from another project' => 'Aktionen von einem anderen Projekt importieren', + 'There is no available project.' => 'Es ist kein Projekt verfügbar.', + 'Local File' => 'Lokale Datei', + 'Configuration' => 'Konfiguration', + 'PHP version:' => 'PHP Version:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'OS Version:', + 'Database version:' => 'Datenbank Version:', + 'Browser:' => 'Browser:', + 'Task view' => 'Aufgaben Ansicht', + 'Edit task' => 'Aufgabe bearbeiten', + 'Edit description' => 'Beschreibung bearbeiten', + 'New internal link' => 'Neue interne Verbindung', + 'Display list of keyboard shortcuts' => 'Liste der Tastaturkürzel anzeigen', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Mein Avatar Bild hochladen', + 'Remove my image' => 'Mein Bild entfernen', + 'The OAuth2 state parameter is invalid' => 'Der OAuth2 Statusparameter ist ungültig', + 'User not found.' => 'Benutzer nicht gefunden', + 'Search in activity stream' => 'Im Aktivitätenstrom suchen', + 'My activities' => 'Meine Aktivitäten', + 'Activity until yesterday' => 'Aktivitäten bis gestern', + 'Activity until today' => 'Aktivitäten bis heute', + 'Search by creator: ' => 'nach Ersteller suchen:', + 'Search by creation date: ' => 'nach Datum suchen:', + 'Search by task status: ' => 'nach Aufgabenstatus suchen:', + 'Search by task title: ' => 'nach Titel suchen:', + 'Activity stream search' => 'Im Aktivitätenstrom suchen', + 'Projects where "%s" is manager' => 'Projekte in denen "%s" Manager ist', + 'Projects where "%s" is member' => 'Projekte in denen "%s" Mitglied ist', + 'Open tasks assigned to "%s"' => 'Offene Aufgaben, die "%s" zugeteilt sind', + 'Closed tasks assigned to "%s"' => 'Geschlossene Aufgaben, die "%s" zugeteilt sind', + 'Assign automatically a color based on a priority' => 'Eine Farbe basierend auf einer Priorität automatisch zuordnen', + 'Overdue tasks for the project(s) "%s"' => 'Überfällige Aufgaben des/der Projekt/e "%s"', + 'Upload files' => 'Dateien hochladen', + 'Installed Plugins' => 'Installierte Plugins', + 'Plugin Directory' => 'Plugin Verzeichnis', + 'Plugin installed successfully.' => 'Plugin erfolgreich installiert.', + 'Plugin updated successfully.' => 'Plugin erfolgreich aktualisiert.', + 'Plugin removed successfully.' => 'Plugin erfolgreich entfernt.', + 'Subtask converted to task successfully.' => 'Teilaufgabe erfolgreich in Aufgabe umgewandelt.', + 'Unable to convert the subtask.' => 'Teilaufgabe kann nicht umgewandelt werden.', + 'Unable to extract plugin archive.' => 'Plugin Archiv kann nicht entpackt werden.', + 'Plugin not found.' => 'Plugin nicht gefunden.', + 'You don\'t have the permission to remove this plugin.' => 'Du darfst dieses Plugin nicht entfernen.', + 'Unable to download plugin archive.' => 'Plugin Archiv kann nicht herunter geladen werden.', + 'Unable to write temporary file for plugin.' => 'Temporäre Dateien für das Plugin können nicht geschrieben werden.', + 'Unable to open plugin archive.' => 'Kann das Plugin Archiv nicht öffnen.', + 'There is no file in the plugin archive.' => 'Es gibt keine Datei im Plugin Archiv.', + 'Create tasks in bulk' => 'Viele Aufgaben auf einmal erstellen', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Deine Kanboard Installation ist nicht dafür konfiguriert, Plugins mit dem Benutzerinterface zu installieren.', + 'There is no plugin available.' => 'Es gibt kein Plugin.', + 'Install' => 'Installieren', + 'Update' => 'Aktualisieren', + 'Up to date' => 'Aktuell', + 'Not available' => 'Nicht verfügbar', + 'Remove plugin' => 'Plugin entfernen', + 'Do you really want to remove this plugin: "%s"?' => 'Willst du das Plugin "%s" wirklich entfernen?', + 'Uninstall' => 'Deinstallieren', + 'Listing' => 'Auflistung', + 'Metadata' => 'Metadaten', + 'Manage projects' => 'Projekte verwalten', + 'Convert to task' => 'In Aufgabe umwandeln', + 'Convert sub-task to task' => 'Teilaufgabe in Aufgabe umwandeln', + 'Do you really want to convert this sub-task to a task?' => 'Willst du diese Teilaufgabe wirklich in eine Aufgabe umwandeln?', + 'My task title' => 'Mein Aufgabentitel', + 'Enter one task by line.' => 'Gib eine Aufgabe pro Zeile ein.', + 'Number of failed login:' => 'Anzahl fehlgeschlagener Anmeldungen:', + 'Account locked until:' => 'Konto gesperrt bis:', + 'Email settings' => 'E-Mail Einstellungen', + 'Email sender address' => 'E-Mail Absender Adresse', + 'Email transport' => 'E-Mail Verkehr', + 'Webhook token' => 'Webhook Token', + 'Project tags management' => 'Projektbezogenes Schlagwort-Management', + 'Tag created successfully.' => 'Schlagwort erfolgreich erstellt.', + 'Unable to create this tag.' => 'Das Schlagwort kann nicht erstellt werden.', + 'Tag updated successfully.' => 'Schlagwort erfolgreich aktualisiert.', + 'Unable to update this tag.' => 'Das Schlagwort kann nicht aktualisiert werden.', + 'Tag removed successfully.' => 'Schlagwort erfolgreich entfernt.', + 'Unable to remove this tag.' => 'Das Schlagwort kann nicht entfernt werden.', + 'Global tags management' => 'Globales Schlagwort-Management', + 'Tags' => 'Schlagworte', + 'Tags management' => 'Schlagwort-Management', + 'Add new tag' => 'Neues Schlagwort hinzufügen', + 'Edit a tag' => 'Schlagwort bearbeiten', + 'Project tags' => 'Projektbezogene Schlagwörter', + 'There is no specific tag for this project at the moment.' => 'Es gibt zur Zeit kein spezifisches Schlagwort.', + 'Tag' => 'Schlagwort', + 'Remove a tag' => 'Schlagwort entfernen', + 'Do you really want to remove this tag: "%s"?' => 'Soll dieses Schlagwort wirklich entfernt werden: "%s"?', + 'Global tags' => 'Globale Schlagwörter', + 'There is no global tag at the moment.' => 'Es gibt zur Zeit kein globales Schlagwort', + 'This field cannot be empty' => 'Dieses Feld kann nicht leer sein', + 'Close a task when there is no activity in a 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 has 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', + '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 schließen, wenn sie im angegebenen Zeitraum nicht verschoben wurde', + 'Edit columns' => 'Spalten bearbeiten', + 'The column restriction has been created successfully.' => 'Die Spalteneinschränkung wurde erfolgreich erstellt.', + 'Unable to create this column restriction.' => 'Erstellen der Spalteneinschränkung fehlgeschlagen.', + 'Column restriction removed successfully.' => ' Spalteneinschränkung erfolgreich entfernt.', + 'Unable to remove this restriction.' => 'Entfernen der Spalteneinschrä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.' => 'Du kannst 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 Schließen 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 Schließen 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 Schließen 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"?' => 'Willst du diese Spalteneinschrä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?' => 'Willst du diese Spalteneinschrä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.' => 'Willst du 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"?' => 'Willst du 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.' => 'Du hast nicht die Berechtigung, diese Aufgabe zu verschieben.', + 'API User Access' => 'API Benutzerzugriff', + 'Preview' => 'Vorschau', + 'Write' => 'Schreiben', + 'Write your text in Markdown' => 'Schreibe deinen Text in Markdown', + 'No personal API access token registered.' => 'Keine persönlichen API-Zugriffsinformationen registriert', + 'Your personal API access token is "%s"' => 'Deine persönlichen API-Zugriffsinformationen: "%s"', + 'Remove your token' => 'Deine Zugriffsinformationen entfernen', + 'Generate a new token' => 'Neue Zugriffsinformationen generieren', + 'Showing %d-%d of %d' => 'Zeige %d-%d von %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ähle 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.' => 'Du wurdest eingeladen, dich auf Kanboard zu registrieren.', + 'Click here to join your team' => 'Klicke hier, um deinem Team beizutreten', + 'Invite people' => 'Leute einladen', + 'Emails' => 'E-Mail', + 'Enter one email address by line.' => 'Gib 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 Person diesem Projekt hinzu', + 'Sign-up' => 'Anmelden', + 'Credentials' => 'Anmeldeinformationen', + 'New user' => 'Neuer Benutzer', + 'This username is already taken' => 'Dieser Benutzername ist bereits vergeben', + 'Your profile must have a valid email address.' => 'Dein Profil muss eine gültige E-Mail-Adresse haben.', + 'TRL - Turkish Lira' => 'TRL - Türkische Lira', + 'The project email is optional and could be used by several plugins.' => 'Die Projekt-E-Mail ist optional und kann von mehreren Plugins verwendet werden.', + 'The project email must be unique across all projects' => 'Die Projekt-E-Mail muss für alle Projekte eindeutig sein', + 'The email configuration has been disabled by the administrator.' => 'Die E-Mail-Konfiguration wurde vom Administrator deaktiviert.', + 'Close this project' => 'Dieses Projekt schließen', + 'Open this project' => 'Dieses Projekt öffnen', + 'Close a project' => 'Ein Projekt schließen', + 'Do you really want to close this project: "%s"?' => 'Möchtest du dieses Projekt wirklich schließen: "%s"?', + 'Reopen a project' => 'Ein Projekt wieder öffnen', + 'Do you really want to reopen this project: "%s"?' => 'Möchtest du dieses Projekt wirklich wieder öffnen: "%s"?', + 'This project is open' => 'Dieses Projekt ist offen', + 'This project is closed' => 'Dieses Projekt ist geschlossen', + 'Unable to upload files, check the permissions of your data folder.' => 'Dateien können nicht hochgeladen werden, überprüfe die Berechtigungen deines Datenordners.', + 'Another category with the same name exists in this project' => 'Eine weitere Kategorie mit demselben Namen existiert in diesem Projekt', + 'Comment sent by email successfully.' => 'Kommentar wurde erfolgreich per E-Mail gesendet.', + 'Sent by email to "%s" (%s)' => 'Wurde per E-Mail an [%s] gesendet "%s"', + 'Unable to read uploaded file.' => 'Die hochgeladene Datei konnte nicht gelesen werden.', + 'Database uploaded successfully.' => 'Die Datenbank wurde erfolgreich hochgeladen.', + 'Task sent by email successfully.' => 'Aufgabe wurde erfolgreich per E-Mail gesendet.', + 'There is no category in this project.' => 'Es gibt keine Kategorie in diesem Projekt', + 'Send by email' => 'Per E-Mail senden', + 'Create and send a comment by email' => 'Erstelle und sende einen Kommentar per E-Mail', + 'Subject' => 'Betreff', + 'Upload the database' => 'Datenbank hochladen', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Du kannst die zuvor heruntergeladene SQLite-Datenbank (Gzip-Format) hochladen.', + 'Database file' => 'Datenbankdatei', + 'Upload' => 'Hochladen', + 'Your project must have at least one active swimlane.' => 'Dein Projekt muss mindestens eine aktive Swimlane haben.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatische Aktion nicht gefunden: "%s"', + '%d projects' => '%d Projekte', + '%d project' => '%d Projekt', + 'There is no project.' => 'Es gibt kein Projekt.', + 'Sort' => 'Sortieren', + 'Project ID' => 'Projekt-ID', + 'Project name' => 'Projekt Name', + 'Public' => 'Öffentlich', + 'Personal' => 'Persönlich', + '%d tasks' => '%d Aufgaben', + '%d task' => '%d Aufgabe', + 'Task ID' => 'Aufgaben-ID', + 'Assign automatically a color when due date is expired' => 'Automatisch eine Farbe zuweisen, wenn das Fälligkeitsdatum abgelaufen ist', + 'Total score in this column across all swimlanes' => 'Gesamtpunktzahl in dieser Spalte über alle Swimlanes', + 'HRK - Kuna' => 'HRK - Kroatische Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentinische Peso', + 'COP - Colombian Peso' => 'COP - Kolumbianische Peso', + '%d groups' => '%d Gruppen', + '%d group' => '%d Gruppe', + 'Group ID' => 'Gruppen-ID', + 'External ID' => 'Externe ID', + '%d users' => '%d Benutzer', + '%d user' => '%d Benutzer', + 'Hide subtasks' => 'Teilaufgaben verstecken', + 'Show subtasks' => 'Teilaufgaben anzeigen', + 'Authentication Parameters' => 'Authentifizierungsparameter', + 'API Access' => 'API-Zugriff', + 'No users found.' => 'Keine Benutzer gefunden.', + 'User ID' => 'Benutzer-ID', + 'Notifications are activated' => 'Benachrichtigungen sind aktiviert', + 'Notifications are disabled' => 'Benachrichtigungen sind deaktiviert', + 'User disabled' => 'Benutzer deaktiviert', + '%d notifications' => '%d Benachrichtigungen', + '%d notification' => '%d Benachrichtigung', + 'There is no external integration installed.' => 'Es ist keine externe Integration installiert.', + 'You are not allowed to update tasks assigned to someone else.' => 'Du bist nicht berechtigt, Aufgaben zu aktualisieren, die jemand anderem zugewiesen wurden.', + 'You are not allowed to change the assignee.' => 'Du darfst den Zuständigen nicht ändern.', + 'Task suppression is not permitted' => 'Entfernen von Aufgaben ist nicht erlaubt.', + 'Changing assignee is not permitted' => 'Änderung des Zuständigen ist nicht zulässig', + 'Update only assigned tasks is permitted' => 'Nur zugeordnete Aufgaben dürfen aktualisiert werden', + 'Only for tasks assigned to the current user' => 'Nur für Aufgaben, die dem aktuellen Benutzer zugeordnet sind', + 'My projects' => 'Meine Projekte', + 'You are not a member of any project.' => 'Du bist nicht Mitglied eines Projektes.', + 'My subtasks' => 'Meine Teilaufgaben', + '%d subtasks' => '%d Teilaufgaben', + '%d subtask' => '%d Teilaufgabe', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Das Bewegen einer Aufgabe zwischen diesen Spalten ist nur für Aufgaben zulässig, die dem aktuellen Benutzer zugewiesen sind', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Dänische Kronen', + 'Remove user from group' => 'Benutzer aus Gruppe löschen', + 'Assign the task to its creator' => 'Aufgabe dem Ersteller zuordnen', + 'This task was sent by email to "%s" with subject "%s".' => 'Diese Aufgabe wurde per Mail an "%s" mit dem Betreff "%s" gesendet.', + 'Predefined Email Subjects' => 'Vordefinierte E-Mail Betreffzeilen', + 'Write one subject by line.' => 'Schreibe ein Betreff pro Zeile.', + 'Create another link' => 'Einen weiteren Link erstellen', + 'BRL - Brazilian Real' => 'BRL - Brasilianische Real', + 'Add a new Kanboard task' => 'Eine neue Kanboard Aufgabe hinzufügen', + 'Subtask not started' => 'Teilaufgabe nicht gestartet', + 'Subtask currently in progress' => 'Teilaufgabe aktuell in Bearbeitung', + 'Subtask completed' => 'Teilaufgabe abgeschlossen', + 'Subtask added successfully.' => 'Teilaufgabe erfolgreich hinzugefügt.', + '%d subtasks added successfully.' => '%d Teilaufgaben erfolgreich hinzugefügt.', + 'Enter one subtask by line.' => 'Gib eine Teilaufgabe pro Zeile ein.', + 'Predefined Contents' => 'Vordefinierte Inhalte', + 'Predefined contents' => 'Vordefinierte Inhalte', + 'Predefined Task Description' => 'Vordefinierte Aufgabenbeschreibung', + 'Do you really want to remove this template? "%s"' => 'Willst du diese Vorlage wirklich löschen? "%s"', + 'Add predefined task description' => 'Vordefinierte Aufgabenbeschreibung hinzufügen', + 'Predefined Task Descriptions' => 'Vordefinierte Aufgabenbeschreibungen', + 'Template created successfully.' => 'Vorlage erfolgreich erstellt.', + 'Unable to create this template.' => 'Erstellen der Vorlage nicht möglich.', + 'Template updated successfully.' => 'Vorlage erfolgreich geändert.', + 'Unable to update this template.' => 'Aktualisierung der Vorlage nicht möglich.', + 'Template removed successfully.' => 'Vorlage erfolgreich gelöscht.', + 'Unable to remove this template.' => 'Löschen der Vorlage nicht möglich.', + 'Template for the task description' => 'Vorlage für die Aufgabenbeschreibung', + 'The start date is greater than the end date' => 'Das Startdatum ist größer als das Enddatum', + 'Tags must be separated by a comma' => 'Schlagworte müssen per Komma getrennt werden', + 'Only the task title is required' => 'Nur der Aufgaben-Titel wird benötigt', + 'Creator Username' => 'Ersteller Benutzername', + 'Color Name' => 'Farbname', + 'Column Name' => 'Spaltenname', + 'Swimlane Name' => 'Swimlane-Name', + 'Time Estimated' => 'geschätzte Zeit', + 'Time Spent' => 'Zeitaufwand', + 'External Link' => 'externer Link', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Diese Funktion aktiviert den iCal Feed, RSS Feed und die öffentliche Board-Ansicht', + 'Stop the timer of all subtasks when moving a task to another column' => 'Beenden des Timers für alle Unteraufgaben, wenn die Aufgabe in eine andere Spalte verschoben wird', + 'Subtask Title' => 'Titel der Teilaufgabe', + 'Add a subtask and activate the timer when moving a task to another column' => 'Teilaufgabe hinzufügen und den Timer aktivieren, wenn die Aufgabe in eine andere Spalte verschoben wird', + 'days' => 'Tage', + 'minutes' => 'Minuten', + 'seconds' => 'Sekunden', + 'Assign automatically a color when preset start date is reached' => 'Automatisch eine Farbe zuweisen, wenn das Startdatum erreicht ist', + 'Move the task to another column once a predefined start date is reached' => 'Verschieben des Tasks in eine andere Spalte, wenn das definierte Startdatum erreicht ist', + 'This task is now linked to the task %s with the relation "%s"' => 'Diese Aufgabe ist jetzt verknüpft mit der Aufgabe %s mit der Relation "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Die Verknüpfung mit der Relation "%s" zur Aufgabe %s wurde entfernt', + 'Custom Filter:' => 'Benutzerdefinierter Filter:', + 'Unable to find this group.' => 'Diese Gruppe konnte nicht gefunden werden', + '%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 auf die Position %d in der Spalte "%s" verschoben', + '%s moved the task #%d to the swimlane "%s"' => '%s hat die Aufgabe #%d in die Swimlane "%s" verschoben', + '%sh spent' => '%sh aufgewendet', + '%sh estimated' => '%sh angesetzt', + 'Select All' => 'Alle auswählen', + 'Unselect All' => 'Keine auswählen', + 'Apply action' => 'Aktion anwenden', + 'Move selected tasks to another column or swimlane' => 'Ausgewählte Aufgaben in andere Spalte verschieben', + 'Edit tasks in bulk' => 'Massenbearbeitung', + 'Choose the properties that you would like to change for the selected tasks.' => 'Wähle die Eigenschaften aus, die du für die ausgewählten Aufgaben ändern möchtest.', + 'Configure this project' => 'Projekteinstellungen', + 'Start now' => 'Jetzt starten', + '%s removed a file from the task #%d' => '%s hat eine Datei aus der Aufgabe #%d entfernt.', + 'Attachment removed from task #%d: %s' => 'Anhang aus Aufgabe #%d entfernt: %s', + 'No color' => 'Keine Farbe', + 'Attachment removed "%s"' => 'Anhang entfernt "%s"', + '%s removed a file from the task %s' => '%s hat eine Datei aus der Aufgabe entfernt %s', + 'Move the task to another swimlane when assigned to a user' => 'Verschieben der Aufgabe in eine andere Swimlane, wenn sie einem Benutzer zugewiesen wird', + 'Destination swimlane' => 'Ziel Swimlane', + 'Assign a category when the task is moved to a specific swimlane' => 'Kategorie zuweisen, wenn Aufgabe in eine bestimmte Swimlane verschoben wird', + 'Move the task to another swimlane when the category is changed' => 'Verschiebe die Aufgabe in eine andere Swimlane, wenn die Kategorie geändert wird', + 'Reorder this column by priority (ASC)' => 'Spalte nach Priorität ordnen (aufsteigend)', + 'Reorder this column by priority (DESC)' => 'Spalte nach Priorität ordnen (absteigend)', + 'Reorder this column by assignee and priority (ASC)' => 'Spalte nach Zuständigem und Priorität ordnen (aufsteigend)', + 'Reorder this column by assignee and priority (DESC)' => 'Spalte nach Zuständigem und Priorität ordnen (absteigend)', + 'Reorder this column by assignee (A-Z)' => 'Spalte nach Zuständigem ordnen (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Spalte nach Zuständigem ordnen (Z-A)', + 'Reorder this column by due date (ASC)' => 'Spalte nach Fälligkeitsdatum ordnen (aufsteigend)', + 'Reorder this column by due date (DESC)' => 'Spalte nach Fälligkeitsdatum ordnen (absteigend)', + 'Reorder this column by id (ASC)' => 'Ordne diese Spalte nach ID neu (aufsteigend)', + 'Reorder this column by id (DESC)' => 'Ordne diese Spalte nach ID neu (absteigend)', + '%s moved the task #%d "%s" to the project "%s"' => '%s hat die Aufgabe #%d "%s" in das Projekt "%s" verschoben', + 'Task #%d "%s" has been moved to the project "%s"' => 'Aufgabe #%d "%s" wurde in das Projekt "%s" verschoben', + 'Move the task to another column when the due date is less than a certain number of days' => 'Verschieben der Aufgabe in eine andere Spalte, wenn die Fälligkeit kleiner als eine bestimmte Anzahl von Tagen ist', + 'Automatically update the start date when the task is moved away from a specific column' => 'Aktualisiert automatisch das Startdatum, wenn die Aufgabe aus einer bestimmten Spalte genommen wird', + 'HTTP Client:' => 'HTTP-Client:', + 'Assigned' => 'Zugeordnet', + 'Task limits apply to each swimlane individually' => 'Aufgabenlimit gilt pro Swimlane', + 'Column task limits apply to each swimlane individually' => 'Spaltenaufgabenlimit für jede Swimlane einzeln anwenden', + 'Column task limits are applied to each swimlane individually' => 'Spaltenaufgabenlimit wird für jede Swimlane einzeln angewendet', + 'Column task limits are applied across swimlanes' => 'Spaltenaufgabenlimit wird swimlaneübergreifend angewendet', + 'Task limit: ' => 'Aufgabenlimit', + 'Change to global tag' => 'Zu globalem Schlagwort machen', + 'Do you really want to make the tag "%s" global?' => 'Das Schlagwort "%s" wirklich global machen?', + 'Enable global tags for this project' => 'Globale Schlagworte für dieses Projekt aktivieren', + 'Group membership(s):' => 'Gruppen-Mitgliedschaft(en):', + '%s is a member of the following group(s): %s' => '%s ist Mitglied in der/den folgenden Gruppe(n): %s', + '%d/%d group(s) shown' => '%d/%d Gruppe(n) angezeigt', + 'Subtask creation or modification' => 'Teilaufgabe erstellen oder ändern', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Aufgabe einem bestimmten Nutzer zuordnen, wenn die Aufgabe in eine bestimmte Swimlane verschoben wird', + 'Comment' => 'Kommentar', + 'Collapse vertically' => 'Vertikal zuklappen', + 'Expand vertically' => 'Vertikal aufklappen', + 'MXN - Mexican Peso' => 'MXN - Mexikanischer Peso', + 'Estimated vs actual time per column' => 'Geschätzte vs. tatsächliche Zeit pro Spalte', + 'HUF - Hungarian Forint' => 'HUF - Ungarischer Forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Du musst eine Datei auswählen, die als Avatar hochgeladen werden soll!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Die hochgeladene Datei ist kein gültiges Bild! (Nur *.gif, *.jpg, *.jpeg and *.png sind erlaubt!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Automatisches Setzen des Fälligkeitsdatums, wenn die Aufgabe aus einer bestimmten Spalte heraus verschoben wird', + 'No other projects found.' => 'Keine weiteren Projekte vorhanden.', + 'Tasks copied successfully.' => 'Die Aufgaben wurden erfolgreich kopiert.', + 'Unable to copy tasks.' => 'Die Aufgaben konnten nicht kopiert werden.', + 'Theme' => 'Thema', + 'Theme:' => 'Thema:', + 'Light theme' => 'Helles Thema', + 'Dark theme' => 'Dunkles Thema', + 'Automatic theme - Sync with system' => 'Automatisches Thema - Mit System synchronisieren', + 'Application managers or more' => 'Anwendungsmanager oder mehr', + 'Administrators' => 'Administratoren', + 'Visibility:' => 'Sichtbarkeit:', + 'Standard users' => 'Standardbenutzer', + 'Visibility is required' => 'Sichtbarkeit ist erforderlich', + 'The visibility should be an app role' => 'Die Sichtbarkeit sollte eine App-Rolle sein', + 'Reply' => 'Antworten', + '%s wrote: ' => '%s schrieb:', + 'Number of visible tasks in this column and swimlane' => 'Anzahl der sichtbaren Aufgaben in dieser Spalte und Lane', + 'Number of tasks in this swimlane' => 'Anzahl der Aufgaben in dieser Lane', + 'Unable to find another subtask in progress, you can close this window.' => 'Es konnte kein weiterer Teilaufgabe in Bearbeitung gefunden werden, du kannst dieses Fenster schließen.', + 'This theme is invalid' => 'Dieses Thema ist ungültig', + 'This role is invalid' => 'Diese Rolle ist ungültig', + 'This timezone is invalid' => 'Diese Zeitzone ist ungültig', + 'This language is invalid' => 'Diese Sprache ist ungültig', + 'This URL is invalid' => 'Diese URL ist ungültig', + 'Date format invalid' => 'Ungültiges Datumsformat', + 'Time format invalid' => 'Ungültiges Zeitformat', + 'Invalid Mail transport' => 'Ungültiger Mail-Transport', + 'Color invalid' => 'Farbe ungültig', + 'This value must be greater or equal to %d' => 'Dieser Wert muss größer oder gleich %d sein', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Füge eine BOM am Anfang der Datei hinzu (für Microsoft Excel erforderlich)', + 'Just add these tag(s)' => 'Schlagwort(e) nur hinzufügen (bereits zugeordnete nicht löschen)', + 'Remove internal link(s)' => 'Interne Verbindung(en) entfernen', + 'Import tasks from another project' => 'Aufgaben aus einem anderen Projekt importieren', + 'Select the project to copy tasks from' => 'Wähle das Projekt aus, aus dem Aufgaben kopiert werden sollen', + 'The total maximum allowed attachments size is %sB.' => 'Die maximal zulässige Gesamtgröße für Anhänge beträgt %sB.', + 'Add attachments' => 'Anhänge hinzufügen', + 'Task #%d "%s" is overdue' => 'Aufgabe #%d "%s" ist überfällig', + 'Enable notifications by default for all new users' => 'Aktiviere Benachrichtigungen standardmäßig für alle neuen Benutzer', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Aufgabe dem Ersteller zuweisen, wenn sie in einer ausgewählten Spalte ohne Bearbeiter ist.', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Aufgabe dem angemeldeten Nutzer bei Spaltenänderung zuweisen, wenn eine Aufgabe in die ausgewählte Spalte verschoben wird.', +]; diff --git a/app/Locale/el_GR/translations.php b/app/Locale/el_GR/translations.php new file mode 100644 index 0000000..5b23996 --- /dev/null +++ b/app/Locale/el_GR/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Κανένα', + 'Edit' => 'ΕπεξεÏγασία', + 'Remove' => 'ΑφαίÏεση', + 'Yes' => 'Îαι', + 'No' => 'Όχι', + 'cancel' => 'ακÏÏωση', + 'or' => 'ή', + 'Yellow' => 'ΚίτÏινο', + 'Blue' => 'Μπλε', + 'Green' => 'ΠÏάσινο', + 'Purple' => 'Βιολετί', + 'Red' => 'Κόκκινο', + 'Orange' => 'ΠοÏτοκαλί', + 'Grey' => 'ΓκÏίζο', + 'Brown' => 'Καφέ', + 'Deep Orange' => 'Î’Î±Î¸Ï Ï€Î¿Ïτοκαλί', + 'Dark Grey' => 'Î’Î±Î¸Ï Î³ÎºÏί', + 'Pink' => 'Ροζ', + 'Teal' => 'ΤυÏκουάζ', + 'Cyan' => 'Γαλάζιο', + 'Lime' => 'Λεμονί', + 'Light Green' => 'Ανοιχτό Ï€Ïάσινο', + 'Amber' => 'ΚεχÏιμπαÏί', + 'Save' => 'Αποθήκευση', + 'Login' => 'Είσοδος', + 'Official website:' => 'Επίσημη ιστοσελίδα:', + 'Unassigned' => 'ΧωÏίς ανάθεση', + 'View this task' => 'ΠÏοβολή της εÏγασίας', + 'Remove user' => 'ΑφαίÏεση χÏήστη', + 'Do you really want to remove this user: "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση του χÏήστη: «%s»;', + 'All users' => 'Όλοι οι χÏήστες', + 'Username' => 'Όνομα χÏήστη', + 'Password' => 'Κωδικός Ï€Ïόσβασης', + 'Administrator' => 'ΔιαχειÏιστής', + 'Sign in' => 'Είσοδος', + 'Users' => 'ΧÏήστες', + 'Forbidden' => 'Δεν επιτÏέπεται η Ï€Ïόσβαση', + 'Access Forbidden' => 'Δεν επιτÏέπεται η Ï€Ïόσβαση', + 'Edit user' => 'ΕπεξεÏγασία χÏήστη', + 'Logout' => 'ΑποσÏνδεση', + 'Bad username or password' => 'Λάθος όνομα χÏήστη ή κωδικός Ï€Ïόσβασης', + 'Edit project' => 'ΕπεξεÏγασία έÏγου', + 'Name' => 'Όνομα', + 'Projects' => 'ΈÏγα', + 'No project' => 'Δεν υπάÏχουν μέλη για το έÏγο', + 'Project' => 'ΈÏγο', + 'Status' => 'Κατάσταση', + 'Tasks' => 'ΕÏγασίες', + 'Board' => 'ΚεντÏικό ταμπλό', + 'Actions' => 'ΕνέÏγειες', + 'Inactive' => 'ΑνενεÏγός', + 'Active' => 'ΕνεÏγός', + 'Unable to update this board.' => 'Δεν ήταν δυνατή η ενημέÏωση Î±Ï…Ï„Î¿Ï Ï„Î¿Ï… πίνακα.', + 'Disable' => 'ΑπενεÏγοποίηση', + 'Enable' => 'ΕνεÏγοποίηση', + 'New project' => 'Îέο έÏγο', + 'Do you really want to remove this project: "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση του έÏγου: «%s»;', + 'Remove project' => 'ΑφαίÏεση του έÏγου', + 'Edit the board for "%s"' => 'ΕπεξεÏγασία του πίνακα για τον/την «%s»', + 'Add a new column' => 'ΠÏοσθήκη στήλης', + 'Title' => 'Τίτλος', + 'Assigned to %s' => 'Ανατεθειμένο στον/στην %s', + 'Remove a column' => 'ΑφαίÏεση στήλης', + 'Unable to remove this column.' => 'Δεν ήταν δυνατή η αφαίÏεση της στήλης.', + 'Do you really want to remove this column: "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση της στήλης: «%s»;', + 'Settings' => 'ΠÏοτιμήσεις', + 'Application settings' => 'Ρυθμίσεις εφαÏμογής', + 'Language' => 'Γλώσσα', + 'Webhook token:' => 'ΔιακÏιτικό ασφαλείας (token) webhooks:', + 'API token:' => 'ΔιακÏιτικό ασφαλείας (token) API:', + 'Database size:' => 'Μέγεθος βάσης δεδομένων:', + 'Download the database' => 'Κατέβασμα της βάσης δεδομένων', + 'Optimize the database' => 'Βελτιστοποίηση της βάσης δεδομένων', + '(VACUUM command)' => '(Εντολή VACUUM)', + '(Gzip compressed Sqlite file)' => '(Συμπιεσμένο κατά Gzip αÏχείο Sqlite)', + 'Close a task' => 'Κλείσιμο εÏγασίας', + 'Column' => 'Στήλη', + 'Color' => 'ΧÏώμα', + 'Assignee' => 'Ανατιθέμενος', + 'Create another task' => 'ΔημιουÏγία άλλης εÏγασίας', + 'New task' => 'Îέα εÏγασία', + 'Open a task' => 'Άνοιγμα εÏγασίας', + 'Do you really want to open this task: "%s"?' => 'Είστε σίγουÏοι για το άνοιγμα της εÏγασίας: «%s»;', + 'Back to the board' => 'ΕπιστÏοφή στον κεντÏικό πίνακα έÏγου', + 'There is nobody assigned' => 'Δεν έχει ανατεθεί σε κάποιον', + 'Column on the board:' => 'Στήλη στον κεντÏικό πίνακα:', + 'Close this 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 χαÏακτήÏες', + 'The password is required' => 'Απαιτείται ο κωδικός Ï€Ïόσβασης', + 'This value must be an integer' => 'Η τιμή Ï€Ïέπει να είναι ακέÏαιος', + 'The username must be unique' => 'Το όνομα χÏήστη Ï€Ïέπει να είναι μοναδικό', + 'The user id is required' => 'Απαιτείται το αναγνωÏιστικό χÏήστη', + 'Passwords don\'t match' => 'Οι κωδικοί Ï€Ïόσβασης δεν ταιÏιάζουν', + 'The confirmation is required' => 'Απαιτείται η επιβεβαίωση', + 'The project is required' => 'Απαιτείται το έÏγο', + 'The id is required' => 'Απαιτείται το αναγνωÏιστικό', + 'The project id is required' => 'Απαιτείται το αναγνωÏιστικό έÏγου', + 'The project name is required' => 'Απαιτείται η ονομασία έÏγου', + 'The title is required' => 'Απαιτείται ο τίτλος', + 'Settings saved successfully.' => 'Οι Ï€Ïοτιμήσεις αποθηκεÏθηκαν με επιτυχία.', + 'Unable to save your settings.' => 'Δεν ήταν δυνατή ή αποθήκευση των Ï€Ïοτιμήσεων.', + 'Database optimization done.' => 'Η βελτιστοποίηση της βάσης δεδομένων έγινε με επιτυχία.', + 'Your project has been created successfully.' => 'Το έÏγο δημιουÏγήθηκε.', + 'Unable to create your project.' => 'Δεν ήταν δυνατή η δημιουÏγία του έÏγου', + 'Project updated successfully.' => 'Το έÏγο ενημεÏώθηκε με επιτυχία.', + 'Unable to update this project.' => 'Δεν ήταν δυνατή η ενημέÏωση του έÏγου.', + 'Unable to remove this project.' => 'Δεν ήταν δυνατή η διαγÏαφή του έÏγου', + 'Project removed successfully.' => 'Το έÏγο αφαιÏέθηκε με επιτυχία.', + 'Project activated successfully.' => 'Το έÏγο ενεÏγοποιήθηκε με επιτυχία', + 'Unable to activate this project.' => 'Δεν ήταν δυνατή η ενεÏγοποίηση του έÏγου', + 'Project disabled successfully.' => 'Το έÏγο ενεÏγοποιήθηκε με επιτυχία', + 'Unable to disable this project.' => 'Δεν ήταν δυνατή η επενεÏγοποίηση του έÏγου.', + 'Unable to open this task.' => 'Δεν είναι δυνατό το άνοιγμα της εÏγασίας', + 'Task opened successfully.' => 'Η εÏγασία άνοιξε με επιτυχία', + 'Unable to close this task.' => 'Δεν είναι δυνατό το κλείσιμο της εÏγασίας', + 'Task closed successfully.' => 'Η εÏγασία έκλεισε με επιτυχία', + 'Unable to update your task.' => 'Δεν ήταν δυνατή η ενημέÏωση της εÏγασίας', + 'Task updated successfully.' => 'Η εÏγασία ενημεÏώθηκε με επιτυχία', + 'Unable to create your task.' => 'Δεν ήταν δυνατή η δημιουÏγία της εÏγασίας', + 'Task created successfully.' => 'Η εÏγασία δημιουÏγήθηκε με επιτυχία', + 'User created successfully.' => 'Ο χÏήστης δημιουÏγήθηκε με επιτυχία', + 'Unable to create your user.' => 'Δεν ήταν δυνατή η δημιουÏγία χÏήστη', + 'User updated successfully.' => 'Ο χÏήστης ενημεÏώθηκε με επιτυχία', + 'User removed successfully.' => 'Ο χÏήστης αφαιÏέθηκε με επιτυχία.', + 'Unable to remove this user.' => 'Δεν ήταν δυνατή η αφαίÏεση χÏήστη.', + 'Board updated successfully.' => 'Ο πίνακας ενημεÏώθηκε με επιτυχία.', + 'Ready' => 'Έτοιμα', + 'Backlog' => 'Σε αναμονή', + 'Work in progress' => 'ΕÏγασία σε εξέλιξη', + 'Done' => 'ΟλοκληÏωμένα', + 'Application version:' => 'Έκδοση εφαÏμογής:', + 'Id' => 'ΑναγνωÏιστικό', + 'Public link' => 'Δημόσιος σÏνδεσμος', + 'Timezone' => 'ÎÏα ζώνης', + 'Sorry, I didn\'t find this information in my database!' => 'Δυστυχώς δε βÏέθηκε αυτή η πληÏοφοÏία στη βάση δεδομένων!', + 'Page not found' => 'Δε βÏέθηκε η σελίδα', + 'Complexity' => 'Πολυπλοκότητα', + 'Task limit' => 'ÎŒÏιο εÏγασιών', + 'Task count' => 'ΑÏίθμηση εÏγασιών', + 'User' => 'ΧÏήστης', + 'Comments' => 'Σχόλια', + 'Comment is required' => 'Απαιτείται το σχόλιο', + 'Comment added successfully.' => 'Το σχόλιο Ï€Ïοστέθηκε με επιτυχία.', + 'Unable to create your comment.' => 'Δεν ήταν δυνατή η Ï€Ïοσθήκη του σχολίου σας.', + 'Due Date' => 'ΗμεÏομηνία Ï€Ïοθεσμίας', + 'Invalid date' => 'Μη έγκυÏη ημεÏομηνία', + 'Automatic actions' => 'Αυτόματες ενέÏγειες', + 'Your automatic action has been created successfully.' => 'Η αυτόματη ενέÏγειά σας δημιουÏγήθηκε με επιτυχία.', + 'Unable to create your automatic action.' => 'Δεν ήταν δυνατή η δημιουÏγία της αυτόματης ενέÏγειάς σας.', + 'Remove an action' => 'ΑφαίÏεση ενέÏγειας', + 'Unable to remove this action.' => 'Δεν ήταν δυνατή η αφαίÏεση αυτής της ενέÏγειας.', + 'Action removed successfully.' => 'Η ενέÏγεια αφαιÏέθηκε με επιτυχία.', + 'Automatic actions for the project "%s"' => 'Αυτόματες ενέÏγειες για το έÏγο «%s»', + 'Add an action' => 'ΠÏοσθήκη ενέÏγειας', + 'Event name' => 'Ονομασία συμβάντος', + 'Action' => 'ΕνέÏγεια', + 'Event' => 'Συμβάν', + 'When the selected event occurs execute the corresponding action.' => 'Όταν εμφανίζεται το επιλεγμένο συμβάν να εκτελείται η αντίστοιχη ενέÏγεια.', + 'Next step' => 'Επόμενο βήμα', + '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 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' => 'Ανάθεση χÏώματος σε συγκεκÏιμένο χÏήστη', + 'Position' => 'Θέση', + 'Duplicate to project' => 'ΑντιγÏαφή σε άλλο έÏγο', + 'Duplicate' => 'ΑντιγÏαφή', + 'Link' => 'ΣÏνδεσμος', + 'Comment updated successfully.' => 'Το σχόλιο ενημεÏώθηκε με επιτυχία.', + 'Unable to update your comment.' => 'Δεν ήταν δυνατή η ενημέÏωση του σχολίου.', + 'Remove a comment' => 'ΔιαγÏαφή σχολίου', + 'Comment removed successfully.' => 'Το σχόλιο διαγÏάφηκε με επιτυχία.', + 'Unable to remove this comment.' => 'Δεν ήταν δυνατή η διαγÏαφή του σχολίου.', + 'Do you really want to remove this comment?' => 'Είστε σίγουÏοι για την αφαίÏεση του σχολίου;', + 'Current password for the user "%s"' => 'Ο Ï„Ïέχων κωδικός Ï€Ïόσβασης για τον χÏήστη «%s»', + 'The current password is required' => 'Απαιτείται ο Ï„Ïέχων κωδικός Ï€Ïόσβασης', + 'Wrong password' => 'Λάθος κωδικός Ï€Ïόσβασης', + 'Unknown' => 'Άγνωστο', + 'Last logins' => 'Τελευταίες συνδέσεις', + 'Login date' => 'ΗμεÏομηνία σÏνδεσης', + 'Authentication method' => 'Μέθοδος αυθεντικοποίησης', + 'IP address' => 'ΔιεÏθυνση IP', + 'User agent' => 'ΠÏόγÏαμμα πελάτη', + 'Persistent connections' => 'Μόνιμες συνδέσεις', + 'No session.' => 'Καμία συνεδÏία.', + 'Expiration date' => 'ΗμεÏομηνία λήξης', + 'Remember Me' => 'Îα με θυμάσαι', + 'Creation date' => 'ΗμεÏομηνία δημιουÏγίας', + 'Everybody' => 'Όλοι', + 'Open' => 'Ανοικτά', + 'Closed' => 'Κλειστά', + 'Search' => 'Αναζήτηση', + 'Nothing found.' => 'Δεν βÏέθηκε κάτι.', + 'Due date' => 'ΗμεÏομηνία Ï€Ïοθεσμίας', + 'Description' => 'ΠεÏιγÏαφή', + '%d comments' => '%d σχόλια', + '%d comment' => '%d σχόλιο', + 'Email address invalid' => 'Μη αποδεκτή διεÏθυνση email', + '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.' => 'Ο λογαÏιασμός σας συνδέθηκε με το Ï€Ïοφίλ σας με επιτυχία.', + 'Email' => 'Email', + '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' => 'Αυτόματη εκχώÏηση μιας κατηγοÏίας με βάση το χÏώμα', + 'Task creation or modification' => 'ΔημιουÏγία ή Ï„Ïοποποίηση εÏγασίας', + 'Category' => 'ΚατηγοÏία', + 'Category:' => 'ΚατηγοÏία:', + 'Categories' => 'ΚατηγοÏίες', + 'Your category has been created successfully.' => 'Η κατηγοÏία δημιουÏγήθηκε.', + 'This category has been updated successfully.' => 'Η ενημέÏωση της κατηγοÏίας έγινε με επιτυχία.', + 'Unable to update this 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' => 'ΧωÏίς κατηγοÏία', + 'The name is required' => 'Απαιτείται η ονομασία', + 'Remove a file' => 'ΑφαίÏεση αÏχείου', + 'Unable to remove this file.' => 'Δεν ήταν δυνατή η αφαίÏεση του αÏχείου.', + 'File removed successfully.' => 'Το αÏχείο αφαιÏέθηκε με επιτυχία.', + 'Attach a document' => 'ΠÏοσθήκη εγγÏάφου', + 'Do you really want to remove this file: "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση του αÏχείου: «%s»;', + 'Attachments' => 'Συνημμένα', + 'Edit the task' => 'ΕπεξεÏγασία της εÏγασίας', + 'Add a comment' => 'ΠÏοσθήκη σχολίου', + 'Edit a comment' => 'ΕπεξεÏγασία σχολίου', + 'Summary' => 'ΠεÏίληψη', + 'Time tracking' => 'ΠαÏακολοÏθηση χÏόνου', + 'Estimate:' => 'Κατ\' εκτίμηση:', + 'Spent:' => 'Δαπανήθηκε:', + 'Do you really want to remove this sub-task?' => 'Είστε σίγουÏοι για την διαγÏαφή της υπο-εÏγασίας;', + 'Remaining:' => 'Απομένει:', + 'hours' => 'ÏŽÏες', + 'estimated' => 'κατ\' εκτίμηση', + 'Sub-Tasks' => 'Υπο-ΕÏγασίες', + 'Add a sub-task' => 'ΠÏοσθήκη υπο-εÏγασίας', + 'Original estimate' => 'ΑÏχική Ï€Ïόβλεψη χÏόνου', + 'Create another sub-task' => 'ΔημιουÏγία κι άλλης υπο-εÏγασίας', + 'Time spent' => 'ΧÏόνος που δαπανήθηκε', + 'Edit a sub-task' => 'ΕπεξεÏγασία υπο-εÏγασίας', + 'Remove a sub-task' => 'ΔιαγÏαφή υπο-εÏγασίας', + 'The time must be a numeric value' => 'Ο χÏόνος Ï€Ïέπει να είναι αÏιθμός', + 'Todo' => 'ΠÏέπει να γίνουν', + 'In progress' => 'Σε Ï€Ïόοδο', + 'Sub-task removed successfully.' => 'Η υπο-εÏγασία αφαιÏέθηκε με επιτυχία.', + 'Unable to remove this sub-task.' => 'Δεν ήταν δυνατή η αφαίÏεση της υπο-εÏγασίας.', + 'Sub-task updated successfully.' => 'Η υπο-εÏγασία ενημεÏώθηκε με επιτυχία.', + 'Unable to update your sub-task.' => 'ΑδÏνατο να ενημεÏωθεί η υπο-εÏγασία.', + 'Unable to create your sub-task.' => 'ΑδÏνατο να δημιουÏγηθεί η υπο-εÏγασία.', + 'Maximum size: ' => 'Μέγιστο μέγεθος: ', + 'Display another project' => 'Εμφάνιση άλλου έÏγου', + 'Created by %s' => 'ΔημιουÏγήθηκε από %s', + 'Tasks Export' => 'Εξαγωγή εÏγασιών', + 'Start Date' => 'ΗμεÏομηνία έναÏξης', + 'Execute' => 'Εκτέλεση', + 'Task Id' => 'ΑναγνωÏιστικό εÏγασίας', + 'Creator' => 'ΔημιουÏγός', + 'Modification date' => 'ΗμεÏομηνία Ï„Ïοποποίησης', + 'Completion date' => 'ΗμεÏομηνία ολοκλήÏωσης', + 'Clone' => 'Κλωνοποίηση', + 'Project cloned successfully.' => 'Το έÏγο κλωνοποιήθηκε με επιτυχία.', + 'Unable to clone this project.' => 'ΑδÏνατο να κλωνοποιηθεί το έÏγο.', + 'Enable email notifications' => 'ΕνεÏγοποίηση ειδοποιήσεων ηλεκτÏÎ¿Î½Î¹ÎºÎ¿Ï Ï„Î±Ï‡Ï…Î´Ïομείου', + 'Task position:' => 'Θέση έÏγου:', + 'The task #%d has been opened.' => 'Η εÏγασία #%d έχει ανοίξει.', + 'The task #%d has been closed.' => 'Η εÏγασία #%d έχει κλείσει.', + 'Sub-task updated' => 'Η υπο-εÏγασία ενημεÏώθηκε', + 'Title:' => 'Τίτλος:', + 'Status:' => 'Κατάσταση:', + 'Assignee:' => 'Ανατιθέμενος:', + 'Time tracking:' => 'ΠαÏακολοÏθηση του χÏόνου:', + 'New sub-task' => 'Îέα υπο-εÏγασία', + 'New attachment added "%s"' => 'Îέα επικόλληση Ï€Ïοστέθηκε «%s»', + 'New comment posted by %s' => 'Îέο σχόλιο από τον χÏήστη «%s»', + 'New comment' => 'Îέο σχόλιο', + 'Comment updated' => 'Το σχόλιο ενημεÏώθηκε', + 'New subtask' => 'Îέα υπο-εÏγασία', + 'I only want to receive notifications for these projects:' => 'Θέλω να ενημεÏώνομαι αποκλειστικά για τα κάτωθι έÏγα:', + 'view the task on Kanboard' => 'ΠÏοβολή της εÏγασίας στο Kanboard', + 'Public access' => 'Δημόσια Ï€Ïόσβαση', + 'Disable public access' => 'ΑπενεÏγοποίηση δημόσιας Ï€Ïόσβασης', + 'Enable public access' => 'ΕνεÏγοποίηση δημόσιας Ï€Ïόσβασης', + 'Public access disabled' => 'Η δημόσια Ï€Ïόσβαση απενεÏγοποιήθηκε', + 'Move the task to another project' => 'ΜεταφοÏά της εÏγασίας σε άλλο έÏγο', + 'Move to project' => 'ΜεταφοÏά σε άλλο έÏγο', + 'Do you really want to duplicate this task?' => 'Είστε σίγουÏοι για την αντιγÏαφή της εÏγασίας;', + 'Duplicate a task' => 'ΑντιγÏαφή εÏγασίας', + 'External accounts' => 'ΕξωτεÏικοί λογαÏιασμοί', + 'Account type' => 'ΤÏπος λογαÏιασμοÏ', + 'Local' => 'Τοπικός', + 'Remote' => 'ΑπομακÏυσμένος', + 'Enabled' => 'ΕνεÏγός', + 'Disabled' => 'ΑπενεÏγοποιημένος', + 'Login:' => 'Ονομα χÏήστη:', + 'Full Name:' => 'ΠλήÏες όνομα:', + 'Email:' => 'Email:', + 'Notifications:' => 'Ειδοποιήσεις:', + 'Notifications' => 'Ειδοποιήσεις', + 'Account type:' => 'ΤÏπος λογαÏιασμοÏ:', + 'Edit profile' => 'ΕπεξεÏγασία Ï€Ïοφίλ', + 'Change password' => 'Αλλαγή κωδικοÏ', + 'Password modification' => 'ΤÏοποποίηση κωδικοÏ', + 'External authentications' => 'ΕξωτεÏικές αυθεντικοποιήσεις', + 'Never connected.' => 'Ποτέ δεν συνδέθηκε.', + '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' => 'Δεν έχει ανατεθεί, εκτίμηση %sh', + '%s updated a comment on the task %s' => 'Ο/Η %s ενημέÏωσε ένα σχόλιο στην εÏγασία %s', + '%s commented the task %s' => 'Ο/Η %s σχολίασε την εÏγασία %s', + '%s\'s activity' => 'δÏαστηÏιότητα του έÏγου %s', + 'RSS feed' => 'Ροή RSS', + '%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)' => 'ΠÏοεπιλεγμένες στήλες για νέα έÏγα (χωÏισμένες με κόμμα)', + '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' => 'Επιλογή συμβάντος', + 'Create a task from an external provider' => 'ΔημιουÏγία εÏγασίας από ένα εξωτεÏικό πάÏοχο', + 'Change the assignee based on an external username' => 'Αλλαγή του ανατιθέμενου βάση ενός εξωτεÏÎ¹ÎºÎ¿Ï Î¿Î½ÏŒÎ¼Î±Ï„Î¿Ï‚ χÏήστη', + 'Change the category based on an external label' => 'Αλλαγή του ανατιθέμενου βάση μιας εξωτεÏικής ετικέτας', + 'Reference' => 'Πηγή', + 'Label' => 'Ετικέτα', + 'Database' => 'Βάση δεδομένων', + 'About' => 'Σχετικά', + 'Database driver:' => 'Οδηγός βάσης δεδομένων:', + 'Board settings' => 'Ρυθμίσεις ταμπλό', + 'Webhook settings' => 'Ρυθμίσεις webhook', + 'Reset token' => 'ΕπαναφοÏά token', + 'API endpoint:' => 'ΔιεÏθυνση URL API endpoint:', + 'Refresh interval for personal 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 δευτεÏόλεπτα από Ï€Ïοεπιλογή)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Συχνότητα σε δευτεÏόλεπτα (0 για απενεÏγοποίηση της λειτουÏγίας, 10 δευτεÏόλεπτα από Ï€Ïοεπιλογή)', + 'Application URL' => 'ΔιεÏθυνση URL εφαÏμογής', + 'Token regenerated.' => 'Το token δημιουÏγήθηκε εκ νέου.', + 'Date format' => 'ΜοÏφή ημεÏομηνίας', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Η μοÏφή ISO είναι πάντα αποδεκτή, πχ.: «%s» και «%s»', + 'New personal project' => 'Îέο ιδιωτικό έÏγο', + 'This project is personal' => 'Αυτό το έÏγο είναι ιδιωτικό', + 'Add' => 'ΠÏοσθήκη', + 'Start date' => 'ΗμεÏομηνία έναÏξης', + 'Time estimated' => 'Εκτιμώμενος χÏόνος', + 'There is nothing assigned to you.' => 'Δεν έχετε κάτι ανατεθειμένο.', + 'My tasks' => 'Οι εÏγασίες μου', + 'Activity stream' => 'Ροή δÏαστηÏιότητας', + 'Dashboard' => 'ΚεντÏικό ταμπλό', + 'Confirmation' => 'Επιβεβαίωση', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'ΔημιουÏγία σχολίου από εξωτεÏικό πάÏοχο', + 'Project management' => 'ΔιαχείÏιση έÏγων', + 'Columns' => 'Στήλες', + 'Task' => 'ΕÏγασία', + 'Percentage' => 'Ποσοστό', + 'Number of tasks' => 'ΑÏιθμός εÏγασιών', + 'Task distribution' => 'Κατανομή εÏγασιών', + 'Analytics' => 'ΑναλÏσεις', + 'Subtask' => 'Υπο-εÏγασία', + 'User repartition' => 'Επαναλήψεις χÏηστών', + 'Clone this project' => 'Κλωνοποίηση έÏγου', + 'Column removed successfully.' => 'Η στήλη αφαιÏέθηκε με επιτυχία.', + '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 subtask id is required' => 'Το αναγνωÏιστικό της υπο-εÏγασίας είναι υποχÏεωτικό', + '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' => 'Το αναγνωÏιστικό χÏήστη Ï€Ïέπει να είναι ακέÏαιος', + 'This value is required' => 'Η τιμή είναι υποχÏεωτική', + 'This value must be numeric' => 'Η τιμή Ï€Ïέπει να είναι αÏιθμός', + 'Unable to create this task.' => 'Δεν ήταν δυνατή η δημιουÏγία αυτής της εÏγασίας.', + 'Cumulative flow diagram' => 'ΣυγκεντÏωτικό διάγÏαμμα Ïοής', + 'Daily project summary' => 'ΚαθημεÏινή πεÏίληψη του έÏγου', + 'Daily project summary export' => 'Εξαγωγή της καθημεÏινής πεÏίληψης του έÏγου', + 'Exports' => 'Εξαγωγές', + 'This export contains the number of tasks per column grouped per day.' => 'Αυτή η εξαγωγή πεÏιέχει τον αÏιθμό των εÏγασιών ανά στήλη ομαδοποιημένες ανά ημέÏα.', + 'Active swimlanes' => 'ΕνεÏγές λωÏίδες', + 'Add a new swimlane' => 'ΠÏοσθήκη λωÏίδας', + 'Default swimlane' => 'ΠÏοκαθοÏισμένη λωÏίδα', + 'Do you really want to remove this swimlane: "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση της λωÏίδας: «%s»;', + 'Inactive swimlanes' => 'ΑνενεÏγές λωÏίδες', + 'Remove a swimlane' => 'ΑφαίÏεση λωÏίδας', + 'Swimlane modification for the project "%s"' => 'ΤÏοποποίηση λωÏίδας για το έÏγο «%s»', + 'Swimlane removed successfully.' => 'Η λωÏίδα αφαιÏέθηκε με επιτυχία.', + 'Swimlanes' => 'ΛωÏίδες', + 'Swimlane updated successfully.' => 'Η λωÏίδα ενημεÏώθηκε με επιτυχία.', + 'Unable to remove this swimlane.' => 'Δεν ήταν δυνατή η αφαίÏεση της λωÏίδας.', + 'Unable to update this swimlane.' => 'Δεν ήταν δυνατή η ενημέÏωση της λωÏίδας.', + 'Your swimlane has been created successfully.' => 'Η λωÏίδα σας δημιουÏγήθηκε με επιτυχία.', + 'Example: "Bug, Feature Request, Improvement"' => 'ΠαÏάδειγμα: «Σφάλμα, Αίτημα για νέο χαÏακτηÏιστικό, Βελτίωση»', + 'Default categories for new projects (Comma-separated)' => 'ΠÏοκαθοÏισμένες κατηγοÏίες για νέα έÏγα (χωÏισμένες με κόμμα)', + 'Integrations' => 'Ενσωματώσεις', + 'Integration with third-party services' => 'Ενσωμάτωση με υπηÏεσίες Ï„Ïίτων', + 'Subtask Id' => 'ΑναγνωÏιστικό υπο-εÏγασίας', + 'Subtasks' => 'Υπο-ΕÏγασίες', + 'Subtasks Export' => 'Εξαγωγή υπο-εÏγασιών', + 'Task Title' => 'Τίτλος εÏγασίας', + 'Untitled' => 'ΧωÏίς τίτλο', + 'Application default' => 'ΠÏοεπιλογή από την εφαÏμογή', + 'Language:' => 'Γλώσσα:', + 'Timezone:' => 'ÎÏα ζώνης:', + 'All columns' => 'Όλες οι στήλες', + 'Next' => 'Επόμενο', + '#%d' => '#%d', + 'All swimlanes' => 'Όλες οι λωÏίδες', + 'All colors' => 'Όλα τα χÏώματα', + 'Moved to column %s' => 'ΜεταφέÏθηκε στη στήλη %s', + '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' => 'ΠαÏακολοÏθηση χÏονοδιαγÏάμματος', + 'You already have one subtask in progress' => 'Έχετε ήδη μια υπο-εÏγασία σε εξέλιξη', + 'Which parts of the project do you want to duplicate?' => 'Ποιά τμήματα του έÏγου θέλετε να αντιγÏάψετε;', + 'Disallow login form' => 'ΑπαγόÏευση φόÏμας σÏνδεσης', + 'Start' => 'Εκκίνηση', + 'End' => 'Τέλος', + 'Task age in days' => 'ΧÏόνος εÏγασίας σε ημέÏες', + 'Days in this column' => 'ΗμέÏες σε αυτή την στήλη', + '%dd' => '%d ημ', + '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' => 'Το πεδίο είναι υποχÏεωτικό', + 'Link added successfully.' => 'Ο σÏνδεσμος Ï€Ïοστέθηκε με επιτυχία.', + 'Link updated successfully.' => 'Ο σÏνδεσμος ενημεÏώθηκε με επιτυχία.', + 'Link removed successfully.' => 'Ο σÏνδεσμος αφαιÏέθηκε με επιτυχία.', + 'Link labels' => 'Ετικέτες συνδέσμων', + 'Link modification' => 'ΤÏοποποίηση συνδέσμου', + 'Opposite label' => 'ΑντίστÏοφη ετικέτα', + 'Remove a link' => 'ΑφαίÏεση συνδέσμου', + '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.' => 'Δεν ήταν δυνατή η αφαίÏεση του συνδέσμου.', + 'relates to' => 'συνδέεται με', + 'blocks' => 'μπλοκάÏει', + 'is blocked by' => 'μπλοκάÏεται από', + 'duplicates' => 'αντιγÏάφει', + 'is duplicated by' => 'αντιγÏάφεται από', + 'is a child of' => 'είναι παιδί του', + 'is a parent of' => 'είναι ο πατέÏας του', + 'targets milestone' => 'στόχοι οÏοσήμου', + 'is a milestone of' => 'είναι ένα οÏόσημο της', + 'fixes' => 'διοÏθώνει', + 'is fixed by' => 'διοÏθώθηκε από', + 'This task' => 'Αυτή η εÏγασία', + '<1h' => '<1 ωÏ', + '%dh' => '%d ωÏ', + 'Expand tasks' => 'Ανάπτυξη εÏγασιών', + 'Collapse tasks' => 'ΣÏμπτυξη εÏγασιών', + 'Expand/collapse tasks' => 'Ανάπτυξη/σÏμπτυξη εÏγασιών', + 'Close dialog box' => 'Κλείσιμο του παÏαθÏÏου διαλόγου', + 'Submit a form' => 'Αποστολή φόÏμας', + 'Board view' => 'ΠÏοβολή κεντÏÎ¹ÎºÎ¿Ï Ï€Î¯Î½Î±ÎºÎ±', + 'Keyboard shortcuts' => 'ΣυντομεÏσεις πληκτÏολογίου', + 'Open board switcher' => 'Άνοιγμα μεταγωγέα κεντÏÎ¹ÎºÎ¿Ï Ï€Î¯Î½Î±ÎºÎ±', + 'Application' => 'ΕφαÏμογή', + 'Compact view' => 'Συμπυκνωμένη Ï€Ïοβολή', + 'Horizontal scrolling' => 'ΟÏιζόντια ολίσθηση', + 'Compact/wide view' => 'Συμπυκνωμένη/ΕυÏεία ΠÏοβολή', + 'Currency' => 'Îόμισμα', + 'Personal project' => 'Ιδιωτικό έÏγο', + 'AUD - Australian Dollar' => 'AUD - Australian Dollar', + 'CAD - Canadian Dollar' => 'CAD - Canadian Dollar', + 'CHF - Swiss Francs' => 'CHF - Swiss Francs', + 'Custom Stylesheet' => 'ΠÏοσαÏμοσμένο CSS stylesheet', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - British Pound', + 'INR - Indian Rupee' => 'INR - Indian Rupee', + 'JPY - Japanese Yen' => 'JPY - Japanese Yen', + 'NZD - New Zealand Dollar' => 'NZD - New Zealand Dollar', + 'PEN - Peruvian Sol' => 'PEN - ΠεÏÎ¿Ï Î£Î¿Î»', + 'RSD - Serbian dinar' => 'RSD - Serbian dinar', + 'CNY - Chinese Yuan' => 'CNY - Chinese Yuan', + 'USD - US Dollar' => 'USD - US Dollar', + 'VES - Venezuelan Bolívar' => 'VES - Venezuelan Bolívar', + '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' => 'ΧÏόνος που αφιεÏώθηκε στη στήλη', + '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.' => 'Η έκθεση αυτή πεÏιέχει όλες τις κινήσεις της στήλης για κάθε εÏγασία με την ημεÏομηνία, το χÏήστη και το χÏόνο που δαπανάται για κάθε μετάβαση.', + 'Currency rates' => 'Ισοτιμίες', + 'Rate' => 'Τιμή', + 'Change reference currency' => 'Αλλαγή ισοτιμίας', + 'Reference currency' => 'ΑναφοÏά ισοτιμίας', + 'The currency rate has been added successfully.' => 'Η ισοτιμία Ï€Ïοστέθηκε με επιτυχία.', + 'Unable to add this currency rate.' => 'Δεν ήταν δυνατή η Ï€Ïοσθήκη της ισοτιμίας.', + 'Webhook URL' => 'ΔιεÏθυνση webhook URL', + '%s removed the assignee of the task %s' => 'Ο/Η %s αφαίÏεσε τον ανατιθέμενο της εÏγασίας %s', + 'Information' => 'ΠληÏοφοÏίες', + 'Check two factor authentication code' => 'Έλεγχος ÎºÏ‰Î´Î¹ÎºÎ¿Ï Î±Ï…Î¸ÎµÎ½Ï„Î¹ÎºÎ¿Ï€Î¿Î¯Î·ÏƒÎ·Ï‚ δÏο παÏαγόντων', + 'The two factor authentication code is not valid.' => 'Ο κωδικός ελέγχου αυθεντικοποίησης δÏο παÏαγόντων δεν είναι σωστός.', + 'The two factor authentication code is valid.' => 'Ο κωδικός ελέγχου αυθεντικοποίησης δÏο παÏαγόντων είναι σωστός.', + 'Code' => 'Κωδικός', + 'Two factor authentication' => 'Αυθεντικοποίηση δÏο παÏαγόντων', + 'This QR code contains the key URI: ' => 'Το QR code πεÏιέχει το URI: ', + 'Check my code' => 'Έλεγχος του κωδικοÏ', + 'Secret key: ' => 'Μυστικό κλειδί: ', + 'Test your device' => 'Ελέγξτε τη συσκευή σας', + 'Assign a color when the task is moved to a specific column' => 'ΕκχώÏηση χÏώματος όταν η εÏγασία κινείται σε μια συγκεκÏιμένη στήλη', + '%s via Kanboard' => '%s μέσω του Kanboard', + 'Burndown chart' => 'ΔημιουÏγία διαγÏάμματος', + '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', + 'Identifier' => 'ΑναγνωÏιστικό', + 'Disable two factor authentication' => 'ΑπενεÏγοποίηση αυθεντικοποίησης δÏο παÏαγόντων', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Είστε σίγουÏοι για την απενεÏγοποίηση της αυθεντικοποίησης δÏο παÏαγόντων για το χÏήστη: «%s»;', + 'Edit link' => 'ΕπεξεÏγασία συνδέσμου', + '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' => 'Έχει Ï€ÏογÏαμματιστεί να δημιουÏγηθεί επαναλαμβανόμενη εÏγασία', + 'Score' => 'ΣκοÏ', + 'The identifier must be unique' => 'Το αναγνωÏιστικό Ï€Ïέπει να είναι μοναδικό', + 'This linked task id doesn\'t exists' => 'Αυτό το συνδεδεμένο αναγνωÏιστικό εÏγασίας δεν υπάÏχει', + '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' => 'ΧÏονικό πλαίσιο για τον υπολογισμό νέας ημεÏομηνίας Ï€Ïοθεσμίας', + '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: ' => 'Συντελεστής για τον υπολογισμό νέας ημεÏομηνίας Ï€Ïοθεσμίας: ', + 'Month(s)' => 'Μήνας(ες)', + 'This task has been created by: ' => 'Αυτή η εÏγασία δημιουÏγήθηκε από τον χÏήστη: ', + 'Recurrent task has been generated:' => 'ΠαÏάχθηκε η επαναλαμβανόμενη εÏγασία:', + 'Timeframe to calculate new due date: ' => 'ΧÏονοδιάγÏαμμα Ï…Ï€Î¿Î»Î¿Î³Î¹ÏƒÎ¼Î¿Ï Î½Î­Î±Ï‚ ημεÏομηνίας Ï€Ïοθεσμίας: ', + 'Trigger to generate recurrent task: ' => 'Έναυσμα για τη δημιουÏγία επαναλαμβανόμενης εÏγασίας: ', + 'When task is closed' => 'Όταν η εÏγασία έχει τελειώσει', + 'When task is moved from first column' => 'Όταν η εÏγασία μετακινηθεί στην Ï€Ïώτη στήλη', + 'When task is moved to last column' => 'Όταν η εÏγασία μετακινηθεί στην τελευταία στήλη', + 'Year(s)' => 'Έτος(η)', + 'Project settings' => 'Ρυθμίσεις έÏγου', + 'Automatically update the start date' => 'Αυτόματη ενημέÏωση της ημεÏομηνίας έναÏξης', + 'iCal feed' => 'Ροή iCal', + 'Preferences' => 'ΠÏοτιμήσεις', + 'Security' => 'Ασφάλεια', + 'Two factor authentication disabled' => 'ΑνενεÏγή αυθεντικοποίηση δÏο παÏαγόντων', + 'Two factor authentication enabled' => 'ΕνεÏγή αυθεντικοποίηση δÏο παÏαγόντων', + 'Unable to update this user.' => 'Δεν ήταν δυνατή η ενημέÏωση του χÏήστη.', + 'There is no user management for personal projects.' => 'Δεν υπάÏχει διαχείÏιση χÏηστών για ιδιωτικά έÏγα.', + 'User that will receive the email' => 'Ο χÏήστης που θα λάβει το μήνυμα email', + 'Email subject' => 'Θέμα email', + 'Date' => 'ΗμεÏομηνία', + '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' => 'Αποστολή εÏγασίας μέσω email σε κάποιον', + 'Reopen a task' => 'Ξανα-άνοιγμα εÏγασίας', + 'Notification' => 'Ειδοποίηση', + '%s moved the task #%d to the first swimlane' => 'Ο/Η %s μετέφεÏε την εÏγασία #%d στην Ï€Ïώτη λωÏίδα', + 'Swimlane' => 'ΛωÏίδα', + '%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.' => 'Η έκθεση αυτή πεÏιέχει όλες τις πληÏοφοÏίες για το συγκεκÏιμένο εÏÏος ημεÏομηνιών.', + 'Project activities for %s' => 'ΔÏαστηÏιότητες έÏγου «%s»', + 'view the board on Kanboard' => 'Ï€Ïοβολή του πίνακα στο Kanboard', + 'The task has been moved to the first swimlane' => 'Η εÏγασία αυτή έχει μετακινηθεί στην Ï€Ïώτη λωÏίδα', + 'The task has been moved to another swimlane:' => 'Η εÏγασία αυτή έχει μετακινηθεί σε άλλη λωÏίδα:', + 'New title: %s' => 'Îέος τίτλος: %s', + 'The task is not assigned anymore' => 'Η εÏγασία δεν είναι πλέον ανατεθειμένη', + 'New assignee: %s' => 'Îέος ανατιθέμενος: %s', + 'There is no category now' => 'Δεν υπάÏχει τώÏα κατηγοÏία', + 'New category: %s' => 'Îέα κατηγοÏία: %s', + 'New color: %s' => 'Îέο χÏώμα: %s', + 'New complexity: %d' => 'Îέα πολυπλοκότητα: %d', + 'The due date has been removed' => 'Η ημεÏομηνία Ï€Ïοθεσμίας έχει αφαιÏεθεί', + 'There is no description anymore' => 'Δεν υπάÏχει πλέον πεÏιγÏαφή', + 'Recurrence settings has been modified' => 'Οι Ïυθμίσεις επανάληψης έχουν Ï„Ïοποποιηθεί', + 'Time spent changed: %sh' => 'Ο χÏόνος που σπαταλήθηκε έχει αλλάξει: %sh', + 'Time estimated changed: %sh' => 'Ο εκτιμώμενος χÏόνος άλλαξε: %sh', + 'The field "%s" has 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:' => 'Επιθυμώ να λαμβάνω ειδοποιήσεις για:', + 'All tasks' => 'Όλες τις εÏγασίες', + 'Only for tasks assigned to me' => 'Μόνο για εÏγασίες που μου έχουν ανατεθεί', + 'Only for tasks created by me' => 'Μόνο για εÏγασίες που έχουν δημιουÏγηθεί από εμένα', + 'Only for tasks created by me and tasks assigned to me' => 'Μόνο για εÏγασίες που έχουν δημιουÏγηθεί από εμένα και μου έχουν ανατεθεί', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'ΣÏνολο για όλες τις στήλες', + 'You need at least 2 days of data to show the chart.' => 'ΧÏειάζονται τουλάχιστον δεδομένα δÏο ημεÏών για να εμφανιστεί το γÏάφημα.', + '<15m' => '<15 λ', + '<30m' => '<30 λ', + 'Stop timer' => 'Διακοπή ÏολογιοÏ', + 'Start timer' => 'ΈναÏξη ÏολογιοÏ', + 'My activity stream' => 'Η Ïοή δÏαστηÏιοτήτων μου', + 'Search tasks' => 'Αναζήτηση εÏγασιών', + 'Reset filters' => 'ΕπαναφοÏά φίλτÏων', + 'My tasks due tomorrow' => 'Οι εÏγασίες μου με αυÏιανή Ï€Ïοθεσμία', + 'Tasks due today' => 'ΕÏγασίες με σημεÏινή Ï€Ïοθεσμία', + 'Tasks due tomorrow' => 'ΕÏγασίες με αυÏιανή Ï€Ïοθεσμία', + 'Tasks due yesterday' => 'ΕÏγασίες με χθεσινή Ï€Ïοθεσμία', + 'Closed tasks' => 'Κλειστές εÏγασίες', + 'Open tasks' => 'Ανοιχτές εÏγασίες', + 'Not assigned' => 'ΧωÏίς ανάθεση', + 'View advanced search syntax' => 'Δείτε τη σÏνταξη αναζήτησης για Ï€ÏοχωÏημένους', + 'Overview' => 'Επισκόπηση', + 'Board/Calendar/List view' => 'Πίνακας/ΗμεÏολόγιο/ΠÏοβολή λίστας', + 'Switch to the board view' => 'Εναλλαγή στην Ï€Ïοβολή πίνακα', + 'Switch to the list view' => 'Εναλλαγή στην Ï€Ïοβολή λίστας', + 'Go to the search/filter box' => 'Μετάβαση στο πλαίσιο αναζήτησης/φίλτÏο', + 'There is no activity yet.' => 'Δεν υπάÏχει ακόμη κάποια δÏαστηÏιότητα.', + 'No tasks found.' => 'Δεν βÏέθηκαν εÏγασίες.', + 'Keyboard shortcut: "%s"' => 'Συντόμευση πληκτÏολογίου: «%s»', + 'List' => 'Λίστα', + 'Filter' => 'ΦίλτÏο', + 'Advanced search' => 'ΠÏοχωÏημένη Αναζήτηση', + 'Example of query: ' => 'ΠαÏάδειγμα εÏωτήματος: ', + 'Search by project: ' => 'Αναζήτηση με βάση το έÏγο: ', + 'Search by column: ' => 'Αναζήτηση με βάση την στήλη: ', + 'Search by assignee: ' => 'Αναζήτηση με βάση τον ανατιθέμενο: ', + 'Search by color: ' => 'Αναζήτηση με βάση το χÏώμα: ', + 'Search by category: ' => 'Αναζήτηση με βάση την κατηγοÏία: ', + 'Search by description: ' => 'Αναζήτηση με βάση την πεÏιγÏαφή: ', + 'Search by due date: ' => 'Αναζήτηση με βάση την ημέÏα Ï€Ïοθεσμίας: ', + 'Average time spent in each column' => 'Μέσος χÏόνος παÏαμονής σε κάθε στήλη', + 'Average time spent' => 'Μέσος χÏόνος που δαπανήθηκε', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Αυτό το γÏάφημα δείχνει το μέσο χÏόνο που δαπανάται σε κάθε στήλη για τις τελευταίες %d εÏγασίες', + 'Average Lead and Cycle time' => 'Μέσοι χÏόνοι Lead & Cycle', + 'Average lead time: ' => 'Μέσος χÏόνος lead: ', + 'Average cycle time: ' => 'Μέσος χÏόνος cycle: ', + 'Cycle Time' => 'ΧÏόνος cycle', + 'Lead Time' => 'ΧÏόνος lead', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Αυτό το γÏάφημα δείχνει το average lead and cycle time για τις τελευταίες %d εÏγασίες κατά τη διάÏκεια του χÏόνου.', + 'Average time into each column' => 'Μέσος χÏόνος σε κάθε στήλη', + 'Lead and cycle time' => 'ΧÏόνος lead και cycle', + 'Lead time: ' => 'ΧÏόνος lead: ', + 'Cycle time: ' => 'ΧÏόνος cycle: ', + 'Time spent in each column' => 'Ο χÏόνος που δαπανήθηκε σε κάθε στήλη', + 'The lead time is the duration between the task creation and the completion.' => 'Το <lead time> είναι η διάÏκεια Î¼ÎµÏ„Î±Î¾Ï Ï„Î·Ï‚ δημιουÏγίας του έÏγου και της ολοκλήÏωσής του.', + 'The cycle time is the duration between the start date and the completion.' => 'Το <cycle time> είναι η διάÏκεια Î¼ÎµÏ„Î±Î¾Ï Ï„Î·Ï‚ ημεÏομηνίας εκκίνησης και της ολοκλήÏωσής του.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Εάν η εÏγασία δεν έχει κλείσει η Ï„Ïέχουσα ÏŽÏα χÏησιμοποιείται αντί της ημεÏομηνίας ολοκλήÏωσης.', + 'Set the start date automatically' => 'Ρυθμίστε αυτόματα την ημεÏομηνία έναÏξης', + '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.' => 'Αν ενεÏγοποιήσετε την επιλογή "ΑπαγόÏευση φόÏμας σÏνδεσης", τα στοιχεία που εισάγονται στη φόÏμα σÏνδεσης αγνοοÏνται.', + '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 category: %s' => 'ΤÏέχουσα κατηγοÏία: %s', + 'no category' => 'Καμία κατηγοÏία', + 'Current assignee: %s' => 'ΤÏέχων ανατιθέμενος: %s', + 'not assigned' => 'δεν έχει ανατεθεί', + 'Author:' => 'ΣυγγÏαφέας:', + 'contributors' => 'συνεισφέÏοντες', + 'License:' => 'Άδεια:', + 'License' => 'Άδεια', + 'Enter the text below' => 'ΠληκτÏολογήστε το κείμενο παÏακάτω', + 'Start date:' => 'ΗμεÏομηνία εκκίνησης:', + 'Due date:' => 'ΗμεÏομηνία Ï€Ïοθεσμίας:', + 'People who are project managers' => 'Οι άνθÏωποι που είναι συντονιστές έÏγων', + 'People who are project members' => 'Οι άνθÏωποι που είναι μέλη έÏγων', + 'NOK - Norwegian Krone' => 'NOK - Norwegian Krone', + 'Show this column' => 'Εμφάνιση αυτής της στήλης', + 'Hide this column' => 'ΑπόκÏυψη αυτής της στήλης', + 'End date' => 'ΗμεÏομηνία λήξης', + 'Users overview' => 'Επισκόπηση χÏηστών', + 'Members' => 'Μέλη', + 'Shared project' => 'ΚοινόχÏηστο έÏγο', + 'Project managers' => 'Συντονιστές έÏγου', + 'Projects list' => 'Λίστα έÏγων', + 'End date:' => 'ΗμεÏομηνία λήξης:', + 'Change task color when using a specific task link' => 'Αλλαγή χÏώματος εÏγασίας χÏησιμοποιώντας συγκεκÏιμένο σÏνδεσμο εÏγασίας', + 'Task link creation or modification' => 'ΣÏνδεσμος δημιουÏγίας ή Ï„Ïοποποίησης εÏγασίας', + 'Milestone' => 'ΟÏόσημο', + 'Reset the search/filter box' => 'ΑÏχικοποίηση του πεδίου αναζήτησης/φιλτÏαÏίσματος', + 'Documentation' => 'ΤεκμηÏίωση', + 'Author' => 'ΔημιουÏγός', + 'Version' => 'Έκδοση', + 'Plugins' => 'ΠÏόσθετα', + 'There is no plugin loaded.' => 'Δεν έχει φοÏτωθεί Ï€Ïόσθετο.', + 'My notifications' => 'Οι ειδοποιήσεις μου', + 'Custom filters' => 'ΠÏοσαÏμοσμένα φίλτÏα', + 'Your custom filter has 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 has been updated successfully.' => 'Το Ï€ÏοσαÏμοσμένο φίλτÏο ενημεÏώθηκε με επιτυχία.', + 'Unable to update custom filter.' => 'Δεν ήταν δυνατή η ενημέÏωση του Ï€ÏοσαÏμοσμένου φίλτÏου.', + 'Web' => 'Ιστός', + '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', + 'New task #%d: %s' => 'Îέα εÏγασία #%d: %s', + 'Task updated #%d' => 'Η εÏγασία #%d ενημεÏώθηκε με επιτυχία', + 'Task #%d closed' => 'Η εÏγασία #%d έκλεισε', + 'Task #%d opened' => 'Η εÏγασία #%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', + '%d overdue tasks' => '%d εκπÏόθεσμες εÏγασίες', + 'No notification.' => 'ΧωÏίς νέες ειδοποιήσεις.', + '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:' => 'Μέθοδοι ειδοποίησης:', + 'Unable to read your file' => 'Δεν ήταν δυνατή η ανάγνωση του αÏχείου', + '%d task(s) have been imported successfully.' => '%d η(οι) εÏγασία(ες) εισήχθησαν με επιτυχία.', + 'Nothing has been imported!' => 'Τίποτα δεν εισήχθη!', + 'Import users from CSV file' => 'Εισαγωγή χÏηστών μέσω αÏχείου CSV', + '%d user(s) have been imported successfully.' => '%d ο(οι) χÏήστης(ες) εισήχθησαν με επιτυχία.', + 'Comma' => 'Κόμμα', + 'Semi-colon' => 'ΕÏωτηματικό', + 'Tab' => 'Στηλοθέτης', + 'Vertical bar' => 'ΚατακόÏυφη μπάÏα', + '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)' => 'ΠÏοσθήκη στο φίλτÏο (αντί για αντικατάσταση)', + 'Append/Replace' => 'ΠÏοσθήκη/Αντικατάσταση', + 'Append' => 'ΠÏοσθήκη', + 'Replace' => 'Αντικατάσταση', + 'Import' => 'Εισαγωγή', + 'Change sorting' => 'Αλλαγή ταξινόμησης', + 'Tasks Importation' => 'Εισαγωγή εÏγασιών', + 'Delimiter' => 'ΔιαχωÏιστής', + 'Enclosure' => 'Enclosure', + 'CSV File' => 'ΑÏχείο CSV', + 'Instructions' => 'Οδηγίες', + 'Your file must use the predefined CSV format' => 'Το αÏχείο σας Ï€Ïέπει να χÏησιμοποιεί την Ï€ÏοκαθοÏισμένη μοÏφοποίηση CSV', + 'Your file must be encoded in UTF-8' => 'Το αÏχείο σας Ï€Ïέπει να έχει κωδικοποίηση χαÏακτήÏων 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' => 'Η ημεÏομηνία Ï€Ïοθεσμίας Ï€Ïέπει να χÏησιμοποιεί τη μοÏφοποίηση ISO: EEEE-MM-HH ή στα αγγλικά YYYY-MM-DD', + 'Download CSV template' => 'Κατέβασμα Ï€Ïότυπου αÏχείου CSV', + 'No external integration registered.' => 'Δεν υπάÏχει καταγεγÏαμμένη εξωτεÏική ενσωμάτωση.', + 'Duplicates are not imported' => 'Δεν εισήχθησαν διπλοεγγÏαφές', + 'Usernames must be lowercase and unique' => 'Οι ονομασίες χÏηστών Ï€Ïέπει να είναι σε μικÏά γÏάμματα (lowercase) και μοναδικά', + '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' => 'Αυτόματη εκχώÏηση κατηγοÏίας, βασισμένη σε σÏνδεσμο', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Όνομα χÏήστη ανατιθέμενου', + 'Assignee Name' => 'Όνομα ανατιθέμενου', + 'Groups' => 'Ομάδες', + 'Members of %s' => 'Μέλη της %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"' => 'ΠÏοσθήκη του μέλους στην ομάδα «%s»', + 'Group member added successfully.' => 'Το μέλος Ï€Ïοστέθηκε στην ομάδα με επιτυχία.', + 'Unable to add group member.' => 'Δεν ήταν δυνατή η Ï€Ïοσθήκη μέλους στην ομάδα.', + 'Remove user from group "%s"' => 'ΑφαίÏεση μέλους από την ομάδα «%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' => 'Ο λογαÏιασμός σας κλειδώθηκε για %d λεπτά', + 'Invalid captcha' => 'Μη αποδεκτός κωδικός captcha', + 'The name must be unique' => 'Το όνομα Ï€Ïέπει να είναι μοναδικό', + 'View all groups' => 'ΠÏοβολή όλων των ομάδων', + 'There is no user available.' => 'Δεν υπάÏχει διαθέσιμος χÏήστης.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση του χÏήστη «%s» από την ομάδα «%s»;', + 'There is no group.' => 'Δεν υπάÏχει ομάδα.', + 'Add group member' => 'ΠÏοσθήκη μέλους ομάδας', + 'Do you really want to remove this group: "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση της ομάδας: «%s»;', + 'There is no user in this group.' => 'Δεν υπάÏχει χÏήστης σε αυτήν την ομάδα.', + 'Permissions' => 'Δικαιώματα', + 'Allowed Users' => 'ΧÏήστες που επιτÏέπονται', + 'No specific user has been allowed.' => 'Δεν υπάÏχει συγκεκÏιμένος χÏήστης που να επιτÏέπεται.', + 'Role' => 'Ρόλος', + 'Enter user name...' => 'Εισαγωγή ονόματος χÏήστη...', + 'Allowed Groups' => 'Ομάδες που επιτÏέπονται', + 'No group has been allowed.' => 'Δεν υπάÏχει συγκεκÏιμένη ομάδα που να επιτÏέπεται.', + 'Group' => 'Ομάδα', + 'Group Name' => 'Ονομασία ομάδας', + 'Enter group name...' => 'Εισαγωγή ονομασίας ομάδας...', + 'Role:' => 'Ρόλος:', + 'Project members' => 'Μέλη έÏγου', + '%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', + 'Estimated hours: ' => 'ΠÏοβλεπόμενες ÏŽÏες: ', + 'Actual hours: ' => 'ΠÏαγματικές ÏŽÏες: ', + 'Hours Spent' => 'Δαπανόμενες ÏŽÏες', + 'Hours Estimated' => 'ΠÏοβλεπόμενες ÏŽÏες', + 'Estimated Time' => 'ΠÏοβλεπόμενος χÏόνος', + 'Actual Time' => 'ΠÏαγματικός χÏόνος', + 'Estimated vs actual time' => 'ΠÏοβλεπόμενος vs Ï€Ïαγματικός χÏόνος', + 'RUB - Russian Ruble' => 'RUB - Russian Ruble', + 'Assign the task to the person who does the action when the column is changed' => 'Ανάθεση της εÏγασίας στο άτομο που εκτελεί την ενέÏγεια όταν η στήλη αλλάζει', + 'Close a task in a specific column' => 'Κλείσιμο εÏγασίας σε συγκεκÏιμένη στήλη', + 'Time-based One-time Password Algorithm' => 'ΑλγόÏιθμος ÎšÏ‰Î´Î¹ÎºÎ¿Ï Î Ïόσβασης Μιας ΧÏήσης Βάσει ΧÏόνου', + 'Two-Factor Provider: ' => 'ΠάÏοχος Αυθεντικοποίησης ΔÏο ΠαÏαγόντων: ', + 'Disable two-factor authentication' => 'ΑπενεÏγοποίηση αυθεντικοποίησης δÏο παÏαγόντων', + 'Enable two-factor authentication' => 'ΕνεÏγοποίηση αυθεντικοποίησης δÏο παÏαγόντων', + 'There is no integration registered at the moment.' => 'Δεν υπάÏχει αυτή τη στιγμή καταχωÏημένη ενσωμάτωση.', + 'Password Reset for Kanboard' => 'ΑÏχικοποίηση κωδικών Ï€Ïόσβασης για την εφαÏμογή Kanboard', + 'Forgot password?' => 'Ξεχάσατε τον κωδικό Ï€Ïόσβασης;', + 'Enable "Forget Password"' => 'ΕνεÏγοποίηση του «Ξέχασα τον κωδικό Ï€Ïόσβασης»', + 'Password Reset' => 'ΑÏχικοποίηση ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης', + 'New password' => 'Îέος κωδικός Ï€Ïόσβασης', + 'Change Password' => 'Αλλαγή ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης', + 'To reset your password click on this link:' => 'Για να αÏχικοποιηθεί ο κωδικός σας Ï€Ïόσβασης πατήστε σε αυτόν τον σÏνδεσμο:', + 'Last Password Reset' => 'ΑÏχικοποίηση τελευταίου ÎºÏ‰Î´Î¹ÎºÎ¿Ï Ï€Ïόσβασης', + 'The password has never been reinitialized.' => 'Ο κωδικός Ï€Ïόσβασης δεν μποÏεί να αÏχικοποιηθεί για δεÏτεÏη φοÏά.', + 'Creation' => 'ΔημιουÏγία', + 'Expiration' => 'Λήξη', + 'Password reset history' => 'ΙστοÏικό αÏχικοποίησης κωδικών Ï€Ïόσβασης', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Όλες οι εÏγασίας της στήλης «%s» και η λωÏίδα «%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.' => '%d εÏγασία(ες) στη στήλη «%s» και στη λωÏίδα «%s» θα κλείσουν.', + 'Close all tasks in this column and this swimlane' => 'Κλείσιμο όλων των εÏγασιών αυτής της στήλης', + '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.' => 'Το αναγνωÏιστικό έÏγου είναι Ï€ÏοαιÏετικό και Ï€Ïέπει να είναι αλφαÏιθμητικό, για παÏάδειγμα: MYPROJECT', + 'Project owner' => 'Ιδιοκτήτης έÏγου', + 'Personal projects do not have users and groups management.' => 'Τα ιδιωτικά έÏγα δεν έχουν χÏήστες και διαχείÏιση ομάδων', + 'There is no project member.' => 'Δεν υπάÏχει μέλος στο έÏγο', + 'Priority' => 'ΠÏοτεÏαιότητα', + 'Task priority' => 'ΠÏοτεÏαιότητα εÏγασίας', + 'General' => 'Γενικά', + 'Dates' => 'ΗμεÏομηνίες', + 'Default priority' => 'Εξ οÏÎ¹ÏƒÎ¼Î¿Ï Ï€ÏοτεÏαιότητα', + 'Lowest priority' => 'Η χαμηλότεÏη Ï€ÏοτεÏαιότητα', + 'Highest priority' => 'Η υψηλότεÏη Ï€ÏοτεÏαιότητα', + 'Close a task when there is no activity' => 'Κλείσιμο εÏγασίας όταν δεν υπάÏχει δÏαστηÏιότητα', + 'Duration in days' => 'ΔιάÏκεια σε ημέÏες', + 'Send email when there is no activity on a task' => 'Αποστολή email όταν δεν υπάÏχει δÏαστηÏιότητα σε εÏγασία', + 'Unable to fetch link information.' => 'Δεν ήταν δυνατή η ανάλυση της πληÏοφοÏίας συνδεσμου', + 'Daily background job for tasks' => 'ΗμεÏήσια παÏασκηνιακή δουλειά για τις εÏγασίες', + 'Auto' => 'Αυτόματο', + 'Related' => 'Σχετίζεται', + 'Attachment' => 'Συνημμένο', + 'Web Link' => 'ΣÏνδεσμος web', + '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' => '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' => '%s ÏŽÏες', + 'Time spent:' => 'χÏόνος που καταναλώθηκε:', + 'Created:' => 'ΔημιουÏγήθηκε:', + 'Modified:' => 'ΤÏοποποιήθηκε:', + 'Completed:' => 'ΟλοκληÏώθηκε:', + 'Started:' => 'Ξεκίνησε:', + 'Moved:' => 'Μετακινήθηκε:', + 'Task #%d' => 'ΕÏγασία #%d', + 'Time format' => 'ΜοÏφή ÏŽÏας', + 'Start date: ' => 'ΗμεÏομηνία έναÏξης: ', + 'End date: ' => 'ΗμεÏομηνία λήξης: ', + 'New due date: ' => 'Îέα ημεÏομηνία Ï€Ïοθεσμίας: ', + 'Start date changed: ' => 'Αλλαγμένη ημεÏομηνία έναÏξης: ', + 'Disable personal projects' => 'ΑπενεÏγοποίηση ιδιωτικών έÏγων', + 'Do you really want to remove this custom filter: "%s"?' => 'Είστε σίγουÏοι για την αφαίÏεση του Ï€ÏοσαÏμοσμένου φίλτÏου: «%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.' => 'Όλα τα αÏχεία ανέβηκαν με επιτυχία.', + 'The maximum allowed file size is %sB.' => 'Το μέγιστο μέγεθος αÏχείου που επιτÏέπεται είναι %sB.', + 'Drag and drop your files here' => 'ΣÏÏετε τα αÏχεία σας εδώ', + 'choose files' => 'επιλέξτε αÏχεία', + 'View profile' => 'ΠÏοβολή Ï€Ïοφίλ', + 'Two Factor' => 'Αυθεντικοποίηση δÏο παÏαγόντων', + 'Disable user' => 'ΑπενεÏγοποίηση χÏήστη', + 'Do you really want to disable this user: "%s"?' => 'Είστε σίγουÏοι για την απενεÏγοποίηση του χÏήστη: «%s»;', + 'Enable user' => 'ΕνεÏγοποίηση χÏήστη', + 'Do you really want to enable this user: "%s"?' => 'Είστε σίγουÏοι για την ενεÏγοποίηση του χÏήστη «%s»;', + 'Download' => 'Κατέβασμα', + 'Uploaded: %s' => 'Ανέβηκε το αÏχείο: %s', + 'Size: %s' => 'Μέγεθος: %s', + 'Uploaded by %s' => 'Ανέβηκε από το χÏήστη %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' => 'Η τιμή Ï€Ïέπει να είναι μεγαλÏτεÏη από %d', + 'Another swimlane with the same name exists in the project' => 'Μια άλλη λωÏίδα, με το ίδιο όνομα υπάÏχει στο έÏγο', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'ΠαÏάδειγμα: https://example.kanboard.org/ (χÏησιμοποιείται για τη δημιουÏγία απόλυτων 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:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Έκδοση λειτουÏÎ³Î¹ÎºÎ¿Ï ÏƒÏ…ÏƒÏ„Î®Î¼Î±Ï„Î¿Ï‚:', + 'Database version:' => 'Έκδοση βάσης δεδομένων:', + 'Browser:' => 'ΠÏόγÏαμμα πλοήγησης:', + 'Task view' => 'ΠÏοβολή εÏγασίας', + 'Edit task' => 'ΕπεξεÏγασία εÏγασίας', + 'Edit description' => 'ΕπεξεÏγασία πεÏιγÏαφής', + 'New internal link' => 'Îέος εσωτεÏικός σÏνδεσμος', + 'Display list of keyboard shortcuts' => 'ΠÏοβολή λίστας συντομεÏσεων πληκτÏολογίου', + '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', + 'Email sender address' => 'ΔιεÏθυνση αποστολέα email', + 'Email transport' => 'Μέθοδος αποστολής email', + 'Webhook token' => 'Webhook 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 a 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 has 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', + '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', + 'No personal API access token registered.' => 'Δεν υπάÏχει καταχωÏημένο Ï€Ïοσωπικό token Ï€Ïόσβασης στο API.', + 'Your personal API access token is "%s"' => 'Το Ï€Ïοσωπικό σας token Ï€Ïόσβασης στο API είναι «%s»', + 'Remove your token' => 'ΑφαίÏεση του token σας', + 'Generate a new token' => 'ΔημιουÏγία νέου token', + 'Showing %d-%d of %d' => 'ΠÏοβολή %d-%d από %d', + 'Outgoing Emails' => 'ΕξεÏχόμενα email', + '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 Ï€Ïοσκλήσεις.', + '%d invitation was sent.' => 'Στάλθηκε %d Ï€Ïόσκληση.', + 'Unable to create this user.' => 'Δεν ήταν δυνατή η δημιουÏγία του χÏήστη.', + 'Kanboard Invitation' => 'ΠÏόσκληση Kanboard', + '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.' => 'Έχετε Ï€Ïοσκληθεί για να εγγÏαφείτε στο Kanboard.', + 'Click here to join your team' => 'Κάντε κλικ εδώ για να συμμετέχετε στην ομάδα σας', + 'Invite people' => 'ΠÏόσκληση ανθÏώπων', + 'Emails' => 'Email', + 'Enter one email address by line.' => 'Εισαγάγετε μια διεÏθυνση email ανά σειÏά.', + 'Add these people to this project' => 'ΠÏοσθήκη των ανθÏώπων στο έÏγο', + 'Add this person to this project' => 'ΠÏοσθήκη του ανθÏώπου στο έÏγο', + 'Sign-up' => 'ΕγγÏαφή', + 'Credentials' => 'ΔιαπιστευτήÏια', + 'New user' => 'Îέος χÏήστης', + 'This username is already taken' => 'Το όνομα χÏήστη χÏησιμοποείται ήδη', + 'Your profile must have a valid email address.' => 'Το Ï€Ïοφίλ σας Ï€Ïέπει να έχει μια έγκυÏη διεÏθυνση email.', + 'TRL - Turkish Lira' => 'TRL - Turkish Lira', + 'The project email is optional and could be used by several plugins.' => 'Η διεÏθυνση email του έÏγου είναι Ï€ÏοαιÏετική και μποÏεί να χÏησιμοποιηθεί από διάφοÏα Ï€Ïόσθετα.', + 'The project email must be unique across all projects' => 'Η διεÏθυνση email του έÏγου Ï€Ïέπει να είναι μοναδική σε όλα τα έÏγα', + 'The email configuration has been disabled by the administrator.' => 'Η παÏαμετÏοποίηση email έχει απενεÏγοποιηθεί από τον διαχειÏιστή σας.', + 'Close this project' => 'Κλείσιμο του έÏγου', + 'Open this project' => 'Άνοιγμα του έÏγου', + 'Close a project' => 'Κλείσιμο ενός έÏγου', + 'Do you really want to close this project: "%s"?' => 'Είστε σίγουÏοι για το κλείσιμο του έÏγου: «%s»;', + 'Reopen a project' => 'Επανα-άνοιγμα ενός έÏγου', + 'Do you really want to reopen this project: "%s"?' => 'Είστε σίγουÏοι για το επανα-άνοιγμα του έÏγου: «%s»;', + 'This project is open' => 'Το έÏγο είναι ανοικτό', + 'This project is closed' => 'Το έÏγο είναι κλειστό', + 'Unable to upload files, check the permissions of your data folder.' => 'ΑδÏνατο το ανέβασμα αÏχείων, ελέγξτε τα δικαιώματα του φακέλου αÏχείων σας.', + 'Another category with the same name exists in this project' => 'ΥπάÏχει ήδη άλλη κατηγοÏία με το ίδιο όνομα σε αυτό το έÏγο', + 'Comment sent by email successfully.' => 'Το σχόλιο στάλθηκε μέσω email με επιτυχία.', + 'Sent by email to "%s" (%s)' => 'Στάλθηκε μέσω email στον/στην «%s» (%s)', + 'Unable to read uploaded file.' => 'Δεν ήταν δυνατή η ανάγνωση του ανεβασμένου αÏχείου.', + 'Database uploaded successfully.' => 'Η βάση δεδομένων ανέβηκε με επιτυχία.', + 'Task sent by email successfully.' => 'Η εÏγασία στάλθηκε μέσω email με επιτυχία.', + 'There is no category in this project.' => 'Δεν υπάÏχει κατηγοÏία στο έÏγο.', + 'Send by email' => 'Αποστολή μέσω email', + 'Create and send a comment by email' => 'ΔημιουÏγία και αποστολή σχολίου μέσω email', + 'Subject' => 'Θέμα', + 'Upload the database' => 'Ανέβασμα της βάσης δεδομένων', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'ΜποÏείτε να ανεβάσετε την κατεβασμένη από Ï€ÏοηγοÏμενη στιγμή βάση δεδομένων Sqlite (σε μοÏφή Gzip).', + 'Database file' => 'ΑÏχείο βάσης δεδομένων', + 'Upload' => 'Ανέβασμα', + 'Your project must have at least one active swimlane.' => 'Το έÏγο σας Ï€Ïέπει να διαθέτει τουλάχιστον μία λωÏίδα.', + 'Project: %s' => 'ΈÏγο: %s', + 'Automatic action not found: "%s"' => 'Δε βÏέθηκε η αυτόματη ενέÏγεια: «%s»', + '%d projects' => '%d έÏγα', + '%d project' => '%d έÏγο', + 'There is no project.' => 'Δεν υπάÏχει κάποιο έÏγο.', + 'Sort' => 'Ταξινόμηση', + 'Project ID' => 'ΑναγνωÏιστικό έÏγου', + 'Project name' => 'Όνομα έÏγου', + 'Public' => 'Δημόσιο', + 'Personal' => 'Ιδιωτικό', + '%d tasks' => '%d εÏγασίες', + '%d task' => '%d εÏγασία', + 'Task ID' => 'ΑναγνωÏιστικό εÏγασίας', + 'Assign automatically a color when due date is expired' => 'Αυτόματη εκχώÏηση χÏώματος όταν πεÏάσει η Ï€Ïοθεσμία της ημεÏομηνίας', + 'Total score in this column across all swimlanes' => 'Συνολικό ÏƒÎºÎ¿Ï Ï„Î·Ï‚ στήλης Î¼ÎµÏ„Î±Î¾Ï ÏŒÎ»Ï‰Î½ των λωÏίδων', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentine Peso', + 'COP - Colombian Peso' => 'COP - Colombian Peso', + '%d groups' => '%d ομάδες', + '%d group' => '%d ομάδα', + 'Group ID' => 'ΑναγνωÏιστικό ομάδας', + 'External ID' => 'ΕξωτεÏικό αναγνωÏιστικό', + '%d users' => '%d χÏήστες', + '%d user' => '%d χÏήστης', + 'Hide subtasks' => 'ΑπόκÏυψη υπο-εÏγασιών', + 'Show subtasks' => 'ΠÏοβολή υπο-εÏγασιών', + 'Authentication Parameters' => 'ΠαÏάμετÏοι αυθεντικοποίησης', + 'API Access' => 'ΠÏόσβαση στο API', + 'No users found.' => 'Δε βÏέθηκαν χÏήστες.', + 'User ID' => 'ΑναγνωÏιστικό χÏήστη', + 'Notifications are activated' => 'Οι ειδοποιήσεις είναι ενεÏγοποιημένες', + 'Notifications are disabled' => 'Οι ειδοποιήσεις είναι απενεÏγοποιημένες', + 'User disabled' => 'ΧÏήστης απενεÏγοποιημένος', + '%d notifications' => '%d ειδοποιήσεις', + '%d notification' => '%d ειδοποίηση', + 'There is no external integration installed.' => 'Δεν υπάÏχει εγκατεστημένη εξωτεÏική ενσωμάτωση.', + 'You are not allowed to update tasks assigned to someone else.' => 'Δεν επιτÏέπεται να ενημεÏώνετε εÏγασίες που έχουν ανατεθεί σε κάποιον άλλον.', + 'You are not allowed to change the assignee.' => 'Δεν επιτÏέπεται να αλλάξετε τον ανατιθέμενο.', + 'Task suppression is not permitted' => 'Δεν επιτÏέπεται η καταστολή εÏγασίας', + 'Changing assignee is not permitted' => 'Δεν επιτÏέπεται η αλλαγή ανατιθέμενου', + 'Update only assigned tasks is permitted' => 'ΕπιτÏέπεται μόνο η επεξεÏγασία ανατεθειμένων εÏγασιών', + 'Only for tasks assigned to the current user' => 'Μόνο για εÏγασίες που έχουν ανατεθεί στον Ï„Ïέχων χÏήστη', + 'My projects' => 'Τα έÏγα μου', + 'You are not a member of any project.' => 'Δεν είστε μέλος σε οποιοδήποτε έÏγο.', + 'My subtasks' => 'Οι υπο-εÏγασίες μου', + '%d subtasks' => '%d υπο-εÏγασίες', + '%d subtask' => '%d υπο-εÏγασία', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'ΕπιτÏέπεται μόνο η μετακίνηση εÏγασιών Î¼ÎµÏ„Î±Î¾Ï Ï„Ï‰Î½ στηλών που είναι ανατεθειμένες στον Ï„Ïέχων χÏήστη', + '[DUPLICATE]' => '[ΔΙΠΛΟΤΥΠΟ]', + 'DKK - Danish Krona' => 'DKK - Danish Krona', + 'Remove user from group' => 'ΑφαίÏεση χÏήση από ομάδα', + 'Assign the task to its creator' => 'Ανάθεση της εÏγασίας στον δημιουÏγό της', + 'This task was sent by email to "%s" with subject "%s".' => 'Η εÏγασία στάλθηκε μέσω email στον/στην «%s» με θέμα «%s».', + 'Predefined Email Subjects' => 'ΠÏοκαθοÏισμένα Θέματα Email', + 'Write one subject by line.' => 'Εισαγάγετε ένα θέμα ανά γÏαμμή.', + 'Create another link' => 'ΔημιουÏγία άλλου συνδέσμου', + 'BRL - Brazilian Real' => 'BRL - Brazilian Real', + 'Add a new Kanboard task' => 'ΠÏοσθήκη νέας εÏγασίας στο Kanboard', + 'Subtask not started' => 'Η υπο-εÏγασία δεν ξεκίνησε', + 'Subtask currently in progress' => 'Η υπο-εÏγασία είναι σε εξέλιξη', + 'Subtask completed' => 'Η υπο-εÏγασία ολοκληÏώθηκε', + 'Subtask added successfully.' => 'Η υπο-εÏγασία Ï€Ïοστέθηκε με επιτυχία.', + '%d subtasks added successfully.' => '%d υπο-εÏγασίες Ï€Ïοστέθηκαν με επιτυχία.', + 'Enter one subtask by line.' => 'Εισαγάγετε μια υπο-εÏγασία ανά γÏαμμή.', + 'Predefined Contents' => 'ΠÏοκαθοÏισμένα ΠεÏιεχόμενα', + 'Predefined contents' => 'ΠÏοκαθοÏισμένα πεÏιεχόμενα', + 'Predefined Task Description' => 'ΠÏοκαθοÏισμένη ΠεÏιγÏαφή ΕÏγασίας', + 'Do you really want to remove this template? "%s"' => 'Είστε σίγουÏοι για την αφαίÏεση του Ï€ÏοτÏπου; «%s»', + 'Add predefined task description' => 'ΠÏοσθήκη Ï€ÏοκαθοÏισμένης πεÏιγÏαφής εÏγασίας', + 'Predefined Task Descriptions' => 'ΠÏοκαθοÏισμένες ΠεÏιγÏαφές ΕÏγασιών', + 'Template created successfully.' => 'Το Ï€Ïότυπο δημιουÏγήθηκε με επιτυχία.', + 'Unable to create this template.' => 'Δεν ήταν δυνατή η δημιουÏγία του Ï€ÏοτÏπου.', + 'Template updated successfully.' => 'Το Ï€Ïότυπο ενημεÏώθηκε με επιτυχία.', + 'Unable to update this template.' => 'Δεν ήταν δυνατή η επεξεÏγασία του Ï€ÏοτÏπου.', + 'Template removed successfully.' => 'Το Ï€Ïότυπο αφαιÏέθηκε με επιτυχία.', + 'Unable to remove this template.' => 'Δεν ήταν δυνατή η αφαίÏεση του Ï€ÏοτÏπου.', + 'Template for the task description' => 'ΠÏότυπο για την πεÏιγÏαφή εÏγασίας', + 'The start date is greater than the end date' => 'Η ημεÏομηνία έναÏξης είναι μεταγενέστεÏη της λήξης', + 'Tags must be separated by a comma' => 'Οι σημάνσεις θα Ï€Ïέπει να χωÏίζονται με κόμμα', + 'Only the task title is required' => 'Απαιτείται μόνο ο τίτλος εÏγασίας', + 'Creator Username' => 'Όνομα ΧÏήστη ΔημιουÏγοÏ', + 'Color Name' => 'Όνομα ΧÏώματος', + 'Column Name' => 'Όνομα Στήλης', + 'Swimlane Name' => 'Όνομα ΛωÏίδας', + 'Time Estimated' => 'Εκτιμώμενος ΧÏόνος', + 'Time Spent' => 'ΧÏόνος που Δαπανήθηκε', + 'External Link' => 'ΕξωτεÏικός ΣÏνδεσμος', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Το χαÏακτηÏιστικό αυτό ενεÏγοποιεί τις Ïοές iCal, RSS και την Ï€Ïοβολή του δημόσιου πίνακα.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Îα σταματά ο χÏονομετÏητής όλων των εÏγασιών όταν μετακινείται μια εÏγασία σε άλλη στήλη', + 'Subtask Title' => 'Τίτλος Υπο-εÏγασίας', + 'Add a subtask and activate the timer when moving a task to another column' => 'ΠÏοσθήκη υπο-εÏγασίας και ενεÏγοποίηση του χÏονομετÏητή όταν μετακινείται μια εÏγασία σε άλλη στήλη', + 'days' => 'ημέÏες', + 'minutes' => 'λεπτά', + 'seconds' => 'δευτεÏόλεπτα', + 'Assign automatically a color when preset start date is reached' => 'Αυτόματη εκχώÏηση χÏώματος όταν φτάνει η ημεÏομηνία έναÏξης', + 'Move the task to another column once a predefined start date is reached' => 'Μετακίνηση της εÏγασίας σε άλλη στήλη όταν φτάσει η Ï€ÏοκαθοÏισμένη ημεÏομηνία έναÏξης', + 'This task is now linked to the task %s with the relation "%s"' => 'Η εÏγασία είναι τώÏα συνδεδεμένη με την εÏγασία %s με τη συσχέτιση «%s»', + 'The link with the relation "%s" to the task %s has been removed' => 'Ο σÏνδεσμος με τη συσχέτιση «%s» με την εÏγασία %s αφαιÏέθηκε', + 'Custom Filter:' => 'ΠÏοσαÏμοσμένο ΦίλτÏο:', + 'Unable to find this group.' => 'Δεν ήταν δυνατή η εÏÏεση της ομάδας.', + '%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 moved the task #%d to the swimlane "%s"' => 'Ο/Η %s μετακίνησε την εÏγασία #%d στην λωÏίδα «%s»', + '%sh spent' => '%sω δαπανήθηκαν', + '%sh estimated' => '%sω εκτιμώμενο', + 'Select All' => 'Επιλογή Όλων', + 'Unselect All' => 'ΑκÏÏωση Επιλογής Όλων', + 'Apply action' => 'ΕφαÏμογή ενέÏγειας', + 'Move selected tasks to another column or swimlane' => 'Μετακίνηση επιλεγμένων εÏγασιών σε άλλη στήλη ή λωÏίδα', + 'Edit tasks in bulk' => 'Μαζική επεξεÏγασία εÏγασιών', + 'Choose the properties that you would like to change for the selected tasks.' => 'Επιλογή των ιδιοτήτων που θέλετε να αλλάξετε για τις επιλεγμένες εÏγασίες.', + 'Configure this project' => 'ΠαÏαμετÏοποίηση του έÏγου', + 'Start now' => 'Εκκίνηση τώÏα', + '%s removed a file from the task #%d' => 'Ο/Η %s αφαίÏεσε ένα αÏχείο από την εÏγασία #%d', + 'Attachment removed from task #%d: %s' => 'ΑφαιÏέθηκε συνημμένο από την εÏγασία #%d: %s', + 'No color' => 'ΧωÏίς χÏώμα', + 'Attachment removed "%s"' => 'ΑφαίÏεση συνημμένου «%s»', + '%s removed a file from the task %s' => 'Ο/Η %s αφαίÏεσε ένα αÏχείο από την εÏγασία %s', + 'Move the task to another swimlane when assigned to a user' => 'Μετακίνηση της εÏγασίας σε άλλη στήλη όταν ανατίθεται σε ένα χÏήστη', + 'Destination swimlane' => 'ΛωÏίδα Ï€ÏοοÏισμοÏ', + 'Assign a category when the task is moved to a specific swimlane' => 'ΕκχώÏηση κατηγοÏίας όταν η εÏγασία μετακινείται σε συγκεκÏιμένη λωÏίδα', + 'Move the task to another swimlane when the category is changed' => 'Μετακίνηση της εÏγασίας σε άλλη λωÏίδα όταν αλλάζει η κατηγοÏία', + 'Reorder this column by priority (ASC)' => 'Ταξινόμηση της στήλης με την Ï€ÏοτεÏαιότητα (ΑΥΞ)', + 'Reorder this column by priority (DESC)' => 'Ταξινόμηση της στήλης με την Ï€ÏοτεÏαιότητα (ΦΘΙ)', + 'Reorder this column by assignee and priority (ASC)' => 'Ταξινόμηση της στήλης με τον ανατιθέμενο και Ï€ÏοτεÏαιότητα (ΑΥΞ)', + 'Reorder this column by assignee and priority (DESC)' => 'Ταξινόμηση της στήλης με τον ανατιθέμενο και Ï€ÏοτεÏαιότητα (ΦΘΙ)', + 'Reorder this column by assignee (A-Z)' => 'Ταξινόμηση της στήλης με τον ανατιθέμενο (ΑΥΞ)', + 'Reorder this column by assignee (Z-A)' => 'Ταξινόμηση της στήλης με τον ανατιθέμενο (ΦΘΙ)', + 'Reorder this column by due date (ASC)' => 'Ταξινόμηση της στήλης με την ημεÏομηνία Ï€Ïοθεσμίας (ΑΥΞ)', + 'Reorder this column by due date (DESC)' => 'Ταξινόμηση της στήλης με την ημεÏομηνία Ï€Ïοθεσμίας (ΦΘΙ)', + 'Reorder this column by id (ASC)' => 'Ταξινόμηση της στήλης με το αναγνωÏιστικό (ΑΥΞ)', + 'Reorder this column by id (DESC)' => 'Ταξινόμηση της στήλης με το αναγνωÏιστικό (ΦΘΙ)', + '%s moved the task #%d "%s" to the project "%s"' => 'Ο/Η %s μετακίνησε την εÏγασία #%d «%s» στο έÏγο «%s»', + 'Task #%d "%s" has been moved to the project "%s"' => 'Η εÏγασία #%d «%s» μετακινήθηκε στο έÏγο «%s»', + 'Move the task to another column when the due date is less than a certain number of days' => 'Μετακίνηση της εÏγασίας σε άλλη στήλη όταν η ημεÏομηνία Ï€Ïοθεσμίας είναι μικÏότεÏη από συγκεκÏιμένο αÏιθμό ημεÏών', + 'Automatically update the start date when the task is moved away from a specific column' => 'Αυτόματη ενημέÏωση της ημεÏομηνίας έναÏξης όταν η εÏγασία μετακινείται εκτός συγκεκÏιμένης στήλης', + 'HTTP Client:' => 'Πελάτης HTTP:', + 'Assigned' => 'Ανατεθειμένο', + 'Task limits apply to each swimlane individually' => 'Τα ÏŒÏια εÏγασιών εφαÏμόζονται σε κάθε λωÏίδα ξεχωÏιστά', + 'Column task limits apply to each swimlane individually' => 'Τα ÏŒÏια των εÏγασιών στις στήλες αφοÏοÏν κάθε λωÏίδα ξεχωÏιστά', + 'Column task limits are applied to each swimlane individually' => 'Τα ÏŒÏια των εÏγασιών στις στήλες εφαÏμόζονται σε κάθε λωÏίδα ξεχωÏιστά', + 'Column task limits are applied across swimlanes' => 'Τα ÏŒÏια των εÏγασιών στις στήλες εφαÏμόζονται σε όλες τις λωÏίδες', + 'Task limit: ' => 'ÎŒÏιο εÏγασιών: ', + 'Change to global tag' => 'Αλλαγή σε καθολική ετικέτα', + 'Do you really want to make the tag "%s" global?' => 'Είστε σίγουÏοι για την μετατÏοπή της ετικέτας «%s» σε καθολική;', + 'Enable global tags for this project' => 'ΕνεÏγοποίηση καθολικών σημάνσεων για το έÏγο', + 'Group membership(s):' => 'Ιδιοκτησία σε ομάδα(ες):', + '%s is a member of the following group(s): %s' => 'Ο/Η %s είναι μέλος των παÏακάτω ομάδων: %s', + '%d/%d group(s) shown' => 'Εμφανίζονται %d/%d ομάδες', + 'Subtask creation or modification' => 'ΔημιουÏγία ή Ï„Ïοποποίηση υπο-εÏγασιών', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Ανάθεση της εÏγασίας σε συγκεκÏιμένο χÏήστη όταν η εÏγασία μετακινείται σε συγκεκÏιμένη λωÏίδα', + 'Comment' => 'Σχόλιο', + 'Collapse vertically' => 'ΣÏμπτυξη κάθετα', + 'Expand vertically' => 'Ανάπτυξη κάθετα', + 'MXN - Mexican Peso' => 'MXN - Mexican Peso', + 'Estimated vs actual time per column' => 'Εκτιμώμενος έναντι Ï€ÏÎ±Î³Î¼Î±Ï„Î¹ÎºÎ¿Ï Ï‡Ïόνου ανά στήλη', + 'HUF - Hungarian Forint' => 'HUF - Hungarian Forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'ΠÏέπει να επιλέξετε ένα αÏχείο για ανέβασμα ως το Î¬Î²Î±Ï„Î¬Ï ÏƒÎ±Ï‚!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Το αÏχείο που ανεβάσατε δεν είναι έγκυÏη εικόνα! (ΕπιτÏέπονται μόνο *.gif, *.jpg, *.jpeg και *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Αυτόματος οÏισμός της ημεÏομηνίας Ï€Ïοθεσμίας όταν η εÏγασία μετακινείται εκτός συγκεκÏιμένης στήλης', + 'No other projects found.' => 'Δε βÏέθηκαν άλλα έÏγα', + 'Tasks copied successfully.' => 'Οι εÏγασίες αντιγÏάφηκαν με επιτυχία.', + 'Unable to copy tasks.' => 'Δεν ήταν δυνατή η αντιγÏαφή των εÏγασιών.', + 'Theme' => 'Θέμα', + 'Theme:' => 'Θέμα:', + 'Light theme' => 'ΑνοιχτόχÏωμο θέμα', + 'Dark theme' => 'ΣκοτεινόχÏωμο θέμα', + 'Automatic theme - Sync with system' => 'Αυτόματο θέμα - ΣυγχÏονισμός με το σÏστημα', + 'Application managers or more' => 'Συντονιστές εφαÏμογής ή πεÏισσότεÏο', + 'Administrators' => 'ΔιαχειÏιστές', + 'Visibility:' => 'ΟÏατότητα', + 'Standard users' => 'Απλοί χÏήστες', + 'Visibility is required' => 'Η οÏατότητα είναι απαιτοÏμενη', + 'The visibility should be an app role' => 'Η οÏατότητα Ï€Ïέπει να είναι Ïόλος της εφαÏμογής', + 'Reply' => 'Απάντηση', + '%s wrote: ' => 'Ο/Η %s έγÏαψε: ', + 'Number of visible tasks in this column and swimlane' => 'ΑÏιθμός οÏατών εÏγασιών σε αυτήν τη στήλη και τη λωÏίδα κολÏμβησης', + 'Number of tasks in this swimlane' => 'ΑÏιθμός εÏγασιών σε αυτήν τη λωÏίδα κολÏμβησης', + 'Unable to find another subtask in progress, you can close this window.' => 'Δεν είναι δυνατή η εÏÏεση άλλης δευτεÏεÏουσας εÏγασίας σε εξέλιξη, μποÏείτε να κλείσετε αυτό το παÏάθυÏο.', + 'This theme is invalid' => 'Αυτό το θέμα είναι μη έγκυÏο', + 'This role is invalid' => 'Αυτός ο Ïόλος είναι μη έγκυÏος', + 'This timezone is invalid' => 'Αυτή η ζώνη ÏŽÏας είναι μη έγκυÏη', + 'This language is invalid' => 'Αυτή η γλώσσα είναι μη έγκυÏη', + 'This URL is invalid' => 'Αυτό το URL είναι μη έγκυÏο', + 'Date format invalid' => 'Μη έγκυÏη μοÏφή ημεÏομηνίας', + 'Time format invalid' => 'Μη έγκυÏη μοÏφή ÏŽÏας', + 'Invalid Mail transport' => 'Μη έγκυÏη μεταφοÏά αλληλογÏαφίας', + 'Color invalid' => 'Μη έγκυÏο χÏώμα', + 'This value must be greater or equal to %d' => 'Αυτή η τιμή Ï€Ïέπει να είναι μεγαλÏτεÏη ή ίση με %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'ΠÏοσθήκη BOM στην αÏχή του αÏχείου (απαιτείται για το Microsoft Excel)', + 'Just add these tag(s)' => 'Απλώς Ï€Ïοσθέστε αυτές τις ετικέτες', + 'Remove internal link(s)' => 'ΑφαίÏεση εσωτεÏικοÏ(ών) συνδέσμου(ων)', + 'Import tasks from another project' => 'Εισαγωγή εÏγασιών από άλλο έÏγο', + 'Select the project to copy tasks from' => 'Επιλέξτε το έÏγο από το οποίο θα αντιγÏάψετε εÏγασίες', + 'The total maximum allowed attachments size is %sB.' => 'Το συνολικό μέγιστο επιτÏεπόμενο μέγεθος συνημμένων είναι %sB.', + 'Add attachments' => 'ΠÏοσθήκη συνημμένων', + 'Task #%d "%s" is overdue' => 'Η εÏγασία #%d «%s» είναι εκπÏόθεσμη', + 'Enable notifications by default for all new users' => 'ΕνεÏγοποίηση ειδοποιήσεων από Ï€Ïοεπιλογή για όλους τους νέους χÏήστες', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Ανάθεση της εÏγασίας στον δημιουÏγό της για συγκεκÏιμένες στήλες, αν δεν έχει οÏιστεί υπεÏθυνος χειÏοκίνητα', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Ανάθεση μιας εÏγασίας στον συνδεδεμένο χÏήστη κατά την αλλαγή στήλης Ï€Ïος την καθοÏισμένη στήλη, αν δεν έχει ανατεθεί χÏήστης', +]; diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php new file mode 100644 index 0000000..75f38b7 --- /dev/null +++ b/app/Locale/es_ES/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Ninguno', + 'Edit' => 'Modificar', + 'Remove' => 'Suprimir', + 'Yes' => 'Sí', + 'No' => 'No', + 'cancel' => 'cancelar', + 'or' => 'o', + 'Yellow' => 'Amarillo', + 'Blue' => 'Azul', + 'Green' => 'Verde', + 'Purple' => 'Púrpura', + 'Red' => 'Rojo', + 'Orange' => 'Naranja', + 'Grey' => 'Gris', + 'Brown' => 'Marron', + 'Deep Orange' => 'Naranja', + 'Dark Grey' => 'Gris Oscuro', + 'Pink' => 'Rosado', + 'Teal' => 'Verde Azulado', + 'Cyan' => 'Azul Claro', + 'Lime' => 'Lima', + 'Light Green' => 'Verde Claro', + 'Amber' => 'Ãmbar', + 'Save' => 'Guardar', + 'Login' => 'Iniciar sesión (Ingresar)', + 'Official website:' => 'Página web oficial :', + 'Unassigned' => 'No asignado', + 'View this task' => 'Ver esta tarea', + 'Remove user' => 'Suprimir un usuario', + 'Do you really want to remove this user: "%s"?' => '¿Realmente quiere suprimir este usuario: « %s » ?', + 'All users' => 'Todos los usuarios', + 'Username' => 'Nombre de usuario', + 'Password' => 'Contraseña', + 'Administrator' => 'Administrador', + 'Sign in' => 'Iniciar sesión', + 'Users' => 'Usuarios', + 'Forbidden' => 'Acceso denegado', + 'Access Forbidden' => 'Acceso denegado', + 'Edit user' => 'Editar un usuario', + 'Logout' => 'Salir', + 'Bad username or password' => 'Usuario o contraseña incorecto', + 'Edit project' => 'Editar el proyecto', + 'Name' => 'Nombre', + 'Projects' => 'Proyectos', + 'No project' => 'Ningún proyecto', + 'Project' => 'Proyecto', + 'Status' => 'Estado', + 'Tasks' => 'Tareas', + 'Board' => 'Tablero', + 'Actions' => 'Acciones', + 'Inactive' => 'Inactivo', + 'Active' => 'Activo', + 'Unable to update this board.' => 'No se puede actualizar este tablero.', + 'Disable' => 'Desactivar', + 'Enable' => 'Activar', + 'New project' => 'Nuevo proyecto', + 'Do you really want to remove this project: "%s"?' => '¿Realmente quiere suprimir este proyecto: « %s » ?', + 'Remove project' => 'Suprimir el proyecto', + 'Edit the board for "%s"' => 'Modificar el tablero para « %s »', + 'Add a new column' => 'Añadir una nueva columna', + 'Title' => 'Titulo', + 'Assigned to %s' => 'Asignada a %s', + 'Remove a column' => 'Suprimir esta columna', + 'Unable to remove this column.' => 'No se puede suprimir esta columna.', + 'Do you really want to remove this column: "%s"?' => '¿Realmente quiere suprimir esta columna : « %s » ?', + 'Settings' => 'Preferencias', + 'Application settings' => 'Parámetros de la aplicación', + 'Language' => 'Idioma', + 'Webhook token:' => 'Token para los webhooks :', + 'API token:' => 'Token de API:', + 'Database size:' => 'Tamaño de la base de datos:', + 'Download the database' => 'Descargar la base de datos', + 'Optimize the database' => 'Optimizar la base de datos', + '(VACUUM command)' => '(Comando VACUUM)', + '(Gzip compressed Sqlite file)' => '(Archivo Sqlite comprimido en Gzip)', + 'Close a task' => 'Cerrar una tarea', + 'Column' => 'Columna', + 'Color' => 'Color', + 'Assignee' => 'Persona asignada', + 'Create another task' => 'Crear una nueva tarea', + 'New task' => 'Nueva tarea', + 'Open a task' => 'Abrir una tarea', + 'Do you really want to open this task: "%s"?' => '¿Realmente quiere abrir esta tarea: « %s » ?', + 'Back to the board' => 'Volver al tablero', + 'There is nobody assigned' => 'No hay nadie asignado a esta tarea', + 'Column on the board:' => 'Columna en el tablero: ', + 'Close this task' => 'Cerrar esta tarea', + 'Open this task' => 'Abrir esta tarea', + 'There is no description.' => 'No hay descripción.', + 'Add a new task' => 'Añadir una nueva tarea', + 'The username is required' => 'El nombre de usuario es obligatorio', + 'The maximum length is %d characters' => 'La longitud máxima es de %d caracteres', + 'The minimum length is %d characters' => 'La longitud mínima es de %d caracteres', + 'The password is required' => 'La contraseña es obligatoria', + 'This value must be an integer' => 'Este valor debe ser un entero', + 'The username must be unique' => 'El nombre de usuario debe ser único', + 'The user id is required' => 'El identificador del usuario es obligatorio', + 'Passwords don\'t match' => 'Las contraseñas no coinciden', + 'The confirmation is required' => 'La confirmación es obligatoria', + 'The project is required' => 'El proyecto es obligatorio', + 'The id is required' => 'El identificador es obligatorio', + 'The project id is required' => 'El identificador del proyecto es obligatorio', + 'The project name is required' => 'El nombre del proyecto es obligatorio', + 'The title is required' => 'El título es obligatorio', + 'Settings saved successfully.' => 'Parámetros guardados correctamente.', + 'Unable to save your settings.' => 'No se pueden guardar sus parámetros.', + 'Database optimization done.' => 'Optimización de la base de datos terminada.', + 'Your project has been created successfully.' => 'El proyecto ha sido creado correctamente.', + 'Unable to create your project.' => 'No se puede crear el proyecto.', + 'Project updated successfully.' => 'El proyecto ha sido actualizado correctamente.', + 'Unable to update this project.' => 'No se puede actualizar el proyecto.', + 'Unable to remove this project.' => 'No se puede suprimir este proyecto.', + 'Project removed successfully.' => 'El proyecto ha sido borrado correctamente.', + 'Project activated successfully.' => 'El proyecto ha sido activado correctamente.', + 'Unable to activate this project.' => 'No se puede activar el proyecto.', + 'Project disabled successfully.' => 'El proyecto ha sido desactivado correctamente.', + 'Unable to disable this project.' => 'No se puede desactivar el proyecto.', + 'Unable to open this task.' => 'No se puede abrir esta tarea.', + 'Task opened successfully.' => 'La tarea ha sido abierta correctamente.', + 'Unable to close this task.' => 'No se puede cerrar esta tarea.', + 'Task closed successfully.' => 'La tarea ha sido cerrada correctamente.', + 'Unable to update your task.' => 'No se puede modificar esta tarea.', + 'Task updated successfully.' => 'La tarea ha sido actualizada correctamente.', + 'Unable to create your task.' => 'No se puede crear esta tarea.', + 'Task created successfully.' => 'La tarea ha sido creada correctamente.', + 'User created successfully.' => 'El usuario ha sido creado correctamente.', + 'Unable to create your user.' => 'No se puede crear este usuario.', + 'User updated successfully.' => 'El usuario ha sido actualizado correctamente.', + 'User removed successfully.' => 'El usuario ha sido creado correctamente.', + 'Unable to remove this user.' => 'No se puede crear este usuario.', + 'Board updated successfully.' => 'El tablero ha sido actualizado correctamente.', + 'Ready' => 'Listo', + 'Backlog' => 'En espera', + 'Work in progress' => 'En curso', + 'Done' => 'Hecho', + 'Application version:' => 'Versión de la aplicación:', + 'Id' => 'Identificador', + 'Public link' => 'Vinculación pública', + 'Timezone' => 'Zona horaria', + 'Sorry, I didn\'t find this information in my database!' => 'Lo siento no he encontrado información en la base de datos!', + 'Page not found' => 'Página no encontrada', + 'Complexity' => 'Complejidad', + 'Task limit' => 'Número máximo de tareas', + 'Task count' => 'Conteo de tareas', + 'User' => 'Usuario', + 'Comments' => 'Comentarios', + 'Comment is required' => 'El comentario es obligatorio', + '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', + 'Invalid date' => 'Fecha no válida', + 'Automatic actions' => 'Acciones automatizadas', + 'Your automatic action has been created successfully.' => 'La acción automatizada ha sido creada correctamente.', + 'Unable to create your automatic action.' => 'No se puede crear esta acción automatizada.', + 'Remove an action' => 'Suprimir una acción', + 'Unable to remove this action.' => 'No se puede suprimir esta accción.', + 'Action removed successfully.' => 'La acción ha sido borrada correctamente.', + 'Automatic actions for the project "%s"' => 'Acciones automatizadas para este proyecto « %s »', + 'Add an action' => 'Agregar una acción', + 'Event name' => 'Nombre del evento', + 'Action' => 'Acción', + 'Event' => 'Evento', + 'When the selected event occurs execute the corresponding action.' => 'Cuando tiene lugar el evento seleccionado, ejecutar la acción correspondiente.', + 'Next step' => 'Etapa siguiente', + 'Define action parameters' => 'Definición de los parametros de la acción', + 'Do you really want to remove this action: "%s"?' => '¿Realmente quiere suprimir esta acción « %s » ?', + 'Remove an automatic action' => 'Suprimir una acción automatizada', + 'Assign the task to a specific user' => 'Asignar una tarea a un usuario especifico', + 'Assign the task to the person who does the action' => 'Asignar la tarea al usuario que hace la acción', + 'Duplicate the task to another project' => 'Duplicar la tarea a otro proyecto', + 'Move a task to another column' => 'Mover una tarea a otra columna', + 'Task modification' => 'Modificación de una tarea', + 'Task creation' => 'Creación de una tarea', + 'Closing a task' => 'Cerrar una tarea', + 'Assign a color to a specific user' => 'Asignar un color a un usuario específico', + 'Position' => 'Posición', + 'Duplicate to project' => 'Duplicar a otro proyecto', + 'Duplicate' => 'Duplicar', + 'Link' => 'Enlace', + 'Comment updated successfully.' => 'El comentario ha sido actualizado correctamente.', + 'Unable to update your comment.' => 'No se puede actualizar este comentario.', + 'Remove a comment' => 'Suprimir un comentario', + 'Comment removed successfully.' => 'El comentario ha sido suprimido correctamente.', + 'Unable to remove this comment.' => 'No se puede suprimir este comentario.', + 'Do you really want to remove this comment?' => '¿Realmente quiere suprimir este comentario?', + 'Current password for the user "%s"' => 'Contraseña actual para el usuario: « %s »', + 'The current password is required' => 'La contraseña es obligatoria', + 'Wrong password' => 'contraseña incorrecta', + 'Unknown' => 'Desconocido', + 'Last logins' => 'Últimos ingresos', + 'Login date' => 'Fecha de ingreso', + 'Authentication method' => 'Método de autenticación', + 'IP address' => 'Dirección IP', + 'User agent' => 'Agente de usuario', + 'Persistent connections' => 'Conexión persistente', + 'No session.' => 'No existe sesión.', + 'Expiration date' => 'Fecha de expiración', + 'Remember Me' => 'Recuérdame', + 'Creation date' => 'Fecha de creación', + 'Everybody' => 'Todo el mundo', + 'Open' => 'Abierto', + 'Closed' => 'Cerrado', + 'Search' => 'Buscar', + 'Nothing found.' => 'Nada hallado.', + 'Due date' => 'Fecha Límite', + 'Description' => 'Descripción', + '%d comments' => '%d comentarios', + '%d comment' => '%d comentario', + 'Email address invalid' => 'Dirección de correo inválida', + 'Your external account is not linked anymore to your profile.' => 'Tu cuenta externa no está vinculada a tu perfil.', + 'Unable to unlink your external account.' => 'No se puede desvincular su cuenta externa.', + 'External authentication failed' => 'Error de autenticación externa', + 'Your external account is linked to your profile successfully.' => 'Su cuenta externa está vinculada a su perfil correctamente.', + 'Email' => 'Correo', + 'Task removed successfully.' => 'Tarea suprimida correctamente.', + 'Unable to remove this task.' => 'No pude suprimir esta tarea.', + 'Remove a task' => 'Borrar una tarea', + 'Do you really want to remove this task: "%s"?' => '¿Realmente quiere suprimir esta tarea: "%s"?', + 'Assign automatically a color based on a category' => 'Asignar un color de forma automática basándose en la categoría', + 'Assign automatically a category based on a color' => 'Asignar una categoría de forma automática basándose en el color', + 'Task creation or modification' => 'Creación o Edición de Tarea', + 'Category' => 'Categoría', + 'Category:' => 'Categoría:', + 'Categories' => 'Categorías', + 'Your category has been created successfully.' => 'Se ha creado tu categoría correctamente.', + 'This category has been updated successfully.' => 'Esta categoría se ha actualizado correctamente.', + 'Unable to update this category.' => 'No se puede actualizar esta categoría.', + 'Remove a category' => 'Suprimir una categoría', + 'Category removed successfully.' => 'Categoría suprimida correctamente.', + 'Unable to remove this category.' => 'No pude suprimir esta categoría.', + 'Category modification for the project "%s"' => 'Modificación de categoría para el proyecto "%s"', + 'Category Name' => 'Nombre de Categoría', + 'Add a new category' => 'Añadir una nueva categoría', + 'Do you really want to remove this category: "%s"?' => '¿Realmente quiere suprimir esta categoría: "%s"?', + 'All categories' => 'Todas las categorías', + 'No category' => 'Sin categoría', + 'The name is required' => 'El nombre es obligatorio', + 'Remove a file' => 'Borrar un fichero', + 'Unable to remove this file.' => 'No pude borrar este fichero.', + 'File removed successfully.' => 'Fichero borrado correctamente.', + 'Attach a document' => 'Adjuntar un documento', + 'Do you really want to remove this file: "%s"?' => '¿Realmente quiere borrar este fichero: "%s"?', + 'Attachments' => 'Adjuntos', + 'Edit the task' => 'Editar la tarea', + 'Add a comment' => 'Añadir un comentario', + 'Edit a comment' => 'Editar un comentario', + 'Summary' => 'Resumen', + 'Time tracking' => 'Control de tiempo', + 'Estimate:' => 'Estimado:', + 'Spent:' => 'Transcurrido:', + 'Do you really want to remove this sub-task?' => '¿Realmente quiere suprimir esta subtarea?', + 'Remaining:' => 'Quedando', + 'hours' => 'horas', + 'estimated' => 'estimado', + 'Sub-Tasks' => 'Sub-Tareas', + 'Add a sub-task' => 'Añadir una subtarea', + 'Original estimate' => 'Horas Presupuestadas', + 'Create another sub-task' => 'Crear otra subtarea', + 'Time spent' => 'Horas Ejecutadas', + 'Edit a sub-task' => 'Editar una subtarea', + 'Remove a sub-task' => 'Suprimir una subtarea', + 'The time must be a numeric value' => 'El tiempo debe de ser un valor numérico', + 'Todo' => 'Por hacer', + 'In progress' => 'En progreso', + 'Sub-task removed successfully.' => 'Sub-tarea suprimida correctamente.', + 'Unable to remove this sub-task.' => 'No pude suprimir esta subtarea.', + 'Sub-task updated successfully.' => 'Sub-tarea actualizada correctamente.', + 'Unable to update your sub-task.' => 'No pude actualizar tu subtarea.', + 'Unable to create your sub-task.' => 'No pude crear tu subtarea.', + 'Maximum size: ' => 'Tamaño máximo', + 'Display another project' => 'Mostrar otro proyecto', + 'Created by %s' => 'Creado por %s', + 'Tasks Export' => 'Exportar tareas', + 'Start Date' => 'Fecha de Inicio', + 'Execute' => 'Ejecutar', + 'Task Id' => 'ID de tarea', + 'Creator' => 'Creador', + 'Modification date' => 'Fecha de modificación', + 'Completion date' => 'Fecha de Fin', + 'Clone' => 'Clonar', + 'Project cloned successfully.' => 'Proyecto clonado correctamente', + 'Unable to clone this project.' => 'Impsible clonar proyecto', + 'Enable email notifications' => 'Habilitar notificaciones por correo electrónico', + 'Task position:' => 'Posición de la tarea', + 'The task #%d has been opened.' => 'La tarea #%d ha sido abierta', + 'The task #%d has been closed.' => 'La tarea #%d ha sido cerrada', + 'Sub-task updated' => 'Subtarea actualizada', + 'Title:' => 'Título:', + 'Status:' => 'Estado:', + 'Assignee:' => 'Asignada a:', + 'Time tracking:' => 'Control de tiempo:', + 'New sub-task' => 'Nueva subtarea', + 'New attachment added "%s"' => 'Nuevo adjunto agregado "%s"', + 'New comment posted by %s' => 'Nuevo comentario agregado por %s', + 'New comment' => 'Nuevo comentario', + 'Comment updated' => 'Comentario actualizado', + 'New subtask' => 'Nueva subtarea', + 'I only want to receive notifications for these projects:' => 'Quiero recibir notificaciones sólo de estos proyectos:', + 'view the task on Kanboard' => 'ver la tarea en Kanboard', + 'Public access' => 'Acceso público', + 'Disable public access' => 'Desactivar acceso público', + 'Enable public access' => 'Activar acceso público', + 'Public access disabled' => 'Acceso público desactivado', + 'Move the task to another project' => 'Mover la tarea a otro proyecto', + 'Move to project' => 'Mover a otro proyecto', + 'Do you really want to duplicate this task?' => '¿Realmente quiere duplicar esta tarea?', + 'Duplicate a task' => 'Duplicar una tarea', + 'External accounts' => 'Cuentas externas', + 'Account type' => 'Tipo de Cuenta', + 'Local' => 'Local', + 'Remote' => 'Remota', + 'Enabled' => 'Activada', + 'Disabled' => 'Deactivada', + 'Login:' => 'Login:', + 'Full Name:' => 'Nombre completo:', + 'Email:' => 'Correo electrónico:', + 'Notifications:' => 'Notificaciones:', + 'Notifications' => 'Notificaciones', + 'Account type:' => 'Tipo de Cuenta:', + 'Edit profile' => 'Editar perfil', + 'Change password' => 'Cambiar contraseña', + 'Password modification' => 'Modificacion de contraseña', + 'External authentications' => 'Autenticación externa', + 'Never connected.' => 'Nunca se ha conectado.', + 'No external authentication enabled.' => 'Sin autenticación externa activa.', + 'Password modified successfully.' => 'Contraseña cambiada correctamente.', + 'Unable to change the password.' => 'No pude cambiar la contraseña.', + 'Change category' => 'Cambiar categoría', + '%s updated the task %s' => '%s actualizó la tarea %s', + '%s opened the task %s' => '%s abrió la tarea %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s movió la tarea %s a la posición #%d de la columna "%s"', + '%s moved the task %s to the column "%s"' => '%s movió la tarea %s a la columna "%s"', + '%s created the task %s' => '%s creó la tarea %s', + '%s closed the task %s' => '%s cerró la tarea %s', + '%s created a subtask for the task %s' => '%s creó una subtarea para la tarea %s', + '%s updated a subtask for the task %s' => '%s actualizó una subtarea para la tarea %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Asignada a %s con una estimación de %s/%sh', + 'Not assigned, estimate of %sh' => 'No asignada, se estima en %sh', + '%s updated a comment on the task %s' => '%s actualizó un comentario de la tarea %s', + '%s commented the task %s' => '%s comentó la tarea %s', + '%s\'s activity' => 'Actividad de %s', + 'RSS feed' => 'Fichero RSS', + '%s updated a comment on the task #%d' => '%s actualizó un comentario de la tarea #%d', + '%s commented on the task #%d' => '%s comentó la tarea #%d', + '%s updated a subtask for the task #%d' => '%s actualizó una subtarea de la tarea #%d', + '%s created a subtask for the task #%d' => '%s creó una subtarea de la tarea #%d', + '%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 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 de los nuevos proyectos (Separadas mediante comas)', + 'Task assignee change' => 'Cambiar persona asignada a la tarea', + '%s changed the assignee of the task #%d to %s' => '%s cambió al asignado de la tarea #%d a %s', + '%s changed the assignee of the task %s to %s' => '%s cambió la persona asignada de la tarea de %s a %s', + 'New password for the user "%s"' => 'Nueva contraseña para el usuario "%s"', + 'Choose an event' => 'Escoja un evento', + 'Create a task from an external provider' => 'Crear una tarea a partir de un proveedor externo', + 'Change the assignee based on an external username' => 'Cambiar la asignación basado en un nombre de usuario externo', + 'Change the category based on an external label' => 'Cambiar la categoría basado en una etiqueta externa', + 'Reference' => 'Referencia', + 'Label' => 'Etiqueta', + 'Database' => 'Base de Datos', + 'About' => 'Acerca de', + 'Database driver:' => 'Driver de la base de datos', + 'Board settings' => 'Configuraciones del Tablero', + 'Webhook settings' => 'Configuraciones del Webhook', + 'Reset token' => 'Resetear token', + 'API endpoint:' => 'Punto final del API', + 'Refresh interval for personal board' => 'Intervalo de refrescamiento del tablero privado', + 'Refresh interval for public board' => 'Intervalo de refrescamiento del tablero público', + 'Task highlight period' => 'Periodo del realce de la tarea', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (en segundos) para considerar que una tarea fué modificada recientemente (0 para deshabilitar, 2 días por defecto)', + 'Frequency in second (60 seconds by default)' => 'Frecuencia en segundos (60 segundos por defecto)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frecuencia en segundos (0 para deshabilitar esta característica, 10 segundos por defecto)', + 'Application URL' => 'URL de la aplicación', + 'Token regenerated.' => 'Token regenerado', + 'Date format' => 'Formato de la fecha', + 'ISO format is always accepted, example: "%s" and "%s"' => 'El formato ISO siempre es aceptado, ejemplo: "%s" y "%s"', + 'New personal project' => 'Nuevo proyecto privado', + 'This project is personal' => 'Este proyecto es privado', + 'Add' => 'Añadir', + 'Start date' => 'Fecha de inicio', + 'Time estimated' => 'Horas Presupuestadas', + 'There is nothing assigned to you.' => 'Esto no le está asignado', + 'My tasks' => 'Mis tareas', + 'Activity stream' => 'Flujo de actividad', + 'Dashboard' => 'Tablero', + 'Confirmation' => 'Confirmación', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Crear un comentario a partir de un proveedor externo', + 'Project management' => 'Gestión de proyectos', + 'Columns' => 'Columnas', + 'Task' => 'Tarea', + 'Percentage' => 'Porcentaje', + 'Number of tasks' => 'Número de tareas', + 'Task distribution' => 'Distribución de tareas', + 'Analytics' => 'Analítica', + 'Subtask' => 'Subtarea', + 'User repartition' => 'Repartición de usuarios', + 'Clone this project' => 'Clonar este proyecto', + 'Column removed successfully.' => 'Columna removida correctamente', + 'Not enough data to show the graph.' => 'No hay suficiente información para mostrar el gráfico', + 'Previous' => 'Anterior', + 'The id must be an integer' => 'El id debe ser un entero', + 'The project id must be an integer' => 'El id del proyecto debe ser un entero', + 'The status must be an integer' => 'El estado debe ser un entero', + 'The subtask id is required' => 'El id de la subtarea es requerido', + 'The subtask id must be an integer' => 'El id de la subtarea debe ser un entero', + 'The task id is required' => 'El id de la tarea es requerido', + 'The task id must be an integer' => 'El id de la tarea debe ser un entero', + 'The user id must be an integer' => 'El id del usuario debe ser un entero', + 'This value is required' => 'El valor es requerido', + 'This value must be numeric' => 'Este valor debe ser numérico', + 'Unable to create this task.' => 'Imposible crear esta tarea', + 'Cumulative flow diagram' => 'Diagrama de flujo acumulativo', + 'Daily project summary' => 'Resumen diario del proyecto', + 'Daily project summary export' => 'Exportar sumario diario del proyecto', + 'Exports' => 'Exportar', + 'This export contains the number of tasks per column grouped per day.' => 'Esta exportación contiene el número de tereas por columna agrupada por día', + 'Active swimlanes' => 'Carriles activos', + 'Add a new swimlane' => 'Añadir nuevo carril', + 'Default swimlane' => 'Carril por defecto', + 'Do you really want to remove this swimlane: "%s"?' => '¿Realmente quiere remover este carril: "%s"?', + 'Inactive swimlanes' => 'Carriles inactivos', + 'Remove a swimlane' => 'Remover un carril', + 'Swimlane modification for the project "%s"' => 'Modificación del carril para el proyecto "%s"', + 'Swimlane removed successfully.' => 'Carril removido correctamente', + 'Swimlanes' => 'Carriles', + 'Swimlane updated successfully.' => 'Carril actualizado correctamente', + 'Unable to remove this swimlane.' => 'Imposible remover este carril', + 'Unable to update this swimlane.' => 'Imposible actualizar este carril', + 'Your swimlane has been created successfully.' => 'Su carril ha sido creado correctamente', + 'Example: "Bug, Feature Request, Improvement"' => 'Ejemplo: "Error, Solicitud de característica, Mejora"', + 'Default categories for new projects (Comma-separated)' => 'Categorías por defecto de los nuevos proyectos (Separadas mediante comas)', + 'Integrations' => 'Integraciones', + 'Integration with third-party services' => 'Integraciones para servicios de terceros', + 'Subtask Id' => 'ID de Subtareas', + 'Subtasks' => 'Subtareas', + 'Subtasks Export' => 'Exportar Subtareas', + 'Task Title' => 'Título de la tarea', + 'Untitled' => 'Sin título', + 'Application default' => 'Predefinido de la aplicación', + 'Language:' => 'Idioma', + 'Timezone:' => 'Zona horaria', + 'All columns' => 'Todas las columnas', + 'Next' => 'Siguiente', + '#%d' => '#%d', + 'All swimlanes' => 'Todos los carriles', + 'All colors' => 'Todos los colores', + 'Moved to column %s' => 'Movido a columna %s', + 'User dashboard' => 'Tablero del usuario', + 'Allow only one subtask in progress at the same time for a user' => 'Permitir únicamente una subtarea en progreso al mismo tiempo para un usuario', + 'Edit column "%s"' => 'Editar columna "%s"', + 'Select the new status of the subtask: "%s"' => 'Seleccione el nuevo estado de la subtarea: "%s"', + 'Subtask timesheet' => 'Hoja de tiempos de la subtarea', + 'There is nothing to show.' => 'No hay nada que mostrar.', + 'Time Tracking' => 'Control de Tiempo', + 'You already have one subtask in progress' => 'Ya tiene una subtarea en progreso', + 'Which parts of the project do you want to duplicate?' => '¿Que partes del proyecto quiere duplicar?', + 'Disallow login form' => 'Deshabilitar el formulario de inicio de sesión', + 'Start' => 'Inicio', + 'End' => 'Fin', + 'Task age in days' => 'Edad de la tarea en días', + 'Days in this column' => 'Días en esta columna', + '%dd' => '%dd', + 'Add a new link' => 'Añadir nuevo vínculo', + 'Do you really want to remove this link: "%s"?' => '¿Realmente quiere suprimir este vínculo: "%s"?', + 'Do you really want to remove this link with task #%d?' => '¿Realmente quiere suprimir este vínculo con la tarea #%d?', + 'Field required' => 'Campo requerido', + 'Link added successfully.' => 'Vínculo añadido correctamente', + 'Link updated successfully.' => 'Vínculo actualizado correctamente', + 'Link removed successfully.' => 'Vínculo suprimido correctamente', + 'Link labels' => 'Etiquetas de vínculos', + 'Link modification' => 'Modificar vínculo', + 'Opposite label' => 'Etiquetas opuestas', + 'Remove a link' => 'Suprimir un vínculo', + 'The labels must be different' => 'Las etiquetas deben ser diferentes', + 'There is no link.' => 'Ahí no hay un vínculo', + 'This label must be unique' => 'Esta etiqueta debe ser única', + 'Unable to create your link.' => 'No se puede crear su vínculo', + 'Unable to update your link.' => 'No se puede actualizar su vínculo', + 'Unable to remove this link.' => 'No se puede suprimir su vínculo', + 'relates to' => 'se relaciona con', + 'blocks' => 'bloqueos', + 'is blocked by' => 'está bloqueado por', + 'duplicates' => 'duplicados', + 'is duplicated by' => 'es duplicado por', + 'is a child of' => 'es hijo de', + 'is a parent of' => 'es padre de', + 'targets milestone' => 'metas del hito', + 'is a milestone of' => 'es un hito de', + 'fixes' => 'arregla', + 'is fixed by' => 'arreglado por', + 'This task' => 'Esta tarea', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Expandir tareas', + 'Collapse tasks' => 'Colapsar tareas', + 'Expand/collapse tasks' => 'Expandir/colapsar tareas', + 'Close dialog box' => 'Cerrar caja de diálogo', + 'Submit a form' => 'Enviar un formulario', + 'Board view' => 'Vista de tablero', + 'Keyboard shortcuts' => 'Atajos del teclado', + 'Open board switcher' => 'Conmutador de tablero abierto', + 'Application' => 'Aplicación', + 'Compact view' => 'Vista compacta', + 'Horizontal scrolling' => 'Desplazamiento horizontal', + 'Compact/wide view' => 'Vista compacta/amplia', + 'Currency' => 'Moneda', + 'Personal project' => 'Proyecto privado', + 'AUD - Australian Dollar' => 'AUD - Dólar australiano', + 'CAD - Canadian Dollar' => 'CAD - Dólar canadiense', + 'CHF - Swiss Francs' => 'CHF - Franco suizo', + 'Custom Stylesheet' => 'Hoja de estilo personalizada', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Libra británica', + 'INR - Indian Rupee' => 'INR - Rupia india', + 'JPY - Japanese Yen' => 'JPY - Yen japonés', + 'NZD - New Zealand Dollar' => 'NZD - Dólar de Nueva Zelanda', + 'PEN - Peruvian Sol' => 'PEN - Sol peruano', + 'RSD - Serbian dinar' => 'RSD - Dinar serbio', + 'CNY - Chinese Yuan' => 'CNY - Yuan chino', + 'USD - US Dollar' => 'USD - Dólar estadounidense', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar venezolano', + 'Destination column' => 'Columna destino', + 'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna cuando sea asignada a un usuario', + 'Move the task to another column when assignee is cleared' => 'Mover la tarea a otra columna cuando se elimine la persona asignada', + 'Source column' => 'Columna de origen', + 'Transitions' => 'Transiciones', + 'Executer' => 'Ejecutor', + 'Time spent in the column' => 'Horas Ejecutadas en la columna', + 'Task transitions' => 'Transiciones de las tareas', + 'Task transitions export' => 'Exportar transiciones de las tareas', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este reporte contiene todos los movimientos de columna por cada tarea con la fecha, el usuario y el tiempo transcurrido para cada transición', + 'Currency rates' => 'Tipos de cambio', + 'Rate' => 'Tarifa', + 'Change reference currency' => 'Cambiar moneda de referencia', + 'Reference currency' => 'Moneda de referencia', + 'The currency rate has been added successfully.' => 'El tipo de cambio ha sido añadido correctamente.', + 'Unable to add this currency rate.' => 'No se puede añadir este tipo de cambio.', + 'Webhook URL' => 'URL del Webhook', + '%s removed the assignee of the task %s' => '%s eliminó al asignado de la tarea %s', + 'Information' => 'Información', + 'Check two factor authentication code' => 'Verificar el código de autenticación de dos factores', + 'The two factor authentication code is not valid.' => 'El código de autenticación de dos factores no es válido', + 'The two factor authentication code is valid.' => 'El código de autenticación de dos factores es válido', + 'Code' => 'Código', + 'Two factor authentication' => 'Autenticación de dos factores', + 'This QR code contains the key URI: ' => 'Este código QR contiene la clave URI: ', + 'Check my code' => 'Verificar mi código', + 'Secret key: ' => 'Clave secreta: ', + 'Test your device' => 'Pruebe su dispositivo', + 'Assign a color when the task is moved to a specific column' => 'Asignar un color cuando la tarea se mueve a una columna específica', + '%s via Kanboard' => '%s vía Kanboard', + 'Burndown chart' => 'Gráfico de Tareas Pendientes', + 'This chart show the task complexity over the time (Work Remaining).' => 'Este gráfico muestra la complejidad de la tarea durante el tiempo (Trabajo restante)', + 'Screenshot taken %s' => 'Captura de pantalla tomada %s', + 'Add a screenshot' => 'Añadir una captura de pantalla', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tomar una captura de pantalla y presione CTRL+V o ⌘+V para pegar aquí.', + 'Screenshot uploaded successfully.' => 'Captura de pantalla subida correctamente', + 'SEK - Swedish Krona' => 'SEK - Corona sueca', + 'Identifier' => 'Identificador', + 'Disable two factor authentication' => 'Deshabilitar la autenticación de dos factores', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '¿Realmente desea desactivar la autenticación de dos factores para este usuario: "%s"?', + 'Edit link' => 'Editar enlace', + 'Start to type task title...' => 'Comenzar a escribir el título de la tarea ...', + 'A task cannot be linked to itself' => 'Una tarea no se puede vincular a sí misma', + 'The exact same link already exists' => 'Ya existe un enlace idéntico', + 'Recurrent task is scheduled to be generated' => 'La tarea periódica está programada para ser generada', + 'Score' => 'Puntuación', + 'The identifier must be unique' => 'El identificador debe ser único', + 'This linked task id doesn\'t exists' => 'Este ID de la tarea enlazada no existe', + 'This value must be alphanumeric' => 'Este valor debe ser alfanumérico', + 'Edit recurrence' => 'Editar recurrencia', + 'Generate recurrent task' => 'Generar tarea recurrente', + 'Trigger to generate recurrent task' => 'Disparador para generar tarea recurrente', + 'Factor to calculate new due date' => 'Factor para calcular la nueva fecha de vencimiento', + 'Timeframe to calculate new due date' => 'Plazo para calcular la nueva fecha de vencimiento', + 'Base date to calculate new due date' => 'Fecha base para calcular la nueva fecha de vencimiento', + 'Action date' => 'Fecha de acción', + 'Base date to calculate new due date: ' => 'Fecha base para calcular la nueva fecha de vencimiento: ', + 'This task has created this child task: ' => 'Esta tarea ha creado esta tarea hija: ', + 'Day(s)' => 'Día(s)', + 'Existing due date' => 'Fecha de vencimiento existente', + 'Factor to calculate new due date: ' => 'Factor para calcular la nueva fecha de vencimiento: ', + 'Month(s)' => 'Mes(es)', + 'This task has been created by: ' => 'Esta tarea ha sido creada por: ', + 'Recurrent task has been generated:' => 'Se ha generado una tarea recurrente:', + 'Timeframe to calculate new due date: ' => 'Plazo para calcular la nueva fecha de vencimiento: ', + 'Trigger to generate recurrent task: ' => 'Disparador para generar la tarea recurrente: ', + 'When task is closed' => 'Cuando la tarea está cerrada', + 'When task is moved from first column' => 'Cuando se mueve la tarea desde la primera columna', + 'When task is moved to last column' => 'Cuando la tarea se mueve a la última columna', + 'Year(s)' => 'Año(s)', + 'Project settings' => 'Configuración del proyecto', + 'Automatically update the start date' => 'Actualizar automáticamente la fecha de inicio', + 'iCal feed' => 'Alimentador de iCal', + 'Preferences' => 'Preferencias', + 'Security' => 'Seguridad', + 'Two factor authentication disabled' => 'Se ha inhabilitado la autenticación de dos factores.', + 'Two factor authentication enabled' => 'Autenticación de dos factores habilitada', + 'Unable to update this user.' => 'No se puede actualizar este usuario.', + 'There is no user management for personal projects.' => 'No hay gestión de usuarios para proyectos privados.', + 'User that will receive the email' => 'Usuario que recibirá el correo electrónico', + 'Email subject' => 'Asunto del email', + 'Date' => 'Fecha', + 'Add a comment log when moving the task between columns' => 'Añadir un registro en los comentarios al mover la tarea entre columnas', + 'Move the task to another column when the category is changed' => 'Mover la tarea a otra columna cuando se cambia la categoría', + 'Send a task by email to someone' => 'Enviar una tarea por correo electrónico a alguien', + 'Reopen a task' => 'Reabrir una tarea', + 'Notification' => 'Notificación', + '%s moved the task #%d to the first swimlane' => '%s movió la tarea #%d al primer carril', + 'Swimlane' => 'Carril', + '%s moved the task %s to the first swimlane' => '%s movió la tarea %s al primer carril', + '%s moved the task %s to the swimlane "%s"' => '%s trasladó la tarea %s al carril "%s"', + 'This report contains all subtasks information for the given date range.' => 'Este informe contiene toda la información de las subtareas para el intervalo de fechas especificado.', + 'This report contains all tasks information for the given date range.' => 'Este informe contiene la información de todas las tareas para el intervalo de fechas dado.', + 'Project activities for %s' => 'Actividades del proyecto para %s', + 'view the board on Kanboard' => 'ver el tablero en Kanboard', + 'The task has been moved to the first swimlane' => 'La tarea se ha movido al primer carril', + 'The task has been moved to another swimlane:' => 'La tarea se ha trasladado a otro carril:', + 'New title: %s' => 'Nuevo título: %s', + 'The task is not assigned anymore' => 'La tarea ya no está asignada', + 'New assignee: %s' => 'Nuevo asignado: %s', + 'There is no category now' => 'No hay categoría ahora', + 'New category: %s' => 'Nueva categoría: %s', + 'New color: %s' => 'Nuevo color: %s', + 'New complexity: %d' => 'Nueva complejidad: %d', + 'The due date has been removed' => 'Se ha eliminado la fecha de vencimiento', + 'There is no description anymore' => 'Ya no hay ninguna descripción', + 'Recurrence settings has been modified' => 'Los parámetros de recurrencia se han modificado', + 'Time spent changed: %sh' => 'Tiempo ejecutado cambiado: %sh', + 'Time estimated changed: %sh' => 'Horas Presupuestadas cambiado: %sh', + 'The field "%s" has been updated' => 'Se ha actualizado el campo "%s"', + 'The description has been modified:' => 'La descripción ha sido modificada:', + 'Do you really want to close the task "%s" as well as all subtasks?' => '¿Realmente desea cerrar la tarea "%s", así como todas sus subtareas?', + 'I want to receive notifications for:' => 'Quiero recibir notificaciones para:', + 'All tasks' => 'Todas las tareas', + 'Only for tasks assigned to me' => 'Sólo para tareas asignadas a mí', + 'Only for tasks created by me' => 'Sólo para tareas creadas por mí', + 'Only for tasks created by me and tasks assigned to me' => 'Sólo para las tareas creadas por mí y asignadas a mí', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Total para todas las columnas', + 'You need at least 2 days of data to show the chart.' => 'Necesita al menos 2 días de datos para mostrar el gráfico.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Detener temporizador', + 'Start timer' => 'Iniciar el temporizador', + 'My activity stream' => 'Mi flujo de actividad', + 'Search tasks' => 'Buscar tareas', + 'Reset filters' => 'Restablecer filtros', + 'My tasks due tomorrow' => 'Mis tareas para mañana', + 'Tasks due today' => 'Tareas pendientes hoy', + 'Tasks due tomorrow' => 'Tareas para mañana', + 'Tasks due yesterday' => 'Tareas vencidas ayer', + 'Closed tasks' => 'Tareas cerradas', + 'Open tasks' => 'Tareas abiertas', + 'Not assigned' => 'No asignado', + 'View advanced search syntax' => 'Ver sintaxis de búsqueda avanzada', + 'Overview' => 'Visión general', + 'Board/Calendar/List view' => 'Vista de Tablero/Calendario/Lista', + 'Switch to the board view' => 'Cambiar a la vista de tablero', + 'Switch to the list view' => 'Cambiar a la vista de lista', + 'Go to the search/filter box' => 'Ir a la caja de búsqueda/filtro', + 'There is no activity yet.' => 'No hay actividad todavía.', + 'No tasks found.' => 'No se han encontrado tareas.', + 'Keyboard shortcut: "%s"' => 'Atajo de teclado: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtrar', + 'Advanced search' => 'Búsqueda Avanzada', + 'Example of query: ' => 'Ejemplo de consulta: ', + 'Search by project: ' => 'Buscar por proyecto: ', + 'Search by column: ' => 'Buscar por columna: ', + 'Search by assignee: ' => 'Buscar por asignado:', + 'Search by color: ' => 'Buscar por color: ', + 'Search by category: ' => 'Buscar por categoría: ', + 'Search by description: ' => 'Buscar por descripción: ', + 'Search by due date: ' => 'Buscar por fecha de vencimiento: ', + 'Average time spent in each column' => 'Tiempo de permanencia promedio en cada columna', + 'Average time spent' => 'Tiempo ejecutado promedio', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Este gráfico muestra el tiempo promedio invertido en cada columna para las últimas %d tareas.', + 'Average Lead and Cycle time' => 'Tiempo de Espera y de Ciclo promedio', + 'Average lead time: ' => 'Tiempo de espera promedio: ', + 'Average cycle time: ' => 'Tiempo del ciclo promedio: ', + 'Cycle Time' => 'Tiempo del Ciclo', + 'Lead Time' => 'Tiempo de Espera', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico muestra el tiempo promedio de espera y de ciclo para las últimas %d tareas.', + 'Average time into each column' => 'Tiempo promedio en cada columna', + 'Lead and cycle time' => 'Tiempo de espera y de ciclo', + 'Lead time: ' => 'Tiempo de Espera: ', + 'Cycle time: ' => 'Tiempo del Ciclo: ', + 'Time spent in each column' => 'Tiempo empleado en cada columna', + 'The lead time is the duration between the task creation and the completion.' => 'El tiempo de espera es la duración entre la creación de la tarea y la finalización.', + 'The cycle time is the duration between the start date and the completion.' => 'El tiempo de ciclo es la duración entre la fecha de inicio y la finalización.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tarea no está cerrada, se utiliza la hora actual en lugar de la fecha de finalización.', + 'Set the start date automatically' => 'Establecer automáticamente la fecha de inicio', + 'Edit Authentication' => 'Editar autenticación', + 'Remote user' => 'Usuario remoto', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Los usuarios remotos no almacenan su contraseña en la base de datos Kanboard, ejemplos: cuentas LDAP, Google y Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marca la casilla "Deshabilitar el formulario de inicio de sesión", las credenciales ingresadas en el formulario de inicio de sesión serán ignoradas.', + 'Default task color' => 'Color predeterminado de la tarea', + 'This feature does not work with all browsers.' => 'Esta función no funciona con todos los navegadores.', + 'There is no destination project available.' => 'No hay proyecto de destino disponible.', + 'Trigger automatically subtask time tracking' => 'Activar automáticamente el seguimiento de tiempo de la subtarea', + 'Include closed tasks in the cumulative flow diagram' => 'Incluir tareas cerradas en el diagrama de flujo acumulativo', + 'Current swimlane: %s' => 'Carril actual: %s', + 'Current column: %s' => 'Columna actual: %s', + 'Current category: %s' => 'Categoría actual: %s', + 'no category' => 'sin categoria', + 'Current assignee: %s' => 'Asignado actual: %s', + 'not assigned' => 'no asignado', + 'Author:' => 'Autor:', + 'contributors' => 'colaboradores', + 'License:' => 'Licencia:', + 'License' => 'Licencia', + 'Enter the text below' => 'Introduzca el texto a continuación', + 'Start date:' => 'Fecha de inicio:', + 'Due date:' => 'Fecha de vencimiento:', + 'People who are project managers' => 'Personas que son gerentes de proyecto', + 'People who are project members' => 'Personas que son miembros del proyecto', + 'NOK - Norwegian Krone' => 'NOK - Corona Noruega', + 'Show this column' => 'Mostrar esta columna', + 'Hide this column' => 'Ocultar esta columna', + 'End date' => 'Fecha final', + 'Users overview' => 'Visión general de los usuarios', + 'Members' => 'Miembros', + 'Shared project' => 'Proyecto compartido', + 'Project managers' => 'Gerentes de Proyecto', + 'Projects list' => 'Lista de proyectos', + 'End date:' => 'Fecha final:', + 'Change task color when using a specific task link' => 'Cambiar el color de la tarea cuando se utiliza un enlace de tarea específico', + 'Task link creation or modification' => 'Creación o modificación de enlaces de tareas', + 'Milestone' => 'Hito', + 'Reset the search/filter box' => 'Restablecer la caja de búsqueda/filtro', + 'Documentation' => 'Documentación', + 'Author' => 'Autor', + 'Version' => 'Versión', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'No hay ningún plugin cargado.', + 'My notifications' => 'Mis Notificaciones', + 'Custom filters' => 'Filtros personalizados', + 'Your custom filter has been created successfully.' => 'Su filtro personalizado se ha creado correctamente.', + 'Unable to create your custom filter.' => 'No se puede crear el filtro personalizado.', + 'Custom filter removed successfully.' => 'Filtro personalizado eliminado correctamente.', + 'Unable to remove this custom filter.' => 'No se puede quitar este filtro personalizado.', + 'Edit custom filter' => 'Editar filtro personalizado', + 'Your custom filter has been updated successfully.' => 'Su filtro personalizado se ha actualizado correctamente.', + 'Unable to update custom filter.' => 'No se puede actualizar el filtro personalizado.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nuevo archivo adjunto en la tarea #%d: %s', + 'New comment on task #%d' => 'Nuevo comentario sobre la tarea #%d', + 'Comment updated on task #%d' => 'Comentario actualizado en la tarea #%d', + 'New subtask on task #%d' => 'Nueva subtarea en la tarea #%d', + 'Subtask updated on task #%d' => 'Subtarea actualizada en la tarea #%d', + 'New task #%d: %s' => 'Nueva tarea #%d: %s', + 'Task updated #%d' => 'Tarea actualizada #%d', + 'Task #%d closed' => 'Tarea #%d cerrada', + 'Task #%d opened' => 'Tarea #%d abierta', + 'Column changed for task #%d' => 'Columna modificada para la tarea #%d', + 'New position for task #%d' => 'Nueva posición para la tarea #%d', + 'Swimlane changed for task #%d' => 'Carril cambiado para la tarea #%d', + 'Assignee changed on task #%d' => 'Asignado cambiado en la tarea #%d', + '%d overdue tasks' => '%d tareas pendientes', + 'No notification.' => 'Sin notificación.', + 'Mark all as read' => 'marcar todo como leido', + 'Mark as read' => 'Marcar como leído', + 'Total number of tasks in this column across all swimlanes' => 'Número total de tareas en esta columna contando todos los carriles', + 'Collapse swimlane' => 'Colapsar el carril', + 'Expand swimlane' => 'Expandir el carril', + 'Add a new filter' => 'Añadir un nuevo filtro', + 'Share with all project members' => 'Compartir con todos los miembros del proyecto', + 'Shared' => 'Compartido', + 'Owner' => 'Propietario', + 'Unread notifications' => 'Notificaciones no leídas', + 'Notification methods:' => 'Métodos de notificación:', + 'Unable to read your file' => 'No se puede leer el archivo', + '%d task(s) have been imported successfully.' => '%d tarea(s) se han importado correctamente.', + 'Nothing has been imported!' => '¡Nada se ha importado!', + 'Import users from CSV file' => 'Importar usuarios del archivo CSV', + '%d user(s) have been imported successfully.' => '%d usuario(s) se han importado correctamente.', + 'Comma' => 'Coma', + 'Semi-colon' => 'Punto y coma', + 'Tab' => 'Tabulación', + 'Vertical bar' => 'Barra vertical', + 'Double Quote' => 'Comillas dobles', + 'Single Quote' => 'Comillas simples', + '%s attached a file to the task #%d' => '%s adjuntó un archivo a la tarea #%d', + 'There is no column or swimlane activated in your project!' => '¡No hay columna ni carril activado en tu proyecto!', + 'Append filter (instead of replacement)' => 'Agregar el filtro (en lugar del reemplazo)', + 'Append/Replace' => 'Añadir/Reemplazar', + 'Append' => 'Adjuntar', + 'Replace' => 'Reemplazar', + 'Import' => 'Importar', + 'Change sorting' => 'Cambio de orden', + 'Tasks Importation' => 'Importación de Tareas', + 'Delimiter' => 'Delimitador', + 'Enclosure' => 'Contenedor', + 'CSV File' => 'Archivo CSV', + 'Instructions' => 'Instrucciones', + 'Your file must use the predefined CSV format' => 'Su archivo debe utilizar el formato CSV predefinido', + 'Your file must be encoded in UTF-8' => 'Su archivo debe estar codificado en UTF-8', + 'The first row must be the header' => 'La primera fila debe ser el encabezado', + 'Duplicates are not verified for you' => 'Los duplicados no se verifican', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La fecha de vencimiento debe utilizar el formato ISO: AAAA-MM-DD', + 'Download CSV template' => 'Descargar plantilla CSV', + 'No external integration registered.' => 'No se ha registrado ninguna integración externa.', + 'Duplicates are not imported' => 'Los duplicados no se importan', + 'Usernames must be lowercase and unique' => 'Los nombres de usuario deben estar en minúsculas y ser únicos', + 'Passwords will be encrypted if present' => 'Las contraseñas se cifrarán si están presentes', + '%s attached a new file to the task %s' => '%s adjunto un nuevo archivo a la tarea %s', + 'Link type' => 'Tipo de enlace', + 'Assign automatically a category based on a link' => 'Asignar automáticamente una categoría basada en un enlace', + 'BAM - Konvertible Mark' => 'BAM - Marco Convertible', + 'Assignee Username' => 'Usuario asignado', + 'Assignee Name' => 'Nombre del asignado', + 'Groups' => 'Grupos', + 'Members of %s' => 'Miembros de %s', + 'New group' => 'Nuevo grupo', + 'Group created successfully.' => 'Grupo creado correctamente.', + 'Unable to create your group.' => 'No se puede crear su grupo.', + 'Edit group' => 'Editar grupo', + 'Group updated successfully.' => 'Grupo actualizado correctamente.', + 'Unable to update your group.' => 'No se puede actualizar su grupo.', + 'Add group member to "%s"' => 'Agregue el miembro del grupo a "%s"', + 'Group member added successfully.' => 'Miembro del grupo agregado correctamente.', + 'Unable to add group member.' => 'No se puede agregar el miembro del grupo.', + 'Remove user from group "%s"' => 'Quitar usuario del grupo "%s"', + 'User removed successfully from this group.' => 'Usuario eliminado correctamente de este grupo.', + 'Unable to remove this user from the group.' => 'No se puede eliminar este usuario del grupo.', + 'Remove group' => 'Eliminar grupo', + 'Group removed successfully.' => 'Grupo eliminado correctamente.', + 'Unable to remove this group.' => 'No se pudo eliminar este grupo.', + 'Project Permissions' => 'Permisos del proyecto', + 'Manager' => 'Gerente', + 'Project Manager' => 'Gerente de proyecto', + 'Project Member' => 'Miembro del proyecto', + 'Project Viewer' => 'Visor de proyectos', + 'Your account is locked for %d minutes' => 'Tu cuenta está bloqueada durante %d minutos', + 'Invalid captcha' => 'Captcha inválido', + 'The name must be unique' => 'El nombre debe ser único', + 'View all groups' => 'Ver todos los grupos', + 'There is no user available.' => 'No hay usuarios disponibles.', + '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.', + 'Add group member' => 'Agregar miembro del grupo', + 'Do you really want to remove this group: "%s"?' => '¿Desea realmente eliminar este grupo: "%s"?', + 'There is no user in this group.' => 'No hay ningún usuario en este grupo.', + 'Permissions' => 'Permisos', + 'Allowed Users' => 'Usuarios permitidos', + 'No specific user has been allowed.' => 'Ningún usuario ha sido autorizado específicamente.', + 'Role' => 'Rol', + 'Enter user name...' => 'Introduzca su nombre de usuario...', + 'Allowed Groups' => 'Grupos permitidos', + 'No group has been allowed.' => 'Ningún grupo se ha permitido específicamente.', + 'Group' => 'Grupo', + 'Group Name' => 'Nombre del grupo', + 'Enter group name...' => 'Introduzca el nombre del grupo ...', + 'Role:' => 'Rol:', + 'Project members' => 'Miembros del proyecto', + '%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 ha mencionado en un comentario sobre la tarea #%d', + 'You were mentioned in the task #%d' => 'Te mencionaron en la tarea #%d', + 'You were mentioned in a comment on the task #%d' => 'Usted fue mencionado en un comentario en la tarea #%d', + 'Estimated hours: ' => 'Horas estimadas: ', + 'Actual hours: ' => 'Horas reales: ', + 'Hours Spent' => 'Horas Gastadas', + 'Hours Estimated' => 'Horas Estimadas', + 'Estimated Time' => 'Tiempo Estimado', + 'Actual Time' => 'Tiempo Real', + 'Estimated vs actual time' => 'Horas Presupuestadas vs. tiempo real', + 'RUB - Russian Ruble' => 'RUB - Rublo Ruso', + 'Assign the task to the person who does the action when the column is changed' => 'Asigne la tarea a la persona que realiza la acción cuando se cambia la columna', + 'Close a task in a specific column' => 'Cerrar una tarea en una columna específica', + 'Time-based One-time Password Algorithm' => 'Algoritmo de contraseña de una sola vez basado en el tiempo', + 'Two-Factor Provider: ' => 'Proveedor de dos factores: ', + 'Disable two-factor authentication' => 'Deshabilitar la autenticación de dos factores', + 'Enable two-factor authentication' => 'Habilitar la autenticación de dos factores', + 'There is no integration registered at the moment.' => 'No hay ninguna integración registrada en este momento.', + 'Password Reset for Kanboard' => 'Restablecer contraseña para Kanboard', + 'Forgot password?' => '¿Olvidó su contraseña?', + 'Enable "Forget Password"' => 'Habilitar "Olvidar contraseña"', + 'Password Reset' => 'Restablecimiento de contraseña', + 'New password' => 'Nueva contraseña', + 'Change Password' => 'Cambiar la contraseña', + 'To reset your password click on this link:' => 'Para restablecer su contraseña, haga clic en este enlace:', + 'Last Password Reset' => 'Restablecer la última contraseña', + 'The password has never been reinitialized.' => 'La contraseña nunca ha sido reinicializada.', + 'Creation' => 'Creación', + 'Expiration' => 'Vencimiento', + 'Password reset history' => 'Historial de restablecimiento de contraseñas', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas las tareas de la columna "%s" y el carril "%s" han sido cerradas con éxito.', + 'Do you really want to close all tasks of this column?' => '¿Realmente desea cerrar todas las tareas de esta columna?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarea(s) en la columna "%s" y el carril "%s" se cerrará.', + 'Close all tasks in this column and this swimlane' => 'Cierre todas las tareas de esta columna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Ningún plugin ha registrado un método de notificación de proyecto. Todavía puede configurar notificaciones individuales en su perfil de usuario.', + 'My dashboard' => 'Mi tablero', + 'My profile' => 'Mi perfil', + '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, por ejemplo: MIPROYECTO.', + 'Project owner' => 'Propietario del proyecto', + 'Personal projects do not have users and groups management.' => 'Los proyectos privados no tienen gestión de usuarios y grupos.', + 'There is no project member.' => 'No hay ningún miembro del proyecto.', + 'Priority' => 'Prioridad', + 'Task priority' => 'Prioridad de la tarea', + 'General' => 'General', + 'Dates' => 'Fechas', + 'Default priority' => 'Prioridad predeterminada', + 'Lowest priority' => 'Menor prioridad', + 'Highest priority' => 'Mayor prioridad', + 'Close a task when there is no activity' => 'Cierra una tarea cuando no hay actividad', + 'Duration in days' => 'Duración en días', + 'Send email when there is no activity on a task' => 'Enviar correo electrónico cuando no hay actividad en una tarea', + 'Unable to fetch link information.' => 'No es posible obtener información de enlace.', + 'Daily background job for tasks' => 'Trabajo en segundo plano diario para las tareas', + 'Auto' => 'Auto', + 'Related' => 'Relacionado', + 'Attachment' => 'Archivo adjunto', + 'Web Link' => 'Enlace web', + 'External links' => 'Enlaces externos', + 'Add external link' => 'Añadir enlace externo', + 'Type' => 'Tipo', + 'Dependency' => 'Dependencia', + 'Add internal link' => 'Añadir enlace interno', + 'Add a new external link' => 'Añadir un nuevo enlace externo', + 'Edit external link' => 'Editar enlace externo', + 'External link' => 'Enlace externo', + 'Copy and paste your link here...' => 'Copia y pega el enlace aquí ...', + 'URL' => 'URL', + 'Internal links' => 'Enlaces internos', + 'Assign to me' => 'Asignármelo', + 'Me' => 'Yo', + 'Do not duplicate anything' => 'No duplique nada', + 'Projects management' => 'Gestión de proyectos', + 'Users management' => 'Gestión de usuarios', + 'Groups management' => 'Gestión de grupos', + 'Create from another project' => 'Crear desde otro proyecto', + 'open' => 'abierto', + 'closed' => 'cerrado', + 'Priority:' => 'Prioridad:', + 'Reference:' => 'Referencia:', + 'Complexity:' => 'Complejidad:', + 'Swimlane:' => 'Carril:', + 'Column:' => 'Columna:', + 'Position:' => 'Posición:', + 'Creator:' => 'Creador:', + 'Time estimated:' => 'Horas Presupuestadas:', + '%s hours' => '%s horas', + 'Time spent:' => 'Tiempo usado:', + 'Created:' => 'Creado:', + 'Modified:' => 'Modificado:', + 'Completed:' => 'Terminado:', + 'Started:' => 'Empezado:', + 'Moved:' => 'Movido:', + 'Task #%d' => 'Tarea número %d', + 'Time format' => 'Formato de tiempo', + 'Start date: ' => 'Fecha de inicio: ', + 'End date: ' => 'Fecha final: ', + 'New due date: ' => 'Nueva fecha de vencimiento: ', + 'Start date changed: ' => 'Fecha de inicio cambiada: ', + 'Disable personal projects' => 'Inhabilitar proyectos privados', + 'Do you really want to remove this custom filter: "%s"?' => '¿Desea realmente eliminar este filtro personalizado: "%s"?', + 'Remove a custom filter' => 'Eliminar un filtro personalizado', + 'User activated successfully.' => 'Usuario activado correctamente.', + 'Unable to enable this user.' => 'No se puede habilitar este usuario.', + 'User disabled successfully.' => 'El usuario ha sido desactivado correctamente.', + 'Unable to disable this user.' => 'No se puede deshabilitar este usuario.', + 'All files have been uploaded successfully.' => 'Todos los archivos se han cargado correctamente.', + 'The maximum allowed file size is %sB.' => 'El tamaño máximo de archivo permitido es %sB.', + 'Drag and drop your files here' => 'Arrastra y suelta tus archivos aquí', + 'choose files' => 'Elija el archivo', + 'View profile' => 'Ver perfil', + 'Two Factor' => 'Dos factores', + 'Disable user' => 'Deshabilitar usuario', + 'Do you really want to disable this user: "%s"?' => '¿Desea realmente desactivar este usuario: "%s"?', + 'Enable user' => 'Habilitar usuario', + 'Do you really want to enable this user: "%s"?' => '¿De verdad quieres habilitar a este usuario: "%s"?', + 'Download' => 'Descargar', + 'Uploaded: %s' => 'Subido: %s', + 'Size: %s' => 'Tamaño: %s', + 'Uploaded by %s' => 'Subido por %s', + 'Filename' => 'Nombre del archivo', + 'Size' => 'Tamaño', + 'Column created successfully.' => 'Columna creada correctamente.', + 'Another column with the same name exists in the project' => 'Otra columna con el mismo nombre existe en el proyecto', + 'Default filters' => 'Filtros predeterminados', + 'Your board doesn\'t have any columns!' => '¡Su tablero no tiene columnas!', + 'Change column position' => 'Cambiar la posición de la columna', + 'Switch to the project overview' => 'Cambiar a la vista general del proyecto', + 'User filters' => 'Filtros de usuario', + 'Category filters' => 'Filtros de categorías', + 'Upload a file' => 'Cargar un archivo', + 'View file' => 'Ver archivo', + 'Last activity' => 'Última actividad', + 'Change subtask position' => 'Cambiar la posición de la subtarea', + 'This value must be greater than %d' => 'Este valor no debe de ser más grande que %d', + 'Another swimlane with the same name exists in the project' => 'En el proyecto existe otro carril con el mismo nombre', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Ejemplo: https://example.kanboard.org/ (utilizado para generar URLs absolutas)', + 'Actions duplicated successfully.' => 'Acciones duplicadas correctamente.', + 'Unable to duplicate actions.' => 'No se pueden duplicar acciones.', + 'Add a new action' => 'Añadir una acción nueva', + 'Import from another project' => 'Importar desde otro proyecto', + 'There is no action at the moment.' => 'No hay acción en este momento.', + 'Import actions from another project' => 'Importar acciones de otro proyecto', + 'There is no available project.' => 'No hay proyecto disponible.', + 'Local File' => 'Archivo local', + 'Configuration' => 'Configuración', + 'PHP version:' => 'Versión de PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versión del sistema operativo:', + 'Database version:' => 'Versión de base de datos:', + 'Browser:' => 'Navegador:', + 'Task view' => 'Vista de la tarea', + 'Edit task' => 'Editar tarea', + 'Edit description' => 'Editar Descripción', + 'New internal link' => 'Nuevo enlace interno', + 'Display list of keyboard shortcuts' => 'Mostrar lista de atajos de teclado', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Subir mi imagen de avatar', + 'Remove my image' => 'Eliminar mi imagen', + 'The OAuth2 state parameter is invalid' => 'El parámetro de estado OAuth2 no es válido', + 'User not found.' => 'Usuario no encontrado.', + 'Search in activity stream' => 'Buscar en el flujo de actividades', + 'My activities' => 'Mis actividades', + 'Activity until yesterday' => 'Actividad hasta ayer', + 'Activity until today' => 'Actividad hasta hoy', + 'Search by creator: ' => 'Búsqueda por creador: ', + 'Search by creation date: ' => 'Búsqueda por fecha de creación: ', + 'Search by task status: ' => 'Búsqueda por estado de la tarea: ', + 'Search by task title: ' => 'Buscar por título de tarea: ', + 'Activity stream search' => 'Búsqueda de flujo de actividad', + 'Projects where "%s" is manager' => 'Proyectos donde "%s" es gerente', + 'Projects where "%s" is member' => 'Proyectos donde "%s" es miembro', + 'Open tasks assigned to "%s"' => 'Tareas abiertas asignadas a "%s"', + 'Closed tasks assigned to "%s"' => 'Tareas cerradas asignadas a "%s"', + 'Assign automatically a color based on a priority' => 'Asignar automáticamente un color basado en una prioridad', + 'Overdue tasks for the project(s) "%s"' => 'Tareas vencidas para el (los) proyecto(s) "%s"', + 'Upload files' => 'Subir archivos', + 'Installed Plugins' => 'Plugins instalados', + 'Plugin Directory' => 'Directorio de Plugins', + 'Plugin installed successfully.' => 'Plugin instalado correctamente.', + 'Plugin updated successfully.' => 'Plugin actualizado correctamente.', + 'Plugin removed successfully.' => 'Plugin eliminado correctamente.', + 'Subtask converted to task successfully.' => 'Subtarea convertida a la tarea con éxito.', + 'Unable to convert the subtask.' => 'No se puede convertir la subtarea.', + 'Unable to extract plugin archive.' => 'No se puede extraer archivo plugin.', + 'Plugin not found.' => 'No se ha encontrado el plugin.', + 'You don\'t have the permission to remove this plugin.' => 'No tienes permiso para eliminar este plugin.', + 'Unable to download plugin archive.' => 'No es posible descargar archivos plugin.', + 'Unable to write temporary file for plugin.' => 'No se puede escribir el archivo temporal para el plugin.', + 'Unable to open plugin archive.' => 'No se puede abrir el archivo de plugin', + 'There is no file in the plugin archive.' => 'No hay ningún archivo en el archivo de plugins.', + 'Create tasks in bulk' => 'Crear tareas en lote', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Tu instancia de Kanboard no está configurada para instalar complementos desde la interfaz de usuario.', + 'There is no plugin available.' => 'No hay un plugin disponible.', + 'Install' => 'Instalar', + 'Update' => 'Actualizar', + 'Up to date' => 'Hasta la fecha', + 'Not available' => 'No disponible', + 'Remove plugin' => 'Eliminar plugin', + 'Do you really want to remove this plugin: "%s"?' => '¿Realmente desea eliminar este plugin: "%s"?', + 'Uninstall' => 'Desinstalar', + 'Listing' => 'Listado', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Gestionar proyectos', + 'Convert to task' => 'Convertir en tarea', + 'Convert sub-task to task' => 'Convertir subtarea en tarea', + 'Do you really want to convert this sub-task to a task?' => '¿Realmente desea convertir esta subtarea a una tarea?', + 'My task title' => 'Título de mi tarea', + 'Enter one task by line.' => 'Ingrese una tarea por línea.', + 'Number of failed login:' => 'Número de inicio de sesión fallido:', + 'Account locked until:' => 'Cuenta bloqueada hasta:', + 'Email settings' => 'Ajustes de correo electrónico', + 'Email sender address' => 'Dirección del remitente del correo electrónico', + 'Email transport' => 'Transporte por correo electrónico', + 'Webhook token' => 'Token Webhook', + 'Project tags management' => 'Gestión de etiquetas de proyecto', + 'Tag created successfully.' => 'Etiqueta creada correctamente.', + 'Unable to create this tag.' => 'No se puede crear esta etiqueta.', + 'Tag updated successfully.' => 'Etiqueta actualizada correctamente.', + 'Unable to update this tag.' => 'No se puede actualizar esta etiqueta.', + 'Tag removed successfully.' => 'Etiqueta eliminada correctamente.', + 'Unable to remove this tag.' => 'No se ha podido eliminar esta etiqueta.', + 'Global tags management' => 'Gestión de etiquetas globales', + 'Tags' => 'Etiquetas', + 'Tags management' => 'Gestión de etiquetas', + 'Add new tag' => 'Añadir nueva etiqueta', + 'Edit a tag' => 'Modificar una etiqueta', + 'Project tags' => 'Etiquetas del proyecto', + 'There is no specific tag for this project at the moment.' => 'No hay una etiqueta específica para este proyecto en este momento.', + 'Tag' => 'Etiqueta', + 'Remove a tag' => 'Eliminar una etiqueta', + 'Do you really want to remove this tag: "%s"?' => '¿Desea realmente eliminar esta etiqueta: "%s"?', + 'Global tags' => 'Etiquetas globales', + 'There is no global tag at the moment.' => 'No hay una etiqueta global en este momento.', + 'This field cannot be empty' => 'Este campo no puede estar vacío', + 'Close a task when there is no activity in a specific column' => 'Cierra una tarea cuando no hay actividad en una columna específica', + '%s removed a subtask for the task #%d' => '%s eliminó una subtarea para la tarea %d', + '%s removed a comment on the task #%d' => '%s eliminó un comentario para la tarea %d', + 'Comment removed on task #%d' => 'Comentario eliminado en la tarea #%d', + 'Subtask removed on task #%d' => 'Subtarea eliminada en la tarea #%d', + 'Hide tasks in this column in the dashboard' => 'Ocultar tareas en esta columna en el tablero', + '%s removed a comment on the task %s' => '%s eliminó un comentario para la tarea %s', + '%s removed a subtask for the task %s' => '%s eliminó una subtarea para la tarea %s', + 'Comment removed' => 'Comentario eliminado', + 'Subtask removed' => 'Sub-tarea eliminada', + '%s set a new internal link for the task #%d' => '%s estableció un nuevo enlace interno para la tarea %d', + '%s removed an internal link for the task #%d' => '%s eliminó un nuevo enlace interno para la tarea %d', + 'A new internal link for the task #%d has been defined' => 'Se ha definido un nuevo enlace interno para la tarea #%d', + 'Internal link removed for the task #%d' => 'Enlace interno eliminado para la tarea #%d', + '%s set a new internal link for the task %s' => '%s estableció un nuevo enlace interno para la tarea %s', + '%s removed an internal link for the task %s' => '%s eliminó un enlace interno para la tarea %s', + 'Automatically set the due date on task creation' => 'Establecer automáticamente la fecha de vencimiento en la creación de tareas', + 'Move the task to another column when closed' => 'Mover la tarea a otra columna cuando esté cerrada', + 'Move the task to another column when not moved during a given period' => 'Mover la tarea a otra columna cuando no se mueve durante un período determinado', + 'Dashboard for %s' => 'Tablero para %s', + 'Tasks overview for %s' => 'Visión general de tareas para %s', + 'Subtasks overview for %s' => 'Visión general de subtareas para %s', + 'Projects overview for %s' => 'Visión general de proyectos para %s', + 'Activity stream for %s' => 'Flujo de actividad para %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Asignar un color cuando la tarea se mueve a un carril específico', + 'Assign a priority when the task is moved to a specific swimlane' => 'Asignar una prioridad cuando la tarea se mueve a un carril específico', + 'User unlocked successfully.' => 'Usuario desbloqueado correctamente.', + 'Unable to unlock the user.' => 'No se puede desbloquear el usuario.', + 'Move a task to another swimlane' => 'Mueve una tarea a otro carril', + 'Creator Name' => 'Nombre del Creador', + 'Time spent and estimated' => 'Tiempo empleado y estimado', + 'Move position' => 'Mover la posición', + 'Move task to another position on the board' => 'Mover la tarea a otra posición en el tablero', + 'Insert before this task' => 'Insertar antes de esta tarea', + 'Insert after this task' => 'Insertar después de esta tarea', + 'Unlock this user' => 'Desbloquear este usuario', + 'Custom Project Roles' => 'Roles de proyecto personalizados', + 'Add a new custom role' => 'Agregar un nuevo rol personalizado', + 'Restrictions for the role "%s"' => 'Restricciones para el rol "%s"', + 'Add a new project restriction' => 'Añadir una nueva restricción de proyecto', + 'Add a new drag and drop restriction' => 'Añadir una nueva restricción de arrastrar y soltar', + 'Add a new column restriction' => 'Agregar una nueva restricción de columna', + 'Edit this role' => 'Editar este rol', + 'Remove this role' => 'Eliminar este rol', + 'There is no restriction for this role.' => 'No hay restricción para este rol.', + 'Only moving task between those columns is permitted' => 'Sólo se permite el movimiento de una tarea entre esas columnas', + 'Close a task in a specific column when not moved during a given period' => 'Cierra una tarea en una columna específica cuando no se mueve durante un período determinado', + 'Edit columns' => 'Editar columnas', + 'The column restriction has been created successfully.' => 'La restricción de columna se ha creado correctamente.', + 'Unable to create this column restriction.' => 'No se puede crear esta restricción de columna.', + 'Column restriction removed successfully.' => 'Se ha eliminado correctamente la restricción de columna.', + 'Unable to remove this restriction.' => 'No se puede eliminar esta restricción.', + 'Your custom project role has been created successfully.' => 'El rol de proyecto personalizado se ha creado correctamente.', + 'Unable to create custom project role.' => 'No se puede crear un rol de proyecto personalizado.', + 'Your custom project role has been updated successfully.' => 'Su rol de proyecto personalizado se ha actualizado correctamente.', + 'Unable to update custom project role.' => 'No se puede actualizar el rol de proyecto personalizado.', + 'Custom project role removed successfully.' => 'El rol de proyecto personalizado se eliminó correctamente.', + 'Unable to remove this project role.' => 'No se pudo eliminar esta función del proyecto.', + 'The project restriction has been created successfully.' => 'La restricción del proyecto se ha creado con éxito.', + 'Unable to create this project restriction.' => 'No se puede crear esta restricción de proyecto.', + 'Project restriction removed successfully.' => 'Se ha eliminado correctamente la restricción de proyecto.', + 'You cannot create tasks in this column.' => 'No puede crear tareas en esta columna.', + 'Task creation is permitted for this column' => 'La creación de tareas está permitida para esta columna', + 'Closing or opening a task is permitted for this column' => 'El cierre o apertura de una tarea está permitido para esta columna', + 'Task creation is blocked for this column' => 'La creación de tareas está bloqueada para esta columna', + 'Closing or opening a task is blocked for this column' => 'El cierre o apertura de una tarea está bloqueado para esta columna', + 'Task creation is not permitted' => 'No se permite la creación de tareas', + 'Closing or opening a task is not permitted' => 'No se permite cerrar ni abrir una tarea', + 'New drag and drop restriction for the role "%s"' => 'Nueva restricción de arrastrar y soltar para el rol "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Las personas pertenecientes a este rol sólo podrán mover las tareas entre la columna de origen y la de destino.', + 'Remove a column restriction' => 'Eliminar una restricción de columna', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => '¿Realmente desea eliminar esta restricción de columna: "%s" a "%s"?', + 'New column restriction for the role "%s"' => 'Nueva restricción de columna para el rol "%s"', + 'Rule' => 'Regla', + 'Do you really want to remove this column restriction?' => '¿Realmente desea eliminar esta restricción de columna?', + 'Custom roles' => 'Roles personalizados', + 'New custom project role' => 'Nuevo rol de proyecto personalizado', + 'Edit custom project role' => 'Editar un rol de proyecto personalizado', + 'Remove a custom role' => 'Eliminar un rol personalizado', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '¿Realmente desea eliminar este rol personalizado: "%s"? Todas las personas asignadas a este rol se convertirán en miembros del proyecto.', + 'There is no custom role for this project.' => 'No hay ningun rol personalizado para este proyecto.', + 'New project restriction for the role "%s"' => 'Nueva restricción de proyecto para el rol "%s"', + 'Restriction' => 'Restricción', + 'Remove a project restriction' => 'Eliminar una restricción de proyecto', + 'Do you really want to remove this project restriction: "%s"?' => '¿Realmente desea eliminar esta restricción de proyecto: "%s"?', + 'Duplicate to multiple projects' => 'Duplicar en varios proyectos', + 'This field is required' => 'Este campo es requerido', + 'Moving a task is not permitted' => 'No se permite mover una tarea', + 'This value must be in the range %d to %d' => 'Este valor debe estar en el rango %d a %d', + 'You are not allowed to move this task.' => 'No se le permite mover esta tarea.', + 'API User Access' => 'Acceso de usuario de API', + 'Preview' => 'Previsualizar', + 'Write' => 'Escribir', + 'Write your text in Markdown' => 'Redacta el texto en Markdown', + 'No personal API access token registered.' => 'No se ha registrado ningún token de acceso a API personal.', + 'Your personal API access token is "%s"' => 'Su token de acceso a la API personal es "%s"', + 'Remove your token' => 'Elimine su token', + 'Generate a new token' => 'Generar un nuevo token', + 'Showing %d-%d of %d' => 'Mostrando %d-%d de %d', + 'Outgoing Emails' => 'Correo electrónico saliente', + 'Add or change currency rate' => 'Añadir o cambiar el tipo de cambio', + 'Reference currency: %s' => 'Divisa de referencia: %s', + 'Add custom filters' => 'Añadir filtros personalizados', + 'Export' => 'Exportar', + 'Add link label' => 'Añadir etiqueta de enlace', + 'Incompatible Plugins' => 'Plugins incompatibles', + 'Compatibility' => 'Compatibilidad', + 'Permissions and ownership' => 'Permisos y propiedad', + 'Priorities' => 'Prioridades', + 'Close this window' => 'Cierra esta ventana', + 'Unable to upload this file.' => 'No se puede subir este archivo.', + 'Import tasks' => 'Importar tareas', + 'Choose a project' => 'Elija un proyecto', + 'Profile' => 'Perfil', + 'Application role' => 'Rol de la aplicación', + '%d invitations were sent.' => '%d invitaciones enviadas.', + '%d invitation was sent.' => '%d invitación fue enviada.', + 'Unable to create this user.' => 'No se puede crear este usuario.', + 'Kanboard Invitation' => 'Invitación de Kanboard', + 'Visible on dashboard' => 'Visible en el tablero', + 'Created at:' => 'Creado en:', + 'Updated at:' => 'Actualizado en:', + 'There is no custom filter.' => 'No hay un filtro personalizado.', + 'New User' => 'Nuevo usuario', + 'Authentication' => 'Autenticación', + 'If checked, this user will use a third-party system for authentication.' => 'Si está marcado, este usuario utilizará un sistema de terceros para la autenticación.', + 'The password is necessary only for local users.' => 'La contraseña sólo es necesaria para los usuarios locales.', + 'You have been invited to register on Kanboard.' => 'Te han invitado a registrarte en Kanboard.', + 'Click here to join your team' => 'Haz clic aquí para unirte a tu equipo', + 'Invite people' => 'Invitar personas', + 'Emails' => 'Emails', + 'Enter one email address by line.' => 'Ingrese una dirección de correo electrónico por línea.', + 'Add these people to this project' => 'Añadir a estas personas a este proyecto', + 'Add this person to this project' => 'Añadir a esta persona a este proyecto', + 'Sign-up' => 'Regístrate', + 'Credentials' => 'Credenciales', + 'New user' => 'Añadir un usuario', + 'This username is already taken' => 'Este nombre de usuario ya está en uso', + 'Your profile must have a valid email address.' => 'Su perfil debe tener una dirección de email válido.', + 'TRL - Turkish Lira' => 'TRL - Lira Turca', + 'The project email is optional and could be used by several plugins.' => 'El email del proyecto es opcional y podría ser usado por varios plugins', + 'The project email must be unique across all projects' => 'El email del proyecto debe ser único entre todos los proyectos', + 'The email configuration has been disabled by the administrator.' => 'La configuración de email se ha deshabilitado por el administrador', + 'Close this project' => 'Cerrar este proyecto', + 'Open this project' => 'Abrir este proyecto', + 'Close a project' => 'Cerrar un proyecto', + 'Do you really want to close this project: "%s"?' => '¿Realmente quiere cerrar este proyecto: "%s"?', + 'Reopen a project' => 'Reabrir un proyecto', + 'Do you really want to reopen this project: "%s"?' => '¿Realmente quiere reabrir este proyecto: "%s"?', + 'This project is open' => 'Este proyecto está abierdo', + 'This project is closed' => 'Este proyecto está cerrado', + 'Unable to upload files, check the permissions of your data folder.' => 'No se pueden cargar archivos, verifique los permisos de su carpeta de datos (data)', + 'Another category with the same name exists in this project' => 'Ya existe otra categoría con el mismo nombre en este proyecto', + 'Comment sent by email successfully.' => 'Comentario enviado exitosamente por email', + 'Sent by email to "%s" (%s)' => 'Enviado por email a "%s" (%s)', + 'Unable to read uploaded file.' => 'No se pudo leer el archivo cargado', + 'Database uploaded successfully.' => 'Base de datos cargada exitosamente', + 'Task sent by email successfully.' => 'Tarea enviada exitosamente por email', + 'There is no category in this project.' => 'No hay categorías en este proyecto', + 'Send by email' => 'Enviar por email', + 'Create and send a comment by email' => 'Crear y enviar un comentario por email', + 'Subject' => 'Asunto', + 'Upload the database' => 'Cargar la base de datos', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Podría cargar la base de datos Sqlite descargada previamente (formato Gzip)', + 'Database file' => 'Archivo de Base de Datos', + 'Upload' => 'Cargar', + 'Your project must have at least one active swimlane.' => 'Su proyecto debe tener al menos un carril activo', + 'Project: %s' => 'Proyecto: %s', + 'Automatic action not found: "%s"' => 'No se encontró la acción automática: "%s"', + '%d projects' => '%d proyectos', + '%d project' => '%d proyecto', + 'There is no project.' => 'No hay proyecto', + 'Sort' => 'Ordenar', + 'Project ID' => 'ID Proyecto', + 'Project name' => 'Nombre del Proyecto', + 'Public' => 'Público', + 'Personal' => 'Privado', + '%d tasks' => '%d tareas', + '%d task' => '%d tarea', + 'Task ID' => 'ID Tarea', + 'Assign automatically a color when due date is expired' => 'Asignar un color automáticamente cuando la fecha de vencimiento haya expirado', + 'Total score in this column across all swimlanes' => 'Puntaje Total en esta columna para todos los carriles', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentino', + 'COP - Colombian Peso' => 'COP - Peso Colombiano', + '%d groups' => '%d grupos', + '%d group' => '%d grupo', + 'Group ID' => 'ID Grupo', + 'External ID' => 'ID Externa', + '%d users' => '%d usuarios', + '%d user' => '%d usuario', + 'Hide subtasks' => 'Ocultar subtareas', + 'Show subtasks' => 'Mostrar subtareas', + 'Authentication Parameters' => 'Parámetros de Autenticación', + 'API Access' => 'Acceso API', + 'No users found.' => 'No se encontraron usuarios', + 'User ID' => 'ID del Usuario', + 'Notifications are activated' => 'Las notificaciones están activadas', + 'Notifications are disabled' => 'Las notificaciones están deshabilitadas', + 'User disabled' => 'Usuario deshabilitado', + '%d notifications' => '%d notificaciones', + '%d notification' => '%d notificación', + 'There is no external integration installed.' => 'No se ha instalado ninguna integración externa.', + 'You are not allowed to update tasks assigned to someone else.' => 'No se le permite actualizar tareas asignadas a alguien más.', + 'You are not allowed to change the assignee.' => 'No se le permite cambiar la persona asignada.', + 'Task suppression is not permitted' => 'No está permitido la eliminación de tarea', + 'Changing assignee is not permitted' => 'No está permitido cambiar la persona asignada', + 'Update only assigned tasks is permitted' => 'Está permitido actualizar solo las tareas asignadas', + 'Only for tasks assigned to the current user' => 'Solo para las tareas asignadas al usuario actual', + 'My projects' => 'Mis proyectos', + 'You are not a member of any project.' => 'No eres miembro de ningún proyecto.', + 'My subtasks' => 'Mis subtareas', + '%d subtasks' => '%d subtareas', + '%d subtask' => '%d subtarea', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Mover tareas entre esas columnas solo está permitido para las tareas asignadas al usuario actual.', + '[DUPLICATE]' => '[DUPLICAR]', + 'DKK - Danish Krona' => 'DKK - Corona Danesa', + 'Remove user from group' => 'Eliminar usuario del grupo', + 'Assign the task to its creator' => 'Asignar la tarea a su creador', + 'This task was sent by email to "%s" with subject "%s".' => 'Esta tarea fue enviada por correo a "%s" con el asunto "%s".', + 'Predefined Email Subjects' => 'Asuntos de correo predefinidos', + 'Write one subject by line.' => 'Escriba un asunto por línea.', + 'Create another link' => 'Crear otro enlace', + 'BRL - Brazilian Real' => 'BRL - Real brasileño', + 'Add a new Kanboard task' => 'Agregar una nueva tarea de Kanboard', + 'Subtask not started' => 'Subtarea no iniciada', + 'Subtask currently in progress' => 'Subtarea actualmente en progreso', + 'Subtask completed' => 'Subtarea completada', + 'Subtask added successfully.' => 'Subtarea agregada con éxito.', + '%d subtasks added successfully.' => '%d subtareas agregadas con éxito.', + 'Enter one subtask by line.' => 'Ingrese una subtarea por línea.', + 'Predefined Contents' => 'Contenidos predefinidos', + 'Predefined contents' => 'Contenidos predefinidos', + 'Predefined Task Description' => 'Descripción de tarea predefinida', + 'Do you really want to remove this template? "%s"' => '¿Realmente quieres eliminar esta plantilla? "%s"', + 'Add predefined task description' => 'Agregar descripción de tarea predefinida', + 'Predefined Task Descriptions' => 'Descripciones de tareas predefinidas', + 'Template created successfully.' => 'Plantilla creada con éxito', + 'Unable to create this template.' => 'No se puede crear esta plantilla.', + 'Template updated successfully.' => 'No se puede actualizar esta plantilla.', + 'Unable to update this template.' => 'No se puede actualizar esta plantilla.', + 'Template removed successfully.' => 'Plantilla eliminada con éxito.', + 'Unable to remove this template.' => 'No se puede eliminar esta plantilla.', + 'Template for the task description' => 'Plantilla para la descripción de la tarea', + 'The start date is greater than the end date' => 'La fecha de inicio es mayor que la fecha de finalización', + 'Tags must be separated by a comma' => 'Las etiquetas deben estar separadas por una coma', + 'Only the task title is required' => 'Solo se requiere el título de la tarea', + 'Creator Username' => 'Nombre del creador', + 'Color Name' => 'Nombre de color', + 'Column Name' => 'Nombre de columna', + 'Swimlane Name' => 'Nombre del carril', + 'Time Estimated' => 'Tiempo estimado', + 'Time Spent' => 'Tiempo empleado', + 'External Link' => 'Enlace externo', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Esta característica habilita el feed iCal, el feed RSS y la vista de la pizarra pública.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Detener el temporizador de todas las subtareas al mover una tarea a otra columna', + 'Subtask Title' => 'Título de subtarea', + 'Add a subtask and activate the timer when moving a task to another column' => 'Agregar una subtarea y activar el temporizador al mover una tarea a otra columna', + 'days' => 'dias', + 'minutes' => 'minutos', + 'seconds' => 'segundos', + 'Assign automatically a color when preset start date is reached' => 'Asigna automáticamente un color cuando se alcanza la fecha de inicio preestablecida', + 'Move the task to another column once a predefined start date is reached' => 'Mover la tarea a otra columna una vez que se alcanza una fecha de inicio predefinida', + 'This task is now linked to the task %s with the relation "%s"' => 'Esta tarea ahora está vinculada a la tarea %s con la relación "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'El enlace con la relación "%s" con la tarea %s se ha eliminado', + 'Custom Filter:' => 'Filtro personalizado:', + 'Unable to find this group.' => 'No se puede encontrar este grupo.', + '%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 en la columna "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s movió la tarea #%d al carril "%s"', + '%sh spent' => '%sh empleados', + '%sh estimated' => '%sh estimado', + 'Select All' => 'Seleccionar todo', + 'Unselect All' => 'Deseleccionar todo', + 'Apply action' => 'Aplicar acción', + 'Move selected tasks to another column or swimlane' => 'Mover tareas seleccionadas a otra columna', + 'Edit tasks in bulk' => 'Editar tareas en masa', + 'Choose the properties that you would like to change for the selected tasks.' => 'Elija las propiedades que le gustaría cambiar para las tareas seleccionadas.', + 'Configure this project' => 'Configurar este proyecto', + 'Start now' => 'Empezar ahora', + '%s removed a file from the task #%d' => '%s eliminó un archivo de la tarea #%d', + 'Attachment removed from task #%d: %s' => 'Adjunto eliminado de la tarea #%d: %s', + 'No color' => 'Sin color', + 'Attachment removed "%s"' => 'Adjunto eliminado "%s"', + '%s removed a file from the task %s' => '%s eliminó un archivo de la tarea %s', + 'Move the task to another swimlane when assigned to a user' => 'Mover la tarea a otro carril cuando se le asigna a un usuario', + 'Destination swimlane' => 'Carril de destino', + 'Assign a category when the task is moved to a specific swimlane' => 'Asigne una categoría cuando la tarea se mueva a un carril específico', + 'Move the task to another swimlane when the category is changed' => 'Mueve la tarea a otro carril cuando se cambia la categoría', + 'Reorder this column by priority (ASC)' => 'Reordenar esta columna por prioridad (ASC)', + 'Reorder this column by priority (DESC)' => 'Reordenar esta columna por prioridad (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Reordenar esta columna por asignación y prioridad (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Reordenar esta columna por asignación y prioridad (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Reordenar esta columna por persona asignada (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Reordenar esta columna por persona asignada (Z-A)', + 'Reorder this column by due date (ASC)' => 'Reordenar esta columna por fecha de vencimiento (ASC)', + 'Reorder this column by due date (DESC)' => 'Reordenar esta columna por fecha de vencimiento (DESC)', + 'Reorder this column by id (ASC)' => 'Reordenar esta columna por id (ASC)', + 'Reorder this column by id (DESC)' => 'Reordenar esta columna por id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s movió la tarea #%d "%s" al proyecto "%s', + 'Task #%d "%s" has been moved to the project "%s"' => 'La tarea #%d "%s" se movió al proyecto "%s', + 'Move the task to another column when the due date is less than a certain number of days' => 'Mueva la tarea a otra columna cuando la fecha de vencimiento sea inferior a un cierto número de días', + 'Automatically update the start date when the task is moved away from a specific column' => 'Actualizar automáticamente la fecha de inicio cuando la tarea se aleje de cierta columna', + 'HTTP Client:' => 'Cliente HTTP:', + 'Assigned' => 'Asignado', + 'Task limits apply to each swimlane individually' => 'Los límites de tareas se aplican a cada carril individualmente', + 'Column task limits apply to each swimlane individually' => 'Los límites de tareas de columna se aplican a cada carril individualmente', + 'Column task limits are applied to each swimlane individually' => 'Los límites de tareas de columna se aplican a cada carril individualmente', + 'Column task limits are applied across swimlanes' => 'Los límites de tareas de columna se aplican a través de los carriles', + 'Task limit: ' => 'Límite de tareas: ', + 'Change to global tag' => 'Cambiar a etiqueta global', + 'Do you really want to make the tag "%s" global?' => '¿Realmente quieres hacer la etiqueta "%s" global?', + 'Enable global tags for this project' => 'Habilitar etiquetas globales para este proyecto', + 'Group membership(s):' => 'Membresía(s) de grupo:', + '%s is a member of the following group(s): %s' => '%s es miembro de los siguientes grupos: %s', + '%d/%d group(s) shown' => '%d/%d grupo(s) mostrados', + 'Subtask creation or modification' => 'Creación o modificación de subtarea', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Asigne la tarea a un usuario específico cuando la tarea se mueva a un carril específico', + 'Comment' => 'Comentario', + 'Collapse vertically' => 'Colapsar verticalmente', + 'Expand vertically' => 'Expandir verticalmente', + 'MXN - Mexican Peso' => 'MXN - Peso mexicano', + 'Estimated vs actual time per column' => 'Tiempo estimado vs real por columna', + 'HUF - Hungarian Forint' => 'HUF - Forinto húngaro', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => '¡Debe seleccionar un archivo para subir como su avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => '¡El archivo que subió no es una imagen válida! (¡Solo se permiten *.gif, *.jpg, *.jpeg y *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Establecer automáticamente la fecha de vencimiento cuando la tarea se mueve de una columna específica', + 'No other projects found.' => 'No se encontraron otros proyectos.', + 'Tasks copied successfully.' => 'Tareas copiadas con éxito.', + 'Unable to copy tasks.' => 'No se pueden copiar las tareas.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema claro', + 'Dark theme' => 'Tema oscuro', + 'Automatic theme - Sync with system' => 'Tema automático - Sincronizar con el sistema', + 'Application managers or more' => 'Gestores de la aplicación o más', + 'Administrators' => 'Administradores', + 'Visibility:' => 'Visibilidad:', + 'Standard users' => 'Usuarios estándar', + 'Visibility is required' => 'La visibilidad es obligatoria', + 'The visibility should be an app role' => 'La visibilidad debe ser un rol de la aplicación', + 'Reply' => 'Responder', + '%s wrote: ' => '%s escribió: ', + 'Number of visible tasks in this column and swimlane' => 'Número de tareas visibles en esta columna y carril', + 'Number of tasks in this swimlane' => 'Número de tareas en este carril', + 'Unable to find another subtask in progress, you can close this window.' => 'No se puede encontrar otra subtarea en progreso, puede cerrar esta ventana.', + 'This theme is invalid' => 'Este tema no es válido', + 'This role is invalid' => 'Este rol no es válido', + 'This timezone is invalid' => 'Esta zona horaria no es válida', + 'This language is invalid' => 'Este idioma no es válido', + 'This URL is invalid' => 'Esta URL no es válida', + 'Date format invalid' => 'Formato de fecha no válido', + 'Time format invalid' => 'Formato de hora no válido', + 'Invalid Mail transport' => 'Transporte de correo no válido', + 'Color invalid' => 'Color no válido', + 'This value must be greater or equal to %d' => 'Este valor debe ser mayor o igual a %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Agregar un BOM al principio del archivo (requerido para Microsoft Excel)', + 'Just add these tag(s)' => 'Agregar solo estas etiquetas', + 'Remove internal link(s)' => 'Eliminar enlaces internos', + 'Import tasks from another project' => 'Importar tareas de otro proyecto', + 'Select the project to copy tasks from' => 'Seleccione el proyecto del que desea copiar tareas', + 'The total maximum allowed attachments size is %sB.' => 'El tamaño máximo total permitido para los archivos adjuntos es %sB.', + 'Add attachments' => 'Agregar archivos adjuntos', + 'Task #%d "%s" is overdue' => 'La tarea #%d "%s" está vencida', + 'Enable notifications by default for all new users' => 'Habilitar notificaciones por defecto para todos los nuevos usuarios', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Asignar la tarea a su creador para columnas específicas si no se ha establecido un responsable manualmente', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Asignar una tarea al usuario conectado al cambiar de columna a la columna especificada si no hay ningún usuario asignado', +]; diff --git a/app/Locale/es_VE/translations.php b/app/Locale/es_VE/translations.php new file mode 100644 index 0000000..7c55433 --- /dev/null +++ b/app/Locale/es_VE/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Ninguno', + 'Edit' => 'Modificar', + 'Remove' => 'Eliminar', + 'Yes' => 'Sí', + 'No' => 'No', + 'cancel' => 'cancelar', + 'or' => 'o', + 'Yellow' => 'amarillo', + 'Blue' => 'azul', + 'Green' => 'verde', + 'Purple' => 'púrpura', + 'Red' => 'rojo', + 'Orange' => 'anaranjado', + 'Grey' => 'gris', + 'Brown' => 'marrón', + 'Deep Orange' => 'anaranjado oscuro', + 'Dark Grey' => 'gris oscuro', + 'Pink' => 'rosado', + 'Teal' => 'verde azulado', + 'Cyan' => 'azul claro', + 'Lime' => 'verde limón', + 'Light Green' => 'verde claro', + 'Amber' => 'ámbar', + 'Save' => 'Guardar', + 'Login' => 'Iniciar sesión', + 'Official website:' => 'Página web oficial :', + 'Unassigned' => 'Sin asignar', + 'View this task' => 'Ver esta tarea', + 'Remove user' => 'Eliminar un usuario(a)', + 'Do you really want to remove this user: "%s"?' => '¿Realmente quiere eliminar este usuario(a): « %s » ?', + 'All users' => 'Todos los usuario(a)s', + 'Username' => 'Nombre de usuario(a)', + 'Password' => 'Contraseña', + 'Administrator' => 'Administrador(a)', + 'Sign in' => 'Iniciar sesión', + 'Users' => 'Usuario(a)s', + 'Forbidden' => 'Proibido', + 'Access Forbidden' => 'Acceso prohibido', + 'Edit user' => 'Editar usuario(a)', + 'Logout' => 'Cerrar sesión', + 'Bad username or password' => 'Usuario(a) incorrecto(a) y/o contraseña incorrecta', + 'Edit project' => 'Editar proyecto', + 'Name' => 'Nombre', + 'Projects' => 'Proyectos', + 'No project' => 'Sin proyecto alguno', + 'Project' => 'Proyecto', + 'Status' => 'Estado', + 'Tasks' => 'Tareas', + 'Board' => 'Tablero', + 'Actions' => 'Acciones', + 'Inactive' => 'Inactivo', + 'Active' => 'Activo', + 'Unable to update this board.' => 'No se puede actualizar este tablero.', + 'Disable' => 'Desactivar', + 'Enable' => 'Activar', + 'New project' => 'Proyecto nuevo', + 'Do you really want to remove this project: "%s"?' => '¿Realmente quiere eliminar este proyecto: « %s » ?', + 'Remove project' => 'Eliminar proyecto', + 'Edit the board for "%s"' => 'Modificar tablero para « %s »', + 'Add a new column' => 'Añadir una nueva columna', + 'Title' => 'Título', + 'Assigned to %s' => 'Asignado(a) a %s', + 'Remove a column' => 'Eliminar esta columna', + 'Unable to remove this column.' => 'No se puede eliminar esta columna.', + 'Do you really want to remove this column: "%s"?' => '¿Realmente quiere eliminar esta columna : « %s » ?', + 'Settings' => 'Configuración de preferencias', + 'Application settings' => 'Parámetros de aplicación', + 'Language' => 'Idioma', + 'Webhook token:' => 'Token para los webhooks :', + 'API token:' => 'Token de la API:', + 'Database size:' => 'Tamaño de la base de datos:', + 'Download the database' => 'Descargar base de datos', + 'Optimize the database' => 'Optimizar base de datos', + '(VACUUM command)' => '(Comando VACUUM)', + '(Gzip compressed Sqlite file)' => '(Archivo Sqlite comprimido en Gzip)', + 'Close a task' => 'Cerrar una tarea', + 'Column' => 'Columna', + 'Color' => 'Color', + 'Assignee' => 'Persona asignada', + 'Create another task' => 'Crear otra tarea', + 'New task' => 'Tarea nueva', + 'Open a task' => 'Abrir una tarea', + 'Do you really want to open this task: "%s"?' => '¿Realmente quiere abrir esta tarea: « %s » ?', + 'Back to the board' => 'Regresar al tablero', + 'There is nobody assigned' => 'Nadie asignado a esta tarea', + 'Column on the board:' => 'Columna en el tablero: ', + 'Close this task' => 'Cerrar esta tarea', + 'Open this task' => 'Abrir esta tarea', + 'There is no description.' => 'No hay descripción.', + 'Add a new task' => 'Añadir una tarea nueva', + 'The username is required' => 'El nombre de usuario(a) es obligatorio', + 'The maximum length is %d characters' => 'La longitud máxima es de %d caracteres', + 'The minimum length is %d characters' => 'La longitud mínima es de %d caracteres', + 'The password is required' => 'La contraseña es obligatoria', + 'This value must be an integer' => 'Este valor debe ser un número entero', + 'The username must be unique' => 'El nombre de usuario(a) debe ser único', + 'The user id is required' => 'El identificador del usuario(a) es obligatorio', + 'Passwords don\'t match' => 'Las contraseñas no coinciden', + 'The confirmation is required' => 'La confirmación es obligatoria', + 'The project is required' => 'El proyecto es obligatorio', + 'The id is required' => 'El identificador es obligatorio', + 'The project id is required' => 'El identificador del proyecto es obligatorio', + 'The project name is required' => 'El nombre del proyecto es obligatorio', + 'The title is required' => 'El título es obligatorio', + 'Settings saved successfully.' => 'Parámetros guardados correctamente.', + 'Unable to save your settings.' => 'No se pueden guardar sus parámetros.', + 'Database optimization done.' => 'Optimización de la base de datos finalizada.', + 'Your project has been created successfully.' => 'El proyecto ha sido creado correctamente.', + 'Unable to create your project.' => 'No se puede crear el proyecto.', + 'Project updated successfully.' => 'El proyecto ha sido actualizado correctamente.', + 'Unable to update this project.' => 'No se puede actualizar el proyecto.', + 'Unable to remove this project.' => 'No se puede eliminar este proyecto.', + 'Project removed successfully.' => 'El proyecto ha sido eliminado correctamente.', + 'Project activated successfully.' => 'El proyecto ha sido activado correctamente.', + 'Unable to activate this project.' => 'No se puede activar el proyecto.', + 'Project disabled successfully.' => 'El proyecto ha sido desactivado correctamente.', + 'Unable to disable this project.' => 'No se puede desactivar el proyecto.', + 'Unable to open this task.' => 'No se puede abrir esta tarea.', + 'Task opened successfully.' => 'La tarea ha sido abierta correctamente.', + 'Unable to close this task.' => 'No se puede cerrar esta tarea.', + 'Task closed successfully.' => 'La tarea ha sido cerrada correctamente.', + 'Unable to update your task.' => 'No se puede modificar esta tarea.', + 'Task updated successfully.' => 'La tarea ha sido actualizada correctamente.', + 'Unable to create your task.' => 'No se puede crear esta tarea.', + 'Task created successfully.' => 'La tarea ha sido creada correctamente.', + 'User created successfully.' => 'El usuario(a) ha sido creado correctamente.', + 'Unable to create your user.' => 'No se puede crear este usuario(a).', + 'User updated successfully.' => 'El usuario(a) ha sido actualizado correctamente.', + 'User removed successfully.' => 'El usuario(a) ha sido creado correctamente.', + 'Unable to remove this user.' => 'No se puede crear este usuario(a).', + 'Board updated successfully.' => 'El tablero ha sido actualizado correctamente.', + 'Ready' => 'Listo', + 'Backlog' => 'En espera', + 'Work in progress' => 'En curso', + 'Done' => 'Hecho', + 'Application version:' => 'Versión de la aplicación:', + 'Id' => 'Identificador', + 'Public link' => 'Vinculación pública', + 'Timezone' => 'Zona horaria', + 'Sorry, I didn\'t find this information in my database!' => 'Lo siento no he encontrado información en la base de datos!', + 'Page not found' => 'Página no encontrada', + 'Complexity' => 'Complejidad', + 'Task limit' => 'Número máximo de tareas', + 'Task count' => 'Conteo de tareas', + 'User' => 'Usuario(a)', + 'Comments' => 'Comentarios', + 'Comment is required' => 'El comentario es obligatorio', + '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', + 'Invalid date' => 'Fecha no válida', + 'Automatic actions' => 'Acciones automatizadas', + 'Your automatic action has been created successfully.' => 'La acción automatizada ha sido creada correctamente.', + 'Unable to create your automatic action.' => 'No se puede crear esta acción automatizada.', + 'Remove an action' => 'Eliminar una acción', + 'Unable to remove this action.' => 'No se puede eliminar esta accción.', + 'Action removed successfully.' => 'La acción ha sido eliminada correctamente.', + 'Automatic actions for the project "%s"' => 'Acciones automatizadas para este proyecto « %s »', + 'Add an action' => 'Agregar una acción', + 'Event name' => 'Nombre del evento', + 'Action' => 'Acción', + 'Event' => 'Evento', + 'When the selected event occurs execute the corresponding action.' => 'Cuando tiene lugar el evento seleccionado, ejecutar la acción correspondiente.', + 'Next step' => 'Siguiente etapa', + 'Define action parameters' => 'Definición de los parametros de la acción', + 'Do you really want to remove this action: "%s"?' => '¿Realmente quiere eliminar esta acción « %s » ?', + 'Remove an automatic action' => 'Eliminar una acción automatizada', + 'Assign the task to a specific user' => 'Asignar una tarea a un usuario(a) específico(a)', + 'Assign the task to the person who does the action' => 'Asignar la tarea al usuario quien realiza la acción', + 'Duplicate the task to another project' => 'Duplicar la tarea a otro proyecto', + 'Move a task to another column' => 'Mover una tarea a otra columna', + 'Task modification' => 'Modificación de una tarea', + 'Task creation' => 'Creación de una tarea', + 'Closing a task' => 'Cerrar una tarea', + 'Assign a color to a specific user' => 'Asignar un color a un(a) usuario(a) específico(a)', + 'Position' => 'Posición', + 'Duplicate to project' => 'Duplicar a otro proyecto', + 'Duplicate' => 'Duplicar', + 'Link' => 'Enlace', + 'Comment updated successfully.' => 'El comentario ha sido actualizado correctamente.', + 'Unable to update your comment.' => 'No se puede actualizar este comentario.', + 'Remove a comment' => 'Eliminar un comentario', + 'Comment removed successfully.' => 'El comentario ha sido eliminado correctamente.', + 'Unable to remove this comment.' => 'No se puede eliminar este comentario.', + 'Do you really want to remove this comment?' => '¿Realmente quiere eliminar este comentario?', + 'Current password for the user "%s"' => 'Contraseña actual para el(la) usuario(a): « %s »', + 'The current password is required' => 'La contraseña es obligatoria', + 'Wrong password' => 'contraseña incorrecta', + 'Unknown' => 'Desconocido', + 'Last logins' => 'Últimos ingresos', + 'Login date' => 'Fecha de ingreso', + 'Authentication method' => 'Método de autenticación', + 'IP address' => 'Dirección IP', + 'User agent' => 'Agente de usuario', + 'Persistent connections' => 'Conexión persistente', + 'No session.' => 'No existe sesión.', + 'Expiration date' => 'Fecha de expiración', + 'Remember Me' => 'Recuérdame', + 'Creation date' => 'Fecha de creación', + 'Everybody' => 'Todo el mundo', + 'Open' => 'Abierto', + 'Closed' => 'Cerrado', + 'Search' => 'Buscar', + 'Nothing found.' => 'Nada hallado.', + 'Due date' => 'Fecha Límite', + 'Description' => 'Descripción', + '%d comments' => '%d comentarios', + '%d comment' => '%d comentario', + 'Email address invalid' => 'Dirección de correo inválida', + 'Your external account is not linked anymore to your profile.' => 'Su cuenta externa no está vinculada a tu perfil.', + 'Unable to unlink your external account.' => 'No se puede desvincular su cuenta externa.', + 'External authentication failed' => 'Error de autenticación externa', + 'Your external account is linked to your profile successfully.' => 'Su cuenta externa está vinculada a su perfil correctamente.', + 'Email' => 'Correo electrónico', + 'Task removed successfully.' => 'Tarea eliminada correctamente.', + 'Unable to remove this task.' => 'No pude eliminar esta tarea.', + 'Remove a task' => 'Eliminar una tarea', + 'Do you really want to remove this task: "%s"?' => '¿Realmente quiere eliminar esta tarea: "%s"?', + 'Assign automatically a color based on a category' => 'Asignar un color de forma automática basándose en la categoría', + 'Assign automatically a category based on a color' => 'Asignar una categoría de forma automática basándose en el color', + 'Task creation or modification' => 'Creación o Edición de Tarea', + 'Category' => 'Categoría', + 'Category:' => 'Categoría:', + 'Categories' => 'Categorías', + 'Your category has been created successfully.' => 'Se ha creado su categoría correctamente.', + 'This category has been updated successfully.' => 'Esta categoría se ha actualizado correctamente.', + 'Unable to update this category.' => 'No se puede actualizar esta categoría.', + 'Remove a category' => 'Eliminar una categoría', + 'Category removed successfully.' => 'Categoría eliminidad correctamente.', + 'Unable to remove this category.' => 'No se pudo eliminar esta categoría.', + 'Category modification for the project "%s"' => 'Modificación de categoría para el proyecto "%s"', + 'Category Name' => 'Nombre de categoría', + 'Add a new category' => 'Añadir una categoría nueva', + 'Do you really want to remove this category: "%s"?' => '¿Realmente quiere eliminar esta categoría: "%s"?', + 'All categories' => 'Todas las categorías', + 'No category' => 'Sin categoría', + 'The name is required' => 'El nombre es obligatorio', + 'Remove a file' => 'Borrar un archivo', + 'Unable to remove this file.' => 'No pude borrar este archivo.', + 'File removed successfully.' => 'Archivo eliminado correctamente.', + 'Attach a document' => 'Adjuntar un documento', + 'Do you really want to remove this file: "%s"?' => '¿Realmente quiere eliminar este archivo: "%s"?', + 'Attachments' => 'Archivos adjuntos', + 'Edit the task' => 'Editar la tarea', + 'Add a comment' => 'Añadir un comentario', + 'Edit a comment' => 'Editar un comentario', + 'Summary' => 'Resumen', + 'Time tracking' => 'Control de tiempo', + 'Estimate:' => 'Estimado:', + 'Spent:' => 'Transcurrido:', + 'Do you really want to remove this sub-task?' => '¿Realmente quiere eliminar esta subtarea?', + 'Remaining:' => 'Restante', + 'hours' => 'horas', + 'estimated' => 'estimado', + 'Sub-Tasks' => 'Subtareas', + 'Add a sub-task' => 'Añadir una subtarea', + 'Original estimate' => 'Horas presupuestadas', + 'Create another sub-task' => 'Crear otra subtarea', + 'Time spent' => 'Horas ejecutadas', + 'Edit a sub-task' => 'Editar una subtarea', + 'Remove a sub-task' => 'Suprimir una subtarea', + 'The time must be a numeric value' => 'El tiempo debe de ser un valor numérico', + 'Todo' => 'Por hacer', + 'In progress' => 'En progreso', + 'Sub-task removed successfully.' => 'Subtarea eliminada correctamente.', + 'Unable to remove this sub-task.' => 'No pude eliminar esta subtarea.', + 'Sub-task updated successfully.' => 'Subtarea actualizada correctamente.', + 'Unable to update your sub-task.' => 'No pude actualizar tu subtarea.', + 'Unable to create your sub-task.' => 'No pude crear tu subtarea.', + 'Maximum size: ' => 'Tamaño máximo', + 'Display another project' => 'Mostrar otro proyecto', + 'Created by %s' => 'Creado por %s', + 'Tasks Export' => 'Exportar tareas', + 'Start Date' => 'Fecha de inicio', + 'Execute' => 'Ejecutar', + 'Task Id' => 'ID de tarea', + 'Creator' => 'Creador', + 'Modification date' => 'Fecha de modificación', + 'Completion date' => 'Fecha de finalización', + 'Clone' => 'Clonar', + 'Project cloned successfully.' => 'Proyecto clonado correctamente', + 'Unable to clone this project.' => 'Impsible clonar proyecto', + 'Enable email notifications' => 'Habilitar notificaciones por correo electrónico', + 'Task position:' => 'Posición de la tarea', + 'The task #%d has been opened.' => 'La tarea #%d ha sido abierta', + 'The task #%d has been closed.' => 'La tarea #%d ha sido cerrada', + 'Sub-task updated' => 'Subtarea actualizada', + 'Title:' => 'Título:', + 'Status:' => 'Estado:', + 'Assignee:' => 'Asignada a:', + 'Time tracking:' => 'Control de tiempo:', + 'New sub-task' => 'Nueva subtarea', + 'New attachment added "%s"' => 'Nuevo adjunto agregado "%s"', + 'New comment posted by %s' => 'Nuevo comentario agregado por %s', + 'New comment' => 'Nuevo comentario', + 'Comment updated' => 'Comentario actualizado', + 'New subtask' => 'Nueva subtarea', + 'I only want to receive notifications for these projects:' => 'Quiero recibir notificaciones sólo de estos proyectos:', + 'view the task on Kanboard' => 'ver la tarea en Kanboard', + 'Public access' => 'Acceso público', + 'Disable public access' => 'Desactivar acceso público', + 'Enable public access' => 'Activar acceso público', + 'Public access disabled' => 'Acceso público desactivado', + 'Move the task to another project' => 'Mover la tarea a otro proyecto', + 'Move to project' => 'Mover a otro proyecto', + 'Do you really want to duplicate this task?' => '¿Realmente quiere duplicar esta tarea?', + 'Duplicate a task' => 'Duplicar una tarea', + 'External accounts' => 'Cuentas externas', + 'Account type' => 'Tipo de Cuenta', + 'Local' => 'Local', + 'Remote' => 'Remota', + 'Enabled' => 'Activada', + 'Disabled' => 'Desactivada', + 'Login:' => 'Login:', + 'Full Name:' => 'Nombre completo:', + 'Email:' => 'Correo electrónico:', + 'Notifications:' => 'Notificaciones:', + 'Notifications' => 'Notificaciones', + 'Account type:' => 'Tipo de Cuenta:', + 'Edit profile' => 'Editar perfil', + 'Change password' => 'Cambiar contraseña', + 'Password modification' => 'Modificacion de contraseña', + 'External authentications' => 'Autenticación externa', + 'Never connected.' => 'Nunca se ha conectado.', + 'No external authentication enabled.' => 'Sin autenticación externa activa.', + 'Password modified successfully.' => 'Contraseña cambiada correctamente.', + 'Unable to change the password.' => 'No pude cambiar la contraseña.', + 'Change category' => 'Cambiar categoría', + '%s updated the task %s' => '%s actualizó la tarea %s', + '%s opened the task %s' => '%s abrió la tarea %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s movió la tarea %s a la posición #%d de la columna "%s"', + '%s moved the task %s to the column "%s"' => '%s movió la tarea %s a la columna "%s"', + '%s created the task %s' => '%s creó la tarea %s', + '%s closed the task %s' => '%s cerró la tarea %s', + '%s created a subtask for the task %s' => '%s creó una subtarea para la tarea %s', + '%s updated a subtask for the task %s' => '%s actualizó una subtarea para la tarea %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Asignada a %s con una estimación de %s/%sh', + 'Not assigned, estimate of %sh' => 'No asignada, se estima en %sh', + '%s updated a comment on the task %s' => '%s actualizó un comentario de la tarea %s', + '%s commented the task %s' => '%s comentó la tarea %s', + '%s\'s activity' => 'Actividad de %s', + 'RSS feed' => 'Archivo RSS', + '%s updated a comment on the task #%d' => '%s actualizó un comentario de la tarea #%d', + '%s commented on the task #%d' => '%s comentó la tarea #%d', + '%s updated a subtask for the task #%d' => '%s actualizó una subtarea de la tarea #%d', + '%s created a subtask for the task #%d' => '%s creó una subtarea de la tarea #%d', + '%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 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 de los nuevos proyectos (Separadas mediante comas)', + 'Task assignee change' => 'Cambiar persona asignada a la tarea', + '%s changed the assignee of the task #%d to %s' => '%s cambió al asignado de la tarea #%d a %s', + '%s changed the assignee of the task %s to %s' => '%s cambió la persona asignada de la tarea de %s a %s', + 'New password for the user "%s"' => 'Nueva contraseña para el usuario(a) "%s"', + 'Choose an event' => 'Seleccione un evento', + 'Create a task from an external provider' => 'Crear una tarea a partir de un proveedor externo', + 'Change the assignee based on an external username' => 'Cambiar la asignación basado en un nombre de usuario(a) externo', + 'Change the category based on an external label' => 'Cambiar la categoría basado en una etiqueta externa', + 'Reference' => 'Referencia', + 'Label' => 'Etiqueta', + 'Database' => 'Base de Datos', + 'About' => 'Acerca de', + 'Database driver:' => 'Controlador de la base de datos', + 'Board settings' => 'Configuraciones del Tablero', + 'Webhook settings' => 'Configuraciones del Webhook', + 'Reset token' => 'Reiniciar token', + 'API endpoint:' => 'Punto extremo del API', + 'Refresh interval for personal board' => 'Intervalo de refrescamiento del tablero privado', + 'Refresh interval for public board' => 'Intervalo de refrescamiento del tablero público', + 'Task highlight period' => 'Período del realce de la tarea', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (en segundos) para considerar que una tarea fue modificada recientemente (0 para deshabilitar, 2 días por defecto)', + 'Frequency in second (60 seconds by default)' => 'Frecuencia en segundos (60 segundos por defecto)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frecuencia en segundos (0 para deshabilitar esta característica, 10 segundos por defecto)', + 'Application URL' => 'URL de la aplicación', + 'Token regenerated.' => 'Token regenerado', + 'Date format' => 'Formato de la fecha', + 'ISO format is always accepted, example: "%s" and "%s"' => 'El formato ISO siempre es aceptado, ejemplo: "%s" y "%s"', + 'New personal project' => 'Nuevo proyecto privado', + 'This project is personal' => 'Este proyecto es privado', + 'Add' => 'Añadir', + 'Start date' => 'Fecha de inicio', + 'Time estimated' => 'Horas presupuestadas', + 'There is nothing assigned to you.' => 'Nada asignado para usted', + 'My tasks' => 'Mis tareas', + 'Activity stream' => 'Flujo de actividades', + 'Dashboard' => 'Tablero', + 'Confirmation' => 'Confirmación', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Crear un comentario a partir de un proveedor externo', + 'Project management' => 'Gestión de proyectos', + 'Columns' => 'Columnas', + 'Task' => 'Tarea', + 'Percentage' => 'Porcentaje', + 'Number of tasks' => 'Número de tareas', + 'Task distribution' => 'Distribución de tareas', + 'Analytics' => 'Analítica', + 'Subtask' => 'Subtarea', + 'User repartition' => 'Usuario(a)s y su repartición de trabajo', + 'Clone this project' => 'Clonar este proyecto', + 'Column removed successfully.' => 'Columna removida correctamente', + 'Not enough data to show the graph.' => 'No hay suficiente información para mostrar el gráfico', + 'Previous' => 'Anterior', + 'The id must be an integer' => 'El identificador debe ser un número entero', + 'The project id must be an integer' => 'El identificador del proyecto debe ser un número entero', + 'The status must be an integer' => 'El estado debe ser un número entero', + 'The subtask id is required' => 'El identificador de la subtarea es requerido', + 'The subtask id must be an integer' => 'El identificador de la subtarea debe ser un número entero', + 'The task id is required' => 'El identificador de la tarea es requerido', + 'The task id must be an integer' => 'El identificador de la tarea debe ser un número entero', + 'The user id must be an integer' => 'El identificador del usuario(a) debe ser un número entero', + 'This value is required' => 'El valor es requerido', + 'This value must be numeric' => 'Este valor debe ser numérico', + 'Unable to create this task.' => 'Imposible crear esta tarea', + 'Cumulative flow diagram' => 'Diagrama de flujo acumulativo', + 'Daily project summary' => 'Resumen diario del proyecto', + 'Daily project summary export' => 'Exportar sumario diario del proyecto', + 'Exports' => 'Exportar', + 'This export contains the number of tasks per column grouped per day.' => 'Esta exportación contiene el número de tereas por columna agrupada por día', + 'Active swimlanes' => 'Carriles activos', + 'Add a new swimlane' => 'Añadir nuevo carril', + 'Default swimlane' => 'Carril por defecto', + 'Do you really want to remove this swimlane: "%s"?' => '¿Realmente quiere remover este carril: "%s"?', + 'Inactive swimlanes' => 'Carriles inactivos', + 'Remove a swimlane' => 'Remover un carril', + 'Swimlane modification for the project "%s"' => 'Modificación del carril para el proyecto "%s"', + 'Swimlane removed successfully.' => 'Carril removido correctamente', + 'Swimlanes' => 'Carriles', + 'Swimlane updated successfully.' => 'Carril actualizado correctamente', + 'Unable to remove this swimlane.' => 'Imposible remover este carril', + 'Unable to update this swimlane.' => 'Imposible actualizar este carril', + 'Your swimlane has been created successfully.' => 'Su carril ha sido creado correctamente', + 'Example: "Bug, Feature Request, Improvement"' => 'Ejemplo: "Error, Solicitud de característica, Mejora"', + 'Default categories for new projects (Comma-separated)' => 'Categorías por defecto de los nuevos proyectos (separadas mediante comas)', + 'Integrations' => 'Integraciones', + 'Integration with third-party services' => 'Integraciones para servicios de terceros', + 'Subtask Id' => 'Identificador de subtareas', + 'Subtasks' => 'Subtareas', + 'Subtasks Export' => 'Exportar subtareas', + 'Task Title' => 'Título de la tarea', + 'Untitled' => 'Sin título', + 'Application default' => 'Predefinido de la aplicación', + 'Language:' => 'Idioma', + 'Timezone:' => 'Zona horaria', + 'All columns' => 'Todas las columnas', + 'Next' => 'Siguiente', + '#%d' => '#%d', + 'All swimlanes' => 'Todos los carriles', + 'All colors' => 'Todos los colores', + 'Moved to column %s' => 'Movido a columna %s', + 'User dashboard' => 'Tablero de el(la) usuario(a)', + 'Allow only one subtask in progress at the same time for a user' => 'Permitir únicamente una subtarea en progreso al mismo tiempo para un usuario(a)', + 'Edit column "%s"' => 'Editar columna "%s"', + 'Select the new status of the subtask: "%s"' => 'Seleccione el nuevo estado de la subtarea: "%s"', + 'Subtask timesheet' => 'Hoja de tiempos de la subtarea', + 'There is nothing to show.' => 'No hay nada que mostrar.', + 'Time Tracking' => 'Control de Tiempo', + 'You already have one subtask in progress' => 'Ya tiene una subtarea en progreso', + 'Which parts of the project do you want to duplicate?' => '¿Que partes del proyecto quiere duplicar?', + 'Disallow login form' => 'Deshabilitar el formulario de inicio de sesión', + 'Start' => 'Inicio', + 'End' => 'Fin', + 'Task age in days' => 'Tiempo de la tarea (en días)', + 'Days in this column' => 'Días en esta columna', + '%dd' => '%dd', + 'Add a new link' => 'Añadir nuevo vínculo', + 'Do you really want to remove this link: "%s"?' => '¿Realmente quiere eliminar este vínculo: "%s"?', + 'Do you really want to remove this link with task #%d?' => '¿Realmente quiere eliminar este vínculo con la tarea #%d?', + 'Field required' => 'Campo requerido', + 'Link added successfully.' => 'Vínculo añadido correctamente', + 'Link updated successfully.' => 'Vínculo actualizado correctamente', + 'Link removed successfully.' => 'Vínculo suprimido correctamente', + 'Link labels' => 'Etiquetas de vínculos', + 'Link modification' => 'Modificar vínculo', + 'Opposite label' => 'Etiquetas opuestas', + 'Remove a link' => 'Suprimir un vínculo', + 'The labels must be different' => 'Las etiquetas deben ser diferentes', + 'There is no link.' => 'No hay vínculo', + 'This label must be unique' => 'Esta etiqueta debe ser única', + 'Unable to create your link.' => 'No se puede crear su vínculo', + 'Unable to update your link.' => 'No se puede actualizar su vínculo', + 'Unable to remove this link.' => 'No se puede suprimir su vínculo', + 'relates to' => 'relacionado con', + 'blocks' => 'bloqueos', + 'is blocked by' => 'esta bloqueado por', + 'duplicates' => 'duplicados', + 'is duplicated by' => 'está duplicado por', + 'is a child of' => 'es hijo de', + 'is a parent of' => 'es padre de', + 'targets milestone' => 'metas del hito', + 'is a milestone of' => 'es un hito de', + 'fixes' => 'arreglo', + 'is fixed by' => 'arreglado por', + 'This task' => 'Esta tarea', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Expandir tareas', + 'Collapse tasks' => 'Colapsar tareas', + 'Expand/collapse tasks' => 'Expandir/colapsar tareas', + 'Close dialog box' => 'Cerrar caudro de diálogo', + 'Submit a form' => 'Enviar un formulario', + 'Board view' => 'Vista de tablero', + 'Keyboard shortcuts' => 'Atajos del teclado', + 'Open board switcher' => 'Conmutador de tablero abierto', + 'Application' => 'Aplicación', + 'Compact view' => 'Vista compacta', + 'Horizontal scrolling' => 'Desplazamiento horizontal', + 'Compact/wide view' => 'Vista compacta/amplia', + 'Currency' => 'Moneda', + 'Personal project' => 'Proyecto privado', + 'AUD - Australian Dollar' => 'AUD - Dólar australiano', + 'CAD - Canadian Dollar' => 'CAD - Dólar canadiense', + 'CHF - Swiss Francs' => 'CHF - Franco suizo', + 'Custom Stylesheet' => 'Hoja de estilo personalizada', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Libra británica', + 'INR - Indian Rupee' => 'INR - Rupia india', + 'JPY - Japanese Yen' => 'JPY - Yen japonés', + 'NZD - New Zealand Dollar' => 'NZD - Dólar de Nueva Zelanda', + 'PEN - Peruvian Sol' => 'PEN - Sol peruano', + 'RSD - Serbian dinar' => 'RSD - Dinar serbio', + 'CNY - Chinese Yuan' => 'CNY - Yuan Chino', + 'USD - US Dollar' => 'USD - Dólar estadounidense', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar Venezolano', + 'Destination column' => 'Columna destino', + 'Move the task to another column when assigned to a user' => 'Mover la tarea a otra columna cuando sea asignada a un usuario(a)', + 'Move the task to another column when assignee is cleared' => 'Mover la tarea a otra columna cuando se elimine la persona asignada', + 'Source column' => 'Columna de origen', + 'Transitions' => 'Transiciones', + 'Executer' => 'Ejecutor', + 'Time spent in the column' => 'Horas ejecutadas en la columna', + 'Task transitions' => 'Transiciones de las tareas', + 'Task transitions export' => 'Exportar transiciones de las tareas', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este reporte contiene todos los movimientos de columna por cada tarea con la fecha, el(la) usuario(a) y el tiempo transcurrido para cada transición', + 'Currency rates' => 'Tipos de cambio', + 'Rate' => 'Tarifa', + 'Change reference currency' => 'Cambiar moneda de referencia', + 'Reference currency' => 'Moneda de referencia', + 'The currency rate has been added successfully.' => 'El tipo de cambio ha sido añadido correctamente.', + 'Unable to add this currency rate.' => 'No se puede añadir este tipo de cambio.', + 'Webhook URL' => 'URL del Webhook', + '%s removed the assignee of the task %s' => '%s eliminó al asignado de la tarea %s', + 'Information' => 'Información', + 'Check two factor authentication code' => 'Verificar el código de autenticación de dos factores', + 'The two factor authentication code is not valid.' => 'El código de autenticación de dos factores no es válido', + 'The two factor authentication code is valid.' => 'El código de autenticación de dos factores es válido', + 'Code' => 'Código', + 'Two factor authentication' => 'Autenticación de dos factores', + 'This QR code contains the key URI: ' => 'Este código QR contiene la clave URI: ', + 'Check my code' => 'Verificar mi código', + 'Secret key: ' => 'Clave secreta: ', + 'Test your device' => 'Pruebe su dispositivo', + 'Assign a color when the task is moved to a specific column' => 'Asignar un color cuando la tarea se mueve a una columna específica', + '%s via Kanboard' => '%s vía Kanboard', + 'Burndown chart' => 'Gráfico de Tareas Pendientes', + 'This chart show the task complexity over the time (Work Remaining).' => 'Este gráfico muestra la complejidad de la tarea durante el tiempo (Trabajo restante)', + 'Screenshot taken %s' => 'Captura de pantalla tomada %s', + 'Add a screenshot' => 'Añadir una captura de pantalla', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tomar una captura de pantalla y presione CTRL+V o ⌘+V para pegar aquí.', + 'Screenshot uploaded successfully.' => 'Captura de pantalla subida correctamente', + 'SEK - Swedish Krona' => 'SEK - Corona sueca', + 'Identifier' => 'Identificador', + 'Disable two factor authentication' => 'Deshabilitar la autenticación de dos factores', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '¿Realmente desea desactivar la autenticación de dos factores para este usuario(a): "%s"?', + 'Edit link' => 'Editar enlace', + 'Start to type task title...' => 'Comenzar a escribir el título de la tarea ...', + 'A task cannot be linked to itself' => 'Una tarea no se puede vincular a sí misma', + 'The exact same link already exists' => 'Ya existe un enlace idéntico', + 'Recurrent task is scheduled to be generated' => 'La tarea periódica está programada para ser generada', + 'Score' => 'Puntuación', + 'The identifier must be unique' => 'El identificador debe ser único', + 'This linked task id doesn\'t exists' => 'El identificador de la tarea enlazada no existe', + 'This value must be alphanumeric' => 'Este valor debe ser alfanumérico', + 'Edit recurrence' => 'Editar recurrencia', + 'Generate recurrent task' => 'Generar tarea recurrente', + 'Trigger to generate recurrent task' => 'Disparador para generar tarea recurrente', + 'Factor to calculate new due date' => 'Factor para calcular la nueva fecha de vencimiento', + 'Timeframe to calculate new due date' => 'Plazo para calcular la nueva fecha de vencimiento', + 'Base date to calculate new due date' => 'Fecha base para calcular la nueva fecha de vencimiento', + 'Action date' => 'Fecha de acción', + 'Base date to calculate new due date: ' => 'Fecha base para calcular la nueva fecha de vencimiento: ', + 'This task has created this child task: ' => 'Esta tarea ha creado esta tarea hija: ', + 'Day(s)' => 'Día(s)', + 'Existing due date' => 'Fecha de vencimiento existente', + 'Factor to calculate new due date: ' => 'Factor para calcular la nueva fecha de vencimiento: ', + 'Month(s)' => 'Mes(es)', + 'This task has been created by: ' => 'Esta tarea ha sido creada por: ', + 'Recurrent task has been generated:' => 'Se ha generado una tarea recurrente:', + 'Timeframe to calculate new due date: ' => 'Plazo para calcular la nueva fecha de vencimiento: ', + 'Trigger to generate recurrent task: ' => 'Disparador para generar la tarea recurrente: ', + 'When task is closed' => 'Cuando la tarea está cerrada', + 'When task is moved from first column' => 'Cuando se mueve la tarea desde la primera columna', + 'When task is moved to last column' => 'Cuando la tarea se mueve a la última columna', + 'Year(s)' => 'Año(s)', + 'Project settings' => 'Configuración del proyecto', + 'Automatically update the start date' => 'Actualizar automáticamente la fecha de inicio', + 'iCal feed' => 'Alimentador de iCal', + 'Preferences' => 'Preferencias', + 'Security' => 'Seguridad', + 'Two factor authentication disabled' => 'Se ha inhabilitado la autenticación de dos factores.', + 'Two factor authentication enabled' => 'Autenticación de dos factores habilitada', + 'Unable to update this user.' => 'No se puede actualizar este usuario(a).', + 'There is no user management for personal projects.' => 'No hay gestión de usuario(a)s para proyectos privados.', + 'User that will receive the email' => 'Usuario(a) que recibirá el correo electrónico', + 'Email subject' => 'Asunto del correo electrónico', + 'Date' => 'Fecha', + 'Add a comment log when moving the task between columns' => 'Añadir un registro en los comentarios al mover la tarea entre columnas', + 'Move the task to another column when the category is changed' => 'Mover la tarea a otra columna cuando se cambia la categoría', + 'Send a task by email to someone' => 'Enviar una tarea por correo electrónico a alguien', + 'Reopen a task' => 'Reabrir una tarea', + 'Notification' => 'Notificación', + '%s moved the task #%d to the first swimlane' => '%s movió la tarea #%d al primer carril', + 'Swimlane' => 'Carril', + '%s moved the task %s to the first swimlane' => '%s movió la tarea %s al primer carril', + '%s moved the task %s to the swimlane "%s"' => '%s trasladó la tarea %s al carril "%s"', + 'This report contains all subtasks information for the given date range.' => 'Este informe contiene toda la información de las subtareas para el intervalo de fechas especificado.', + 'This report contains all tasks information for the given date range.' => 'Este informe contiene la información de todas las tareas para el intervalo de fechas dado.', + 'Project activities for %s' => 'Actividades del proyecto para %s', + 'view the board on Kanboard' => 'ver el tablero en Kanboard', + 'The task has been moved to the first swimlane' => 'La tarea se ha movido al primer carril', + 'The task has been moved to another swimlane:' => 'La tarea se ha trasladado a otro carril:', + 'New title: %s' => 'Nuevo título: %s', + 'The task is not assigned anymore' => 'La tarea ya no está asignada', + 'New assignee: %s' => 'Nuevo asignado: %s', + 'There is no category now' => 'No hay categoría ahora', + 'New category: %s' => 'Nueva categoría: %s', + 'New color: %s' => 'Nuevo color: %s', + 'New complexity: %d' => 'Nueva complejidad: %d', + 'The due date has been removed' => 'Se ha eliminado la fecha de vencimiento', + 'There is no description anymore' => 'Ya no hay ninguna descripción', + 'Recurrence settings has been modified' => 'Los parámetros de recurrencia se han modificado', + 'Time spent changed: %sh' => 'Tiempo ejecutado cambiado: %sh', + 'Time estimated changed: %sh' => 'Horas Presupuestadas cambiado: %sh', + 'The field "%s" has been updated' => 'Se ha actualizado el campo "%s"', + 'The description has been modified:' => 'La descripción ha sido modificada:', + 'Do you really want to close the task "%s" as well as all subtasks?' => '¿Realmente desea cerrar la tarea "%s", así como todas sus subtareas?', + 'I want to receive notifications for:' => 'Quiero recibir notificaciones para:', + 'All tasks' => 'Todas las tareas', + 'Only for tasks assigned to me' => 'Sólo para tareas asignadas a mí', + 'Only for tasks created by me' => 'Sólo para tareas creadas por mí', + 'Only for tasks created by me and tasks assigned to me' => 'Sólo para las tareas creadas por mí y asignadas a mí', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Total para todas las columnas', + 'You need at least 2 days of data to show the chart.' => 'Necesita al menos 2 días de datos para mostrar el gráfico.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Detener temporizador', + 'Start timer' => 'Iniciar el temporizador', + 'My activity stream' => 'Mi flujo de actividad', + 'Search tasks' => 'Buscar tareas', + 'Reset filters' => 'Restablecer filtros', + 'My tasks due tomorrow' => 'Mis tareas para mañana', + 'Tasks due today' => 'Tareas pendientes hoy', + 'Tasks due tomorrow' => 'Tareas para mañana', + 'Tasks due yesterday' => 'Tareas vencidas ayer', + 'Closed tasks' => 'Tareas cerradas', + 'Open tasks' => 'Tareas abiertas', + 'Not assigned' => 'No asignado', + 'View advanced search syntax' => 'Ver sintaxis de búsqueda avanzada', + 'Overview' => 'Visión general', + 'Board/Calendar/List view' => 'Vista de Tablero/Calendario/Lista', + 'Switch to the board view' => 'Cambiar a la vista de tablero', + 'Switch to the list view' => 'Cambiar a la vista de lista', + 'Go to the search/filter box' => 'Ir a la caja de búsqueda/filtro', + 'There is no activity yet.' => 'No hay actividad todavía.', + 'No tasks found.' => 'No se han encontrado tareas.', + 'Keyboard shortcut: "%s"' => 'Atajo de teclado: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtrar', + 'Advanced search' => 'Búsqueda Avanzada', + 'Example of query: ' => 'Ejemplo de consulta: ', + 'Search by project: ' => 'Buscar por proyecto: ', + 'Search by column: ' => 'Buscar por columna: ', + 'Search by assignee: ' => 'Buscar por asignado:', + 'Search by color: ' => 'Buscar por color: ', + 'Search by category: ' => 'Buscar por categoría: ', + 'Search by description: ' => 'Buscar por descripción: ', + 'Search by due date: ' => 'Buscar por fecha de vencimiento: ', + 'Average time spent in each column' => 'Tiempo de permanencia promedio en cada columna', + 'Average time spent' => 'Tiempo ejecutado promedio', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Este gráfico muestra el tiempo promedio invertido en cada columna para las últimas %d tareas.', + 'Average Lead and Cycle time' => 'Tiempo de Espera y de Ciclo promedio', + 'Average lead time: ' => 'Tiempo de espera promedio: ', + 'Average cycle time: ' => 'Tiempo del ciclo promedio: ', + 'Cycle Time' => 'Tiempo del Ciclo', + 'Lead Time' => 'Tiempo de Espera', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico muestra el tiempo promedio de espera y de ciclo para las últimas %d tareas.', + 'Average time into each column' => 'Tiempo promedio en cada columna', + 'Lead and cycle time' => 'Tiempo de espera y de ciclo', + 'Lead time: ' => 'Tiempo de Espera: ', + 'Cycle time: ' => 'Tiempo del Ciclo: ', + 'Time spent in each column' => 'Tiempo empleado en cada columna', + 'The lead time is the duration between the task creation and the completion.' => 'El tiempo de espera es la duración entre la creación de la tarea y la finalización.', + 'The cycle time is the duration between the start date and the completion.' => 'El tiempo de ciclo es la duración entre la fecha de inicio y la finalización.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tarea no está cerrada, se utiliza la hora actual en lugar de la fecha de finalización.', + 'Set the start date automatically' => 'Establecer automáticamente la fecha de inicio', + 'Edit Authentication' => 'Editar autenticación', + 'Remote user' => 'Usuario(a) remoto', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Los usuario(a)s remotos no almacenan su contraseña en la base de datos Kanboard, ejemplos: cuentas LDAP, Google y Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Si marca la casilla "Deshabilitar el formulario de inicio de sesión", las credenciales ingresadas en el formulario de inicio de sesión serán ignoradas.', + 'Default task color' => 'Color predeterminado de la tarea', + 'This feature does not work with all browsers.' => 'Esta función no funciona con todos los navegadores.', + 'There is no destination project available.' => 'No hay proyecto de destino disponible.', + 'Trigger automatically subtask time tracking' => 'Activar automáticamente el seguimiento de tiempo de la subtarea', + 'Include closed tasks in the cumulative flow diagram' => 'Incluir tareas cerradas en el diagrama de flujo acumulativo', + 'Current swimlane: %s' => 'Carril actual: %s', + 'Current column: %s' => 'Columna actual: %s', + 'Current category: %s' => 'Categoría actual: %s', + 'no category' => 'sin categoria', + 'Current assignee: %s' => 'Asignado actual: %s', + 'not assigned' => 'no asignado', + 'Author:' => 'Autor:', + 'contributors' => 'colaboradores', + 'License:' => 'Licencia:', + 'License' => 'Licencia', + 'Enter the text below' => 'Introduzca el texto a continuación', + 'Start date:' => 'Fecha de inicio:', + 'Due date:' => 'Fecha de vencimiento:', + 'People who are project managers' => 'Personas que son gerentes de proyecto', + 'People who are project members' => 'Personas que son miembros del proyecto', + 'NOK - Norwegian Krone' => 'NOK - Corona Noruega', + 'Show this column' => 'Mostrar esta columna', + 'Hide this column' => 'Ocultar esta columna', + 'End date' => 'Fecha final', + 'Users overview' => 'Visión general de los usuario(a)s', + 'Members' => 'Miembros', + 'Shared project' => 'Proyecto compartido', + 'Project managers' => 'Gerentes de Proyecto', + 'Projects list' => 'Lista de proyectos', + 'End date:' => 'Fecha final:', + 'Change task color when using a specific task link' => 'Cambiar el color de la tarea cuando se utiliza un enlace de tarea específico', + 'Task link creation or modification' => 'Creación o modificación de enlaces de tareas', + 'Milestone' => 'Hito', + 'Reset the search/filter box' => 'Restablecer la caja de búsqueda/filtro', + 'Documentation' => 'Documentación', + 'Author' => 'Autor', + 'Version' => 'Versión', + 'Plugins' => 'Complementos', + 'There is no plugin loaded.' => 'No hay ningún complemento cargado.', + 'My notifications' => 'Mis Notificaciones', + 'Custom filters' => 'Filtros personalizados', + 'Your custom filter has been created successfully.' => 'Su filtro personalizado se ha creado correctamente.', + 'Unable to create your custom filter.' => 'No se puede crear el filtro personalizado.', + 'Custom filter removed successfully.' => 'Filtro personalizado eliminado correctamente.', + 'Unable to remove this custom filter.' => 'No se puede quitar este filtro personalizado.', + 'Edit custom filter' => 'Editar filtro personalizado', + 'Your custom filter has been updated successfully.' => 'Su filtro personalizado se ha actualizado correctamente.', + 'Unable to update custom filter.' => 'No se puede actualizar el filtro personalizado.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nuevo archivo adjunto en la tarea #%d: %s', + 'New comment on task #%d' => 'Nuevo comentario sobre la tarea #%d', + 'Comment updated on task #%d' => 'Comentario actualizado en la tarea #%d', + 'New subtask on task #%d' => 'Nueva subtarea en la tarea #%d', + 'Subtask updated on task #%d' => 'Subtarea actualizada en la tarea #%d', + 'New task #%d: %s' => 'Nueva tarea #%d: %s', + 'Task updated #%d' => 'Tarea actualizada #%d', + 'Task #%d closed' => 'Tarea #%d cerrada', + 'Task #%d opened' => 'Tarea #%d abierta', + 'Column changed for task #%d' => 'Columna modificada para la tarea #%d', + 'New position for task #%d' => 'Nueva posición para la tarea #%d', + 'Swimlane changed for task #%d' => 'Carril cambiado para la tarea #%d', + 'Assignee changed on task #%d' => 'Asignado cambiado en la tarea #%d', + '%d overdue tasks' => '%d tareas pendientes', + 'No notification.' => 'Sin notificación.', + 'Mark all as read' => 'marcar todo como leido', + 'Mark as read' => 'Marcar como leído', + 'Total number of tasks in this column across all swimlanes' => 'Número total de tareas en esta columna contando todos los carriles', + 'Collapse swimlane' => 'Colapsar el carril', + 'Expand swimlane' => 'Expandir el carril', + 'Add a new filter' => 'Añadir un nuevo filtro', + 'Share with all project members' => 'Compartir con todos los miembros del proyecto', + 'Shared' => 'Compartido', + 'Owner' => 'Propietario', + 'Unread notifications' => 'Notificaciones no leídas', + 'Notification methods:' => 'Métodos de notificación:', + 'Unable to read your file' => 'No se puede leer el archivo', + '%d task(s) have been imported successfully.' => '%d tarea(s) se han importado correctamente.', + 'Nothing has been imported!' => '¡Ningún dato importado!', + 'Import users from CSV file' => 'Importar usuario(a)s de un archivo CSV (valores separados por comas)', + '%d user(s) have been imported successfully.' => '%d usuario(s) se han importado correctamente.', + 'Comma' => 'Coma', + 'Semi-colon' => 'Punto y coma', + 'Tab' => 'Tabulación', + 'Vertical bar' => 'Barra vertical', + 'Double Quote' => 'Comillas dobles', + 'Single Quote' => 'Comillas simples', + '%s attached a file to the task #%d' => '%s adjuntó un archivo a la tarea #%d', + 'There is no column or swimlane activated in your project!' => '¡No hay columna ni carril activado en tu proyecto!', + 'Append filter (instead of replacement)' => 'Agregar el filtro (en lugar del reemplazo)', + 'Append/Replace' => 'Añadir/Reemplazar', + 'Append' => 'Adjuntar', + 'Replace' => 'Reemplazar', + 'Import' => 'Importar', + 'Change sorting' => 'Cambio de orden', + 'Tasks Importation' => 'Importación de Tareas', + 'Delimiter' => 'Delimitador', + 'Enclosure' => 'Contenedor', + 'CSV File' => 'Archivo CSV', + 'Instructions' => 'Instrucciones', + 'Your file must use the predefined CSV format' => 'Su archivo debe utilizar el formato CSV predefinido', + 'Your file must be encoded in UTF-8' => 'Su archivo debe estar codificado en UTF-8', + 'The first row must be the header' => 'La primera fila debe ser el encabezado', + 'Duplicates are not verified for you' => 'Los duplicados no se verifican', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La fecha de vencimiento debe utilizar el formato ISO: AAAA-MM-DD', + 'Download CSV template' => 'Descargar plantilla CSV', + 'No external integration registered.' => 'No se ha registrado ninguna integración externa.', + 'Duplicates are not imported' => 'Los duplicados no se importan', + 'Usernames must be lowercase and unique' => 'Los nombres de usuario(a) deben estar en minúsculas y ser únicos', + 'Passwords will be encrypted if present' => 'Las contraseñas se cifrarán si están presentes', + '%s attached a new file to the task %s' => '%s adjunto un nuevo archivo a la tarea %s', + 'Link type' => 'Tipo de enlace', + 'Assign automatically a category based on a link' => 'Asignar automáticamente una categoría basada en un enlace', + 'BAM - Konvertible Mark' => 'BAM - Marco Convertible', + 'Assignee Username' => 'Usuario(a) asignado', + 'Assignee Name' => 'Nombre del asignado', + 'Groups' => 'Grupos', + 'Members of %s' => 'Miembros de %s', + 'New group' => 'Nuevo grupo', + 'Group created successfully.' => 'Grupo creado correctamente.', + 'Unable to create your group.' => 'No se puede crear su grupo.', + 'Edit group' => 'Editar grupo', + 'Group updated successfully.' => 'Grupo actualizado correctamente.', + 'Unable to update your group.' => 'No se puede actualizar su grupo.', + 'Add group member to "%s"' => 'Agregue el miembro del grupo a "%s"', + 'Group member added successfully.' => 'Miembro del grupo agregado correctamente.', + 'Unable to add group member.' => 'No se puede agregar el miembro del grupo.', + 'Remove user from group "%s"' => 'Quitar usuario del grupo "%s"', + 'User removed successfully from this group.' => 'Usuario(a) eliminadov correctamente de este grupo.', + 'Unable to remove this user from the group.' => 'No se puede eliminar este usuario(a) del grupo.', + 'Remove group' => 'Eliminar grupo', + 'Group removed successfully.' => 'Grupo eliminado correctamente.', + 'Unable to remove this group.' => 'No se pudo eliminar este grupo.', + 'Project Permissions' => 'Permisos del proyecto', + 'Manager' => 'Gerente', + 'Project Manager' => 'Gerente de proyecto', + 'Project Member' => 'Miembro del proyecto', + 'Project Viewer' => 'Visor de proyectos', + 'Your account is locked for %d minutes' => 'Tu cuenta está bloqueada durante %d minutos', + 'Invalid captcha' => 'CAPTCHA inválido', + 'The name must be unique' => 'El nombre debe ser único', + 'View all groups' => 'Ver todos los grupos', + 'There is no user available.' => 'No hay usuario(a)s disponibles.', + 'Do you really want to remove the user "%s" from the group "%s"?' => '¿Realmente desea eliminar el(la) usuario(a) "%s" del grupo "%s"?', + 'There is no group.' => 'No hay grupo.', + 'Add group member' => 'Agregar miembro del grupo', + 'Do you really want to remove this group: "%s"?' => '¿Desea realmente eliminar este grupo: "%s"?', + 'There is no user in this group.' => 'No hay ningún usuario(a) en este grupo.', + 'Permissions' => 'Permisos', + 'Allowed Users' => 'Usuario(a)s permitidos', + 'No specific user has been allowed.' => 'Ningún usuario(a) ha sido autorizado específicamente.', + 'Role' => 'Rol', + 'Enter user name...' => 'Introduzca su nombre de usuario(a)...', + 'Allowed Groups' => 'Grupos permitidos', + 'No group has been allowed.' => 'Ningún grupo se ha permitido específicamente.', + 'Group' => 'Grupo', + 'Group Name' => 'Nombre del grupo', + 'Enter group name...' => 'Introduzca el nombre del grupo ...', + 'Role:' => 'Rol:', + 'Project members' => 'Miembros del proyecto', + '%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 ha mencionado en un comentario sobre la tarea #%d', + 'You were mentioned in the task #%d' => 'Te mencionaron en la tarea #%d', + 'You were mentioned in a comment on the task #%d' => 'Usted fue mencionado en un comentario en la tarea #%d', + 'Estimated hours: ' => 'Horas estimadas: ', + 'Actual hours: ' => 'Horas reales: ', + 'Hours Spent' => 'Horas Gastadas', + 'Hours Estimated' => 'Horas Estimadas', + 'Estimated Time' => 'Tiempo Estimado', + 'Actual Time' => 'Tiempo Real', + 'Estimated vs actual time' => 'Horas Presupuestadas vs. tiempo real', + 'RUB - Russian Ruble' => 'RUB - Rublo Ruso', + 'Assign the task to the person who does the action when the column is changed' => 'Asigne la tarea a la persona que realiza la acción cuando se cambia la columna', + 'Close a task in a specific column' => 'Cerrar una tarea en una columna específica', + 'Time-based One-time Password Algorithm' => 'Algoritmo de contraseña de una sola vez basado en el tiempo', + 'Two-Factor Provider: ' => 'Proveedor de dos factores: ', + 'Disable two-factor authentication' => 'Deshabilitar la autenticación de dos factores', + 'Enable two-factor authentication' => 'Habilitar la autenticación de dos factores', + 'There is no integration registered at the moment.' => 'No hay ninguna integración registrada en este momento.', + 'Password Reset for Kanboard' => 'Restablecer contraseña para Kanboard', + 'Forgot password?' => '¿Olvidó su contraseña?', + 'Enable "Forget Password"' => 'Habilitar "Olvidar contraseña"', + 'Password Reset' => 'Restablecimiento de contraseña', + 'New password' => 'Nueva contraseña', + 'Change Password' => 'Cambiar la contraseña', + 'To reset your password click on this link:' => 'Para restablecer su contraseña, haga clic en este enlace:', + 'Last Password Reset' => 'Restablecer la última contraseña', + 'The password has never been reinitialized.' => 'La contraseña nunca ha sido reinicializada.', + 'Creation' => 'Creación', + 'Expiration' => 'Vencimiento', + 'Password reset history' => 'Historial de restablecimiento de contraseñas', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas las tareas de la columna "%s" y el carril "%s" han sido cerradas con éxito.', + 'Do you really want to close all tasks of this column?' => '¿Realmente desea cerrar todas las tareas de esta columna?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tarea(s) en la columna "%s" y el carril "%s" se cerrará.', + 'Close all tasks in this column and this swimlane' => 'Cierre todas las tareas de esta columna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Ningún plugin ha registrado un método de notificación de proyecto. Todavía puede configurar notificaciones individuales en su perfil de usuario(a).', + 'My dashboard' => 'Mi tablero', + 'My profile' => 'Mi perfil', + '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, por ejemplo: MIPROYECTO.', + 'Project owner' => 'Propietario del proyecto', + 'Personal projects do not have users and groups management.' => 'Los proyectos privados no tienen gestión de usuario(a)s y grupos.', + 'There is no project member.' => 'No hay ningún miembro del proyecto.', + 'Priority' => 'Prioridad', + 'Task priority' => 'Prioridad de la tarea', + 'General' => 'General', + 'Dates' => 'Fechas', + 'Default priority' => 'Prioridad predeterminada', + 'Lowest priority' => 'Menor prioridad', + 'Highest priority' => 'Mayor prioridad', + 'Close a task when there is no activity' => 'Cierra una tarea cuando no hay actividad', + 'Duration in days' => 'Duración en días', + 'Send email when there is no activity on a task' => 'Enviar correo electrónico cuando no hay actividad en una tarea', + 'Unable to fetch link information.' => 'No es posible obtener información de enlace.', + 'Daily background job for tasks' => 'Trabajo en segundo plano diario para las tareas', + 'Auto' => 'Auto', + 'Related' => 'Relacionado', + 'Attachment' => 'Archivo adjunto', + 'Web Link' => 'Enlace web', + 'External links' => 'Enlaces externos', + 'Add external link' => 'Añadir enlace externo', + 'Type' => 'Tipo', + 'Dependency' => 'Dependencia', + 'Add internal link' => 'Añadir enlace interno', + 'Add a new external link' => 'Añadir un nuevo enlace externo', + 'Edit external link' => 'Editar enlace externo', + 'External link' => 'Enlace externo', + 'Copy and paste your link here...' => 'Copia y pega el enlace aquí ...', + 'URL' => 'URL', + 'Internal links' => 'Enlaces internos', + 'Assign to me' => 'Asignármelo', + 'Me' => 'Yo', + 'Do not duplicate anything' => 'No duplique nada', + 'Projects management' => 'Gestión de proyectos', + 'Users management' => 'Gestión de usuario(a)s', + 'Groups management' => 'Gestión de grupos', + 'Create from another project' => 'Crear desde otro proyecto', + 'open' => 'abierto', + 'closed' => 'cerrado', + 'Priority:' => 'Prioridad:', + 'Reference:' => 'Referencia:', + 'Complexity:' => 'Complejidad:', + 'Swimlane:' => 'Carril:', + 'Column:' => 'Columna:', + 'Position:' => 'Posición:', + 'Creator:' => 'Creador:', + 'Time estimated:' => 'Horas Presupuestadas:', + '%s hours' => '%s horas', + 'Time spent:' => 'Tiempo usado:', + 'Created:' => 'Creado:', + 'Modified:' => 'Modificado:', + 'Completed:' => 'Terminado:', + 'Started:' => 'Empezado:', + 'Moved:' => 'Movido:', + 'Task #%d' => 'Tarea número %d', + 'Time format' => 'Formato de tiempo', + 'Start date: ' => 'Fecha de inicio: ', + 'End date: ' => 'Fecha final: ', + 'New due date: ' => 'Nueva fecha de vencimiento: ', + 'Start date changed: ' => 'Fecha de inicio cambiada: ', + 'Disable personal projects' => 'Inhabilitar proyectos privados', + 'Do you really want to remove this custom filter: "%s"?' => '¿Desea realmente eliminar este filtro personalizado: "%s"?', + 'Remove a custom filter' => 'Eliminar un filtro personalizado', + 'User activated successfully.' => 'Usuario(a) activado correctamente.', + 'Unable to enable this user.' => 'No se puede habilitar este usuario(a).', + 'User disabled successfully.' => 'El usuario(a) ha sido desactivado correctamente.', + 'Unable to disable this user.' => 'No se puede deshabilitar este usuario(a).', + 'All files have been uploaded successfully.' => 'Todos los archivos se han cargado correctamente.', + 'The maximum allowed file size is %sB.' => 'El tamaño máximo de archivo permitido es %sB.', + 'Drag and drop your files here' => 'Arrastra y suelta tus archivos aquí', + 'choose files' => 'Elija el archivo', + 'View profile' => 'Ver perfil', + 'Two Factor' => 'Dos factores', + 'Disable user' => 'Deshabilitar usuario(a)', + 'Do you really want to disable this user: "%s"?' => '¿Desea realmente desactivar este usuario(a): "%s"?', + 'Enable user' => 'Habilitar usuario(a)', + 'Do you really want to enable this user: "%s"?' => '¿De verdad quieres habilitar a este usuario(a): "%s"?', + 'Download' => 'Descargar', + 'Uploaded: %s' => 'Subido: %s', + 'Size: %s' => 'Tamaño: %s', + 'Uploaded by %s' => 'Subido por %s', + 'Filename' => 'Nombre del archivo', + 'Size' => 'Tamaño', + 'Column created successfully.' => 'Columna creada correctamente.', + 'Another column with the same name exists in the project' => 'Otra columna con el mismo nombre existe en el proyecto', + 'Default filters' => 'Filtros predeterminados', + 'Your board doesn\'t have any columns!' => '¡Su tablero no tiene columnas!', + 'Change column position' => 'Cambiar la posición de la columna', + 'Switch to the project overview' => 'Cambiar a la vista general del proyecto', + 'User filters' => 'Filtros de usuario(a)', + 'Category filters' => 'Filtros de categorías', + 'Upload a file' => 'Cargar un archivo', + 'View file' => 'Ver archivo', + 'Last activity' => 'Última actividad', + 'Change subtask position' => 'Cambiar la posición de la subtarea', + 'This value must be greater than %d' => 'Este valor no debe de ser más grande que %d', + 'Another swimlane with the same name exists in the project' => 'En el proyecto existe otro carril con el mismo nombre', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Ejemplo: https://example.kanboard.org/ (utilizado para generar URLs absolutas)', + 'Actions duplicated successfully.' => 'Acciones duplicadas correctamente.', + 'Unable to duplicate actions.' => 'No se pueden duplicar acciones.', + 'Add a new action' => 'Añadir una acción nueva', + 'Import from another project' => 'Importar desde otro proyecto', + 'There is no action at the moment.' => 'No hay acción en este momento.', + 'Import actions from another project' => 'Importar acciones de otro proyecto', + 'There is no available project.' => 'No hay proyecto disponible.', + 'Local File' => 'Archivo local', + 'Configuration' => 'Configuración', + 'PHP version:' => 'Versión de PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versión del sistema operativo:', + 'Database version:' => 'Versión de base de datos:', + 'Browser:' => 'Navegador:', + 'Task view' => 'Vista de la tarea', + 'Edit task' => 'Editar tarea', + 'Edit description' => 'Editar Descripción', + 'New internal link' => 'Nuevo enlace interno', + 'Display list of keyboard shortcuts' => 'Mostrar lista de atajos de teclado', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Subir mi imagen de avatar', + 'Remove my image' => 'Eliminar mi imagen', + 'The OAuth2 state parameter is invalid' => 'El parámetro de estado OAuth2 no es válido', + 'User not found.' => 'Usuario(a) no encontrado(a).', + 'Search in activity stream' => 'Buscar en el flujo de actividades', + 'My activities' => 'Mis actividades', + 'Activity until yesterday' => 'Actividad hasta ayer', + 'Activity until today' => 'Actividad hasta hoy', + 'Search by creator: ' => 'Búsqueda por creador: ', + 'Search by creation date: ' => 'Búsqueda por fecha de creación: ', + 'Search by task status: ' => 'Búsqueda por estado de la tarea: ', + 'Search by task title: ' => 'Buscar por título de tarea: ', + 'Activity stream search' => 'Búsqueda de flujo de actividad', + 'Projects where "%s" is manager' => 'Proyectos donde "%s" es gerente', + 'Projects where "%s" is member' => 'Proyectos donde "%s" es miembro', + 'Open tasks assigned to "%s"' => 'Tareas abiertas asignadas a "%s"', + 'Closed tasks assigned to "%s"' => 'Tareas cerradas asignadas a "%s"', + 'Assign automatically a color based on a priority' => 'Asignar automáticamente un color basado en una prioridad', + 'Overdue tasks for the project(s) "%s"' => 'Tareas vencidas para el (los) proyecto(s) "%s"', + 'Upload files' => 'Subir archivos', + 'Installed Plugins' => 'Plugins instalados', + 'Plugin Directory' => 'Directorio de Plugins', + 'Plugin installed successfully.' => 'Plugin instalado correctamente.', + 'Plugin updated successfully.' => 'Plugin actualizado correctamente.', + 'Plugin removed successfully.' => 'Plugin eliminado correctamente.', + 'Subtask converted to task successfully.' => 'Subtarea convertida a la tarea con éxito.', + 'Unable to convert the subtask.' => 'No se puede convertir la subtarea.', + 'Unable to extract plugin archive.' => 'No se puede extraer archivo plugin.', + 'Plugin not found.' => 'No se ha encontrado el plugin.', + 'You don\'t have the permission to remove this plugin.' => 'No tienes permiso para eliminar este plugin.', + 'Unable to download plugin archive.' => 'No es posible descargar archivos plugin.', + 'Unable to write temporary file for plugin.' => 'No se puede escribir el archivo temporal para el plugin.', + 'Unable to open plugin archive.' => 'No se puede abrir el archivo de plugin', + 'There is no file in the plugin archive.' => 'No hay ningún archivo en el archivo de plugins.', + 'Create tasks in bulk' => 'Crear tareas en lote', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Tu instancia de Kanboard no está configurada para instalar complementos desde la interfaz de usuario(a).', + 'There is no plugin available.' => 'No hay un plugin disponible.', + 'Install' => 'Instalar', + 'Update' => 'Actualizar', + 'Up to date' => 'Hasta la fecha', + 'Not available' => 'No disponible', + 'Remove plugin' => 'Eliminar plugin', + 'Do you really want to remove this plugin: "%s"?' => '¿Realmente desea eliminar este plugin: "%s"?', + 'Uninstall' => 'Desinstalar', + 'Listing' => 'Listado', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Gestionar proyectos', + 'Convert to task' => 'Convertir en tarea', + 'Convert sub-task to task' => 'Convertir subtarea en tarea', + 'Do you really want to convert this sub-task to a task?' => '¿Realmente desea convertir esta subtarea a una tarea?', + 'My task title' => 'Título de mi tarea', + 'Enter one task by line.' => 'Ingrese una tarea por línea.', + 'Number of failed login:' => 'Número de inicio de sesión fallido:', + 'Account locked until:' => 'Cuenta bloqueada hasta:', + 'Email settings' => 'Ajustes de correo electrónico', + 'Email sender address' => 'Dirección del remitente del correo electrónico', + 'Email transport' => 'Transporte por correo electrónico', + 'Webhook token' => 'Token Webhook', + 'Project tags management' => 'Gestión de etiquetas de proyecto', + 'Tag created successfully.' => 'Etiqueta creada correctamente.', + 'Unable to create this tag.' => 'No se puede crear esta etiqueta.', + 'Tag updated successfully.' => 'Etiqueta actualizada correctamente.', + 'Unable to update this tag.' => 'No se puede actualizar esta etiqueta.', + 'Tag removed successfully.' => 'Etiqueta eliminada correctamente.', + 'Unable to remove this tag.' => 'No se ha podido eliminar esta etiqueta.', + 'Global tags management' => 'Gestión de etiquetas globales', + 'Tags' => 'Etiquetas', + 'Tags management' => 'Gestión de etiquetas', + 'Add new tag' => 'Añadir nueva etiqueta', + 'Edit a tag' => 'Modificar una etiqueta', + 'Project tags' => 'Etiquetas del proyecto', + 'There is no specific tag for this project at the moment.' => 'No hay una etiqueta específica para este proyecto en este momento.', + 'Tag' => 'Etiqueta', + 'Remove a tag' => 'Eliminar una etiqueta', + 'Do you really want to remove this tag: "%s"?' => '¿Desea realmente eliminar esta etiqueta: "%s"?', + 'Global tags' => 'Etiquetas globales', + 'There is no global tag at the moment.' => 'No hay una etiqueta global en este momento.', + 'This field cannot be empty' => 'Este campo no puede estar vacío', + 'Close a task when there is no activity in a specific column' => 'Cierra una tarea cuando no hay actividad en una columna específica', + '%s removed a subtask for the task #%d' => '%s eliminó una subtarea para la tarea %d', + '%s removed a comment on the task #%d' => '%s eliminó un comentario para la tarea %d', + 'Comment removed on task #%d' => 'Comentario eliminado en la tarea #%d', + 'Subtask removed on task #%d' => 'Subtarea eliminada en la tarea #%d', + 'Hide tasks in this column in the dashboard' => 'Ocultar tareas en esta columna en el tablero', + '%s removed a comment on the task %s' => '%s eliminó un comentario para la tarea %s', + '%s removed a subtask for the task %s' => '%s eliminó una subtarea para la tarea %s', + 'Comment removed' => 'Comentario eliminado', + 'Subtask removed' => 'Sub-tarea eliminada', + '%s set a new internal link for the task #%d' => '%s estableció un nuevo enlace interno para la tarea %d', + '%s removed an internal link for the task #%d' => '%s eliminó un nuevo enlace interno para la tarea %d', + 'A new internal link for the task #%d has been defined' => 'Se ha definido un nuevo enlace interno para la tarea #%d', + 'Internal link removed for the task #%d' => 'Enlace interno eliminado para la tarea #%d', + '%s set a new internal link for the task %s' => '%s estableció un nuevo enlace interno para la tarea %s', + '%s removed an internal link for the task %s' => '%s eliminó un enlace interno para la tarea %s', + 'Automatically set the due date on task creation' => 'Establecer automáticamente la fecha de vencimiento en la creación de tareas', + 'Move the task to another column when closed' => 'Mover la tarea a otra columna cuando esté cerrada', + 'Move the task to another column when not moved during a given period' => 'Mover la tarea a otra columna cuando no se mueve durante un período determinado', + 'Dashboard for %s' => 'Tablero para %s', + 'Tasks overview for %s' => 'Visión general de tareas para %s', + 'Subtasks overview for %s' => 'Visión general de subtareas para %s', + 'Projects overview for %s' => 'Visión general de proyectos para %s', + 'Activity stream for %s' => 'Flujo de actividad para %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Asignar un color cuando la tarea se mueve a un carril específico', + 'Assign a priority when the task is moved to a specific swimlane' => 'Asignar una prioridad cuando la tarea se mueve a un carril específico', + 'User unlocked successfully.' => 'Usuario(a) desbloqueado(a) correctamente.', + 'Unable to unlock the user.' => 'No se puede desbloquear el usuario(a).', + 'Move a task to another swimlane' => 'Mueve una tarea a otro carril', + 'Creator Name' => 'Nombre del Creador', + 'Time spent and estimated' => 'Tiempo empleado y estimado', + 'Move position' => 'Mover la posición', + 'Move task to another position on the board' => 'Mover la tarea a otra posición en el tablero', + 'Insert before this task' => 'Insertar antes de esta tarea', + 'Insert after this task' => 'Insertar después de esta tarea', + 'Unlock this user' => 'Desbloquear este usuario(a)', + 'Custom Project Roles' => 'Roles de proyecto personalizados', + 'Add a new custom role' => 'Agregar un nuevo rol personalizado', + 'Restrictions for the role "%s"' => 'Restricciones para el rol "%s"', + 'Add a new project restriction' => 'Añadir una nueva restricción de proyecto', + 'Add a new drag and drop restriction' => 'Añadir una nueva restricción de arrastrar y soltar', + 'Add a new column restriction' => 'Agregar una nueva restricción de columna', + 'Edit this role' => 'Editar este rol', + 'Remove this role' => 'Eliminar este rol', + 'There is no restriction for this role.' => 'No hay restricción para este rol.', + 'Only moving task between those columns is permitted' => 'Sólo se permite el movimiento de una tarea entre esas columnas', + 'Close a task in a specific column when not moved during a given period' => 'Cierra una tarea en una columna específica cuando no se mueve durante un período determinado', + 'Edit columns' => 'Editar columnas', + 'The column restriction has been created successfully.' => 'La restricción de columna se ha creado correctamente.', + 'Unable to create this column restriction.' => 'No se puede crear esta restricción de columna.', + 'Column restriction removed successfully.' => 'Se ha eliminado correctamente la restricción de columna.', + 'Unable to remove this restriction.' => 'No se puede eliminar esta restricción.', + 'Your custom project role has been created successfully.' => 'El rol de proyecto personalizado se ha creado correctamente.', + 'Unable to create custom project role.' => 'No se puede crear un rol de proyecto personalizado.', + 'Your custom project role has been updated successfully.' => 'Su rol de proyecto personalizado se ha actualizado correctamente.', + 'Unable to update custom project role.' => 'No se puede actualizar el rol de proyecto personalizado.', + 'Custom project role removed successfully.' => 'El rol de proyecto personalizado se eliminó correctamente.', + 'Unable to remove this project role.' => 'No se pudo eliminar esta función del proyecto.', + 'The project restriction has been created successfully.' => 'La restricción del proyecto se ha creado con éxito.', + 'Unable to create this project restriction.' => 'No se puede crear esta restricción de proyecto.', + 'Project restriction removed successfully.' => 'Se ha eliminado correctamente la restricción de proyecto.', + 'You cannot create tasks in this column.' => 'No puede crear tareas en esta columna.', + 'Task creation is permitted for this column' => 'La creación de tareas está permitida para esta columna', + 'Closing or opening a task is permitted for this column' => 'El cierre o apertura de una tarea está permitido para esta columna', + 'Task creation is blocked for this column' => 'La creación de tareas está bloqueada para esta columna', + 'Closing or opening a task is blocked for this column' => 'El cierre o apertura de una tarea está bloqueado para esta columna', + 'Task creation is not permitted' => 'No se permite la creación de tareas', + 'Closing or opening a task is not permitted' => 'No se permite cerrar ni abrir una tarea', + 'New drag and drop restriction for the role "%s"' => 'Nueva restricción de arrastrar y soltar para el rol "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Las personas pertenecientes a este rol sólo podrán mover las tareas entre la columna de origen y la de destino.', + 'Remove a column restriction' => 'Eliminar una restricción de columna', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => '¿Realmente desea eliminar esta restricción de columna: "%s" a "%s"?', + 'New column restriction for the role "%s"' => 'Nueva restricción de columna para el rol "%s"', + 'Rule' => 'Regla', + 'Do you really want to remove this column restriction?' => '¿Realmente desea eliminar esta restricción de columna?', + 'Custom roles' => 'Roles personalizados', + 'New custom project role' => 'Nuevo rol de proyecto personalizado', + 'Edit custom project role' => 'Editar un rol de proyecto personalizado', + 'Remove a custom role' => 'Eliminar un rol personalizado', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => '¿Realmente desea eliminar este rol personalizado: "%s"? Todas las personas asignadas a este rol se convertirán en miembros del proyecto.', + 'There is no custom role for this project.' => 'No hay ningun rol personalizado para este proyecto.', + 'New project restriction for the role "%s"' => 'Nueva restricción de proyecto para el rol "%s"', + 'Restriction' => 'Restricción', + 'Remove a project restriction' => 'Eliminar una restricción de proyecto', + 'Do you really want to remove this project restriction: "%s"?' => '¿Realmente desea eliminar esta restricción de proyecto: "%s"?', + 'Duplicate to multiple projects' => 'Duplicar en varios proyectos', + 'This field is required' => 'Este campo es requerido', + 'Moving a task is not permitted' => 'No se permite mover una tarea', + 'This value must be in the range %d to %d' => 'Este valor debe estar en el rango %d a %d', + 'You are not allowed to move this task.' => 'No se le permite mover esta tarea.', + 'API User Access' => 'Acceso de usuario de API', + 'Preview' => 'Previsualizar', + 'Write' => 'Escribir', + 'Write your text in Markdown' => 'Redacta el texto en Markdown', + 'No personal API access token registered.' => 'No se ha registrado ningún token de acceso a API personal.', + 'Your personal API access token is "%s"' => 'Su token de acceso a la API personal es "%s"', + 'Remove your token' => 'Elimine su token', + 'Generate a new token' => 'Generar un nuevo token', + 'Showing %d-%d of %d' => 'Mostrando %d-%d de %d', + 'Outgoing Emails' => 'Correo electrónico saliente', + 'Add or change currency rate' => 'Añadir o cambiar el tipo de cambio', + 'Reference currency: %s' => 'Divisa de referencia: %s', + 'Add custom filters' => 'Añadir filtros personalizados', + 'Export' => 'Exportar', + 'Add link label' => 'Añadir etiqueta de enlace', + 'Incompatible Plugins' => 'Plugins incompatibles', + 'Compatibility' => 'Compatibilidad', + 'Permissions and ownership' => 'Permisos y propiedad', + 'Priorities' => 'Prioridades', + 'Close this window' => 'Cierra esta ventana', + 'Unable to upload this file.' => 'No se puede subir este archivo.', + 'Import tasks' => 'Importar tareas', + 'Choose a project' => 'Elija un proyecto', + 'Profile' => 'Perfil', + 'Application role' => 'Rol de la aplicación', + '%d invitations were sent.' => '%d invitaciones enviadas.', + '%d invitation was sent.' => '%d invitación fue enviada.', + 'Unable to create this user.' => 'No se puede crear este(a) usuario(a).', + 'Kanboard Invitation' => 'Invitación de Kanboard', + 'Visible on dashboard' => 'Visible en el tablero', + 'Created at:' => 'Creado en:', + 'Updated at:' => 'Actualizado en:', + 'There is no custom filter.' => 'No hay un filtro personalizado.', + 'New User' => 'Nuevo(a) usuario(a)', + 'Authentication' => 'Autenticación', + 'If checked, this user will use a third-party system for authentication.' => 'Si está marcado, este(a) usuario(a) utilizará un sistema de terceros para la autenticación.', + 'The password is necessary only for local users.' => 'La contraseña sólo es necesaria para lo(a)s usuario(a)s locales.', + 'You have been invited to register on Kanboard.' => 'Te han invitado a registrarte en Kanboard.', + 'Click here to join your team' => 'Haz clic aquí para unirte a tu equipo', + 'Invite people' => 'Invitar personas', + 'Emails' => ' correos electrónicos', + 'Enter one email address by line.' => 'Ingrese una dirección de correo electrónico por línea.', + 'Add these people to this project' => 'Añadir a estas personas a este proyecto', + 'Add this person to this project' => 'Añadir a esta persona a este proyecto', + 'Sign-up' => 'Regístrate', + 'Credentials' => 'Credenciales', + 'New user' => 'Añadir un(a) usuario(a)', + 'This username is already taken' => 'Este nombre de usuario(a) ya está en uso', + 'Your profile must have a valid email address.' => 'Su perfil debe tener una dirección de correo electrónico válido.', + 'TRL - Turkish Lira' => 'TRL - Lira Turca', + 'The project email is optional and could be used by several plugins.' => 'El correo electrónico del proyecto es opcional y podría ser usado por varios plugins', + 'The project email must be unique across all projects' => 'El correo electrónico del proyecto debe ser único entre todos los proyectos', + 'The email configuration has been disabled by the administrator.' => 'La configuración de correo electrónico se ha deshabilitado por el administrador', + 'Close this project' => 'Cerrar este proyecto', + 'Open this project' => 'Abrir este proyecto', + 'Close a project' => 'Cerrar un proyecto', + 'Do you really want to close this project: "%s"?' => '¿Realmente quiere cerrar este proyecto: "%s"?', + 'Reopen a project' => 'Reabrir un proyecto', + 'Do you really want to reopen this project: "%s"?' => '¿Realmente quiere reabrir este proyecto: "%s"?', + 'This project is open' => 'Este proyecto está abierdo', + 'This project is closed' => 'Este proyecto está cerrado', + 'Unable to upload files, check the permissions of your data folder.' => 'No se pueden cargar archivos, verifique los permisos de su carpeta de datos (data)', + 'Another category with the same name exists in this project' => 'Ya existe otra categoría con el mismo nombre en este proyecto', + 'Comment sent by email successfully.' => 'Comentario enviado exitosamente por correo electrónico', + 'Sent by email to "%s" (%s)' => 'Enviado por correo electrónico a "%s" (%s)', + 'Unable to read uploaded file.' => 'No se pudo leer el archivo cargado', + 'Database uploaded successfully.' => 'Base de datos cargada exitosamente', + 'Task sent by email successfully.' => 'Tarea enviada exitosamente por correo electrónico', + 'There is no category in this project.' => 'No hay categorías en este proyecto', + 'Send by email' => 'Enviar por correo electrónico', + 'Create and send a comment by email' => 'Crear y enviar un comentario por correo electrónico', + 'Subject' => 'Asunto', + 'Upload the database' => 'Cargar la base de datos', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Podría cargar la base de datos Sqlite descargada previamente (formato Gzip)', + 'Database file' => 'Archivo de Base de Datos', + 'Upload' => 'Cargar', + 'Your project must have at least one active swimlane.' => 'Su proyecto debe tener al menos un carril activo', + 'Project: %s' => 'Proyecto: %s', + 'Automatic action not found: "%s"' => 'No se encontró la acción automática: "%s"', + '%d projects' => '%d proyectos', + '%d project' => '%d proyecto', + 'There is no project.' => 'No hay proyecto', + 'Sort' => 'Ordenar', + 'Project ID' => 'ID Proyecto', + 'Project name' => 'Nombre del Proyecto', + 'Public' => 'Público', + 'Personal' => 'Privado', + '%d tasks' => '%d tareas', + '%d task' => '%d tarea', + 'Task ID' => 'ID Tarea', + 'Assign automatically a color when due date is expired' => 'Asignar un color automáticamente cuando la fecha de vencimiento haya expirado', + 'Total score in this column across all swimlanes' => 'Puntaje Total en esta columna para todos los carriles', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentino', + 'COP - Colombian Peso' => 'COP - Peso Colombiano', + '%d groups' => '%d grupos', + '%d group' => '%d grupo', + 'Group ID' => 'ID Grupo', + 'External ID' => 'ID Externa', + '%d users' => '%d usuario(a)s', + '%d user' => '%d usuario(a)', + 'Hide subtasks' => 'Ocultar subtareas', + 'Show subtasks' => 'Mostrar subtareas', + 'Authentication Parameters' => 'Parámetros de Autenticación', + 'API Access' => 'Acceso API', + 'No users found.' => 'No se encontraron usuario(a)s', + 'User ID' => 'ID del Usuario(a)', + 'Notifications are activated' => 'Las notificaciones están activadas', + 'Notifications are disabled' => 'Las notificaciones están deshabilitadas', + 'User disabled' => 'Usuario(a) deshabilitado(a)', + '%d notifications' => '%d notificaciones', + '%d notification' => '%d notificación', + 'There is no external integration installed.' => 'No se ha instalado ninguna integración externa.', + 'You are not allowed to update tasks assigned to someone else.' => 'No se le permite actualizar tareas asignadas a alguien más.', + 'You are not allowed to change the assignee.' => 'No se le permite cambiar la persona asignada.', + 'Task suppression is not permitted' => 'No está permitido la eliminación de tarea', + 'Changing assignee is not permitted' => 'No está permitido cambiar la persona asignada', + 'Update only assigned tasks is permitted' => 'Está permitido actualizar solo las tareas asignadas', + 'Only for tasks assigned to the current user' => 'Solo para las tareas asignadas al usuario(a) actual', + 'My projects' => 'Mis proyectos', + 'You are not a member of any project.' => 'No eres miembro de ningún proyecto.', + 'My subtasks' => 'Mis subtareas', + '%d subtasks' => '%d subtareas', + '%d subtask' => '%d subtarea', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Mover tareas entre esas columnas solo está permitido para las tareas asignadas al usuario(a) actual.', + '[DUPLICATE]' => '[DUPLICAR]', + 'DKK - Danish Krona' => 'DKK - Corona Danesa', + 'Remove user from group' => 'Eliminar usuario(a) del grupo', + 'Assign the task to its creator' => 'Asignar la tarea a su creador', + 'This task was sent by email to "%s" with subject "%s".' => 'Esta tarea fue enviada por correo a "%s" con el asunto "%s".', + 'Predefined Email Subjects' => 'Asuntos de correo predefinidos', + 'Write one subject by line.' => 'Escriba un asunto por línea.', + 'Create another link' => 'Crear otro enlace', + 'BRL - Brazilian Real' => 'BRL - Real brasileño', + 'Add a new Kanboard task' => 'Agregar una nueva tarea de Kanboard', + 'Subtask not started' => 'Subtarea no iniciada', + 'Subtask currently in progress' => 'Subtarea actualmente en progreso', + 'Subtask completed' => 'Subtarea completada', + 'Subtask added successfully.' => 'Subtarea agregada con éxito.', + '%d subtasks added successfully.' => '%d subtareas agregadas con éxito.', + 'Enter one subtask by line.' => 'Ingrese una subtarea por línea.', + 'Predefined Contents' => 'Contenidos predefinidos', + 'Predefined contents' => 'Contenidos predefinidos', + 'Predefined Task Description' => 'Descripción de tarea predefinida', + 'Do you really want to remove this template? "%s"' => '¿Realmente quieres eliminar esta plantilla? "%s"', + 'Add predefined task description' => 'Agregar descripción de tarea predefinida', + 'Predefined Task Descriptions' => 'Descripciones de tareas predefinidas', + 'Template created successfully.' => 'Plantilla creada con éxito', + 'Unable to create this template.' => 'No se puede crear esta plantilla.', + 'Template updated successfully.' => 'No se puede actualizar esta plantilla.', + 'Unable to update this template.' => 'No se puede actualizar esta plantilla.', + 'Template removed successfully.' => 'Plantilla eliminada con éxito.', + 'Unable to remove this template.' => 'No se puede eliminar esta plantilla.', + 'Template for the task description' => 'Plantilla para la descripción de la tarea', + 'The start date is greater than the end date' => 'La fecha de inicio es mayor que la fecha de finalización', + 'Tags must be separated by a comma' => 'Las etiquetas deben estar separadas por una coma', + 'Only the task title is required' => 'Solo se requiere el título de la tarea', + 'Creator Username' => 'Nombre del creador', + 'Color Name' => 'Nombre de color', + 'Column Name' => 'Nombre de columna', + 'Swimlane Name' => 'Nombre del carril', + 'Time Estimated' => 'Tiempo estimado', + 'Time Spent' => 'Tiempo empleado', + 'External Link' => 'Enlace externo', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Esta característica habilita el feed iCal, el feed RSS y la vista de la pizarra pública.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Detener el temporizador de todas las subtareas al mover una tarea a otra columna', + 'Subtask Title' => 'Título de subtarea', + 'Add a subtask and activate the timer when moving a task to another column' => 'Agregar una subtarea y activar el temporizador al mover una tarea a otra columna', + 'days' => 'dias', + 'minutes' => 'minutos', + 'seconds' => 'segundos', + 'Assign automatically a color when preset start date is reached' => 'Asigna automáticamente un color cuando se alcanza la fecha de inicio preestablecida', + 'Move the task to another column once a predefined start date is reached' => 'Mover la tarea a otra columna una vez que se alcanza una fecha de inicio predefinida', + 'This task is now linked to the task %s with the relation "%s"' => 'Esta tarea ahora está vinculada a la tarea %s con la relación "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'El enlace con la relación "%s" con la tarea %s se ha eliminado', + 'Custom Filter:' => 'Filtro personalizado:', + 'Unable to find this group.' => 'No se puede encontrar este grupo.', + '%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 en la columna "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s movió la tarea #%d al carril "%s"', + '%sh spent' => '%sh empleados', + '%sh estimated' => '%sh estimado', + 'Select All' => 'Seleccionar todo', + 'Unselect All' => 'Deseleccionar todo', + 'Apply action' => 'Aplicar acción', + 'Move selected tasks to another column or swimlane' => 'Mover tareas seleccionadas a otra columna', + 'Edit tasks in bulk' => 'Editar tareas en masa', + 'Choose the properties that you would like to change for the selected tasks.' => 'Elija las propiedades que le gustaría cambiar para las tareas seleccionadas.', + 'Configure this project' => 'Configurar este proyecto', + 'Start now' => 'Empezar ahora', + '%s removed a file from the task #%d' => '%s eliminó un archivo de la tarea #%d', + 'Attachment removed from task #%d: %s' => 'Adjunto eliminado de la tarea #%d: %s', + 'No color' => 'Sin color', + 'Attachment removed "%s"' => 'Adjunto eliminado "%s"', + '%s removed a file from the task %s' => '%s eliminó un archivo de la tarea %s', + 'Move the task to another swimlane when assigned to a user' => 'Mover la tarea a otro carril cuando se le asigna a un usuario(a)', + 'Destination swimlane' => 'Carril de destino', + 'Assign a category when the task is moved to a specific swimlane' => 'Asigne una categoría cuando la tarea se mueva a un carril específico', + 'Move the task to another swimlane when the category is changed' => 'Mueve la tarea a otro carril cuando se cambia la categoría', + 'Reorder this column by priority (ASC)' => 'Reordenar esta columna por prioridad (ASC)', + 'Reorder this column by priority (DESC)' => 'Reordenar esta columna por prioridad (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Reordenar esta columna por asignación y prioridad (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Reordenar esta columna por asignación y prioridad (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Reordenar esta columna por persona asignada (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Reordenar esta columna por persona asignada (Z-A)', + 'Reorder this column by due date (ASC)' => 'Reordenar esta columna por fecha de vencimiento (ASC)', + 'Reorder this column by due date (DESC)' => 'Reordenar esta columna por fecha de vencimiento (DESC)', + 'Reorder this column by id (ASC)' => 'Reordenar esta columna por id (ASC)', + 'Reorder this column by id (DESC)' => 'Reordenar esta columna por id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s movió la tarea #%d "%s" al proyecto "%s', + 'Task #%d "%s" has been moved to the project "%s"' => 'La tarea #%d "%s" se movió al proyecto "%s', + 'Move the task to another column when the due date is less than a certain number of days' => 'Mueva la tarea a otra columna cuando la fecha de vencimiento sea inferior a un cierto número de días', + 'Automatically update the start date when the task is moved away from a specific column' => 'Actualizar automáticamente la fecha de inicio cuando la tarea se mueve fuera de una columna específica', + 'HTTP Client:' => 'Cliente HTTP:', + 'Assigned' => 'Asignado', + 'Task limits apply to each swimlane individually' => 'Los límites de tareas se aplican a cada carril de forma individual', + 'Column task limits apply to each swimlane individually' => 'Los límites de tareas por columna se aplican a cada carril de forma individual', + 'Column task limits are applied to each swimlane individually' => 'Los límites de tareas por columna se aplican a cada carril de forma individual', + 'Column task limits are applied across swimlanes' => 'Los límites de tareas por columna se aplican a través de los carriles', + 'Task limit: ' => 'Límite de tareas:', + 'Change to global tag' => 'Cambiar a etiqueta global', + 'Do you really want to make the tag "%s" global?' => '¿Realmente quieres hacer global la etiqueta "%s"?', + 'Enable global tags for this project' => 'Habilitar etiquetas globales para este proyecto', + 'Group membership(s):' => 'Membresía(s) de grupo:', + '%s is a member of the following group(s): %s' => '%s es miembro del(los) siguiente(s) grupo(s): %s', + '%d/%d group(s) shown' => '%d/%d grupo(s) mostrado(s)', + 'Subtask creation or modification' => 'Creación o modificación de subtareas', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Asignar la tarea a un usuario específico cuando la tarea se mueve a un carril específico', + 'Comment' => 'Comentario', + 'Collapse vertically' => 'Contraer verticalmente', + 'Expand vertically' => 'Expandir verticalmente', + 'MXN - Mexican Peso' => 'MXN - Peso Mexicano', + 'Estimated vs actual time per column' => 'Tiempo estimado vs tiempo real por columna', + 'HUF - Hungarian Forint' => 'HUF - Florín Húngaro', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => '¡Debes seleccionar un archivo para subir como tu avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => '¡El archivo que subiste no es una imagen válida! (Solo se permiten *.gif, *.jpg, *.jpeg y *.png)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Establecer automáticamente la fecha de vencimiento cuando la tarea se mueve fuera de una columna específica', + 'No other projects found.' => 'No se encontraron otros proyectos.', + 'Tasks copied successfully.' => 'Tareas copiadas con éxito.', + 'Unable to copy tasks.' => 'No se pudieron copiar las tareas.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema claro', + 'Dark theme' => 'Tema oscuro', + 'Automatic theme - Sync with system' => 'Tema automático - Sincronizar con el sistema', + 'Application managers or more' => 'Gerentes de aplicación o más', + 'Administrators' => 'Administradores', + 'Visibility:' => 'Visibilidad:', + 'Standard users' => 'Usuarios estándar', + 'Visibility is required' => 'La visibilidad es obligatoria', + 'The visibility should be an app role' => 'La visibilidad debe ser un rol de aplicación', + 'Reply' => 'Responder', + '%s wrote: ' => '%s escribió:', + 'Number of visible tasks in this column and swimlane' => 'Número de tareas visibles en esta columna y carril', + 'Number of tasks in this swimlane' => 'Número de tareas en este carril', + 'Unable to find another subtask in progress, you can close this window.' => 'No se puede encontrar otra subtarea en progreso, puede cerrar esta ventana.', + 'This theme is invalid' => 'Este tema no es válido', + 'This role is invalid' => 'Este rol no es válido', + 'This timezone is invalid' => 'Esta zona horaria no es válida', + 'This language is invalid' => 'Este idioma no es válido', + 'This URL is invalid' => 'Esta URL no es válida', + 'Date format invalid' => 'Formato de fecha inválido', + 'Time format invalid' => 'Formato de hora inválido', + 'Invalid Mail transport' => 'Transporte de correo inválido', + 'Color invalid' => 'Color inválido', + 'This value must be greater or equal to %d' => 'Este valor debe ser mayor o igual a %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Agregar un BOM al principio del archivo (requerido para Microsoft Excel)', + 'Just add these tag(s)' => 'Simplemente agregue esta(s) etiqueta(s)', + 'Remove internal link(s)' => 'Eliminar enlace(s) interno(s)', + 'Import tasks from another project' => 'Importar tareas de otro proyecto', + 'Select the project to copy tasks from' => 'Seleccione el proyecto desde el que copiar tareas', + 'The total maximum allowed attachments size is %sB.' => 'El tamaño total máximo permitido para los archivos adjuntos es %sB.', + 'Add attachments' => 'Agregar archivos adjuntos', + 'Task #%d "%s" is overdue' => 'La tarea #%d "%s" está vencida', + 'Enable notifications by default for all new users' => 'Habilitar notificaciones por defecto para todos los nuevos usuarios', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Asignar la tarea a su creador para columnas específicas si no se ha establecido un responsable manualmente', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Asignar una tarea al usuario conectado al cambiar de columna a la columna especificada si no hay ningún usuario asignado', +]; diff --git a/app/Locale/fa_IR/translations.php b/app/Locale/fa_IR/translations.php new file mode 100644 index 0000000..8b4a33f --- /dev/null +++ b/app/Locale/fa_IR/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => 'Ù«', + 'number.thousands_separator' => 'Ù¬', + 'None' => 'هیچ', + 'Edit' => 'ویرایش', + 'Remove' => 'حذÙ', + 'Yes' => 'بلی', + 'No' => 'خیر', + 'cancel' => 'انصراÙ', + 'or' => 'یا', + 'Yellow' => 'زرد', + 'Blue' => 'آبی', + 'Green' => 'سبز', + 'Purple' => 'ارغوانی', + 'Red' => 'قرمز', + 'Orange' => 'نارنجی', + 'Grey' => 'خاکستری', + 'Brown' => 'قهوه ای', + 'Deep Orange' => 'نارنجی پررنگ', + 'Dark Grey' => 'خاکستری تیره', + 'Pink' => 'صورتی', + 'Teal' => 'آبی سیر', + 'Cyan' => 'یشمی', + 'Lime' => 'لیمویی', + 'Light Green' => 'سبز کمرنگ', + 'Amber' => 'زرد کهربایی', + 'Save' => 'ذخیره', + 'Login' => 'ورود', + 'Official website:' => 'وبسایت رسمی :', + 'Unassigned' => 'محول نشده', + 'View this task' => 'نمایش این کار', + 'Remove user' => 'حذ٠کاربر', + 'Do you really want to remove this user: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این کاربر حذ٠شود؟ : « %s » ', + 'All users' => 'تمامی کاربران', + 'Username' => 'نام کاربری', + 'Password' => 'گذرواژه', + 'Administrator' => 'مدیر سیستم', + 'Sign in' => 'ورود', + 'Users' => 'کاربران', + 'Forbidden' => 'ممنوع شده', + 'Access Forbidden' => 'دسترسی غیرمجاز', + 'Edit user' => 'ویرایش کاربر', + 'Logout' => 'خروج', + 'Bad username or password' => 'نام کاربری یا گذرواژه اشتباه', + 'Edit project' => 'ویرایش پروژه', + 'Name' => 'نام', + 'Projects' => 'پروژه ها', + 'No project' => 'پروژه ای نیست', + 'Project' => 'پروژه', + 'Status' => 'وضعیت', + 'Tasks' => 'کارها', + 'Board' => 'برد', + 'Actions' => 'ÙØ¹Ø§Ù„یت ها', + 'Inactive' => 'ØºÛŒØ±ÙØ¹Ø§Ù„', + 'Active' => 'ÙØ¹Ø§Ù„', + 'Unable to update this board.' => 'امکان ÙØ¹Ø§Ù„ کردن این برد وجود ندارد.', + 'Disable' => 'ØºÛŒØ±ÙØ¹Ø§Ù„', + 'Enable' => 'ÙØ¹Ø§Ù„', + 'New project' => 'پروژه جدید', + 'Do you really want to remove this project: "%s"?' => 'واقعاً مایل به حذ٠این پروژه هستید : « %s » ؟', + 'Remove project' => 'حذ٠پروژه', + 'Edit the board for "%s"' => 'ویرایش برد برای « %s »', + 'Add a new column' => 'Ø§ÙØ²ÙˆØ¯Ù† یک ستون جدید', + 'Title' => 'عنوان', + 'Assigned to %s' => 'اختصاص به %s', + 'Remove a column' => 'حذ٠یک ستون', + 'Unable to remove this column.' => 'امکان حذ٠این ستون وجود ندارد.', + 'Do you really want to remove this column: "%s"?' => 'واقعاً مایل به حذ٠این ستون هستید : « %s » ؟', + 'Settings' => 'تنظیمات', + 'Application settings' => 'تنظیمات برنامه', + 'Language' => 'زبان', + 'Webhook token:' => 'توکن Webhook:', + 'API token:' => 'توکن API:', + 'Database size:' => 'اندازه پایگاه داده :', + 'Download the database' => 'بارگیری پایگاه داده', + 'Optimize the database' => 'بهینه سازی پایگاه داده', + '(VACUUM command)' => '(دستور VACUUM)', + '(Gzip compressed Sqlite file)' => '(ÙØ§ÛŒÙ„ ÙØ´Ø±Ø¯Ù‡ sqlite Gzip)', + 'Close a task' => 'بستن یک کار', + 'Column' => 'ستون', + 'Color' => 'رنگ', + 'Assignee' => 'شخص محول شده', + 'Create another task' => 'ایجاد کاری دیگر', + 'New task' => 'کار جدید', + 'Open a task' => 'یک کار را باز کنید', + 'Do you really want to open this task: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این کار را باز کنید: "%s"ØŸ', + 'Back to the board' => 'بازگشت به برد', + 'There is nobody assigned' => 'به هیچ کسی محول نشده', + 'Column on the board:' => 'ستون در برد', + 'Close this 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 حر٠است', + 'The password is required' => 'Ù…ÛŒ بایست گذرواژه را وارد کنید', + 'This value must be an integer' => 'این مقدار باید عدد صحیح باشد', + 'The username must be unique' => 'نام کاربری باید یکتا باشد', + 'The user id is required' => 'شناسه کاربر الزامی است', + 'Passwords don\'t match' => 'گذرواژه مطابقت ندارد', + 'The confirmation is required' => 'تصدیق الزامی است', + 'The project is required' => 'پروژه الزامی است', + 'The id is required' => 'شناسه الزامی است', + 'The project id is required' => 'شناسه پروژه الزامی است', + 'The project name is required' => 'نام پروژه الزامی است', + 'The title is required' => 'عنوان الزامی است', + 'Settings saved successfully.' => 'تنظیمات با موÙقیت ذخیره شدند.', + 'Unable to save your settings.' => 'تنظیمات شما ذخیره نمی شود.', + 'Database optimization done.' => 'بهینه سازی پایگاه داده انجام شد.', + 'Your project has been created successfully.' => 'پروژه شما با موÙقیت ایجاد شد', + 'Unable to create your project.' => 'ایجاد پروژه شما امکان پذیر نیست.', + 'Project updated successfully.' => 'پروژه با موÙقیت به روز رسانی شد.', + 'Unable to update this project.' => 'بروز رسانی این پروژه امکان پذیر نیست.', + 'Unable to remove this project.' => 'حذ٠این پروژه امکان پذیر نیست.', + 'Project removed successfully.' => 'پروژه با موÙقیت حذ٠شد.', + 'Project activated successfully.' => 'پروژه با موÙقیت ÙØ¹Ø§Ù„ شد.', + 'Unable to activate this project.' => 'ÙØ¹Ø§Ù„ شدن این پروژه امکان پذیر نیست.', + 'Project disabled successfully.' => 'پروژه با موÙقیت ØºÛŒØ±ÙØ¹Ø§Ù„ شد.', + 'Unable to disable this project.' => 'غیر ÙØ¹Ø§Ù„ شدن این پروژه امکان پذیر نیست.', + 'Unable to open this task.' => 'باز کردن این کار امکان پذیر نیست.', + 'Task opened successfully.' => 'کار با موÙقیت باز شد.', + 'Unable to close this task.' => 'بست این کار امکان پذیر نیست.', + 'Task closed successfully.' => 'کار با موÙقیت بسته شد.', + 'Unable to update your task.' => 'بروز رسانی کار شما امکان پذیر نیست.', + 'Task updated successfully.' => 'کار با موÙقیت بروز رسانی شد.', + 'Unable to create your task.' => 'ایجاد کار شما امکان پذیر نیست.', + 'Task created successfully.' => 'کار با موÙقیت ایجاد شد.', + 'User created successfully.' => 'کاربر با موÙقیت ایجاد شد.', + 'Unable to create your user.' => 'ایجاد کاربر شما امکان پذیر نیست.', + 'User updated successfully.' => 'کاربر با موÙقیت بروز رسانی شد.', + 'User removed successfully.' => 'کاربر با موÙقیت حذ٠شد.', + 'Unable to remove this user.' => 'حذ٠این کاربر امکان پذیر نیست.', + 'Board updated successfully.' => 'برد با موÙقیت بروز رسانی شد.', + 'Ready' => 'آماده', + 'Backlog' => 'بلاتکلیÙ', + 'Work in progress' => 'کار در حال انجام', + 'Done' => 'انجام شده', + 'Application version:' => 'نسخه برنامه:', + 'Id' => 'شناسه', + 'Public link' => 'پیوند عمومی', + 'Timezone' => 'منطقه زمانی', + 'Sorry, I didn\'t find this information in my database!' => 'شرمنده، من این اطلاعات را در پایگاه داده ام پیدا نکردم!', + 'Page not found' => 'ØµÙØ­Ù‡ پیدا نشد', + 'Complexity' => 'پیچیدگی', + 'Task limit' => 'محدودیت کار', + 'Task count' => 'تعداد کار', + 'User' => 'کاربر', + 'Comments' => 'نظرات', + 'Comment is required' => 'نظر الزامی است', + 'Comment added successfully.' => 'نظر با موÙقیت Ø§ÙØ²ÙˆØ¯Ù‡ شد', + 'Unable to create your comment.' => 'ایجاد نظر شما امکان پذیر نیست', + 'Due Date' => 'سر رسید', + 'Invalid date' => 'تاریخ نامعتبر', + 'Automatic actions' => 'اعمال خودکار', + 'Your automatic action has been created successfully.' => 'عمل خودکار شما با موÙقیت ایجاد شد', + 'Unable to create your automatic action.' => 'ایجاد عمل خودکار شما امکان پذیر نیست.', + 'Remove an action' => 'حذ٠یک عمل', + 'Unable to remove this action.' => 'حذ٠این عمل امکان پذیر نیست.', + 'Action removed successfully.' => 'عمل با موÙقیت حذ٠شد', + 'Automatic actions for the project "%s"' => 'اعمال خودکار برای پروژه "%s"', + 'Add an action' => 'یک عمل اضاÙÙ‡ کنید', + 'Event name' => 'نام رویداد', + 'Action' => 'عمل', + 'Event' => 'رویداد', + 'When the selected event occurs execute the corresponding action.' => 'وقتی رویداد انتخابی رخ دهد، عمل متناظر با آن را اجرا Ú©Ù†.', + 'Next step' => 'گام بعدی', + '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 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' => 'تخصیص یک رنگ به یک کاربر خاص', + 'Position' => 'موقعیت', + 'Duplicate to project' => 'Ú©Ù¾ÛŒ به پروژه ای دیگر', + 'Duplicate' => 'Ú©Ù¾ÛŒ کردن', + 'Link' => 'پیوند', + 'Comment updated successfully.' => 'نظر با موÙقیت بروز رسانی شد.', + 'Unable to update your comment.' => 'بروز رسانی نظر شما امکان پذیر نیست.', + 'Remove a comment' => 'حذ٠یک نظر', + 'Comment removed successfully.' => 'نظر با موÙقیت حذ٠شد.', + 'Unable to remove this comment.' => 'حذ٠این نظر امکان پذیر نیست.', + 'Do you really want to remove this comment?' => 'واقعاً مایل به حذ٠این نظر هستید؟', + 'Current password for the user "%s"' => 'گذرواژه کنونی برای کاربر "%s"', + 'The current password is required' => 'گذرواژه کنونی الزامی است', + 'Wrong password' => 'گذرواژه اشتباه', + 'Unknown' => 'ناشناخته', + 'Last logins' => 'آخرین ورود ها', + 'Login date' => 'تاریخ ورود', + 'Authentication method' => 'شیوه تایید هویت', + 'IP address' => 'آدرس IP', + 'User agent' => 'واسط کاربری', + 'Persistent connections' => 'ارتباطات برقرار شده', + 'No session.' => 'نشستی وجود ندارد.', + 'Expiration date' => 'تاریخ انقضاء', + 'Remember Me' => 'مرا به خاطر بسپار', + 'Creation date' => 'تاریخ ایجاد', + 'Everybody' => 'همه', + 'Open' => 'باز', + 'Closed' => 'بسته', + 'Search' => 'جستجو', + 'Nothing found.' => 'چیزی ÛŒØ§ÙØª نشد', + 'Due date' => 'تاریخ سررسید', + 'Description' => 'شرح', + '%d comments' => '%d نظر', + '%d comment' => '%d نظر', + '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.' => 'حساب خارجی شما به Ù¾Ø±ÙˆÙØ§ÛŒÙ„ شما با موÙقیت پیوند خورد.', + 'Email' => 'پست الکترونیکی', + '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' => 'اختصاص خودکار یک دسته بندی بر اساس یک رنگ', + 'Task creation or modification' => 'ایجاد یا تغییر کار', + 'Category' => 'دسته بندی', + 'Category:' => 'دسته بندی:', + 'Categories' => 'دسته بندی ها', + 'Your category has been created successfully.' => 'دسته بندی شما با موÙقیت ایجاد شد.', + 'This category has been updated successfully.' => 'دسته بندی با موÙقیت بروز رسانی شد.', + 'Unable to update this 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' => 'بدون دسته بندی', + 'The name is required' => 'نام الزامی است', + 'Remove a file' => 'حذ٠یک ÙØ§ÛŒÙ„', + 'Unable to remove this file.' => 'حذ٠این ÙØ§ÛŒÙ„ امکان پذیر نیست.', + 'File removed successfully.' => 'ÙØ§ÛŒÙ„ با موÙقیت حذ٠شد.', + 'Attach a document' => 'پیوست یک سند', + 'Do you really want to remove this file: "%s"?' => 'واقعاً از Ø­Ø°Ù ÙØ§ÛŒÙ„ "%s" اطمینان دارید؟', + 'Attachments' => 'پیوست ها', + 'Edit the task' => 'ویرایش کار', + 'Add a comment' => 'Ø§ÙØ²ÙˆØ¯Ù† یک نظر', + 'Edit a comment' => 'ویرایش یک نظر', + 'Summary' => 'خلاصه', + 'Time tracking' => 'پیگیری زمانی', + 'Estimate:' => 'تخمین:', + 'Spent:' => 'صر٠شده:', + 'Do you really want to remove this sub-task?' => 'آیا واقعاً از حذ٠این کار ÙØ±Ø¹ÛŒ اطمینان دارید؟', + 'Remaining:' => 'باقیمانده:', + 'hours' => 'ساعت', + 'estimated' => 'تخمینی', + 'Sub-Tasks' => 'کارهای ÙØ±Ø¹ÛŒ', + 'Add a sub-task' => 'Ø§ÙØ²ÙˆØ¯Ù† یک کار ÙØ±Ø¹ÛŒ', + 'Original estimate' => 'تخمین اصلی', + 'Create another sub-task' => 'ایجاد یک کار ÙØ±Ø¹ÛŒ دیگر', + 'Time spent' => 'زمان سپری شده', + 'Edit a sub-task' => 'ویرایش یک کار ÙØ±Ø¹ÛŒ', + 'Remove a sub-task' => 'حذ٠یک کار ÙØ±Ø¹ÛŒ', + 'The time must be a numeric value' => 'زمان باید بصورت عدد باشد', + 'Todo' => 'لیست جهت انجام کار', + 'In progress' => 'در حال انجام', + 'Sub-task removed successfully.' => 'کار ÙØ±Ø¹ÛŒ با موÙقیت حذ٠شد.', + 'Unable to remove this sub-task.' => 'حذ٠این کار ÙØ±Ø¹ÛŒ امکان پذیر نیست.', + 'Sub-task updated successfully.' => 'کار ÙØ±Ø¹ÛŒ با موÙقیت بروز رسانی شد.', + 'Unable to update your sub-task.' => 'بروز رسانی کار ÙØ±Ø¹ÛŒ شما امکان پذیر نیست.', + 'Unable to create your sub-task.' => 'ایجاد کار ÙØ±Ø¹ÛŒ شما امکان پذیر نیست.', + 'Maximum size: ' => 'بیشترین اندازه: ', + 'Display another project' => 'نمایش پروژه دیگر', + 'Created by %s' => 'ایجاد شده توسط %s', + 'Tasks Export' => 'برون ریزی کار ها', + 'Start Date' => 'تاریخ شروع', + 'Execute' => 'اجرا', + 'Task Id' => 'شناسه کار', + 'Creator' => 'ایجاد کننده', + 'Modification date' => 'تاریخ تغییر', + 'Completion date' => 'تاریخ تکمیل', + 'Clone' => 'تکثیر', + 'Project cloned successfully.' => 'پروژه با موÙقیت تکثیر شد.', + 'Unable to clone this project.' => 'تکثیر این پروژه امکان پذیر نیست.', + 'Enable email notifications' => 'ÙØ¹Ø§Ù„ کردن آگاه سازی از طریق پست الکترونیکی', + 'Task position:' => 'جایگاه کار:', + 'The task #%d has been opened.' => 'کار #%d باز شد.', + 'The task #%d has been closed.' => 'کار #%d بسته شد.', + 'Sub-task updated' => 'کار ÙØ±Ø¹ÛŒ بروز رسانی شد', + 'Title:' => 'عنوان:', + 'Status:' => 'وضعیت:', + 'Assignee:' => 'شخص محول شده:', + 'Time tracking:' => 'رهگیری زمان:', + 'New sub-task' => 'کار ÙØ±Ø¹ÛŒ جدید', + 'New attachment added "%s"' => 'پیوست جدید Ø§ÙØ²ÙˆØ¯Ù‡ شده: "%s"', + 'New comment posted by %s' => 'نظر جدید توسط %s ارسال شد.', + 'New comment' => 'نظر جدید', + 'Comment updated' => 'نظر بروز رسانی شد', + 'New subtask' => 'کار ÙØ±Ø¹ÛŒ جدید', + 'I only want to receive notifications for these projects:' => 'Ù…ÛŒ خواهم آگاه سازی ها را Ùقط برای این پروژه ها Ø¯Ø±ÛŒØ§ÙØª کنم:', + 'view the task on Kanboard' => 'نمایش کار در Ú©Ù† برد', + 'Public access' => 'دسترسی عمومی', + 'Disable public access' => 'غیر ÙØ¹Ø§Ù„ کردن دسترسی عمومی', + 'Enable public access' => 'ÙØ¹Ø§Ù„ کردن دسترسی عمومی', + 'Public access disabled' => 'دسترسی عمومی غیر ÙØ¹Ø§Ù„ شد', + 'Move the task to another project' => 'انتقال کار به پروژه ای دیگر', + 'Move to project' => 'انتقال به پروژه ای دیگر', + 'Do you really want to duplicate this task?' => 'واقعاً Ù…ÛŒ خواهید این کار را Ú©Ù¾ÛŒ کنید؟', + 'Duplicate a task' => 'Ú©Ù¾ÛŒ یک کار', + 'External accounts' => 'حساب های خارجی', + 'Account type' => 'نوع حساب', + 'Local' => 'محلی', + 'Remote' => 'از راه دور', + 'Enabled' => 'ÙØ¹Ø§Ù„', + 'Disabled' => 'غیر ÙØ¹Ø§Ù„', + 'Login:' => 'ورود:', + 'Full Name:' => 'نام کامل:', + 'Email:' => 'پست الکترونیکی:', + 'Notifications:' => 'آگاه سازی ها:', + 'Notifications' => 'آگاه سازی ها', + 'Account type:' => 'نوع حساب:', + 'Edit profile' => 'ویرایش Ù¾Ø±ÙˆÙØ§ÛŒÙ„', + 'Change password' => 'تغییر گذرواژه', + 'Password modification' => 'تغییر گذرواژه', + 'External authentications' => 'تایید هویت خارجی', + 'Never connected.' => 'هرگز متصل نشده.', + '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' => 'محول نشده، تخمین %sh', + '%s updated a comment on the task %s' => '%s یک نظر در وظیÙÙ‡ %s را به‌روزرسانی کرد', + '%s commented the task %s' => '%s روی وظیÙÙ‡ %s نظر داد', + '%s\'s activity' => 'ÙØ¹Ø§Ù„یت %s', + 'RSS feed' => 'خوراک RSS', + '%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)' => 'ستون های Ù¾ÛŒØ´ÙØ±Ø¶ برای پروژه جدید - با کاما از هم جدا کنید', + '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' => 'یک رویداد را انتخاب کنید', + 'Create a task from an external provider' => 'ایجاد یک کار از یک ÙØ±Ø§Ù‡Ù… کننده خارجی', + 'Change the assignee based on an external username' => 'تغییر شخص محول شده بر اساس یک نام کاربری خارجی', + 'Change the category based on an external label' => 'تغییر دسته بندی بر اساس یک برچسب لیبل خارجی', + 'Reference' => 'مرجع', + 'Label' => 'برچسب لیبل', + 'Database' => 'پایگاه داده', + 'About' => 'درباره', + 'Database driver:' => 'راه انداز پایگاه داده:', + 'Board settings' => 'تنظیمات برد', + 'Webhook settings' => 'تنظیمات Webhook', + 'Reset token' => 'بازنشانی توکن', + 'API endpoint:' => 'نقطه پایانی API:', + 'Refresh interval for personal 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 وارد کنید، بصورت Ù¾ÛŒØ´ÙØ±Ø¶ دو روز است.', + 'Frequency in second (60 seconds by default)' => 'تواتر به ثانیه، بصورت Ù¾ÛŒØ´ÙØ±Ø¶ 60 ثانیه است.', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'تواتر به ثانیه، 0 برای غیر ÙØ¹Ø§Ù„ کردن، بصورت Ù¾ÛŒØ´ÙØ±Ø¶ 10 ثانیه است.', + 'Application URL' => 'آدرس اپلیکیشن', + 'Token regenerated.' => 'توکن بازتولید شد.', + 'Date format' => 'ÙØ±Ù…ت تاریخ', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ÙØ±Ù…ت ایزو همواره مورد قبول است، مثال: "%s" Ùˆ "%s"', + 'New personal project' => 'پروژه خصوصی جدید', + 'This project is personal' => 'این پروژه خصوصی است', + 'Add' => 'Ø§ÙØ²ÙˆØ¯Ù†', + 'Start date' => 'تاریخ شروع', + 'Time estimated' => 'زمان تخمینی', + 'There is nothing assigned to you.' => 'هیچ چیزی به شما محول نشده است.', + 'My tasks' => 'کارهای من', + 'Activity stream' => 'جریان ÙØ¹Ø§Ù„یت', + 'Dashboard' => 'میز کار', + 'Confirmation' => 'تائیدیه', + 'Webhooks' => 'Webhook ها', + 'API' => 'واسط API', + 'Create a comment from an external provider' => 'ایجاد یک نظر از ÙØ±Ø§Ù‡Ù… کننده خارجی', + 'Project management' => 'مدیریت پروژه', + 'Columns' => 'ستون ها', + 'Task' => 'کار', + 'Percentage' => 'درصد', + 'Number of tasks' => 'تعداد کارها', + 'Task distribution' => 'توزیع کار', + 'Analytics' => 'آمار', + 'Subtask' => 'کار ÙØ±Ø¹ÛŒ', + 'User repartition' => 'توزیع مجدد کاربر', + 'Clone this project' => 'Ú©Ù¾ÛŒ این پروژه', + 'Column removed successfully.' => 'ستون با موÙقیت حذ٠شد', + '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 subtask id is required' => 'شناسه کار ÙØ±Ø¹ÛŒ الزامی است', + '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' => 'شناسه کاربر باید از یک عدد باشد', + 'This value is required' => 'این مقدار الزامی است', + 'This value must be numeric' => 'این مقدار باید عددی باشد', + 'Unable to create this task.' => 'ایجاد این کار امکان پذیر نیست.', + 'Cumulative flow diagram' => 'نمودار جریان تجمعی', + 'Daily project summary' => 'خلاصه روزانه پروژه', + 'Daily project summary export' => 'برون ریزی خلاصه روزانه پروژه', + 'Exports' => 'برون ریزی ها', + 'This export contains the number of tasks per column grouped per day.' => 'این برون ریزی شامل تعداد کار در ستون است Ú©Ù‡ بر اساس روز دسته بندی شده', + 'Active swimlanes' => 'مسیرهای شنای ÙØ¹Ø§Ù„', + 'Add a new swimlane' => 'Ø§ÙØ²ÙˆØ¯Ù† یک مسیر شنا', + 'Default swimlane' => 'مسیر شنای Ù¾ÛŒØ´ÙØ±Ø¶', + 'Do you really want to remove this swimlane: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این مسیر شنا را حذ٠کنید : "%s" ØŸ', + 'Inactive swimlanes' => 'مسیرهای شنای غیر ÙØ¹Ø§Ù„', + 'Remove a swimlane' => 'حذ٠یک مسیر شنا', + 'Swimlane modification for the project "%s"' => 'تغییر مسیر شنا برای پروژه "%s"', + 'Swimlane removed successfully.' => 'مسیر شنا با موÙقیت حذ٠شد.', + 'Swimlanes' => 'مسیرهای شنا', + 'Swimlane updated successfully.' => 'مسیر شنا با موÙقیت بروز رسانی شد.', + 'Unable to remove this swimlane.' => 'حذ٠این مسیر شنا امکان پذیر نیست.', + 'Unable to update this swimlane.' => 'بروز رسانی این مسیر شنا امکان پذیر نیست.', + 'Your swimlane has been created successfully.' => 'مسیر شنای شما با موÙقیت ایجاد شد.', + 'Example: "Bug, Feature Request, Improvement"' => 'مثال: "باگ, درخواست ویژگی, بهبود"', + 'Default categories for new projects (Comma-separated)' => 'دسته بندی ها برای پروژه جدید، با کاما از هم جدا کنید', + 'Integrations' => 'تلÙیق ها', + 'Integration with third-party services' => 'تلÙیق با خدمات شخص ثالث', + 'Subtask Id' => 'شناسه کار ÙØ±Ø¹ÛŒ', + 'Subtasks' => 'کارهای ÙØ±Ø¹ÛŒ', + 'Subtasks Export' => 'برون ریزی کارهای ÙØ±Ø¹ÛŒ', + 'Task Title' => 'عنوان کار', + 'Untitled' => 'بدون عنوان', + 'Application default' => 'Ù¾ÛŒØ´ÙØ±Ø¶ اپلیکیشن', + 'Language:' => 'زبان:', + 'Timezone:' => 'ناحیه زمانی:', + 'All columns' => 'همه ستون ها', + 'Next' => 'بعدی', + '#%d' => '#%d', + 'All swimlanes' => 'همه مسیرهای شنا', + 'All colors' => 'تمامی رنگ ها', + 'Moved to column %s' => 'منتقل شده به ستون %s', + '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' => 'پیگیری زمان', + 'You already have one subtask in progress' => 'شما در حال حاضر یک کار ÙØ±Ø¹ÛŒ در حال انجام دارید', + 'Which parts of the project do you want to duplicate?' => 'کدام بخش از پروژه را Ù…ÛŒ خواهید Ú©Ù¾ÛŒ کنید؟', + 'Disallow login form' => 'غیرمجاز کردن ÙØ±Ù… ورود', + 'Start' => 'شروع', + 'End' => 'پایان', + 'Task age in days' => 'عمر کار به روز', + 'Days in this column' => 'روزها در این ستون', + '%dd' => '%dروز', + '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' => 'Ùیلد اجباری', + 'Link added successfully.' => 'پیوند با موÙقیت Ø§ÙØ²ÙˆØ¯Ù‡ شد', + 'Link updated successfully.' => 'پیوند با موÙقیت بروز شد.', + 'Link removed successfully.' => 'پیوند با موÙقیت حذ٠شد.', + 'Link labels' => 'برچسب های پیوند', + 'Link modification' => 'تغییرات پیوند', + 'Opposite label' => 'برچسب مخالÙ', + 'Remove a link' => 'حذ٠یک برچسب', + '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.' => 'حذ٠این پیوند امکان پذیر نیست.', + 'relates to' => 'مرتبط است با', + 'blocks' => 'مسدود Ù…ÛŒ کند', + 'is blocked by' => 'مسدود شده توسط', + 'duplicates' => 'Ú©Ù¾ÛŒ است', + 'is duplicated by' => 'Ú©Ù¾ÛŒ شده توسط', + 'is a child of' => 'ÙØ±Ø²Ù†Ø¯ÛŒ است از', + 'is a parent of' => 'پدری است برای', + 'targets milestone' => 'نقطه عط٠را مورد هد٠قرار Ù…ÛŒ دهد', + 'is a milestone of' => 'نقطه عطÙÛŒ است از', + 'fixes' => 'مشکل را Ø±ÙØ¹ Ù…ÛŒ کند', + 'is fixed by' => 'مشکل حل شده توسط', + 'This task' => 'این کار', + '<1h' => 'زیر یکساعت', + '%dh' => '%d ساعت', + 'Expand tasks' => 'گستردن کارها', + 'Collapse tasks' => 'جمع کردن کارها', + 'Expand/collapse tasks' => 'جمع/گستردن کارها', + 'Close dialog box' => 'بستن کادر محاوره', + 'Submit a form' => 'ارسال یک ÙØ±Ù…', + 'Board view' => 'نمای برد', + 'Keyboard shortcuts' => 'میانبرهای ØµÙØ­Ù‡ کلید', + 'Open board switcher' => 'باز کردن سویچر برد', + 'Application' => 'اپلیکیشن', + 'Compact view' => 'نمای جمع Ùˆ جور', + 'Horizontal scrolling' => 'پیمایش اÙÙ‚ÛŒ', + 'Compact/wide view' => 'نمای جمع Ùˆ جور/عریض', + 'Currency' => 'واحد پول', + 'Personal project' => 'پروژه خصوصی', + 'AUD - Australian Dollar' => 'AUD - دلار استرالیا', + 'CAD - Canadian Dollar' => 'CAD - دلار کانادا', + 'CHF - Swiss Francs' => 'CHF - ÙØ±Ø§Ù†Ú© سویس', + 'Custom Stylesheet' => 'Stylesheet Ø³ÙØ§Ø±Ø´ÛŒ', + 'EUR - Euro' => 'EUR - یورو اروپا', + 'GBP - British Pound' => 'GBP - پوند انگلیس', + 'INR - Indian Rupee' => 'INR - روپیه هند', + 'JPY - Japanese Yen' => 'JPY - ین ژاپن', + 'NZD - New Zealand Dollar' => 'NZD - دلار نیوزلند', + 'PEN - Peruvian Sol' => 'PEN - سول پرو', + 'RSD - Serbian dinar' => 'RSD - دینار صرب', + 'CNY - Chinese Yuan' => 'CNY - یوآن چین', + 'USD - US Dollar' => 'USD - دلار آمریکا', + 'VES - Venezuelan Bolívar' => 'VES - بولیوار ونزوئلا', + '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' => 'زمان صر٠شده در ستون', + '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.' => 'این گزارش حاوی همه انتقال های ستون برای هر کار با تاریخ، کاربر Ùˆ زمان صر٠شده برای هر گذار است.', + 'Currency rates' => 'نرخ های واحد پول', + 'Rate' => 'نرخ', + 'Change reference currency' => 'تغییر مرجع واحد پول', + 'Reference currency' => 'مرجع واحد پول', + 'The currency rate has been added successfully.' => 'نرخ واحد پول با موÙقیت Ø§ÙØ²ÙˆØ¯Ù‡ شد.', + 'Unable to add this currency rate.' => 'Ø§ÙØ²ÙˆØ¯Ù† این نرخ واحد پول امکان پذیر نیست.', + 'Webhook URL' => 'آدرس Webhook', + '%s removed the assignee of the task %s' => 'کاربر %s شخص محول شده برای کار %s را حذ٠کرد.', + 'Information' => 'اطلاعات', + 'Check two factor authentication code' => 'بررسی کد احراز هویت دو مرحله ای', + 'The two factor authentication code is not valid.' => 'کد احراز هویت دو مرحله ای معتبر نیست.', + 'The two factor authentication code is valid.' => 'کد احراز دو مرحله ای معتبر است.', + 'Code' => 'کد', + 'Two factor authentication' => 'احراز هویت دو مرحله ای', + 'This QR code contains the key URI: ' => 'این کد QR حاوی کلید URI است: ', + 'Check my code' => 'کد مرا بررسی Ú©Ù†', + 'Secret key: ' => 'کلید سرّی', + 'Test your device' => 'دستگاه خود را آزمایش کنید', + 'Assign a color when the task is moved to a specific column' => 'به هنگام انتقال کار به یک ستون خاص، یک رنگ اختصاص یابد', + '%s via Kanboard' => '%s از طریق کان Ø¨ÙØ±Ø¯', + 'Burndown chart' => 'نمودار کار باقی مانده', + 'This chart show the task complexity over the time (Work Remaining).' => 'این نمودار پیچیدگی کار Ø·ÛŒ زمان را نشان Ù…ÛŒ دهد (کار باقیمانده)', + 'Screenshot taken %s' => 'اسکرین شات Ú¯Ø±ÙØªÙ‡ شد %s', + 'Add a screenshot' => 'Ø§ÙØ²ÙˆØ¯Ù† اسکرین شات', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'یک اسکرین شات بگیرید Ùˆ با زدن دکمه های کنترل+ÙˆÛŒ یا کامند+ÙˆÛŒ اینجا بچسبانید.', + 'Screenshot uploaded successfully.' => 'اسکرین شات با موÙقیت بارگذاری شد.', + 'SEK - Swedish Krona' => 'SEK - کرون سوئد', + 'Identifier' => 'شناسه', + 'Disable two factor authentication' => 'ØºÛŒØ±ÙØ¹Ø§Ù„ کردن تایید هویت دو مرحله ای', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'آیا واقعاً Ù…ÛŒ خواهید تایید هویت دو مرحله ای برای کاربر "%s" را غیر ÙØ¹Ø§Ù„ کنید؟', + 'Edit link' => 'ویرایش پیوند', + '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' => 'کار متناوب برنامه ریزی شده تا تولید شود', + 'Score' => 'امتیاز', + 'The identifier must be unique' => 'شناسه باید یکتا باشد', + 'This linked task id doesn\'t exists' => 'این کار پیوند داده شده وجود ندارد', + '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' => 'دوره زمانی برای محاسبه تاریخ سررسید جدید', + '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: ' => 'ÙØ§Ú©ØªÙˆØ± محاسبه تاریخ سررسید جدید: ', + 'Month(s)' => 'ماه(ها)', + 'This task has been created by: ' => 'این کار ایجاد شده توسط: ', + 'Recurrent task has been generated:' => 'کار متناوب تولید شد: ', + 'Timeframe to calculate new due date: ' => 'دوره زمانی برای محاسبه تاریخ سررسید جدید: ', + 'Trigger to generate recurrent task: ' => 'تنظیم برای تولید کار متناوب: ', + 'When task is closed' => 'وقتی کار بسته شد', + 'When task is moved from first column' => 'وقتی کار از ستون اول منتقل شد', + 'When task is moved to last column' => 'وقتی کار به ستون آخر منتقل شد', + 'Year(s)' => 'سال(ها)', + 'Project settings' => 'تنظیمات پروژه', + 'Automatically update the start date' => 'بصورت خودکار تاریخ شروع بروز رسانی شود', + 'iCal feed' => 'خوراک iCal', + 'Preferences' => 'اولویت ها', + 'Security' => 'امنیت', + 'Two factor authentication disabled' => 'احراز هویت دو مرحله ای غیر ÙØ¹Ø§Ù„ شده است', + 'Two factor authentication enabled' => 'احراز هویت دو مرحله ای ÙØ¹Ø§Ù„ است', + 'Unable to update this user.' => 'بروز رسانی این کاربر امکان پذیر نیست.', + 'There is no user management for personal 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' => 'وقتی دسته بندی تغییر کرد، کار را به ستونی دیگر منتقل Ú©Ù†', + 'Send a task by email to someone' => 'ارسال کار توسط پست الکترونیکی به یک شخص', + 'Reopen a task' => 'بازگشایی مجدد یک کار', + 'Notification' => 'آگاه سازی', + '%s moved the task #%d to the first swimlane' => 'کاربر %s کار #%d را به مسیر شنای اول منتقل کرد', + 'Swimlane' => 'مسیر شنا', + '%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.' => 'این گزارش حاوی همه اطلاعات کارها برای بازه زمانی داده شده است.', + 'Project activities for %s' => 'ÙØ¹Ø§Ù„یت های پروژه برای %s', + 'view the board on Kanboard' => 'نمایش برد در کان Ø¨ÙØ±Ø¯', + 'The task has been moved to the first swimlane' => 'کار به مسیر شنای اول منتقل شد', + 'The task has been moved to another swimlane:' => 'کار به مسیر شنای دیگر منتقل شد:', + 'New title: %s' => 'عنوان جدید: %s', + 'The task is not assigned anymore' => 'کار به هیچ کسی محول نشده', + 'New assignee: %s' => 'شخص محول شده جدید: %s', + 'There is no category now' => 'الان هیچگونه دسته بندی وجود ندارد', + 'New category: %s' => 'دسته بندی جدید: %s', + 'New color: %s' => 'رنگ جدید: %s', + 'New complexity: %d' => 'پیچیدگی جدید: %d', + 'The due date has been removed' => 'تاریخ سررسید حذ٠شد', + 'There is no description anymore' => 'دیگر شرحی وجود ندارد', + 'Recurrence settings has been modified' => 'تکرار تنظیمات اصلاح شد', + 'Time spent changed: %sh' => 'زمان صر٠شده تغییر کرد: %sh', + 'Time estimated changed: %sh' => 'زمان تخمینی تغییر کرد: %sh', + 'The field "%s" has 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:' => 'Ù…ÛŒ خواهم آگاه سازی ها را برای این موارد Ø¯Ø±ÛŒØ§ÙØª کنم:', + 'All tasks' => 'تمامی کارها', + 'Only for tasks assigned to me' => 'Ùقط برای کارهایی Ú©Ù‡ به من محول شده', + 'Only for tasks created by me' => 'Ùقط برای کارهایی Ú©Ù‡ توسط من ایجاد شده', + 'Only for tasks created by me and tasks assigned to me' => 'Ùقط برای کارهایی Ú©Ù‡ توسط من ایجاد شده Ùˆ کارهایی Ú©Ù‡ به من محول شده', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'جمع برای تمامی ستون ها', + 'You need at least 2 days of data to show the chart.' => 'برای نمایش نمودار شما نیاز به حداقل 2 روز داده دارید.', + '<15m' => 'زیر یک ربع', + '<30m' => 'زیر نیم ساعت', + 'Stop timer' => 'توق٠زمانسنج', + 'Start timer' => 'شروع زمانسنج', + 'My activity stream' => 'جریان ÙØ¹Ø§Ù„یت من', + 'Search tasks' => 'جستجوی کارها', + 'Reset filters' => 'بازنشانی Ùیلترها', + 'My tasks due tomorrow' => 'کارهای من Ø·ÛŒ ÙØ±Ø¯Ø§', + 'Tasks due today' => 'کارها Ø·ÛŒ امروز', + 'Tasks due tomorrow' => 'کارها Ø·ÛŒ ÙØ±Ø¯Ø§', + 'Tasks due yesterday' => 'کارها Ø·ÛŒ دیروز', + 'Closed tasks' => 'کارهای بسته شده', + 'Open tasks' => 'کارهای باز', + 'Not assigned' => 'محول نـشده', + 'View advanced search syntax' => 'نمایش دستور زبان جستجوی Ù¾ÛŒØ´Ø±ÙØªÙ‡', + 'Overview' => 'بررسی اجمالی', + 'Board/Calendar/List view' => 'نمای برد/تقویم/لیست', + 'Switch to the board view' => 'تغییر به نمای برد', + 'Switch to the list view' => 'تغییر به نمای لیست', + 'Go to the search/filter box' => 'برو به جعبه جستجو/Ùیلتر', + 'There is no activity yet.' => 'هنوز ÙØ¹Ø§Ù„یتی وجود ندارد.', + 'No tasks found.' => 'کاری پیدا نشد.', + 'Keyboard shortcut: "%s"' => 'میانبر ØµÙØ­Ù‡ کلید: "%s"', + 'List' => 'لیست', + 'Filter' => 'Ùیلتر', + 'Advanced search' => 'جستجوی Ù¾ÛŒØ´Ø±ÙØªÙ‡', + 'Example of query: ' => 'مثال پرس Ùˆ جو: ', + 'Search by project: ' => 'جستجو بر اساس پروژه: ', + 'Search by column: ' => 'جستجو بر اساس ستون: ', + 'Search by assignee: ' => 'جستجو بر اساس شخص محول شده: ', + 'Search by color: ' => 'جستجو بر اساس رنگ: ', + 'Search by category: ' => 'جستجو بر اساس دسته بندی: ', + 'Search by description: ' => 'جستجو بر اساس شرح: ', + 'Search by due date: ' => 'جستجو بر اساس تاریخ سررسید: ', + 'Average time spent in each column' => 'زمان صر٠شده میانگین در هر ستون', + 'Average time spent' => 'میانگین زمان صر٠شده', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'این نمودار زمان صر٠شده میانگین در هر ستون برای %d کار آخر را نشان Ù…ÛŒ دهد.', + 'Average Lead and Cycle time' => 'میانگین زمان Lead Ùˆ Cycle', + 'Average lead time: ' => 'میانگین lead time:‫ ', + 'Average cycle time: ' => 'میانگین cycle time:‫ ', + 'Cycle Time' => 'Cycle Time‫', + 'Lead Time' => 'Lead Time‫', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'این نمودار میانگین زمان Lead Ùˆ Cycle را برای %d کار آخر Ø·ÛŒ زمان را نشان Ù…ÛŒ دهد.', + 'Average time into each column' => 'میانگین زمانی برای هر ستون', + 'Lead and cycle time' => 'زمان Lead Ùˆ Cycle', + 'Lead time: ' => 'Lead time: ‫', + 'Cycle time: ' => 'Cycle time: ‫', + 'Time spent in each column' => 'زمان صر٠شده در هر ستون', + 'The lead time is the duration between the task creation and the completion.' => 'اصطلاح Lead time یعنی مدتی Ú©Ù‡ بین ایجاد کار Ùˆ اتمام کار سپری شده است.', + 'The cycle time is the duration between the start date and the completion.' => 'اصطلاح Cycle time یعنی مدتی Ú©Ù‡ از تاریخ شروع تا تاریخ اتمام سپری شده است.', + 'If the task is not closed the current time is used instead of the completion date.' => 'اگر کار بسته نشده باشد، زمان کنونی بجای زمان اتمام Ø§Ø³ØªÙØ§Ø¯Ù‡ شده است.', + 'Set the start date automatically' => 'تاریخ شروع بصورت خودکار تنظیم شود', + 'Edit Authentication' => 'ویرایش احراز هویت', + 'Remote user' => 'کاربر راه دور', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'کاربران راه دور گذرواژه خود را در پایگاه داده کان Ø¨ÙØ±Ø¯ ذخیره نمی کنند، بطور مثال: LDAP, حساب های Ú¯ÙˆÚ¯Ù„ Ùˆ گیتهاب.', + '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.' => 'هیچ پروژه مقصدی وجود ندارد.', + 'Trigger automatically subtask time tracking' => 'بصورت خودکار پیگیری زمان کار ÙØ±Ø¹ÛŒ را راه اندازی Ú©Ù†', + 'Include closed tasks in the cumulative flow diagram' => 'کارهای بسته شده نیز در جریان دیاگرام انباشته شامل شود', + 'Current swimlane: %s' => 'مسیر شنای ÙØ¹Ù„ÛŒ: %s', + 'Current column: %s' => 'ستون ÙØ¹Ù„ÛŒ: %s', + 'Current category: %s' => 'دسته بندی ÙØ¹Ù„ÛŒ: %s', + 'no category' => 'بدون دسته بندی', + 'Current assignee: %s' => 'شخص محول شده ÙØ¹Ù„ÛŒ: %s', + 'not assigned' => 'محول نشده', + 'Author:' => 'نویسنده:', + 'contributors' => 'شرکت کنندگان', + 'License:' => 'گواهینامه:', + 'License' => 'گواهینامه', + 'Enter the text below' => 'متن زیر را وارد کنید', + 'Start date:' => 'تاریخ شروع:', + 'Due date:' => 'تاریخ سررسید:', + 'People who are project managers' => 'اشخاصی Ú©Ù‡ مدیر پروژه هستند', + 'People who are project members' => 'اشخاصی Ú©Ù‡ عضو پروژه هستند', + 'NOK - Norwegian Krone' => 'NOK - کرون نروژ', + 'Show this column' => 'نمایش این ستون', + 'Hide this column' => 'مخÙÛŒ کردن این ستون', + 'End date' => 'تاریخ پایان', + 'Users overview' => 'بررسی اجمالی کاربران', + 'Members' => 'اعضاء', + 'Shared project' => 'پروژه مشترک', + 'Project managers' => 'مدیران پروژه', + 'Projects list' => 'لیست پروژه ها', + 'End date:' => 'تاریخ پایان:', + 'Change task color when using a specific task link' => 'رنگ کار وقتی Ú©Ù‡ از پیوند یک کار مشخص Ø§Ø³ØªÙØ§Ø¯Ù‡ Ù…ÛŒ شود تغییر داده شود', + 'Task link creation or modification' => 'ایجاد یا تغییر پیوند کار', + 'Milestone' => 'نقطه عطÙ', + 'Reset the search/filter box' => 'بازنشانی جعبه جستجو/Ùیلتر', + 'Documentation' => 'مستندات', + 'Author' => 'نویسنده', + 'Version' => 'نسخه', + 'Plugins' => 'پلاگین', + 'There is no plugin loaded.' => 'هیچ پلاگینی بارگیری نشده.', + 'My notifications' => 'آگاه سازی های من', + 'Custom filters' => 'Ùیلترهای Ø³ÙØ§Ø±Ø´ÛŒ', + 'Your custom filter has 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 has been updated successfully.' => 'Ùیلتر Ø³ÙØ§Ø±Ø´ÛŒ شما با موÙقیت بروز رسانی شد.', + 'Unable to update custom filter.' => 'بروز رسانی Ùیلتر Ø³ÙØ§Ø±Ø´ÛŒ امکان پذیر نیست.', + 'Web' => 'وب', + '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 بروز شد', + 'New task #%d: %s' => 'کار جدید #%d: %s', + 'Task updated #%d' => 'کار بروز شد #%d', + 'Task #%d closed' => 'کار #%d بسته شد', + 'Task #%d opened' => 'کار #%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 تغییر کرد', + '%d overdue tasks' => '%d کار عقب Ø§ÙØªØ§Ø¯Ù‡', + 'No notification.' => 'بدون آگاه سازی.', + '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:' => 'روش های آگاه سازی:', + 'Unable to read your file' => 'خواندن ÙØ§ÛŒÙ„ شما امکان پذیر نیست', + '%d task(s) have been imported successfully.' => 'تعداد %d عدد کار با موÙقیت درون ریزی شد.', + 'Nothing has been imported!' => 'هیچی چیزی درون ریزی نشد!', + 'Import users from CSV file' => 'درون ریزی کاربران از طریق ÙØ§ÛŒÙ„ CSV', + '%d user(s) have been imported successfully.' => 'تعداد %d کاربر با موÙقیت درون ریزی شدند.', + 'Comma' => 'کاما', + 'Semi-colon' => 'سمی کالن', + 'Tab' => 'تب', + 'Vertical bar' => 'تب عمودی', + '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)' => 'Ùیلتر Ø§ÙØ²ÙˆØ¯Ù‡ شود (بجای اینکه جایگزین شود)', + 'Append/Replace' => 'Ø§ÙØ²ÙˆØ¯Ù†/جایگزینی', + 'Append' => 'Ø§ÙØ²ÙˆØ¯Ù†', + 'Replace' => 'جایگزینی', + 'Import' => 'درون ریزی', + 'Change sorting' => 'تغییر مرتب سازی', + 'Tasks Importation' => 'برون ریزی کردن کارها', + 'Delimiter' => 'جدا ساز', + 'Enclosure' => 'محصور ساز', + 'CSV File' => 'ÙØ§ÛŒÙ„ CSV', + 'Instructions' => 'دستورالعمل ها', + 'Your file must use the predefined CSV format' => 'ÙØ§ÛŒÙ„ شما باید از ÙØ±Ù…ت از پیش تعری٠شده CSV Ø§Ø³ØªÙØ§Ø¯Ù‡ کند', + 'Your file must be encoded in UTF-8' => 'کدبندی ÙØ§ÛŒÙ„ شما باید 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' => 'تاریخ سررسید باید از استاندارد ISO بصورت YYYY-MM-DD پیروی کند.', + 'Download CSV template' => 'بارگیری قالب CSV', + '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' => 'کاربر %s یک ÙØ§ÛŒÙ„ جدید به کار %s پیوست کرد.', + 'Link type' => 'نوع پیوند', + 'Assign automatically a category based on a link' => 'اختصاص خودکار یک دسته بندی بر اساس یک پیوند', + 'BAM - Konvertible Mark' => 'BAM - مارک تبدیل پذیر بوسنی هرزگوین', + 'Assignee Username' => 'نام کاربری شخص محول شده', + 'Assignee Name' => 'نام شخص محول شده', + 'Groups' => 'گروه ها', + 'Members of %s' => 'اعضای %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"' => 'Ø§ÙØ²ÙˆØ¯Ù† عضو گروه به "%s"', + 'Group member added successfully.' => 'عضو گروه با موÙقیت Ø§ÙØ²ÙˆØ¯Ù‡ شد.', + 'Unable to add group member.' => 'Ø§ÙØ²ÙˆØ¯Ù† عضو گروه امکان پذیر نیست.', + 'Remove user from group "%s"' => 'حذ٠کاربر از گروه "%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' => 'حساب کاربری شما به مدت %d دقیقه مسدود شده', + 'Invalid captcha' => 'کد امنیتی کپچا نامعتبر است', + 'The name must be unique' => 'نام بایستی یکتا باشد', + 'View all groups' => 'نمایش همه گروه ها', + 'There is no user available.' => 'کاربری در دسترس نیست', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'واقعاً Ù…ÛŒ خواهید کاربر "%s" را از گروه "%s" حذ٠کنید؟', + 'There is no group.' => 'گروهی وجود ندارد.', + 'Add group member' => 'Ø§ÙØ²ÙˆÙ† عضو گروه', + 'Do you really want to remove this group: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این گروه را حذ٠کنید؟ : "%s"', + 'There is no user in this group.' => 'کاربری در این گروه نیست.', + 'Permissions' => 'مجوز ها', + 'Allowed Users' => 'کاربران مجاز', + 'No specific user has been allowed.' => 'به کاربری بصورت خاص اجازه داده نشده است.', + 'Role' => 'نقش', + 'Enter user name...' => 'نام کاربری را وارد کنید...', + 'Allowed Groups' => 'گروه های مجاز', + 'No group has been allowed.' => 'گروهی بصورت خاص اجازه داده نشده است.', + 'Group' => 'گروه', + 'Group Name' => 'نام گروه', + 'Enter group name...' => 'نام گروه را وارد کنید...', + 'Role:' => 'نقش:', + 'Project members' => 'اعضای پروژه', + '%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 بوده نام بری شدید.', + 'Estimated hours: ' => 'ساعت های تخمینی: ', + 'Actual hours: ' => 'ساعت های واقعی: ', + 'Hours Spent' => 'ساعت های صر٠شده', + 'Hours Estimated' => 'ساعت های تخمینی', + 'Estimated Time' => 'زمان تخمینی', + 'Actual Time' => 'زمان واقعی', + 'Estimated vs actual time' => 'مقایسه زمان تخمینی با زمان واقعی', + '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' => 'الگوریتم گذرواژه یکبارمصر٠مبتنی بر زمان', + 'Two-Factor Provider: ' => 'ÙØ±Ø§Ù‡Ù… کننده دو مرحله ای: ', + 'Disable two-factor authentication' => 'احراز هویت دو مرحله ای غیر ÙØ¹Ø§Ù„ گردد', + 'Enable two-factor authentication' => 'احراز هویت دو مرحله ای ÙØ¹Ø§Ù„ گردد', + 'There is no integration registered at the moment.' => 'تلÙیق ثبت شده در این لحظه وجود ندارد.', + 'Password Reset for Kanboard' => 'بازنشانی گذرواژه برای کان Ø¨ÙØ±Ø¯', + 'Forgot password?' => 'ÙØ±Ø§Ù…وشی گذرواژه؟', + 'Enable "Forget Password"' => 'ÙØ¹Ø§Ù„ کردن "ÙØ±Ø§Ù…وشی گذرواژه"', + 'Password Reset' => 'بازنشانی گذرواژه', + 'New password' => 'گذرواژه جدید', + 'Change Password' => 'تغییر گذرواژه', + 'To reset your password click on this link:' => 'برای بازنشانی گذرواژه خود روی پیوند زیر کلیک کنید:', + 'Last Password Reset' => 'آخرین بازنشانی گذرواژه', + 'The password has never been reinitialized.' => 'گذرواژه هرگز از نو مقداردهی نشده است.', + 'Creation' => 'ایجاد', + 'Expiration' => 'انقضاء', + 'Password reset history' => 'تاریخچه بازنشانی گذرواژه', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'تمامی کارهای ستون "%s" Ùˆ مسیر شنای "%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.' => '%d کار در ستون "%s" Ùˆ مسیر شنای "%s" بسته خواهند شد.', + 'Close all tasks in this column and this swimlane' => 'تمامی کارهای این ستون بسته شود', + '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.' => 'شناسه پروژه اختیاری است Ùˆ باید حتما از حرو٠انگلیسی یا عدد تشکیل شود. مثال MYPROJECT', + 'Project owner' => 'صاحب پروژه', + 'Personal projects do not have users and groups management.' => 'پروژه های خصوصی مدیریت کاربر Ùˆ گروه ندارند', + 'There is no project member.' => 'عضوی در پروژه وجود ندارد', + 'Priority' => 'اولویت', + 'Task priority' => 'اولویت کار', + 'General' => 'عمومی', + 'Dates' => 'تاریخ ها', + 'Default priority' => 'اولویت Ù¾ÛŒØ´ÙØ±Ø¶', + 'Lowest priority' => 'کمترین اولویت', + 'Highest priority' => 'بیشترین اولویت', + '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' => 'پیوست', + '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' => '%s ساعت', + 'Time spent:' => 'زمان صر٠شده:', + 'Created:' => 'ایجاد شده:', + 'Modified:' => 'تغییر ÛŒØ§ÙØªÙ‡:', + 'Completed:' => 'تکمیل شده:', + 'Started:' => 'شروع شده:', + 'Moved:' => 'منتقل شده:', + 'Task #%d' => 'کار #%d', + 'Time format' => 'ÙØ±Ù…ت زمان', + 'Start date: ' => 'تاریخ شروع: ', + 'End date: ' => 'تاریخ پایان: ', + 'New due date: ' => 'تاریخ سررسید جدید: ', + 'Start date changed: ' => 'تاریخ شروع تغییر کرده: ', + 'Disable personal projects' => 'غیر ÙØ¹Ø§Ù„ کردن پروژه های خصوصی', + 'Do you really want to remove this custom filter: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این Ùیلتر Ø³ÙØ§Ø±Ø´ÛŒ را حذ٠کنید؟ "%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.' => 'همه ÙØ§ÛŒÙ„ ها با موÙقیت بارگذاری شدند.', + 'The maximum allowed file size is %sB.' => 'بیشترین اندازه ÙØ§ÛŒÙ„ مجاز %sB است.', + 'Drag and drop your files here' => 'ÙØ§ÛŒÙ„ های خود را در اینجا کشیده Ùˆ رها کنید.', + 'choose files' => 'انتخاب ÙØ§ÛŒÙ„ ها', + 'View profile' => 'نمایش Ù¾Ø±ÙˆÙØ§ÛŒÙ„', + 'Two Factor' => 'دو مرحله ای', + 'Disable user' => 'غیر ÙØ¹Ø§Ù„ کردن کاربر', + 'Do you really want to disable this user: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این کاربر را غیر ÙØ¹Ø§Ù„ کنید؟ "%s"', + 'Enable user' => 'ÙØ¹Ø§Ù„ کردن کاربر', + 'Do you really want to enable this user: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این کاربر را ÙØ¹Ø§Ù„ کنید؟ "%s"', + 'Download' => 'بارگیری', + 'Uploaded: %s' => 'بارگذاری: %s', + 'Size: %s' => 'اندازه: %s', + 'Uploaded by %s' => 'بارگذاری شده توسط %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' => 'این مقدار باید از %d بزرگتر باشد', + 'Another swimlane with the same name exists in the project' => 'یک مسیر شنای دیگر با همین نام در پروژه وجود دارد', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'مثال: https://example.kanboard.org/ (برای تولید نشانی های کامل Ø§Ø³ØªÙØ§Ø¯Ù‡ شده)', + '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:', + 'PHP SAPI:' => 'رابط SAPI PHP:', + 'OS version:' => 'نسخه سیستم عامل:', + 'Database version:' => 'نسخه پایگاه داده:', + 'Browser:' => 'مرورگر:', + 'Task view' => 'نمای کار', + 'Edit task' => 'ویرایش کار', + 'Edit description' => 'ویرایش شرح', + 'New internal link' => 'لینک داخلی جدید', + 'Display list of keyboard shortcuts' => 'نمایش لیست از میانبرهای ØµÙØ­Ù‡ کلید', + '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.' => 'کان Ø¨ÙØ±Ø¯ شما طوری تنظیم نشده Ú©Ù‡ بتوان پلاگین ها را از رابط کاربری نصب کرد.', + 'There is no plugin available.' => 'پلاگینی در دسترس نیست.', + 'Install' => 'نصب', + 'Update' => 'بروز رسانی', + 'Up to date' => 'بروز', + 'Not available' => 'در دسترس نیست', + 'Remove plugin' => 'حذ٠پلاگین', + 'Do you really want to remove this plugin: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این پلاگین را حذ٠کنید؟ : "%s"', + 'Uninstall' => 'حذ٠نصب', + 'Listing' => 'لیست کردن', + 'Metadata' => 'متا دیتا', + 'Manage projects' => 'مدیریت پروژه ها', + 'Convert to task' => 'تبدیل به کار', + 'Convert sub-task to task' => 'تبدیل کار ÙØ±Ø¹ÛŒ به کار', + 'Do you really want to convert this sub-task to a task?' => 'واقعاً Ù…ÛŒ خواهید این کار ÙØ±Ø¹ÛŒ را به کار تبدیل کنید؟', + 'My task title' => 'عنوان کار من', + 'Enter one task by line.' => 'یک کار را در یک خط وارد کنید.', + 'Number of failed login:' => 'تعداد ورودهای ناموÙÙ‚:', + 'Account locked until:' => 'حساب Ù‚ÙÙ„ شده تا:', + 'Email settings' => 'تنظیمات پست الکترونیکی', + 'Email sender address' => 'آدرس ÙØ±Ø³ØªÙ†Ø¯Ù‡ پست الکترونیکی', + 'Email transport' => 'ترابری پست الکترونیکی', + 'Webhook token' => 'توکن Webhook', + '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 a 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 has 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', + '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)', + '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' => 'ایمیل های خروجی', + '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 دعوتنامه ارسال شد.', + '%d invitation was sent.' => 'تعداد %d دعوتنامه ارسال شد.', + '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' => 'این نام کاربری در حال حاضر Ø§Ø³ØªÙØ§Ø¯Ù‡ شده است', + 'Your profile must have a valid email address.' => 'Ù¾Ø±ÙˆÙØ§ÛŒÙ„ شما باید یک آدرس ایمیل معتبر داشته باشد', + 'TRL - Turkish Lira' => 'TRL - لیر ترکیه', + 'The project email is optional and could be used by several plugins.' => 'ایمیل پروژه اختیاری است Ùˆ Ù…ÛŒ تواند توسط چندین پلاگین مورد Ø§Ø³ØªÙØ§Ø¯Ù‡ قرار گیرد.', + 'The project email must be unique across all projects' => 'ایمیل پروژه باید بین همه پروژه ها یکتا باشد', + 'The email configuration has been disabled by the administrator.' => 'تنظیمات ایمیل توسط مدیر سیستم غیر ÙØ¹Ø§Ù„ شده است', + 'Close this project' => 'بستن این پروژه', + 'Open this project' => 'باز کردن این پروژه', + 'Close a project' => 'بستن یک پروژه', + 'Do you really want to close this project: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این پروژه را ببندید؟ : "%s"', + 'Reopen a project' => 'بازگشایی مجدد یک پروژه', + 'Do you really want to reopen this project: "%s"?' => 'واقعاً Ù…ÛŒ خواهید این پروژه را مجددا بازگشایی کنید؟ : "%s"', + 'This project is open' => 'این پروژه باز است', + 'This project is closed' => 'این پروژه بسته است', + 'Unable to upload files, check the permissions of your data folder.' => 'بارگذاری ÙØ§ÛŒÙ„ امکان پذیر نیست، مجوزهای پوشه data را بررسی کنید.', + 'Another category with the same name exists in this project' => 'یک دسته بندی دیگر با همین نام در این پروژه وجود دارد', + 'Comment sent by email successfully.' => 'نظر با موÙقیت توسط ایمیل ارسال شد.', + 'Sent by email to "%s" (%s)' => 'توسط ایمیل ارسال شد به "%s" (%s)', + 'Unable to read uploaded file.' => 'خواندن ÙØ§ÛŒÙ„ بارگذاری شده امکان پذیر نیست.', + 'Database uploaded successfully.' => 'پایگاه داده با موÙقیت بارگذاری شد.', + 'Task sent by email successfully.' => 'کار با موÙقیت توسط ایمیل ارسال شد.', + 'There is no category in this project.' => 'هیچ گونه دسته بندی در این پروژه وجود ندارد.', + 'Send by email' => 'ارسال توسط ایمیل', + 'Create and send a comment by email' => 'ایجاد Ùˆ ارسال یک نظر توسط ایمیل', + 'Subject' => 'موضوع', + 'Upload the database' => 'بارگذاری پایگاه داده', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Ù…ÛŒ توانید پایگاه داده Sqlite Ú©Ù‡ قبلا بارگیری کردید را بارگذاری کنید (با ÙØ±Ù…ت Gzip)', + 'Database file' => 'ÙØ§ÛŒÙ„ پایگاه داده', + 'Upload' => 'بارگذاری', + 'Your project must have at least one active swimlane.' => 'پروژه شما باید حداقل یک مسیر شنای ÙØ¹Ø§Ù„ داشته باشد', + 'Project: %s' => 'پروژه: %s', + 'Automatic action not found: "%s"' => 'عمل خودکار ÛŒØ§ÙØª نشد: %s', + '%d projects' => '%d پروژه', + '%d project' => '%d پروژه', + 'There is no project.' => 'هیچ پروژه ای وجود ندارد', + 'Sort' => 'مرتب سازی', + 'Project ID' => 'شناسه پروژه', + 'Project name' => 'نام پروژه', + 'Public' => 'عمومی', + 'Personal' => 'خصوصی', + '%d tasks' => '%d کار', + '%d task' => '%d کار', + 'Task ID' => 'شناسه کار', + 'Assign automatically a color when due date is expired' => 'وقتی تاریخ سررسید منقضی شد، بصورت خودکار یک رنگ به آن اختصاص بده', + 'Total score in this column across all swimlanes' => 'امتیاز Ú©Ù„ در این ستون بین تمامی مسیرهای شنا', + 'HRK - Kuna' => 'HRK - کونای کرواسی', + 'ARS - Argentine Peso' => 'ARS - پزو آرژانتین', + 'COP - Colombian Peso' => 'COP - پزو کلمبیا', + '%d groups' => '%d گروه', + '%d group' => '%d گروه', + 'Group ID' => 'شناسه گروه', + 'External ID' => 'شناسه خارجی', + '%d users' => '%d کاربر', + '%d user' => '%d کاربر', + 'Hide subtasks' => 'مخÙÛŒ کردن کارهای ÙØ±Ø¹ÛŒ', + 'Show subtasks' => 'نمایش کارهای ÙØ±Ø¹ÛŒ', + 'Authentication Parameters' => 'پارامتر های احراز هویت', + 'API Access' => 'دسترسی API', + 'No users found.' => 'کاربری ÛŒØ§ÙØª نشد.', + 'User ID' => 'شناسه کاربر', + 'Notifications are activated' => 'آگاه سازی ها ÙØ¹Ø§Ù„ شدند', + 'Notifications are disabled' => 'آگاه سازی ها غیر ÙØ¹Ø§Ù„ شدند', + 'User disabled' => 'کاربر ØºÛŒØ±ÙØ¹Ø§Ù„ شد', + '%d notifications' => '%d آگاه سازی', + '%d notification' => '%d آگاه سازی', + 'There is no external integration installed.' => 'هیچ تلÙیق خارجی نصب نشده است.', + 'You are not allowed to update tasks assigned to someone else.' => 'شما اجازه بروز رسانی کارهایی Ú©Ù‡ به شخص دیگر محول شده را ندارید.', + 'You are not allowed to change the assignee.' => 'شما اجازه تغییر شخص محول شده را ندارید.', + 'Task suppression is not permitted' => 'زدودن کار مجاز نیست', + 'Changing assignee is not permitted' => 'تغییر شخص محول شده مجاز نیست', + 'Update only assigned tasks is permitted' => 'Ùقط بروز رسانی کارهای محول شده مجاز است', + 'Only for tasks assigned to the current user' => 'Ùقط برای کارهایی Ú©Ù‡ به کاربر جاری محول شده', + 'My projects' => 'پروژه های من', + 'You are not a member of any project.' => 'شما عضو هیچ پروژه ای نمی باشید.', + 'My subtasks' => 'کارهای ÙØ±Ø¹ÛŒ من', + '%d subtasks' => '%d کار ÙØ±Ø¹ÛŒ', + '%d subtask' => '%d کار ÙØ±Ø¹ÛŒ', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'تنها انتقال کار بین ستون هایی مجاز است Ú©Ù‡ آن کار به کاربر جاری محول شده باشد', + '[DUPLICATE]' => '[تکراری]', + 'DKK - Danish Krona' => 'DKK - کرون دانمارک', + 'Remove user from group' => 'حذ٠کاربر از گروه', + 'Assign the task to its creator' => 'محول کردن کار به ایجاد کننده آن', + 'This task was sent by email to "%s" with subject "%s".' => 'این کار توسط ایمیل به "%s" با موضوع "%s" ارسال شد.', + 'Predefined Email Subjects' => 'موضوعات ایمیل از پیش تعری٠شده', + 'Write one subject by line.' => 'در هر خط یک موضوع بنویسید.', + 'Create another link' => 'ایجاد پیوندی دیگر', + 'BRL - Brazilian Real' => 'BRL - رال برزیل', + 'Add a new Kanboard task' => 'ایجاد یک کار جدید کان Ø¨ÙØ±Ø¯', + 'Subtask not started' => 'کار ÙØ±Ø¹ÛŒ شروع نشده', + 'Subtask currently in progress' => 'کار ÙØ±Ø¹ÛŒ در حال حاضر در حال انجام است', + 'Subtask completed' => 'کار ÙØ±Ø¹ÛŒ تکمیل شده', + 'Subtask added successfully.' => 'کار ÙØ±Ø¹ÛŒ با موÙقیت Ø§ÙØ²ÙˆØ¯Ù‡ شد.', + '%d subtasks added successfully.' => '%d کار ÙØ±Ø¹ÛŒ با موÙقیت Ø§ÙØ²ÙˆØ¯Ù‡ شد.', + 'Enter one subtask by line.' => 'در هر خط یک کار ÙØ±Ø¹ÛŒ وارد کنید.', + 'Predefined Contents' => 'محتوای از پیش تعری٠شده', + 'Predefined contents' => 'محتوای از پیش تعری٠شده', + 'Predefined Task Description' => 'شرح کار از پیش تعری٠شده', + 'Do you really want to remove this template? "%s"' => 'آیا واقعاً Ù…ÛŒ خواهید این قالب را حذ٠کنید؟ "%s"', + 'Add predefined task description' => 'Ø§ÙØ²ÙˆØ¯Ù† شرح کار از پیش تعری٠شده', + 'Predefined Task Descriptions' => 'شرح کار از پیش تعری٠شده', + 'Template created successfully.' => 'قالب با موÙقیت ایجاد شد.', + 'Unable to create this template.' => 'ایجاد این قالب امکان پذیر نیست.', + 'Template updated successfully.' => 'قالب با موÙقیت بروز شد.', + 'Unable to update this template.' => 'بروز رسانی این قالب امکان پذیر نیست.', + 'Template removed successfully.' => 'قالب با موÙقیت حذ٠شد.', + 'Unable to remove this template.' => 'حذ٠این قالب امکان پذیر نیست.', + 'Template for the task description' => 'قالب برای این شرح کار', + 'The start date is greater than the end date' => 'تاریخ شروع از تاریخ پایان جلوتر است', + 'Tags must be separated by a comma' => 'برچسب ها باید با کاما از هم جدا شوند', + 'Only the task title is required' => 'تنها عنوان کار الزامی است', + 'Creator Username' => 'نام کاربری ایجاد کننده', + 'Color Name' => 'نام رنگ', + 'Column Name' => 'نام ستون', + 'Swimlane Name' => 'نام مسیر شنا', + 'Time Estimated' => 'زمان تخمینی', + 'Time Spent' => 'زمان سپری شده', + 'External Link' => 'لینک خارجی', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'این ویژگی خوراک iCalØŒ خوراک RSS Ùˆ نمای عمومی برد را ÙØ¹Ø§Ù„ Ù…ÛŒ کند.', + 'Stop the timer of all subtasks when moving a task to another column' => 'زمان سنج همه کارهای ÙØ±Ø¹ÛŒ را وقتی Ú©Ù‡ کار به ستون دیگری منتقل Ù…ÛŒ شود متوق٠کن', + 'Subtask Title' => 'عنوان کار ÙØ±Ø¹ÛŒ', + 'Add a subtask and activate the timer when moving a task to another column' => 'وقتی کار به ستون دیگری منتقل Ù…ÛŒ شود، یک کار ÙØ±Ø¹ÛŒ به آن Ø§ÙØ²ÙˆØ¯Ù‡ Ùˆ زمان سنج ÙØ¹Ø§Ù„ شود', + 'days' => 'روز', + 'minutes' => 'دقیقه', + 'seconds' => 'ثانیه', + 'Assign automatically a color when preset start date is reached' => 'هنگامی Ú©Ù‡ تاریخ شروع از قبل تنظیم شده ÙØ±Ø§ برسد، بصورت خودکار یک رنگ به آن اختصاص بده', + 'Move the task to another column once a predefined start date is reached' => 'هنگامی Ú©Ù‡ تاریخ شروع از پیش تعری٠شده ای ÙØ±Ø§ رسید، کار را به ستون دیگری منتقل Ú©Ù†', + 'This task is now linked to the task %s with the relation "%s"' => 'این کار اکنون به کار %s با ارتباط "%s" پیوند خورده است.', + 'The link with the relation "%s" to the task %s has been removed' => 'پیوند با ارتباط "%s" به کار %s حذ٠شد.', + 'Custom Filter:' => 'Ùیلتر Ø³ÙØ§Ø±Ø´ÛŒ:', + 'Unable to find this group.' => 'ÛŒØ§ÙØªÙ† این گروه امکان پذیر نیست.', + '%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 moved the task #%d to the swimlane "%s"' => 'کاربر %s کار #%d را به مسیر شنای "%s" منتقل کرد', + '%sh spent' => '%sh سپری شده', + '%sh estimated' => '%sh تخمین زده شده', + 'Select All' => 'انتخاب همه', + 'Unselect All' => 'عدم انتخاب همه', + 'Apply action' => 'انجام عمل', + 'Move selected tasks to another column or swimlane' => 'کارهای انتخاب شده به ستون دیگری منتقل شوند', + 'Edit tasks in bulk' => 'ویرایش کارها بصورت دسته ای', + 'Choose the properties that you would like to change for the selected tasks.' => 'مشخصاتی Ú©Ù‡ Ù…ÛŒ خواهید برای کارهای انتخاب شده تغییر کنند را انتخاب کنید.', + 'Configure this project' => 'پیکربندی این پروژه', + 'Start now' => 'اکنون شروع شود', + '%s removed a file from the task #%d' => 'کاربر %s یک ÙØ§ÛŒÙ„ را از کار #%d حذ٠کرد', + 'Attachment removed from task #%d: %s' => 'پیوست از کار #%d حذ٠شد: %s', + 'No color' => 'بدون رنگ', + 'Attachment removed "%s"' => 'پیوست حذ٠شد "%s"', + '%s removed a file from the task %s' => 'کاربر %s یک ÙØ§ÛŒÙ„ را از کار %s حذ٠کرد', + 'Move the task to another swimlane when assigned to a user' => 'هنگامی Ú©Ù‡ کاری به یک کاربر محول شد، آن کار را به مسیر شنای دیگر منتقل Ú©Ù†', + 'Destination swimlane' => 'مسیر شنای مقصد', + 'Assign a category when the task is moved to a specific swimlane' => 'هنگامی Ú©Ù‡ کار به یک مسیر شنای مشخص منتقل شد، یک دسته بندی به آن اختصاص یابد', + 'Move the task to another swimlane when the category is changed' => 'هنگامی Ú©Ù‡ دسته بندی تغییر کرد، کار را به مسیر شنایی دیگر منتقل Ú©Ù†', + 'Reorder this column by priority (ASC)' => 'مرتب سازی این ستون بر اساس اولویت (صعودی)', + 'Reorder this column by priority (DESC)' => 'مرتب سازی این ستون بر اساس اولویت (نزولی)', + 'Reorder this column by assignee and priority (ASC)' => 'مرتب سازی این ستون بر اساس شخص محول شده Ùˆ اولویت (صعودی)', + 'Reorder this column by assignee and priority (DESC)' => 'مرتب سازی این ستون بر اساس شخص محول شده Ùˆ اولویت (نزولی)', + 'Reorder this column by assignee (A-Z)' => 'مرتب سازی این ستون بر اساس شخص محول شده (Ø­Ø±ÙˆÙ Ø§Ù„ÙØ¨Ø§ صعودی)', + 'Reorder this column by assignee (Z-A)' => 'مرتب سازی این ستون بر اساس شخص محول شده (Ø­Ø±ÙˆÙ Ø§Ù„ÙØ¨Ø§ نزولی)', + 'Reorder this column by due date (ASC)' => 'مرتب سازی این ستون بر اساس تاریخ سررسید (صعودی)', + 'Reorder this column by due date (DESC)' => 'مرتب سازی این ستون بر اساس تاریخ سررسید (نزولی)', + 'Reorder this column by id (ASC)' => 'مرتب‌سازی این ستون بر اساس شناسه (صعودی)', + 'Reorder this column by id (DESC)' => 'مرتب‌سازی این ستون بر اساس شناسه (نزولی)', + '%s moved the task #%d "%s" to the project "%s"' => 'کاربر %s کار #%d "%s" را به پروژه "%s" منتقل کرد.', + 'Task #%d "%s" has been moved to the project "%s"' => 'کار #%d "%s" به پروژه "%s" منتقل شد.', + 'Move the task to another column when the due date is less than a certain number of days' => 'هنگامی Ú©Ù‡ تاریخ سررسید کمتر از تعداد روز مشخصی شد، کار را به ستونی دیگر منتقل Ú©Ù†', + 'Automatically update the start date when the task is moved away from a specific column' => 'هنگامی Ú©Ù‡ کار از ستونی مشخص منتقل شد، تاریخ شروع کار را بصورت خودکار بروز رسانی Ú©Ù†', + 'HTTP Client:' => 'کلاینت HTTP:', + 'Assigned' => 'محول شده', + 'Task limits apply to each swimlane individually' => 'محدودیت های کار به هر مسیر شنا بصورت مجزا اعمال Ù…ÛŒ گردد', + 'Column task limits apply to each swimlane individually' => 'محدودیت های کارهای هر ستون به مسیرهای شنای مجزا اعمال Ù…ÛŒ گردد', + 'Column task limits are applied to each swimlane individually' => 'محدودیت های کارهای هر ستون برای هر مسیر شنا مجزا است', + 'Column task limits are applied across swimlanes' => 'محدودیت های کارهای هر ستون برای همه مسیرهای شنا اعمال شده است', + 'Task limit: ' => 'محدودیت کار: ', + 'Change to global tag' => 'تبدیل به برچسب جامع', + 'Do you really want to make the tag "%s" global?' => 'واقعا Ù…ÛŒ خواهید برچسب "%s" را تبدیل به برچسب جامع کنید؟', + 'Enable global tags for this project' => 'برچسب های جامع برای این پروژه ÙØ¹Ø§Ù„ گردند', + 'Group membership(s):' => 'عضویت(های) گروهی:', + '%s is a member of the following group(s): %s' => '%s عضوی از این گروه(ها) است: %s', + '%d/%d group(s) shown' => '%d/%d گروه(ها) نمایش داده شده است', + 'Subtask creation or modification' => 'ایجاد یا تغییر کار ÙØ±Ø¹ÛŒ', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'وقتی Ú©Ù‡ کار به یک مسیر شنای خاص منتقل شد، آن را به یک کاربر خاص اختصاص بده', + 'Comment' => 'توضیح', + 'Collapse vertically' => 'جمع شدن عمودی', + 'Expand vertically' => 'باز شدن عمودی', + 'MXN - Mexican Peso' => 'MXN - پزوی مکزیک', + 'Estimated vs actual time per column' => 'زمان تخمینی در قیاس با واقعی برای هر ستون', + 'HUF - Hungarian Forint' => 'HUF - Ùورینت مجراستان', + 'XBT - Bitcoin' => 'XBT - بیت کوین', + 'You must select a file to upload as your avatar!' => 'برای بارگذاری تصویر نمایه تان باید ÙØ§ÛŒÙ„ÛŒ انتخاب کنید!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'ÙØ§ÛŒÙ„ÛŒ Ú©Ù‡ بارگذاری کردید یک تصویر معتبر نیست! (Ùقط *.gif, *.jpg, *.jpeg Ùˆ *.png مجاز هستند!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'تاریخ سررسید به‌طور خودکار هنگام جابجایی وظیÙÙ‡ از یک ستون خاص تنظیم شود', + 'No other projects found.' => 'هیچ پروژه دیگری ÛŒØ§ÙØª نشد.', + 'Tasks copied successfully.' => 'وظای٠با موÙقیت Ú©Ù¾ÛŒ شدند.', + 'Unable to copy tasks.' => 'Ú©Ù¾ÛŒ کردن وظای٠امکان‌پذیر نیست.', + 'Theme' => 'پوسته', + 'Theme:' => 'پوسته:', + 'Light theme' => 'پوسته روشن', + 'Dark theme' => 'پوسته تیره', + 'Automatic theme - Sync with system' => 'پوسته خودکار - هماهنگ با سیستم', + 'Application managers or more' => 'مدیران برنامه یا بالاتر', + 'Administrators' => 'مدیران', + 'Visibility:' => 'قابلیت مشاهده:', + 'Standard users' => 'کاربران عادی', + 'Visibility is required' => 'مشاهده‌پذیری لازم است', + 'The visibility should be an app role' => 'مشاهده‌پذیری باید یک نقش برنامه باشد', + 'Reply' => 'پاسخ', + '%s wrote: ' => '%s نوشت:', + 'Number of visible tasks in this column and swimlane' => 'تعداد وظای٠قابل مشاهده در این ستون Ùˆ ردی٠شناور', + 'Number of tasks in this swimlane' => 'تعداد وظای٠در این ردی٠شناور', + 'Unable to find another subtask in progress, you can close this window.' => 'زیر‌وظیÙÙ‡ دیگری در حال انجام ÛŒØ§ÙØª نشد، می‌توانید این پنجره را ببندید.', + 'This theme is invalid' => 'این پوسته نامعتبر است', + 'This role is invalid' => 'این نقش نامعتبر است', + 'This timezone is invalid' => 'این منطقه زمانی نامعتبر است', + 'This language is invalid' => 'این زبان نامعتبر است', + 'This URL is invalid' => 'این نشانی اینترنتی نامعتبر است', + 'Date format invalid' => 'ÙØ±Ù…ت تاریخ نامعتبر است', + 'Time format invalid' => 'ÙØ±Ù…ت زمان نامعتبر است', + 'Invalid Mail transport' => 'انتقال ایمیل نامعتبر است', + 'Color invalid' => 'رنگ نامعتبر است', + 'This value must be greater or equal to %d' => 'این مقدار باید بزرگتر یا مساوی %d باشد', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Ø§ÙØ²ÙˆØ¯Ù† BOM در ابتدای ÙØ§ÛŒÙ„ (مورد نیاز برای Microsoft Excel)', + 'Just add these tag(s)' => 'Ùقط این برچسب(ها) را اضاÙÙ‡ کنید', + 'Remove internal link(s)' => 'پیوند(های) داخلی را حذ٠کنید', + 'Import tasks from another project' => 'وارد کردن وظای٠از پروژه‌ای دیگر', + 'Select the project to copy tasks from' => 'پروژه‌ای را Ú©Ù‡ می‌خواهید وظای٠از آن Ú©Ù¾ÛŒ شود انتخاب کنید', + 'The total maximum allowed attachments size is %sB.' => 'حداکثر حجم مجاز پیوست‌ها %sB است.', + 'Add attachments' => 'Ø§ÙØ²ÙˆØ¯Ù† پیوست‌ها', + 'Task #%d "%s" is overdue' => 'کار #%d "%s" از موعد گذشته است', + 'Enable notifications by default for all new users' => 'به‌طور Ù¾ÛŒØ´â€ŒÙØ±Ø¶ آگاه‌سازی‌ها را برای همه کاربران جدید ÙØ¹Ø§Ù„ کنید', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'اگر مسئول به‌صورت دستی تعیین نشده باشد، برای ستون‌های مشخص، کار را به ایجادکنندهٔ آن واگذار Ú©Ù†', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'اگر کاربری تعیین نشده باشد، هنگام تغییر ستون به ستون مشخص‌شده، کار را به کاربر واردشده اختصاص بده', +]; diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php new file mode 100644 index 0000000..a3e85a3 --- /dev/null +++ b/app/Locale/fi_FI/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Ei mikään', + 'Edit' => 'Muokkaa', + 'Remove' => 'Poista', + 'Yes' => 'Kyllä', + 'No' => 'Ei', + 'cancel' => 'peruuta', + 'or' => 'tai', + 'Yellow' => 'Keltainen', + 'Blue' => 'Sininen', + 'Green' => 'Vihreä', + 'Purple' => 'Violetti', + 'Red' => 'Punainen', + 'Orange' => 'Oranssi', + 'Grey' => 'Harmaa', + 'Brown' => 'Ruskea', + 'Deep Orange' => 'Tumma oranssi', + 'Dark Grey' => 'Tummanharmaa', + 'Pink' => 'Pinkki', + 'Teal' => 'Sinivihreä', + 'Cyan' => 'Syaani', + 'Lime' => 'Limetin vihreä', + 'Light Green' => 'Vaaleanvihreä', + 'Amber' => 'Keltainen', + 'Save' => 'Tallenna', + 'Login' => 'Sisäänkirjautuminen', + 'Official website:' => 'Virallinen verkkosivu:', + 'Unassigned' => 'Ei suorittajaa', + 'View this task' => 'Näytä tämä tehtävä', + 'Remove user' => 'Poista käyttäjä', + 'Do you really want to remove this user: "%s"?' => 'Oletko varma että haluat poistaa käyttäjän "%s"?', + 'All users' => 'Kaikki käyttäjät', + 'Username' => 'Käyttäjänimi', + 'Password' => 'Salasana', + 'Administrator' => 'Ylläpitäjä', + 'Sign in' => 'Kirjaudu sisään', + 'Users' => 'Käyttäjät', + 'Forbidden' => 'Estetty', + 'Access Forbidden' => 'Pääsy estetty', + 'Edit user' => 'Muokkaa käyttäjää', + 'Logout' => 'Kirjaudu ulos', + 'Bad username or password' => 'Väärä käyttäjätunnus tai salasana', + 'Edit project' => 'Muokkaa projektia', + 'Name' => 'Nimi', + 'Projects' => 'Projektit', + 'No project' => 'Ei projektia', + 'Project' => 'Projekti', + 'Status' => 'Status', + 'Tasks' => 'Tehtävät', + 'Board' => 'Taulu', + 'Actions' => 'Toiminnot', + 'Inactive' => 'Ei aktiivinen', + 'Active' => 'Aktiivinen', + 'Unable to update this board.' => 'Taulun muuttaminen ei onnistunut.', + '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"', + 'Add a new column' => 'Lisää uusi sarake', + 'Title' => 'Nimi', + 'Assigned to %s' => 'Tekijä: %s', + 'Remove a column' => 'Poista sarake', + 'Unable to remove this column.' => 'Sarakkeen poistaminen ei onnistunut.', + 'Do you really want to remove this column: "%s"?' => 'Haluatko varmasti poistaa sarakkeen "%s"?', + 'Settings' => 'Asetukset', + 'Application settings' => 'Ohjelman asetukset', + 'Language' => 'Kieli', + 'Webhook token:' => 'Webhooks avain:', + 'API token:' => 'API-tunnus:', + 'Database size:' => 'Tietokannan koko:', + 'Download the database' => 'Lataa tietokanta', + 'Optimize the database' => 'Optimoi tietokanta', + '(VACUUM command)' => '(VACUUM-komento)', + '(Gzip compressed Sqlite file)' => '(Gzip-pakattu Sqlite-tiedosto)', + 'Close a task' => 'Sulje tehtävä', + 'Column' => 'Sarake', + 'Color' => 'Väri', + 'Assignee' => 'Suorittaja', + 'Create another task' => 'Luo toinen tehtävä', + 'New task' => 'Uusi tehtävä', + 'Open a task' => 'Avaa tehtävä', + 'Do you really want to open this task: "%s"?' => 'Haluatko varmasti avata tehtävän: "%s"?', + 'Back to the board' => 'Takaisin tauluun', + 'There is nobody assigned' => 'Ei suorittajaa', + 'Column on the board:' => 'Sarake taululla: ', + 'Close this task' => 'Sulje tämä tehtävä', + 'Open this task' => 'Avaa tämä tehtävä', + 'There is no description.' => 'Ei kuvausta.', + 'Add a new task' => 'Lisää uusi tehtävä', + 'The username is required' => 'Käyttäjätunnut vaaditaan', + 'The maximum length is %d characters' => 'Maksimipituus on %d merkkiä', + 'The minimum length is %d characters' => 'Vähimmäispituus on %d merkkiä', + 'The password is required' => 'Salasana vaaditaan', + 'This value must be an integer' => 'Tämän arvon täytyy olla numero', + 'The username must be unique' => 'Käyttäjänimi täytyy olla uniikki', + 'The user id is required' => 'Käyttäjän id on pakollinen', + 'Passwords don\'t match' => 'Salasanat eivät täsmää', + 'The confirmation is required' => 'Varmistus vaaditaan', + 'The project is required' => 'Projekti on pakollinen', + 'The id is required' => 'ID vaaditaan', + 'The project id is required' => 'Projektin ID on pakollinen', + 'The project name is required' => 'Projektin nimi on pakollinen', + 'The title is required' => 'Otsikko vaaditaan', + 'Settings saved successfully.' => 'Asetukset tallennettu onnistuneesti.', + 'Unable to save your settings.' => 'Asetusten tallentaminen epäonnistui.', + 'Database optimization done.' => 'Tietokannan optimointi suoritettu.', + 'Your project has been created successfully.' => 'Projekti luotiin onnistuneesti.', + 'Unable to create your project.' => 'Projektin luominen epäonnistui.', + 'Project updated successfully.' => 'Projekti päivitettiin onnistuneesti.', + 'Unable to update this project.' => 'Projektin muuttaminen epäonnistui.', + 'Unable to remove this project.' => 'Projektin poistaminen epäonnistui.', + 'Project removed successfully.' => 'Projekti poistettiin onnistuneesti.', + 'Project activated successfully.' => 'Projekti aktivoitiin onnistuneesti.', + 'Unable to activate this project.' => 'Projektin aktivoiminen epäonnistui.', + 'Project disabled successfully.' => 'Projektin disabloiminen onnistui.', + 'Unable to disable this project.' => 'Projektin disabloiminen epäonnistui.', + 'Unable to open this task.' => 'Tehtävän avaus epäonnistui.', + 'Task opened successfully.' => 'Tehtävä avattiin onnistuneesti.', + 'Unable to close this task.' => 'Tehtävän sulkeminen epäonnistui.', + 'Task closed successfully.' => 'Tehtävä suljettiin onnistuneesti.', + 'Unable to update your task.' => 'Tehtävän muokkaaminen epäonnistui.', + 'Task updated successfully.' => 'Tehtävä päivitettiin onnistuneesti.', + 'Unable to create your task.' => 'Tehtävän luominen epäonnistui.', + 'Task created successfully.' => 'Tehtävä luotiin onnistuneesti.', + 'User created successfully.' => 'Käyttäjä lisättiin onnistuneesti.', + 'Unable to create your user.' => 'Käyttäjän lisäys epäonnistui.', + 'User updated successfully.' => 'Käyttäjätietojen päivitys onnistui.', + 'User removed successfully.' => 'Käyttäjä poistettiin onnistuneesti.', + 'Unable to remove this user.' => 'Käyttäjän poistaminen epäonnistui.', + 'Board updated successfully.' => 'Taulu päivitettiin onnistuneesti.', + 'Ready' => 'Valmis', + 'Backlog' => 'Tehtäväjono', + 'Work in progress' => 'Työnalla', + 'Done' => 'Tehty', + 'Application version:' => 'Ohjelman versio:', + 'Id' => 'Id', + 'Public link' => 'Julkinen linkki', + 'Timezone' => 'Aikavyöhyke', + 'Sorry, I didn\'t find this information in my database!' => 'Anteeksi, en löytänyt tätä tietoa tietokannastani', + 'Page not found' => 'Sivua ei löydy', + 'Complexity' => 'Monimutkaisuus', + 'Task limit' => 'Tehtävien maksimimäärä', + 'Task count' => 'Tehtävien määrä', + 'User' => 'Käyttäjät', + 'Comments' => 'Kommentit', + 'Comment is required' => 'Kommentti vaaditaan', + 'Comment added successfully.' => 'Kommentti lisättiin onnistuneesti.', + 'Unable to create your comment.' => 'Kommentin lisäys epäonnistui.', + 'Due Date' => 'Deadline', + 'Invalid date' => 'Virheellinen päiväys', + 'Automatic actions' => 'Automaattiset toiminnot', + 'Your automatic action has been created successfully.' => 'Toiminto suoritettiin onnistuneesti.', + 'Unable to create your automatic action.' => 'Automaattisen toiminnon luominen epäonnistui.', + 'Remove an action' => 'Poista toiminto', + 'Unable to remove this action.' => 'Toiminnon poistaminen epäonnistui.', + 'Action removed successfully.' => 'Toiminto poistettiin onnistuneesti.', + 'Automatic actions for the project "%s"' => 'Automaattiset toiminnot projektille "%s"', + 'Add an action' => 'Lisää toiminto', + 'Event name' => 'Tapahtuman nimi', + 'Action' => 'Toiminto', + 'Event' => 'Tapahtuma', + 'When the selected event occurs execute the corresponding action.' => 'Kun valittu tapahtuma tapahtuu, suorita vastaava toiminto.', + 'Next step' => 'Seuraava vaihe', + 'Define action parameters' => 'Määrittele toiminnon parametrit', + 'Do you really want to remove this action: "%s"?' => 'Oletko varma että haluat poistaa toiminnon "%s"?', + 'Remove an automatic action' => 'Poista automaattintn toiminto', + 'Assign the task to a specific user' => 'Osoita tehtävä käyttäjälle', + 'Assign the task to the person who does the action' => 'Määritä suorittaja tehtävälle', + 'Duplicate the task to another project' => 'Monista tehtävä toiselle projektille', + 'Move a task to another column' => 'Siirrä tehtävä toiseen sarakkeeseen', + 'Task modification' => 'Tehtävän muokkaus', + 'Task creation' => 'Tehtävän luominen', + 'Closing a task' => 'Tehtävää suljetaan', + 'Assign a color to a specific user' => 'Valitse väri käyttäjälle', + 'Position' => 'Positio', + 'Duplicate to project' => 'Kopioi toiseen projektiin', + 'Duplicate' => 'Monista', + 'Link' => 'Linkki', + 'Comment updated successfully.' => 'Kommentti päivitettiin onnistuneesti.', + 'Unable to update your comment.' => 'Kommentin päivitys epäonnistui.', + 'Remove a comment' => 'Poista kommentti', + 'Comment removed successfully.' => 'Kommentti poistettiin onnistuneesti.', + 'Unable to remove this comment.' => 'Kommentin poistaminen epäonnistui.', + 'Do you really want to remove this comment?' => 'Haluatko varmasti poistaa tämän kommentin?', + 'Current password for the user "%s"' => 'Käyttäjän "%s" salasana', + 'The current password is required' => 'Salasana vaaditaan', + 'Wrong password' => 'Väärä salasana', + 'Unknown' => 'Tuntematon', + 'Last logins' => 'Viimeisimmät kirjautumiset', + 'Login date' => 'Kirjautumispäivä', + 'Authentication method' => 'Autentikointimenetelmä', + 'IP address' => 'IP-Osoite', + 'User agent' => 'Selain', + 'Persistent connections' => 'Voimassa olevat yhteydet', + 'No session.' => 'Ei sessioita.', + 'Expiration date' => 'Vanhentumispäivä', + 'Remember Me' => 'Muista minut', + 'Creation date' => 'Luomispäivä', + 'Everybody' => 'Kaikki', + 'Open' => 'Avoin', + 'Closed' => 'Suljettu', + 'Search' => 'Etsi', + 'Nothing found.' => 'Ei löytynyt.', + 'Due date' => 'Deadline', + 'Description' => 'Kuvaus', + '%d comments' => '%d kommenttia', + '%d comment' => '%d kommentti', + 'Email address invalid' => 'Email ei kelpaa', + 'Your external account is not linked anymore to your profile.' => 'Ulkoinen tilisi ei ole enää linkitetty profiiliisi.', + 'Unable to unlink your external account.' => 'Ulkoisen tilin irrottaminen epäonnistui.', + 'External authentication failed' => 'Ulkoinen tunnistautuminen epäonnistui', + 'Your external account is linked to your profile successfully.' => 'Ulkoinen tilisi on linkitetty profiiliisi onnistuneesti.', + 'Email' => 'Sähköposti', + 'Task removed successfully.' => 'Tehtävä poistettiin onnistuneesti.', + 'Unable to remove this task.' => 'Tehtävän poistaminen epäonnistui.', + 'Remove a task' => 'Poista tehtävä', + 'Do you really want to remove this task: "%s"?' => 'Haluatko varmasti poistaa tehtävän: "%s"?', + 'Assign automatically a color based on a category' => 'Aseta väri automaattisesti kategorian mukaan', + 'Assign automatically a category based on a color' => 'Aseta kategoria automaattisesti värin mukaan', + 'Task creation or modification' => 'Tehtävän luonti tai muuttaminen', + 'Category' => 'Kategoria', + 'Category:' => 'Kategoria:', + 'Categories' => 'Kategoriat', + 'Your category has been created successfully.' => 'Kategoria luotiin onnistuneesti.', + 'This category has been updated successfully.' => 'Kategoriaa muokattiin onnistuneesti.', + 'Unable to update this category.' => 'Kategorian muokkaaminen epäonnistui.', + 'Remove a category' => 'Poista kategoria', + 'Category removed successfully.' => 'Kategoria poistettu onnistuneesti.', + 'Unable to remove this category.' => 'Kategorian poisto epäonnistui.', + 'Category modification for the project "%s"' => 'Kategorian muutos projektissa "%s"', + 'Category Name' => 'Kategorian nimi', + 'Add a new category' => 'Lisää uusi kategoria', + 'Do you really want to remove this category: "%s"?' => 'Haluatko varmasti poistaa kategorian: "%s"?', + 'All categories' => 'Kaikki kategoriat', + 'No category' => 'Kategoriaa ei löydy', + 'The name is required' => 'Nimi vaaditaan', + 'Remove a file' => 'Poista tiedosto', + 'Unable to remove this file.' => 'Tiedoston poistaminen epäonnistui.', + 'File removed successfully.' => 'Tiedosto poistettiin onnistuneesti.', + 'Attach a document' => 'Liitä dokumentti', + 'Do you really want to remove this file: "%s"?' => 'Haluatko varmasti poistaa tiedoston: "%s"?', + 'Attachments' => 'Liitteet', + 'Edit the task' => 'Muokkaa tehtävää', + 'Add a comment' => 'Lisää kommentti', + 'Edit a comment' => 'Muokkaa kommenttia', + 'Summary' => 'Yhteenveto', + 'Time tracking' => 'Ajan seuranta', + 'Estimate:' => 'Arvio:', + 'Spent:' => 'Käytetty:', + 'Do you really want to remove this sub-task?' => 'Haluatko varmasti poistaa tämän alitehtävän?', + 'Remaining:' => 'Jäljellä', + 'hours' => 'tuntia', + 'estimated' => 'estimoitu', + 'Sub-Tasks' => 'Alitehtävät', + 'Add a sub-task' => 'Lisää alitehtävä', + 'Original estimate' => 'Alkuperäinen estimaatti', + 'Create another sub-task' => 'Lisää toinen alitehtävä', + 'Time spent' => 'Käytetty aika', + 'Edit a sub-task' => 'Muokkaa alitehtävää', + 'Remove a sub-task' => 'Poista alitehtävä', + 'The time must be a numeric value' => 'Ajan pitää olla numero', + 'Todo' => 'Todo', + 'In progress' => 'Työnalla', + 'Sub-task removed successfully.' => 'Alitehtävä poistettu onnistuneesti.', + 'Unable to remove this sub-task.' => 'Alitehtävän poistaminen epäonnistui.', + 'Sub-task updated successfully.' => 'Alitehtävä päivitettiin onnistuneesti.', + 'Unable to update your sub-task.' => 'Alitehtävän päivitys epäonnistui.', + 'Unable to create your sub-task.' => 'Alitehtävän luonti epäonnistui.', + 'Maximum size: ' => 'Maksimikoko: ', + 'Display another project' => 'Näytä toinen projekti', + 'Created by %s' => 'Luonut: %s', + 'Tasks Export' => 'Tehtävien vienti', + 'Start Date' => 'Aloituspäivä', + 'Execute' => 'Suorita', + 'Task Id' => 'Tehtävän ID', + 'Creator' => 'Luonut', + 'Modification date' => 'Muokkauspäivä', + 'Completion date' => 'Valmistumispäivä', + 'Clone' => 'Kahdenna', + 'Project cloned successfully.' => 'Projekti kahdennettu onnistuneesti', + 'Unable to clone this project.' => 'Projektin kahdennus epäonnistui', + 'Enable email notifications' => 'Ota käyttöön sähköposti-ilmoitukset', + 'Task position:' => 'Tehtävän sijainti', + 'The task #%d has been opened.' => 'Tehtävä #%d on avattu', + 'The task #%d has been closed.' => 'Tehtävä #%d on suljettu', + 'Sub-task updated' => 'Alitehtävä päivitetty', + 'Title:' => 'Otsikko:', + 'Status:' => 'Tila:', + 'Assignee:' => 'Vastaanottaja:', + 'Time tracking:' => 'Ajan seuranta:', + '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 comment' => 'Uusi kommentti', + 'Comment updated' => 'Kommentti päivitetty', + 'New subtask' => 'Uusi alitehtävä', + 'I only want to receive notifications for these projects:' => 'Haluan vastaanottaa ilmoituksia ainoastaan näistä projekteista:', + 'view the task on Kanboard' => 'katso tehtävää Kanboardissa', + 'Public access' => 'Julkinen käyttöoikeus', + 'Disable public access' => 'Poista käytöstä julkinen käyttöoikeus', + 'Enable public access' => 'Ota käyttöön ', + 'Public access disabled' => 'Julkinen käyttöoikeus ei ole käytössä', + 'Move the task to another project' => 'Siirrä tehtävä toiseen projektiin', + 'Move to project' => 'Siirrä toiseen projektiin', + 'Do you really want to duplicate this task?' => 'Haluatko varmasti kahdentaa tämän tehtävän?', + 'Duplicate a task' => 'Kahdenna tehtävä', + 'External accounts' => 'Muut tilit', + 'Account type' => 'Tilin tyyppi', + 'Local' => 'Paikallinen', + 'Remote' => 'Etä', + 'Enabled' => 'Käytössä', + 'Disabled' => 'Pois käytöstä', + 'Login:' => 'Käyttäjänimi:', + 'Full Name:' => 'Nimi:', + 'Email:' => 'Sähköpostiosoite:', + 'Notifications:' => 'Ilmoitukset:', + 'Notifications' => 'Ilmoitukset', + 'Account type:' => 'Tilin tyyppi:', + 'Edit profile' => 'Muokkaa profiilia', + 'Change password' => 'Vaihda salasana', + 'Password modification' => 'Salasanan vaihto', + 'External authentications' => 'Muut tunnistautumistavat', + 'Never connected.' => 'Ei koskaan liitetty.', + 'No external authentication enabled.' => 'Muita tunnistautumistapoja ei ole otettu käyttöön.', + 'Password modified successfully.' => 'Salasana vaihdettu onnistuneesti.', + 'Unable to change the password.' => 'Salasanan vaihto epäonnistui.', + 'Change category' => 'Vaihda kategoria', + '%s updated the task %s' => '%s päivitti tehtävän %s', + '%s opened the task %s' => '%s avasi tehtävän %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s siirsi tehtävän %s %d. sarakkeessa "%s"', + '%s moved the task %s to the column "%s"' => '%s siirsi tehtävän %s sarakkeeseen "%s"', + '%s created the task %s' => '%s loi tehtävän %s', + '%s closed the task %s' => '%s sulki tehtävän %s', + '%s created a subtask for the task %s' => '%s loi alitehtävän tehtävälle %s', + '%s updated a subtask for the task %s' => '%s päivitti tehtävän %s alitehtävää', + 'Assigned to %s with an estimate of %s/%sh' => 'Annettu henkilölle %s arviolla %s/%sh', + 'Not assigned, estimate of %sh' => 'Ei annettu kenellekään, arvio %sh', + '%s updated a comment on the task %s' => '%s päivitti kommentia tehtävässä %s', + '%s commented the task %s' => '%s kommentoi tehtävää %s', + '%s\'s activity' => 'Henkilön %s toiminta', + 'RSS feed' => 'RSS-syöte', + '%s updated a comment on the task #%d' => '%s päivitti kommenttia tehtävässä #%d', + '%s commented on the task #%d' => '%s kommentoi tehtävää #%d', + '%s updated a subtask for the task #%d' => '%s päivitti tehtävän #%d alitehtävää', + '%s created a subtask for the task #%d' => '%s loi alitehtävän tehtävälle #%d', + '%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 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 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', + 'Create a task from an external provider' => 'Luo tehtävä ulkoiselta tarjoajalta', + 'Change the assignee based on an external username' => 'Vaihda tehtävän saajaa perustuen ulkoiseen käyttäjänimeen', + 'Change the category based on an external label' => 'Vaihda kategoriaa perustuen ulkoiseen labeliin', + 'Reference' => 'Viite', + 'Label' => 'Label', + 'Database' => 'Tietokanta', + 'About' => 'Tietoja', + 'Database driver:' => 'Tietokantaohjelmisto:', + 'Board settings' => 'Taulun asetukset', + 'Webhook settings' => 'Webhookin asetukset', + 'Reset token' => 'Vaihda token', + 'API endpoint:' => 'API päätepiste:', + 'Refresh interval for personal board' => 'Päivitystiheys yksityisille tauluille', + 'Refresh interval for public board' => 'Päivitystiheys julkisille tauluille', + 'Task highlight period' => 'Tehtävän korostusaika', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Aika (sekunteina) kuinka kauan tehtävä voidaan katsoa äskettäin muokatuksi (0 poistaa toiminnon käytöstä, oletuksena 2 päivää)', + 'Frequency in second (60 seconds by default)' => 'Päivitystiheys sekunteina (60 sekuntia oletuksena)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Päivitystiheys sekunteina (0 poistaa toiminnon käytöstä, oletuksena 10 sekuntia)', + 'Application URL' => 'Sovelluksen URL', + 'Token regenerated.' => 'Token uudelleenluotu.', + 'Date format' => 'Päiväyksen muoto', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO-muoto on aina hyväksytty, esimerkiksi %s ja %s', + 'New personal project' => 'Uusi henkilökohtainen projekti', + 'This project is personal' => 'Tämä projekti on henkilökohtainen', + 'Add' => 'Lisää', + 'Start date' => 'Aloituspäivä', + 'Time estimated' => 'Arvioitu aika', + 'There is nothing assigned to you.' => 'Ei tehtäviä, joihin sinut olisi merkitty tekijäksi.', + 'My tasks' => 'Minun tehtävät', + 'Activity stream' => 'Toiminta', + 'Dashboard' => 'Työpöytä', + 'Confirmation' => 'Vahvistus', + 'Webhooks' => 'Webhookit', + 'API' => 'API', + 'Create a comment from an external provider' => 'Luo kommentti ulkoiselta palveluntarjoajalta', + 'Project management' => 'Projektin hallinta', + 'Columns' => 'Sarakkeet', + 'Task' => 'Tehtävät', + 'Percentage' => 'Prosentti', + 'Number of tasks' => 'Tehtävien määrä', + 'Task distribution' => 'Tehtävien jakauma', + 'Analytics' => 'Analytiikka', + 'Subtask' => 'Alitehtävä', + 'User repartition' => 'Käyttäjien jakauma', + '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.', + 'Previous' => 'Edellinen', + 'The id must be an integer' => 'ID:n on oltava kokonaisluku', + 'The project id must be an integer' => 'Projektin ID:n on oltava kokonaisluku', + 'The status must be an integer' => 'Tilan on oltava kokonaisluku', + 'The subtask id is required' => 'Alitehtävän ID vaaditaan', + 'The subtask id must be an integer' => 'Alitehtävän ID:ntulee olla kokonaisluku', + 'The task id is required' => 'Tehtävän ID vaaditaan', + 'The task id must be an integer' => 'Tehtävän ID on oltava kokonaisluku', + 'The user id must be an integer' => 'Käyttäjän ID on oltava kokonaisluku', + 'This value is required' => 'Tämä arvo on pakollinen', + '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', + 'Daily project summary' => 'Päivittäinen yhteenveto', + 'Daily project summary export' => 'Päivittäisen yhteenvedon vienti', + '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', + 'Add a new swimlane' => 'Lisää uusi kaista', + 'Default swimlane' => 'Oletuskaista', + 'Do you really want to remove this swimlane: "%s"?' => 'Haluatko varmasti poistaa tämän kaistan: "%s"?', + 'Inactive swimlanes' => 'Passiiviset kaistat', + 'Remove a swimlane' => 'Poista kaista', + 'Swimlane modification for the project "%s"' => 'Kaistamuutos projektille "%s"', + 'Swimlane removed successfully.' => 'Kaista poistettu onnistuneesti.', + 'Swimlanes' => 'Kaistat', + 'Swimlane updated successfully.' => 'Kaista päivitetty onnistuneesti.', + 'Unable to remove this swimlane.' => 'Kaistan poisto epäonnistui.', + 'Unable to update this swimlane.' => 'Kaistan päivittäminen epäonnistui.', + 'Your swimlane has been created successfully.' => 'Kaista luotu onnistuneesti.', + 'Example: "Bug, Feature Request, Improvement"' => 'Esimerkiksi: "Bugit, Ominaisuuspyynnöt, Parannukset"', + 'Default categories for new projects (Comma-separated)' => 'Oletuskategoriat uusille projekteille (pilkuin eroteltu)', + 'Integrations' => 'Integraatiot', + 'Integration with third-party services' => 'Integrointi kolmannen osapuolen palveluihin', + 'Subtask Id' => 'Alitehtävän tunnus', + 'Subtasks' => 'Alitehtävät', + 'Subtasks Export' => 'Alitehtävien vienti', + 'Task Title' => 'Tehtävän otsikko', + 'Untitled' => 'Nimetön', + 'Application default' => 'Sovelluksen oletus', + 'Language:' => 'Kieli:', + 'Timezone:' => 'Aikavyöhyke:', + 'All columns' => 'Kaikki sarakkeet', + 'Next' => 'Seuraava', + '#%d' => '#%d', + 'All swimlanes' => 'Kaikki kaistat', + 'All colors' => 'Kaikki värit', + 'Moved to column %s' => 'Siirretty sarakkeeseen %s', + 'User dashboard' => 'Käyttäjän hallintapaneeli', + 'Allow only one subtask in progress at the same time for a user' => 'Salli käyttäjälle vain yksi alitehtävä kerrallaan kesken', + 'Edit column "%s"' => 'Muokkaa saraketta "%s"', + 'Select the new status of the subtask: "%s"' => 'Valitse alitehtävän uusi tila: "%s"', + 'Subtask timesheet' => 'Alitehtävien tuntilista', + 'There is nothing to show.' => 'Ei näytettävää.', + 'Time Tracking' => 'Ajankäytön seuranta', + 'You already have one subtask in progress' => 'Sinulla on jo yksi alitehtävä kesken', + 'Which parts of the project do you want to duplicate?' => 'Mitkä osat projektista haluat kopioida?', + 'Disallow login form' => 'Estä kirjautumislomake', + 'Start' => 'Alku', + 'End' => 'Loppu', + 'Task age in days' => 'Tehtävän ikä päivinä', + 'Days in this column' => 'Päivää tässä sarakkeessa', + '%dd' => '%dp', + 'Add a new link' => 'Lisää uusi linkki', + 'Do you really want to remove this link: "%s"?' => 'Haluatko todella poistaa tämän linkin: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Haluatko todella poistaa tämän linkin tehtävään #%d?', + 'Field required' => 'Kenttä on pakollinen', + 'Link added successfully.' => 'Linkki lisättiin onnistuneesti.', + 'Link updated successfully.' => 'Linkki päivitettiin onnistuneesti.', + 'Link removed successfully.' => 'Linkki poistettiin onnistuneesti.', + 'Link labels' => 'Linkkien selitteet', + 'Link modification' => 'Linkin muokkaus', + 'Opposite label' => 'Vastakkainen selite', + 'Remove a link' => 'Poista linkki', + 'The labels must be different' => 'Selitteiden on oltava erilaisia', + 'There is no link.' => 'Ei linkkejä.', + 'This label must be unique' => 'Tämän selitteen on oltava yksilöllinen', + 'Unable to create your link.' => 'Linkin luominen epäonnistui.', + 'Unable to update your link.' => 'Linkin päivittäminen epäonnistui.', + 'Unable to remove this link.' => 'Linkin poistaminen epäonnistui.', + 'relates to' => 'liittyy', + 'blocks' => 'estää', + 'is blocked by' => 'estää', + 'duplicates' => 'kopioi', + 'is duplicated by' => 'on kopioitu', + 'is a child of' => 'on alitehtävä', + 'is a parent of' => 'on ylätason tehtävä', + 'targets milestone' => 'kohdistuu välitavoitteeseen', + 'is a milestone of' => 'on välitavoite', + 'fixes' => 'korjaa', + 'is fixed by' => 'korjaa', + 'This task' => 'Tämä tehtävä', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Laajenna tehtävät', + 'Collapse tasks' => 'Kutista tehtävät', + 'Expand/collapse tasks' => 'Laajenna/kutista tehtävät', + 'Close dialog box' => 'Sulje valintaikkuna', + 'Submit a form' => 'Lähetä lomake', + 'Board view' => 'Taulunäkymä', + 'Keyboard shortcuts' => 'Pikanäppäimet', + 'Open board switcher' => 'Avaa taulun valitsin', + 'Application' => 'Sovellus', + 'Compact view' => 'Tiivis näkymä', + 'Horizontal scrolling' => 'Vaakavieritys', + 'Compact/wide view' => 'Tiivis/leveä näkymä', + 'Currency' => 'Valuutta', + 'Personal project' => 'Henkilökohtainen projekti', + 'AUD - Australian Dollar' => 'AUD - Australian dollari', + 'CAD - Canadian Dollar' => 'CAD - Kanadan dollari', + 'CHF - Swiss Francs' => 'CHF - Sveitsin frangi', + 'Custom Stylesheet' => 'Mukautettu tyylitiedosto', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Englannin punta', + 'INR - Indian Rupee' => 'INR - Intian rupia', + 'JPY - Japanese Yen' => 'JPY - Japanin jeni', + 'NZD - New Zealand Dollar' => 'NZD - Uuden-Seelannin dollari', + 'PEN - Peruvian Sol' => 'Perun sol', + 'RSD - Serbian dinar' => 'RSD - Serbian dinaari', + 'CNY - Chinese Yuan' => 'CNY - Kiinan yuan', + 'USD - US Dollar' => 'USD - Yhdysvaltain dollari', + 'VES - Venezuelan Bolívar' => 'VES - Venezuelan bolívar', + 'Destination column' => 'Kohdesarake', + 'Move the task to another column when assigned to a user' => 'Siirrä tehtävä toiseen sarakkeeseen, kun se on määritetty käyttäjälle', + 'Move the task to another column when assignee is cleared' => 'Siirrä tehtävä toiseen sarakkeeseen, kun määräys on poistettu', + 'Source column' => 'Lähdesarake', + 'Transitions' => 'Siirtymät', + 'Executer' => 'Suorittaja', + 'Time spent in the column' => 'Aika, joka on käytetty sarakkeessa', + 'Task transitions' => 'Tehtävän siirtymät', + 'Task transitions export' => 'Tehtävän siirtymien vienti', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Tämä raportti sisältää kaikki sarakkeiden siirtymät kullekin tehtävälle päivämäärän, käyttäjän ja kunkin siirtymän käytetyn ajan kanssa.', + 'Currency rates' => 'Valuuttakurssit', + 'Rate' => 'Kurssi', + 'Change reference currency' => 'Vaihda vertailuvaluutta', + 'Reference currency' => 'Vertailuvaluutta', + 'The currency rate has been added successfully.' => 'Valuuttakurssi lisättiin onnistuneesti.', + 'Unable to add this currency rate.' => 'Valuuttakurssia ei voitu lisätä.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s poisti tehtävän %s määräyksen', + 'Information' => 'Tietoja', + 'Check two factor authentication code' => 'Tarkista kaksivaiheisen tunnistautumisen koodi', + 'The two factor authentication code is not valid.' => 'Kaksivaiheisen tunnistautumisen koodi ei ole kelvollinen.', + 'The two factor authentication code is valid.' => 'Kaksivaiheisen tunnistautumisen koodi on kelvollinen.', + 'Code' => 'Koodi', + 'Two factor authentication' => 'Kaksivaiheinen tunnistautuminen', + 'This QR code contains the key URI: ' => 'Tämä QR-koodi sisältää avaimen URI:n:', + 'Check my code' => 'Tarkista koodini', + 'Secret key: ' => 'Salainen avain:', + 'Test your device' => 'Testaa laitettasi', + 'Assign a color when the task is moved to a specific column' => 'Määritä väri, kun tehtävä siirretään tiettyyn sarakkeeseen', + '%s via Kanboard' => '%s Kanboardin kautta', + 'Burndown chart' => 'Burndown-kaavio', + 'This chart show the task complexity over the time (Work Remaining).' => 'Tämä kaavio näyttää tehtävän monimutkaisuuden ajan myötä (Jäljellä oleva työ).', + 'Screenshot taken %s' => 'Kuvakaappaus otettu %s', + 'Add a screenshot' => 'Lisää kuvakaappaus', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Ota kuvakaappaus ja paina CTRL+V tai ⌘+V liittääksesi sen tähän.', + 'Screenshot uploaded successfully.' => 'Kuvakaappaus ladattiin onnistuneesti.', + 'SEK - Swedish Krona' => 'SEK - Ruotsin kruunu', + 'Identifier' => 'Tunnus', + 'Disable two factor authentication' => 'Poista kaksivaiheinen tunnistautuminen käytöstä', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Haluatko todella poistaa kaksivaiheisen tunnistautumisen käytöstä tälle käyttäjälle: "%s"?', + 'Edit link' => 'Muokkaa linkkiä', + 'Start to type task title...' => 'Aloita tehtävän otsikon kirjoittaminen...', + 'A task cannot be linked to itself' => 'Tehtävää ei voida linkittää itseensä', + 'The exact same link already exists' => 'Täsmälleen sama linkki on jo olemassa', + 'Recurrent task is scheduled to be generated' => 'Toistuva tehtävä on ajoitettu luotavaksi', + 'Score' => 'Pisteet', + 'The identifier must be unique' => 'Tunnuksen on oltava yksilöllinen', + 'This linked task id doesn\'t exists' => 'Tätä linkitettyä tehtävän tunnusta ei ole olemassa', + 'This value must be alphanumeric' => 'Tämän arvon on oltava aakkosnumeerinen', + 'Edit recurrence' => 'Muokkaa toistuvuutta', + 'Generate recurrent task' => 'Luo toistuva tehtävä', + 'Trigger to generate recurrent task' => 'Laukaisin toistuvan tehtävän luomiseksi', + 'Factor to calculate new due date' => 'Tekijä uuden määräpäivän laskemiseksi', + 'Timeframe to calculate new due date' => 'Aikataulu uuden määräpäivän laskemiseksi', + 'Base date to calculate new due date' => 'Peruspäivämäärä uuden määräpäivän laskemiseksi', + 'Action date' => 'Toiminnon päivämäärä', + 'Base date to calculate new due date: ' => 'Peruspäivämäärä uuden määräpäivän laskemiseksi:', + 'This task has created this child task: ' => 'Tämä tehtävä on luonut tämän alitehtävän:', + 'Day(s)' => 'Päivä(t)', + 'Existing due date' => 'Olemassa oleva määräpäivä', + 'Factor to calculate new due date: ' => 'Tekijä uuden määräpäivän laskemiseksi:', + 'Month(s)' => 'Kuukausi(t)', + 'This task has been created by: ' => 'Tämän tehtävän on luonut:', + 'Recurrent task has been generated:' => 'Toistuva tehtävä on luotu:', + 'Timeframe to calculate new due date: ' => 'Aikataulu uuden määräpäivän laskemiseksi:', + 'Trigger to generate recurrent task: ' => 'Laukaisin toistuvan tehtävän luomiseksi:', + 'When task is closed' => 'Kun tehtävä suljetaan', + 'When task is moved from first column' => 'Kun tehtävä siirretään ensimmäisestä sarakkeesta', + 'When task is moved to last column' => 'Kun tehtävä siirretään viimeiseen sarakkeeseen', + 'Year(s)' => 'Vuosi(t)', + 'Project settings' => 'Projektin asetukset', + 'Automatically update the start date' => 'Päivitä aloituspäivämäärä automaattisesti', + 'iCal feed' => 'iCal-syöte', + 'Preferences' => 'Asetukset', + 'Security' => 'Turvallisuus', + 'Two factor authentication disabled' => 'Kaksivaiheinen tunnistautuminen poistettu käytöstä', + 'Two factor authentication enabled' => 'Kaksivaiheinen tunnistautuminen käytössä', + 'Unable to update this user.' => 'Tämän käyttäjän päivittäminen epäonnistui.', + 'There is no user management for personal projects.' => 'Henkilökohtaisissa projekteissa ei ole käyttäjien hallintaa.', + 'User that will receive the email' => 'Käyttäjä, joka vastaanottaa sähköpostin', + 'Email subject' => 'Sähköpostin aihe', + 'Date' => 'Päivämäärä', + 'Add a comment log when moving the task between columns' => 'Lisää kommenttiloki, kun tehtävää siirretään sarakkeiden välillä', + 'Move the task to another column when the category is changed' => 'Siirrä tehtävä toiseen sarakkeeseen, kun luokkaa muutetaan', + 'Send a task by email to someone' => 'Lähetä tehtävä sähköpostitse jollekin', + 'Reopen a task' => 'Avaa tehtävä uudelleen', + 'Notification' => 'Ilmoitus', + '%s moved the task #%d to the first swimlane' => '%s siirsi tehtävän #%d ensimmäiseen kaistaan', + 'Swimlane' => 'Kaista', + '%s moved the task %s to the first swimlane' => '%s siirsi tehtävän %s ensimmäiseen kaistaan', + '%s moved the task %s to the swimlane "%s"' => '%s siirsi tehtävän %s kaistaan "%s"', + 'This report contains all subtasks information for the given date range.' => 'Tämä raportti sisältää kaikki alitehtävien tiedot annetulta aikaväliltä.', + 'This report contains all tasks information for the given date range.' => 'Tämä raportti sisältää kaikki tehtävien tiedot annetulta aikaväliltä.', + 'Project activities for %s' => 'Projektin aktiviteetit käyttäjälle %s', + 'view the board on Kanboard' => 'katso taulua Kanboardissa', + 'The task has been moved to the first swimlane' => 'Tehtävä on siirretty ensimmäiseen kaistaan', + 'The task has been moved to another swimlane:' => 'Tehtävä on siirretty toiseen kaistaan:', + 'New title: %s' => 'Uusi otsikko: %s', + 'The task is not assigned anymore' => 'Tehtävää ei ole enää määritetty', + 'New assignee: %s' => 'Uusi määrätty: %s', + 'There is no category now' => 'Ei luokkaa nyt', + 'New category: %s' => 'Uusi luokka: %s', + 'New color: %s' => 'Uusi väri: %s', + 'New complexity: %d' => 'Uusi monimutkaisuus: %d', + 'The due date has been removed' => 'Määräpäivä on poistettu', + 'There is no description anymore' => 'Kuvausta ei ole enää', + 'Recurrence settings has been modified' => 'Toistuvuuden asetuksia on muutettu', + 'Time spent changed: %sh' => 'Käytetty aika muuttui: %sh', + 'Time estimated changed: %sh' => 'Arvioitu aika muuttui: %sh', + 'The field "%s" has been updated' => 'Kenttä "%s" on päivitetty', + 'The description has been modified:' => 'Kuvaus on muokattu:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Haluatko todella sulkea tehtävän "%s" ja kaikki alitehtävät?', + 'I want to receive notifications for:' => 'Haluan vastaanottaa ilmoituksia:', + 'All tasks' => 'Kaikki tehtävät', + 'Only for tasks assigned to me' => 'Vain minulle määritetyistä tehtävistä', + 'Only for tasks created by me' => 'Vain minun luomistani tehtävistä', + 'Only for tasks created by me and tasks assigned to me' => 'Vain minun luomistani tehtävistä ja minulle määritetyistä tehtävistä', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Yhteensä kaikille sarakkeille', + 'You need at least 2 days of data to show the chart.' => 'Tarvitset vähintään 2 päivän tiedot kaavion näyttämiseksi.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Pysäytä ajastin', + 'Start timer' => 'Käynnistä ajastin', + 'My activity stream' => 'Oma aktiviteettivirta', + 'Search tasks' => 'Hae tehtäviä', + 'Reset filters' => 'Nollaa suodattimet', + 'My tasks due tomorrow' => 'Tehtäväni, jotka ovat huomenna määräpäivä', + 'Tasks due today' => 'Tehtävät, jotka ovat tänään määräpäivä', + 'Tasks due tomorrow' => 'Tehtävät, jotka ovat huomenna määräpäivä', + 'Tasks due yesterday' => 'Tehtävät, jotka olivat eilen määräpäivä', + 'Closed tasks' => 'Suljetut tehtävät', + 'Open tasks' => 'Avoimet tehtävät', + 'Not assigned' => 'Ei määritetty', + 'View advanced search syntax' => 'Näytä tarkennetun haun syntaksi', + 'Overview' => 'Yleiskatsaus', + 'Board/Calendar/List view' => 'Taulu/Kalenteri/Lista-näkymä', + 'Switch to the board view' => 'Vaihda taulunäkymään', + 'Switch to the list view' => 'Vaihda listanäkymään', + 'Go to the search/filter box' => 'Siirry haku/suodatin-kenttään', + 'There is no activity yet.' => 'Ei vielä toimintaa.', + 'No tasks found.' => 'Tehtäviä ei löytynyt.', + 'Keyboard shortcut: "%s"' => 'Pikanäppäin: "%s"', + 'List' => 'Lista', + 'Filter' => 'Suodatin', + 'Advanced search' => 'Tarkennettu haku', + 'Example of query: ' => 'Esimerkki kyselystä:', + 'Search by project: ' => 'Hae projektin mukaan:', + 'Search by column: ' => 'Hae sarakkeen mukaan:', + 'Search by assignee: ' => 'Hae määrätyn mukaan:', + 'Search by color: ' => 'Hae värin mukaan:', + 'Search by category: ' => 'Hae luokan mukaan:', + 'Search by description: ' => 'Hae kuvauksen mukaan:', + 'Search by due date: ' => 'Hae määräpäivän mukaan:', + 'Average time spent in each column' => 'Keskimääräinen aika, joka on käytetty kussakin sarakkeessa', + 'Average time spent' => 'Keskimääräinen käytetty aika', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Tämä kaavio näyttää keskimääräisen ajan, joka on käytetty kussakin sarakkeessa viimeisimmän %d tehtävän osalta.', + 'Average Lead and Cycle time' => 'Keskimääräinen läpimeno- ja sykliaika', + 'Average lead time: ' => 'Keskimääräinen läpimenoaika:', + 'Average cycle time: ' => 'Keskimääräinen sykliaika:', + 'Cycle Time' => 'Sykliaika', + 'Lead Time' => 'Läpimenoaika', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Tämä kaavio näyttää keskimääräisen läpimeno- ja sykliajan viimeisimmän %d tehtävän osalta ajan myötä.', + 'Average time into each column' => 'Keskimääräinen aika kuhunkin sarakkeeseen', + 'Lead and cycle time' => 'Läpimeno- ja sykliaika', + 'Lead time: ' => 'Läpimenoaika:', + 'Cycle time: ' => 'Sykliaika:', + 'Time spent in each column' => 'Aika, joka on käytetty kussakin sarakkeessa', + 'The lead time is the duration between the task creation and the completion.' => 'Läpimenoaika on tehtävän luomisen ja valmistumisen välinen kesto.', + 'The cycle time is the duration between the start date and the completion.' => 'Sykliaika on aloituspäivämäärän ja valmistumisen välinen kesto.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Jos tehtävää ei ole suljettu, käytetään nykyistä aikaa valmistumispäivämäärän sijaan.', + 'Set the start date automatically' => 'Aseta aloituspäivämäärä automaattisesti', + 'Edit Authentication' => 'Muokkaa tunnistautumista', + 'Remote user' => 'Etäkäyttäjä', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Etäkäyttäjät eivät tallenna salasanaansa Kanboard-tietokantaan, esimerkiksi: LDAP-, Google- ja Github-tilit.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Jos valitset ruudun "Älä salli kirjautumislomaketta", kirjautumislomakkeeseen syötetyt tunnukset ohitetaan.', + 'Default task color' => 'Tehtävän oletusväri', + 'This feature does not work with all browsers.' => 'Tämä ominaisuus ei toimi kaikilla selaimilla.', + 'There is no destination project available.' => 'Ei käytettävissä olevaa kohdeprojektia.', + 'Trigger automatically subtask time tracking' => 'Käynnistä automaattisesti alitehtävän ajankäytön seuranta', + 'Include closed tasks in the cumulative flow diagram' => 'Sisällytä suljetut tehtävät kumulatiiviseen virtauskaavioon', + 'Current swimlane: %s' => 'Nykyinen kaista: %s', + 'Current column: %s' => 'Nykyinen sarake: %s', + 'Current category: %s' => 'Nykyinen luokka: %s', + 'no category' => 'ei luokkaa', + 'Current assignee: %s' => 'Nykyinen määrätty: %s', + 'not assigned' => 'ei määritetty', + 'Author:' => 'Tekijä:', + 'contributors' => 'avustajat', + 'License:' => 'Lisenssi:', + 'License' => 'Lisenssi', + 'Enter the text below' => 'Syötä teksti alle', + 'Start date:' => 'Aloituspäivämäärä:', + 'Due date:' => 'Määräpäivä:', + 'People who are project managers' => 'Henkilöt, jotka ovat projektipäälliköitä', + 'People who are project members' => 'Henkilöt, jotka ovat projektin jäseniä', + 'NOK - Norwegian Krone' => 'NOK - Norjan kruunu', + 'Show this column' => 'Näytä tämä sarake', + 'Hide this column' => 'Piilota tämä sarake', + 'End date' => 'Lopetuspäivämäärä', + 'Users overview' => 'Käyttäjien yleiskatsaus', + 'Members' => 'Jäsenet', + 'Shared project' => 'Jaettu projekti', + 'Project managers' => 'Projektipäälliköt', + 'Projects list' => 'Projektilista', + 'End date:' => 'Lopetuspäivämäärä:', + 'Change task color when using a specific task link' => 'Muuta tehtävän väriä, kun käytetään tiettyä tehtävälinkkiä', + 'Task link creation or modification' => 'Tehtävälinkin luominen tai muokkaus', + 'Milestone' => 'Välitavoite', + 'Reset the search/filter box' => 'Nollaa haku/suodatin-kenttä', + 'Documentation' => 'Dokumentaatio', + 'Author' => 'Tekijä', + 'Version' => 'Versio', + 'Plugins' => 'Lisäosat', + 'There is no plugin loaded.' => 'Lisäosia ei ole ladattu.', + 'My notifications' => 'Omat ilmoitukset', + 'Custom filters' => 'Mukautetut suodattimet', + 'Your custom filter has been created successfully.' => 'Mukautettu suodatin on luotu onnistuneesti.', + 'Unable to create your custom filter.' => 'Mukautettua suodatinta ei voitu luoda.', + 'Custom filter removed successfully.' => 'Mukautettu suodatin poistettiin onnistuneesti.', + 'Unable to remove this custom filter.' => 'Mukautetun suodattimen poistaminen epäonnistui.', + 'Edit custom filter' => 'Muokkaa mukautettua suodatinta', + 'Your custom filter has been updated successfully.' => 'Mukautettu suodatin on päivitetty onnistuneesti.', + 'Unable to update custom filter.' => 'Mukautetun suodattimen päivittäminen epäonnistui.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Uusi liite tehtävässä #%d: %s', + 'New comment on task #%d' => 'Uusi kommentti tehtävässä #%d', + 'Comment updated on task #%d' => 'Kommentti päivitetty tehtävässä #%d', + 'New subtask on task #%d' => 'Uusi alitehtävä tehtävässä #%d', + 'Subtask updated on task #%d' => 'Alitehtävä päivitetty tehtävässä #%d', + 'New task #%d: %s' => 'Uusi tehtävä #%d: %s', + 'Task updated #%d' => 'Tehtävä päivitetty #%d', + 'Task #%d closed' => 'Tehtävä #%d suljettu', + 'Task #%d opened' => 'Tehtävä #%d avattu', + 'Column changed for task #%d' => 'Sarake muutettu tehtävälle #%d', + 'New position for task #%d' => 'Uusi sijainti tehtävälle #%d', + 'Swimlane changed for task #%d' => 'Kaista muutettu tehtävälle #%d', + 'Assignee changed on task #%d' => 'Määräys muutettu tehtävässä #%d', + '%d overdue tasks' => '%d myöhässä olevaa tehtävää', + 'No notification.' => 'Ei ilmoituksia.', + 'Mark all as read' => 'Merkitse kaikki luetuksi', + 'Mark as read' => 'Merkitse luetuksi', + 'Total number of tasks in this column across all swimlanes' => 'Tehtävien kokonaismäärä tässä sarakkeessa kaikilla kaistoilla', + 'Collapse swimlane' => 'Kutista kaista', + 'Expand swimlane' => 'Laajenna kaista', + 'Add a new filter' => 'Lisää uusi suodatin', + 'Share with all project members' => 'Jaa kaikille projektin jäsenille', + 'Shared' => 'Jaettu', + 'Owner' => 'Omistaja', + 'Unread notifications' => 'Lukemattomat ilmoitukset', + 'Notification methods:' => 'Ilmoitusmenetelmät:', + 'Unable to read your file' => 'Tiedostoa ei voitu lukea', + '%d task(s) have been imported successfully.' => '%d tehtävä(t) on tuotu onnistuneesti.', + 'Nothing has been imported!' => 'Mitään ei ole tuotu!', + 'Import users from CSV file' => 'Tuo käyttäjät CSV-tiedostosta', + '%d user(s) have been imported successfully.' => '%d käyttäjä(t) on tuotu onnistuneesti.', + 'Comma' => 'Pilkku', + 'Semi-colon' => 'Puolipiste', + 'Tab' => 'Sarkain', + 'Vertical bar' => 'Pystyviiva', + 'Double Quote' => 'Kaksoishevonkenkä', + 'Single Quote' => 'Yksinkertainen hevonkenkä', + '%s attached a file to the task #%d' => '%s liitti tiedoston tehtävään #%d', + 'There is no column or swimlane activated in your project!' => 'Projektissasi ei ole aktivoitu sarakkeita tai kaistoja!', + 'Append filter (instead of replacement)' => 'Lisää suodatin (korvaamisen sijaan)', + 'Append/Replace' => 'Lisää/Korvaa', + 'Append' => 'Lisää', + 'Replace' => 'Korvaa', + 'Import' => 'Tuo', + 'Change sorting' => 'Muuta lajittelua', + 'Tasks Importation' => 'Tehtävien tuonti', + 'Delimiter' => 'Erotin', + 'Enclosure' => 'Sulkeet', + 'CSV File' => 'CSV-tiedosto', + 'Instructions' => 'Ohjeet', + 'Your file must use the predefined CSV format' => 'Tiedostosi on käytettävä es määriteltyä CSV-muotoa', + 'Your file must be encoded in UTF-8' => 'Tiedostosi on oltava koodattu UTF-8:lla', + 'The first row must be the header' => 'Ensimmäisen rivin on oltava otsikkorivi', + 'Duplicates are not verified for you' => 'Kopioita ei tarkisteta puolestasi', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Määräpäivän on käytettävä ISO-muotoa: VVVV-KK-PV', + 'Download CSV template' => 'Lataa CSV-mallitiedosto', + 'No external integration registered.' => 'Ei ulkoisia integraatioita rekisteröity.', + 'Duplicates are not imported' => 'Kopioita ei tuoda', + 'Usernames must be lowercase and unique' => 'Käyttäjänimien on oltava pienikirjaimisia ja yksilöllisiä', + 'Passwords will be encrypted if present' => 'Salasanat salataan, jos niitä on', + '%s attached a new file to the task %s' => '%s liitti uuden tiedoston tehtävään %s', + 'Link type' => 'Linkin tyyppi', + 'Assign automatically a category based on a link' => 'Määritä automaattisesti luokka linkin perusteella', + 'BAM - Konvertible Mark' => 'BAM - Konvertibilna Marka', + 'Assignee Username' => 'Määrätyn käyttäjänimi', + 'Assignee Name' => 'Määrätyn nimi', + 'Groups' => 'Ryhmät', + 'Members of %s' => 'Ryhmän %s jäsenet', + 'New group' => 'Uusi ryhmä', + 'Group created successfully.' => 'Ryhmä luotiin onnistuneesti.', + 'Unable to create your group.' => 'Ryhmää ei voitu luoda.', + 'Edit group' => 'Muokkaa ryhmää', + 'Group updated successfully.' => 'Ryhmä päivitettiin onnistuneesti.', + 'Unable to update your group.' => 'Ryhmän päivittäminen epäonnistui.', + 'Add group member to "%s"' => 'Lisää ryhmän jäsen ryhmään "%s"', + 'Group member added successfully.' => 'Ryhmän jäsen lisättiin onnistuneesti.', + 'Unable to add group member.' => 'Ryhmän jäsentä ei voitu lisätä.', + 'Remove user from group "%s"' => 'Poista käyttäjä ryhmästä "%s"', + 'User removed successfully from this group.' => 'Käyttäjä poistettiin onnistuneesti tästä ryhmästä.', + 'Unable to remove this user from the group.' => 'Tämän käyttäjän poistaminen ryhmästä epäonnistui.', + 'Remove group' => 'Poista ryhmä', + 'Group removed successfully.' => 'Ryhmä poistettiin onnistuneesti.', + 'Unable to remove this group.' => 'Ryhmän poistaminen epäonnistui.', + 'Project Permissions' => 'Projektin käyttöoikeudet', + 'Manager' => 'Päällikkö', + 'Project Manager' => 'Projektipäällikkö', + 'Project Member' => 'Projektin jäsen', + 'Project Viewer' => 'Projektin katselija', + 'Your account is locked for %d minutes' => 'Tilisi on lukittu %d minuutiksi', + 'Invalid captcha' => 'Virheellinen captcha', + 'The name must be unique' => 'Nimen on oltava yksilöllinen', + 'View all groups' => 'Näytä kaikki ryhmät', + 'There is no user available.' => 'Käyttäjiä ei ole saatavilla.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Haluatko todella poistaa käyttäjän "%s" ryhmästä "%s"?', + 'There is no group.' => 'Ei ryhmiä.', + 'Add group member' => 'Lisää ryhmän jäsen', + 'Do you really want to remove this group: "%s"?' => 'Haluatko todella poistaa tämän ryhmän: "%s"?', + 'There is no user in this group.' => 'Ei käyttäjiä tässä ryhmässä.', + 'Permissions' => 'Käyttöoikeudet', + 'Allowed Users' => 'Sallitut käyttäjät', + 'No specific user has been allowed.' => 'Mitään tiettyä käyttäjää ei ole sallittu.', + 'Role' => 'Rooli', + 'Enter user name...' => 'Syötä käyttäjänimi...', + 'Allowed Groups' => 'Sallitut ryhmät', + 'No group has been allowed.' => 'Mitään ryhmää ei ole sallittu.', + 'Group' => 'Ryhmä', + 'Group Name' => 'Ryhmän nimi', + 'Enter group name...' => 'Syötä ryhmän nimi...', + 'Role:' => 'Rooli:', + 'Project members' => 'Projektin jäsenet', + '%s mentioned you in the task #%d' => '%s mainitsi sinut tehtävässä #%d', + '%s mentioned you in a comment on the task #%d' => '%s mainitsi sinut kommentissa tehtävässä #%d', + 'You were mentioned in the task #%d' => 'Sinut mainittiin tehtävässä #%d', + 'You were mentioned in a comment on the task #%d' => 'Sinut mainittiin kommentissa tehtävässä #%d', + 'Estimated hours: ' => 'Arvioidut tunnit:', + 'Actual hours: ' => 'Todelliset tunnit:', + 'Hours Spent' => 'Käytetyt tunnit', + 'Hours Estimated' => 'Arvioidut tunnit', + 'Estimated Time' => 'Arvioitu aika', + 'Actual Time' => 'Todellinen aika', + 'Estimated vs actual time' => 'Arvioitu vs todellinen aika', + 'RUB - Russian Ruble' => 'RUB - Venäjän rupla', + 'Assign the task to the person who does the action when the column is changed' => 'Määritä tehtävä henkilölle, joka suorittaa toimenpiteen saraketta vaihdettaessa', + 'Close a task in a specific column' => 'Sulje tehtävä tietyssä sarakkeessa', + 'Time-based One-time Password Algorithm' => 'Aikaan perustuva kertaluonteinen salasana-algoritmi', + 'Two-Factor Provider: ' => 'Kaksivaiheisen tunnistautumisen tarjoaja:', + 'Disable two-factor authentication' => 'Poista kaksivaiheinen tunnistautuminen käytöstä', + 'Enable two-factor authentication' => 'Ota kaksivaiheinen tunnistautuminen käyttöön', + 'There is no integration registered at the moment.' => 'Tällä hetkellä ei ole rekisteröity integraatioita.', + 'Password Reset for Kanboard' => 'Kanboardin salasanan palautus', + 'Forgot password?' => 'Unohtuiko salasana?', + 'Enable "Forget Password"' => 'Ota "Unohdin salasanan" käyttöön', + 'Password Reset' => 'Salasanan palautus', + 'New password' => 'Uusi salasana', + 'Change Password' => 'Vaihda salasana', + 'To reset your password click on this link:' => 'Palauttaaksesi salasanasi klikkaa tätä linkkiä:', + 'Last Password Reset' => 'Viimeisin salasanan palautus', + 'The password has never been reinitialized.' => 'Salasanaa ei ole koskaan alustettu uudelleen.', + 'Creation' => 'Luonti', + 'Expiration' => 'Vanheneminen', + 'Password reset history' => 'Salasanan palautushistoria', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Kaikki sarakkeen "%s" ja kaistan "%s" tehtävät on suljettu onnistuneesti.', + 'Do you really want to close all tasks of this column?' => 'Haluatko todella sulkea kaikki tämän sarakkeen tehtävät?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tehtävä(t) sarakkeessa "%s" ja kaistalla "%s" suljetaan.', + 'Close all tasks in this column and this swimlane' => 'Sulje kaikki tehtävät tässä sarakkeessa ja tällä kaistalla', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Yksikään lisäosa ei ole rekisteröinyt projektin ilmoitusmenetelmää. Voit edelleen määrittää yksittäisiä ilmoituksia käyttäjäprofiilissasi.', + 'My dashboard' => 'Oma hallintapaneelini', + 'My profile' => 'Oma profiilini', + 'Project owner: ' => 'Projektin omistaja:', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Projektin tunniste on valinnainen ja sen on oltava aakkosnumeerinen, esimerkiksi: MYPROJECT.', + 'Project owner' => 'Projektin omistaja', + 'Personal projects do not have users and groups management.' => 'Henkilökohtaisissa projekteissa ei ole käyttäjä- ja ryhmähallintaa.', + 'There is no project member.' => 'Ei projektin jäseniä.', + 'Priority' => 'Prioriteetti', + 'Task priority' => 'Tehtävän prioriteetti', + 'General' => 'Yleiset', + 'Dates' => 'Päivämäärät', + 'Default priority' => 'Oletusprioriteetti', + 'Lowest priority' => 'Alin prioriteetti', + 'Highest priority' => 'Korkein prioriteetti', + 'Close a task when there is no activity' => 'Sulje tehtävä, kun siinä ei ole toimintaa', + 'Duration in days' => 'Kesto päivinä', + 'Send email when there is no activity on a task' => 'Lähetä sähköposti, kun tehtävässä ei ole toimintaa', + 'Unable to fetch link information.' => 'Linkin tietojen haku epäonnistui.', + 'Daily background job for tasks' => 'Päivittäinen taustatyö tehtäville', + 'Auto' => 'Auto', + 'Related' => 'Liittyvät', + 'Attachment' => 'Liite', + 'Web Link' => 'Web-linkki', + 'External links' => 'Ulkoiset linkit', + 'Add external link' => 'Lisää ulkoinen linkki', + 'Type' => 'Tyyppi', + 'Dependency' => 'Riippuvuus', + 'Add internal link' => 'Lisää sisäinen linkki', + 'Add a new external link' => 'Lisää uusi ulkoinen linkki', + 'Edit external link' => 'Muokkaa ulkoista linkkiä', + 'External link' => 'Ulkoinen linkki', + 'Copy and paste your link here...' => 'Kopioi ja liitä linkkisi tähän...', + 'URL' => 'URL', + 'Internal links' => 'Sisäiset linkit', + 'Assign to me' => 'Määritä minulle', + 'Me' => 'Minä', + 'Do not duplicate anything' => 'Älä kopioi mitään', + 'Projects management' => 'Projektien hallinta', + 'Users management' => 'Käyttäjien hallinta', + 'Groups management' => 'Ryhmien hallinta', + 'Create from another project' => 'Luo toisesta projektista', + 'open' => 'avoinna', + 'closed' => 'suljettu', + 'Priority:' => 'Prioriteetti:', + 'Reference:' => 'Viite:', + 'Complexity:' => 'Monimutkaisuus:', + 'Swimlane:' => 'Kaista:', + 'Column:' => 'Sarake:', + 'Position:' => 'Sijainti:', + 'Creator:' => 'Luonut:', + 'Time estimated:' => 'Arvioitu aika:', + '%s hours' => '%s tuntia', + 'Time spent:' => 'Käytetty aika:', + 'Created:' => 'Luotu:', + 'Modified:' => 'Muokattu:', + 'Completed:' => 'Valmistunut:', + 'Started:' => 'Aloitettu:', + 'Moved:' => 'Siirretty:', + 'Task #%d' => 'Tehtävä #%d', + 'Time format' => 'Ajan muoto', + 'Start date: ' => 'Aloituspäivämäärä:', + 'End date: ' => 'Lopetuspäivämäärä:', + 'New due date: ' => 'Uusi määräpäivä:', + 'Start date changed: ' => 'Aloituspäivämäärä muutettu:', + 'Disable personal projects' => 'Poista henkilökohtaiset projektit käytöstä', + 'Do you really want to remove this custom filter: "%s"?' => 'Haluatko todella poistaa tämän mukautetun suodattimen: "%s"?', + 'Remove a custom filter' => 'Poista mukautettu suodatin', + 'User activated successfully.' => 'Käyttäjä aktivoitu onnistuneesti.', + 'Unable to enable this user.' => 'Tämän käyttäjän aktivointi epäonnistui.', + 'User disabled successfully.' => 'Käyttäjä poistettu käytöstä onnistuneesti.', + 'Unable to disable this user.' => 'Tämän käyttäjän käytöstä poistaminen epäonnistui.', + 'All files have been uploaded successfully.' => 'Kaikki tiedostot on ladattu onnistuneesti.', + 'The maximum allowed file size is %sB.' => 'Suurin sallittu tiedostokoko on %sB.', + 'Drag and drop your files here' => 'Vedä ja pudota tiedostosi tähän', + 'choose files' => 'valitse tiedostot', + 'View profile' => 'Näytä profiili', + 'Two Factor' => 'Kaksivaiheinen', + 'Disable user' => 'Poista käyttäjä käytöstä', + 'Do you really want to disable this user: "%s"?' => 'Haluatko todella poistaa tämän käyttäjän käytöstä: "%s"?', + 'Enable user' => 'Ota käyttäjä käyttöön', + 'Do you really want to enable this user: "%s"?' => 'Haluatko todella ottaa tämän käyttäjän käyttöön: "%s"?', + 'Download' => 'Lataa', + 'Uploaded: %s' => 'Ladattu: %s', + 'Size: %s' => 'Koko: %s', + 'Uploaded by %s' => 'Ladannut %s', + 'Filename' => 'Tiedostonimi', + 'Size' => 'Koko', + 'Column created successfully.' => 'Sarake luotiin onnistuneesti.', + 'Another column with the same name exists in the project' => 'Projektissa on jo toinen sarake samalla nimellä', + 'Default filters' => 'Oletussuodattimet', + 'Your board doesn\'t have any columns!' => 'Taulussasi ei ole yhtään saraketta!', + 'Change column position' => 'Muuta sarakkeen sijaintia', + 'Switch to the project overview' => 'Vaihda projektin yleiskatsaukseen', + 'User filters' => 'Käyttäjäsuodattimet', + 'Category filters' => 'Luokkusuodattimet', + 'Upload a file' => 'Lataa tiedosto', + 'View file' => 'Näytä tiedosto', + 'Last activity' => 'Viimeisin aktiviteetti', + 'Change subtask position' => 'Muuta alitehtävän sijaintia', + 'This value must be greater than %d' => 'Tämän arvon on oltava suurempi kuin %d', + 'Another swimlane with the same name exists in the project' => 'Projektissa on jo toinen kaista samalla nimellä', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Esimerkki: https://example.kanboard.org/ (käytetään absoluuttisten URL-osoitteiden luomiseen)', + 'Actions duplicated successfully.' => 'Toiminnot kopioitu onnistuneesti.', + 'Unable to duplicate actions.' => 'Toimintoja ei voitu kopioida.', + 'Add a new action' => 'Lisää uusi toiminto', + 'Import from another project' => 'Tuo toisesta projektista', + 'There is no action at the moment.' => 'Tällä hetkellä ei ole toimintoja.', + 'Import actions from another project' => 'Tuo toiminnot toisesta projektista', + 'There is no available project.' => 'Ei käytettävissä olevia projekteja.', + 'Local File' => 'Paikallinen tiedosto', + 'Configuration' => 'Konfiguraatio', + 'PHP version:' => 'PHP-versio:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Käyttöjärjestelmän versio:', + 'Database version:' => 'Tietokannan versio:', + 'Browser:' => 'Selain:', + 'Task view' => 'Tehtävänäkymä', + 'Edit task' => 'Muokkaa tehtävää', + 'Edit description' => 'Muokkaa kuvausta', + 'New internal link' => 'Uusi sisäinen linkki', + 'Display list of keyboard shortcuts' => 'Näytä lista pikanäppäimistä', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Lataa avatar-kuvani', + 'Remove my image' => 'Poista kuvani', + 'The OAuth2 state parameter is invalid' => 'OAuth2 state -parametri on virheellinen', + 'User not found.' => 'Käyttäjää ei löydy.', + 'Search in activity stream' => 'Hae aktiviteettivirrasta', + 'My activities' => 'Omat aktiviteetit', + 'Activity until yesterday' => 'Aktiviteetti eiliseen asti', + 'Activity until today' => 'Aktiviteetti tähän päivään asti', + 'Search by creator: ' => 'Hae luojan mukaan:', + 'Search by creation date: ' => 'Hae luontipäivämäärän mukaan:', + 'Search by task status: ' => 'Hae tehtävän tilan mukaan:', + 'Search by task title: ' => 'Hae tehtävän otsikon mukaan:', + 'Activity stream search' => 'Aktiviteettivirran haku', + 'Projects where "%s" is manager' => 'Projektit, joissa "%s" on päällikkö', + 'Projects where "%s" is member' => 'Projektit, joissa "%s" on jäsen', + 'Open tasks assigned to "%s"' => 'Avoimet tehtävät, jotka on määritetty käyttäjälle "%s"', + 'Closed tasks assigned to "%s"' => 'Suljetut tehtävät, jotka on määritetty käyttäjälle "%s"', + 'Assign automatically a color based on a priority' => 'Määritä automaattisesti väri prioriteetin perusteella', + 'Overdue tasks for the project(s) "%s"' => 'Myöhässä olevat tehtävät projektille/projekteille "%s"', + 'Upload files' => 'Lataa tiedostoja', + 'Installed Plugins' => 'Asennetut lisäosat', + 'Plugin Directory' => 'Lisäosahakemisto', + 'Plugin installed successfully.' => 'Lisäosa asennettiin onnistuneesti.', + 'Plugin updated successfully.' => 'Lisäosa päivitettiin onnistuneesti.', + 'Plugin removed successfully.' => 'Lisäosa poistettiin onnistuneesti.', + 'Subtask converted to task successfully.' => 'Alitehtävä muunnettiin tehtäväksi onnistuneesti.', + 'Unable to convert the subtask.' => 'Alitehtävää ei voitu muuntaa.', + 'Unable to extract plugin archive.' => 'Lisäosan arkistoa ei voitu purkaa.', + 'Plugin not found.' => 'Lisäosaa ei löydy.', + 'You don\'t have the permission to remove this plugin.' => 'Sinulla ei ole oikeutta poistaa tätä lisäosaa.', + 'Unable to download plugin archive.' => 'Lisäosan arkiston lataaminen epäonnistui.', + 'Unable to write temporary file for plugin.' => 'Lisäosan väliaikaista tiedostoa ei voitu kirjoittaa.', + 'Unable to open plugin archive.' => 'Lisäosan arkistoa ei voitu avata.', + 'There is no file in the plugin archive.' => 'Lisäosan arkistossa ei ole tiedostoa.', + 'Create tasks in bulk' => 'Luo tehtäviä joukkona', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Kanboard-instanssiasi ei ole määritetty asentamaan lisäosia käyttöliittymän kautta.', + 'There is no plugin available.' => 'Lisäosia ei ole saatavilla.', + 'Install' => 'Asenna', + 'Update' => 'Päivitä', + 'Up to date' => 'Ajantasalla', + 'Not available' => 'Ei saatavilla', + 'Remove plugin' => 'Poista lisäosa', + 'Do you really want to remove this plugin: "%s"?' => 'Haluatko todella poistaa tämän lisäosan: "%s"?', + 'Uninstall' => 'Poista asennus', + 'Listing' => 'Listaus', + 'Metadata' => 'Metatiedot', + 'Manage projects' => 'Hallinnoi projekteja', + 'Convert to task' => 'Muunna tehtäväksi', + 'Convert sub-task to task' => 'Muunna alitehtävä tehtäväksi', + 'Do you really want to convert this sub-task to a task?' => 'Haluatko todella muuntaa tämän alitehtävän tehtäväksi?', + 'My task title' => 'Oman tehtävän otsikko', + 'Enter one task by line.' => 'Syötä yksi tehtävä riviä kohden.', + 'Number of failed login:' => 'Epäonnistuneiden kirjautumisten määrä:', + 'Account locked until:' => 'Tili lukittu:', + 'Email settings' => 'Sähköpostiasetukset', + 'Email sender address' => 'Sähköpostin lähettäjän osoite', + 'Email transport' => 'Sähköpostin siirto', + 'Webhook token' => 'Webhook-tunnus', + 'Project tags management' => 'Projektin tunnisteiden hallinta', + 'Tag created successfully.' => 'Tunniste luotiin onnistuneesti.', + 'Unable to create this tag.' => 'Tätä tunnistetta ei voitu luoda.', + 'Tag updated successfully.' => 'Tunniste päivitettiin onnistuneesti.', + 'Unable to update this tag.' => 'Tätä tunnistetta ei voitu päivittää.', + 'Tag removed successfully.' => 'Tunniste poistettiin onnistuneesti.', + 'Unable to remove this tag.' => 'Tämän tunnisteen poistaminen epäonnistui.', + 'Global tags management' => 'Globaalien tunnisteiden hallinta', + 'Tags' => 'Tunnisteet', + 'Tags management' => 'Tunnisteiden hallinta', + 'Add new tag' => 'Lisää uusi tunniste', + 'Edit a tag' => 'Muokkaa tunnistetta', + 'Project tags' => 'Projektin tunnisteet', + 'There is no specific tag for this project at the moment.' => 'Tällä hetkellä projektille ei ole erityistä tunnistetta.', + 'Tag' => 'Tunniste', + 'Remove a tag' => 'Poista tunniste', + 'Do you really want to remove this tag: "%s"?' => 'Haluatko todella poistaa tämän tunnisteen: "%s"?', + 'Global tags' => 'Globaalit tunnisteet', + 'There is no global tag at the moment.' => 'Tällä hetkellä ei ole globaalia tunnistetta.', + 'This field cannot be empty' => 'Tämä kenttä ei saa olla tyhjä', + 'Close a task when there is no activity in a specific column' => 'Sulje tehtävä, kun tietyssä sarakkeessa ei ole toimintaa', + '%s removed a subtask for the task #%d' => '%s poisti alitehtävän tehtävälle #%d', + '%s removed a comment on the task #%d' => '%s poisti kommentin tehtävästä #%d', + 'Comment removed on task #%d' => 'Kommentti poistettu tehtävästä #%d', + 'Subtask removed on task #%d' => 'Alitehtävä poistettu tehtävästä #%d', + 'Hide tasks in this column in the dashboard' => 'Piilota tehtävät tässä sarakkeessa hallintapaneelissa', + '%s removed a comment on the task %s' => '%s poisti kommentin tehtävästä %s', + '%s removed a subtask for the task %s' => '%s poisti alitehtävän tehtävästä %s', + 'Comment removed' => 'Kommentti poistettu', + 'Subtask removed' => 'Alitehtävä poistettu', + '%s set a new internal link for the task #%d' => '%s asetti uuden sisäisen linkin tehtävälle #%d', + '%s removed an internal link for the task #%d' => '%s poisti sisäisen linkin tehtävälle #%d', + 'A new internal link for the task #%d has been defined' => 'Uusi sisäinen linkki tehtävälle #%d on määritetty', + 'Internal link removed for the task #%d' => 'Sisäinen linkki poistettu tehtävälle #%d', + '%s set a new internal link for the task %s' => '%s asetti uuden sisäisen linkin tehtävälle %s', + '%s removed an internal link for the task %s' => '%s poisti sisäisen linkin tehtävälle %s', + 'Automatically set the due date on task creation' => 'Aseta automaattisesti määräpäivä tehtävän luonnin yhteydessä', + 'Move the task to another column when closed' => 'Siirrä tehtävä toiseen sarakkeeseen, kun se suljetaan', + 'Move the task to another column when not moved during a given period' => 'Siirrä tehtävä toiseen sarakkeeseen, kun sitä ei ole siirretty tietyn ajanjakson aikana', + 'Dashboard for %s' => 'Hallintapaneeli käyttäjälle %s', + 'Tasks overview for %s' => 'Tehtävien yleiskatsaus käyttäjälle %s', + 'Subtasks overview for %s' => 'Alitehtävien yleiskatsaus käyttäjälle %s', + 'Projects overview for %s' => 'Projektien yleiskatsaus käyttäjälle %s', + 'Activity stream for %s' => 'Aktiviteettivirta käyttäjälle %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Määritä väri, kun tehtävä siirretään tiettyyn kaistaan', + 'Assign a priority when the task is moved to a specific swimlane' => 'Määritä prioriteetti, kun tehtävä siirretään tiettyyn kaistaan', + 'User unlocked successfully.' => 'Käyttäjä avattiin onnistuneesti.', + 'Unable to unlock the user.' => 'Käyttäjää ei voitu avata.', + 'Move a task to another swimlane' => 'Siirrä tehtävä toiseen kaistaan', + 'Creator Name' => 'Luojan nimi', + 'Time spent and estimated' => 'Käytetty ja arvioitu aika', + 'Move position' => 'Siirrä sijaintia', + 'Move task to another position on the board' => 'Siirrä tehtävä toiseen sijaintiin taululla', + 'Insert before this task' => 'Lisää ennen tätä tehtävää', + 'Insert after this task' => 'Lisää tämän tehtävän jälkeen', + 'Unlock this user' => 'Avaa tämä käyttäjä', + 'Custom Project Roles' => 'Mukautetut projektiroolit', + 'Add a new custom role' => 'Lisää uusi mukautettu rooli', + 'Restrictions for the role "%s"' => 'Rajoitukset roolille "%s"', + 'Add a new project restriction' => 'Lisää uusi projektirajoitus', + 'Add a new drag and drop restriction' => 'Lisää uusi vedä ja pudota -rajoitus', + 'Add a new column restriction' => 'Lisää uusi sarakkeiden rajoitus', + 'Edit this role' => 'Muokkaa tätä roolia', + 'Remove this role' => 'Poista tämä rooli', + 'There is no restriction for this role.' => 'Tälle roolille ei ole rajoituksia.', + 'Only moving task between those columns is permitted' => 'Tehtävän siirtäminen on sallittua vain näiden sarakkeiden välillä', + 'Close a task in a specific column when not moved during a given period' => 'Sulje tehtävä tietyssä sarakkeessa, kun sitä ei ole siirretty tietyn ajanjakson aikana', + 'Edit columns' => 'Muokkaa sarakkeita', + 'The column restriction has been created successfully.' => 'Sarakkeiden rajoitus luotiin onnistuneesti.', + 'Unable to create this column restriction.' => 'Tätä sarakkeiden rajoitusta ei voitu luoda.', + 'Column restriction removed successfully.' => 'Sarakkeiden rajoitus poistettiin onnistuneesti.', + 'Unable to remove this restriction.' => 'Tämän rajoituksen poistaminen epäonnistui.', + 'Your custom project role has been created successfully.' => 'Mukautettu projektiroolisi luotiin onnistuneesti.', + 'Unable to create custom project role.' => 'Mukautettua projektiroolia ei voitu luoda.', + 'Your custom project role has been updated successfully.' => 'Mukautettu projektiroolisi on päivitetty onnistuneesti.', + 'Unable to update custom project role.' => 'Mukautetun projektiroolin päivittäminen epäonnistui.', + 'Custom project role removed successfully.' => 'Mukautettu projektirooli poistettiin onnistuneesti.', + 'Unable to remove this project role.' => 'Tämän projektiroolin poistaminen epäonnistui.', + 'The project restriction has been created successfully.' => 'Projektirajoitus luotiin onnistuneesti.', + 'Unable to create this project restriction.' => 'Tätä projektirajoitusta ei voitu luoda.', + 'Project restriction removed successfully.' => 'Projektirajoitus poistettiin onnistuneesti.', + 'You cannot create tasks in this column.' => 'Et voi luoda tehtäviä tähän sarakkeeseen.', + 'Task creation is permitted for this column' => 'Tehtävien luominen on sallittu tähän sarakkeeseen', + 'Closing or opening a task is permitted for this column' => 'Tehtävän sulkeminen tai avaaminen on sallittu tähän sarakkeeseen', + 'Task creation is blocked for this column' => 'Tehtävien luominen on estetty tähän sarakkeeseen', + 'Closing or opening a task is blocked for this column' => 'Tehtävän sulkeminen tai avaaminen on estetty tähän sarakkeeseen', + 'Task creation is not permitted' => 'Tehtävien luominen ei ole sallittua', + 'Closing or opening a task is not permitted' => 'Tehtävän sulkeminen tai avaaminen ei ole sallittua', + 'New drag and drop restriction for the role "%s"' => 'Uusi vedä ja pudota -rajoitus roolille "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Tähän rooliin kuuluvat henkilöt voivat siirtää tehtäviä vain lähde- ja kohdesarakkeen välillä.', + 'Remove a column restriction' => 'Poista sarakkeiden rajoitus', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Haluatko todella poistaa tämän sarakkeiden rajoituksen: "%s" to "%s"?', + 'New column restriction for the role "%s"' => 'Uusi sarakkeiden rajoitus roolille "%s"', + 'Rule' => 'Sääntö', + 'Do you really want to remove this column restriction?' => 'Haluatko todella poistaa tämän sarakkeiden rajoituksen?', + 'Custom roles' => 'Mukautetut roolit', + 'New custom project role' => 'Uusi mukautettu projektirooli', + 'Edit custom project role' => 'Muokkaa mukautettua projektiroolia', + 'Remove a custom role' => 'Poista mukautettu rooli', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Haluatko todella poistaa tämän mukautetun roolin: "%s"? Kaikista tähän rooliin määritetyistä henkilöistä tulee projektin jäseniä.', + 'There is no custom role for this project.' => 'Tälle projektille ei ole mukautettua roolia.', + 'New project restriction for the role "%s"' => 'Uusi projektirajoitus roolille "%s"', + 'Restriction' => 'Rajoitus', + 'Remove a project restriction' => 'Poista projektirajoitus', + 'Do you really want to remove this project restriction: "%s"?' => 'Haluatko todella poistaa tämän projektirajoituksen: "%s"?', + 'Duplicate to multiple projects' => 'Kopioi useisiin projekteihin', + 'This field is required' => 'Tämä kenttä on pakollinen', + 'Moving a task is not permitted' => 'Tehtävän siirtäminen ei ole sallittua', + 'This value must be in the range %d to %d' => 'Tämän arvon on oltava välillä %d–%d', + 'You are not allowed to move this task.' => 'Sinulla ei ole oikeutta siirtää tätä tehtävää.', + 'API User Access' => 'API-käyttäjän käyttöoikeus', + 'Preview' => 'Esikatselu', + 'Write' => 'Kirjoita', + 'Write your text in Markdown' => 'Kirjoita teksti Markdown-muodossa', + 'No personal API access token registered.' => 'Henkilökohtaista API-käyttöoikeustunnusta ei ole rekisteröity.', + 'Your personal API access token is "%s"' => 'Henkilökohtainen API-käyttöoikeustunnuksesi on "%s"', + 'Remove your token' => 'Poista tunnuksesi', + 'Generate a new token' => 'Luo uusi tunnus', + 'Showing %d-%d of %d' => 'Näytetään %d–%d / %d', + 'Outgoing Emails' => 'Lähtevät sähköpostit', + 'Add or change currency rate' => 'Lisää tai muuta valuuttakurssi', + 'Reference currency: %s' => 'Vertailuvaluutta: %s', + 'Add custom filters' => 'Lisää mukautettuja suodattimia', + 'Export' => 'Vie', + 'Add link label' => 'Lisää linkin selite', + 'Incompatible Plugins' => 'Yhteensopimattomat lisäosat', + 'Compatibility' => 'Yhteensopivuus', + 'Permissions and ownership' => 'Oikeudet ja omistajuus', + 'Priorities' => 'Prioriteetit', + 'Close this window' => 'Sulje tämä ikkuna', + 'Unable to upload this file.' => 'Tiedoston lataaminen epäonnistui.', + 'Import tasks' => 'Tuo tehtäviä', + 'Choose a project' => 'Valitse projekti', + 'Profile' => 'Profiili', + 'Application role' => 'Sovelluksen rooli', + '%d invitations were sent.' => '%d kutsua lähetettiin.', + '%d invitation was sent.' => '%d kutsu lähetettiin.', + 'Unable to create this user.' => 'Tämän käyttäjän luominen epäonnistui.', + 'Kanboard Invitation' => 'Kanboard-kutsu', + 'Visible on dashboard' => 'Näkyvissä hallintapaneelissa', + 'Created at:' => 'Luotu:', + 'Updated at:' => 'Päivitetty:', + 'There is no custom filter.' => 'Mukautettuja suodattimia ei ole.', + 'New User' => 'Uusi käyttäjä', + 'Authentication' => 'Tunnistautuminen', + 'If checked, this user will use a third-party system for authentication.' => 'Jos valittu, tämä käyttäjä käyttää kolmannen osapuolen järjestelmää tunnistautumiseen.', + 'The password is necessary only for local users.' => 'Salasana on tarpeen vain paikallisille käyttäjille.', + 'You have been invited to register on Kanboard.' => 'Sinut on kutsuttu rekisteröitymään Kanboardiin.', + 'Click here to join your team' => 'Klikkaa tästä liittyäksesi tiimiisi', + 'Invite people' => 'Kutsu ihmisiä', + 'Emails' => 'Sähköpostit', + 'Enter one email address by line.' => 'Syötä yksi sähköpostiosoite riviä kohden.', + 'Add these people to this project' => 'Lisää nämä henkilöt tähän projektiin', + 'Add this person to this project' => 'Lisää tämä henkilö tähän projektiin', + 'Sign-up' => 'Rekisteröidy', + 'Credentials' => 'Tunnistetiedot', + 'New user' => 'Uusi käyttäjä', + 'This username is already taken' => 'Tämä käyttäjänimi on jo käytössä', + 'Your profile must have a valid email address.' => 'Profiilillasi on oltava kelvollinen sähköpostiosoite.', + 'TRL - Turkish Lira' => 'TRL - Turkin liira', + 'The project email is optional and could be used by several plugins.' => 'Projektin sähköposti on valinnainen ja sitä voivat käyttää useat lisäosat.', + 'The project email must be unique across all projects' => 'Projektin sähköpostin on oltava yksilöllinen kaikissa projekteissa', + 'The email configuration has been disabled by the administrator.' => 'Sähköpostiasetukset on poistettu käytöstä järjestelmänvalvojan toimesta.', + 'Close this project' => 'Sulje tämä projekti', + 'Open this project' => 'Avaa tämä projekti', + 'Close a project' => 'Sulje projekti', + 'Do you really want to close this project: "%s"?' => 'Haluatko todella sulkea tämän projektin: "%s"?', + 'Reopen a project' => 'Avaa projekti uudelleen', + 'Do you really want to reopen this project: "%s"?' => 'Haluatko todella avata tämän projektin uudelleen: "%s"?', + 'This project is open' => 'Tämä projekti on avoinna', + 'This project is closed' => 'Tämä projekti on suljettu', + 'Unable to upload files, check the permissions of your data folder.' => 'Tiedostojen lataaminen epäonnistui, tarkista datakansion oikeudet.', + 'Another category with the same name exists in this project' => 'Projektissa on jo toinen luokka samalla nimellä', + 'Comment sent by email successfully.' => 'Kommentti lähetettiin sähköpostitse onnistuneesti.', + 'Sent by email to "%s" (%s)' => 'Lähetetty sähköpostitse osoitteeseen "%s" (%s)', + 'Unable to read uploaded file.' => 'Ladattua tiedostoa ei voitu lukea.', + 'Database uploaded successfully.' => 'Tietokanta ladattiin onnistuneesti.', + 'Task sent by email successfully.' => 'Tehtävä lähetettiin sähköpostitse onnistuneesti.', + 'There is no category in this project.' => 'Projektissa ei ole luokkia.', + 'Send by email' => 'Lähetä sähköpostitse', + 'Create and send a comment by email' => 'Luo ja lähetä kommentti sähköpostitse', + 'Subject' => 'Aihe', + 'Upload the database' => 'Lataa tietokanta', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Voit ladata aiemmin ladatun Sqlite-tietokannan (Gzip-muodossa).', + 'Database file' => 'Tietokantatiedosto', + 'Upload' => 'Lataa', + 'Your project must have at least one active swimlane.' => 'Projektissasi on oltava vähintään yksi aktiivinen kaista.', + 'Project: %s' => 'Projekti: %s', + 'Automatic action not found: "%s"' => 'Automaattista toimintoa ei löytynyt: "%s"', + '%d projects' => '%d projektia', + '%d project' => '%d projekti', + 'There is no project.' => 'Projekteja ei ole.', + 'Sort' => 'Järjestys', + 'Project ID' => 'Projektin ID', + 'Project name' => 'Projektin nimi', + 'Public' => 'Julkinen', + 'Personal' => 'Henkilökohtainen', + '%d tasks' => '%d tehtävää', + '%d task' => '%d tehtävä', + 'Task ID' => 'Tehtävän ID', + 'Assign automatically a color when due date is expired' => 'Määritä automaattisesti väri, kun määräpäivä on ohitettu', + 'Total score in this column across all swimlanes' => 'Kokonaispistemäärä tässä sarakkeessa kaikilla kaistoilla', + 'HRK - Kuna' => 'HRK - Kroatian kuna', + 'ARS - Argentine Peso' => 'ARS - Argentiinan peso', + 'COP - Colombian Peso' => 'COP - Kolumbian peso', + '%d groups' => '%d ryhmää', + '%d group' => '%d ryhmä', + 'Group ID' => 'Ryhmän ID', + 'External ID' => 'Ulkoinen ID', + '%d users' => '%d käyttäjää', + '%d user' => '%d käyttäjä', + 'Hide subtasks' => 'Piilota alitehtävät', + 'Show subtasks' => 'Näytä alitehtävät', + 'Authentication Parameters' => 'Tunnistautumisparametrit', + 'API Access' => 'API-käyttöoikeus', + 'No users found.' => 'Käyttäjiä ei löytynyt.', + 'User ID' => 'Käyttäjän ID', + 'Notifications are activated' => 'Ilmoitukset ovat aktivoitu', + 'Notifications are disabled' => 'Ilmoitukset ovat poistettu käytöstä', + 'User disabled' => 'Käyttäjä poistettu käytöstä', + '%d notifications' => '%d ilmoitusta', + '%d notification' => '%d ilmoitus', + 'There is no external integration installed.' => 'Ulkoisia integraatioita ei ole asennettu.', + 'You are not allowed to update tasks assigned to someone else.' => 'Sinulla ei ole oikeutta päivittää muiden määrittämiä tehtäviä.', + 'You are not allowed to change the assignee.' => 'Sinulla ei ole oikeutta muuttaa määrättyä henkilöä.', + 'Task suppression is not permitted' => 'Tehtävien poistaminen ei ole sallittua', + 'Changing assignee is not permitted' => 'Määrätyn henkilön vaihtaminen ei ole sallittua', + 'Update only assigned tasks is permitted' => 'Vain määrättyjen tehtävien päivittäminen on sallittua', + 'Only for tasks assigned to the current user' => 'Vain tehtäviin, jotka on määritetty nykyiselle käyttäjälle', + 'My projects' => 'Omat projektini', + 'You are not a member of any project.' => 'Et ole minkään projektin jäsen.', + 'My subtasks' => 'Omat alitehtävät', + '%d subtasks' => '%d alitehtävää', + '%d subtask' => '%d alitehtävä', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Vain tehtävän siirtäminen näiden sarakkeiden välillä on sallittua nykyiselle käyttäjälle määritetyille tehtäville', + '[DUPLICATE]' => '[KOPIO]', + 'DKK - Danish Krona' => 'DKK - Tanskan kruunu', + 'Remove user from group' => 'Poista käyttäjä ryhmästä', + 'Assign the task to its creator' => 'Määritä tehtävä sen luojalle', + 'This task was sent by email to "%s" with subject "%s".' => 'Tämä tehtävä lähetettiin sähköpostitse "%s" aiheella "%s".', + 'Predefined Email Subjects' => 'Ennalta määritellyt sähköpostin aiheet', + 'Write one subject by line.' => 'Kirjoita yksi aihe riviä kohden.', + 'Create another link' => 'Luo toinen linkki', + 'BRL - Brazilian Real' => 'BRL - Brasilian real', + 'Add a new Kanboard task' => 'Lisää uusi Kanboard-tehtävä', + 'Subtask not started' => 'Alitehtävä ei aloitettu', + 'Subtask currently in progress' => 'Alitehtävä on parhaillaan kesken', + 'Subtask completed' => 'Alitehtävä valmis', + 'Subtask added successfully.' => 'Alitehtävä lisättiin onnistuneesti.', + '%d subtasks added successfully.' => '%d alitehtävää lisättiin onnistuneesti.', + 'Enter one subtask by line.' => 'Syötä yksi alitehtävä riviä kohden.', + 'Predefined Contents' => 'Ennalta määritellyt sisällöt', + 'Predefined contents' => 'Ennalta määritellyt sisällöt', + 'Predefined Task Description' => 'Ennalta määritelty tehtävän kuvaus', + 'Do you really want to remove this template? "%s"' => 'Haluatko todella poistaa tämän mallin? "%s"', + 'Add predefined task description' => 'Lisää ennalta määritelty tehtävän kuvaus', + 'Predefined Task Descriptions' => 'Ennalta määritellyt tehtävän kuvaukset', + 'Template created successfully.' => 'Malli luotiin onnistuneesti.', + 'Unable to create this template.' => 'Malliin luominen epäonnistui.', + 'Template updated successfully.' => 'Malli päivitettiin onnistuneesti.', + 'Unable to update this template.' => 'Mallin päivittäminen epäonnistui.', + 'Template removed successfully.' => 'Malli poistettiin onnistuneesti.', + 'Unable to remove this template.' => 'Mallin poistaminen epäonnistui.', + 'Template for the task description' => 'Malli tehtävän kuvaukseen', + 'The start date is greater than the end date' => 'Aloituspäivämäärä on suurempi kuin lopetuspäivämäärä', + 'Tags must be separated by a comma' => 'Tunnisteet on erotettava pilkulla', + 'Only the task title is required' => 'Vain tehtävän otsikko on pakollinen', + 'Creator Username' => 'Luojan käyttäjänimi', + 'Color Name' => 'Värin nimi', + 'Column Name' => 'Sarakkeen nimi', + 'Swimlane Name' => 'Kaistan nimi', + 'Time Estimated' => 'Arvioitu aika', + 'Time Spent' => 'Käytetty aika', + 'External Link' => 'Ulkoinen linkki', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Tämä ominaisuus mahdollistaa iCal-syötteen, RSS-syötteen ja julkisen taulunäkymän.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Pysäytä kaikkien alitehtävien ajastin, kun tehtävä siirretään toiseen sarakkeeseen', + 'Subtask Title' => 'Alitehtävän otsikko', + 'Add a subtask and activate the timer when moving a task to another column' => 'Lisää alitehtävä ja aktivoi ajastin, kun tehtävä siirretään toiseen sarakkeeseen', + 'days' => 'päivää', + 'minutes' => 'minuuttia', + 'seconds' => 'sekuntia', + 'Assign automatically a color when preset start date is reached' => 'Määritä automaattisesti väri, kun esiasetettu aloituspäivämäärä saavutetaan', + 'Move the task to another column once a predefined start date is reached' => 'Siirrä tehtävä toiseen sarakkeeseen, kun es määritelty aloituspäivämäärä on saavutettu', + 'This task is now linked to the task %s with the relation "%s"' => 'Tämä tehtävä on nyt linkitetty tehtävään %s suhteella "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Linkki tehtävään %s suhteella "%s" on poistettu', + 'Custom Filter:' => 'Mukautettu suodatin:', + 'Unable to find this group.' => 'Ryhmää ei löytynyt.', + '%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 sarakkeeseen "%s" paikkaan %d', + '%s moved the task #%d to the swimlane "%s"' => '%s siirsi tehtävän #%d kaistaan "%s"', + '%sh spent' => '%sh käytetty', + '%sh estimated' => '%sh arvioitu', + 'Select All' => 'Valitse kaikki', + 'Unselect All' => 'Poista kaikkien valinta', + 'Apply action' => 'Suorita toiminto', + 'Move selected tasks to another column or swimlane' => 'Siirrä valitut tehtävät toiseen sarakkeeseen tai kaistaan', + 'Edit tasks in bulk' => 'Muokkaa tehtäviä joukkona', + 'Choose the properties that you would like to change for the selected tasks.' => 'Valitse ominaisuudet, jotka haluat muuttaa valituille tehtäville.', + 'Configure this project' => 'Määritä tämä projekti', + 'Start now' => 'Aloita nyt', + '%s removed a file from the task #%d' => '%s poisti tiedoston tehtävästä #%d', + 'Attachment removed from task #%d: %s' => 'Liite poistettu tehtävästä #%d: %s', + 'No color' => 'Ei väriä', + 'Attachment removed "%s"' => 'Liite poistettu "%s"', + '%s removed a file from the task %s' => '%s poisti tiedoston tehtävästä %s', + 'Move the task to another swimlane when assigned to a user' => 'Siirrä tehtävä toiseen kaistaan, kun se on määritetty käyttäjälle', + 'Destination swimlane' => 'Kohdekaista', + 'Assign a category when the task is moved to a specific swimlane' => 'Määritä luokka, kun tehtävä siirretään tiettyyn kaistaan', + 'Move the task to another swimlane when the category is changed' => 'Siirrä tehtävä toiseen kaistaan, kun luokkaa muutetaan', + 'Reorder this column by priority (ASC)' => 'Järjestä tämä sarake uudelleen prioriteetin mukaan (nouseva)', + 'Reorder this column by priority (DESC)' => 'Järjestä tämä sarake uudelleen prioriteetin mukaan (laskeva)', + 'Reorder this column by assignee and priority (ASC)' => 'Järjestä tämä sarake uudelleen määrätyn ja prioriteetin mukaan (nouseva)', + 'Reorder this column by assignee and priority (DESC)' => 'Järjestä tämä sarake uudelleen määrätyn ja prioriteetin mukaan (laskeva)', + 'Reorder this column by assignee (A-Z)' => 'Järjestä tämä sarake uudelleen määrätyn mukaan (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Järjestä tämä sarake uudelleen määrätyn mukaan (Z-A)', + 'Reorder this column by due date (ASC)' => 'Järjestä tämä sarake uudelleen määräpäivän mukaan (nouseva)', + 'Reorder this column by due date (DESC)' => 'Järjestä tämä sarake uudelleen määräpäivän mukaan (laskeva)', + 'Reorder this column by id (ASC)' => 'Järjestä tämä sarake uudelleen ID:n mukaan (nouseva)', + 'Reorder this column by id (DESC)' => 'Järjestä tämä sarake uudelleen ID:n mukaan (laskeva)', + '%s moved the task #%d "%s" to the project "%s"' => '%s siirsi tehtävän #%d "%s" projektiin "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Tehtävä #%d "%s" on siirretty projektiin "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Siirrä tehtävä toiseen sarakkeeseen, kun määräpäivään on vähemmän kuin tietty määrä päiviä', + 'Automatically update the start date when the task is moved away from a specific column' => 'Päivitä aloituspäivämäärä automaattisesti, kun tehtävä siirretään pois tietystä sarakkeesta', + 'HTTP Client:' => 'HTTP-asiakas:', + 'Assigned' => 'Määritetty', + 'Task limits apply to each swimlane individually' => 'Tehtävärajoitukset koskevat kutakin kaistaa erikseen', + 'Column task limits apply to each swimlane individually' => 'Sarakkeen tehtävärajoitukset koskevat kutakin kaistaa erikseen', + 'Column task limits are applied to each swimlane individually' => 'Sarakkeen tehtävärajoitukset koskevat kutakin kaistaa erikseen', + 'Column task limits are applied across swimlanes' => 'Sarakkeen tehtävärajoitukset koskevat kaikkia kaistoja', + 'Task limit: ' => 'Tehtävärajoitus:', + 'Change to global tag' => 'Vaihda globaaliksi tunnisteeksi', + 'Do you really want to make the tag "%s" global?' => 'Haluatko todella tehdä tunnisteesta "%s" globaalin?', + 'Enable global tags for this project' => 'Ota globaalit tunnisteet käyttöön tässä projektissa', + 'Group membership(s):' => 'Ryhmäjäsenyys (jäsenyydet):', + '%s is a member of the following group(s): %s' => '%s on jäsen seuraavissa ryhmissä: %s', + '%d/%d group(s) shown' => '%d/%d ryhmä(t) näytetty', + 'Subtask creation or modification' => 'Alitehtävän luominen tai muokkaaminen', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Määritä tehtävä tietylle käyttäjälle, kun tehtävä siirretään tiettyyn kaistaan', + 'Comment' => 'Kommentti', + 'Collapse vertically' => 'Kutista pystysuunnassa', + 'Expand vertically' => 'Laajenna pystysuunnassa', + 'MXN - Mexican Peso' => 'MXN - Meksikon peso', + 'Estimated vs actual time per column' => 'Arvioitu vs. todellinen aika saraketta kohden', + 'HUF - Hungarian Forint' => 'HUF - Unkarin forintti', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Sinun on valittava tiedosto ladattavaksi avatariksesi!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Lataamasi tiedosto ei ole kelvollinen kuva! (Vain *.gif, *.jpg, *.jpeg ja *.png on sallittu!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Aseta automaattisesti määräpäivä, kun tehtävä siirretään pois tietystä sarakkeesta', + 'No other projects found.' => 'Muita projekteja ei löytynyt.', + 'Tasks copied successfully.' => 'Tehtävät kopioitu onnistuneesti.', + 'Unable to copy tasks.' => 'Tehtäviä ei voitu kopioida.', + 'Theme' => 'Teema', + 'Theme:' => 'Teema:', + 'Light theme' => 'Vaalea teema', + 'Dark theme' => 'Tumma teema', + 'Automatic theme - Sync with system' => 'Automaattinen teema - Synkronoi järjestelmän kanssa', + 'Application managers or more' => 'Sovelluksen hallitsijat tai enemmän', + 'Administrators' => 'Ylläpitäjät', + 'Visibility:' => 'Näkyvyys:', + 'Standard users' => 'Standardikäyttäjät', + 'Visibility is required' => 'Näkyvyys on pakollinen', + 'The visibility should be an app role' => 'Näkyvyyden tulisi olla sovellusrooli', + 'Reply' => 'Vastaa', + '%s wrote: ' => '%s kirjoitti:', + 'Number of visible tasks in this column and swimlane' => 'Tässä sarakkeessa ja kaistassa näkyvien tehtävien määrä', + 'Number of tasks in this swimlane' => 'Tehtävien määrä tässä kaistassa', + 'Unable to find another subtask in progress, you can close this window.' => 'Toista kesken olevaa alitehtävää ei löytynyt, voit sulkea tämän ikkunan.', + 'This theme is invalid' => 'Tämä teema on virheellinen', + 'This role is invalid' => 'Tämä rooli on virheellinen', + 'This timezone is invalid' => 'Tämä aikavyöhyke on virheellinen', + 'This language is invalid' => 'Tämä kieli on virheellinen', + 'This URL is invalid' => 'Tämä URL on virheellinen', + 'Date format invalid' => 'Päivämäärämuoto virheellinen', + 'Time format invalid' => 'Ajan muoto virheellinen', + 'Invalid Mail transport' => 'Virheellinen sähköpostin siirto', + 'Color invalid' => 'Väri virheellinen', + 'This value must be greater or equal to %d' => 'Tämän arvon on oltava suurempi tai yhtä suuri kuin %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Lisää BOM tiedoston alkuun (vaaditaan Microsoft Excelille)', + 'Just add these tag(s)' => 'Lisää vain nämä tunnisteet', + 'Remove internal link(s)' => 'Poista sisäinen linkki/linkit', + 'Import tasks from another project' => 'Tuo tehtäviä toisesta projektista', + 'Select the project to copy tasks from' => 'Valitse projekti, josta kopioida tehtäviä', + 'The total maximum allowed attachments size is %sB.' => 'Sallittu liitteiden kokonaiskoko on %sB.', + 'Add attachments' => 'Lisää liitteitä', + 'Task #%d "%s" is overdue' => 'Tehtävä #%d "%s" on myöhässä', + 'Enable notifications by default for all new users' => 'Ota ilmoitukset käyttöön oletuksena kaikille uusille käyttäjille', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Määritä tehtävä sen luojalle tietyissä sarakkeissa, jos vastuuhenkilöä ei ole asetettu manuaalisesti', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Määritä tehtävä kirjautuneelle käyttäjälle siirrettäessä määritettyyn sarakkeeseen, jos käyttäjää ei ole määritetty', +]; diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php new file mode 100644 index 0000000..5b289c5 --- /dev/null +++ b/app/Locale/fr_FR/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Aucun', + 'Edit' => 'Modifier', + 'Remove' => 'Supprimer', + 'Yes' => 'Oui', + 'No' => 'Non', + 'cancel' => 'annuler', + 'or' => 'ou', + 'Yellow' => 'Jaune', + 'Blue' => 'Bleu', + 'Green' => 'Vert', + 'Purple' => 'Violet', + 'Red' => 'Rouge', + 'Orange' => 'Orange', + 'Grey' => 'Gris', + 'Brown' => 'Marron', + 'Deep Orange' => 'Orange foncé', + 'Dark Grey' => 'Gris foncé', + 'Pink' => 'Rose', + 'Teal' => 'Turquoise', + 'Cyan' => 'Bleu intense', + 'Lime' => 'Vert citron', + 'Light Green' => 'Vert clair', + 'Amber' => 'Ambre', + 'Save' => 'Enregistrer', + 'Login' => 'Connexion', + 'Official website:' => 'Site web officiel :', + 'Unassigned' => 'Non assigné', + 'View this task' => 'Voir cette tâche', + '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' => 'Identifiant', + 'Password' => 'Mot de passe', + 'Administrator' => 'Administrateur', + 'Sign in' => 'Connexion', + 'Users' => 'Utilisateurs', + 'Forbidden' => 'Accès interdit', + 'Access Forbidden' => 'Accès interdit', + 'Edit user' => 'Modifier un utilisateur', + 'Logout' => 'Déconnexion', + 'Bad username or password' => 'Identifiant ou mot de passe incorrect', + 'Edit project' => 'Modifier le projet', + 'Name' => 'Nom', + 'Projects' => 'Projets', + 'No project' => 'Aucun projet', + 'Project' => 'Projet', + 'Status' => 'État', + 'Tasks' => 'Tâches', + 'Board' => 'Tableau', + 'Actions' => 'Actions', + 'Inactive' => 'Inactif', + 'Active' => 'Actif', + 'Unable to update this board.' => 'Impossible de mettre à jour ce 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 »', + 'Add a new column' => 'Ajouter une nouvelle colonne', + 'Title' => 'Titre', + 'Assigned to %s' => 'Assigné à %s', + 'Remove a column' => 'Supprimer une colonne', + '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 » ?', + 'Settings' => 'Préférences', + 'Application settings' => 'Paramètres de l\'application', + 'Language' => 'Langue', + 'Webhook token:' => 'Jeton de sécurité pour les webhooks :', + 'API token:' => 'Jeton de sécurité pour l\'API :', + 'Database size:' => 'Taille de la base de données :', + 'Download the database' => 'Télécharger la base de données', + 'Optimize the database' => 'Optimiser la base de données', + '(VACUUM command)' => '(Commande VACUUM)', + '(Gzip compressed Sqlite file)' => '(Fichier SQLite compressé en Gzip)', + 'Close a task' => 'Fermer une tâche', + 'Column' => 'Colonne', + 'Color' => 'Couleur', + 'Assignee' => 'Personne assignée', + 'Create another task' => 'Créer une autre tâche', + 'New task' => 'Nouvelle tâche', + 'Open a task' => 'Ouvrir une tâche', + 'Do you really want to open this task: "%s"?' => 'Voulez-vous vraiment ouvrir cette tâche : « %s » ?', + 'Back to the board' => 'Retour au tableau', + 'There is nobody assigned' => 'Il n\'y a personne d\'assigné à cette tâche', + 'Column on the board:' => 'Colonne sur le tableau : ', + 'Close this task' => 'Fermer cette tâche', + 'Open this task' => 'Ouvrir cette tâche', + 'There is no description.' => 'Il n\'y a pas de description.', + 'Add a new task' => 'Ajouter une nouvelle tâche', + 'The username is required' => 'Le nom d\'utilisateur est obligatoire', + 'The maximum length is %d characters' => 'La longueur maximale est de %d caractères', + 'The minimum length is %d characters' => 'La longueur minimale est de %d caractères', + 'The password is required' => 'Le mot de passe est obligatoire', + 'This value must be an integer' => 'Cette valeur doit être un entier', + 'The username must be unique' => 'Le nom d\'utilisateur doit être unique', + 'The user id is required' => 'L\'id de l\'utilisateur est obligatoire', + 'Passwords don\'t match' => 'Les mots de passe ne correspondent pas', + 'The confirmation is required' => 'La confirmation est requise', + 'The project is required' => 'Le projet est obligatoire', + 'The id is required' => 'L\'identifiant est obligatoire', + 'The project id is required' => 'L\'identifiant du projet est obligatoire', + 'The project name is required' => 'Le nom du projet est obligatoire', + 'The title is required' => 'Le titre est obligatoire', + 'Settings saved successfully.' => 'Paramètres sauvegardés avec succès.', + 'Unable to save your settings.' => 'Impossible de sauvegarder vos réglages.', + 'Database optimization done.' => 'Optimisation de la base de données terminée.', + 'Your project has been created successfully.' => 'Votre projet a été créé avec succès.', + 'Unable to create your project.' => 'Impossible de créer un projet.', + 'Project updated successfully.' => 'Votre projet a été mis à jour avec succès.', + 'Unable to update this project.' => 'Impossible de mettre à jour ce projet.', + 'Unable to remove this project.' => 'Impossible de supprimer ce projet.', + 'Project removed successfully.' => 'Votre projet a été supprimé avec succès.', + 'Project activated successfully.' => 'Votre projet a été activé avec succès.', + 'Unable to activate this project.' => 'Impossible d\'activer ce projet.', + 'Project disabled successfully.' => 'Votre projet a été désactivé avec succès.', + 'Unable to disable this project.' => 'Impossible de désactiver ce projet.', + 'Unable to open this task.' => 'Impossible d\'ouvrir cette tâche.', + 'Task opened successfully.' => 'Tâche ouverte avec succès.', + 'Unable to close this task.' => 'Impossible de fermer cette tâche.', + 'Task closed successfully.' => 'Tâche fermée avec succès.', + 'Unable to update your task.' => 'Impossible de modifier cette tâche.', + 'Task updated successfully.' => 'Tâche mise à jour avec succès.', + 'Unable to create your task.' => 'Impossible de créer cette tâche.', + 'Task created successfully.' => 'Tâche créée avec succès.', + 'User created successfully.' => 'Utilisateur créé avec succès.', + 'Unable to create your user.' => 'Impossible de créer cet utilisateur.', + 'User updated successfully.' => 'Utilisateur mis à jour avec succès.', + 'User removed successfully.' => 'Utilisateur supprimé avec succès.', + 'Unable to remove this user.' => 'Impossible de supprimer cet utilisateur.', + 'Board updated successfully.' => 'Tableau mis à jour avec succès.', + 'Ready' => 'Prêt', + 'Backlog' => 'En attente', + 'Work in progress' => 'En cours', + 'Done' => 'Terminé', + 'Application version:' => 'Version de l\'application :', + 'Id' => 'Id.', + 'Public link' => 'Lien public', + 'Timezone' => 'Fuseau horaire', + 'Sorry, I didn\'t find this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !', + 'Page not found' => 'Page introuvable', + 'Complexity' => 'Complexité', + 'Task limit' => 'Tâches Max.', + 'Task count' => 'Nombre de tâches', + 'User' => 'Utilisateur', + 'Comments' => 'Commentaires', + 'Comment is required' => 'Le commentaire est obligatoire', + 'Comment added successfully.' => 'Commentaire ajouté avec succès.', + 'Unable to create your comment.' => 'Impossible de sauvegarder votre commentaire.', + 'Due Date' => 'Date d\'échéance', + 'Invalid date' => 'Date invalide', + 'Automatic actions' => 'Actions automatisées', + 'Your automatic action has been created successfully.' => 'Votre action automatisée a été ajoutée avec succès.', + 'Unable to create your automatic action.' => 'Impossible de créer votre action automatisée.', + 'Remove an action' => 'Supprimer une action', + 'Unable to remove this action.' => 'Impossible de supprimer cette action', + 'Action removed successfully.' => 'Action supprimée avec succès.', + 'Automatic actions for the project "%s"' => 'Actions automatisées pour le projet « %s »', + 'Add an action' => 'Ajouter une action', + 'Event name' => 'Nom de l\'évènement', + 'Action' => 'Action', + 'Event' => 'Évènement', + 'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'évènement sélectionné se déclenche, exécuter l\'action correspondante.', + 'Next step' => 'Étape suivante', + 'Define action parameters' => 'Définition des paramètres de l\'action', + 'Do you really want to remove this action: "%s"?' => 'Voulez-vous vraiment supprimer cette action « %s » ?', + 'Remove an automatic action' => 'Supprimer une action automatisée', + 'Assign the task to a specific user' => 'Assigner la tâche à un utilisateur spécifique', + 'Assign the task to the person who does the action' => 'Assigner la tâche à la personne qui fait l\'action', + 'Duplicate the task to another project' => 'Dupliquer la tâche vers un autre projet', + 'Move a task to another column' => 'Déplacement d\'une tâche vers une autre colonne', + 'Task modification' => 'Modification d\'une tâche', + 'Task creation' => 'Création d\'une tâche', + 'Closing a task' => 'Fermeture d\'une tâche', + 'Assign a color to a specific user' => 'Assigner une couleur à un utilisateur', + 'Position' => 'Position', + 'Duplicate to project' => 'Dupliquer dans un autre projet', + 'Duplicate' => 'Dupliquer', + 'Link' => 'Lien', + 'Comment updated successfully.' => 'Commentaire mis à jour avec succès.', + 'Unable to update your comment.' => 'Impossible de supprimer votre commentaire.', + 'Remove a comment' => 'Supprimer un commentaire', + 'Comment removed successfully.' => 'Commentaire supprimé avec succès.', + 'Unable to remove this comment.' => 'Impossible de supprimer ce commentaire.', + 'Do you really want to remove this comment?' => 'Voulez-vous vraiment supprimer ce commentaire ?', + 'Current password for the user "%s"' => 'Mot de passe actuel pour l\'utilisateur « %s »', + 'The current password is required' => 'Le mot de passe actuel est obligatoire', + 'Wrong password' => 'Mot de passe invalide', + 'Unknown' => 'Inconnu', + 'Last logins' => 'Dernières connexions', + 'Login date' => 'Date de connexion', + 'Authentication method' => 'Méthode d\'authentification', + 'IP address' => 'Adresse IP', + 'User agent' => 'Agent utilisateur', + 'Persistent connections' => 'Connexions persistantes', + 'No session.' => 'Aucune session.', + 'Expiration date' => 'Date d\'expiration', + 'Remember Me' => 'Connexion automatique', + 'Creation date' => 'Date de création', + 'Everybody' => 'Tout le monde', + 'Open' => 'Ouvert', + 'Closed' => 'Fermé', + 'Search' => 'Rechercher', + 'Nothing found.' => 'Rien trouvé.', + 'Due date' => 'Date d\'échéance', + 'Description' => 'Description', + '%d comments' => '%d commentaires', + '%d comment' => '%d commentaire', + 'Email address invalid' => 'Adresse email invalide', + 'Your external account is not linked anymore to your profile.' => 'Votre compte externe n\'est plus relié à votre profil.', + 'Unable to unlink your external account.' => 'Impossible de supprimer votre compte externe.', + 'External authentication failed' => 'L’authentification externe a échoué', + 'Your external account is linked to your profile successfully.' => 'Votre compte externe est désormais lié à votre profil.', + 'Email' => 'Email', + 'Task removed successfully.' => 'Tâche supprimée avec succès.', + 'Unable to remove this task.' => 'Impossible de supprimer cette tâche.', + 'Remove a task' => 'Supprimer une tâche', + 'Do you really want to remove this task: "%s"?' => 'Voulez-vous vraiment supprimer cette tâche « %s » ?', + 'Assign automatically a color based on a category' => 'Assigner automatiquement une couleur par rapport à une catégorie définie', + 'Assign automatically a category based on a color' => 'Assigner automatiquement une catégorie par rapport à une couleur définie', + 'Task creation or modification' => 'Création ou modification d\'une tâche', + 'Category' => 'Catégorie', + 'Category:' => 'Catégorie :', + 'Categories' => 'Catégories', + 'Your category has been created successfully.' => 'Votre catégorie a été créée avec succès.', + 'This category has been updated successfully.' => 'Cette catégorie a été mise à jour avec succès.', + 'Unable to update this category.' => 'Impossible de mettre à jour cette catégorie.', + 'Remove a category' => 'Supprimer une catégorie', + 'Category removed successfully.' => 'Catégorie supprimée avec succès.', + 'Unable to remove this category.' => 'Impossible de supprimer cette catégorie.', + 'Category modification for the project "%s"' => 'Modification d\'une catégorie pour le projet « %s »', + 'Category Name' => 'Nom de la catégorie', + 'Add a new category' => 'Ajouter une nouvelle catégorie', + 'Do you really want to remove this category: "%s"?' => 'Voulez-vous vraiment supprimer cette catégorie « %s » ?', + 'All categories' => 'Toutes les catégories', + 'No category' => 'Aucune catégorie', + 'The name is required' => 'Le nom est requis', + 'Remove a file' => 'Supprimer un fichier', + 'Unable to remove this file.' => 'Impossible de supprimer ce fichier.', + 'File removed successfully.' => 'Fichier supprimé avec succès.', + 'Attach a document' => 'Joindre un document', + 'Do you really want to remove this file: "%s"?' => 'Voulez-vous vraiment supprimer ce fichier « %s » ?', + 'Attachments' => 'Pièces-jointes', + 'Edit the task' => 'Modifier la tâche', + 'Add a comment' => 'Ajouter un commentaire', + 'Edit a comment' => 'Modifier un commentaire', + 'Summary' => 'Résumé', + 'Time tracking' => 'Suivi du temps', + 'Estimate:' => 'Estimation :', + 'Spent:' => 'Passé :', + 'Do you really want to remove this sub-task?' => 'Voulez-vous vraiment supprimer cette sous-tâche ?', + 'Remaining:' => 'Restant :', + 'hours' => 'heures', + 'estimated' => 'estimé', + 'Sub-Tasks' => 'Sous-tâches', + 'Add a sub-task' => 'Ajouter une sous-tâche', + 'Original estimate' => 'Estimation originale', + 'Create another sub-task' => 'Créer une autre sous-tâche', + 'Time spent' => 'Temps passé', + 'Edit a sub-task' => 'Modifier une sous-tâche', + 'Remove a sub-task' => 'Supprimer une sous-tâche', + 'The time must be a numeric value' => 'Le temps doit-être une valeur numérique', + 'Todo' => 'À faire', + 'In progress' => 'En cours', + 'Sub-task removed successfully.' => 'Sous-tâche supprimée avec succès.', + 'Unable to remove this sub-task.' => 'Impossible de supprimer cette sous-tâche.', + 'Sub-task updated successfully.' => 'Sous-tâche mise à jour avec succès.', + 'Unable to update your sub-task.' => 'Impossible de mettre à jour votre sous-tâche.', + 'Unable to create your sub-task.' => 'Impossible de créer votre sous-tâche.', + 'Maximum size: ' => 'Taille maximum : ', + 'Display another project' => 'Afficher un autre projet', + 'Created by %s' => 'Créé par %s', + 'Tasks Export' => 'Exportation des tâches', + 'Start Date' => 'Date de début', + 'Execute' => 'Exécuter', + 'Task Id' => 'Identifiant de la tâche', + 'Creator' => 'Créateur', + 'Modification date' => 'Date de modification', + 'Completion date' => 'Date de complétion', + 'Clone' => 'Clone', + 'Project cloned successfully.' => 'Projet cloné avec succès.', + 'Unable to clone this project.' => 'Impossible de cloner ce projet.', + 'Enable email notifications' => 'Activer les notifications par email', + 'Task position:' => 'Position de la tâche :', + 'The task #%d has been opened.' => 'La tâche #%d a été ouverte.', + 'The task #%d has been closed.' => 'La tâche #%d a été fermée.', + 'Sub-task updated' => 'Sous-tâche mise à jour', + 'Title:' => 'Titre :', + 'Status:' => 'État :', + 'Assignee:' => 'Assigné :', + 'Time tracking:' => 'Gestion du temps :', + '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 comment' => 'Nouveau commentaire', + 'Comment updated' => 'Commentaire mis à jour', + 'New subtask' => 'Nouvelle sous-tâche', + 'I only want to receive notifications for these projects:' => 'Je souhaite recevoir 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', + 'Disable public access' => 'Désactiver l\'accès public', + 'Enable public access' => 'Activer l\'accès public', + 'Public access disabled' => 'Accès public désactivé', + 'Move the task to another project' => 'Déplacer la tâche vers un autre projet', + 'Move to project' => 'Déplacer vers un autre projet', + 'Do you really want to duplicate this task?' => 'Voulez-vous vraiment dupliquer cette tâche ?', + 'Duplicate a task' => 'Dupliquer une tâche', + 'External accounts' => 'Comptes externes', + 'Account type' => 'Type de compte', + 'Local' => 'Local', + 'Remote' => 'Distant', + 'Enabled' => 'Activé', + 'Disabled' => 'Désactivé', + 'Login:' => 'Nom d\'utilisateur :', + 'Full Name:' => 'Nom :', + 'Email:' => 'Email :', + 'Notifications:' => 'Notifications :', + 'Notifications' => 'Notifications', + 'Account type:' => 'Type de compte :', + 'Edit profile' => 'Modifier le profil', + 'Change password' => 'Changer le mot de passe', + 'Password modification' => 'Changement de mot de passe', + 'External authentications' => 'Authentifications externes', + 'Never connected.' => 'Jamais connecté.', + 'No external authentication enabled.' => 'Aucune authentification externe activée.', + 'Password modified successfully.' => 'Mot de passe changé avec succès.', + 'Unable to change the password.' => 'Impossible de changer le mot de passe.', + 'Change category' => 'Changer de catégorie', + '%s updated the task %s' => '%s a mis à jour la tâche %s', + '%s opened the task %s' => '%s a ouvert la tâche %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s a déplacé la tâche %s à la position n°%d dans la colonne « %s »', + '%s moved the task %s to the column "%s"' => '%s a déplacé la tâche %s dans la colonne « %s »', + '%s created the task %s' => '%s a créé la tâche %s', + '%s closed the task %s' => '%s a fermé la tâche %s', + '%s created a subtask for the task %s' => '%s a créé une sous-tâche pour la tâche %s', + '%s updated a subtask for the task %s' => '%s a mis à jour une sous-tâche appartenant à la tâche %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Assigné à %s avec un estimé de %s/%s h', + 'Not assigned, estimate of %sh' => 'Personne assigné, estimé de %s h', + '%s updated a comment on the task %s' => '%s a mis à jour un commentaire appartenant à la tâche %s', + '%s commented the task %s' => '%s a ajouté un commentaire sur la tâche %s', + '%s\'s activity' => 'Activité du projet %s', + 'RSS feed' => 'Flux RSS', + '%s updated a comment on the task #%d' => '%s a mis à jour un commentaire sur la tâche n°%d', + '%s commented on the task #%d' => '%s a ajouté un commentaire sur la tâche n°%d', + '%s updated a subtask for the task #%d' => '%s a mis à jour une sous-tâche appartenant à la tâche n°%d', + '%s created a subtask for the task #%d' => '%s a créé une sous-tâche pour la tâche n°%d', + '%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 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 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', + 'Create a task from an external provider' => 'Créer une tâche depuis un fournisseur externe', + 'Change the assignee based on an external username' => 'Changer l\'assigné en fonction d\'un utilisateur externe', + 'Change the category based on an external label' => 'Changer la catégorie en fonction d\'un libellé externe', + 'Reference' => 'Référence', + 'Label' => 'Libellé', + 'Database' => 'Base de données', + 'About' => 'À propos', + 'Database driver:' => 'Type de base de données :', + 'Board settings' => 'Paramètres du tableau', + 'Webhook settings' => 'Paramètres pour les webhooks', + 'Reset token' => 'Regénérer le jeton de sécurité', + 'API endpoint:' => 'URL de l\'API :', + 'Refresh interval for personal board' => 'Intervalle pour rafraichir un tableau personnel', + 'Refresh interval for public board' => 'Intervalle pour rafraichir un tableau public', + 'Task highlight period' => 'Durée pour mettre une tâche en évidence', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Durée en seconde pour considérer une tâche comme récemment modifiée (0 pour désactiver, 2 jours par défaut)', + 'Frequency in second (60 seconds by default)' => 'Fréquence en seconde (60 secondes par défaut)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Fréquence en seconde (0 pour désactiver, 10 secondes par défaut)', + 'Application URL' => 'URL de l\'application', + 'Token regenerated.' => 'Jeton de sécurité regénéré.', + 'Date format' => 'Format des dates', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Le format ISO est toujours accepté, exemple : « %s » et « %s »', + 'New personal project' => 'Nouveau projet personnel', + 'This project is personal' => 'Ce projet est personnel', + 'Add' => 'Ajouter', + 'Start date' => 'Date de début', + 'Time estimated' => 'Temps estimé', + 'There is nothing assigned to you.' => 'Il n\'y a rien d\'assigné pour vous.', + 'My tasks' => 'Mes tâches', + 'Activity stream' => 'Flux d\'activité', + 'Dashboard' => 'Tableau de bord', + 'Confirmation' => 'Confirmation', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Créer un commentaire depuis un fournisseur externe', + 'Project management' => 'Gestion des projets', + 'Columns' => 'Colonnes', + 'Task' => 'Tâche', + 'Percentage' => 'Pourcentage', + 'Number of tasks' => 'Nombre de tâches', + 'Task distribution' => 'Répartition des tâches', + 'Analytics' => 'Analytique', + 'Subtask' => 'Sous-tâche', + 'User repartition' => 'Répartition des utilisateurs', + '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.', + 'Previous' => 'Précédent', + 'The id must be an integer' => 'L\'id doit être un entier', + 'The project id must be an integer' => 'L\'id du projet doit être un entier', + 'The status must be an integer' => 'Le statut doit être un entier', + 'The subtask id is required' => 'L\'id de la sous-tâche est obligatoire', + 'The subtask id must be an integer' => 'L\'id de la sous-tâche doit être un entier', + 'The task id is required' => 'L\'id de la tâche est obligatoire', + 'The task id must be an integer' => 'L\'id de la tâche doit être un entier', + 'The user id must be an integer' => 'L\'id de l\'utilisateur doit être un entier', + 'This value is required' => 'Cette valeur est obligatoire', + '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é', + 'Daily project summary' => 'Résumé journalier du projet', + 'Daily project summary export' => 'Export du résumé journalier du projet', + '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', + 'Add a new swimlane' => 'Ajouter une nouvelle swimlane', + 'Default swimlane' => 'Swimlane par défaut', + 'Do you really want to remove this swimlane: "%s"?' => 'Voulez-vous vraiment supprimer cette swimlane : « %s » ?', + 'Inactive swimlanes' => 'Swimlanes inactives', + 'Remove a swimlane' => 'Supprimer une swimlane', + 'Swimlane modification for the project "%s"' => 'Modification d\'une swimlane pour le projet « %s »', + 'Swimlane removed successfully.' => 'Swimlane supprimée avec succès.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane mise à jour avec succès.', + 'Unable to remove this swimlane.' => 'Impossible de supprimer cette swimlane.', + 'Unable to update this swimlane.' => 'Impossible de mettre à jour cette swimlane.', + 'Your swimlane has been created successfully.' => 'Votre swimlane a été créée avec succès.', + 'Example: "Bug, Feature Request, Improvement"' => 'Exemple : « Incident, Demande de fonctionnalité, Amélioration »', + 'Default categories for new projects (Comma-separated)' => 'Catégories par défaut pour les nouveaux projets (séparation par des virgules)', + 'Integrations' => 'Intégrations', + 'Integration with third-party services' => 'Intégration avec des services externes', + 'Subtask Id' => 'Identifiant de la sous-tâche', + 'Subtasks' => 'Sous-tâches', + 'Subtasks Export' => 'Exportation des sous-tâches', + 'Task Title' => 'Titre de la tâche', + 'Untitled' => 'Sans nom', + 'Application default' => 'Valeur par défaut de l\'application', + 'Language:' => 'Langue :', + 'Timezone:' => 'Fuseau horaire :', + 'All columns' => 'Toutes les colonnes', + 'Next' => 'Suivant', + '#%d' => 'nËš%d', + 'All swimlanes' => 'Toutes les swimlanes', + 'All colors' => 'Toutes les couleurs', + 'Moved to column %s' => 'Tâche déplacée à la colonne %s', + 'User dashboard' => 'Tableau de bord de l\'utilisateur', + 'Allow only one subtask in progress at the same time for a user' => 'Autoriser une seule sous-tâche en progrès en même temps pour un utilisateur', + 'Edit column "%s"' => 'Modifier la colonne « %s »', + 'Select the new status of the subtask: "%s"' => 'Sélectionnez le nouveau statut de la sous-tâche : « %s »', + 'Subtask timesheet' => 'Feuille de temps des sous-tâches', + 'There is nothing to show.' => 'Il n\'y a rien à montrer.', + 'Time Tracking' => 'Feuille de temps', + 'You already have one subtask in progress' => 'Vous avez déjà une sous-tâche en progrès', + 'Which parts of the project do you want to duplicate?' => 'Quelles parties du projet voulez-vous dupliquer ?', + 'Disallow login form' => 'Interdire le formulaire d\'authentification', + 'Start' => 'Début', + 'End' => 'Fin', + 'Task age in days' => 'Âge de la tâche en jours', + 'Days in this column' => 'Jours dans cette colonne', + '%dd' => '%d j', + 'Add a new link' => 'Ajouter un nouveau lien', + 'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?', + 'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%d ?', + 'Field required' => 'Champ obligatoire', + 'Link added successfully.' => 'Lien créé avec succès.', + 'Link updated successfully.' => 'Lien mis à jour avec succès.', + 'Link removed successfully.' => 'Lien supprimé avec succès.', + 'Link labels' => 'Libellé des liens', + 'Link modification' => 'Modification d\'un lien', + 'Opposite label' => 'Nom du libellé opposé', + 'Remove a link' => 'Supprimer un lien', + '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', + 'Unable to create your link.' => 'Impossible d\'ajouter ce lien.', + 'Unable to update your link.' => 'Impossible de mettre à jour ce lien.', + 'Unable to remove this link.' => 'Impossible de supprimer ce lien.', + 'relates to' => 'est liée à', + 'blocks' => 'bloque', + 'is blocked by' => 'est bloquée par', + 'duplicates' => 'duplique', + 'is duplicated by' => 'est dupliquée par', + 'is a child of' => 'est un enfant de', + 'is a parent of' => 'est un parent de', + 'targets milestone' => 'vise l\'étape importante', + 'is a milestone of' => 'est une étape importante incluant', + 'fixes' => 'corrige', + 'is fixed by' => 'est corrigée par', + 'This task' => 'Cette tâche', + '<1h' => '< 1 h', + '%dh' => '%d h', + 'Expand tasks' => 'Déplier les tâches', + 'Collapse tasks' => 'Replier les tâches', + 'Expand/collapse tasks' => 'Plier/déplier les tâches', + 'Close dialog box' => 'Fermer une boite de dialogue', + 'Submit a form' => 'Enregistrer un formulaire', + 'Board view' => 'Page du tableau', + 'Keyboard shortcuts' => 'Raccourcis clavier', + 'Open board switcher' => 'Ouvrir le sélecteur de tableau', + 'Application' => 'Application', + 'Compact view' => 'Vue compacte', + 'Horizontal scrolling' => 'Défilement horizontal', + 'Compact/wide view' => 'Basculer entre la vue compacte et étendue', + 'Currency' => 'Devise', + 'Personal project' => 'Projet personnel', + 'AUD - Australian Dollar' => 'AUD - Dollar australien', + 'CAD - Canadian Dollar' => 'CAD - Dollar canadien', + 'CHF - Swiss Francs' => 'CHF - Franc suisse', + 'Custom Stylesheet' => 'Feuille de style personnalisée', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Livre sterling', + 'INR - Indian Rupee' => 'INR - Roupie indienne', + 'JPY - Japanese Yen' => 'JPY - Yen', + 'NZD - New Zealand Dollar' => 'NZD - Dollar néo-zélandais', + 'PEN - Peruvian Sol' => 'PEN - Sol péruvien', + 'RSD - Serbian dinar' => 'RSD - Dinar serbe', + 'CNY - Chinese Yuan' => 'CNY - Yuan (Chine)', + 'USD - US Dollar' => 'USD - Dollar américain', + 'VES - Venezuelan Bolívar' => 'Bolivar vénézuélien', + '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', + 'Move the task to another column when assignee is cleared' => 'Déplacer la tâche dans une autre colonne lorsque celle-ci n\'est plus assignée', + 'Source column' => 'Colonne d\'origine', + 'Transitions' => 'Transitions', + 'Executer' => 'Exécutant', + 'Time spent in the column' => 'Temps passé dans la colonne', + 'Task transitions' => 'Transitions des tâches', + 'Task transitions export' => 'Export des transitions des tâches', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ce rapport contient tous les mouvements de colonne pour chaque tâche avec la date, l\'utilisateur et le temps passé pour chaque transition.', + 'Currency rates' => 'Taux de change des devises', + 'Rate' => 'Taux', + 'Change reference currency' => 'Changer la monnaie de référence', + 'Reference currency' => 'Devise de référence', + 'The currency rate has 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 removed the assignee of the task %s' => '%s a enlevé la personne assignée à la tâche %s', + 'Information' => 'Informations', + 'Check two factor authentication code' => 'Vérification du code pour l\'authentification à deux-facteurs', + 'The two factor authentication code is not valid.' => 'Le code pour l\'authentification à deux-facteurs n\'est pas valide.', + 'The two factor authentication code is valid.' => 'Le code pour l\'authentification à deux-facteurs est valide.', + 'Code' => 'Code', + 'Two factor authentication' => 'Authentification à deux-facteurs', + 'This QR code contains the key URI: ' => 'Ce code QR contient l\'URL de la clé : ', + 'Check my code' => 'Vérifier mon code', + 'Secret key: ' => 'Clé secrète : ', + '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' => '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', + 'Add a screenshot' => 'Ajouter une capture d\'écran', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Prenez une capture d\'écran et appuyez sur CTRL+V ou ⌘+V pour coller ici.', + 'Screenshot uploaded successfully.' => 'Capture d\'écran téléchargée avec succès.', + 'SEK - Swedish Krona' => 'SEK - Couronne suédoise', + 'Identifier' => 'Identificateur', + 'Disable two factor authentication' => 'Désactiver l\'authentification à deux facteurs', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Voulez-vous vraiment désactiver l\'authentification à deux facteurs pour cet utilisateur : « %s » ?', + 'Edit link' => 'Modifier un lien', + 'Start to type task title...' => 'Entrez le titre de la tâche…', + 'A task cannot be linked to itself' => 'Une tâche ne peut être liée à elle-même', + 'The exact same link already exists' => 'Un lien identique existe déjà', + 'Recurrent task is scheduled to be generated' => 'La tâche récurrente est programmée pour être créée', + 'Score' => 'Complexité', + 'The identifier must be unique' => 'L\'identifiant doit être unique', + 'This linked task id doesn\'t exists' => 'L\'identifiant de la tâche liée n\'existe pas', + 'This value must be alphanumeric' => 'Cette valeur doit être alphanumérique', + 'Edit recurrence' => 'Modifier la récurrence', + 'Generate recurrent task' => 'Générer une tâche récurrente', + 'Trigger to generate recurrent task' => 'Déclencheur pour générer la tâche récurrente', + 'Factor to calculate new due date' => 'Facteur pour calculer la nouvelle date d\'échéance', + 'Timeframe to calculate new due date' => 'Échelle de temps pour calculer la nouvelle date d\'échéance', + 'Base date to calculate new due date' => 'Date à utiliser pour calculer la nouvelle date d\'échéance', + 'Action date' => 'Date de l\'action', + 'Base date to calculate new due date: ' => 'Date utilisée pour calculer la nouvelle date d\'échéance : ', + 'This task has created this child task: ' => 'Cette tâche a créé la tâche enfant : ', + 'Day(s)' => 'Jour(s)', + 'Existing due date' => 'Date d\'échéance existante', + 'Factor to calculate new due date: ' => 'Facteur pour calculer la nouvelle date d\'échéance : ', + 'Month(s)' => 'Mois', + 'This task has been created by: ' => 'Cette tâche a été créée par :', + 'Recurrent task has been generated:' => 'Une tâche récurrente a été générée :', + 'Timeframe to calculate new due date: ' => 'Échelle de temps pour calculer la nouvelle date d\'échéance : ', + 'Trigger to generate recurrent task: ' => 'Déclencheur pour générer la tâche récurrente : ', + 'When task is closed' => 'Lorsque la tâche est fermée', + 'When task is moved from first column' => 'Lorsque la tâche est déplacée en dehors de la première colonne', + 'When task is moved to last column' => 'Lorsque la tâche est déplacée dans la dernière colonne', + 'Year(s)' => 'Année(s)', + 'Project settings' => 'Paramètres du projet', + 'Automatically update the start date' => 'Mettre à jour automatiquement la date de début', + 'iCal feed' => 'Abonnement iCal', + 'Preferences' => 'Préférences', + 'Security' => 'Sécurité', + 'Two factor authentication disabled' => 'Authentification à deux facteurs désactivée', + 'Two factor authentication enabled' => 'Authentification à deux facteurs activée', + 'Unable to update this user.' => 'Impossible de mettre à jour cet utilisateur.', + 'There is no user management for personal projects.' => 'Il n\'y a pas de gestion d\'utilisateurs pour les projets personnels.', + 'User that will receive the email' => 'Utilisateur qui va recevoir l\'email', + 'Email subject' => 'Sujet de l\'email', + 'Date' => 'Date', + 'Add a comment log when moving the task between columns' => 'Ajouter un commentaire d\'information lorsque une tâche est déplacée dans une autre colonne', + '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', + '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', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => '%s a déplacé la tâche %s dans la première swimlane', + '%s moved the task %s to the swimlane "%s"' => '%s a déplacé la tâche %s dans la swimlane « %s »', + 'This report contains all subtasks information for the given date range.' => 'Ce rapport contient les informations de toutes les sous-tâches pour la période sélectionnée.', + 'This report contains all tasks information for the given date range.' => 'Ce rapport contient les informations de toutes les tâches pour la période sélectionnée.', + 'Project activities for %s' => 'Activité des projets pour « %s »', + 'view the board on Kanboard' => 'voir le tableau sur Kanboard', + 'The task has been moved to the first swimlane' => 'La tâche a été déplacée dans la première swimlane', + 'The task has been moved to another swimlane:' => 'La tâche a été déplacée dans une autre swimlane :', + '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', + 'There is no category now' => 'Il n\'y a plus de catégorie maintenant', + 'New category: %s' => 'Nouvelle catégorie : %s', + 'New color: %s' => 'Nouvelle couleur : %s', + 'New complexity: %d' => 'Nouvelle complexité : %d', + 'The due date has been removed' => 'La date d\'échéance a été enlevée', + 'There is no description anymore' => 'Il n\'y a plus de description maintenant', + 'Recurrence settings has been modified' => 'Les réglages de la récurrence ont été modifiés', + 'Time spent changed: %sh' => 'Le temps passé a été changé : %s h', + 'Time estimated changed: %sh' => 'Le temps estimé a été changé : %s h', + 'The field "%s" has been updated' => 'Le champ « %s » a été mis à jour', + 'The description has been modified:' => 'La description a été modifiée', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Voulez-vous vraiment fermer la tâche « %s » ainsi que toutes ses sous-tâches ?', + 'I want to receive notifications for:' => 'Je veux recevoir les notifications pour :', + 'All tasks' => 'Toutes les Tâches', + 'Only for tasks assigned to me' => 'Seulement les tâches qui me sont assignées', + 'Only for tasks created by me' => 'Seulement les tâches que j\'ai créées', + 'Only for tasks created by me and tasks assigned to me' => 'Seulement les tâches créées par moi-même et celles qui me sont assignées', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Total pour toutes les colonnes', + 'You need at least 2 days of data to show the chart.' => 'Vous avez besoin d\'au minimum 2 jours de données pour afficher le graphique.', + '<15m' => '< 15 min', + '<30m' => '< 30 min', + 'Stop timer' => 'Stopper le chrono', + 'Start timer' => 'Démarrer le chrono', + 'My activity stream' => 'Mon flux d\'activité', + 'Search tasks' => 'Rechercher des tâches', + 'Reset filters' => 'Réinitialiser les filtres', + 'My tasks due tomorrow' => 'Mes tâches qui arrivent à échéance demain', + 'Tasks due today' => 'Tâches qui arrivent à échéance aujourd\'hui', + 'Tasks due tomorrow' => 'Tâches qui arrivent à échéance demain', + 'Tasks due yesterday' => 'Tâches qui sont arrivées à échéance hier', + 'Closed tasks' => 'Tâches fermées', + 'Open tasks' => 'Tâches ouvertes', + 'Not assigned' => 'Non assignées', + 'View advanced search syntax' => 'Voir la syntaxe pour la recherche avancée', + 'Overview' => 'Vue d\'ensemble', + 'Board/Calendar/List view' => 'Vue Tableau/Calendrier/Liste', + 'Switch to the board view' => 'Basculer vers le tableau', + 'Switch to the list view' => 'Basculer vers la vue en liste', + 'Go to the search/filter box' => 'Aller au champ de recherche', + 'There is no activity yet.' => 'Il n\'y a pas encore d\'activité.', + 'No tasks found.' => 'Aucune tâche trouvée.', + 'Keyboard shortcut: "%s"' => 'Raccourci clavier : « %s »', + 'List' => 'Liste', + 'Filter' => 'Filtre', + 'Advanced search' => 'Recherche avancée', + 'Example of query: ' => 'Exemple de requête : ', + 'Search by project: ' => 'Rechercher par projet : ', + 'Search by column: ' => 'Rechercher par colonne : ', + 'Search by assignee: ' => 'Rechercher par assigné : ', + 'Search by color: ' => 'Rechercher par couleur : ', + 'Search by category: ' => 'Rechercher par catégorie : ', + 'Search by description: ' => 'Rechercher par description : ', + 'Search by due date: ' => 'Rechercher par date d\'échéance : ', + 'Average time spent in each column' => 'Temps moyen passé dans chaque colonne', + 'Average time spent' => 'Temps moyen passé', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Ce graphique montre le temps passé moyen dans chaque colonne pour les %d dernières tâches.', + 'Average Lead and Cycle time' => 'Durée moyenne du lead et cycle time', + 'Average lead time: ' => 'Lead time moyen : ', + 'Average cycle time: ' => 'Cycle time moyen : ', + 'Cycle Time' => 'Cycle time', + 'Lead Time' => 'Lead time', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Ce graphique montre la durée moyenne du lead et cycle time pour les %d dernières tâches.', + 'Average time into each column' => 'Temps moyen dans chaque colonne', + 'Lead and cycle time' => 'Lead et cycle time', + 'Lead time: ' => 'Lead time : ', + 'Cycle time: ' => 'Temps de cycle : ', + 'Time spent in each column' => 'Temps passé dans chaque colonne', + 'The lead time is the duration between the task creation and the completion.' => 'Le lead time est la durée entre la création de la tâche et sa complétion.', + 'The cycle time is the duration between the start date and the completion.' => 'Le cycle time est la durée entre la date de début et la complétion.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Si la tâche n\'est pas fermée, l\'heure courante est utilisée à la place de la date de complétion.', + 'Set the start date automatically' => 'Définir automatiquement la date de début', + 'Edit Authentication' => 'Modifier l\'authentification', + '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.', + '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.', + 'Trigger automatically subtask time tracking' => 'Déclencher automatiquement le suivi du temps pour les sous-tâches', + 'Include closed tasks in the cumulative flow diagram' => 'Inclure les tâches fermées dans le diagramme de flux cumulé', + 'Current swimlane: %s' => 'Swimlane actuelle : %s', + 'Current column: %s' => 'Colonne actuelle : %s', + 'Current category: %s' => 'Catégorie actuelle : %s', + 'no category' => 'aucune catégorie', + 'Current assignee: %s' => 'Assigné actuel : %s', + 'not assigned' => 'non assigné', + 'Author:' => 'Auteur :', + 'contributors' => 'contributeurs', + 'License:' => 'Licence :', + 'License' => 'Licence', + 'Enter the text below' => 'Entrez le texte ci-dessous', + 'Start date:' => 'Date de début :', + 'Due date:' => 'Date d\'échéance :', + 'People who are project managers' => 'Personnes qui sont gestionnaires de projet', + 'People who are project members' => 'Personnes qui sont membres de projet', + 'NOK - Norwegian Krone' => 'NOK - Couronne norvégienne', + 'Show this column' => 'Montrer cette colonne', + 'Hide this column' => 'Cacher cette colonne', + 'End date' => 'Date de fin', + 'Users overview' => 'Vue d\'ensemble des utilisateurs', + 'Members' => 'Membres', + 'Shared project' => 'Projet partagé', + 'Project managers' => 'Gestionnaires de projet', + 'Projects list' => 'Liste des projets', + 'End date:' => 'Date de fin :', + 'Change task color when using a specific task link' => 'Changer la couleur de la tâche lorsqu\'un lien spécifique est utilisé', + 'Task link creation or modification' => 'Création ou modification d\'un lien sur une tâche', + 'Milestone' => 'Étape importante', + 'Reset the search/filter box' => 'Réinitialiser le champ de recherche', + 'Documentation' => 'Documentation', + 'Author' => 'Auteur', + 'Version' => 'Version', + 'Plugins' => 'Extensions', + 'There is no plugin loaded.' => 'Il n\'y a aucune extension chargée.', + 'My notifications' => 'Mes notifications', + 'Custom filters' => 'Filtres personnalisés', + 'Your custom filter has been created successfully.' => 'Votre filtre personnalisé a été créé avec succès.', + 'Unable to create your custom filter.' => 'Impossible de créer votre filtre personnalisé.', + 'Custom filter removed successfully.' => 'Filtre personnalisé supprimé avec succès.', + 'Unable to remove this custom filter.' => 'Impossible de supprimer ce filtre personnalisé.', + 'Edit custom filter' => 'Modification d\'un filtre personnalisé', + 'Your custom filter has been updated successfully.' => 'Votre filtre personnalisé a été mis à jour avec succès.', + 'Unable to update custom filter.' => 'Impossible de mettre à jour votre filtre personnalisé.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nouveau fichier joint sur la tâche n°%d : %s', + 'New comment on task #%d' => 'Nouveau commentaire sur la tâche n°%d', + 'Comment updated on task #%d' => 'Commentaire mis à jour sur la tâche n°%d', + 'New subtask on task #%d' => 'Nouvelle sous-tâche sur la tâche n°%d', + 'Subtask updated on task #%d' => 'Sous-tâche mise à jour sur la tâche n°%d', + 'New task #%d: %s' => 'Nouvelle tâche n°%d : %s', + 'Task updated #%d' => 'Tâche n°%d mise à jour', + 'Task #%d closed' => 'Tâche n°%d fermée', + 'Task #%d opened' => 'Tâche n°%d ouverte', + 'Column changed for task #%d' => 'Changement de colonne pour la tâche n°%d', + 'New position for task #%d' => 'Nouvelle position pour la tâche n°%d', + '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', + 'No notification.' => 'Aucune notification.', + 'Mark all as read' => 'Tout marquer comme lu', + 'Mark as read' => 'Marquer comme lu', + 'Total number of tasks in this column across all swimlanes' => 'Nombre total de tâches dans cette colonne pour toutes les swimlanes', + 'Collapse swimlane' => 'Replier la swimlane', + 'Expand swimlane' => 'Déplier la swimlane', + 'Add a new filter' => 'Ajouter un nouveau filtre', + 'Share with all project members' => 'Partager avec tous les membres du projet', + 'Shared' => 'Partagé', + 'Owner' => 'Propriétaire', + 'Unread notifications' => 'Notifications non lus', + 'Notification methods:' => 'Méthodes de notifications :', + '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 has been imported!' => 'Rien n\'a été importé', + 'Import users from CSV file' => 'Importer des utilisateurs depuis un fichier CSV', + '%d user(s) have been imported successfully.' => '%d utilisateur(s) ont été importés avec succès.', + 'Comma' => 'Virgule', + 'Semi-colon' => 'Point-virgule', + 'Tab' => 'Tabulation', + 'Vertical bar' => 'Barre verticale', + 'Double Quote' => 'Guillemet double', + 'Single Quote' => 'Guillemet simple', + '%s attached a file to the task #%d' => '%s a attaché un fichier sur la tâche n°%d', + 'There is no column or swimlane activated in your project!' => 'Il n\'y a aucune colonne ou swimlane dans votre projet !', + 'Append filter (instead of replacement)' => 'Ajouter le filtre au lieu de le remplacer', + 'Append/Replace' => 'Ajouter/Remplacer', + 'Append' => 'Ajouter', + 'Replace' => 'Remplacer', + 'Import' => 'Importation', + 'Change sorting' => 'Changer l\'ordre', + 'Tasks Importation' => 'Importation des tâches', + 'Delimiter' => 'Délimiteur', + 'Enclosure' => 'Caractère d\'encadrement', + 'CSV File' => 'Fichier CSV', + 'Instructions' => 'Instructions', + 'Your file must use the predefined CSV format' => 'Votre fichier doit utiliser le format CSV prédéfini', + 'Your file must be encoded in UTF-8' => 'Votre fichier doit être encodé en UTF-8', + 'The first row must be the header' => 'La première ligne doit être le titre des colonnes', + 'Duplicates are not verified for you' => 'Les doublons ne sont pas vérifiés pour vous', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La date d\'échéance doit utiliser le format ISO : AAAA-MM-JJ', + 'Download CSV template' => 'Télécharger le modèle CSV', + 'No external integration registered.' => 'Aucune intégration externe enregistrée.', + 'Duplicates are not imported' => 'Les doublons ne sont pas importés', + 'Usernames must be lowercase and unique' => 'Les noms d\'utilisateurs doivent être en minuscule et unique', + 'Passwords will be encrypted if present' => 'Les mots de passe seront chiffrés si présent', + '%s attached a new file to the task %s' => '%s a attaché un nouveau fichier à la tâche %s', + 'Link type' => 'Type de lien', + 'Assign automatically a category based on a link' => 'Assigner automatiquement une catégorie en fonction d\'un lien', + 'BAM - Konvertible Mark' => 'BAM - Mark convertible', + 'Assignee Username' => 'Utilisateur assigné', + 'Assignee Name' => 'Nom de l\'assigné', + 'Groups' => 'Groupes', + 'Members of %s' => 'Membres de %s', + 'New group' => 'Nouveau groupe', + 'Group created successfully.' => 'Groupe créé avec succès.', + 'Unable to create your group.' => 'Impossible de créer votre groupe.', + 'Edit group' => 'Modifier le groupe', + 'Group updated successfully.' => 'Groupe mis à jour avec succès.', + 'Unable to update your group.' => 'Impossible de mettre à jour votre groupe.', + 'Add group member to "%s"' => 'Ajouter un membre au groupe « %s »', + 'Group member added successfully.' => 'Membre ajouté avec succès au groupe.', + 'Unable to add group member.' => 'Impossible d\'ajouter ce membre au groupe.', + 'Remove user from group "%s"' => 'Supprimer un utilisateur d\'un groupe « %s »', + 'User removed successfully from this group.' => 'Utilisateur supprimé avec succès de ce groupe.', + 'Unable to remove this user from the group.' => 'Impossible de supprimer cet utilisateur du groupe.', + 'Remove group' => 'Supprimer le groupe', + 'Group removed successfully.' => 'Groupe supprimé avec succès.', + 'Unable to remove this group.' => 'Impossible de supprimer ce groupe.', + 'Project Permissions' => 'Permissions du projet', + 'Manager' => 'Gestionnaire', + 'Project Manager' => 'Chef de projet', + 'Project Member' => 'Membre du projet', + 'Project Viewer' => 'Visualiseur de projet', + 'Your account is locked for %d minutes' => 'Votre compte est verrouillé pour %d minutes', + 'Invalid captcha' => 'Captcha invalide', + 'The name must be unique' => 'Le nom doit être unique', + 'View all groups' => 'Voir tous les groupes', + '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.', + 'Add group member' => 'Ajouter un membre au groupe', + 'Do you really want to remove this group: "%s"?' => 'Voulez-vous vraiment supprimer ce groupe : « %s » ?', + 'There is no user in this group.' => 'Il n\'y a aucun utilisateur dans ce groupe', + 'Permissions' => 'Permissions', + 'Allowed Users' => 'Utilisateurs autorisés', + 'No specific user has been allowed.' => 'Aucun utilisateur a été autorisé spécifiquement.', + 'Role' => 'Rôle', + 'Enter user name...' => 'Entrez le nom de l\'utilisateur…', + 'Allowed Groups' => 'Groupes autorisés', + 'No group has been allowed.' => 'Aucun groupe a été autorisé spécifiquement.', + 'Group' => 'Groupe', + 'Group Name' => 'Nom du groupe', + 'Enter group name...' => 'Entrez le nom du groupe…', + 'Role:' => 'Rôle :', + 'Project members' => 'Membres du projet', + '%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', + 'Estimated hours: ' => 'Heures estimées : ', + 'Actual hours: ' => 'Heures actuelles : ', + 'Hours Spent' => 'Heures passées', + 'Hours Estimated' => 'Heures estimées', + 'Estimated Time' => 'Temps estimé', + 'Actual Time' => 'Temps actuel', + 'Estimated vs actual time' => 'Temps estimé vs réel', + 'RUB - Russian Ruble' => 'RUB - Rouble russe', + 'Assign the task to the person who does the action when the column is changed' => 'Assigner la tâche à la personne qui fait l\'action lorsque la colonne est changée', + 'Close a task in a specific column' => 'Fermer une tâche dans une colonne spécifique', + 'Time-based One-time Password Algorithm' => 'Mot de passe à usage unique basé sur le temps', + 'Two-Factor Provider: ' => 'Fournisseur d\'authentification à deux facteurs : ', + 'Disable two-factor authentication' => 'Désactiver l\'authentification à deux-facteurs', + 'Enable two-factor authentication' => 'Activer l\'authentification à deux-facteurs', + 'There is no integration registered at the moment.' => 'Il n\'y a aucune intégration enregistrée pour le moment.', + 'Password Reset for Kanboard' => 'Réinitialisation du mot de passe pour Kanboard', + 'Forgot password?' => 'Mot de passe oublié ?', + 'Enable "Forget Password"' => 'Activer la fonctionnalité « Mot de passe oublié »', + 'Password Reset' => 'Réinitialisation du mot de passe', + 'New password' => 'Nouveau mot de passe', + 'Change Password' => 'Changer de mot de passe', + 'To reset your password click on this link:' => 'Pour réinitialiser votre mot de passe cliquer sur ce lien :', + 'Last Password Reset' => 'Dernières réinitialisations de mot de passe', + 'The password has never been reinitialized.' => 'Le mot de passe n\'a jamais été réinitialisé.', + 'Creation' => 'Création', + 'Expiration' => 'Expiration', + 'Password reset history' => 'Historique de la réinitialisation du mot de passe', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Toutes les tâches de la colonne « %s » et de la swimlane « %s » ont été fermées avec succès.', + 'Do you really want to close all tasks of this column?' => 'Voulez-vous vraiment fermer toutes les tâches de cette colonne ?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tâche(s) dans la colonne « %s » et la swimlane « %s » seront fermées.', + 'Close all tasks in this column and this swimlane' => 'Fermer toutes les tâches dans cette colonne et cette swimlane', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Aucun plugin n\'a enregistré une méthode de notification de projet. Vous pouvez toujours configurer les notifications individuelles dans votre profil utilisateur.', + 'My dashboard' => 'Mon tableau de bord', + 'My profile' => 'Mon profil', + '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, exemple : MONPROJET.', + 'Project owner' => 'Responsable du projet', + 'Personal projects do not have users and groups management.' => 'Les projets personnels 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é', + 'Task priority' => 'Priorité des tâches', + 'General' => 'Général', + 'Dates' => 'Dates', + 'Default priority' => 'Priorité par défaut', + 'Lowest priority' => 'Priorité basse', + 'Highest priority' => 'Priorité haute', + 'Close a task when there is no activity' => 'Fermer une tâche sans activité', + 'Duration in days' => 'Durée en jours', + 'Send email when there is no activity on a task' => 'Envoyer un email lorsqu\'il n\'y a pas d\'activité sur une tâche', + 'Unable to fetch link information.' => 'Impossible de récupérer les informations sur le lien.', + 'Daily background job for tasks' => 'Tâche planifiée quotidienne pour les tâches', + 'Auto' => 'Auto', + 'Related' => 'Relié', + 'Attachment' => 'Pièce-jointe', + 'Web Link' => 'Lien web', + 'External links' => 'Liens externes', + 'Add external link' => 'Ajouter un lien externe', + 'Type' => 'Type', + 'Dependency' => 'Dépendance', + 'Add internal link' => 'Ajouter un lien interne', + 'Add a new external link' => 'Ajouter un nouveau lien externe', + 'Edit external link' => 'Modifier un lien externe', + 'External link' => 'Lien externe', + 'Copy and paste your link here...' => 'Copier-coller votre lien ici…', + 'URL' => 'URL', + 'Internal links' => 'Liens internes', + 'Assign to me' => 'Assigner à moi', + 'Me' => 'Moi', + 'Do not duplicate anything' => 'Ne rien dupliquer', + 'Projects management' => 'Gestion des projets', + 'Users management' => 'Gestion des utilisateurs', + 'Groups management' => 'Gestion des groupes', + 'Create from another project' => 'Créer depuis un autre projet', + 'open' => 'ouvert', + 'closed' => 'fermé', + 'Priority:' => 'Priorité :', + 'Reference:' => 'Référence :', + 'Complexity:' => 'Complexité :', + 'Swimlane:' => 'Swimlane :', + 'Column:' => 'Colonne :', + 'Position:' => 'Position :', + 'Creator:' => 'Créateur :', + 'Time estimated:' => 'Temps estimé :', + '%s hours' => '%s heures', + 'Time spent:' => 'Temps passé :', + 'Created:' => 'Créé le :', + 'Modified:' => 'Modifié le :', + 'Completed:' => 'Terminé le :', + 'Started:' => 'Commencé le :', + 'Moved:' => 'Déplacé le : ', + 'Task #%d' => 'Tâche n°%d', + 'Time format' => 'Format de l\'heure', + 'Start date: ' => 'Date de début : ', + 'End date: ' => 'Date de fin : ', + 'New due date: ' => 'Nouvelle date d\'échéance : ', + 'Start date changed: ' => 'Date de début modifiée : ', + 'Disable personal projects' => 'Désactiver les projets personnels', + 'Do you really want to remove this custom filter: "%s"?' => 'Voulez-vous vraiment supprimer ce filtre personnalisé : « %s » ?', + 'Remove a custom filter' => 'Supprimer un filtre personnalisé', + 'User activated successfully.' => 'Utilisateur activé avec succès.', + 'Unable to enable this user.' => 'Impossible d\'activer cet utilisateur.', + '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é envoyés avec succès.', + 'The maximum allowed file size is %sB.' => 'La taille maximale autorisée pour les fichiers est de %s o.', + 'Drag and drop your files here' => 'Glissez-déposez vos fichiers ici', + 'choose files' => 'choisissez des fichiers', + 'View profile' => 'Voir le profil', + 'Two Factor' => 'Deux-Facteurs', + 'Disable user' => 'Désactiver l\'utilisateur', + 'Do you really want to disable this user: "%s"?' => 'Voulez-vous vraiment désactiver cet utilisateur : « %s » ?', + 'Enable user' => 'Activer un utilisateur', + 'Do you really want to enable this user: "%s"?' => 'Voulez-vous vraiment activer cet utilisateur : « %s » ?', + 'Download' => 'Télécharger', + 'Uploaded: %s' => 'Envoyé : %s', + 'Size: %s' => 'Taille : %s', + 'Uploaded by %s' => 'Envoyé par %s', + 'Filename' => 'Nom du fichier', + 'Size' => 'Taille', + 'Column created successfully.' => 'La colonne a été créée avec succès.', + 'Another column with the same name exists in the project' => 'Une autre colonne existe avec le même nom dans le projet', + 'Default filters' => 'Filtres par défaut', + 'Your board doesn\'t have any columns!' => 'Votre tableau n\'a aucune colonne', + 'Change column position' => 'Changer la position de la colonne', + 'Switch to the project overview' => 'Aller à l\'aperçu du projet', + 'User filters' => 'Filtres des utilisateurs', + 'Category filters' => 'Filtres des catégories', + 'Upload a file' => 'Envoyer un fichier', + 'View file' => 'Voir le fichier', + 'Last activity' => 'Dernières activités', + 'Change subtask position' => 'Changer la position de la sous-tâche', + 'This value must be greater than %d' => 'Cette valeur doit être plus grande que %d', + 'Another swimlane with the same name exists in the project' => 'Une autre swimlane existe avec le même nom dans le projet', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Exemple : https://exemple.kanboard.org/ (utilisé pour générer les URL absolues)', + 'Actions duplicated successfully.' => 'Actions dupliquées avec succès.', + 'Unable to duplicate actions.' => 'Impossible de dupliquer les actions.', + 'Add a new action' => 'Ajouter une nouvelle action', + 'Import from another project' => 'Importer depuis un autre projet', + 'There is no action at the moment.' => 'Il n\'y a aucune action pour le moment.', + 'Import actions from another project' => 'Importer les actions depuis un autre projet', + 'There is no available project.' => 'Il n\'y a pas de projet disponible.', + 'Local File' => 'Fichier local', + 'Configuration' => 'Configuration', + 'PHP version:' => 'Version de PHP :', + 'PHP SAPI:' => 'PHP SAPI :', + 'OS version:' => 'Version du système d\'exploitation :', + 'Database version:' => 'Version de la base de donnée :', + 'Browser:' => 'Navigateur web :', + 'Task view' => 'Vue détaillée d\'une tâche', + 'Edit task' => 'Modifier la tâche', + 'Edit description' => 'Modifier la description', + 'New internal link' => 'Nouveau lien interne', + 'Display list of keyboard shortcuts' => 'Afficher la liste des raccourcis claviers', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Uploader mon image d\'avatar', + 'Remove my image' => 'Supprimer mon image', + 'The OAuth2 state parameter is invalid' => 'Le paramètre « state » de OAuth2 est invalide', + 'User not found.' => 'Utilisateur introuvable.', + 'Search in activity stream' => 'Chercher dans le flux d\'activité', + 'My activities' => 'Mes activités', + 'Activity until yesterday' => 'Activités jusqu\'à hier', + 'Activity until today' => 'Activités jusqu\'à aujourd\'hui', + 'Search by creator: ' => 'Rechercher par créateur : ', + 'Search by creation date: ' => 'Rechercher par date de création : ', + 'Search by task status: ' => 'Rechercher par le statut des tâches : ', + 'Search by task title: ' => 'Rechercher par le titre des tâches : ', + 'Activity stream search' => 'Recherche dans le flux d\'activité', + 'Projects where "%s" is manager' => 'Projets où « %s » est gestionnaire', + 'Projects where "%s" is member' => 'Projets où « %s » est membre du projet', + 'Open tasks assigned to "%s"' => 'Tâches ouvertes assignées à « %s »', + 'Closed tasks assigned to "%s"' => 'Tâches fermées assignées à « %s »', + 'Assign automatically a color based on a priority' => 'Assigner automatiquement une couleur par rapport à une priorité', + 'Overdue tasks for the project(s) "%s"' => 'Tâches en retard pour le projet(s) « %s »', + 'Upload files' => 'Envoyer les fichiers', + 'Installed Plugins' => 'Extensions installées', + 'Plugin Directory' => 'Liste des extensions', + 'Plugin installed successfully.' => 'Extension installée avec succès.', + 'Plugin updated successfully.' => 'Extension mise à jour avec succès.', + 'Plugin removed successfully.' => 'Extension supprimée avec succès.', + 'Subtask converted to task successfully.' => 'Sous-tâche convertie en tâche avec succès.', + 'Unable to convert the subtask.' => 'Impossible de convertir cette sous-tâche.', + 'Unable to extract plugin archive.' => 'Impossible d\'extraire l\'archive de l\'extension.', + 'Plugin not found.' => 'Extension introuvable.', + 'You don\'t have the permission to remove this plugin.' => 'Vous n\'avez pas la permission de supprimer ce plugin.', + 'Unable to download plugin archive.' => 'Impossible de télécharger l\'archive du plugin.', + 'Unable to write temporary file for plugin.' => 'Impossible d\'écrire le fichier temporaire pour l\'extension.', + 'Unable to open plugin archive.' => 'Impossible d\'ouvrir l\'archive du plugin.', + 'There is no file in the plugin archive.' => 'Il n\'y a aucun fichier dans l\'archive du plugin.', + 'Create tasks in bulk' => 'Créer plusieurs tâches en même temps', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Votre instance de Kanboard n\'est pas configurée pour installer des extensions depuis l\'interface utilisateur.', + 'There is no plugin available.' => 'Il n\'a aucune extension disponible.', + 'Install' => 'Installer', + 'Update' => 'Mettre à jour', + 'Up to date' => 'À jour', + 'Not available' => 'Non disponible', + 'Remove plugin' => 'Supprimer l\'extension', + 'Do you really want to remove this plugin: "%s"?' => 'Voulez-vous vraiment supprimer cette extension : « %s » ?', + 'Uninstall' => 'Désinstaller', + 'Listing' => 'Listing', + 'Metadata' => 'Metadonnées', + 'Manage projects' => 'Gérer les projets', + 'Convert to task' => 'Convertir en tâche', + 'Convert sub-task to task' => 'Convertir une sous-tâche en tâche', + 'Do you really want to convert this sub-task to a task?' => 'Voulez-vous vraiment convertir cette sous-tâche en tâche ?', + 'My task title' => 'Mon titre pour la tâche', + 'Enter one task by line.' => 'Entrez une tâche par ligne.', + 'Number of failed login:' => 'Nombre de connexions échouées :', + 'Account locked until:' => 'Compte bloqué jusqu\'au :', + 'Email settings' => 'Paramètres des emails', + 'Email sender address' => 'Adresse email de l\'expéditeur', + 'Email transport' => 'Transport des emails', + 'Webhook token' => 'Jeton de sécurité des webhooks', + 'Project tags management' => 'Gestion des libellés pour le projet', + 'Tag created successfully.' => 'Libellé créé avec succès.', + 'Unable to create this tag.' => 'Impossible de créer ce libellé.', + 'Tag updated successfully.' => 'Libellé mis à jour avec succès.', + 'Unable to update this tag.' => 'Impossible de mettre à jour ce libellé.', + 'Tag removed successfully.' => 'Libellé supprimé avec succès.', + 'Unable to remove this tag.' => 'Impossible de supprimer ce libellé.', + 'Global tags management' => 'Gestion des libellés globaux', + 'Tags' => 'Libellés', + 'Tags management' => 'Gestion des libellés', + 'Add new tag' => 'Ajouter un nouveau libellé', + 'Edit a tag' => 'Mettre à jour un libellé', + 'Project tags' => 'Libellés du projet', + 'There is no specific tag for this project at the moment.' => 'Il n\'y a aucun libellé spécifique pour ce projet pour le moment.', + 'Tag' => 'Libellé', + 'Remove a tag' => 'Supprimer un libellé', + 'Do you really want to remove this tag: "%s"?' => 'Voulez-vous vraiment supprimer ce libellé : « %s » ?', + 'Global tags' => 'Libellés globaux', + 'There is no global tag at the moment.' => 'Il n\'y a aucun libellé global pour le moment.', + 'This field cannot be empty' => 'Ce champ ne peut être vide', + 'Close a task when there is no activity in a 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 has 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', + 'Assign a color when the task is moved to a specific swimlane' => 'Assigner une couleur lorsque une tâche est déplacé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éplacé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éplacer 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éplacer 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 n\'est 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 partie de ce rôle pourront seulement déplacer 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 deviendront 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 projet', + '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éplacer 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', + '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', + 'Your profile must have a valid email address.' => 'Votre profil doit avoir une adresse email valide.', + 'TRL - Turkish Lira' => 'TRL - Livre turque', + 'The project email is optional and could be used by several plugins.' => 'L\'adresse email d\'un projet est optionnelle et pourrait être utilisée par plusieurs extensions.', + 'The project email must be unique across all projects' => 'L\'adresse email d\'un projet doit être unique pour tous les projets', + 'The email configuration has been disabled by the administrator.' => 'La configuration des emails a été désactivée par l\'administrateur.', + 'Close this project' => 'Fermer ce projet', + 'Open this project' => 'Ouvrir ce projet', + 'Close a project' => 'Fermer un projet', + 'Do you really want to close this project: "%s"?' => 'Voulez-vous vraiment fermer ce projet : « %s » ?', + 'Reopen a project' => 'Rouvrir un projet', + 'Do you really want to reopen this project: "%s"?' => 'Voulez-vous vraiment rouvrir ce projet : « %s » ?', + 'This project is open' => 'Ce projet est actif', + 'This project is closed' => 'Ce projet est fermé', + 'Unable to upload files, check the permissions of your data folder.' => 'Impossible de transférer le ou les fichiers, vérifiez les permissions du répertoire de données.', + 'Another category with the same name exists in this project' => 'Une autre catégorie avec le même nom existe dans ce projet', + 'Comment sent by email successfully.' => 'Commentaire envoyé par email avec succès.', + 'Sent by email to "%s" (%s)' => 'Envoyé par email à « %s » (%s)', + 'Unable to read uploaded file.' => 'Impossible de lire le fichier téléversé.', + 'Database uploaded successfully.' => 'Base de données téléversée avec succès.', + 'Task sent by email successfully.' => 'Tâche envoyée par email avec succès.', + 'There is no category in this project.' => 'Il n\'y a aucune catégorie dans ce projet.', + 'Send by email' => 'Envoyer par email', + 'Create and send a comment by email' => 'Créer et envoyer un commentaire par email', + 'Subject' => 'Sujet', + 'Upload the database' => 'Téléverser la base de données', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Vous pouvez téléverser la base de données SQLite précédemment téléchargée au format Gzip.', + 'Database file' => 'Fichier de la base de données', + 'Upload' => 'Téléverser', + 'Your project must have at least one active swimlane.' => 'Votre projet doit avoir au moins une swimlane active.', + 'Project: %s' => 'Projet : %s', + 'Automatic action not found: "%s"' => 'Action automatique introuvable : « %s »', + '%d projects' => '%d projets', + '%d project' => '%d projet', + 'There is no project.' => 'Il n\'y a pas de projet.', + 'Sort' => 'Trier', + 'Project ID' => 'ID du projet', + 'Project name' => 'Nom du projet', + 'Public' => 'Public', + 'Personal' => 'Personnel', + '%d tasks' => '%d tâches', + '%d task' => '%d tâche', + 'Task ID' => 'ID de la tâche', + 'Assign automatically a color when due date is expired' => 'Assigner automatiquement une couleur lorsque la date d\'échéance expire', + 'Total score in this column across all swimlanes' => 'Score total dans cette colonne à travers toutes les swimlanes', + 'HRK - Kuna' => 'HRK - Kuna croate', + 'ARS - Argentine Peso' => 'ARS - Peso argentin', + 'COP - Colombian Peso' => 'COP - Peso colombien', + '%d groups' => '%d groupes', + '%d group' => '%d groupe', + 'Group ID' => 'ID du groupe', + 'External ID' => 'Identifiant externe', + '%d users' => '%d utilisateurs', + '%d user' => '%d utilisateur', + 'Hide subtasks' => 'Cacher les sous-tâches', + 'Show subtasks' => 'Montrer les sous-tâches', + 'Authentication Parameters' => 'Paramètres d\'authentification', + 'API Access' => 'Accès à l\'API', + 'No users found.' => 'Aucun utilisateur trouvé.', + 'User ID' => 'ID de l\'utilisateur', + 'Notifications are activated' => 'Notifications activées', + 'Notifications are disabled' => 'Notifications désactivées', + 'User disabled' => 'Utilisateur désactivé', + '%d notifications' => '%d notifications', + '%d notification' => '%d notification', + 'There is no external integration installed.' => 'Il n\'y a aucune intégration externe installée.', + 'You are not allowed to update tasks assigned to someone else.' => 'Vous n\'êtes pas autorisé à mettre à jour une tâche assignée à quelqu\'un d\'autre.', + 'You are not allowed to change the assignee.' => 'Vous n\'êtes pas autorisé à changer l\'assigné', + 'Task suppression is not permitted' => 'La suppression des tâches n\'est pas autorisée', + 'Changing assignee is not permitted' => 'Changement d\'assigné non autorisé', + 'Update only assigned tasks is permitted' => 'Seulement la mise à jour des tâches assignées est autorisé', + 'Only for tasks assigned to the current user' => 'Seulement pour les tâches assignées à l\'utilisateur courant', + 'My projects' => 'Mes projets', + 'You are not a member of any project.' => 'Vous n\'êtes membre d\'aucun projet', + 'My subtasks' => 'Mes sous-tâches', + '%d subtasks' => '%d sous-tâches', + '%d subtask' => '%d sous-tâche', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Seulement le déplacement des tâches entre ces colonnes est autorisé pour l\'utilisateur courant', + '[DUPLICATE]' => '[COPIE]', + 'DKK - Danish Krona' => 'DKK - Couronne danoise', + 'Remove user from group' => 'Supprimer cet utilisateur du groupe', + 'Assign the task to its creator' => 'Assigner une tâche à son créateur', + 'This task was sent by email to "%s" with subject "%s".' => 'Cette tâche a été envoyée par courrier électronique à « %s » avec le sujet « %s ».', + 'Predefined Email Subjects' => 'Sujets de courrier électronique prédéfinis', + 'Write one subject by line.' => 'Écrivez un sujet par ligne.', + 'Create another link' => 'Créer un autre lien', + 'BRL - Brazilian Real' => 'BRL - Real brésilien', + 'Add a new Kanboard task' => 'Ajouter une nouvelle tâche Kanboard', + 'Subtask not started' => 'Sous-tâche pas encore commencé', + 'Subtask currently in progress' => 'Sous-tâche actuellement en progrès', + 'Subtask completed' => 'Sous-tâche terminée', + 'Subtask added successfully.' => 'Sous-tâche ajoutée avec succès.', + '%d subtasks added successfully.' => '%d sous-tâches ajoutées avec succès.', + 'Enter one subtask by line.' => 'Entrez une sous-tâche par ligne.', + 'Predefined Contents' => 'Contenus prédéfinis', + 'Predefined contents' => 'Contenus prédéfinis', + 'Predefined Task Description' => 'Modèles de description de tâches', + 'Do you really want to remove this template? "%s"' => 'Voulez-vous vraiment supprimer ce modèle ? « %s »', + 'Add predefined task description' => 'Ajouter un modèle de description de tâche', + 'Predefined Task Descriptions' => 'Modèles de description de tâches', + 'Template created successfully.' => 'Modèle créé avec succès.', + 'Unable to create this template.' => 'Impossible de créer ce modèle.', + 'Template updated successfully.' => 'Modèle mis à jour avec succès.', + 'Unable to update this template.' => 'Impossible de mettre à jour ce modèle.', + 'Template removed successfully.' => 'Modèle supprimé avec succès.', + 'Unable to remove this template.' => 'Impossible de supprimer ce modèle.', + 'Template for the task description' => 'Modèle pour la description des tâches', + 'The start date is greater than the end date' => 'La date de début est plus grande que la date d\'échéance', + 'Tags must be separated by a comma' => 'Les libellés doivent être séparés par une virgule', + 'Only the task title is required' => 'Seulement le titre est obligatoire', + 'Creator Username' => 'Identifiant du créateur', + 'Color Name' => 'Nom de la couleur', + 'Column Name' => 'Nom de la colonne', + 'Swimlane Name' => 'Nom de la swimlane', + 'Time Estimated' => 'Durée estimée', + 'Time Spent' => 'Temps passé', + 'External Link' => 'Lien externe', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Cette fonctionnalité active l\'abonnement iCal, le flux RSS et la vue publique du tableau.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Arrêter le minuteur de toutes les sous-tâches lorsque la tâche est déplacée dans une autre colonne', + 'Subtask Title' => 'Titre de la sous-tâche', + 'Add a subtask and activate the timer when moving a task to another column' => 'Ajouter une sous-tâche et activer le minuteur lorsque la tâche est déplacée dans une autre colonne', + 'days' => 'jours', + 'minutes' => 'minutes', + 'seconds' => 'secondes', + 'Assign automatically a color when preset start date is reached' => 'Assigner automatiquement une couleur lorsque la date de début est passée', + 'Move the task to another column once a predefined start date is reached' => 'Déplacer la tâche vers une autre colonne lorsque la date de début est passée', + 'This task is now linked to the task %s with the relation "%s"' => 'Cette tâche est maintenant liée à la tâche %s avec la relation « %s »', + 'The link with the relation "%s" to the task %s has been removed' => 'Le lien avec la relation « %s » de la tâche %s a été supprimé', + 'Custom Filter:' => 'Filtre personnalisé :', + 'Unable to find this group.' => 'Impossible de trouver ce groupe.', + '%s moved the task #%d to the column "%s"' => '%s a déplacé la tâche #%d vers la colonne « %s »', + '%s moved the task #%d to the position %d in the column "%s"' => '%s a déplacé la tâche #%d à la position %d dans la colonne « %s »', + '%s moved the task #%d to the swimlane "%s"' => '%s a déplacé la tâche #%d vers la swimlane « %s »', + '%sh spent' => '%s h passé', + '%sh estimated' => '%s h estimé', + 'Select All' => 'Tout sélectionner', + 'Unselect All' => 'Tout désélectionner', + 'Apply action' => 'Appliquer une action', + 'Move selected tasks to another column or swimlane' => 'Déplacer les tâches sélectionnées vers une autre colonne ou swimlane', + 'Edit tasks in bulk' => 'Modifier les tâches en masse', + 'Choose the properties that you would like to change for the selected tasks.' => 'Choisissez les propriétés que vous souhaitez changer pour les tâches sélectionnées.', + 'Configure this project' => 'Configurer ce projet', + 'Start now' => 'Débuter maintenant', + '%s removed a file from the task #%d' => '%s a supprimé un fichier de la tâche #%d', + 'Attachment removed from task #%d: %s' => 'Pièce jointe supprimée de la tâche #%d : %s', + 'No color' => 'Aucune couleur', + 'Attachment removed "%s"' => 'Pièce jointe « %s » supprimée', + '%s removed a file from the task %s' => '%s a supprimé un fichier de la tâche %s', + 'Move the task to another swimlane when assigned to a user' => 'Déplacer la tâche dans une autre swimlane lorsqu\'un utilisateur est assigné', + 'Destination swimlane' => 'Swimlane de destination', + 'Assign a category when the task is moved to a specific swimlane' => 'Assigner une catégorie lorsque la tâche est déplacée dans une swimlane spécifique', + 'Move the task to another swimlane when the category is changed' => 'Déplacer la tâche dans une autre swimlane lorsque la catégorie est modifiée', + 'Reorder this column by priority (ASC)' => 'Réorganiser cette colonne par priorité (ASC)', + 'Reorder this column by priority (DESC)' => 'Réorganiser cette colonne par priorité (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Réorganiser cette colonne par assigné et par priorité (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Réorganiser cette colonne par assigné et par priorité (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Réorganiser cette colonne par assigné (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Réorganiser cette colonne par assigné (Z-A)', + 'Reorder this column by due date (ASC)' => 'Réorganiser cette colonne par date d\'échéance (ASC)', + 'Reorder this column by due date (DESC)' => 'Réorganiser cette colonne par date d\'échéance (DESC)', + 'Reorder this column by id (ASC)' => 'Réorganiser cette colonne par ID (ASC)', + 'Reorder this column by id (DESC)' => 'Réorganiser cette colonne par ID (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s a déplacé la tâche #%d « %s » vers le projet « %s »', + 'Task #%d "%s" has been moved to the project "%s"' => 'La tâche #%d « %s » a été déplacée vers le projet « %s »', + 'Move the task to another column when the due date is less than a certain number of days' => 'Déplacer la tâche dans une autre colonne lorsque la date d\'échéance est inférieure à un certain nombre de jour', + 'Automatically update the start date when the task is moved away from a specific column' => 'Mettre à jour automatiquement la date de début lorsque la tâche change de colonne', + 'HTTP Client:' => 'Client HTTP :', + 'Assigned' => 'Assigné', + 'Task limits apply to each swimlane individually' => 'Les limites de tâches s\'appliquent à chaque swimlane individuellement', + 'Column task limits apply to each swimlane individually' => 'Les limites de tâches par colonne s\'appliquent à chaque swimlane individuellement', + 'Column task limits are applied to each swimlane individually' => 'Les limites de tâches par colonne sont appliquées à chaque swimlane individuellement', + 'Column task limits are applied across swimlanes' => 'Les limites des tâches par colonne sont appliquées entre les swimlanes', + 'Task limit: ' => 'Limite de tâche : ', + 'Change to global tag' => 'Changer le libellé à global', + 'Do you really want to make the tag "%s" global?' => 'Voulez-vous vraiment rendre le libellé « %s » global ?', + 'Enable global tags for this project' => 'Activer les libellés globaux pour ce projet', + 'Group membership(s):' => 'Membre des groupes :', + '%s is a member of the following group(s): %s' => '%s est membre des groupes suivants : %s', + '%d/%d group(s) shown' => '%d/%d groupe(s) affiché(s)', + 'Subtask creation or modification' => 'Création ou modification d\'une sous-tâche', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Assigner la tâche à un utilisateur lorsque la tâche est déplacée dans une swimelane spécifique', + 'Comment' => 'Commentaire', + 'Collapse vertically' => 'Réduire verticalement', + 'Expand vertically' => 'Agrandir verticalement', + 'MXN - Mexican Peso' => 'MXN - Peso mexicain', + 'Estimated vs actual time per column' => 'Temps estimé vs temps réel par colonne', + 'HUF - Hungarian Forint' => 'HUF - Forint hongrois', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Vous devez sélectionner un fichier à télécharger pour votre avatar !', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Le fichier que vous avez téléchargé n\'est pas une image valide ! (Seuls * .gif, * .jpg, * .jpeg et * .png sont autorisés !)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Changer automatiquement la date d\'échéance lorsque la tâche est déplacée dans une colonne spécifique', + 'No other projects found.' => 'Aucun autre projet trouvé.', + 'Tasks copied successfully.' => 'Tâches copiées avec succès.', + 'Unable to copy tasks.' => 'Impossible de copier les tâches.', + 'Theme' => 'Thème', + 'Theme:' => 'Thème :', + 'Light theme' => 'Thème clair', + 'Dark theme' => 'Thème sombre', + 'Automatic theme - Sync with system' => 'Thème automatique - Se synchronize avec le système d\'exploitation', + 'Application managers or more' => 'Gestionnaires de l\'application ou plus', + 'Administrators' => 'Administrateurs', + 'Visibility:' => 'Visibilité :', + 'Standard users' => 'Utilisateurs standards', + 'Visibility is required' => 'La visibilité est obligatoire', + 'The visibility should be an app role' => 'La visibilité doit être un rôle de l\'application', + 'Reply' => 'Répondre', + '%s wrote: ' => '%s a écrit : ', + 'Number of visible tasks in this column and swimlane' => 'Nombre de tâches visibles dans cette colonne et cette swimlane', + 'Number of tasks in this swimlane' => 'Nombre de tâches dans cette swimlane', + 'Unable to find another subtask in progress, you can close this window.' => 'Impossible de trouver une autre sous-tâche en cours, vous pouvez fermer cette fenêtre.', + 'This theme is invalid' => 'Ce thème est invalide', + 'This role is invalid' => 'Ce rôle est invalide', + 'This timezone is invalid' => 'Ce fuseau horaire est invalide', + 'This language is invalid' => 'Cette langue est invalide', + 'This URL is invalid' => 'Cette URL est invalide', + 'Date format invalid' => 'Format de date invalide', + 'Time format invalid' => 'Format de l\'heure invalide', + 'Invalid Mail transport' => 'Transport de courrier invalide', + 'Color invalid' => 'Couleur invalide', + 'This value must be greater or equal to %d' => 'Cette valeur doit être supérieure ou égale à %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Ajouter un BOM au début du fichier (requis pour Microsoft Excel)', + 'Just add these tag(s)' => 'Ajouter seulement ces libellés', + 'Remove internal link(s)' => 'Enlever le(s) lien(s) interne(s)', + 'Import tasks from another project' => 'Importer les tâches d\'un autre projet', + 'Select the project to copy tasks from' => 'Sélectionner le projet dont vous souhaitez copier les tâches', + 'The total maximum allowed attachments size is %sB.' => 'La taille totale maximale autorisée pour les pièces jointes est de %sB.', + 'Add attachments' => 'Ajouter des pièces jointes', + 'Task #%d "%s" is overdue' => 'La tâche n°%d « %s » est en retard', + 'Enable notifications by default for all new users' => 'Activer les notifications par défaut pour tous les nouveaux utilisateurs', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Assigner la tâche à son créateur pour des colonnes spécifiques si aucun responsable n\'est défini manuellement', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Assigner une tâche à l\'utilisateur connecté lors du changement de colonne vers la colonne spécifiée si aucun utilisateur n\'est assigné', +]; diff --git a/app/Locale/hr_HR/translations.php b/app/Locale/hr_HR/translations.php new file mode 100644 index 0000000..ee8c8de --- /dev/null +++ b/app/Locale/hr_HR/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Nema', + 'Edit' => 'Uredi', + 'Remove' => 'ObriÅ¡i', + 'Yes' => 'Da', + 'No' => 'Ne', + 'cancel' => 'odustani', + 'or' => 'ili', + 'Yellow' => 'Žuta', + 'Blue' => 'Plava', + 'Green' => 'Zelena', + 'Purple' => 'LjubiÄasta', + 'Red' => 'Crvena', + 'Orange' => 'NaranÄasta', + 'Grey' => 'Siva', + 'Brown' => 'SmeÄ‘a', + 'Deep Orange' => 'Tamno naranÄasta', + 'Dark Grey' => 'Tamno siva', + 'Pink' => 'Roza', + 'Teal' => 'Tirkizna', + 'Cyan' => 'Cyan', + 'Lime' => 'Limun zelena', + 'Light Green' => 'Svijetlo zelena', + 'Amber' => 'Jantar', + 'Save' => 'Snimi', + 'Login' => 'Prijava', + 'Official website:' => 'Službena Web stranica:', + 'Unassigned' => 'Nedodijeljen', + 'View this task' => 'Pogledaj zadatak', + 'Remove user' => 'Makni korisnika', + 'Do you really want to remove this user: "%s"?' => 'Doista želiÅ¡ maknuti korisnika: "%s"?', + 'All users' => 'Svi korisnici', + 'Username' => 'Korisnik', + 'Password' => 'Lozinka', + 'Administrator' => 'Administrator', + 'Sign in' => 'Prijava', + 'Users' => 'Korisnik', + 'Forbidden' => 'Zabranjeno', + 'Access Forbidden' => 'Zabranjen prostup', + 'Edit user' => 'Promijeni korisnika', + 'Logout' => 'Odjava', + 'Bad username or password' => 'Neispravno korisniÄko ime ili lozinka', + 'Edit project' => 'Promijeni projekt', + 'Name' => 'Naziv', + 'Projects' => 'Projekti', + 'No project' => 'Bez projekta', + 'Project' => 'Projekt', + 'Status' => 'Status', + 'Tasks' => 'Zadatak', + 'Board' => 'PloÄa', + 'Actions' => 'Radnje', + 'Inactive' => 'Neaktivan', + 'Active' => 'Aktivan', + 'Unable to update this board.' => 'Nije moguće promijeniti ovu ploÄu.', + 'Disable' => 'Onemogući', + 'Enable' => 'Omogući', + 'New project' => 'Novi projekt', + 'Do you really want to remove this project: "%s"?' => 'Doista želiÅ¡ maknuti ovaj projekt: "%s"?', + 'Remove project' => 'Makni projekt', + 'Edit the board for "%s"' => 'Promijeni ploÄu za "%s"', + 'Add a new column' => 'Dodaj novi stupac', + 'Title' => 'Naslov', + 'Assigned to %s' => 'Dodijeljen korisniku %s', + 'Remove a column' => 'Makni stupac', + 'Unable to remove this column.' => 'Ne mogu maknuti stupac.', + 'Do you really want to remove this column: "%s"?' => 'Doista želiÅ¡ maknuti ovaj stupac: "%s"?', + 'Settings' => 'Postavke', + 'Application settings' => 'Postavke aplikacije', + 'Language' => 'Jezik', + 'Webhook token:' => 'Token :', + 'API token:' => 'Token za API', + 'Database size:' => 'VeliÄina baze :', + 'Download the database' => 'Preuzmi bazu', + 'Optimize the database' => 'Optimiziraj bazu', + '(VACUUM command)' => '(naredba VACUUM)', + '(Gzip compressed Sqlite file)' => '(Sqlite baza zapakirana Gzip-om)', + 'Close a task' => 'Zatvori zadatak', + 'Column' => 'Stupac', + 'Color' => 'Boja', + 'Assignee' => 'Dodijeli', + 'Create another task' => 'Dodaj idući zadatak', + 'New task' => 'Novi zadatak', + 'Open a task' => 'Otvori zadatak', + 'Do you really want to open this task: "%s"?' => 'Da li zaista želiÅ¡ otvoriti zadatak: "%s"?', + 'Back to the board' => 'Natrag na ploÄu', + 'There is nobody assigned' => 'Nitko nije dodijeljen!', + 'Column on the board:' => 'Stupac na ploÄi:', + 'Close this task' => 'Zatvori ovaj zadatak', + 'Open this task' => 'Otvori ovaj zadatak', + 'There is no description.' => 'Bez opisa.', + 'Add a new task' => 'Dodaj zadatak', + 'The username is required' => 'KorisniÄko ime je obavezno', + 'The maximum length is %d characters' => 'Maksimalna dužina je %d znakova', + 'The minimum length is %d characters' => 'Minimalna dužina je %d znakova', + 'The password is required' => 'Lozinka je obavezna', + 'This value must be an integer' => 'Mora biti cijeli broj', + 'The username must be unique' => 'KorisniÄko ime mora biti jedinstveno', + 'The user id is required' => 'Oznaka korisnika je obavezna', + 'Passwords don\'t match' => 'Lozinke se ne podudaraju', + 'The confirmation is required' => 'Potvrda je obavezna', + 'The project is required' => 'Projekt je obavezan', + 'The id is required' => 'Oznaka je obavezna', + 'The project id is required' => 'Oznaka projekta je obavezna', + 'The project name is required' => 'Naziv projekta je obavezan', + 'The title is required' => 'Naslov je obavezan', + 'Settings saved successfully.' => 'Postavke su uspjeÅ¡no snimljene.', + 'Unable to save your settings.' => 'Nije moguće snimanje postavki.', + 'Database optimization done.' => 'Optimizacija baze je zavrÅ¡ena.', + 'Your project has been created successfully.' => 'Projekt je uspjeÅ¡no kreiran.', + 'Unable to create your project.' => 'Nije moguće kreiranje projekta.', + 'Project updated successfully.' => 'Projekt je uspjeÅ¡no promijenjen.', + 'Unable to update this project.' => 'Nije moguća dopuna projekta.', + 'Unable to remove this project.' => 'Nije moguće uklanjanje projekta.', + 'Project removed successfully.' => 'Projekt uspjeÅ¡no maknut.', + 'Project activated successfully.' => 'Projekt je uspjeÅ¡no aktiviran.', + 'Unable to activate this project.' => 'Nije moguće aktiviranje projekta.', + 'Project disabled successfully.' => 'Projekt je uspjeÅ¡no deaktiviran.', + 'Unable to disable this project.' => 'Nije moguće deaktiviranje projekta.', + 'Unable to open this task.' => 'Nije moguće otvaranje zadatka.', + 'Task opened successfully.' => 'Zadatak je uspjeÅ¡no otvoren.', + 'Unable to close this task.' => 'Nije moguće zatvaranje ovog zadatka.', + 'Task closed successfully.' => 'Zadatak je uspjeÅ¡no zatvoren.', + 'Unable to update your task.' => 'Nije moguća dopuna zadatka.', + 'Task updated successfully.' => 'Zadatak je uspjeÅ¡no dopunjen.', + 'Unable to create your task.' => 'Nije moguće kreiranje zadatka.', + 'Task created successfully.' => 'Zadatak je uspjeÅ¡no kreiran.', + 'User created successfully.' => 'Korisnik je uspjeÅ¡no kreiran', + 'Unable to create your user.' => 'Nije uspjelo kreiranje korisnika.', + 'User updated successfully.' => 'Korisnik je uspjeÅ¡no dopunjen.', + 'User removed successfully.' => 'Korisnik je uspjeÅ¡no maknut.', + 'Unable to remove this user.' => 'Nije moguće uklanjanje korisnika.', + 'Board updated successfully.' => 'PloÄa uspjeÅ¡no dopunjena.', + 'Ready' => 'Spreman', + 'Backlog' => 'Nadolazeće', + 'Work in progress' => 'U radu', + 'Done' => 'Gotovo', + 'Application version:' => 'Verzija aplikacije:', + 'Id' => 'Rb', + 'Public link' => 'Javni link', + 'Timezone' => 'Vremenska zona', + 'Sorry, I didn\'t find this information in my database!' => 'Na žalost, nije pronaÄ‘ena informacija u bazi', + 'Page not found' => 'Strana nije pronaÄ‘ena', + 'Complexity' => 'Složenost', + 'Task limit' => 'OgraniÄenje zadatka', + 'Task count' => 'Broj zadataka', + 'User' => 'Korisnik', + 'Comments' => 'Komentari', + 'Comment is required' => 'Komentar je obavezan', + 'Comment added successfully.' => 'Komentar je uspjeÅ¡no dodan', + 'Unable to create your comment.' => 'Nije moguće kreiranje komentara', + 'Due Date' => 'Rok', + 'Invalid date' => 'Neispravan datum', + 'Automatic actions' => 'Automatske radnje', + 'Your automatic action has been created successfully.' => 'UspjeÅ¡no kreirana automatska radnja', + 'Unable to create your automatic action.' => 'Nije moguće kreiranje automatske radnje', + 'Remove an action' => 'ObriÅ¡i radnju', + 'Unable to remove this action.' => 'Nije moguće obrisati radnju', + 'Action removed successfully.' => 'Radnja obrisana', + 'Automatic actions for the project "%s"' => 'Radnje za automatizaciju projekta "%s"', + 'Add an action' => 'Dodaj radnju', + 'Event name' => 'Naziv dogaÄ‘aja', + 'Action' => 'Radnja', + 'Event' => 'DogaÄ‘aj', + 'When the selected event occurs execute the corresponding action.' => 'Kad se dogodi odabrani dogaÄ‘aj izvrÅ¡i odgovarajuću radnju', + 'Next step' => 'Idući korak', + 'Define action parameters' => 'Definiraj parametre radnje', + 'Do you really want to remove this action: "%s"?' => 'Želite obrisati radnju "%s"?', + 'Remove an automatic action' => 'ObriÅ¡i automatsku radnju', + 'Assign the task to a specific user' => 'Dodijeli zadatak odreÄ‘enom korisniku', + 'Assign the task to the person who does the action' => 'Dodijeli zadatak korisniku koji je napravio radnju', + 'Duplicate the task to another project' => 'Kopiraj zadatak u drugi projekt', + 'Move a task to another column' => 'Premjesti zadatak u drugi stupac', + 'Task modification' => 'Izman zadatka', + 'Task creation' => 'Kreiranje zadatka', + 'Closing a task' => 'Zatvaranja zadatka', + 'Assign a color to a specific user' => 'Dodijeli boju korisniku', + 'Position' => 'Pozicija', + 'Duplicate to project' => 'Kopiraj u drugi projekt', + 'Duplicate' => 'Napravi kopiju', + 'Link' => 'Poveznica', + 'Comment updated successfully.' => 'Komentar je uspjeÅ¡no dopunjen.', + 'Unable to update your comment.' => 'Nije moguće dopuniti komentar.', + 'Remove a comment' => 'ObriÅ¡i komentar', + 'Comment removed successfully.' => 'Komentar je uspjeÅ¡no maknut.', + 'Unable to remove this comment.' => 'Nije uspjelo brisanje komentara.', + 'Do you really want to remove this comment?' => 'Da li da obriÅ¡em ovaj komentar?', + 'Current password for the user "%s"' => 'Trenutna lozinka za korisnika "%s"', + 'The current password is required' => 'Trenutna lozinka je obavezna', + 'Wrong password' => 'PogreÅ¡na lozinka', + 'Unknown' => 'Nepoznat', + 'Last logins' => 'Zadnja prijava', + 'Login date' => 'Datum prijave', + 'Authentication method' => 'Metoda autorizacije', + 'IP address' => 'IP adresa', + 'User agent' => 'Browser', + 'Persistent connections' => 'Stalna konekcija', + 'No session.' => 'Nema sesije', + 'Expiration date' => 'IstiÄe', + 'Remember Me' => 'Zapamti me', + 'Creation date' => 'Datum kreiranja', + 'Everybody' => 'Svi', + 'Open' => 'Otvoreni', + 'Closed' => 'Zatvoreni', + 'Search' => 'Traži', + 'Nothing found.' => 'NiÅ¡ta nije pronaÄ‘eno', + 'Due date' => 'Rok', + 'Description' => 'Opis', + '%d comments' => '%d komentara', + '%d comment' => '%d komentar', + 'Email address invalid' => 'PogreÅ¡an e-mail', + 'Your external account is not linked anymore to your profile.' => 'Vanjski korisniÄki raÄun viÅ¡e nije povezan sa vaÅ¡im profilom.', + 'Unable to unlink your external account.' => 'Nije moguće maknuti vezu sa vanjskim korisniÄkim raÄunom.', + 'External authentication failed' => 'Vanjska autorizacija nije uspjela', + 'Your external account is linked to your profile successfully.' => 'Vanjski korisniÄki raÄun je uspjeÅ¡no povezan sa vaÅ¡im profilom.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Zadatak uspjeÅ¡no maknut.', + 'Unable to remove this task.' => 'Nije moguće uklanjanje zadatka.', + 'Remove a task' => 'Makni zadatak', + 'Do you really want to remove this task: "%s"?' => 'Da li da obriÅ¡em zadatak "%s"?', + 'Assign automatically a color based on a category' => 'Automatski dodijeli boju po kategoriji', + 'Assign automatically a category based on a color' => 'Automatski dodijeli kategoriju po boji', + 'Task creation or modification' => 'Kreiranje ili promjena zadatka', + 'Category' => 'Kategorija', + 'Category:' => 'Kategorija:', + 'Categories' => 'Kategorije', + 'Your category has been created successfully.' => 'UspjeÅ¡no kreirana kategorija.', + 'This category has been updated successfully.' => 'Kategorija uspjeÅ¡no dopunjena.', + 'Unable to update this category.' => 'Kategoriju nije moguće dopuniti.', + 'Remove a category' => 'ObriÅ¡i kategoriju', + 'Category removed successfully.' => 'Kategorija je uspjeÅ¡no maknuta.', + 'Unable to remove this category.' => 'Nije moguće maknuti kategoriju.', + 'Category modification for the project "%s"' => 'Promjena kategorije za projekt "%s"', + 'Category Name' => 'Naziv kategorije', + 'Add a new category' => 'Dodaj novu kategoriju', + 'Do you really want to remove this category: "%s"?' => 'Doista želite maknuti kategoriju: "%s"?', + 'All categories' => 'Sve kategorije', + 'No category' => 'Bez kategorije', + 'The name is required' => 'Naziv je obavezan', + 'Remove a file' => 'Makni datoteku', + 'Unable to remove this file.' => 'Datoteku nije moguće maknuti.', + 'File removed successfully.' => 'Datoteka je uspjeÅ¡no maknuta.', + 'Attach a document' => 'Dodaj dokument', + 'Do you really want to remove this file: "%s"?' => 'Doista želite maknuti datoteku: "%s"?', + 'Attachments' => 'Prilozi', + 'Edit the task' => 'Promjena zadatka', + 'Add a comment' => 'Dodaj komentar', + 'Edit a comment' => 'Promjena komentara', + 'Summary' => 'Pregled', + 'Time tracking' => 'Praćenje vremena', + 'Estimate:' => 'Procjena:', + 'Spent:' => 'PotroÅ¡eno:', + 'Do you really want to remove this sub-task?' => 'Doista želite maknuti podzadatak?', + 'Remaining:' => 'Preostalo:', + 'hours' => 'sati', + 'estimated' => 'procjena', + 'Sub-Tasks' => 'Podzadaci', + 'Add a sub-task' => 'Dodaj podzadatak', + 'Original estimate' => 'Izvorna procjena', + 'Create another sub-task' => 'Dodaj novi podzadatak', + 'Time spent' => 'PotroÅ¡eno vremena', + 'Edit a sub-task' => 'Promijeni podzadatak', + 'Remove a sub-task' => 'Makni podzadatak', + 'The time must be a numeric value' => 'Vrijeme mora biti broj', + 'Todo' => 'Za rad', + 'In progress' => 'U radu', + 'Sub-task removed successfully.' => 'Podzadatak je uspjeÅ¡no maknut.', + 'Unable to remove this sub-task.' => 'Nije mouće maknuti ovaj podzadatak.', + 'Sub-task updated successfully.' => 'Podzadatak je uspjeÅ¡no dopunjen.', + 'Unable to update your sub-task.' => 'Nije moguće dopuniti ovaj podzadatak.', + 'Unable to create your sub-task.' => 'Nije moguće krerati ovaj podzadatak.', + 'Maximum size: ' => 'Maksimalna veliÄina: ', + 'Display another project' => 'Prikaži drugi projekt', + 'Created by %s' => 'Kreirao %s', + 'Tasks Export' => 'Izvoz zadataka', + 'Start Date' => 'PoÄetni datum', + 'Execute' => 'IzvrÅ¡i', + 'Task Id' => 'Identifikator Zadatka', + 'Creator' => 'Autor', + 'Modification date' => 'Datum promjene', + 'Completion date' => 'Datum kompletiranja', + 'Clone' => 'Iskopiraj', + 'Project cloned successfully.' => 'Projekt uspjeÅ¡no kopiran.', + 'Unable to clone this project.' => 'Nije moguće kopirati projekt.', + 'Enable email notifications' => 'Omogući obaveÅ¡tenja e-mailom', + 'Task position:' => 'Pozicija zadatka:', + 'The task #%d has been opened.' => 'Zadatak #%d je otvoren.', + 'The task #%d has been closed.' => 'Zadatak #%d je zatvoren.', + 'Sub-task updated' => 'Podzadatak dopunjen', + 'Title:' => 'Naslov:', + 'Status:' => 'Status:', + 'Assignee:' => 'Dodijeljeno:', + 'Time tracking:' => 'Praćenje vremena: ', + 'New sub-task' => 'Novi Podzadatak', + 'New attachment added "%s"' => 'Novi prilog ubaÄen "%s"', + 'New comment posted by %s' => 'Novi komentar ostavio %s', + 'New comment' => 'Novi komentar', + 'Comment updated' => 'Komentar dopunjen', + 'New subtask' => 'Novi podzadatak', + 'I only want to receive notifications for these projects:' => 'Želim primati obavijesti samo za ove projekte:', + 'view the task on Kanboard' => 'Pregledaj zadatke', + 'Public access' => 'Javni pristup', + 'Disable public access' => 'Zabrani javni pristup', + 'Enable public access' => 'Dozvoli javni pristup', + 'Public access disabled' => 'Javni pristup onemogućen!', + 'Move the task to another project' => 'Premjesti zadatak u drugi projekt', + 'Move to project' => 'Premjesti u drugi projekt', + 'Do you really want to duplicate this task?' => 'Želite duplicirati ovaj zadatak?', + 'Duplicate a task' => 'Kopiraj zadatak', + 'External accounts' => 'Vanjski korisniÄki raÄun', + 'Account type' => 'Vrsta korisniÄkog raÄuna', + 'Local' => 'Lokalno', + 'Remote' => 'Udaljeno', + 'Enabled' => 'Omogući', + 'Disabled' => 'Onemogući', + 'Login:' => 'KorisniÄko ime:', + 'Full Name:' => 'Ime i prezime:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Obavijesti:', + 'Notifications' => 'Obavijesti', + 'Account type:' => 'Vrsta korisniÄkog raÄuna:', + 'Edit profile' => 'Promjena profila', + 'Change password' => 'Promjena lozinke', + 'Password modification' => 'Promjena lozinke', + 'External authentications' => 'Vanjske autorizacije', + 'Never connected.' => 'Bez spajanja.', + 'No external authentication enabled.' => 'Nisu omogućene vanjske autorizacije.', + 'Password modified successfully.' => 'UspjeÅ¡na promjena lozinke.', + 'Unable to change the password.' => 'Nije moguće promijeniti lozinku.', + 'Change category' => 'Promijeni kategoriju', + '%s updated the task %s' => '%s dopunio zadatak %s', + '%s opened the task %s' => '%s otvorio zadatak %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s pomaknuo zadatak %s na poziciju #%d u stupcu "%s"', + '%s moved the task %s to the column "%s"' => '%s pomaknuo zadatak %s u stupac "%s"', + '%s created the task %s' => '%s je kreirao zadatak %s', + '%s closed the task %s' => '%s je zatvorio zadatak %s', + '%s created a subtask for the task %s' => '%s je kreirao podzadatak zadatka %s', + '%s updated a subtask for the task %s' => '%s je dopunio podzadatak zadatka %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Dodijeljen korisniku %s uz procjenu vremena %s/%sh', + 'Not assigned, estimate of %sh' => 'Nije dodijeljen, procijenjeno vrijeme %sh', + '%s updated a comment on the task %s' => '%s je dopunio komentar zadatka %s', + '%s commented the task %s' => '%s je komentirao zadatak %s', + '%s\'s activity' => 'Aktivnosti %s', + 'RSS feed' => 'RSS kanal', + '%s updated a comment on the task #%d' => '%s je dopunio komentar zadatka #%d', + '%s commented on the task #%d' => '%s je komentirao zadatak #%d', + '%s updated a subtask for the task #%d' => '%s je dopunio podzadatak zadatka #%d', + '%s created a subtask for the task #%d' => '%s je kreirao podzadatak zadatka #%d', + '%s updated the task #%d' => '%s je dopunio zadatak #%d', + '%s created the task #%d' => '%s je kreirao zadatak #%d', + '%s closed the task #%d' => '%s je zatvorio zadatak #%d', + '%s opened the task #%d' => '%s je otvorio zadatak #%d', + 'Activity' => 'Aktivnosti', + 'Default values are "%s"' => 'Inicijalne vrijednosti su: "%s"', + 'Default columns for new projects (Comma-separated)' => 'Inicijalni stupci za novi projekt (odvojeni zarezom)', + 'Task assignee change' => 'Promjena dodjele zadatka', + '%s changed the assignee of the task #%d to %s' => '%s promijenio kome je dodijeljen zadatak #%d na %s', + '%s changed the assignee of the task %s to %s' => '%s promijenio kome je dodijeljen zadatak %s na %s', + 'New password for the user "%s"' => 'Nova lozinka za korisnika "%s"', + 'Choose an event' => 'Odaberi dogaÄ‘aj', + 'Create a task from an external provider' => 'Kreiraj zadatak iz vanjskog izvora', + 'Change the assignee based on an external username' => 'Dodijeli zadatak ovisno o vanjskom imenu korisnika', + 'Change the category based on an external label' => 'Dodijeli zadatak ovisno o vanjskoj oznaci', + 'Reference' => 'Referenca', + 'Label' => 'Oznaka', + 'Database' => 'Baza podataka', + 'About' => 'O programu', + 'Database driver:' => 'UpravljaÄki program baze podataka:', + 'Board settings' => 'PodeÅ¡avanje ploÄe', + 'Webhook settings' => 'Webhook postavke', + 'Reset token' => 'Resetiraj token', + 'API endpoint:' => 'API pristupna toÄka:', + 'Refresh interval for personal board' => 'Osvježavanje privatnih ploÄa', + 'Refresh interval for public board' => 'Osvježavanje javnih ploÄa', + 'Task highlight period' => 'Koliko dugo je zadatak istaknut', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Vrijeme (u sekundama) koliko se zadatak smatra nedavno mijenjanim (0 iskljuÄi, inicijalno 2 dana)', + 'Frequency in second (60 seconds by default)' => 'UÄestalost u sekundama (inicijalno 60 sekundi)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'UÄestalost u sekundama (inicijalno 10 sekundi)', + 'Application URL' => 'URL adresa aplikacije', + 'Token regenerated.' => 'Token wygenerowany ponownie.', + 'Date format' => 'Oblik datuma', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO je uvijek prihvatljiv, npr: "%s", "%s"', + 'New personal project' => 'Novi privatni projekt', + 'This project is personal' => 'Ovo je privatni projekt', + 'Add' => 'Dodaj', + 'Start date' => 'Datum poÄetka', + 'Time estimated' => 'Procjena vremena', + 'There is nothing assigned to you.' => 'NiÅ¡ta vam nije dodijeljeno', + 'My tasks' => 'Moji zadaci', + 'Activity stream' => 'Tijek aktivnosti', + 'Dashboard' => 'Nadzorna ploÄa', + 'Confirmation' => 'Potvrda', + 'Webhooks' => 'Webhook-ovi', + 'API' => 'API', + 'Create a comment from an external provider' => 'Kreirajte komentar za vanjskog pružatelja', + 'Project management' => 'UreÄ‘ivanje projekata', + 'Columns' => 'Stupci', + 'Task' => 'Zadaci', + 'Percentage' => 'Postotak', + 'Number of tasks' => 'Broj zadataka', + 'Task distribution' => 'Podjela zadataka', + 'Analytics' => 'Analiza', + 'Subtask' => 'Podzadatak', + 'User repartition' => 'Zaduženja korisnika', + 'Clone this project' => 'Kopiraj projekt', + 'Column removed successfully.' => 'Stupac je uspjeÅ¡no maknut.', + 'Not enough data to show the graph.' => 'Nedovoljno podataka za grafikon.', + 'Previous' => 'Prethodni', + 'The id must be an integer' => 'RB mora biti cijeli broj', + 'The project id must be an integer' => 'RB projekta mora biti cijeli broj', + 'The status must be an integer' => 'Status mora biti cijeli broj', + 'The subtask id is required' => 'RB podzadatka je obavezan', + 'The subtask id must be an integer' => 'RB podzadatka mora biti cijeli broj', + 'The task id is required' => 'RB zadatka je obavezan', + 'The task id must be an integer' => 'RB zadatka mora biti cijeli broj', + 'The user id must be an integer' => 'RB korisnika mora biti cijeli broj', + 'This value is required' => 'Ova vrijednost je obavezna', + 'This value must be numeric' => 'Ova vrijednost mora biti broj', + 'Unable to create this task.' => 'Nije moguće kreirati zadatak.', + 'Cumulative flow diagram' => 'Kumulativni dijagram toka', + 'Daily project summary' => 'Dnevni sažetak projekta', + 'Daily project summary export' => 'Izvoz dnevnog sažetka projekta', + 'Exports' => 'Izvoz', + 'This export contains the number of tasks per column grouped per day.' => 'Ovaj izvoz sadrži broj zadataka po stupcu grupirano po danima.', + 'Active swimlanes' => 'Aktivne staze', + 'Add a new swimlane' => 'Dodaj novu stazu', + 'Default swimlane' => 'Inicijalna staza', + 'Do you really want to remove this swimlane: "%s"?' => 'Želite maknuti ovu stazu: "%s"?', + 'Inactive swimlanes' => 'Neaktivne staze', + 'Remove a swimlane' => 'Makni stazu', + 'Swimlane modification for the project "%s"' => 'Promjena staza za projekt "%s"', + 'Swimlane removed successfully.' => 'Staza uspjeÅ¡no maknuta.', + 'Swimlanes' => 'Staze', + 'Swimlane updated successfully.' => 'Staza je uspjeÅ¡no dopunjena.', + 'Unable to remove this swimlane.' => 'Nije moguće maknuti ovu stazu', + 'Unable to update this swimlane.' => 'Nije moguća dopuna ove staze', + 'Your swimlane has been created successfully.' => 'VaÅ¡a staza je uspjeÅ¡no kreirana.', + 'Example: "Bug, Feature Request, Improvement"' => 'Npr: "GreÅ¡ka, Zahtjev za promjenom, PoboljÅ¡anje"', + 'Default categories for new projects (Comma-separated)' => 'Inicijalne kategorije projekta', + 'Integrations' => 'Integracije', + 'Integration with third-party services' => 'Integracija sa uslugama vanjskih servisa', + 'Subtask Id' => 'RB podzadatka', + 'Subtasks' => 'Podzadaci', + 'Subtasks Export' => 'Izvoz podzadataka', + 'Task Title' => 'Naslov zadatka', + 'Untitled' => 'Bez naslova', + 'Application default' => 'Inicijalne postavke aplikacje', + 'Language:' => 'Jezik:', + 'Timezone:' => 'Vremenska zona:', + 'All columns' => 'Svi stupci', + 'Next' => 'Idući', + '#%d' => '#%d', + 'All swimlanes' => 'Sve staze', + 'All colors' => 'Sve boje', + 'Moved to column %s' => 'Pomaknut u stupac %s', + 'User dashboard' => 'KorisniÄka nadzorna ploÄa', + 'Allow only one subtask in progress at the same time for a user' => 'Dozvoli samo jedan podzadatak "u tijeku" po korisniku', + 'Edit column "%s"' => 'Uredi stupac "%s"', + 'Select the new status of the subtask: "%s"' => 'Izaberi novi status za podzadatak: "%s"', + 'Subtask timesheet' => 'Tabela vremena za podzadatke', + 'There is nothing to show.' => 'Nema podataka', + 'Time Tracking' => 'Praćenje vremena', + 'You already have one subtask in progress' => 'Već imate jedan podzadatak "u radu"', + 'Which parts of the project do you want to duplicate?' => 'Koje dijelove projekta želite kopirati?', + 'Disallow login form' => 'Zabrani formu prijave', + 'Start' => 'PoÄetak', + 'End' => 'Kraj', + 'Task age in days' => 'Starost zadatka u danima', + 'Days in this column' => 'Dana u ovom stupcu', + '%dd' => '%dd', + 'Add a new link' => 'Dodaj novu vezu', + 'Do you really want to remove this link: "%s"?' => 'Doista želite maknuti ovu vezu: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Doista želite maknuti ovu vezu sa zadatkom #%d?', + 'Field required' => 'Polje je obavezno', + 'Link added successfully.' => 'Veza je uspjeÅ¡no dodana.', + 'Link updated successfully.' => 'Veza je uspjeÅ¡no dopunjena.', + 'Link removed successfully.' => 'Veza je uspjeÅ¡no maknuta.', + 'Link labels' => 'Oznake poveznica', + 'Link modification' => 'Promjena veze', + 'Opposite label' => 'Suprotna oznaka', + 'Remove a link' => 'Makni vezu', + 'The labels must be different' => 'Oznake moraju biti razliÄite', + 'There is no link.' => 'Ne postoji veza', + 'This label must be unique' => 'Ova oznaka mora biti jedinstvena', + 'Unable to create your link.' => 'Nije moguće kreirati vezu.', + 'Unable to update your link.' => 'Nije moguće dopuniti vezu.', + 'Unable to remove this link.' => 'Nije moguće maknuti vezu.', + 'relates to' => 'povezan sa', + 'blocks' => 'blokira', + 'is blocked by' => 'je blokiran od', + 'duplicates' => 'duplicira', + 'is duplicated by' => 'je duplikat od', + 'is a child of' => 'je dijete od', + 'is a parent of' => 'je roditelj od', + 'targets milestone' => 'cilj kljuÄne toÄke', + 'is a milestone of' => 'je kljuÄna toÄka od', + 'fixes' => 'popravlja', + 'is fixed by' => 'je popravljen od', + 'This task' => 'Ovaj zadatak', + '<1h' => '<1s', + '%dh' => '%ds', + 'Expand tasks' => 'ProÅ¡iri zadatke', + 'Collapse tasks' => 'Skupi zadatke', + 'Expand/collapse tasks' => 'ProÅ¡iri/skupi zadatke', + 'Close dialog box' => 'Zatvori dijalog', + 'Submit a form' => 'PoÅ¡alji obrazac', + 'Board view' => 'Pregled ploÄe', + 'Keyboard shortcuts' => 'PreÄice tipkovnice', + 'Open board switcher' => 'Otvori promjenu ploÄe', + 'Application' => 'Aplikacija', + 'Compact view' => 'Kompaktni prikaz', + 'Horizontal scrolling' => 'Horizontalno listanje', + 'Compact/wide view' => 'Kompaktni/Å¡iroki pregled', + 'Currency' => 'Valuta', + 'Personal project' => 'Osobni projekt', + 'AUD - Australian Dollar' => 'AUD - Australski dolar', + 'CAD - Canadian Dollar' => 'CAD - Kanadski dolar', + 'CHF - Swiss Francs' => 'CHF - Å vicarski franak', + 'Custom Stylesheet' => 'PrilagoÄ‘eni CSS', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britanska funta', + 'INR - Indian Rupee' => 'INR - Indijski rupi', + 'JPY - Japanese Yen' => 'JPY - Japanski yen', + 'NZD - New Zealand Dollar' => 'NZD - Novozelandski dolar', + 'PEN - Peruvian Sol' => 'PEN - Peruanski Sol', + 'RSD - Serbian dinar' => 'RSD - Srpski dinar', + 'CNY - Chinese Yuan' => 'CNY - Kineski jen', + 'USD - US Dollar' => 'USD - AmeriÄki dolar', + 'VES - Venezuelan Bolívar' => 'VES - Venecuelanski bolivar', + 'Destination column' => 'OdrediÅ¡ni stupac', + 'Move the task to another column when assigned to a user' => 'Pomakni zadatak u drugi stupac kada se dodijeli izvrÅ¡itelju', + 'Move the task to another column when assignee is cleared' => 'Pomakni zadatak u drugi stupac kada se makne izvrÅ¡itelj', + 'Source column' => 'Izvorni stupac', + 'Transitions' => 'Pomicanja', + 'Executer' => 'IzvrÅ¡itelj', + 'Time spent in the column' => 'Vrijeme provedeno u stupcu', + 'Task transitions' => 'Pomicanja zadatka', + 'Task transitions export' => 'Izvezi prelaze zadataka', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ovaj izvjeÅ¡taj sadrži sva pomicanja unutar stupaca za svaki zadatak sa datumom, korisnikom i potroÅ¡enim vremenom za svako pomicanje.', + 'Currency rates' => 'TeÄajna lista', + 'Rate' => 'TeÄaj', + 'Change reference currency' => 'Promijeni osnovnu valutu', + 'Reference currency' => 'Referentna valuta', + 'The currency rate has been added successfully.' => 'TeÄaj je uspjeÅ¡no dodan.', + 'Unable to add this currency rate.' => 'Nije moguće dodati ovaj teÄaj.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s je maknuo/la izvrÅ¡itelja zadatka %s', + 'Information' => 'Informacije', + 'Check two factor authentication code' => 'Provjerite kod za dvo-faktorsku autorizaciju', + 'The two factor authentication code is not valid.' => 'Kod za dvo-faktorsku autorizaciju nije ispravan.', + 'The two factor authentication code is valid.' => 'Kod za dvo-faktorsku autorizaciju je ispravan', + 'Code' => 'Kod', + 'Two factor authentication' => 'Dvo-faktorska autorizacija', + 'This QR code contains the key URI: ' => 'Ovaj QR kod sadrži kljuÄni URL:', + 'Check my code' => 'Provjeri moj kod', + 'Secret key: ' => 'Tajni kljuÄ: ', + 'Test your device' => 'Testiraj svoj ureÄ‘aj', + 'Assign a color when the task is moved to a specific column' => 'Dodijeli boju kada je zadatak pomaknut u odabrani stupac', + '%s via Kanboard' => '%s pomoću Kanboard', + 'Burndown chart' => 'Preostalo posla / vrijeme', + 'This chart show the task complexity over the time (Work Remaining).' => 'Ovaj graf pokazuje kompleksnost zadataka tokom vremena (Preostalo posla)', + 'Screenshot taken %s' => 'Slika ekrana preuzeta %s', + 'Add a screenshot' => 'Dodaj sliku ekrana', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Uzmi sliku ekrana i pritisni CTRL+V ili ⌘+V za zalijepiti.', + 'Screenshot uploaded successfully.' => 'Slika ekrana uspjeÅ¡no dodana.', + 'SEK - Swedish Krona' => 'SEK - Å vedska kruna', + 'Identifier' => 'Oznaka', + 'Disable two factor authentication' => 'Onemogućite dvo-faktorsku autorizaciju', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Doista želite onemogućiti dvo-faktorsku autorizaciju za ovog korisnika: "%s"?', + 'Edit link' => 'Uredi vezu', + 'Start to type task title...' => 'PoÄnite pisati naslov zadatka...', + 'A task cannot be linked to itself' => 'Zadatak ne može biti povezan sa samim sobom', + 'The exact same link already exists' => 'IdentiÄna veza već postoji', + 'Recurrent task is scheduled to be generated' => 'Ponavljajući zadatak je pripremljen za kreiranje', + 'Score' => 'Bodovi', + 'The identifier must be unique' => 'Identifikator mora biti jedinstven', + 'This linked task id doesn\'t exists' => 'Povezani RB zadatka ne postoji', + 'This value must be alphanumeric' => 'Ova vrijednost mora biti znakovno-brojÄana', + 'Edit recurrence' => 'Promjena ponavljanja', + 'Generate recurrent task' => 'Kreiraj ponavljajući zadatak', + 'Trigger to generate recurrent task' => 'OkidaÄ koji kreira ponavljajući zadatak', + 'Factor to calculate new due date' => 'Faktor za raÄunanje novog roka zavrÅ¡etka', + 'Timeframe to calculate new due date' => 'Vremenski okvir za raÄunanje novog roka zavrÅ¡etka', + 'Base date to calculate new due date' => 'PoÄetni datum za raÄunanje novog roka zavrÅ¡etka', + 'Action date' => 'Datum radnje', + 'Base date to calculate new due date: ' => 'PoÄetni datum za raÄunanje novog roka zavrÅ¡etka: ', + 'This task has created this child task: ' => 'Zadatak je kreirao ovaj zadatak-dijete: ', + 'Day(s)' => 'Dan/i', + 'Existing due date' => 'Postojeći rok zavrÅ¡etka', + 'Factor to calculate new due date: ' => 'Faktor za raÄunanje novog roka zavrÅ¡etka: ', + 'Month(s)' => 'Mjesec(i)', + 'This task has been created by: ' => 'Ovaj zadatak je kreirao: ', + 'Recurrent task has been generated:' => 'Ponavljajući zadatak je kreirao:', + 'Timeframe to calculate new due date: ' => 'Vremenski okvir za raÄunanje novog roka zavrÅ¡etka:', + 'Trigger to generate recurrent task: ' => 'OkidaÄ za kreiranje ponavljajućeg zadatka', + 'When task is closed' => 'Kada je zadatak zatvoren', + 'When task is moved from first column' => 'Kada je zadatak pomaknut iz prvog stupca', + 'When task is moved to last column' => 'Kada je zadatak pomaknut u poslednji stupac', + 'Year(s)' => 'Godina/e', + 'Project settings' => 'Postavke projekta', + 'Automatically update the start date' => 'Automatski dopuni poÄetni datum', + 'iCal feed' => 'iCal kanal', + 'Preferences' => 'Postavke', + 'Security' => 'Sigurnost', + 'Two factor authentication disabled' => 'Dvo-faktorska autorizacija je onemogućena', + 'Two factor authentication enabled' => 'Dvo-faktorska autorizacija je omogućena', + 'Unable to update this user.' => 'Nije moguće dopuniti ovog korisnika', + 'There is no user management for personal projects.' => 'Nema upravljanja korisnicima kod osobnih projekata.', + 'User that will receive the email' => 'Korisnik koji će primiti e-mail', + 'Email subject' => 'Predmet e-mail-a', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'Dodaj komentar u dnevnik kada se zadatak pomakne iz jednog stupca u drugi', + 'Move the task to another column when the category is changed' => 'Pomakni zadatak u drugi stupac kada se promijeni kategorija', + 'Send a task by email to someone' => 'PoÅ¡alji zadatak nekome putem e-mail-a', + 'Reopen a task' => 'Ponovo otvori zadatak', + 'Notification' => 'Obavijesti', + '%s moved the task #%d to the first swimlane' => '%s je pomaknuo/la zadatak #%d u prvu stazu', + 'Swimlane' => 'Staza', + '%s moved the task %s to the first swimlane' => '%s je pomaknuo/la zadatak %s u prvu stazu', + '%s moved the task %s to the swimlane "%s"' => '%s je pomaknuo/la zadatak %s u stazu "%s"', + 'This report contains all subtasks information for the given date range.' => 'Ovaj izvjeÅ¡taj sadrži sve informacije o podzadacima u zadanom razdoblju', + 'This report contains all tasks information for the given date range.' => 'Ovaj izvjeÅ¡taj sadrži sve informacije o zadacima u zadanom razdoblju', + 'Project activities for %s' => 'Aktivnosti projekta za %s', + 'view the board on Kanboard' => 'Pregledaj ploÄu', + 'The task has been moved to the first swimlane' => 'Zadatak je pomaknut u prvu stazu', + 'The task has been moved to another swimlane:' => 'Zadatak je pomaknut u drugu stazu:', + 'New title: %s' => 'Novi naslov: %s', + 'The task is not assigned anymore' => 'Ovaj zadatak viÅ¡e nema izvrÅ¡itelja', + 'New assignee: %s' => 'Novi izvrÅ¡itelj: %s', + 'There is no category now' => 'Sada nema kategorije', + 'New category: %s' => 'Nova kategorija: %s', + 'New color: %s' => 'Nova boja: %s', + 'New complexity: %d' => 'Nova kompleksnost: %d', + 'The due date has been removed' => 'Rok zavrÅ¡etka je maknut', + 'There is no description anymore' => 'Nema viÅ¡e opisa', + 'Recurrence settings has been modified' => 'Promjena podeÅ¡enja za ponavljajuće zadatke', + 'Time spent changed: %sh' => 'PotroÅ¡eno vrijeme je promijenjeno: %sh', + 'Time estimated changed: %sh' => 'Procjena vremena je promijenjena: %sh', + 'The field "%s" has been updated' => 'Polje "%s" je dopunjeno', + 'The description has been modified:' => 'Promjena opisa:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Doista želite zatvoriti zadatak "%s" i sve podzadatke?', + 'I want to receive notifications for:' => 'Želim primati obavijesti za:', + 'All tasks' => 'Sve zadatke', + 'Only for tasks assigned to me' => 'Samo zadatke koji su mi dodijeljeni', + 'Only for tasks created by me' => 'Samo zadatke koje sam kreirao', + 'Only for tasks created by me and tasks assigned to me' => 'Samo zadatke koji su mi dodijeljeni i koje sam kreirao', + '%%Y-%%m-%%d' => '%%d.%%m.%%Y', + 'Total for all columns' => 'Ukupno za sve stupce', + 'You need at least 2 days of data to show the chart.' => 'Da bi se prikazao grafikon trebaju postojati podaci barem dva dana unatrag.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Zaustavi timer', + 'Start timer' => 'Pokreni timer', + 'My activity stream' => 'Tijek mojih aktivnosti', + 'Search tasks' => 'Pretraživanje zadataka', + 'Reset filters' => 'ObriÅ¡i filtere', + 'My tasks due tomorrow' => 'Moji zadaci sa rokom sutra', + 'Tasks due today' => 'Zadaci sa rokom danas', + 'Tasks due tomorrow' => 'Zadaci sa rokom sutra', + 'Tasks due yesterday' => 'Zadaci sa rokom juÄer', + 'Closed tasks' => 'Zatvoreni zadaci', + 'Open tasks' => 'Otvoreni zadaci', + 'Not assigned' => 'Nije dodijeljeno', + 'View advanced search syntax' => 'Pogledaj naprednu sintaksu pretraživanja', + 'Overview' => 'Pregled', + 'Board/Calendar/List view' => 'Pregled PloÄe/Kalendara/Liste', + 'Switch to the board view' => 'Prebacivanje na prikaz ploÄe', + 'Switch to the list view' => 'Prebacivanje na prikaz liste', + 'Go to the search/filter box' => 'Idi na polje za pretragu / filter', + 'There is no activity yet.' => 'JoÅ¡ uvijek nema aktivnosti.', + 'No tasks found.' => 'Zadaci nisu pronaÄ‘eni.', + 'Keyboard shortcut: "%s"' => 'PreÄica tipkovnice: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filter', + 'Advanced search' => 'Napredno pretraživanje', + 'Example of query: ' => 'Primjer upita: ', + 'Search by project: ' => 'Pretraživanje po projektu: ', + 'Search by column: ' => 'Pretraživanje po stupcu: ', + 'Search by assignee: ' => 'Pretraživanje kome je dodijeljeno: ', + 'Search by color: ' => 'Pretraživanje po boji: ', + 'Search by category: ' => 'Pretraživanje po kategoriji: ', + 'Search by description: ' => 'Pretraživanje po opisu: ', + 'Search by due date: ' => 'Pretraživanje po datumskom roku: ', + 'Average time spent in each column' => 'Prosek potroÅ¡enog vremena u svakom stupcu', + 'Average time spent' => 'Prosjek potroÅ¡enog vremena', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Ovaj graf pokazuje prosjek potroÅ¡enog vremena u svakom stupcu za zadnjih %d zadataka.', + 'Average Lead and Cycle time' => 'ProsjeÄno vrijeme realizacije i izvoÄ‘enja', + 'Average lead time: ' => 'ProsjeÄno vrijeme realizacije: ', + 'Average cycle time: ' => 'ProsjeÄno vrijeme ciklusa: ', + 'Cycle Time' => 'Vrijeme ciklusa', + 'Lead Time' => 'Vrijeme realizacije', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Ova grafika prikazuje prosjek vremena realizacije i ciklusa za zadnjih %d zadataka tijekom vremena.', + 'Average time into each column' => 'ProsjeÄno vrijeme u svakom stupcu', + 'Lead and cycle time' => 'Vrijeme realizacije i ciklusa', + 'Lead time: ' => 'Vrijeme realizacije: ', + 'Cycle time: ' => 'Vrijeme ciklusa: ', + 'Time spent in each column' => 'PotroÅ¡eno vrijeme u svakom stupcu', + 'The lead time is the duration between the task creation and the completion.' => 'Vrijeme realizacije je trajanje od otvaranja do zavrÅ¡etka zadatka.', + 'The cycle time is the duration between the start date and the completion.' => 'Vrijeme ciklusa je trajanje od poÄetka do zavrÅ¡etka zadatka.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Ako zadatak nije zavrÅ¡en, trenutno vrijeme se koristi umesto datuma zavrÅ¡etka.', + 'Set the start date automatically' => 'Automatski postavi poÄetno vrijeme', + 'Edit Authentication' => 'Uredi Autorizacije', + 'Remote user' => 'Udaljeni korisnik', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Za udaljenog korisnika lozinka se ne Äuva 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 je oznaÄeno polje "Zabrani formu prijave" unos pristupnih podataka u formi prijave biti će ignoriran.', + 'Default task color' => 'Inicijalna boja zadataka', + 'This feature does not work with all browsers.' => 'Ova funckionalnost ne radi na svim Web preglednicima.', + 'There is no destination project available.' => 'Nije dostupan niti jedan odrediÅ¡ni projekt.', + 'Trigger automatically subtask time tracking' => 'Automatsko pokretanje evidencije vremena za podzadatke', + 'Include closed tasks in the cumulative flow diagram' => 'UkljuÄi zatvorene zadatke u kumulativni dijagram toka', + 'Current swimlane: %s' => 'Trenutna staza: %s', + 'Current column: %s' => 'Trenutni stupac: %s', + 'Current category: %s' => 'Trenutna kategorija: %s', + 'no category' => 'bez kategorije', + 'Current assignee: %s' => 'Trenutni izvrÅ¡itelj: %s', + 'not assigned' => 'nije dodijeljeno', + 'Author:' => 'Autor:', + 'contributors' => 'suradnici', + 'License:' => 'Licenca:', + 'License' => 'Licenca', + 'Enter the text below' => 'UpiÅ¡ite tekst ispod', + 'Start date:' => 'PoÄetno vrijeme:', + 'Due date:' => 'Rok:', + 'People who are project managers' => 'Osobe koji su manageri projekta', + 'People who are project members' => 'Osobe koje su Älanovi projekta', + 'NOK - Norwegian Krone' => 'NOK - NorveÅ¡ka kruna', + 'Show this column' => 'Prikaži ovaj stupac', + 'Hide this column' => 'Sakrij ovaj stupac', + 'End date' => 'Datum zavrÅ¡etka', + 'Users overview' => 'Pregled korisnika', + 'Members' => 'ÄŒlanovi', + 'Shared project' => 'Dijeljeni projekt', + 'Project managers' => 'Manageri projekta', + 'Projects list' => 'Lista projekata', + 'End date:' => 'Datum zavrÅ¡etka:', + 'Change task color when using a specific task link' => 'Promijeni boju kada se koristi odreÄ‘ena vrsta veze na zadatku', + 'Task link creation or modification' => 'Veza na zadatku je kreirana ili promijenjena', + 'Milestone' => 'KljuÄna toÄka', + 'Reset the search/filter box' => 'Resetiraj polje za pretragu/filtere', + 'Documentation' => 'Dokumentacija', + 'Author' => 'Autor', + 'Version' => 'Verzija', + 'Plugins' => 'Dodaci', + 'There is no plugin loaded.' => 'Nema uÄitanih dodataka.', + 'My notifications' => 'Moje obavijesti', + 'Custom filters' => 'PrilagoÄ‘eni filteri', + 'Your custom filter has been created successfully.' => 'PrilagoÄ‘eni filter je uspjeÅ¡no kreiran.', + 'Unable to create your custom filter.' => 'Nije moguće kreirati prilagoÄ‘eni filter.', + 'Custom filter removed successfully.' => 'PrilagoÄ‘eni filter je uspjeÅ¡no maknut.', + 'Unable to remove this custom filter.' => 'Nije moguće maknuti prilagoÄ‘eni filter.', + 'Edit custom filter' => 'Uredi prilagoÄ‘eni filter', + 'Your custom filter has been updated successfully.' => 'PrilagoÄ‘eni filter je uspjeÅ¡no dopunjen.', + 'Unable to update custom filter.' => 'Nije moguće dopuniti prilagoÄ‘eni filter', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Novi privitak na zadatku #%d: %s', + 'New comment on task #%d' => 'Novi komentar na zadatku #%d', + 'Comment updated on task #%d' => 'Dopunjen komentar na zadatku #%d', + 'New subtask on task #%d' => 'Novi podzadatak na zadatku #%d', + 'Subtask updated on task #%d' => 'Podzadatak dopunjen na zadatku #%d', + 'New task #%d: %s' => 'Novi zadatak #%d: %s', + 'Task updated #%d' => 'Zadatak dopunjen #%d', + 'Task #%d closed' => 'Zadatak #%d zatvoren', + 'Task #%d opened' => 'Zadatak #%d otvoren', + 'Column changed for task #%d' => 'Promijenjen stupac za zadatak #%d', + 'New position for task #%d' => 'Nova pozicija za zadatak #%d', + 'Swimlane changed for task #%d' => 'Staza izmjenjena za zadatak #%d', + 'Assignee changed on task #%d' => 'IzvrÅ¡itelj je promijenjen na zadatku #%d', + '%d overdue tasks' => '%d zadataka kasni', + 'No notification.' => 'Nema novih obavijesti.', + 'Mark all as read' => 'OznaÄi sve kao proÄitano', + 'Mark as read' => 'OznaÄi kao proÄitano', + 'Total number of tasks in this column across all swimlanes' => 'Ukupan broj zadataka u ovom stupcu u svim stazama', + 'Collapse swimlane' => 'Skupi stazu', + 'Expand swimlane' => 'ProÅ¡iri stazu', + 'Add a new filter' => 'Dodaj novi filter', + 'Share with all project members' => 'Podijeli sa svim Älanovima projekta', + 'Shared' => 'Podijeljeno', + 'Owner' => 'Vlasnik', + 'Unread notifications' => 'NeproÄitane obavijesti', + 'Notification methods:' => 'Vrste obavijesti:', + 'Unable to read your file' => 'Nije moguće proÄitati datoteku', + '%d task(s) have been imported successfully.' => '%d zadataka uspjeÅ¡no uvezeno.', + 'Nothing has been imported!' => 'NiÅ¡ta nije uvezeno!', + 'Import users from CSV file' => 'Uvezi korisnike putem CSV datoteke', + '%d user(s) have been imported successfully.' => '%d korisnika je uspjeÅ¡no uvezeno.', + 'Comma' => 'Zarez', + 'Semi-colon' => 'ToÄka-zarez', + 'Tab' => 'Tab', + 'Vertical bar' => 'Vertikalna traka', + 'Double Quote' => 'Dvostruki navodnici', + 'Single Quote' => 'Jednostruki navodnici', + '%s attached a file to the task #%d' => '%s dodao/la je novu datoteku u zadatak %d', + 'There is no column or swimlane activated in your project!' => 'Nema stupca ili aktivne staze u vaÅ¡em projektu!', + 'Append filter (instead of replacement)' => 'Dodaj filter (umesto zamjene postojećeg)', + 'Append/Replace' => 'Dodaj/Zamijeni', + 'Append' => 'Dodaj', + 'Replace' => 'Zamijeni', + 'Import' => 'Uvoz', + 'Change sorting' => 'Promijeni sortiranje', + 'Tasks Importation' => 'Uvoz zadataka', + 'Delimiter' => 'GraniÄnik', + 'Enclosure' => 'Prilog', + 'CSV File' => 'CSV datoteka', + 'Instructions' => 'Uputstva', + 'Your file must use the predefined CSV format' => 'VaÅ¡a datoteka mora koristiti predefinirani CSV format', + 'Your file must be encoded in UTF-8' => 'VaÅ¡a datoteka mora koristiti UTF-8 kodiranje', + 'The first row must be the header' => 'Prvi red mora biti zaglavlje', + 'Duplicates are not verified for you' => 'Duplikati se ne provjeravaju (morate sami)', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Datum roka zavrÅ¡etka mora biti u ISO formatu: YYYY-MM-DD', + 'Download CSV template' => 'Preuzmi CSV predložak', + 'No external integration registered.' => 'Nije zabilježena niti jedna vanjska integracija.', + 'Duplicates are not imported' => 'Duplikati nisu uveženi', + 'Usernames must be lowercase and unique' => 'KorisniÄka imena moraju biti napisana malim slovima i jedinstvena', + 'Passwords will be encrypted if present' => 'Lozinke (ako postoje) će biti kriptirane', + '%s attached a new file to the task %s' => '%s je dodao datoteku za zadatak %s', + 'Link type' => 'Tip veze', + 'Assign automatically a category based on a link' => 'Automatsko dodjeljivanje kategorije bazirano na vezi', + 'BAM - Konvertible Mark' => 'BAM - Konvertibilna marka', + 'Assignee Username' => 'KorisniÄko ime izvrÅ¡itelja', + 'Assignee Name' => 'Ime izvrÅ¡itelja', + 'Groups' => 'Grupe', + 'Members of %s' => 'ÄŒlanovi u %s', + 'New group' => 'Nova grupa', + 'Group created successfully.' => 'Grupa uspjeÅ¡no kreirana.', + 'Unable to create your group.' => 'Nije moguće kreirati grupu.', + 'Edit group' => 'Uredi grupu', + 'Group updated successfully.' => 'Grupa uspjeÅ¡no dopunjena.', + 'Unable to update your group.' => 'Nije moguće dopuniti grupu', + 'Add group member to "%s"' => 'Dodaj Älana u grupu "%s"', + 'Group member added successfully.' => 'UspjeÅ¡no dodan Älan grupe.', + 'Unable to add group member.' => 'Nije moguće dodati Älana grupi.', + 'Remove user from group "%s"' => 'Makni korisnika iz grupe "%s"', + 'User removed successfully from this group.' => 'Korisnik uspjeÅ¡no maknut iz grupe.', + 'Unable to remove this user from the group.' => 'Nije moguće maknuti korisnika iz grupe.', + 'Remove group' => 'Makni grupu', + 'Group removed successfully.' => 'Grupa je uspjeÅ¡no maknuta.', + 'Unable to remove this group.' => 'Nije moguće maknuti grupu.', + 'Project Permissions' => 'Prava na projektu', + 'Manager' => 'Manager', + 'Project Manager' => 'Manager projekta', + 'Project Member' => 'ÄŒlan projekta', + 'Project Viewer' => 'PromatraÄ projekta', + 'Your account is locked for %d minutes' => 'VaÅ¡ korisniÄki raÄun je zakljuÄan idućih %d minuta', + 'Invalid captcha' => 'PogreÅ¡na captcha', + 'The name must be unique' => 'Ime mora biti jedinstveno', + 'View all groups' => 'Pregled svih grupa', + 'There is no user available.' => 'Nema dostupnih korisnika.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Doista želite maknuti korisnika "%s" iz grupe "%s"?', + 'There is no group.' => 'Nema grupe', + 'Add group member' => 'Dodaj Älana grupe', + 'Do you really want to remove this group: "%s"?' => 'Doista želite da maknuti ovu grupu: "%s"?', + 'There is no user in this group.' => 'Trenutno nema korisnika u grupi.', + 'Permissions' => 'Prava', + 'Allowed Users' => 'Dozvoljeni korisnici', + 'No specific user has been allowed.' => 'Nije dozvoljeno niti jednom korisniku.', + 'Role' => 'Uloga', + 'Enter user name...' => 'UpiÅ¡i korisniÄko ime...', + 'Allowed Groups' => 'Dozvoljene grupe', + 'No group has been allowed.' => 'Nije dozvoljeno niti jednoj grupi.', + 'Group' => 'Grupa', + 'Group Name' => 'Ime grupe', + 'Enter group name...' => 'UpiÅ¡i ime grupe...', + 'Role:' => 'Uloga:', + 'Project members' => 'ÄŒlanovi projekta', + '%s mentioned you in the task #%d' => '%s vas je spomenuo/la u zadatku #%d', + '%s mentioned you in a comment on the task #%d' => '%s vas je spomenuo/la u komentaru zadatka #%d', + 'You were mentioned in the task #%d' => 'Spomenuti ste u zadatku #%d', + 'You were mentioned in a comment on the task #%d' => 'Spomenuti ste u komntaru od zadatka #%d', + 'Estimated hours: ' => 'Procijenjeno sati:', + 'Actual hours: ' => 'Stvarno sati:', + 'Hours Spent' => 'PotroÅ¡eno sati:', + 'Hours Estimated' => 'Sati procijenjeno', + 'Estimated Time' => 'Procijenjeno vrijeme', + 'Actual Time' => 'Stvarno Vrijeme', + 'Estimated vs actual time' => 'Procijenjeno / stvarno vrijeme', + 'RUB - Russian Ruble' => 'RUB - Ruska rublja', + 'Assign the task to the person who does the action when the column is changed' => 'Dodijeli zadatak osobi koja izvrÅ¡i radnju promjene stupca', + 'Close a task in a specific column' => 'Zatvori zadatak u odreÄ‘enom stupcu', + 'Time-based One-time Password Algorithm' => 'Algoritam vremenski bazirane jednokratne lozinke', + 'Two-Factor Provider: ' => 'IzdavaÄ dvofaktorske prijave:', + 'Disable two-factor authentication' => 'Onemogući dvofaktorsku autorizaciju', + 'Enable two-factor authentication' => 'Omogući dvofaktorsku autorizaciju', + 'There is no integration registered at the moment.' => 'Trenutno nije registrirana niti jedna integracija.', + 'Password Reset for Kanboard' => 'Promjena lozinke za Kanboard', + 'Forgot password?' => 'Zaboravili ste lozinku?', + 'Enable "Forget Password"' => 'Omogući opciju "Zaboravljena lozinka"', + 'Password Reset' => 'Promjena lozinke', + 'New password' => 'Nova lozinka', + 'Change Password' => 'Promijeni lozinku', + 'To reset your password click on this link:' => 'Za promjenu lozinke kliknite na ovaj link:', + 'Last Password Reset' => 'Zadnja promjena lozinke', + 'The password has never been reinitialized.' => 'Lozinka nikada nije bila mijenjana.', + 'Creation' => 'Napravljeno', + 'Expiration' => 'IstiÄe', + 'Password reset history' => 'Povijest promjena lozinke', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Svi zadaci stupca "%s" i staze "%s" su uspjeÅ¡no zatvoreni.', + 'Do you really want to close all tasks of this column?' => 'Doista želite zatvoriti sve zadatke ovog stupca?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d zadatak/a u stupcu "%s" i stazi "%s" će biti zatvoreno.', + 'Close all tasks in this column and this swimlane' => 'Zatvori sve zadatke ovog stupca', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Niti jedan dodatak nije registrirao metodu za obavijesti o projektu. U korisniÄkom profilu i dalje možete urediti pojedinaÄne obavijesti.', + 'My dashboard' => 'Moja nadzorna ploÄa', + 'My profile' => 'Moj profil', + 'Project owner: ' => 'Vlasnik projekta: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Oznaka projekta je opcija i mora biti slovno-brojÄana, npr. MOJPROJEKT1', + 'Project owner' => 'Vlasnik projekta', + 'Personal projects do not have users and groups management.' => 'Osobni projekti nemaju upravljanje korisnicima i grupama.', + 'There is no project member.' => 'Nema Älanova projekta.', + 'Priority' => 'Prioritet', + 'Task priority' => 'Prioritet zadataka', + 'General' => 'Općenito', + 'Dates' => 'Datumi', + 'Default priority' => 'Inicijalni prioritet', + 'Lowest priority' => 'Najmanji prioritet', + 'Highest priority' => 'Najveći prioritet', + 'Close a task when there is no activity' => 'Zatvori zadatak kada nema aktivnosti', + 'Duration in days' => 'Trajanje u danima', + 'Send email when there is no activity on a task' => 'PoÅ¡alji e-mail kada nema aktivnosti na zadatku', + 'Unable to fetch link information.' => 'Ne mogu dohvatiti informacije o vezi.', + 'Daily background job for tasks' => 'Dnevni poslovi u pozadini za zadatke', + 'Auto' => 'Automatski', + 'Related' => 'Povezan', + 'Attachment' => 'Privitak', + 'Web Link' => 'Web link', + 'External links' => 'Vanjske veze', + 'Add external link' => 'Dodaj vanjsku vezu', + 'Type' => 'Vrsta', + 'Dependency' => 'Zavisnost', + 'Add internal link' => 'Dodaj unutarnju vezu', + 'Add a new external link' => 'Dodaj novu vanjsku vezu', + 'Edit external link' => 'Uredi vanjsku vezu', + 'External link' => 'Vanjska veza', + 'Copy and paste your link here...' => 'Kopirajte i zalijepite ovdje vaÅ¡u vezu...', + 'URL' => 'URL', + 'Internal links' => 'Unutarnje veze', + 'Assign to me' => 'Dodijeli meni', + 'Me' => 'Ja', + 'Do not duplicate anything' => 'NiÅ¡ta ne dupliraj', + 'Projects management' => 'UreÄ‘ivanje projekata', + 'Users management' => 'Upravljanje korisnicima', + 'Groups management' => 'Upravljanje grupama', + 'Create from another project' => 'Napravi iz drugog projekta', + 'open' => 'otvoren', + 'closed' => 'zatvoren', + 'Priority:' => 'Prioritet:', + 'Reference:' => 'Referenca:', + 'Complexity:' => 'Kompleksnost:', + 'Swimlane:' => 'Staza:', + 'Column:' => 'Stupac:', + 'Position:' => 'Pozicija:', + 'Creator:' => 'Napravio:', + 'Time estimated:' => 'Procjena vremena:', + '%s hours' => '%s sati', + 'Time spent:' => 'PotroÅ¡eno vrijeme:', + 'Created:' => 'Kreirano:', + 'Modified:' => 'Promjenjeno:', + 'Completed:' => 'ZavrÅ¡eno:', + 'Started:' => 'ZapoÄeto:', + 'Moved:' => 'Pomaknuto:', + 'Task #%d' => 'Zadatak #%d', + 'Time format' => 'Oblik vremena', + 'Start date: ' => 'PoÄetni datum:', + 'End date: ' => 'Datum zavrÅ¡etka:', + 'New due date: ' => 'Novi rok zavrÅ¡etka:', + 'Start date changed: ' => 'PoÄetni datum promijenjen: ', + 'Disable personal projects' => 'Onemogući privatne projekte', + 'Do you really want to remove this custom filter: "%s"?' => 'Doista želite maknuti ovaj prilagoÄ‘eni filter "%s"?', + 'Remove a custom filter' => 'Makni prilagoÄ‘eni filter', + 'User activated successfully.' => 'Korisnik uspjeÅ¡no aktiviran.', + 'Unable to enable this user.' => 'Nije moguće aktivirati ovog korisnika.', + 'User disabled successfully.' => 'Korisnik uspjeÅ¡no deaktiviran.', + 'Unable to disable this user.' => 'Nije moguće deaktivirati ovog korisnika.', + 'All files have been uploaded successfully.' => 'Sve datoteke su uspjeÅ¡no poslane.', + 'The maximum allowed file size is %sB.' => 'Maksimalna dozvoljena veliÄina datoteke je %sB.', + 'Drag and drop your files here' => 'Povucite i ovdje spustite datoteke', + 'choose files' => 'izaberite datoteke', + 'View profile' => 'Pregledaj profil', + 'Two Factor' => 'Dva faktora', + 'Disable user' => 'Deaktiviraj korisnika', + 'Do you really want to disable this user: "%s"?' => 'Doista želite deaktivirati ovog korisnika: "%s"?', + 'Enable user' => 'Aktiviraj korisnika', + 'Do you really want to enable this user: "%s"?' => 'Doista želite aktivirati ovog korisnika: "%s"?', + 'Download' => 'Preuzimanje', + 'Uploaded: %s' => 'Poslano: %s', + 'Size: %s' => 'VeliÄina: %s', + 'Uploaded by %s' => 'Poslao %s', + 'Filename' => 'Ime datoteke', + 'Size' => 'VeliÄina', + 'Column created successfully.' => 'Stupac uspjeÅ¡no napravljen.', + 'Another column with the same name exists in the project' => 'Već postoji stupac sa istim imenom u ovom projektu.', + 'Default filters' => 'Inicijalni filteri', + 'Your board doesn\'t have any columns!' => 'Tvoja ploÄa nema niti jedan stupac!', + 'Change column position' => 'Promijeni poziciju stupca', + 'Switch to the project overview' => 'Prebaci na pregled projekta', + 'User filters' => 'Fliteri korisnika', + 'Category filters' => 'Fliteri kategorija', + 'Upload a file' => 'PoÅ¡alji datoteku', + 'View file' => 'Pogledaj datoteku', + 'Last activity' => 'Zadnja aktivnost', + 'Change subtask position' => 'Promijeni poziciju podzadatka', + 'This value must be greater than %d' => 'Ova vrijednost mora biti veća od %d', + 'Another swimlane with the same name exists in the project' => 'Već postoji staza sa istim imenom u ovom projektu', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Primjer: https://primjer.kanboard.org/ (koristi se za stvaranje apsolutnih URL adresa)', + 'Actions duplicated successfully.' => 'Radnje uspjeÅ¡no duplirane.', + 'Unable to duplicate actions.' => 'Nije moguće duplirati radnje.', + 'Add a new action' => 'Dodaj novu radnju', + 'Import from another project' => 'Uvezi iz drugog projekta', + 'There is no action at the moment.' => 'Trenutno nema radnji.', + 'Import actions from another project' => 'Uvezi radnje iz drugog projekta', + 'There is no available project.' => 'Trenutno nema dostupnih projekata.', + 'Local File' => 'Lokalna datoteka', + 'Configuration' => 'UreÄ‘ivanje', + 'PHP version:' => 'Verzija PHP-a:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Verzija operativnog sustava:', + 'Database version:' => 'Verzija baze podataka:', + 'Browser:' => 'Web preglednik:', + 'Task view' => 'Pregled zadataka', + 'Edit task' => 'Uredi zadatak', + 'Edit description' => 'Uredi opis', + 'New internal link' => 'Nova unutarnja veza', + 'Display list of keyboard shortcuts' => 'Prikaži listu preÄica na tipkovnici', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'PoÅ¡alji moju avatar sliku', + 'Remove my image' => 'Makni moju sliku', + 'The OAuth2 state parameter is invalid' => 'Parametar stanja OAuth2 nije ispravan', + 'User not found.' => 'Korisnik nije pronaÄ‘en.', + 'Search in activity stream' => 'Pretraživanje u tijeku aktivnosti', + 'My activities' => 'Moje aktivnosti', + 'Activity until yesterday' => 'Aktivnosti do juÄer', + 'Activity until today' => 'Aktivnosti do danas', + 'Search by creator: ' => 'Pretraživanje tko je kreirao: ', + 'Search by creation date: ' => 'Pretraživanje po datumu kreiranja: ', + 'Search by task status: ' => 'Pretraživanje po statusu zadatka: ', + 'Search by task title: ' => 'Pretraživanje po naslovu zadatka: ', + 'Activity stream search' => 'Pretraživanje tijeka aktivnosti', + 'Projects where "%s" is manager' => 'Projekti gde je "%s" manager', + 'Projects where "%s" is member' => 'Projekti gde je "%s" Älan', + 'Open tasks assigned to "%s"' => 'Otvoreni zadaci dodijeljeni "%s"', + 'Closed tasks assigned to "%s"' => 'Zatvoreni zadaci dodijeljeni "%s"', + 'Assign automatically a color based on a priority' => 'Automatski dodijeli boju ovisno o prioritetu', + 'Overdue tasks for the project(s) "%s"' => 'Zadaci koji kasne za projekt/e "%s"', + 'Upload files' => 'PoÅ¡alji datoteke', + 'Installed Plugins' => 'Instalirani dodaci', + 'Plugin Directory' => 'Folder dodataka', + 'Plugin installed successfully.' => 'Dodatak je uspjeÅ¡no instaliran.', + 'Plugin updated successfully.' => 'Dodatak je uspjeÅ¡no dopunjen.', + 'Plugin removed successfully.' => 'Dodatak uspjeÅ¡no maknut.', + 'Subtask converted to task successfully.' => 'Podzadatak uspjeÅ¡no pretvoren u zadatak.', + 'Unable to convert the subtask.' => 'Nije moguće pretvoriti podzadatak.', + 'Unable to extract plugin archive.' => 'Nije moguće raspakirati arhivu dodatka.', + 'Plugin not found.' => 'Dodatak nije pronaÄ‘en.', + 'You don\'t have the permission to remove this plugin.' => 'Nemate pravo za micanje ovog dodatka.', + 'Unable to download plugin archive.' => 'Nije moguće preuzeti arhivu dodatka.', + 'Unable to write temporary file for plugin.' => 'Nije moguće zapisati privremenu datoteku za dodatak.', + 'Unable to open plugin archive.' => 'Nije moguće otvoriti arhivu dodatka.', + 'There is no file in the plugin archive.' => 'Nema datoteke u arhivi dodatka.', + 'Create tasks in bulk' => 'Masovno kreiranja zadataka', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Ovaj Kanboard nije namjeÅ¡ten za instaliranje dodataka kroz korisniÄko suÄelje.', + 'There is no plugin available.' => 'Nema dostupnih dodataka.', + 'Install' => 'Instaliraj', + 'Update' => 'Dopuni', + 'Up to date' => 'Ažurno', + 'Not available' => 'Nije dostupno', + 'Remove plugin' => 'Makni dodatak', + 'Do you really want to remove this plugin: "%s"?' => 'Doista želite maknuti ovaj dodatak: "%s"?', + 'Uninstall' => 'Deinstaliraj', + 'Listing' => 'Popis', + 'Metadata' => 'Meta-podaci', + 'Manage projects' => 'UreÄ‘ivanje projekata', + 'Convert to task' => 'Pretvori u zadatak', + 'Convert sub-task to task' => 'Pretvori podzadatak u zadatak', + 'Do you really want to convert this sub-task to a task?' => 'Doista želite pretvoriti podzadatak u zadatak?', + 'My task title' => 'Naslov zadatka', + 'Enter one task by line.' => 'UpiÅ¡i jedan zadatak u svakom redu.', + 'Number of failed login:' => 'Broj neuspjeÅ¡nih prijava:', + 'Account locked until:' => 'KorisniÄki raÄun zakljuÄan do:', + 'Email settings' => 'E-mail postavke', + 'Email sender address' => 'E-mail adresa poÅ¡iljatelja', + 'Email transport' => 'NaÄin slanja e-maila', + 'Webhook token' => 'Webhook token', + 'Project tags management' => 'Upravljanje oznakama projekta', + 'Tag created successfully.' => 'Oznaka uspjeÅ¡no kreirana.', + 'Unable to create this tag.' => 'Nije moguće kreirati oznaku.', + 'Tag updated successfully.' => 'Oznaka uspjeÅ¡no dopunjena.', + 'Unable to update this tag.' => 'Nije moguće dopuniti ovu oznaku.', + 'Tag removed successfully.' => 'Oznaka uspjeÅ¡no maknuta.', + 'Unable to remove this tag.' => 'Nije moguće maknuti ovu oznaku.', + 'Global tags management' => 'Globalno upravljanje oznakama.', + 'Tags' => 'Oznake', + 'Tags management' => 'UreÄ‘ivanje oznaka', + 'Add new tag' => 'Dodaj novu oznaku', + 'Edit a tag' => 'UreÄ‘ivanje oznake', + 'Project tags' => 'Oznake projekta', + 'There is no specific tag for this project at the moment.' => 'Trenutno nema oznaka za ovaj projekt.', + 'Tag' => 'Oznaka', + 'Remove a tag' => 'Brisanje oznake', + 'Do you really want to remove this tag: "%s"?' => 'Doista želite obrisati oznaku: "%s"?', + 'Global tags' => 'Globalne oznake', + 'There is no global tag at the moment.' => 'Trenutno nema globalno definiranih oznaka.', + 'This field cannot be empty' => 'Ovo polje ne može biti prazno', + 'Close a task when there is no activity in a specific column' => 'Zatvori zadatak kada nema aktivnosti u odabranom stupcu', + '%s removed a subtask for the task #%d' => '%s je maknuo podzadatak od zadataka #%d', + '%s removed a comment on the task #%d' => '%s je maknuo/la komentar na zadatku #%d', + 'Comment removed on task #%d' => 'Komentar je maknut na zadatku #%d', + 'Subtask removed on task #%d' => 'Podzadatak je maknut na zadatku #%d', + 'Hide tasks in this column in the dashboard' => 'Sakrijte zadatke u ovom stupcu na kontrolnoj ploÄi', + '%s removed a comment on the task %s' => '%s je maknuo/la komentar za zadatak %s', + '%s removed a subtask for the task %s' => '%s je maknuo podzadatak od zadataka #%s', + 'Comment removed' => 'Komentar maknut', + 'Subtask removed' => 'Podzadatak maknut', + '%s set a new internal link for the task #%d' => '%s je postavio/la novu unutarnju vezu na zadatak #%d', + '%s removed an internal link for the task #%d' => '%s je maknuo/la unutarnju vezu na zadatak #%d', + 'A new internal link for the task #%d has been defined' => 'Nova unutarnja veza na zadatak #%d je postavljena', + 'Internal link removed for the task #%d' => 'unutarnja veza je maknuta za zadatak #%d', + '%s set a new internal link for the task %s' => '%s je postavio/la novu unutarnju vezu za zadatak %s', + '%s removed an internal link for the task %s' => '%s je maknuo/la unutarnju vezu za zadatak %s', + 'Automatically set the due date on task creation' => 'Automatski dodaj rok zavrÅ¡etka na kreiranom zadatku', + 'Move the task to another column when closed' => 'Makni zadatak u drugi stupac kada je zatvoren', + 'Move the task to another column when not moved during a given period' => 'Makni zadatak u drugi stupac kada ne bude pomaknut odreÄ‘eno vrijeme', + 'Dashboard for %s' => 'Nadzorna ploÄa za %s', + 'Tasks overview for %s' => 'Pregled zadataka za %s', + 'Subtasks overview for %s' => 'Pregled podzadataka za %s', + 'Projects overview for %s' => 'Pregled projekata za %s', + 'Activity stream for %s' => 'Tijek aktivnosti za %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Dodijeli boju kada je zadatak pomaknut u odreÄ‘enu stazu', + 'Assign a priority when the task is moved to a specific swimlane' => 'Dodijeli prioritet kada je zadatak pomaknut u odreÄ‘enu stazu', + 'User unlocked successfully.' => 'Korisnik je uspjeÅ¡no otkljuÄan.', + 'Unable to unlock the user.' => 'Nije moguće otkljuÄati korisnika.', + 'Move a task to another swimlane' => 'Pomakni zadatak u drugu stazu', + 'Creator Name' => 'Ime tvorca', + 'Time spent and estimated' => 'PotroÅ¡eno i procijenjeno vrijeme', + 'Move position' => 'Promjena pozicije', + 'Move task to another position on the board' => 'Pomakni zadatak na drugu poziciju na ploÄi', + 'Insert before this task' => 'Umetni prije ovog zadatka', + 'Insert after this task' => 'Umetni poslije ovog zadatka', + 'Unlock this user' => 'OtkljuÄaj ovog korisnika', + 'Custom Project Roles' => 'PrilagoÄ‘ne uloge projekta', + 'Add a new custom role' => 'Dodaj novu prilagoÄ‘enu ulogu', + 'Restrictions for the role "%s"' => 'OgraniÄenja za ulogu "%s"', + 'Add a new project restriction' => 'Dodaj nova ograniÄenja na projektu', + 'Add a new drag and drop restriction' => 'Dodaj nova ograniÄenja za "povuci i spusti"', + 'Add a new column restriction' => 'Dodaj nova ograniÄenja za stupac', + 'Edit this role' => 'Uredi ovu ulogu', + 'Remove this role' => 'Makni ovu ulogu', + 'There is no restriction for this role.' => 'Nema ograniÄenja za ovu ulogu.', + 'Only moving task between those columns is permitted' => 'Dozvoljeno micanje zadataka samo meÄ‘u ovim stupcima', + 'Close a task in a specific column when not moved during a given period' => 'Zatvori zadatak u odabranom stupcu kada nema micanja odreÄ‘eno vrijeme', + 'Edit columns' => 'Uredi stupce', + 'The column restriction has been created successfully.' => 'OgraniÄenje za stupac je uspjeÅ¡no kreiranao.', + 'Unable to create this column restriction.' => 'Nije moguće kreirati ograniÄenja za stupac.', + 'Column restriction removed successfully.' => 'OgraniÄenje za stupac je uspjeÅ¡no obrisano.', + 'Unable to remove this restriction.' => 'Nije moguće maknuti ovo ograniÄenje.', + 'Your custom project role has been created successfully.' => 'Tvoja prilagoÄ‘ena uloga na projektu je uspjeÅ¡no kreirana.', + 'Unable to create custom project role.' => 'Nije moguće kreirati prilagoÄ‘enu ulogu na projektu.', + 'Your custom project role has been updated successfully.' => 'Tvoja prilagoÄ‘ena uloga na projektu je uspjeÅ¡no dopunjena.', + 'Unable to update custom project role.' => 'Nije moguće dopuniti prilagoÄ‘enu ulogu na projektu.', + 'Custom project role removed successfully.' => 'PrilagoÄ‘ena uloga na projektu je uspjeÅ¡no maknuta.', + 'Unable to remove this project role.' => 'Nije moguće maknuti ovu ulogu na projektu.', + 'The project restriction has been created successfully.' => 'OgraniÄenje na projektu je uspjeÅ¡no kreirano.', + 'Unable to create this project restriction.' => 'Nije moguće kreirati ovo ograniÄenje na projektu.', + 'Project restriction removed successfully.' => 'OgraniÄenje na projektu je uspjeÅ¡no maknuto.', + 'You cannot create tasks in this column.' => 'Ne možete kreirati zadatak u ovom stupcu.', + 'Task creation is permitted for this column' => 'Kreiranje zadataka u ovom stupcu je zabranjeno', + 'Closing or opening a task is permitted for this column' => 'Zatvaranje ili otvaranje zadatka u ovom stupcu je zabranjeno', + 'Task creation is blocked for this column' => 'Kreiranje zadataka je onemogućeno za ovaj stupac', + 'Closing or opening a task is blocked for this column' => 'Zatvaranje ili otvaranje zadataka u ovom stupcu je onemogućeno', + 'Task creation is not permitted' => 'Kreiranje zadatka nije dozvoljeno', + 'Closing or opening a task is not permitted' => 'Zatvarnaje ili otvaranje zadatka nije dozvoljeno', + 'New drag and drop restriction for the role "%s"' => 'Novo "povuci i spusti" ograniÄenje za ulogu "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Osobe sa ovom ulogom će moći samo micati zadatke izmeÄ‘u izvornog i odrediÅ¡nog stupca.', + 'Remove a column restriction' => 'Makni ograniÄenja za stupac', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Doista želite maknuti ovo ograniÄenje za stupac: "%s" u "%s"?', + 'New column restriction for the role "%s"' => 'Novo ograniÄenje za stupac za ulogu "%s"', + 'Rule' => 'Pravilo', + 'Do you really want to remove this column restriction?' => 'Doista želite maknuti ovo ograniÄenje za stupac?', + 'Custom roles' => 'PrilagoÄ‘ene uloge', + 'New custom project role' => 'Nova prilagoÄ‘ena uloga za projekt', + 'Edit custom project role' => 'Uredi prilagoÄ‘enu ulogu za projekt', + 'Remove a custom role' => 'Makni prilagoÄ‘enu ulogu', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Doista želite maknuti ovu prilagoÄ‘enu ulogu: "%s"? Sve osobe kojima je dodijeljena ova uloga će postati Älanovi projekta.', + 'There is no custom role for this project.' => 'Nema prilagoÄ‘enih uloga za ovaj projekt.', + 'New project restriction for the role "%s"' => 'Novo ograniÄenje na projektu za ulogu "%s"', + 'Restriction' => 'OgraniÄenje', + 'Remove a project restriction' => 'Makni ograniÄenje na projektu', + 'Do you really want to remove this project restriction: "%s"?' => 'Doista želite da maknuti ovo ograniÄenje na projektu: "%s"?', + 'Duplicate to multiple projects' => 'Dupliraj u viÅ¡e projekata', + 'This field is required' => 'Ovo polje je obavezno', + 'Moving a task is not permitted' => 'Pomicanje zadatka nije dozvoljeno', + 'This value must be in the range %d to %d' => 'Ova vrijednost mora biti u rasponu od %d do %d', + 'You are not allowed to move this task.' => 'Nemate dozvolu za pomicanje ovog zadatka.', + 'API User Access' => 'Pristup korisnika putem API', + 'Preview' => 'Pregled', + 'Write' => 'UpiÅ¡i', + 'Write your text in Markdown' => 'UpiÅ¡i tekst u Markdown notaciji', + 'No personal API access token registered.' => 'Nije zabilježen niti jedan pristupni token.', + 'Your personal API access token is "%s"' => 'VaÅ¡ osobni API token za pristup je: "%s"', + 'Remove your token' => 'Makni vaÅ¡ token', + 'Generate a new token' => 'Kreiraj novi token', + 'Showing %d-%d of %d' => 'Prikaži %d-%d od %d', + 'Outgoing Emails' => 'Odlazne e-mail poruke', + 'Add or change currency rate' => 'Dodaj ili promijeni teÄaj', + 'Reference currency: %s' => 'Osnovna valuta: %s', + 'Add custom filters' => 'Dodaj prilagoÄ‘ene filtere', + 'Export' => 'Izvezi', + 'Add link label' => 'Dodaj oznaku poveznice', + 'Incompatible Plugins' => 'Nekompatibilni dodaci', + 'Compatibility' => 'Kompatibilnost', + 'Permissions and ownership' => 'Dozvole i vlasniÅ¡tvo', + 'Priorities' => 'Prioriteti', + 'Close this window' => 'Zatvori ovaj prozor', + 'Unable to upload this file.' => 'Nije moguće poslati ovu datoteku.', + 'Import tasks' => 'Uvezi zadatke', + 'Choose a project' => 'Odaberite projekt', + 'Profile' => 'Profil', + 'Application role' => 'Uloga u aplikaciji', + '%d invitations were sent.' => '%d poslano pozivnica.', + '%d invitation was sent.' => '%d pozivnica poslana.', + 'Unable to create this user.' => 'Nije moguće kreirati ovog korisnika.', + 'Kanboard Invitation' => 'Kanboard pozivnica', + 'Visible on dashboard' => 'Vidljivo na upravljaÄkoj ploÄi', + 'Created at:' => 'Kreirano:', + 'Updated at:' => 'Dopunjeno:', + 'There is no custom filter.' => 'Nema prilagoÄ‘enih filtera.', + 'New User' => 'Novi korisnik', + 'Authentication' => 'Autorizacija', + 'If checked, this user will use a third-party system for authentication.' => 'Ako je oznaÄeno, ovaj korisnik će koristiti vanjski sistem za autorizaciju.', + 'The password is necessary only for local users.' => 'Lozinka je obavezna samo za lokalne korisnike.', + 'You have been invited to register on Kanboard.' => 'Pozvani ste da se registrirate u Kanboard.', + 'Click here to join your team' => 'Kliknite ovdje kako bi se prodružili vaÅ¡em timu', + 'Invite people' => 'Pozovi ljude', + 'Emails' => 'E-mail-ovi', + 'Enter one email address by line.' => 'UpiÅ¡i jednu email adresu u redu.', + 'Add these people to this project' => 'Dodaj ove ljude u ovaj projekt', + 'Add this person to this project' => 'Dodaj ovu osobu u ovaj projekt', + 'Sign-up' => 'Registracija', + 'Credentials' => 'Pristupni podaci', + 'New user' => 'Novi korisnik', + 'This username is already taken' => 'Ovo korisniÄko ime je zauzeto', + 'Your profile must have a valid email address.' => 'Profil mora imati ispravnu email adresu.', + 'TRL - Turkish Lira' => 'TRL - Turska lira', + 'The project email is optional and could be used by several plugins.' => 'e-mail projekta je opcija, a mogi je koristiti i dodaci (plugins).', + 'The project email must be unique across all projects' => 'e-mail projekta mora biti jedinstven za svaki projekt', + 'The email configuration has been disabled by the administrator.' => 'UreÄ‘ivanje e-mail postavki je onemogućeno od strane administratora.', + 'Close this project' => 'Zatvori ovaj projekt', + 'Open this project' => 'Otvori ovaj projekt', + 'Close a project' => 'Zatvori projekt', + 'Do you really want to close this project: "%s"?' => 'Doista želite zatvoriti projekt: "%s"?', + 'Reopen a project' => 'Ponovo otvori projekt', + 'Do you really want to reopen this project: "%s"?' => 'Doista želite ponovo otvoriti projekt: "%s"?', + 'This project is open' => 'Ovaj projekt je otvoren', + 'This project is closed' => 'Ovaj projekt je zatvoren', + 'Unable to upload files, check the permissions of your data folder.' => 'Nije moguće poslati datoteke, provjerite prava na server folderima.', + 'Another category with the same name exists in this project' => 'Kategorija sa istim imenom već postoji na ovom projektu', + 'Comment sent by email successfully.' => 'Komentar je uspjeÅ¡no poslan e-mail-om.', + 'Sent by email to "%s" (%s)' => 'Poslano na e-mail "%s" (%s)', + 'Unable to read uploaded file.' => 'Nije moguće proÄitati poslanu datoteku.', + 'Database uploaded successfully.' => 'Baza podataka je uspjeÅ¡no poslana.', + 'Task sent by email successfully.' => 'Zadatak je uspjeÅ¡no poslan e-mail-om.', + 'There is no category in this project.' => 'Nema kategorija u ovom projektu.', + 'Send by email' => 'PoÅ¡alji e-mail-om', + 'Create and send a comment by email' => 'Napravi i poÅ¡alji komentar e-mail-om', + 'Subject' => 'Predmet', + 'Upload the database' => 'PoÅ¡alji bazu podataka', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Možete poslati prethodno preuzetu Sqlite bazu podataka (Gzip format).', + 'Database file' => 'Datoteka baze podataka', + 'Upload' => 'PoÅ¡alji', + 'Your project must have at least one active swimlane.' => 'Projekt mora imati barem jednu aktivnu stazu.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatska radnja nije pronaÄ‘ena: "%s"', + '%d projects' => '%d projekta', + '%d project' => '%d projekt', + 'There is no project.' => 'Nema projekata.', + 'Sort' => 'Sortiraj', + 'Project ID' => 'RB projekta', + 'Project name' => 'Naziv projekta', + 'Public' => 'Javno', + 'Personal' => 'Osobno', + '%d tasks' => '%d zadataka', + '%d task' => '%d zadatak', + 'Task ID' => 'RB zadatka', + 'Assign automatically a color when due date is expired' => 'Automatski postavi boju kada proÅ¡ao rok zavrÅ¡etka', + 'Total score in this column across all swimlanes' => 'Ukupni rezultat u ovom stupcu za sve staze', + 'HRK - Kuna' => 'HRK - Hrvatska Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentiski pezos', + 'COP - Colombian Peso' => 'COP - Kolumbijski pezos', + '%d groups' => '%d grupe', + '%d group' => '%d grupa', + 'Group ID' => 'RB grupe', + 'External ID' => 'Vanjski RB', + '%d users' => '%d korisnika', + '%d user' => '%d korisnik', + 'Hide subtasks' => 'Sakrij podzadatke', + 'Show subtasks' => 'Prikaži podzadatke', + 'Authentication Parameters' => 'Parametri za autorizaciju', + 'API Access' => 'API pristup', + 'No users found.' => 'Nema ponaÄ‘enih korisnika.', + 'User ID' => 'RB korisnika', + 'Notifications are activated' => 'Obavijesti su omogućene', + 'Notifications are disabled' => 'Obavijesti su onemogućene', + 'User disabled' => 'Korisnik je onemogućen', + '%d notifications' => '%d obavjesti', + '%d notification' => '%d obavijest', + 'There is no external integration installed.' => 'Nema usluga vanjskih servisa.', + 'You are not allowed to update tasks assigned to someone else.' => 'Nemate dozvolu mijenjati zadatke dodijeljene nekom drugom.', + 'You are not allowed to change the assignee.' => 'Nemate dozvolu mijenjati izvrÅ¡itelja.', + 'Task suppression is not permitted' => 'Suzbijanje zadataka nije dozvoljeno', + 'Changing assignee is not permitted' => 'Promjena izvrÅ¡itelja nije dozvoljena', + 'Update only assigned tasks is permitted' => 'Dozvoljena je promjena samo dodijeljenih zadataka', + 'Only for tasks assigned to the current user' => 'Samo za zadatke dodijeljene trenutnom korisniku', + 'My projects' => 'Moji projekti', + 'You are not a member of any project.' => 'Niste Älan niti jednog projekta.', + 'My subtasks' => 'Moji podzadaci', + '%d subtasks' => '%d podzadataka', + '%d subtask' => '%d podzadatak', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'PremjeÅ¡tanje izmeÄ‘u tih stupaca je dozvoljeno samo za zadatke dodijeljene trenutnom korisniku', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Danska kruna', + 'Remove user from group' => 'Makni korisnika iz grupe', + 'Assign the task to its creator' => 'Dodijeli zadatak njegovom tvorcu', + 'This task was sent by email to "%s" with subject "%s".' => 'Ovaj zadatak je poslan na email "%s" sa predmetom "%s".', + 'Predefined Email Subjects' => 'Unaprijed definirani e-mail predmeti', + 'Write one subject by line.' => 'Unesite jedan predmet po liniji', + 'Create another link' => 'Napravi joÅ¡ jednu vezu', + 'BRL - Brazilian Real' => 'BRL - Brazilski real', + 'Add a new Kanboard task' => 'Dodajte novi Kanboard zadatak', + 'Subtask not started' => 'Podzadatak nije zapoÄeo', + 'Subtask currently in progress' => 'Podzadatak je trenutno u tijeku', + 'Subtask completed' => 'Podzadatak je zavrÅ¡en', + 'Subtask added successfully.' => 'Podzadatak je uspjeÅ¡no dodan.', + '%d subtasks added successfully.' => '%d podzadataka je uspjeÅ¡no dodano.', + 'Enter one subtask by line.' => 'Unesite jedan podzadatak u redu.', + 'Predefined Contents' => 'Unaprijed definirani sadržaj', + 'Predefined contents' => 'Unaprijed definirani sadržaj', + 'Predefined Task Description' => 'Predefinirani opis zadatka', + 'Do you really want to remove this template? "%s"' => 'Doista želite maknuti ovaj predložak? "%s"', + 'Add predefined task description' => 'Dodan predefinirani opis zadatka', + 'Predefined Task Descriptions' => 'Predefinirani opis zadatka', + 'Template created successfully.' => 'Predložak uspjeÅ¡no kreiran', + 'Unable to create this template.' => 'Nije moguće kreirati ovaj predložak', + 'Template updated successfully.' => 'Predložak uspjeÅ¡no dopunjen', + 'Unable to update this template.' => 'Nije moguće dopuniti ovaj predložak', + 'Template removed successfully.' => 'Predložak uspjeÅ¡no maknut', + 'Unable to remove this template.' => 'Nije moguće maknuti ovaj predložak', + 'Template for the task description' => 'Predložak za opis zadatka', + 'The start date is greater than the end date' => 'PoÄetni datum je veći od kranjeg datuma', + 'Tags must be separated by a comma' => 'Oznake moraju biti razdvojene zarezom', + 'Only the task title is required' => 'Samo naslov zadatka je obavezan', + 'Creator Username' => 'KorisniÄko ime tvorca', + 'Color Name' => 'Boja stupca', + 'Column Name' => 'Naziv stupca', + 'Swimlane Name' => 'Naziv staze', + 'Time Estimated' => 'Procijenjeno vrijeme', + 'Time Spent' => 'PotroÅ¡eno vrijeme', + 'External Link' => 'Vanjska veza', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Ova funkcionalnost omogućava iCal kanal, RSS kanal i javni prikaz ploÄe.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Zaustavi timer na svim podzadacima prilikom premjeÅ¡tanja zadatka u drugi stupac', + 'Subtask Title' => 'Naslov podzadatka', + 'Add a subtask and activate the timer when moving a task to another column' => 'Dodaj podzadatak i aktiviraj timer prilikom micanja zadatka u drugi stupac', + 'days' => 'dana', + 'minutes' => 'minuta', + 'seconds' => 'sekundi', + 'Assign automatically a color when preset start date is reached' => 'Automatski postavi boju kada se dosegne unaprijed zadani datum poÄetka', + 'Move the task to another column once a predefined start date is reached' => 'Premjesti zadatak u drugi stupac kada se dosegne unaprijed zadani datum poÄetka', + 'This task is now linked to the task %s with the relation "%s"' => 'Zadatak je sada povezan sa zadatkom %s relacijom "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Veza sa relacijom "%s" na zadatak %s je maknuta', + 'Custom Filter:' => 'PrilagoÄ‘eni filter', + 'Unable to find this group.' => 'Nije moguće pronaći ovu grupu.', + '%s moved the task #%d to the column "%s"' => '%s je pomaknuo/la zadatak #%d u stupac "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s je pmaknuo/la zadatak #%d na poziciju %d u stupcu "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s je pomaknuo/la zadatak #%d u stazu "%s"', + '%sh spent' => '%ss potroÅ¡eno', + '%sh estimated' => '%ss procijenjeno', + 'Select All' => 'OznaÄi sve', + 'Unselect All' => 'PoniÅ¡ti odabir svega', + 'Apply action' => 'Primjeni radnju', + 'Move selected tasks to another column or swimlane' => 'Pomakni odabrani zadatak u neki drugi stupac', + 'Edit tasks in bulk' => 'Masovno ureÄ‘ivanje zadataka', + 'Choose the properties that you would like to change for the selected tasks.' => 'Izaberite svojstva koja želite promijeniti na izabranim zadacima.', + 'Configure this project' => 'Uredite ovaj projekt', + 'Start now' => 'ZapoÄni sada', + '%s removed a file from the task #%d' => '%s je maknuo/la dataoteku sa zadatka #%d', + 'Attachment removed from task #%d: %s' => 'Privitak je maknut sa zadatka #%d: %s', + 'No color' => 'Bez boje', + 'Attachment removed "%s"' => 'Privitak maknut "%s"', + '%s removed a file from the task %s' => '%s je maknuo/la datoteku sa zadatka %s', + 'Move the task to another swimlane when assigned to a user' => 'Premjesti zadatak u drugu stazu kada je zadatak dodijeljen korisniku', + 'Destination swimlane' => 'OdrediÅ¡na staza', + 'Assign a category when the task is moved to a specific swimlane' => 'Postavi kategoriju kada je zadatak premjeÅ¡ten u odreÄ‘enu stazu', + 'Move the task to another swimlane when the category is changed' => 'Pomakni zadatak u drugu stazu kada se promijeni kategorija', + 'Reorder this column by priority (ASC)' => 'Presloži ovaj stupac po prioritetu (UZLAZNO)', + 'Reorder this column by priority (DESC)' => 'Presloži ovaj stupac po prioritetu (SILAZNO)', + 'Reorder this column by assignee and priority (ASC)' => 'Presloži ovaj stupac po izvrÅ¡itelju (UZLAZNO)', + 'Reorder this column by assignee and priority (DESC)' => 'Presloži ovaj stupac po izvrÅ¡itelju (SILAZNO)', + 'Reorder this column by assignee (A-Z)' => 'Presloži ovaj stupac po izvrÅ¡itelju (A-Ž)', + 'Reorder this column by assignee (Z-A)' => 'Presloži ovaj stupac po izvrÅ¡itelju (Ž-A)', + 'Reorder this column by due date (ASC)' => 'Presloži ovaj stupac po roku zavrÅ¡etka (UZLAZNO)', + 'Reorder this column by due date (DESC)' => 'Presloži ovaj stupac po roku zavrÅ¡etka (SILAZNO)', + 'Reorder this column by id (ASC)' => 'Poredaj stupac po id-u (ASC)', + 'Reorder this column by id (DESC)' => 'Poredaj stupac po id-u (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s je premjestio/la zadatak #%d "%s" u projekt "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Zadatak #%d "%s" je premjeÅ¡ten u projekt "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Pomakni zadatak u drugi stupac kada je datum roka zavrÅ¡etka kraći od odreÄ‘enog broja dana', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automatski dopuni datum poÄetka kada se zadatak pomakne iz odreÄ‘enog stupca', + 'HTTP Client:' => 'HTTP klijent', + 'Assigned' => 'Dodijeljeno', + 'Task limits apply to each swimlane individually' => 'OgraniÄenje broja zadataka vrijedi za svaku stazu zasebno', + 'Column task limits apply to each swimlane individually' => 'OgraniÄenje broja zadataka u stupcu je za svaku stazu posebno', + 'Column task limits are applied to each swimlane individually' => 'OgraniÄenje broja zadataka u stupcu se primjenjuje za svaku stazu posebno', + 'Column task limits are applied across swimlanes' => 'OgraniÄenje broja zadataka u stupcu je po svim stazama', + 'Task limit: ' => 'OgraniÄenje broja zadataka: ', + 'Change to global tag' => 'Promijeni u globalnu oznaku', + 'Do you really want to make the tag "%s" global?' => 'Doista želite da oznaka "%s" postane globalna?', + 'Enable global tags for this project' => 'Omogući globalne oznake za ovaj projekt', + 'Group membership(s):' => 'ÄŒlanstvo u grupama:', + '%s is a member of the following group(s): %s' => '%s je Älan grupa: %s', + '%d/%d group(s) shown' => '%d/%d prikazane grupe', + 'Subtask creation or modification' => 'Kreiranje ili promjena podzadatka', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Dodijeli zadatak odreÄ‘enom korisniku kada je zadatak premjeÅ¡ten u odreÄ‘enu stazu', + 'Comment' => 'Komentar', + 'Collapse vertically' => 'Skupi vertikalno', + 'Expand vertically' => 'ProÅ¡iri vertikalno', + 'MXN - Mexican Peso' => 'MXN - MeksiÄki pezos', + 'Estimated vs actual time per column' => 'Procijenjeno u odnosu na stvarno vrijeme po stupcu', + 'HUF - Hungarian Forint' => 'HUD - MaÄ‘arska forinta', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Morate odabrati datoteku za prijenos kao svoj avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Datoteka koju ste prenijeli nije važeća slika! (DopuÅ¡teni su samo *.gif, *.jpg, *.jpeg i *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Automatski postavite datum dospijeća kada se zadatak premjesti iz odreÄ‘enog stupca', + 'No other projects found.' => 'Nema pronaÄ‘enih drugih projekata.', + 'Tasks copied successfully.' => 'Zadaci uspjeÅ¡no kopirani.', + 'Unable to copy tasks.' => 'Nije moguće kopirati zadatke.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Svijetla tema', + 'Dark theme' => 'Tamna tema', + 'Automatic theme - Sync with system' => 'Automatska tema - Sinkronizacija sa sustavom', + 'Application managers or more' => 'Voditelji aplikacija ili viÅ¡e', + 'Administrators' => 'Administratori', + 'Visibility:' => 'Vidljivost:', + 'Standard users' => 'Standardni korisnici', + 'Visibility is required' => 'Vidljivost je obavezna', + 'The visibility should be an app role' => 'Vidljivost bi trebala biti uloga aplikacije', + 'Reply' => 'Odgovori', + '%s wrote: ' => '%s napisao: ', + 'Number of visible tasks in this column and swimlane' => 'Broj vidljivih zadataka u ovom stupcu i plivaÄkoj stazi', + 'Number of tasks in this swimlane' => 'Broj zadataka u ovoj plivaÄkoj stazi', + 'Unable to find another subtask in progress, you can close this window.' => 'Nije moguće pronaći drugi podzadatak u tijeku, možete zatvoriti ovaj prozor.', + 'This theme is invalid' => 'Ova tema je nevažeća', + 'This role is invalid' => 'Ova uloga je nevažeća', + 'This timezone is invalid' => 'Ova vremenska zona je nevažeća', + 'This language is invalid' => 'Ovaj jezik je nevažeći', + 'This URL is invalid' => 'Ovaj URL je nevažeći', + 'Date format invalid' => 'Format datuma nevažeći', + 'Time format invalid' => 'Format vremena nevažeći', + 'Invalid Mail transport' => 'Nevažeći transport poÅ¡te', + 'Color invalid' => 'Boja nevažeća', + 'This value must be greater or equal to %d' => 'Ova vrijednost mora biti veća ili jednaka %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Dodajte BOM na poÄetak datoteke (obavezno za Microsoft Excel)', + 'Just add these tag(s)' => 'Samo dodajte ove oznake', + 'Remove internal link(s)' => 'Uklonite unutarnje poveznice', + 'Import tasks from another project' => 'Uvezite zadatke iz drugog projekta', + 'Select the project to copy tasks from' => 'Odaberite projekt iz kojeg želite kopirati zadatke', + 'The total maximum allowed attachments size is %sB.' => 'Ukupna maksimalna dopuÅ¡tena veliÄina privitaka je %sB.', + 'Add attachments' => 'Dodaj privitke', + 'Task #%d "%s" is overdue' => 'Zadatak #%d "%s" je istekao', + 'Enable notifications by default for all new users' => 'Omogući obavijesti prema zadanim postavkama za sve nove korisnike', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Dodijeli zadatak njegovom kreatoru za odreÄ‘ene stupce ako izvrÅ¡itelj nije ruÄno postavljen', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Dodijeli zadatak prijavljenom korisniku pri promjeni stupca na zadani stupac ako nitko nije dodijeljen', +]; diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php new file mode 100644 index 0000000..7820426 --- /dev/null +++ b/app/Locale/hu_HU/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Nincs', + 'Edit' => 'Szerkesztés', + 'Remove' => 'Eltávolítás', + 'Yes' => 'Igen', + 'No' => 'Nem', + 'cancel' => 'mégse', + 'or' => 'vagy', + 'Yellow' => 'Sárga', + 'Blue' => 'Kék', + 'Green' => 'Zöld', + 'Purple' => 'Lila', + 'Red' => 'Piros', + 'Orange' => 'Narancssárga', + 'Grey' => 'Szürke', + 'Brown' => 'Barna', + 'Deep Orange' => 'Sötét narancs', + 'Dark Grey' => 'Sötét szürke', + 'Pink' => 'Rózsaszín', + 'Teal' => 'Kékeszöld', + 'Cyan' => 'Ciánkék', + 'Lime' => 'Citrus', + 'Light Green' => 'Világos zöld', + 'Amber' => 'Borostyán', + 'Save' => 'Mentés', + 'Login' => 'Bejelentkezés', + 'Official website:' => 'Hivatalos weboldal:', + 'Unassigned' => 'Nincs felelÅ‘s', + 'View this task' => 'Feladat megtekintése', + 'Remove user' => 'Felhasználó eltávolítása', + 'Do you really want to remove this user: "%s"?' => 'Valóban el szeretné távolítani ezt a felhasználót: „%sâ€?', + 'All users' => 'Összes felhasználó', + 'Username' => 'Felhasználónév', + 'Password' => 'Jelszó', + 'Administrator' => 'Adminisztrátor', + 'Sign in' => 'Bejelentkezés', + 'Users' => 'Felhasználók', + 'Forbidden' => 'Tiltott', + 'Access Forbidden' => 'Hozzáférés megtagadva', + 'Edit user' => 'Felhasználó szerkesztése', + 'Logout' => 'Kilépés', + 'Bad username or password' => 'Hibás felhasználónév vagy jelszó', + 'Edit project' => 'Projekt szerkesztése', + 'Name' => 'Név', + 'Projects' => 'Projektek', + 'No project' => 'Nincs projekt', + 'Project' => 'Projekt', + 'Status' => 'Ãllapot', + 'Tasks' => 'Feladatok', + 'Board' => 'Tábla', + 'Actions' => 'Műveletek', + 'Inactive' => 'Inaktív', + 'Active' => 'Aktív', + 'Unable to update this board.' => 'Nem lehet frissíteni ezt a táblát.', + 'Disable' => 'Letiltás', + 'Enable' => 'Engedélyezés', + 'New project' => 'Új projekt', + 'Do you really want to remove this project: "%s"?' => 'Valóban el szeretné távolítani ezt a projektet: „%sâ€?', + 'Remove project' => 'Projekt eltávolítása', + 'Edit the board for "%s"' => 'Tábla szerkesztése: „%sâ€', + 'Add a new column' => 'Új oszlop hozzáadása', + 'Title' => 'Cím', + 'Assigned to %s' => 'FelelÅ‘s: %s', + 'Remove a column' => 'Oszlop eltávolítása', + 'Unable to remove this column.' => 'Nem lehet eltávolítani ezt az oszlopot.', + 'Do you really want to remove this column: "%s"?' => 'Valóban el szeretné távolítani ezt az oszlopot: „%sâ€?', + 'Settings' => 'Beállítások', + 'Application settings' => 'Alkalmazás beállításai', + 'Language' => 'Nyelv', + 'Webhook token:' => 'Webhurok token:', + 'API token:' => 'API token:', + 'Database size:' => 'Adatbázis mérete:', + 'Download the database' => 'Adatbázis letöltése', + 'Optimize the database' => 'Adatbázis optimalizálása', + '(VACUUM command)' => '(VACUUM parancs)', + '(Gzip compressed Sqlite file)' => '(Gzip-pel tömörített SQLite fájl)', + 'Close a task' => 'Feladat lezárása', + 'Column' => 'Oszlop', + 'Color' => 'Szín', + 'Assignee' => 'FelelÅ‘s', + 'Create another task' => 'Másik feladat létrehozása', + 'New task' => 'Új feladat', + 'Open a task' => 'Feladat megnyitása', + 'Do you really want to open this task: "%s"?' => 'Valóban meg szeretné nyitni ezt a feladatot: „%sâ€?', + 'Back to the board' => 'Vissza a táblához', + 'There is nobody assigned' => 'Nincs senki se hozzárendelve', + 'Column on the board:' => 'Oszlop a táblán:', + 'Close this task' => 'Feladat lezárása', + 'Open this task' => 'Feladat megnyitása', + 'There is no description.' => 'Nincs leírás.', + 'Add a new task' => 'Új feladat hozzáadása', + 'The username is required' => 'A felhasználónév kötelezÅ‘', + 'The maximum length is %d characters' => 'A legnagyobb hossz %d karakter', + 'The minimum length is %d characters' => 'A legkisebb hossz %d karakter', + 'The password is required' => 'A jelszó kötelezÅ‘', + 'This value must be an integer' => 'Ez az érték csak egész szám lehet', + 'The username must be unique' => 'A felhasználónévnek egyedinek kell lennie', + 'The user id is required' => 'A felhasználó-azonosító kötelezÅ‘', + 'Passwords don\'t match' => 'A jelszavak nem egyeznek', + 'The confirmation is required' => 'A megerÅ‘sítés kötelezÅ‘', + 'The project is required' => 'A projekt kötelezÅ‘', + 'The id is required' => 'Az azonosító kötelezÅ‘', + 'The project id is required' => 'A projekt-azonosító kötelezÅ‘', + 'The project name is required' => 'A projektnév kötelezÅ‘', + 'The title is required' => 'A cím kötelezÅ‘', + 'Settings saved successfully.' => 'A beállítások sikeresen mentve.', + 'Unable to save your settings.' => 'Nem lehet elmenteni a beállításokat.', + 'Database optimization done.' => 'Az adatbázis optimalizálása kész.', + 'Your project has been created successfully.' => 'A projekt sikeresen létrehozva.', + 'Unable to create your project.' => 'Nem lehet létrehozni a projektet.', + 'Project updated successfully.' => 'A projekt sikeresen frissítve.', + 'Unable to update this project.' => 'Nem lehet frissíteni ezt a projektet.', + 'Unable to remove this project.' => 'Nem lehet eltávolítani ezt a projektet.', + 'Project removed successfully.' => 'A projekt sikeresen eltávolítva.', + 'Project activated successfully.' => 'A projekt sikeresen aktiválva.', + 'Unable to activate this project.' => 'Nem lehet aktiválni ezt a projektet.', + 'Project disabled successfully.' => 'A projekt sikeresen letiltva.', + 'Unable to disable this project.' => 'Nem lehet letiltani ezt a projektet.', + 'Unable to open this task.' => 'Nem lehet megnyitni ezt a feladatot.', + 'Task opened successfully.' => 'A feladat sikeresen megnyitva.', + 'Unable to close this task.' => 'Nem lehet lezárni ezt a feladatot.', + 'Task closed successfully.' => 'A feladat sikeresen lezárva.', + 'Unable to update your task.' => 'Nem lehet frissíteni a feladatot.', + 'Task updated successfully.' => 'A feladat sikeresen frissítve.', + 'Unable to create your task.' => 'Nem lehet létrehozni a feladatot.', + 'Task created successfully.' => 'A feladat sikeresen létrehozva.', + 'User created successfully.' => 'A felhasználó sikeresen létrehozva.', + 'Unable to create your user.' => 'Nem lehet létrehozni a felhasználót.', + 'User updated successfully.' => 'A felhasználó sikeresen frissítve.', + 'User removed successfully.' => 'A felhasználó sikeresen eltávolítva.', + 'Unable to remove this user.' => 'Nem lehet eltávolítani ezt a felhasználót.', + 'Board updated successfully.' => 'A tábla sikeresen frissítve.', + 'Ready' => 'Felkészülés', + 'Backlog' => 'ElintézendÅ‘', + 'Work in progress' => 'Folyamatban', + 'Done' => 'Kész', + 'Application version:' => 'Alkalmazás verziója:', + 'Id' => 'Azonosító', + 'Public link' => 'Nyilvános hivatkozás', + 'Timezone' => 'IdÅ‘zóna', + 'Sorry, I didn\'t find this information in my database!' => 'Elnézést, ez az információ nem található az adatbázisban!', + 'Page not found' => 'Az oldal nem található', + 'Complexity' => 'Bonyolultság', + 'Task limit' => 'Feladatkorlát', + 'Task count' => 'Feladatok száma', + 'User' => 'Felhasználó', + 'Comments' => 'Hozzászólások', + 'Comment is required' => 'A hozzászólás kötelezÅ‘', + 'Comment added successfully.' => 'A hozzászólás sikeresen hozzáadva.', + 'Unable to create your comment.' => 'Nem lehet létrehozni a hozzászólást.', + 'Due Date' => 'HatáridÅ‘', + 'Invalid date' => 'Érvénytelen dátum', + 'Automatic actions' => 'Automatikus műveletek', + 'Your automatic action has been created successfully.' => 'Az automatikus művelet sikeresen létrehozva.', + 'Unable to create your automatic action.' => 'Nem lehet létrehozni az automatikus műveletet.', + 'Remove an action' => 'Művelet eltávolítása', + 'Unable to remove this action.' => 'Nem lehet eltávolítani ezt a műveletet.', + 'Action removed successfully.' => 'A művelet sikeresen eltávolítva.', + 'Automatic actions for the project "%s"' => 'Automatikus műveletek a projekthez: „%sâ€', + 'Add an action' => 'Művelet hozzáadása', + 'Event name' => 'Esemény neve', + 'Action' => 'Művelet', + 'Event' => 'Esemény', + 'When the selected event occurs execute the corresponding action.' => 'Ha a kiválasztott esemény bekövetkezik, hajtsa végre a megfelelÅ‘ műveletet.', + 'Next step' => 'KövetkezÅ‘ lépés', + 'Define action parameters' => 'Művelet paramétereinek meghatározása', + 'Do you really want to remove this action: "%s"?' => 'Valóban el szeretné távolítani ezt a műveletet: „%sâ€?', + 'Remove an automatic action' => 'Automatikus művelet eltávolítása', + 'Assign the task to a specific user' => 'Feladat kiosztása egy adott felhasználónak', + 'Assign the task to the person who does the action' => 'Feladat kiosztása a műveletet elvégzÅ‘ személynek', + 'Duplicate the task to another project' => 'Feladat másolása egy másik projektbe', + 'Move a task to another column' => 'Feladat áthelyezése egy másik oszlopba', + 'Task modification' => 'Feladat módosítása', + 'Task creation' => 'Feladat létrehozása', + 'Closing a task' => 'Feladat lezárása', + 'Assign a color to a specific user' => 'Szín hozzárendelése egy adott felhasználóhoz', + 'Position' => 'Pozíció', + 'Duplicate to project' => 'Másolás egy projektbe', + 'Duplicate' => 'Másolás', + 'Link' => 'Hivatkozás', + 'Comment updated successfully.' => 'A megjegyzés sikeresen frissítve.', + 'Unable to update your comment.' => 'Nem lehet frissíteni a megjegyzést.', + 'Remove a comment' => 'Megjegyzés eltávolítása', + 'Comment removed successfully.' => 'A megjegyzés sikeresen eltávolítva.', + 'Unable to remove this comment.' => 'Nem lehet eltávolítani a megjegyzést.', + 'Do you really want to remove this comment?' => 'Valóban el szeretné távolítani ezt a megjegyzést?', + 'Current password for the user "%s"' => '„%s†felhasználó jelenlegi jelszava', + 'The current password is required' => 'A jelenlegi jelszó kötelezÅ‘', + 'Wrong password' => 'Hibás jelszó', + 'Unknown' => 'Ismeretlen', + 'Last logins' => 'Legutóbbi bejelentkezések', + 'Login date' => 'Bejelentkezés dátuma', + 'Authentication method' => 'Hitelesítési módszer', + 'IP address' => 'IP-cím', + 'User agent' => 'Felhasználói ügynök', + 'Persistent connections' => 'Tartós kapcsolatok', + 'No session.' => 'Nincs munkamenet.', + 'Expiration date' => 'Lejárat dátuma', + 'Remember Me' => 'Emlékezzen rám', + 'Creation date' => 'Létrehozás dátuma', + 'Everybody' => 'Mindenki', + 'Open' => 'Nyitott', + 'Closed' => 'Lezárt', + 'Search' => 'Keresés', + 'Nothing found.' => 'Nincs találat.', + 'Due date' => 'HatáridÅ‘', + 'Description' => 'Leírás', + '%d comments' => '%d megjegyzés', + '%d comment' => '%d megjegyzés', + 'Email address invalid' => 'Érvénytelen e-mail-cím', + 'Your external account is not linked anymore to your profile.' => 'A külsÅ‘ fiókja többé nincs hozzákapcsolva a profiljához.', + 'Unable to unlink your external account.' => 'Nem lehet megszüntetni a kapcsolatot a külsÅ‘ fiókjával.', + 'External authentication failed' => 'A külsÅ‘ hitelesítés sikertelen', + 'Your external account is linked to your profile successfully.' => 'A külsÅ‘ fiókja sikeresen össze lett kapcsolva a profiljával.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'A feladat sikeresen eltávolítva.', + 'Unable to remove this task.' => 'Nem lehet eltávolítani a feladatot.', + 'Remove a task' => 'Feladat eltávolítása', + 'Do you really want to remove this task: "%s"?' => 'Valóban el szeretné távolítani ezt a feladatot: „%sâ€?', + 'Assign automatically a color based on a category' => 'Szín automatikus hozzárendelése egy kategória alapján', + 'Assign automatically a category based on a color' => 'Kategória automatikus hozzárendelése egy szín alapján', + 'Task creation or modification' => 'Feladat létrehozása vagy módosítása', + 'Category' => 'Kategória', + 'Category:' => 'Kategória:', + 'Categories' => 'Kategóriák', + 'Your category has been created successfully.' => 'A kategória sikeresen létrehozva.', + 'This category has been updated successfully.' => 'A kategória sikeresen frissítve.', + 'Unable to update this category.' => 'Nem lehet frissíteni ezt a kategóriát.', + 'Remove a category' => 'Kategória eltávolítása', + 'Category removed successfully.' => 'A kategória sikeresen eltávolítva.', + 'Unable to remove this category.' => 'Nem lehet eltávolítani ezt a kategóriát.', + 'Category modification for the project "%s"' => 'Kategória módosítása a projektnél: „%sâ€', + 'Category Name' => 'Kategória neve', + 'Add a new category' => 'Új kategória hozzáadása', + 'Do you really want to remove this category: "%s"?' => 'Valóban el szeretné távolítani ezt a kategóriát: „%sâ€?', + 'All categories' => 'Összes kategória', + 'No category' => 'Nincs kategória', + 'The name is required' => 'A név kötelezÅ‘', + 'Remove a file' => 'Fájl eltávolítása', + 'Unable to remove this file.' => 'Nem lehet eltávolítani ezt a fájlt.', + 'File removed successfully.' => 'A fájl sikeresen eltávolítva.', + 'Attach a document' => 'Dokumentum csatolása', + 'Do you really want to remove this file: "%s"?' => 'Valóban el szeretné távolítani ezt a fájlt: „%sâ€?', + 'Attachments' => 'Mellékletek', + 'Edit the task' => 'Feladat szerkesztése', + 'Add a comment' => 'Megjegyzés hozzáadása', + 'Edit a comment' => 'Megjegyzés szerkesztése', + 'Summary' => 'Összegzés', + 'Time tracking' => 'IdÅ‘ követés', + 'Estimate:' => 'Becsült:', + 'Spent:' => 'Eltöltött:', + 'Do you really want to remove this sub-task?' => 'Valóban el szeretné távolítani ezt a részfeladatot?', + 'Remaining:' => 'HátralévÅ‘:', + 'hours' => 'óra', + 'estimated' => 'becsült', + 'Sub-Tasks' => 'Részfeladatok', + 'Add a sub-task' => 'Részfeladat hozzáadása', + 'Original estimate' => 'Eredeti idÅ‘becslés', + 'Create another sub-task' => 'További részfeladat létrehozása', + 'Time spent' => 'Eltöltött idÅ‘', + 'Edit a sub-task' => 'Részfeladat szerkesztése', + 'Remove a sub-task' => 'Részfeladat eltávolítása', + 'The time must be a numeric value' => 'Az idÅ‘ csak számérték lehet', + 'Todo' => 'TeendÅ‘', + 'In progress' => 'Folyamatban', + 'Sub-task removed successfully.' => 'A részfeladat sikeresen eltávolítva.', + 'Unable to remove this sub-task.' => 'Nem lehet eltávolítani a részfeladatot.', + 'Sub-task updated successfully.' => 'A részfeladat sikeresen frissítve.', + 'Unable to update your sub-task.' => 'Nem lehet frissíteni a részfeladatot.', + 'Unable to create your sub-task.' => 'Nem lehet létrehozni a részfeladatot.', + 'Maximum size: ' => 'Legnagyobb méret: ', + 'Display another project' => 'Másik projekt megjelenítése', + 'Created by %s' => 'Létrehozta: %s', + 'Tasks Export' => 'Feladatok exportálása', + 'Start Date' => 'Kezdési dátum', + 'Execute' => 'Végrehajtás', + 'Task Id' => 'Feladatazonosító', + 'Creator' => 'Létrehozó', + 'Modification date' => 'Módosítás dátuma', + 'Completion date' => 'Befejezés dátuma', + 'Clone' => 'Klónozás', + 'Project cloned successfully.' => 'A projekt sikeresen lemásolva.', + 'Unable to clone this project.' => 'Nem lehet lemásolni a projektet.', + 'Enable email notifications' => 'E-mail értesítések engedélyezése', + 'Task position:' => 'Feladat helye:', + 'The task #%d has been opened.' => '#%d. feladat megnyitva.', + 'The task #%d has been closed.' => '#%d. feladat lezárva.', + 'Sub-task updated' => 'Részfeladat frissítve', + 'Title:' => 'Cím', + 'Status:' => 'Ãllapot:', + 'Assignee:' => 'FelelÅ‘s:', + 'Time tracking:' => 'IdÅ‘ követés:', + 'New sub-task' => 'Új részfeladat', + 'New attachment added "%s"' => 'Új melléklet hozzáadva: „%sâ€.', + 'New comment posted by %s' => 'Új megjegyzést adott hozzá: %s', + 'New comment' => 'Új megjegyzés', + 'Comment updated' => 'Megjegyzés frissítve', + 'New subtask' => 'Új részfeladat', + 'I only want to receive notifications for these projects:' => 'Csak ezekrÅ‘l a projektekrÅ‘l szeretnék értesítést kapni:', + 'view the task on Kanboard' => 'feladat megtekintése a Kanboardon', + 'Public access' => 'Nyilvános hozzáférés', + 'Disable public access' => 'Nyilvános hozzáférés letiltása', + 'Enable public access' => 'Nyilvános hozzáférés engedélyezése', + 'Public access disabled' => 'Nyilvános hozzáférés letiltva', + 'Move the task to another project' => 'Feladat áthelyezése másik projektbe', + 'Move to project' => 'Ãthelyezés egy projektbe', + 'Do you really want to duplicate this task?' => 'Valóban le szeretné másolni ezt a feladatot?', + 'Duplicate a task' => 'Feladat másolása', + 'External accounts' => 'KülsÅ‘ fiókok', + 'Account type' => 'Fiók típusa', + 'Local' => 'Helyi', + 'Remote' => 'Távoli', + 'Enabled' => 'Engedélyezve', + 'Disabled' => 'Letiltva', + 'Login:' => 'Felhasználónév:', + 'Full Name:' => 'Teljes név:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Értesítések:', + 'Notifications' => 'Értesítések', + 'Account type:' => 'Fiók típusa:', + 'Edit profile' => 'Profil szerkesztése', + 'Change password' => 'Jelszó megváltoztatása', + 'Password modification' => 'Jelszó módosítása', + 'External authentications' => 'KülsÅ‘ hitelesítések', + 'Never connected.' => 'Sosem csatlakoztatott.', + 'No external authentication enabled.' => 'Nincs külsÅ‘ hitelesítés engedélyezve.', + 'Password modified successfully.' => 'A jelszó sikeresen módosítva.', + 'Unable to change the password.' => 'Nem lehet megváltoztatni a jelszót.', + 'Change category' => 'Kategória megváltoztatása', + '%s updated the task %s' => '%s frissítette a(z) %s feladatot', + '%s opened the task %s' => '%s megnyitott a(z) %s feladatot', + '%s moved the task %s to the position #%d in the column "%s"' => '%s áthelyezte a(z) %s feladatot a(z) #%d. pozícióba a(z) „%s†oszlopban', + '%s moved the task %s to the column "%s"' => '%s áthelyezte a(z) %s feladatot a(z) „%s†oszlopba', + '%s created the task %s' => '%s létrehozta a(z) %s feladatot', + '%s closed the task %s' => '%s lezárta a(z) %s feladatot', + '%s created a subtask for the task %s' => '%s létrehozott egy részfeladatot a(z) %s feladathoz', + '%s updated a subtask for the task %s' => '%s frissített egy részfeladatot a(z) %s feladatnál', + 'Assigned to %s with an estimate of %s/%sh' => 'Hozzárendelve a(z) %s feladathoz %s/%s óra becsült idÅ‘vel', + 'Not assigned, estimate of %sh' => 'Nincs kiosztva, becsült idÅ‘: %s óra', + '%s updated a comment on the task %s' => '%s frissített egy megjegyzését a(z) %s feladatban', + '%s commented the task %s' => '%s megjegyzést írt a(z) %s feladathoz', + '%s\'s activity' => '%s tevékenységei', + 'RSS feed' => 'RSS hírforrás', + '%s updated a comment on the task #%d' => '%s frissített egy megjegyzést a(z) #%d. feladatban', + '%s commented on the task #%d' => '%s megjegyzést írt a(z) #%d. feladathoz', + '%s updated a subtask for the task #%d' => '%s frissített egy részfeladatot a(z) #%d. feladatnál', + '%s created a subtask for the task #%d' => '%s létrehozott egy részfeladatot a(z) #%d. feladatnál', + '%s updated the task #%d' => '%s frissítette a(z) #%d. feladatot', + '%s created the task #%d' => '%s létrehozta a(z) #%d. feladatot', + '%s closed the task #%d' => '%s lezárta a(z) #%d feladatot', + '%s opened the task #%d' => '%s megnyitotta a(z) #%d. feladatot', + 'Activity' => 'Tevékenység', + 'Default values are "%s"' => 'Az alapértelmezett értékek: „%sâ€', + 'Default columns for new projects (Comma-separated)' => 'Alapértelmezett oszlopok az új projekteknél (vesszÅ‘vel elválasztva)', + 'Task assignee change' => 'A feladat felelÅ‘sének megváltoztatása', + '%s changed the assignee of the task #%d to %s' => '%s megváltoztatta a(z) #%d. feladat felelÅ‘sét erre: %s', + '%s changed the assignee of the task %s to %s' => '%s megváltoztatta a(z) %s feladat felelÅ‘sét erre: %s', + 'New password for the user "%s"' => '„%s†felhasználó új jelszava', + 'Choose an event' => 'Esemény választása', + 'Create a task from an external provider' => 'Feladat létrehozása egy külsÅ‘s szolgáltatóból', + 'Change the assignee based on an external username' => 'FelelÅ‘s megváltoztatása egy külsÅ‘ felhasználónév alapján', + 'Change the category based on an external label' => 'Kategória megváltoztatása egy külsÅ‘ címke alapján', + 'Reference' => 'Hivatkozás', + 'Label' => 'Címke', + 'Database' => 'Adatbázis', + 'About' => 'Névjegy', + 'Database driver:' => 'Adatbázis-meghajtó:', + 'Board settings' => 'Tábla beállításai', + 'Webhook settings' => 'Webhurok beállításai', + 'Reset token' => 'Token visszaállítása', + 'API endpoint:' => 'API végpont:', + 'Refresh interval for personal board' => 'Frissítési idÅ‘köz a személyes táblánál', + 'Refresh interval for public board' => 'Frissítési idÅ‘köz a nyilvános táblánál', + 'Task highlight period' => 'Feladat kiemelésének idÅ‘tartama', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Az idÅ‘szak (másodpercben), amíg a feladatot legutóbb módosítottnak kell tekinteni (0: letiltás, alapértelmezetten 2 nap)', + 'Frequency in second (60 seconds by default)' => 'Gyakoriság másodpercben (alapértelmezetten 60 másodperc)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Gyakoriság másodpercben (0: a funkció letiltása, alapértelmezetten 10 másodperc)', + 'Application URL' => 'Alkalmazás URL', + 'Token regenerated.' => 'A token újra létrehozva.', + 'Date format' => 'Dátumformátum', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Az ISO formátum mindig elfogadott, például „%s†és „%sâ€', + 'New personal project' => 'Új személyes projekt', + 'This project is personal' => 'Ez a projekt személyes', + 'Add' => 'Hozzáadás', + 'Start date' => 'Kezdési dátum', + 'Time estimated' => 'Becsült idÅ‘', + 'There is nothing assigned to you.' => 'Nincs semmi sem Önhöz rendelve.', + 'My tasks' => 'Saját feladatok', + 'Activity stream' => 'Tevékenységfolyam', + 'Dashboard' => 'VezérlÅ‘pult', + 'Confirmation' => 'MegerÅ‘sítés', + 'Webhooks' => 'Webhurkok', + 'API' => 'API', + 'Create a comment from an external provider' => 'Megjegyzés létrehozása egy külsÅ‘ szolgáltatótól', + 'Project management' => 'Projektmenedzsment', + 'Columns' => 'Oszlopok', + 'Task' => 'Feladat', + 'Percentage' => 'Százalék', + 'Number of tasks' => 'Feladatok száma', + 'Task distribution' => 'Feladatelosztás', + 'Analytics' => 'Elemzések', + 'Subtask' => 'Részfeladat', + 'User repartition' => 'Felhasználónkénti megoszlás', + 'Clone this project' => 'Projekt másolása', + 'Column removed successfully.' => 'Az oszlop sikeresen eltávolítva.', + 'Not enough data to show the graph.' => 'Nincs elég adat a grafikon megjelenítéséhez.', + 'Previous' => 'ElÅ‘zÅ‘', + 'The id must be an integer' => 'Az azonosító csak egész szám lehet', + 'The project id must be an integer' => 'A projektazonosító csak egész szám lehet', + 'The status must be an integer' => 'Az állapot csak egész szám lehet', + 'The subtask id is required' => 'A részfeladat-azonosító kötelezÅ‘', + 'The subtask id must be an integer' => 'A részfeladat-azonosító csak egész szám lehet', + 'The task id is required' => 'A feladatazonosító kötelezÅ‘', + 'The task id must be an integer' => 'A feladatazonosító csak egész szám lehet', + 'The user id must be an integer' => 'A felhasználó-azonosító csak egész szám lehet', + 'This value is required' => 'Ez az érték kötelezÅ‘', + 'This value must be numeric' => 'Ez az érték csak szám lehet', + 'Unable to create this task.' => 'Nem lehet létrehozni a feladatot.', + 'Cumulative flow diagram' => 'Halmozott folyamatdiagram', + 'Daily project summary' => 'Napi projektösszefoglaló', + 'Daily project summary export' => 'Napi projektösszefoglaló exportálása', + '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 csoportosítva, napokra lebontva.', + 'Active swimlanes' => 'Aktív sávok', + 'Add a new swimlane' => 'Új sáv hozzáadása', + 'Default swimlane' => 'Alapértelmezett sáv', + 'Do you really want to remove this swimlane: "%s"?' => 'Valóban el szeretné távolítani ezt a sávot: „%sâ€?', + 'Inactive swimlanes' => 'Inaktív sávok', + 'Remove a swimlane' => 'Sáv eltávolítása', + 'Swimlane modification for the project "%s"' => 'Sávmódosítás a(z) „%s†projektnél', + 'Swimlane removed successfully.' => 'A sáv sikeresen eltávolítva.', + 'Swimlanes' => 'Sávok', + 'Swimlane updated successfully.' => 'A sáv sikeresen frissítve.', + 'Unable to remove this swimlane.' => 'Nem lehet eltávolítani ezt a sávot.', + 'Unable to update this swimlane.' => 'Nem lehet frissíteni ezt a sávot.', + 'Your swimlane has been created successfully.' => 'A sáv sikeresen létrehozva.', + 'Example: "Bug, Feature Request, Improvement"' => 'Például: Hiba, Funkciókérés, Fejlesztés', + 'Default categories for new projects (Comma-separated)' => 'Alapértelmezett kategóriák az új projekteknél (vesszÅ‘vel elválasztva)', + 'Integrations' => 'Integrációk', + 'Integration with third-party services' => 'Integráció harmadik féltÅ‘l származó szolgáltatásokkal', + 'Subtask Id' => 'Részfeladat-azonosító', + 'Subtasks' => 'Részfeladatok', + 'Subtasks Export' => 'Részfeladat exportálása', + 'Task Title' => 'Feladat címe', + 'Untitled' => 'Névtelen', + 'Application default' => 'Alkalmazás alapértelmezettje', + 'Language:' => 'Nyelv:', + 'Timezone:' => 'IdÅ‘zóna:', + 'All columns' => 'Összes oszlop', + 'Next' => 'KövetkezÅ‘', + '#%d' => '#%d', + 'All swimlanes' => 'Összes sáv', + 'All colors' => 'Összes szín', + 'Moved to column %s' => '%s oszlopba áthelyezve', + 'User dashboard' => 'Felhasználó vezérlÅ‘pultja', + 'Allow only one subtask in progress at the same time for a user' => 'Egyszerre csak egy folyamatban levÅ‘ részfeladat engedélyezése egy felhasználónak', + 'Edit column "%s"' => '„%s†oszlop szerkesztése', + 'Select the new status of the subtask: "%s"' => 'A részfeladat új állapotának kiválasztása: „%sâ€', + 'Subtask timesheet' => 'Részfeladat idÅ‘beosztása', + 'There is nothing to show.' => 'Nincs mit megjeleníteni.', + 'Time Tracking' => 'IdÅ‘ követés', + 'You already have one subtask in progress' => 'Már van egy folyamatban levÅ‘ részfeladata', + 'Which parts of the project do you want to duplicate?' => 'A projekt mely részeit szeretné másolni?', + 'Disallow login form' => 'Bejelentkezési űrlap letiltása', + 'Start' => 'Kezdet', + 'End' => 'Vég', + 'Task age in days' => 'Feladat életkora napokban', + 'Days in this column' => 'Napok ebben az oszlopban', + '%dd' => '%dn', + 'Add a new link' => 'Új hivatkozás hozzáadása', + 'Do you really want to remove this link: "%s"?' => 'Valóban el szeretné távolítani ezt a hivatkozást: „%sâ€?', + 'Do you really want to remove this link with task #%d?' => 'Valóban el szeretné távolítani a(z) #%d. feladatra mutató hivatkozást?', + 'Field required' => 'A mezÅ‘ kötelezÅ‘', + 'Link added successfully.' => 'A hivatkozás sikeresen hozzáadva.', + 'Link updated successfully.' => 'A hivatkozás sikeresen frissítve.', + 'Link removed successfully.' => 'A hivatkozás sikeresen eltávolítva.', + 'Link labels' => 'Hivatkozás címkék', + 'Link modification' => 'Hivatkozás módosítása', + 'Opposite label' => 'EllenkezÅ‘ címke', + 'Remove a link' => 'Hivatkozás eltávolítása', + '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.', + 'Unable to create your link.' => 'Nem lehet létrehozni a hivatkozást.', + 'Unable to update your link.' => 'Nem lehet frissíteni a hivatkozást.', + 'Unable to remove this link.' => 'Nem lehet eltávolítani a hivatkozást.', + 'relates to' => 'ehhez tartozik:', + 'blocks' => 'blokkolja:', + 'is blocked by' => 'blokkolva van:', + 'duplicates' => 'másolja:', + 'is duplicated by' => 'másolva van:', + 'is a child of' => 'gyermeke a következÅ‘nek:', + 'is a parent of' => 'szülÅ‘je a következÅ‘nek:', + 'targets milestone' => 'megcélzott mérföldkÅ‘:', + 'is a milestone of' => 'mérföldköve:', + 'fixes' => 'javítja:', + 'is fixed by' => 'javította:', + 'This task' => 'Ez a feladat', + '<1h' => '<1ó', + '%dh' => '%dó', + 'Expand tasks' => 'Feladatok kinyitása', + 'Collapse tasks' => 'Feladatok összecsukása', + 'Expand/collapse tasks' => 'Feladatok kinyitása/összecsukása', + 'Close dialog box' => 'Párbeszédablak bezárása', + 'Submit a form' => 'Űrlap elküldése', + 'Board view' => 'Tábla nézet', + 'Keyboard shortcuts' => 'Gyorsbillentyűk', + 'Open board switcher' => 'Táblaválasztó megnyitása', + 'Application' => 'Alkalmazás', + 'Compact view' => 'Tömör nézet', + 'Horizontal scrolling' => 'Vízszintes görgetés', + 'Compact/wide view' => 'Tömör/széles nézet', + 'Currency' => 'Pénznem', + 'Personal project' => 'Személyes projekt', + 'AUD - Australian Dollar' => 'AUD – ausztrál dollár', + 'CAD - Canadian Dollar' => 'CAD – kanadai dollár', + 'CHF - Swiss Francs' => 'CHF – svájci frank', + 'Custom Stylesheet' => 'Egyéni stíluslap', + 'EUR - Euro' => 'EUR – euro', + 'GBP - British Pound' => 'GBP – angol font', + 'INR - Indian Rupee' => 'INR – indiai rúpia', + 'JPY - Japanese Yen' => 'JPY – japán jen', + 'NZD - New Zealand Dollar' => 'NZD – új-zélandi dollár', + 'PEN - Peruvian Sol' => 'PEN - Perui sol', + 'RSD - Serbian dinar' => 'RSD – szerb dinár', + 'CNY - Chinese Yuan' => 'CNY – kínai jüan', + 'USD - US Dollar' => 'USD – amerikai dollár', + 'VES - Venezuelan Bolívar' => 'VES – venezuelai bolívar', + 'Destination column' => 'Céloszlop', + 'Move the task to another column when assigned to a user' => 'Feladat áthelyezése másik oszlopba, ha egy felhasználóhoz rendelik', + 'Move the task to another column when assignee is cleared' => 'Feladat áthelyezése másik oszlopba, ha a felelÅ‘st törlik', + 'Source column' => 'Forrásoszlop', + 'Transitions' => 'Ãtmenetek', + 'Executer' => 'Végrehajtó', + 'Time spent in the column' => 'Az oszlopban eltöltött idÅ‘', + 'Task transitions' => 'Feladatátmenetek', + 'Task transitions export' => 'Feladatátmenetek exportálása', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ez a jelentés az egyes feladatok összes oszlopáthelyezését tartalmazza dátummal, felhasználóval és az egyes átmenetekben eltöltött idÅ‘vel.', + 'Currency rates' => 'Devizaárfolyamok', + 'Rate' => 'Ãrfolyam', + 'Change reference currency' => 'Bázis pénznem megváltoztatása', + 'Reference currency' => 'Bázis pénznem', + 'The currency rate has been added successfully.' => 'A devizaárfolyam sikeresen hozzáadva.', + 'Unable to add this currency rate.' => 'Nem lehet hozzáadni ezt a devizaárfolyamot.', + 'Webhook URL' => 'Webhurok URL', + '%s removed the assignee of the task %s' => '%s eltávolította a(z) %s feladat felelÅ‘sét', + 'Information' => 'Információ', + 'Check two factor authentication code' => 'A kétlépcsÅ‘s hitelesítés kódjának ellenÅ‘rzése', + 'The two factor authentication code is not valid.' => 'A kétlépcsÅ‘s hitelesítés kódja nem érvényes.', + 'The two factor authentication code is valid.' => 'A kétlépcsÅ‘s hitelesítés kódja érvényes.', + 'Code' => 'Kód', + 'Two factor authentication' => 'KétlépcsÅ‘s hitelesítés', + 'This QR code contains the key URI: ' => 'Ez a QR-kód tartalmazza a kulcs URI-t: ', + 'Check my code' => 'A kódom ellenÅ‘rzése', + 'Secret key: ' => 'Titkos kulcs: ', + '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 helyezték át', + '%s via Kanboard' => '%s a Kanboardon keresztül', + 'Burndown chart' => 'Burndown diagram', + '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' => 'KépernyÅ‘kép készült: %s', + 'Add a screenshot' => 'KépernyÅ‘kép hozzáadása', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Készítsen képernyÅ‘képet, majd a CTRL+V vagy ⌘+V megnyomásával illessze be ide.', + 'Screenshot uploaded successfully.' => 'A képernyÅ‘kép sikeresen feltöltése.', + 'SEK - Swedish Krona' => 'SEK – svéd korona', + 'Identifier' => 'Azonosító', + 'Disable two factor authentication' => 'A kétlépcsÅ‘s hitelesítés letiltása', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Valóban le szeretné tiltani a kétlépcsÅ‘s hitelesítést ennél a felhasználónál: „%sâ€?', + 'Edit link' => 'Hivatkozás szerkesztése', + 'Start to type task title...' => 'Kezdje el gépelni a feladat címét…', + 'A task cannot be linked to itself' => 'Egy feladatot nem lehet önmagához kapcsolni', + 'The exact same link already exists' => 'Már létezik pontosan ugyanez a hivatkozás', + 'Recurrent task is scheduled to be generated' => 'Az ismétlÅ‘dÅ‘ feladat előállítása ütemezve lett', + 'Score' => 'Pontszám', + 'The identifier must be unique' => 'Az azonosítónak egyedinek kell lennie', + 'This linked task id doesn\'t exists' => 'Ez a hivatkozott feladatazonosító nem létezik', + 'This value must be alphanumeric' => 'Ez az érték csak betűket és számokat tartalmazhat', + 'Edit recurrence' => 'IsmétlÅ‘dés szerkesztése', + 'Generate recurrent task' => 'IsmétlÅ‘dÅ‘ feladat előállítása', + 'Trigger to generate recurrent task' => 'Aktiváló az ismétlÅ‘dÅ‘ feladat előállításához', + 'Factor to calculate new due date' => 'TényezÅ‘ az új határidÅ‘ kiszámításához', + 'Timeframe to calculate new due date' => 'IdÅ‘ablak az új határidÅ‘ kiszámításához', + 'Base date to calculate new due date' => 'Alapdátum az új határidÅ‘ kiszámításához', + 'Action date' => 'Tevékenység dátuma', + 'Base date to calculate new due date: ' => 'Alapdátum az új határidÅ‘ kiszámításához: ', + 'This task has created this child task: ' => 'Ez a feladat ezt a gyermekfeladatot hozta létre: ', + 'Day(s)' => 'Nap', + 'Existing due date' => 'MeglévÅ‘ határidÅ‘', + 'Factor to calculate new due date: ' => 'TényezÅ‘ az új határidÅ‘ kiszámításához: ', + 'Month(s)' => 'Hónap', + 'This task has been created by: ' => 'Ezt a feladatot a következÅ‘ személy hozta létre: ', + 'Recurrent task has been generated:' => 'IsmétlÅ‘dÅ‘ feladat lett előállítva: ', + 'Timeframe to calculate new due date: ' => 'IdÅ‘ablak az új határidÅ‘ kiszámításához: ', + 'Trigger to generate recurrent task: ' => 'Aktiváló az ismétlÅ‘dÅ‘ feladat előállításához: ', + 'When task is closed' => 'Ha a feladatok lezárták', + 'When task is moved from first column' => 'Ha a feladatot áthelyezték az elsÅ‘ oszlopból', + 'When task is moved to last column' => 'Ha a feladatot áthelyezték az utolsó oszlopba', + 'Year(s)' => 'Év', + 'Project settings' => 'Projekt beállításai', + 'Automatically update the start date' => 'A kezdési dátum automatikus frissítése', + 'iCal feed' => 'iCal hírforrás', + 'Preferences' => 'Beállítások', + 'Security' => 'Biztonság', + 'Two factor authentication disabled' => 'A kétlépcsÅ‘s hitelesítés letiltva', + 'Two factor authentication enabled' => 'A kétlépcsÅ‘s hitelesítés engedélyezve', + 'Unable to update this user.' => 'Nem lehet frissíteni a felhasználót.', + 'There is no user management for personal projects.' => 'Nincs felhasználókezelés a személyes projekteknél.', + 'User that will receive the email' => 'A felhasználó, aki megkapja az e-mailt', + 'Email subject' => 'E-mail tárgya', + 'Date' => 'Dátum', + 'Add a comment log when moving the task between columns' => 'Megjegyzésnapló hozzáadása, ha a feladatot az oszlopok között mozgatják', + 'Move the task to another column when the category is changed' => 'Feladat áthelyezése egy másik oszlopba, ha megváltozik a kategória', + 'Send a task by email to someone' => 'Feladat elküldése e-mailben valakinek', + 'Reopen a task' => 'Feladat ismételt megnyitása', + 'Notification' => 'Értesítés', + '%s moved the task #%d to the first swimlane' => '%s áthelyezte a(z) #%d. feladatot az elsÅ‘ sávba', + 'Swimlane' => 'Sáv', + '%s moved the task %s to the first swimlane' => '%s áthelyezte a(z) %s feladatot az elsÅ‘ sávba', + '%s moved the task %s to the swimlane "%s"' => '%s áthelyezte a(z) %s feladatot a(z) „%s†sávba', + 'This report contains all subtasks information for the given date range.' => 'Ez a jelentés az összes részfeladat-információt tartalmazza az adott dátumtartományban', + 'This report contains all tasks information for the given date range.' => 'Ez a jelentés az összes feladatinformációt tartalmazza az adott dátumtartományban', + 'Project activities for %s' => '%s projekttevékenységei', + 'view the board on Kanboard' => 'a tábla megjelenítése a Kanboardon', + 'The task has been moved to the first swimlane' => 'A feladat át lett helyezve az elsÅ‘ sávba', + 'The task has been moved to another swimlane:' => 'A feladat át lett helyezve egy másik sávba:', + 'New title: %s' => 'Új cím: %s', + 'The task is not assigned anymore' => 'A feladatnak többé már nincs felelÅ‘se', + 'New assignee: %s' => 'Új felelÅ‘s: %s', + 'There is no category now' => 'Jelenleg nincs kategória', + 'New category: %s' => 'Új kategória: %s', + 'New color: %s' => 'Új szín: %s', + 'New complexity: %d' => 'Új bonyolultság: %d', + 'The due date has been removed' => 'A határidÅ‘ eltávolításra került', + 'There is no description anymore' => 'Többé már nincs leírás', + 'Recurrence settings has been modified' => 'Az ismétlÅ‘dés beállításai módosítva lettek', + 'Time spent changed: %sh' => 'Az eltöltött idÅ‘ megváltozott: %s óra', + 'Time estimated changed: %sh' => 'A becsült idÅ‘ megváltozott: %s óra', + 'The field "%s" has been updated' => 'A(z) „%s†mezÅ‘ frissítve lett', + 'The description has been modified:' => 'A leírás módosítva lett:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Valóban le szeretné zárni a(z) „%s†feladatot, valamint az összes részfeladatot?', + 'I want to receive notifications for:' => 'Értesítéseket szeretnék kapni a következÅ‘krÅ‘l:', + 'All tasks' => 'Összes feladat', + 'Only for tasks assigned to me' => 'Csak a hozzám rendelt feladatok', + 'Only for tasks created by me' => 'Csak az általam létrehozott feladatok', + 'Only for tasks created by me and tasks assigned to me' => 'Csak az általam létrehozott és a hozzám rendelt feladatok', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Az összes oszlop összege', + 'You need at least 2 days of data to show the chart.' => 'Legalább 2 nap adatára van szükség a diagram megjelenítéséhez.', + '<15m' => '<15p', + '<30m' => '<30p', + 'Stop timer' => 'IdÅ‘mérÅ‘ leállítása', + 'Start timer' => 'IdÅ‘mérÅ‘ elindítása', + 'My activity stream' => 'Saját tevékenységfolyamom', + 'Search tasks' => 'Feladatok keresése', + 'Reset filters' => 'SzűrÅ‘k visszaállítása', + 'My tasks due tomorrow' => 'Holnapi határidejű feladataim', + 'Tasks due today' => 'Mai határidejű feladatok', + 'Tasks due tomorrow' => 'Holnapi határidejű feladatok', + 'Tasks due yesterday' => 'Tegnapi határidejű feladatok', + 'Closed tasks' => 'Lezárt feladatok', + 'Open tasks' => 'Nyitott feladatok', + 'Not assigned' => 'Nincs hozzárendelve', + 'View advanced search syntax' => 'Speciális keresés szintaxis megtekintése', + 'Overview' => 'Ãttekintés', + 'Board/Calendar/List view' => 'Tábla/Naptár/Lista nézet', + 'Switch to the board view' => 'Ãtváltás tábla nézetre', + 'Switch to the list view' => 'Ãtváltás lista nézetre', + 'Go to the search/filter box' => 'Ugrás a keresés/szűrés dobozhoz', + 'There is no activity yet.' => 'Még nincs tevékenység.', + 'No tasks found.' => 'Nem található feladat.', + 'Keyboard shortcut: "%s"' => 'Gyorsbillentyű: „%sâ€', + 'List' => 'Lista', + 'Filter' => 'SzűrÅ‘', + 'Advanced search' => 'Speciális keresés', + 'Example of query: ' => 'Lekérdezési példa: ', + 'Search by project: ' => 'Keresés projekt alapján: ', + 'Search by column: ' => 'Keresés oszlop alapján: ', + 'Search by assignee: ' => 'Keresés felelÅ‘s alapján: ', + 'Search by color: ' => 'Keresés szín alapján: ', + '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: ', + 'Average time spent in each column' => 'Ãtlagosan eltöltött idÅ‘ az egyes oszlopokban', + 'Average time spent' => 'Ãtlagosan eltöltött idÅ‘', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Ez a diagram az egyes oszlopokban átlagosan eltöltött idÅ‘t jeleníti meg az utolsó %d feladatnál.', + 'Average Lead and Cycle time' => 'Ãtlagos átfutási és ciklusidÅ‘', + 'Average lead time: ' => 'Ãtlagos átfutási idÅ‘: ', + 'Average cycle time: ' => 'Ãtlagos ciklusidÅ‘: ', + 'Cycle Time' => 'CiklusidÅ‘', + 'Lead Time' => 'Ãtfutási idÅ‘', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Ez a diagram az átlagos átfutási és ciklusidÅ‘t jeleníti meg az utolsó %d feladatnál az idÅ‘ függvényében.', + 'Average time into each column' => 'Ãtlagos idÅ‘ az egyes oszlopokban', + 'Lead and cycle time' => 'Ãtfutási és ciklusidÅ‘', + 'Lead time: ' => 'Ãtfutási idÅ‘: ', + 'Cycle time: ' => 'CiklusidÅ‘: ', + 'Time spent in each column' => 'Az egyes oszlopokban eltöltött idÅ‘', + 'The lead time is the duration between the task creation and the completion.' => 'Az átfutási idÅ‘ a feladat létrehozása és befejezése közötti idÅ‘tartam.', + 'The cycle time is the duration between the start date and the completion.' => 'A ciklusidÅ‘ a kezdési dátum és a befejezés közötti idÅ‘tartam.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Ha a feladat nincs lezárva, akkor az aktuális idÅ‘ lesz használva a befejezés dátuma helyett.', + 'Set the start date automatically' => 'A kezdési dátum automatikus beállítása', + 'Edit Authentication' => 'Hitelesítés szerkesztése', + '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 fiókok.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ha bejelöli a „Bejelentkezési űrlap letiltása†jelölÅ‘négyzetet, akkor a bejelentkezési űrlapon megadott hitelesítési adatok figyelmen kívül lesznek hagyva.', + 'Default task color' => 'Alapértelmezett feladatszín', + 'This feature does not work with all browsers.' => 'Ez a funkció nem működik minden böngészÅ‘ben.', + 'There is no destination project available.' => 'Nem érhetÅ‘ el célprojekt.', + 'Trigger automatically subtask time tracking' => 'A részfeladatok idÅ‘követésének automatikus aktiválása', + 'Include closed tasks in the cumulative flow diagram' => 'A lezárt feladatok is legyenek benne a halmozott folyamdiagramban', + 'Current swimlane: %s' => 'Jelenlegi sáv: %s', + 'Current column: %s' => 'Jelenlegi oszlop: %s', + 'Current category: %s' => 'Jelenlegi kategória: %s', + 'no category' => 'nincs kategória', + 'Current assignee: %s' => 'Jelenlegi felelÅ‘s: %s', + 'not assigned' => 'nincs kijelölt felelÅ‘s', + 'Author:' => 'SzerzÅ‘:', + 'contributors' => 'közreműködÅ‘k', + 'License:' => 'Licenc:', + 'License' => 'Licenc', + 'Enter the text below' => 'Adja meg a lenti szöveget', + 'Start date:' => 'Kezdési dátum:', + 'Due date:' => 'HatáridÅ‘:', + 'People who are project managers' => 'Emberek, akik projektvezetÅ‘k', + 'People who are project members' => 'Emberek, akik projekttagok', + 'NOK - Norwegian Krone' => 'NOK – norvég korona', + 'Show this column' => 'Oszlop megjelenítése', + 'Hide this column' => 'Oszlop elrejtése', + 'End date' => 'Befejezési dátum', + 'Users overview' => 'Felhasználók áttekintése', + 'Members' => 'Tagok', + 'Shared project' => 'Megosztott projekt', + 'Project managers' => 'ProjektvezetÅ‘k', + 'Projects list' => 'Projektek listája', + 'End date:' => 'Befejezési dátum:', + 'Change task color when using a specific task link' => 'Feladatszín megváltoztatása, ha egy adott feladathivatkozást használnak', + 'Task link creation or modification' => 'Feladathivatkozás létrehozása vagy módosítása', + 'Milestone' => 'MérföldkÅ‘', + 'Reset the search/filter box' => 'A keresés/szűrés doboz visszaállítása', + 'Documentation' => 'Dokumentáció', + 'Author' => 'SzerzÅ‘', + 'Version' => 'Verzió', + 'Plugins' => 'BÅ‘vítmények', + 'There is no plugin loaded.' => 'Nincsenek betöltött bÅ‘vítmények.', + 'My notifications' => 'Saját emlékeztetÅ‘k', + 'Custom filters' => 'Egyéni szűrÅ‘k', + 'Your custom filter has been created successfully.' => 'Az egyéni szűrÅ‘je sikeresen létrehozva.', + 'Unable to create your custom filter.' => 'Nem lehet létrehozni az egyéni szűrÅ‘jét.', + 'Custom filter removed successfully.' => 'Az egyéni szűrÅ‘ sikeresen eltávolítva.', + 'Unable to remove this custom filter.' => 'Nem lehet eltávolítani az egyéni szűrÅ‘t.', + 'Edit custom filter' => 'Egyéni szűrÅ‘ szerkesztése', + 'Your custom filter has been updated successfully.' => 'Az egyéni szűrÅ‘je sikeresen frissítve.', + 'Unable to update custom filter.' => 'Nem lehet frissíteni az egyéni szűrÅ‘t.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Új melléklet a(z) #%d. feladatnál: %s', + 'New comment on task #%d' => 'Új megjegyzés a(z) #%d. feladatnál', + 'Comment updated on task #%d' => 'A megjegyzés frissítve lett a(z) #%d. feladatnál', + 'New subtask on task #%d' => 'Új részfeladat a(z) #%d. feladatnál', + 'Subtask updated on task #%d' => 'A részfeladat frissítve lett a(z) #%d. feladatnál', + 'New task #%d: %s' => 'Új #%d számú feladat: %s', + 'Task updated #%d' => 'A(z) #%d. feladat frissült', + 'Task #%d closed' => 'A(z) #%d. feladat le lett zárva', + 'Task #%d opened' => 'A(z) #%d. feladat meg lett nyitva', + 'Column changed for task #%d' => 'Az oszlop megváltozott a(z) #%d. feladatnál', + 'New position for task #%d' => 'Új pozíció a(z) #%d. feladatnál', + 'Swimlane changed for task #%d' => 'A sáv megváltozott a(z) #%d. feladatnál', + 'Assignee changed on task #%d' => 'A felelÅ‘s megváltozott a(z) #%d. feladatnál', + '%d overdue tasks' => '%d lejárt feladat', + 'No notification.' => 'Nincs értesítés.', + 'Mark all as read' => 'Az összes megjelölése olvasottként', + 'Mark as read' => 'Megjelölés olvasottként', + 'Total number of tasks in this column across all swimlanes' => 'Az ebben az oszlopban, az összes sávban lévÅ‘ feladatok száma', + 'Collapse swimlane' => 'Sáv összecsukása', + 'Expand swimlane' => 'Sáv kinyitása', + 'Add a new filter' => 'Új szűrÅ‘ hozzáadása', + 'Share with all project members' => 'Megosztás az összes projekttaggal', + 'Shared' => 'Megosztva', + 'Owner' => 'Tulajdonos', + 'Unread notifications' => 'Olvasatlan értesítések', + 'Notification methods:' => 'Értesítési módszerek:', + 'Unable to read your file' => 'Nem lehet beolvasni a fájlt', + '%d task(s) have been imported successfully.' => '%d feladat sikeresen importálva.', + 'Nothing has been imported!' => 'Semmi sem lett importálva!', + 'Import users from CSV file' => 'Felhasználók importálása CSV-fájlból', + '%d user(s) have been imported successfully.' => '%d felhasználó sikeresen importálva.', + 'Comma' => 'VesszÅ‘', + 'Semi-colon' => 'PontosvesszÅ‘', + 'Tab' => 'Tabulátor', + 'Vertical bar' => 'FüggÅ‘leges vonal', + 'Double Quote' => 'IdézÅ‘jel', + 'Single Quote' => 'Aposztróf', + '%s attached a file to the task #%d' => '%s mellékelt egy fájlt a(z) #%d. feladathoz', + 'There is no column or swimlane activated in your project!' => 'Nincs aktivált oszlop vagy sáv a projektjében!', + 'Append filter (instead of replacement)' => 'SzűrÅ‘ hozzáfűzése (kicserélés helyett)', + 'Append/Replace' => 'Hozzáfűzés/csere', + 'Append' => 'Hozzáfűzés', + 'Replace' => 'Csere', + 'Import' => 'Importálás', + 'Change sorting' => 'Rendezés megváltoztatása', + 'Tasks Importation' => 'Feladat importálása', + 'Delimiter' => 'Elválasztó', + 'Enclosure' => 'Határoló', + 'CSV File' => 'CSV-fájl', + 'Instructions' => 'Utasítások', + 'Your file must use the predefined CSV format' => 'A fájlnak az elÅ‘re meghatározott CSV-formátumot kell használnia', + 'Your file must be encoded in UTF-8' => 'A fájlnak UTF-8 kódolásúnak kell lennie', + 'The first row must be the header' => 'Az elsÅ‘ sornak fejlécnek kell lennie', + 'Duplicates are not verified for you' => 'Az ismétlÅ‘dések nincsenek ellenÅ‘rizve', + 'The due date must use the ISO format: YYYY-MM-DD' => 'A határidÅ‘nek ISO formátumot kell használnia: YYYY-MM-DD', + 'Download CSV template' => 'CSV-sablon letöltése', + 'No external integration registered.' => 'Nincs külsÅ‘ integráció regisztrálva.', + 'Duplicates are not imported' => 'Az ismétlÅ‘dések nincsenek importálva', + 'Usernames must be lowercase and unique' => 'A felhasználóneveknek kisbetűsnek és egyedinek kell lenniük', + 'Passwords will be encrypted if present' => 'A jelszavak titkosítva lesznek, ha meg vannak adva', + '%s attached a new file to the task %s' => '%s mellékelt egy új fájlt a(z) %s feladathoz', + 'Link type' => 'Hivatkozás típusa', + 'Assign automatically a category based on a link' => 'Kategória automatikus hozzárendelése egy hivatkozás alapján', + 'BAM - Konvertible Mark' => 'BAM – konvertibilis márka', + 'Assignee Username' => 'FelelÅ‘s felhasználóneve', + 'Assignee Name' => 'FelelÅ‘s neve', + 'Groups' => 'Csoportok', + 'Members of %s' => '%s tagjai', + 'New group' => 'Új csoport', + 'Group created successfully.' => 'A csoport sikeresen létrehozva.', + 'Unable to create your group.' => 'Nem lehet létrehozni a csoportot.', + 'Edit group' => 'Csoport szerkesztése', + 'Group updated successfully.' => 'A csoport sikeresen frissítve.', + 'Unable to update your group.' => 'Nem lehet frissíteni a csoportot.', + 'Add group member to "%s"' => 'Csoporttag hozzáadása ehhez: „%sâ€', + 'Group member added successfully.' => 'A csoporttag sikeresen hozzáadva.', + 'Unable to add group member.' => 'Nem lehet hozzáadni a csoporttagot.', + 'Remove user from group "%s"' => 'Felhasználó eltávolítása a(z) „%s†csoportból', + 'User removed successfully from this group.' => 'A felhasználó sikeresen el lett távolítva a csoportból.', + 'Unable to remove this user from the group.' => 'Nem lehet eltávolítani a felhasználót a csoportból.', + 'Remove group' => 'Csoport eltávolítása', + 'Group removed successfully.' => 'A csoport sikeresen eltávolítva.', + 'Unable to remove this group.' => 'Nem lehet eltávolítani a csoportot.', + 'Project Permissions' => 'Projektjogosultságok', + 'Manager' => 'VezetÅ‘', + 'Project Manager' => 'ProjektvezetÅ‘', + 'Project Member' => 'Projekttag', + 'Project Viewer' => 'ProjektmegtekintÅ‘', + 'Your account is locked for %d minutes' => 'A fiókja zárolva lett %d percre', + 'Invalid captcha' => 'Érvénytelen captcha', + 'The name must be unique' => 'A névnek egyedinek kell lennie', + 'View all groups' => 'Az összes csoport megtekintése', + 'There is no user available.' => 'Nincs elérhetÅ‘ felhasználó.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Valóban el szeretné távolítani „%s†felhasználót a(z) „%s†csoportból?', + 'There is no group.' => 'Nincs csoport.', + 'Add group member' => 'Csoporttag hozzáadása', + 'Do you really want to remove this group: "%s"?' => 'Valóban el szeretné távolítani ezt a csoportot: „%sâ€?', + 'There is no user in this group.' => 'Nincs felhasználó ebben a csoportban.', + 'Permissions' => 'Jogosultságok', + 'Allowed Users' => 'Engedélyezett felhasználók', + 'No specific user has been allowed.' => 'Egyetlen megadott felhasználó sem lett engedélyezve.', + 'Role' => 'Szerep', + 'Enter user name...' => 'Adja meg a felhasználó nevét…', + 'Allowed Groups' => 'Engedélyezett csoportok', + 'No group has been allowed.' => 'Egyetlen csoport sem lett engedélyezve.', + 'Group' => 'Csoport', + 'Group Name' => 'Csoport neve', + 'Enter group name...' => 'Adja meg a csoport nevét…', + 'Role:' => 'Szerep:', + 'Project members' => 'Projekttagok', + '%s mentioned you in the task #%d' => '%s megemlítette Önt a(z) #%d. feladatban', + '%s mentioned you in a comment on the task #%d' => '%s megemlítette Önt a(z) #%d. feladathoz fűzött megjegyzésben', + 'You were mentioned in the task #%d' => 'Megemlítették Önt a(z) #%d. feladatban', + 'You were mentioned in a comment on the task #%d' => 'Megemlítették Önt a(z) #%d. feladathoz fűzött megjegyzésben', + 'Estimated hours: ' => 'Becsült órák: ', + 'Actual hours: ' => 'Tényleges órák: ', + 'Hours Spent' => 'Eltöltött órák', + 'Hours Estimated' => 'Becsült órák', + 'Estimated Time' => 'Becsült idÅ‘', + 'Actual Time' => 'Tényleges idÅ‘', + 'Estimated vs actual time' => 'Becsült ↔ tényleges idÅ‘', + 'RUB - Russian Ruble' => 'RUB – orosz rubel', + 'Assign the task to the person who does the action when the column is changed' => 'A feladat hozzárendelése ahhoz a személyhez, aki elvégzi a műveletet az oszlop megváltoztatásakor', + 'Close a task in a specific column' => 'Feladat lezárása egy adott oszlopban', + 'Time-based One-time Password Algorithm' => 'IdÅ‘alapú, egyszer használható jelszó algoritmus', + 'Two-Factor Provider: ' => 'KétlépcsÅ‘s hitelesítés szolgáltatója: ', + 'Disable two-factor authentication' => 'KétlépcsÅ‘s hitelesítés letiltása', + 'Enable two-factor authentication' => 'KétlépcsÅ‘s hitelesítés engedélyezése', + 'There is no integration registered at the moment.' => 'Jelenleg nincs integráció regisztrálva.', + 'Password Reset for Kanboard' => 'Kanboard jelszó visszaállítása', + 'Forgot password?' => 'Elfelejtette a jelszavát?', + 'Enable "Forget Password"' => '„Elfelejtett jelszó†engedélyezése', + 'Password Reset' => 'Jelszó visszaállítása', + 'New password' => 'Új jelszó', + 'Change Password' => 'Jelszó megváltoztatása', + 'To reset your password click on this link:' => 'A jelszó visszaállításához kattintson erre a hivatkozásra:', + 'Last Password Reset' => 'Jelszó alaphelyzetbe állítás utoljára ekkor', + 'The password has never been reinitialized.' => 'A jelszó soha sem volt visszaállítva.', + 'Creation' => 'Létrehozás', + 'Expiration' => 'Lejárat', + 'Password reset history' => 'Jelszó visszaállításának elÅ‘zményei ', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'A(z) „%s†oszlop és a(z) „%s†sáv összes feladata sikeresen lezárva.', + 'Do you really want to close all tasks of this column?' => 'Valóban le szeretné zárni az oszlop összes feladatát?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d feladat lesz lezárva a(z) „%s†oszlopban és a(z) „%s†sávban.', + 'Close all tasks in this column and this swimlane' => 'Az összes feladat lezárása ebben az oszlopban és ebben a sávban', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Egyetlen bÅ‘vítmény sem regisztrált projekt értesítési módszert. Egyedi értesítéseket továbbra is beállíthat a felhasználói profiljában.', + 'My dashboard' => 'Saját vezérlÅ‘pult', + 'My profile' => 'Saját profil', + 'Project owner: ' => 'Projekttulajdonos: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'A projektazonosító elhagyható, és csak betűk és számok lehetnek, például: MYPROJECT.', + 'Project owner' => 'Projekttulajdonos', + 'Personal projects do not have users and groups management.' => 'A személyes projekteknek nincs felhasználó- és csoportkezelése.', + 'There is no project member.' => 'Nincs projekttag.', + 'Priority' => 'Prioritás', + 'Task priority' => 'Feladat prioritása', + 'General' => 'Ãltalános', + 'Dates' => 'Dátumok', + 'Default priority' => 'Alapértelmezett prioritás', + 'Lowest priority' => 'Legalacsonyabb prioritás', + 'Highest priority' => 'Legmagasabb prioritás', + 'Close a task when there is no activity' => 'Feladat lezárása, ha nincs tevékenység', + 'Duration in days' => 'IdÅ‘tartam napokban', + 'Send email when there is no activity on a task' => 'E-mail küldése, ha nincs tevékenység egy feladatban', + 'Unable to fetch link information.' => 'Nem lehet lekérni a hivatkozás információit.', + 'Daily background job for tasks' => 'Napi háttérmunka a feladatoknál', + 'Auto' => 'Automatikus', + 'Related' => 'Kapcsolódó', + 'Attachment' => 'Melléklet', + 'Web Link' => 'Webes hivatkozás', + 'External links' => 'KülsÅ‘ hivatkozások', + 'Add external link' => 'KülsÅ‘ hivatkozás hozzáadása', + 'Type' => 'Típus', + 'Dependency' => 'FüggÅ‘ség', + 'Add internal link' => 'BelsÅ‘ hivatkozás hozzáadása', + 'Add a new external link' => 'Új külsÅ‘ hivatkozás hozzáadása', + 'Edit external link' => 'KülsÅ‘ hivatkozás szerkesztése', + 'External link' => 'KülsÅ‘ hivatkozás', + 'Copy and paste your link here...' => 'Másolja ki és illessze be a hivatkozást ide…', + 'URL' => 'URL', + 'Internal links' => 'BelsÅ‘ hivatkozások', + 'Assign to me' => 'Rendelje hozzám', + 'Me' => 'Hozzám', + 'Do not duplicate anything' => 'Ne kettÅ‘zzön semmit sem', + 'Projects management' => 'Projektek kezelése', + 'Users management' => 'Felhasználók kezelése', + 'Groups management' => 'Csoportok kezelése', + 'Create from another project' => 'Létrehozás egy másik projektbÅ‘l', + 'open' => 'nyitott', + 'closed' => 'lezárt', + 'Priority:' => 'Prioritás:', + 'Reference:' => 'Hivatkozás:', + 'Complexity:' => 'Bonyolultság:', + 'Swimlane:' => 'Sáv:', + 'Column:' => 'Oszlop:', + 'Position:' => 'Pozíció:', + 'Creator:' => 'Létrehozó:', + 'Time estimated:' => 'Becsült idÅ‘:', + '%s hours' => '%s óra', + 'Time spent:' => 'Eltöltött idÅ‘:', + 'Created:' => 'Létrehozva:', + 'Modified:' => 'Módosítva:', + 'Completed:' => 'Elkészült:', + 'Started:' => 'Elindult:', + 'Moved:' => 'Ãthelyezve:', + 'Task #%d' => '#%d. feladat', + 'Time format' => 'IdÅ‘formátum', + 'Start date: ' => 'Kezdési dátum: ', + 'End date: ' => 'Befejezési dátum: ', + 'New due date: ' => 'Új határidÅ‘: ', + 'Start date changed: ' => 'A kezdési dátum megváltozott: ', + 'Disable personal projects' => 'Személyes projektek letiltása', + 'Do you really want to remove this custom filter: "%s"?' => 'Valóban el szeretné távolítani ezt az egyéni szűrÅ‘t: „%sâ€?', + 'Remove a custom filter' => 'Egyéni szűrÅ‘ eltávolítása', + 'User activated successfully.' => 'A felhasználó sikeresen aktiválva.', + 'Unable to enable this user.' => 'Nem lehet engedélyezni a felhasználót.', + 'User disabled successfully.' => 'A felhasználó sikeresen letiltva.', + 'Unable to disable this user.' => 'Nem lehet letiltani a felhasználót.', + 'All files have been uploaded successfully.' => 'Az összes fájl sikeresen feltöltve.', + 'The maximum allowed file size is %sB.' => 'A legnagyobb megengedett fájlméret %s bájt.', + 'Drag and drop your files here' => 'Fogd-és-vidd módszerrel húzza ide a fájlokat', + 'choose files' => 'fájlok kiválasztása', + 'View profile' => 'Profil megtekintése', + 'Two Factor' => 'KétlépcsÅ‘s', + 'Disable user' => 'Felhasználó letiltása', + 'Do you really want to disable this user: "%s"?' => 'Valóban le szeretné tiltani ezt a felhasználót: „%sâ€?', + 'Enable user' => 'Felhasználó engedélyezése', + 'Do you really want to enable this user: "%s"?' => 'Valóban engedélyezni szeretné ezt a felhasználót: „%sâ€?', + 'Download' => 'Letöltés', + 'Uploaded: %s' => 'Feltöltve: %s', + 'Size: %s' => 'Méret: %s', + 'Uploaded by %s' => 'Feltöltötte: %s', + 'Filename' => 'Fájlnév', + 'Size' => 'Méret', + 'Column created successfully.' => 'Az oszlop sikeresen létrehozva.', + 'Another column with the same name exists in the project' => 'Létezik egy ugyanilyen nevű oszlop a projektben', + 'Default filters' => 'Alapértelmezett szűrÅ‘k', + 'Your board doesn\'t have any columns!' => 'A táblának nincs egyetlen oszlopa sem!', + 'Change column position' => 'Oszlop helyzetének megváltoztatása', + 'Switch to the project overview' => 'Ãtváltás a projekt áttekintÅ‘re', + 'User filters' => 'Felhasználók szűrÅ‘i', + 'Category filters' => 'Kategóriák szűrÅ‘i', + 'Upload a file' => 'Fájl feltöltése', + 'View file' => 'Fájl megtekintése', + 'Last activity' => 'Utolsó tevékenység', + 'Change subtask position' => 'Részfeladat helyzetének megváltoztatása', + 'This value must be greater than %d' => 'Ennek az értéknek nagyobbnak kell lennie mint %d', + 'Another swimlane with the same name exists in the project' => 'Létezik egy ugyanilyen nevű sáv a projektben', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Példa: https://example.kanboard.org/ (abszolút URL-ek előállításához használható)', + 'Actions duplicated successfully.' => 'A műveletek sikeresen megkettÅ‘zve.', + 'Unable to duplicate actions.' => 'Nem lehet megkettÅ‘zni a műveleteket.', + 'Add a new action' => 'Új művelet hozzáadása', + 'Import from another project' => 'Importálás egy másik projektbÅ‘l', + 'There is no action at the moment.' => 'Jelenleg nincs egyetlen művelet sem.', + 'Import actions from another project' => 'Műveletek importálása egy másik projektbÅ‘l', + 'There is no available project.' => 'Nincs elérhetÅ‘ projekt.', + 'Local File' => 'Helyi fájl', + 'Configuration' => 'Konfiguráció', + 'PHP version:' => 'PHP verziója:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Operációs rendszer verziója:', + 'Database version:' => 'Adatbázis verziója:', + 'Browser:' => 'BöngészÅ‘:', + 'Task view' => 'Feladat nézet', + 'Edit task' => 'Feladat szerkesztése', + 'Edit description' => 'Leírás szerkesztése', + 'New internal link' => 'Új belsÅ‘ hivatkozás', + 'Display list of keyboard shortcuts' => 'Gyorsbillentyűk listájának megjelenítése', + 'Avatar' => 'Profilkép', + 'Upload my avatar image' => 'Saját profilkép feltöltése', + 'Remove my image' => 'Saját kép eltávolítása', + 'The OAuth2 state parameter is invalid' => 'Az OAuth2 állapotparaméter érvénytelen', + 'User not found.' => 'A felhasználó nem található.', + 'Search in activity stream' => 'Keresés a tevékenységfolyamban', + 'My activities' => 'Saját tevékenységek', + 'Activity until yesterday' => 'Tevékenységek tegnapig', + 'Activity until today' => 'Tevékenységek a mai napig', + 'Search by creator: ' => 'Keresés a létrehozó szerint: ', + 'Search by creation date: ' => 'Keresés a létrehozás dátuma szerint: ', + 'Search by task status: ' => 'Keresés a feladat állapota szerint: ', + 'Search by task title: ' => 'Keresés a feladat címe szerint: ', + 'Activity stream search' => 'Tevékenységfolyam keresés', + 'Projects where "%s" is manager' => 'Azok a projektek, ahol „%s†vezetÅ‘', + 'Projects where "%s" is member' => 'Azok a projektek, ahol „%s†tag', + 'Open tasks assigned to "%s"' => '„%s†felhasználóhoz rendelt nyitott feladatok', + 'Closed tasks assigned to "%s"' => '„%s†felhasználóhoz rendelt lezárt feladatok', + 'Assign automatically a color based on a priority' => 'Szín automatikus hozzárendelése prioritás alapján', + 'Overdue tasks for the project(s) "%s"' => 'Lejárt feladatok a(z) „%s†projektnél', + 'Upload files' => 'Fájlok feltöltése', + 'Installed Plugins' => 'Telepített bÅ‘vítmények', + 'Plugin Directory' => 'BÅ‘vítmény könyvtár', + 'Plugin installed successfully.' => 'A bÅ‘vítmény sikeresen telepítve.', + 'Plugin updated successfully.' => 'A bÅ‘vítmény sikeresen frissítve.', + 'Plugin removed successfully.' => 'A bÅ‘vítmény sikeresen eltávolítva.', + 'Subtask converted to task successfully.' => 'A részfeladat sikeresen átalakítva feladattá.', + 'Unable to convert the subtask.' => 'Nem lehet átalakítani a részfeladatot.', + 'Unable to extract plugin archive.' => 'Nem lehet kibontani a bÅ‘vítmény archívumot.', + 'Plugin not found.' => 'A bÅ‘vítmény nem található.', + 'You don\'t have the permission to remove this plugin.' => 'Nincs jogosultsága eltávolítani ezt a bÅ‘vítményt.', + 'Unable to download plugin archive.' => 'Nem lehet letölteni a bÅ‘vítmény archívumot.', + 'Unable to write temporary file for plugin.' => 'Nem lehet írni az átmeneti fájlt a bÅ‘vítményhez.', + 'Unable to open plugin archive.' => 'Nem lehet megnyitni a bÅ‘vítmény archívumot.', + 'There is no file in the plugin archive.' => 'Nincs fájl a bÅ‘vítmény archívumban.', + 'Create tasks in bulk' => 'Feladatok tömeges létrehozása', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'A Kanboard példánya úgy lett beállítva, hogy ne lehessen bÅ‘vítményeket telepíteni a felhasználói felületrÅ‘l.', + 'There is no plugin available.' => 'Nincs elérhetÅ‘ bÅ‘vítmény.', + 'Install' => 'Telepítés', + 'Update' => 'Frissítés', + 'Up to date' => 'Naprakész', + 'Not available' => 'Nem érhetÅ‘ el', + 'Remove plugin' => 'BÅ‘vítmény eltávolítása', + 'Do you really want to remove this plugin: "%s"?' => 'Valóban el szeretné távolítani ezt a bÅ‘vítményt: „%sâ€?', + 'Uninstall' => 'Eltávolítás', + 'Listing' => 'Listázás', + 'Metadata' => 'Metaadat', + 'Manage projects' => 'Projektek kezelése', + 'Convert to task' => 'Ãtalakítás feladattá', + 'Convert sub-task to task' => 'Részfeladat átalakítása feladattá', + 'Do you really want to convert this sub-task to a task?' => 'Valóban át szeretné alakítani ezt a részfeladatot feladattá?', + 'My task title' => 'Saját feladatcím', + 'Enter one task by line.' => 'Adjon meg soronként egy feladatot.', + 'Number of failed login:' => 'Sikertelen bejelentkezések száma:', + 'Account locked until:' => 'A fiók zárolva eddig:', + 'Email settings' => 'E-mail beállítások', + 'Email sender address' => 'E-mail küldÅ‘címe', + 'Email transport' => 'E-mail átviteli módja', + 'Webhook token' => 'Webhurok token', + 'Project tags management' => 'Projektcímkék kezelése', + 'Tag created successfully.' => 'A címke sikeresen létrehozva.', + 'Unable to create this tag.' => 'Nem lehet létrehozni a címkét.', + 'Tag updated successfully.' => 'A címke sikeresen frissítve.', + 'Unable to update this tag.' => 'Nem lehet frissíteni a címkét.', + 'Tag removed successfully.' => 'A címke sikeresen eltávolítva.', + 'Unable to remove this tag.' => 'Nem lehet eltávolítani a címkét.', + 'Global tags management' => 'Globális címkék kezelése', + 'Tags' => 'Címkék', + 'Tags management' => 'Címkék kezelése', + 'Add new tag' => 'Új címke hozzáadása', + 'Edit a tag' => 'Címke szerkesztése', + 'Project tags' => 'Projektcímkék', + 'There is no specific tag for this project at the moment.' => 'Jelenleg nincs címke megadva ehhez a projekthez.', + 'Tag' => 'Címke', + 'Remove a tag' => 'Címke eltávolítása', + 'Do you really want to remove this tag: "%s"?' => 'Valóban el szeretné távolítani ezt a címkét: „%sâ€?', + 'Global tags' => 'Globális címkék', + 'There is no global tag at the moment.' => 'Jelenleg nincs globális címke.', + 'This field cannot be empty' => 'Ez a mezÅ‘ nem lehet üres', + 'Close a task when there is no activity in a specific column' => 'Feladat lezárása, ha nincs tevékenység egy adott oszlopban', + '%s removed a subtask for the task #%d' => '%s eltávolított egy részfeladatot a(z) #%d. feladatból', + '%s removed a comment on the task #%d' => '%s eltávolított egy megjegyzést a(z) #%d. feladatból', + 'Comment removed on task #%d' => 'Megjegyzés eltávolítva a(z) #%d. feladatból', + 'Subtask removed on task #%d' => 'Részfeladat eltávolítva a(z) #%d. feladatból', + 'Hide tasks in this column in the dashboard' => 'Feladatok elrejtése ebben az oszlopban a vezérlÅ‘pulton', + '%s removed a comment on the task %s' => '%s eltávolított egy megjegyzést a(z) %s feladatból', + '%s removed a subtask for the task %s' => '%s eltávolított egy részfeladatot a(z) %s feladatból', + 'Comment removed' => 'Megjegyzés eltávolítva', + 'Subtask removed' => 'Részfeladat eltávolítva', + '%s set a new internal link for the task #%d' => '%s beállított egy új belsÅ‘ hivatkozást a(z) #%d. feladathoz', + '%s removed an internal link for the task #%d' => '%s eltávolított egy belsÅ‘ hivatkozást a(z) #%d. feladatból', + 'A new internal link for the task #%d has been defined' => 'Új belsÅ‘ hivatkozás lett meghatározva a(z) #%d. feladathoz', + 'Internal link removed for the task #%d' => 'BelsÅ‘ hivatkozás eltávolítva a(z) #%d. feladatból', + '%s set a new internal link for the task %s' => '%s beállított egy új belsÅ‘ hivatkozást a(z) %s feladathoz', + '%s removed an internal link for the task %s' => '%s eltávolított egy belsÅ‘ hivatkozást a(z) %s feladatból', + 'Automatically set the due date on task creation' => 'A határidÅ‘ automatikus beállítása a feladat létrehozásakor', + 'Move the task to another column when closed' => 'Feladat áthelyezése egy másik oszlopba, ha lezárják', + 'Move the task to another column when not moved during a given period' => 'Feladat áthelyezése egy másik oszlopba, ha nincs áthelyezve egy adott ideig', + 'Dashboard for %s' => '%s vezérlÅ‘pultja', + 'Tasks overview for %s' => 'Feladatok áttekintése: %s', + 'Subtasks overview for %s' => 'Részfeladatok áttekintése: %s', + 'Projects overview for %s' => 'Projektek áttekintése: %s', + 'Activity stream for %s' => '%s tevékenységfolyama', + 'Assign a color when the task is moved to a specific swimlane' => 'Szín hozzárendelése, ha a feladatot áthelyezik egy adott sávba', + 'Assign a priority when the task is moved to a specific swimlane' => 'Prioritás hozzárendelése, ha a feladatot áthelyezik egy adott sávba', + 'User unlocked successfully.' => 'A felhasználó sikeresen feloldva.', + 'Unable to unlock the user.' => 'Nem lehet feloldani a felhasználót.', + 'Move a task to another swimlane' => 'Feladat áthelyezése egy másik sávba', + 'Creator Name' => 'Létrehozó neve', + 'Time spent and estimated' => 'Eltöltött és becsült idÅ‘', + 'Move position' => 'Pozíció áthelyezése', + 'Move task to another position on the board' => 'Feladat áthelyezése egy másik helyre a táblán', + 'Insert before this task' => 'Beszúrás a feladat elé', + 'Insert after this task' => 'Beszúrás a feladat után', + 'Unlock this user' => 'Felhasználó feloldása', + 'Custom Project Roles' => 'Egyéni projektszerepek', + 'Add a new custom role' => 'Új egyéni szerep hozzáadása', + 'Restrictions for the role "%s"' => 'Korlátozások a(z) „%s†szerepnél', + 'Add a new project restriction' => 'Új projektkorlátozás hozzáadása', + 'Add a new drag and drop restriction' => 'Új fogd-és-vidd korlátozás hozzáadása', + 'Add a new column restriction' => 'Új oszlopkorlátozás hozzáadása', + 'Edit this role' => 'Szerep szerkesztése', + 'Remove this role' => 'Szerep eltávolítása', + 'There is no restriction for this role.' => 'Nincs korlátozás ennél a szerepnél.', + 'Only moving task between those columns is permitted' => 'Az oszlopok között csak áthelyezés megengedett', + 'Close a task in a specific column when not moved during a given period' => 'Feladat lezárása egy adott oszlopban, ha nem helyezték át egy adott ideig', + 'Edit columns' => 'Oszlopok szerkesztése', + 'The column restriction has been created successfully.' => 'Az oszlopkorlátozás sikeresen létrehozva.', + 'Unable to create this column restriction.' => 'Nem lehet létrehozni az oszlopkorlátozást.', + 'Column restriction removed successfully.' => 'Az oszlopkorlátozás sikeresen eltávolítva.', + 'Unable to remove this restriction.' => 'Nem lehet eltávolítani a korlátozást.', + 'Your custom project role has been created successfully.' => 'Az egyéni projektszerep sikeresen létrehozva.', + 'Unable to create custom project role.' => 'Nem lehet létrehozni az egyéni projektszerepet.', + 'Your custom project role has been updated successfully.' => 'Az egyéni projektszerep sikeresen frissítve.', + 'Unable to update custom project role.' => 'Nem lehet frissíteni az egyéni projektszerepet.', + 'Custom project role removed successfully.' => 'Az egyéni projektszerep sikeresen eltávolítva.', + 'Unable to remove this project role.' => 'Nem lehet eltávolítani a projektszerepet.', + 'The project restriction has been created successfully.' => 'A projektkorlátozás sikeresen létrehozva.', + 'Unable to create this project restriction.' => 'Nem lehet létrehozni a projektkorlátozást.', + 'Project restriction removed successfully.' => 'A projektkorlátozás sikeresen eltávolítva.', + 'You cannot create tasks in this column.' => 'Nem lehet feladatokat létrehozni ebben az oszlopban.', + 'Task creation is permitted for this column' => 'A feladat létrehozása engedélyezett ennél az oszlopnál', + 'Closing or opening a task is permitted for this column' => 'A feladat lezárása vagy megnyitása engedélyezett ennél az oszlopnál', + 'Task creation is blocked for this column' => 'A feladat létrehozása blokkolva van ennél az oszlopnál', + 'Closing or opening a task is blocked for this column' => 'A feladat lezárása vagy megnyitása blokkolva van ennél az oszlopnál', + 'Task creation is not permitted' => 'A feladat létrehozása nem engedélyezett', + 'Closing or opening a task is not permitted' => 'Feladat lezárása vagy megnyitása nem engedélyezett', + 'New drag and drop restriction for the role "%s"' => 'Új fogd-és-vidd korlátozás a(z) „%s†szerephez', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Az ehhez a szerephez tartozó emberek csak a forrás- és a céloszlopok között helyezhetnek át feladatokat.', + 'Remove a column restriction' => 'Oszlopkorlátozás eltávolítása', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Valóban el szeretné távolítani ezt az oszlopkorlátozást: „%s†erre: „%sâ€?', + 'New column restriction for the role "%s"' => 'Új oszlopkorlátozás a(z) „%s†szerephez', + 'Rule' => 'Szabály', + 'Do you really want to remove this column restriction?' => 'Valóban el szeretné távolítani ezt az oszlopkorlátozást?', + 'Custom roles' => 'Egyéni szerepek', + 'New custom project role' => 'Új egyéni projektszerep', + 'Edit custom project role' => 'Egyéni projektszerep szerkesztése', + 'Remove a custom role' => 'Egyéni szerep eltávolítása', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Valóban el szeretné távolítani ezt az egyéni szerepet: „%sâ€? A szerephez rendelt összes ember projekttaggá válik.', + 'There is no custom role for this project.' => 'Nincs egyéni szerep ennél a projektnél.', + 'New project restriction for the role "%s"' => 'Új projektkorlátozás a(z) „%s†szerephez', + 'Restriction' => 'Korlátozás', + 'Remove a project restriction' => 'Projektkorlátozás eltávolítása', + 'Do you really want to remove this project restriction: "%s"?' => 'Valóban el szeretné távolítani ezt a projektkorlátozást: „%sâ€?', + 'Duplicate to multiple projects' => 'Másolás több projektbe', + 'This field is required' => 'Ez a mezÅ‘ kötelezÅ‘', + 'Moving a task is not permitted' => 'A feladat áthelyezése nem engedélyezett', + 'This value must be in the range %d to %d' => 'Ennek az értéknek %d és %d közötti tartományban kell lennie', + 'You are not allowed to move this task.' => 'A feladat áthelyezése nem engedélyezett.', + 'API User Access' => 'API felhasználói hozzáférés', + 'Preview' => 'ElÅ‘nézet', + 'Write' => 'Ãrás', + 'Write your text in Markdown' => 'Ãrja be a szöveget Markdown formátumban', + 'No personal API access token registered.' => 'Nincs személyes API hozzáférési token regisztrálva.', + 'Your personal API access token is "%s"' => 'A személyes API hozzáférési token: „%sâ€', + 'Remove your token' => 'Token eltávolítása', + 'Generate a new token' => 'Új token előállítása', + 'Showing %d-%d of %d' => '%d-%d / %d megjelenítése', + 'Outgoing Emails' => 'KimenÅ‘ e-mailek', + 'Add or change currency rate' => 'Devizaárfolyam hozzáadása vagy megváltoztatása', + 'Reference currency: %s' => 'Bázis pénznem: %s', + 'Add custom filters' => 'Egyéni szűrÅ‘k hozzáadása', + 'Export' => 'Exportálás', + 'Add link label' => 'Hivatkozási címke hozzáadása', + 'Incompatible Plugins' => 'Összeférhetetlen bÅ‘vítmények', + 'Compatibility' => 'ÖsszeférhetÅ‘ség', + 'Permissions and ownership' => 'Jogosultságok és tulajdonjog', + 'Priorities' => 'Prioritások', + 'Close this window' => 'Ablak bezárása', + 'Unable to upload this file.' => 'Nem lehet feltölteni a fájlt.', + 'Import tasks' => 'Feladatok importálása', + 'Choose a project' => 'Projekt kiválasztása', + 'Profile' => 'Profil', + 'Application role' => 'Alkalmazás szerep', + '%d invitations were sent.' => '%d meghívás elküldve.', + '%d invitation was sent.' => '%d meghívás elküldve.', + 'Unable to create this user.' => 'Nem lehet létrehozni a felhasználót.', + 'Kanboard Invitation' => 'Kanboard meghívás', + 'Visible on dashboard' => 'Látható a vezérlÅ‘pulton', + 'Created at:' => 'Létrehozva:', + 'Updated at:' => 'Frissítve:', + 'There is no custom filter.' => 'Nincs egyéni szűrÅ‘.', + 'New User' => 'Új felhasználó', + 'Authentication' => 'Hitelesítés', + 'If checked, this user will use a third-party system for authentication.' => 'Ha be van jelölve, akkor ez a felhasználó harmadik fél rendszerét fogja használni a hitelesítéshez.', + 'The password is necessary only for local users.' => 'A jelszó csak helyi felhasználóknak szükséges.', + 'You have been invited to register on Kanboard.' => 'Meghívták, hogy regisztráljon a Kanboardra.', + 'Click here to join your team' => 'Kattintson ide a csapathoz való csatlakozáshoz', + 'Invite people' => 'Emberek meghívása', + 'Emails' => 'E-mailek', + 'Enter one email address by line.' => 'Adjon meg soronként egy e-mail-címet.', + 'Add these people to this project' => 'Az emberek hozzáadása a projekthez', + 'Add this person to this project' => 'A személy hozzáadása a projekthez', + 'Sign-up' => 'Regisztráció', + 'Credentials' => 'Hitelesítési adatok', + 'New user' => 'Új felhasználó', + 'This username is already taken' => 'Ez a felhasználónév már foglalt', + 'Your profile must have a valid email address.' => 'A profiljának érvényes e-mail-címmel kell rendelkeznie.', + 'TRL - Turkish Lira' => 'TRL – török líra', + 'The project email is optional and could be used by several plugins.' => 'A projekt e-mail elhagyható, és több bÅ‘vítmény is használhatja.', + 'The project email must be unique across all projects' => 'A projekt e-mailnek egyedinek kell lennie a projektek között', + 'The email configuration has been disabled by the administrator.' => 'Az e-mail beállításait letiltotta az adminisztrátor.', + 'Close this project' => 'Projekt bezárása', + 'Open this project' => 'Projekt megnyitása', + 'Close a project' => 'Projekt bezárása', + 'Do you really want to close this project: "%s"?' => 'Valóban le szeretné zárni ezt a projektet: „%sâ€?', + 'Reopen a project' => 'Projekt újranyitása', + 'Do you really want to reopen this project: "%s"?' => 'Valóban újra szeretné nyitni ezt a projektet: „%sâ€?', + 'This project is open' => 'A projekt nyitva van', + 'This project is closed' => 'A projekt le van zárva', + 'Unable to upload files, check the permissions of your data folder.' => 'Nem lehet feltölteni fájlokat. EllenÅ‘rizze az adatkönyvtár jogosultságait.', + 'Another category with the same name exists in this project' => 'Már létezik egy ugyanilyen nevű kategória ebben a projektben', + 'Comment sent by email successfully.' => 'A megjegyzés sikeresen elküldve e-mailben.', + 'Sent by email to "%s" (%s)' => 'E-mail küldve neki: „%s†(%s)', + 'Unable to read uploaded file.' => 'Nem lehet beolvasni a feltöltött fájlt.', + 'Database uploaded successfully.' => 'Az adatbázis sikeresen feltöltve.', + 'Task sent by email successfully.' => 'A feladat sikeresen elküldve e-mailben.', + 'There is no category in this project.' => 'Nincs kategória ebben a projektben.', + 'Send by email' => 'Küldés e-mailben', + 'Create and send a comment by email' => 'Megjegyzés létrehozása és küldése e-mailben', + 'Subject' => 'Tárgy', + 'Upload the database' => 'Az adatbázis feltöltése', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Feltöltheti az elÅ‘zÅ‘leg letöltött Sqlite adatbázist (Gzip formátumban).', + 'Database file' => 'Adatbázisfájl', + 'Upload' => 'Feltöltés', + 'Your project must have at least one active swimlane.' => 'A projektnek legalább egy aktív sávval kell rendelkeznie.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Az automatikus művelet nem található: „%sâ€', + '%d projects' => '%d projekt', + '%d project' => '%d projekt', + 'There is no project.' => 'Nincs projekt.', + 'Sort' => 'Rendezés', + 'Project ID' => 'Projektazonosító', + 'Project name' => 'Projektnév', + 'Public' => 'Nyilvános', + 'Personal' => 'Személyes', + '%d tasks' => '%d feladat', + '%d task' => '%d feladat', + 'Task ID' => 'Feladatazonosító', + 'Assign automatically a color when due date is expired' => 'Szín automatikus hozzárendelése, ha a határidÅ‘ lejárt', + 'Total score in this column across all swimlanes' => 'Összes pontszám ebben az oszlopban minden sávból', + 'HRK - Kuna' => 'HRK – horvát kuna', + 'ARS - Argentine Peso' => 'ARS – argentin peso', + 'COP - Colombian Peso' => 'COP – kolumbiai peso', + '%d groups' => '%d csoport', + '%d group' => '%d csoport', + 'Group ID' => 'Csoportazonosító', + 'External ID' => 'KülsÅ‘ azonosító', + '%d users' => '%d felhasználó', + '%d user' => '%d felhasználó', + 'Hide subtasks' => 'Részfeladatok elrejtése', + 'Show subtasks' => 'Részfeladatok megjelenítése', + 'Authentication Parameters' => 'Hitelesítési paraméterek', + 'API Access' => 'API hozzáférés', + 'No users found.' => 'Nem találhatók felhasználók.', + 'User ID' => 'Felhasználó-azonosító', + 'Notifications are activated' => 'Értesítések aktiválva', + 'Notifications are disabled' => 'Értesítések letiltva', + 'User disabled' => 'Felhasználó letiltva', + '%d notifications' => '%d értesítés', + '%d notification' => '%d értesítés', + 'There is no external integration installed.' => 'Nincs külsÅ‘ integráció telepítve.', + 'You are not allowed to update tasks assigned to someone else.' => 'Nem engedélyezett a valaki máshoz rendelt feladatok frissítése.', + 'You are not allowed to change the assignee.' => 'Nem engedélyezett a felelÅ‘s megváltoztatása.', + 'Task suppression is not permitted' => 'A feladatok eltávolítása nem engedélyezett', + 'Changing assignee is not permitted' => 'A felelÅ‘s megváltoztatása nem engedélyezett', + 'Update only assigned tasks is permitted' => 'Csak a hozzárendelt feladatok frissítése engedélyezett', + 'Only for tasks assigned to the current user' => 'Csak az aktuális felhasználóhoz rendelt feladatoknál', + 'My projects' => 'Saját projektek', + 'You are not a member of any project.' => 'Ön nem tagja egyetlen projektnek sem.', + 'My subtasks' => 'Saját részfeladatok', + '%d subtasks' => '%d részfeladat', + '%d subtask' => '%d részfeladat', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Az aktuális felhasználóhoz rendelt feladatok csak az engedélyezett oszlopok között helyezhetÅ‘k át', + '[DUPLICATE]' => '[KETTÅZÖTT]', + 'DKK - Danish Krona' => 'DKK – dán korona', + 'Remove user from group' => 'Felhasználó eltávolítása a csoportból', + 'Assign the task to its creator' => 'Feladat hozzárendelése a létrehozójához', + 'This task was sent by email to "%s" with subject "%s".' => 'A feladat elküldésre került e-mailben ide: „%sâ€, a következÅ‘ tárggyal: „%sâ€.', + 'Predefined Email Subjects' => 'ElÅ‘re meghatározott e-mail tárgyak', + 'Write one subject by line.' => 'Ãrjon be soronként egy tárgyat.', + 'Create another link' => 'Másik hivatkozás létrehozása', + 'BRL - Brazilian Real' => 'BRL – brazil real', + 'Add a new Kanboard task' => 'Új Kanboard feladat hozzáadása', + 'Subtask not started' => 'A részfeladat nincs elindítva', + 'Subtask currently in progress' => 'A részfeladat jelenleg folyamatban van', + 'Subtask completed' => 'A részfeladat befejezÅ‘dött', + 'Subtask added successfully.' => 'A részfeladat sikeresen hozzáadva.', + '%d subtasks added successfully.' => '%d részfeladat sikeresen hozzáadva.', + 'Enter one subtask by line.' => 'Adjon meg soronként egy részfeladatot.', + 'Predefined Contents' => 'ElÅ‘re meghatározott tartalmak', + 'Predefined contents' => 'ElÅ‘re meghatározott tartalmak', + 'Predefined Task Description' => 'ElÅ‘re meghatározott feladatleírás', + 'Do you really want to remove this template? "%s"' => 'Valóban el szeretné távolítani ezt a sablont: „%sâ€?', + 'Add predefined task description' => 'ElÅ‘re meghatározott feladatleírás hozzáadása', + 'Predefined Task Descriptions' => 'ElÅ‘re meghatározott feladatleírások', + 'Template created successfully.' => 'A sablon sikeresen létrehozva.', + 'Unable to create this template.' => 'Nem lehet létrehozni a sablont.', + 'Template updated successfully.' => 'A sablon sikeresen frissítve.', + 'Unable to update this template.' => 'Nem lehet frissíteni a sablont.', + 'Template removed successfully.' => 'A sablon sikeresen eltávolítva.', + 'Unable to remove this template.' => 'Nem lehet eltávolítani a sablont.', + 'Template for the task description' => 'Sablon a feladatleíráshoz', + 'The start date is greater than the end date' => 'A kezdési dátum nagyobb mint a befejezési dátum', + 'Tags must be separated by a comma' => 'A címkéket vesszÅ‘vel kell elválasztani', + 'Only the task title is required' => 'Csak a feladat címe kötelezÅ‘', + 'Creator Username' => 'Létrehozó felhasználóneve', + 'Color Name' => 'Szín neve', + 'Column Name' => 'Oszlop neve', + 'Swimlane Name' => 'Sáv neve', + 'Time Estimated' => 'Becsült idÅ‘', + 'Time Spent' => 'Eltöltött idÅ‘', + 'External Link' => 'KülsÅ‘ hivatkozás', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Ez a funkció engedélyezi az iCal hírforrást, az RSS hírforrást és a nyilvános táblanézetet.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Az összes részfeladat idÅ‘mérÅ‘jének leállítása, ha a feladatot egy másik oszlopba helyezik át', + 'Subtask Title' => 'Részfeladat címe', + 'Add a subtask and activate the timer when moving a task to another column' => 'Részfeladat hozzáadása és az idÅ‘mérÅ‘ bekapcsolása, ha a feladatot egy másik oszlopba helyezik át', + 'days' => 'nap', + 'minutes' => 'perc', + 'seconds' => 'másodperc', + 'Assign automatically a color when preset start date is reached' => 'Szín automatikus hozzárendelése, ha az elÅ‘re beállított kezdési dátumot elért', + 'Move the task to another column once a predefined start date is reached' => 'Feladat áthelyezése egy másik oszlopba, ha egy elÅ‘re meghatározott kezdési dátumot elért', + 'This task is now linked to the task %s with the relation "%s"' => 'Ez a feladat mostantól hozzá van kapcsolva a(z) %s feladathoz „%s†kapcsolattal', + 'The link with the relation "%s" to the task %s has been removed' => 'A(z) „%s†kapcsolattal rendelkezÅ‘ összekapcsolás a(z) %s feladatnál el lett távolítva', + 'Custom Filter:' => 'Egyéni szűrÅ‘:', + 'Unable to find this group.' => 'Nem található ez a csoport.', + '%s moved the task #%d to the column "%s"' => '%s áthelyezte a(z) #%d. feladatot a következÅ‘ oszlopba: „%sâ€', + '%s moved the task #%d to the position %d in the column "%s"' => '%s áthelyezte a(z) #%d. feladatot a(z) %d. pozícióba a következÅ‘ oszlopban: „%sâ€', + '%s moved the task #%d to the swimlane "%s"' => '%s áthelyezte a(z) #%d. feladatot a következÅ‘ sávba: „%sâ€', + '%sh spent' => '%s óra eltöltve', + '%sh estimated' => '%s óra becsülve', + 'Select All' => 'Összes kijelölése', + 'Unselect All' => 'Összes kijelölésének megszüntetése', + 'Apply action' => 'Művelet alkalmazása', + 'Move selected tasks to another column or swimlane' => 'Kijelölt feladatok áthelyezése egy másik oszlopba vagy sávba', + 'Edit tasks in bulk' => 'Feladatok tömeges szerkesztése', + 'Choose the properties that you would like to change for the selected tasks.' => 'Válassza ki azokat a tulajdonságokat, amelyeket meg szeretne változtatni a kijelölt feladatoknál.', + 'Configure this project' => 'A projekt beállítása', + 'Start now' => 'Kezdés most', + '%s removed a file from the task #%d' => '%s eltávolított egy fájlt a(z) #%d. feladatból', + 'Attachment removed from task #%d: %s' => 'Melléklet eltávolítva a(z) #%d. feladatból: %s', + 'No color' => 'Nincs szín', + 'Attachment removed "%s"' => 'Melléklet eltávolítva: „%sâ€', + '%s removed a file from the task %s' => '%s eltávolított egy fájlt a(z) %s feladatból', + 'Move the task to another swimlane when assigned to a user' => 'Feladat áthelyezése egy másik sávba, ha hozzárendelésre kerül egy felhasználóhoz', + 'Destination swimlane' => 'Célsáv', + 'Assign a category when the task is moved to a specific swimlane' => 'Kategória hozzárendelése, ha a feladatot egy bizonyos sávba helyezik át', + 'Move the task to another swimlane when the category is changed' => 'Feladat áthelyezése egy másik sávba, ha a kategória megváltozik', + 'Reorder this column by priority (ASC)' => 'Oszlop átrendezése prioritás szerint (növekvÅ‘)', + 'Reorder this column by priority (DESC)' => 'Oszlop átrendezése prioritás szerint (csökkenÅ‘)', + 'Reorder this column by assignee and priority (ASC)' => 'Oszlop átrendezése felelÅ‘s és prioritás szerint (növekvÅ‘)', + 'Reorder this column by assignee and priority (DESC)' => 'Oszlop átrendezése felelÅ‘s és prioritás szerint (csökkenÅ‘)', + 'Reorder this column by assignee (A-Z)' => 'Oszlop átrendezése felelÅ‘s szerint (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Oszlop átrendezése felelÅ‘s szerint (Z-A)', + 'Reorder this column by due date (ASC)' => 'Oszlop átrendezése határidÅ‘ szerint (növekvÅ‘)', + 'Reorder this column by due date (DESC)' => 'Oszlop átrendezése határidÅ‘ szerint (csökkenÅ‘)', + 'Reorder this column by id (ASC)' => 'Oszlop átrendezése azonosító szerint (növekvÅ‘)', + 'Reorder this column by id (DESC)' => 'Oszlop átrendezése azonosító szerint (csökkenÅ‘)', + '%s moved the task #%d "%s" to the project "%s"' => '%s áthelyezte a(z) #%d. „%s†feladatot ebbe a projektbe: „%sâ€', + 'Task #%d "%s" has been moved to the project "%s"' => 'A(z) #%d. „%s†feladat áthelyezésre került ebbe a projektbe: „%sâ€', + 'Move the task to another column when the due date is less than a certain number of days' => 'A feladat áthelyezése egy másik oszlopba, ha a határidÅ‘ kevesebb egy bizonyos számú napnál', + 'Automatically update the start date when the task is moved away from a specific column' => 'A kezdési dátum automatikus frissítése, ha a feladatot áthelyezték egy bizonyos oszlopból', + 'HTTP Client:' => 'HTTP ügyfél:', + 'Assigned' => 'Hozzárendelve', + 'Task limits apply to each swimlane individually' => 'A feladatkorlátok alkalmazása az egyes sávokra egyénileg', + 'Column task limits apply to each swimlane individually' => 'Az oszlop feladatkorlátjainak alkalmazása az egyes sávokra egyénileg', + 'Column task limits are applied to each swimlane individually' => 'Az oszlop feladatkorlátjai alkalmazva lettek az egyes sávokra egyénileg', + 'Column task limits are applied across swimlanes' => 'Az oszlop feladatkorlátjai alkalmazva lettek a sávok között', + 'Task limit: ' => 'Feladatkorlát: ', + 'Change to global tag' => 'Megváltoztatás globális címkére', + 'Do you really want to make the tag "%s" global?' => 'Valóban globálissá szeretné tenni a(z) „%s†címkét?', + 'Enable global tags for this project' => 'Globális címkék engedélyezése ennél a projektnél', + 'Group membership(s):' => 'Csoporttagságok:', + '%s is a member of the following group(s): %s' => '%s a következÅ‘ csoportok tagja: %s', + '%d/%d group(s) shown' => '%d/%d csoport megjelenítve', + 'Subtask creation or modification' => 'Részfeladat létrehozása vagy módosítása', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'A feladat hozzárendelése egy adott felhasználóhoz, ha a feladatot áthelyezik egy adott sávba', + 'Comment' => 'Megjegyzés', + 'Collapse vertically' => 'Összecsukás függÅ‘legesen', + 'Expand vertically' => 'Kinyitás függÅ‘legesen', + 'MXN - Mexican Peso' => 'MXN – mexikói peso', + 'Estimated vs actual time per column' => 'Becsült és tényleges idÅ‘ oszloponként', + 'HUF - Hungarian Forint' => 'HUF – magyar forint', + 'XBT - Bitcoin' => 'XBT – bitcoin', + 'You must select a file to upload as your avatar!' => 'Ki kell választania egy fájlt a profilképként való feltöltéshez!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'A feltöltött fájl nem érvényes kép! Csak *.gif, *.jpg, *.jpeg és *.png engedélyezett.', + 'Automatically set the due date when the task is moved away from a specific column' => 'A határidÅ‘ automatikus beállítása, ha a feladatot áthelyezik egy adott oszlopból', + 'No other projects found.' => 'Nem találhatók más projektek.', + 'Tasks copied successfully.' => 'A feladatok sikeresen másolva.', + 'Unable to copy tasks.' => 'Nem lehet másolni a feladatokat', + 'Theme' => 'Téma', + 'Theme:' => 'Téma:', + 'Light theme' => 'Világos téma', + 'Dark theme' => 'Sötét téma', + 'Automatic theme - Sync with system' => 'Automatikus téma – szinkronizálás a rendszerrel', + 'Application managers or more' => 'AlkalmazáskezelÅ‘k vagy továbbiak', + 'Administrators' => 'Adminisztrátorok', + 'Visibility:' => 'Láthatóság', + 'Standard users' => 'Szabványos felhasználók', + 'Visibility is required' => 'A láthatóság kötelezÅ‘', + 'The visibility should be an app role' => 'A láthatóságnak alkalmazásszerepnek kell lennie', + 'Reply' => 'Válasz', + '%s wrote: ' => '%s írta: ', + 'Number of visible tasks in this column and swimlane' => 'Látható feladatok száma ebben az oszlopban és sávban', + 'Number of tasks in this swimlane' => 'Feladatok száma ebben a sávban', + 'Unable to find another subtask in progress, you can close this window.' => 'Nem található másik folyamatban lévÅ‘ alerÅ‘forrás, bezárhatja ezt az ablakot.', + 'This theme is invalid' => 'Ez a téma érvénytelen', + 'This role is invalid' => 'Ez a szerepkör érvénytelen', + 'This timezone is invalid' => 'Ez az idÅ‘zóna érvénytelen', + 'This language is invalid' => 'Ez a nyelv érvénytelen', + 'This URL is invalid' => 'Ez az URL érvénytelen', + 'Date format invalid' => 'Érvénytelen dátumformátum', + 'Time format invalid' => 'Érvénytelen idÅ‘formátum', + 'Invalid Mail transport' => 'Érvénytelen levélátvitel', + 'Color invalid' => 'Érvénytelen szín', + 'This value must be greater or equal to %d' => 'Ennek az értéknek nagyobbnak vagy egyenlÅ‘nek kell lennie, mint %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'BOM hozzáadása a fájl elejéhez (kötelezÅ‘ a Microsoft Excel számára)', + 'Just add these tag(s)' => 'Csak adja hozzá ezeket a címkéket', + 'Remove internal link(s)' => 'BelsÅ‘ hivatkozás(ok) eltávolítása', + 'Import tasks from another project' => 'Feladatok importálása másik projektbÅ‘l', + 'Select the project to copy tasks from' => 'Válassza ki a projektet, ahonnan a feladatokat másolni szeretné', + 'The total maximum allowed attachments size is %sB.' => 'A mellékletek maximális megengedett mérete %sB.', + 'Add attachments' => 'Mellékletek hozzáadása', + 'Task #%d "%s" is overdue' => 'Zadatak #%d "%s" je s izteÄen rok', + 'Enable notifications by default for all new users' => 'Értesítések alapértelmezett engedélyezése minden új felhasználó számára', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'A feladat hozzárendelése a létrehozójához meghatározott oszlopokban, ha nincs kézzel beállított felelÅ‘s', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'A feladat hozzárendelése a bejelentkezett felhasználóhoz oszlopváltáskor a megadott oszlopba, ha nincs hozzárendelt felhasználó', +]; diff --git a/app/Locale/id_ID/translations.php b/app/Locale/id_ID/translations.php new file mode 100644 index 0000000..a85534e --- /dev/null +++ b/app/Locale/id_ID/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Tidak satupun', + 'Edit' => 'Edit', + 'Remove' => 'Hapus', + 'Yes' => 'Ya', + 'No' => 'Tidak', + 'cancel' => 'batal', + 'or' => 'atau', + 'Yellow' => 'Kuning', + 'Blue' => 'Biru', + 'Green' => 'Hijau', + 'Purple' => 'Ungu', + 'Red' => 'Merah', + 'Orange' => 'Jingga', + 'Grey' => 'Abu-abu', + 'Brown' => 'Coklat', + 'Deep Orange' => 'Oranye', + 'Dark Grey' => 'Abu-abu Gelap', + 'Pink' => 'Merah Muda', + 'Teal' => 'Teal', + 'Cyan' => 'Sian', + 'Lime' => 'Lime', + 'Light Green' => 'Hijau Muda', + 'Amber' => 'Amber', + 'Save' => 'Simpan', + 'Login' => 'Masuk', + '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 mau menghapus pengguna ini: "%s"?', + 'All users' => 'Semua pengguna', + 'Username' => 'Nama pengguna', + 'Password' => 'Kata sandi', + 'Administrator' => 'Administrator', + 'Sign in' => 'Masuk', + 'Users' => 'Pengguna', + 'Forbidden' => 'Terlarang', + 'Access Forbidden' => 'Akses Dilarang', + 'Edit user' => 'Edit pengguna', + 'Logout' => 'Keluar', + 'Bad username or password' => 'Nama pengguna atau password salah', + 'Edit project' => 'Edit proyek', + 'Name' => 'Nama', + 'Projects' => 'Proyek', + 'No project' => 'Tidak ada proyek', + 'Project' => 'Proyek', + 'Status' => 'Status', + 'Tasks' => 'Tugas', + 'Board' => 'Papan', + 'Actions' => 'Tindakan', + 'Inactive' => 'Non Aktif', + 'Active' => 'Aktif', + '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 mau menghapus proyek ini: "%s"?', + 'Remove project' => 'Hapus proyek', + 'Edit the board for "%s"' => 'Edit papan untuk "%s"', + 'Add a new column' => 'Tambah kolom baru', + 'Title' => 'Judul', + 'Assigned to %s' => 'Ditugaskan kepada %s', + 'Remove a column' => 'Hapus kolom', + 'Unable to remove this column.' => 'Tidak dapat menghapus kolom ini.', + 'Do you really want to remove this column: "%s"?' => 'Apakah Anda yakin mau menghapus kolom ini: "%s"?', + 'Settings' => 'Pengaturan', + 'Application settings' => 'Pengaturan aplikasi', + 'Language' => 'Bahasa', + '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', + '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 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:', + '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 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' => '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' => '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 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 database selesai.', + 'Your project has been created successfully.' => 'Proyek anda berhasil dibuat.', + 'Unable to create your project.' => 'Tidak dapat membuat proyek anda.', + '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 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.', + 'Unable to open this task.' => 'Tidak dapat membuka tugas ini.', + '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 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 diperbarui.', + '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', + 'Public link' => 'Tautan publik', + 'Timezone' => 'Zona waktu', + '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 count' => 'Jumlah tugas', + 'User' => 'Pengguna', + 'Comments' => 'Komentar', + 'Comment is required' => 'Komentar dibutuhkan', + 'Comment added successfully.' => 'Komentar berhasil ditambahkan.', + 'Unable to create your comment.' => 'Tidak dapat menambahkan komentar Anda.', + 'Due Date' => 'Batas Tanggal Terakhir', + 'Invalid date' => 'Tanggal tidak sesuai', + 'Automatic actions' => 'Tindakan otomatis', + 'Your automatic action has 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.', + 'Action removed successfully.' => 'Tindakan berhasil dihapus.', + 'Automatic actions for the project "%s"' => 'Tindakan otomatis untuk proyek ini "%s"', + 'Add an action' => 'Tambah tindakan', + 'Event name' => 'Nama acara', + 'Action' => 'Tindakan', + 'Event' => 'Acara', + '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 mau menghapus tindakan ini: "%s"?', + 'Remove an automatic action' => 'Hapus tindakan otomatis', + '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', + 'Task creation' => 'Membuat tugas', + 'Closing a task' => 'Menutup tugas', + 'Assign a color to a specific user' => 'Menetapkan warna untuk pengguna tertentu', + 'Position' => 'Posisi', + 'Duplicate to project' => 'Duplikasi ke proyek lain', + 'Duplicate' => 'Duplikat', + 'Link' => 'tautan', + '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 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 tetap', + 'No session.' => 'Tidak ada sesi.', + 'Expiration date' => 'Tanggal kadaluarsa', + 'Remember Me' => 'Ingat Saya', + 'Creation date' => 'Tanggal pembuatan', + 'Everybody' => 'Semua orang', + 'Open' => 'Terbuka', + 'Closed' => 'Ditutup', + 'Search' => 'Cari', + 'Nothing found.' => 'Tidak ditemukan.', + 'Due date' => 'Batas tanggal terakhir', + 'Description' => 'Deskripsi', + '%d comments' => '%d komentar', + '%d comment' => '%d komentar', + '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.', + '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 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 modifikasi', + 'Category' => 'Kategori', + 'Category:' => 'Kategori:', + 'Categories' => 'Kategori', + 'Your category has been created successfully.' => 'Kategori Anda berhasil dibuat.', + 'This category has been updated successfully.' => 'Kategori Anda berhasil diperbarui.', + 'Unable to update this 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 Name' => 'Nama Kategori', + 'Add a new category' => 'Tambah kategori baru', + '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', + 'Remove a file' => 'Hapus berkas', + '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"?', + 'Attachments' => 'Lampiran', + 'Edit the task' => 'Edit tugas', + 'Add a comment' => 'Tambahkan komentar', + 'Edit a comment' => 'Edit komentar', + 'Summary' => 'Ringkasan', + 'Time tracking' => 'Pelacakan waktu', + 'Estimate:' => 'Estimasi:', + 'Spent:' => 'Menghabiskan:', + 'Do you really want to remove this sub-task?' => 'Apakah Anda yakin mau menghapus sub-tugas ini?', + 'Remaining:' => 'Tersisa:', + 'hours' => 'jam', + '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' => 'Edit sub-tugas', + 'Remove a sub-task' => 'Hapus sub-tugas', + 'The time must be a numeric value' => 'Waktu harus berupa angka', + 'Todo' => 'Yang harus dilakukan', + '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 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.', + 'Maximum size: ' => 'Ukuran maksimum: ', + 'Display another project' => 'Lihat proyek lain', + 'Created by %s' => 'Dibuat oleh %s', + 'Tasks Export' => 'Ekspor Tugas', + 'Start Date' => 'Tanggal Mulai', + 'Execute' => 'Eksekusi', + 'Task Id' => 'ID Tugas', + 'Creator' => 'Pembuat', + 'Modification date' => 'Tanggal modifikasi', + 'Completion date' => 'Tanggal penyelesaian', + 'Clone' => 'Klon', + '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:', + 'The task #%d has been opened.' => 'Tugas #%d telah dibuka.', + 'The task #%d has been closed.' => 'Tugas #%d telah ditutup.', + '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 comment' => 'Komentar baru', + 'Comment updated' => 'Komentar diperbarui', + 'New subtask' => 'Sub-tugas baru', + 'I only want to receive notifications for these projects:' => 'Saya ingin menerima pemberitahuan hanya untuk proyek-proyek yang dipilih :', + 'view the task on Kanboard' => 'lihat tugas di Kanboard', + 'Public access' => 'Akses publik', + 'Disable public access' => 'Nonaktifkan akses publik', + 'Enable public access' => 'Aktifkan akses publik', + 'Public access disabled' => 'Akses publik dinonaktifkan', + 'Move the task to another project' => 'Pindahkan tugas ke proyek lain', + 'Move to project' => 'Pindahkan ke proyek lain', + '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' => 'Jarak Jauh', + 'Enabled' => 'Aktif', + 'Disabled' => 'Nonaktif', + 'Login:' => 'Masuk:', + 'Full Name:' => 'Nama Lengkap:', + 'Email:' => 'Email:', + 'Notifications:' => 'Pemberitahuan:', + 'Notifications' => 'Pemberitahuan', + '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.' => '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 #%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 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 memperbarui komentar pada tugas %s', + '%s commented the task %s' => '%s memberikan komentar pada tugas %s', + '%s\'s activity' => 'Aktifitas dari %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"' => 'Nilai default adalah "%s"', + 'Default columns for new projects (Comma-separated)' => 'Kolom default untuk proyek baru (dipisahkan dengan koma)', + '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 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' => 'Database', + 'About' => 'Tentang', + 'Database driver:' => 'Driver database:', + 'Board settings' => 'Pengaturan papan', + 'Webhook settings' => 'Pengaturan Webhook', + 'Reset token' => 'Reset token', + 'API endpoint:' => 'API endpoint :', + 'Refresh interval for personal board' => 'Interval pembaruan untuk papan pribadi', + 'Refresh interval for public board' => 'Interval pembaruan untuk papan publik', + '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" dan "%s"', + 'New personal project' => 'Proyek pribadi baru', + 'This project is personal' => 'Proyek ini pribadi', + 'Add' => 'Tambah', + 'Start date' => 'Tanggal mulai', + 'Time estimated' => 'Perkiraan waktu', + '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', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Buat komentar dari penyedia eksternal', + 'Project management' => 'Manajemen proyek', + 'Columns' => 'Kolom', + 'Task' => 'Tugas', + 'Percentage' => 'Persentasi', + 'Number of tasks' => 'Jumlah dari tugas', + 'Task distribution' => 'Pembagian tugas', + 'Analytics' => 'Analitik', + 'Subtask' => 'Sub-tugas', + 'User repartition' => 'Partisi ulang pengguna', + '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 status must be an integer' => 'Status 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', + 'Daily project summary' => 'Ringkasan proyek harian', + 'Daily project summary export' => 'Ekspor ringkasan proyek harian', + 'Exports' => 'Ekspor', + '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', + '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' => 'Hapus swimlane', + 'Swimlane modification for the project "%s"' => 'Modifikasi swimlane untuk proyek "%s"', + 'Swimlane removed successfully.' => 'Swimlane berhasil dihapus.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane berhasil diperbarui.', + 'Unable to remove this swimlane.' => 'Tidak dapat menghapus swimlane ini.', + 'Unable to update this swimlane.' => 'Tidak dapat memperbarui swimlane ini.', + 'Your swimlane has 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 Sub-tugas', + 'Subtasks' => 'Sub-tugas', + 'Subtasks Export' => 'Ekspor Sub-tugas', + 'Task Title' => 'Judul Tugas', + 'Untitled' => 'Tanpa Nama', + 'Application default' => 'Default aplikasi', + 'Language:' => 'Bahasa:', + 'Timezone:' => 'Zona waktu:', + 'All columns' => 'Semua kolom', + 'Next' => 'Selanjutnya', + '#%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 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' => '%dd', + 'Add a new link' => 'Tambah tautan baru', + '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 diperbarui.', + 'Link removed successfully.' => 'Tautan berhasil dihapus.', + 'Link labels' => 'Label tautan', + 'Link modification' => 'Modifikasi tautan', + 'Opposite label' => 'Label berlawanan', + 'Remove a link' => 'Hapus tautan', + '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 memperbarui tautan Anda.', + 'Unable to remove this link.' => 'Tidak dapat menghapus tautan ini.', + 'relates to' => 'berhubungan dengan', + 'blocks' => 'blokir', + 'is blocked by' => 'diblokir oleh', + 'duplicates' => 'duplikat', + 'is duplicated by' => 'diduplikasi oleh', + 'is a child of' => 'anak 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' => 'Tutup tugas', + 'Expand/collapse tasks' => 'Perluas/tutup tugas', + 'Close dialog box' => 'Tutup kotak dialog', + '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' => 'Gulir horizontal', + 'Compact/wide view' => 'Tampilan kompak/lebar', + 'Currency' => 'Mata uang', + 'Personal project' => 'Proyek pribadi', + 'AUD - Australian Dollar' => 'AUD - Dolar Australia', + 'CAD - Canadian Dollar' => 'CAD - Dolar Kanada', + 'CHF - Swiss Francs' => 'CHF - Francs Swiss', + 'Custom Stylesheet' => 'Kustomisasi CSS', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Poundsterling Inggris', + 'INR - Indian Rupee' => 'INR - Rupe India', + 'JPY - Japanese Yen' => 'JPY - Yen Jepang', + 'NZD - New Zealand Dollar' => 'NZD - Dolar Selandia baru', + 'PEN - Peruvian Sol' => 'PEN - Sol Peru', + 'RSD - Serbian dinar' => 'RSD - Dinar Serbia', + 'CNY - Chinese Yuan' => 'CNY - Yuan Tiongkok', + 'USD - US Dollar' => 'USD - Dolar Amerika', + 'VES - Venezuelan Bolívar' => 'VES - Bolivar Venezuela', + 'Destination column' => 'Kolom tujuan', + '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', + 'Time spent in the column' => 'Waktu yang dihabiskan dalam kolom', + 'Task transitions' => 'Transisi tugas', + 'Task transitions export' => 'Ekspor transisi tugas', + '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' => 'Ganti referensi mata uang', + 'Reference currency' => 'Referensi mata uang', + 'The currency rate has 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 removed the assignee of the task %s' => '%s menghapus penugasan dari tugas %s', + '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 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' => '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' => '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.' => '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 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' => '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 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 value must be alphanumeric' => 'Nilai harus alfanumerik', + '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 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', + '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' => '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', + 'Project settings' => 'Pengaturan proyek', + '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', + 'Two factor authentication enabled' => 'Otentifikasi dua faktor dihidupkan', + 'Unable to update this user.' => 'Tidak dapat memperbarui pengguna ini.', + 'There is no user management for personal projects.' => 'Tidak ada manajemen pengguna untuk proyek-proyek pribadi.', + 'User that will receive the email' => 'Pengguna yang akan menerima 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' => 'Buka kembali tugas', + 'Notification' => 'Pemberitahuan', + '%s moved the task #%d to the first swimlane' => '%s memindahkan tugas #%d ke swimlane pertama', + 'Swimlane' => 'Swimlane', + '%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 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"', + 'view the board on Kanboard' => 'lihat papan di Kanboard', + 'The task has been moved to the first swimlane' => 'Tugas telah dipindahkan ke swimlane pertama', + 'The task has been moved to another swimlane:' => 'Tugas telah dipindahkan ke swimlane lain:', + '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 saat ini', + 'New category: %s' => 'Kategori baru: %s', + 'New color: %s' => 'Warna baru: %s', + 'New complexity: %d' => 'Kompleksitas baru: %d', + 'The due date has been removed' => 'Tanggal jatuh tempo telah dihapus', + 'There is no description anymore' => 'Tidak ada deskripsi lagi', + 'Recurrence settings has been modified' => 'Pengaturan pengulangan telah dimodifikasi', + 'Time spent changed: %sh' => 'Waktu yang dihabiskan telah diganti: %sh', + 'Time estimated changed: %sh' => 'Perkiraan waktu telah diganti: %sh', + 'The field "%s" has 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 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', + 'Only for tasks created by me and tasks assigned to me' => 'Hanya untuk tugas yang dibuat oleh saya dan ditugaskan ke saya', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Total untuk semua kolom', + 'You need at least 2 days of data to show the chart.' => 'Anda memerlukan setidaknya 2 hari dari data yang menunjukkan grafik.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Hentikan timer', + 'Start timer' => 'Mulai timer', + 'My activity stream' => 'Aliran kegiatan saya', + 'Search tasks' => 'Cari tugas', + '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' => 'Tugas terbuka', + 'Not assigned' => 'Tidak ditugaskan', + 'View advanced search syntax' => 'Lihat sintaks pencarian lanjutan', + 'Overview' => 'Ringkasan', + 'Board/Calendar/List view' => 'Tampilan Papan/Kalender/Daftar', + 'Switch to the board view' => 'Beralih ke tampilan papan', + 'Switch to the list view' => 'Beralih ke tampilan daftar', + '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"' => 'Pintasan keyboard: "%s"', + 'List' => 'Daftar', + 'Filter' => 'Saringan', + 'Advanced search' => 'Pencarian lanjutan', + 'Example of query: ' => 'Contoh dari query : ', + '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 in each column' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom', + 'Average time spent' => 'Rata-rata waktu yang dihabiskan', + 'This chart shows the average time spent in 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 shows 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 cycle time', + 'Lead time: ' => 'Lead time: ', + 'Cycle time: ' => 'Cycle time: ', + 'Time spent in 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.' => '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 the start date automatically' => 'Secara otomatis mengatur tanggal mulai', + '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.', + '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' => 'Orang yang ditugaskan saat ini: %s', + 'not assigned' => 'belum ditugaskan', + 'Author:' => 'Penulis:', + 'contributors' => 'kontributor', + 'License:' => 'Lisensi:', + 'License' => 'Lisensi', + 'Enter the text below' => 'Masukkan teks di bawah', + 'Start date:' => 'Tanggal mulai:', + 'Due date:' => 'Batas waktu:', + 'People who are project managers' => 'Orang-orang yang menjadi manajer proyek', + 'People who are project members' => 'Orang-orang yang menjadi anggota proyek', + 'NOK - Norwegian Krone' => 'NOK - Krone Norwegia', + 'Show this column' => 'Perlihatkan kolom ini', + 'Hide this column' => 'Sembunyikan kolom ini', + 'End date' => 'Waktu berakhir', + 'Users overview' => 'Ringkasan pengguna', + 'Members' => 'Anggota', + 'Shared project' => 'Proyek bersama', + 'Project managers' => 'Manajer proyek', + 'Projects list' => 'Daftar proyek', + 'End date:' => 'Waktu berakhir:', + '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', + 'Reset the search/filter box' => 'Reset kotak pencarian/saringan', + 'Documentation' => 'Dokumentasi', + '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 has 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 has 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', + 'No notification.' => '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 has 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' => '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', + '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', + 'Permissions' => 'Izin', + 'Allowed Users' => 'Pengguna Yang Diizinkan', + 'No specific user has been allowed.' => 'Tidak ada user yang diperbolehkan secara khusus', + 'Role' => 'Peran', + 'Enter user name...' => 'Masukkan nama pengguna...', + 'Allowed Groups' => 'Grup Yang Diizinkan', + 'No group has been allowed.' => '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', + '%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 in this column and this swimlane' => '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', + 'Personal 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', + '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', + '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 personal 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: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Contoh: https://contoh.kanboard.org/ (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', + '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 a 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 has 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', + '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', + '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' => 'Email Keluar', + 'Add or change currency rate' => 'Tambah atau ubah nilai mata uang', + 'Reference currency: %s' => 'referensi Mata uang: %s', + 'Add custom filters' => 'Tambahkan filter khusus', + 'Export' => 'Export', + 'Add link label' => 'Tambahkan label tautan', + 'Incompatible Plugins' => 'Plugin Tidak Kompatibel', + 'Compatibility' => 'Kesesuaian', + 'Permissions and ownership' => 'Izin dan kepemilikan', + 'Priorities' => 'Prioritas', + 'Close this window' => 'Tutup jendela ini', + 'Unable to upload this file.' => 'Tidak dapat mengupload file ini.', + 'Import tasks' => 'Impor tugas', + 'Choose a project' => 'Pilih sebuah proyek', + 'Profile' => 'Profil', + 'Application role' => 'Peran aplikasi', + '%d invitations were sent.' => '%d undangan telah dikirim.', + '%d invitation was sent.' => '%d undangan telah dikirim.', + 'Unable to create this user.' => 'Tidak dapat membuat pengguna.', + 'Kanboard Invitation' => 'Undangan Kanboard', + 'Visible on dashboard' => 'Terlihat pada dashboard', + 'Created at:' => 'Dibuat pada:', + 'Updated at:' => 'Diperbarui pada:', + 'There is no custom filter.' => 'Tidak ada filter khusus.', + 'New User' => 'Pengguna Baru', + 'Authentication' => 'Otentikasi', + 'If checked, this user will use a third-party system for authentication.' => 'Jika dicentang, pengguna ini akan menggunakan sistem pihak ketiga untuk otentikasi.', + 'The password is necessary only for local users.' => 'Kata sandi hanya diperlukan untuk pengguna lokal.', + 'You have been invited to register on Kanboard.' => 'Anda telah diundang untuk mendaftar di Kanboard.', + 'Click here to join your team' => 'Klik di sini untuk bergabung dengan tim Anda', + 'Invite people' => 'Undang orang', + 'Emails' => 'Email', + 'Enter one email address by line.' => 'Masukkan satu alamat email per baris.', + 'Add these people to this project' => 'Tambahkan orang-orang ini ke proyek ini', + 'Add this person to this project' => 'Tambahkan orang ini ke proyek ini', + 'Sign-up' => 'Daftar', + 'Credentials' => 'Credential', + 'New user' => 'Pengguna baru', + 'This username is already taken' => 'Nama pengguna ini sudah dipakai', + 'Your profile must have a valid email address.' => 'Profil Anda harus memiliki alamat email yang valid.', + 'TRL - Turkish Lira' => 'TRL - Lira Turki', + 'The project email is optional and could be used by several plugins.' => 'Email proyek adalah opsional dan dapat digunakan oleh beberapa plugin.', + 'The project email must be unique across all projects' => 'Email proyek harus unik di semua proyek', + 'The email configuration has been disabled by the administrator.' => 'Konfigurasi email telah dinonaktifkan oleh administrator.', + 'Close this project' => 'Tutup proyek ini', + 'Open this project' => 'Buka proyek ini', + 'Close a project' => 'Tutup proyek', + 'Do you really want to close this project: "%s"?' => 'Anda yakin ingin menutup proyek ini: "%s"?', + 'Reopen a project' => 'Buka kembali proyek', + 'Do you really want to reopen this project: "%s"?' => 'Anda yakin ingin membuka kembali proyek ini: "%s"?', + 'This project is open' => 'Proyek ini terbuka', + 'This project is closed' => 'Proyek ini ditutup', + 'Unable to upload files, check the permissions of your data folder.' => 'Tidak dapat mengunggah file, periksa izin folder data Anda.', + 'Another category with the same name exists in this project' => 'Kategori lain dengan nama yang sama ada dalam proyek ini', + 'Comment sent by email successfully.' => 'Komentar berhasil dikirim melalui email.', + 'Sent by email to "%s" (%s)' => 'Dikirim melalui email ke "%s" (%s)', + 'Unable to read uploaded file.' => 'Tidak dapat membaca file yang diunggah.', + 'Database uploaded successfully.' => 'Database berhasil diunggah.', + 'Task sent by email successfully.' => 'Tugas berhasil dikirim melalui email.', + 'There is no category in this project.' => 'Tidak ada kategori dalam proyek ini.', + 'Send by email' => 'Kirim melalui email', + 'Create and send a comment by email' => 'Buat dan kirim komentar melalui email', + 'Subject' => 'Subjek', + 'Upload the database' => 'Unggah database', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Anda dapat mengunggah basis data Sqlite yang diunduh sebelumnya (format Gzip).', + 'Database file' => 'File database', + 'Upload' => 'Unggah', + 'Your project must have at least one active swimlane.' => 'Proyek Anda harus memiliki setidaknya satu swimlane aktif.', + 'Project: %s' => 'Proyek: %s', + 'Automatic action not found: "%s"' => 'Tindakan otomatis tidak ditemukan: "%s', + '%d projects' => '%d proyek', + '%d project' => '%d proyek', + 'There is no project.' => 'Tidak ada proyek', + 'Sort' => 'Urutkan', + 'Project ID' => 'ID Proyek', + 'Project name' => 'Nama proyek', + 'Public' => 'Publik', + 'Personal' => 'Pribadi', + '%d tasks' => '%d tugas', + '%d task' => '%d tugas', + 'Task ID' => 'ID Tugas', + 'Assign automatically a color when due date is expired' => 'Tetapkan warna secara otomatis ketika tanggal jatuh tempo kadaluarsa', + 'Total score in this column across all swimlanes' => 'Skor total di kolom ini di semua swimlanes', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentina', + 'COP - Colombian Peso' => 'COP - Peso Kolombia', + '%d groups' => '%d grup', + '%d group' => '%d grup', + 'Group ID' => 'ID Grup', + 'External ID' => 'ID External', + '%d users' => '%d pengguna', + '%d user' => '%d pengguna', + 'Hide subtasks' => 'Sembunyikan subtugas', + 'Show subtasks' => 'Tampilkan subtugas', + 'Authentication Parameters' => 'Parameter Otentikasi', + 'API Access' => 'Akses API', + 'No users found.' => 'Tidak ada pengguna yang ditemukan.', + 'User ID' => 'ID Pengguna', + 'Notifications are activated' => 'Notifikasi diaktifkan', + 'Notifications are disabled' => 'Notifikasi dinonaktifkan', + 'User disabled' => 'Pengguna dinonaktifkan', + '%d notifications' => '%d notifikasi', + '%d notification' => '%d notifikasi', + 'There is no external integration installed.' => 'Tidak ada integrasi eksternal yang terpasang.', + 'You are not allowed to update tasks assigned to someone else.' => 'Anda tidak diizinkan untuk memperbarui tugas yang diberikan kepada orang lain.', + 'You are not allowed to change the assignee.' => 'Anda tidak diizinkan untuk mengubah penerima.', + 'Task suppression is not permitted' => 'Penindasan tugas tidak diizinkan', + 'Changing assignee is not permitted' => 'Tidak diizinkan mengubah penerima', + 'Update only assigned tasks is permitted' => 'Perbarui hanya tugas yang diizinkan', + 'Only for tasks assigned to the current user' => 'Hanya untuk tugas yang diberikan kepada pengguna saat ini', + 'My projects' => 'Proyek saya', + 'You are not a member of any project.' => 'Anda bukan anggota proyek mana pun.', + 'My subtasks' => 'Subtugas saya', + '%d subtasks' => '%d subtugas', + '%d subtask' => '%d subtugas', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Hanya memindahkan tugas di antara kolom-kolom yang diizinkan untuk tugas yang diberikan kepada pengguna saat ini', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Krona Denmark', + 'Remove user from group' => 'Hapus pengguna dari grup', + 'Assign the task to its creator' => 'Tetapkan tugas untuk pembuatnya', + 'This task was sent by email to "%s" with subject "%s".' => 'Tugas ini dikirim melalui email ke "%s" dengan subjek "%s".', + 'Predefined Email Subjects' => 'Default Subjek Email', + 'Write one subject by line.' => 'Tulis satu subjek per baris.', + 'Create another link' => 'Buat tautan lain', + 'BRL - Brazilian Real' => 'BRL - Real Brasil', + 'Add a new Kanboard task' => 'Tambahkan tugas Kanboard baru', + 'Subtask not started' => 'Subtugas belum dimulai', + 'Subtask currently in progress' => 'Subtugas sedang dalam proses', + 'Subtask completed' => 'Subtugas selesai', + 'Subtask added successfully.' => 'Subtugas berhasil ditambahkan.', + '%d subtasks added successfully.' => '%d subtugas berhasil ditambahkan.', + 'Enter one subtask by line.' => 'Masukkan satu subtugas per baris.', + 'Predefined Contents' => 'Default konten', + 'Predefined contents' => 'Default konten', + 'Predefined Task Description' => 'Default Deskripsi Tugas', + 'Do you really want to remove this template? "%s"' => 'Anda yakin ingin menghapus template ini? "%s"', + 'Add predefined task description' => 'Tambahkan deskripsi tugas default', + 'Predefined Task Descriptions' => 'Default deskripsi tugas', + 'Template created successfully.' => 'Template berhasil dibuat.', + 'Unable to create this template.' => 'Tidak dapat membuat template ini.', + 'Template updated successfully.' => 'Template berhasil diperbarui.', + 'Unable to update this template.' => 'Tidak dapat memperbarui template ini.', + 'Template removed successfully.' => 'Template berhasil dihapus.', + 'Unable to remove this template.' => 'Tidak dapat menghapus template ini.', + 'Template for the task description' => 'Template untuk deskripsi tugas', + 'The start date is greater than the end date' => 'Tanggal mulai lebih besar dari tanggal akhir', + 'Tags must be separated by a comma' => 'Tag harus dipisahkan dengan koma', + 'Only the task title is required' => 'Hanya judul tugas yang dibutuhkan', + 'Creator Username' => 'Nama Pengguna Pembuat', + 'Color Name' => 'Nama Warna', + 'Column Name' => 'Nama Kolom', + 'Swimlane Name' => 'Nama Swimlane', + 'Time Estimated' => 'Perkiraan Waktu', + 'Time Spent' => 'Waktu yang dihabiskan', + 'External Link' => 'Tautan Eksternal', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Fitur ini mengaktifkan umpan iCal, umpan RSS, dan tampilan papan publik.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Hentikan timer semua subtugas saat memindahkan tugas ke kolom lain', + 'Subtask Title' => 'Judul Subtugas', + 'Add a subtask and activate the timer when moving a task to another column' => 'Tambahkan subtugas dan aktifkan pengatur waktu saat memindahkan tugas ke kolom lain', + 'days' => 'hari', + 'minutes' => 'menit', + 'seconds' => 'detik', + 'Assign automatically a color when preset start date is reached' => 'Tetapkan warna secara otomatis ketika tanggal mulai preset tercapai', + 'Move the task to another column once a predefined start date is reached' => 'Pindahkan tugas ke kolom lain setelah tanggal mulai yang ditentukan tercapai', + 'This task is now linked to the task %s with the relation "%s"' => 'Tugas ini sekarang ditautkan ke tugas %s dengan relasi "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Tautan dengan relasi "%s" ke tugas %s telah dihapus', + 'Custom Filter:' => 'Kustomisasi Filter:', + 'Unable to find this group.' => 'Tidak dapat menemukan grup ini.', + '%s moved the task #%d to the column "%s"' => '%s memindahkan tugas #%d ke kolom "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s memindahkan tugas #%d ke posisi %d di kolom "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s memindahkan tugas #%d ke swimlane "%s"', + '%sh spent' => '%sh menghabiskan', + '%sh estimated' => '%sh perkiraan', + 'Select All' => 'Pilih Semua', + 'Unselect All' => 'Batal Pilih Semua', + 'Apply action' => 'Terapkan tindakan', + 'Move selected tasks to another column or swimlane' => 'Pindahkan tugas yang dipilih ke kolom lain', + 'Edit tasks in bulk' => 'Edit tugas secara massal', + 'Choose the properties that you would like to change for the selected tasks.' => 'Pilih properti yang ingin Anda ubah untuk tugas yang dipilih.', + 'Configure this project' => 'Konfigurasi proyek ini', + 'Start now' => 'Mulai sekarang', + '%s removed a file from the task #%d' => '%s menghapus file dari tugas #%d', + 'Attachment removed from task #%d: %s' => 'Lampiran dihapus dari tugas #%d: %s', + 'No color' => 'Tanpa warna', + 'Attachment removed "%s"' => 'Lampiran dihapus "%s"', + '%s removed a file from the task %s' => '%s menghapus file dari tugas %s', + 'Move the task to another swimlane when assigned to a user' => 'Pindahkan tugas ke swimlane lain ketika ditugaskan ke pengguna', + 'Destination swimlane' => 'Swimlane tujuan', + 'Assign a category when the task is moved to a specific swimlane' => 'Tetapkan kategori ketika tugas dipindahkan ke swimlane tertentu', + 'Move the task to another swimlane when the category is changed' => 'Pindahkan tugas ke swimlane lain saat kategorinya diubah', + 'Reorder this column by priority (ASC)' => 'Susun ulang kolom ini berdasarkan prioritas (ASC)', + 'Reorder this column by priority (DESC)' => 'Susun ulang kolom ini berdasarkan prioritas (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Susun ulang kolom ini menurut penerima tugas dan prioritas (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Susun ulang kolom ini menurut penerima tugas dan prioritas (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Susun ulang kolom ini oleh penerima tugas (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Susun ulang kolom ini oleh penerima tugas (Z-A)', + 'Reorder this column by due date (ASC)' => 'Susun ulang kolom ini sebelum tanggal jatuh tempo (ASC)', + 'Reorder this column by due date (DESC)' => 'Susun ulang kolom ini berdasarkan tanggal jatuh tempo (DESC)', + 'Reorder this column by id (ASC)' => 'Susun ulang kolom ini berdasarkan id (ASC)', + 'Reorder this column by id (DESC)' => 'Susun ulang kolom ini berdasarkan id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s memindahkan tugas #%d "%s" ke proyek "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Task #%d "%s" telah dipindahkan ke proyek "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Pindahkan tugas ke kolom lain ketika batas waktu kurang dari jumlah hari tertentu', + 'Automatically update the start date when the task is moved away from a specific column' => 'Secara otomatis memperbarui tanggal mulai ketika tugas dipindahkan dari kolom tertentu', + 'HTTP Client:' => 'HTTP Client:', + 'Assigned' => 'Ditugaskan', + 'Task limits apply to each swimlane individually' => 'Batas tugas berlaku untuk setiap swimlane secara individual', + 'Column task limits apply to each swimlane individually' => 'Batas tugas kolom berlaku untuk setiap swimlane secara individual', + 'Column task limits are applied to each swimlane individually' => 'Batas tugas kolom diterapkan ke setiap swimlane secara individual', + 'Column task limits are applied across swimlanes' => 'Batas tugas kolom diterapkan di seluruh swimlanes', + 'Task limit: ' => 'Batas tugas:', + 'Change to global tag' => 'Ubah ke tag global', + 'Do you really want to make the tag "%s" global?' => 'Anda yakin ingin menjadikan tag "%s" global?', + 'Enable global tags for this project' => 'Aktifkan tag global untuk proyek ini', + 'Group membership(s):' => 'Keanggotaan grup:', + '%s is a member of the following group(s): %s' => '%s adalah anggota dari grup berikut: %s', + '%d/%d group(s) shown' => '%d/%d grup ditampilkan', + 'Subtask creation or modification' => 'Pembuatan atau modifikasi subtugas', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Tetapkan tugas ke pengguna tertentu saat tugas dipindahkan ke swimlane tertentu', + 'Comment' => 'Komentar', + 'Collapse vertically' => 'Ciutkan secara vertikal', + 'Expand vertically' => 'Bentangkan secara vertikal', + 'MXN - Mexican Peso' => 'MXN - Peso Meksiko', + 'Estimated vs actual time per column' => 'Estimasi vs waktu aktual per kolom', + 'HUF - Hungarian Forint' => 'HUF - Forint Hongaria', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Anda harus memilih berkas untuk diunggah sebagai avatar Anda!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Berkas yang Anda unggah bukan gambar yang valid! (Hanya berkas *.gif, *.jpg, *.jpeg, dan *.png yang diizinkan!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Atur tenggat waktu secara otomatis ketika tugas dipindahkan dari kolom tertentu', + 'No other projects found.' => 'Tidak ada proyek lain yang ditemukan.', + 'Tasks copied successfully.' => 'Tugas berhasil disalin.', + 'Unable to copy tasks.' => 'Tidak dapat menyalin tugas.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema terang', + 'Dark theme' => 'Tema gelap', + 'Automatic theme - Sync with system' => 'Tema otomatis - Sinkronkan dengan sistem', + 'Application managers or more' => 'Pengelola aplikasi atau lebih', + 'Administrators' => 'Administrator', + 'Visibility:' => 'Visibilitas:', + 'Standard users' => 'Pengguna standar', + 'Visibility is required' => 'Visibilitas diperlukan', + 'The visibility should be an app role' => 'Visibilitas harus berupa peran aplikasi', + 'Reply' => 'Balas', + '%s wrote: ' => '%s menulis: ', + 'Number of visible tasks in this column and swimlane' => 'Jumlah tugas yang terlihat di kolom dan lajur ini', + 'Number of tasks in this swimlane' => 'Jumlah tugas di lajur ini', + 'Unable to find another subtask in progress, you can close this window.' => 'Tidak dapat menemukan subtugas lain dalam proses, Anda dapat menutup jendela ini.', + 'This theme is invalid' => 'Tema ini tidak valid', + 'This role is invalid' => 'Peran ini tidak valid', + 'This timezone is invalid' => 'Zona waktu ini tidak valid', + 'This language is invalid' => 'Bahasa ini tidak valid', + 'This URL is invalid' => 'URL ini tidak valid', + 'Date format invalid' => 'Format tanggal tidak valid', + 'Time format invalid' => 'Format waktu tidak valid', + 'Invalid Mail transport' => 'Transportasi surel tidak valid', + 'Color invalid' => 'Warna tidak valid', + 'This value must be greater or equal to %d' => 'Nilai ini harus lebih besar dari atau sama dengan %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Tambahkan BOM di awal berkas (diperlukan untuk Microsoft Excel)', + 'Just add these tag(s)' => 'Cukup tambahkan tag ini', + 'Remove internal link(s)' => 'Hapus tautan internal', + 'Import tasks from another project' => 'Impor tugas dari proyek lain', + 'Select the project to copy tasks from' => 'Pilih proyek untuk menyalin tugas darinya', + 'The total maximum allowed attachments size is %sB.' => 'Ukuran total maksimum lampiran yang diizinkan adalah %sB.', + 'Add attachments' => 'Tambahkan lampiran', + 'Task #%d "%s" is overdue' => 'Tugas #%d "%s" sudah kadaluarsa', + 'Enable notifications by default for all new users' => 'Aktifkan notifikasi secara default untuk semua pengguna baru', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Tetapkan tugas kepada pembuatnya untuk kolom tertentu jika tidak ada penanggung jawab yang diatur secara manual', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Tetapkan tugas kepada pengguna yang masuk saat kolom berubah ke kolom yang ditentukan jika tidak ada pengguna yang ditetapkan', +]; diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php new file mode 100644 index 0000000..7fbd277 --- /dev/null +++ b/app/Locale/it_IT/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Nessuno', + 'Edit' => 'Modifica', + 'Remove' => 'Cancella', + 'Yes' => 'Si', + 'No' => 'No', + 'cancel' => 'annulla', + 'or' => 'o', + 'Yellow' => 'Giallo', + 'Blue' => 'Blu', + 'Green' => 'Verde', + 'Purple' => 'Viola', + 'Red' => 'Rosso', + 'Orange' => 'Arancione', + 'Grey' => 'Grigio', + 'Brown' => 'Marrone', + 'Deep Orange' => 'Arancio scuro', + 'Dark Grey' => 'Grigio scuro', + 'Pink' => 'Rosa', + 'Teal' => 'Verde foglia di tè', + 'Cyan' => 'Ciano', + 'Lime' => 'Verde lime', + 'Light Green' => 'Verde chiaro', + 'Amber' => 'Ambra', + 'Save' => 'Salva', + 'Login' => 'Accedi', + 'Official website:' => 'Sito web ufficiale:', + 'Unassigned' => 'Non assegnato', + 'View this task' => 'Visualizza questo compito', + 'Remove user' => 'Cancella un utente', + 'Do you really want to remove this user: "%s"?' => 'Veramente vuoi cancellare questo utente: "%s" ?', + 'All users' => 'Tutti gli utenti', + 'Username' => 'Nome utente', + 'Password' => 'Password', + 'Administrator' => 'Amministratore', + 'Sign in' => 'Accedi', + 'Users' => 'Utenti', + 'Forbidden' => 'Vietato', + 'Access Forbidden' => 'Accesso vietato', + 'Edit user' => 'Modifica un utente', + 'Logout' => 'Esci', + 'Bad username or password' => 'Utente o password errati', + 'Edit project' => 'Modifica progetto', + 'Name' => 'Nome', + 'Projects' => 'Progetti', + 'No project' => 'Nessun progetto', + 'Project' => 'Progetto', + 'Status' => 'Stato', + 'Tasks' => 'Task', + 'Board' => 'Bacheca', + 'Actions' => 'Azioni', + 'Inactive' => 'Inattivo', + 'Active' => 'Attivo', + 'Unable to update this board.' => 'Impossibile aggiornare 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"', + 'Add a new column' => 'Aggiungi una nuova colonna', + 'Title' => 'Titolo', + 'Assigned to %s' => 'Assegnato a %s', + 'Remove a column' => 'Cancella questa colonna', + 'Unable to remove this column.' => 'Impossibile cancellare questa colonna.', + 'Do you really want to remove this column: "%s"?' => 'Desideri davvero cancellare questa colonna: "%s" ?', + 'Settings' => 'Impostazioni', + 'Application settings' => 'Impostazioni dell\'applicazione', + 'Language' => 'Lingua', + 'Webhook token:' => 'Identificatore (token) per i webhooks :', + 'API token:' => 'Token dell\'API:', + 'Database size:' => 'Dimensioni della base dati:', + 'Download the database' => 'Scaricare la base dati', + 'Optimize the database' => 'Ottimizare la base dati', + '(VACUUM command)' => '(Comando VACUUM)', + '(Gzip compressed Sqlite file)' => '(File Sqlite compresso in Gzip)', + 'Close a task' => 'Chiudi un compito', + 'Column' => 'Colonna', + 'Color' => 'Colore', + 'Assignee' => 'Assegnatario', + 'Create another task' => 'Crea un nuovo compito', + 'New task' => 'Nuovo compito', + 'Open a task' => 'Apri un compito', + 'Do you really want to open this task: "%s"?' => 'Desideri davvero aprire questo compito: "%s" ?', + 'Back to the board' => 'Torna alla bacheca', + 'There is nobody assigned' => 'Nessuno è assegnato a questo compito', + 'Column on the board:' => 'Colonna sulla bacheca: ', + 'Close this task' => 'Chiudi questo compito', + 'Open this task' => 'Apri questo compito', + 'There is no description.' => 'Nessuna descrizione presente.', + 'Add a new task' => 'Aggiungere un nuovo compito', + 'The username is required' => 'Si richiede un nome di utente', + 'The maximum length is %d characters' => 'La lunghezza massima è di %d caratteri', + 'The minimum length is %d characters' => 'La lunghezza minima è di %d caratteri', + 'The password is required' => 'Si richiede una password', + 'This value must be an integer' => 'questo valore deve essere un intero', + 'The username must be unique' => 'Il nome di utente deve essere unico', + 'The user id is required' => 'Si richiede l\'identificatore dell\'utente', + 'Passwords don\'t match' => 'Le password non corrispondono', + 'The confirmation is required' => 'Si richiede una conferma', + 'The project is required' => 'Si richiede il progetto', + 'The id is required' => 'Si richiede l\'identificatore', + 'The project id is required' => 'Si richiede l\'identificatore del progetto', + 'The project name is required' => 'Si richiede il nome del progetto', + 'The title is required' => 'Si richiede un titolo', + 'Settings saved successfully.' => 'Impostazioni salvate con successo.', + 'Unable to save your settings.' => 'Impossibile salvare le impostazioni.', + 'Database optimization done.' => 'Ottimizzazione della base dati conclusa.', + 'Your project has been created successfully.' => 'Il tuo progetto è stato creato con successo.', + 'Unable to create your project.' => 'Impossibile creare il progetto.', + 'Project updated successfully.' => 'Progetto aggiornato con successo.', + 'Unable to update this project.' => 'Impossibile aggiornare il progetto.', + 'Unable to remove this project.' => 'Impossibile cancellare questo progetto.', + 'Project removed successfully.' => 'Progetto cancellato con successo.', + 'Project activated successfully.' => 'Progetto attivato con successo.', + 'Unable to activate this project.' => 'Impossibile attivare il progetto.', + 'Project disabled successfully.' => 'Progetto disattivato con successo.', + 'Unable to disable this project.' => 'Impossibile disattivare il progetto.', + 'Unable to open this task.' => 'Impossibile aprire questo compito.', + 'Task opened successfully.' => 'Il compito è stato aperto con successo.', + 'Unable to close this task.' => 'Impossibile chiudere questo compito.', + 'Task closed successfully.' => 'Task chiuso con successo.', + 'Unable to update your task.' => 'Impossibile modificare questo compito.', + 'Task updated successfully.' => 'Task modificato con successo.', + 'Unable to create your task.' => 'Impossibile creare questo compito.', + 'Task created successfully.' => 'Task creato con successo.', + 'User created successfully.' => 'Utente creato con successo.', + 'Unable to create your user.' => 'Impossibile creare l\'utente.', + 'User updated successfully.' => 'Utente aggiornato con successo.', + 'User removed successfully.' => 'Utente cancellato con successo.', + 'Unable to remove this user.' => 'Impossibile cancellare questo utente.', + 'Board updated successfully.' => 'Bacheca aggiornata con successo.', + 'Ready' => 'Pronto', + 'Backlog' => 'In attesa', + 'Work in progress' => 'In corso', + 'Done' => 'Fatto', + 'Application version:' => 'Versione dell\'applicazione:', + 'Id' => 'ID', + 'Public link' => 'Link pubblico', + 'Timezone' => 'Fuso orario', + 'Sorry, I didn\'t find this information in my database!' => 'Spiacente, non ho trovato questa informazione sul database!', + 'Page not found' => 'Pagina non trovata', + 'Complexity' => 'Complessità', + 'Task limit' => 'Limite di compito', + 'Task count' => 'Numero di compito', + 'User' => 'Utente', + 'Comments' => 'Commenti', + 'Comment is required' => 'Si richiede un commento', + 'Comment added successfully.' => 'Commenti aggiunti con successo.', + 'Unable to create your comment.' => 'Impossibile creare questo commento.', + 'Due Date' => 'Data di scadenza', + 'Invalid date' => 'Data non valida', + 'Automatic actions' => 'Azioni automatiche', + 'Your automatic action has been created successfully.' => 'l\'azione automatica è stata creata con successo.', + 'Unable to create your automatic action.' => 'Impossibile creare quest\'azione automatica.', + 'Remove an action' => 'Cancellare un\'azione', + 'Unable to remove this action.' => 'Impossibile cancellare questa azione.', + 'Action removed successfully.' => 'Azione cancellata con successo.', + 'Automatic actions for the project "%s"' => 'Azioni automatiche per il progetto "%s"', + 'Add an action' => 'Aggiungi un\'azione', + 'Event name' => 'Nome dell\'evento', + 'Action' => 'Azione', + 'Event' => 'Evento', + 'When the selected event occurs execute the corresponding action.' => 'Quando si verifica l\'evento selezionato, eseguire l\'azione corrispondente.', + 'Next step' => 'Passo successivo', + 'Define action parameters' => 'Definire i parametri dell\'azione', + 'Do you really want to remove this action: "%s"?' => 'Vuoi davvero cancellare la seguente azione: "%s"?', + 'Remove an automatic action' => 'Cancella un\'azione automatica', + 'Assign the task to a specific user' => 'Assegna il compito ad un utente specifico', + 'Assign the task to the person who does the action' => 'Assegna il compito all\'utente che compie l\'azione', + 'Duplicate the task to another project' => 'Duplica il compito in altro progetto', + 'Move a task to another column' => 'Sposta un compito in un\'altra colonna', + 'Task modification' => 'Modifica di un compito', + 'Task creation' => 'Creazione di un compito', + 'Closing a task' => 'Chiusura di un compito', + 'Assign a color to a specific user' => 'Assegna un colore ad un utente specifico', + 'Position' => 'Posizione', + 'Duplicate to project' => 'Duplica in un altro progetto', + 'Duplicate' => 'Duplica', + 'Link' => 'Relazione', + 'Comment updated successfully.' => 'Commento aggiornato con successo.', + 'Unable to update your comment.' => 'Impossibile aggiornare questo commento.', + 'Remove a comment' => 'Cancella un commento', + 'Comment removed successfully.' => 'Commento cancellato con successo.', + 'Unable to remove this comment.' => 'Impossibile cancellare questo commento.', + 'Do you really want to remove this comment?' => 'Vuoi davvero cancellare questo commento?', + 'Current password for the user "%s"' => 'Password attuale per l\'utente "%s"', + 'The current password is required' => 'Si richiede la password attuale', + 'Wrong password' => 'Password errata', + 'Unknown' => 'Sconociuto', + 'Last logins' => 'Ultimi accessi', + 'Login date' => 'Data di accesso', + 'Authentication method' => 'Metodo di autenticazione', + 'IP address' => 'Indirizzo IP', + 'User agent' => 'User agent', + 'Persistent connections' => 'Connessioni persistenti', + 'No session.' => 'Non esiste sessione.', + 'Expiration date' => 'Data di scadenza', + 'Remember Me' => 'Ricordami', + 'Creation date' => 'Data di creazione', + 'Everybody' => 'Tutti', + 'Open' => 'Aperto', + 'Closed' => 'Chiuso', + 'Search' => 'Cerca', + 'Nothing found.' => 'Non si è trovato nulla.', + 'Due date' => 'Data di scadenza', + 'Description' => 'Descrizione', + '%d comments' => '%d commenti', + '%d comment' => '%d commento', + 'Email address invalid' => 'Indirizzo Email non valido', + 'Your external account is not linked anymore to your profile.' => 'Il tuo account esterno non è più collegato al tuo profilo.', + 'Unable to unlink your external account.' => 'Impossibile scollegare il tuo account esterno.', + 'External authentication failed' => 'Autenticazione esterna fallita', + 'Your external account is linked to your profile successfully.' => 'Il tuo account esterno è stato collegato al tuo profilo con successo.', + 'Email' => 'Email', + 'Task removed successfully.' => 'Task cancellato con successo.', + 'Unable to remove this task.' => 'Impossibile cancellare questo compito.', + 'Remove a task' => 'Cancella un compito', + 'Do you really want to remove this task: "%s"?' => 'Vuoi davvero cancellare questo compito: "%s"?', + 'Assign automatically a color based on a category' => 'Assegna un colore in modo automatico basandosi sulla categoria', + 'Assign automatically a category based on a color' => 'Assegna una categoria in modo automatico basandosi sul colore', + 'Task creation or modification' => 'Creazione o modifica di compito', + 'Category' => 'Categoria', + 'Category:' => 'Categoria:', + 'Categories' => 'Categorie', + 'Your category has been created successfully.' => 'La tua categoria è stata creata con successo.', + 'This category has been updated successfully.' => 'La tua categoria è stata aggiornata con successo.', + 'Unable to update this category.' => 'Impossibile aggiornare la tua categoria.', + 'Remove a category' => 'Cancella una categoria', + 'Category removed successfully.' => 'Categoria cancellata con successo.', + 'Unable to remove this category.' => 'Impossibile cancellare questa categoria.', + 'Category modification for the project "%s"' => 'Modifica della categoria per il progetto "%s"', + 'Category Name' => 'Nome della categoria', + 'Add a new category' => 'Aggiungere una nuova categoria', + 'Do you really want to remove this category: "%s"?' => 'Vuoi davvero cancellare la seguente categoria: "%s"?', + 'All categories' => 'Tutte le categorie', + 'No category' => 'Senza categoria', + 'The name is required' => 'Si richiede un nome', + 'Remove a file' => 'Cancella un file', + 'Unable to remove this file.' => 'Impossibile cancellare questo file.', + 'File removed successfully.' => 'File cancellato con successo.', + 'Attach a document' => 'Allega un documento', + 'Do you really want to remove this file: "%s"?' => 'Vuoi davvero cancellare questo file: "%s"?', + 'Attachments' => 'Allegati', + 'Edit the task' => 'Modifica il compito', + 'Add a comment' => 'Aggiungi un commento', + 'Edit a comment' => 'Modifica un commento', + 'Summary' => 'Sommario', + 'Time tracking' => 'Monitoraggio delle tempistiche', + 'Estimate:' => 'Stimato:', + 'Spent:' => 'Trascorso:', + 'Do you really want to remove this sub-task?' => 'Vuoi davvero cancellare questo sotto-compito?', + 'Remaining:' => 'Rimangono', + 'hours' => 'ore', + 'estimated' => 'stimate', + 'Sub-Tasks' => 'Sotto-compito', + 'Add a sub-task' => 'Aggiungi un sotto-compito', + 'Original estimate' => 'Stima originale', + 'Create another sub-task' => 'Crea un altro sotto-compito', + 'Time spent' => 'Tempo trascorso', + 'Edit a sub-task' => 'Modifica un sotto-compito', + 'Remove a sub-task' => 'Cancella un sotto-compito', + 'The time must be a numeric value' => 'Il tempo deve essere un valore numerico', + 'Todo' => 'Da fare', + 'In progress' => 'In corso', + 'Sub-task removed successfully.' => 'Sotto-compito cancellato con successo.', + 'Unable to remove this sub-task.' => 'Impossibile cancellare questo sotto-compito.', + 'Sub-task updated successfully.' => 'Sotto-compito aggiornato con successo.', + 'Unable to update your sub-task.' => 'Impossibile aggiornare il tuo sotto-compito.', + 'Unable to create your sub-task.' => 'Impossibile creare il tuo sotto-compito.', + 'Maximum size: ' => 'Dimensioni massime: ', + 'Display another project' => 'Mostra un altro progetto', + 'Created by %s' => 'Creato da %s', + 'Tasks Export' => 'Export dei compito', + 'Start Date' => 'Data di inizio', + 'Execute' => 'Esegui', + 'Task Id' => 'Id del compito', + 'Creator' => 'Creatore', + 'Modification date' => 'Data di modifica', + 'Completion date' => 'Data di termine', + 'Clone' => 'Clona', + 'Project cloned successfully.' => 'Progetto clonato con successo.', + 'Unable to clone this project.' => 'Impossibile clonare questo progetto', + 'Enable email notifications' => 'Abilita le notifiche via email', + 'Task position:' => 'Posizione del compito:', + 'The task #%d has been opened.' => 'Il compito #%d è stato aperto.', + 'The task #%d has been closed.' => 'Il compito #%d è stato chiuso.', + 'Sub-task updated' => 'Sotto-compito aggiornato', + 'Title:' => 'Titolo', + 'Status:' => 'Stato', + 'Assignee:' => 'Assegnatario:', + 'Time tracking:' => 'Monitoraggio delle tempistiche:', + 'New sub-task' => 'Nuovo sotto-compito', + 'New attachment added "%s"' => 'Nuovo allegato aggiunto "%s"', + 'New comment posted by %s' => 'Nuovo commento aggiunto da "%s"', + 'New comment' => 'Nuovo commento', + 'Comment updated' => 'Commento aggiornato', + 'New subtask' => 'Nuovo sotto-compito', + 'I only want to receive notifications for these projects:' => 'Vorrei ricevere le notifiche solo da questi progetti:', + 'view the task on Kanboard' => 'visualizza il compito su Kanboard', + 'Public access' => 'Accesso pubblico', + 'Disable public access' => 'Disabilita l\'accesso pubblico', + 'Enable public access' => 'Abilita l\'accesso pubblico', + 'Public access disabled' => 'Accesso pubblico disattivato', + 'Move the task to another project' => 'Sposta il compito in un altro progetto', + 'Move to project' => 'Sposta in un altro progetto', + 'Do you really want to duplicate this task?' => 'Vuoi davvero duplicare questo compito?', + 'Duplicate a task' => 'Duplica il compito', + 'External accounts' => 'Account esterni', + 'Account type' => 'Tipo di account', + 'Local' => 'Locale', + 'Remote' => 'Remoto', + 'Enabled' => 'Abilitato', + 'Disabled' => 'Disabilitato', + 'Login:' => 'Nome utente:', + 'Full Name:' => 'Nome:', + 'Email:' => 'Email:', + 'Notifications:' => 'Notifiche:', + 'Notifications' => 'Notifiche', + 'Account type:' => 'Tipo di account', + 'Edit profile' => 'Modifica profilo', + 'Change password' => 'Cambia password', + 'Password modification' => 'Modifica della password', + 'External authentications' => 'Autenticazione esterna', + 'Never connected.' => 'Mai connesso.', + 'No external authentication enabled.' => 'Nessuna autenticazione esterna abilitata.', + 'Password modified successfully.' => 'Password modificata con successo.', + 'Unable to change the password.' => 'Impossibile cambiare la password.', + 'Change category' => 'Cambia categoria', + '%s updated the task %s' => '%s ha aggiornato il compito %s', + '%s opened the task %s' => '%s ha aperto il compito %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s ha spostato il compito %s nella posizione #%d della colonna "%s"', + '%s moved the task %s to the column "%s"' => '%s ha spostato il compito %s nella colonna "%s"', + '%s created the task %s' => '%s ha creato il compito %s', + '%s closed the task %s' => '%s ha chiuso il compito %s', + '%s created a subtask for the task %s' => '%s ha creato un sotto-compito per il compito %s', + '%s updated a subtask for the task %s' => '%s ha aggiornato un sotto-compito per il compito %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Assegnato a %s con una stima di %s/%sh', + 'Not assigned, estimate of %sh' => 'Non assegnato, stima %sh', + '%s updated a comment on the task %s' => '%s ha aggiornato un commento nel compito %s', + '%s commented the task %s' => '%s ha commentato il compito %s', + '%s\'s activity' => 'Attività di %s', + 'RSS feed' => 'Feed RSS', + '%s updated a comment on the task #%d' => '%s ha aggiornato un commento del compito #%d', + '%s commented on the task #%d' => '%s ha commentato il compito #%d', + '%s updated a subtask for the task #%d' => '%s ha aggiornato un sotto-compito del compito #%d', + '%s created a subtask for the task #%d' => '%s ha creato un sotto-compito del compito #%d', + '%s updated the task #%d' => '%s ha aggiornato il compito #%d', + '%s created the task #%d' => '%s ha creato il compito #%d', + '%s closed the task #%d' => '%s ha chiuso il compito #%d', + '%s opened the task #%d' => '%s ha aperto il compito #%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 compito', + '%s changed the assignee of the task #%d to %s' => '%s dai l\'assegnazione del compito #%d a %s', + '%s changed the assignee of the task %s to %s' => '%s ha cambiato l\'assegnatario del compito %s a %s', + 'New password for the user "%s"' => 'Nuova password per l\'utente "%s"', + 'Choose an event' => 'Scegli un evento', + 'Create a task from an external provider' => 'Crea un compito da un provider esterno', + 'Change the assignee based on an external username' => 'Cambia l\'assegnatario basandosi su un username esterno', + 'Change the category based on an external label' => 'Cambia la categoria basandosi su un\'etichetta esterna', + 'Reference' => 'Riferimento', + 'Label' => 'Etichetta', + 'Database' => 'Database', + 'About' => 'Informazioni', + 'Database driver:' => 'Driver per Database', + 'Board settings' => 'Impostazioni bacheca', + 'Webhook settings' => 'Impostazione Webhook', + 'Reset token' => 'Rigenera il token', + 'API endpoint:' => 'Endpoint dell\'API:', + 'Refresh interval for personal board' => 'Intervallo di refresh per le bacheche private', + 'Refresh interval for public board' => 'Intervallo di refresh per le bacheche pubbliche', + 'Task highlight period' => 'Periodo di evidenza per il compito', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periodo (in secondi) per considerare un compito come modificato recentemente (0 per disabilitare, 2 giorni di default)', + 'Frequency in second (60 seconds by default)' => 'Frequenza in secondi (60 secondi di default)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequenza in secondi (0 secondi di default)', + 'Application URL' => 'URL dell\'applicazione', + 'Token regenerated.' => 'Token rigenerato.', + 'Date format' => 'Formato data', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Il formato ISO è sempre accettato, esempio: "%s" e "%s"', + 'New personal project' => 'Nuovo progetto privato', + 'This project is personal' => 'Questo progetto è privato', + 'Add' => 'Aggiungi', + 'Start date' => 'Data di inizio', + 'Time estimated' => 'Tempo stimato', + 'There is nothing assigned to you.' => 'Non c\'è nulla assegnato a te.', + 'My tasks' => 'I miei compiti', + 'Activity stream' => 'Flusso attività', + 'Dashboard' => 'Bacheca', + 'Confirmation' => 'Conferma', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Crea un commit da un provider esterno', + 'Project management' => 'Gestione progetti', + 'Columns' => 'Colonne', + 'Task' => 'Task', + 'Percentage' => 'Percentuale', + 'Number of tasks' => 'Numero di compiti', + 'Task distribution' => 'Distribuzione dei compiti', + 'Analytics' => 'Analitiche', + 'Subtask' => 'Sotto-compito', + 'User repartition' => 'Ripartizione per utente', + '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.', + 'Previous' => 'Precedente', + 'The id must be an integer' => 'L\'id deve essere un intero', + 'The project id must be an integer' => 'L\'id del progetto deve essere un intero', + 'The status must be an integer' => 'Lo status deve essere un intero', + 'The subtask id is required' => 'L\'id del sotto-compito è necessario', + 'The subtask id must be an integer' => 'L\'id del sotto-compito deve essere un intero', + 'The task id is required' => 'Richiesto l\'id del compito', + 'The task id must be an integer' => 'L\'id del compito deve essere un intero', + 'The user id must be an integer' => 'L\'id dell\'utente deve essere un intero', + 'This value is required' => 'Questo valore è necessario', + 'This value must be numeric' => 'Questo valore deve essere numerico', + 'Unable to create this task.' => 'Impossibile creare questo compito', + 'Cumulative flow diagram' => 'Diagramma di flusso cumulativo', + 'Daily project summary' => 'Sommario giornaliero del progetto', + 'Daily project summary export' => 'Export del sommario giornaliero del progetto', + 'Exports' => 'Esporta', + 'This export contains the number of tasks per column grouped per day.' => 'Questo export contiene il numero di compiti per colonna raggruppati per giorno', + 'Active swimlanes' => 'Corsie attive', + 'Add a new swimlane' => 'Aggiungi una corsia', + 'Default swimlane' => 'Corsia predefinita', + 'Do you really want to remove this swimlane: "%s"?' => 'Vuoi davvero rimuovere la seguente corsia: "%s"?', + 'Inactive swimlanes' => 'Corsie inattive', + 'Remove a swimlane' => 'Rimuovi una corsia', + 'Swimlane modification for the project "%s"' => 'Modifica corsia per il progetto "%s"', + 'Swimlane removed successfully.' => 'Corsia rimossa con successo.', + 'Swimlanes' => 'Corsie', + 'Swimlane updated successfully.' => 'Corsia aggiornata con successo.', + 'Unable to remove this swimlane.' => 'Impossibile rimuovere questa corsia.', + 'Unable to update this swimlane.' => 'Impossibile aggiornare questa corsia.', + 'Your swimlane has been created successfully.' => 'La tua corsia è stata creata con successo', + 'Example: "Bug, Feature Request, Improvement"' => 'Esempi: "Bug, Richiesta di Funzioni, Migliorie"', + 'Default categories for new projects (Comma-separated)' => 'Categorie di default per i progetti (Separati da virgola)', + 'Integrations' => 'Integrazioni', + 'Integration with third-party services' => 'Integrazione con servizi di terze parti', + 'Subtask Id' => 'Id del sotto-compito', + 'Subtasks' => 'Sotto-compiti', + 'Subtasks Export' => 'Esporta i sotto-compiti', + 'Task Title' => 'Titolo del compito', + 'Untitled' => 'Senza titolo', + 'Application default' => 'Default dell\'applicazione', + 'Language:' => 'Lingua', + 'Timezone:' => 'Fuso Orario', + 'All columns' => 'Tutte le colonne', + 'Next' => 'Prossimo', + '#%d' => '#%d', + 'All swimlanes' => 'Tutte le corsie', + 'All colors' => 'Tutti i colori', + 'Moved to column %s' => 'Spostato sulla colonna "%s"', + 'User dashboard' => 'Bacheca utente', + 'Allow only one subtask in progress at the same time for a user' => 'Permetti un solo sotto-compito in corso per utente alla volta', + 'Edit column "%s"' => 'Modifica la colonna "%s"', + 'Select the new status of the subtask: "%s"' => 'Seleziona il nuovo status per il sotto-compito: "%s"', + 'Subtask timesheet' => 'Timesheet del sotto-compito', + 'There is nothing to show.' => 'Nulla da mostrare.', + 'Time Tracking' => 'Monitoraggio delle tempistiche', + 'You already have one subtask in progress' => 'Hai già un sotto-compito in corso', + 'Which parts of the project do you want to duplicate?' => 'Quali parti del progetto vuoi duplicare?', + 'Disallow login form' => 'Disabilita il form di login', + 'Start' => 'Inizio', + 'End' => 'Fine', + 'Task age in days' => 'Anzianità del compito in giorni', + 'Days in this column' => 'Giorni in questa colonna', + '%dd' => '%dg', + 'Add a new link' => 'Aggiungi una nuova relazione', + 'Do you really want to remove this link: "%s"?' => 'Vuoi davvero rimuovere la seguente relazione: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Vuoi davvero rimuovere questa relazione dal compito #%d?', + 'Field required' => 'Campo necessario', + 'Link added successfully.' => 'Relazione aggiunta con successo.', + 'Link updated successfully.' => 'Relazione aggiornata con successo.', + 'Link removed successfully.' => 'Relazione rimossa con successo.', + 'Link labels' => 'Etichette delle relazioni', + 'Link modification' => 'Modifica relazione', + 'Opposite label' => 'Etichetta contraria', + 'Remove a link' => 'Rimuovi una relazione', + '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', + 'Unable to create your link.' => 'Impossibile creare la relazione.', + 'Unable to update your link.' => 'Impossibile aggiornare la relazione.', + 'Unable to remove this link.' => 'Impossibile rimuovere la relazione.', + 'relates to' => 'si riferisce a', + 'blocks' => 'blocca', + 'is blocked by' => 'è bloccato da', + 'duplicates' => 'duplica', + 'is duplicated by' => 'è duplicato da', + 'is a child of' => 'è un figlio di', + 'is a parent of' => 'è un genitore di', + 'targets milestone' => 'punta alla milestone', + 'is a milestone of' => 'è una milestone di', + 'fixes' => 'sistema', + 'is fixed by' => 'è sistemato da', + 'This task' => 'Questo compito', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Espandi i compiti', + 'Collapse tasks' => 'Minimizza i compiti', + 'Expand/collapse tasks' => 'Espandi/minimizza i compiti', + 'Close dialog box' => 'Chiudi la finestra di dialogo', + 'Submit a form' => 'Invia i dati', + 'Board view' => 'Vista bacheca', + 'Keyboard shortcuts' => 'Scorciatoie da tastiera', + 'Open board switcher' => 'Apri il selettore di bacheche', + 'Application' => 'Applicazione', + 'Compact view' => 'Vista compatta', + 'Horizontal scrolling' => 'Scrolling orizzontale', + 'Compact/wide view' => 'Vista compatta/estesa', + 'Currency' => 'Valuta', + 'Personal project' => 'Progetto privato', + 'AUD - Australian Dollar' => 'AUD - Dollari Australiani', + 'CAD - Canadian Dollar' => 'CAD - Dollari Canadesi', + 'CHF - Swiss Francs' => 'CHF - Franchi Svizzeri', + 'Custom Stylesheet' => 'Foglio di stile personalizzato', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Pound Inglesi', + 'INR - Indian Rupee' => 'INR - Rupie Indiani', + 'JPY - Japanese Yen' => 'JPY - Yen Giapponesi', + 'NZD - New Zealand Dollar' => 'NZD - Dollari della Nuova Zelanda', + 'PEN - Peruvian Sol' => 'PEN - Sol peruviano', + 'RSD - Serbian dinar' => 'RSD - Dinar serbo', + 'CNY - Chinese Yuan' => 'CNY - Yuan cinese', + 'USD - US Dollar' => 'USD - Dollari Americani', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar venezuelano', + 'Destination column' => 'Colonna destinazione', + 'Move the task to another column when assigned to a user' => 'Sposta il compito in un\'altra colonna quando viene assegnato ad un utente', + 'Move the task to another column when assignee is cleared' => 'Sposta il compito in un\'altra colonna quando l\'assegnatario viene cancellato', + 'Source column' => 'Colonna sorgente', + 'Transitions' => 'Transizioni', + 'Executer' => 'Esecutore', + 'Time spent in the column' => 'Tempo trascorso nella colonna', + 'Task transitions' => 'Transizioni dei compiti', + 'Task transitions export' => 'Export delle transizioni dei compiti', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Questo report contiene tutti gli spotamenti di colonna per ogni compito con le date, l\'utente ed il tempo trascorso per ogni transizione', + 'Currency rates' => 'Tassi di cambio', + 'Rate' => 'Cambio', + 'Change reference currency' => 'Cambia la valuta di riferimento', + 'Reference currency' => 'Valuta di riferimento', + 'The currency rate has 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 removed the assignee of the task %s' => '%s rimuove l\'assegnatario del compito %s', + 'Information' => 'Informazioni', + 'Check two factor authentication code' => 'Controlla il codice di autenticazione "two-factor"', + 'The two factor authentication code is not valid.' => 'Il codice di autenticazione "two-factor" non è valido', + 'The two factor authentication code is valid.' => 'Il codice di autenticazione "two-factor" è valido', + 'Code' => 'Codice', + 'Two factor authentication' => 'Autenticazione "two-factor"', + 'This QR code contains the key URI: ' => 'Questo QR code contiene l\'URI: ', + 'Check my code' => 'Controlla il mio codice', + 'Secret key: ' => 'Chiave privata:', + 'Test your device' => 'Testa il tuo dispositivo', + 'Assign a color when the task is moved to a specific column' => 'Assegna un colore quando il compito viene spostato in una colonna specifica', + '%s via Kanboard' => '%s tramite Kanboard', + 'Burndown chart' => 'Grafico Burndown', + 'This chart show the task complexity over the time (Work Remaining).' => 'Questo grafico mostra la complessità dei compiti nel tempo (Lavoro residuo).', + 'Screenshot taken %s' => 'Schermata catturata %s', + 'Add a screenshot' => 'Aggiungi una schermata', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Cattura una schermata e premi CTRL+V o ⌘+V per incollarla qui.', + 'Screenshot uploaded successfully.' => 'Schermata caricata con successo.', + 'SEK - Swedish Krona' => 'SEK - Corona svedese', + 'Identifier' => 'Identificatore', + 'Disable two factor authentication' => 'Disabilita l\'autenticazione "two-factor"', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Vuoi davvero disabilitare l\'autenticazione "two-factor" per questo utente: "%s"?', + 'Edit link' => 'Modifica relazione', + 'Start to type task title...' => 'Inzia a digitare il titolo di un compito...', + 'A task cannot be linked to itself' => 'Un compito non può essere correlato a se stesso', + 'The exact same link already exists' => 'La stessa relazione risulta già esistente', + 'Recurrent task is scheduled to be generated' => 'Il compito ricorrente è pianificato per essere generato', + 'Score' => 'Punteggio', + 'The identifier must be unique' => 'L\'identificatore deve essere univoco', + 'This linked task id doesn\'t exists' => 'L\'id del compito correlato non esiste', + 'This value must be alphanumeric' => 'Questo valore deve essere alfanumerico', + 'Edit recurrence' => 'Modifica ricorrenza', + 'Generate recurrent task' => 'Genera compito ricorrente', + 'Trigger to generate recurrent task' => 'Trigger per generare il compito ricorrente', + 'Factor to calculate new due date' => 'Fattore numerico per calcolare la nuova data di scadenza', + 'Timeframe to calculate new due date' => 'Lasso temporale per calcolare la nuova data di scadenza', + 'Base date to calculate new due date' => 'Data di partenza per calcolare la nuova data di scadenza', + 'Action date' => 'Data di azione (action date)', + 'Base date to calculate new due date: ' => 'Data di base per calcolare la nuova data di scadenza: ', + 'This task has created this child task: ' => 'Questo compito ha creato il seguente compito figlio: ', + 'Day(s)' => 'Giorno/i', + 'Existing due date' => 'Data di scadenza esistente', + 'Factor to calculate new due date: ' => 'Fattore numerico per calcolare la nuova data di scadenza: ', + 'Month(s)' => 'Mese/i', + 'This task has been created by: ' => 'Questo compito è stato creato da: ', + 'Recurrent task has been generated:' => 'Il compito ricorrente è stato generato:', + 'Timeframe to calculate new due date: ' => 'Lasso temporale per calcolare la nuova data di scadenza: ', + 'Trigger to generate recurrent task: ' => 'Trigger per generare il compito ricorrente: ', + 'When task is closed' => 'Quando un compito è chiuso', + 'When task is moved from first column' => 'Quando un compito è spostato dalla prima colonna', + 'When task is moved to last column' => 'Quando un compito è spostato nell\'ultima colonna', + 'Year(s)' => 'Anno/i', + 'Project settings' => 'Impostazioni di progetto', + 'Automatically update the start date' => 'Aggiorna automaticamente la data di inizio', + 'iCal feed' => 'feed iCal', + 'Preferences' => 'Preferenze', + 'Security' => 'Sicurezza', + 'Two factor authentication disabled' => 'Two factor authentication disabilitata', + 'Two factor authentication enabled' => 'Two factor authentication abilitata', + 'Unable to update this user.' => 'Impossibile aggiornare questo utente.', + 'There is no user management for personal projects.' => 'Non è prevista la gestione di utenti per i progetti privati.', + 'User that will receive the email' => 'Utente che riceverà l\'email', + 'Email subject' => 'Soggetto dell\'email', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'Aggiungi un log quando si sposta un compito tra colonne', + 'Move the task to another column when the category is changed' => 'Sposta il compito in un\'altra colonna quando la categoria viene modificata', + 'Send a task by email to someone' => 'Invia un compito via email a qualcuno', + 'Reopen a task' => 'Riapri un compito', + 'Notification' => 'Notifica', + '%s moved the task #%d to the first swimlane' => '%s ha spostato il compito #%d nella prima corsia', + 'Swimlane' => 'Corsia', + '%s moved the task %s to the first swimlane' => '%s ha spostato il compito %s nella prima corsia', + '%s moved the task %s to the swimlane "%s"' => '%s ha spostato il compito %s nella corsia %s', + 'This report contains all subtasks information for the given date range.' => 'Questo report contiente tutte le informazioni sui sotto-compiti nell\'arco temporale indicato.', + 'This report contains all tasks information for the given date range.' => 'Questo report contiente tutte le informazioni sui compiti nell\'arco temporale indicato.', + 'Project activities for %s' => 'Attività di progetto per %s', + 'view the board on Kanboard' => 'guarda la bacheca su Kanboard', + 'The task has been moved to the first swimlane' => 'Il compito è stato spostato nella prima corsia', + 'The task has been moved to another swimlane:' => 'Il compito è stato spostato in un\'altra corsia:', + 'New title: %s' => 'Nuovo titolo: %s', + 'The task is not assigned anymore' => 'Il compito non è più assegnato a nessuno', + 'New assignee: %s' => 'Nuovo assegnatario: %s', + 'There is no category now' => 'Non è presente più nessuna categoria', + 'New category: %s' => 'Nuova categoria: %s', + 'New color: %s' => 'Nuovo colore: %s', + 'New complexity: %d' => 'Nuova complessità: %d', + 'The due date has been removed' => 'La data di scadenza è stata rimossa', + 'There is no description anymore' => 'Non è presente più alcuna descrizione.', + 'Recurrence settings has been modified' => 'Le impostazioni di ricorrenza sono state modificate', + 'Time spent changed: %sh' => 'Tempo trascorso modificato: %sh', + 'Time estimated changed: %sh' => 'Tempo stimato modificato: %sh', + 'The field "%s" has been updated' => 'Il campo %s è stato aggiornato', + 'The description has been modified:' => 'La descrizione è stata modificata', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vuoi veramente chiudere il compito "%s" e i relativi sotto-compiti?', + 'I want to receive notifications for:' => 'Voglio ricevere le notifiche per:', + 'All tasks' => 'Tutti i compiti', + 'Only for tasks assigned to me' => 'Solo per i compiti assegnati a me', + 'Only for tasks created by me' => 'Solo per i compiti creati da me', + 'Only for tasks created by me and tasks assigned to me' => 'Solo per i compiti creati da me o assegnati a me', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Totale per tutte le colonne', + 'You need at least 2 days of data to show the chart.' => 'Hai bisogno di almeno 2 giorni di dati per mostrare il grafico.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Ferma il timer', + 'Start timer' => 'Avvia il timer', + 'My activity stream' => 'Il mio flusso attività', + 'Search tasks' => 'Ricerca compiti', + 'Reset filters' => 'Annulla filtri', + 'My tasks due tomorrow' => 'I miei compiti da completare per domani', + 'Tasks due today' => 'Task da completare oggi', + 'Tasks due tomorrow' => 'Task da completare per domani', + 'Tasks due yesterday' => 'Task da completare ieri', + 'Closed tasks' => 'Task chiusi', + 'Open tasks' => 'Task aperti', + 'Not assigned' => 'Non assegnato', + 'View advanced search syntax' => 'Visualizza la sintassi di ricerca avanzata', + 'Overview' => 'Panoramica', + 'Board/Calendar/List view' => 'Vista Bacheca/Calendario/Lista', + 'Switch to the board view' => 'Passa alla vista "bacheca"', + 'Switch to the list view' => 'Passa alla vista "elenco"', + 'Go to the search/filter box' => 'Vai alla casella di ricerca/filtro', + 'There is no activity yet.' => 'Non è presente ancora nessuna attività.', + 'No tasks found.' => 'Nessun compito trovato.', + 'Keyboard shortcut: "%s"' => 'Scorciatoia da tastiera: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtro', + 'Advanced search' => 'Ricerca avanzata', + 'Example of query: ' => 'Esempio di query: ', + 'Search by project: ' => 'Ricerca per progetto: ', + 'Search by column: ' => 'Ricerca per colonna: ', + 'Search by assignee: ' => 'Ricerca per assegnatario: ', + 'Search by color: ' => 'Ricerca per colore: ', + 'Search by category: ' => 'Ricerca per categoria: ', + 'Search by description: ' => 'Ricerca per descrizione: ', + 'Search by due date: ' => 'Ricerca per data di scadenza: ', + 'Average time spent in each column' => 'Tempo medio trascorso in ogni colonna', + 'Average time spent' => 'Tempo medio trascorso', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Questo grafico mostra il tempo medio trascorso in ogni colonna per gli ultimi %d compiti.', + 'Average Lead and Cycle time' => 'Tempo medio di consegna (Lead Time) e lavorazione (Cycle Time)', + 'Average lead time: ' => 'Tempo medio di consegna (Lead Time): ', + 'Average cycle time: ' => 'Tempo medio di lavorazione (Cycle Time): ', + 'Cycle Time' => 'Tempo di lavorazione (Cycle Time)', + 'Lead Time' => 'Tempo di consegna (Lead Time)', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Questo grafico mostra i tempi medi di consegna (Lead Time) e lavorazione (Cycle Time) per gli ultimi %d compiti.', + 'Average time into each column' => 'Tempo medio in ogni colonna', + 'Lead and cycle time' => 'Tempo di consegna e lavorazione', + 'Lead time: ' => 'Tempo di consegna (Lead Time): ', + 'Cycle time: ' => 'Tempo di lavorazione (Cycle Time): ', + 'Time spent in 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 compito 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 compito ed il suo completamento.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Se il compito non è chiuso sarà usata la data attuale invece della data di completamento.', + 'Set the start date automatically' => '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 selezioni la casella "Disabilita il form di login", le credenziali immesse nel modulo di accesso verranno ignorate.', + 'Default task color' => 'Colore predefinito dei compiti', + '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.', + 'Trigger automatically subtask time tracking' => 'Attiva automaticamente il time-tracking per i sotto-compiti', + 'Include closed tasks in the cumulative flow diagram' => 'Includi i compiti chiusi nel diagramma di flusso cumulativo', + 'Current swimlane: %s' => 'Corsia attuale: %s', + 'Current column: %s' => 'Colonna attuale: %s', + 'Current category: %s' => 'Categoria attuale: %s', + 'no category' => 'nessuna categoria', + 'Current assignee: %s' => 'Assegnatario attuale: %s', + 'not assigned' => 'non assegnato', + 'Author:' => 'Autore', + 'contributors' => 'contributori', + 'License:' => 'Licenza:', + 'License' => 'Licenza', + 'Enter the text below' => 'Inserisci il testo qui sotto', + 'Start date:' => 'Data di inizio:', + 'Due date:' => 'Data di completamento:', + 'People who are project managers' => 'Persone che sono manager di progetto', + 'People who are project members' => 'Persone che sono membri di progetto', + 'NOK - Norwegian Krone' => 'NOK - Corone norvegesi', + 'Show this column' => 'Mostra questa colonna', + 'Hide this column' => 'Nascondi questa colonna', + 'End date' => 'Data di fine', + 'Users overview' => 'Panoramica utenti', + 'Members' => 'Membri', + 'Shared project' => 'Progetto condiviso', + 'Project managers' => 'Manager di progetto', + 'Projects list' => 'Elenco progetti', + 'End date:' => 'Data di fine:', + 'Change task color when using a specific task link' => 'Cambia colore del compito quando si un utilizza una determinata relazione di compito', + 'Task link creation or modification' => 'Creazione o modifica di relazione di compito', + 'Milestone' => 'Milestone', + 'Reset the search/filter box' => 'Resetta la riceca/filtro', + 'Documentation' => 'Documentazione', + 'Author' => 'Autore', + 'Version' => 'Versione', + 'Plugins' => 'Plugin', + 'There is no plugin loaded.' => 'Nessun plugin è stato caricato.', + 'My notifications' => 'Le mie notifiche', + 'Custom filters' => 'Filtri personalizzati', + 'Your custom filter has been created successfully.' => 'Il filtro personalizzato è stato creato con successo.', + 'Unable to create your custom filter.' => 'Impossibile creare il filtro personalizzato.', + 'Custom filter removed successfully.' => 'Filtro personalizzato rimosso con successo.', + 'Unable to remove this custom filter.' => 'Impossibile rimuovere questo filtro personalizzato', + 'Edit custom filter' => 'Modifica il filtro personalizzato', + 'Your custom filter has been updated successfully.' => 'Il filtro personalizzato è stato aggiornato con successo.', + 'Unable to update custom filter.' => 'Impossibile aggiornare il filtro personalizzato.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nuovo allegato nel compito #%d: %s', + 'New comment on task #%d' => 'Nuovo commento nel compito #%d', + 'Comment updated on task #%d' => 'Commento aggiornato nel compito #%d', + 'New subtask on task #%d' => 'Nuovo sotto-compito nel compito #%d', + 'Subtask updated on task #%d' => 'Sotto-compito aggiornato nel compito #%d', + 'New task #%d: %s' => 'Nuovo compito #%d: %s', + 'Task updated #%d' => 'Task #%d aggiornato', + 'Task #%d closed' => 'Task #%d chiuso', + 'Task #%d opened' => 'Task #%d aperto', + 'Column changed for task #%d' => 'Colonna modificata per il compito #%d', + 'New position for task #%d' => 'Nuova posizione per il compito #%d', + 'Swimlane changed for task #%d' => 'Corsia modificata per il compito #%d', + 'Assignee changed on task #%d' => 'Assegnatario modificato per il compito #%d', + '%d overdue tasks' => '%d compiti scaduti', + 'No notification.' => 'Nessuna nuova notifica.', + 'Mark all as read' => 'Segna tutti come letti', + 'Mark as read' => 'Segna come letto', + 'Total number of tasks in this column across all swimlanes' => 'Numero totale di compito in questa colonna per tutte le corsie', + 'Collapse swimlane' => 'Minimizza corsia', + 'Expand swimlane' => 'Espandi corsia', + 'Add a new filter' => 'Aggiungi un nuovo filtro', + 'Share with all project members' => 'Condividi con tutti i membri del progetto', + 'Shared' => 'Condiviso', + 'Owner' => 'Proprietario', + 'Unread notifications' => 'Notifiche non lette', + 'Notification methods:' => 'Metodi di notifica', + 'Unable to read your file' => 'Impossibile leggere il file', + '%d task(s) have been imported successfully.' => '%d compito/i sono stati importati con successo.', + 'Nothing has been imported!' => 'Non è stato importato nulla!', + 'Import users from CSV file' => 'Importa utenti da file CSV', + '%d user(s) have been imported successfully.' => '%d utenti importati con successo.', + 'Comma' => 'Virgola', + 'Semi-colon' => 'Punto e virgola', + 'Tab' => 'Tabulazione', + 'Vertical bar' => 'Barra verticale', + 'Double Quote' => 'Apice singolo', + 'Single Quote' => 'Doppio apice', + '%s attached a file to the task #%d' => '%s ha allegato un file al compito #%d', + 'There is no column or swimlane activated in your project!' => 'Non ci sono colonne o corsie attive all\'interno del tuo progetto!', + 'Append filter (instead of replacement)' => 'Aggiungi filtro (anzichè sostituirlo)', + 'Append/Replace' => 'Aggiungi/Sostituisci', + 'Append' => 'Aggiungi', + 'Replace' => 'Sostituisci', + 'Import' => 'Importa', + 'Change sorting' => 'cambia ordinamento', + 'Tasks Importation' => 'Importazione compito', + 'Delimiter' => 'Delimitatore', + 'Enclosure' => 'Contenitore', + 'CSV File' => 'File CSV', + 'Instructions' => 'Istruzioni', + 'Your file must use the predefined CSV format' => 'Il file deve rispettare il formato CSV predefinito', + 'Your file must be encoded in UTF-8' => 'Il file deve essere codificato in formato UTF-8', + 'The first row must be the header' => 'La prima riga deve contenere l\'intestazione', + 'Duplicates are not verified for you' => 'Le righe duplicate non verranno controllate', + 'The due date must use the ISO format: YYYY-MM-DD' => 'La data di scadenza deve usare il formato ISO: YYYY-MM-DD', + 'Download CSV template' => 'Scarica il template CSV', + 'No external integration registered.' => 'Nessuna integrazione esterna presente.', + 'Duplicates are not imported' => 'I duplicati non veranno importati', + 'Usernames must be lowercase and unique' => 'I nomi utente devono essere in minuscolo e univoci', + 'Passwords will be encrypted if present' => 'Se presenti, le password verranno criptate', + '%s attached a new file to the task %s' => '%s ha allegato un nuovo file al compito %s', + 'Link type' => 'Tipo di link', + 'Assign automatically a category based on a link' => 'Assegna automaticamente una categoria sulla base di una relazione', + 'BAM - Konvertible Mark' => 'BAM - Marco bosniaco', + 'Assignee Username' => 'Nome utente dell\'assegnatario', + 'Assignee Name' => 'Nome dell\'assegnatario', + 'Groups' => 'Gruppi', + 'Members of %s' => 'Membri di %s', + 'New group' => 'Nuovo gruppo', + 'Group created successfully.' => 'Gruppo creato con successo', + 'Unable to create your group.' => 'Impossibile creare il gruppo', + 'Edit group' => 'Modifica gruppo', + 'Group updated successfully.' => 'Gruppo aggiornato con successo.', + 'Unable to update your group.' => 'Impossibile aggiornare il gruppo', + 'Add group member to "%s"' => 'Aggiungi un membro al gruppo "%s"', + 'Group member added successfully.' => 'Membro del gruppo aggiunto con successo.', + 'Unable to add group member.' => 'Impossibile aggiungere un membro del gruppo', + 'Remove user from group "%s"' => 'Rimuovi utente dal gruppo "%s"', + 'User removed successfully from this group.' => 'Utente rimosso dal gruppo con successo.', + 'Unable to remove this user from the group.' => 'Impossibile rimuovere l\'utentei dal gruppo.', + 'Remove group' => 'Rimuovi gruppo', + 'Group removed successfully.' => 'Gruppo rimosso con successo.', + 'Unable to remove this group.' => 'Impossibile rimuovere questo gruppo.', + 'Project Permissions' => 'Permessi del progetto', + 'Manager' => 'Manager', + 'Project Manager' => 'Manager del progetto', + 'Project Member' => 'Membro del progetto', + 'Project Viewer' => 'Osservatore del progetto', + 'Your account is locked for %d minutes' => 'Il tuo account è bloccato per %d minuti', + 'Invalid captcha' => 'Captcha non valido', + 'The name must be unique' => 'Il nome deve essere univoco', + 'View all groups' => 'Visualiza tutti i gruppi', + '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', + 'Add group member' => 'Aggiungi un membro del gruppo', + 'Do you really want to remove this group: "%s"?' => 'Vuoi davvero rimuovere questo gruppo: "%s"?', + 'There is no user in this group.' => 'Nessun utente in questo gruppo.', + 'Permissions' => 'Permessi', + 'Allowed Users' => 'Utenti autorizzati', + 'No specific user has been allowed.' => 'Nessun utente è stato esplicitamente autorizzato.', + 'Role' => 'Ruolo', + 'Enter user name...' => 'Inserisci il nome utente...', + 'Allowed Groups' => 'Gruppi autorizzati', + 'No group has been allowed.' => 'Nessun gruppo è stato esplicitamente autorizzato.', + 'Group' => 'Gruppo', + 'Group Name' => 'Nome del gruppo', + 'Enter group name...' => 'Inserisci il nome del gruppo...', + 'Role:' => 'Ruolo:', + 'Project members' => 'Membri di progetto', + '%s mentioned you in the task #%d' => '%s ti ha menzionato nel compito #%d', + '%s mentioned you in a comment on the task #%d' => '%s ti ha menzionato in un commento del compito #%d', + 'You were mentioned in the task #%d' => 'Sei stato menzionato nel compito #%d', + 'You were mentioned in a comment on the task #%d' => 'Sei stato menzionato in un commento del compito #%d', + 'Estimated hours: ' => 'Ore stimate: ', + 'Actual hours: ' => 'Ore effettive: ', + 'Hours Spent' => 'Ore impiegate', + 'Hours Estimated' => 'Ore stimate', + 'Estimated Time' => 'Tempo stimato', + 'Actual Time' => 'Tempo effettivo', + 'Estimated vs actual time' => 'Tempo stimato vs Tempo effettivo', + 'RUB - Russian Ruble' => 'RUB - Rublo russo', + 'Assign the task to the person who does the action when the column is changed' => 'Assegna il compito alla persona che esegue l\'azione quando la colonna viene modificata', + 'Close a task in a specific column' => 'Chiudi un compito in una specifica colonna', + 'Time-based One-time Password Algorithm' => 'Algoritmo per la Time-based One-time Password', + 'Two-Factor Provider: ' => 'Provider autenticazione a due fattori: ', + 'Disable two-factor authentication' => 'Disabilita l\'autenticazione "two-factor"', + 'Enable two-factor authentication' => 'Abilita l\'autenticazione "two-factor"', + 'There is no integration registered at the moment.' => 'Nessuna integrazione disponibile al momento.', + 'Password Reset for Kanboard' => 'Reimposta password per Kanboard', + 'Forgot password?' => 'Password dimenticata?', + 'Enable "Forget Password"' => 'Abilita funzione "Password dimenticata"', + 'Password Reset' => 'Reimposta password', + 'New password' => 'Nuova password', + 'Change Password' => 'Cambia password', + 'To reset your password click on this link:' => 'Per reimpostare la tua password clicca su questo link:', + 'Last Password Reset' => 'Ultimo cambio password', + 'The password has never been reinitialized.' => 'La password non è mai stata reimpostata.', + 'Creation' => 'Creazione', + 'Expiration' => 'Scadenza', + 'Password reset history' => 'Storico cambio password', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Tutti i compiti della colonna "%s" e della corsia "%s" sono stati chiusi con successo.', + 'Do you really want to close all tasks of this column?' => 'Vuoi davvero chiudere tutti i compiti di questa colonna?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d compito/i della colonna "%s" e della corsia "%s" saranno chiusi.', + 'Close all tasks in this column and this swimlane' => 'Chiudi tutti i compiti di questa colonna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nessun plugin ha caricato un metodo di notifica di progetto. Puoi tuttavia configurare le notifiche personali dal tuo profilo utente.', + 'My dashboard' => 'La mia bacheca', + 'My profile' => 'Il mio profilo', + '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', + 'Personal 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à', + 'Task priority' => 'Priorità del compito', + 'General' => 'Generale', + 'Dates' => 'Date', + 'Default priority' => 'Priorità predefinita', + 'Lowest priority' => 'Priorità minima', + 'Highest priority' => 'Priorità massima', + 'Close a task when there is no activity' => 'Chiudi un compito quando non c\'è nessuna attività', + 'Duration in days' => 'Durata in giorni', + 'Send email when there is no activity on a task' => 'Invia un\'email quando non c\'è attività sul compito', + 'Unable to fetch link information.' => 'Impossibile recuperare informazioni sul link.', + 'Daily background job for tasks' => 'Job giornaliero in background per i compiti', + 'Auto' => 'Automatico', + 'Related' => 'Correlato', + 'Attachment' => 'Allegato', + 'Web Link' => 'Link Web', + 'External links' => 'Link esterni', + 'Add external link' => 'Aggiungi link esterno', + 'Type' => 'Tipo', + 'Dependency' => 'Dipendenza', + 'Add internal link' => 'Aggiungi link interno', + 'Add a new external link' => 'Aggiungi un nuovo link esterno', + 'Edit external link' => 'Modifica link esterno', + 'External link' => 'Link esterno', + 'Copy and paste your link here...' => 'Copia e incolla il tuo link qui...', + 'URL' => 'URL', + 'Internal links' => 'Link interni', + 'Assign to me' => 'Assegna a me', + 'Me' => 'Me stesso', + 'Do not duplicate anything' => 'Non duplicare nulla', + 'Projects management' => 'Gestione progetti', + 'Users management' => 'Gestione utenti', + 'Groups management' => 'Gestione gruppi', + 'Create from another project' => 'Crea da un\'altro progetto', + 'open' => 'aperto', + 'closed' => 'chiuso', + 'Priority:' => 'Priorità:', + 'Reference:' => 'Riferimento:', + 'Complexity:' => 'Complessità:', + 'Swimlane:' => 'Corsia:', + 'Column:' => 'Colonna:', + 'Position:' => 'Posizione:', + 'Creator:' => 'Creatore:', + 'Time estimated:' => 'Tempo stimato:', + '%s hours' => '%s ore', + 'Time spent:' => 'Tempo trascorso:', + 'Created:' => 'Creato:', + 'Modified:' => 'Modificato:', + 'Completed:' => 'Completato:', + 'Started:' => 'Iniziato:', + 'Moved:' => 'Spostato:', + 'Task #%d' => 'Task #%d', + 'Time format' => 'Formato ora', + 'Start date: ' => 'Data di inizio: ', + 'End date: ' => 'Data di fine: ', + 'New due date: ' => 'Nuova data di scadenza: ', + 'Start date changed: ' => 'Data di inizio cambiata: ', + 'Disable personal projects' => 'Disabilita progetti privati', + 'Do you really want to remove this custom filter: "%s"?' => 'Vuoi davvero rimuovere questo filtro personalizato: "%s"?', + 'Remove a custom filter' => 'Rimuovi un filtro personalizzato', + 'User activated successfully.' => 'Utente attivato con successo.', + 'Unable to enable this user.' => 'Impossibile abilitare questo utente.', + '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.', + 'The maximum allowed file size is %sB.' => 'La dimensione massima consentita del file è %sB.', + 'Drag and drop your files here' => 'Trascina i tuoi file qui', + 'choose files' => 'seleziona i file', + 'View profile' => 'Guarda il profilo', + 'Two Factor' => 'Due fattori', + 'Disable user' => 'Disabilita utente', + 'Do you really want to disable this user: "%s"?' => 'Vuoi davvero disabilitare questo utente: "%s"?', + 'Enable user' => 'Abilita utente', + 'Do you really want to enable this user: "%s"?' => 'Vuoi davvero abilitare questo utente: "%s"?', + 'Download' => 'Scarica', + 'Uploaded: %s' => 'Caricato: %s', + 'Size: %s' => 'Dimensione: %s', + 'Uploaded by %s' => 'Caricato da %s', + 'Filename' => 'Nome del file', + 'Size' => 'Dimensione', + 'Column created successfully.' => 'Colonna creata con successo.', + 'Another column with the same name exists in the project' => 'Un\'altra colonna con lo stesso nome è già esistente in questo progetto', + 'Default filters' => 'Filtri predefiniti', + 'Your board doesn\'t have any columns!' => 'La tua bacheca non ha nessuna colonna!', + 'Change column position' => 'Modifica la posizione della colonna', + 'Switch to the project overview' => 'Passa alla panoramica di progetto', + 'User filters' => 'Filtri utente', + 'Category filters' => 'Filtri di categorie', + 'Upload a file' => 'Carica un file', + 'View file' => 'Visualizza file', + 'Last activity' => 'Attività recente', + 'Change subtask position' => 'Cambia la posizione del sotto-compito', + 'This value must be greater than %d' => 'Questo valore deve essere magiore di %d', + 'Another swimlane with the same name exists in the project' => 'Un\'altra corsia con lo stesso nome è già esistente in questo progetto', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Esempio: https:example.kanboard.org/ (usato per generare URL assolute)', + 'Actions duplicated successfully.' => 'Azioni duplicate con successo.', + 'Unable to duplicate actions.' => 'Impossibile duplicare le azioni.', + 'Add a new action' => 'Aggiungi una nuova azione', + 'Import from another project' => 'Importa da un altro progetto', + 'There is no action at the moment.' => 'Nessuna azione disponibile al momento.', + 'Import actions from another project' => 'Importa azioni da un altro progetto', + 'There is no available project.' => 'Nessun progetto disponibile.', + 'Local File' => 'File locale', + 'Configuration' => 'Configurazione', + 'PHP version:' => 'Versione PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versione OS:', + 'Database version:' => 'Versione database:', + 'Browser:' => 'Browser:', + 'Task view' => 'Vista dei compiti', + 'Edit task' => 'Modifica compito', + 'Edit description' => 'Modifica descrizione', + 'New internal link' => 'Nuovo link interno', + 'Display list of keyboard shortcuts' => 'Mostra una lista di scorciatoie da tastiera', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Carica l\'immagine del mio avatar', + 'Remove my image' => 'Rimuovi la mia immagine', + 'The OAuth2 state parameter is invalid' => 'Il parametro di stato OAuth2 non è valido.', + 'User not found.' => 'Utente non trovato.', + 'Search in activity stream' => 'Ricerca nel mio flusso attività', + 'My activities' => 'Le mie attività', + 'Activity until yesterday' => 'Attività ad oggi', + 'Activity until today' => 'Attività fino a ieri', + 'Search by creator: ' => 'Ricerca per creatore: ', + 'Search by creation date: ' => 'Ricerca per data di creazione: ', + 'Search by task status: ' => 'Ricerca per stato del compito: ', + 'Search by task title: ' => 'Ricerca per titolo del compito: ', + 'Activity stream search' => 'Ricerca nel flusso attività', + 'Projects where "%s" is manager' => 'Progetti all\'interno dei quali "%s" è manager', + 'Projects where "%s" is member' => 'Progetti all\'interno dei quali "%s" è membro', + 'Open tasks assigned to "%s"' => 'Compiti aperti assegnati a "%s"', + 'Closed tasks assigned to "%s"' => 'Compiti chiusi assegnati a "%s"', + 'Assign automatically a color based on a priority' => 'Assegna automaticamente un colore in base alla priorità', + 'Overdue tasks for the project(s) "%s"' => 'Task scaduti per il progetto "%s"', + 'Upload files' => 'Carica file', + 'Installed Plugins' => 'Plugin installati', + 'Plugin Directory' => 'Directory dei plugin', + 'Plugin installed successfully.' => 'Plugin installato con successo.', + 'Plugin updated successfully.' => 'Plugin aggiornato con successo.', + 'Plugin removed successfully.' => 'Plugin rimosso con successo.', + 'Subtask converted to task successfully.' => 'Sotto-compito converito in compito con successo.', + 'Unable to convert the subtask.' => 'Impossibile convertire il sotto-compito.', + 'Unable to extract plugin archive.' => 'Impossibile estrarre l\' archivio del plugin.', + 'Plugin not found.' => 'Plugin non trovato.', + 'You don\'t have the permission to remove this plugin.' => 'Non hai i permessi per rimuovere questo plugin.', + 'Unable to download plugin archive.' => 'Impossibile scaricare l\'archivo del plugin', + 'Unable to write temporary file for plugin.' => 'Impossibile scrivere il file temporaneo per il plugin.', + 'Unable to open plugin archive.' => 'Impossibile aprire l\'archivio del plugin.', + 'There is no file in the plugin archive.' => 'Non ci sono file nell\' archivio del plugin.', + 'Create tasks in bulk' => 'Creazione massiva di compiti', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'La tua installazione Kanboard non è configurata per installare plugin tramite l\'interfaccia utente.', + 'There is no plugin available.' => 'Non ci sono plugin disponibili.', + 'Install' => 'Installa', + 'Update' => 'Aggiorna', + 'Up to date' => 'Aggiornato', + 'Not available' => 'Non disponibile', + 'Remove plugin' => 'Disinstalla plugin', + 'Do you really want to remove this plugin: "%s"?' => 'Vuoi davvero rimuovere questo plugin: "%s"?', + 'Uninstall' => 'Disinstalla', + 'Listing' => 'Elenco', + 'Metadata' => 'Metadati', + 'Manage projects' => 'Gestione progetti', + 'Convert to task' => 'Converti in compito', + 'Convert sub-task to task' => 'Converti il sotto-compito in compito', + 'Do you really want to convert this sub-task to a task?' => 'Vuoi davvero convertire questo sotto-compito in un compito?', + 'My task title' => 'Titolo del mio compito', + 'Enter one task by line.' => 'Inserisci un compito per ogni riga.', + 'Number of failed login:' => 'Numero di login falliti:', + 'Account locked until:' => 'Account bloccato fino al:', + 'Email settings' => 'Impostazioni Email', + 'Email sender address' => 'Indirizzo Email mittente', + 'Email transport' => 'Trasporto Email', + '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.', + 'Tag updated successfully.' => 'Tag aggiornato con successo.', + 'Unable to update this tag.' => 'Impossibile aggiornare questo tag.', + 'Tag removed successfully.' => 'Tag rimosso con successo.', + 'Unable to remove this tag.' => 'Impossibile rimuovere questo tag.', + 'Global tags management' => 'Gestione dei tag globali', + 'Tags' => 'Tag', + 'Tags management' => 'Gestione dei tag', + 'Add new tag' => 'Aggiungi un nuovo tag', + 'Edit a tag' => 'Modifica un tag', + 'Project tags' => 'Tag di progetto', + 'There is no specific tag for this project at the moment.' => 'Non è definito nessun tag specifico per questo progetto al momento.', + 'Tag' => 'Tag', + 'Remove a tag' => 'Rimuovi un tag', + 'Do you really want to remove this tag: "%s"?' => 'Vuoi davvero rimuovere questo tag: "%s"?', + 'Global tags' => 'Tag globali', + 'There is no global tag at the moment.' => 'Non sono definiti tag globali al momento.', + 'This field cannot be empty' => 'Questo campo non può essere vuoto', + 'Close a task when there is no activity in a specific column' => 'Chiudi un compito quando non vi è alcuna attività in una specifica colonna', + '%s removed a subtask for the task #%d' => '%s rimosso un sotto-compito per il compito #%d', + '%s removed a comment on the task #%d' => '%s rimosso un commento nel compito #%d', + 'Comment removed on task #%d' => 'Commento rimosso nel compito #%d', + 'Subtask removed on task #%d' => 'Sotto-compito rimosso nel compito #%d', + 'Hide tasks in this column in the dashboard' => 'Nascondi i compiti in questa colonna nella dashboard', + '%s removed a comment on the task %s' => '%s rimosso un commento nel compito %s', + '%s removed a subtask for the task %s' => '%s rimosso un sotto-compito per il compito %s', + 'Comment removed' => 'Commento rimosso', + 'Subtask removed' => 'Sotto-compito rimosso', + '%s set a new internal link for the task #%d' => '%s imposta un nuovo link interno per il compito #%d', + '%s removed an internal link for the task #%d' => '%s rimosso un link interno per il compito #%d', + 'A new internal link for the task #%d has been defined' => 'E\' stato definito un nuovo link interno per il compito #%d', + 'Internal link removed for the task #%d' => 'Link interno rimosso per il compito #%d', + '%s set a new internal link for the task %s' => '%s imposta un nuovo link interno per il compito %s', + '%s removed an internal link for the task %s' => '%s rimosso un link interno per il compito %s', + 'Automatically set the due date on task creation' => 'Imposta automaticamente la data di scadenza alla creazione del compito', + 'Move the task to another column when closed' => 'Sposta il compito su un\'altra colonna quando viene chiuso', + 'Move the task to another column when not moved during a given period' => 'Sposta il compito su un\'altra colonna quando non viene spostato entro un certo periodo', + 'Dashboard for %s' => 'Dashboard per %s', + 'Tasks overview for %s' => 'Panoramica dei compiti per %s', + 'Subtasks overview for %s' => 'Panoramica dei compiti per %s', + 'Projects overview for %s' => 'Panoramica dei progetti per %s', + 'Activity stream for %s' => 'Flusso attività per %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Assegna un colore quando il compito viene spostato su una specifica corsia', + 'Assign a priority when the task is moved to a specific swimlane' => 'Assegna una priorità quando il compito viene spostato su una specifica corsia', + 'User unlocked successfully.' => 'Utente sbloccato con successo.', + 'Unable to unlock the user.' => 'Impossibile sbloccare l\'utente.', + 'Move a task to another swimlane' => 'Sposta un compito su un\'altra corsia', + '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 compito su un\'altra posizione nella board', + 'Insert before this task' => 'Inserisci prima di questo compito', + 'Insert after this task' => 'Inserisci dopo questo compito', + '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 compiti tra queste colonne', + 'Close a task in a specific column when not moved during a given period' => 'Chiudi un compito 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 compiti in questa colonna.', + 'Task creation is permitted for this column' => 'La creazione di compiti è permessa per questa colonna', + 'Closing or opening a task is permitted for this column' => 'Chiudere o aprire compito è permesso per questa colonna', + 'Task creation is blocked for this column' => 'La creazione di compiti è bloccata per questa colonna', + 'Closing or opening a task is blocked for this column' => 'Chiudere o aprire compito è bloccato per questa colonna', + 'Task creation is not permitted' => 'Creazione compito non permessa', + 'Closing or opening a task is not permitted' => 'Chiudere o aprire compito 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 compiti 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 un compito 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 compito.', + 'API User Access' => 'API per l\'accesso utente', + 'Preview' => 'Anteprima', + 'Write' => 'Scrivi', + 'Write your text in Markdown' => 'Scrivi il tuo testo in Markdown', + 'No personal API access token registered.' => 'Nessun token di accesso API personale registrato.', + 'Your personal API access token is "%s"' => 'Il tuo token di accesso API personale è "%s"', + 'Remove your token' => 'Rimuovi il tuo token', + 'Generate a new token' => 'Genera un nuovo token', + 'Showing %d-%d of %d' => 'Visualizzazione %d-%d di %d', + 'Outgoing Emails' => 'Email in uscita', + 'Add or change currency rate' => 'Aggiungi o modifica la valuta di riferimento', + 'Reference currency: %s' => 'Valuta di riferimento: %s', + 'Add custom filters' => 'Aggiungi filtri personalizzati', + 'Export' => 'Esporta', + 'Add link label' => 'Aggiungi un\'etichetta al link', + 'Incompatible Plugins' => 'Plugin incompatibili', + 'Compatibility' => 'Compatibilità', + 'Permissions and ownership' => 'Permessi e assegnazioni', + 'Priorities' => 'Priorità', + 'Close this window' => 'Chiudi questa finestra', + 'Unable to upload this file.' => 'Non è stato possibile caricare questo file.', + 'Import tasks' => 'Importa compiti', + 'Choose a project' => 'Scegli un progetto', + 'Profile' => 'Profilo', + 'Application role' => 'Ruolo applicativo', + '%d invitations were sent.' => 'Sono stati spediti %d inviti.', + '%d invitation was sent.' => 'E\' stato spedito %d invito.', + 'Unable to create this user.' => 'Non è stato possibile creare questo utente.', + 'Kanboard Invitation' => 'Invito a Kanboard', + 'Visible on dashboard' => 'Visibile sulla dashboard', + 'Created at:' => 'Creato il:', + 'Updated at:' => 'Aggiornato il:', + 'There is no custom filter.' => 'Non c\'è alcun filtro personalizzato.', + 'New User' => 'Nuovo Utente', + 'Authentication' => 'Autenticazione', + 'If checked, this user will use a third-party system for authentication.' => 'Quando selezionato, l\'utente utilizzerà un sistema di terza parte per l\'autenticazione.', + 'The password is necessary only for local users.' => 'La password è obbligatoria solo per gli utenti locali.', + 'You have been invited to register on Kanboard.' => 'Sei stato invitato a registrarti su Kanboard.', + 'Click here to join your team' => 'Clicca qui per accedere al tuo gruppo di lavoro', + 'Invite people' => 'Invita altre persone', + 'Emails' => 'Emails', + 'Enter one email address by line.' => 'Inserisci un indirizzo email per riga.', + 'Add these people to this project' => 'Aggiungi queste persone al progetto', + 'Add this person to this project' => 'Aggiungi questa persona al progetto', + 'Sign-up' => 'Registrati', + 'Credentials' => 'Credenziali', + 'New user' => 'Nuovo utente', + 'This username is already taken' => 'Questo nome utente esiste già', + 'Your profile must have a valid email address.' => 'Il tuo profilo deve avere un indirizzo email valido.', + 'TRL - Turkish Lira' => 'TRL - Lira Turca', + 'The project email is optional and could be used by several plugins.' => 'L\'email del progetto è opzionale e può essere utilizzata da plugin differenti.', + 'The project email must be unique across all projects' => 'L\'email del progetto deve essere univoca tra tutti i progetti', + 'The email configuration has been disabled by the administrator.' => 'La configurazione della email è stata disabilitata dall\'amministratore', + 'Close this project' => 'Chiudi questo progetto', + 'Open this project' => 'Apri questo progetto', + 'Close a project' => 'Chiudi un progetto', + 'Do you really want to close this project: "%s"?' => 'Vuoi chiudere veramente questo progetto: "%s"? ', + 'Reopen a project' => 'Riapri un progetto', + 'Do you really want to reopen this project: "%s"?' => 'Vuoi riaprire veramente questo progetto: "%s"?', + 'This project is open' => 'Questo progetto è aperto', + 'This project is closed' => 'Questo progetto è chiuso', + 'Unable to upload files, check the permissions of your data folder.' => 'Non è stato possibile caricare i file, verifica i permessi della cartella contenente i dati.', + 'Another category with the same name exists in this project' => 'Esiste già una categoria con lo stesso nome in questo progetto', + 'Comment sent by email successfully.' => 'Commento inviato per email con successo.', + 'Sent by email to "%s" (%s)' => 'Inviato per email a "%s" (%s)', + 'Unable to read uploaded file.' => 'Non è stato possibile leggere il file caricato.', + 'Database uploaded successfully.' => 'Database caricato con successo.', + 'Task sent by email successfully.' => 'Task inviato per email con successo.', + 'There is no category in this project.' => 'Non esiste alcuna categoria in questo progetto.', + 'Send by email' => 'Invia per email', + 'Create and send a comment by email' => 'Crea ed invia un commento per email', + 'Subject' => 'Oggetto', + 'Upload the database' => 'Carica il database', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Puoi caricare il database Sqlite precedentemente scaricato (Gzip format).', + 'Database file' => 'File del database', + 'Upload' => 'Carica', + 'Your project must have at least one active swimlane.' => 'Il tuo progetto deve avere almeno una corsia attiva.', + 'Project: %s' => 'Progetto: %s', + 'Automatic action not found: "%s"' => 'Azione automatica non trovata: "%s"', + '%d projects' => '%d progetti', + '%d project' => '%d progetto', + 'There is no project.' => 'Non esiste alcun progetto.', + 'Sort' => 'Ordina', + 'Project ID' => 'ID progetto', + 'Project name' => 'Nome del progetto', + 'Public' => 'Pubblico', + 'Personal' => 'Privato', + '%d tasks' => '%d dei compiti', + '%d task' => '%d del compito', + 'Task ID' => 'ID del compito', + 'Assign automatically a color when due date is expired' => 'Assegna automaticamente un colore quando la data di scadenza è stata raggiunta', + 'Total score in this column across all swimlanes' => 'Punteggio totale di questa colonna in tutte le corsie', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentino', + 'COP - Colombian Peso' => 'COP - Peso Colombiano', + '%d groups' => '%d gruppi', + '%d group' => '%d gruppo', + 'Group ID' => 'ID gruppo', + 'External ID' => 'ID esterno', + '%d users' => '%d utenti', + '%d user' => '%d utente', + 'Hide subtasks' => 'Nascondi sotto-compiti', + 'Show subtasks' => 'Mostra sotto-compiti', + 'Authentication Parameters' => 'Parametri di Autenticazione', + 'API Access' => 'Accesso API', + 'No users found.' => 'Nessun utente trovato.', + 'User ID' => 'ID utente', + 'Notifications are activated' => 'Le notifiche sono attivate', + 'Notifications are disabled' => 'Le notifiche sono disattivate', + 'User disabled' => 'Utente disabilitato', + '%d notifications' => '%d notifiche', + '%d notification' => '%d notifica', + 'There is no external integration installed.' => 'Nessuna integrazione esterna installata.', + 'You are not allowed to update tasks assigned to someone else.' => 'Non sei abilitato ad aggiornare i compiti assegnati a qualcun altro.', + 'You are not allowed to change the assignee.' => 'Non sei abilitato a modificare l\'assegnatario', + 'Task suppression is not permitted' => 'L\'eliminazione del compito non è permessa', + 'Changing assignee is not permitted' => 'Non è permesso modificare l\'assegnatario', + 'Update only assigned tasks is permitted' => 'E\' permesso aggiornare solo i compiti assegnati', + 'Only for tasks assigned to the current user' => 'Solo per i compiti assegnati all\'utente corrente', + 'My projects' => 'I miei progetti', + 'You are not a member of any project.' => 'Non sei membro di alcun progetto.', + 'My subtasks' => 'I miei sotto-compiti', + '%d subtasks' => '%d sotto-compiti', + '%d subtask' => '%d sotto-compiti', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'I compiti assegnati all\'utente corrente possono essere solo spostati in quelle colonne', + '[DUPLICATE]' => '[DUPLICA]', + 'DKK - Danish Krona' => 'DKK - Corona Danese', + 'Remove user from group' => 'Rimuovi utente dal gruppo', + 'Assign the task to its creator' => 'Assegna il compito al suo creatore', + 'This task was sent by email to "%s" with subject "%s".' => 'Questo compito è stato inviato per email a "%s" con oggetto "%s".', + 'Predefined Email Subjects' => 'Oggetti email predefiniti', + 'Write one subject by line.' => 'Scrivi un oggetto per riga.', + 'Create another link' => 'Crea un altro link', + 'BRL - Brazilian Real' => 'BRL - Real Brasiliano', + 'Add a new Kanboard task' => 'Crea un nuovo compito Kanboard', + 'Subtask not started' => 'Sotto-compito non iniziato', + 'Subtask currently in progress' => 'Sotto-compito attualmente in corso', + 'Subtask completed' => 'Sotto-compito completato', + 'Subtask added successfully.' => 'Sotto-compito aggiunto con successo.', + '%d subtasks added successfully.' => '%d sotto-compiti aggiunti con successo.', + 'Enter one subtask by line.' => 'Inserisci un sotto-compito per riga.', + 'Predefined Contents' => 'Contenuti Predefiniti', + 'Predefined contents' => 'Contenuti predefiniti', + 'Predefined Task Description' => 'Descrizione predefinita del compito', + 'Do you really want to remove this template? "%s"' => 'Vuoi eliminare veramente questo template? "%s"', + 'Add predefined task description' => 'Aggiungi una descrizione predefinita del compito', + 'Predefined Task Descriptions' => 'Descrizioni predefinite dei compito', + 'Template created successfully.' => 'Il template è stato creato correttamente.', + 'Unable to create this template.' => 'Non è stato possibile creare questo template.', + 'Template updated successfully.' => 'Template aggiornato con successo.', + 'Unable to update this template.' => 'Non è stato possibile aggiornare questo template.', + 'Template removed successfully.' => 'Template eliminato con successo.', + 'Unable to remove this template.' => 'Non è stato possibile eliminare questo template.', + 'Template for the task description' => 'Template per la descrizione del compito', + 'The start date is greater than the end date' => 'La data di inizio è superiore alla data di fine', + 'Tags must be separated by a comma' => 'I tag devono essere separati da una virgola', + 'Only the task title is required' => 'Solo il titolo del compito è obbligatorio', + 'Creator Username' => 'Nome utente del creatore', + 'Color Name' => 'Nome del colore', + 'Column Name' => 'Nome della colonna', + 'Swimlane Name' => 'Nome della corsia', + 'Time Estimated' => 'Tempo stimato', + 'Time Spent' => 'Tempo effettivo', + 'External Link' => 'Link esterno', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Questa funzionalità abilita il feed iCal, il feed RSS e la visualizzazione pubblica della dashboard', + 'Stop the timer of all subtasks when moving a task to another column' => 'Interrompi il timer di tutti i sotto-compiti quando un compito viene spostato in un\'altra colonna', + 'Subtask Title' => 'Titolo del sotto-compito', + 'Add a subtask and activate the timer when moving a task to another column' => 'Aggiungi un sotto-compito e attiva il timer quando un compito viene spostato in un\'altra colonna', + 'days' => 'giorni', + 'minutes' => 'minuti', + 'seconds' => 'secondi', + 'Assign automatically a color when preset start date is reached' => 'Assegna automaticamente un colore quando la data di inzio prestabilita viene raggiunta', + 'Move the task to another column once a predefined start date is reached' => 'Sposta il compito in un\'altra colonna quando si raggiunge una data predefinita', + 'This task is now linked to the task %s with the relation "%s"' => 'Questo compito è ora collegato al compito %s tramite la relazione "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Il collegamento con la relazione "%s" al compito %s è stato rimosso', + 'Custom Filter:' => 'Filtro personalizzato', + 'Unable to find this group.' => 'Non è stato possibile trovare questo gruppo', + '%s moved the task #%d to the column "%s"' => '%s ha spostato il compito #%d nella colonna "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s ha spostato il compito #%d a posizione %d nella colonna "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s ha spostato il compito #%d nella corsia "%s"', + '%sh spent' => '%sh effettive', + '%sh estimated' => '%sh stimate', + 'Select All' => 'Seleziona tutti', + 'Unselect All' => 'Deseleziona tutti', + 'Apply action' => 'Applica azione', + 'Move selected tasks to another column or swimlane' => 'Sposta i compiti selezionati in un\'altra colonna', + 'Edit tasks in bulk' => 'Modifica i compiti massivamente', + 'Choose the properties that you would like to change for the selected tasks.' => 'Scegli le proprietà che vorresti modificare per i compiti selezionato.', + 'Configure this project' => 'Configura questo progetto', + 'Start now' => 'Inizia ora', + '%s removed a file from the task #%d' => '%s ha rimosso un file dal compito #%d', + 'Attachment removed from task #%d: %s' => 'Allegato rimosso dal compito #%d: %s', + 'No color' => 'Nessun colore', + 'Attachment removed "%s"' => 'Allegato rimosso "%s"', + '%s removed a file from the task %s' => '%s ha rimosso un file dal compito %s', + 'Move the task to another swimlane when assigned to a user' => 'Sposta il compito in un\'altra corsia quando viene assegnato ad un utente', + 'Destination swimlane' => 'Corsia di destinazione', + 'Assign a category when the task is moved to a specific swimlane' => 'Assegna una categoria quando il compito viene spostato in una corsia specifica', + 'Move the task to another swimlane when the category is changed' => 'Sposta il compito in un\'altra corsia quando la categoria viene modificata', + 'Reorder this column by priority (ASC)' => 'Ordina questa colonna per priorità (ASC)', + 'Reorder this column by priority (DESC)' => 'Ordina questa colonna per priorità (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Ordina questa colonna per assegnatario e per priorità (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Ordina questa colonna per assegnatario e per priorità (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Ordina questa colonna per assegnatario (ASC)', + 'Reorder this column by assignee (Z-A)' => 'Ordina questa colonna per assegnatario (DESC)', + 'Reorder this column by due date (ASC)' => 'Riordina questa colonna per dat di scadenza (ASCENDENTE)', + 'Reorder this column by due date (DESC)' => 'Riordina questa colonna per dat di scadenza (DISCENDENTE)', + 'Reorder this column by id (ASC)' => 'Riordina questa colonna per ID (ASC)', + 'Reorder this column by id (DESC)' => 'Riordina questa colonna per ID (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s ha spostato il compito #%d "%s" al progetto "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Il compito #%d "%s"è stato spostato al progetto "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Sposta il compito ad un\'altra colonna se la dat di scadenza è più vicina di un certo numero di giorni', + 'Automatically update the start date when the task is moved away from a specific column' => 'Aggiorna automaticamente la data di inizio quando il compito è tolto da una colonna specifica', + 'HTTP Client:' => 'Client HTTP', + 'Assigned' => 'Assegnato', + 'Task limits apply to each swimlane individually' => 'I limiti di compito si applicano individualmente ad ogni corsia', + 'Column task limits apply to each swimlane individually' => 'I limiti di compito per colonna si applicano individualmente ad ogni corsia', + 'Column task limits are applied to each swimlane individually' => 'I limiti di compito per colonna vengono applicati individualmente ad ogni corsia', + 'Column task limits are applied across swimlanes' => 'I limiti di compito per colonna vengono applicati a tutte le corsie', + 'Task limit: ' => 'Limite di compito', + 'Change to global tag' => 'Modifica all\'etichetta globale', + 'Do you really want to make the tag "%s" global?' => 'Vuoi davvero rendere globale l\'etichetta "%s"?', + 'Enable global tags for this project' => 'Abilita le etichette globali per questo progetto', + 'Group membership(s):' => 'Assegnazioni di gruppo', + '%s is a member of the following group(s): %s' => '%s è membro dei gruppi seguenti: %s', + '%d/%d group(s) shown' => '%d/%d gruppi mostrati', + 'Subtask creation or modification' => 'Creazione o modifica sotto-compiti', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Assegna il compito ad un utente specifico quando il compito viene spostato ad una corsia specifica', + 'Comment' => 'Commento', + 'Collapse vertically' => 'Riduci verticalmente', + 'Expand vertically' => 'Espandi verticalmente', + 'MXN - Mexican Peso' => 'MXN - Peso messicano', + 'Estimated vs actual time per column' => 'Tempo stimato vs reale per colonna', + 'HUF - Hungarian Forint' => 'HUF - Fiorino ungherese', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Devi selezionare un file da caricare come avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Il file caricato non è un\'immagine valida! (Sono consentiti solo *.gif, *.jpg, *.jpeg e *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Imposta automaticamente la data di scadenza quando il task viene spostato da una colonna specifica', + 'No other projects found.' => 'Nessun altro progetto trovato.', + 'Tasks copied successfully.' => 'Attività copiate con successo.', + 'Unable to copy tasks.' => 'Impossibile copiare le attività.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema chiaro', + 'Dark theme' => 'Tema scuro', + 'Automatic theme - Sync with system' => 'Tema automatico - Sincronizza con il sistema', + 'Application managers or more' => 'Gestori dell\'applicazione o più', + 'Administrators' => 'Amministratori', + 'Visibility:' => 'Visibilità:', + 'Standard users' => 'Utenti standard', + 'Visibility is required' => 'La visibilità è obbligatoria', + 'The visibility should be an app role' => 'La visibilità deve essere un ruolo dell\'applicazione', + 'Reply' => 'Rispondi', + '%s wrote: ' => '%s ha scritto: ', + 'Number of visible tasks in this column and swimlane' => 'Numero di attività visibili in questa colonna e swimlane', + 'Number of tasks in this swimlane' => 'Numero di attività in questa swimlane', + 'Unable to find another subtask in progress, you can close this window.' => 'Impossibile trovare un\'altra sotto-attività in corso, puoi chiudere questa finestra.', + 'This theme is invalid' => 'Questo tema non è valido', + 'This role is invalid' => 'Questo ruolo non è valido', + 'This timezone is invalid' => 'Questo fuso orario non è valido', + 'This language is invalid' => 'Questa lingua non è valida', + 'This URL is invalid' => 'Questo URL non è valido', + 'Date format invalid' => 'Formato data non valido', + 'Time format invalid' => 'Formato ora non valido', + 'Invalid Mail transport' => 'Trasporto email non valido', + 'Color invalid' => 'Colore non valido', + 'This value must be greater or equal to %d' => 'Questo valore deve essere maggiore o uguale a %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Aggiungi un BOM all\'inizio del file (richiesto per Microsoft Excel)', + 'Just add these tag(s)' => 'Aggiungi solo queste etichette', + 'Remove internal link(s)' => 'Rimuovi i collegamenti interni', + 'Import tasks from another project' => 'Importa attività da un altro progetto', + 'Select the project to copy tasks from' => 'Seleziona il progetto da cui copiare le attività', + 'The total maximum allowed attachments size is %sB.' => 'La dimensione massima totale consentita per gli allegati è %sB.', + 'Add attachments' => 'Aggiungi allegati', + 'Task #%d "%s" is overdue' => 'Il compito #%d "%s" è scaduto', + 'Enable notifications by default for all new users' => 'Abilita le notifiche per impostazione predefinita per tutti i nuovi utenti', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Assegna l\'attività al suo creatore per colonne specifiche se non è stato impostato manualmente alcun assegnatario', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Assegna un\'attività all\'utente connesso al cambio di colonna verso la colonna specificata se nessun utente è assegnato', +]; diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php new file mode 100644 index 0000000..c3614b1 --- /dev/null +++ b/app/Locale/ja_JP/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'ãªã—', + 'Edit' => '編集', + 'Remove' => '削除', + 'Yes' => 'ã¯ã„', + 'No' => 'ã„ã„ãˆ', + 'cancel' => 'キャンセル', + 'or' => 'ã¾ãŸã¯', + 'Yellow' => 'イエロー', + 'Blue' => 'ブルー', + 'Green' => 'グリーン', + 'Purple' => 'パープル', + 'Red' => 'レッド', + 'Orange' => 'オレンジ', + 'Grey' => 'グレー', + 'Brown' => 'ブラウン', + 'Deep Orange' => 'ディープオレンジ', + 'Dark Grey' => 'ダークグレー', + 'Pink' => 'ピンク', + 'Teal' => 'ティール', + 'Cyan' => 'シアン', + 'Lime' => 'ライム', + 'Light Green' => 'ライトグリーン', + 'Amber' => 'アンãƒãƒ¼', + 'Save' => 'ä¿å­˜', + 'Login' => 'ログイン', + 'Official website:' => 'å…¬å¼ Webサイト:', + 'Unassigned' => '担当ãªã—', + 'View this task' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã‚’見る', + 'Remove user' => 'ユーザーã®å‰Šé™¤', + 'Do you really want to remove this user: "%s"?' => 'ユーザー「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'All users' => 'ã™ã¹ã¦ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'Username' => 'ユーザーå', + 'Password' => 'パスワード', + 'Administrator' => 'システム管ç†è€…', + 'Sign in' => 'ログイン', + 'Users' => 'ユーザー', + 'Forbidden' => 'アクセス拒å¦', + 'Access Forbidden' => 'ã‚¢ã‚¯ã‚»ã‚¹ãŒæ‹’å¦ã•れã¾ã—ãŸ', + 'Edit user' => 'ユーザーを編集ã™ã‚‹', + 'Logout' => 'ログアウト', + 'Bad username or password' => 'ユーザーåã¾ãŸã¯ãƒ‘スワードãŒé•ã„ã¾ã™', + 'Edit project' => 'プロジェクトを編集', + 'Name' => 'åå‰', + 'Projects' => 'プロジェクト', + 'No project' => 'プロジェクトãŒã‚りã¾ã›ã‚“', + 'Project' => 'プロジェクト', + 'Status' => 'ステータス', + 'Tasks' => 'タスク', + 'Board' => 'ボード', + 'Actions' => 'アクション', + 'Inactive' => '無効', + 'Active' => '有効', + 'Unable to update this board.' => 'ボードを更新ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Disable' => '無効ã«ã™ã‚‹', + 'Enable' => '有効ã«ã™ã‚‹', + 'New project' => 'プロジェクト作æˆ', + 'Do you really want to remove this project: "%s"?' => 'プロジェクト「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Remove project' => 'プロジェクトã®å‰Šé™¤', + 'Edit the board for "%s"' => 'ボード「%sã€ã‚’編集', + 'Add a new column' => 'カラムã®è¿½åŠ ', + 'Title' => 'タイトル', + 'Assigned to %s' => '%sãŒæ‹…当', + 'Remove a column' => 'カラムã®å‰Šé™¤', + 'Unable to remove this column.' => 'カラムを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Do you really want to remove this column: "%s"?' => 'カラム「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Settings' => '設定', + 'Application settings' => 'アプリケーション設定', + 'Language' => '言語', + 'Webhook token:' => 'Webhook トークン:', + 'API token:' => 'API トークン:', + 'Database size:' => 'データベースã®ã‚µã‚¤ã‚ºï¼š', + 'Download the database' => 'データベースã®ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰', + 'Optimize the database' => 'ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®æœ€é©åŒ–', + '(VACUUM command)' => '(VACUUM コマンド)', + '(Gzip compressed Sqlite file)' => '(GZip コマンドã§åœ§ç¸®ã•れ㟠Sqlite ファイル)', + 'Close a task' => 'タスクを完了', + 'Column' => 'カラム', + 'Color' => '色', + 'Assignee' => '担当', + 'Create another task' => 'ç¶šã‘ã¦åˆ¥ã®ã‚¿ã‚¹ã‚¯ã‚’追加', + 'New task' => 'タスクを追加', + 'Open a task' => 'タスクを開ã', + 'Do you really want to open this task: "%s"?' => 'タスク「%sã€ã‚’作æˆã—ã¾ã™ã‹ï¼Ÿ', + 'Back to the board' => 'ãƒœãƒ¼ãƒ‰ã«æˆ»ã‚‹', + 'There is nobody assigned' => '担当者ãŒã„ã¾ã›ã‚“', + 'Column on the board:' => 'カラム:', + 'Close this 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 文字必è¦ã§ã™', + 'The password is required' => 'パスワードãŒå¿…è¦ã§ã™', + 'This value must be an integer' => 'æ•´æ•°ã§å…¥åŠ›ã—ã¦ãã ã•ã„', + 'The username must be unique' => 'ユーザーåãŒã™ã§ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™', + 'The user id is required' => 'ユーザー ID ãŒå¿…è¦ã§ã™', + 'Passwords don\'t match' => 'パスワードãŒä¸€è‡´ã—ã¾ã›ã‚“', + 'The confirmation is required' => '確èªç”¨ã®ãƒ‘スワードを入力ã—ã¦ãã ã•ã„', + 'The project is required' => 'プロジェクトãŒå¿…è¦ã§ã™', + 'The id is required' => 'ID ãŒå¿…è¦ã§ã™', + 'The project id is required' => 'プロジェクト ID ãŒå¿…è¦ã§ã™', + 'The project name is required' => 'プロジェクトåãŒå¿…è¦ã§ã™', + 'The title is required' => 'タイトルãŒå¿…è¦ã§ã™', + 'Settings saved successfully.' => '設定をä¿å­˜ã—ã¾ã—ãŸ', + 'Unable to save your settings.' => '設定ã®ä¿å­˜ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Database optimization done.' => 'ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã®æœ€é©åŒ–ãŒçµ‚ã‚りã¾ã—ãŸ', + 'Your project has been created successfully.' => 'プロジェクトを作æˆã—ã¾ã—ãŸ', + 'Unable to create your project.' => 'プロジェクトã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Project updated successfully.' => 'プロジェクトを更新ã—ã¾ã—ãŸ', + 'Unable to update this project.' => 'ãƒ—ãƒ­ã‚¸ã‚§ã‚¯ãƒˆã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Unable to remove this project.' => 'プロジェクトã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Project removed successfully.' => 'プロジェクトを削除ã—ã¾ã—ãŸ', + 'Project activated successfully.' => 'プロジェクトを有効ã«ã—ã¾ã—ãŸ', + 'Unable to activate this project.' => 'プロジェクトを有効ã«ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Project disabled successfully.' => 'プロジェクトを無効ã«ã—ã¾ã—ãŸ', + 'Unable to disable this project.' => 'プロジェクトを無効ã«ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Unable to open this task.' => 'タスクã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task opened successfully.' => 'タスクを作æˆã—ã¾ã—ãŸ', + 'Unable to close this task.' => 'タスクã®å®Œäº†ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task closed successfully.' => 'タスクを完了ã—ã¾ã—ãŸ', + 'Unable to update your task.' => 'ã‚¿ã‚¹ã‚¯ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task updated successfully.' => 'タスクを更新ã—ã¾ã—ãŸ', + 'Unable to create your task.' => 'タスクã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Task created successfully.' => 'タスクを追加ã—ã¾ã—ãŸ', + 'User created successfully.' => 'ユーザーを追加ã—ã¾ã—ãŸ', + 'Unable to create your user.' => 'ユーザーã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'User updated successfully.' => 'ユーザーを更新ã—ã¾ã—ãŸ', + 'User removed successfully.' => 'ユーザーを削除ã—ã¾ã—ãŸ', + 'Unable to remove this user.' => 'ユーザーã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Board updated successfully.' => 'ボードを更新ã—ã¾ã—ãŸ', + 'Ready' => 'Ready', + 'Backlog' => 'Backlog', + 'Work in progress' => 'Work in progress', + 'Done' => 'Done', + 'Application version:' => 'アプリケーションã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', + 'Id' => 'ID', + 'Public link' => '公開アクセス用リンク', + 'Timezone' => 'タイムゾーン', + 'Sorry, I didn\'t find this information in my database!' => 'ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ä¸Šã§æƒ…å ±ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸï¼', + 'Page not found' => 'ページãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'Complexity' => '複雑ã•', + 'Task limit' => 'タスク数制é™', + 'Task count' => 'タスク数', + 'User' => 'ユーザー', + 'Comments' => 'コメント', + 'Comment is required' => 'コメントを入力ã—ã¦ãã ã•ã„', + 'Comment added successfully.' => 'コメントを追加ã—ã¾ã—ãŸ', + 'Unable to create your comment.' => 'コメントã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Due Date' => '期é™', + 'Invalid date' => '日付ãŒç„¡åйã§ã™', + 'Automatic actions' => '自動アクションã®ç®¡ç†', + 'Your automatic action has been created successfully.' => '自動アクションを作æˆã—ã¾ã—ãŸ', + 'Unable to create your automatic action.' => '自動アクションã®ä½œæˆã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Remove an action' => '自動アクションã®å‰Šé™¤', + 'Unable to remove this action.' => '自動アクションã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Action removed successfully.' => '自動アクションã®å‰Šé™¤ã«æˆåŠŸã—ã¾ã—ãŸ', + 'Automatic actions for the project "%s"' => 'プロジェクト「%sã€ã®è‡ªå‹•アクション', + 'Add an action' => '自動アクションã®è¿½åŠ ', + 'Event name' => 'イベントå', + 'Action' => 'アクション', + 'Event' => 'イベント', + 'When the selected event occurs execute the corresponding action.' => 'é¸æŠžã•れãŸã‚¤ãƒ™ãƒ³ãƒˆãŒç™ºç”Ÿã—ãŸæ™‚ã€å¯¾å¿œã™ã‚‹ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‚’実行', + 'Next step' => '次ã®ã‚¹ãƒ†ãƒƒãƒ—', + '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 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' => '色をユーザーã«å‰²å½“ã¦ã‚‹', + 'Position' => 'ä½ç½®', + 'Duplicate to project' => '別プロジェクトã«è¤‡è£½', + 'Duplicate' => '複製', + 'Link' => 'リンク', + 'Comment updated successfully.' => 'コメントを更新ã—ã¾ã—ãŸ', + 'Unable to update your comment.' => 'ã‚³ãƒ¡ãƒ³ãƒˆã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Remove a comment' => 'コメントを削除', + 'Comment removed successfully.' => 'コメントを削除ã—ã¾ã—ãŸ', + 'Unable to remove this comment.' => 'コメントã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Do you really want to remove this comment?' => 'コメントを削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Current password for the user "%s"' => 'ユーザー「%sã€ã®ç¾åœ¨ã®ãƒ‘スワード', + 'The current password is required' => 'ç¾åœ¨ã®ãƒ‘スワードを入力ã—ã¦ãã ã•ã„', + 'Wrong password' => 'パスワードãŒé•ã„ã¾ã™', + 'Unknown' => '䏿˜Ž', + 'Last logins' => 'ログインã®ä¸€è¦§', + 'Login date' => 'ログイン日時', + 'Authentication method' => 'èªè¨¼æ–¹æ³•', + 'IP address' => 'IP アドレス', + 'User agent' => 'ユーザーエージェント', + 'Persistent connections' => '既存ã®ã‚³ãƒã‚¯ã‚·ãƒ§ãƒ³', + 'No session.' => 'セッションãªã—', + 'Expiration date' => '有効期é™', + 'Remember Me' => '次回ã‹ã‚‰è‡ªå‹•çš„ã«ãƒ­ã‚°ã‚¤ãƒ³', + 'Creation date' => 'ä½œæˆæ—¥', + 'Everybody' => '全員', + 'Open' => '作æˆ', + 'Closed' => '完了', + 'Search' => '検索', + 'Nothing found.' => 'çµæžœãªã—', + 'Due date' => '期é™', + 'Description' => '説明', + '%d comments' => '%d 個ã®ã‚³ãƒ¡ãƒ³ãƒˆ', + '%d comment' => '%d 個ã®ã‚³ãƒ¡ãƒ³ãƒˆ', + '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.' => 'ã‚ãªãŸã®å¤–部アカウントã¯ãƒ—ãƒ­ãƒ•ã‚£ãƒ¼ãƒ«ã«æ­£å¸¸ã«ãƒªãƒ³ã‚¯ã—ã¦ã„ã¾ã™', + 'Email' => 'Email', + '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' => '色ã«åŸºã„ã¦ã‚«ãƒ†ã‚´ãƒªã‚’変ãˆã‚‹', + 'Task creation or modification' => 'タスクã®ä½œæˆã¾ãŸã¯å¤‰æ›´', + 'Category' => 'カテゴリ', + 'Category:' => 'カテゴリ:', + 'Categories' => 'カテゴリ', + 'Your category has been created successfully.' => 'カテゴリを作æˆã—ã¾ã—ãŸ', + 'This category has been updated successfully.' => 'カテゴリを更新ã—ã¾ã—ãŸ', + 'Unable to update this 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' => 'カテゴリãªã—', + 'The name is required' => 'åå‰ã‚’入力ã—ã¦ãã ã•ã„', + 'Remove a file' => 'ファイルã®å‰Šé™¤', + 'Unable to remove this file.' => 'ファイルã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'File removed successfully.' => 'ファイルを削除ã—ã¾ã—ãŸ', + 'Attach a document' => 'ドキュメントを添付', + 'Do you really want to remove this file: "%s"?' => 'ファイル「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Attachments' => '添付', + 'Edit the task' => 'タスクを編集', + 'Add a comment' => 'コメントを追加', + 'Edit a comment' => 'コメントを編集', + 'Summary' => 'è¦ç´„', + 'Time tracking' => '時間ã®è¿½è·¡', + 'Estimate:' => '見ç©ï¼š', + 'Spent:' => '経éŽï¼š', + 'Do you really want to remove this sub-task?' => 'サブタスクを削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Remaining:' => '残:', + 'hours' => '時間', + 'estimated' => '見ç©', + 'Sub-Tasks' => 'サブタスク', + 'Add a sub-task' => 'サブタスクを追加', + 'Original estimate' => 'åˆæœŸã®è¦‹ç©', + 'Create another sub-task' => 'ç¶šã‘ã¦åˆ¥ã®ã‚µãƒ–タスクを追加', + 'Time spent' => 'çµŒéŽæ™‚é–“', + 'Edit a sub-task' => 'サブタスクを編集', + 'Remove a sub-task' => 'サブタスクを削除', + 'The time must be a numeric value' => 'æ™‚é–“ã¯æ•°å­—ã§å…¥åŠ›ã—ã¦ãã ã•ã„', + 'Todo' => '作業予定', + 'In progress' => '作業中', + 'Sub-task removed successfully.' => 'サブタスクを削除ã—ã¾ã—ãŸ', + 'Unable to remove this sub-task.' => 'サブタスクã®å‰Šé™¤ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Sub-task updated successfully.' => 'サブタスクを更新ã—ã¾ã—ãŸ', + 'Unable to update your sub-task.' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã®æ›´æ–°ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Unable to create your sub-task.' => 'サブタスクã®è¿½åŠ ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Maximum size: ' => '最大:', + 'Display another project' => '別ã®ãƒ—ロジェクトを表示', + 'Created by %s' => '%s ãŒä½œæˆ', + 'Tasks Export' => 'タスクã®å‡ºåŠ›', + 'Start Date' => 'é–‹å§‹æ—¥', + 'Execute' => '実行', + 'Task Id' => 'タスク ID', + 'Creator' => '作æˆè€…', + 'Modification date' => 'æ›´æ–°æ—¥', + 'Completion date' => '完了日', + 'Clone' => '複製', + 'Project cloned successfully.' => 'プロジェクトを複製ã—ã¾ã—ãŸ', + 'Unable to clone this project.' => 'プロジェクトã®è¤‡è£½ã«å¤±æ•—ã—ã¾ã—ãŸ', + 'Enable email notifications' => 'メール通知を設定', + 'Task position:' => 'タスクã®ä½ç½®ï¼š', + 'The task #%d has been opened.' => 'タスク #%d を作æˆã—ã¾ã—ãŸ', + 'The task #%d has been closed.' => 'タスク #%d を完了ã—ã¾ã—ãŸ', + 'Sub-task updated' => 'ã‚µãƒ–ã‚¿ã‚¹ã‚¯ã®æ›´æ–°', + 'Title:' => 'タイトル:', + 'Status:' => 'ステータス:', + 'Assignee:' => '担当:', + 'Time tracking:' => '時間計測:', + 'New sub-task' => 'æ–°ã—ã„サブタスク', + 'New attachment added "%s"' => '添付ファイル「%sã€ãŒè¿½åŠ ã•れã¾ã—ãŸ', + 'New comment posted by %s' => '「%sã€ã®æ–°ã—ã„コメントãŒè¿½åŠ ã•れã¾ã—ãŸ', + 'New comment' => 'æ–°ã—ã„コメント', + 'Comment updated' => 'ã‚³ãƒ¡ãƒ³ãƒˆãŒæ›´æ–°ã•れã¾ã—ãŸ', + 'New subtask' => 'æ–°ã—ã„サブタスク', + 'I only want to receive notifications for these projects:' => '以下ã®ãƒ—ロジェクトã«ã®ã¿é€šçŸ¥ã‚’å—ã‘å–る:', + 'view the task on Kanboard' => 'Kanboard ã§ã‚¿ã‚¹ã‚¯ã‚’見る', + 'Public access' => '公開アクセス', + 'Disable public access' => '公開アクセスを無効ã«ã™ã‚‹', + 'Enable public access' => '公開アクセスを有効ã«ã™ã‚‹', + 'Public access disabled' => '公開アクセスã¯ç„¡åŠ¹åŒ–ã•れã¦ã„ã¾ã™', + 'Move the task to another project' => 'タスクを別プロジェクトã«ç§»å‹•', + 'Move to project' => '別プロジェクトã«ç§»å‹•', + 'Do you really want to duplicate this task?' => 'タスクを複製ã—ã¾ã™ã‹ï¼Ÿ', + 'Duplicate a task' => 'タスクã®è¤‡è£½', + 'External accounts' => '外部アカウント', + 'Account type' => 'アカウントã®ç¨®é¡ž', + 'Local' => 'ローカル', + 'Remote' => 'リモート', + 'Enabled' => '有効', + 'Disabled' => '無効', + 'Login:' => 'ユーザーå:', + 'Full Name:' => 'åå‰ï¼š', + 'Email:' => 'Email:', + 'Notifications:' => '通知:', + 'Notifications' => '通知', + 'Account type:' => 'アカウントã®ç¨®é¡žï¼š', + 'Edit profile' => 'プロフィールã®ç·¨é›†', + 'Change password' => 'パスワードã®å¤‰æ›´', + 'Password modification' => 'パスワードã®å¤‰æ›´', + 'External authentications' => '外部èªè¨¼', + 'Never connected.' => '未接続', + '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/%s時間 ã«å¤‰æ›´ã—ã¾ã—ãŸ', + '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 フィード', + '%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)' => 'æ–°è¦ãƒ—ãƒ­ã‚¸ã‚§ã‚¯ãƒˆã®æ—¢å®šã‚«ãƒ©ãƒ  (コンマã§åŒºåˆ‡ã£ã¦å…¥åŠ›)', + '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' => 'イベントã®é¸æŠž', + 'Create a task from an external provider' => 'タスクを外部サービスã‹ã‚‰ä½œæˆ', + 'Change the assignee based on an external username' => '担当者を外部サービスã«åŸºã„ã¦å¤‰æ›´', + 'Change the category based on an external label' => 'カテゴリを外部サービスã«åŸºã„ã¦å¤‰æ›´', + 'Reference' => 'å‚ç…§', + 'Label' => 'ラベル', + 'Database' => 'データベース', + 'About' => '情報', + 'Database driver:' => 'データベースドライãƒï¼š', + 'Board settings' => '基本設定', + 'Webhook settings' => 'Webhook ã®è¨­å®š', + 'Reset token' => 'トークンã®ãƒªã‚»ãƒƒãƒˆ', + 'API endpoint:' => 'API エンドãƒã‚¤ãƒ³ãƒˆï¼š', + 'Refresh interval for personal 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 ç§’)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'ç§’æ•° (0 ã¯æ©Ÿèƒ½ã‚’ç„¡åŠ¹åŒ–ã€æ—¢å®š 10 ç§’)', + 'Application URL' => 'アプリケーション㮠URL', + 'Token regenerated.' => 'トークンãŒå†ç”Ÿæˆã•れã¾ã—ãŸ', + 'Date format' => 'データã®ãƒ•ォーマット', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO フォーマットãŒå…¥åŠ›ã§ãã¾ã™(例: %s ã¾ãŸã¯ %s)', + 'New personal project' => 'éžå…¬é–‹ãƒ—ロジェクト作æˆ', + 'This project is personal' => 'ã“ã®ãƒ—ロジェクトã¯éžå…¬é–‹ã§ã™', + 'Add' => '追加', + 'Start date' => '開始時間', + 'Time estimated' => 'è¦‹ç©æ™‚é–“', + 'There is nothing assigned to you.' => '何もアサインã•れã¦ã„ã¾ã›ã‚“', + 'My tasks' => '自分ã®ã‚¿ã‚¹ã‚¯', + 'Activity stream' => '活動状æ³', + 'Dashboard' => 'ダッシュボード', + 'Confirmation' => '確èª', + 'Webhooks' => 'Webhook', + 'API' => 'API', + 'Create a comment from an external provider' => '外部サービスã‹ã‚‰ã‚³ãƒ¡ãƒ³ãƒˆã‚’作æˆ', + 'Project management' => 'プロジェクト管ç†', + 'Columns' => 'カラム', + 'Task' => 'タスク', + 'Percentage' => '割åˆ', + 'Number of tasks' => 'タスク数', + 'Task distribution' => 'タスク分布', + 'Analytics' => '分æž', + 'Subtask' => 'サブタスク', + 'User repartition' => '担当者分布', + 'Clone this project' => 'ã“ã®ãƒ—ロジェクトを複製', + 'Column removed successfully.' => 'カラムを削除ã—ã¾ã—ãŸ', + 'Not enough data to show the graph.' => 'グラフをæç”»ã™ã‚‹ãŸã‚ã®ãƒ‡ãƒ¼ã‚¿ãŒä¸è¶³ã—ã¦ã„ã¾ã™', + 'Previous' => '戻る', + 'The id must be an integer' => 'id ã¯æ•°å­—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'The project id must be an integer' => 'project id ã¯æ•°å­—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'The status must be an integer' => 'status ã¯æ•°å­—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'The subtask id is required' => 'subtask id ãŒå¿…è¦ã§ã™', + 'The subtask id must be an integer' => 'subtask id ã¯æ•°å­—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'The task id is required' => 'task id ãŒå¿…è¦ã§ã™', + 'The task id must be an integer' => 'task id ã¯æ•°å­—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'The user id must be an integer' => 'user id ã¯æ•°å­—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'This value is required' => 'ã“ã®å€¤ãŒå¿…è¦ã§ã™', + 'This value must be numeric' => 'ã“ã®å€¤ã¯æ•°å­—ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'Unable to create this task.' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã‚’作æˆã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Cumulative flow diagram' => 'ç´¯ç©ãƒ•ロー図', + 'Daily project summary' => '日時プロジェクトサマリー', + 'Daily project summary export' => '日時プロジェクトサマリーã®å‡ºåŠ›', + 'Exports' => '出力', + 'This export contains the number of tasks per column grouped per day.' => 'ã“ã®å‡ºåŠ›ã¯æ—¥æ™‚ã®ã‚«ãƒ©ãƒ ã”ã¨ã®ã‚¿ã‚¹ã‚¯æ•°ã‚’集計ã—ãŸã‚‚ã®ã§ã™', + 'Active swimlanes' => 'アクティブãªã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³', + 'Add a new swimlane' => 'æ–°ã—ã„スイムレーン', + 'Default swimlane' => '既定スイムレーン', + 'Do you really want to remove this swimlane: "%s"?' => 'スイムレーン「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'Inactive swimlanes' => '無効ãªã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³', + 'Remove a swimlane' => 'スイムレーンã®å‰Šé™¤', + 'Swimlane modification for the project "%s"' => '「%sã€ã«å¯¾ã™ã‚‹ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³å¤‰æ›´', + 'Swimlane removed successfully.' => 'スイムレーンを削除ã—ã¾ã—ãŸ', + 'Swimlanes' => 'スイムレーン', + 'Swimlane updated successfully.' => 'スイムレーンを更新ã—ã¾ã—ãŸ', + 'Unable to remove this swimlane.' => 'スイムレーンを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Unable to update this swimlane.' => 'スイムレーンを更新ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'Your swimlane has been created successfully.' => 'スイムレーンãŒä½œæˆã•れã¾ã—ãŸ', + 'Example: "Bug, Feature Request, Improvement"' => '例: ãƒã‚°, 機能, 改善', + 'Default categories for new projects (Comma-separated)' => 'æ–°ã—ã„ãƒ—ãƒ­ã‚¸ã‚§ã‚¯ãƒˆã®æ—¢å®šã‚«ãƒ†ã‚´ãƒªãƒ¼ (コンマ区切り)', + 'Integrations' => '連æº', + 'Integration with third-party services' => 'サードパーティサービスã¨ã®é€£æº', + 'Subtask Id' => 'サブタスク Id', + 'Subtasks' => 'サブタスク', + 'Subtasks Export' => 'サブタスクã®å‡ºåŠ›', + 'Task Title' => 'タスクタイトル', + 'Untitled' => 'タイトル無ã—', + 'Application default' => 'ã‚¢ãƒ—ãƒªã‚±ãƒ¼ã‚·ãƒ§ãƒ³ã®æ—¢å®šå€¤', + 'Language:' => '言語:', + 'Timezone:' => 'タイムゾーン:', + 'All columns' => 'å…¨ã¦ã®ã‚«ãƒ©ãƒ ', + 'Next' => '次ã¸', + '#%d' => '#%d', + 'All swimlanes' => 'å…¨ã¦ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³', + 'All colors' => 'å…¨ã¦ã®è‰²', + 'Moved to column %s' => 'カラム %s ã¸ç§»å‹•ã—ã¾ã—ãŸ', + 'User dashboard' => 'ユーザーダッシュボード', + 'Allow only one subtask in progress at the same time for a user' => '1ユーザーã«ã¤ã1ä»¶ã®ã‚¿ã‚¹ã‚¯ã®ã¿ã‚’進行中ã«ã§ãるよã†åˆ¶é™', + 'Edit column "%s"' => 'カラム「%sã€ã®ç·¨é›†', + 'Select the new status of the subtask: "%s"' => 'サブタスク「%sã€ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹ã‚’é¸æŠž', + 'Subtask timesheet' => 'サブタスクã®ã‚¿ã‚¤ãƒ ã‚·ãƒ¼ãƒˆ', + 'There is nothing to show.' => '何も表示ã™ã‚‹ã‚‚ã®ãŒã‚りã¾ã›ã‚“', + 'Time Tracking' => '時間ã®è¿½è·¡', + 'You already have one subtask in progress' => 'ã™ã§ã«é€²è¡Œä¸­ã®ã‚µãƒ–タスクãŒã‚りã¾ã™', + 'Which parts of the project do you want to duplicate?' => 'プロジェクトã®ä½•を複製ã—ã¾ã™ã‹ï¼Ÿ', + 'Disallow login form' => 'ログインフォームã‹ã‚‰ã®ãƒ­ã‚°ã‚¤ãƒ³ã‚’許å¯ã—ãªã„', + 'Start' => 'é–‹å§‹', + 'End' => '終了', + 'Task age in days' => 'タスクã®çµŒéŽæ—¥æ•°', + 'Days in this column' => 'カラムã§ã®çµŒéŽæ—¥æ•°', + '%dd' => '%d æ—¥', + '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' => 'フィールドãŒå¿…è¦ã§ã™', + 'Link added successfully.' => 'リンクを追加ã—ã¾ã—ãŸ', + 'Link updated successfully.' => 'リンクを更新ã—ã¾ã—ãŸ', + 'Link removed successfully.' => 'リンクを削除ã—ã¾ã—ãŸ', + 'Link labels' => 'リンクラベル', + 'Link modification' => 'リンクã®å¤‰æ›´', + 'Opposite label' => 'å対ã®ãƒ©ãƒ™ãƒ«', + 'Remove a link' => 'ラベルã®å‰Šé™¤', + '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.' => 'リンクを削除ã§ãã¾ã›ã‚“ã§ã—ãŸ', + 'relates to' => '次ã«é–¢é€£ã—ã¾ã™', + 'blocks' => '次をブロックã—ã¦ã„ã¾ã™', + 'is blocked by' => '次ã«ãƒ–ロックã•れã¦ã„ã¾ã™', + 'duplicates' => '次ã«é‡è¤‡ã—ã¦ã„ã¾ã™', + 'is duplicated by' => '次ã«é‡è¤‡ã—ã¦ã„ã¾ã™', + 'is a child of' => '次ã®å­ã‚¿ã‚¹ã‚¯ã§ã™ã€€', + 'is a parent of' => '次ã®è¦ªã‚¿ã‚¹ã‚¯ã§ã™', + 'targets milestone' => '次ã®ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã‚’目標ã¨ã—ã¾ã™', + 'is a milestone of' => '次ã®ã‚¿ã‚¹ã‚¯ã®ãƒžã‚¤ãƒ«ã‚¹ãƒˆãƒ¼ãƒ³ã§ã™', + 'fixes' => '次を修正ã—ã¾ã™', + 'is fixed by' => '次ã«ä¿®æ­£ã•れã¾ã™', + 'This task' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã¯', + '<1h' => '<1時間', + '%dh' => '%d 時間', + 'Expand tasks' => 'タスクを詳細表示', + 'Collapse tasks' => 'タスクを簡略表示', + 'Expand/collapse tasks' => 'タスクã®å±•é–‹ï¼é–‰ã˜ã‚‹', + 'Close dialog box' => 'ダイアログボックスを閉ã˜ã‚‹', + 'Submit a form' => 'フォームをé€ä¿¡', + 'Board view' => 'ボードビュー', + 'Keyboard shortcuts' => 'キーボードショートカット', + 'Open board switcher' => 'ボード切り替ãˆã‚’é–‹ã', + 'Application' => 'アプリケーション', + 'Compact view' => 'コンパクトビュー', + 'Horizontal scrolling' => '横スクロール', + 'Compact/wide view' => 'コンパクトï¼ãƒ¯ã‚¤ãƒ‰ãƒ“ュー', + 'Currency' => '通貨', + 'Personal project' => 'éžå…¬é–‹ãƒ—ロジェクト', + 'AUD - Australian Dollar' => 'AUD - 豪ドル', + 'CAD - Canadian Dollar' => 'CAD - 加ドル', + 'CHF - Swiss Francs' => 'CHF - スイスフラン', + 'Custom Stylesheet' => 'カスタムスタイルシート', + 'EUR - Euro' => 'EUR - ユーロ', + 'GBP - British Pound' => 'GBP - 独ãƒãƒ³ãƒ‰', + 'INR - Indian Rupee' => 'INR - 伊ルピー', + 'JPY - Japanese Yen' => 'JPY - 日本円', + 'NZD - New Zealand Dollar' => 'NZD - NZ ドル', + 'PEN - Peruvian Sol' => 'PEN - ペルー・ソル', + 'RSD - Serbian dinar' => 'RSD - セルビアデナール', + 'CNY - Chinese Yuan' => 'CNY - 中国元', + 'USD - US Dollar' => 'USD - 米ドル', + 'VES - Venezuelan Bolívar' => 'VES - ベãƒã‚ºã‚¨ãƒ©ãƒ»ãƒœãƒªãƒãƒ«', + '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' => 'カラムã§ã®çµŒéŽæ™‚é–“', + '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.' => 'ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã¯ã‚¿ã‚¹ã‚¯ã®ã‚«ãƒ©ãƒ é–“ã«ãŠã‘る移動を時間ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€çµŒéŽæ™‚é–“ã¨å…±ã«è¨˜éŒ²ã—ãŸç‰©ã§ã™', + 'Currency rates' => '為替レート', + 'Rate' => 'レート', + 'Change reference currency' => 'ç¾åœ¨ã®åŸºè»¸é€šè²¨', + 'Reference currency' => '基軸通貨', + 'The currency rate has been added successfully.' => 'é€šè²¨ãƒ¬ãƒ¼ãƒˆãŒæ­£å¸¸ã«è¿½åŠ ã•れã¾ã—ãŸ', + 'Unable to add this currency rate.' => 'ã“ã®é€šè²¨ãƒ¬ãƒ¼ãƒˆã‚’追加ã§ãã¾ã›ã‚“', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ã€Œ%sã€ã®æ‹…当を解除ã—ã¾ã—ãŸ', + 'Information' => '情報 ', + 'Check two factor authentication code' => '二è¦ç´ èªè¨¼ã‚’確èª', + 'The two factor authentication code is not valid.' => '二è¦ç´ èªè¨¼ã‚³ãƒ¼ãƒ‰ã¯ç„¡åйã§ã™', + 'The two factor authentication code is valid.' => '二è¦ç´ èªè¨¼ã‚³ãƒ¼ãƒ‰ã¯æœ‰åйã§ã™', + 'Code' => 'コード', + 'Two factor authentication' => '二è¦ç´ èªè¨¼', + 'This QR code contains the key URI: ' => 'ã“ã® QR コード㌠URI キーをå«ã‚“ã§ã„ã¾ã™ï¼š', + 'Check my code' => '自分ã®ã‚³ãƒ¼ãƒ‰ã‚’ãƒã‚§ãƒƒã‚¯', + 'Secret key: ' => '秘密éµï¼š', + 'Test your device' => 'ã‚ãªãŸã®ãƒ‡ãƒã‚¤ã‚¹ã‚’テスト', + 'Assign a color when the task is moved to a specific column' => 'タスクãŒç‰¹å®šã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã—ãŸæ™‚ã®è‰²ã‚’設定', + '%s via Kanboard' => '%s by Kanboard', + 'Burndown chart' => 'ãƒãƒ¼ãƒ³ãƒ€ã‚¦ãƒ³ãƒãƒ£ãƒ¼ãƒˆ', + '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 - スウェーデン クローナ', + 'Identifier' => '識別å­', + 'Disable two factor authentication' => '二è¦ç´ èªè¨¼ã‚’無効ã«ã™ã‚‹', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '本当ã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã€Œ%sã€ã®äºŒè¦ç´ èªè¨¼ã‚’無効ã«ã—ã¾ã™ã‹ï¼Ÿ', + 'Edit link' => 'リンクを編集', + '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' => 'å復タスクãŒç”Ÿæˆã•れるよã†ã«ã‚¹ã‚±ã‚¸ãƒ¥ãƒ¼ãƒ«ã•れã¦ã„ã¾ã™', + 'Score' => 'スコア', + 'The identifier must be unique' => '識別å­ã¯ãƒ¦ãƒ‹ãƒ¼ã‚¯ã§ãªã‘れã°ãªã‚Šã¾ã›ã‚“', + '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' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹ãŸã‚ã®æ™‚é–“æž ', + '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: ' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹è¦ç´ ï¼š', + 'Month(s)' => 'ヶ月', + 'This task has been created by: ' => 'タスク作æˆè€…', + 'Recurrent task has been generated:' => 'å復タスクãŒç”Ÿæˆã•れã¾ã—ãŸï¼š', + 'Timeframe to calculate new due date: ' => 'æ–°ã—ã„æœŸé™ã‚’計算ã™ã‚‹æ™‚間枠:', + 'Trigger to generate recurrent task: ' => 'å復タスクを生æˆã™ã‚‹ãŸã‚ã®ãƒˆãƒªã‚¬ãƒ¼ï¼š', + 'When task is closed' => 'タスクãŒå®Œäº†ã—ãŸã¨ã', + 'When task is moved from first column' => 'ã‚¿ã‚¹ã‚¯ãŒæœ€åˆã®ã‚«ãƒ©ãƒ ã‹ã‚‰ç§»å‹•ã•れãŸã¨ã', + 'When task is moved to last column' => 'ã‚¿ã‚¹ã‚¯ãŒæœ€å¾Œã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã—ãŸã¨ã', + 'Year(s)' => 'å¹´', + 'Project settings' => 'プロジェクト設定', + 'Automatically update the start date' => 'é–‹å§‹æ—¥ã‚’è‡ªå‹•çš„ã«æ›´æ–°', + 'iCal feed' => 'iCal é…ä¿¡', + 'Preferences' => 'プリファレンス', + 'Security' => 'セキュリティ', + 'Two factor authentication disabled' => '二è¦ç´ èªè¨¼ã¯ç„¡åйã§ã™', + 'Two factor authentication enabled' => '二è¦ç´ èªè¨¼ã¯æœ‰åйã§ã™', + 'Unable to update this user.' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’æ›´æ–°ã§ãã¾ã›ã‚“', + 'There is no user management for personal 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' => 'カテゴリãŒå¤‰æ›´ã•れãŸã¨ãã«ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', + 'Send a task by email to someone' => 'タスクをメールã§é€ä¿¡', + 'Reopen a task' => 'タスクをå†é–‹', + 'Notification' => '通知', + '%s moved the task #%d to the first swimlane' => '%sã¯ã‚¿ã‚¹ã‚¯ï¼ƒ%dを最åˆã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ç§»å‹•ã—ã¾ã—ãŸ', + 'Swimlane' => 'スイムレーン', + '%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.' => 'ã“ã®ãƒ¬ãƒãƒ¼ãƒˆã«ã¯ã€æŒ‡å®šã—ãŸæœŸé–“ã®ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯æƒ…å ±ãŒå«ã¾ã‚Œã¾ã™', + 'Project activities for %s' => '%sã®ãƒ—ãƒ­ã‚¸ã‚§ã‚¯ãƒˆã®æ´»å‹•状æ³', + 'view the board on Kanboard' => 'Kanboard上ã®ãƒœãƒ¼ãƒ‰ã‚’見る', + 'The task has been moved to the first swimlane' => 'ã‚¿ã‚¹ã‚¯ã¯æœ€åˆã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ç§»å‹•ã•れã¾ã—ãŸ', + 'The task has been moved to another swimlane:' => 'ã‚¿ã‚¹ã‚¯ã¯æ¬¡ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ç§»å‹•ã—ã¾ã—ãŸï¼š', + 'New title: %s' => 'æ–°ã—ã„タイトル:%s', + 'The task is not assigned anymore' => 'タスクã¯ã‚‚ã†å‰²å½“ã¦ã‚‰ã‚Œã¾ã›ã‚“', + 'New assignee: %s' => 'æ–°ã—ã„æ‹…当者:%s', + 'There is no category now' => 'カテゴリã¯ã‚りã¾ã›ã‚“', + 'New category: %s' => 'æ–°ã—ã„カテゴリ:%s', + 'New color: %s' => 'æ–°ã—ã„色:%s', + 'New complexity: %d' => 'æ–°ã—ã„複雑ã•:%d', + 'The due date has been removed' => '期é™ãŒå‰Šé™¤ã•れã¾ã—ãŸ', + 'There is no description anymore' => '説明ã¯ã‚りã¾ã›ã‚“', + 'Recurrence settings has been modified' => 'å復設定ãŒå¤‰æ›´ã•れã¾ã—ãŸ', + 'Time spent changed: %sh' => 'çµŒéŽæ™‚é–“ãŒå¤‰æ›´ã•れã¾ã—ãŸï¼š%sh', + 'Time estimated changed: %sh' => 'è¦‹ç©æ™‚é–“ãŒå¤‰æ›´ã•れã¾ã—ãŸï¼š%sh', + 'The field "%s" has 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:' => '次ã®é€šçŸ¥ã‚’å—ä¿¡ã—ã¾ã™ï¼š', + 'All tasks' => 'ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯', + 'Only for tasks assigned to me' => '自分ã®ã‚¿ã‚¹ã‚¯ã®ã¿', + 'Only for tasks created by me' => '自分ãŒä½œæˆã—ãŸã‚¿ã‚¹ã‚¯ã®ã¿', + 'Only for tasks created by me and tasks assigned to me' => '自分ãŒä½œæˆã—ãŸè‡ªåˆ†ã®ã‚¿ã‚¹ã‚¯ã®ã¿', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'ã™ã¹ã¦ã®ã‚«ãƒ©ãƒ ã®åˆè¨ˆ', + 'You need at least 2 days of data to show the chart.' => 'グラフを表示ã™ã‚‹ã«ã¯æœ€ä½Ž2日間ã®ãƒ‡ãƒ¼ã‚¿ãŒå¿…è¦ã§ã™', + '<15m' => '15分未満', + '<30m' => '30分未満', + 'Stop timer' => 'ã‚¿ã‚¤ãƒžãƒ¼åœæ­¢', + 'Start timer' => 'タイマー開始', + 'My activity stream' => 'è‡ªåˆ†ã®æ´»å‹•状æ³', + 'Search tasks' => 'タスクを検索', + 'Reset filters' => 'フィルターをリセット', + 'My tasks due tomorrow' => '明日ã®è‡ªåˆ†ã®ã‚¿ã‚¹ã‚¯', + 'Tasks due today' => '今日ã®ã‚¿ã‚¹ã‚¯', + 'Tasks due tomorrow' => '明日ã®ã‚¿ã‚¹ã‚¯', + 'Tasks due yesterday' => '昨日ã®ã‚¿ã‚¹ã‚¯', + 'Closed tasks' => '完了タスク', + 'Open tasks' => '未完了タスク', + 'Not assigned' => '未割当', + 'View advanced search syntax' => 'è©³ç´°æ¤œç´¢ã®æ§‹æ–‡ã‚’見る', + 'Overview' => '概è¦', + 'Board/Calendar/List view' => 'ボード/カレンダー/リストビュー', + 'Switch to the board view' => 'ボードビューã«åˆ‡æ›¿', + 'Switch to the list view' => 'リストビューã«åˆ‡æ›¿', + 'Go to the search/filter box' => '検索/フィルタボックスã«ç§»å‹•', + 'There is no activity yet.' => 'ã¾ã æ´»å‹•状æ³ã¯ã‚りã¾ã›ã‚“', + 'No tasks found.' => 'タスクãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'Keyboard shortcut: "%s"' => 'キーボード・ショートカット: "%s"', + 'List' => 'リスト', + 'Filter' => 'フィルター', + 'Advanced search' => '詳細検索', + 'Example of query: ' => 'クエリã®ä¾‹ï¼š', + 'Search by project: ' => 'ãƒ—ãƒ­ã‚¸ã‚§ã‚¯ãƒˆã§æ¤œç´¢ï¼š', + 'Search by column: ' => 'ã‚«ãƒ©ãƒ ã§æ¤œç´¢ï¼š', + 'Search by assignee: ' => 'æ‹…å½“è€…ã§æ¤œç´¢ï¼š', + 'Search by color: ' => 'è‰²ã§æ¤œç´¢ï¼š', + 'Search by category: ' => 'ã‚«ãƒ†ã‚´ãƒªã§æ¤œç´¢ï¼š', + 'Search by description: ' => 'èª¬æ˜Žã§æ¤œç´¢ï¼š', + 'Search by due date: ' => '期é™ã§æ¤œç´¢ï¼š', + 'Average time spent in each column' => 'å„カラムã§çµŒéŽã—ãŸæ™‚é–“ã®å¹³å‡', + 'Average time spent' => 'å¹³å‡çµŒéŽæ™‚é–“', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'ã“ã®ã‚°ãƒ©ãƒ•ã¯æœ€å¾Œã®%d個ã®ã‚¿ã‚¹ã‚¯ã«å¯¾ã—ã¦å„カラムã§çµŒéŽã—ãŸå¹³å‡æ™‚間を示ã—ã¦ã„ã¾ã™', + 'Average Lead and Cycle time' => 'å¹³å‡ãƒªãƒ¼ãƒ‰ã‚¿ã‚¤ãƒ ãƒ»å¹³å‡ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ ', + 'Average lead time: ' => 'å¹³å‡ãƒªãƒ¼ãƒ‰ã‚¿ã‚¤ãƒ ï¼š', + 'Average cycle time: ' => 'å¹³å‡ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ ï¼š', + 'Cycle Time' => 'サイクルタイム', + 'Lead Time' => 'リードタイム', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'ã“ã®ã‚°ãƒ©ãƒ•ã¯æ™‚é–“ã®çµŒéŽã«ä¼´ã†æœ€å¾Œã®%d個ã®ã‚¿ã‚¹ã‚¯ã®å¹³å‡ãƒªãƒ¼ãƒ‰ã‚¿ã‚¤ãƒ ã¨ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ ã‚’示ã—ã¾ã™', + 'Average time into each column' => 'å„カラムã®å¹³å‡æ™‚é–“', + 'Lead and cycle time' => 'リードï¼ã‚µã‚¤ã‚¯ãƒ«ã‚¿ã‚¤ãƒ ', + 'Lead time: ' => 'リードタイム:', + 'Cycle time: ' => 'サイクルタイム:', + 'Time spent in 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 the start date automatically' => '開始日を自動的ã«è¨­å®š', + 'Edit Authentication' => 'èªè¨¼ã®ç·¨é›†', + 'Remote user' => 'リモートユーザー', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'リモートユーザーã¯ãƒ‘スワードをKanboardデータベースã«ä¿å­˜ã—ã¾ã›ã‚“(例:LDAPã€Googleã€Githubã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼‰', + '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.' => '利用å¯èƒ½ãªãƒ—ロジェクトã¯ã‚りã¾ã›ã‚“', + 'Trigger automatically subtask time tracking' => '自動的ã«ã‚µãƒ–タスク時間トラッキングを作動ã•ã›ã‚‹', + 'Include closed tasks in the cumulative flow diagram' => 'ç´¯ç©ãƒ•ロー図ã«å®Œäº†ã—ãŸã‚¿ã‚¹ã‚¯ã‚’å«ã‚ã‚‹', + 'Current swimlane: %s' => 'ç¾åœ¨ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ï¼š%s', + 'Current column: %s' => 'ç¾åœ¨ã®ã‚«ãƒ©ãƒ ï¼š%s', + 'Current category: %s' => 'ç¾åœ¨ã®ã‚«ãƒ†ã‚´ãƒªï¼š%s', + 'no category' => 'カテゴリãªã—', + 'Current assignee: %s' => 'ç¾åœ¨ã®æ‹…当者:%s', + 'not assigned' => '未割当', + 'Author:' => '著者:', + 'contributors' => 'å”力者', + 'License:' => 'ライセンス:', + 'License' => 'ライセンス', + 'Enter the text below' => 'テキストを入力', + 'Start date:' => '開始日:', + 'Due date:' => '期é™ï¼š', + 'People who are project managers' => 'プロジェクト管ç†è€…', + 'People who are project members' => 'プロジェクトメンãƒãƒ¼', + 'NOK - Norwegian Krone' => 'NOK - ノルウェークローãƒ', + 'Show this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã‚’表示', + 'Hide this column' => 'ã“ã®ã‚«ãƒ©ãƒ ã‚’éš ã™', + 'End date' => '終了日', + 'Users overview' => 'ユーザー概è¦', + 'Members' => 'メンãƒãƒ¼', + 'Shared project' => '共有プロジェクト', + 'Project managers' => 'プロジェクト管ç†è€…', + 'Projects list' => 'プロジェクトリスト', + 'End date:' => '終了日:', + 'Change task color when using a specific task link' => '特定ã®ã‚¿ã‚¹ã‚¯ãƒªãƒ³ã‚¯ã‚’使用ã™ã‚‹ã¨ã‚¿ã‚¹ã‚¯ã®è‰²ã‚’変更', + 'Task link creation or modification' => 'タスクã¸ã®ãƒªãƒ³ã‚¯ã®ä½œæˆã¾ãŸã¯å¤‰æ›´', + 'Milestone' => 'マイルストーン', + 'Reset the search/filter box' => '検索/フィルタをリセット', + 'Documentation' => 'ドキュメント', + 'Author' => '著者', + 'Version' => 'ãƒãƒ¼ã‚¸ãƒ§ãƒ³', + 'Plugins' => 'プラグイン', + 'There is no plugin loaded.' => 'プラグインãŒãƒ­ãƒ¼ãƒ‰ã•れã¦ã„ã¾ã›ã‚“', + 'My notifications' => '自分ã®é€šçŸ¥', + 'Custom filters' => 'カスタムフィルタ', + 'Your custom filter has 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 has been updated successfully.' => 'カスタムフィルタを更新ã—ã¾ã—ãŸ', + 'Unable to update custom filter.' => 'カスタムフィルタを更新ã§ãã¾ã›ã‚“', + 'Web' => 'Web', + '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ã§æ›´æ–°ã•れãŸã‚µãƒ–タスク', + 'New task #%d: %s' => 'æ–°ã—ã„タスク#%d:%s', + 'Task updated #%d' => 'ã‚¿ã‚¹ã‚¯ãŒæ›´æ–°ã•れã¾ã—ãŸï¼ƒ%d', + 'Task #%d closed' => 'タスク#%dã¯å®Œäº†ã—ã¾ã—ãŸ', + 'Task #%d opened' => 'タスク#%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ã®æ‹…当者ãŒå¤‰æ›´ã•れã¾ã—ãŸ', + '%d overdue tasks' => '%d ä»¶ã®æœŸé™åˆ‡ã‚Œã‚¿ã‚¹ã‚¯', + 'No notification.' => '通知ãªã—', + '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:' => '通知方法:', + 'Unable to read your file' => 'ã‚ãªãŸã®ãƒ•ァイルを読むã“ã¨ãŒã§ãã¾ã›ã‚“', + '%d task(s) have been imported successfully.' => '%dä»¶ã®ã‚¿ã‚¹ã‚¯ãŒæ­£å¸¸ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã•れã¾ã—ãŸ', + 'Nothing has been imported!' => '何もインãƒãƒ¼ãƒˆã•れã¦ã„ã¾ã›ã‚“ï¼', + 'Import users from CSV file' => 'CSVファイルã‹ã‚‰ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’インãƒãƒ¼ãƒˆ', + '%d user(s) have been imported successfully.' => '%d人ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒæ­£å¸¸ã«ã‚¤ãƒ³ãƒãƒ¼ãƒˆã•れã¾ã—ãŸ', + 'Comma' => 'カンマ', + 'Semi-colon' => 'セミコロン', + 'Tab' => 'タブ', + 'Vertical bar' => '縦棒', + '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)' => 'ç½®æ›ã®ä»£ã‚りã«ãƒ•ィルターを追加', + 'Append/Replace' => '追加/ç½®æ›', + 'Append' => '追加', + 'Replace' => 'ç½®æ›', + 'Import' => 'インãƒãƒ¼ãƒˆ', + 'Change sorting' => 'ソートã®å¤‰æ›´', + 'Tasks Importation' => 'タスクã®ã‚¤ãƒ³ãƒãƒ¼ãƒˆ', + 'Delimiter' => '区切り', + 'Enclosure' => 'エンクロージャ', + 'CSV File' => 'CSVファイル', + 'Instructions' => '指示', + 'Your file must use the predefined CSV format' => 'ファイルã¯å®šç¾©æ¸ˆã¿ã®CSVå½¢å¼ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'Your file must be encoded in UTF-8' => 'ファイルã¯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' => '期é™ã¯ISOå½¢å¼ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™ï¼šYYYY-MM-DD', + 'Download CSV template' => 'CSVテンプレートをダウンロード', + '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' => '%sãŒã€ã‚¿ã‚¹ã‚¯ %s ã«æ–°ã—ã„ファイルを添付ã—ã¾ã—ãŸ', + 'Link type' => 'リンクタイプ', + 'Assign automatically a category based on a link' => 'リンクã«åŸºã¥ã„ã¦ã‚«ãƒ†ã‚´ãƒªã‚’自動的ã«å‰²å½“ã¦ã‚‹', + 'BAM - Konvertible Mark' => 'BAM - å…Œæ›ãƒžãƒ«ã‚¯', + 'Assignee Username' => '担当者ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼å', + 'Assignee Name' => '担当者å', + 'Groups' => 'グループ', + 'Members of %s' => '%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"' => '「%sã€ã«ã‚°ãƒ«ãƒ¼ãƒ—メンãƒãƒ¼ã‚’追加', + 'Group member added successfully.' => 'グループメンãƒãƒ¼ãŒæ­£å¸¸ã«è¿½åŠ ã•れã¾ã—ãŸ', + 'Unable to add group member.' => 'グループメンãƒãƒ¼ã‚’追加ã§ãã¾ã›ã‚“', + 'Remove user from group "%s"' => 'グループ「%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' => 'ã‚ãªãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯%d分間ロックã•れã¦ã„ã¾ã™', + 'Invalid captcha' => '無効ãªcaptcha', + 'The name must be unique' => 'åå‰ã¯ãƒ¦ãƒ‹ãƒ¼ã‚¯ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'View all groups' => 'ã™ã¹ã¦ã®ã‚°ãƒ«ãƒ¼ãƒ—を表示', + 'There is no user available.' => '利用å¯èƒ½ãªãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã‚りã¾ã›ã‚“', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'ユーザー「%sã€ã‚’グループ「%sã€ã‹ã‚‰å‰Šé™¤ã—ã¾ã™ã‹ï¼Ÿ', + 'There is no group.' => 'グループã¯ã‚りã¾ã›ã‚“', + 'Add group member' => 'グループメンãƒãƒ¼ã‚’追加', + 'Do you really want to remove this group: "%s"?' => 'グループ「%sã€ã‚’削除ã—ã¾ã™ã‹ï¼Ÿ', + 'There is no user in this group.' => 'ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«ã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼ãŒã„ã¾ã›ã‚“', + 'Permissions' => '権é™', + 'Allowed Users' => '許å¯ã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'No specific user has been allowed.' => '許å¯ã•れãŸãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã‚りã¾ã›ã‚“', + 'Role' => '役割', + 'Enter user name...' => 'ユーザーåを入力...', + 'Allowed Groups' => '許å¯ã•れãŸã‚°ãƒ«ãƒ¼ãƒ—', + 'No group has been allowed.' => '許å¯ã•れãŸã‚°ãƒ«ãƒ¼ãƒ—ã¯ã‚りã¾ã›ã‚“', + 'Group' => 'グループ', + 'Group Name' => 'グループå', + 'Enter group name...' => 'グループåを入力...', + 'Role:' => '役割:', + 'Project members' => 'プロジェクトメンãƒãƒ¼', + '%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ã®ã‚³ãƒ¡ãƒ³ãƒˆã§ã‚ãªãŸã®ã“ã¨ãŒè¨€åŠã•れã¾ã—ãŸ', + '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' => 'Kanboardã®ãƒ‘スワードリセット', + 'Forgot password?' => 'パスワードをãŠå¿˜ã‚Œã§ã™ã‹ï¼Ÿ', + 'Enable "Forget Password"' => '「パスワードをãŠå¿˜ã‚Œã§ã™ã‹ï¼Ÿã€ã‚’有効ã«ã™ã‚‹', + 'Password Reset' => 'パスワードリセット', + 'New password' => 'æ–°ã—ã„パスワード', + 'Change Password' => 'パスワードを変更', + 'To reset your password click on this link:' => 'ã“ã®ãƒªãƒ³ã‚¯ã‚’クリックã™ã‚‹ã¨ãƒ‘スワードをリセットã—ã¾ã™ï¼š', + 'Last Password Reset' => '最後ã®ãƒ‘スワードリセット', + 'The password has never been reinitialized.' => 'パスワードãŒå†è¨­å®šã•れã¦ã„ã¾ã›ã‚“', + 'Creation' => '作æˆ', + 'Expiration' => '失効', + 'Password reset history' => 'パスワードリセット履歴', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'カラム「%s〠ã‹ã¤ã€ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã€Œ%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.' => '%dä»¶ã®ã€ã‚«ãƒ©ãƒ ã€Œ%s〠ã‹ã¤ã€ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã€Œ%s〠ã«ã‚るタスクを終了ã—ã¾ã™', + 'Close all tasks in this column and this swimlane' => 'ã“ã®ã‚«ãƒ©ãƒ ã®ã™ã¹ã¦ã®ã‚¿ã‚¹ã‚¯ã‚’終了', + '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.' => 'プロジェクト識別å­ã¯ã‚ªãƒ—ションã§ã™ã€‚設定ã™ã‚‹å ´åˆã¯è‹±æ•°å­—ã«ã—ã¦ãã ã•ã„。 例:MYPROJECT', + 'Project owner' => 'プロジェクト責任者', + 'Personal projects do not have users and groups management.' => 'éžå…¬é–‹ãƒ—ロジェクトã«ã¯ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¨ã‚°ãƒ«ãƒ¼ãƒ—ã®ç®¡ç†ã¯ã‚りã¾ã›ã‚“', + 'There is no project member.' => 'プロジェクトメンãƒãƒ¼ãŒã„ã¾ã›ã‚“', + 'Priority' => '優先度', + 'Task priority' => 'タスクã®å„ªå…ˆåº¦', + 'General' => '一般', + 'Dates' => '日付', + 'Default priority' => '既定ã®å„ªå…ˆåº¦', + 'Lowest priority' => '最低優先度', + 'Highest priority' => '最高優先度', + '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' => '添付', + 'Web Link' => 'Webリンク', + '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' => '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' => '%s時間', + 'Time spent:' => 'çµŒéŽæ™‚間:', + 'Created:' => '作æˆï¼š', + 'Modified:' => '更新:', + 'Completed:' => '完了:', + 'Started:' => '開始:', + 'Moved:' => '移動:', + 'Task #%d' => 'タスク #%d', + 'Time format' => '時刻形å¼', + 'Start date: ' => '開始日:', + 'End date: ' => '完了日:', + 'New due date: ' => 'æ–°ã—ã„æœŸé™ï¼š', + 'Start date changed: ' => 'é–‹å§‹æ—¥ãŒå¤‰æ›´ã•れã¾ã—ãŸï¼š', + 'Disable personal projects' => 'éžå…¬é–‹ãƒ—ロジェクトを無効ã«ã™ã‚‹', + 'Do you really want to remove this custom filter: "%s"?' => 'ã“ã®ã‚«ã‚¹ã‚¿ãƒ ãƒ•ィルタ「%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.' => 'ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ãŒæ­£å¸¸ã«ã‚¢ãƒƒãƒ—ロードã•れã¾ã—ãŸ', + 'The maximum allowed file size is %sB.' => '最大許容ファイルサイズ:%sB', + 'Drag and drop your files here' => 'ファイルをドラッグアンドドロップ', + 'choose files' => 'ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠž', + 'View profile' => 'プロフィールを見る', + 'Two Factor' => '二è¦ç´ èªè¨¼', + 'Disable user' => 'ユーザーを無効ã«ã™ã‚‹', + 'Do you really want to disable this user: "%s"?' => 'ユーザー「%sã€ã‚’無効ã«ã—ã¾ã™ã‹ï¼Ÿ', + 'Enable user' => 'ユーザーを有効ã«ã™ã‚‹', + 'Do you really want to enable this user: "%s"?' => 'ユーザー「%sã€ã‚’有効ã«ã—ã¾ã™ã‹ï¼Ÿ', + 'Download' => 'ダウンロード', + 'Uploaded: %s' => 'アップロード完了:%s', + 'Size: %s' => 'サイズ:%s', + 'Uploaded by %s' => '%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' => '%dより大ãããªã‘れã°ãªã‚Šã¾ã›ã‚“', + 'Another swimlane with the same name exists in the project' => 'åŒã˜åå‰ã®åˆ¥ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ãŒãƒ—ロジェクトã«ã‚りã¾ã™', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => '例:https://example.kanboard.org/(絶対URLã®ç”Ÿæˆã«ä½¿ç”¨ï¼‰', + '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ãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', + 'PHP SAPI:' => 'PHP SAPI', + 'OS version:' => 'OSãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', + 'Database version:' => 'データベース・ãƒãƒ¼ã‚¸ãƒ§ãƒ³ï¼š', + 'Browser:' => 'ブラウザー:', + 'Task view' => 'タスクビュー', + 'Edit task' => 'タスクを編集', + 'Edit description' => '説明を編集', + 'New internal link' => 'æ–°ã—ã„内部リンク', + 'Display list of keyboard shortcuts' => 'キーボードショートカットã®ãƒªã‚¹ãƒˆã‚’表示', + '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.' => '1行ã«1ä»¶ã®ã‚¿ã‚¹ã‚¯ã‚’入力', + 'Number of failed login:' => 'ログイン失敗回数:', + 'Account locked until:' => 'ロックã•れãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆï¼š', + 'Email settings' => 'メール設定', + 'Email sender address' => 'é€ä¿¡è€…ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹', + 'Email transport' => 'メールã®é€ä¿¡', + '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 a specific column' => '特定ã®ã‚«ãƒ©ãƒ ã«æ´»å‹•ãŒãªã„å ´åˆã«ã‚¿ã‚¹ã‚¯ã‚’終了', + '%s removed a subtask for the task #%d' => 'タスク#%dã‹ã‚‰ã‚µãƒ–タスク%s件を削除ã—ã¾ã—ãŸ', + '%s removed a comment on the task #%d' => 'タスク#%dã‹ã‚‰ã‚³ãƒ¡ãƒ³ãƒˆ%s件を削除ã—ã¾ã—ãŸ', + '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 has 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ã®æ´»å‹•状æ³', + '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ã§å…¥åŠ›', + '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' => 'é€ä¿¡ãƒ¡ãƒ¼ãƒ«', + '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é€šã®æ‹›å¾…状をé€ä¿¡ã—ã¾ã—ãŸ', + '%d invitation was sent.' => '%dã«æ‹›å¾…状をé€ä¿¡ã—ã¾ã—ãŸ', + 'Unable to create this user.' => 'ユーザーを作æˆã§ãã¾ã›ã‚“', + 'Kanboard Invitation' => 'Kanboardã¸ã®æ‹›å¾…', + '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.' => 'ã‚ãªãŸã¯Kanboardã«ç™»éŒ²ã•れã¾ã—ãŸ', + 'Click here to join your team' => 'クリックã—ã¦ã‚ãªãŸã®ãƒãƒ¼ãƒ ã«å‚加', + 'Invite people' => '招待', + 'Emails' => 'Eメール', + 'Enter one email address by line.' => '行ã”ã¨ã«1ã¤ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ã‚’入力ã—ã¦ãã ã•ã„', + 'Add these people to this project' => 'ã“れらã®äººã€…をプロジェクトã«è¿½åŠ ', + 'Add this person to this project' => 'ã“ã®äººã‚’プロジェクトã«è¿½åŠ ', + 'Sign-up' => 'サインアップ', + 'Credentials' => '資格情報', + 'New user' => 'æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼', + 'This username is already taken' => 'ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼åã¯ã™ã§ã«ä½¿ç”¨ã•れã¦ã„ã¾ã™', + 'Your profile must have a valid email address.' => 'プロフィールã«ã¯æœ‰åйãªãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹ãŒå¿…è¦ã§ã™', + 'TRL - Turkish Lira' => 'トルコリラ', + 'The project email is optional and could be used by several plugins.' => 'プロジェクトã®Eメールã¯ã‚ªãƒ—ションï¼è¤‡æ•°ã®ãƒ—ラグインã§ä½¿ç”¨å¯èƒ½', + 'The project email must be unique across all projects' => 'プロジェクトã®Eメールã¯ã™ã¹ã¦ã®ãƒ—ロジェクトã§ãƒ¦ãƒ‹ãƒ¼ã‚¯ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'The email configuration has been disabled by the administrator.' => '管ç†è€…ãŒEメールã®è¨­å®šã‚’無効ã«ã—ã¾ã—ãŸ', + 'Close this project' => 'プロジェクトを終了', + 'Open this project' => 'ã“ã®ãƒ—ロジェクトを開ã', + 'Close a project' => 'プロジェクトを終了', + 'Do you really want to close this project: "%s"?' => 'ã“ã®ãƒ—ロジェクトを本当ã«çµ‚了ã—ã¾ã™ã‹ï¼Ÿï¼š %s', + 'Reopen a project' => 'プロジェクトを開ã', + 'Do you really want to reopen this project: "%s"?' => 'ã“ã®ãƒ—ロジェクトを開ãã¾ã™ã‹ï¼Ÿï¼š %s', + 'This project is open' => 'ã“ã®ãƒ—ロジェクトã¯é€²è¡Œä¸­', + 'This project is closed' => 'ã“ã®ãƒ—ロジェクトã¯çµ‚了ã—ã¾ã—ãŸ', + 'Unable to upload files, check the permissions of your data folder.' => 'ファイルをアップロードã§ãã¾ã›ã‚“。データフォルダã®ã‚¢ã‚¯ã‚»ã‚¹æ¨©ã‚’確èªã—ã¦ãã ã•ã„。', + 'Another category with the same name exists in this project' => 'åŒã˜åå‰ã®åˆ¥ã®ã‚«ãƒ†ã‚´ãƒªãŒãƒ—ロジェクトã«å­˜åœ¨ã—ã¾ã™', + 'Comment sent by email successfully.' => 'メールã§ã‚³ãƒ¡ãƒ³ãƒˆã‚’é€ä¿¡ã—ã¾ã—ãŸ', + 'Sent by email to "%s" (%s)' => '%s をメールã§é€ä¿¡ã—ã¾ã—㟠"%s"', + 'Unable to read uploaded file.' => 'アップロードã•れãŸãƒ•ァイルを読ã¿è¾¼ã‚ã¾ã›ã‚“', + 'Database uploaded successfully.' => 'データベースをアップロードã—ã¾ã—ãŸ', + 'Task sent by email successfully.' => 'タスクをメールã§é€ä¿¡ã—ã¾ã—ãŸ', + 'There is no category in this project.' => 'ã“ã®ãƒ—ロジェクトã«ã‚«ãƒ†ã‚´ãƒªã¯ã‚りã¾ã›ã‚“', + 'Send by email' => 'メールã§é€ã‚‹', + 'Create and send a comment by email' => 'Eメールã§ã‚³ãƒ¡ãƒ³ãƒˆã‚’é€ä¿¡', + 'Subject' => 'ä»¶å', + 'Upload the database' => 'データベースをアップロード', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => '以å‰ã«ãƒ€ã‚¦ãƒ³ãƒ­ãƒ¼ãƒ‰ã—ãŸSqliteデータベース(Gzipå½¢å¼ï¼‰ã‚’アップロードã§ãã¾ã™', + 'Database file' => 'データベースファイル', + 'Upload' => 'アップロード', + 'Your project must have at least one active swimlane.' => 'プロジェクトã«ã¯å°‘ãªãã¨ã‚‚1ã¤ã®ã‚¢ã‚¯ãƒ†ã‚£ãƒ–ãªã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ãŒå¿…è¦ã§ã™', + 'Project: %s' => 'プロジェクト:%s', + 'Automatic action not found: "%s"' => '自動アクションãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“: %s', + '%d projects' => '%d ä»¶ã®ãƒ—ロジェクト', + '%d project' => '%d プロジェクト', + 'There is no project.' => 'プロジェクトã¯ã‚りã¾ã›ã‚“', + 'Sort' => 'ソート', + 'Project ID' => 'プロジェクトID', + 'Project name' => 'プロジェクトå', + 'Public' => '公開', + 'Personal' => 'éžå…¬é–‹', + '%d tasks' => '%d ä»¶ã®ã‚¿ã‚¹ã‚¯', + '%d task' => '%d タスク', + 'Task ID' => 'タスクID', + 'Assign automatically a color when due date is expired' => '期é™ãŒåˆ‡ã‚ŒãŸã‚‰è‡ªå‹•çš„ã«è‰²ã‚’割当ã¦', + 'Total score in this column across all swimlanes' => 'ã“ã®ã‚«ãƒ©ãƒ ã®ã™ã¹ã¦ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã§åˆè¨ˆå¾—点', + 'HRK - Kuna' => 'クロアãƒã‚¢ クーナ', + 'ARS - Argentine Peso' => 'ARS - アルゼンãƒãƒ³ãƒšã‚½', + 'COP - Colombian Peso' => 'COP - コロンビアペソ', + '%d groups' => '%d個ã®ã‚°ãƒ«ãƒ¼ãƒ—', + '%d group' => '%d グループ', + 'Group ID' => 'グループID', + 'External ID' => '外部ID', + '%d users' => '%d 人ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼', + '%d user' => '%d ユーザー', + 'Hide subtasks' => 'サブタスクを隠ã™', + 'Show subtasks' => 'サブタスクを表示', + 'Authentication Parameters' => 'èªè¨¼ãƒ‘ラメータ', + 'API Access' => 'APIアクセス', + 'No users found.' => 'ユーザーãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + 'User ID' => 'ユーザーID', + 'Notifications are activated' => 'é€šçŸ¥ã¯æœ‰åй', + 'Notifications are disabled' => '通知ã¯ç„¡åй', + 'User disabled' => 'ユーザーを無効ã«ã—ã¾ã—ãŸ', + '%d notifications' => '%d ä»¶ã®é€šçŸ¥', + '%d notification' => '%d 通知', + 'There is no external integration installed.' => '外部インテグレーションã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã•れã¦ã„ã¾ã›ã‚“', + 'You are not allowed to update tasks assigned to someone else.' => 'ä»–ã®äººã«å‰²å½“ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã‚’æ›´æ–°ã™ã‚‹æ¨©é™ãŒã‚りã¾ã›ã‚“', + 'You are not allowed to change the assignee.' => '担当者を変更ã™ã‚‹æ¨©é™ã¯ã‚りã¾ã›ã‚“', + 'Task suppression is not permitted' => 'ã‚¿ã‚¹ã‚¯ã®æŠ‘åˆ¶ã¯è¨±å¯ã•れã¦ã„ã¾ã›ã‚“', + 'Changing assignee is not permitted' => '担当者ã¯å¤‰æ›´ã§ãã¾ã›ã‚“', + 'Update only assigned tasks is permitted' => '割当ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã®ã¿æ›´æ–°ã§ãã¾ã™', + 'Only for tasks assigned to the current user' => 'ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å‰²å½“ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã®ã¿', + 'My projects' => '自分ã®ãƒ—ロジェクト', + 'You are not a member of any project.' => 'ã‚ãªãŸãŒæ‰€å±žã—ã¦ã„るプロジェクトã«ã¯ã‚りã¾ã›ã‚“', + 'My subtasks' => '自分ã®ã‚µãƒ–タスク', + '%d subtasks' => '%d ä»¶ã®ã‚µãƒ–タスク', + '%d subtask' => '%d サブタスク', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'ç¾åœ¨ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å‰²å½“ã¦ã‚‰ã‚ŒãŸã‚¿ã‚¹ã‚¯ã¯ã€ã“れらã®ã‚«ãƒ©ãƒ é–“ã®ç§»å‹•ã®ã¿ãŒè¨±å¯ã•れã¾ã™', + '[DUPLICATE]' => '[コピー]', + 'DKK - Danish Krona' => 'DKK - デンマーククローナ', + 'Remove user from group' => 'ユーザーをグループã‹ã‚‰å‰Šé™¤', + 'Assign the task to its creator' => '作æˆè€…ã«ã‚¿ã‚¹ã‚¯ã‚’割当ã¦', + 'This task was sent by email to "%s" with subject "%s".' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã‚’メール㧠「%s〠ã«é€ä¿¡ã—ã¾ã—ãŸï¼ˆä»¶å "%s")', + 'Predefined Email Subjects' => '既定ã®ãƒ¡ãƒ¼ãƒ«ã®ä»¶å', + 'Write one subject by line.' => '行ã”ã¨ã«1ã¤ãšã¤ä»¶åを書ã', + 'Create another link' => '別ã®ãƒªãƒ³ã‚¯ã‚’作æˆ', + 'BRL - Brazilian Real' => 'BRL - ブラジルレアル', + 'Add a new Kanboard task' => 'Kanboard タスクを追加', + 'Subtask not started' => 'サブタスクã¯é–‹å§‹ã•れã¦ã„ã¾ã›ã‚“', + 'Subtask currently in progress' => 'サブタスクã¯é€²è¡Œä¸­', + 'Subtask completed' => 'サブタスクã¯å®Œäº†', + 'Subtask added successfully.' => 'サブタスクを追加ã—ã¾ã—ãŸ', + '%d subtasks added successfully.' => '%d ä»¶ã®ã‚µãƒ–タスクを追加ã—ã¾ã—ãŸ', + 'Enter one subtask by line.' => '1行ã«1ä»¶ã®ã‚µãƒ–タスクを入力', + 'Predefined Contents' => '定義済ã¿ã‚³ãƒ³ãƒ†ãƒ³ãƒ„', + 'Predefined contents' => '定義済ã¿ã‚³ãƒ³ãƒ†ãƒ³ãƒ„', + 'Predefined Task Description' => '定義済ã¿ã‚¿ã‚¹ã‚¯ã®èª¬æ˜Ž', + 'Do you really want to remove this template? "%s"' => 'ã“ã®ãƒ†ãƒ³ãƒ—レートを削除ã—ã¾ã™ã‹ï¼Ÿï¼š %s', + 'Add predefined task description' => '定義済ã¿ã®ã‚¿ã‚¹ã‚¯ã®èª¬æ˜Žã‚’追加', + 'Predefined Task Descriptions' => '定義済ã¿ã‚¿ã‚¹ã‚¯ã®èª¬æ˜Ž', + 'Template created successfully.' => 'テンプレートを作æˆã—ã¾ã—ãŸ', + 'Unable to create this template.' => 'テンプレートを作æˆã§ãã¾ã›ã‚“', + 'Template updated successfully.' => 'テンプレートを更新ã—ã¾ã—ãŸ', + 'Unable to update this template.' => 'テンプレートを更新ã§ãã¾ã›ã‚“', + 'Template removed successfully.' => 'テンプレートを削除ã—ã¾ã—ãŸ', + 'Unable to remove this template.' => 'テンプレートを削除ã§ãã¾ã›ã‚“', + 'Template for the task description' => 'タスク説明ã®ãƒ†ãƒ³ãƒ—レート', + 'The start date is greater than the end date' => 'é–‹å§‹æ—¥ãŒçµ‚了日を超ãˆã¦ã„ã¾ã™', + 'Tags must be separated by a comma' => 'ã‚¿ã‚°ã¯ã‚³ãƒ³ãƒžã§åŒºåˆ‡ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'Only the task title is required' => 'タスクã®ã‚¿ã‚¤ãƒˆãƒ«ã®ã¿ãŒå¿…è¦ã§ã™', + 'Creator Username' => '作æˆè€…å', + 'Color Name' => '色å', + 'Column Name' => 'カラムå', + 'Swimlane Name' => 'スイムレーンå', + 'Time Estimated' => 'è¦‹ç©æ™‚é–“', + 'Time Spent' => 'çµŒéŽæ™‚é–“', + 'External Link' => '外部リンク', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'ã“ã®æ©Ÿèƒ½ã¯ã€iCal フィードã¨RSSフィードã€ãã—ã¦ãƒœãƒ¼ãƒ‰ãƒ“ューを公開ã—ã¾ã™ã€‚', + 'Stop the timer of all subtasks when moving a task to another column' => 'タスクを別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã—ãŸã¨ãã«ã€å…¨ã¦ã®ã‚µãƒ–タスクã®ã‚¿ã‚¤ãƒžãƒ¼ã‚’åœæ­¢ã™ã‚‹', + 'Subtask Title' => 'サブタスクã®ã‚¿ã‚¤ãƒˆãƒ«', + 'Add a subtask and activate the timer when moving a task to another column' => 'サブタスクを追加ã—ã€ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã—ãŸã¨ãã«ã‚¿ã‚¤ãƒžãƒ¼ã‚’有効化ã™ã‚‹', + 'days' => 'æ—¥', + 'minutes' => '分', + 'seconds' => 'ç§’', + 'Assign automatically a color when preset start date is reached' => '予ã‚設定ã—ãŸé–‹å§‹æ—¥ã«ãªã£ãŸã‚‰ã€è‡ªå‹•çš„ã«è‰²ã‚’割り当ã¦ã‚‹', + 'Move the task to another column once a predefined start date is reached' => '予ã‚設定ã—ãŸé–‹å§‹æ—¥ã«ãªã£ãŸã‚‰ã€ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã™ã‚‹', + 'This task is now linked to the task %s with the relation "%s"' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã¨ã€ã‚¿ã‚¹ã‚¯ %s ã‚’ã€"%s"ã¨ã—ã¦é–¢é€£ä»˜ã‘ã¾ã—㟠', + 'The link with the relation "%s" to the task %s has been removed' => 'ã“ã®ã‚¿ã‚¹ã‚¯ã¨ "%s" ã¨ã—ã¦é–¢é€£ä»˜ã‘ã¦ã„ãŸã€ã‚¿ã‚¹ã‚¯ %s ã¨ã®ãƒªãƒ³ã‚¯ã‚’削除ã—ã¾ã—ãŸ', + 'Custom Filter:' => 'カスタムフィルタ', + 'Unable to find this group.' => 'ã“ã®ã‚°ãƒ«ãƒ¼ãƒ—ãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“', + '%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 moved the task #%d to the swimlane "%s"' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d をスイムレーン "%s"ã«ç§»å‹•ã—ã¾ã—ãŸ', + '%sh spent' => '%sh 経éŽ', + '%sh estimated' => '%sh ã®è¦‹ç©', + 'Select All' => 'å…¨ã¦é¸æŠž', + 'Unselect All' => 'å…¨ã¦é¸æŠžè§£é™¤', + 'Apply action' => 'アクションを実行', + 'Move selected tasks to another column or swimlane' => 'é¸æŠžã—ãŸã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•', + 'Edit tasks in bulk' => 'タスクを一括編集', + 'Choose the properties that you would like to change for the selected tasks.' => 'é¸æŠžã—ãŸã‚¿ã‚¹ã‚¯ã®ã€å¤‰æ›´ã—ãŸã„ãƒ—ãƒ­ãƒ‘ãƒ†ã‚£ã‚’é¸æŠžã—ã¦ãã ã•ã„。', + 'Configure this project' => 'ã“ã®ãƒ—ロジェクトã®è¨­å®š', + 'Start now' => '今ã™ãé–‹å§‹', + '%s removed a file from the task #%d' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d ã‹ã‚‰ãƒ•ァイルを削除ã—ã¾ã—ãŸ', + 'Attachment removed from task #%d: %s' => 'タスク #%d ã¸ã®æ·»ä»˜ãƒ•ァイル %s ã¯å‰Šé™¤ã•れã¾ã—ãŸ', + 'No color' => '色無ã—', + 'Attachment removed "%s"' => '添付 "%s" ã¯å‰Šé™¤ã•れã¾ã—ãŸ', + '%s removed a file from the task %s' => '%s ãŒã‚¿ã‚¹ã‚¯ %s ã‹ã‚‰ãƒ•ァイルを削除ã—ã¾ã—ãŸ', + 'Move the task to another swimlane when assigned to a user' => '担当者を割り当ã¦ãŸã¨ãã€ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ç§»å‹•ã™ã‚‹', + 'Destination swimlane' => '宛先ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³', + 'Assign a category when the task is moved to a specific swimlane' => 'タスクãŒç‰¹å®šã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ç§»å‹•ã—ãŸã¨ãã«ã‚«ãƒ†ã‚´ãƒªã‚’割り当ã¦ã‚‹', + 'Move the task to another swimlane when the category is changed' => 'カテゴリãŒå¤‰æ›´ã•れãŸã¨ãã€ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ç§»å‹•ã™ã‚‹', + 'Reorder this column by priority (ASC)' => '優先度ã§ä¸¦ã³æ›¿ãˆã‚‹(昇順)', + 'Reorder this column by priority (DESC)' => '優先度ã§ä¸¦ã³æ›¿ãˆã‚‹(é™é †)', + 'Reorder this column by assignee and priority (ASC)' => '担当者ã¨å„ªå…ˆåº¦ã§ä¸¦ã³æ›¿ãˆã‚‹(昇順)', + 'Reorder this column by assignee and priority (DESC)' => '担当者ã¨å„ªå…ˆåº¦ã§ä¸¦ã³æ›¿ãˆã‚‹(é™é †)', + 'Reorder this column by assignee (A-Z)' => '担当者ã§ä¸¦ã³æ›¿ãˆã‚‹(A-Z)', + 'Reorder this column by assignee (Z-A)' => '担当者ã§ä¸¦ã³æ›¿ãˆã‚‹(Z-A)', + 'Reorder this column by due date (ASC)' => '期é™ã§ä¸¦ã³æ›¿ãˆã‚‹(昇順)', + 'Reorder this column by due date (DESC)' => '期é™ã§ä¸¦ã³æ›¿ãˆã‚‹(é™é †)', + 'Reorder this column by id (ASC)' => 'ã“ã®åˆ—ã‚’IDã§ä¸¦ã¹æ›¿ãˆ (昇順)', + 'Reorder this column by id (DESC)' => 'ã“ã®åˆ—ã‚’IDã§ä¸¦ã¹æ›¿ãˆ (é™é †)', + '%s moved the task #%d "%s" to the project "%s"' => '%s ãŒã‚¿ã‚¹ã‚¯ #%d "%s" をプロジェクト "%s" ã«ç§»å‹•ã—ã¾ã—ãŸ', + 'Task #%d "%s" has been moved to the project "%s"' => 'タスク#%d "%s" ã¯ãƒ—ロジェクト "%s" ã¸ç§»å‹•ã•れã¾ã—㟠', + 'Move the task to another column when the due date is less than a certain number of days' => '期é™ã¾ã§ã®æ—¥æ•°ãŒè¦å®šå€¤ä»¥ä¸‹ã«ãªã£ãŸã‚‰ã€ã‚¿ã‚¹ã‚¯ã‚’別ã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã™ã‚‹', + 'Automatically update the start date when the task is moved away from a specific column' => 'タスクãŒç‰¹å®šã®ã‚«ãƒ©ãƒ ã‹ã‚‰ç§»å‹•ã•ã‚ŒãŸæ™‚ã€è‡ªå‹•çš„ã«é–‹å§‹æ—¥ã‚’æ›´æ–°ã™ã‚‹', + 'HTTP Client:' => 'HTTPクライアント', + 'Assigned' => '割当済ã¿', + 'Task limits apply to each swimlane individually' => 'スイムレーン毎ã«ã‚¿ã‚¹ã‚¯æ•°åˆ¶é™ã‚’é©ç”¨ã™ã‚‹ã€‚', + 'Column task limits apply to each swimlane individually' => 'スイムレーン毎ã«ã‚«ãƒ©ãƒ ã®ã‚¿ã‚¹ã‚¯æ•°åˆ¶é™ãŒé©ç”¨ã•れã¾ã™ã€‚', + 'Column task limits are applied to each swimlane individually' => 'カラムã®ã‚¿ã‚¹ã‚¯æ•°åˆ¶é™ã¯ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³æ¯Žã«é©ç”¨ã•れã¦ã„ã¾ã™ã€‚', + 'Column task limits are applied across swimlanes' => 'カラムã®ã‚¿ã‚¹ã‚¯æ•°åˆ¶é™ã¯ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã‚’è·¨ã„ã§é©ç”¨ã•れã¦ã„ã¾ã™ã€‚', + 'Task limit: ' => 'タスク数制é™', + 'Change to global tag' => 'グローãƒãƒ«ã‚¿ã‚°ã«å¤‰æ›', + 'Do you really want to make the tag "%s" global?' => '本当ã«ã‚¿ã‚° "%s" をグローãƒãƒ«ã‚¿ã‚°ã«å¤‰æ›ã—ã¾ã™ã‹?', + 'Enable global tags for this project' => 'ã“ã®ãƒ—ロジェクトã§ã‚°ãƒ­ãƒ¼ãƒãƒ«ã‚¿ã‚°ã‚’有効ã«ã™ã‚‹', + 'Group membership(s):' => '所属グループ', + '%s is a member of the following group(s): %s' => 'メンãƒãƒ¼ %s ã¯ä»¥ä¸‹ã®ã‚°ãƒ«ãƒ¼ãƒ—ã«æ‰€å±žã—ã¦ã„ã¾ã™: %s', + '%d/%d group(s) shown' => 'グループ %d/%d を表示', + 'Subtask creation or modification' => 'サブタスクãŒä½œæˆoræ›´æ–°ã•れã¾ã—ãŸ', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => '特定ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ã‚¿ã‚¹ã‚¯ã‚’移動ã—ãŸæ™‚ã«ã€ç‰¹å®šã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ã‚¿ã‚¹ã‚¯ã‚’割当ã¦ã‚‹', + 'Comment' => 'コメント', + 'Collapse vertically' => 'ç¸¦ã«æŠ˜ã‚ŠãŸãŸã‚€', + 'Expand vertically' => 'ç¸¦ã®æŠ˜ã‚ŠãŸãŸã¿ã‚’解除', + 'MXN - Mexican Peso' => 'メキシコ・ペソ', + 'Estimated vs actual time per column' => '列ã”ã¨ã®è¦‹ç©ã‚‚り時間 vs 実際ã«ã‹ã‹ã£ãŸæ™‚é–“', + 'HUF - Hungarian Forint' => 'HUF - ãƒãƒ³ã‚¬ãƒªãƒ¼ãƒ»ãƒ•ォリント', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'ã‚¢ãƒã‚¿ãƒ¼ã¨ã—ã¦ã‚¢ãƒƒãƒ—ロードã™ã‚‹ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠžã—ã¦ãã ã•ã„ï¼', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'アップロードã•れãŸãƒ•ã‚¡ã‚¤ãƒ«ã¯æœ‰åйãªç”»åƒã§ã¯ã‚りã¾ã›ã‚“ï¼ (許å¯ã•れã¦ã„ã‚‹ã®ã¯ *.gif, *.jpg, *.jpeg, *.png ã®ã¿ã§ã™)', + 'Automatically set the due date when the task is moved away from a specific column' => '特定ã®åˆ—ã‹ã‚‰ã‚¿ã‚¹ã‚¯ãŒç§»å‹•ã•れãŸã¨ãã«æœŸé™ã‚’自動的ã«è¨­å®šã™ã‚‹', + 'No other projects found.' => 'ä»–ã®ãƒ—ロジェクトã¯è¦‹ã¤ã‹ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚', + 'Tasks copied successfully.' => 'ã‚¿ã‚¹ã‚¯ãŒæ­£å¸¸ã«ã‚³ãƒ”ーã•れã¾ã—ãŸã€‚', + 'Unable to copy tasks.' => 'タスクをコピーã§ãã¾ã›ã‚“。', + 'Theme' => 'テーマ', + 'Theme:' => 'テーマ:', + 'Light theme' => 'ライトテーマ', + 'Dark theme' => 'ダークテーマ', + 'Automatic theme - Sync with system' => '自動テーマ - システムã¨åŒæœŸ', + 'Application managers or more' => 'アプリケーションマãƒãƒ¼ã‚¸ãƒ£ãƒ¼ä»¥ä¸Š', + 'Administrators' => '管ç†è€…', + 'Visibility:' => 'å¯è¦–性:', + 'Standard users' => '標準ユーザー', + 'Visibility is required' => 'å¯è¦–性ã¯å¿…é ˆã§ã™', + 'The visibility should be an app role' => 'å¯è¦–性ã¯ã‚¢ãƒ—リã®å½¹å‰²ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'Reply' => '返信', + '%s wrote: ' => '%s ãŒæ›¸ãã¾ã—ãŸ:', + 'Number of visible tasks in this column and swimlane' => 'ã“ã®åˆ—ã¨ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã§è¡¨ç¤ºã•れã¦ã„ã‚‹ã‚¿ã‚¹ã‚¯ã®æ•°', + 'Number of tasks in this swimlane' => 'ã“ã®ã‚¹ã‚¤ãƒ ãƒ¬ãƒ¼ãƒ³ã«ã‚ã‚‹ã‚¿ã‚¹ã‚¯ã®æ•°', + 'Unable to find another subtask in progress, you can close this window.' => 'ä»–ã®é€²è¡Œä¸­ã®ã‚µãƒ–タスクãŒè¦‹ã¤ã‹ã‚Šã¾ã›ã‚“。ã“ã®ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã‚’é–‰ã˜ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚', + 'This theme is invalid' => 'ã“ã®ãƒ†ãƒ¼ãƒžã¯ç„¡åйã§ã™', + 'This role is invalid' => 'ã“ã®å½¹å‰²ã¯ç„¡åйã§ã™', + 'This timezone is invalid' => 'ã“ã®ã‚¿ã‚¤ãƒ ã‚¾ãƒ¼ãƒ³ã¯ç„¡åйã§ã™', + 'This language is invalid' => 'ã“ã®è¨€èªžã¯ç„¡åйã§ã™', + 'This URL is invalid' => 'ã“ã®URLã¯ç„¡åйã§ã™', + 'Date format invalid' => '日付形å¼ãŒç„¡åйã§ã™', + 'Time format invalid' => '時刻形å¼ãŒç„¡åйã§ã™', + 'Invalid Mail transport' => '無効ãªãƒ¡ãƒ¼ãƒ«è»¢é€', + 'Color invalid' => '色ãŒç„¡åйã§ã™', + 'This value must be greater or equal to %d' => 'ã“ã®å€¤ã¯ %d 以上ã§ã‚ã‚‹å¿…è¦ãŒã‚りã¾ã™', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'ファイルã®å…ˆé ­ã«BOMを追加ã™ã‚‹ï¼ˆMicrosoft Excelã«å¿…è¦ï¼‰', + 'Just add these tag(s)' => 'ã“れらã®ã‚¿ã‚°ã‚’追加ã™ã‚‹ã ã‘', + 'Remove internal link(s)' => '内部リンクを削除', + 'Import tasks from another project' => 'ä»–ã®ãƒ—ロジェクトã‹ã‚‰ã‚¿ã‚¹ã‚¯ã‚’インãƒãƒ¼ãƒˆã™ã‚‹', + 'Select the project to copy tasks from' => 'タスクをコピーã™ã‚‹ãƒ—ãƒ­ã‚¸ã‚§ã‚¯ãƒˆã‚’é¸æŠžã—ã¦ãã ã•ã„', + 'The total maximum allowed attachments size is %sB.' => '添付ファイルã®åˆè¨ˆæœ€å¤§è¨±å®¹ã‚µã‚¤ã‚ºã¯ %sB ã§ã™ã€‚', + 'Add attachments' => '添付ファイルを追加', + 'Task #%d "%s" is overdue' => 'タスク #%d "%s" ã¯æœŸé™åˆ‡ã‚Œã§ã™', + 'Enable notifications by default for all new users' => 'æ–°è¦ãƒ¦ãƒ¼ã‚¶ãƒ¼ã«å¯¾ã—ã¦ã€ãƒ‡ãƒ•ォルトã§é€šçŸ¥ã‚’有効ã«ã™ã‚‹', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'æ‹…å½“è€…ãŒæ‰‹å‹•ã§è¨­å®šã•れã¦ã„ãªã„å ´åˆã€æŒ‡å®šã—ãŸã‚«ãƒ©ãƒ ã§ã¯ã‚¿ã‚¹ã‚¯ã‚’作æˆè€…ã«å‰²ã‚Šå½“ã¦ã‚‹', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => '担当者ãŒã„ãªã„å ´åˆã€ã‚«ãƒ©ãƒ å¤‰æ›´ã§æŒ‡å®šã®ã‚«ãƒ©ãƒ ã«ç§»å‹•ã—ãŸã¨ãã«ã‚¿ã‚¹ã‚¯ã‚’ログインユーザーã«å‰²ã‚Šå½“ã¦ã‚‹', +]; diff --git a/app/Locale/ko_KR/translations.php b/app/Locale/ko_KR/translations.php new file mode 100644 index 0000000..b776f59 --- /dev/null +++ b/app/Locale/ko_KR/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'ì—†ìŒ', + 'Edit' => '수정', + 'Remove' => 'ì‚­ì œ', + 'Yes' => '예', + 'No' => '아니오', + 'cancel' => '취소', + 'or' => 'ë˜ëŠ”', + 'Yellow' => '노랑', + 'Blue' => '파랑', + 'Green' => 'ì´ˆë¡', + 'Purple' => 'ë³´ë¼', + 'Red' => '빨강', + 'Orange' => '주황', + 'Grey' => '회색', + 'Brown' => '브ë¼ìš´', + 'Deep Orange' => 'ì§„í™', + 'Dark Grey' => '암회', + 'Pink' => 'í•‘í¬', + 'Teal' => '암녹', + 'Cyan' => 'ì²­ë…¹', + 'Lime' => 'ë¼ìž„', + 'Light Green' => '연회', + 'Amber' => '호박', + 'Save' => '저장', + 'Login' => '로그ì¸', + 'Official website:' => 'ê³µì‹ ì›¹ì‚¬ì´íЏ:', + 'Unassigned' => 'ë‹´ë‹¹ìž ì—†ìŒ', + 'View this task' => 'ì´ í• ì¼ ë³´ê¸°', + 'Remove user' => 'ì‚¬ìš©ìž ì‚­ì œ', + 'Do you really want to remove this user: "%s"?' => 'ì‚¬ìš©ìž "%s"를 ì •ë§ë¡œ 삭제하시겠습니까?', + 'All users' => '모든 사용ìž', + 'Username' => 'ì‚¬ìš©ìž ì´ë¦„', + 'Password' => '패스워드', + 'Administrator' => '관리ìž', + 'Sign in' => '로그ì¸', + 'Users' => '사용ìž', + 'Forbidden' => 'ì ‘ê·¼ ê±°ë¶€', + 'Access Forbidden' => 'ì ‘ì†ì´ ê±°ë¶€ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Edit user' => '사용ìžë¥¼ 변경하는 ', + 'Logout' => '로그아웃', + 'Bad username or password' => 'ì‚¬ìš©ìž ì´ë¦„ ë˜ëŠ” 패스워드가 다릅니다.', + 'Edit project' => '프로ì íЏ 수정', + 'Name' => 'ì´ë¦„', + 'Projects' => '프로ì íЏ', + 'No project' => '프로ì íŠ¸ê°€ 없습니다', + 'Project' => '프로ì íЏ', + 'Status' => 'ìƒíƒœ', + 'Tasks' => 'í• ì¼', + 'Board' => '보드', + 'Actions' => 'Actions', + 'Inactive' => '무효', + 'Active' => '유효', + 'Unable to update this board.' => '보드를 갱신할 수 없었습니다', + 'Disable' => '비활성화', + 'Enable' => '유효하게 한다', + 'New project' => '새 프로ì íЏ', + 'Do you really want to remove this project: "%s"?' => '프로ì íŠ¸ë¥¼ 삭제하시겠습니까: "%s"?', + 'Remove project' => '프로ì íŠ¸ì˜ ì‚­ì œ', + 'Edit the board for "%s"' => '"%s"를 위한 보드 수정', + 'Add a new column' => 'ì»¬ëŸ¼ì˜ ì¶”ê°€', + 'Title' => '제목', + 'Assigned to %s' => 'ë‹´ë‹¹ìž %s', + 'Remove a column' => '컬럼 ì‚­ì œ', + 'Unable to remove this column.' => '(※)ì»¬ëŸ¼ì„ ì‚­ì œí•  수 없었습니다.', + 'Do you really want to remove this column: "%s"?' => 'ì»¬ëŸ¼ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ: "%s"?', + 'Settings' => '설정', + 'Application settings' => '애플리케ì´ì…˜ì˜ 설정', + 'Language' => '언어', + 'Webhook token:' => 'Webhook토í°:', + 'API token:' => 'API토í°:', + 'Database size:' => 'ë°ì´í„°ë² ì´ìŠ¤ì˜ ì‚¬ì´ì¦ˆ:', + 'Download the database' => 'ë°ì´í„°ë² ì´ìŠ¤ì˜ ë‹¤ìš´ë¡œë“œ', + 'Optimize the database' => 'ë°ì´í„°ë² ì´ìФ 최ì í™”', + '(VACUUM command)' => '(VACUUM명령)', + '(Gzip compressed Sqlite file)' => '(GZip명령으로 ì••ì¶•ëœ Sqlite파ì¼)', + 'Close a task' => 'í• ì¼ ë§ˆì¹˜ê¸°', + 'Column' => '컬럼', + 'Color' => '색', + 'Assignee' => '담당ìž', + 'Create another task' => '다른 í• ì¼ ì¶”ê°€', + 'New task' => '새로운 í• ì¼', + 'Open a task' => 'í• ì¼ ì—´ê¸°', + 'Do you really want to open this task: "%s"?' => 'í• ì¼ì€ 시작 하시겠습니까: "%s"?', + 'Back to the board' => '보드로 ëŒì•„가기', + 'There is nobody assigned' => '담당ìžê°€ 없습니다', + 'Column on the board:' => '컬럼:', + 'Close this 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" ê¸€ìž ìž…ë‹ˆë‹¤', + 'The password is required' => '패스워드가 필요합니다', + 'This value must be an integer' => '정수로 입력하세요', + 'The username must be unique' => 'ì‚¬ìš©ìž ì´ë¦„ì´ ì´ë¯¸ 사용ë˜ê³  있습니다', + 'The user id is required' => 'ì‚¬ìš©ìž IDê°€ 필요합니다', + 'Passwords don\'t match' => '패스워드가 ì¼ì¹˜í•˜ì§€ 않습니다', + 'The confirmation is required' => '확ì¸ìš© 패스워드를 입력하세요', + 'The project is required' => '프로ì íŠ¸ê°€ 필요합니다', + 'The id is required' => 'IDê°€ 필요합니다', + 'The project id is required' => '프로ì íЏ IDê°€ 필요합니다', + 'The project name is required' => '프로ì íЏ ì´ë¦„ì´ í•„ìš”í•©ë‹ˆë‹¤', + 'The title is required' => 'ì œëª©ì´ í•„ìš”í•©ë‹ˆë‹¤', + 'Settings saved successfully.' => 'ì„¤ì •ì„ ì €ìž¥í•˜ì˜€ìŠµë‹ˆë‹¤', + 'Unable to save your settings.' => 'ì„¤ì •ì˜ ë³´ì¡´ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Database optimization done.' => 'ë°ì´í„°ë² ì´ìФ 최ì í™”ê°€ ë났습니다.', + 'Your project has been created successfully.' => '프로ì íŠ¸ë¥¼ 작성했습니다.', + 'Unable to create your project.' => '프로ì íŠ¸ì˜ ìž‘ì„±ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Project updated successfully.' => '프로ì íŠ¸ë¥¼ 갱신했습니다.', + 'Unable to update this project.' => '프로ì íŠ¸ì˜ ê°±ì‹ ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Unable to remove this project.' => '프로ì íŠ¸ì˜ ì‚­ì œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Project removed successfully.' => '프로ì íŠ¸ë¥¼ 삭제했습니다.', + 'Project activated successfully.' => '프로ì íŠ¸ë¥¼ 유효로 했습니다.', + 'Unable to activate this project.' => '프로ì íŠ¸ì˜ ìœ íš¨í•˜ê²Œ 못했어요.', + 'Project disabled successfully.' => '프로ì íŠ¸ë¥¼ 무효로 했습니다.', + 'Unable to disable this project.' => '프로ì íŠ¸ì˜ ë¬´íš¨í™”í•  수 없었습니다.', + 'Unable to open this task.' => 'í• ì¼ì˜ ì˜¤í”ˆì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Task opened successfully.' => 'í• ì¼ì„ 오픈했습니다.', + 'Unable to close this task.' => 'í• ì¼ì˜ í´ë¡œì¦ˆì— 실패했습니다.', + 'Task closed successfully.' => 'í• ì¼ì„ 마쳤습니다.', + 'Unable to update your task.' => 'í• ì¼ì˜ ê°±ì‹ ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Task updated successfully.' => 'í• ì¼ì„ 갱신했습니다.', + 'Unable to create your task.' => 'í• ì¼ì˜ ì¶”ê°€ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Task created successfully.' => 'í• ì¼ì„ 추가했습니다.', + 'User created successfully.' => '사용ìžë¥¼ 추가했습니다.', + 'Unable to create your user.' => '사용ìžì˜ ì¶”ê°€ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'User updated successfully.' => '사용ìžë¥¼ 갱신했습니다.', + 'User removed successfully.' => '사용ìžë¥¼ 삭제했습니다.', + 'Unable to remove this user.' => 'ì‚¬ìš©ìž ì‚­ì œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Board updated successfully.' => '보드를 갱신했습니다.', + 'Ready' => '준비중', + 'Backlog' => '요구사항', + 'Work in progress' => '진행중', + 'Done' => '완료', + 'Application version:' => '애플리케ì´ì…˜ì˜ 버전:', + 'Id' => 'ID', + 'Public link' => '공개 ì ‘ì† ë§í¬', + 'Timezone' => '시간대', + 'Sorry, I didn\'t find this information in my database!' => 'ë°ì´í„°ë² ì´ìФì—서 ì •ë³´ê°€ 발견ë˜ì§€ 않았습니다!', + 'Page not found' => '페ì´ì§€ê°€ 발견ë˜ì§€ 않는다', + 'Complexity' => '복잡ë„', + 'Task limit' => 'í• ì¼ ìˆ˜ 제한', + 'Task count' => 'í• ì¼ ìˆ˜', + 'User' => '사용ìž', + 'Comments' => '댓글', + 'Comment is required' => 'ëŒ“ê¸€ì„ ìž…ë ¥í•˜ì„¸ìš”', + 'Comment added successfully.' => 'ì˜ê²¬ì„ 추가했습니다.', + 'Unable to create your comment.' => 'ëŒ“ê¸€ì˜ ì¶”ê°€ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Due Date' => '마ê°ì¼', + 'Invalid date' => '날짜가 무효입니다', + 'Automatic actions' => 'ìžë™ì•¡ì…˜ 관리', + 'Your automatic action has been created successfully.' => 'ìžë™ ì•¡ì…˜ì„ ìž‘ì„±í–ˆìŠµë‹ˆë‹¤.', + 'Unable to create your automatic action.' => 'ìžë™ ì•¡ì…˜ì˜ ìž‘ì„±ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Remove an action' => 'ìžë™ ì•¡ì…˜ì˜ ì‚­ì œ', + 'Unable to remove this action.' => 'ìžë™ ì•¡ì…˜ì˜ ì‚­ì œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Action removed successfully.' => 'ìžë™ ì•¡ì…˜ì˜ ì‚­ì œì— ì„±ê³µí–ˆì–´ìš”.', + 'Automatic actions for the project "%s"' => '"%s" 프로ì íŠ¸ë¥¼ 위한 ìžë™ ì•¡ì…˜', + 'Add an action' => 'ìžë™ ì•¡ì…˜ 추가', + 'Event name' => 'ì´ë²¤íЏ ì´ë¦„', + 'Action' => 'ì•¡ì…˜', + 'Event' => 'ì´ë²¤íЏ', + 'When the selected event occurs execute the corresponding action.' => 'ì„ íƒëœ ì´ë²¤íŠ¸ê°€ ë°œìƒí–ˆì„ 때 대ì‘하는 ì•¡ì…˜ì„ ì‹¤í–‰í•œë‹¤.', + 'Next step' => 'ë‹¤ìŒ ë‹¨ê³„', + '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 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' => 'ìƒ‰ì„ ì‚¬ìš©ìžì— 할당', + 'Position' => '위치', + 'Duplicate to project' => '다른 프로ì íŠ¸ì— ë³µì‚¬', + 'Duplicate' => '복사', + 'Link' => 'ë§í¬', + 'Comment updated successfully.' => 'ëŒ“ê¸€ì„ ê°±ì‹ í–ˆìŠµë‹ˆë‹¤.', + 'Unable to update your comment.' => 'ëŒ“ê¸€ì˜ ê°±ì‹ ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Remove a comment' => '댓글 ì‚­ì œ', + 'Comment removed successfully.' => 'ëŒ“ê¸€ì„ ì‚­ì œí–ˆìŠµë‹ˆë‹¤.', + 'Unable to remove this comment.' => 'ëŒ“ê¸€ì˜ ì‚­ì œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Do you really want to remove this comment?' => 'ëŒ“ê¸€ì„ ì‚­ì œí•©ë‹ˆê¹Œ?', + 'Current password for the user "%s"' => 'ì‚¬ìš©ìž "%s"ì˜ í˜„ìž¬ 패스워드', + 'The current password is required' => 'í˜„ìž¬ì˜ íŒ¨ìŠ¤ì›Œë“œë¥¼ 입력하세요', + 'Wrong password' => '패스워드가 다릅니다', + 'Unknown' => '불명', + 'Last logins' => '마지막 로그ì¸', + 'Login date' => 'ë¡œê·¸ì¸ ì¼ì‹œ', + 'Authentication method' => 'ì¸ì¦ 방법', + 'IP address' => 'IP 주소', + 'User agent' => 'ì‚¬ìš©ìž ì—ì´ì „트', + 'Persistent connections' => '세션', + 'No session.' => '세션 ì—†ìŒ', + 'Expiration date' => '유효기간', + 'Remember Me' => 'ìžë™ 로그ì¸', + 'Creation date' => '작성ì¼', + 'Everybody' => '모ë‘', + 'Open' => '열림', + 'Closed' => '닫힘', + 'Search' => '검색', + 'Nothing found.' => '결과가 없습니다', + 'Due date' => '마ê°ì¼', + 'Description' => '설명', + '%d comments' => '%dê°œì˜ ëŒ“ê¸€', + '%d comment' => '%dê°œì˜ ëŒ“ê¸€', + '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.' => '외부 계정과 í”„ë¡œí•„ì´ ì„±ê³µì ìœ¼ë¡œ ì—°ê²°ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Email' => 'ì´ë©”ì¼', + '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' => 'ìƒ‰ì— ë°”íƒ•ì„ ë‘ê³  카테고리를 바꾸었다', + 'Task creation or modification' => 'í• ì¼ì˜ 작성 ë˜ëŠ” 변경', + 'Category' => '카테고리', + 'Category:' => '카테고리:', + 'Categories' => '카테고리', + 'Your category has been created successfully.' => '카테고리를 작성했습니다.', + 'This category has been updated successfully.' => '카테고리를 갱신했습니다.', + 'Unable to update this 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' => '카테고리 ì—†ìŒ', + 'The name is required' => 'ì´ë¦„ì„ ìž…ë ¥í•˜ì‹­ì‹œì˜¤', + 'Remove a file' => 'íŒŒì¼ ì‚­ì œ', + 'Unable to remove this file.' => 'íŒŒì¼ ì‚­ì œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'File removed successfully.' => '파ì¼ì„ 삭제했습니다.', + 'Attach a document' => '문서 첨부', + 'Do you really want to remove this file: "%s"?' => 'íŒŒì¼ "%s" ì„ ì‚­ì œí• ê¹Œìš”?', + 'Attachments' => '첨부', + 'Edit the task' => 'í• ì¼ ìˆ˜ì •', + 'Add a comment' => '댓글 추가', + 'Edit a comment' => '댓글 수정', + 'Summary' => '개요', + 'Time tracking' => '시간 ì¶”ì ', + 'Estimate:' => '예측:', + 'Spent:' => '경과:', + 'Do you really want to remove this sub-task?' => '서브 í• ì¼ì„ 삭제합니까?', + 'Remaining:' => '나머지:', + 'hours' => '시간', + 'estimated' => '예측', + 'Sub-Tasks' => '서브 í• ì¼', + 'Add a sub-task' => '서브 í• ì¼ ì¶”ê°€', + 'Original estimate' => '최초 예측시간', + 'Create another sub-task' => 'ë‹¤ìŒ ì„œë¸Œ í• ì¼ ì¶”ê°€', + 'Time spent' => '경과시간', + 'Edit a sub-task' => '서브 í• ì¼ì„ 변경하는 ', + 'Remove a sub-task' => '서브 í• ì¼ì„ 삭제하는 ', + 'The time must be a numeric value' => 'ì‹œê°„ì€ ìˆ«ìžë¡œ 입력하세요', + 'Todo' => 'í• ì¼ ì˜ˆì •', + 'In progress' => 'í• ì¼ ì¤‘', + 'Sub-task removed successfully.' => '서브 í• ì¼ì„ 삭제했습니다.', + 'Unable to remove this sub-task.' => '서브 í• ì¼ì˜ 삭제가 실패했습니다.', + 'Sub-task updated successfully.' => '서브 í• ì¼ì„ 갱신했습니다.', + 'Unable to update your sub-task.' => '서브 í• ì¼ì˜ ê²½ì‹ ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Unable to create your sub-task.' => '서브 í• ì¼ì˜ ì¶”ê°€ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Maximum size: ' => '최대: ', + 'Display another project' => '프로ì íЏ 보기', + 'Created by %s' => 'ìž‘ì„±ìž %s', + 'Tasks Export' => 'í• ì¼ ë‚´ë³´ë‚´ê¸°', + 'Start Date' => '시작ì¼', + 'Execute' => '실행', + 'Task Id' => 'í• ì¼ ID', + 'Creator' => '작성ìž', + 'Modification date' => '변경 ì¼', + 'Completion date' => '완료ì¼', + 'Clone' => '복사', + 'Project cloned successfully.' => '프로ì íŠ¸ë¥¼ 복제했습니다.', + 'Unable to clone this project.' => '프로ì íŠ¸ì˜ ë³µì œì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤.', + 'Enable email notifications' => 'ì´ë©”ì¼ ì•Œë¦¼ 설정', + 'Task position:' => 'í• ì¼ ìœ„ì¹˜:', + 'The task #%d has been opened.' => 'í• ì¼ #%dê°€ 시작ë˜ì—ˆìŠµë‹ˆë‹¤', + 'The task #%d has been closed.' => 'í• ì¼ #%dê°€ 종료ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Sub-task updated' => '서브 í• ì¼ ê°±ì‹ ', + 'Title:' => '제목:', + 'Status:' => 'ìƒíƒœ:', + 'Assignee:' => '담당:', + 'Time tracking:' => '시간 계측:', + 'New sub-task' => '새로운 서브 í• ì¼', + 'New attachment added "%s"' => '"%s"ì˜ ìƒˆë¡œìš´ 첨부 파ì¼', + 'New comment posted by %s' => '"%s"ë‹˜ì´ ëŒ“ê¸€ì„ ì¶”ê°€í•˜ì˜€ìŠµë‹ˆë‹¤', + 'New comment' => ' 새로운 댓글', + 'Comment updated' => '댓글가 갱신ë˜ì—ˆìŠµë‹ˆë‹¤', + 'New subtask' => ' 새로운 서브 í• ì¼', + 'I only want to receive notifications for these projects:' => 'ë‹¤ìŒ í”„ë¡œì íŠ¸ì˜ ì•Œë¦¼ë§Œ 받겠습니다:', + 'view the task on Kanboard' => 'Kanboardì—서 í• ì¼ì„ 본다', + 'Public access' => '공개 ì ‘ì† ì„¤ì •', + 'Disable public access' => '공개 ì ‘ì† ë¹„í™œì„±í™”', + 'Enable public access' => '공개 ì ‘ì† í™œì„±í™”', + 'Public access disabled' => '공개 ì ‘ì† ë¶ˆê°€', + 'Move the task to another project' => 'í• ì¼ë³„ 프로ì íŠ¸ì— ì˜®ê¸°', + 'Move to project' => '다른 프로ì íŠ¸ë¡œ ì´ë™', + 'Do you really want to duplicate this task?' => 'í• ì¼ì„ 복제합니까?', + 'Duplicate a task' => 'í• ì¼ ë³µì‚¬', + 'External accounts' => '외부 계정', + 'Account type' => '계정종류', + 'Local' => '로컬', + 'Remote' => 'ì›ê²©', + 'Enabled' => '활성화', + 'Disabled' => '비활성화', + 'Login:' => '사용ìžëª…', + 'Full Name:' => 'ì´ë¦„:', + 'Email:' => 'ì´ë©”ì¼:', + 'Notifications:' => '알림:', + 'Notifications' => '알림', + 'Account type:' => '계정종류:', + 'Edit profile' => '프로필 변경', + 'Change password' => '패스워드 변경', + 'Password modification' => '패스워드 변경', + 'External authentications' => '외부 ì¸ì¦', + 'Never connected.' => 'ì ‘ì†ê¸°ë¡ì—†ìŒ', + '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' => 'ì˜ˆìƒ %shê°€ 할당ë˜ì§€ 않았습니다', + '%s updated a comment on the task %s' => '%sì´ í• ì¼%sì˜ ëŒ“ê¸€ì„ ìˆ˜ì •í–ˆìŠµë‹ˆë‹¤', + '%s commented the task %s' => '%sì´ í• ì¼%sì— ëŒ“ê¸€ì„ ë‚¨ê²¼ìŠµë‹ˆë‹¤', + '%s\'s activity' => '%sì˜ í™œë™', + 'RSS feed' => 'RSS피드', + '%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)' => '새로운 프로ì íŠ¸ì˜ ê¸°ë³¸ 컬럼 (콤마(,)로 분리ë¨)', + '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' => 'í–‰ì‚¬ì˜ ì„ íƒ', + 'Create a task from an external provider' => 'í• ì¼ì„ 외부 서비스로부터 작성하는 ', + 'Change the assignee based on an external username' => '담당ìžë¥¼ 외부 ì„œë¹„ìŠ¤ì— ë°”íƒ•ì„ ë‘ê³  변경하는 ', + 'Change the category based on an external label' => '카테고리를 외부 ì„œë¹„ìŠ¤ì— ë°”íƒ•ì„ ë‘ê³  변경하는 ', + 'Reference' => '참조', + 'Label' => 'ë¼ë²¨', + 'Database' => 'ë°ì´í„°ë² ì´ìФ', + 'About' => 'ì •ë³´', + 'Database driver:' => 'ë°ì´í„°ë² ì´ìФ 드ë¼ì´ë²„:', + 'Board settings' => '기본 설정', + 'Webhook settings' => 'Webhookì˜ ì„¤ì •', + 'Reset token' => 'í† í° ë¦¬ì…‹', + 'API endpoint:' => 'API엔드 í¬ì¸íЏ:', + 'Refresh interval for personal 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ì´ˆ)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '초당 횟수(비활성화:0, 기본값:10ì´ˆ)', + 'Application URL' => '애플리케ì´ì…˜ì˜ URL', + 'Token regenerated.' => '토í°ì´ 다시 ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Date format' => 'ë°ì´í„° í¬ë©§', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO í¬ë©§ì€ í•­ìƒ ê°€ëŠ¥í•©ë‹ˆë‹¤. 예를 들어: "%s" 와 "%s"', + 'New personal project' => '새 비공개 프로ì íЏ', + 'This project is personal' => 'ì´ í”„ë¡œì íŠ¸ëŠ” 비공개입니다', + 'Add' => '추가', + 'Start date' => '시작시간', + 'Time estimated' => '예ìƒì‹œê°„', + 'There is nothing assigned to you.' => 'í• ì¼ì´ 없습니다. ì˜†ì‚¬ëžŒì˜ ì¼ì„ ë„와주면 어떨까요?', + 'My tasks' => 'ë‚´ í• ì¼', + 'Activity stream' => '활ë™ê¸°ë¡', + 'Dashboard' => '대시보드', + 'Confirmation' => '확ì¸', + 'Webhooks' => 'Webhook', + 'API' => 'API', + 'Create a comment from an external provider' => '외부 서비스로부터 ì˜ê²¬ì„ 작성한다', + 'Project management' => '프로ì íЏ 관리', + 'Columns' => '컬럼', + 'Task' => 'í• ì¼', + 'Percentage' => '비중', + 'Number of tasks' => 'í• ì¼ ìˆ˜', + 'Task distribution' => 'í• ì¼ ë¶„í¬', + 'Analytics' => 'ë¶„ì„', + 'Subtask' => '서브 í• ì¼', + 'User repartition' => 'ë‹´ë‹¹ìž ë¶„í¬', + 'Clone this project' => 'ì´ í”„ë¡œì íŠ¸ë¥¼ 복제하는 ', + 'Column removed successfully.' => '(※)ì»¬ëŸ¼ì„ ì‚­ì œí–ˆìŠµë‹ˆë‹¤', + 'Not enough data to show the graph.' => '그래프를 선묘화하려면 나왔지만 부족합니다', + 'Previous' => ' ëŒì•„ê°€', + 'The id must be an integer' => 'idì€ ìˆ«ìžê°€ 아니면 안 ë©ë‹ˆë‹¤', + 'The project id must be an integer' => 'project idì€ ìˆ«ìžê°€ 아니면 안 ë©ë‹ˆë‹¤', + 'The status must be an integer' => 'status는 숫ìžì§€ 않으면 안 ë©ë‹ˆë‹¤', + 'The subtask id is required' => 'subtask idê°€ 필요합니다', + 'The subtask id must be an integer' => 'subtask idì€ ìˆ«ìžê°€ 아니면 안 ë©ë‹ˆë‹¤', + 'The task id is required' => 'task idê°€ 필요합니다', + 'The task id must be an integer' => 'task idì€ ìˆ«ìžê°€ 아니면 안 ë©ë‹ˆë‹¤', + 'The user id must be an integer' => 'user idì€ ìˆ«ìžê°€ 아니면 안 ë©ë‹ˆë‹¤', + 'This value is required' => 'ì´ ê°’ì´ í•„ìš”í•©ë‹ˆë‹¤', + 'This value must be numeric' => 'ì´ ê°’ì€ ìˆ«ìžê°€ 아니면 안 ë©ë‹ˆë‹¤', + 'Unable to create this task.' => 'ì´ í• ì¼ì„ 작성할 수 없었습니다', + 'Cumulative flow diagram' => 'ì¶•ì  í름', + 'Daily project summary' => '하루 프로ì íЏ 개요', + 'Daily project summary export' => '하루 프로ì íЏ ê°œìš”ì˜ ì¶œë ¥', + 'Exports' => '출력', + 'This export contains the number of tasks per column grouped per day.' => 'ì´ ì¶œë ¥ì€ ë‚ ì§œì˜ ì¹¼ëžŒë³„ í• ì¼ ìˆ˜ë¥¼ 집계한 것입니다', + 'Active swimlanes' => '액티브한 스윔레ì¸', + 'Add a new swimlane' => ' 새로운 스윔레ì¸', + 'Default swimlane' => '기본 스윔레ì¸', + 'Do you really want to remove this swimlane: "%s"?' => '스웜레ì¸ì„ 삭제하시겠습니까: "%s"?', + 'Inactive swimlanes' => 'ì¸í„°ëž™í‹°ë¸Œí•œ 스윔레ì¸', + 'Remove a swimlane' => '스윔레ì¸ì˜ ì‚­ì œ', + 'Swimlane modification for the project "%s"' => '"%s" 프로ì íŠ¸ì˜ ìŠ¤ì›œë ˆì¸ ìˆ˜ì •', + 'Swimlane removed successfully.' => '스윔레ì¸ì„ 삭제했습니다.', + 'Swimlanes' => '스윔레ì¸', + 'Swimlane updated successfully.' => '스윔레ì¸ì„ 갱신했습니다.', + 'Unable to remove this swimlane.' => '스윔레ì¸ì„ 삭제할 수 없었습니다.', + 'Unable to update this swimlane.' => '스윔레ì¸ì„ 갱신할 수 없었습니다.', + 'Your swimlane has been created successfully.' => '스윔레ì¸ì´ 작성ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Example: "Bug, Feature Request, Improvement"' => '예: "버그, 특성 요청, í–¥ìƒ"', + 'Default categories for new projects (Comma-separated)' => '새로운 프로ì íŠ¸ì˜ ê¸°ë³¸ 카테고리 (콤마(,)로 구분)', + 'Integrations' => '연계', + 'Integration with third-party services' => '외부 서비스 연계', + 'Subtask Id' => '서브 í• ì¼ Id', + 'Subtasks' => '서브 í• ì¼', + 'Subtasks Export' => '서브 í• ì¼ ì¶œë ¥', + 'Task Title' => 'í• ì¼ ì œëª©', + 'Untitled' => '제목 ì—†ìŒ', + 'Application default' => '애플리케ì´ì…˜ 기본', + 'Language:' => '언어:', + 'Timezone:' => '시간대:', + 'All columns' => '모든 컬럼', + 'Next' => '다ìŒì— ', + '#%d' => '#%d', + 'All swimlanes' => '모든 스윔레ì¸', + 'All colors' => '모든 색', + 'Moved to column %s' => '"%s" 컬럼으로 ì´ë™', + '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' => '시간 트레킹', + 'You already have one subtask in progress' => 'ì´ë¯¸ ì§„í–‰ ì¤‘ì¸ ì„œë¸Œ í• ì¼ê°€ 있습니다.', + 'Which parts of the project do you want to duplicate?' => '프로ì íŠ¸ì˜ ë¬´ì—‡ì„ ë³µì œí•©ë‹ˆê¹Œ?', + 'Disallow login form' => '허가ë˜ì§€ ì•Šì€ ë¡œê·¸ì¸ ì–‘ì‹', + 'Start' => '시작', + 'End' => '종료', + 'Task age in days' => 'í• ì¼ì´ ìƒê¸´ 시간', + 'Days in this column' => 'ì´ ì»¬ëŸ¼ì— ìžˆëŠ” 시간', + '%dd' => '%dì¼', + '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' => '필드가 필요합니다', + 'Link added successfully.' => 'ë§í¬ë¥¼ 추가했습니다.', + 'Link updated successfully.' => 'ë§í¬ë¥¼ 갱신했습니다.', + 'Link removed successfully.' => 'ë§í¬ë¥¼ 삭제했습니다.', + 'Link labels' => 'ë§í¬ ë¼ë²¨', + 'Link modification' => 'ë§í¬ì˜ 변경', + 'Opposite label' => 'ë°˜ëŒ€ì˜ ë¼ë²¨', + 'Remove a link' => 'ë¼ë²¨ì˜ ì‚­ì œ', + '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.' => 'ë§í¬ë¥¼ 삭제할 수 없었습니다.', + 'relates to' => 'ì—°ê´€ ë§í¬', + 'blocks' => '다ìŒì„ 딜레ì´í•˜ëŠ”', + 'is blocked by' => 'ë‹¤ìŒ ë•Œë¬¸ì— ë”œë ˆì´ë˜ëŠ”', + 'duplicates' => '다ìŒê³¼ 중복하는', + 'is duplicated by' => '다ìŒì— 중복ë˜ëŠ”', + 'is a child of' => '다ìŒì˜ 하위 í• ì¼', + 'is a parent of' => '다ìŒì˜ ìƒìœ„ í• ì¼', + 'targets milestone' => '다ìŒì˜ ì´ì •표를 목표로 하는', + 'is a milestone of' => '다ìŒì˜ ì´ì •표ì¸', + 'fixes' => '다ìŒì„ 수정하는', + 'is fixed by' => '다ìŒì— ì˜í•´ 수정ë˜ëŠ”', + 'This task' => 'ì´ í• ì¼ì˜ ', + '<1h' => '<1시간', + '%dh' => '%d시간', + 'Expand tasks' => 'í• ì¼ í¬ê²Œ', + 'Collapse tasks' => 'í• ì¼ ìž‘ê²Œ', + 'Expand/collapse tasks' => 'í• ì¼ í¬ê²Œ/작게', + 'Close dialog box' => '다ì´ì–¼ë¡œê·¸ë¥¼ 닫습니다', + 'Submit a form' => '제출', + 'Board view' => '보드 ë·°', + 'Keyboard shortcuts' => '키보드 ìˆ ì»·', + 'Open board switcher' => '보드 ì „í™˜ì„ ì—´', + 'Application' => '애플리케ì´ì…˜', + 'Compact view' => '컴팩트 ë·°', + 'Horizontal scrolling' => '세로 스í¬ë¡¤', + 'Compact/wide view' => '컴팩트/와ì´ë“œ ë·°', + 'Currency' => '통화', + 'Personal project' => 'ê°œì¸ í”„ë¡œì íЏ', + 'AUD - Australian Dollar' => 'AUD - 호주 달러', + 'CAD - Canadian Dollar' => 'CAD -ìºë‚˜ë‹¤ 달러', + 'CHF - Swiss Francs' => 'CHF - 스위스 프랑', + 'Custom Stylesheet' => '커스텀 ìŠ¤íƒ€ì¼ ì‹œíŠ¸', + 'EUR - Euro' => 'EUR - 유로', + 'GBP - British Pound' => 'GBP - ì˜êµ­ 파운드', + 'INR - Indian Rupee' => 'INR - ì¸ë„ 루피', + 'JPY - Japanese Yen' => 'JPY - ì¼ë³¸ ì—”', + 'NZD - New Zealand Dollar' => 'NZD - 뉴질랜드 달러', + 'PEN - Peruvian Sol' => 'PEN - 페루 솔', + 'RSD - Serbian dinar' => 'RSD - 세르비아 디나르', + 'CNY - Chinese Yuan' => 'CNY - 중국 위안', + 'USD - US Dollar' => 'USD - 미국 달러', + 'VES - Venezuelan Bolívar' => 'VES - ë² ë„¤ìˆ˜ì—˜ë¼ ë³¼ë¦¬ë°”ë¥´', + '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' => 'ì»¬ëŸ¼ì— ìžˆë˜ ì‹œê°„', + '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.' => 'ì´ ë¦¬í¬íŠ¸ëŠ” í• ì¼ì˜ 컬럼 ê°„ ì´ë™ì„ 시간, 유저, 경과 시간과 함께 기ë¡í•œ 것입니다.', + 'Currency rates' => '환율', + 'Rate' => 'ë ˆì´íЏ', + 'Change reference currency' => 'í˜„ìž¬ì˜ ê¸°ì¶• 통화', + 'Reference currency' => '기축 통화', + 'The currency rate has been added successfully.' => '통화가 성공ì ìœ¼ë¡œ 추가ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Unable to add this currency rate.' => 'ì´ í†µí™” í™˜ìœ¨ì„ ì¶”ê°€í•  수 없습니다.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%sì´ í• ì¼ %sì˜ ë‹´ë‹¹ì„ ì‚­ì œí–ˆìŠµë‹ˆë‹¤', + 'Information' => 'ì •ë³´', + 'Check two factor authentication code' => '2단 ì¸ì¦ì„ ì²´í¬í•œë‹¤', + 'The two factor authentication code is not valid.' => '2단 ì¸ì¦ 코드는 무효입니다.', + 'The two factor authentication code is valid.' => '2단 ì¸ì¦ 코드는 유효합니다.', + 'Code' => '코드', + 'Two factor authentication' => '2단 ì¸ì¦', + 'This QR code contains the key URI: ' => 'QR 코드는 키 URI를 í¬í•¨í•©ë‹ˆë‹¤: ', + 'Check my code' => '코드 ì²´í¬', + 'Secret key: ' => '비밀키: ', + 'Test your device' => '디바ì´ìФ 테스트', + 'Assign a color when the task is moved to a specific column' => 'ìƒì„¸ 컬럼으로 ì´ë™í•  í• ì¼ì˜ ìƒ‰ê¹”ì„ ì§€ì •í•˜ì„¸ìš”', + '%s via Kanboard' => '%s via E-board', + 'Burndown chart' => '번다운 차트', + '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 - 스위스 í¬ë¡œë‚˜', + 'Identifier' => 'ì‹ë³„ìž', + 'Disable two factor authentication' => 'ì´ì¤‘ ì¸ì¦ 비활성화', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '"%s" 담당ìžì˜ ì´ì¤‘ ì¸ì¦ì„ 비활성화 하시겠습니까?', + 'Edit link' => 'ë§í¬ 수정', + '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' => '반복 í• ì¼ì´ ìƒì„±ëœ 예정입니다.', + 'Score' => 'ì ìˆ˜', + 'The identifier must be unique' => 'ì‹ë³„ìžëŠ” 유ì¼í•´ì•¼ 합니다', + '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' => '종료날짜 계산 단위', + '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: ' => '새로운 종료날짜 계산: ', + 'Month(s)' => 'ì›”', + 'This task has been created by: ' => 'í• ì¼ì„ 만들었습니다: ', + 'Recurrent task has been generated:' => '반복 í• ì¼ì´ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Timeframe to calculate new due date: ' => '종료날짜 계산 단위', + 'Trigger to generate recurrent task: ' => '반복 í• ì¼ ìƒì„± 트리거', + 'When task is closed' => 'í• ì¼ì„ 마쳤ì„때', + 'When task is moved from first column' => 'í• ì¼ì´ 첫번째 컬럼으로 옮겨졌ì„때', + 'When task is moved to last column' => 'í• ì¼ì´ 마지막 컬럼으로 옮겨졌ì„때', + 'Year(s)' => 'ë…„', + 'Project settings' => '프로ì íЏ 설정', + 'Automatically update the start date' => '시작ì¼ì— ìžë™ 갱신', + 'iCal feed' => 'iCal 피드', + 'Preferences' => 'ìš°ì„ ê¶Œ', + 'Security' => '보안', + 'Two factor authentication disabled' => 'ì´ì¤‘ ì¸ì¦ì´ 비활성화 ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Two factor authentication enabled' => 'ì´ì¤‘ ì¸ì¦ì´ 활성화 ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Unable to update this user.' => 'ë‹´ë‹¹ìž ê°±ì‹ ì´ ê°€ëŠ¥í•©ë‹ˆë‹¤', + 'There is no user management for personal 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' => '카테고리 변경시 í• ì¼ì„ 다른 컬럼으로 ì´ë™', + 'Send a task by email to someone' => 'í• ì¼ì„ ì´ë©”ì¼ë¡œ 보내기', + 'Reopen a task' => 'í• ì¼ ë‹¤ì‹œ 시작', + 'Notification' => '알림', + '%s moved the task #%d to the first swimlane' => '%sê°€ í• ì¼ #%d를 첫번째 스웜레ì¸ìœ¼ë¡œ ì´ë™ì‹œì¼°ìŠµë‹ˆë‹¤', + 'Swimlane' => '스윔레ì¸', + '%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.' => '해당 ê¸°ê°„ì˜ ëª¨ë“  í• ì¼ ì •ë³´ê°€ ë³´ê³ ì„œì— í¬í•¨ë©ë‹ˆë‹¤', + 'Project activities for %s' => '%sì˜ í”„ë¡œì íЏ 활성화', + 'view the board on Kanboard' => 'kanboard로 보드 보기', + 'The task has been moved to the first swimlane' => 'í• ì¼ì´ 첫번째 스웜ë¼ì¸ìœ¼ë¡œ ì´ë™ë˜ì–´ 있습니다', + 'The task has been moved to another swimlane:' => 'í• ì¼ì´ 다른 스웜ë¼ì¸ìœ¼ë¡œ ì´ë™ë˜ì–´ 있습니다', + 'New title: %s' => '제목 변경: %s', + 'The task is not assigned anymore' => 'ë‹´ë‹¹ìž ì—†ìŒ', + 'New assignee: %s' => 'ë‹´ë‹¹ìž ë³€ê²½: %s', + 'There is no category now' => '카테고리 ì—†ìŒ', + 'New category: %s' => '카테고리 변경: %s', + 'New color: %s' => '색깔 변경: %s', + 'New complexity: %d' => 'ë³µìž¡ë„ ë³€ê²½: %d', + 'The due date has been removed' => '마ê°ë‚ ì§œ ì‚­ì œ', + 'There is no description anymore' => '설명 ì—†ìŒ', + 'Recurrence settings has been modified' => 'ë°˜ë³µí• ì¼ ì„¤ì • 수정', + 'Time spent changed: %sh' => '경과시간 변경: %s시간', + 'Time estimated changed: %sh' => '%s시간으로 예ìƒì‹œê°„ 변경', + 'The field "%s" has 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:' => '다ìŒì˜ ì•Œë¦¼ì„ ë°›ê¸°ë¥¼ ì›í•©ë‹ˆë‹¤:', + 'All tasks' => '모든 í• ì¼', + 'Only for tasks assigned to me' => 'ë‚´ê°€ 담당ìžì¸ ì¼', + 'Only for tasks created by me' => 'ë‚´ê°€ 만든 ì¼', + 'Only for tasks created by me and tasks assigned to me' => 'ë‚´ê°€ 만들었거나 ë‚´ê°€ 담당ìžì¸ ì¼', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => '모든 컬럼', + 'You need at least 2 days of data to show the chart.' => '차트를 보기 위하여 최소 2ì¼ì˜ ë°ì´í„°ê°€ 필요합니다', + '<15m' => '<15ë¶„', + '<30m' => '<30ë¶„', + 'Stop timer' => '타ì´ë¨¸ ì •ì§€', + 'Start timer' => '타ì´ë¨¸ 시작', + 'My activity stream' => 'ë‚´ 활ë™ê¸°ë¡', + 'Search tasks' => 'í• ì¼ ì°¾ê¸°', + 'Reset filters' => 'í•„í„° 리셋', + 'My tasks due tomorrow' => 'ë‚´ì¼ê¹Œì§€ ë‚´ í• ì¼', + 'Tasks due today' => '오늘까지 í• ì¼', + 'Tasks due tomorrow' => 'ë‚´ì¼ê¹Œì§€ í• ì¼', + 'Tasks due yesterday' => '어제까지 í• ì¼', + 'Closed tasks' => '닫힌 í• ì¼', + 'Open tasks' => '열린 í• ì¼', + 'Not assigned' => '담당ìžê°€ 없는 ì¼', + 'View advanced search syntax' => '추가 검색 문법보기', + 'Overview' => '개요', + 'Board/Calendar/List view' => '보드/달력/리스트 보기', + 'Switch to the board view' => '보드 보기로 전환', + 'Switch to the list view' => '리스트 보기로 전환', + 'Go to the search/filter box' => '검색/í•„í„° 박스로 ì´ë™', + 'There is no activity yet.' => '활ë™ì´ 없습니다', + 'No tasks found.' => 'í• ì¼ì„ ì°¾ì„ ìˆ˜ 없습니다', + 'Keyboard shortcut: "%s"' => '쉬운 키보드: "%s"', + 'List' => '목ë¡', + 'Filter' => 'í•„í„°', + 'Advanced search' => '검색 문법', + 'Example of query: ' => '문법 예제 ', + 'Search by project: ' => '프로ì íŠ¸ë¡œ 찾기 ', + 'Search by column: ' => '컬럼으로 찾기 ', + 'Search by assignee: ' => '담당ìžë¡œ 찾기 ', + 'Search by color: ' => '색깔로 찾기 ', + 'Search by category: ' => '카테고리로 찾기 ', + 'Search by description: ' => '설명으로 찾기 ', + 'Search by due date: ' => '마ê°ë‚ ì§œë¡œ 찾기 ', + 'Average time spent in each column' => 'ê° ì¹¼ëŸ¼ì˜ í‰ê·  소요시간', + 'Average time spent' => 'í‰ê·  소요시간', + 'This chart shows the average time spent in 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 shows the average lead and cycle time for the last %d tasks over the time.' => '마지막 %d í• ì¼ì˜ í‰ê·  리드와 사ì´í´ ì‹œê°„ì„ ì°¨íŠ¸ì— í‘œì‹œí•©ë‹ˆë‹¤', + 'Average time into each column' => 'ê° ì»¬ëŸ¼ì˜ í‰ê·  시간', + 'Lead and cycle time' => '리드와 사ì´í´ 시간', + 'Lead time: ' => '리드 시간: ', + 'Cycle time: ' => '사ì´í´ 시간: ', + 'Time spent in 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 the start date automatically' => 'ìžë™ìœ¼ë¡œ 시작 날짜를 설정합니다', + '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.' => '만약 "ë¡œê·¸ì¸ í¼ ê±°ì ˆ"ì— ì²´í¬í•œë‹¤ë©´, ë¡œê·¸ì¸ í¼ì— 접근할 ìžê²©ì´ 무시ë©ë‹ˆë‹¤', + '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 category: %s' => '현재 카테고리: %s', + 'no category' => '카테고리 아님', + 'Current assignee: %s' => '현재 할당ìž: %s', + 'not assigned' => '할당ë˜ì§€ 않ìŒ', + 'Author:' => '글쓴ì´:', + 'contributors' => '기여ìž', + 'License:' => 'ë¼ì´ì„¼ìФ:', + 'License' => 'ë¼ì´ì„¼ìФ', + 'Enter the text below' => '아랫글로 들어가기', + 'Start date:' => '시작ì¼:', + 'Due date:' => '만기ì¼:', + 'People who are project managers' => '프로ì íЏ 매니저', + 'People who are project members' => '프로ì íЏ 멤버', + 'NOK - Norwegian Krone' => 'NOK - ë…¸ë¥´ì›¨ì´ í¬ë¡œë„¤', + 'Show this column' => '컬럼 ë³´ì´ê¸°', + 'Hide this column' => '컬럼 숨기기', + 'End date' => '종료 ë‚ ì§œ', + 'Users overview' => '유저 전체보기', + 'Members' => '멤버', + 'Shared project' => '프로ì íЏ 공유', + 'Project managers' => '프로ì íЏ 매니저', + 'Projects list' => '프로ì íЏ 리스트', + 'End date:' => 'ë‚ ì§œ 수정', + 'Change task color when using a specific task link' => '특정 í• ì¼ ë§í¬ë¥¼ 사용할때 í• ì¼ì˜ 색깔 변경', + 'Task link creation or modification' => 'í• ì¼ ë§í¬ ìƒì„± í˜¹ì€ ìˆ˜ì •', + 'Milestone' => '마ì¼ìŠ¤í†¤', + 'Reset the search/filter box' => '찾기/í•„í„° 박스 초기화', + 'Documentation' => '문서', + 'Author' => '글쓴ì´', + 'Version' => '버전', + 'Plugins' => '플러그ì¸', + 'There is no plugin loaded.' => '플러그ì¸ì´ 로드ë˜ì§€ 않았습니다', + 'My notifications' => 'ë‚´ 알림', + 'Custom filters' => 'ì‚¬ìš©ìž ì •ì˜ í•„í„°', + 'Your custom filter has 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 has been updated successfully.' => 'ì‚¬ìš©ìž ì •ì˜ í•„í„°ê°€ 성공ì ìœ¼ë¡œ 수정ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Unable to update custom filter.' => 'ì •ì˜ í•„í„° 수정 비활성화', + 'Web' => '웹', + '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ê°€ 갱신 ë˜ì—ˆìŠµë‹ˆë‹¤', + 'New task #%d: %s' => 'í• ì¼ #%d: %sì´ ì¶”ê°€ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Task updated #%d' => 'í• ì¼ #%dì´ ê°±ì‹  ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Task #%d closed' => 'í• ì¼ #%d를 마쳤습니다', + 'Task #%d opened' => 'í• ì¼ #%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 í• ì¼ì˜ 담당ìžê°€ 변경ë©ë‹ˆë‹¤', + '%d overdue tasks' => 'í• ì¼ì˜ ê¸°í•œì´ %dì¼ ì§€ë‚¬ìŠµë‹ˆë‹¤', + 'No notification.' => 'ì•Œë¦¼ì´ ì—†ìŠµë‹ˆë‹¤', + '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:' => '알림 방법', + 'Unable to read your file' => '파ì¼ì„ ì½ì„ 수 없습니다', + '%d task(s) have been imported successfully.' => '%d í• ì¼ì´ 성공ì ìœ¼ë¡œ 추가ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Nothing has been imported!' => '추가가 ë˜ì§€ 않았습니다!', + 'Import users from CSV file' => 'CSV 파ì¼ì—서 ì‚¬ìš©ìž ê°€ì ¸ì˜¤ê¸°', + '%d user(s) have been imported successfully.' => '%d 사용ìžê°€ 성공ì ìœ¼ë¡œ 추가ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Comma' => '콤마', + 'Semi-colon' => '세미콜론', + 'Tab' => '탭', + 'Vertical bar' => '세로줄', + '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)' => 'í•„í„° (변경 대신)추가', + 'Append/Replace' => '추가/변경', + 'Append' => '추가', + 'Replace' => '변경', + 'Import' => '가져오기', + 'Change sorting' => 'ì •ë ¬ 변경', + 'Tasks Importation' => 'í• ì¼ ê°€ì ¸ì˜¤ê¸°', + 'Delimiter' => '구분ìž', + 'Enclosure' => 'ë™ë´‰', + 'CSV File' => 'CSV 파ì¼', + 'Instructions' => '명령', + 'Your file must use the predefined CSV format' => '파ì¼ì€ 미리 ì •ì˜ëœ CVS 형ì‹ì´ì–´ì•¼ 합니다', + 'Your file must be encoded in UTF-8' => '파ì¼ì€ 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' => '만기ì¼ì€ ISO 형ì‹ì´ì–´ì•¼ 합니다: YYYY-MM-DD', + 'Download CSV template' => 'CVS 탬플릿 내려받기', + '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' => '%sì´ ìƒˆë¡œìš´ 파ì¼ì„ í• ì¼ %sì— ì¶”ê°€í–ˆìŠµë‹ˆë‹¤', + 'Link type' => 'ë§í¬ 형ì‹', + 'Assign automatically a category based on a link' => 'ë§í¬ 기반 ìžë™ 카테고리 할당', + 'BAM - Konvertible Mark' => 'BAM - 보스니아 마르카', + 'Assignee Username' => '담당ìžì˜ 사용ìžì´ë¦„', + 'Assignee Name' => 'ë‹¹ìž¥ìž ì´ë¦„', + 'Groups' => '그룹', + 'Members of %s' => '%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"' => '%s 그룹 맴버 추가', + 'Group member added successfully.' => '그룹 맴버가 성공ì ìœ¼ë¡œ 추가ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Unable to add group member.' => '그룹 맴버 추가 비활성화', + 'Remove user from group "%s"' => '%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' => '%dë¶„ ë™ì•ˆ ê³„ì •ì´ ìž ê¹ë‹ˆë‹¤', + 'Invalid captcha' => 'ìž˜ëª»ëœ ë³´ì•ˆ 문ìž', + 'The name must be unique' => 'ì´ë¦„ì€ ìœ ì¼í•´ì•¼ 합니다', + 'View all groups' => '모든그룹보기', + 'There is no user available.' => '가능한 사용ìžê°€ 없습니다', + 'Do you really want to remove the user "%s" from the group "%s"?' => '"%s" 사용ìžë¥¼ "%s" ì—서 삭제하시겠습니까?', + 'There is no group.' => 'ê·¸ë£¹ì´ ì—†ìŠµë‹ˆë‹¤', + 'Add group member' => '멤버추가', + 'Do you really want to remove this group: "%s"?' => 'ê·¸ë£¹ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ: "%s"?', + 'There is no user in this group.' => 'ì´ ê·¸ë£¹ì—는 사용ìžê°€ 없습니다', + 'Permissions' => '권한', + 'Allowed Users' => 'ì‚¬ìš©ìž ìŠ¹ì¸', + 'No specific user has been allowed.' => '구체ì ìœ¼ë¡œ 승ì¸ëœ 사용ìžê°€ 없습니다', + 'Role' => 'ì—­í• ', + 'Enter user name...' => 'ì‚¬ìš©ìž ì´ë¦„ì„ ìž…ë ¥í•©ë‹ˆë‹¤...', + 'Allowed Groups' => '승ì¸ëœ 그룹', + 'No group has been allowed.' => '구체ì ìœ¼ë¡œ 승ì¸ëœ ê·¸ë£¹ì´ ì—†ìŠµë‹ˆë‹¤', + 'Group' => '그룹', + 'Group Name' => '그룹명', + 'Enter group name...' => 'ê·¸ë£¹ëª…ì„ ìž…ë ¥í•©ë‹ˆë‹¤...', + 'Role:' => 'ì—­í• : ', + 'Project members' => '프로ì íЏ 멤버', + '%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ì˜ ëŒ“ê¸€ì—서 언급ë˜ì—ˆìŠµë‹ˆë‹¤', + 'Estimated hours: ' => 'ì˜ˆìƒ ì‹œê°„: ', + 'Actual hours: ' => '실제 시간: ', + 'Hours Spent' => '소요 시간', + 'Hours Estimated' => 'ì˜ˆìƒ ì‹œê°„', + 'Estimated Time' => 'ì˜ˆìƒ ì‹œê°„', + 'Actual Time' => '실제 시간', + 'Estimated vs actual time' => 'ì˜ˆìƒ vs 실제 시간', + '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.' => '현재 등ë¡ëœ í†µí•©ì´ ì—†ìŠµë‹ˆë‹¤.', + 'Password Reset for Kanboard' => 'Kanboardì˜ ë¹„ë°€ë²ˆí˜¸ 초기화', + 'Forgot password?' => '비밀번호 찾기', + 'Enable "Forget Password"' => '"비밀번호 분실" 활성화', + 'Password Reset' => '비밀번호 초기화', + 'New password' => '새로운 비밀번호', + 'Change Password' => '비밀번호 변경', + 'To reset your password click on this link:' => '비밀번호를 초기화 하시려면 ë§í¬ë¥¼ 눌러주세요:', + 'Last Password Reset' => '마지막 비밀번호 초기화', + 'The password has never been reinitialized.' => '비밀번호가 초기화ë˜ì§€ 않았습니다', + 'Creation' => 'ìƒì„±', + 'Expiration' => '만료', + 'Password reset history' => '비밀번호 초기화 기ë¡', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '컬럼 "%s"와 스웜ë¼ì¸ "%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 in this column and this swimlane' => 'ì»¬ëŸ¼ì˜ ëª¨ë“  í• ì¼ ë§ˆì¹˜ê¸°', + '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.' => '프로ì íЏ 구분ìžëŠ” ì„ íƒì‚¬í•­ì´ë©°, 숫ìžì™€ 문ìžë§Œ 가능합니다. 예: MYPROJECT.', + 'Project owner' => '프로ì íЏ 소유ìž', + 'Personal projects do not have users and groups management.' => '비밀 프로ì íŠ¸ëŠ” 사용ìžë‚˜ 관리 ê·¸ë£¹ì´ ì†Œìœ í•˜ì§€ 않습니다', + 'There is no project member.' => '프로ì íЏ 맴버가 없습니다', + 'Priority' => '우선순위', + 'Task priority' => 'í• ì¼ì˜ 우선순위', + 'General' => 'ì¼ë°˜ì ì¸', + 'Dates' => 'ë‚ ì§œ', + 'Default priority' => '기본 우선순위', + 'Lowest priority' => 'ë‚®ì€ ìš°ì„ ìˆœìœ„', + 'Highest priority' => 'ë†’ì€ ìš°ì„ ìˆœìœ„', + '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' => '첨부', + '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' => '%s 시간', + 'Time spent:' => '소요 시간:', + 'Created:' => 'ìƒì„±:', + 'Modified:' => '수정;', + 'Completed:' => '완료:', + 'Started:' => '시작:', + 'Moved:' => 'ì´ë™:', + 'Task #%d' => 'í• ì¼ #%d', + 'Time format' => '시간 형ì‹', + 'Start date: ' => '시작ì¼: ', + 'End date: ' => '종료ì¼: ', + 'New due date: ' => '새로운 만기ì¼: ', + 'Start date changed: ' => 'ë³€ê²½ëœ ì‹œìž‘ì¼: ', + 'Disable personal projects' => '비밀 프로ì íЏ 비활성화', + 'Do you really want to remove this custom filter: "%s"?' => 'ì •ì˜ í•„í„°ë¥¼ 삭제하시겠습니까: "%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.' => '모든 파ì¼ì´ 성공ì ìœ¼ë¡œ 업로드ë˜ì—ˆìŠµë‹ˆë‹¤', + 'The maximum allowed file size is %sB.' => '업로드 파ì¼ì˜ 최대 í¬ê¸°ëŠ” %sB 입니다', + 'Drag and drop your files here' => '파ì¼ì„ ì´ê³³ìœ¼ë¡œ ëŒê³ ì˜¤ê¸°', + 'choose files' => 'íŒŒì¼ ì„ íƒ', + 'View profile' => 'í”„ë¡œíŒŒì¼ ë³´ê¸°', + 'Two Factor' => 'ì´ì¤‘', + 'Disable user' => 'ì‚¬ìš©ìž ë¹„í™œì„±í™”', + 'Do you really want to disable this user: "%s"?' => '사용ìžë¥¼ 비활성화 시키겠습니까: "%s"?', + 'Enable user' => 'ì‚¬ìš©ìž í™œì„±í™”', + 'Do you really want to enable this user: "%s"?' => '사용ìžë¥¼ 활성화 시키겠습니까: "%s"?', + 'Download' => '내려받기', + 'Uploaded: %s' => '올리기: %s', + 'Size: %s' => 'í¬ê¸°: %s', + 'Uploaded by %s' => '%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' => 'ì´ ê°’ì€ %d보다 커야 합니다', + 'Another swimlane with the same name exists in the project' => '프로ì íŠ¸ì— ê°™ì€ ì´ë¦„ì˜ ìŠ¤ì›œë¼ì¸ì´ 존재합니다', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => '예: https://example.kanboard.org/ (ì ˆëŒ€ì  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 버전:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'ìš´ì˜ì²´ì œ 버전:', + 'Database version:' => 'ë°ì´í„°ë² ì´ìФ 버전:', + 'Browser:' => '브ë¼ìš°ì €:', + 'Task view' => 'í• ì¼ ë³´ê¸°', + 'Edit task' => 'í• ì¼ ìˆ˜ì •', + 'Edit description' => '설명 수정', + 'New internal link' => '새로운 ë‚´ë¶€ ë§í¬', + 'Display list of keyboard shortcuts' => '키보드 ìˆì»· 리스트 보여주기', + '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.' => '칸보드 ì¸ìŠ¤í„´ìŠ¤ê°€ ì‚¬ìš©ìž ì¸í„°íŽ˜ì´ìФì—서 플러그ì¸ì„ 설치하ë„ë¡ êµ¬ì„±ë˜ì§€ 않았습니다', + 'There is no plugin available.' => '사용 가능한 플러그ì¸ì´ 없습니다.', + 'Install' => '설치', + 'Update' => '갱신', + 'Up to date' => 'ë‚ ì§œ 갱신', + 'Not available' => '사용 불가능', + 'Remove plugin' => 'í”ŒëŸ¬ê·¸ì¸ ì‚­ì œ', + 'Do you really want to remove this plugin: "%s"?' => 'ì •ë§ë¡œ 플러그ì¸ì„ 삭제하시겠습니까: "%s"?', + 'Uninstall' => 'ì‚­ì œ', + 'Listing' => '목ë¡', + 'Metadata' => '메타ë°ì´í„°', + 'Manage projects' => '프로ì íЏ 관리', + 'Convert to task' => 'í• ì¼ë¡œ 변경하기', + 'Convert sub-task to task' => '서브 í• ì¼ì„ í• ì¼ë¡œ 변경하기', + 'Do you really want to convert this sub-task to a task?' => 'ì •ë§ë¡œ 서브 í• ì¼ì„ í• ì¼ë¡œ 변경하시겠습니까?', + 'My task title' => 'ë‚˜ì˜ í• ì¼ ì œëª©', + 'Enter one task by line.' => '정확히 í•˜ë‚˜ì˜ í• ì¼ë¡œ ì§„ìž…', + 'Number of failed login:' => 'ë¡œê·¸ì¸ ì‹¤íŒ¨ 횟수:', + 'Account locked until:' => '다ìŒë™ì•ˆ ê³„ì •ì´ ìž ê²¼ìŠµë‹ˆë‹¤:', + 'Email settings' => 'ì´ë©”ì¼ ì„¤ì •', + 'Email sender address' => 'ì´ë©”ì¼ ë³´ë‚¸ì´ ì£¼ì†Œ', + 'Email transport' => 'ì´ë©”ì¼ ì „ì†¡', + 'Webhook token' => 'Webhook토í°', + '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 a 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 has 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ì˜ í™œë™ê¸°ë¡', + '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' => '마í¬ë‹¤ìš´ìœ¼ë¡œ í…스트 작성', + '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' => '발신 ì´ë©”ì¼', + '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ê±´ì´ ë°œì†¡ë˜ì—ˆìŠµë‹ˆë‹¤.', + '%d invitation was sent.' => '초대 %dê±´ì´ ë°œì†¡ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Unable to create this user.' => 'ì´ ì‚¬ìš©ìžë¥¼ ìƒì„±í•  수 없습니다.', + 'Kanboard Invitation' => 'Kanboard 초대', + '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.' => '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' => 'ì´ ì‚¬ìš©ìž ì´ë¦„ì€ ì´ë¯¸ 사용 중입니다.', + 'Your profile must have a valid email address.' => 'í”„ë¡œí•„ì— ìœ íš¨í•œ ì´ë©”ì¼ ì£¼ì†Œê°€ 있어야 합니다.', + 'TRL - Turkish Lira' => 'TRL - 터키 리ë¼', + 'The project email is optional and could be used by several plugins.' => '프로ì íЏ ì´ë©”ì¼ì€ ì„ íƒ ì‚¬í•­ì´ë©° 여러 플러그ì¸ì—서 사용할 수 있습니다.', + 'The project email must be unique across all projects' => '프로ì íЏ ì´ë©”ì¼ì€ 모든 프로ì íЏì—서 고유해야 합니다.', + 'The email configuration has been disabled by the administrator.' => 'ì´ë©”ì¼ ì„¤ì •ì€ ê´€ë¦¬ìžì— ì˜í•´ 비활성화ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Close this project' => 'ì´ í”„ë¡œì íЏ 닫기', + 'Open this project' => 'ì´ í”„ë¡œì íЏ 열기', + 'Close a project' => '프로ì íЏ 닫기', + 'Do you really want to close this project: "%s"?' => 'ì´ í”„ë¡œì íŠ¸ë¥¼ ì •ë§ë¡œ 닫으시겠습니까: "%s"?', + 'Reopen a project' => '프로ì íЏ 다시 열기', + 'Do you really want to reopen this project: "%s"?' => 'ì´ í”„ë¡œì íŠ¸ë¥¼ ì •ë§ë¡œ 다시 여시겠습니까: "%s"?', + 'This project is open' => 'ì´ í”„ë¡œì íŠ¸ëŠ” ì—´ë ¤ 있습니다.', + 'This project is closed' => 'ì´ í”„ë¡œì íŠ¸ëŠ” 닫혀 있습니다.', + 'Unable to upload files, check the permissions of your data folder.' => '파ì¼ì„ 업로드할 수 없습니다. ë°ì´í„° í´ë”ì˜ ê¶Œí•œì„ í™•ì¸í•˜ì‹­ì‹œì˜¤.', + 'Another category with the same name exists in this project' => 'ì´ í”„ë¡œì íŠ¸ì— ê°™ì€ ì´ë¦„ì˜ ë‹¤ë¥¸ 카테고리가 있습니다.', + 'Comment sent by email successfully.' => 'ì´ë©”ì¼ë¡œ ëŒ“ê¸€ì„ ì„±ê³µì ìœ¼ë¡œ 보냈습니다.', + 'Sent by email to "%s" (%s)' => '"%s" (%s)ì—게 ì´ë©”ì¼ë¡œ 보냄', + 'Unable to read uploaded file.' => 'ì—…ë¡œë“œëœ íŒŒì¼ì„ ì½ì„ 수 없습니다.', + 'Database uploaded successfully.' => 'ë°ì´í„°ë² ì´ìŠ¤ê°€ 성공ì ìœ¼ë¡œ 업로드ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Task sent by email successfully.' => 'ìž‘ì—…ì´ ì´ë©”ì¼ë¡œ 성공ì ìœ¼ë¡œ 발송ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'There is no category in this project.' => 'ì´ í”„ë¡œì íЏì—는 카테고리가 없습니다.', + 'Send by email' => 'ì´ë©”ì¼ë¡œ 보내기', + 'Create and send a comment by email' => 'ì´ë©”ì¼ë¡œ 댓글 작성 ë° ë³´ë‚´ê¸°', + 'Subject' => '제목', + 'Upload the database' => 'ë°ì´í„°ë² ì´ìФ 업로드', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'ì´ì „ì— ë‹¤ìš´ë¡œë“œí•œ Sqlite ë°ì´í„°ë² ì´ìФ(Gzip 형ì‹)를 업로드할 수 있습니다.', + 'Database file' => 'ë°ì´í„°ë² ì´ìФ 파ì¼', + 'Upload' => '업로드', + 'Your project must have at least one active swimlane.' => '프로ì íЏì—는 하나 ì´ìƒì˜ 활성 스윔레ì¸ì´ 있어야 합니다.', + 'Project: %s' => '프로ì íЏ: %s', + 'Automatic action not found: "%s"' => 'ìžë™ ì•¡ì…˜ì„ ì°¾ì„ ìˆ˜ 없습니다: "%s"', + '%d projects' => '프로ì íЏ %dê°œ', + '%d project' => '프로ì íЏ %dê°œ', + 'There is no project.' => '프로ì íŠ¸ê°€ 없습니다.', + 'Sort' => 'ì •ë ¬', + 'Project ID' => '프로ì íЏ ID', + 'Project name' => '프로ì íЏ ì´ë¦„', + 'Public' => '공개', + 'Personal' => 'ê°œì¸', + '%d tasks' => '작업 %dê°œ', + '%d task' => '작업 %dê°œ', + 'Task ID' => '작업 ID', + 'Assign automatically a color when due date is expired' => '마ê°ì¼ì´ ì§€ë‚¬ì„ ë•Œ ìžë™ìœ¼ë¡œ ìƒ‰ìƒ í• ë‹¹', + 'Total score in this column across all swimlanes' => '모든 스윔레ì¸ì—서 ì´ ì—´ì˜ ì´ ì ìˆ˜', + 'HRK - Kuna' => 'HRK - 쿠나', + 'ARS - Argentine Peso' => 'ARS - 아르헨티나 페소', + 'COP - Colombian Peso' => 'COP - 콜롬비아 페소', + '%d groups' => '그룹 %dê°œ', + '%d group' => '그룹 %dê°œ', + 'Group ID' => '그룹 ID', + 'External ID' => '외부 ID', + '%d users' => 'ì‚¬ìš©ìž %d명', + '%d user' => 'ì‚¬ìš©ìž %d명', + 'Hide subtasks' => '하위 작업 숨기기', + 'Show subtasks' => '하위 작업 표시', + 'Authentication Parameters' => 'ì¸ì¦ 매개변수', + 'API Access' => 'API ì ‘ê·¼', + 'No users found.' => '사용ìžë¥¼ ì°¾ì„ ìˆ˜ 없습니다.', + 'User ID' => 'ì‚¬ìš©ìž ID', + 'Notifications are activated' => 'ì•Œë¦¼ì´ í™œì„±í™”ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Notifications are disabled' => 'ì•Œë¦¼ì´ ë¹„í™œì„±í™”ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'User disabled' => 'ì‚¬ìš©ìž ë¹„í™œì„±í™”ë¨', + '%d notifications' => '알림 %dê°œ', + '%d notification' => '알림 %dê°œ', + 'There is no external integration installed.' => 'ì„¤ì¹˜ëœ ì™¸ë¶€ í†µí•©ì´ ì—†ìŠµë‹ˆë‹¤.', + 'You are not allowed to update tasks assigned to someone else.' => '다른 사람ì—게 í• ë‹¹ëœ ìž‘ì—…ì„ ì—…ë°ì´íŠ¸í•  수 없습니다.', + 'You are not allowed to change the assignee.' => '담당ìžë¥¼ 변경할 수 없습니다.', + 'Task suppression is not permitted' => '작업 삭제는 허용ë˜ì§€ 않습니다.', + 'Changing assignee is not permitted' => 'ë‹´ë‹¹ìž ë³€ê²½ì€ í—ˆìš©ë˜ì§€ 않습니다.', + 'Update only assigned tasks is permitted' => 'í• ë‹¹ëœ ìž‘ì—…ë§Œ ì—…ë°ì´íŠ¸í•  수 있습니다.', + 'Only for tasks assigned to the current user' => '현재 사용ìžì—게 í• ë‹¹ëœ ìž‘ì—…ì—ë§Œ 해당', + 'My projects' => 'ë‚´ 프로ì íЏ', + 'You are not a member of any project.' => 'ì–´ë–¤ 프로ì íŠ¸ì˜ ë©¤ë²„ë„ ì•„ë‹™ë‹ˆë‹¤.', + 'My subtasks' => 'ë‚´ 하위 작업', + '%d subtasks' => '하위 작업 %dê°œ', + '%d subtask' => '하위 작업 %dê°œ', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => '현재 사용ìžì—게 í• ë‹¹ëœ ìž‘ì—…ì˜ ê²½ìš° 해당 ì—´ ê°„ì—ë§Œ 작업 ì´ë™ì´ 허용ë©ë‹ˆë‹¤.', + '[DUPLICATE]' => '[복제]', + 'DKK - Danish Krona' => 'DKK - ë´ë§ˆí¬ í¬ë¡œë„¤', + 'Remove user from group' => '그룹ì—서 ì‚¬ìš©ìž ì œê±°', + 'Assign the task to its creator' => 'ìž‘ì—…ì„ ìƒì„±ìžì—게 할당', + 'This task was sent by email to "%s" with subject "%s".' => 'ì´ ìž‘ì—…ì€ "%s"ì—게 제목 "%s"으로 ì´ë©”ì¼ì´ 발송ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Predefined Email Subjects' => '사전 ì •ì˜ëœ ì´ë©”ì¼ ì œëª©', + 'Write one subject by line.' => '한 ì¤„ì— í•˜ë‚˜ì˜ ì œëª©ì„ ìž‘ì„±í•˜ì„¸ìš”.', + 'Create another link' => '다른 ë§í¬ ìƒì„±', + 'BRL - Brazilian Real' => 'BRL - 브ë¼ì§ˆ 헤알', + 'Add a new Kanboard task' => '새 Kanboard 작업 추가', + 'Subtask not started' => '하위 작업 시작 안 함', + 'Subtask currently in progress' => '하위 작업 현재 ì§„í–‰ 중', + 'Subtask completed' => '하위 작업 완료ë¨', + 'Subtask added successfully.' => '하위 ìž‘ì—…ì´ ì„±ê³µì ìœ¼ë¡œ 추가ë˜ì—ˆìŠµë‹ˆë‹¤.', + '%d subtasks added successfully.' => '하위 작업 %d개가 성공ì ìœ¼ë¡œ 추가ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Enter one subtask by line.' => '한 ì¤„ì— í•˜ë‚˜ì˜ í•˜ìœ„ ìž‘ì—…ì„ ìž…ë ¥í•˜ì„¸ìš”.', + 'Predefined Contents' => '사전 ì •ì˜ëœ ë‚´ìš©', + 'Predefined contents' => '사전 ì •ì˜ëœ ë‚´ìš©', + 'Predefined Task Description' => '사전 ì •ì˜ëœ 작업 설명', + 'Do you really want to remove this template? "%s"' => 'ì •ë§ë¡œ ì´ í…œí”Œë¦¿ì„ ì œê±°í•˜ì‹œê² ìŠµë‹ˆê¹Œ? "%s"', + 'Add predefined task description' => '사전 ì •ì˜ëœ 작업 설명 추가', + 'Predefined Task Descriptions' => '사전 ì •ì˜ëœ 작업 설명', + 'Template created successfully.' => 'í…œí”Œë¦¿ì´ ì„±ê³µì ìœ¼ë¡œ ìƒì„±ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Unable to create this template.' => 'ì´ í…œí”Œë¦¿ì„ ìƒì„±í•  수 없습니다.', + 'Template updated successfully.' => 'í…œí”Œë¦¿ì´ ì„±ê³µì ìœ¼ë¡œ ì—…ë°ì´íЏë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Unable to update this template.' => 'ì´ í…œí”Œë¦¿ì„ ì—…ë°ì´íŠ¸í•  수 없습니다.', + 'Template removed successfully.' => 'í…œí”Œë¦¿ì´ ì„±ê³µì ìœ¼ë¡œ 제거ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Unable to remove this template.' => 'ì´ í…œí”Œë¦¿ì„ ì œê±°í•  수 없습니다.', + 'Template for the task description' => '작업 설명 템플릿', + 'The start date is greater than the end date' => '시작 날짜가 종료 날짜보다 í½ë‹ˆë‹¤.', + 'Tags must be separated by a comma' => '태그는 쉼표로 구분해야 합니다.', + 'Only the task title is required' => '작업 제목만 필수입니다.', + 'Creator Username' => 'ìƒì„±ìž ì‚¬ìš©ìž ì´ë¦„', + 'Color Name' => 'ìƒ‰ìƒ ì´ë¦„', + 'Column Name' => 'ì—´ ì´ë¦„', + 'Swimlane Name' => 'ìŠ¤ìœ”ë ˆì¸ ì´ë¦„', + 'Time Estimated' => 'ì˜ˆìƒ ì‹œê°„', + 'Time Spent' => '소요 시간', + 'External Link' => '외부 ë§í¬', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'ì´ ê¸°ëŠ¥ì€ iCal 피드, RSS 피드 ë° ê³µê°œ 보드 보기를 활성화합니다.', + 'Stop the timer of all subtasks when moving a task to another column' => 'ìž‘ì—…ì„ ë‹¤ë¥¸ 열로 ì´ë™í•  때 모든 하위 ìž‘ì—…ì˜ íƒ€ì´ë¨¸ 중지', + 'Subtask Title' => '하위 작업 제목', + 'Add a subtask and activate the timer when moving a task to another column' => '하위 ìž‘ì—…ì„ ì¶”ê°€í•˜ê³  ìž‘ì—…ì´ ë‹¤ë¥¸ 열로 ì´ë™í•  때 타ì´ë¨¸ 활성화', + 'days' => 'ì¼', + 'minutes' => 'ë¶„', + 'seconds' => 'ì´ˆ', + 'Assign automatically a color when preset start date is reached' => '사전 ì„¤ì •ëœ ì‹œìž‘ ë‚ ì§œì— ë„달하면 ìžë™ìœ¼ë¡œ ìƒ‰ìƒ í• ë‹¹', + 'Move the task to another column once a predefined start date is reached' => '사전 ì •ì˜ëœ 시작 ë‚ ì§œì— ë„달하면 ìž‘ì—…ì„ ë‹¤ë¥¸ 열로 ì´ë™', + 'This task is now linked to the task %s with the relation "%s"' => 'ì´ ìž‘ì—…ì€ ì´ì œ "%s" 관계를 사용하여 작업 %sì— ì—°ê²°ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'The link with the relation "%s" to the task %s has been removed' => '작업 %sì— ëŒ€í•œ "%s" ê´€ê³„ì˜ ë§í¬ê°€ 제거ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Custom Filter:' => 'ì‚¬ìš©ìž ì •ì˜ í•„í„°:', + 'Unable to find this group.' => 'ì´ ê·¸ë£¹ì„ ì°¾ì„ ìˆ˜ 없습니다.', + '%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 moved the task #%d to the swimlane "%s"' => '%sì´(ê°€) 작업 #%dì„(를) "%s" 스윔레ì¸(으)로 ì´ë™í–ˆìŠµë‹ˆë‹¤.', + '%sh spent' => '%sh 소요', + '%sh estimated' => '%sh 예ìƒ', + 'Select All' => 'ëª¨ë‘ ì„ íƒ', + 'Unselect All' => 'ëª¨ë‘ ì„ íƒ í•´ì œ', + 'Apply action' => '작업 ì ìš©', + 'Move selected tasks to another column or swimlane' => 'ì„ íƒí•œ ìž‘ì—…ì„ ë‹¤ë¥¸ ì—´ ë˜ëŠ” 스윔레ì¸ìœ¼ë¡œ ì´ë™', + 'Edit tasks in bulk' => '작업 ì¼ê´„ 편집', + 'Choose the properties that you would like to change for the selected tasks.' => 'ì„ íƒí•œ ìž‘ì—…ì— ëŒ€í•´ 변경하려는 ì†ì„±ì„ ì„ íƒí•˜ì‹­ì‹œì˜¤.', + 'Configure this project' => 'ì´ í”„ë¡œì íЏ 구성', + 'Start now' => '지금 시작', + '%s removed a file from the task #%d' => '%sì´(ê°€) 작업 #%dì—서 파ì¼ì„ 제거했습니다.', + 'Attachment removed from task #%d: %s' => '작업 #%dì—서 첨부 íŒŒì¼ ì œê±°ë¨: %s', + 'No color' => 'ìƒ‰ìƒ ì—†ìŒ', + 'Attachment removed "%s"' => '첨부 íŒŒì¼ "%s" 제거ë¨', + '%s removed a file from the task %s' => '%sì´(ê°€) 작업 %sì—서 파ì¼ì„ 제거했습니다.', + 'Move the task to another swimlane when assigned to a user' => '사용ìžì—게 í• ë‹¹ë  ë•Œ ìž‘ì—…ì„ ë‹¤ë¥¸ 스윔레ì¸ìœ¼ë¡œ ì´ë™', + 'Destination swimlane' => 'ëŒ€ìƒ ìŠ¤ìœ”ë ˆì¸', + 'Assign a category when the task is moved to a specific swimlane' => 'ìž‘ì—…ì´ íŠ¹ì • 스윔레ì¸ìœ¼ë¡œ ì´ë™ë  때 카테고리 할당', + 'Move the task to another swimlane when the category is changed' => '카테고리가 ë³€ê²½ë  ë•Œ ìž‘ì—…ì„ ë‹¤ë¥¸ 스윔레ì¸ìœ¼ë¡œ ì´ë™', + 'Reorder this column by priority (ASC)' => 'ì´ ì—´ì„ ìš°ì„ ìˆœìœ„ë¡œ 재정렬 (오름차순)', + 'Reorder this column by priority (DESC)' => 'ì´ ì—´ì„ ìš°ì„ ìˆœìœ„ë¡œ 재정렬 (내림차순)', + 'Reorder this column by assignee and priority (ASC)' => 'ì´ ì—´ì„ ë‹´ë‹¹ìž ë° ìš°ì„ ìˆœìœ„ë¡œ 재정렬 (오름차순)', + 'Reorder this column by assignee and priority (DESC)' => 'ì´ ì—´ì„ ë‹´ë‹¹ìž ë° ìš°ì„ ìˆœìœ„ë¡œ 재정렬 (내림차순)', + 'Reorder this column by assignee (A-Z)' => 'ì´ ì—´ì„ ë‹´ë‹¹ìž (A-Z)로 재정렬', + 'Reorder this column by assignee (Z-A)' => 'ì´ ì—´ì„ ë‹´ë‹¹ìž (Z-A)로 재정렬', + 'Reorder this column by due date (ASC)' => 'ì´ ì—´ì„ ë§ˆê°ì¼ë¡œ 재정렬 (오름차순)', + 'Reorder this column by due date (DESC)' => 'ì´ ì—´ì„ ë§ˆê°ì¼ë¡œ 재정렬 (내림차순)', + 'Reorder this column by id (ASC)' => 'ì´ ì—´ì„ ID로 재정렬 (오름차순)', + 'Reorder this column by id (DESC)' => 'ì´ ì—´ì„ ID로 재정렬 (내림차순)', + '%s moved the task #%d "%s" to the project "%s"' => '%sì´(ê°€) 작업 #%d "%s"ì„(를) 프로ì íЏ "%s"(으)로 ì´ë™í–ˆìŠµë‹ˆë‹¤.', + 'Task #%d "%s" has been moved to the project "%s"' => '작업 #%d "%s"ì´(ê°€) 프로ì íЏ "%s"(으)로 ì´ë™ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Move the task to another column when the due date is less than a certain number of days' => '마ê°ì¼ì´ 특정 ì¼ìˆ˜ ë¯¸ë§Œì¼ ë•Œ ìž‘ì—…ì„ ë‹¤ë¥¸ 열로 ì´ë™', + 'Automatically update the start date when the task is moved away from a specific column' => 'ìž‘ì—…ì´ íŠ¹ì • ì—´ì—서 ì´ë™ë  때 시작 ë‚ ì§œ ìžë™ ì—…ë°ì´íЏ', + 'HTTP Client:' => 'HTTP í´ë¼ì´ì–¸íЏ:', + 'Assigned' => '할당ë¨', + 'Task limits apply to each swimlane individually' => '작업 ì œí•œì€ ê° ìŠ¤ìœ”ë ˆì¸ì— 개별ì ìœ¼ë¡œ ì ìš©ë©ë‹ˆë‹¤.', + 'Column task limits apply to each swimlane individually' => 'ì—´ 작업 ì œí•œì€ ê° ìŠ¤ìœ”ë ˆì¸ì— 개별ì ìœ¼ë¡œ ì ìš©ë©ë‹ˆë‹¤.', + 'Column task limits are applied to each swimlane individually' => 'ì—´ 작업 ì œí•œì€ ê° ìŠ¤ìœ”ë ˆì¸ì— 개별ì ìœ¼ë¡œ ì ìš©ë©ë‹ˆë‹¤.', + 'Column task limits are applied across swimlanes' => 'ì—´ 작업 ì œí•œì€ ìŠ¤ìœ”ë ˆì¸ ì „ë°˜ì— ì ìš©ë©ë‹ˆë‹¤.', + 'Task limit: ' => '작업 제한:', + 'Change to global tag' => '글로벌 태그로 변경', + 'Do you really want to make the tag "%s" global?' => 'ì •ë§ë¡œ 태그 "%s"를 글로벌로 만드시겠습니까?', + 'Enable global tags for this project' => 'ì´ í”„ë¡œì íŠ¸ì— ëŒ€í•´ 글로벌 태그 활성화', + 'Group membership(s):' => '그룹 구성ì›:', + '%s is a member of the following group(s): %s' => '%sì€(는) ë‹¤ìŒ ê·¸ë£¹ì˜ êµ¬ì„±ì›ìž…니다: %s', + '%d/%d group(s) shown' => '그룹 %d/%d 표시ë¨', + 'Subtask creation or modification' => '하위 작업 ìƒì„± ë˜ëŠ” 수정', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'ìž‘ì—…ì´ íŠ¹ì • 스윔레ì¸ìœ¼ë¡œ ì´ë™ë  때 특정 사용ìžì—게 작업 할당', + 'Comment' => '댓글', + 'Collapse vertically' => '세로로 접기', + 'Expand vertically' => '세로로 확장', + 'MXN - Mexican Peso' => 'MXN - 멕시코 페소', + 'Estimated vs actual time per column' => '열당 ì˜ˆìƒ ì‹œê°„ 대 실제 시간', + 'HUF - Hungarian Forint' => 'HUF - í—가리 í¬ë¦°íЏ', + 'XBT - Bitcoin' => 'XBT - 비트코ì¸', + 'You must select a file to upload as your avatar!' => '아바타로 업로드할 파ì¼ì„ ì„ íƒí•´ì•¼ 합니다!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => '업로드한 파ì¼ì€ 유효한 ì´ë¯¸ì§€ê°€ 아닙니다! (*.gif, *.jpg, *.jpeg ë° *.pngë§Œ 허용ë©ë‹ˆë‹¤!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'ìž‘ì—…ì´ íŠ¹ì • ì—´ì—서 ì´ë™ë  때 마ê°ì¼ ìžë™ 설정', + 'No other projects found.' => '다른 프로ì íŠ¸ë¥¼ ì°¾ì„ ìˆ˜ 없습니다.', + 'Tasks copied successfully.' => 'ìž‘ì—…ì´ ì„±ê³µì ìœ¼ë¡œ 복사ë˜ì—ˆìŠµë‹ˆë‹¤.', + 'Unable to copy tasks.' => 'ìž‘ì—…ì„ ë³µì‚¬í•  수 없습니다.', + 'Theme' => '테마', + 'Theme:' => '테마:', + 'Light theme' => 'ë°ì€ 테마', + 'Dark theme' => 'ì–´ë‘ìš´ 테마', + 'Automatic theme - Sync with system' => 'ìžë™ 테마 - 시스템과 ë™ê¸°í™”', + 'Application managers or more' => '애플리케ì´ì…˜ ê´€ë¦¬ìž ì´ìƒ', + 'Administrators' => '관리ìž', + 'Visibility:' => '가시성:', + 'Standard users' => '표준 사용ìž', + 'Visibility is required' => 'ê°€ì‹œì„±ì€ í•„ìˆ˜ìž…ë‹ˆë‹¤.', + 'The visibility should be an app role' => 'ê°€ì‹œì„±ì€ ì•± ì—­í• ì´ì–´ì•¼ 합니다.', + 'Reply' => '답글', + '%s wrote: ' => '%s 작성: ', + 'Number of visible tasks in this column and swimlane' => 'ì´ ì—´ê³¼ 스윔레ì¸ì— 표시ë˜ëŠ” 작업 수', + 'Number of tasks in this swimlane' => 'ì´ ìŠ¤ìœ”ë ˆì¸ì˜ 작업 수', + 'Unable to find another subtask in progress, you can close this window.' => '다른 ì§„í–‰ ì¤‘ì¸ í•˜ìœ„ ìž‘ì—…ì„ ì°¾ì„ ìˆ˜ 없습니다. ì´ ì°½ì„ ë‹«ì„ ìˆ˜ 있습니다.', + 'This theme is invalid' => 'ì´ í…Œë§ˆëŠ” 유효하지 않습니다.', + 'This role is invalid' => 'ì´ ì—­í• ì€ ìœ íš¨í•˜ì§€ 않습니다.', + 'This timezone is invalid' => 'ì´ ì‹œê°„ëŒ€ëŠ” 유효하지 않습니다.', + 'This language is invalid' => 'ì´ ì–¸ì–´ëŠ” 유효하지 않습니다.', + 'This URL is invalid' => 'ì´ URLì€ ìœ íš¨í•˜ì§€ 않습니다.', + 'Date format invalid' => 'ìž˜ëª»ëœ ë‚ ì§œ 형ì‹', + 'Time format invalid' => 'ìž˜ëª»ëœ ì‹œê°„ 형ì‹', + 'Invalid Mail transport' => 'ìž˜ëª»ëœ ë©”ì¼ ì „ì†¡', + 'Color invalid' => 'ìž˜ëª»ëœ ìƒ‰ìƒ', + 'This value must be greater or equal to %d' => 'ì´ ê°’ì€ %d보다 í¬ê±°ë‚˜ 같아야 합니다.', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'íŒŒì¼ ì‹œìž‘ ë¶€ë¶„ì— BOM 추가 (Microsoft Excelì— í•„ìš”)', + 'Just add these tag(s)' => 'ì´ íƒœê·¸(들)ë§Œ 추가', + 'Remove internal link(s)' => 'ë‚´ë¶€ ë§í¬ 제거', + 'Import tasks from another project' => '다른 프로ì íЏì—서 작업 가져오기', + 'Select the project to copy tasks from' => 'ìž‘ì—…ì„ ë³µì‚¬í•  프로ì íЏ ì„ íƒ', + 'The total maximum allowed attachments size is %sB.' => '허용ë˜ëŠ” ì´ ì²¨ë¶€ íŒŒì¼ ìµœëŒ€ í¬ê¸°ëŠ” %sB입니다.', + 'Add attachments' => '첨부 íŒŒì¼ ì¶”ê°€', + 'Task #%d "%s" is overdue' => 'í• ì¼ #%d "%s"ì˜ ê¸°í•œì´ ì§€ë‚¬ìŠµë‹ˆë‹¤', + 'Enable notifications by default for all new users' => '모든 ì‹ ê·œ 사용ìžì— 대해 기본ì ìœ¼ë¡œ 알림 활성화', + 'Assign the task to its creator for specific columns if no assignee is set manually' => '담당ìžê°€ 수ë™ìœ¼ë¡œ 지정ë˜ì§€ ì•Šì€ ê²½ìš°, 지정한 컬럼ì—서는 ìž‘ì—…ì„ ìƒì„±ìžì—게 할당', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => '사용ìžê°€ 할당ë˜ì§€ ì•Šì€ ê²½ìš°, 컬럼 변경으로 ì§€ì •ëœ ì»¬ëŸ¼ìœ¼ë¡œ ì´ë™í•  때 ìž‘ì—…ì„ ë¡œê·¸ì¸í•œ 사용ìžì—게 할당', +]; diff --git a/app/Locale/mk_MK/translations.php b/app/Locale/mk_MK/translations.php new file mode 100644 index 0000000..72e2b51 --- /dev/null +++ b/app/Locale/mk_MK/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Ðикој', + 'Edit' => 'Уредување', + 'Remove' => 'ОтÑтрани', + 'Yes' => 'Да', + 'No' => 'Ðе', + 'cancel' => 'Откажи', + 'or' => 'или', + 'Yellow' => 'Жолто', + 'Blue' => 'Сина', + 'Green' => 'Зелена', + 'Purple' => 'Виолетова', + 'Red' => 'Црвено', + 'Orange' => 'Портокалова', + 'Grey' => 'Сиво', + 'Brown' => 'Кафеав', + 'Deep Orange' => 'Темно портокалова', + 'Dark Grey' => 'Темно Ñива', + 'Pink' => 'Розова', + 'Teal' => 'Тиркизна боја', + 'Cyan' => 'Цијан', + 'Lime' => 'Вар', + 'Light Green' => 'Светло зелено', + 'Amber' => 'Килибарна', + 'Save' => 'Зачувај', + 'Login' => 'Чекирање', + 'Official website:' => 'Официјална Ñтраница:', + 'Unassigned' => 'Ðе е доделен', + 'View this task' => 'Прегледајте ја задачата', + 'Remove user' => 'ОтÑтранете го кориÑникот', + 'Do you really want to remove this user: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да го отÑтраните кориÑникот: "%s"?', + 'All users' => 'Сите кориÑници', + 'Username' => 'КориÑник', + 'Password' => 'Лозинка', + 'Administrator' => 'ÐдминиÑтратор', + 'Sign in' => 'Чекирање', + 'Users' => 'КориÑници', + 'Forbidden' => 'Забрането', + 'Access Forbidden' => 'Одбиен приÑтап', + 'Edit user' => 'Уредете го кориÑникот', + 'Logout' => 'Одјави Ñе', + 'Bad username or password' => 'Лошо кориÑничко име или лозинка', + 'Edit project' => 'Уредете го проектот', + 'Name' => 'Име', + 'Projects' => 'Проекти', + 'No project' => 'Ðема проект', + 'Project' => 'Проект', + 'Status' => 'СтатуÑ', + 'Tasks' => 'Доделување', + 'Board' => 'Табла', + 'Actions' => 'ДејÑтва', + 'Inactive' => 'Ðеактивен', + 'Active' => 'Ðктивни', + 'Unable to update this board.' => 'Ова ажурирање на таблата не уÑпеа', + 'Disable' => 'Оневозможи', + 'Enable' => 'Овозможи', + 'New project' => 'Ðов проект', + 'Do you really want to remove this project: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да го отÑтраните проектот: "%s"?', + 'Remove project' => 'ОтÑтрани проект', + 'Edit the board for "%s"' => 'Уредете ја таблата за "%s"', + 'Add a new column' => 'Додадете нова колона', + 'Title' => 'ÐаÑлов', + 'Assigned to %s' => 'Доделено на %s', + 'Remove a column' => 'ОтÑтранете ја колоната', + 'Unable to remove this column.' => 'Ðе може да Ñе отÑтрани оваа колона.', + 'Do you really want to remove this column: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да ја отÑтраните оваа колона: "%s"?', + 'Settings' => 'ПоÑтавки', + 'Application settings' => 'ПоÑтавки за апликација', + 'Language' => 'Јазик', + 'Webhook token:' => 'Webhook токен:', + 'API token:' => 'Token API', + 'Database size:' => 'Големина на оÑновата:', + 'Download the database' => 'Преземете ја базата на податоци', + 'Optimize the database' => 'Оптимизирајте ја базата на податоци', + '(VACUUM command)' => '(Команда Ð’ÐКУУМ)', + '(Gzip compressed Sqlite file)' => '(Sqlite база на податоци Ñпакувана Ñо Gzip)', + 'Close a task' => 'Затворете ја задачата', + 'Column' => 'Колона', + 'Color' => 'Бојата', + 'Assignee' => 'Извршител', + 'Create another task' => 'Додадете задача', + 'New task' => 'Ðова задача', + 'Open a task' => 'Отворете ја задачата', + 'Do you really want to open this task: "%s"?' => 'Дали навиÑтина Ñакате да ја отворите оваа задача: "%s"?', + 'Back to the board' => 'Ðазад на таблата', + 'There is nobody assigned' => 'Ðе е доделен на никого', + 'Column on the board:' => 'Колона на табла:', + 'Close this 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 карактери', + 'The password is required' => 'Потребна е лозинка', + 'This value must be an integer' => 'Мора да биде цел број', + 'The username must be unique' => 'КориÑничкото име мора да биде единÑтвено', + 'The user id is required' => 'Потребна е кориÑничка лична карта', + 'Passwords don\'t match' => 'Лозинките не Ñе Ñовпаѓаат', + 'The confirmation is required' => 'Потребна е потврда', + 'The project is required' => 'Проектот е задолжителен', + 'The id is required' => 'Потребна е лична карта', + 'The project id is required' => 'Потребен е проект за проект', + 'The project name is required' => 'Задолжително е името на проектот', + 'The title is required' => 'Потребен е наÑлов', + 'Settings saved successfully.' => 'ПоÑтавките беа уÑпешно зачувани.', + 'Unable to save your settings.' => 'Ðе може да Ñе зачуваат поÑтавките.', + 'Database optimization done.' => 'Оптимизацијата на базата на податоци е завршена.', + 'Your project has been created successfully.' => 'Проектот беше уÑпешно завршен.', + 'Unable to create your project.' => 'Ðе може да Ñе Ñоздаде проект.', + 'Project updated successfully.' => 'Проектот е уÑпешно ажуриран.', + 'Unable to update this project.' => 'Ðе може да Ñе ажурира овој проект.', + 'Unable to remove this project.' => 'Ðе може да Ñе отÑтрани овој проект.', + 'Project removed successfully.' => 'Проектот е уÑпешно отÑтранет.', + 'Project activated successfully.' => 'Проектот е уÑпешно активиран.', + 'Unable to activate this project.' => 'Ðе може да Ñе активира овој проект.', + 'Project disabled successfully.' => 'Проектот е уÑпешно деактивиран.', + 'Unable to disable this project.' => 'Ðе може да Ñе оневозможи овој проект.', + 'Unable to open this task.' => 'Ðе може да Ñе отвори оваа задача', + 'Task opened successfully.' => 'Задачата уÑпешно Ñе отвори.', + 'Unable to close this task.' => 'Ðе може да Ñе затвори оваа задача.', + 'Task closed successfully.' => 'Задачата е уÑпешно затворена.', + 'Unable to update your task.' => 'Ðе може да Ñе ажурира задачата.', + 'Task updated successfully.' => 'Задачата е уÑпешно ажурирана.', + 'Unable to create your task.' => 'Ðе може да Ñе Ñоздаде задача.', + 'Task created successfully.' => 'Задачата е уÑпешно креирана.', + 'User created successfully.' => 'КориÑникот е уÑпешно Ñоздаден', + 'Unable to create your user.' => 'Ðе уÑпеа да Ñе Ñоздаде кориÑник.', + 'User updated successfully.' => 'КориÑникот е уÑпешно ажуриран.', + 'User removed successfully.' => 'КориÑникот е уÑпешно отÑтранет.', + 'Unable to remove this user.' => 'Ðе може да Ñе отÑтрани кориÑникот.', + 'Board updated successfully.' => 'Таблата уÑпешно Ñе ажурираше.', + 'Ready' => 'Спремен', + 'Backlog' => 'Дневник', + 'Work in progress' => 'Во тек', + 'Done' => 'Ðаправено', + 'Application version:' => 'Верзија на апликација:', + 'Id' => 'ИД', + 'Public link' => 'Јавна врÑка', + 'Timezone' => 'ВременÑка зона', + 'Sorry, I didn\'t find this information in my database!' => 'Извинете, не Ñе пронајдени информации во базата на податоци', + 'Page not found' => 'Страната не е пронајдена', + 'Complexity' => 'СложеноÑÑ‚', + 'Task limit' => 'Ограничување на задачите', + 'Task count' => 'Број на задачи', + 'User' => 'КориÑник', + 'Comments' => 'Коментари', + 'Comment is required' => 'Потребен е коментар', + 'Comment added successfully.' => 'Коментарот е уÑпешно оÑтавен', + 'Unable to create your comment.' => 'Ðе може да Ñе Ñоздадат коментари', + 'Due Date' => 'Рок за завршување', + 'Invalid date' => 'Лош датум', + 'Automatic actions' => 'ÐвтоматÑки дејÑтва', + 'Your automatic action has been created successfully.' => 'УÑпешно Ñоздадено автоматÑко дејÑтво', + 'Unable to create your automatic action.' => 'Ðе може да Ñе Ñоздаде автоматÑко дејÑтво', + 'Remove an action' => 'Ðкција за бришење', + 'Unable to remove this action.' => 'Ðе може да Ñе избрише дејÑтвото', + 'Action removed successfully.' => 'ДејÑтвото е избришано', + 'Automatic actions for the project "%s"' => 'Ðкции за автоматизација на проектот "%s"', + 'Add an action' => 'додадете акција', + 'Event name' => 'Име на наÑтанот', + 'Action' => 'Ðкција', + 'Event' => 'ÐаÑтан', + 'When the selected event occurs execute the corresponding action.' => 'Кога ќе Ñе Ñлучи наÑтан, преземете Ñоодветно дејÑтво.', + 'Next step' => 'Следен чекор', + '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 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' => 'Доделете боја на кориÑникот', + 'Position' => 'Позиција', + 'Duplicate to project' => 'Копирајте во друг проект', + 'Duplicate' => 'Ðаправете копија', + 'Link' => 'Ð’Ñ€Ñката', + 'Comment updated successfully.' => 'Коментарот е уÑпешно ажуриран.', + 'Unable to update your comment.' => 'Ðжурирањето на коментарите не уÑпеа.', + 'Remove a comment' => 'Избриши коментар', + 'Comment removed successfully.' => 'Коментарот е уÑпешно избришан.', + 'Unable to remove this comment.' => 'Ðе уÑпеа да ги избрише коментарите.', + 'Do you really want to remove this comment?' => 'Треба ли да го избришам овој коментар?', + 'Current password for the user "%s"' => 'Тековна лозинка за кориÑникот "%s"', + 'The current password is required' => 'Тековната лозинка е потребна', + 'Wrong password' => 'Погрешна лозинка', + 'Unknown' => 'Ðепознато', + 'Last logins' => 'ПоÑледна најава', + 'Login date' => 'Датум на аплицирање', + 'Authentication method' => 'Метод за проверка', + 'IP address' => 'IP адреÑа', + 'User agent' => 'ПрелиÑтувач', + 'Persistent connections' => 'ПоÑтојана врÑка', + 'No session.' => 'Ðема ÑеÑија', + 'Expiration date' => 'Определување', + 'Remember Me' => 'Запомни ме', + 'Creation date' => 'Датум на производÑтво', + 'Everybody' => 'Сите', + 'Open' => 'Отворено', + 'Closed' => 'Затворено', + 'Search' => 'Пребарување', + 'Nothing found.' => 'Ðишто не е пронајдено', + 'Due date' => 'Рок за завршување', + 'Description' => 'ОпиÑ', + '%d comments' => '%d коментари', + '%d comment' => '%d коментар', + '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.' => 'Вашиот надворешен профил е уÑпешно поврзан Ñо вашиот профил.', + 'Email' => 'Е-пошта', + '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' => 'ÐвтоматÑки доделува категорија по боја', + 'Task creation or modification' => 'Создадете или изменете задача', + 'Category' => 'Категорија', + 'Category:' => 'Категорија:', + 'Categories' => 'Категории', + 'Your category has been created successfully.' => 'УÑпешно креирана категорија.', + 'This category has been updated successfully.' => 'Категоријата е уÑпешно променета', + 'Unable to update this 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' => 'Ðема категорија', + 'The name is required' => 'Потребно е име', + 'Remove a file' => 'ОтÑтранете ја датотеката', + 'Unable to remove this file.' => 'Датотеката не може да Ñе отÑтрани.', + 'File removed successfully.' => 'Датотеката е уÑпешно отÑтранета.', + 'Attach a document' => 'Прикачете го документот', + 'Do you really want to remove this file: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да ја отÑтраните датотеката: "%s"?', + 'Attachments' => 'Прилози', + 'Edit the task' => 'Уредување задача', + 'Add a comment' => 'Додај коментар', + 'Edit a comment' => 'Уредете го коментарот', + 'Summary' => 'Преглед', + 'Time tracking' => 'Следење на времето', + 'Estimate:' => 'Проценка:', + 'Spent:' => 'Поминато:', + 'Do you really want to remove this sub-task?' => 'Дали навиÑтина Ñакате да ја отÑтраните подзадачата?', + 'Remaining:' => 'ОÑтатокот:', + 'hours' => 'чаÑови', + 'estimated' => 'проценето', + 'Sub-Tasks' => 'Подзадачи', + 'Add a sub-task' => 'Додадете подзадача', + 'Original estimate' => 'Оригинална проценка', + 'Create another sub-task' => 'Додадете нова подзадача', + 'Time spent' => 'Потрошено време', + 'Edit a sub-task' => 'Уредете подзадача', + 'Remove a sub-task' => 'ОтÑтранете ја подзадачата', + 'The time must be a numeric value' => 'Времето мора да биде нумеричка вредноÑÑ‚', + 'Todo' => 'Да направиш', + 'In progress' => 'Во тек', + 'Sub-task removed successfully.' => 'Потзадачата е уÑпешно отÑтранета.', + 'Unable to remove this sub-task.' => 'Ðе може да Ñе отÑтрани оваа подзадача', + 'Sub-task updated successfully.' => 'Подзадача уÑпешно Ñе ажурираше', + 'Unable to update your sub-task.' => 'Ðе уÑпеа да Ñе ажурира подзадачата.', + 'Unable to create your sub-task.' => 'Ðе може да Ñе Ñоздаде подзадача.', + 'Maximum size: ' => 'МакÑимална големина:', + 'Display another project' => 'Покажете друг проект', + 'Created by %s' => 'Ðаправено од %s', + 'Tasks Export' => 'Извозни задачи', + 'Start Date' => 'Почетен датум', + 'Execute' => 'Изврши', + 'Task Id' => 'Идентификатор на задача', + 'Creator' => 'Ðвторот', + 'Modification date' => 'Датум на измена', + 'Completion date' => 'Датум на завршување', + 'Clone' => 'Клон', + 'Project cloned successfully.' => 'Проектот е уÑпешно клониран.', + 'Unable to clone this project.' => 'Ðе може да Ñе клонира проект.', + 'Enable email notifications' => 'Овозможете извеÑтувања по е-пошта', + 'Task position:' => 'Позиција на задачата:', + 'The task #%d has been opened.' => 'Задачата #%d е отворена.', + 'The task #%d has been closed.' => 'Задачата #%d е затворена.', + 'Sub-task updated' => 'Подзадача Ñе Ñмени', + 'Title:' => 'ÐаÑлов:', + 'Status:' => 'СтатуÑ', + 'Assignee:' => 'Извршител:', + 'Time tracking:' => 'Следење на време:', + 'New sub-task' => 'Ðова подзадача', + 'New attachment added "%s"' => 'Вметнат е нов прилог "%s"', + 'New comment posted by %s' => 'Ðов коментар оÑтавен од %s', + 'New comment' => 'Ðов коментар', + 'Comment updated' => 'Коментарот е ажуриран', + 'New subtask' => 'Ðова подзадача', + 'I only want to receive notifications for these projects:' => 'Сакам извеÑтувања Ñамо за проекти:', + 'view the task on Kanboard' => 'преглед на задачи во Канборд', + 'Public access' => 'ПриÑтап до јавноÑта', + 'Disable public access' => 'Забрани јавен приÑтап', + 'Enable public access' => 'Дозволете приÑтап до јавноÑта', + 'Public access disabled' => 'Оневозможен приÑтап до јавноÑта', + 'Move the task to another project' => 'ПомеÑтете ја задачата во друг проект', + 'Move to project' => 'Префрлете Ñе на друг проект', + 'Do you really want to duplicate this task?' => 'Дали навиÑтина Ñакате да ја копирате оваа задача?', + 'Duplicate a task' => 'Копирајте ја задачата', + 'External accounts' => 'Ðадворешни профил', + 'Account type' => 'Тип на профилот', + 'Local' => 'Локално', + 'Remote' => 'Далеку', + 'Enabled' => 'Овозможено', + 'Disabled' => 'Оневозможено', + 'Login:' => 'Пријавување:', + 'Full Name:' => 'Име и презиме', + 'Email:' => 'Е-пошта:', + 'Notifications:' => 'ИзвеÑтувања:', + 'Notifications' => 'ИзвеÑтувања', + 'Account type:' => 'Тип на профилот', + 'Edit profile' => 'Уреди го профилот', + 'Change password' => 'Промени го паÑвордот', + 'Password modification' => 'Промени го паÑвордот', + 'External authentications' => 'Ðадворешни идентификации', + 'Never connected.' => 'Ðикогаш не Ñе поврзани', + '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/%sч', + '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-фид', + '%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)' => 'Стандардни колони за нов проект (одделена Ñо запирка)', + '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' => 'Изберете наÑтан', + 'Create a task from an external provider' => 'Ðаправете ја задачата преку поÑредник', + 'Change the assignee based on an external username' => 'Променете го извршителот врз оÑнова на надворешно кориÑничко име', + 'Change the category based on an external label' => 'Променете ја категоријата врз оÑнова на надворешната ознака', + 'Reference' => 'Референца', + 'Label' => 'Етикета', + 'Database' => 'База', + 'About' => 'Информации', + 'Database driver:' => 'Возач на база на податоци:', + 'Board settings' => 'Прилагодување на панелот', + 'Webhook settings' => 'ПоÑтавки за Webhook', + 'Reset token' => 'РеÑетирајте го токенот', + 'API endpoint:' => 'Крајна точка на API:', + 'Refresh interval for personal 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)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Фреквенција во Ñекунди (0 ја иÑклучува оваа функционалноÑÑ‚, 10 е Ñтандардно)', + 'Application URL' => 'URL на апликација', + 'Token regenerated.' => 'Токенот е регенериран', + 'Date format' => 'Формат на датум', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ИСО-форматот е Ñекогаш прифатлив, пример: "%s", "%s"', + 'New personal project' => 'Ðов личен проект', + 'This project is personal' => 'Овој проект е личен', + 'Add' => 'Додади', + 'Start date' => 'Почетен датум', + 'Time estimated' => 'Проценето време', + 'There is nothing assigned to you.' => 'Ðишто не ти е доделено', + 'My tasks' => 'Моите задачи', + 'Activity stream' => 'СпиÑок на активноÑти', + 'Dashboard' => 'Контролна табла', + 'Confirmation' => 'Потврда', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Ðаправете коментар преку надворешен поÑредник', + 'Project management' => 'Уредување на проектот', + 'Columns' => 'Колумни', + 'Task' => 'Доделување', + 'Percentage' => 'Процент', + 'Number of tasks' => 'Број на задачи', + 'Task distribution' => 'Поделба на задачи', + 'Analytics' => 'Ðнализа', + 'Subtask' => 'Подзадача', + 'User repartition' => 'КориÑнички одговорноÑти', + 'Clone this project' => 'Клонирајте го проектот', + 'Column removed successfully.' => 'Колоната е уÑпешно отÑтранета.', + 'Not enough data to show the graph.' => 'Ðедоволни податоци за графиконот.', + 'Previous' => 'Претходна', + 'The id must be an integer' => 'ID мора да биде цел број', + '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 task id is required' => 'Потребен е ИД на задача', + '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' => 'Дијаграм на кумулативен проток', + 'Daily project summary' => 'Дневно резиме на проектот', + 'Daily project summary export' => 'Дневно резиме на проект - извоз', + 'Exports' => 'Извозот', + 'This export contains the number of tasks per column grouped per day.' => 'Овој извоз Ñодржи број на задачи по колона групирани по денови.', + 'Active swimlanes' => 'Ðктивна патека', + 'Add a new swimlane' => 'Додадете нова патека', + 'Default swimlane' => 'Стандардна патека', + 'Do you really want to remove this swimlane: "%s"?' => 'Дали навиÑтина Ñакате да ја отÑтраните оваа патека: "%s"?', + 'Inactive swimlanes' => 'Ðеактивни патеки', + 'Remove a swimlane' => 'ОтÑтрани патека', + 'Swimlane modification for the project "%s"' => 'Измена на патеката за проектот "%s"', + 'Swimlane removed successfully.' => 'Патеката е уÑпешно отÑтранета.', + 'Swimlanes' => 'Патеки', + 'Swimlane updated successfully.' => 'Патеката е уÑпешно ажурирана.', + 'Unable to remove this swimlane.' => 'Ðе може да Ñе отÑтрани оваа патеката', + 'Unable to update this swimlane.' => 'Ðе може да Ñе ажурира оваа патеката', + 'Your swimlane has been created successfully.' => 'Вашата патека е уÑпешно креирана.', + 'Example: "Bug, Feature Request, Improvement"' => 'Ðа пример: „Грешка, барање за функционалноÑÑ‚, подобрување“', + 'Default categories for new projects (Comma-separated)' => 'Стандардни категории за нови проекти', + 'Integrations' => 'Интеграција', + 'Integration with third-party services' => 'Интеграција Ñо надворешни уÑлуги', + 'Subtask Id' => 'ИД на подзадача', + 'Subtasks' => 'Подзадачи', + 'Subtasks Export' => 'Извоз на подподатоци', + 'Task Title' => 'ÐаÑлов на задача', + 'Untitled' => 'Без наÑлов', + 'Application default' => 'Стандардна апликација', + 'Language:' => 'Јазик:', + 'Timezone:' => 'ВременÑка зона:', + 'All columns' => 'Сите колони', + 'Next' => 'Следното', + '#%d' => '#%d', + 'All swimlanes' => 'Сите патеки', + 'All colors' => 'Сите бои', + 'Moved to column %s' => 'ПремеÑтено во колоната %s', + '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' => 'Следење на времето', + 'You already have one subtask in progress' => 'Веќе имате една подзадача „во тек“', + 'Which parts of the project do you want to duplicate?' => 'Кои делови од проектот Ñакате да ги дуплирате', + 'Disallow login form' => 'Забранете го формуларот за апликација', + 'Start' => 'Почеток', + 'End' => 'Крај', + 'Task age in days' => 'ВозраÑÑ‚ на задачи во денови', + 'Days in this column' => 'Денови во оваа колона', + '%dd' => '%dd', + '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' => 'Полето е задолжително', + 'Link added successfully.' => 'Ð’Ñ€Ñката е уÑпешно додадена.', + 'Link updated successfully.' => 'Ð’Ñ€Ñката е уÑпешно ажурирана.', + 'Link removed successfully.' => 'Ð’Ñ€Ñката беше уÑпешно отÑтранета.', + 'Link labels' => 'Поврзување Ñо етикета', + 'Link modification' => 'Модификација на врÑката', + 'Opposite label' => 'ÐаÑпроти етикетата', + 'Remove a link' => 'ОтÑтранете ја врÑката', + '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.' => 'Ðе може да Ñе отÑтрани врÑката.', + 'relates to' => 'релација Ñо', + 'blocks' => 'блокира', + 'is blocked by' => 'е блокиран од', + 'duplicates' => 'двојки', + 'is duplicated by' => 'е удвоена од', + 'is a child of' => 'е дете на', + 'is a parent of' => 'е родител на', + 'targets milestone' => 'целта на преÑвртната точка', + 'is a milestone of' => 'е од преÑвртна точка', + 'fixes' => 'поправа', + 'is fixed by' => 'е поправен оттогаш', + 'This task' => 'Оваа задача', + '<1h' => '<1ч', + '%dh' => '%dч', + 'Expand tasks' => 'Проширете ги задачите', + 'Collapse tasks' => 'Собери ги задачите', + 'Expand/collapse tasks' => 'Задачи проширување/Ñоберивање', + 'Close dialog box' => 'Затвори дијалог-кутија', + 'Submit a form' => 'ПоднеÑете формулар', + 'Board view' => 'Преглед на табла', + 'Keyboard shortcuts' => 'Кратенки за таÑтатура', + 'Open board switcher' => 'Отворете ги прекинувачите на панелот', + 'Application' => 'Ðпликација', + 'Compact view' => 'Компактен преглед', + 'Horizontal scrolling' => 'Хоризонтално лизгање', + 'Compact/wide view' => 'Собери / прошири го прегледот', + 'Currency' => 'Валута', + 'Personal project' => 'Личен проект', + 'AUD - Australian Dollar' => 'ÐUD- авÑтралиÑки долар', + 'CAD - Canadian Dollar' => 'CAD - канадÑки долар', + 'CHF - Swiss Francs' => 'CHF - швајцарÑки франк', + 'Custom Stylesheet' => 'Прилагоден Ñтил', + 'EUR - Euro' => 'ЕUR - Евра', + 'GBP - British Pound' => 'GBP - британÑка фунта', + 'INR - Indian Rupee' => 'INR - индиÑка дупка', + 'JPY - Japanese Yen' => 'JPY - јапонÑки јен', + 'NZD - New Zealand Dollar' => 'NZD - Долар од Ðов Зеланд', + 'PEN - Peruvian Sol' => 'ПЕР- ПеруанÑки Сол', + 'RSD - Serbian dinar' => 'RSD - ÑрпÑки динар', + 'CNY - Chinese Yuan' => 'CNY - кинеÑки јен', + 'USD - US Dollar' => 'USD - американÑки долар', + 'VES - Venezuelan Bolívar' => 'VES - венецуелÑки боливар', + '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' => 'Време поминато во колоната', + '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.' => 'Овој извештај ги Ñодржи Ñите потези во колоните за Ñекоја задача Ñо датумот, кориÑникот и времето поминато за Ñекој потег.', + 'Currency rates' => 'Девизна Ñтапка', + 'Rate' => 'Оцени', + 'Change reference currency' => 'Променете ја референтната валута', + 'Reference currency' => 'Референтна валута', + 'The currency rate has been added successfully.' => 'Девизната Ñтапка е уÑпешно додадена.', + 'Unable to add this currency rate.' => 'Ðе може да Ñе додаде оваа Ñтапка на валута.', + 'Webhook URL' => 'Webhook URL-адреÑа', + '%s removed the assignee of the task %s' => '%s го отÑтрани извршителот на задачите %s', + 'Information' => 'Информации', + 'Check two factor authentication code' => 'Проверете го двофакторниот код за автентикација', + 'The two factor authentication code is not valid.' => 'Ðвтентикацијата Ñо два фактори не е валидна.', + 'The two factor authentication code is valid.' => 'Двофакторниот код за автентикација е валиден', + 'Code' => 'Код', + 'Two factor authentication' => 'Два фактори за проверка', + 'This QR code contains the key URI: ' => 'Овој QR-код го Ñодржи клучот URL:', + 'Check my code' => 'Проверете го мојот код', + 'Secret key: ' => 'Таен клуч:', + 'Test your device' => 'ТеÑтирајте го вашиот уред', + 'Assign a color when the task is moved to a specific column' => 'Доделете боја кога задачата Ñе премеÑтува во избраната колона', + '%s via Kanboard' => '%s преку Канборд', + 'Burndown chart' => 'ПреоÑтаната работа / време', + 'This chart show the task complexity over the time (Work Remaining).' => 'Овој графикон ја покажува комплекÑноÑта на задачата Ñо текот на времето (преоÑтаната работа)', + '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 - шведÑка круна', + 'Identifier' => 'Идентификатор', + 'Disable two factor authentication' => 'Оневозможете автентикација Ñо два фактори', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да ја оневозможите автентикацијата Ñо два фактори за овој кориÑник: "%s"?', + 'Edit link' => 'Уредете ја врÑката', + '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' => 'Подготвена е периодична задача да Ñе креира', + 'Score' => 'Рејтинг', + 'The identifier must be unique' => 'Идентификаторот мора да биде единÑтвен', + 'This linked task id doesn\'t exists' => 'Овој ИД на поврзана задача не поÑтои', + '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' => 'ВременÑка рамка за преÑметување на новиот рок за завршување', + '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: ' => 'Фактор за преÑметување на новиот рок за завршување:', + 'Month(s)' => 'МеÑец(и)', + 'This task has been created by: ' => 'Оваа задача беше направена од:', + 'Recurrent task has been generated:' => 'Генерирана е периодична задача:', + 'Timeframe to calculate new due date: ' => 'ВременÑка рамка за преÑметување на новиот рок за завршување:', + 'Trigger to generate recurrent task: ' => 'Ðктивирање за Ñоздавање повторувачка задача', + 'When task is closed' => 'Кога задачата е затворена', + 'When task is moved from first column' => 'Кога задачата Ñе премеÑтува од првата колона', + 'When task is moved to last column' => 'Кога задачата е премеÑтена во поÑледната колона', + 'Year(s)' => 'Годинa(и)', + 'Project settings' => 'ПоÑтавки на проектот', + 'Automatically update the start date' => 'ÐвтоматÑки ажурирајте го датумот на започнување', + 'iCal feed' => 'iCal канал', + 'Preferences' => 'ПоÑтавки', + 'Security' => 'БезбедноÑÑ‚', + 'Two factor authentication disabled' => 'Ðвтентикацијата Ñо два фактори е оневозможена', + 'Two factor authentication enabled' => 'Овозможена е автентикација Ñо два фактори', + 'Unable to update this user.' => 'Ðе може да Ñе ажурира овој кориÑник', + 'There is no user management for personal 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' => 'ПомеÑтете ја задачата во втората колона кога категоријата Ñе менува', + 'Send a task by email to someone' => 'Задачата иÑпратете ја по е-пошта некому', + 'Reopen a task' => 'Повторно отворете ја задачата', + 'Notification' => 'ИзвеÑтување', + '%s moved the task #%d to the first swimlane' => '%s ја премеÑти задачата #%d на првата патека', + 'Swimlane' => 'Патека', + '%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.' => 'Овој извештај ги Ñодржи Ñите информации за задачите во даден период', + 'Project activities for %s' => 'Проектни активноÑти за %s', + 'view the board on Kanboard' => 'преглед на табла на Канборд', + 'The task has been moved to the first swimlane' => 'Задачата е премеÑтена на првата патека', + 'The task has been moved to another swimlane:' => 'Задачата е премеÑтена на друга патека:', + 'New title: %s' => 'Ðов наÑлов: %s', + 'The task is not assigned anymore' => 'Задачата веќе нема извршител', + 'New assignee: %s' => 'Ðов извршител: %s', + 'There is no category now' => 'Сега нема категорија', + 'New category: %s' => 'Ðова категорија: %s', + 'New color: %s' => 'Ðова боја: %s', + 'New complexity: %d' => 'Ðова ÑложеноÑÑ‚: %d', + 'The due date has been removed' => 'Рокот за завршување е отÑтранет', + 'There is no description anymore' => 'Ðема повеќе опиÑи', + 'Recurrence settings has been modified' => 'Променети поÑтавки за периодични задачи', + 'Time spent changed: %sh' => 'Времето поминато Ñе Ñмени: %sч', + 'Time estimated changed: %sh' => 'Проценетото време Ñе Ñмени: %sч', + 'The field "%s" has 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:' => 'Сакам да добивам извеÑтувања за:', + 'All tasks' => 'Сите задачи', + 'Only for tasks assigned to me' => 'Само за задачи за кои Ñум извршител', + 'Only for tasks created by me' => 'Само за задачи што ги креирав', + 'Only for tasks created by me and tasks assigned to me' => 'Само за задачи што ги креирав или Ñум извршител на задачата', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Вкупно за Ñите колони', + 'You need at least 2 days of data to show the chart.' => 'Потребни ви Ñе најмалку 2 дена податоци за да ја прикажете табелата.', + '<15m' => '<15м', + '<30m' => '<30м', + 'Stop timer' => 'Запрете го тајмерот', + 'Start timer' => 'Започнете го тајмерот', + 'My activity stream' => 'Текот на моите активноÑти', + 'Search tasks' => 'Задачи за пребарување', + 'Reset filters' => 'РеÑетирајте ги филтрите', + 'My tasks due tomorrow' => 'Моите задачи да бидат завршени утре', + 'Tasks due today' => 'Задачите да бидат завршени денеÑ', + 'Tasks due tomorrow' => 'Задачите ќе бидат завршени утре', + 'Tasks due yesterday' => 'Задачи што требаше да бидат завршени вчера', + 'Closed tasks' => 'Затворени задачи', + 'Open tasks' => 'Отворени задачи', + 'Not assigned' => 'Без извршител', + 'View advanced search syntax' => 'Погледнете ÑинтакÑа за напредно пребарување', + 'Overview' => 'Преглед', + 'Board/Calendar/List view' => 'Преглед на табела / календар / ÑпиÑок', + 'Switch to the board view' => 'Префрлете Ñе на погледот на таблата', + 'Switch to the list view' => 'Префрлете Ñе на прегледот на ÑпиÑокот', + 'Go to the search/filter box' => 'Одете во полето за пребарување / филтер', + 'There is no activity yet.' => 'Сè уште нема активноÑÑ‚.', + 'No tasks found.' => 'Задачите не Ñе пронајдени.', + 'Keyboard shortcut: "%s"' => 'Кратенка за таÑтатура: "%s"', + 'List' => 'СпиÑок', + 'Filter' => 'Филтер', + 'Advanced search' => 'Ðапредно пребарување', + 'Example of query: ' => 'Пример за пребарување:', + 'Search by project: ' => 'Пребарување по проект:', + 'Search by column: ' => 'Пребарување по колона:', + 'Search by assignee: ' => 'Пребарување по извршителот:', + 'Search by color: ' => 'Пребарување Ñпоред боја:', + 'Search by category: ' => 'Пребарај по категорија:', + 'Search by description: ' => 'Пребарување по опиÑ:', + 'Search by due date: ' => 'Пребарување Ñпоред рок за завршување:', + 'Average time spent in each column' => 'ПроÑечно време поминато во Ñекоја колона', + 'Average time spent' => 'ПроÑечно поминато време', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Овој графикон го покажува проÑечното време поминато во Ñекоја колона за поÑледните %d задачи.', + 'Average Lead and Cycle time' => 'ПроÑечно време на реализација и циклуÑ:', + 'Average lead time: ' => 'ПроÑечно време на реализација:', + 'Average cycle time: ' => 'ПроÑечно време на циклуÑ:', + 'Cycle Time' => 'Време на циклуÑ', + 'Lead Time' => 'Време на реализација', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Овој графикон ги прикажува проÑечното време на реализација и циклуÑите за поÑледните %d задачи Ñо текот на времето.', + 'Average time into each column' => 'ПроÑечно време во Ñекоја колона', + 'Lead and cycle time' => 'Време на реализација и циклуÑ', + 'Lead time: ' => 'Време на реализација:', + 'Cycle time: ' => 'Време на циклуÑ:', + 'Time spent in 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 the start date automatically' => 'ÐвтоматÑки поÑтавете го времето на започнување', + 'Edit Authentication' => 'Уредете ја автентикацијата', + 'Remote user' => 'Далечен кориÑник', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'ДалечинÑките кориÑници не ја чуваат Ñвојата лозинка во базата на податоци на Kanboard, примери: LDAP, Google и Github профил.', + '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.' => 'Ðема доÑтапен проект за деÑтинација.', + 'Trigger automatically subtask time tracking' => 'Ðктивирајте автоматÑки Ñледете го Ñледењето на времето.', + 'Include closed tasks in the cumulative flow diagram' => 'Вклучете ги затворените задачи во табелата за кумулативен проток', + 'Current swimlane: %s' => 'Тековна патека: %s', + 'Current column: %s' => 'Тековна колона: %s', + 'Current category: %s' => 'Тековна категорија: %s', + 'no category' => 'без категорија', + 'Current assignee: %s' => 'Тековен извршител: %s', + 'not assigned' => 'не е доделен', + 'Author:' => 'Ðвторот:', + 'contributors' => 'Ñоработници', + 'License:' => 'Лиценца:', + 'License' => 'Лиценца', + 'Enter the text below' => 'ВнеÑете го текÑтот подолу', + 'Start date:' => 'Време на започнување:', + 'Due date:' => 'Рок за завршување:', + 'People who are project managers' => 'Луѓе кои Ñе менаџери на проекти', + 'People who are project members' => 'Луѓе кои Ñе членови на проектот', + 'NOK - Norwegian Krone' => 'NOK - норвешка круна', + 'Show this column' => 'Покажете ја оваа колона', + 'Hide this column' => 'Скријте ја оваа колона', + 'End date' => 'Датум на завршување', + 'Users overview' => 'Преглед на кориÑник', + 'Members' => 'Членови', + 'Shared project' => 'Заеднички проект', + 'Project managers' => 'Менаџери на проекти', + 'Projects list' => 'СпиÑок на проекти', + 'End date:' => 'Датум на завршување:', + 'Change task color when using a specific task link' => 'Променете ја бојата на задачата кога Ñе кориÑти Ñпецифичен линк за задачата', + 'Task link creation or modification' => 'Ð’Ñ€Ñката за задачата е направена или изменета', + 'Milestone' => 'Прекретница', + 'Reset the search/filter box' => 'РеÑетирајте го полето за пребарување / филтер', + 'Documentation' => 'Документација', + 'Author' => 'Ðвторот', + 'Version' => 'Верзија', + 'Plugins' => 'Додатоци', + 'There is no plugin loaded.' => 'Ðе Ñе вчитани додатоци.', + 'My notifications' => 'Моите извеÑтувања', + 'Custom filters' => 'Прилагодени филтри', + 'Your custom filter has 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 has been updated successfully.' => 'Прилагодениот филтер е уÑпешно ажуриран.', + 'Unable to update custom filter.' => 'Ðе може да Ñе ажурира прилагодениот филтер', + 'Web' => 'Веб', + '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', + 'New task #%d: %s' => 'Ðова задача #%d: %s', + 'Task updated #%d' => 'Задачата е ажурирана #%d', + 'Task #%d closed' => 'Задачата #%d е затворена', + 'Task #%d opened' => 'Задачата #%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', + '%d overdue tasks' => '%d доцни задачи', + 'No notification.' => 'Ðема нови извеÑтувања.', + '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:' => 'Методи за извеÑтување:', + 'Unable to read your file' => 'Ðе може да Ñе прочита датотеката', + '%d task(s) have been imported successfully.' => '%d задачите Ñе уÑпешно увезени.', + 'Nothing has been imported!' => 'Ðишто увезено!', + 'Import users from CSV file' => 'Увезете кориÑници преку CSV-датотека', + '%d user(s) have been imported successfully.' => '%d кориÑниците уÑпешно Ñе увезени.', + 'Comma' => 'Запирка', + 'Semi-colon' => 'Точка и запирка', + 'Tab' => 'Таб', + 'Vertical bar' => 'Вертикална лента', + '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)' => 'Додадете филтер (намеÑто да го замените поÑтоечкиот)', + 'Append/Replace' => 'Додај / замени', + 'Append' => 'Додади', + 'Replace' => 'Заменете', + 'Import' => 'Увоз', + 'Change sorting' => 'Променете го Ñортирањето', + 'Tasks Importation' => 'Задачи за увоз', + 'Delimiter' => 'Делимтер', + 'Enclosure' => 'Прилог', + 'CSV File' => 'CSV-датотека', + 'Instructions' => 'ИнÑтрукции', + 'Your file must use the predefined CSV format' => 'Вашата датотека мора да кориÑти предефиниран CSV формат', + 'Your file must be encoded in UTF-8' => 'Вашата датотека мора мора да биде кодирана у 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' => 'Рок за завршување мора да биде во ISO формат: YYYY-MM-DD', + 'Download CSV template' => 'Преземете го шаблонот CSV', + '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' => '%s додаде нова датотека во задачата %s', + 'Link type' => 'Тип на врÑка', + 'Assign automatically a category based on a link' => 'ÐвтоматÑко доделување категории базирани на врÑка', + 'BAM - Konvertible Mark' => 'BAM - Конвертибилна марка', + 'Assignee Username' => 'КориÑничко име на извршителот', + 'Assignee Name' => 'Име на извршител', + 'Groups' => 'Групи', + 'Members of %s' => 'Членови на %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"' => 'Додадете член во групата "%s"', + 'Group member added successfully.' => 'УÑпешно додаден член на групата.', + 'Unable to add group member.' => 'Ðе може да Ñе додаде член во групата.', + 'Remove user from group "%s"' => 'ОтÑтранете го кориÑникот од групата "%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' => 'Ðабverудувач на проектот', + 'Your account is locked for %d minutes' => 'Вашиот профил е заклучен во Ñледните %d минути', + 'Invalid captcha' => 'Погрешно фаќање', + 'The name must be unique' => 'Името мора да биде единÑтвено', + 'View all groups' => 'ПрелиÑтајте ги Ñите групи', + 'There is no user available.' => 'Ðема доÑтапни кориÑници.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да отÑтраните "%s" од групата "%s"?', + 'There is no group.' => 'Ðема група', + 'Add group member' => 'Додадете член на групата', + 'Do you really want to remove this group: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да ја отÑтраните оваа група: "%s"?', + 'There is no user in this group.' => 'Во моментов нема кориÑници во оваа група.', + 'Permissions' => 'Дозволи', + 'Allowed Users' => 'Дозволени кориÑници', + 'No specific user has been allowed.' => 'Ðема Ñпецифичен кориÑник.', + 'Role' => 'Улоги', + 'Enter user name...' => 'ВнеÑете кориÑничко име ...', + 'Allowed Groups' => 'Дозволени групи', + 'No group has been allowed.' => 'Ðе Ñе дозволени групи.', + 'Group' => 'Група', + 'Group Name' => 'Име на групата', + 'Enter group name...' => 'ВнеÑете име на групата ...', + 'Role:' => 'Улогата:', + 'Project members' => 'Членови на проектот', + '%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', + 'Estimated hours: ' => 'Проценети чаÑови:', + 'Actual hours: ' => 'ÐавиÑтина чаÑови:', + 'Hours Spent' => 'Поминати чаÑови:', + 'Hours Estimated' => 'Проценети чаÑови', + 'Estimated Time' => 'Проценето време', + 'Actual Time' => 'ВиÑтинÑко време', + 'Estimated vs actual time' => 'Проценето / виÑтинÑко време', + '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' => 'Ðлгоритам за еднократна лозинка заÑнована на време', + 'Two-Factor Provider: ' => 'ДвофакторÑки добавувач:', + 'Disable two-factor authentication' => 'Оневозможете автентикација Ñо два фактори', + 'Enable two-factor authentication' => 'Овозможете автентикација Ñо два фактори', + 'There is no integration registered at the moment.' => 'Во моментов не Ñе региÑтрирани интеграции.', + 'Password Reset for Kanboard' => 'Променете ја лозинката за Канборд', + 'Forgot password?' => 'Ја заборави лозинката?', + 'Enable "Forget Password"' => 'Овозможете ја заборавената лозинка', + 'Password Reset' => 'Сменете ја вашата лозинка', + 'New password' => 'Ðова лозинка', + 'Change Password' => 'Променете ја лозинката', + 'To reset your password click on this link:' => 'За промена на лозинката кликнете на овој линк:', + 'Last Password Reset' => 'ПоÑледна промена на лозинка', + 'The password has never been reinitialized.' => 'Лозинката никогаш не е променета.', + 'Creation' => 'Ðаправени', + 'Expiration' => 'ИÑтекување', + 'Password reset history' => 'ИÑторија на промена на лозинка', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Сите задачи во колоната "%s" и патеката "%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.' => '%d Задачa(и) во колоната "%s" и патеката "%s" ќе бидат затворени.', + 'Close all tasks in this column and this swimlane' => 'Затворете ги Ñите задачи во оваа колона и оваа патека', + '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' => 'СопÑтвеник на проектот', + 'Personal projects do not have users and groups management.' => 'Личните проекти не управуваат Ñо кориÑници и групи.', + 'There is no project member.' => 'Ðема членови на проектот.', + 'Priority' => 'Приоритет', + 'Task priority' => 'Приоритет на задачата', + 'General' => 'Општи', + 'Dates' => 'Датуми', + 'Default priority' => 'Стандарден приоритет', + 'Lowest priority' => 'Ðајнизок приоритет', + 'Highest priority' => 'ÐајвиÑок приоритет', + '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' => 'Прилог', + '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' => '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' => '%s чаÑови', + 'Time spent:' => 'Потрошено време:', + 'Created:' => 'Создадено на:', + 'Modified:' => 'Изменето:', + 'Completed:' => 'Завршено:', + 'Started:' => 'Почна:', + 'Moved:' => 'ПремеÑтен:', + 'Task #%d' => 'Задача #%d', + 'Time format' => 'Формат на време', + 'Start date: ' => 'Почетен датум:', + 'End date: ' => 'Крајна дата:', + 'New due date: ' => 'Ðов рок за завршување:', + 'Start date changed: ' => 'Датумот на почеток е променет:', + 'Disable personal projects' => 'Оневозможете лични проекти', + 'Do you really want to remove this custom filter: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да го отÑтраните овој прилагоден филтер "%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.' => 'Сите датотеки беа уÑпешно поÑтавени.', + 'The maximum allowed file size is %sB.' => 'МакÑималната дозволена големина на датотека е %sB.', + 'Drag and drop your files here' => 'Повлечете ги и иÑпуштете ги вашите датотеки овде', + 'choose files' => 'изберете датотеки', + 'View profile' => 'Види профил', + 'Two Factor' => 'Два фактори', + 'Disable user' => 'Оневозможете го кориÑникот', + 'Do you really want to disable this user: "%s"?' => 'Дали навиÑтина Ñакате да го оневозможите овој кориÑник: "%s"?', + 'Enable user' => 'Овозможете кориÑник', + 'Do you really want to enable this user: "%s"?' => 'Дали навиÑтина Ñакате да го овозможите овој кориÑник: "%s"?', + 'Download' => 'Преземено', + 'Uploaded: %s' => 'ИÑпратено: %s', + 'Size: %s' => 'Големина: %s', + 'Uploaded by %s' => 'ИÑпратено од %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' => 'Оваа вредноÑÑ‚ мора да биде поголема од %d', + 'Another swimlane with the same name exists in the project' => 'Друга патека Ñо иÑто име поÑтои во проектот', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Ðа пример: https://example.kanboard.org/ (Ñе кориÑти за генерирање на апÑолутна URL-адреÑа)', + '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:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Верзија за ОС:', + 'Database version:' => 'Верзија на базата на податоци:', + 'Browser:' => 'ПрелиÑтувач:', + 'Task view' => 'Преглед на задача', + 'Edit task' => 'Уредете ја задачата', + 'Edit description' => 'Уредете го опиÑот', + 'New internal link' => 'Ðова внатрешна врÑка', + 'Display list of keyboard shortcuts' => 'Покажете ÑпиÑок Ñо кратенки на таÑтатурата', + '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.' => 'Овој Канборд не е конфигуриран да поддржува инÑталирање додатоци преку кориÑничкото опкружување.', + 'There is no plugin available.' => 'Ðема доÑтапни додатоци.', + 'Install' => 'ИнÑталирајте', + 'Update' => 'Ðжурирање', + 'Up to date' => 'Во тек', + 'Not available' => 'Ðе е доÑтапно', + 'Remove plugin' => 'ОтÑтранете го додатокот', + 'Do you really want to remove this plugin: "%s"?' => 'Дали Ñте Ñигурни дека Ñакате да го отÑтраните овој додатокот: "%s"?', + 'Uninstall' => 'ДеинÑталирај', + 'Listing' => 'ЛиÑтата', + 'Metadata' => 'Метаподатоци', + 'Manage projects' => 'Управување Ñо проекти', + 'Convert to task' => 'Претворете го во задача', + 'Convert sub-task to task' => 'Претворете ја подзадачата во задача', + 'Do you really want to convert this sub-task to a task?' => 'Дали навиÑтина Ñакате подзадача да ја претворите во задача?', + 'My task title' => 'ÐаÑлов на задача', + 'Enter one task by line.' => 'ВнеÑете една задача по линија.', + 'Number of failed login:' => 'Број на неуÑпешни апликации:', + 'Account locked until:' => 'Профилот е заклучен до:', + 'Email settings' => 'ПоÑтавки за е-пошта', + 'Email sender address' => 'ÐдреÑа за е-пошта на иÑпраќачот', + 'Email transport' => 'ТранÑпорт по е-пошта', + 'Webhook token' => 'Токен за Webhook', + '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' => 'Ознакa', + 'Remove a tag' => 'ОтÑтранете ја oзнакa', + '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 a 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 has 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', + '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' => 'ВнеÑете текÑÑ‚ во Маркдаун', + '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' => 'Е-пошта што излегуваат', + '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 е иÑпратена покана.', + '%d invitation was sent.' => '%d е иÑпратена покана.', + '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' => 'Ова кориÑничко име е зафатено', + 'Your profile must have a valid email address.' => 'Профилот мора да има важечка адреÑа за е-пошта.', + 'TRL - Turkish Lira' => 'TRL - турÑка лира', + 'The project email is optional and could be used by several plugins.' => 'Проектот за е-пошта е опционален и може да Ñе кориÑти од неколку додатоци.', + 'The project email must be unique across all projects' => 'Е-поштата на проектот мора да биде единÑтвена за Ñекој проект', + 'The email configuration has been disabled by the administrator.' => 'Уредувањето на е-пошта е оневозможено од админиÑтраторот.', + 'Close this project' => 'Затворете го овој проект', + 'Open this project' => 'Отворете го овој проект', + 'Close a project' => 'Затворете го проектот', + 'Do you really want to close this project: "%s"?' => 'Дали навиÑтина Ñакате да го затворите проектот: "%s"?', + 'Reopen a project' => 'Повторно отворете го проектот', + 'Do you really want to reopen this project: "%s"?' => 'Дали навиÑтина Ñакате повторно да го отворите проектот: "%s"?', + 'This project is open' => 'Овој проект е отворен', + 'This project is closed' => 'Овој проект е затворен', + 'Unable to upload files, check the permissions of your data folder.' => 'Ðе може да Ñе поÑтават датотеки, проверете ги дозволите за директориум на Ñерверот.', + 'Another category with the same name exists in this project' => 'Категорија Ñо ова име веќе поÑтои на овој проект', + 'Comment sent by email successfully.' => 'Коментарот е уÑпешно иÑпратен по е-пошта.', + 'Sent by email to "%s" (%s)' => 'ПоÑлато е-поштом на "%s" (%s)', + 'Unable to read uploaded file.' => 'Ðе можам да ја читам поÑтавената датотека.', + 'Database uploaded successfully.' => 'Базата на податоци е уÑпешно поÑтавена.', + 'Task sent by email successfully.' => 'Задачата е уÑпешно иÑпратена по е-пошта.', + 'There is no category in this project.' => 'Ðема категории во овој проект.', + 'Send by email' => 'ИÑпратете по е-пошта', + 'Create and send a comment by email' => 'Ðаправете и иÑпратете коментар по е-пошта', + 'Subject' => 'Предмет', + 'Upload the database' => 'ПоÑтавете база на податоци', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Може да иÑпратите претходно преземена база на податоци Sqlite (формат Gzip).', + 'Database file' => 'Датотека Ñо базата на податоци', + 'Upload' => 'ПоÑтавете', + 'Your project must have at least one active swimlane.' => 'Вашиот проект мора да има барем една активна патека.', + 'Project: %s' => 'Проект: %s', + 'Automatic action not found: "%s"' => 'ÐвтоматÑкото дејÑтво не е пронајдено: "%s"', + '%d projects' => '%d проекти', + '%d project' => '%d проект', + 'There is no project.' => 'Ðема проект.', + 'Sort' => 'Сортирај', + 'Project ID' => 'ИД на проект', + 'Project name' => 'Име на проектот', + 'Public' => 'Јавно', + 'Personal' => 'Лично', + '%d tasks' => '%d задачи', + '%d task' => '%d задача', + 'Task ID' => 'ИД на задача', + 'Assign automatically a color when due date is expired' => 'ÐвтоматÑки ја поÑтавува бојата кога иÑтече рок за завршување', + 'Total score in this column across all swimlanes' => 'Вкупниот резултат во оваа колона за Ñите патеки', + 'HRK - Kuna' => 'HRK - хрватÑка куна', + 'ARS - Argentine Peso' => 'ARS- аргентинÑки пезоÑ', + 'COP - Colombian Peso' => 'COP - колумбиÑки пезоÑ', + '%d groups' => '%d групи', + '%d group' => '%d група', + 'Group ID' => 'ID на групата', + 'External ID' => 'Ðадворешна ИД', + '%d users' => '%d кориÑници', + '%d user' => '%d кориÑник', + 'Hide subtasks' => 'Сокриј ги подзадачите', + 'Show subtasks' => 'Прикажи ги подзадачите', + 'Authentication Parameters' => 'Параметри за автентикација', + 'API Access' => 'ÐPI приÑтап', + 'No users found.' => 'Ðе Ñе пронајдени кориÑници.', + 'User ID' => 'КориÑнички проект', + 'Notifications are activated' => 'ИзвеÑтувањата Ñе овозможени', + 'Notifications are disabled' => 'ИзвеÑтувањата Ñе оневозможени', + 'User disabled' => 'КориÑникот е оневозможен', + '%d notifications' => '%d извеÑтувањe(a)', + '%d notification' => '%d извеÑтување', + 'There is no external integration installed.' => 'Ðе е инÑталирана надворешна интеграција.', + 'You are not allowed to update tasks assigned to someone else.' => 'Ðе ви е дозволено да ги ажурирате задачите доделени на некој друг.', + 'You are not allowed to change the assignee.' => 'Ðе Ñмеете да го менувате извршителот.', + 'Task suppression is not permitted' => 'Сузбивање на задачите не е дозволено', + 'Changing assignee is not permitted' => 'Промена на извршителот не е дозволена', + 'Update only assigned tasks is permitted' => 'Само доделените задачи имаат дозвола да Ñе ажурираат', + 'Only for tasks assigned to the current user' => 'Само за задачите доделени на тековниот кориÑник', + 'My projects' => 'Мои проекти', + 'You are not a member of any project.' => 'Вие не Ñте член на ниеден проект.', + 'My subtasks' => 'Мои подзадачи', + '%d subtasks' => '%d подзадачи', + '%d subtask' => '%d подзадача', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Дозволено е Ñамо помеÑтување на задача помеѓу овие колони за задачите доделени на тековниот кориÑник', + '[DUPLICATE]' => '[ДУПЛИКÐТ]', + 'DKK - Danish Krona' => 'DKK- данÑка круна', + 'Remove user from group' => 'ОтÑтранете го кориÑникот од групата', + 'Assign the task to its creator' => 'Доделете задача на нејзиниот творец', + 'This task was sent by email to "%s" with subject "%s".' => 'Оваа задача беше иÑпратена по е-пошта до "%s" Ñо предмет "%s".', + 'Predefined Email Subjects' => 'Преддефинирани Ñтавки по е-пошта', + 'Write one subject by line.' => 'ВнеÑете една Ñтавка по линија', + 'Create another link' => 'Создадете друга врÑка', + 'BRL - Brazilian Real' => 'BRL - бразилÑки реал', + 'Add a new Kanboard task' => 'Додадете нова задача на Канборд', + 'Subtask not started' => 'Подзадача не започна', + 'Subtask currently in progress' => 'Подзадачата е во тек', + 'Subtask completed' => 'Потзадачата е завршена', + 'Subtask added successfully.' => 'Подзадача уÑпешно додаде.', + '%d subtasks added successfully.' => 'УÑпешно додадени подзадачи %d.', + 'Enter one subtask by line.' => 'ВнеÑете една подзадача по линија.', + 'Predefined Contents' => 'Преддефинирана Ñодржина', + 'Predefined contents' => 'Преддефинирана Ñодржина', + 'Predefined Task Description' => 'Преддефиниран Ð¾Ð¿Ð¸Ñ Ð½Ð° задачата', + 'Do you really want to remove this template? "%s"' => 'Дали Ñте Ñигурни дека Ñакате да го отÑтраните овој образец? "%s"', + 'Add predefined task description' => 'Додаден е предефиниран Ð¾Ð¿Ð¸Ñ Ð½Ð° задачата', + 'Predefined Task Descriptions' => 'Преддефиниран Ð¾Ð¿Ð¸Ñ Ð½Ð° задачата', + 'Template created successfully.' => 'Шаблонот е уÑпешно Ñоздаден', + 'Unable to create this template.' => 'Ðе може да Ñе Ñоздаде овој шаблон', + 'Template updated successfully.' => 'Шаблонот е уÑпешно ажуриран', + 'Unable to update this template.' => 'Ðе може да Ñе ажурира овој образец', + 'Template removed successfully.' => 'Шаблонот е уÑпешно отÑтранет', + 'Unable to remove this template.' => 'Ðе може да Ñе отÑтрани овој образец', + 'Template for the task description' => 'Шаблон за Ð¾Ð¿Ð¸Ñ Ð½Ð° задача', + 'The start date is greater than the end date' => 'Датумот на почеток е поголем од датумот на крај', + 'Tags must be separated by a comma' => 'Ознаките мора да Ñе одделат Ñо запирки', + 'Only the task title is required' => 'Потребен е Ñамо наÑлов на задачата', + 'Creator Username' => 'КориÑничко име на креаторот', + 'Color Name' => 'Име на боја', + 'Column Name' => 'Име на колона', + 'Swimlane Name' => 'Име на патеката', + 'Time Estimated' => 'Проценето време', + 'Time Spent' => 'Потрошено време', + 'External Link' => 'Ðадворешна врÑка', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Оваа функционалноÑÑ‚ овозможува приказ на iCal фид, RSS фид и јавна табла.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Запрете го тајмерот на Ñите подзадачи кога ја премеÑтувате задачата во друга колона', + 'Subtask Title' => 'ÐаÑлов на подзадача', + 'Add a subtask and activate the timer when moving a task to another column' => 'Додадете подзадача и активирајте тајмер кога ќе ја премеÑтите задачата во друга колона', + 'days' => 'денови', + 'minutes' => 'минути', + 'seconds' => 'Ñекунди', + 'Assign automatically a color when preset start date is reached' => 'ÐвтоматÑки ја поÑтавува бојата кога ќе Ñе доÑтигне претходно поÑтавениот датум за почеток', + 'Move the task to another column once a predefined start date is reached' => 'ПомеÑтете ја задачата во втората колона кога ќе Ñе доÑтигне предефинираниот датум за почеток', + 'This task is now linked to the task %s with the relation "%s"' => 'Задачата Ñега е поврзана Ñо задачата %s Ñо релација "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Ð’Ñ€Ñката Ñо релацијата "%s" Ñо задачата %s е отÑтранета', + 'Custom Filter:' => 'Прилагодено филтер', + 'Unable to find this group.' => 'Ðе може да Ñе најде оваа група.', + '%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 moved the task #%d to the swimlane "%s"' => '%s ја премеÑти задачата #%d на патеката "%s"', + '%sh spent' => '%sч потрошени', + '%sh estimated' => '%sч проценето', + 'Select All' => 'Изберете Ñè', + 'Unselect All' => 'Врати Ñè', + 'Apply action' => 'Примени акција', + 'Move selected tasks to another column or swimlane' => 'ПремеÑтете ја избраната задача во друга колона или патека', + 'Edit tasks in bulk' => 'МаÑовно уредување на задачите', + 'Choose the properties that you would like to change for the selected tasks.' => 'Изберете ги ÑвојÑтвата што Ñакате да ги промените за избраните задачи.', + 'Configure this project' => 'Конфигурирајте го овој проект', + 'Start now' => 'Почни Ñега', + '%s removed a file from the task #%d' => '%s ја отÑтрани датотеката од задачата #%d', + 'Attachment removed from task #%d: %s' => 'Приклучокот е отÑтранет од задачата #%d: %s', + 'No color' => 'Без боја', + 'Attachment removed "%s"' => 'Прилогот е отÑтранет "%s"', + '%s removed a file from the task %s' => '%s ја отÑтрани датотека од задача %s', + 'Move the task to another swimlane when assigned to a user' => 'ПомеÑтете ја задачата на друга патека кога задачата е доделена на кориÑникот', + 'Destination swimlane' => 'Патека за деÑтинација', + 'Assign a category when the task is moved to a specific swimlane' => 'Доделете категорија кога задачата е премеÑтена на одредена патека', + 'Move the task to another swimlane when the category is changed' => 'ПомеÑтете ја задачата на друга патека кога категоријата Ñе менува', + 'Reorder this column by priority (ASC)' => 'Променете го редоÑледот на оваа колона Ñпоред приоритет (РÐСТЕЧКИ)', + 'Reorder this column by priority (DESC)' => 'Променете го редоÑледот на оваа колона по приоритет (ОПÐЃÐЧКИ)', + 'Reorder this column by assignee and priority (ASC)' => 'Променете го редоÑледот на оваа колона Ñпоред извршителот (РÐСТЕЧКИ)', + 'Reorder this column by assignee and priority (DESC)' => 'Променете го редоÑледот на оваа колона Ñпоред извршителот (ОПÐЃÐЧКИ)', + 'Reorder this column by assignee (A-Z)' => 'Променете го редоÑледот на оваа колона Ñпоред извршителот (Ð-Ш)', + 'Reorder this column by assignee (Z-A)' => 'Променете го редоÑледот на оваа колона од извршител (Ш-A)', + 'Reorder this column by due date (ASC)' => 'Променете го редоÑледот на оваа колона Ñпоред рок за завршување (РÐСТЕЧКИ)', + 'Reorder this column by due date (DESC)' => 'Променете го редоÑледот на оваа колона Ñпоред рок за завршување (ОПÐЃÐЧКИ)', + 'Reorder this column by id (ASC)' => 'Променете го редоÑледот на оваа колона Ñпоред ИД (РÐСТЕЧКИ)', + 'Reorder this column by id (DESC)' => 'Променете го редоÑледот на оваа колона Ñпоред ИД (ОПÐЃÐЧКИ)', + '%s moved the task #%d "%s" to the project "%s"' => '%s ја премеÑти задачата #%d "%s" за да проектира "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Задачата #%d "%s" е премеÑтена во проектот "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'ПомеÑтете ја задачата во друга колона кога рок за завршување е пократок од одреден број денови', + 'Automatically update the start date when the task is moved away from a specific column' => 'ÐвтоматÑки ажурирајте го датумот на почеток кога задачата е отÑтранета од одредена колона', + 'HTTP Client:' => 'Клиент за HTTP', + 'Assigned' => 'Доделени', + 'Task limits apply to each swimlane individually' => 'Границата за бројот на задачите Ñе однеÑува на Ñекоја патеката одделно', + 'Column task limits apply to each swimlane individually' => 'Границата за бројот на задачи во колоната Ñе однеÑува на Ñекоја патеката одделно', + 'Column task limits are applied to each swimlane individually' => 'Границата за бројот на задачи во колоната Ñе однеÑува на Ñекоја патеката одделно', + 'Column task limits are applied across swimlanes' => 'Границата за бројот на задачи во колоната Ñе однеÑува на повеќе патеки', + 'Task limit: ' => 'Границата за бројот на задача:', + 'Change to global tag' => 'Промена на глобална ознака', + 'Do you really want to make the tag "%s" global?' => 'Дали навиÑтина Ñакате ознаката "%s" да ја направите глобална?', + 'Enable global tags for this project' => 'Овозможете глобални ознаки за овој проект', + 'Group membership(s):' => 'ЧленÑтво во група:', + '%s is a member of the following group(s): %s' => '%s е член на група(и): %s', + '%d/%d group(s) shown' => '%d/%d прикажана група(и)', + 'Subtask creation or modification' => 'Создавање или модифицирање на подзадача', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Доделете ја задачата на одреден кориÑник кога задачата е премеÑтена на одредена патека', + 'Comment' => 'Коментар', + 'Collapse vertically' => 'Собери вертикално', + 'Expand vertically' => 'Проширете вертикално', + 'MXN - Mexican Peso' => 'MXN - МекÑиканÑки пезоÑ', + 'Estimated vs actual time per column' => 'Проценето наÑпроти реалното време по колона', + 'HUF - Hungarian Forint' => 'HUF - УнгарÑка форинта', + 'XBT - Bitcoin' => 'XBT - биткоин', + 'You must select a file to upload as your avatar!' => 'Мора да изберете датотека што ќе ја поÑтавите како ваш аватар!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Датотеката што ја поÑтавивте не е валидна Ñлика! (Дозволени Ñе Ñамо *.gif, *.jpg, *.jpeg и *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'ÐвтоматÑки поÑтавете го рок за завршување кога задачата ќе Ñе оддалечи од одредена колона', + 'No other projects found.' => 'Ðе Ñе пронајдени други проекти.', + 'Tasks copied successfully.' => 'Задачите Ñе уÑпешно копирани.', + 'Unable to copy tasks.' => 'Ðе може да Ñе копираат задачите.', + 'Theme' => 'Тема', + 'Theme:' => 'Тема:', + 'Light theme' => 'Светла тема', + 'Dark theme' => 'Темна тема', + 'Automatic theme - Sync with system' => 'ÐвтоматÑка тема - Синхронизирај Ñо ÑиÑтемот', + 'Application managers or more' => 'Менаџери на апликации или повеќе', + 'Administrators' => 'ÐдминиÑтратори', + 'Visibility:' => 'ВидливоÑÑ‚:', + 'Standard users' => 'Стандардни кориÑници', + 'Visibility is required' => 'ВидливоÑта е задолжителна', + 'The visibility should be an app role' => 'ВидливоÑта треба да биде улога на апликацијата', + 'Reply' => 'Одговори', + '%s wrote: ' => '%s напиша: ', + 'Number of visible tasks in this column and swimlane' => 'Број на видливи задачи во оваа колона и патека', + 'Number of tasks in this swimlane' => 'Број на задачи во оваа патека', + 'Unable to find another subtask in progress, you can close this window.' => 'Ðе можам да најдам друг подзадача во тек, можете да го затворите овој прозорец.', + 'This theme is invalid' => 'Оваа тема е неважечка', + 'This role is invalid' => 'Оваа улога е неважечка', + 'This timezone is invalid' => 'Оваа временÑка зона е неважечка', + 'This language is invalid' => 'Овој јазик е неважечки', + 'This URL is invalid' => 'Оваа URL адреÑа е неважечка', + 'Date format invalid' => 'Ðеважечки формат на датум', + 'Time format invalid' => 'Ðеважечки формат на време', + 'Invalid Mail transport' => 'Ðеважечки транÑпорт на пошта', + 'Color invalid' => 'Ðеважечка боја', + 'This value must be greater or equal to %d' => 'Оваа вредноÑÑ‚ мора да биде поголема или еднаква на %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Додадете BOM на почетокот на датотеката (задолжително за Microsoft Excel)', + 'Just add these tag(s)' => 'Само додадете ги овие ознаки', + 'Remove internal link(s)' => 'ОтÑтрани внатрешна врÑка(и)', + 'Import tasks from another project' => 'Увези задачи од друг проект', + 'Select the project to copy tasks from' => 'Изберете го проектот од кој Ñакате да копирате задачи', + 'The total maximum allowed attachments size is %sB.' => 'Вкупната макÑимална дозволена големина на прилози е %sB.', + 'Add attachments' => 'Додади прилози', + 'Task #%d "%s" is overdue' => 'Задачата #%d "%s" е задоцнета', + 'Enable notifications by default for all new users' => 'Овозможете извеÑтувања по подразбирање за Ñите нови кориÑници', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Додели ја задачата на нејзиниот креатор за одредени колони ако не е рачно поÑтавен извршител', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Додели ја задачата на најавениот кориÑник при промена на колоната во зададената колона ако нема доделен кориÑник', +]; diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php new file mode 100644 index 0000000..8e3f593 --- /dev/null +++ b/app/Locale/my_MY/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'Tiada', + 'Edit' => 'Sunting', + 'Remove' => 'Hapus', + 'Yes' => 'Ya', + 'No' => 'Tidak', + 'cancel' => 'batal', + 'or' => 'atau', + 'Yellow' => 'Kuning', + 'Blue' => 'Biru', + 'Green' => 'Hijau', + 'Purple' => 'Ungu', + 'Red' => 'Merah', + 'Orange' => 'Oren', + 'Grey' => 'Kelabu', + 'Brown' => 'Coklat', + 'Deep Orange' => 'Oren Gelap', + 'Dark Grey' => 'Kelabu Malap', + 'Pink' => 'Merah Jambu', + 'Teal' => 'Teal', + 'Cyan' => 'Sian', + 'Lime' => 'Lime', + 'Light Green' => 'Hijau Muda', + 'Amber' => 'Amber', + 'Save' => 'Simpan', + 'Login' => 'Masuk', + 'Official website:' => 'Laman rasmi :', + 'Unassigned' => 'Belum ditugaskan', + 'View this task' => 'Lihat tugas ini', + 'Remove user' => 'Hapus pengguna', + 'Do you really want to remove this user: "%s"?' => 'Anda yakin mahu menghapus pengguna ini : « %s » ?', + 'All users' => 'Semua pengguna', + 'Username' => 'Nama pengguna', + 'Password' => 'Kata laluan', + 'Administrator' => 'Pentadbir', + 'Sign in' => 'Masuk', + 'Users' => 'Para Pengguna', + 'Forbidden' => 'Larangan', + 'Access Forbidden' => 'Akses Dilarang', + 'Edit user' => 'Ubah Pengguna', + 'Logout' => 'Keluar', + 'Bad username or password' => 'Nama pengguna atau kata laluan tidak sepadan', + 'Edit project' => 'Ubah projek', + 'Name' => 'Nama', + 'Projects' => 'Projek', + 'No project' => 'Tiada projek', + 'Project' => 'Projek', + 'Status' => 'Status', + 'Tasks' => 'Tugasan', + 'Board' => 'Papan', + 'Actions' => 'Tindakan', + 'Inactive' => 'Tidak Aktif', + 'Active' => 'Aktif', + 'Unable to update this board.' => 'Tidak berupaya mengemaskini papan ini', + '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 »', + 'Add a new column' => 'Tambah kolom baru', + 'Title' => 'Judul', + 'Assigned to %s' => 'Ditugaskan ke %s', + 'Remove a column' => 'Hapus kolom', + '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 » ?', + 'Settings' => 'Penetapan', + 'Application settings' => 'Penetapan aplikasi', + 'Language' => 'Bahasa', + 'Webhook token:' => 'Token webhook :', + 'API token:' => 'Token API :', + 'Database size:' => 'Saiz pengkalan data:', + 'Download the database' => 'Muat turun pengkalan data', + 'Optimize the database' => 'Optimakan pengkalan data', + '(VACUUM command)' => '(perintah VACUUM)', + '(Gzip compressed Sqlite file)' => '(File Sqlite yang termampat Gzip)', + 'Close a task' => 'Tutup tugas', + 'Column' => 'Kolom', + 'Color' => 'Warna', + 'Assignee' => 'Orang yang ditugaskan', + 'Create another task' => 'Buat tugas lain', + 'New task' => 'Tugasan baru', + 'Open a task' => 'Buka tugas', + 'Do you really want to open this task: "%s"?' => 'Anda yakin untuk buka 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 : ', + 'Close this task' => 'Tutup tugas ini', + 'Open this task' => 'Buka tugas ini', + 'There is no description.' => 'Tidak ada keterangan.', + 'Add a new task' => 'Tambah tugas baru', + 'The username is required' => 'Nama pengguna adalah wajib', + '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 laluan adalah wajib', + 'This value must be an integer' => 'Nilai ini harus integer', + 'The username must be unique' => 'Nama pengguna semestinya unik', + 'The user id is required' => 'Id Pengguna adalah wajib', + 'Passwords don\'t match' => 'Kata laluan tidak sepadan', + 'The confirmation is required' => 'Pengesahan diperlukan', + 'The project is required' => 'Projek diperlukan', + 'The id is required' => 'Id diperlukan', + 'The project id is required' => 'Id projek diperlukan', + 'The project name is required' => 'Nama projek diperlukan', + 'The title is required' => 'Judul diperlukan', + 'Settings saved successfully.' => 'Penetapan berjaya disimpan.', + 'Unable to save your settings.' => 'Tidak dapat menyimpan penetapan anda.', + 'Database optimization done.' => 'Optimasi pengkalan data selesai.', + 'Your project has been created successfully.' => 'Projek anda berhasil dibuat.', + 'Unable to create your project.' => 'Tidak dapat membuat projek anda.', + 'Project updated successfully.' => 'projek berhasil diperbaharui.', + 'Unable to update this project.' => 'Tidak dapat memperbaharui projek ini.', + 'Unable to remove this project.' => 'Tidak dapat menghapus projek ini.', + 'Project removed successfully.' => 'projek berhasil dihapus.', + 'Project activated successfully.' => 'projek berhasil diaktivasi.', + 'Unable to activate this project.' => 'Tidak dapat mengaktifkan projek ini.', + 'Project disabled successfully.' => 'projek berhasil dinonaktifkan.', + 'Unable to disable this project.' => 'Tidak dapat menonaktifkan projek ini.', + 'Unable to open this task.' => 'Tidak dapat membuka tugas ini.', + '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 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.', + '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.', + 'Public link' => 'Pautan publik', + 'Timezone' => 'Zona waktu', + 'Sorry, I didn\'t find this information in my database!' => 'Maaf, saya tidak menemukan informasi ini dalam basis data saya !', + 'Page not found' => 'Halaman tidak ditemukan', + 'Complexity' => 'Kompleksitas', + 'Task limit' => 'Batas tugas.', + 'Task count' => 'Jumlah tugas', + 'User' => 'Pengguna', + 'Comments' => 'Komentar', + 'Comment is required' => 'Komentar diperlukan', + 'Comment added successfully.' => 'Komentar berhasil ditambahkan.', + 'Unable to create your comment.' => 'Tidak dapat menambahkan komentar anda.', + 'Due Date' => 'Batas Tanggal Terakhir', + 'Invalid date' => 'Tanggal tidak valid', + 'Automatic actions' => 'Tindakan otomatis', + 'Your automatic action has 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', + 'Action removed successfully.' => 'Tindakan berhasil dihapus.', + 'Automatic actions for the project "%s"' => 'Tindakan otomatis untuk projek ini « %s »', + 'Add an action' => 'Tambah tindakan', + 'Event name' => 'Nama acara', + 'Action' => 'Tindakan', + 'Event' => 'Acara', + 'When the selected event occurs execute the corresponding action.' => 'Ketika acara yang dipilih terjadi, melakukan tindakan yang sesuai.', + '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 » ?', + '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', + 'Duplicate the task to another project' => 'Duplikasi tugas ke projek lain', + 'Move a task to another column' => 'Pindahkan tugas ke kolom lain', + 'Task modification' => 'Modifikasi tugas', + 'Task creation' => 'Membuat tugas', + 'Closing a task' => 'Menutup tugas', + 'Assign a color to a specific user' => 'Menetapkan warna untuk pengguna tertentu', + 'Position' => 'Posisi', + 'Duplicate to project' => 'Duplikasi ke projek lain', + 'Duplicate' => 'Duplikasi', + 'Link' => 'Pautan', + 'Comment updated successfully.' => 'Komentar berhasil diperbaharui.', + 'Unable to update your comment.' => 'Tidak dapat memperbaharui 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 laluan saat ini untuk pengguna « %s »', + 'The current password is required' => 'Kata laluan saat ini diperlukan', + 'Wrong password' => 'Kata laluan 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', + 'No session.' => 'Tidak ada sesi.', + 'Expiration date' => 'Tanggal kadaluarsa', + 'Remember Me' => 'Ingat Saya', + 'Creation date' => 'Tanggal dibuat', + 'Everybody' => 'Semua orang', + 'Open' => 'Terbuka', + 'Closed' => 'Ditutup', + 'Search' => 'Cari', + 'Nothing found.' => 'Tidak ditemukan.', + 'Due date' => 'Batas tanggal terakhir', + '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.' => 'Akaun eksternal anda tidak lagi terhubung ke profil anda.', + 'Unable to unlink your external account.' => 'Tidak dapat memutuskan Akaun eksternal anda.', + 'External authentication failed' => 'Otentifikasi eksternal gagal', + 'Your external account is linked to your profile successfully.' => 'Akaun 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 » ?', + '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', + 'Category' => 'Kategori', + 'Category:' => 'Kategori :', + 'Categories' => 'Kategori', + 'Your category has been created successfully.' => 'Kategori anda berhasil dibuat.', + 'This category has been updated successfully.' => 'Kategori anda berhasil diperbaharui.', + 'Unable to update this category.' => 'Tidak dapat memperbaharui 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 projek « %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 » ?', + 'All categories' => 'Semua kategori', + 'No category' => 'Tidak ada kategori', + 'The name is required' => 'Nama diperlukan', + 'Remove a file' => 'Hapus berkas', + '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 » ?', + 'Attachments' => 'Lampiran', + 'Edit the task' => 'Modifikasi tugas', + 'Add a comment' => 'Tambahkan komentar', + 'Edit a comment' => 'Modifikasi komentar', + 'Summary' => 'Ringkasan', + 'Time tracking' => 'Pelacakan waktu', + 'Estimate:' => 'Estimasi :', + 'Spent:' => 'Menghabiskan:', + 'Do you really want to remove this sub-task?' => 'Apakah anda yakin akan menghapus sub-tugas ini ?', + 'Remaining:' => 'Tersisa:', + 'hours' => 'jam', + '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', + 'Remove a sub-task' => 'Hapus sub-tugas', + 'The time must be a numeric value' => 'Waktu harus berisikan numerik', + 'Todo' => 'Yang harus dilakukan', + 'In progress' => 'Sedang 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.', + 'Maximum size: ' => 'Ukuran maksimum: ', + 'Display another project' => 'Lihat projek lain', + 'Created by %s' => 'Dibuat oleh %s', + 'Tasks Export' => 'Ekspor Tugas', + 'Start Date' => 'Tanggal Mulai', + 'Execute' => 'Eksekusi', + 'Task Id' => 'Id Tugas', + 'Creator' => 'Pembuat', + 'Modification date' => 'Tanggal modifikasi', + 'Completion date' => 'Tanggal penyelesaian', + 'Clone' => 'Klon', + 'Project cloned successfully.' => 'Kloning projek berhasil.', + 'Unable to clone this project.' => 'Tidak dapat mengkloning projek.', + 'Enable email notifications' => 'Aktifkan pemberitahuan dari email', + 'Task position:' => 'Posisi tugas :', + 'The task #%d has been opened.' => 'Tugas #%d telah dibuka.', + 'The task #%d has been closed.' => 'Tugas #%d telah ditutup.', + 'Sub-task updated' => 'Sub-tugas diperbaharui', + '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 comment' => 'Komentar baru', + 'Comment updated' => 'Komentar diperbaharui', + 'New subtask' => 'Sub-tugas baru', + 'I only want to receive notifications for these projects:' => 'Saya ingin menerima pemberitahuan hanya untuk projek-projek yang dipilih :', + 'view the task on Kanboard' => 'lihat tugas di Kanboard', + 'Public access' => 'Akses awam', + 'Disable public access' => 'Nyahaktifkan akses awam', + 'Enable public access' => 'Aktifkan akses awam', + 'Public access disabled' => 'Akses awam dinyahaktif', + 'Move the task to another project' => 'Pindahkan tugas ke projek lain', + 'Move to project' => 'Pindahkan ke projek lain', + 'Do you really want to duplicate this task?' => 'Anda yakin mengembarkan tugas ini ?', + 'Duplicate a task' => 'Kembarkan tugas', + 'External accounts' => 'Akaun luaran', + 'Account type' => 'Jenis Akaun', + 'Local' => 'Lokal', + 'Remote' => 'Jauh', + 'Enabled' => 'Aktif', + 'Disabled' => 'Tidak aktif', + 'Login:' => 'Nama pengguna :', + 'Full Name:' => 'Nama:', + 'Email:' => 'Emel:', + 'Notifications:' => 'Makluman:', + 'Notifications' => 'Makluman', + 'Account type:' => 'Jenis Akaun :', + 'Edit profile' => 'Sunting profil', + 'Change password' => 'Rubah kata sandri', + 'Password modification' => 'Modifikasi kata laluan', + 'External authentications' => 'Otentifikasi eksternal', + 'Never connected.' => 'Tidak pernah terhubung.', + 'No external authentication enabled.' => 'Tidak ada otentifikasi eksternal yang aktif.', + 'Password modified successfully.' => 'Kata laluan telah berjaya ditukar.', + 'Unable to change the password.' => 'Tidak dapat merubah kata laluanr.', + 'Change category' => 'Tukar kategori', + '%s updated the task %s' => '%s memperbaharui 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 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', + 'Not assigned, estimate of %sh' => 'Tiada yang ditugaskan, perkiraan %sh', + '%s updated a comment on the task %s' => '%s memperbaharui 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 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 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', + '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', + 'Reference' => 'Referensi', + 'Label' => 'Label', + 'Database' => 'Pengkalan data', + 'About' => 'Tentang', + 'Database driver:' => 'Driver pengkalan data:', + 'Board settings' => 'Pengaturan papan', + 'Webhook settings' => 'Penetapan webhook', + 'Reset token' => 'Menetap semula token', + 'API endpoint:' => 'API endpoint :', + 'Refresh interval for personal 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 saat)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekuensi dalam detik (0 untuk menonaktifkan fitur ini, standar 10 detik)', + 'Application URL' => 'URL Aplikasi', + 'Token regenerated.' => 'Token diregenerasi.', + 'Date format' => 'Format tarikh', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO selalunya diterima, contoh: « %s » et « %s »', + 'New personal project' => 'Projek peribadi baharu', + 'This project is personal' => 'projek ini adalah peribadi', + 'Add' => 'Tambah', + 'Start date' => 'Tarikh mula', + 'Time estimated' => 'Anggaran masa', + 'There is nothing assigned to you.' => 'Tidak ada yang diberikan kepada anda.', + 'My tasks' => 'Tugas saya', + 'Activity stream' => 'Arus aktifitas', + 'Dashboard' => 'Dasbor', + 'Confirmation' => 'Konfirmasi', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Buat komentar dari pemasok eksternal', + 'Project management' => 'Manajemen projek', + 'Columns' => 'Kolom', + 'Task' => 'Tugas', + 'Percentage' => 'Persentasi', + 'Number of tasks' => 'Jumlah dari tugas', + 'Task distribution' => 'Pembagian tugas', + 'Analytics' => 'Analitis', + 'Subtask' => 'Subtugas', + 'User repartition' => 'Partisi ulang pengguna', + '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.', + 'Previous' => 'Sebelumnya', + 'The id must be an integer' => 'Id harus integer', + 'The project id must be an integer' => 'Id projek 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', + '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', + 'Daily project summary' => 'Ringkasan projek harian', + 'Daily project summary export' => 'Ekspot ringkasan projek harian', + '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', + 'Add a new swimlane' => 'Tambah swimlane baharu', + 'Default swimlane' => 'Piawai swimlane', + 'Do you really want to remove this swimlane: "%s"?' => 'Anda yakin untuk menghapus swimlane ini : « %s » ?', + 'Inactive swimlanes' => 'Swimlanes tidak aktif', + 'Remove a swimlane' => 'Padam swimlane', + 'Swimlane modification for the project "%s"' => 'Modifikasi swimlane untuk projek « %s »', + 'Swimlane removed successfully.' => 'Swimlane telah dipadamkan.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane telah dikemaskini.', + 'Unable to remove this swimlane.' => 'Tidak dapat menghapus swimlane ini.', + 'Unable to update this swimlane.' => 'Tidak dapat memperbaharui swimlane ini.', + 'Your swimlane has been created successfully.' => 'Swimlane anda berhasil dibuat.', + 'Example: "Bug, Feature Request, Improvement"' => 'Contoh: « Insiden, Permintaan Ciri, Pembaikan »', + 'Default categories for new projects (Comma-separated)' => 'Piawaian kategori untuk projek baru (asingkan guna koma)', + 'Integrations' => 'Integrasi', + 'Integration with third-party services' => 'Integrasi dengan khidmat pihak ketiga', + 'Subtask Id' => 'Id Subtugas', + 'Subtasks' => 'Subtugas', + 'Subtasks Export' => 'Ekspot Subtugas', + 'Task Title' => 'Judul Tugas', + 'Untitled' => 'Tanpa nama', + 'Application default' => 'Aplikasi Piawaian', + 'Language:' => 'Bahasa:', + 'Timezone:' => 'Zon masa:', + 'All columns' => 'Semua kolom', + 'Next' => 'Selanjutnya', + '#%d' => 'n°%d', + 'All swimlanes' => 'Semua swimlane', + 'All colors' => 'Semua warna', + 'Moved to column %s' => 'Pindah ke kolom %s', + 'User dashboard' => 'Papan Kenyataan 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 projek mana yang ingin anda duplikasi?', + 'Disallow login form' => 'Larang formulir masuk', + 'Start' => 'Mula', + 'End' => 'Selesai', + 'Task age in days' => 'Usia tugas dalam bentuk harian', + 'Days in this column' => 'Hari dalam kolom ini', + '%dd' => '%dj', + 'Add a new link' => 'Tambah Pautan baru', + 'Do you really want to remove this link: "%s"?' => 'Anda yakin akan menghapus Pautan ini : « %s » ?', + 'Do you really want to remove this link with task #%d?' => 'Anda yakin akan menghapus Pautan ini dengan tugas n°%d ?', + 'Field required' => 'Medan diperlukan', + 'Link added successfully.' => 'Pautan berhasil ditambahkan.', + 'Link updated successfully.' => 'Pautan berhasil diperbaharui.', + 'Link removed successfully.' => 'Pautan berhasil dihapus.', + 'Link labels' => 'Label Pautan', + 'Link modification' => 'Modifikasi Pautan', + 'Opposite label' => 'Label berlawanan', + 'Remove a link' => 'Hapus Pautan', + 'The labels must be different' => 'Label harus berbeda', + 'There is no link.' => 'Tidak ada Pautan.', + 'This label must be unique' => 'Label ini harus unik', + 'Unable to create your link.' => 'Tidak dapat membuat Pautan anda.', + 'Unable to update your link.' => 'Tidak dapat memperbaharui Pautan anda.', + 'Unable to remove this link.' => 'Tidak dapat menghapus Pautan ini.', + 'relates to' => 'berhubungan dengan', + 'blocks' => 'blok', + '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', + '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', + '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', + 'Application' => 'Aplikasi', + 'Compact view' => 'Tampilan kompak', + 'Horizontal scrolling' => 'Horisontal bergulir', + 'Compact/wide view' => 'Beralih antara tampilan kompak dan diperluas', + 'Currency' => 'Mata uang', + 'Personal project' => 'projek pribadi', + 'AUD - Australian Dollar' => 'AUD - Dollar Australia', + 'CAD - Canadian Dollar' => 'CAD - Dollar Kanada', + 'CHF - Swiss Francs' => 'CHF - Swiss Prancis', + 'Custom Stylesheet' => 'Kustomisasi Stylesheet', + 'EUR - Euro' => 'EUR - Euro', + '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', + 'PEN - Peruvian Sol' => 'PEN - Sol Peru', + 'RSD - Serbian dinar' => 'RSD - Dinar Serbia', + 'CNY - Chinese Yuan' => 'CNY - Yuan China', + 'USD - US Dollar' => 'USD - Dollar Amerika', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar Venezuela', + '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', + 'Source column' => 'Sumber kolom', + 'Transitions' => 'Transisi', + 'Executer' => 'Eksekusi', + 'Time spent in the column' => 'Waktu yang dihabiskan dalam kolom', + 'Task transitions' => 'Transisi tugas', + 'Task transitions export' => 'Ekspor transisi tugas', + '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', + 'Reference currency' => 'Referensi mata uang', + 'The currency rate has 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 removed the assignee of the task %s' => '%s menghapus penugasan dari tugas %s', + '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.', + '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', + '%s via Kanboard' => '%s via Kanboard', + '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.' => 'Ambil tangkapan skrin dan tekan CTRL+V atau ⌘+V untuk tampal 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 Pautan', + 'Start to type task title...' => 'Mulai mengetik judul tugas...', + 'A task cannot be linked to itself' => 'Sebuah tugas tidak dapat dikaitkan dengan dirinya sendiri', + 'The exact same link already exists' => 'Pautan yang sama persis sudah ada', + 'Recurrent task is scheduled to be generated' => 'Tugas berulang dijadwalkan akan dihasilkan', + 'Score' => 'Skor', + 'The identifier must be unique' => 'Identifier harus unik', + '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', + '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: ', + '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', + 'This task has been created by: ' => 'Tugas ini telah dibuat oleh:', + 'Recurrent task has been generated:' => 'Tugas berulang telah dihasilkan:', + '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', + 'Year(s)' => 'Tahun', + 'Project settings' => 'Pengaturan projek', + 'Automatically update the start date' => 'Otomatikkan pengemaskinian tanggal', + 'iCal feed' => 'iCal feed', + 'Preferences' => 'Keutamaan', + 'Security' => 'Keamanan', + 'Two factor authentication disabled' => 'Otentifikasi dua faktor dimatikan', + 'Two factor authentication enabled' => 'Otentifikasi dua faktor dihidupkan', + 'Unable to update this user.' => 'Tidak dapat memperbarui pengguna ini.', + 'There is no user management for personal projects.' => 'Tidak ada manajemen pengguna untuk projek-projek pribadi.', + 'User that will receive the email' => 'Pengguna yang akan menerima email', + 'Email subject' => 'Subjek Emel', + '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', + 'Notification' => 'Pemberitahuan', + '%s moved the task #%d to the first swimlane' => '%s memindahkan tugas n°%d ke swimlane pertama', + 'Swimlane' => 'Swimlane', + '%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.', + '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 projek untuk « %s »', + 'view the board on Kanboard' => 'lihat papan di Kanboard', + 'The task has been moved to the first swimlane' => 'Tugas telah dipindahkan ke swimlane pertama', + 'The task has been moved to another swimlane:' => 'Tugas telah dipindahkan ke swimlane lain:', + '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', + 'The due date has been removed' => 'Tanggal jatuh tempo telah dihapus', + 'There is no description anymore' => 'Tidak ada deskripsi lagi', + 'Recurrence settings has 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" has been updated' => 'Field « %s » telah diperbaharui', + '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 :', + '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', + 'Only for tasks created by me and tasks assigned to me' => 'Hanya untuk tugas yang dibuat oleh saya dan ditugaskan ke saya', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Total untuk semua kolom', + 'You need at least 2 days of data to show the chart.' => 'Anda memerlukan setidaknya 2 hari dari data yang menunjukkan grafik.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Hentikan timer', + 'Start timer' => 'Mulai timer', + 'My activity stream' => 'Aliran kegiatan saya', + 'Search tasks' => 'Cari tugas', + 'Reset filters' => 'Reset ulang filter', + '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', + 'Not assigned' => 'Tidak ditugaskan', + 'View advanced search syntax' => 'Lihat sintaks pencarian lanjutan', + 'Overview' => 'Ikhtisar', + 'Board/Calendar/List view' => 'Tampilan Papan/Kalender/Daftar', + 'Switch to the board view' => 'Beralih ke tampilan papan', + '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.', + 'No tasks found.' => 'Tidak ada tugas yang ditemukan.', + 'Keyboard shortcut: "%s"' => 'Keyboard shortcut : « %s »', + 'List' => 'Daftar', + 'Filter' => 'Filter', + 'Advanced search' => 'Pencarian lanjutan', + 'Example of query: ' => 'Contoh dari query : ', + 'Search by project: ' => 'Pencarian berdasarkan projek : ', + '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 : ', + 'Average time spent in each column' => 'Rata-rata waktu yang dihabiskan dalam setiap kolom', + 'Average time spent' => 'Rata-rata waktu yang dihabiskan', + 'This chart shows the average time spent in 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', + 'Lead Time' => 'Lead Time', + 'This chart shows 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.', + '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 : ', + 'Time spent in 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.', + 'Set the start date automatically' => 'Secara otomatis mengatur tanggal mulai', + 'Edit Authentication' => 'Modifikasi 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 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.', + '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.', + 'Trigger automatically subtask time tracking' => 'Picu pengesanan subtugas secara otomatik', + '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', + 'no category' => 'tiada kategori', + 'Current assignee: %s' => 'Saat ini ditugaskan pada: %s', + 'not assigned' => 'Belum ditugaskan', + 'Author:' => 'Penulis:', + 'contributors' => 'Penggiat', + 'License:' => 'Lesen:', + 'License' => 'Lesen', + 'Enter the text below' => 'Masukkan teks di bawah', + 'Start date:' => 'Tanggal mulai:', + 'Due date:' => 'Batas waktu:', + 'People who are project managers' => 'Orang-orang yang menjadi pengurus projek', + 'People who are project members' => 'Orang-orang yang menjadi anggota projek', + 'NOK - Norwegian Krone' => 'NOK - Krone Norwegia', + 'Show this column' => 'Perlihatkan kolom ini', + 'Hide this column' => 'Sembunyikan kolom ini', + 'End date' => 'Waktu berakhir', + 'Users overview' => 'Ikhtisar pengguna', + 'Members' => 'Anggota', + 'Shared project' => 'projek bersama', + 'Project managers' => 'Pengurus projek', + 'Projects list' => 'Senarai projek', + 'End date:' => 'Waktu berakhir :', + 'Change task color when using a specific task link' => 'Rubah warna tugas ketika menggunakan Pautan tugas yang spesifik', + 'Task link creation or modification' => 'Pautan tugas pada penciptaan atau penyuntingan', + 'Milestone' => 'Batu Tanda', + 'Reset the search/filter box' => 'Tetap semula pencarian/saringan', + 'Documentation' => 'Dokumentasi', + 'Author' => 'Pengarang', + 'Version' => 'Versi', + 'Plugins' => 'Plugin', + 'There is no plugin loaded.' => 'Tiada plugin yang dimuat.', + 'My notifications' => 'Notifikasi saya', + 'Custom filters' => 'Penapis kustom', + 'Your custom filter has been created successfully.' => 'Penapis kustom anda telah berjaya dicipta.', + 'Unable to create your custom filter.' => 'Tidak dapat mencipta penapis kustom anda.', + 'Custom filter removed successfully.' => 'Penapis kustom berjaya dibuang.', + 'Unable to remove this custom filter.' => 'Tidak dapat membuang penapis kustom ini.', + 'Edit custom filter' => 'Edit penapis kustom', + 'Your custom filter has been updated successfully.' => 'Penapis kustom anda telah berjaya dikemas kini.', + 'Unable to update custom filter.' => 'Tidak dapat mengemaskini penapis kustom.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Lampiran baru pada tugas #%d: %s', + 'New comment on task #%d' => 'Komen baru pada tugas #%d', + 'Comment updated on task #%d' => 'Komen dikemas kini pada tugas #%d', + 'New subtask on task #%d' => 'Subtugas baru pada tugas #%d', + 'Subtask updated on task #%d' => 'Subtugas dikemas kini pada tugas #%d', + 'New task #%d: %s' => 'Tugas baru #%d: %s', + 'Task updated #%d' => 'Tugas dikemas kini #%d', + 'Task #%d closed' => 'Tugas #%d ditutup', + 'Task #%d opened' => 'Tugas #%d dibuka', + 'Column changed for task #%d' => 'Lajur diubah untuk tugas #%d', + 'New position for task #%d' => 'Kedudukan baru untuk tugas #%d', + 'Swimlane changed for task #%d' => 'Swimlane diubah untuk tugas #%d', + 'Assignee changed on task #%d' => 'Penerima tugas diubah pada tugas #%d', + '%d overdue tasks' => '%d tugas tertunggak', + 'No notification.' => 'Tiada notifikasi.', + 'Mark all as read' => 'Tandakan semua sebagai dibaca', + 'Mark as read' => 'Tandakan sebagai dibaca', + 'Total number of tasks in this column across all swimlanes' => 'Jumlah tugas dalam lajur ini merentasi semua swimlane', + 'Collapse swimlane' => 'Lipat swimlane', + 'Expand swimlane' => 'Kembangkan swimlane', + 'Add a new filter' => 'Tambah penapis baru', + 'Share with all project members' => 'Kongsi dengan semua ahli projek', + 'Shared' => 'Dikongsi', + 'Owner' => 'Pemilik', + 'Unread notifications' => 'Notifikasi belum dibaca', + 'Notification methods:' => 'Kaedah notifikasi:', + 'Unable to read your file' => 'Tidak dapat membaca fail anda', + '%d task(s) have been imported successfully.' => '%d tugas telah berjaya diimport.', + 'Nothing has been imported!' => 'Tiada yang diimport!', + 'Import users from CSV file' => 'Import pengguna dari fail CSV', + '%d user(s) have been imported successfully.' => '%d pengguna telah berjaya diimport.', + 'Comma' => 'Koma', + 'Semi-colon' => 'Koma bertitik', + 'Tab' => 'Tab', + 'Vertical bar' => 'Bar menegak', + 'Double Quote' => 'Tanda Petik Ganda', + 'Single Quote' => 'Tanda Petik Tunggal', + '%s attached a file to the task #%d' => '%s melampirkan fail ke tugas #%d', + 'There is no column or swimlane activated in your project!' => 'Tiada lajur atau swimlane yang diaktifkan dalam projek anda!', + 'Append filter (instead of replacement)' => 'Tambah penapis (bukannya penggantian)', + 'Append/Replace' => 'Tambah/Ganti', + 'Append' => 'Tambah', + 'Replace' => 'Ganti', + 'Import' => 'Import', + 'Change sorting' => 'Ubah susunan', + 'Tasks Importation' => 'Pengimportan Tugas', + 'Delimiter' => 'Pembatas', + 'Enclosure' => 'Penutup', + 'CSV File' => 'Fail CSV', + 'Instructions' => 'Arahan', + 'Your file must use the predefined CSV format' => 'Fail anda mesti menggunakan format CSV yang telah ditentukan', + 'Your file must be encoded in UTF-8' => 'Fail anda mesti dikodkan dalam UTF-8', + 'The first row must be the header' => 'Baris pertama mestilah tajuk', + 'Duplicates are not verified for you' => 'Pendua tidak disahkan untuk anda', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Tarikh tamat tempoh mesti menggunakan format ISO: YYYY-MM-DD', + 'Download CSV template' => 'Muat turun templat CSV', + 'No external integration registered.' => 'Tiada integrasi luaran yang berdaftar.', + 'Duplicates are not imported' => 'Pendua tidak diimport', + 'Usernames must be lowercase and unique' => 'Nama pengguna mestilah huruf kecil dan unik', + 'Passwords will be encrypted if present' => 'Kata laluan akan disulitkan jika ada', + '%s attached a new file to the task %s' => '%s melampirkan fail baru ke tugas %s', + 'Link type' => 'Jenis pautan', + 'Assign automatically a category based on a link' => 'Tetapkan kategori secara automatik berdasarkan pautan', + 'BAM - Konvertible Mark' => 'BAM - Mark Boleh Tukar', + 'Assignee Username' => 'Nama Pengguna Penerima Tugas', + 'Assignee Name' => 'Nama Penerima Tugas', + 'Groups' => 'Kumpulan', + 'Members of %s' => 'Ahli %s', + 'New group' => 'Kumpulan baru', + 'Group created successfully.' => 'Kumpulan berjaya dicipta.', + 'Unable to create your group.' => 'Tidak dapat mencipta kumpulan anda.', + 'Edit group' => 'Edit kumpulan', + 'Group updated successfully.' => 'Kumpulan berjaya dikemas kini.', + 'Unable to update your group.' => 'Tidak dapat mengemaskini kumpulan anda.', + 'Add group member to "%s"' => 'Tambah ahli kumpulan ke "%s"', + 'Group member added successfully.' => 'Ahli kumpulan berjaya ditambah.', + 'Unable to add group member.' => 'Tidak dapat menambah ahli kumpulan.', + 'Remove user from group "%s"' => 'Buang pengguna dari kumpulan "%s"', + 'User removed successfully from this group.' => 'Pengguna berjaya dibuang dari kumpulan ini.', + 'Unable to remove this user from the group.' => 'Tidak dapat membuang pengguna ini dari kumpulan.', + 'Remove group' => 'Buang kumpulan', + 'Group removed successfully.' => 'Kumpulan berjaya dibuang.', + 'Unable to remove this group.' => 'Tidak dapat membuang kumpulan ini.', + 'Project Permissions' => 'Kebenaran Projek', + 'Manager' => 'Pengurus', + 'Project Manager' => 'Pengurus Projek', + 'Project Member' => 'Ahli Projek', + 'Project Viewer' => 'Penonton Projek', + 'Your account is locked for %d minutes' => 'Akaun anda dikunci selama %d minit', + 'Invalid captcha' => 'Captcha tidak sah', + 'The name must be unique' => 'Nama mestilah unik', + 'View all groups' => 'Lihat semua kumpulan', + 'There is no user available.' => 'Tiada pengguna yang tersedia.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Adakah anda benar-benar mahu membuang pengguna "%s" dari kumpulan "%s"?', + 'There is no group.' => 'Tiada kumpulan.', + 'Add group member' => 'Tambah ahli kumpulan', + 'Do you really want to remove this group: "%s"?' => 'Adakah anda benar-benar mahu membuang kumpulan ini: "%s"?', + 'There is no user in this group.' => 'Tiada pengguna dalam kumpulan ini.', + 'Permissions' => 'Kebenaran', + 'Allowed Users' => 'Pengguna Dibenarkan', + 'No specific user has been allowed.' => 'Tiada pengguna tertentu yang dibenarkan.', + 'Role' => 'Peranan', + 'Enter user name...' => 'Masukkan nama pengguna...', + 'Allowed Groups' => 'Kumpulan Dibenarkan', + 'No group has been allowed.' => 'Tiada kumpulan yang dibenarkan.', + 'Group' => 'Kumpulan', + 'Group Name' => 'Nama Kumpulan', + 'Enter group name...' => 'Masukkan nama kumpulan...', + 'Role:' => 'Peranan', + 'Project members' => 'Anggota projek', + '%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 komen pada tugas #%d', + 'You were mentioned in the task #%d' => 'Anda disebut dalam tugas #%d', + 'You were mentioned in a comment on the task #%d' => 'Anda disebut dalam komen pada tugas #%d', + 'Estimated hours: ' => 'Anggaran jam: ', + 'Actual hours: ' => 'Jam sebenar: ', + 'Hours Spent' => 'Jam Dihabiskan', + 'Hours Estimated' => 'Jam Dianggarkan', + 'Estimated Time' => 'Masa Dianggarkan', + 'Actual Time' => 'Masa Sebenar', + 'Estimated vs actual time' => 'Masa anggaran vs sebenar', + 'RUB - Russian Ruble' => 'RUB - Rubel Rusia', + 'Assign the task to the person who does the action when the column is changed' => 'Tetapkan tugas kepada orang yang melakukan tindakan apabila lajur diubah', + 'Close a task in a specific column' => 'Tutup tugas dalam lajur tertentu', + 'Time-based One-time Password Algorithm' => 'Algoritma Kata Laluan Satu Kali Berasaskan Masa', + 'Two-Factor Provider: ' => 'Pembekal Dua Faktor: ', + 'Disable two-factor authentication' => 'Nyahaktifkan pengesahan dua faktor', + 'Enable two-factor authentication' => 'Aktifkan pengesahan dua faktor', + 'There is no integration registered at the moment.' => 'Tiada integrasi yang berdaftar pada masa ini.', + 'Password Reset for Kanboard' => 'Tetapan Semula Kata Laluan untuk Kanboard', + 'Forgot password?' => 'Lupa kata laluan?', + 'Enable "Forget Password"' => 'Aktifkan "Lupa Kata Laluan"', + 'Password Reset' => 'Tetapan Semula Kata Laluan', + 'New password' => 'Kata laluan baru', + 'Change Password' => 'Tukar Kata Laluan', + 'To reset your password click on this link:' => 'Untuk menetapkan semula kata laluan anda klik pada pautan ini:', + 'Last Password Reset' => 'Tetapan Semula Kata Laluan Terakhir', + 'The password has never been reinitialized.' => 'Kata laluan tidak pernah ditetapkan semula.', + 'Creation' => 'Ciptaan', + 'Expiration' => 'Jangka hayat', + 'Password reset history' => 'Sirah tetap semula kata laluan', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Semua tugas dalam lajur "%s" dan swimlane "%s" telah berjaya ditutup.', + 'Do you really want to close all tasks of this column?' => 'Adakah anda benar-benar mahu menutup semua tugas dalam lajur ini?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d tugas dalam lajur "%s" dan swimlane "%s" akan ditutup.', + 'Close all tasks in this column and this swimlane' => 'Tutup semua tugas dalam lajur ini dan swimlane ini', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Tiada plugin yang mendaftarkan kaedah notifikasi projek. Anda masih boleh mengkonfigurasi notifikasi individu dalam profil pengguna anda.', + 'My dashboard' => 'Papan pemuka saya', + 'My profile' => 'Profil saya', + 'Project owner: ' => 'Pemilik projek: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Pengecam projek adalah pilihan dan mesti alfanumerik, contoh: PROJEKSAYA.', + 'Project owner' => 'Pemilik projek', + 'Personal projects do not have users and groups management.' => 'Projek peribadi tidak mempunyai pengurusan pengguna dan kumpulan.', + 'There is no project member.' => 'Tiada ahli projek.', + 'Priority' => 'Keutamaan', + 'Task priority' => 'Keutamaan tugas', + 'General' => 'Umum', + 'Dates' => 'Tarikh', + 'Default priority' => 'Keutamaan lalai', + 'Lowest priority' => 'Keutamaan terendah', + 'Highest priority' => 'Keutamaan tertinggi', + 'Close a task when there is no activity' => 'Tutup tugas apabila tiada aktiviti', + 'Duration in days' => 'Tempoh dalam hari', + 'Send email when there is no activity on a task' => 'Hantar e-mel apabila tiada aktiviti pada tugas', + 'Unable to fetch link information.' => 'Tidak dapat mengambil maklumat pautan.', + 'Daily background job for tasks' => 'Tugasan latar belakang harian untuk tugas', + 'Auto' => 'Auto', + 'Related' => 'Berkaitan', + 'Attachment' => 'Lampiran', + 'Web Link' => 'Pautan Web', + 'External links' => 'Pautan luaran', + 'Add external link' => 'Tambah pautan luaran', + 'Type' => 'Jenis', + 'Dependency' => 'Kebergantungan', + 'Add internal link' => 'Tambah pautan dalaman', + 'Add a new external link' => 'Tambah pautan luaran baru', + 'Edit external link' => 'Edit pautan luaran', + 'External link' => 'Pautan luaran', + 'Copy and paste your link here...' => 'Salin dan tampal pautan anda di sini...', + 'URL' => 'URL', + 'Internal links' => 'Pautan dalaman', + 'Assign to me' => 'Tetapkan kepada saya', + 'Me' => 'Saya', + 'Do not duplicate anything' => 'Jangan pendua apa-apa', + 'Projects management' => 'Pengurusan projek', + 'Users management' => 'Pengurusan pengguna', + 'Groups management' => 'Pengurusan kumpulan', + 'Create from another project' => 'Cipta dari projek lain', + 'open' => 'terbuka', + 'closed' => 'ditutup', + 'Priority:' => 'Keutamaan:', + 'Reference:' => 'Rujukan:', + 'Complexity:' => 'Kerumitan:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Lajur:', + 'Position:' => 'Kedudukan:', + 'Creator:' => 'Pencipta:', + 'Time estimated:' => 'Masa dianggarkan:', + '%s hours' => '%s jam', + 'Time spent:' => 'Masa dihabiskan:', + 'Created:' => 'Dicipta:', + 'Modified:' => 'Diubah suai:', + 'Completed:' => 'Selesai:', + 'Started:' => 'Dimulakan:', + 'Moved:' => 'Dipindahkan:', + 'Task #%d' => 'Tugas #%d', + 'Time format' => 'Format masa', + 'Start date: ' => 'Tarikh mula: ', + 'End date: ' => 'Tarikh tamat: ', + 'New due date: ' => 'Tarikh tamat tempoh baru: ', + 'Start date changed: ' => 'Tarikh mula diubah: ', + 'Disable personal projects' => 'Nyahaktifkan projek peribadi', + 'Do you really want to remove this custom filter: "%s"?' => 'Adakah anda benar-benar mahu membuang penapis kustom ini: "%s"?', + 'Remove a custom filter' => 'Buang penapis kustom', + 'User activated successfully.' => 'Pengguna berjaya diaktifkan.', + 'Unable to enable this user.' => 'Tidak dapat mengaktifkan pengguna ini.', + 'User disabled successfully.' => 'Pengguna berjaya dinyahaktifkan.', + 'Unable to disable this user.' => 'Tidak dapat menyahaktifkan pengguna ini.', + 'All files have been uploaded successfully.' => 'Semua fail telah berjaya dimuat naik.', + 'The maximum allowed file size is %sB.' => 'Saiz fail maksimum yang dibenarkan ialah %sB.', + 'Drag and drop your files here' => 'Seret dan lepaskan fail anda di sini', + 'choose files' => 'pilih fail', + 'View profile' => 'Lihat profil', + 'Two Factor' => 'Dua Faktor', + 'Disable user' => 'Nyahaktifkan pengguna', + 'Do you really want to disable this user: "%s"?' => 'Adakah anda benar-benar mahu menyahaktifkan pengguna ini: "%s"?', + 'Enable user' => 'Aktifkan pengguna', + 'Do you really want to enable this user: "%s"?' => 'Adakah anda benar-benar mahu mengaktifkan pengguna ini: "%s"?', + 'Download' => 'Muat turun', + 'Uploaded: %s' => 'Dimuat naik: %s', + 'Size: %s' => 'Saiz: %s', + 'Uploaded by %s' => 'Dimuat naik oleh %s', + 'Filename' => 'Nama fail', + 'Size' => 'Saiz', + 'Column created successfully.' => 'Lajur berjaya dicipta.', + 'Another column with the same name exists in the project' => 'Lajur lain dengan nama yang sama wujud dalam projek', + 'Default filters' => 'Penapis lalai', + 'Your board doesn\'t have any columns!' => 'Papan anda tidak mempunyai sebarang lajur!', + 'Change column position' => 'Ubah kedudukan lajur', + 'Switch to the project overview' => 'Beralih ke gambaran keseluruhan projek', + 'User filters' => 'Penapis pengguna', + 'Category filters' => 'Penapis kategori', + 'Upload a file' => 'Muat naik fail', + 'View file' => 'Lihat fail', + 'Last activity' => 'Aktiviti terakhir', + 'Change subtask position' => 'Ubah kedudukan subtugas', + 'This value must be greater than %d' => 'Nilai ini mestilah lebih besar daripada %d', + 'Another swimlane with the same name exists in the project' => 'Swimlane lain dengan nama yang sama wujud dalam projek', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Contoh: https://example.kanboard.org/ (digunakan untuk menjana URL mutlak)', + 'Actions duplicated successfully.' => 'Tindakan berjaya diduplikat.', + 'Unable to duplicate actions.' => 'Tidak dapat menduplikat tindakan.', + 'Add a new action' => 'Tambah tindakan baru', + 'Import from another project' => 'Import dari projek lain', + 'There is no action at the moment.' => 'Tiada tindakan pada masa ini.', + 'Import actions from another project' => 'Import tindakan dari projek lain', + 'There is no available project.' => 'Tiada projek yang tersedia.', + 'Local File' => 'Fail Tempatan', + 'Configuration' => 'Konfigurasi', + 'PHP version:' => 'Versi PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versi OS:', + 'Database version:' => 'Versi pangkalan data:', + 'Browser:' => 'Pelayar:', + 'Task view' => 'Paparan tugas', + 'Edit task' => 'Edit tugas', + 'Edit description' => 'Edit penerangan', + 'New internal link' => 'Pautan dalaman baru', + 'Display list of keyboard shortcuts' => 'Papar senarai pintasan papan kekunci', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Muat naik imej avatar saya', + 'Remove my image' => 'Buang imej saya', + 'The OAuth2 state parameter is invalid' => 'Parameter keadaan OAuth2 tidak sah', + 'User not found.' => 'Pengguna tidak dijumpai.', + 'Search in activity stream' => 'Cari dalam aliran aktiviti', + 'My activities' => 'Aktiviti saya', + 'Activity until yesterday' => 'Aktiviti sehingga semalam', + 'Activity until today' => 'Aktiviti sehingga hari ini', + 'Search by creator: ' => 'Cari mengikut pencipta: ', + 'Search by creation date: ' => 'Cari mengikut tarikh cipta: ', + 'Search by task status: ' => 'Cari mengikut status tugas: ', + 'Search by task title: ' => 'Cari mengikut tajuk tugas: ', + 'Activity stream search' => 'Carian aliran aktiviti', + 'Projects where "%s" is manager' => 'Projek di mana "%s" adalah pengurus', + 'Projects where "%s" is member' => 'Projek di mana "%s" adalah ahli', + 'Open tasks assigned to "%s"' => 'Tugas terbuka yang ditetapkan kepada "%s"', + 'Closed tasks assigned to "%s"' => 'Tugas tertutup yang ditetapkan kepada "%s"', + 'Assign automatically a color based on a priority' => 'Tetapkan warna secara automatik berdasarkan keutamaan', + 'Overdue tasks for the project(s) "%s"' => 'Tugas terlambat untuk projek « %s »', + 'Upload files' => 'Muat naik fail', + 'Installed Plugins' => 'Plugin yang Dipasang', + 'Plugin Directory' => 'Direktori Plugin', + 'Plugin installed successfully.' => 'Plugin berjaya dipasang.', + 'Plugin updated successfully.' => 'Plugin berjaya dikemas kini.', + 'Plugin removed successfully.' => 'Plugin berjaya dibuang.', + 'Subtask converted to task successfully.' => 'Subtugas berjaya ditukar kepada tugas.', + 'Unable to convert the subtask.' => 'Tidak dapat menukar subtugas.', + 'Unable to extract plugin archive.' => 'Tidak dapat mengekstrak arkib plugin.', + 'Plugin not found.' => 'Plugin tidak dijumpai.', + 'You don\'t have the permission to remove this plugin.' => 'Anda tidak mempunyai kebenaran untuk membuang plugin ini.', + 'Unable to download plugin archive.' => 'Tidak dapat memuat turun arkib plugin.', + 'Unable to write temporary file for plugin.' => 'Tidak dapat menulis fail sementara untuk plugin.', + 'Unable to open plugin archive.' => 'Tidak dapat membuka arkib plugin.', + 'There is no file in the plugin archive.' => 'Tiada fail dalam arkib plugin.', + 'Create tasks in bulk' => 'Cipta tugas secara pukal', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Instans Kanboard anda tidak dikonfigurasi untuk memasang plugin dari antara muka pengguna.', + 'There is no plugin available.' => 'Tiada plugin yang tersedia.', + 'Install' => 'Pasang', + 'Update' => 'Kemas kini', + 'Up to date' => 'Terkini', + 'Not available' => 'Tidak tersedia', + 'Remove plugin' => 'Buang plugin', + 'Do you really want to remove this plugin: "%s"?' => 'Adakah anda benar-benar mahu membuang plugin ini: "%s"?', + 'Uninstall' => 'Nyahpasang', + 'Listing' => 'Penyenaraian', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Urus projek', + 'Convert to task' => 'Tukar kepada tugas', + 'Convert sub-task to task' => 'Tukar sub-tugas kepada tugas', + 'Do you really want to convert this sub-task to a task?' => 'Adakah anda benar-benar mahu menukar sub-tugas ini kepada tugas?', + 'My task title' => 'Tajuk tugas saya', + 'Enter one task by line.' => 'Masukkan satu tugas setiap baris.', + 'Number of failed login:' => 'Bilangan log masuk gagal:', + 'Account locked until:' => 'Akaun dikunci sehingga:', + 'Email settings' => 'Tetapan e-mel', + 'Email sender address' => 'Alamat penghantar e-mel', + 'Email transport' => 'Pengangkutan e-mel', + 'Webhook token' => 'Token webhook', + 'Project tags management' => 'Pengurusan tag projek', + 'Tag created successfully.' => 'Tag berjaya dicipta.', + 'Unable to create this tag.' => 'Tidak dapat mencipta tag ini.', + 'Tag updated successfully.' => 'Tag berjaya dikemas kini.', + 'Unable to update this tag.' => 'Tidak dapat mengemaskini tag ini.', + 'Tag removed successfully.' => 'Tag berjaya dibuang.', + 'Unable to remove this tag.' => 'Tidak dapat membuang tag ini.', + 'Global tags management' => 'Pengurusan tag global', + 'Tags' => 'Tag', + 'Tags management' => 'Pengurusan tag', + 'Add new tag' => 'Tambah tag baru', + 'Edit a tag' => 'Edit tag', + 'Project tags' => 'Tag projek', + 'There is no specific tag for this project at the moment.' => 'Tiada tag khusus untuk projek ini pada masa ini.', + 'Tag' => 'Tag', + 'Remove a tag' => 'Buang tag', + 'Do you really want to remove this tag: "%s"?' => 'Adakah anda benar-benar mahu membuang tag ini: "%s"?', + 'Global tags' => 'Tag global', + 'There is no global tag at the moment.' => 'Tiada tag global pada masa ini.', + 'This field cannot be empty' => 'Medan ini tidak boleh kosong', + 'Close a task when there is no activity in a specific column' => 'Tutup tugas apabila tiada aktiviti dalam lajur tertentu', + '%s removed a subtask for the task #%d' => '%s membuang subtugas untuk tugas #%d', + '%s removed a comment on the task #%d' => '%s membuang komen pada tugas #%d', + 'Comment removed on task #%d' => 'Komen dibuang pada tugas #%d', + 'Subtask removed on task #%d' => 'Subtugas dibuang pada tugas #%d', + 'Hide tasks in this column in the dashboard' => 'Sembunyikan tugas dalam lajur ini di papan pemuka', + '%s removed a comment on the task %s' => '%s membuang komen pada tugas %s', + '%s removed a subtask for the task %s' => '%s membuang subtugas untuk tugas %s', + 'Comment removed' => 'Komen dibuang', + 'Subtask removed' => 'Subtugas dibuang', + '%s set a new internal link for the task #%d' => '%s menetapkan pautan dalaman baru untuk tugas #%d', + '%s removed an internal link for the task #%d' => '%s membuang pautan dalaman untuk tugas #%d', + 'A new internal link for the task #%d has been defined' => 'Pautan dalaman baru untuk tugas #%d telah ditentukan', + 'Internal link removed for the task #%d' => 'Pautan dalaman dibuang untuk tugas #%d', + '%s set a new internal link for the task %s' => '%s menetapkan pautan dalaman baru untuk tugas %s', + '%s removed an internal link for the task %s' => '%s membuang pautan dalaman untuk tugas %s', + 'Automatically set the due date on task creation' => 'Tetapkan tarikh tamat tempoh secara automatik semasa penciptaan tugas', + 'Move the task to another column when closed' => 'Pindahkan tugas ke lajur lain apabila ditutup', + 'Move the task to another column when not moved during a given period' => 'Pindahkan tugas ke lajur lain apabila tidak dipindahkan dalam tempoh yang diberikan', + 'Dashboard for %s' => 'Papan pemuka untuk %s', + 'Tasks overview for %s' => 'Gambaran keseluruhan tugas untuk %s', + 'Subtasks overview for %s' => 'Gambaran keseluruhan subtugas untuk %s', + 'Projects overview for %s' => 'Gambaran keseluruhan projek untuk %s', + 'Activity stream for %s' => 'Strim aktiviti untuk %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Berikan warna apabila tugas dipindahkan ke lajur tertentu', + 'Assign a priority when the task is moved to a specific swimlane' => 'Berikan keutamaan apabila tugas dipindahkan ke lajur tertentu', + 'User unlocked successfully.' => 'Pengguna berjaya dinyahkunci.', + 'Unable to unlock the user.' => 'Tidak dapat menyahkunci pengguna.', + 'Move a task to another swimlane' => 'Pindahkan tugas ke lajur lain', + 'Creator Name' => 'Nama Pencipta', + 'Time spent and estimated' => 'Masa yang dihabiskan dan dianggarkan', + 'Move position' => 'Alih kedudukan', + 'Move task to another position on the board' => 'Pindahkan tugas ke kedudukan lain pada papan', + 'Insert before this task' => 'Sisipkan sebelum tugas ini', + 'Insert after this task' => 'Sisipkan selepas tugas ini', + 'Unlock this user' => 'Nyahkunci pengguna ini', + 'Custom Project Roles' => 'Peranan Projek Tersuai', + 'Add a new custom role' => 'Tambah peranan tersuai baharu', + 'Restrictions for the role "%s"' => 'Sekatan untuk peranan "%s"', + 'Add a new project restriction' => 'Tambah sekatan projek baharu', + 'Add a new drag and drop restriction' => 'Tambah sekatan seret dan lepas baharu', + 'Add a new column restriction' => 'Tambah sekatan lajur baharu', + 'Edit this role' => 'Edit peranan ini', + 'Remove this role' => 'Alih keluar peranan ini', + 'There is no restriction for this role.' => 'Tiada sekatan untuk peranan ini.', + 'Only moving task between those columns is permitted' => 'Hanya memindahkan tugas antara lajur tersebut dibenarkan', + 'Close a task in a specific column when not moved during a given period' => 'Tutup tugas dalam lajur tertentu apabila tidak dipindahkan dalam tempoh yang diberikan', + 'Edit columns' => 'Edit lajur', + 'The column restriction has been created successfully.' => 'Sekatan lajur telah berjaya dicipta.', + 'Unable to create this column restriction.' => 'Tidak dapat mencipta sekatan lajur ini.', + 'Column restriction removed successfully.' => 'Sekatan lajur berjaya dialih keluar.', + 'Unable to remove this restriction.' => 'Tidak dapat mengalih keluar sekatan ini.', + 'Your custom project role has been created successfully.' => 'Peranan projek tersuai anda berjaya dicipta.', + 'Unable to create custom project role.' => 'Tidak dapat mencipta peranan projek tersuai.', + 'Your custom project role has been updated successfully.' => 'Peranan projek tersuai anda berjaya dikemas kini.', + 'Unable to update custom project role.' => 'Tidak dapat mengemas kini peranan projek tersuai.', + 'Custom project role removed successfully.' => 'Peranan projek tersuai berjaya dialih keluar.', + 'Unable to remove this project role.' => 'Tidak dapat mengalih keluar peranan projek ini.', + 'The project restriction has been created successfully.' => 'Sekatan projek telah berjaya dicipta.', + 'Unable to create this project restriction.' => 'Tidak dapat mencipta sekatan projek ini.', + 'Project restriction removed successfully.' => 'Sekatan projek berjaya dialih keluar.', + 'You cannot create tasks in this column.' => 'Anda tidak dapat mencipta tugas dalam lajur ini.', + 'Task creation is permitted for this column' => 'Penciptaan tugas dibenarkan untuk lajur ini', + 'Closing or opening a task is permitted for this column' => 'Menutup atau membuka tugas dibenarkan untuk lajur ini', + 'Task creation is blocked for this column' => 'Penciptaan tugas disekat untuk lajur ini', + 'Closing or opening a task is blocked for this column' => 'Menutup atau membuka tugas disekat untuk lajur ini', + 'Task creation is not permitted' => 'Penciptaan tugas tidak dibenarkan', + 'Closing or opening a task is not permitted' => 'Menutup atau membuka tugas tidak dibenarkan', + 'New drag and drop restriction for the role "%s"' => 'Sekatan seret dan lepas baharu untuk peranan "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Orang yang mempunyai peranan ini hanya boleh memindahkan tugas antara lajur sumber dan destinasi.', + 'Remove a column restriction' => 'Alih keluar sekatan lajur', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Adakah anda benar-benar mahu mengalih keluar sekatan lajur ini: "%s" ke "%s"?', + 'New column restriction for the role "%s"' => 'Sekatan lajur baharu untuk peranan "%s"', + 'Rule' => 'Peraturan', + 'Do you really want to remove this column restriction?' => 'Adakah anda benar-benar mahu mengalih keluar sekatan lajur ini?', + 'Custom roles' => 'Peranan tersuai', + 'New custom project role' => 'Peranan projek tersuai baharu', + 'Edit custom project role' => 'Edit peranan projek tersuai', + 'Remove a custom role' => 'Alih keluar peranan tersuai', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Adakah anda benar-benar mahu mengalih keluar peranan tersuai ini: "%s"? Semua orang yang diberikan peranan ini akan menjadi ahli projek.', + 'There is no custom role for this project.' => 'Tiada peranan tersuai untuk projek ini.', + 'New project restriction for the role "%s"' => 'Sekatan projek baharu untuk peranan "%s"', + 'Restriction' => 'Sekatan', + 'Remove a project restriction' => 'Alih keluar sekatan projek', + 'Do you really want to remove this project restriction: "%s"?' => 'Adakah anda benar-benar mahu mengalih keluar sekatan projek ini: "%s"?', + 'Duplicate to multiple projects' => 'Gandakan ke pelbagai projek', + 'This field is required' => 'Ruang ini diperlukan', + 'Moving a task is not permitted' => 'Memindahkan tugas tidak dibenarkan', + 'This value must be in the range %d to %d' => 'Nilai ini mestilah dalam julat %d hingga %d', + 'You are not allowed to move this task.' => 'Anda tidak dibenarkan untuk memindahkan tugas ini.', + 'API User Access' => 'Akses Pengguna API', + 'Preview' => 'Pratonton', + 'Write' => 'Tulis', + 'Write your text in Markdown' => 'Tulis teks anda dalam Markdown', + 'No personal API access token registered.' => 'Tiada token akses API peribadi didaftarkan.', + 'Your personal API access token is "%s"' => 'Token akses API peribadi anda ialah "%s"', + 'Remove your token' => 'Alih keluar token anda', + 'Generate a new token' => 'Jana token baharu', + 'Showing %d-%d of %d' => 'Memaparkan %d-%d daripada %d', + 'Outgoing Emails' => 'E-mel Keluar', + 'Add or change currency rate' => 'Tambah atau ubah kadar mata wang', + 'Reference currency: %s' => 'Mata wang rujukan: %s', + 'Add custom filters' => 'Tambah penapis tersuai', + 'Export' => 'Eksport', + 'Add link label' => 'Tambah label pautan', + 'Incompatible Plugins' => 'Pemalam Tidak Serasi', + 'Compatibility' => 'Keserasian', + 'Permissions and ownership' => 'Kebenaran dan pemilikan', + 'Priorities' => 'Keutamaan', + 'Close this window' => 'Tutup tetingkap ini', + 'Unable to upload this file.' => 'Tidak dapat memuat naik fail ini.', + 'Import tasks' => 'Import tugas', + 'Choose a project' => 'Pilih projek', + 'Profile' => 'Profil', + 'Application role' => 'Peranan aplikasi', + '%d invitations were sent.' => '%d jemputan telah dihantar.', + '%d invitation was sent.' => '%d jemputan telah dihantar.', + 'Unable to create this user.' => 'Tidak dapat mencipta pengguna ini.', + 'Kanboard Invitation' => 'Jemputan Kanboard', + 'Visible on dashboard' => 'Terlihat pada papan pemuka', + 'Created at:' => 'Dicipta pada:', + 'Updated at:' => 'Dikemas kini pada:', + 'There is no custom filter.' => 'Tiada penapis tersuai.', + 'New User' => 'Pengguna Baharu', + 'Authentication' => 'Pengesahan', + 'If checked, this user will use a third-party system for authentication.' => 'Jika ditandakan, pengguna ini akan menggunakan sistem pihak ketiga untuk pengesahan.', + 'The password is necessary only for local users.' => 'Kata laluan hanya diperlukan untuk pengguna tempatan.', + 'You have been invited to register on Kanboard.' => 'Anda telah dijemput untuk mendaftar di Kanboard.', + 'Click here to join your team' => 'Klik di sini untuk menyertai pasukan anda', + 'Invite people' => 'Jemput orang', + 'Emails' => 'E-mel', + 'Enter one email address by line.' => 'Masukkan satu alamat e-mel setiap baris.', + 'Add these people to this project' => 'Tambah orang-orang ini ke projek ini', + 'Add this person to this project' => 'Tambah orang ini ke projek ini', + 'Sign-up' => 'Daftar', + 'Credentials' => 'Kelayakan', + 'New user' => 'Pengguna baharu', + 'This username is already taken' => 'Nama pengguna ini sudah digunakan', + 'Your profile must have a valid email address.' => 'Profil anda mesti mempunyai alamat e-mel yang sah.', + 'TRL - Turkish Lira' => 'TRL - Lira Turki', + 'The project email is optional and could be used by several plugins.' => 'E-mel projek adalah pilihan dan boleh digunakan oleh beberapa pemalam.', + 'The project email must be unique across all projects' => 'E-mel projek mestilah unik di semua projek', + 'The email configuration has been disabled by the administrator.' => 'Konfigurasi e-mel telah dilumpuhkan oleh pentadbir.', + 'Close this project' => 'Tutup projek ini', + 'Open this project' => 'Buka projek ini', + 'Close a project' => 'Tutup projek', + 'Do you really want to close this project: "%s"?' => 'Adakah anda benar-benar mahu menutup projek ini: "%s"?', + 'Reopen a project' => 'Buka semula projek', + 'Do you really want to reopen this project: "%s"?' => 'Adakah anda benar-benar mahu membuka semula projek ini: "%s"?', + 'This project is open' => 'Projek ini dibuka', + 'This project is closed' => 'Projek ini ditutup', + 'Unable to upload files, check the permissions of your data folder.' => 'Tidak dapat memuat naik fail, periksa kebenaran folder data anda.', + 'Another category with the same name exists in this project' => 'Kategori lain dengan nama yang sama wujud dalam projek ini', + 'Comment sent by email successfully.' => 'Komen berjaya dihantar melalui e-mel.', + 'Sent by email to "%s" (%s)' => 'Dihantar melalui e-mel kepada "%s" (%s)', + 'Unable to read uploaded file.' => 'Tidak dapat membaca fail yang dimuat naik.', + 'Database uploaded successfully.' => 'Pangkalan data berjaya dimuat naik.', + 'Task sent by email successfully.' => 'Tugas berjaya dihantar melalui e-mel.', + 'There is no category in this project.' => 'Tiada kategori dalam projek ini.', + 'Send by email' => 'Hantar melalui e-mel', + 'Create and send a comment by email' => 'Cipta dan hantar komen melalui e-mel', + 'Subject' => 'Subjek', + 'Upload the database' => 'Muat naik pangkalan data', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Anda boleh memuat naik pangkalan data Sqlite yang dimuat turun sebelum ini (format Gzip).', + 'Database file' => 'Fail pangkalan data', + 'Upload' => 'Muat naik', + 'Your project must have at least one active swimlane.' => 'Projek anda mesti mempunyai sekurang-kurangnya satu lajur aktif.', + 'Project: %s' => 'Projek: %s', + 'Automatic action not found: "%s"' => 'Tindakan automatik tidak ditemui: "%s"', + '%d projects' => '%d projek', + '%d project' => '%d projek', + 'There is no project.' => 'Tiada projek.', + 'Sort' => 'Isih', + 'Project ID' => 'ID Projek', + 'Project name' => 'Nama projek', + 'Public' => 'Awam', + 'Personal' => 'Peribadi', + '%d tasks' => '%d tugas', + '%d task' => '%d tugas', + 'Task ID' => 'ID Tugas', + 'Assign automatically a color when due date is expired' => 'Berikan warna secara automatik apabila tarikh tamat tempoh telah tamat', + 'Total score in this column across all swimlanes' => 'Jumlah markah dalam lajur ini di semua lajur', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentina', + 'COP - Colombian Peso' => 'COP - Peso Colombia', + '%d groups' => '%d kumpulan', + '%d group' => '%d kumpulan', + 'Group ID' => 'ID Kumpulan', + 'External ID' => 'ID Luaran', + '%d users' => '%d pengguna', + '%d user' => '%d pengguna', + 'Hide subtasks' => 'Sembunyikan subtugas', + 'Show subtasks' => 'Tunjukkan subtugas', + 'Authentication Parameters' => 'Parameter Pengesahan', + 'API Access' => 'Akses API', + 'No users found.' => 'Tiada pengguna ditemui.', + 'User ID' => 'ID Pengguna', + 'Notifications are activated' => 'Pemberitahuan diaktifkan', + 'Notifications are disabled' => 'Pemberitahuan dilumpuhkan', + 'User disabled' => 'Pengguna dilumpuhkan', + '%d notifications' => '%d pemberitahuan', + '%d notification' => '%d pemberitahuan', + 'There is no external integration installed.' => 'Tiada integrasi luaran dipasang.', + 'You are not allowed to update tasks assigned to someone else.' => 'Anda tidak dibenarkan untuk mengemas kini tugas yang diberikan kepada orang lain.', + 'You are not allowed to change the assignee.' => 'Anda tidak dibenarkan untuk menukar penerima tugas.', + 'Task suppression is not permitted' => 'Penindasan tugas tidak dibenarkan', + 'Changing assignee is not permitted' => 'Menukar penerima tugas tidak dibenarkan', + 'Update only assigned tasks is permitted' => 'Kemas kini hanya tugas yang diberikan dibenarkan', + 'Only for tasks assigned to the current user' => 'Hanya untuk tugas yang diberikan kepada pengguna semasa', + 'My projects' => 'Projek saya', + 'You are not a member of any project.' => 'Anda bukan ahli mana-mana projek.', + 'My subtasks' => 'Subtugas saya', + '%d subtasks' => '%d subtugas', + '%d subtask' => '%d subtugas', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Memindahkan tugas antara lajur tersebut hanya dibenarkan untuk tugas yang diberikan kepada pengguna semasa', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Krona Denmark', + 'Remove user from group' => 'Alih keluar pengguna dari kumpulan', + 'Assign the task to its creator' => 'Berikan tugas kepada penciptanya', + 'This task was sent by email to "%s" with subject "%s".' => 'Tugas ini telah dihantar melalui e-mel kepada "%s" dengan subjek "%s".', + 'Predefined Email Subjects' => 'Subjek E-mel Pratakrif', + 'Write one subject by line.' => 'Tulis satu subjek setiap baris.', + 'Create another link' => 'Cipta pautan lain', + 'BRL - Brazilian Real' => 'BRL - Real Brazil', + 'Add a new Kanboard task' => 'Tambah tugas Kanboard baharu', + 'Subtask not started' => 'Subtugas belum dimulakan', + 'Subtask currently in progress' => 'Subtugas sedang dijalankan', + 'Subtask completed' => 'Subtugas selesai', + 'Subtask added successfully.' => 'Subtugas berjaya ditambahkan.', + '%d subtasks added successfully.' => '%d subtugas berjaya ditambahkan.', + 'Enter one subtask by line.' => 'Masukkan satu subtugas setiap baris.', + 'Predefined Contents' => 'Kandungan Pratakrif', + 'Predefined contents' => 'Kandungan pratakrif', + 'Predefined Task Description' => 'Penerangan Tugas Pratakrif', + 'Do you really want to remove this template? "%s"' => 'Adakah anda benar-benar mahu mengalih keluar templat ini? "%s"', + 'Add predefined task description' => 'Tambah penerangan tugas pratakrif', + 'Predefined Task Descriptions' => 'Penerangan Tugas Pratakrif', + 'Template created successfully.' => 'Templat berjaya dicipta.', + 'Unable to create this template.' => 'Tidak dapat mencipta templat ini.', + 'Template updated successfully.' => 'Templat berjaya dikemas kini.', + 'Unable to update this template.' => 'Tidak dapat mengemas kini templat ini.', + 'Template removed successfully.' => 'Templat berjaya dialih keluar.', + 'Unable to remove this template.' => 'Tidak dapat mengalih keluar templat ini.', + 'Template for the task description' => 'Templat untuk penerangan tugas', + 'The start date is greater than the end date' => 'Tarikh mula lebih besar daripada tarikh tamat', + 'Tags must be separated by a comma' => 'Tag mestilah dipisahkan oleh koma', + 'Only the task title is required' => 'Hanya tajuk tugas diperlukan', + 'Creator Username' => 'Nama Pengguna Pencipta', + 'Color Name' => 'Nama Warna', + 'Column Name' => 'Nama Lajur', + 'Swimlane Name' => 'Nama Lajur', + 'Time Estimated' => 'Anggaran Masa', + 'Time Spent' => 'Masa Dihabiskan', + 'External Link' => 'Pautan Luaran', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Ciri ini membolehkan suapan iCal, suapan RSS dan paparan papan awam.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Hentikan pemasa semua subtugas apabila memindahkan tugas ke lajur lain', + 'Subtask Title' => 'Tajuk Subtugas', + 'Add a subtask and activate the timer when moving a task to another column' => 'Tambah subtugas dan aktifkan pemasa apabila memindahkan tugas ke lajur lain', + 'days' => 'hari', + 'minutes' => 'minit', + 'seconds' => 'saat', + 'Assign automatically a color when preset start date is reached' => 'Berikan warna secara automatik apabila tarikh mula pratetap dicapai', + 'Move the task to another column once a predefined start date is reached' => 'Pindahkan tugas ke lajur lain apabila tarikh mula pratakrif dicapai', + 'This task is now linked to the task %s with the relation "%s"' => 'Tugas ini kini dipautkan ke tugas %s dengan hubungan "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Pautan dengan hubungan "%s" ke tugas %s telah dialih keluar', + 'Custom Filter:' => 'Penapis Tersuai:', + 'Unable to find this group.' => 'Tidak dapat mencari kumpulan ini.', + '%s moved the task #%d to the column "%s"' => '%s memindahkan tugas #%d ke lajur "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s memindahkan tugas #%d ke kedudukan %d dalam lajur "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s memindahkan tugas #%d ke lajur "%s"', + '%sh spent' => '%sj dihabiskan', + '%sh estimated' => '%sj dianggarkan', + 'Select All' => 'Pilih Semua', + 'Unselect All' => 'Nyahpilih Semua', + 'Apply action' => 'Mohon tindakan', + 'Move selected tasks to another column or swimlane' => 'Pindahkan tugas terpilih ke lajur atau lajur lain', + 'Edit tasks in bulk' => 'Edit tugas secara pukal', + 'Choose the properties that you would like to change for the selected tasks.' => 'Pilih sifat yang anda ingin ubah untuk tugas terpilih.', + 'Configure this project' => 'Konfigurasi projek ini', + 'Start now' => 'Mula sekarang', + '%s removed a file from the task #%d' => '%s mengalih keluar fail dari tugas #%d', + 'Attachment removed from task #%d: %s' => 'Lampiran dialih keluar dari tugas #%d: %s', + 'No color' => 'Tiada warna', + 'Attachment removed "%s"' => 'Lampiran dialih keluar "%s"', + '%s removed a file from the task %s' => '%s mengalih keluar fail dari tugas %s', + 'Move the task to another swimlane when assigned to a user' => 'Pindahkan tugas ke lajur lain apabila diberikan kepada pengguna', + 'Destination swimlane' => 'Lajur destinasi', + 'Assign a category when the task is moved to a specific swimlane' => 'Berikan kategori apabila tugas dipindahkan ke lajur tertentu', + 'Move the task to another swimlane when the category is changed' => 'Pindahkan tugas ke lajur lain apabila kategori ditukar', + 'Reorder this column by priority (ASC)' => 'Susun semula lajur ini mengikut keutamaan (ASC)', + 'Reorder this column by priority (DESC)' => 'Susun semula lajur ini mengikut keutamaan (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Susun semula lajur ini mengikut penerima dan keutamaan (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Susun semula lajur ini mengikut penerima dan keutamaan (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Susun semula lajur ini mengikut penerima (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Susun semula lajur ini mengikut penerima (Z-A)', + 'Reorder this column by due date (ASC)' => 'Susun semula lajur ini mengikut tarikh tamat tempoh (ASC)', + 'Reorder this column by due date (DESC)' => 'Susun semula lajur ini mengikut tarikh tamat tempoh (DESC)', + 'Reorder this column by id (ASC)' => 'Susun semula lajur ini mengikut id (ASC)', + 'Reorder this column by id (DESC)' => 'Susun semula lajur ini mengikut id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s memindahkan tugas #%d "%s" ke projek "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Tugas #%d "%s" telah dipindahkan ke projek "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Pindahkan tugas ke lajur lain apabila tarikh tamat tempoh kurang daripada bilangan hari tertentu', + 'Automatically update the start date when the task is moved away from a specific column' => 'Kemas kini tarikh mula secara automatik apabila tugas dialihkan dari lajur tertentu', + 'HTTP Client:' => 'Klien HTTP:', + 'Assigned' => 'Ditugaskan', + 'Task limits apply to each swimlane individually' => 'Had tugas dikenakan kepada setiap lajur secara individu', + 'Column task limits apply to each swimlane individually' => 'Had tugas lajur dikenakan kepada setiap lajur secara individu', + 'Column task limits are applied to each swimlane individually' => 'Had tugas lajur dikenakan kepada setiap lajur secara individu', + 'Column task limits are applied across swimlanes' => 'Had tugas lajur dikenakan di seluruh lajur', + 'Task limit: ' => 'Had tugas: ', + 'Change to global tag' => 'Tukar kepada tag global', + 'Do you really want to make the tag "%s" global?' => 'Adakah anda benar-benar mahu menjadikan tag "%s" global?', + 'Enable global tags for this project' => 'Dayakan tag global untuk projek ini', + 'Group membership(s):' => 'Keahlian kumpulan:', + '%s is a member of the following group(s): %s' => '%s adalah ahli kumpulan berikut: %s', + '%d/%d group(s) shown' => '%d/%d kumpulan ditunjukkan', + 'Subtask creation or modification' => 'Penciptaan atau pengubahsuaian subtugas', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Berikan tugas kepada pengguna tertentu apabila tugas dipindahkan ke lajur tertentu', + 'Comment' => 'Komen', + 'Collapse vertically' => 'Runtuhkan secara menegak', + 'Expand vertically' => 'Kembangkan secara menegak', + 'MXN - Mexican Peso' => 'MXN - Peso Meksiko', + 'Estimated vs actual time per column' => 'Anggaran vs masa sebenar setiap lajur', + 'HUF - Hungarian Forint' => 'HUF - Forint Hungary', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Anda mesti memilih fail untuk dimuat naik sebagai avatar anda!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Fail yang anda muat naik bukan imej yang sah! (Hanya *.gif, *.jpg, *.jpeg dan *.png dibenarkan!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Tetapkan tarikh tamat tempoh secara automatik apabila tugas dialihkan dari lajur tertentu', + 'No other projects found.' => 'Tiada projek lain ditemui.', + 'Tasks copied successfully.' => 'Tugas berjaya disalin.', + 'Unable to copy tasks.' => 'Tidak dapat menyalin tugas.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema cerah', + 'Dark theme' => 'Tema gelap', + 'Automatic theme - Sync with system' => 'Tema automatik - Segerak dengan sistem', + 'Application managers or more' => 'Pengurus aplikasi atau lebih', + 'Administrators' => 'Pentadbir', + 'Visibility:' => 'Kebolehlihatan:', + 'Standard users' => 'Pengguna standard', + 'Visibility is required' => 'Kebolehlihatan diperlukan', + 'The visibility should be an app role' => 'Kebolehlihatan hendaklah peranan aplikasi', + 'Reply' => 'Balas', + '%s wrote: ' => '%s menulis: ', + 'Number of visible tasks in this column and swimlane' => 'Bilangan tugas yang kelihatan dalam lajur dan laluan renang ini', + 'Number of tasks in this swimlane' => 'Bilangan tugas dalam laluan renang ini', + 'Unable to find another subtask in progress, you can close this window.' => 'Tidak dapat mencari subtugas lain dalam proses, anda boleh menutup tetingkap ini.', + 'This theme is invalid' => 'Tema ini tidak sah', + 'This role is invalid' => 'Peranan ini tidak sah', + 'This timezone is invalid' => 'Zon waktu ini tidak sah', + 'This language is invalid' => 'Bahasa ini tidak sah', + 'This URL is invalid' => 'URL ini tidak sah', + 'Date format invalid' => 'Format tarikh tidak sah', + 'Time format invalid' => 'Format masa tidak sah', + 'Invalid Mail transport' => 'Pengangkutan Mel tidak sah', + 'Color invalid' => 'Warna tidak sah', + 'This value must be greater or equal to %d' => 'Nilai ini mestilah lebih besar daripada atau sama dengan %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Tambah BOM di permulaan fail (diperlukan untuk Microsoft Excel)', + 'Just add these tag(s)' => 'Hanya tambah tag ini', + 'Remove internal link(s)' => 'Buang pautan dalaman', + 'Import tasks from another project' => 'Import tugas dari projek lain', + 'Select the project to copy tasks from' => 'Pilih projek untuk menyalin tugas dari', + 'The total maximum allowed attachments size is %sB.' => 'Jumlah saiz lampiran maksimum yang dibenarkan ialah %sB.', + 'Add attachments' => 'Tambah lampiran', + 'Task #%d "%s" is overdue' => 'Tugas #%d « %s » telah tamat tempoh', + 'Enable notifications by default for all new users' => 'Dayakan pemberitahuan secara lalai untuk semua pengguna baharu', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Tetapkan tugasan kepada penciptanya untuk lajur tertentu jika tiada penerima tugasan ditetapkan secara manual', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Tetapkan tugasan kepada pengguna yang log masuk apabila lajur berubah ke lajur yang ditetapkan jika tiada pengguna ditetapkan', +]; diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php new file mode 100644 index 0000000..44dfea1 --- /dev/null +++ b/app/Locale/nb_NO/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Ingen', + 'Edit' => 'Rediger', + 'Remove' => 'Fjern', + 'Yes' => 'Ja', + 'No' => 'Nei', + 'cancel' => 'avbryt', + 'or' => 'eller', + 'Yellow' => 'Gul', + 'Blue' => 'BlÃ¥', + 'Green' => 'Grønn', + 'Purple' => 'Lilla', + 'Red' => 'Rød', + 'Orange' => 'Orange', + 'Grey' => 'GrÃ¥', + 'Brown' => 'Brun', + 'Deep Orange' => 'Mørk orange', + 'Dark Grey' => 'Mørk grÃ¥', + 'Pink' => 'Rosa', + 'Teal' => 'Sjøgrønn', + 'Cyan' => 'Cyan', + 'Lime' => 'Lime', + 'Light Green' => 'Lys grønn', + 'Amber' => 'Ravgul', + 'Save' => 'Lagre', + 'Login' => 'Logg inn', + 'Official website:' => 'Offisielt nettsted:', + 'Unassigned' => 'Ikke tildelt', + 'View this task' => 'Se denne oppgaven', + 'Remove user' => 'Fjern bruker', + 'Do you really want to remove this user: "%s"?' => 'Er du sikker pÃ¥ at du vil fjerne brukeren: "%s"?', + 'All users' => 'Alle brukere', + 'Username' => 'Brukernavn', + 'Password' => 'Passord', + 'Administrator' => 'Administrator', + 'Sign in' => 'Logg inn', + 'Users' => 'Brukere', + 'Forbidden' => 'Ikke tillatt', + 'Access Forbidden' => 'Adgang ikke tillatt', + 'Edit user' => 'Rediger bruker', + 'Logout' => 'Logg ut', + 'Bad username or password' => 'Feil brukernavn eller passord', + 'Edit project' => 'Endre prosjekt', + 'Name' => 'Navn', + 'Projects' => 'Prosjekter', + 'No project' => 'Ingen prosjekter', + 'Project' => 'Prosjekt', + 'Status' => 'Status', + 'Tasks' => 'Oppgaver', + 'Board' => 'Tavle', + 'Actions' => 'Handlinger', + 'Inactive' => 'Inaktiv', + 'Active' => 'Aktiv', + 'Unable to update this board.' => 'Ikke mulig Ã¥ oppdatere tavlesiden', + 'Disable' => 'SlÃ¥ av', + 'Enable' => 'SlÃ¥ pÃ¥', + 'New project' => 'Nytt prosjekt', + 'Do you really want to remove this project: "%s"?' => 'Er du sikker pÃ¥ at du vil du slette dette prosjektet: "%s"?', + 'Remove project' => 'Slett prosjekt', + 'Edit the board for "%s"' => 'Endre tavlesiden for "%s"', + 'Add a new column' => 'Legg til ny kolonne', + 'Title' => 'Tittel', + 'Assigned to %s' => 'Tildelt: %s', + 'Remove a column' => 'Fjern en kolonne', + 'Unable to remove this column.' => 'Kan ikke fjerne kolonnen', + 'Do you really want to remove this column: "%s"?' => 'Er du sikker pÃ¥ at du vil fjerne denne kolonnen: "%s"?', + 'Settings' => 'Innstillinger', + 'Application settings' => 'Applikasjonsinnstillinger', + 'Language' => 'SprÃ¥k', + 'Webhook token:' => 'Webhook token:', + 'API token:' => 'API token:', + 'Database size:' => 'Databasestørrelse:', + 'Download the database' => 'Last ned databasen', + 'Optimize the database' => 'Optimaliser databasen', + '(VACUUM command)' => '(VACUUM kommando)', + '(Gzip compressed Sqlite file)' => '(Gzip-komprimert Sqlite fil)', + 'Close a task' => 'Lukk oppgave', + 'Column' => 'Kolonne', + 'Color' => 'Farge', + 'Assignee' => 'Tildelt', + 'Create another task' => 'Opprett en ny oppgave etter denne', + 'New task' => 'Ny oppgave', + 'Open a task' => 'Ã…pne oppgave', + 'Do you really want to open this task: "%s"?' => 'Er du sikker pÃ¥ at du vil Ã¥pne denne oppgaven: "%s"?', + 'Back to the board' => 'Tilbake til tavlesiden', + 'There is nobody assigned' => 'Mangler tildeling', + 'Column on the board:' => 'Kolonne:', + 'Close this task' => 'Lukk oppgave', + 'Open this task' => 'Ã…pne oppgave', + 'There is no description.' => 'Ingen beskrivelse.', + 'Add a new task' => 'Lag ny oppgave', + 'The username is required' => 'Brukernavn er pÃ¥krevd', + 'The maximum length is %d characters' => 'Den maksimale lengden er %d tegn', + 'The minimum length is %d characters' => 'Den minimale lengden er %d tegn', + 'The password is required' => 'Passord pÃ¥krevet', + 'This value must be an integer' => 'Verdien mÃ¥ være et heltall', + 'The username must be unique' => 'Brukernavnet mÃ¥ være unikt', + 'The user id is required' => 'Bruker-id pÃ¥krevet', + 'Passwords don\'t match' => 'Passordene stemmer ikke overens', + 'The confirmation is required' => 'Bekreftelse pÃ¥krevet', + 'The project is required' => 'Prosjekt pÃ¥krevet', + 'The id is required' => 'ID pÃ¥krevet', + 'The project id is required' => 'Prosjekt-id pÃ¥krevet', + 'The project name is required' => 'Prosjektnavn pÃ¥krevet', + 'The title is required' => 'Tittel pÃ¥krevet', + 'Settings saved successfully.' => 'Innstillinger lagret.', + 'Unable to save your settings.' => 'Feil ved lagring av innstillinger.', + 'Database optimization done.' => 'Databaseoptimalisering er fullført.', + 'Your project has been created successfully.' => 'Prosjektet er opprettet.', + 'Unable to create your project.' => 'Prosjektet kunne ikke opprettes', + 'Project updated successfully.' => 'Prosjektet er oppdatert.', + 'Unable to update this project.' => 'Prosjektet kunne ikke oppdateres.', + 'Unable to remove this project.' => 'Prosjektet kunne ikke slettes.', + 'Project removed successfully.' => 'Prosjektet er slettet.', + 'Project activated successfully.' => 'Prosjektet er aktivert.', + 'Unable to activate this project.' => 'Prosjektet kunne ikke aktiveres.', + 'Project disabled successfully.' => 'Prosjektet er deaktiveret.', + 'Unable to disable this project.' => 'Prosjektet kunne ikke deaktiveres.', + 'Unable to open this task.' => 'Oppgaven kunne ikke Ã¥pnes.', + 'Task opened successfully.' => 'Oppgaven er Ã¥pnet.', + 'Unable to close this task.' => 'Oppgaven kunne ikke Ã¥pnes.', + 'Task closed successfully.' => 'Oppgaven er lukket.', + 'Unable to update your task.' => 'Oppgaven kunne ikke oppdateres.', + 'Task updated successfully.' => 'Oppgaven er oppdatert.', + 'Unable to create your task.' => 'Oppgave kunne ikke opprettes.', + 'Task created successfully.' => 'Oppgaven er opprettet.', + 'User created successfully.' => 'Brukeren er opprettet.', + 'Unable to create your user.' => 'Brukeren kunne ikke opprettes.', + 'User updated successfully.' => 'Brukeren er oppdatert', + 'User removed successfully.' => 'Brukeren er fjernet.', + 'Unable to remove this user.' => 'Brukeren kunne ikke slettes.', + 'Board updated successfully.' => 'Hovedsiden er oppdatert.', + 'Ready' => 'Klar', + 'Backlog' => 'Backlog', + 'Work in progress' => 'Under arbeid', + 'Done' => 'Utført', + 'Application version:' => 'Versjon:', + 'Id' => 'ID', + 'Public link' => 'Offentligt lenke', + 'Timezone' => 'Tidssone', + 'Sorry, I didn\'t find this information in my database!' => 'Denne informasjonen kunne ikke finnes i databasen!', + 'Page not found' => 'Siden er ikke funnet', + 'Complexity' => 'Kompleksitet', + 'Task limit' => 'Oppgavebegrensning', + 'Task count' => 'Antall oppgaver', + 'User' => 'Bruker', + 'Comments' => 'Kommentarer', + 'Comment is required' => 'Kommentar mÃ¥ legges inn', + 'Comment added successfully.' => 'Kommentaren er lagt til.', + 'Unable to create your comment.' => 'Din kommentar kunne ikke opprettes.', + 'Due Date' => 'Frist', + 'Invalid date' => 'Ugyldig dato', + 'Automatic actions' => 'Automatiske handlinger', + 'Your automatic action has been created successfully.' => 'Din automatiske handling er opprettet.', + 'Unable to create your automatic action.' => 'Din automatiske handling kunne ikke opprettes.', + 'Remove an action' => 'Fjern en handling', + 'Unable to remove this action.' => 'Handlingen kunne ikke fjernes.', + 'Action removed successfully.' => 'Handlingen er fjernet.', + 'Automatic actions for the project "%s"' => 'Automatiske handlinger for prosjektet "%s"', + 'Add an action' => 'Legg til en handling', + 'Event name' => 'Hendelsehet', + 'Action' => 'Handling', + 'Event' => 'Hendelse', + 'When the selected event occurs execute the corresponding action.' => 'NÃ¥r den valgte hendelsen oppstÃ¥r, utfør tilsvarende handling.', + 'Next step' => 'Neste', + 'Define action parameters' => 'Definer handlingsparametre', + 'Do you really want to remove this action: "%s"?' => 'Vil du slette denne handlingen: "%s"?', + 'Remove an automatic action' => 'Fjern en automatisk handling', + 'Assign the task to a specific user' => 'Tildel oppgaven til en bestemt bruker', + 'Assign the task to the person who does the action' => 'Tildel oppgaven til den person, som utfører handlingen', + 'Duplicate the task to another project' => 'Kopier oppgaven til et annet prosjekt', + 'Move a task to another column' => 'Flytt oppgaven til en annen kolonne', + 'Task modification' => 'Oppgaveendring', + 'Task creation' => 'Oppgaveoprettelse', + 'Closing a task' => 'Lukke en oppgave', + 'Assign a color to a specific user' => 'Tildel en farge til en bestemt bruker', + 'Position' => 'Posisjon', + 'Duplicate to project' => 'Kopier til et annet prosjekt', + 'Duplicate' => 'Kopier', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Kommentar oppdatert.', + 'Unable to update your comment.' => 'Din kommentar kunne ikke oppdateres.', + 'Remove a comment' => 'Fjern en kommentar', + 'Comment removed successfully.' => 'Kommentaren ble fjernet.', + 'Unable to remove this comment.' => 'Kommentaren kunne ikke fjernes.', + 'Do you really want to remove this comment?' => 'Vil du fjerne denne kommentaren?', + 'Current password for the user "%s"' => 'Aktivt passord for brukeren "%s"', + 'The current password is required' => 'Passord er pÃ¥krevet', + 'Wrong password' => 'Feil passord', + 'Unknown' => 'Ukjent', + 'Last logins' => 'Siste innlogging', + 'Login date' => 'Login dato', + 'Authentication method' => 'Godkjenningsmetode', + 'IP address' => 'IP Adresse', + 'User agent' => 'User Agent', + 'Persistent connections' => 'Nettforbindelser', + 'No session.' => 'Ingen sesjoner.', + 'Expiration date' => 'Utløpsdato', + 'Remember Me' => 'Husk meg', + 'Creation date' => 'Opprettelsesdato', + 'Everybody' => 'Alle', + 'Open' => 'Ã…pen', + 'Closed' => 'Lukket', + 'Search' => 'Søk', + 'Nothing found.' => 'Intet funnet.', + 'Due date' => 'Tidsfrist', + 'Description' => 'Beskrivelse', + '%d comments' => '%d kommentarer', + '%d comment' => '%d kommentar', + 'Email address invalid' => 'Ugyldig epost', + 'Your external account is not linked anymore to your profile.' => 'Din eksterne konto er ikke lenger lenket til profilen din', + 'Unable to unlink your external account.' => 'Kan ikke fjerne lenken til din eksterne konto', + 'External authentication failed' => 'Ekstern autentisering feilet', + 'Your external account is linked to your profile successfully.' => 'Den eksterne konto er lenket til profilen din', + 'Email' => 'E-post', + 'Task removed successfully.' => 'Oppgaven er fjernet.', + 'Unable to remove this task.' => 'Oppgaven kunne ikke fjernes.', + 'Remove a task' => 'Fjern en oppgave', + 'Do you really want to remove this task: "%s"?' => 'Vil du fjerne denne oppgaven: "%s"?', + 'Assign automatically a color based on a category' => 'Tildel automatisk en farge baseret for en kategori', + 'Assign automatically a category based on a color' => 'Tildel automatisk en kategori basert pÃ¥ en farve', + 'Task creation or modification' => 'Oppgaveopprettelse eller endring', + 'Category' => 'Kategori', + 'Category:' => 'Kategori:', + 'Categories' => 'Kategorier', + 'Your category has been created successfully.' => 'Kategorien er opprettet.', + 'This category has been updated successfully.' => 'Kategorien er oppdatert.', + 'Unable to update this category.' => 'Kategorien kunne ikke oppdateres.', + 'Remove a category' => 'Fjern en kategori', + 'Category removed successfully.' => 'Kategorien er fjernet.', + 'Unable to remove this category.' => 'Kategorien kunne ikke fjernes.', + 'Category modification for the project "%s"' => 'Endring av kategori for prosjektet "%s"', + 'Category Name' => 'Kategorinavn', + 'Add a new category' => 'Legg til ny kategori', + 'Do you really want to remove this category: "%s"?' => 'Vil du fjerne kategorien: "%s"?', + 'All categories' => 'Alle kategorier', + 'No category' => 'Ingen kategori', + 'The name is required' => 'Navnet er pÃ¥krevet', + 'Remove a file' => 'Fjern en fil', + 'Unable to remove this file.' => 'Filen kunne ikke fjernes.', + 'File removed successfully.' => 'Filen er fjernet.', + 'Attach a document' => 'Legg til et dokument', + 'Do you really want to remove this file: "%s"?' => 'Vil du fjerne filen: "%s"?', + 'Attachments' => 'Vedlegg', + 'Edit the task' => 'Rediger oppgaven', + 'Add a comment' => 'Legg til en kommentar', + 'Edit a comment' => 'Rediger en kommentar', + 'Summary' => 'Sammendrag', + 'Time tracking' => 'Tidsregistrering', + 'Estimate:' => 'Estimat:', + 'Spent:' => 'Brukt:', + 'Do you really want to remove this sub-task?' => 'Vil du fjerne denne deloppgaven?', + 'Remaining:' => 'Gjenværende:', + 'hours' => 'timer', + 'estimated' => 'estimat', + 'Sub-Tasks' => 'Deloppgave', + 'Add a sub-task' => 'Legg til en deloppgave', + 'Original estimate' => 'Estimert tidsbruk', + 'Create another sub-task' => 'Legg til en ny deloppgave', + 'Time spent' => 'Tidsforbruk', + 'Edit a sub-task' => 'Rediger en deloppgave', + 'Remove a sub-task' => 'Fjern en deloppgave', + 'The time must be a numeric value' => 'Tiden skal være en nummerisk erdi', + 'Todo' => 'GjøremÃ¥l', + 'In progress' => 'Under arbeid', + 'Sub-task removed successfully.' => 'Deloppgaven er fjernet.', + 'Unable to remove this sub-task.' => 'Deloppgaven kunne ikke fjernes.', + 'Sub-task updated successfully.' => 'Deloppgaven er opdateret.', + 'Unable to update your sub-task.' => 'Deloppgaven kunne ikke opdateres.', + 'Unable to create your sub-task.' => 'Deloppgaven kunne ikke oprettes.', + 'Maximum size: ' => 'Maksimum størrelse: ', + 'Display another project' => 'Vis annet prosjekt...', + 'Created by %s' => 'Opprettet av %s', + 'Tasks Export' => 'Oppgave eksport', + 'Start Date' => 'Startdato', + 'Execute' => 'KKjør', + 'Task Id' => 'Oppgave ID', + 'Creator' => 'Laget av', + 'Modification date' => 'Endringsdato', + 'Completion date' => 'Ferdigstillingsdato', + 'Clone' => 'Kopier', + 'Project cloned successfully.' => 'Prosjektet er kopiert.', + 'Unable to clone this project.' => 'Prosjektet kunne ikke kopieres', + 'Enable email notifications' => 'Aktiver epostvarslinger', + 'Task position:' => 'Posisjon:', + 'The task #%d has been opened.' => 'Oppgaven #%d er Ã¥pnet.', + 'The task #%d has been closed.' => 'Oppgaven #%d er lukket.', + 'Sub-task updated' => 'Deloppgaven er oppdatert', + 'Title:' => 'Tittel:', + 'Status:' => 'Status:', + 'Assignee:' => 'Ansvarlig:', + 'Time tracking:' => 'TidsmÃ¥ling:', + '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 comment' => 'Ny kommentar', + 'Comment updated' => 'Kommentar oppdatert', + 'New subtask' => 'Ny deloppgave', + 'I only want to receive notifications for these projects:' => 'Jeg vil kun ha varslinger for disse prosjekter:', + 'view the task on Kanboard' => 'se oppgaven', + 'Public access' => 'Offentlig tilgang', + 'Disable public access' => 'Deaktiver offentlig tilgang', + 'Enable public access' => 'Aktiver offentlig tilgang', + 'Public access disabled' => 'Offentlig tilgang er deaktivert', + 'Move the task to another project' => 'Flytt oppgaven til et annet prosjekt', + 'Move to project' => 'Flytt til et annet prosjekt', + 'Do you really want to duplicate this task?' => 'Vil du kopiere denne oppgaven?', + 'Duplicate a task' => 'Kopier en oppgave', + 'External accounts' => 'Eksterne kontoer', + 'Account type' => 'Kontotype', + 'Local' => 'Lokal', + 'Remote' => 'Fjernstyrt', + 'Enabled' => 'Aktiv', + 'Disabled' => 'Deaktivert', + 'Login:' => 'Brukernavn', + 'Full Name:' => 'Navn:', + 'Email:' => 'Epost:', + 'Notifications:' => 'Varslinger:', + 'Notifications' => 'Varslinger', + 'Account type:' => 'Konto type:', + 'Edit profile' => 'Rediger profil', + 'Change password' => 'Endre passord', + 'Password modification' => 'Passordendring', + 'External authentications' => 'Ekstern godkjenning', + 'Never connected.' => 'Aldri innlogget.', + 'No external authentication enabled.' => 'Ingen eksterne godkjenninger aktiveret.', + 'Password modified successfully.' => 'Passord er endret.', + 'Unable to change the password.' => 'Passordet kuenne ikke endres.', + 'Change category' => 'Endre kategori', + '%s updated the task %s' => '%s oppdaterte oppgaven %s', + '%s opened the task %s' => '%s Ã¥pnet oppgaven %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s flyttet oppgaven %s til posisjon #%d i kolonne "%s"', + '%s moved the task %s to the column "%s"' => '%s flyttet oppgaven %s til kolonnen "%s"', + '%s created the task %s' => '%s opprettet oppgaven %s', + '%s closed the task %s' => '%s lukket oppgaven %s', + '%s created a subtask for the task %s' => '%s opprettet en deloppgave for oppgaven %s', + '%s updated a subtask for the task %s' => '%s oppdaterte en deloppgave for oppgaven %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Tildelt til %s med et estimat pÃ¥ %s/%sh', + 'Not assigned, estimate of %sh' => 'Ikke tildelt, estimert til %sh', + '%s updated a comment on the task %s' => '%s oppdaterte en kommentar til oppgaven %s', + '%s commented the task %s' => '%s har kommentert oppgaven %s', + '%s\'s activity' => 'Aktiviteter: %s', + 'RSS feed' => 'RSS feed', + '%s updated a comment on the task #%d' => '%s oppdaterte en kommentar til oppgaven #%d', + '%s commented on the task #%d' => '%s kommenterte oppgaven #%d', + '%s updated a subtask for the task #%d' => '%s oppdaterte en deloppgave til oppgaven #%d', + '%s created a subtask for the task #%d' => '%s opprettet en deloppgave til oppgaven #%d', + '%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 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 (kommaseparert)', + 'Task assignee change' => 'Endring av oppgaveansvarlig', + '%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', + 'Create a task from an external provider' => 'Oppret en oppgave fra en ekstern tilbyder', + 'Change the assignee based on an external username' => 'Endre ansvarlige baseret pÃ¥ et eksternt brukernavn', + 'Change the category based on an external label' => 'Endre kategorien basert pÃ¥ en ekstern etikett', + 'Reference' => 'Referanse', + 'Label' => 'Etikett', + 'Database' => 'Database', + 'About' => 'Om', + 'Database driver:' => 'Databasedriver:', + 'Board settings' => 'Tavlesiden', + 'Webhook settings' => 'Webhook innstillinger', + 'Reset token' => 'Resette token', + 'API endpoint:' => 'API endpoint:', + 'Refresh interval for personal board' => 'Oppdateringsintervall for privat tavleside', + 'Refresh interval for public board' => 'Oppdateringsintervall for offentlig tavleside', + 'Task highlight period' => 'Periode for fremheving av oppgaver', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode for Ã¥ fastslÃ¥ en oppgave nylig er endret (0 for Ã¥ deaktivere, 2 dager er standard)', + 'Frequency in second (60 seconds by default)' => 'Frekevens i sekunder (60 sekunder som standard)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvens i sekunder (0 for Ã¥ deaktivere denne funksjonen, 10 sekunder er standard)', + 'Application URL' => 'Applikasjon-URL', + 'Token regenerated.' => 'Token regenerert.', + 'Date format' => 'Datoformat', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format, eksempel: "%s" og "%s"', + 'New personal project' => 'Nytt privat prosjekt', + 'This project is personal' => 'Dette projektet er privat', + 'Add' => 'Legg til', + 'Start date' => 'Startdato', + 'Time estimated' => 'Tid estimert', + 'There is nothing assigned to you.' => 'Ingen elementer er tildelt deg.', + 'My tasks' => 'Mine oppgaver', + 'Activity stream' => 'Aktivitetslogg', + 'Dashboard' => 'Hovedsiden', + 'Confirmation' => 'Bekreftelse', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Opprett en kommentar fra en ekstern tilbyder', + 'Project management' => 'Prosjektinnstillinger', + 'Columns' => 'Kolonner', + 'Task' => 'Oppgave', + 'Percentage' => 'Prosent', + 'Number of tasks' => 'Antall oppgaver', + 'Task distribution' => 'Kolonnefordeling', + 'Analytics' => 'Analyser', + 'Subtask' => 'Deloppgave', + 'User repartition' => 'Brukerfordeling', + 'Clone this project' => 'Kopier dette prosjektet', + 'Column removed successfully.' => 'Kolonne flyttet', + 'Not enough data to show the graph.' => 'Ikke nok data til Ã¥ vise grafen', + 'Previous' => 'Forrige', + 'The id must be an integer' => 'ID mÃ¥ være heltall', + 'The project id must be an integer' => 'ProsjektID mÃ¥ være et heltall', + 'The status must be an integer' => 'Status mÃ¥ være et heltall', + 'The subtask id is required' => 'Du mÃ¥ angi ID for deloppgaven', + 'The subtask id must be an integer' => 'ID for deloppgaven mÃ¥ være et heltall', + 'The task id is required' => 'OppgaveID er pÃ¥krevet', + 'The task id must be an integer' => 'OppgaveID mÃ¥ være et heltall', + 'The user id must be an integer' => 'BrukerID mÃ¥ være et heltall', + 'This value is required' => 'Denne verdien er pÃ¥krevet', + 'This value must be numeric' => 'Verdien mÃ¥ være numerisk', + 'Unable to create this task.' => 'Kunne ikke opprette oppgaven', + 'Cumulative flow diagram' => 'Kumulativt flytdiagram', + 'Daily project summary' => 'Daglig prosjektsammendrag', + 'Daily project summary export' => 'Daglig prosjektsammendrag', + 'Exports' => 'Eksporter', + 'This export contains the number of tasks per column grouped per day.' => 'Denne eksporten inneholde antall oppgaver pr. kolonne gruppert pr. dag', + 'Active swimlanes' => 'Aktive svømmebaner', + 'Add a new swimlane' => 'Legg til en ny svømmebane', + 'Default swimlane' => 'Standard svømmebane', + 'Do you really want to remove this swimlane: "%s"?' => 'Er du sikker pÃ¥ at du vil fjerne svømmebane: "%s"?', + 'Inactive swimlanes' => 'Deaktiverte svømmebaner', + 'Remove a swimlane' => 'Fjern en svømmebane', + 'Swimlane modification for the project "%s"' => 'Svømmebane endrei i prosjektet "%s"', + 'Swimlane removed successfully.' => 'Svømmebane fjernet', + 'Swimlanes' => 'Svømmebaner', + 'Swimlane updated successfully.' => 'Svømmebane oppdatert', + 'Unable to remove this swimlane.' => 'Kunne ikke fjerne svømmebanen', + 'Unable to update this swimlane.' => 'Kunne ikke endre svømmebanen', + 'Your swimlane has been created successfully.' => 'Svømmebanen er opprettet!', + 'Example: "Bug, Feature Request, Improvement"' => 'Eksempel: Avvik, forbedringsforslag, ny funksjonalitet', + 'Default categories for new projects (Comma-separated)' => 'Standardkategorier for nye prosjekter (kommaseparert)', + 'Integrations' => 'Integrasjoner', + 'Integration with third-party services' => 'Integrasjoner med tredjeparts-tjenester', + 'Subtask Id' => 'Deloppgave ID', + 'Subtasks' => 'Deloppgaver', + 'Subtasks Export' => 'Eksporter deloppgaver', + 'Task Title' => 'Oppgavetittel', + 'Untitled' => 'Uten navn', + 'Application default' => 'Standardinstilling', + 'Language:' => 'SprÃ¥k', + 'Timezone:' => 'Tidssone', + 'All columns' => 'Alle kolonner', + 'Next' => 'Neste', + '#%d' => '#%d', + 'All swimlanes' => 'Alle svømmebaner', + 'All colors' => 'Alle farger', + 'Moved to column %s' => 'Flyttet til kolonne %s', + 'User dashboard' => 'Brukerens hovedside', + 'Allow only one subtask in progress at the same time for a user' => 'Tillat kun en aktiv deloppgave for brukerne', + 'Edit column "%s"' => 'Endre kolonne "%s"', + 'Select the new status of the subtask: "%s"' => 'Sett ny status for deloppgave: "%s"', + 'Subtask timesheet' => 'Tidsskjema for deloppgaver', + 'There is nothing to show.' => 'Ingen data Ã¥ vise', + 'Time Tracking' => 'Tidsregistrering', + 'You already have one subtask in progress' => 'Du har allerede en aktiv deloppgave i dette prosjektet', + 'Which parts of the project do you want to duplicate?' => 'Hvilke deler av dette prosjektet ønsker du Ã¥ kopiere?', + 'Disallow login form' => 'Deaktiver innloggingsbilde', + 'Start' => 'Start', + 'End' => 'Slutt', + 'Task age in days' => 'Antall dager siden siden oppgaven ble opprettet', + 'Days in this column' => 'Antall dager siden oppgaven ble lagt i denne kolonnen', + '%dd' => '%dd', + 'Add a new link' => 'Legg til en ny relasjon', + 'Do you really want to remove this link: "%s"?' => 'Ønsker du virkelig Ã¥ fjerne lenken: "%s"', + 'Do you really want to remove this link with task #%d?' => 'Ønsker du virkelig Ã¥ fjerne lenken oppgaven #%d?', + 'Field required' => 'Feltet mÃ¥ fylles ut', + 'Link added successfully.' => 'Ny relasjon er lagt til', + 'Link updated successfully.' => 'Relasjon er oppdatert', + 'Link removed successfully.' => 'Relasjon er fjernet', + 'Link labels' => 'Relasjoner', + 'Link modification' => 'Relasjonsmodifisering', + 'Opposite label' => 'Etikett for relatert motsatt oppgave', + 'Remove a link' => 'Fjern relasjon', + 'The labels must be different' => 'Titlene mÃ¥ være ulike', + 'There is no link.' => 'Ingen lenker funnet', + 'This label must be unique' => 'Tittelen mÃ¥ være unik', + 'Unable to create your link.' => 'Kunne ikke opprette lenken.', + 'Unable to update your link.' => 'Kunne ikke endre lenken.', + 'Unable to remove this link.' => 'Kunne ikke fjerne lenken', + 'relates to' => 'relatert til', + 'blocks' => 'blokkerer', + 'is blocked by' => 'blokkeres av', + 'duplicates' => 'kopierer', + 'is duplicated by' => 'er en kopi av', + 'is a child of' => 'er en underordnet oppgave av', + 'is a parent of' => 'er en overordnet oppgave for', + 'targets milestone' => 'milepel', + 'is a milestone of' => 'er en milepel for', + 'fixes' => 'løser', + 'is fixed by' => 'løses av', + 'This task' => 'Denne oppgaven', + '<1h' => '<1t', + '%dh' => '%dt', + 'Expand tasks' => 'Utvid oppgavevisning', + 'Collapse tasks' => 'Komprimer oppgavevisning', + 'Expand/collapse tasks' => 'Utvide/komprimere oppgavevisning', + 'Close dialog box' => 'Lukk dialogvindu', + 'Submit a form' => 'Send inn skjema', + 'Board view' => 'Tavlevisning', + 'Keyboard shortcuts' => 'Hurtigtaster', + 'Open board switcher' => 'Ã…pne tavleskifter', + 'Application' => 'Applikasjon', + 'Compact view' => 'Kompakt visning', + 'Horizontal scrolling' => 'Bla horisontalt', + 'Compact/wide view' => 'Kompakt/bred visning', + 'Currency' => 'Valuta', + 'Personal project' => 'Privat prosjekt', + 'AUD - Australian Dollar' => 'AUD - Australske dollar', + 'CAD - Canadian Dollar' => 'CAD - Kanadiske dollar', + 'CHF - Swiss Francs' => 'CHF - Sveitsiske frank', + 'Custom Stylesheet' => 'Egendefinert stilark', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britiske pund', + 'INR - Indian Rupee' => 'INR - Indiske rupi', + 'JPY - Japanese Yen' => 'JPY - Japanske yen', + 'NZD - New Zealand Dollar' => 'NZD - New Zealand Dollar', + 'PEN - Peruvian Sol' => 'PEN - Peruansk sol', + 'RSD - Serbian dinar' => 'RSD - Serbiske dinarer', + 'CNY - Chinese Yuan' => 'CNY - Kinesiske yuan', + 'USD - US Dollar' => 'USD - Amerikanske dollar', + 'VES - Venezuelan Bolívar' => 'VES - Venezuelansk bolívar', + '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', + 'Move the task to another column when assignee is cleared' => 'Flytt oppgaven til en annen kolonne nÃ¥r ppgavetildeling fjernes ', + 'Source column' => 'Opprinnelig kolonne', + 'Transitions' => 'Statusendringer', + 'Executer' => 'Utfører', + 'Time spent in the column' => 'MedgÃ¥tt tid i kolonnen', + 'Task transitions' => 'Oppgavebevegelser', + 'Task transitions export' => 'Eksportere oppgavebevegelser', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Rapporten inneholder alle kolonnebevegelser for hver oppgave, med dato, bruker og tidsforbruk for hver bevegelse', + 'Currency rates' => 'Valutakurser', + 'Rate' => 'Kurs', + 'Change reference currency' => 'Endre refereransevaluta', + 'Reference currency' => 'Referansevaluta', + 'The currency rate has been added successfully.' => 'Referansevaluta er endret', + 'Unable to add this currency rate.' => 'Kan ikke legge til denne valutakursen', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s fjernet ansvarlige for oppgaven %s', + 'Information' => 'Informasjon', + 'Check two factor authentication code' => 'Kontrollerer kode for tofaktor autensitering', + 'The two factor authentication code is not valid.' => 'Autentiseringskoden er ikke gyldig', + 'The two factor authentication code is valid.' => 'Autentiseringskoden er gyldig', + 'Code' => 'Kode', + 'Two factor authentication' => 'Tofaktor autentisering', + 'This QR code contains the key URI: ' => 'Denne QR-koden inneholder nøkkel URI: ', + 'Check my code' => 'Sjekk koden min', + 'Secret key: ' => 'Hemmelig nøkkel', + 'Test your device' => 'Test din enhet', + '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' => '%s via Kanboard', + 'Burndown chart' => 'Burndown diagram', + 'This chart show the task complexity over the time (Work Remaining).' => 'Dette diagrammet viser oppgavekopleksitet over tid (gjenstÃ¥ende arbeid)', + 'Screenshot taken %s' => 'Skjemskudd lagt inn %s', + 'Add a screenshot' => 'Legg til et skjermbilde', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Ta et skjermskudd og trykk CTRL+V eller ⌘+V for Ã¥ lime inn her.', + 'Screenshot uploaded successfully.' => 'Skjermbilde opplastet', + 'SEK - Swedish Krona' => 'SEK - Svenske kroner', + 'Identifier' => 'Prosjektkode', + 'Disable two factor authentication' => 'SlÃ¥ av tofaktor autentisering', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Er du sikker pÃ¥ at du vil slÃ¥ av tofaktor autentisering for bruker: "%s"?', + 'Edit link' => 'Endre lenke', + 'Start to type task title...' => 'Skriv in tittel', + 'A task cannot be linked to itself' => 'Oppgaver kan ikke lenkes til seg selv', + 'The exact same link already exists' => 'Denne lenken finnes fra før', + 'Recurrent task is scheduled to be generated' => 'Gjentagende oppgave vil bli opprettet', + 'Score' => 'Poeng', + 'The identifier must be unique' => 'Identifikatoren mÃ¥ være unik', + 'This linked task id doesn\'t exists' => 'Den lenkede oppgave-IDen finnes ikke', + 'This value must be alphanumeric' => 'Verdien mÃ¥ være alfanumerisk', + 'Edit recurrence' => 'Endre gjentakelser', + 'Generate recurrent task' => 'Opprett gjentagende oppgave', + 'Trigger to generate recurrent task' => 'Betingelse for Ã¥ generere gjentakende oppgave', + 'Factor to calculate new due date' => 'Faktor for Ã¥ beregne ny tidsfrist', + 'Timeframe to calculate new due date' => 'Tidsramme for Ã¥ beregne ny tidsfrist', + 'Base date to calculate new due date' => 'Grunnlagsdato for Ã¥ beregne ny tidsfrist', + 'Action date' => 'Hendelsesdato', + 'Base date to calculate new due date: ' => 'Grunnlagsdato for Ã¥ beregne ny tidsfrist', + 'This task has created this child task: ' => 'Denne oppgaven har opprettet denne relaterte oppgaven', + 'Day(s)' => 'Dager', + 'Existing due date' => 'Gjeldende tidsfrist', + 'Factor to calculate new due date: ' => 'Faktor for Ã¥ beregne ny tidsfrist', + 'Month(s)' => 'MÃ¥neder', + 'This task has been created by: ' => 'Denne oppgaven er opprettet av:', + 'Recurrent task has been generated:' => 'Repeterende oppgave er opprettet', + 'Timeframe to calculate new due date: ' => 'Tidsrom for beregning av ny tidsfrist: ', + 'Trigger to generate recurrent task: ' => 'Trigger for Ã¥ generere gjentagende oppgave: ', + 'When task is closed' => 'NÃ¥r oppgaven er lukket', + 'When task is moved from first column' => 'NÃ¥r oppgaven er flyttet fra første kolon', + 'When task is moved to last column' => 'NÃ¥r oppgaven er flyttet til siste kolonne', + 'Year(s)' => 'Ã¥r', + 'Project settings' => 'Prosjektinnstillinger', + 'Automatically update the start date' => 'Oppdater automatisk start-datoen', + 'iCal feed' => 'iCal strøm', + 'Preferences' => 'Preferanser', + 'Security' => 'Sikkerhet', + 'Two factor authentication disabled' => 'Dobbelgodkjenning deaktivert', + 'Two factor authentication enabled' => 'Dobbelgodkjenning aktivert', + 'Unable to update this user.' => 'Kan ikke oppdatere brukeren', + 'There is no user management for personal projects.' => 'Private prosjekter har ikke brukeradministrasjon', + 'User that will receive the email' => 'Bruker som vil motta den nye e-posten', + 'Email subject' => 'E-post emne', + 'Date' => 'Dato', + 'Add a comment log when moving the task between columns' => 'Legg til en kommentar i loggen nÃ¥r en oppgave flyttes mellom kolonnene', + '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' => 'GjenÃ¥pne oppgave', + 'Notification' => 'Varsel', + '%s moved the task #%d to the first swimlane' => '%s flyttet oppgaven #%d til første svømmebane', + 'Swimlane' => 'Svømmebane', + '%s moved the task %s to the first swimlane' => '%s flyttet oppgaven %s til første svømmebane', + '%s moved the task %s to the swimlane "%s"' => '%s flyttet oppgaven %s til svømmebanen "%s"', + 'This report contains all subtasks information for the given date range.' => 'Rapporten viser informasjon om alle deloppgaver for valgte tidsrom.', + 'This report contains all tasks information for the given date range.' => 'Rapporten inneholder informasjon om alle oppgaver for valgte tidsrom', + 'Project activities for %s' => 'Prosjektaktiviteter for %s', + 'view the board on Kanboard' => 'vis tavlen', + 'The task has been moved to the first swimlane' => 'Oppgaven er flyttet til første svømmebane', + 'The task has been moved to another swimlane:' => 'Oppgaven er flyttet til en annen svømmebane:', + 'New title: %s' => 'Ny tittel: %s', + 'The task is not assigned anymore' => 'Oppgaven har ikke lenger noen ansvarlig', + 'New assignee: %s' => 'Ny ansvarlig: %s', + 'There is no category now' => 'Ingen kategori nÃ¥', + 'New category: %s' => 'Ny kategori: %s', + 'New color: %s' => 'Ny farge: %s', + 'New complexity: %d' => 'Ny kompleksitet: %d', + 'The due date has been removed' => 'Fristdato er fjernet', + 'There is no description anymore' => 'Ikke lenger noen beskrivelse', + 'Recurrence settings has been modified' => 'Instillinger for gjentagelse er endret', + 'Time spent changed: %sh' => 'Tidsforbruk er endret: %sh', + 'Time estimated changed: %sh' => 'Tidsestimat er endret: %sh', + 'The field "%s" has been updated' => 'Feltet "%s" er oppdatert', + 'The description has been modified:' => 'Beskrivelsen er endret', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Ønsker du virkelig Ã¥ lukke oppgaven "%s" og alle tilhørende deloppgaver?', + 'I want to receive notifications for:' => 'Jeg vil motta varslinger om:', + 'All tasks' => 'Alle oppgaver', + 'Only for tasks assigned to me' => 'Kun oppgaver som er tildelt meg', + 'Only for tasks created by me' => 'Kun oppgaver som er opprettet av meg', + 'Only for tasks created by me and tasks assigned to me' => 'Kun oppgaver som er opprettet av meg og tildelt meg', + '%%Y-%%m-%%d' => '%%d-%%m-%%Y', + 'Total for all columns' => 'Totalt for alle kolonner', + 'You need at least 2 days of data to show the chart.' => 'Du mÃ¥ ha minimum 2 dager med data for Ã¥ kunne se dette diagrammet', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Stopp timer', + 'Start timer' => 'Start timer', + 'My activity stream' => 'Aktivitetslogg', + 'Search tasks' => 'Søk oppgave', + 'Reset filters' => 'Nullstill filter', + 'My tasks due tomorrow' => 'Mine oppgaver med frist i morgen', + 'Tasks due today' => 'Oppgaver med frist i dag', + 'Tasks due tomorrow' => 'Oppgaver med frist i morgen', + 'Tasks due yesterday' => 'Oppgaver med frist i gÃ¥r', + 'Closed tasks' => 'Fullførte oppgaver', + 'Open tasks' => 'Ã…pne oppgaver', + 'Not assigned' => 'Ikke tildelt', + 'View advanced search syntax' => 'Vis hjelp for avansert søk ', + 'Overview' => 'Oversikt', + 'Board/Calendar/List view' => 'Oversikt/kalender/listevisning', + 'Switch to the board view' => 'Oversiktsvisning', + 'Switch to the list view' => 'Listevisning', + 'Go to the search/filter box' => 'GÃ¥ til søk/filter', + 'There is no activity yet.' => 'Ingen aktiviteter ennÃ¥.', + 'No tasks found.' => 'Ingen oppgaver funnet', + 'Keyboard shortcut: "%s"' => 'Hurtigtaster: "%s"', + 'List' => 'Liste', + 'Filter' => 'Filter', + 'Advanced search' => 'Avansert søk', + 'Example of query: ' => 'Eksempel pÃ¥ spørring', + 'Search by project: ' => 'Søk etter prosjekt', + 'Search by column: ' => 'Søk etter kolonne', + 'Search by assignee: ' => 'Søk etter tildelt', + 'Search by color: ' => 'Søk etter farge', + 'Search by category: ' => 'Søk etter kategori', + 'Search by description: ' => 'Søk etter beskrivelse', + 'Search by due date: ' => 'Søk etter frist', + 'Average time spent in each column' => 'Gjennomsnittlig tid i hver kolonne', + 'Average time spent' => 'Gjennomsnittlig tidsbruk', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Dette diagrammet viser gjennomsnittlig tid i hver kolonne for de siste %d oppgavene.', + 'Average Lead and Cycle time' => 'Gjennomsnittlig ledetid og syklustid', + 'Average lead time: ' => 'Gjennomsnittlig ledetid', + 'Average cycle time: ' => 'Gjennomsnitlig syklustid', + 'Cycle Time' => 'Syklustid', + 'Lead Time' => 'Ledetid', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Dette diagrammet viser gjennomsnittlig ledetid og syklustid for de siste %d oppgavene.', + 'Average time into each column' => 'Gjennomsnittlig tid i hver kolonne', + 'Lead and cycle time' => 'Ledetid og syklustid', + 'Lead time: ' => 'Ledetid', + 'Cycle time: ' => 'Syklustid', + 'Time spent in each column' => 'Tid brukt i hver kolonne', + 'The lead time is the duration between the task creation and the completion.' => 'Ledetid er tidsrommet fra oppgaven opprettes til den er fullført', + 'The cycle time is the duration between the start date and the completion.' => 'Syklustid er tidsrommet fra oppgaven starter til den er fullført', + 'If the task is not closed the current time is used instead of the completion date.' => 'Dersom oppgaven ikke er lukket brukes dagens dato i stedet for fullført dato.', + 'Set the start date automatically' => 'Sett startdato automatisk', + 'Edit Authentication' => 'Endre autentisering', + 'Remote user' => 'Ekstern bruker', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Eksterne brukere har ikke passordet lagret i Kanboard. Eksempel: LDAP, Google og Github-kontoer.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Hvis du har krysset av for "Deaktiver innloggingsbilde" vil opplysninger som tastes inn i innloggingsskjemaet bli ignorert', + 'Default task color' => 'Standard oppgavefarge', + 'This feature does not work with all browsers.' => 'Denne funksjonen virker ikke i alle nettlesere', + 'There is no destination project available.' => 'Finner ikke noe mÃ¥lprosjekt', + 'Trigger automatically subtask time tracking' => 'Automatisk tidsforbruk for deloppgaver', + 'Include closed tasks in the cumulative flow diagram' => 'Ta med lukkede oppgaver i kumulativt flytdiagram', + 'Current swimlane: %s' => 'NÃ¥værende svømmebane: %s', + 'Current column: %s' => 'NÃ¥værende kolonne: %s', + 'Current category: %s' => ': %s', + 'no category' => 'ingen kategori', + 'Current assignee: %s' => 'Tildelt til %s', + 'not assigned' => 'ikke tildelt', + 'Author:' => 'Opprettet av', + 'contributors' => 'bidragsytere', + 'License:' => 'Lisens:', + 'License' => 'Lisens', + 'Enter the text below' => 'Skriv inn bokstavene du ser i bildet', + 'Start date:' => 'Startdato:', + 'Due date:' => 'Frist:', + 'People who are project managers' => 'Prosjektledere', + 'People who are project members' => 'Prosjektmedlemmer', + 'NOK - Norwegian Krone' => 'NOK - Norske kroner', + 'Show this column' => 'Vis denne kolonnen', + 'Hide this column' => 'Skjul denne kolonnen', + 'End date' => 'Sluttdato', + 'Users overview' => 'Brukeroversikt', + 'Members' => 'Medlemmer', + 'Shared project' => 'Delt prosjekt', + 'Project managers' => 'Prosjektledere', + 'Projects list' => 'Prosjektliste', + 'End date:' => 'Sluttdato:', + 'Change task color when using a specific task link' => 'Endre oppgavefarge nÃ¥r det brukes en definert oppgavelenke', + 'Task link creation or modification' => 'Oppgavelenke opprettet eller endret', + 'Milestone' => 'Milepæl', + 'Reset the search/filter box' => 'Nullstill søk/filter', + 'Documentation' => 'Dokumentasjon', + 'Author' => 'Forfatter', + 'Version' => 'Versjon', + 'Plugins' => 'Innstikk', + 'There is no plugin loaded.' => 'Ingen innstikk er lastet', + 'My notifications' => 'Mine varslinger', + 'Custom filters' => 'Egendefinerte filtre', + 'Your custom filter has been created successfully.' => 'Egendefinert filter er opprettet.', + 'Unable to create your custom filter.' => 'Feil ved oppretting av egendefinert filter.', + 'Custom filter removed successfully.' => 'Egendefinert filter er slettet.', + 'Unable to remove this custom filter.' => 'Feil ved sletting av egendefinert filter.', + 'Edit custom filter' => 'Endre egendefinert filter', + 'Your custom filter has been updated successfully.' => 'Egendefinert filter er endret.', + 'Unable to update custom filter.' => 'Feil ved endring av egendefinert filter.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nytt vedlegg til oppgaven #%d: %s', + 'New comment on task #%d' => 'Ny kommentar pÃ¥ oppgaven #%d', + 'Comment updated on task #%d' => 'Kommentar endret for oppgaven #%d', + 'New subtask on task #%d' => 'Ny deloppgave for oppgaven #%d', + 'Subtask updated on task #%d' => 'Deloppgave er endret for oppgave #%d', + 'New task #%d: %s' => 'Ny oppgave #%d: %s', + 'Task updated #%d' => 'Oppgaven #%d er endret', + 'Task #%d closed' => 'Oppgaven #%d er lukket', + 'Task #%d opened' => 'Oppgaven #%d er Ã¥pnet', + 'Column changed for task #%d' => 'Kolonne endret for oppgaven #%d', + 'New position for task #%d' => 'Ny posisjon for oppgaven #%d', + 'Swimlane changed for task #%d' => 'Svømmebane er endret for oppgaven #%d', + 'Assignee changed on task #%d' => 'Ansvarlig er endret for oppgaven #%d', + '%d overdue tasks' => '%d forfalte oppgaver', + 'No notification.' => 'Ingen varslinger', + 'Mark all as read' => 'Merk alle som lest', + 'Mark as read' => 'Merk som lest', + 'Total number of tasks in this column across all swimlanes' => 'Totalt antall oppgaver i denne kolonnen gjennom alle svømmebaner', + 'Collapse swimlane' => 'SlÃ¥ sammen svømmebanen', + 'Expand swimlane' => 'Fold ut svømmebane', + 'Add a new filter' => 'Lag nytt filter', + 'Share with all project members' => 'Del med alle prosjektdeltagere', + 'Shared' => 'Delt', + 'Owner' => 'Eier', + 'Unread notifications' => 'Uleste varslinger', + 'Notification methods:' => 'Varslingsmetoder', + 'Unable to read your file' => 'Kan ikke lese filen', + '%d task(s) have been imported successfully.' => '%d oppgave(r) er importert.', + 'Nothing has been imported!' => 'Ingenting ble importert!', + 'Import users from CSV file' => 'Importere brukere fra CSV-fil', + '%d user(s) have been imported successfully.' => '%d bruker(e) er imporert.', + 'Comma' => 'Komma', + 'Semi-colon' => 'Semikolon', + 'Tab' => 'Tabulator', + 'Vertical bar' => 'Loddrett strek', + 'Double Quote' => 'Rett sitattegn', + 'Single Quote' => 'Rett apostrof', + '%s attached a file to the task #%d' => '%s la ved en fil til oppgaven #%d', + 'There is no column or swimlane activated in your project!' => 'Prosjektet har ingen aktive kolonner eller svømmebaner!', + 'Append filter (instead of replacement)' => 'Tilføy filter (i stedet for Ã¥ erstatte)', + 'Append/Replace' => 'Tilføu/erstatt', + 'Append' => 'Tilføy', + 'Replace' => 'Erstatt', + 'Import' => 'Importer', + 'Change sorting' => 'Endre sortering', + 'Tasks Importation' => 'Importere oppgaver', + 'Delimiter' => 'Feltavgrenser', + 'Enclosure' => 'Streng skilletegn', + 'CSV File' => 'CSV-fil', + 'Instructions' => 'Instruksjoner', + 'Your file must use the predefined CSV format' => 'Filen mÃ¥ ha et forhÃ¥ndsdefinert CSV-format', + 'Your file must be encoded in UTF-8' => 'Filen mÃ¥ være kodet i UTF-8', + 'The first row must be the header' => 'Første rad mÃ¥ være overskrifter', + 'Duplicates are not verified for you' => 'Det blir ikke sjekket for duplikater', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Tidsfrister mÃ¥ angis i ISO-format: Ã…Ã…Ã…Ã…-MM-DD', + 'Download CSV template' => 'Last ned en CSV-mal', + 'No external integration registered.' => 'Ingen ekstern intergasjon er registrert', + 'Duplicates are not imported' => 'Duplikater importeres ikke', + 'Usernames must be lowercase and unique' => 'Brukernavn mÃ¥ være unike og skrevet i smÃ¥ bokstaver', + 'Passwords will be encrypted if present' => 'Eventuelt oppgitte passord vil blir kryptert', + '%s attached a new file to the task %s' => '%s la ved en ny fil til oppgaven %s', + 'Link type' => 'Relasjonstype', + 'Assign automatically a category based on a link' => 'Tildel kategori automatisk basert pÃ¥ lenke', + 'BAM - Konvertible Mark' => 'BAM - Bosniske mark', + 'Assignee Username' => 'Brukernavn ansvarlig', + 'Assignee Name' => 'Fullt navn ansvarlig', + 'Groups' => 'Grupper', + 'Members of %s' => 'Medlemmer av %s', + 'New group' => 'Ny gruppe', + 'Group created successfully.' => 'Gruppen er opprettet', + 'Unable to create your group.' => 'Kunne ikke opprette gruppen', + 'Edit group' => 'Endre gruppe', + 'Group updated successfully.' => 'Gruppen er endret', + 'Unable to update your group.' => 'Kunne ikke endre gruppen', + 'Add group member to "%s"' => 'Legg gruppemedlem til "%s"', + 'Group member added successfully.' => 'Gruppemedlem lagt til', + 'Unable to add group member.' => 'Kunne ikke legge til gruppemedlem', + 'Remove user from group "%s"' => 'Fjern bruker fra gruppen "%s"', + 'User removed successfully from this group.' => 'Bruker er fjernet fra denne gruppen.', + 'Unable to remove this user from the group.' => 'Kunne ikke fjerne bruker fra gruppen.', + 'Remove group' => 'Fjern gruppe', + 'Group removed successfully.' => 'Gruppen er fjernet', + 'Unable to remove this group.' => 'Kunne ikke fjerne gruppen', + 'Project Permissions' => 'Prosjektrettigheter', + 'Manager' => 'Leder', + 'Project Manager' => 'Prosjektleder', + 'Project Member' => 'Prosjektdeltager', + 'Project Viewer' => 'Kun lesetilgang', + 'Your account is locked for %d minutes' => 'Kontoen din vil være lÃ¥st i %d minutter', + 'Invalid captcha' => 'Ugyldig captcha', + 'The name must be unique' => 'Navnet mÃ¥ være unikt', + 'View all groups' => 'Vis alle grupper', + 'There is no user available.' => 'Ingen tilgjengelig bruker', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Ønsker du virkelig Ã¥ fjerne bruker "%s" fra gruppen "%s"?', + 'There is no group.' => 'Ingen grupper opprettet', + 'Add group member' => 'Legg til medlem', + 'Do you really want to remove this group: "%s"?' => 'Ønsker du virkelig Ã¥ fjerne gruppen "%s"', + 'There is no user in this group.' => 'Gruppen har ingen medlemmer', + 'Permissions' => 'Rettigheter', + 'Allowed Users' => 'Brukere med tilgang', + 'No specific user has been allowed.' => 'Ingen brukere er lagt inn foreløpig', + 'Role' => 'Rolle', + 'Enter user name...' => 'Skriv brukenavn', + 'Allowed Groups' => 'Brukergrupper med tilgang', + 'No group has been allowed.' => 'Ingen brukergrupper er lagt inn foreløpig', + 'Group' => 'Gruppe', + 'Group Name' => 'Gruppenavn', + 'Enter group name...' => 'Skriv gruppenavn', + 'Role:' => 'Rolle', + 'Project members' => 'Prosjektmedlemmer', + '%s mentioned you in the task #%d' => '%s nevnte deg i oppgaven #%d', + '%s mentioned you in a comment on the task #%d' => '%s nevnte deg i en kommentar pÃ¥ oppgaven #%d', + 'You were mentioned in the task #%d' => 'Du ble nevnt i oppgaven #%d', + 'You were mentioned in a comment on the task #%d' => 'Du ble nevnt i en kommentar pÃ¥ oppgaven #%d', + 'Estimated hours: ' => 'Estimerte timer:', + 'Actual hours: ' => 'Faktisk medgÃ¥tte timer', + 'Hours Spent' => 'Brukt timer', + 'Hours Estimated' => 'Estimerte timer', + 'Estimated Time' => 'Estimert tid', + 'Actual Time' => 'MedgÃ¥tt tid', + 'Estimated vs actual time' => 'Estimert mot virkelig tid', + 'RUB - Russian Ruble' => 'RUB - Russiske rubler', + 'Assign the task to the person who does the action when the column is changed' => 'Tildel oppgaven til personen som flytter den til en annen kolonne', + 'Close a task in a specific column' => 'Lukk en oppgave i en angitt kolonne', + 'Time-based One-time Password Algorithm' => 'Algoritme for tidsbasert engangspassord', + 'Two-Factor Provider: ' => 'Tofaktorleverandør:', + 'Disable two-factor authentication' => 'Deaktiver tofaktorautentisering', + 'Enable two-factor authentication' => 'Aktiver tofaktorautentisering', + 'There is no integration registered at the moment.' => 'Ingen registrert integrasjon.', + 'Password Reset for Kanboard' => 'Nytt passord til Kanboard', + 'Forgot password?' => 'Glemt passord?', + 'Enable "Forget Password"' => 'Bruk "Glemt passord"', + 'Password Reset' => 'Nytt passord', + 'New password' => 'Nytt passord', + 'Change Password' => 'Endre passord', + 'To reset your password click on this link:' => 'Bruk denne lenken for Ã¥ tilbakestille passordet ditt:', + 'Last Password Reset' => 'Forrige passordendring', + 'The password has never been reinitialized.' => 'Passordet er aldri blitt endret', + 'Creation' => 'Opprettet', + 'Expiration' => 'Utløper', + 'Password reset history' => 'Passordhistorikk', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Alle oppgaver i kolonne "%s" og svømmebane "%s" er lukket.', + 'Do you really want to close all tasks of this column?' => 'Ønsker du virkelig Ã¥ lukke ALLE saker i kolonnen?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d oppgave(r) i kolonne "%s" og svømmebane "%s" vil bli lukket.', + 'Close all tasks in this column and this swimlane' => 'Lukk alle saker i denne kolonnen', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Det finnes ingen innstikk for prosjektvarsling. Du kan likevel sette opp individuelle varslinger i brukerprofilen din.', + 'My dashboard' => 'Mitt dashbord', + 'My profile' => 'Min profil', + 'Project owner: ' => 'Prosjekteier: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Du kan lage en valgfri, alfanumerisk prosjektkode — for eksempel MITTPROSJEKT', + 'Project owner' => 'Prosjekteier', + 'Personal projects do not have users and groups management.' => 'Private prosjekter har ikke brukere eller gruppeadministrasjon', + 'There is no project member.' => 'Ingen prosjektmedlemmer', + 'Priority' => 'Prioritet', + 'Task priority' => 'Prioritet', + 'General' => 'Generelt', + 'Dates' => 'Datoer', + 'Default priority' => 'Standard', + 'Lowest priority' => 'Lav', + 'Highest priority' => 'Høy', + 'Close a task when there is no activity' => 'Lukk enoppgave nÃ¥r det ikke er noen aktivitet', + 'Duration in days' => 'Varighet i dager', + 'Send email when there is no activity on a task' => 'Send e-post nÃ¥r det ikke er noen aktivitet pÃ¥ en oppgave', + 'Unable to fetch link information.' => 'Kan ikke hente lenkeinformasjon', + 'Daily background job for tasks' => 'Daglig bakgrunnsjobb for oppgaver', + 'Auto' => 'Auto', + 'Related' => 'Relasjon', + 'Attachment' => 'Vedlegg', + 'Web Link' => 'Web-lenke', + 'External links' => 'Eksterne lenker', + 'Add external link' => 'Ny ekstern lenke', + 'Type' => 'Type', + 'Dependency' => 'Avhenger av', + 'Add internal link' => 'Lag intern lenke', + 'Add a new external link' => 'Lag ekstern lenke', + 'Edit external link' => 'Endre ekstern lenke', + 'External link' => 'Ekstern lenke', + 'Copy and paste your link here...' => 'Kopier lenken og lim den inn her', + 'URL' => 'URL', + 'Internal links' => 'Interne lenker', + 'Assign to me' => 'Tildel denne til meg', + 'Me' => 'Meg', + 'Do not duplicate anything' => 'Ingen duplisering', + 'Projects management' => 'Prosjektadministrasjon', + 'Users management' => 'Brukeradministrasjon', + 'Groups management' => 'Gruppeadministrasjon', + 'Create from another project' => 'Lag fra et annet prosjekt', + 'open' => 'Ã¥pen', + 'closed' => 'lukket', + 'Priority:' => 'Prioritet', + 'Reference:' => 'Referanse', + 'Complexity:' => 'Kompleksitet', + 'Swimlane:' => 'Svømmebane', + 'Column:' => 'Kolonne', + 'Position:' => 'Posisjon', + 'Creator:' => 'Opprettet av', + 'Time estimated:' => 'Estimert tidsforbruk', + '%s hours' => '%s timer', + 'Time spent:' => 'MedgÃ¥tt tid', + 'Created:' => 'Opprettet', + 'Modified:' => 'Endret', + 'Completed:' => 'Fullført', + 'Started:' => 'Startet:', + 'Moved:' => 'Flyttet:', + 'Task #%d' => 'Oppgave #%d', + 'Time format' => 'Tidsformat', + 'Start date: ' => 'Startdato:', + 'End date: ' => 'Sluttdato:', + 'New due date: ' => 'Ny frist:', + 'Start date changed: ' => 'Startdato endret:', + 'Disable personal projects' => 'Deaktiver mulighet for private prosjekter', + 'Do you really want to remove this custom filter: "%s"?' => 'Ønsker du virkelig Ã¥ fjerne det egendefinerte filteret: "%s"?', + 'Remove a custom filter' => 'Fjern egendefinert filter', + 'User activated successfully.' => 'Brukeren er aktivert.', + 'Unable to enable this user.' => 'Kan ikke aktivere brukeren.', + 'User disabled successfully.' => 'Brukeren er deaktivert.', + 'Unable to disable this user.' => 'Kan ikke deaktivere brukeren.', + 'All files have been uploaded successfully.' => 'Alle filer er lastet opp.', + 'The maximum allowed file size is %sB.' => 'Maks. tillatte filstørrelse er %sB', + 'Drag and drop your files here' => 'Dra og slipp filene her', + 'choose files' => 'velg filer', + 'View profile' => 'Vis profil', + 'Two Factor' => 'Tofaktor', + 'Disable user' => 'Deaktiver bruker', + 'Do you really want to disable this user: "%s"?' => 'Ønsker du virkelig Ã¥ DEAKTIVERE denne brukeren: "%s"?', + 'Enable user' => 'Aktiver bruker', + 'Do you really want to enable this user: "%s"?' => 'Ønsker du virkelig Ã¥ AKTIVERE denne brukeren: "%s"?', + 'Download' => 'Last ned', + 'Uploaded: %s' => 'Lastet opp: %s', + 'Size: %s' => 'Størrelse: %s', + 'Uploaded by %s' => 'Lastet opp av %s', + 'Filename' => 'Filnavn', + 'Size' => 'Størrelse', + 'Column created successfully.' => 'Kolonnen er opprettet', + 'Another column with the same name exists in the project' => 'Kolonnen finnes allerede i dette prosjektet', + 'Default filters' => 'Standardfiltere', + 'Your board doesn\'t have any columns!' => 'Tavlen har ingen kolonner!', + 'Change column position' => 'Endre kolonneplassering', + 'Switch to the project overview' => 'Bytt til prosjektoversikt', + 'User filters' => 'Brukerfilter', + 'Category filters' => 'Kategorifiltere', + 'Upload a file' => 'Last opp fil', + 'View file' => 'Vis fil', + 'Last activity' => 'Nyeste aktivitet', + 'Change subtask position' => 'Endre posisjon for deloppgave', + 'This value must be greater than %d' => 'Verdien mÃ¥ være større en %d', + 'Another swimlane with the same name exists in the project' => 'Denne svømmebanen finnes allerede i dette prosjektet', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Eksempel: https://eksempel.kanboard.org/ (brukes for Ã¥ lage absolutte URLer)', + 'Actions duplicated successfully.' => 'Handlinger er duplisert.', + 'Unable to duplicate actions.' => 'Feil ved duplisering av handlinger.', + 'Add a new action' => 'Ny automatisk handling', + 'Import from another project' => 'Importer fra et annet prosjekt', + 'There is no action at the moment.' => 'Ingen automatiske handlinger', + 'Import actions from another project' => 'Importer handlinger fra et annet prosjekt', + 'There is no available project.' => 'Ingen prosjekter', + 'Local File' => 'Lokal fil', + 'Configuration' => 'Konfigurasjon', + 'PHP version:' => 'PHP versjon:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'OS versjon', + 'Database version:' => 'Databaseversjon', + 'Browser:' => 'Nettleser', + 'Task view' => 'Vis oppgave', + 'Edit task' => 'Endre oppgave', + 'Edit description' => 'Endre beskrivelse', + 'New internal link' => 'Ny intern lenke', + 'Display list of keyboard shortcuts' => 'Oversikt over tastatursnarveier', + 'Avatar' => 'Profilbilde', + 'Upload my avatar image' => 'Last opp profilbilde', + 'Remove my image' => 'Fjern profilbilde', + 'The OAuth2 state parameter is invalid' => 'Feil parameter for OAuth2 tilstand', + 'User not found.' => 'Bruker ikke funnet', + 'Search in activity stream' => 'Søk i aktivitetsstrøm', + 'My activities' => 'Mine aktiviteter', + 'Activity until yesterday' => 'Aktivitet fram til i gÃ¥r', + 'Activity until today' => 'Aktivitet fram til i dag', + 'Search by creator: ' => 'Søk > Opprettet av:', + 'Search by creation date: ' => 'Søk > Opprettet dato:', + 'Search by task status: ' => 'Søk > Oppgavestatus', + 'Search by task title: ' => 'Søk > Oppgavetittel', + 'Activity stream search' => 'Søk > Aktivitetsstrøm', + 'Projects where "%s" is manager' => 'Prosjekter hvor "%s" er prosjektleder', + 'Projects where "%s" is member' => 'Prosjekter hvor "%s" er medlem', + 'Open tasks assigned to "%s"' => 'Ã…pne oppgaver tildelt "%s"', + 'Closed tasks assigned to "%s"' => 'Lukkede oppgaver tildelt "%s"', + 'Assign automatically a color based on a priority' => 'Tilordne farge automatisk ut fra prioritet', + 'Overdue tasks for the project(s) "%s"' => 'Forsinkede oppgaver for prosjekt(ene) "%s"', + 'Upload files' => 'Last opp filer', + 'Installed Plugins' => 'Installerte innstikk', + 'Plugin Directory' => 'Instikk (katalog)', + 'Plugin installed successfully.' => 'Innstikket er installert', + 'Plugin updated successfully.' => 'Innstikket er oppdatert', + 'Plugin removed successfully.' => 'Innstikket er fjernet', + 'Subtask converted to task successfully.' => 'Deloppgaven er konvertert til en oppgave', + 'Unable to convert the subtask.' => 'Kunne ikke konvertere deloppgaven', + 'Unable to extract plugin archive.' => 'Feil ved utpakking av innstikk', + 'Plugin not found.' => 'Finner ikke innstikk', + 'You don\'t have the permission to remove this plugin.' => 'Du har ikke rettigheter til Ã¥ fjerne dette innstikket', + 'Unable to download plugin archive.' => 'Feil ved nedlasting av innstikk', + 'Unable to write temporary file for plugin.' => 'Feil ved skriving av midlertidige filer for innstikket', + 'Unable to open plugin archive.' => 'Feil ved Ã¥pning av innstikk', + 'There is no file in the plugin archive.' => 'Innstikket inneholder ingen filer!', + 'Create tasks in bulk' => 'Masseoppretting av saker', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Denne Kanboardinstallasjonen er ikke satt opp for Ã¥ legge til innstikk fra brukergrensensnittet.', + 'There is no plugin available.' => 'Finner ingen innstikk', + 'Install' => 'Installer', + 'Update' => 'Oppdater', + 'Up to date' => 'Ajour', + 'Not available' => 'Ikke tilgjengelig', + 'Remove plugin' => 'Fjern innstikk', + 'Do you really want to remove this plugin: "%s"?' => 'Er du sikker pÃ¥ at du vil fjerne innstikket "%s"?', + 'Uninstall' => 'Avinstaller', + 'Listing' => 'Oversikt', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Administrere prosjekter', + 'Convert to task' => 'Gjør om til oppgave', + 'Convert sub-task to task' => 'Gjør om deloppgave til oppgave', + 'Do you really want to convert this sub-task to a task?' => 'Er du sikker pÃ¥ at du vil gjøre om denne deloppgaven til en oppgave?', + 'My task title' => 'Oppgavetittel', + 'Enter one task by line.' => 'Du kan skrive inn en oppgave pr rad', + 'Number of failed login:' => 'Antall feilede loginforsøk:', + 'Account locked until:' => 'Kontoen er lÃ¥st til:', + 'Email settings' => 'E-post', + 'Email sender address' => 'Avsenderadresse (e-post):', + 'Email transport' => 'E-post metode', + 'Webhook token' => 'Webhook token', + 'Project tags management' => 'Etiketter', + 'Tag created successfully.' => 'Etiketten er opprettet', + 'Unable to create this tag.' => 'Kunne ikke opprette etiketten', + 'Tag updated successfully.' => 'Etiketten er endret', + 'Unable to update this tag.' => 'Kunne ikke endre etiketten', + 'Tag removed successfully.' => 'Etiketten er slettet', + 'Unable to remove this tag.' => 'Kunne ikke slette etiketten', + 'Global tags management' => 'Administrere globale etiketter', + 'Tags' => 'Etiketter', + 'Tags management' => 'Etiketter', + 'Add new tag' => 'Ny etikett', + 'Edit a tag' => 'Endre etikett', + 'Project tags' => 'Etiketter', + 'There is no specific tag for this project at the moment.' => 'Prosjektet har for tiden ingen etiketter', + 'Tag' => 'Etikett', + 'Remove a tag' => 'Fjern etikett', + 'Do you really want to remove this tag: "%s"?' => 'Er du sikker pÃ¥ at du vil fjerne etiketten: "%s"?', + 'Global tags' => 'Globale etiketter', + 'There is no global tag at the moment.' => 'Ingen globale etiketter', + 'This field cannot be empty' => 'Dette feltet kan ikke være tomt', + 'Close a task when there is no activity in a specific column' => 'Lukk oppgaven nÃ¥r det ikke er aktivitet i en angitt kolonne', + '%s removed a subtask for the task #%d' => '%s slettet en deloppgave for oppgaven #%d', + '%s removed a comment on the task #%d' => '%s slettet en kommentar pÃ¥ oppgaven #%d', + 'Comment removed on task #%d' => 'Kommentar slettet fra oppgave #%d', + 'Subtask removed on task #%d' => 'Deloppgave slettet fra oppgave #%d', + 'Hide tasks in this column in the dashboard' => 'Ikke vis oppgaver i denne kolonnen pÃ¥ dashbordet', + '%s removed a comment on the task %s' => '%s slettet en kommentar pÃ¥ oppgaven %s', + '%s removed a subtask for the task %s' => '%s slettet en deloppgave for oppgaven %s', + 'Comment removed' => 'Kommentaren er fjernet', + 'Subtask removed' => 'Deloppgaven er slettet', + '%s set a new internal link for the task #%d' => '%s lagde en internlenke pÃ¥ oppgaven #%d', + '%s removed an internal link for the task #%d' => '%s fjernet en internlenke pÃ¥ oppgaven #%d', + 'A new internal link for the task #%d has been defined' => 'Det er laget en ny internlenke for oppgaven #%d', + 'Internal link removed for the task #%d' => 'Internlenke fjernet pÃ¥ oppgaven #%d', + '%s set a new internal link for the task %s' => '%s laget en ny internlenke for oppgaven %s', + '%s removed an internal link for the task %s' => '%s fjernet en internlenke for opgaven %s', + 'Automatically set the due date on task creation' => 'Sett tidsfrist automatisk ved opprettelse av oppgave', + 'Move the task to another column when closed' => 'Flytt oppgaven til en annen kolonne nÃ¥r den lukkes', + 'Move the task to another column when not moved during a given period' => 'Flytt oppgaven til en annen kolonne nÃ¥r den ikke flyttes i et gitt tidsrom', + 'Dashboard for %s' => 'Dashbord for %s', + 'Tasks overview for %s' => 'Oppgaveoversikt for %s', + 'Subtasks overview for %s' => 'Deloppgaver for %s', + 'Projects overview for %s' => 'Prosjektoversikt for %s', + 'Activity stream for %s' => 'Aktivitetsstrøm for %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Tilordne farge nÃ¥r oppgaven flyttes til angitt svømmebane', + 'Assign a priority when the task is moved to a specific swimlane' => 'Tilordne prioritet nÃ¥r oppgaven flyttes til angitt svømmebane', + 'User unlocked successfully.' => 'Brukeren er lÃ¥st opp', + 'Unable to unlock the user.' => 'Kunne ikke lÃ¥se opp brukeren', + 'Move a task to another swimlane' => 'Flytt oppgaven til en annen svømmebane', + 'Creator Name' => 'Opprettet av', + 'Time spent and estimated' => 'Estimert og medgÃ¥tt tid', + 'Move position' => 'Endre posisjon', + 'Move task to another position on the board' => 'Flytt oppgaven', + 'Insert before this task' => 'Sett inn før denne oppgaven', + 'Insert after this task' => 'Sett inn etter denne oppgaven', + 'Unlock this user' => 'LÃ¥s opp bruker', + 'Custom Project Roles' => 'Egendefinerte prosjektroller', + 'Add a new custom role' => 'Ny egendefinert rolle', + 'Restrictions for the role "%s"' => 'Restriksjoner for rollen "%s"', + 'Add a new project restriction' => 'Ny prosjektrestriksjon', + 'Add a new drag and drop restriction' => 'Ny dra og slipp-restriksjon', + 'Add a new column restriction' => 'Ny kolonnerestriksjon', + 'Edit this role' => 'Rediger rolle', + 'Remove this role' => 'Fjern denne rollen', + 'There is no restriction for this role.' => 'Denne rollen har ingen restriksjoner', + 'Only moving task between those columns is permitted' => 'Oppgaver kan kun flyttes mellom disse kolonnene', + 'Close a task in a specific column when not moved during a given period' => 'Lukk oppgave i angitt kolonne nÃ¥r den ikke flyttes i en angitt periode', + 'Edit columns' => 'Endre kolonner', + 'The column restriction has been created successfully.' => 'Kolonnerestriksjonen er opprettet', + 'Unable to create this column restriction.' => 'Kunne ikke opprette denne kolonnerestriksjonen', + 'Column restriction removed successfully.' => 'Kolonnerestriksjon fjernet', + 'Unable to remove this restriction.' => 'Kan ikke fjerne denne restriksjonen', + 'Your custom project role has been created successfully.' => 'Egendefinert prosjektrolle er oppdatert', + 'Unable to create custom project role.' => 'Kunne ikke opprette egendefinert prosjektrolle', + 'Your custom project role has been updated successfully.' => 'Egendefinert ptosjektrolle er endret', + 'Unable to update custom project role.' => 'Kunne ikke endre egendefinert prosjektrolle', + 'Custom project role removed successfully.' => 'Egendefinert prosjektrolle er fjernet', + 'Unable to remove this project role.' => 'Kunne ikke fjerne denne prosjektrollen', + 'The project restriction has been created successfully.' => 'Prosjektrestriksjonen er opprettet', + 'Unable to create this project restriction.' => 'Kunne ikke opprette prosjektrestriksjon', + 'Project restriction removed successfully.' => 'Prosjektrestriksjon er fjernet', + 'You cannot create tasks in this column.' => 'Du kan ikke opprette oppgaver i denne kolonnen', + 'Task creation is permitted for this column' => 'Oppretting av oppgaver er tillatt i denne kolonnen', + 'Closing or opening a task is permitted for this column' => 'Lukking/Ã¥pning av oppgaver er tillatt i denne kolonnen', + 'Task creation is blocked for this column' => 'Denne kolonnen er lÃ¥st for oppretting av nye oppgaver', + 'Closing or opening a task is blocked for this column' => 'Oppgaver i denne kolonnen er lÃ¥st for lukking/Ã¥pning', + 'Task creation is not permitted' => 'Kan ikke opprette oppgaver', + 'Closing or opening a task is not permitted' => 'Kan ikke lukke eller Ã¥pne oppgaver', + 'New drag and drop restriction for the role "%s"' => 'Ny dra-og-slipp-restriksjon for rollen "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Brukere med denne rollen vil bare kunne flytte oppgaver mellom kilde- og mÃ¥lkolonne.', + 'Remove a column restriction' => 'Fjern kolonnerestriksjon', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Ønsker du virkelig Ã¥ fjerne kolonnerestriksjonen: "%s" til "%s"?', + 'New column restriction for the role "%s"' => 'Ny kolonnerestriksjon for rollen "%s"', + 'Rule' => 'Regel', + 'Do you really want to remove this column restriction?' => 'Er du sikker pÃ¥ at du vil fjerne denne kolonnerestriksjonen', + 'Custom roles' => 'Egendefinerte roller', + 'New custom project role' => 'Ny egendefinert rolle', + 'Edit custom project role' => 'Endre egendefinert rolle', + 'Remove a custom role' => 'Fjern egendefinert rolle', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Ønsker du virkelig Ã¥ fjerne den egendefinerte rollen: "%s"? Alle brukere med denne rollen vil bli prosjektmedlemmer.', + 'There is no custom role for this project.' => 'Prosjektet har ingen egendefinerte roller', + 'New project restriction for the role "%s"' => 'Ny prosjektrestriksjon for rollen "%s"', + 'Restriction' => 'Restriksjon', + 'Remove a project restriction' => 'Fjern prosjektrestriksjon', + 'Do you really want to remove this project restriction: "%s"?' => 'Er du sikker pÃ¥ at du vil fjerne restriksjonen "%s"', + 'Duplicate to multiple projects' => 'Dupliser til flere prosjekter', + 'This field is required' => 'Feltet er pÃ¥krevet', + 'Moving a task is not permitted' => 'Flytting av oppgaven er ikke tillatt', + 'This value must be in the range %d to %d' => 'Verdien mÃ¥ være i omrÃ¥det %d to %d', + 'You are not allowed to move this task.' => 'Du har ikke rettigheter til Ã¥ flutte denne oppgaven', + 'API User Access' => 'API brukertilgang', + 'Preview' => 'ForhÃ¥ndsvisning', + 'Write' => 'Skriv', + 'Write your text in Markdown' => 'Du kan skrive teksten din i Markdown hvis du ønsker det.', + 'No personal API access token registered.' => 'Ingen personlig API tilgangsnøkkel er registrert.', + 'Your personal API access token is "%s"' => 'Din personlige API tilgangsnøkkel er "%s"', + 'Remove your token' => 'Fjern din nøkkel', + 'Generate a new token' => 'Lag ny nøkkel', + 'Showing %d-%d of %d' => 'Viser %d-%d of %d', + 'Outgoing Emails' => 'UtgÃ¥ende e-post', + 'Add or change currency rate' => 'Legg til eller endre valuta', + 'Reference currency: %s' => 'Referansevaluta: %s', + 'Add custom filters' => 'Lag egendefinerte filtere', + 'Export' => 'Eksporter', + 'Add link label' => 'Ny etikett', + 'Incompatible Plugins' => 'Ukompatible innstikk', + 'Compatibility' => 'Kompatibilitet', + 'Permissions and ownership' => 'Tillatelser og eierskap', + 'Priorities' => 'Prioriteter', + 'Close this window' => 'Lukk vindu', + 'Unable to upload this file.' => 'Kunne ikke laste opp filen', + 'Import tasks' => 'Importere oppgaver', + 'Choose a project' => 'Velg prosjekt', + 'Profile' => 'Profil', + 'Application role' => 'Rolle', + '%d invitations were sent.' => '%d invitasjoner ble sendt', + '%d invitation was sent.' => '%d invitasjon ble sendt', + 'Unable to create this user.' => 'Kan ikke opprette brukeren.', + 'Kanboard Invitation' => 'Kanboard invitasjon', + 'Visible on dashboard' => 'Synlig pÃ¥ dashbordet', + 'Created at:' => 'Opprettet:', + 'Updated at:' => 'Endret:', + 'There is no custom filter.' => 'Ingen egendefinerte filtre', + 'New User' => 'Ny bruker', + 'Authentication' => 'Autentisering', + 'If checked, this user will use a third-party system for authentication.' => 'Dersom dette er valgt vil brukeren logges inn via et tredjepartssystem', + 'The password is necessary only for local users.' => 'Passord er kun nødvendig for lokale brukere', + 'You have been invited to register on Kanboard.' => 'Du er invitert til Ã¥ registrere deg pÃ¥ Kanboard', + 'Click here to join your team' => 'Klikk her for Ã¥ bli med i teamet ditt', + 'Invite people' => 'Inviter personer', + 'Emails' => 'E-poster', + 'Enter one email address by line.' => 'Legg inn en e-postadresse pr. linje', + 'Add these people to this project' => 'Legg til disse personene i prosjektet', + 'Add this person to this project' => 'Legg til denne personen i prosjektet', + 'Sign-up' => 'Registrering', + 'Credentials' => 'Akkreditiver', + 'New user' => 'Ny bruker', + 'This username is already taken' => 'Brukernavn finnes fra før', + 'Your profile must have a valid email address.' => 'Brukerprofilen din mÃ¥ ha en gyldig e-postadresse.', + 'TRL - Turkish Lira' => 'TRL - Tyrkiske lira', + 'The project email is optional and could be used by several plugins.' => 'Du kan angi en e-postadresse for prosjektet, og den kan brukes av ulike innstikksmoduler', + 'The project email must be unique across all projects' => 'Prosjektets e-postadresse mÃ¥ være unik for alle prosjekter', + 'The email configuration has been disabled by the administrator.' => 'Administrator har deaktivert e-postinnstillinger', + 'Close this project' => 'Lukk dette prosjektet', + 'Open this project' => 'GjenÃ¥pne dette prosjektet', + 'Close a project' => 'Lukk et prosekt', + 'Do you really want to close this project: "%s"?' => 'Er du sikker pÃ¥ at vil lukke prosjektet: "%s"?', + 'Reopen a project' => 'GjenÃ¥pne prosjekt', + 'Do you really want to reopen this project: "%s"?' => 'Er du sikker pÃ¥ at du vil gjenÃ¥pne prosjektet: "%s"?', + 'This project is open' => 'Dette prosjektet er aktivt', + 'This project is closed' => 'Dette prosjektet er lukket', + 'Unable to upload files, check the permissions of your data folder.' => 'Kunne ikke laste opp filen!', + 'Another category with the same name exists in this project' => 'Kategorien finnes allerede i dette prosjektet', + 'Comment sent by email successfully.' => 'Kommentar sendt med e-post.', + 'Sent by email to "%s" (%s)' => 'Sendt med e-post til "%s" (%s)', + 'Unable to read uploaded file.' => 'Kan ikke lese opplastet fil.', + 'Database uploaded successfully.' => 'Databasen er lastet opp.', + 'Task sent by email successfully.' => 'Oppgaven er sendt med e-post.', + 'There is no category in this project.' => 'Prosjektet har ingen kategorier.', + 'Send by email' => 'Send i e-post', + 'Create and send a comment by email' => 'Opprett og send en kommentar med e-post', + 'Subject' => 'Emne', + 'Upload the database' => 'Last opp databasen', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Du kan laste opp en tidligere nedlastet Sqlite database (Gzip-format).', + 'Database file' => 'Databasefil', + 'Upload' => 'Last opp', + 'Your project must have at least one active swimlane.' => 'Prosjektet mÃ¥ ha minst en aktiv svømmebane', + 'Project: %s' => 'Prosjekt: %s', + 'Automatic action not found: "%s"' => 'Finner ingen automatisk handling: "%s"', + '%d projects' => '%d prosjekter', + '%d project' => '%d prosjekt', + 'There is no project.' => 'Ingen prosjekter', + 'Sort' => 'Sorter', + 'Project ID' => 'Prosjekt-ID', + 'Project name' => 'Prosjektnavn', + 'Public' => 'Felles', + 'Personal' => 'Privat', + '%d tasks' => '%d oppgaver', + '%d task' => '%d oppgave', + 'Task ID' => 'Oppgave-ID', + 'Assign automatically a color when due date is expired' => 'Tilordne farge automatisk nÃ¥r fristen utløper', + 'Total score in this column across all swimlanes' => 'Totalt resultat for denne kolonnen i alle svømmebaner', + 'HRK - Kuna' => 'HRK - Kroatiske kuna', + 'ARS - Argentine Peso' => 'ARS - Argentinske pesos', + 'COP - Colombian Peso' => 'COP - Columbianske pesos', + '%d groups' => '%d grupper', + '%d group' => '%d gruppe', + 'Group ID' => 'Gruppe-ID', + 'External ID' => 'Ekstern ID', + '%d users' => '%d brukere', + '%d user' => '%d bruker', + 'Hide subtasks' => 'Skjul deloppgaver', + 'Show subtasks' => 'Vis deloppgaver', + 'Authentication Parameters' => 'Autensitering', + 'API Access' => 'API-tilgang', + 'No users found.' => 'Finner ingen brukere', + 'User ID' => 'Bruker-ID', + 'Notifications are activated' => 'Varslinger aktivert', + 'Notifications are disabled' => 'Varslinger deaktivert', + 'User disabled' => 'Bruker er deaktivert', + '%d notifications' => '%d varslinger', + '%d notification' => '%d varsling', + 'There is no external integration installed.' => 'Ingen ekstern interasjon er installert.', + 'You are not allowed to update tasks assigned to someone else.' => 'Du kan ikke endre oppgaver hvor en annen bruker er ansvarlig', + 'You are not allowed to change the assignee.' => 'Du har ikke rettigheter til Ã¥ endre ansvarlig', + 'Task suppression is not permitted' => 'Endring av posisjon er ikke tillatt', + 'Changing assignee is not permitted' => 'Endring av ansvarlig er ikke tillatt', + 'Update only assigned tasks is permitted' => 'Du kan bare endre oppgaver du selv er ansvarlig for', + 'Only for tasks assigned to the current user' => 'Kun for oppgaver tilordnet aktiv bruker', + 'My projects' => 'Mine prosjekter', + 'You are not a member of any project.' => 'Du er ikke med i noen prosjekter', + 'My subtasks' => 'Mine deloppgaver', + '%d subtasks' => '%d deloppgaver', + '%d subtask' => '%d deloppgave', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Aktiv bruker kan kun flytte oppgaver mellom angitte kolonner', + '[DUPLICATE]' => 'DUPLISER', + 'DKK - Danish Krona' => 'DKK - Danske kroner', + 'Remove user from group' => 'Fjern bruker fra gruppen', + 'Assign the task to its creator' => 'Tildel oppgaven til den som har opprettet den', + 'This task was sent by email to "%s" with subject "%s".' => 'Denne oppgaven ble sendt pÃ¥ e-post til "%s" med emne "%s".', + 'Predefined Email Subjects' => 'ForhÃ¥ndsdefinerte e-post emner', + 'Write one subject by line.' => 'Skriv et emne pÃ¥ hver linje', + 'Create another link' => 'Lag en ny lenke', + 'BRL - Brazilian Real' => 'BRL - Brasilianske real', + 'Add a new Kanboard task' => 'Lag ny oppgave', + 'Subtask not started' => 'Deloppgaven er ikke startet', + 'Subtask currently in progress' => 'Deloppgaver i prosess', + 'Subtask completed' => 'Deloppgave fullført', + 'Subtask added successfully.' => 'Deloppgave er lagt til', + '%d subtasks added successfully.' => '%d deloppgaver er lagt til', + 'Enter one subtask by line.' => 'Legg inn en deloppgave pr. rad', + 'Predefined Contents' => 'ForhÃ¥ndsdefinert innhold', + 'Predefined contents' => 'ForhÃ¥ndsdefinert innhold', + 'Predefined Task Description' => 'ForhÃ¥ndsdefinert oppgavebeskrivelse', + 'Do you really want to remove this template? "%s"' => 'Ønsker du virkelig Ã¥ slette denne malen? "%s"', + 'Add predefined task description' => 'Ny forhÃ¥ndsdefinert oppgavebeskrivelse', + 'Predefined Task Descriptions' => 'ForhÃ¥ndsdefinerte oppgavebeskrivelser', + 'Template created successfully.' => 'Malen er opprettet', + 'Unable to create this template.' => 'Kunne ikke opprette malen', + 'Template updated successfully.' => 'Malen er endret', + 'Unable to update this template.' => 'Kunne ikke endre malen', + 'Template removed successfully.' => 'Malen er slettet', + 'Unable to remove this template.' => 'Kunne ikke slette malen', + 'Template for the task description' => 'Mal for oppgavbeskrivelse', + 'The start date is greater than the end date' => 'Startdato er større en sluttdato', + 'Tags must be separated by a comma' => 'Etiketter mÃ¥ skilles med komma', + 'Only the task title is required' => 'Kun oppgavetittel er pÃ¥krevet', + 'Creator Username' => 'Opprettet av', + 'Color Name' => 'Farge', + 'Column Name' => 'Kolonne', + 'Swimlane Name' => 'Svømmebane', + 'Time Estimated' => 'Estimert tid', + 'Time Spent' => 'MedgÃ¥tt tid', + 'External Link' => 'Ekstern lenke', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'OBS Forsiktig: Denne funksjonen aktiverer iCal-strøm, RSS-strøm og offentlig tavlevisning!', + 'Stop the timer of all subtasks when moving a task to another column' => 'Stopp timeren for alle deloppgaver nÃ¥r en oppgave flyttes til en annen kolonne', + 'Subtask Title' => 'Deloppgave', + 'Add a subtask and activate the timer when moving a task to another column' => 'Opprett en deloppgave og aktiver timeren nÃ¥r en oppgave flyttes til en annen kolonne', + 'days' => 'dager', + 'minutes' => 'minutter', + 'seconds' => 'sekunder', + 'Assign automatically a color when preset start date is reached' => 'Tilordne farge automatisk pÃ¥ valgt startdato', + 'Move the task to another column once a predefined start date is reached' => 'Flytt oppgaven til en annen lolonne pÃ¥ valgt startdato', + 'This task is now linked to the task %s with the relation "%s"' => 'Oppgaven er nÃ¥ lenket til oppgaven %s med relasjonen "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Lenken med relasjonen "%s" til oppgaven %s er fjernet', + 'Custom Filter:' => 'Egendefinert filter', + 'Unable to find this group.' => 'Finner ikke gruppen', + '%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 posisjon %d i kolonnen "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s flyttet oppgaven #%d til svømmebane "%s"', + '%sh spent' => '%st brukt', + '%sh estimated' => '%st estimert', + 'Select All' => 'Velg alle', + 'Unselect All' => 'Fravelg alle', + 'Apply action' => 'Utfør', + 'Move selected tasks to another column or swimlane' => 'Flytt merkede oppgaver til en annen kolonne', + 'Edit tasks in bulk' => 'Masseredigering av oppgaver', + 'Choose the properties that you would like to change for the selected tasks.' => 'Velg egenskapene du vil endre for valgte oppgaver.', + 'Configure this project' => 'Prosjekt', + 'Start now' => 'Start nÃ¥', + '%s removed a file from the task #%d' => '%s fjernet en fil fra oppgaven #%d', + 'Attachment removed from task #%d: %s' => 'Vedlegg fjernet fra oppgave #%d: %s', + 'No color' => 'Ingen farge', + 'Attachment removed "%s"' => 'Vedlegg fjernet "%s"', + '%s removed a file from the task %s' => '%s fjernet en fil fra oppgaven %s', + 'Move the task to another swimlane when assigned to a user' => 'Flytt oppgaven til en annen svømmebane nÃ¥r den tildeles en bruker', + 'Destination swimlane' => 'MÃ¥l-svømmebane', + 'Assign a category when the task is moved to a specific swimlane' => 'Tildel en kategori nÃ¥r oppgaven flyttes til en bestemt svømmebane', + 'Move the task to another swimlane when the category is changed' => 'Flytt oppgaven til en annen svømmebane nÃ¥r kategorien endres', + 'Reorder this column by priority (ASC)' => 'Sorter denne kolonnen etter prioritet (stigende)', + 'Reorder this column by priority (DESC)' => 'Sorter denne kolonnen etter prioritet (synkende)', + 'Reorder this column by assignee and priority (ASC)' => 'Sorter denne kolonnen etter ansvarlig og prioritet (stigende)', + 'Reorder this column by assignee and priority (DESC)' => 'Sorter denne kolonnen etter ansvarlig og prioritet (synkende)', + 'Reorder this column by assignee (A-Z)' => 'Sorter denne kolonnen etter ansvarlig (A–Å)', + 'Reorder this column by assignee (Z-A)' => 'Sorter denne kolonnen etter ansvarlig (Å–A)', + 'Reorder this column by due date (ASC)' => 'Sorter denne kolonnen etter forfallsdato (stigende)', + 'Reorder this column by due date (DESC)' => 'Sorter denne kolonnen etter forfallsdato (synkende)', + 'Reorder this column by id (ASC)' => 'Sorter denne kolonnen etter ID (stigende)', + 'Reorder this column by id (DESC)' => 'Sorter denne kolonnen etter ID (synkende)', + '%s moved the task #%d "%s" to the project "%s"' => '%s flyttet oppgaven #%d "%s" til prosjektet "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Oppgave #%d "%s" har blitt flyttet til prosjektet "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Flytt oppgaven til en annen kolonne nÃ¥r forfallsdatoen er mindre enn et bestemt antall dager', + 'Automatically update the start date when the task is moved away from a specific column' => 'Oppdater startdatoen automatisk nÃ¥r oppgaven flyttes fra en bestemt kolonne', + 'HTTP Client:' => 'HTTP-klient:', + 'Assigned' => 'Tildelt', + 'Task limits apply to each swimlane individually' => 'Oppgavegrenser gjelder for hver svømmebane individuelt', + 'Column task limits apply to each swimlane individually' => 'Kolonneoppgavegrenser gjelder for hver svømmebane individuelt', + 'Column task limits are applied to each swimlane individually' => 'Kolonneoppgavegrenser brukes pÃ¥ hver svømmebane individuelt', + 'Column task limits are applied across swimlanes' => 'Kolonneoppgavegrenser brukes pÃ¥ tvers av svømmebaner', + 'Task limit: ' => 'Oppgavegrense: ', + 'Change to global tag' => 'Endre til global tag', + 'Do you really want to make the tag "%s" global?' => 'Vil du virkelig gjøre taggen "%s" global?', + 'Enable global tags for this project' => 'Aktiver globale tagger for dette prosjektet', + 'Group membership(s):' => 'Gruppemedlemskap:', + '%s is a member of the following group(s): %s' => '%s er medlem av følgende gruppe(r): %s', + '%d/%d group(s) shown' => '%d/%d gruppe(r) vist', + 'Subtask creation or modification' => 'Opprettelse eller endring av deloppgave', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Tildel oppgaven til en bestemt bruker nÃ¥r den flyttes til en bestemt svømmebane', + 'Comment' => 'Kommentar', + 'Collapse vertically' => 'Skjul vertikalt', + 'Expand vertically' => 'Utvid vertikalt', + 'MXN - Mexican Peso' => 'MXN - Meksikansk peso', + 'Estimated vs actual time per column' => 'Estimert vs faktisk tid per kolonne', + 'HUF - Hungarian Forint' => 'HUF - Ungarsk forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Du mÃ¥ velge en fil for Ã¥ laste opp som din avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Filen du lastet opp er ikke et gyldig bilde! (Kun *.gif, *.jpg, *.jpeg og *.png er tillatt!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Angi forfallsdato automatisk nÃ¥r oppgaven flyttes fra en bestemt kolonne', + 'No other projects found.' => 'Ingen andre prosjekter funnet.', + 'Tasks copied successfully.' => 'Oppgaver kopiert vellykket.', + 'Unable to copy tasks.' => 'Kan ikke kopiere oppgaver.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Lyst tema', + 'Dark theme' => 'Mørkt tema', + 'Automatic theme - Sync with system' => 'Automatisk tema - Synkroniser med systemet', + 'Application managers or more' => 'Applikasjonsadministratorer eller høyere', + 'Administrators' => 'Administratorer', + 'Visibility:' => 'Synlighet:', + 'Standard users' => 'Standardbrukere', + 'Visibility is required' => 'Synlighet er pÃ¥krevd', + 'The visibility should be an app role' => 'Synligheten bør være en applikasjonsrolle', + 'Reply' => 'Svar', + '%s wrote: ' => '%s skrev: ', + 'Number of visible tasks in this column and swimlane' => 'Antall synlige oppgaver i denne kolonnen og svømmebanen', + 'Number of tasks in this swimlane' => 'Antall oppgaver i denne svømmebanen', + 'Unable to find another subtask in progress, you can close this window.' => 'Kan ikke finne en annen deloppgave i arbeid, du kan lukke dette vinduet.', + 'This theme is invalid' => 'Dette temaet er ugyldig', + 'This role is invalid' => 'Denne rollen er ugyldig', + 'This timezone is invalid' => 'Denne tidssonen er ugyldig', + 'This language is invalid' => 'Dette sprÃ¥ket er ugyldig', + 'This URL is invalid' => 'Denne URL-en er ugyldig', + 'Date format invalid' => 'Ugyldig datoformat', + 'Time format invalid' => 'Ugyldig tidsformat', + 'Invalid Mail transport' => 'Ugyldig e-posttransport', + 'Color invalid' => 'Ugyldig farge', + 'This value must be greater or equal to %d' => 'Denne verdien mÃ¥ være større enn eller lik %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Legg til en BOM i begynnelsen av filen (pÃ¥krevd for Microsoft Excel)', + 'Just add these tag(s)' => 'Bare legg til disse taggene', + 'Remove internal link(s)' => 'Fjern interne lenker', + 'Import tasks from another project' => 'Importer oppgaver fra et annet prosjekt', + 'Select the project to copy tasks from' => 'Velg prosjektet du vil kopiere oppgaver fra', + 'The total maximum allowed attachments size is %sB.' => 'Den totale maksimalt tillatte vedleggsstørrelsen er %sB.', + 'Add attachments' => 'Legg til vedlegg', + 'Task #%d "%s" is overdue' => 'Oppgaven #%d "%s" er forsinket', + 'Enable notifications by default for all new users' => 'Aktiver varslinger som standard for alle nye brukere', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Tilordne oppgaven til dens oppretter for bestemte kolonner hvis ingen ansvarlig er satt manuelt', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Tilordne en oppgave til innlogget bruker ved kolonneendring til angitt kolonne hvis ingen bruker er tilordnet', +]; diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php new file mode 100644 index 0000000..3bce85c --- /dev/null +++ b/app/Locale/nl_NL/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Geen', + 'Edit' => 'Bewerken', + 'Remove' => 'Verwijderen', + 'Yes' => 'Ja', + 'No' => 'Nee', + 'cancel' => 'annuleren', + 'or' => 'of', + 'Yellow' => 'Geel', + 'Blue' => 'Blauw', + 'Green' => 'Groen', + 'Purple' => 'Paars', + 'Red' => 'Rood', + 'Orange' => 'Oranje', + 'Grey' => 'Grijs', + 'Brown' => 'Bruin', + 'Deep Orange' => 'Dieporanje', + 'Dark Grey' => 'Donkergrijs', + 'Pink' => 'Roze', + 'Teal' => 'Groenblauw', + 'Cyan' => 'Cyaan', + 'Lime' => 'Limoen', + 'Light Green' => 'Lichtgroen', + 'Amber' => 'Amber', + 'Save' => 'Opslaan', + 'Login' => 'Inloggen', + 'Official website:' => 'Officiële website:', + 'Unassigned' => 'Niet toegewezen', + 'View this task' => 'Deze taak bekijken', + 'Remove user' => 'Gebruiker verwijderen', + 'Do you really want to remove this user: "%s"?' => 'Wil je deze gebruiker echt verwijderen: "%s" ?', + 'All users' => 'Alle gebruikers', + 'Username' => 'Gebruikersnaam', + 'Password' => 'Wachtwoord', + 'Administrator' => 'Administrator', + 'Sign in' => 'Inloggen', + 'Users' => 'Gebruikers', + 'Forbidden' => 'Geweigerd', + 'Access Forbidden' => 'Toegang geweigerd', + 'Edit user' => 'Gebruiker bewerken', + 'Logout' => 'Uitloggen', + 'Bad username or password' => 'Verkeerde gebruikersnaam of wachtwoord', + 'Edit project' => 'Project bewerken', + 'Name' => 'Naam', + 'Projects' => 'Projecten', + 'No project' => 'Geen project', + 'Project' => 'Project', + 'Status' => 'Status', + 'Tasks' => 'Taken', + 'Board' => 'Bord', + 'Actions' => 'Acties', + 'Inactive' => 'Inactief', + 'Active' => 'Actief', + 'Unable to update this board.' => 'Kan dit bord niet bijwerken.', + 'Disable' => 'Deactiveren', + 'Enable' => 'Activeren', + 'New project' => 'Nieuw project', + 'Do you really want to remove this project: "%s"?' => 'Wil je dit project echt verwijderen: "%s"?', + 'Remove project' => 'Project verwijderen', + 'Edit the board for "%s"' => 'Bord bewerken voor "%s"', + 'Add a new column' => 'Kolom toevoegen', + 'Title' => 'Titel', + 'Assigned to %s' => 'Toegewezen aan %s', + 'Remove a column' => 'Kolom verwijderen', + 'Unable to remove this column.' => 'Kan deze kolom niet verwijderen.', + 'Do you really want to remove this column: "%s"?' => 'Wil je deze kolom echt verwijderen: "%s"?', + 'Settings' => 'Instellingen', + 'Application settings' => 'Applicatie-instellingen', + 'Language' => 'Taal', + 'Webhook token:' => 'Webhook token:', + 'API token:' => 'API token:', + 'Database size:' => 'Database-grootte:', + 'Download the database' => 'Database downloaden', + 'Optimize the database' => 'Database optimaliseren', + '(VACUUM command)' => '(VACUUM commando)', + '(Gzip compressed Sqlite file)' => '(Gzip ingepakt Sqlite-bestand)', + 'Close a task' => 'Taak sluiten', + 'Column' => 'Kolom', + 'Color' => 'Kleur', + 'Assignee' => 'Toegewezene', + 'Create another task' => 'Nog een taak aanmaken', + 'New task' => 'Nieuwe taak', + 'Open a task' => 'Een taak openen', + 'Do you really want to open this task: "%s"?' => 'Wil je deze taak echt openen: "%s"?', + 'Back to the board' => 'Terug naar het bord', + 'There is nobody assigned' => 'Er is niemand toegewezen', + 'Column on the board:' => 'Kolom op het bord:', + 'Close this task' => 'Deze taak sluiten', + 'Open this task' => 'Deze taak openen', + 'There is no description.' => 'Er is geen omschrijving.', + 'Add a new task' => 'Een nieuwe taak toevoegen', + 'The username is required' => 'De gebruikersnaam is verplicht', + 'The maximum length is %d characters' => 'De maximale lengte is %d tekens', + 'The minimum length is %d characters' => 'De minimale lengte is %d tekens', + 'The password is required' => 'Het wachtwoord is verplicht', + 'This value must be an integer' => 'Deze waarde dient een integer te zijn', + 'The username must be unique' => 'De gebruikersnaam moet uniek zijn', + 'The user id is required' => 'De gebruikers-ID is verplicht', + 'Passwords don\'t match' => 'De wachtwoorden komen niet overeen', + 'The confirmation is required' => 'De bevestiging is verplicht', + 'The project is required' => 'Het project is verplicht', + 'The id is required' => 'De ID is verplicht', + 'The project id is required' => 'De project-ID is verplicht', + 'The project name is required' => 'De projectnaam is verplicht', + 'The title is required' => 'De titel is verplicht', + 'Settings saved successfully.' => 'Instellingen succesvol opgeslagen.', + 'Unable to save your settings.' => 'Kan je instellingen niet opslaan.', + 'Database optimization done.' => 'Database optimaliseren voltooid.', + 'Your project has been created successfully.' => 'Je project is succesvol aangemaakt.', + 'Unable to create your project.' => 'Kan je project niet aanmaken.', + 'Project updated successfully.' => 'Project succesvol bijgewerkt.', + 'Unable to update this project.' => 'Kan dit project niet bijwerken.', + 'Unable to remove this project.' => 'Kan dit project niet verwijderen.', + 'Project removed successfully.' => 'Project succesvol verwijderd.', + 'Project activated successfully.' => 'Project succesvol geactiveerd.', + 'Unable to activate this project.' => 'Kan dit project niet activeren.', + 'Project disabled successfully.' => 'Project uitschakelen succesvol.', + 'Unable to disable this project.' => 'Kan dit project niet uitschakelen.', + 'Unable to open this task.' => 'Kan deze taak niet openen.', + 'Task opened successfully.' => 'Taak succesvol geopend.', + 'Unable to close this task.' => 'Kan deze taak niet sluiten.', + 'Task closed successfully.' => 'Taak succesvol gesloten.', + 'Unable to update your task.' => 'Kan je taak niet bijwerken.', + 'Task updated successfully.' => 'Taak succesvol bijgewerkt.', + 'Unable to create your task.' => 'Kan je taak niet aanmaken.', + 'Task created successfully.' => 'Taak succesvol aangemaakt.', + 'User created successfully.' => 'Gebruiker succesvol aangemaakt.', + 'Unable to create your user.' => 'Kan je gebruiker niet aanmaken.', + 'User updated successfully.' => 'Gebruiker succesvol bijgewerkt', + 'User removed successfully.' => 'Gebruiker succesvol verwijderd.', + 'Unable to remove this user.' => 'Kan deze gebruiker niet verwijderen.', + 'Board updated successfully.' => 'Bord succesvol bijgewerkt.', + 'Ready' => 'Klaar', + 'Backlog' => 'In afwachting', + 'Work in progress' => 'In uitvoering', + 'Done' => 'Afgerond', + 'Application version:' => 'Applicatieversie:', + 'Id' => 'Id', + 'Public link' => 'Publieke link', + 'Timezone' => 'Tijdzone', + 'Sorry, I didn\'t find this information in my database!' => 'Sorry, ik heb deze informatie niet gevonden in de database!', + 'Page not found' => 'Pagina niet gevonden', + 'Complexity' => 'Complexiteit', + 'Task limit' => 'Taaklimiet', + 'Task count' => 'Aantal taken', + 'User' => 'Gebruiker', + 'Comments' => 'Commentaar', + 'Comment is required' => 'Commentaar is verplicht', + 'Comment added successfully.' => 'Commentaar succesvol toegevoegd.', + 'Unable to create your comment.' => 'Kan je commentaar niet aanmaken.', + 'Due Date' => 'Vervaldag', + 'Invalid date' => 'Ongeldige datum', + 'Automatic actions' => 'Geautomatiseerde acties', + 'Your automatic action has been created successfully.' => 'Geautomatiseerde actie succesvol aangemaakt.', + 'Unable to create your automatic action.' => 'Kan je automatische actie niet aanmaken.', + 'Remove an action' => 'Een actie verwijderen', + 'Unable to remove this action.' => 'Kan deze actie niet verwijderen.', + 'Action removed successfully.' => 'Actie succesvol verwijderd.', + 'Automatic actions for the project "%s"' => 'Geautomatiseerde acties voor project "%s"', + 'Add an action' => 'Een actie toevoegen', + 'Event name' => 'Naam gebeurtenis', + 'Action' => 'Actie', + 'Event' => 'Gebeurtenis', + 'When the selected event occurs execute the corresponding action.' => 'Wanneer de geselecteerde gebeurtenis plaatsvindt, voer dan de bijbehorende actie uit.', + 'Next step' => 'Volgende stap', + 'Define action parameters' => 'Bepaal actieparameters', + 'Do you really want to remove this action: "%s"?' => 'Wil je deze actie echt verwijderen?: "%s"?', + 'Remove an automatic action' => 'Een automatische actie verwijderen', + 'Assign the task to a specific user' => 'De taak toewijzen aan een specifieke gebruiker', + 'Assign the task to the person who does the action' => 'De taak toewijzen aan de persoon die de actie uitvoert', + 'Duplicate the task to another project' => 'De taak dupliceren in een ander project', + 'Move a task to another column' => 'Een taak verplaatsen naar een andere kolom', + 'Task modification' => 'Aanpassen taak', + 'Task creation' => 'Aanmaken taak', + 'Closing a task' => 'Een taak sluiten', + 'Assign a color to a specific user' => 'Een kleur toewijzen aan een specifieke gebruiker', + 'Position' => 'Positie', + 'Duplicate to project' => 'Dupliceren in een ander project', + 'Duplicate' => 'Dupliceren', + 'Link' => 'Linken', + 'Comment updated successfully.' => 'Commentaar succesvol bijgewerkt.', + 'Unable to update your comment.' => 'Kan je commentaar niet bijwerken.', + 'Remove a comment' => 'Commentaar verwijderen', + 'Comment removed successfully.' => 'Commentaar succesvol verwijderd.', + 'Unable to remove this comment.' => 'Kan dit commentaar niet verwijderen.', + 'Do you really want to remove this comment?' => 'Wil je dit commentaar echt verwijderen?', + 'Current password for the user "%s"' => 'Huidig wachtwoord voor de gebruiker "%s"', + 'The current password is required' => 'Huidig wachtwoord is verplicht', + 'Wrong password' => 'Verkeerd wachtwoord', + 'Unknown' => 'Onbekend', + 'Last logins' => 'Laatste logins', + 'Login date' => 'Logindatum', + 'Authentication method' => 'Authenticatiemethode', + 'IP address' => 'IP-adres', + 'User agent' => 'User agent', + 'Persistent connections' => 'Persistente connecties', + 'No session.' => 'Geen sessie.', + 'Expiration date' => 'Verloopdatum', + 'Remember Me' => 'Onthoud mij', + 'Creation date' => 'Aanmaakdatum', + 'Everybody' => 'Iedereen', + 'Open' => 'Open', + 'Closed' => 'Gesloten', + 'Search' => 'Zoeken', + 'Nothing found.' => 'Niets gevonden.', + 'Due date' => 'Vervaldatum', + 'Description' => 'Omschrijving', + '%d comments' => '%d commentaren', + '%d comment' => '%d commentaar', + 'Email address invalid' => 'Ongeldig e-mailadres', + 'Your external account is not linked anymore to your profile.' => 'Je externe account is niet meer gekoppeld aan je profiel.', + 'Unable to unlink your external account.' => 'Kan je externe account niet ontkoppelen.', + 'External authentication failed' => 'Externe authenticatie mislukt', + 'Your external account is linked to your profile successfully.' => 'Je externe account is succesvol gekoppeld aan je profiel.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Taak succesvol verwijderd.', + 'Unable to remove this task.' => 'Kan deze taak niet verwijderen.', + 'Remove a task' => 'Een taak verwijderen', + 'Do you really want to remove this task: "%s"?' => 'Wil je deze taak echt verwijderen "%s"?', + 'Assign automatically a color based on a category' => 'Automatisch een kleur toewijzen op basis van een categorie', + 'Assign automatically a category based on a color' => 'Automatisch een categorie toewijzen op basis van een kleur', + 'Task creation or modification' => 'Taak aanmaken of wijzigen', + 'Category' => 'Categorie', + 'Category:' => 'Categorie:', + 'Categories' => 'Categorieën', + 'Your category has been created successfully.' => 'Je categorie is succesvol aangemaakt.', + 'This category has been updated successfully.' => 'Je categorie is succesvol bijgewerkt.', + 'Unable to update this category.' => 'Kan deze categorie niet bijwerken.', + 'Remove a category' => 'Categorie verwijderen', + 'Category removed successfully.' => 'Categorie succesvol verwijderd.', + 'Unable to remove this category.' => 'Kan deze categorie niet verwijderen.', + 'Category modification for the project "%s"' => 'Categorie aanpassen voor project "%s"', + 'Category Name' => 'Categorienaam', + 'Add a new category' => 'Categorie toevoegen', + 'Do you really want to remove this category: "%s"?' => 'Wil je deze categorie echt verwijderen: "%s"?', + 'All categories' => 'Alle categorieën', + 'No category' => 'Geen categorie', + 'The name is required' => 'De naam is verplicht', + 'Remove a file' => 'Een bestand verwijderen', + 'Unable to remove this file.' => 'Kan dit bestand niet verwijderen.', + 'File removed successfully.' => 'Bestand succesvol verwijdered.', + 'Attach a document' => 'Document toevoegen', + 'Do you really want to remove this file: "%s"?' => 'Wil je dit bestand echt verwijderen: "%s"?', + 'Attachments' => 'Bijlages', + 'Edit the task' => 'De taak bewerken', + 'Add a comment' => 'Commentaar toevoegen', + 'Edit a comment' => 'Commentaar bewerken', + 'Summary' => 'Samenvatting', + 'Time tracking' => 'Tijdschrijven', + 'Estimate:' => 'Schatting:', + 'Spent:' => 'Besteed:', + 'Do you really want to remove this sub-task?' => 'Wil je deze subtaak echt verwijderen?', + 'Remaining:' => 'Resterend:', + 'hours' => 'uren', + 'estimated' => 'geschat', + 'Sub-Tasks' => 'Subtaken', + 'Add a sub-task' => 'Een subtaak toevoegen', + 'Original estimate' => 'Orginele schatting', + 'Create another sub-task' => 'Nog een subtaak toevoegen', + 'Time spent' => 'Bestede tijd', + 'Edit a sub-task' => 'Subtaak bewerken', + 'Remove a sub-task' => 'Subtaak verwijderen', + 'The time must be a numeric value' => 'De tijd moet een numerieke waarde zijn', + 'Todo' => 'Nog te doen', + 'In progress' => 'In behandeling', + 'Sub-task removed successfully.' => 'Subtaak succesvol verwijderd.', + 'Unable to remove this sub-task.' => 'Kan deze subtaak niet verwijderen.', + 'Sub-task updated successfully.' => 'Subtaak succesvol bijgewerkt.', + 'Unable to update your sub-task.' => 'Kan deze subtaak niet bijwerken.', + 'Unable to create your sub-task.' => 'Kan deze subtaak niet aanmaken.', + 'Maximum size: ' => 'Maximale grootte: ', + 'Display another project' => 'Een ander project weergeven', + 'Created by %s' => 'Aangemaakt door %s', + 'Tasks Export' => 'Taken exporteren', + 'Start Date' => 'Startdatum', + 'Execute' => 'Uitvoeren', + 'Task Id' => 'Taak-ID', + 'Creator' => 'Aangemaakt door', + 'Modification date' => 'Wijzigingsdatum', + 'Completion date' => 'Afgerond op', + 'Clone' => 'Kloon', + 'Project cloned successfully.' => 'Project succesvol gekloond.', + 'Unable to clone this project.' => 'Kan dit project niet klonen.', + 'Enable email notifications' => 'E-mailmeldingen inschakelen', + 'Task position:' => 'Taak positie:', + 'The task #%d has been opened.' => 'Taak #%d is geopend.', + 'The task #%d has been closed.' => 'Taak #%d is gesloten.', + 'Sub-task updated' => 'Subtaak bijgewerkt', + 'Title:' => 'Titel:', + 'Status:' => 'Status:', + 'Assignee:' => 'Toegewezene:', + 'Time tracking:' => 'Tijdschrijven:', + 'New sub-task' => 'Nieuwe subtaak', + 'New attachment added "%s"' => 'Nieuwe bijlage toegevoegd "%s"', + 'New comment posted by %s' => 'Nieuw commentaar geplaatst door "%s"', + 'New comment' => 'Nieuw commentaar', + 'Comment updated' => 'Commentaar bijgewerkt', + 'New subtask' => 'Nieuwe subtaak', + 'I only want to receive notifications for these projects:' => 'Ik wil meldingen ontvangen van de volgende projecten:', + 'view the task on Kanboard' => 'taak bekijken op Kanboard', + 'Public access' => 'Publieke toegang', + 'Disable public access' => 'Publieke toegang uitschakelen', + 'Enable public access' => 'Publieke toegang inschakelen', + 'Public access disabled' => 'Publieke toegang uitgeschakeld', + 'Move the task to another project' => 'Taak verplaatsen naar een ander project', + 'Move to project' => 'Verplaats naar een ander project', + 'Do you really want to duplicate this task?' => 'Wil je deze taak echt dupliceren?', + 'Duplicate a task' => 'Taak dupliceren', + 'External accounts' => 'Externe accounts', + 'Account type' => 'Account type', + 'Local' => 'Lokaal', + 'Remote' => 'Remote', + 'Enabled' => 'Actief', + 'Disabled' => 'Inactief', + 'Login:' => 'Gebruikersnaam:', + 'Full Name:' => 'Naam:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Meldingen:', + 'Notifications' => 'Meldingen', + 'Account type:' => 'Accounttype:', + 'Edit profile' => 'Profiel aanpassen', + 'Change password' => 'Wachtwoord wijzigen', + 'Password modification' => 'Wijziging wachtwoord', + 'External authentications' => 'Externe authenticatie', + 'Never connected.' => 'Nooit verbonden.', + 'No external authentication enabled.' => 'Geen externe verificatie ingeschakeld.', + 'Password modified successfully.' => 'Wachtwoord succesvol aangepast.', + 'Unable to change the password.' => 'Kan het wachtwoord niet wijzigen.', + 'Change category' => 'Categorie veranderen', + '%s updated the task %s' => '%s heeft taak %s bijgewerkt', + '%s opened the task %s' => '%s heeft taak %s geopend', + '%s moved the task %s to the position #%d in the column "%s"' => '%s heeft taak %s naar positie %d in de kolom "%s" verplaatst', + '%s moved the task %s to the column "%s"' => '%s heeft taak %s verplaatst naar kolom "%s"', + '%s created the task %s' => '%s heeft taak %s aangemaakt', + '%s closed the task %s' => '%s heeft taak %s gesloten', + '%s created a subtask for the task %s' => '%s heeft een subtaak aangemaakt voor taak %s', + '%s updated a subtask for the task %s' => '%s heeft een subtaak bijgewerkt voor taak %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Toegewezen aan %s met een schatting van %s/%sh', + 'Not assigned, estimate of %sh' => 'Niet toegewezen, schatting: %sh', + '%s updated a comment on the task %s' => '%s heeft een commentaar bijgewerkt voor taak %s', + '%s commented the task %s' => '%s heeft een commentaar geplaatst voor taak %s', + '%s\'s activity' => 'Activiteiten van %s', + 'RSS feed' => 'RSS-feed', + '%s updated a comment on the task #%d' => '%s heeft een commentaar bijgewerkt voor taak %d', + '%s commented on the task #%d' => '%s heeft commentaar geplaatst voor taak %d', + '%s updated a subtask for the task #%d' => '%s heeft een commentaar bijgewerkt voor subtaak %d', + '%s created a subtask for the task #%d' => '%s heeft een subtaak aangemaakt voor taak %d', + '%s updated the task #%d' => '%s heeft taak %d bijgewerkt', + '%s created the task #%d' => '%s heeft taak %d aangemaakt', + '%s closed the task #%d' => '%s heeft taak %d gesloten', + '%s opened the task #%d' => '%s heeft taak %d geopend', + 'Activity' => 'Activiteit', + 'Default values are "%s"' => 'Standaardwaarden zijn "%s"', + 'Default columns for new projects (Comma-separated)' => 'Standaardkolommen voor nieuw projecten (komma-gescheiden)', + 'Task assignee change' => 'Taak toegewezene wijziging', + '%s changed the assignee of the task #%d to %s' => '%s heeft de toegewezene voor taak %d gewijzigd in %s', + '%s changed the assignee of the task %s to %s' => '%s heeft de toegewezene voor taak %s gewijzigd in %s', + 'New password for the user "%s"' => 'Nieuw wachtwoord voor de gebruiker "%s"', + 'Choose an event' => 'Kies een gebeurtenis', + 'Create a task from an external provider' => 'Maak een taak aan vanuit een externe provider', + 'Change the assignee based on an external username' => 'De toewijzing wijzigen op basis van een externe gebruikersnaam', + 'Change the category based on an external label' => 'Verander de categorie op basis van een extern label', + 'Reference' => 'Referentie', + 'Label' => 'Label', + 'Database' => 'Database', + 'About' => 'Over', + 'Database driver:' => 'Database-driver:', + 'Board settings' => 'Bord-instellingen', + 'Webhook settings' => 'Webhook-instellingen', + 'Reset token' => 'Token resetten', + 'API endpoint:' => 'API-endpoint:', + 'Refresh interval for personal board' => 'Verversingsinterval voor persoonlijk bord', + 'Refresh interval for public board' => 'Verversingsinterval voor publiek bord', + 'Task highlight period' => 'Taak highlight-periode', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Periode (in seconden) om aan te nemen dat een taak onlangs is gewijzigd (0 om uit te schakelen, standaard 2 dagen)', + 'Frequency in second (60 seconds by default)' => 'Frequentie in seconden (standaard 60)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequentie in seconden (0 om uit te schakelen, standaard 10)', + 'Application URL' => 'Applicatie-URL', + 'Token regenerated.' => 'Token opnieuw gegenereerd.', + 'Date format' => 'Datumformaat', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO-formaat wordt altijd geaccepteerd, bijvoorbeeld: "%s" en "%s"', + 'New personal project' => 'Nieuw persoonlijk project', + 'This project is personal' => 'Dit project is persoonlijk', + 'Add' => 'Toevoegen', + 'Start date' => 'Startdatum', + 'Time estimated' => 'Geschatte tijd', + 'There is nothing assigned to you.' => 'Er is niets aan jou toegewezen.', + 'My tasks' => 'Mijn taken', + 'Activity stream' => 'Activiteiten', + 'Dashboard' => 'Dashboard', + 'Confirmation' => 'Bevestiging', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Commentaar toevoegen van een externe provider', + 'Project management' => 'Projectmanagement', + 'Columns' => 'Kolommen', + 'Task' => 'Taak', + 'Percentage' => 'Percentage', + 'Number of tasks' => 'Aantal taken', + 'Task distribution' => 'Distributie van taken', + 'Analytics' => 'Analytics', + 'Subtask' => 'Subtaak', + 'User repartition' => 'Gebruikersverdeling', + 'Clone this project' => 'Dit project klonen', + 'Column removed successfully.' => 'Kolom succesvol verwijderd.', + 'Not enough data to show the graph.' => 'Niet genoeg data om de grafiek te laten zien.', + 'Previous' => 'Vorige', + 'The id must be an integer' => 'De ID moet een integer zijn', + 'The project id must be an integer' => 'De project-ID moet een integer zijn', + 'The status must be an integer' => 'De status moet een integer zijn', + 'The subtask id is required' => 'De ID van de subtaak is verplicht', + 'The subtask id must be an integer' => 'De ID van de subtaak moet een integer zijn', + 'The task id is required' => 'De ID van de taak is verplicht', + 'The task id must be an integer' => 'De ID van de taak moet een integer zijn', + 'The user id must be an integer' => 'De ID van de gebruiker moet een integer zijn', + 'This value is required' => 'Deze waarde is verplicht', + 'This value must be numeric' => 'Deze waarde moet numeriek zijn', + 'Unable to create this task.' => 'Kan deze taak niet aanmaken.', + 'Cumulative flow diagram' => 'Cumulatief stroomdiagram', + 'Daily project summary' => 'Dagelijks projectoverzicht', + 'Daily project summary export' => 'Export van het dagelijkse projectoverzicht', + 'Exports' => 'Exports', + 'This export contains the number of tasks per column grouped per day.' => 'Deze export bevat het aantal taken per kolom, gegroepeerd per dag.', + 'Active swimlanes' => 'Actieve swimlanes', + 'Add a new swimlane' => 'Nieuwe swimlane toevoegen', + 'Default swimlane' => 'Standaard swimlane', + 'Do you really want to remove this swimlane: "%s"?' => 'Wil je deze swimlane echt verwijderen: "%s"?', + 'Inactive swimlanes' => 'Inactieve swimlanes', + 'Remove a swimlane' => 'Een swimlane verwijderen', + 'Swimlane modification for the project "%s"' => 'Swimlane aanpassing voor project "%s"', + 'Swimlane removed successfully.' => 'Swimlane succesvol verwijderd.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane succesvol bijgewerkt.', + 'Unable to remove this swimlane.' => 'Kan deze swimlane niet verwijderen.', + 'Unable to update this swimlane.' => 'Kan deze swimlane niet bijwerken.', + 'Your swimlane has been created successfully.' => 'Swimlane succesvol aangemaakt.', + 'Example: "Bug, Feature Request, Improvement"' => 'Voorbeeld: "Bug, Feature Request, Improvement"', + 'Default categories for new projects (Comma-separated)' => 'Standaardcategorieën voor nieuwe projecten (komma-gescheiden)', + 'Integrations' => 'Integraties', + 'Integration with third-party services' => 'Integratie met derde-partij-services', + 'Subtask Id' => 'Subtaak-ID', + 'Subtasks' => 'Subtaken', + 'Subtasks Export' => 'Subtaken exporteren', + 'Task Title' => 'Taaktitel', + 'Untitled' => 'Geen titel', + 'Application default' => 'Standaard voor applicatie', + 'Language:' => 'Taal:', + 'Timezone:' => 'Tijdzone:', + 'All columns' => 'Alle kolommen', + 'Next' => 'Volgende', + '#%d' => '#%d', + 'All swimlanes' => 'Alle swimlanes', + 'All colors' => 'Alle kleuren', + 'Moved to column %s' => 'Verplaatst naar kolom %s', + 'User dashboard' => 'Gebruikersdashboard', + 'Allow only one subtask in progress at the same time for a user' => 'Een gebruiker mag maar één subtaak tegelijk uitvoeren', + 'Edit column "%s"' => 'Kolom "%s" bewerken', + 'Select the new status of the subtask: "%s"' => 'Selecteer de nieuwe status voor de subtaak: "%s"', + 'Subtask timesheet' => 'Subtaak-timesheet', + 'There is nothing to show.' => 'Er is niets om te laten zien.', + 'Time Tracking' => 'Tijdschrijven', + 'You already have one subtask in progress' => 'Je hebt al een subtaak in behandeling', + 'Which parts of the project do you want to duplicate?' => 'Welke onderdelen van het project wil je dupliceren?', + 'Disallow login form' => 'Inlogformulier blokkeren', + 'Start' => 'Start', + 'End' => 'Eind', + 'Task age in days' => 'Leeftijd van de taak in dagen', + 'Days in this column' => 'Dagen in deze kolom', + '%dd' => '%dd', + 'Add a new link' => 'Nieuwe link toevoegen', + 'Do you really want to remove this link: "%s"?' => 'Wil je deze link echt verwijderen: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Wil je deze link met taak %d echt verwijderen?', + 'Field required' => 'Veld verplicht', + 'Link added successfully.' => 'Link succesvol toegevoegd.', + 'Link updated successfully.' => 'Link succesvol bijgewerkt.', + 'Link removed successfully.' => 'Link succesvol verwijderd.', + 'Link labels' => 'Linklabels', + 'Link modification' => 'Link-aanpassing', + 'Opposite label' => 'Tegenovergesteld label', + 'Remove a link' => 'Een link verwijderen', + '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', + 'Unable to create your link.' => 'Kan je link niet aanmaken.', + 'Unable to update your link.' => 'Kan je link niet aanpassen.', + 'Unable to remove this link.' => 'Kan je link niet verwijderen.', + 'relates to' => 'is gerelateerd aan', + 'blocks' => 'blokkeert', + 'is blocked by' => 'wordt geblokkeerd door', + 'duplicates' => 'dupliceert', + 'is duplicated by' => 'wordt gedupliceerd door', + 'is a child of' => 'is een kind van', + 'is a parent of' => 'is een ouder van', + 'targets milestone' => 'is nodig voor milestone', + 'is a milestone of' => 'is een milestone voor', + 'fixes' => 'corrigeert', + 'is fixed by' => 'wordt gecorrigeerd door', + 'This task' => 'Deze taak', + '<1h' => '<1u', + '%dh' => '%du', + 'Expand tasks' => 'Taken uitklappen', + 'Collapse tasks' => 'Taken inklappen', + 'Expand/collapse tasks' => 'Taken in/uiklappen', + 'Close dialog box' => 'Venster sluiten', + 'Submit a form' => 'Een formulier indienen', + 'Board view' => 'Bordweergave', + 'Keyboard shortcuts' => 'Sneltoetsen', + 'Open board switcher' => 'Open bordwisselaar', + 'Application' => 'Applicatie', + 'Compact view' => 'Compacte weergave', + 'Horizontal scrolling' => 'Horizontaal scrollen', + 'Compact/wide view' => 'Compacte/breedbeeld-weergave', + 'Currency' => 'Valuta', + 'Personal project' => 'Persoonlijk project', + 'AUD - Australian Dollar' => 'AUD - Australische dollar', + 'CAD - Canadian Dollar' => 'CAD - Canadese dollar', + 'CHF - Swiss Francs' => 'CHF - Zwitserse frank', + 'Custom Stylesheet' => 'Aangepast stylesheet', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britse pond', + 'INR - Indian Rupee' => 'INR - Indiaase rupee', + 'JPY - Japanese Yen' => 'JPY - Japanse yen', + 'NZD - New Zealand Dollar' => 'NZD - Nieuw-Zeelandse dollar', + 'PEN - Peruvian Sol' => 'PEN - Peruaanse sol', + 'RSD - Serbian dinar' => 'RSD - Servische dinar', + 'CNY - Chinese Yuan' => 'CNY - Chinese yuan', + 'USD - US Dollar' => 'USD - Amerikaanse dollar', + 'VES - Venezuelan Bolívar' => 'VES - Venezolaanse bolivar', + 'Destination column' => 'Doelkolom', + 'Move the task to another column when assigned to a user' => 'Verplaats de taak naar een andere kolom wanneer deze is toegewezen aan een gebruiker', + 'Move the task to another column when assignee is cleared' => 'Verplaats de taak naar een andere kolom wanneer de toewijzing is verwijderd', + 'Source column' => 'Bronkolom', + 'Transitions' => 'Transities', + 'Executer' => 'Uitvoerder', + 'Time spent in the column' => 'Tijd besteed in de kolom', + 'Task transitions' => 'Taak-transities', + 'Task transitions export' => 'Taak-transities exporteren', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Dit rapport bevat alle kolombewegingen voor elke taak met de datum, de gebruiker en de bestede tijd voor elke overgang.', + 'Currency rates' => 'Wisselkoersen', + 'Rate' => 'Koers', + 'Change reference currency' => 'Referentievaluta wijzigen', + 'Reference currency' => 'Referentievaluta', + 'The currency rate has been added successfully.' => 'De valutakoers is succesvol toegevoegd.', + 'Unable to add this currency rate.' => 'Kan deze valutakoers niet toevoegen.', + 'Webhook URL' => 'Webhook-URL', + '%s removed the assignee of the task %s' => '%s heeft de ontvanger van de taak %s verwijderd', + 'Information' => 'Informatie', + 'Check two factor authentication code' => 'Controleer de twee-factor authenticatiecode', + 'The two factor authentication code is not valid.' => 'De twee-factor authenticatiecode is niet geldig.', + 'The two factor authentication code is valid.' => 'De twee-factor authenticatiecode is geldig.', + 'Code' => 'Code', + 'Two factor authentication' => 'Twee-factor authenticatie', + 'This QR code contains the key URI: ' => 'Deze QR code bevat de sleutel-URI: ', + 'Check my code' => 'Mijn code controleren', + 'Secret key: ' => 'Geheime sleutel', + 'Test your device' => 'Je device testen', + 'Assign a color when the task is moved to a specific column' => 'Een kleur toewijzen wanneer de taak wordt verplaatst naar een specifieke kolom', + '%s via Kanboard' => '%s via Kanboard', + 'Burndown chart' => 'Burndown-grafiek', + 'This chart show the task complexity over the time (Work Remaining).' => 'Deze grafiek toont de complexiteit van de taak over de tijd (Work Remaining).', + 'Screenshot taken %s' => 'Screenshot gemaakt %s', + 'Add a screenshot' => 'Screenshot toevoegen', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Maak een screenshot en toets CTRL+V or ⌘+V om het hier te plakken.', + 'Screenshot uploaded successfully.' => 'Screenshot succesvol geüpload.', + 'SEK - Swedish Krona' => 'SEK - Zweedse kroon', + 'Identifier' => 'Identifier', + 'Disable two factor authentication' => 'Twee-factor authenticatie uitschakelen', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Wil je echt de twee-factor authenticatie uitschakelen voor deze gebruiker: "%s"?', + 'Edit link' => 'Link newerken', + 'Start to type task title...' => 'Begin met typen van de taaktitel...', + 'A task cannot be linked to itself' => 'Een taak kan niet aan zichzelf worden gekoppeld', + 'The exact same link already exists' => 'Dezelfde koppeling bestaat al', + 'Recurrent task is scheduled to be generated' => 'Terugkerende taak is gepland om te worden gegenereerd', + 'Score' => 'Score', + 'The identifier must be unique' => 'De identifier moet uniek zijn', + 'This linked task id doesn\'t exists' => 'Deze gekoppelde taak-ID bestaat niet', + 'This value must be alphanumeric' => 'Deze waarde moet alfanumeriek zijn', + 'Edit recurrence' => 'Herhaling bewerken', + 'Generate recurrent task' => 'Terugkerende taak genereren', + 'Trigger to generate recurrent task' => 'Trigger om terugkerende taak te genereren', + 'Factor to calculate new due date' => 'Factor om nieuwe vervaldatum te berekenen', + 'Timeframe to calculate new due date' => 'Tijdsbestek om nieuwe vervaldatum te berekenen', + 'Base date to calculate new due date' => 'Basisdatum om nieuwe vervaldatum te berekenen', + 'Action date' => 'Actiedatum', + 'Base date to calculate new due date: ' => 'Basisdatum voor het berekenen van de nieuwe vervaldatum: ', + 'This task has created this child task: ' => 'Deze taak heeft deze subtaak aangemaakt: ', + 'Day(s)' => 'Dag(en)', + 'Existing due date' => 'Bestaande vervaldatum', + 'Factor to calculate new due date: ' => 'Factor om nieuwe vervaldatum te berekenen: ', + 'Month(s)' => 'Maand(en)', + 'This task has been created by: ' => 'Deze taak is aangemaakt door: ', + 'Recurrent task has been generated:' => 'Terugkerende taak is gegenereerd:', + 'Timeframe to calculate new due date: ' => 'Tijdsbestek om nieuwe vervaldatum te berekenen: ', + 'Trigger to generate recurrent task: ' => 'Trigger om terugkerende taak te genereren: ', + 'When task is closed' => 'Wanneer taak is gesloten', + 'When task is moved from first column' => 'Wanneer de taak is verplaatst van de eerste kolom', + 'When task is moved to last column' => 'Wanneer de taak is verplaatst naar de laatste kolom', + 'Year(s)' => 'Jaar/Jaren', + 'Project settings' => 'Project instellingen', + 'Automatically update the start date' => 'Automatisch de begindatum bijwerken', + 'iCal feed' => 'iCal-feed', + 'Preferences' => 'Voorkeuren', + 'Security' => 'Beveiliging', + 'Two factor authentication disabled' => 'Twee-factor authenticatie uitgeschakeld', + 'Two factor authentication enabled' => 'Twee-factor authenticatie ingeschakeld', + 'Unable to update this user.' => 'Kan deze gebruiker niet bijwerken.', + 'There is no user management for personal projects.' => 'Er is geen gebruikersbeheer voor persoonlijke projecten.', + 'User that will receive the email' => 'Gebruiker die de e-mail ontvangt', + 'Email subject' => 'Onderwerp e-mail', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'Een commentaarlog toevoegen bij het verplaatsen van de taak tussen kolommen', + 'Move the task to another column when the category is changed' => 'Verplaats de taak naar een andere kolom wanneer de categorie wordt gewijzigd', + 'Send a task by email to someone' => 'Een taak per e-mail naar iemand verzenden', + 'Reopen a task' => 'Heropen een taak', + 'Notification' => 'Melding', + '%s moved the task #%d to the first swimlane' => '%s heeft de taak #%d naar de eerste swimlane verplaatst', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => '%s heeft de taak %s naar de eerste swimlane verplaatst', + '%s moved the task %s to the swimlane "%s"' => '%s heeft taak %s naar swimlane "%s" verplaatst', + 'This report contains all subtasks information for the given date range.' => 'Dit rapport bevat alle subtakeninformatie voor het gegeven datumbereik.', + 'This report contains all tasks information for the given date range.' => 'Dit rapport bevat alle takeninformatie voor het gegeven datumbereik.', + 'Project activities for %s' => 'Projectactiviteiten voor %s', + 'view the board on Kanboard' => 'het bord bekijken op Kanboard', + 'The task has been moved to the first swimlane' => 'De taak is verplaatst naar de eerste swimlane', + 'The task has been moved to another swimlane:' => 'De taak is verplaatst naar een andere swimlane:', + 'New title: %s' => 'Nieuw titel: %s', + 'The task is not assigned anymore' => 'De taak is niet meer toegewezen', + 'New assignee: %s' => 'Nieuwe toegewezene: %s', + 'There is no category now' => 'Er is nu geen categorie', + 'New category: %s' => 'Nieuwe categorie: %s', + 'New color: %s' => 'Nieuwe kleur: %s', + 'New complexity: %d' => 'Nieuwe complexiteit: %d', + 'The due date has been removed' => 'De vervaldatum is verwijderd', + 'There is no description anymore' => 'Er is geen beschrijving meer', + 'Recurrence settings has been modified' => 'Herhalingsinstellingen zijn gewijzigd', + 'Time spent changed: %sh' => 'Bestede tijd gewijzigd: %su', + 'Time estimated changed: %sh' => 'Geschatte tijd gewijzigd: %su', + 'The field "%s" has been updated' => 'Het veld "%s" is bijgewerkt', + 'The description has been modified:' => 'De beschrijving is gewijzigd:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Wil je echt de taak "%s" afsluiten, evenals alle subtaken?', + 'I want to receive notifications for:' => 'Ik wil notificaties ontvangen voor:', + 'All tasks' => 'Alle taken', + 'Only for tasks assigned to me' => 'Alleen voor taken die aan mij zijn toegewezen', + 'Only for tasks created by me' => 'Alleen voor taken die door mij zijn aangemaakt', + 'Only for tasks created by me and tasks assigned to me' => 'Alleen voor taken die door mij zijn aangemaakt en taken die aan mij zijn toegewezen', + '%%Y-%%m-%%d' => '%%d-%%m-%%Y', + 'Total for all columns' => 'Totaal voor alle kolommen', + 'You need at least 2 days of data to show the chart.' => 'Je hebt gegevens voor minstens 2 dagen nodig om de grafiek te tonen.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Stop timer', + 'Start timer' => 'Start timer', + 'My activity stream' => 'Mijn activiteiten', + 'Search tasks' => 'Zoek taken', + 'Reset filters' => 'Reset filters', + 'My tasks due tomorrow' => 'Mijn taken voor morgen', + 'Tasks due today' => 'Taken voor vandaag', + 'Tasks due tomorrow' => 'Taken voor morgen', + 'Tasks due yesterday' => 'Taken voor gisteren', + 'Closed tasks' => 'Gesloten taken', + 'Open tasks' => 'Open taken', + 'Not assigned' => 'Niet toegewezen', + 'View advanced search syntax' => 'Geavanceerde zoeksyntax bekijken', + 'Overview' => 'Overzicht', + 'Board/Calendar/List view' => 'Bord/Kalender/Lijst weergave', + 'Switch to the board view' => 'Overschakelen naar de bordweergave', + 'Switch to the list view' => 'Overschakelen naar de lijstweergave', + 'Go to the search/filter box' => 'Ga naar het zoek-/filtervak', + 'There is no activity yet.' => 'Er is nog geen activiteit.', + 'No tasks found.' => 'Geen taken gevonden.', + 'Keyboard shortcut: "%s"' => 'Sneltoets: "%s".', + 'List' => 'Lijst', + 'Filter' => 'Filter', + 'Advanced search' => 'Uitgebreid zoeken', + 'Example of query: ' => 'Voorbeeld van zoekopdracht: ', + 'Search by project: ' => 'Zoek op project', + 'Search by column: ' => 'Zoek op kolom', + 'Search by assignee: ' => 'Zoeken op toegewezene: ', + 'Search by color: ' => 'Zoek op kleur', + 'Search by category: ' => 'Zoek op categorie', + 'Search by description: ' => 'Zoek op omschrijving', + 'Search by due date: ' => 'Zoeken op vervaldatum: ', + 'Average time spent in each column' => 'Gemiddelde tijd besteed in elke kolom', + 'Average time spent' => 'Gemiddelde tijd besteed', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Deze grafiek toont de gemiddelde tijd gespendeerd in elke kolom voor de laatste %d taken.', + 'Average Lead and Cycle time' => 'Gemiddelde doorlooptijd en cyclustijd', + 'Average lead time: ' => 'Gemiddelde doorlooptijd: ', + 'Average cycle time: ' => 'Gemiddelde cyclustijd: ', + 'Cycle Time' => 'Cyclustijd', + 'Lead Time' => 'Doorlooptijd', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Deze grafiek toont de gemiddelde doorlooptijd en cyclustijd voor de laatste %d taken door de tijd.', + 'Average time into each column' => 'Gemiddelde tijd in elke kolom', + 'Lead and cycle time' => 'Doorlooptijd en cyclustijd', + 'Lead time: ' => 'Doorlooptijd: ', + 'Cycle time: ' => 'Cyclustijd: ', + 'Time spent in each column' => 'Tijd besteed in elke kolom', + 'The lead time is the duration between the task creation and the completion.' => 'De doorlooptijd is de duur tussen het aanmaken van de taak en de voltooiing.', + 'The cycle time is the duration between the start date and the completion.' => 'De cyclustijd is de tijd tussen de begindatum en de voltooiing.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Als de taak niet is afgesloten, wordt de huidige tijd gebruikt in plaats van de voltooiingsdatum.', + 'Set the start date automatically' => 'De begindatum automatisch instellen', + 'Edit Authentication' => 'Authenticatie bewerken', + 'Remote user' => 'Externe gebruiker', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Externe gebruikers slaan hun wachtwoord niet op in de Kanboard-database, voorbeelden: LDAP-, Google- en Github-accounts.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Als je het vakje "Inlogformulier uitzetten" aanvinkt, worden inloggegevens die zijn ingevoerd in het inlogformulier genegeerd.', + 'Default task color' => 'Standaard taakkleur', + 'This feature does not work with all browsers.' => 'Deze functie werkt niet met alle browsers.', + 'There is no destination project available.' => 'Er is geen doelproject beschikbaar.', + 'Trigger automatically subtask time tracking' => 'Automatisch bijhouden van subtaaktijd triggeren', + 'Include closed tasks in the cumulative flow diagram' => 'Neem afgesloten taken op in het cumulatieve stroomdiagram', + 'Current swimlane: %s' => 'Huidige swimlane: %s', + 'Current column: %s' => 'Huidige kolom: %s', + 'Current category: %s' => 'Huidige categorie: %s', + 'no category' => 'geen categorie', + 'Current assignee: %s' => 'Huidige toegewezene: %s', + 'not assigned' => 'niet toegewezen', + 'Author:' => 'Auteur:', + 'contributors' => 'bijdragers', + 'License:' => 'Licentie:', + 'License' => 'Licentie', + 'Enter the text below' => 'Voer de tekst hieronder in', + 'Start date:' => 'Startdatum:', + 'Due date:' => 'Vervaldatum:', + 'People who are project managers' => 'Mensen die projectbeheerder zijn', + 'People who are project members' => 'Mensen die projectleden zijn', + 'NOK - Norwegian Krone' => 'NOK - Noorse kroon', + 'Show this column' => 'Toon deze kolom', + 'Hide this column' => 'Verberg deze kolom', + 'End date' => 'Einddatum', + 'Users overview' => 'Overzicht gebruikers', + 'Members' => 'Leden', + 'Shared project' => 'Gedeeld project', + 'Project managers' => 'Projectbeheerders', + 'Projects list' => 'Lijst projecten', + 'End date:' => 'Einddatum:', + 'Change task color when using a specific task link' => 'Verander de kleur van een taak wanneer je een specifieke taaklink gebruikt', + 'Task link creation or modification' => 'Taaklink maken of wijzigen', + 'Milestone' => 'Mijlpaal', + 'Reset the search/filter box' => 'Het zoek/filtervak resetten', + 'Documentation' => 'Documentatie', + 'Author' => 'Auteur', + 'Version' => 'Versie', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Er is geen plugin geladen.', + 'My notifications' => 'Mijn meldingen', + 'Custom filters' => 'Aangepaste filters', + 'Your custom filter has been created successfully.' => 'Je aangepaste filter is met succes aangemaakt.', + 'Unable to create your custom filter.' => 'Kan je aangepaste filter niet aanmaken.', + 'Custom filter removed successfully.' => 'Aangepast filter met succes verwijderd.', + 'Unable to remove this custom filter.' => 'Kan dit aangepaste filter niet verwijderen.', + 'Edit custom filter' => 'Aangepast filter bewerken', + 'Your custom filter has been updated successfully.' => 'Je aangepaste filter is met succes bijgewerkt.', + 'Unable to update custom filter.' => 'Kan aangepast filter niet bijwerken.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nieuwe bijlage bij taak #%d: %s', + 'New comment on task #%d' => 'Nieuw commentaar op taak #%d', + 'Comment updated on task #%d' => 'Commentaar bijgewerkt op taak #%d', + 'New subtask on task #%d' => 'Nieuwe subtaak op taak #%d', + 'Subtask updated on task #%d' => 'Subtaak bijgewerkt op taak #%d', + 'New task #%d: %s' => 'Nieuwe taak #%d: %s', + 'Task updated #%d' => 'Taak bijgewerkt #%d', + 'Task #%d closed' => 'Taak #%d gesloten', + 'Task #%d opened' => 'Taak #%d geopend', + 'Column changed for task #%d' => 'Kolom gewijzigd voor taak #%d', + 'New position for task #%d' => 'Nieuwe positie voor taak #%d', + 'Swimlane changed for task #%d' => 'Swimlane gewijzigd voor taak #%d', + 'Assignee changed on task #%d' => 'Toegewezene gewijzigd voor taak #%d', + '%d overdue tasks' => '%d achterstallige taken', + 'No notification.' => 'Geen melding.', + 'Mark all as read' => 'Markeer alles als gelezen', + 'Mark as read' => 'Markeer als gelezen', + 'Total number of tasks in this column across all swimlanes' => 'Totaal aantal taken in deze kolom in alle swimlanes', + 'Collapse swimlane' => 'Voeg swimlane samen', + 'Expand swimlane' => 'Breid swimlane uit', + 'Add a new filter' => 'Een nieuw filter toevoegen', + 'Share with all project members' => 'Delen met alle projectleden', + 'Shared' => 'Gedeeld', + 'Owner' => 'Eigenaar', + 'Unread notifications' => 'Ongelezen meldingen', + 'Notification methods:' => 'Kennisgevingsmethoden:', + 'Unable to read your file' => 'Kan je bestand niet lezen', + '%d task(s) have been imported successfully.' => '%d taak (taken) is (zijn) met succes geïmporteerd.', + 'Nothing has been imported!' => 'Er is niets geïmporteerd!', + 'Import users from CSV file' => 'Gebruikers importeren uit CSV-bestand', + '%d user(s) have been imported successfully.' => '%d gebruiker(s) zijn succesvol geïmporteerd.', + 'Comma' => 'Komma', + 'Semi-colon' => 'Punt-komma', + 'Tab' => 'Tab', + 'Vertical bar' => 'Verticale balk', + 'Double Quote' => 'Dubbele aanhalingstekens', + 'Single Quote' => 'Enkele aanhalingstekens', + '%s attached a file to the task #%d' => '%s heeft een bestand toegevoegd aan de taak #%d', + 'There is no column or swimlane activated in your project!' => 'Er is geen kolom of swimlane geactiveerd in je project!', + 'Append filter (instead of replacement)' => 'Voeg filter toe (in plaats van vervangen)', + 'Append/Replace' => 'Toevoegen/vervangen', + 'Append' => 'Toevoegen', + 'Replace' => 'Vervang', + 'Import' => 'Importeer', + 'Change sorting' => 'Sortering wijzigen', + 'Tasks Importation' => 'Taken importeren', + 'Delimiter' => 'Scheidingsteken', + 'Enclosure' => 'Omsluiting', + 'CSV File' => 'CSV bestand', + 'Instructions' => 'Instructies', + 'Your file must use the predefined CSV format' => 'Je bestand moet de vooraf gedefinieerde CSV-indeling gebruiken', + 'Your file must be encoded in UTF-8' => 'Je bestand moet gecodeerd zijn in UTF-8', + 'The first row must be the header' => 'De eerste rij moet de koptekst zijn', + 'Duplicates are not verified for you' => 'Duplicaten worden niet voor je gecontroleerd', + 'The due date must use the ISO format: YYYY-MM-DD' => 'De vervaldatum moet het ISO-formaat gebruiken: JJJJ-MM-DD', + 'Download CSV template' => 'CSV-sjabloon downloaden', + 'No external integration registered.' => 'Geen externe integratie geregistreerd.', + 'Duplicates are not imported' => 'Duplicaten worden niet geïmporteerd', + 'Usernames must be lowercase and unique' => 'Gebruikersnamen moeten kleine letters en uniek zijn', + 'Passwords will be encrypted if present' => 'Wachtwoorden worden gecodeerd indien aanwezig', + '%s attached a new file to the task %s' => '%s heeft een nieuw bestand toegevoegd aan de taak %s', + 'Link type' => 'Type koppeling', + 'Assign automatically a category based on a link' => 'Automatisch een categorie toewijzen op basis van een link', + 'BAM - Konvertible Mark' => 'BAM - converteerbare mark', + 'Assignee Username' => 'Gebruikersnaam ontvanger', + 'Assignee Name' => 'Naam toewijzing', + 'Groups' => 'Groepen', + 'Members of %s' => 'Leden van %s', + 'New group' => 'Nieuwe groep', + 'Group created successfully.' => 'Groep succesvol aangemaakt.', + 'Unable to create your group.' => 'Kan je groep niet aanmaken.', + 'Edit group' => 'Groep bewerken', + 'Group updated successfully.' => 'Groep bijgewerkt.', + 'Unable to update your group.' => 'Kan je groep niet bijwerken.', + 'Add group member to "%s"' => 'Groepslid toevoegen aan "%s"', + 'Group member added successfully.' => 'Groepslid toegevoegd.', + 'Unable to add group member.' => 'Kan geen groepslid toevoegen.', + 'Remove user from group "%s"' => 'Gebruiker verwijderd uit groep "%s"', + 'User removed successfully from this group.' => 'Gebruiker succesvol verwijderd uit deze groep.', + 'Unable to remove this user from the group.' => 'Kan deze gebruiker niet uit de groep verwijderen.', + 'Remove group' => 'Verwijder groep', + 'Group removed successfully.' => 'Groep succesvol verwijderd.', + 'Unable to remove this group.' => 'Kan deze groep niet verwijderen.', + 'Project Permissions' => 'Projectmachtigingen', + 'Manager' => 'Manager', + 'Project Manager' => 'Projectbeheerder', + 'Project Member' => 'Projectlid', + 'Project Viewer' => 'Project-viewer', + 'Your account is locked for %d minutes' => 'Je account is geblokkeerd voor %d minuten', + 'Invalid captcha' => 'Ongeldige captcha', + 'The name must be unique' => 'De naam moet uniek zijn', + 'View all groups' => 'Alle groepen weergeven', + 'There is no user available.' => 'Er is geen gebruiker beschikbaar.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Wil je echt de gebruiker "%s" verwijderen uit de groep "%s"?', + 'There is no group.' => 'Er is geen groep.', + 'Add group member' => 'Groepslid toevoegen', + 'Do you really want to remove this group: "%s"?' => 'Wil je deze groep echt verwijderen? "%s"?', + 'There is no user in this group.' => 'Er is geen gebruiker in deze groep.', + 'Permissions' => 'Rechten', + 'Allowed Users' => 'Toegestane gebruikers', + 'No specific user has been allowed.' => 'Er is geen specifieke gebruiker toegestaan.', + 'Role' => 'Rol', + 'Enter user name...' => 'Voer gebruikersnaam in...', + 'Allowed Groups' => 'Toegestane groepen', + 'No group has been allowed.' => 'Er is geen groep toegestaan.', + 'Group' => 'Groep', + 'Group Name' => 'Naam groep', + 'Enter group name...' => 'Naam van groep invoeren...', + 'Role:' => 'Rol:', + 'Project members' => 'Projectleden', + '%s mentioned you in the task #%d' => '%s noemde jou in de taak #%d', + '%s mentioned you in a comment on the task #%d' => '%s noemde jou in een commentaar op de taak #%d', + 'You were mentioned in the task #%d' => 'Je werd genoemd in de taak #%d', + 'You were mentioned in a comment on the task #%d' => 'Je werd genoemd in een commentaar op de taak #%d', + 'Estimated hours: ' => 'Geschatte uren: ', + 'Actual hours: ' => 'Werkelijke uren: ', + 'Hours Spent' => 'Bestede uren', + 'Hours Estimated' => 'Geschatte uren', + 'Estimated Time' => 'Geschatte tijd', + 'Actual Time' => 'Werkelijke tijd', + 'Estimated vs actual time' => 'Geschatte vs. werkelijke tijd', + 'RUB - Russian Ruble' => 'RUB - Russische roebel', + 'Assign the task to the person who does the action when the column is changed' => 'Wijs de taak toe aan de persoon die de actie uitvoert wanneer de kolom wordt gewijzigd', + 'Close a task in a specific column' => 'Sluit een taak in een specifieke kolom', + 'Time-based One-time Password Algorithm' => 'Tijdgebaseerd algoritme voor eenmalig wachtwoord', + 'Two-Factor Provider: ' => 'Provider met twee factoren: ', + 'Disable two-factor authentication' => 'Twee-factor authenticatie uitschakelen', + 'Enable two-factor authentication' => 'Twee-factor authenticatie inschakelen', + 'There is no integration registered at the moment.' => 'Er is momenteel geen integratie geregistreerd.', + 'Password Reset for Kanboard' => 'Wachtwoord opnieuw instellen voor Kanboard', + 'Forgot password?' => 'Wachtwoord vergeten?', + 'Enable "Forget Password"' => '"Wachtwoord vergeten" inschakelen', + 'Password Reset' => 'Wachtwoord opnieuw instellen', + 'New password' => 'Nieuw wachtwoord', + 'Change Password' => 'Wijzig wachtwoord', + 'To reset your password click on this link:' => 'Klik op deze link om je wachtwoord opnieuw in te stellen:', + 'Last Password Reset' => 'Laatste wachtwoordverandering', + 'The password has never been reinitialized.' => 'Het wachtwoord is nooit opnieuw geïnitialiseerd.', + 'Creation' => 'Aanmaak', + 'Expiration' => 'Vervaldatum', + 'Password reset history' => 'Geschiedenis wachtwoordwijzigingen', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Alle taken van de kolom "%s" en de swimlane "%s" zijn met succes afgesloten.', + 'Do you really want to close all tasks of this column?' => 'Wil je echt alle taken van deze kolom sluiten?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d taak (taken) in de kolom "%s" en de swimlane "%s" zullen worden gesloten.', + 'Close all tasks in this column and this swimlane' => 'Sluit alle taken in deze kolom en deze swimlane', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Geen enkele plugin heeft een projectnotificatiemethode geregistreerd. Je kunt nog steeds individuele meldingen configureren in je gebruikersprofiel.', + 'My dashboard' => 'Mijn dashboard', + 'My profile' => 'Mijn profiel', + 'Project owner: ' => 'Project eigenaar: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'De projectidentificatie is optioneel en moet alfanumeriek zijn, bijvoorbeeld: MYPROJECT.', + 'Project owner' => 'Project eigenaar', + 'Personal projects do not have users and groups management.' => 'Persoonlijke projecten hebben geen gebruikers- en groepenbeheer.', + 'There is no project member.' => 'Er is geen projectlid.', + 'Priority' => 'Prioriteit', + 'Task priority' => 'Taak prioriteit', + 'General' => 'Algemeen', + 'Dates' => 'Datums', + 'Default priority' => 'Standaard prioriteit', + 'Lowest priority' => 'Laagste prioriteit', + 'Highest priority' => 'Hoogste prioriteit', + 'Close a task when there is no activity' => 'Een taak sluiten als er geen activiteit is', + 'Duration in days' => 'Duur in dagen', + 'Send email when there is no activity on a task' => 'Een e-mail sturen wanneer er geen activiteit is voor een taak', + 'Unable to fetch link information.' => 'Kan geen linkinformatie ophalen.', + 'Daily background job for tasks' => 'Dagelijkse achtergrondtaak voor taken', + 'Auto' => 'Auto', + 'Related' => 'Gerelateerd', + 'Attachment' => 'Bijlage', + 'Web Link' => 'Weblink', + 'External links' => 'Externe links', + 'Add external link' => 'Externe link toevoegen', + 'Type' => 'Type', + 'Dependency' => 'Afhankelijkheid', + 'Add internal link' => 'Interne link toevoegen', + 'Add a new external link' => 'Nieuwe externe link toevoegen', + 'Edit external link' => 'Externe link bewerken', + 'External link' => 'Externe link', + 'Copy and paste your link here...' => 'Kopieer en plak je link hier...', + 'URL' => 'URL', + 'Internal links' => 'Interne koppelingen', + 'Assign to me' => 'Aan mij toewijzen', + 'Me' => 'Mij', + 'Do not duplicate anything' => 'Niets dupliceren', + 'Projects management' => 'Projecten beheren', + 'Users management' => 'Gebruikers beheren', + 'Groups management' => 'Groepen beheren', + 'Create from another project' => 'Aanmaken vanuit een ander project', + 'open' => 'open', + 'closed' => 'gesloten', + 'Priority:' => 'Prioriteit:', + 'Reference:' => 'Referentie:', + 'Complexity:' => 'Complexiteit:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Kolom:', + 'Position:' => 'Positie:', + 'Creator:' => 'Aanmaker:', + 'Time estimated:' => 'Ingeschatte tijd:', + '%s hours' => '%s uur', + 'Time spent:' => 'Tijd besteed:', + 'Created:' => 'Aangemaakt:', + 'Modified:' => 'Gewijzigd:', + 'Completed:' => 'Afgerond:', + 'Started:' => 'Begonnen:', + 'Moved:' => 'Verplaatst:', + 'Task #%d' => 'Taak #%d', + 'Time format' => 'Tijd formaat', + 'Start date: ' => 'Begindatum: ', + 'End date: ' => 'Einddatum: ', + 'New due date: ' => 'Nieuwe einddatum: ', + 'Start date changed: ' => 'Startdatum gewijzigd: ', + 'Disable personal projects' => 'Persoonlijke projecten uitschakelen', + 'Do you really want to remove this custom filter: "%s"?' => 'Wil je deze aangepaste filter echt verwijderen: "%s"?', + 'Remove a custom filter' => 'Een aangepast filter verwijderen', + 'User activated successfully.' => 'Gebruiker succesvol geactiveerd.', + 'Unable to enable this user.' => 'Kan deze gebruiker niet inschakelen.', + 'User disabled successfully.' => 'Gebruiker uitgeschakeld.', + 'Unable to disable this user.' => 'Kan deze gebruiker niet uitschakelen.', + 'All files have been uploaded successfully.' => 'Alle bestanden zijn succesvol geüpload.', + 'The maximum allowed file size is %sB.' => 'De maximaal toegestane bestandsgrootte is %sB.', + 'Drag and drop your files here' => 'Sleep je bestanden hierheen', + 'choose files' => 'kies bestanden', + 'View profile' => 'Profiel bekijken', + 'Two Factor' => 'Twee-factor', + 'Disable user' => 'Gebruiker uitschakelen', + 'Do you really want to disable this user: "%s"?' => 'Wil je deze gebruiker echt uitschakelen: "%s"?', + 'Enable user' => 'Gebruiker inschakelen', + 'Do you really want to enable this user: "%s"?' => 'Wil je deze gebruiker echt inschakelen: "%s"?', + 'Download' => 'Downloaden', + 'Uploaded: %s' => 'Geüpload: %s', + 'Size: %s' => 'Grootte: %s', + 'Uploaded by %s' => 'Geüpload door %s', + 'Filename' => 'Bestandsnaam', + 'Size' => 'Grootte', + 'Column created successfully.' => 'Kolom succesvol aangemaakt.', + 'Another column with the same name exists in the project' => 'Een andere kolom met dezelfde naam bestaat in het project', + 'Default filters' => 'Standaard filters', + 'Your board doesn\'t have any columns!' => 'Je bord heeft geen kolommen!', + 'Change column position' => 'Kolompositie wijzigen', + 'Switch to the project overview' => 'Ga naar het projectoverzicht', + 'User filters' => 'Gebruikersfilters', + 'Category filters' => 'Categoriefilters', + 'Upload a file' => 'Een bestand uploaden', + 'View file' => 'Bestand bekijken', + 'Last activity' => 'Laatste activiteit', + 'Change subtask position' => 'Positie van subtaak wijzigen', + 'This value must be greater than %d' => 'Deze waarde moet groter zijn dan %d', + 'Another swimlane with the same name exists in the project' => 'Er bestaat een andere swimlane met dezelfde naam in het project', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Voorbeeld: https://example.kanboard.org/ (gebruikt om absolute URL\'s te genereren)', + 'Actions duplicated successfully.' => 'Acties succesvol gedupliceerd.', + 'Unable to duplicate actions.' => 'Kan acties niet dupliceren.', + 'Add a new action' => 'Voeg een nieuwe actie toe', + 'Import from another project' => 'Importeren vanuit een ander project', + 'There is no action at the moment.' => 'Er is op dit moment geen actie.', + 'Import actions from another project' => 'Importeer acties van een ander project', + 'There is no available project.' => 'Er is geen project beschikbaar.', + 'Local File' => 'Lokaal bestand', + 'Configuration' => 'Configuratie', + 'PHP version:' => 'PHP versie:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'OS versie:', + 'Database version:' => 'Database versie:', + 'Browser:' => 'Browser:', + 'Task view' => 'Taak bekijken', + 'Edit task' => 'Taak bewerken', + 'Edit description' => 'Beschrijving bewerken', + 'New internal link' => 'Nieuwe interne link', + 'Display list of keyboard shortcuts' => 'Lijst met sneltoetsen weergeven', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Mijn avatar uploaden', + 'Remove my image' => 'Mijn afbeelding verwijderen', + 'The OAuth2 state parameter is invalid' => 'De OAuth2-statusparameter is ongeldig', + 'User not found.' => 'Gebruiker niet gevonden.', + 'Search in activity stream' => 'Zoeken in activiteitenstroom', + 'My activities' => 'Mijn activiteiten', + 'Activity until yesterday' => 'Activiteit tot gisteren', + 'Activity until today' => 'Activiteit tot vandaag', + 'Search by creator: ' => 'Zoeken op maker: ', + 'Search by creation date: ' => 'Zoeken op aanmaakdatum: ', + 'Search by task status: ' => 'Zoeken op taakstatus: ', + 'Search by task title: ' => 'Zoeken op taaktitel: ', + 'Activity stream search' => 'Zoeken op activiteitenstroom', + 'Projects where "%s" is manager' => 'Projecten waar "%s" manager is', + 'Projects where "%s" is member' => 'Projecten waar "%s" lid van is', + 'Open tasks assigned to "%s"' => 'Open taken toegewezen aan "%s', + 'Closed tasks assigned to "%s"' => 'Gesloten taken toegewezen aan "%s', + 'Assign automatically a color based on a priority' => 'Automatisch een kleur toewijzen op basis van prioriteit', + 'Overdue tasks for the project(s) "%s"' => 'Achterstallige taken voor het project (de projecten) "%s".', + 'Upload files' => 'Bestanden uploaden', + 'Installed Plugins' => 'Geïnstalleerde plugins', + 'Plugin Directory' => 'Plugin map', + 'Plugin installed successfully.' => 'Plugin met succes geïnstalleerd.', + 'Plugin updated successfully.' => 'Plugin bijgewerkt.', + 'Plugin removed successfully.' => 'Plugin met succes verwijderd.', + 'Subtask converted to task successfully.' => 'Subtaak met succes geconverteerd naar taak.', + 'Unable to convert the subtask.' => 'Kan de subtaak niet converteren.', + 'Unable to extract plugin archive.' => 'Plugin-archief kan niet worden uitgepakt.', + 'Plugin not found.' => 'Plugin niet gevonden.', + 'You don\'t have the permission to remove this plugin.' => 'Je hebt geen toestemming om deze plugin te verwijderen.', + 'Unable to download plugin archive.' => 'Kan het plugin-archief niet downloaden.', + 'Unable to write temporary file for plugin.' => 'Kan geen tijdelijk bestand voor plugin schrijven.', + 'Unable to open plugin archive.' => 'Kan het plugin archief niet openen.', + 'There is no file in the plugin archive.' => 'Er is geen bestand in het plugin archief.', + 'Create tasks in bulk' => 'Taken in bulk aanmaken', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Je Kanboard-instantie is niet geconfigureerd om plugins te installeren vanuit de gebruikersinterface.', + 'There is no plugin available.' => 'Er is geen plugin beschikbaar.', + 'Install' => 'Installeer', + 'Update' => 'Bijwerken', + 'Up to date' => 'Up to date', + 'Not available' => 'Niet beschikbaar', + 'Remove plugin' => 'Plugin verwijderen', + 'Do you really want to remove this plugin: "%s"?' => 'Wil je deze plugin echt verwijderen: "%s"?', + 'Uninstall' => 'Verwijder', + 'Listing' => 'Lijst', + 'Metadata' => 'Metagegevens', + 'Manage projects' => 'Projecten beheren', + 'Convert to task' => 'Omzetten naar taak', + 'Convert sub-task to task' => 'Subtaak omzetten naar taak', + 'Do you really want to convert this sub-task to a task?' => 'Wil je deze subtaak echt omzetten naar een taak?', + 'My task title' => 'Mijn taak titel', + 'Enter one task by line.' => 'Per regel een taak invoeren.', + 'Number of failed login:' => 'Aantal mislukte aanmeldingen:', + 'Account locked until:' => 'Account vergrendeld tot:', + 'Email settings' => 'E-mailinstellingen', + 'Email sender address' => 'E-mailadres afzender', + 'Email transport' => 'E-mail transport', + 'Webhook token' => 'Webhook token', + 'Project tags management' => 'Project tags beheer', + 'Tag created successfully.' => 'Tag met succes aangemaakt.', + 'Unable to create this tag.' => 'Kan deze tag niet aanmaken.', + 'Tag updated successfully.' => 'Tag met succes bijgewerkt.', + 'Unable to update this tag.' => 'Kan deze tag niet bijwerken.', + 'Tag removed successfully.' => 'Tag met succes verwijderd.', + 'Unable to remove this tag.' => 'Kan deze tag niet verwijderen.', + 'Global tags management' => 'Beheer globale tags', + 'Tags' => 'Tags', + 'Tags management' => 'Beheer tags', + 'Add new tag' => 'Voeg een nieuwe tag toe', + 'Edit a tag' => 'Een tag bewerken', + 'Project tags' => 'Projecttags', + 'There is no specific tag for this project at the moment.' => 'Er zijn geen tags gedefinieerd voor dit project.', + 'Tag' => 'Tag', + 'Remove a tag' => 'Een tag verwijderen', + 'Do you really want to remove this tag: "%s"?' => 'Wil je deze tag echt verwijderen: "%s"?', + 'Global tags' => 'Globale tags', + 'There is no global tag at the moment.' => 'Er is momenteel geen globale tag.', + 'This field cannot be empty' => 'Dit veld kan niet leeg zijn', + 'Close a task when there is no activity in a specific column' => 'Een taak sluiten wanneer er geen activiteit is in een specifieke kolom', + '%s removed a subtask for the task #%d' => '%s verwijderde een subtaak voor de taak #%d', + '%s removed a comment on the task #%d' => '%s heeft een commentaar bij taak #%d verwijderd', + 'Comment removed on task #%d' => 'Commentaar bij taak #%d verwijderd', + 'Subtask removed on task #%d' => 'Subtaak verwijderd voor taak #%d', + 'Hide tasks in this column in the dashboard' => 'Verberg taken in deze kolom in het dashboard', + '%s removed a comment on the task %s' => '%s heeft een commentaar bij taak %s verwijderd', + '%s removed a subtask for the task %s' => '%s heeft een subtaak verwijderd voor de taak %s', + 'Comment removed' => 'Commentaar verwijderd', + 'Subtask removed' => 'Subtaak verwijderd', + '%s set a new internal link for the task #%d' => '%s heeft een nieuwe interne link geplaatst voor de taak #%d', + '%s removed an internal link for the task #%d' => '%s verwijderde een interne link voor de taak #%d', + 'A new internal link for the task #%d has been defined' => 'Een nieuwe interne link voor de taak #%d is gedefinieerd.', + 'Internal link removed for the task #%d' => 'Interne link verwijderd voor de taak #%d', + '%s set a new internal link for the task %s' => '%s heeft een nieuwe interne link ingesteld voor de taak %s', + '%s removed an internal link for the task %s' => '%s heeft een interne link verwijderd voor de taak %s', + 'Automatically set the due date on task creation' => 'Stel automatisch de vervaldatum in bij het aanmaken van een taak', + 'Move the task to another column when closed' => 'Verplaats de taak naar een andere kolom wanneer deze is gesloten', + 'Move the task to another column when not moved during a given period' => 'Verplaats de taak naar een andere kolom wanneer deze niet is verplaatst gedurende een bepaalde periode', + 'Dashboard for %s' => 'Dashboard voor %s', + 'Tasks overview for %s' => 'Taken overzicht voor %s', + 'Subtasks overview for %s' => 'Subtaken overzicht voor %s', + 'Projects overview for %s' => 'Projecten overzicht voor %s', + 'Activity stream for %s' => 'Activiteitenstroom voor %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Wijs een kleur toe wanneer de taak wordt verplaatst naar een specifieke swimlane', + 'Assign a priority when the task is moved to a specific swimlane' => 'Prioriteit toekennen wanneer de taak wordt verplaatst naar een specifieke swimlane', + 'User unlocked successfully.' => 'Gebruiker succesvol ontgrendeld.', + 'Unable to unlock the user.' => 'Kan de gebruiker niet ontgrendelen.', + 'Move a task to another swimlane' => 'Een taak naar een andere swimlane verplaatsen', + 'Creator Name' => 'Naam aanmaker', + 'Time spent and estimated' => 'Tijd besteed en geschat', + 'Move position' => 'Positie verplaatsen', + 'Move task to another position on the board' => 'Verplaats taak naar een andere positie op het bord', + 'Insert before this task' => 'Invoegen vóór deze taak', + 'Insert after this task' => 'Invoegen na deze taak', + 'Unlock this user' => 'Ontgrendel deze gebruiker', + 'Custom Project Roles' => 'Aangepaste projectrollen', + 'Add a new custom role' => 'Een nieuwe aangepaste rol toevoegen', + 'Restrictions for the role "%s"' => 'Beperkingen voor de rol "%s".', + 'Add a new project restriction' => 'Een nieuwe projectbeperking toevoegen', + 'Add a new drag and drop restriction' => 'Een nieuwe slepen-en-neerzetten beperking toevoegen', + 'Add a new column restriction' => 'Een nieuwe kolombeperking toevoegen', + 'Edit this role' => 'Deze rol bewerken', + 'Remove this role' => 'Deze role verwijderen', + 'There is no restriction for this role.' => 'Er is geen beperking voor deze rol.', + 'Only moving task between those columns is permitted' => 'Alleen het verplaatsen van taken tussen deze kolommen is toegestaan', + 'Close a task in a specific column when not moved during a given period' => 'Sluit een taak in een specifieke kolom als deze niet is verplaatst gedurende een bepaalde periode', + 'Edit columns' => 'Kolommen berwerken', + 'The column restriction has been created successfully.' => 'De kolombeperking is succesvol aangemaakt.', + 'Unable to create this column restriction.' => 'Kan deze kolombeperking niet aanmaken.', + 'Column restriction removed successfully.' => 'Kolombeperking succesvol verwijderd.', + 'Unable to remove this restriction.' => 'Kan deze beperking niet verwijderen.', + 'Your custom project role has been created successfully.' => 'Je aangepaste projectrol is succesvol aangemaakt.', + 'Unable to create custom project role.' => 'Kan aangepaste projectrol niet aanmaken.', + 'Your custom project role has been updated successfully.' => 'Je aangepaste projectrol is succesvol bijgewerkt.', + 'Unable to update custom project role.' => 'Kan aangepaste projectrol niet bijwerken.', + 'Custom project role removed successfully.' => 'Aangepaste projectrol succesvol verwijderd.', + 'Unable to remove this project role.' => 'Kan deze projectrol niet verwijderen.', + 'The project restriction has been created successfully.' => 'De projectrestrictie is succesvol aangemaakt.', + 'Unable to create this project restriction.' => 'Kan deze projectrestrictie niet aanmaken.', + 'Project restriction removed successfully.' => 'Projectbeperking succesvol verwijderd.', + 'You cannot create tasks in this column.' => 'Je kunt geen taken aanmaken in deze kolom.', + 'Task creation is permitted for this column' => 'Taken aanmaken is toegestaan voor deze kolom.', + 'Closing or opening a task is permitted for this column' => 'Een taak sluiten of openen is toegestaan voor deze kolom.', + 'Task creation is blocked for this column' => 'Taak aanmaken is geblokkeerd voor deze kolom', + 'Closing or opening a task is blocked for this column' => 'Een taak sluiten of openen is geblokkeerd voor deze kolom', + 'Task creation is not permitted' => 'Taken aanmaken is niet toegestaan', + 'Closing or opening a task is not permitted' => 'Een taak sluiten of openen is niet toegestaan', + 'New drag and drop restriction for the role "%s"' => 'Nieuwe slepen en neerzetten beperking voor de rol "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Mensen die behoren tot deze rol kunnen alleen taken verplaatsen tussen de bron- en bestemmingskolom.', + 'Remove a column restriction' => 'Een kolombeperking verwijderen', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Wil je deze kolombeperking echt verwijderen? "%s" naar "%s"?', + 'New column restriction for the role "%s"' => 'Nieuwe kolombeperking voor de rol "%s".', + 'Rule' => 'Regel', + 'Do you really want to remove this column restriction?' => 'Wil je deze kolombeperking echt verwijderen?', + 'Custom roles' => 'Aangepaste rollen', + 'New custom project role' => 'Nieuwe aangepaste projectrol', + 'Edit custom project role' => 'Aangepaste projectrol bewerken', + 'Remove a custom role' => 'Een aangepaste rol verwijderen', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Wil je deze aangepaste rol echt verwijderen: "%s"? Alle mensen die aan deze rol zijn toegewezen worden projectlid.', + 'There is no custom role for this project.' => 'Er is geen aangepaste rol voor dit project.', + 'New project restriction for the role "%s"' => 'Nieuwe projectbeperking voor de rol "%s".', + 'Restriction' => 'Beperking', + 'Remove a project restriction' => 'Een projectbeperking verwijderen', + 'Do you really want to remove this project restriction: "%s"?' => 'Wil je deze projectrestrictie echt verwijderen: "%s"?', + 'Duplicate to multiple projects' => 'Dupliceren naar meerdere projecten', + 'This field is required' => 'Dit veld is verplicht', + 'Moving a task is not permitted' => 'Een taak verplaatsen is niet toegestaan', + 'This value must be in the range %d to %d' => 'Deze waarde moet in het bereik %d tot %d liggen', + 'You are not allowed to move this task.' => 'Je mag deze taak niet verplaatsen.', + 'API User Access' => 'API-gebruikerstoegang', + 'Preview' => 'Voorbeeld', + 'Write' => 'Schrijven', + 'Write your text in Markdown' => 'Schrijf je tekst in Markdown', + 'No personal API access token registered.' => 'Geen persoonlijk API toegangstoken geregistreerd.', + 'Your personal API access token is "%s"' => 'Je persoonlijke API toegang token is "%s".', + 'Remove your token' => 'Verwijder je token', + 'Generate a new token' => 'Genereer een nieuw token', + 'Showing %d-%d of %d' => 'Weergave %d-%d van %d', + 'Outgoing Emails' => 'Uitgaande e-mails', + 'Add or change currency rate' => 'Valutakoers toevoegen of wijzigen', + 'Reference currency: %s' => 'Referentievaluta: %s', + 'Add custom filters' => 'Nieuw aangepast filter toevoegen', + 'Export' => 'Exporteren', + 'Add link label' => 'Linklabel toevoegen', + 'Incompatible Plugins' => 'Onverenigbare plugins', + 'Compatibility' => 'Compatibiliteit', + 'Permissions and ownership' => 'Rechten en eigendom', + 'Priorities' => 'Prioriteiten', + 'Close this window' => 'Dit scherm sluiten', + 'Unable to upload this file.' => 'Kan dit bestand niet uploaden.', + 'Import tasks' => 'Taken importeren', + 'Choose a project' => 'Kies een project', + 'Profile' => 'Profiel', + 'Application role' => 'Rol toepassing', + '%d invitations were sent.' => '%d uitnodigingen zijn verzonden.', + '%d invitation was sent.' => '%d uitnodigingen verzonden.', + 'Unable to create this user.' => 'Kan deze gebruiker niet aanmaken.', + 'Kanboard Invitation' => 'Kanboard uitnodiging', + 'Visible on dashboard' => 'Zichtbaar op dashboard', + 'Created at:' => 'Aangemaakt op:', + 'Updated at:' => 'Bijgewerkt op:', + 'There is no custom filter.' => 'Er is geen aangepaste filter.', + 'New User' => 'Nieuwe gebruiker', + 'Authentication' => 'Authenticatie', + 'If checked, this user will use a third-party system for authentication.' => 'Als deze optie is aangevinkt, zal deze gebruiker een systeem van derden gebruiken voor verificatie.', + 'The password is necessary only for local users.' => 'Het wachtwoord is alleen nodig voor lokale gebruikers.', + 'You have been invited to register on Kanboard.' => 'Je bent uitgenodigd om je te registreren op Kanboard.', + 'Click here to join your team' => 'Klik hier om lid te worden van je team', + 'Invite people' => 'Nodig personen uit', + 'Emails' => 'E-mails', + 'Enter one email address by line.' => 'Voer één e-mailadres per regel in.', + 'Add these people to this project' => 'Voeg deze mensen toe aan dit project', + 'Add this person to this project' => 'Voeg deze persoon toe aan dit project', + 'Sign-up' => 'Aanmelden', + 'Credentials' => 'Gegevens', + 'New user' => 'Nieuwe gebruiker', + 'This username is already taken' => 'Deze gebruikersnaam is al gebruikt', + 'Your profile must have a valid email address.' => 'Je profiel moet een geldig e-mailadres hebben.', + 'TRL - Turkish Lira' => 'TRL - Turkse lire', + 'The project email is optional and could be used by several plugins.' => 'Het e-mailadres van het project is optioneel en kan door verschillende plugins worden gebruikt.', + 'The project email must be unique across all projects' => 'Het project e-mailadres moet uniek zijn voor alle projecten', + 'The email configuration has been disabled by the administrator.' => 'De e-mailconfiguratie is uitgeschakeld door de beheerder.', + 'Close this project' => 'Dit project sluiten', + 'Open this project' => 'Dit project openen', + 'Close a project' => 'Sluit een project', + 'Do you really want to close this project: "%s"?' => 'Wil je dit project echt sluiten: "%s"?', + 'Reopen a project' => 'Heropen dit project', + 'Do you really want to reopen this project: "%s"?' => 'Wil je dit project echt heropenen: "%s"?', + 'This project is open' => 'Dit project is open', + 'This project is closed' => 'Dit project is gesloten', + 'Unable to upload files, check the permissions of your data folder.' => 'Kan geen bestanden uploaden, controleer de rechten van je gegevensmap.', + 'Another category with the same name exists in this project' => 'Er bestaat een andere categorie met dezelfde naam in dit project', + 'Comment sent by email successfully.' => 'Commentaar succesvol verzonden per e-mail.', + 'Sent by email to "%s" (%s)' => 'Per e-mail verzonden naar "%s" (%s)', + 'Unable to read uploaded file.' => 'Kan geüpload bestand niet lezen.', + 'Database uploaded successfully.' => 'Database succesvol geüpload.', + 'Task sent by email successfully.' => 'Taak succesvol verzonden per e-mail.', + 'There is no category in this project.' => 'Er is geen categorie in dit project.', + 'Send by email' => 'Per e-mail verzenden', + 'Create and send a comment by email' => 'Een opmerking maken en verzenden per e-mail', + 'Subject' => 'Onderwerp', + 'Upload the database' => 'De database uploaden', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Je kunt de eerder gedownloade Sqlite-database uploaden (Gzip-formaat).', + 'Database file' => 'Bestand database', + 'Upload' => 'uploaden', + 'Your project must have at least one active swimlane.' => 'Je project moet ten minste één actieve swimlane hebben.', + 'Project: %s' => 'Project: %s', + 'Automatic action not found: "%s"' => 'Automatische actie niet gevonden: "%s"', + '%d projects' => '%d projecten', + '%d project' => '%d project', + 'There is no project.' => 'Er is geen project.', + 'Sort' => 'Sorteren', + 'Project ID' => 'Project-ID', + 'Project name' => 'Naam project', + 'Public' => 'Publiek', + 'Personal' => 'Persoonlijk', + '%d tasks' => '%d taken', + '%d task' => '%d taak', + 'Task ID' => 'Taak-ID', + 'Assign automatically a color when due date is expired' => 'Wijs automatisch een kleur toe als de deadline is verstreken', + 'Total score in this column across all swimlanes' => 'Totale score in deze kolom over alle swimlanes', + 'HRK - Kuna' => 'HRK - kuna', + 'ARS - Argentine Peso' => 'ARS - Argentijnse peso', + 'COP - Colombian Peso' => 'COP - Colombiaanse peso', + '%d groups' => '%d groepen', + '%d group' => '%d groep', + 'Group ID' => 'Groep ID', + 'External ID' => 'Externe ID', + '%d users' => '%d gebruikers', + '%d user' => '%d gebruiker', + 'Hide subtasks' => 'Subtaken verbergen', + 'Show subtasks' => 'Subtaken weergeven', + 'Authentication Parameters' => 'Verificatieparameters', + 'API Access' => 'API toegang', + 'No users found.' => 'Geen gebruikers gevonden', + 'User ID' => 'Gebruikers-ID', + 'Notifications are activated' => 'Meldingen zijn geactiveerd', + 'Notifications are disabled' => 'Meldingen zijn uitgeschakeld', + 'User disabled' => 'Gebruiker uitgeschakeld', + '%d notifications' => '%d meldingen', + '%d notification' => '%d melding', + 'There is no external integration installed.' => 'Er is geen externe integratie geïnstalleerd.', + 'You are not allowed to update tasks assigned to someone else.' => 'Je mag taken die aan iemand anders zijn toegewezen niet bijwerken.', + 'You are not allowed to change the assignee.' => 'Je mag de toegewezene niet wijzigen.', + 'Task suppression is not permitted' => 'Taakonderdrukking is niet toegestaan', + 'Changing assignee is not permitted' => 'Het wijzigen van de toegewezene is niet toegestaan', + 'Update only assigned tasks is permitted' => 'Alleen toegewezen taken bijwerken is toegestaan', + 'Only for tasks assigned to the current user' => 'Alleen voor taken die zijn toegewezen aan de huidige gebruiker', + 'My projects' => 'Mijn projecten', + 'You are not a member of any project.' => 'Je bent geen lid van een project.', + 'My subtasks' => 'Mijn subtaken', + '%d subtasks' => '%d subtaken', + '%d subtask' => '%d subtaken', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Alleen het verplaatsen van taken tussen deze kolommen is toegestaan voor taken die zijn toegewezen aan de huidige gebruiker.', + '[DUPLICATE]' => '[DUPLICEREN]', + 'DKK - Danish Krona' => 'DKK - Deense kroon', + 'Remove user from group' => 'Verwijder gebruiker uit groep', + 'Assign the task to its creator' => 'Wijs de taak toe aan de maker', + 'This task was sent by email to "%s" with subject "%s".' => 'Deze taak is per e-mail verzonden naar "%s" met als onderwerp "%s".', + 'Predefined Email Subjects' => 'Vooraf gedefinieerde e-mailonderwerpen', + 'Write one subject by line.' => 'Schrijf een onderwerp per regel.', + 'Create another link' => 'Maak een andere link', + 'BRL - Brazilian Real' => 'BRL - Braziliaanse real', + 'Add a new Kanboard task' => 'Een nieuwe Kanboard-taak toevoegen', + 'Subtask not started' => 'Subtaak niet gestart', + 'Subtask currently in progress' => 'Subtaak momenteel bezig', + 'Subtask completed' => 'Subtaak voltooid', + 'Subtask added successfully.' => 'Subtaak succesvol toegevoegd.', + '%d subtasks added successfully.' => '%d subtaken succesvol toegevoegd.', + 'Enter one subtask by line.' => 'Voer één subtaak per regel in.', + 'Predefined Contents' => 'Vooraf gedefinieerde inhoud', + 'Predefined contents' => 'Vooraf gedefinieerde inhoud', + 'Predefined Task Description' => 'Vooraf gedefinieerde taakbeschrijving', + 'Do you really want to remove this template? "%s"' => 'Wil je deze sjabloon echt verwijderen? "%s"', + 'Add predefined task description' => 'Vooraf gedefinieerde taakbeschrijving toevoegen', + 'Predefined Task Descriptions' => 'Vooraf gedefinieerde taakbeschrijvingen', + 'Template created successfully.' => 'Sjabloon succesvol aangemaakt.', + 'Unable to create this template.' => 'Kan deze sjabloon niet aanmaken.', + 'Template updated successfully.' => 'Sjabloon bijgewerkt.', + 'Unable to update this template.' => 'Kan deze sjabloon niet bijwerken.', + 'Template removed successfully.' => 'Sjabloon met succes verwijderd.', + 'Unable to remove this template.' => 'Kan deze sjabloon niet verwijderen.', + 'Template for the task description' => 'Sjabloon voor de taakbeschrijving', + 'The start date is greater than the end date' => 'De begindatum is groter dan de einddatum', + 'Tags must be separated by a comma' => 'Tags moeten worden gescheiden door een komma', + 'Only the task title is required' => 'Alleen de taaktitel is verplicht', + 'Creator Username' => 'Bedenker Gebruikersnaam', + 'Color Name' => 'Kleur Naam', + 'Column Name' => 'Kolom Naam', + 'Swimlane Name' => 'Naam swimlane', + 'Time Estimated' => 'Geschatte tijd', + 'Time Spent' => 'Bestede tijd', + 'External Link' => 'Externe link', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Deze functie schakelt de iCal feed, RSS feed en de openbare bordweergave in.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Stop de timer van alle subtaken wanneer je een taak naar een andere kolom verplaatst', + 'Subtask Title' => 'Subtaak Titel', + 'Add a subtask and activate the timer when moving a task to another column' => 'Voeg een subtaak toe en activeer de timer bij het verplaatsen van een taak naar een andere kolom', + 'days' => 'dagen', + 'minutes' => 'minuten', + 'seconds' => 'seconden', + 'Assign automatically a color when preset start date is reached' => 'Automatisch een kleur toewijzen wanneer een vooraf ingestelde begindatum is bereikt', + 'Move the task to another column once a predefined start date is reached' => 'Verplaats de taak naar een andere kolom wanneer een vooraf ingestelde begindatum is bereikt', + 'This task is now linked to the task %s with the relation "%s"' => 'Deze taak is nu gekoppeld aan de taak %s met de relatie "%s".', + 'The link with the relation "%s" to the task %s has been removed' => 'De link met de relatie "%s" naar de taak %s is verwijderd', + 'Custom Filter:' => 'Aangepast filter:', + 'Unable to find this group.' => 'Deze groep kan niet worden gevonden', + '%s moved the task #%d to the column "%s"' => '%s heeft de taak #%d naar de kolom "%s" verplaatst', + '%s moved the task #%d to the position %d in the column "%s"' => '%s verplaatste de taak #%d naar de positie %d in de kolom "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s heeft de taak #%d verplaatst naar de swimlane "%s".', + '%sh spent' => '%sh besteed', + '%sh estimated' => '%sh geschat', + 'Select All' => 'Alles selecteren', + 'Unselect All' => 'Alles deselecteren', + 'Apply action' => 'Actie toepassen', + 'Move selected tasks to another column or swimlane' => 'Verplaats geselecteerde taken naar een andere kolom of swimlane', + 'Edit tasks in bulk' => 'Taken in bulk bewerken', + 'Choose the properties that you would like to change for the selected tasks.' => 'Kies de eigenschappen die je wilt wijzigen voor de geselecteerde taken.', + 'Configure this project' => 'Configureer dit project', + 'Start now' => 'Start nu', + '%s removed a file from the task #%d' => '%s heeft een bestand verwijderd uit de taak #%d', + 'Attachment removed from task #%d: %s' => 'Bijlage verwijderd uit taak #%d: %s', + 'No color' => 'Geen kleur', + 'Attachment removed "%s"' => 'Bijlage verwijderd "%s".', + '%s removed a file from the task %s' => '%s heeft een bestand van de taak %s verwijderd', + 'Move the task to another swimlane when assigned to a user' => 'Verplaats de taak naar een andere swimlane wanneer deze is toegewezen aan een gebruiker', + 'Destination swimlane' => 'Bestemming swimlane', + 'Assign a category when the task is moved to a specific swimlane' => 'Een categorie toewijzen wanneer de taak wordt verplaatst naar een specifieke swimlane', + 'Move the task to another swimlane when the category is changed' => 'Verplaats de taak naar een ander swimlane wanneer de categorie is gewijzigd', + 'Reorder this column by priority (ASC)' => 'Herschik deze kolom op prioriteit (oplopend)', + 'Reorder this column by priority (DESC)' => 'Herschik deze kolom op prioriteit (aflopend)', + 'Reorder this column by assignee and priority (ASC)' => 'Herschik deze kolom op ontvanger en prioriteit (oplopend)', + 'Reorder this column by assignee and priority (DESC)' => 'Sorteer deze kolom opnieuw op geadresseerde en prioriteit (aflopend)', + 'Reorder this column by assignee (A-Z)' => 'Sorteer deze kolom opnieuw op geadresseerde (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Sorteer deze kolom opnieuw op geadresseerde (Z-A)', + 'Reorder this column by due date (ASC)' => 'Sorteer deze kolom opnieuw op vervaldatum (oplopend)', + 'Reorder this column by due date (DESC)' => 'Sorteer deze kolom opnieuw op vervaldatum (aflopend)', + 'Reorder this column by id (ASC)' => 'Sorteer deze kolom opnieuw op id (oplopend)', + 'Reorder this column by id (DESC)' => 'Sorteer deze kolom opnieuw op id (aflopend)', + '%s moved the task #%d "%s" to the project "%s"' => '%s heeft de taak #%d "%s" verplaatst naar het project "%s".', + 'Task #%d "%s" has been moved to the project "%s"' => 'Taak #%d "%s" is verplaatst naar het project "%s".', + 'Move the task to another column when the due date is less than a certain number of days' => 'Verplaats de taak naar een andere kolom wanneer de vervaldatum minder is dan een bepaald aantal dagen', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automatisch de begindatum bijwerken wanneer de taak wordt verplaatst uit een specifieke kolom', + 'HTTP Client:' => 'HTTP-client', + 'Assigned' => 'Toegekend', + 'Task limits apply to each swimlane individually' => 'Taaklimieten gelden voor elke swimlane afzonderlijk', + 'Column task limits apply to each swimlane individually' => 'Kolom taaklimieten zijn van toepassing op elke swimlane afzonderlijk', + 'Column task limits are applied to each swimlane individually' => 'Kolomtaaklimieten worden toegepast op elke swimlane afzonderlijk', + 'Column task limits are applied across swimlanes' => 'Kolom taaklimieten worden toegepast op alle swimlanes', + 'Task limit: ' => 'Taaklimiet', + 'Change to global tag' => 'Globale tag wijzigen', + 'Do you really want to make the tag "%s" global?' => 'Wil je de tag "%s" echt globaal maken?', + 'Enable global tags for this project' => 'Globale tags inschakelen voor dit project', + 'Group membership(s):' => 'Groep lidmaatschap(pen)', + '%s is a member of the following group(s): %s' => '%s is lid van de volgende groep(en): %s', + '%d/%d group(s) shown' => '%d/%d groep(en) getoond', + 'Subtask creation or modification' => 'Subtaak aanmaken of wijzigen', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Wijs de taak toe aan een specifieke gebruiker wanneer de taak wordt verplaatst naar een specifieke swimlane', + 'Comment' => 'Commentaar', + 'Collapse vertically' => 'Verticaal samenvouwen', + 'Expand vertically' => 'Verticaal uitvouwen', + 'MXN - Mexican Peso' => 'MXN - Mexicaanse peso', + 'Estimated vs actual time per column' => 'Geschatte vs werkelijke tijd per kolom', + 'HUF - Hungarian Forint' => 'HUF - Hongaarse forint', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'U moet een bestand selecteren om als uw avatar te uploaden!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Het bestand dat u heeft geüpload is geen geldige afbeelding! (Alleen *.gif, *.jpg, *.jpeg en *.png zijn toegestaan!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Stel automatisch de vervaldatum in wanneer de taak uit een specifieke kolom wordt verplaatst', + 'No other projects found.' => 'Geen andere projecten gevonden.', + 'Tasks copied successfully.' => 'Taken succesvol gekopieerd.', + 'Unable to copy tasks.' => 'Kan taken niet kopiëren.', + 'Theme' => 'Thema', + 'Theme:' => 'Thema:', + 'Light theme' => 'Licht thema', + 'Dark theme' => 'Donker thema', + 'Automatic theme - Sync with system' => 'Automatisch thema - Synchroniseren met systeem', + 'Application managers or more' => 'Applicatiebeheerders of meer', + 'Administrators' => 'Beheerders', + 'Visibility:' => 'Zichtbaarheid:', + 'Standard users' => 'Standaardgebruikers', + 'Visibility is required' => 'Zichtbaarheid is vereist', + 'The visibility should be an app role' => 'De zichtbaarheid moet een app-rol zijn', + 'Reply' => 'Beantwoorden', + '%s wrote: ' => '%s schreef: ', + 'Number of visible tasks in this column and swimlane' => 'Aantal zichtbare taken in deze kolom en swimlane', + 'Number of tasks in this swimlane' => 'Aantal taken in deze swimlane', + 'Unable to find another subtask in progress, you can close this window.' => 'Kan geen andere subtaak in uitvoering vinden, u kunt dit venster sluiten.', + 'This theme is invalid' => 'Dit thema is ongeldig', + 'This role is invalid' => 'Deze rol is ongeldig', + 'This timezone is invalid' => 'Deze tijdzone is ongeldig', + 'This language is invalid' => 'Deze taal is ongeldig', + 'This URL is invalid' => 'Deze URL is ongeldig', + 'Date format invalid' => 'Ongeldig datumnotatie', + 'Time format invalid' => 'Ongeldige tijdnotatie', + 'Invalid Mail transport' => 'Ongeldige mailtransport', + 'Color invalid' => 'Ongeldige kleur', + 'This value must be greater or equal to %d' => 'Deze waarde moet groter of gelijk zijn aan %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Voeg een BOM toe aan het begin van het bestand (vereist voor Microsoft Excel)', + 'Just add these tag(s)' => 'Voeg alleen deze tags toe', + 'Remove internal link(s)' => 'Verwijder interne links', + 'Import tasks from another project' => 'Importeer taken uit een ander project', + 'Select the project to copy tasks from' => 'Selecteer het project waarvan u taken wilt kopiëren', + 'The total maximum allowed attachments size is %sB.' => 'De maximaal toegestane totale grootte voor bijlagen is %sB.', + 'Add attachments' => 'Bijlagen toevoegen', + 'Task #%d "%s" is overdue' => 'Taak #%d "%s" is te laat', + 'Enable notifications by default for all new users' => 'Standaard meldingen inschakelen voor alle nieuwe gebruikers', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Wijs de taak toe aan de maker voor specifieke kolommen als er niet handmatig een gebruiker is toegewezen', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Wijs een taak toe aan de ingelogde gebruiker bij het wijzigen van kolom naar de opgegeven kolom als er geen gebruiker is toegewezen', +]; diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php new file mode 100644 index 0000000..c66fe26 --- /dev/null +++ b/app/Locale/pl_PL/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Brak', + 'Edit' => 'Edytuj', + 'Remove' => 'UsuÅ„', + 'Yes' => 'Tak', + 'No' => 'Nie', + 'cancel' => 'anuluj', + 'or' => 'lub', + 'Yellow' => 'Żółty', + 'Blue' => 'Niebieski', + 'Green' => 'Zielony', + 'Purple' => 'Fioletowy', + 'Red' => 'Czerwony', + 'Orange' => 'PomaraÅ„czowy', + 'Grey' => 'Szary', + 'Brown' => 'BrÄ…z', + 'Deep Orange' => 'CiemnopomaraÅ„czowy', + 'Dark Grey' => 'Ciemnoszary', + 'Pink' => 'Różowy', + 'Teal' => 'Turkusowy', + 'Cyan' => 'Cyjan', + 'Lime' => 'Limonkowy', + 'Light Green' => 'Jasnozielony', + 'Amber' => 'Bursztynowy', + 'Save' => 'Zapisz', + 'Login' => 'Login', + 'Official website:' => 'Oficjalna strona:', + 'Unassigned' => 'Nieprzypisany', + 'View this task' => 'Zobacz zadanie', + 'Remove user' => 'UsuÅ„ użytkownika', + 'Do you really want to remove this user: "%s"?' => 'Na pewno chcesz usunąć użytkownika: "%s"?', + 'All users' => 'Wszyscy użytkownicy', + 'Username' => 'Nazwa użytkownika', + 'Password' => 'HasÅ‚o', + 'Administrator' => 'Administrator', + 'Sign in' => 'Zaloguj', + 'Users' => 'Użytkownicy', + 'Forbidden' => 'Zabroniony', + 'Access Forbidden' => 'DostÄ™p zabroniony', + 'Edit user' => 'Edytuj użytkownika', + 'Logout' => 'Wyloguj', + 'Bad username or password' => 'ZÅ‚a nazwa użytkownika lub hasÅ‚o', + 'Edit project' => 'Edytuj projekt', + 'Name' => 'Nazwa', + 'Projects' => 'Projekty', + 'No project' => 'Brak projektów', + 'Project' => 'Projekt', + 'Status' => 'Status', + 'Tasks' => 'Zadania', + 'Board' => 'Tablica', + 'Actions' => 'Akcje', + 'Inactive' => 'Nieaktywny', + 'Active' => 'Aktywny', + 'Unable to update this board.' => 'Nie można zaktualizować tablicy.', + '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"', + 'Add a new column' => 'Dodaj nowÄ… kolumnÄ™', + 'Title' => 'Nazwa', + 'Assigned to %s' => 'Przypisane do %s', + 'Remove a column' => 'UsuÅ„ kolumnÄ™', + '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"?', + 'Settings' => 'Ustawienia', + 'Application settings' => 'Ustawienia aplikacji', + 'Language' => 'JÄ™zyk', + 'Webhook token:' => 'Token :', + 'API token:' => 'Token dla API', + 'Database size:' => 'Rozmiar bazy danych :', + 'Download the database' => 'Pobierz bazÄ™ danych', + 'Optimize the database' => 'Optymalizuj bazÄ™ danych', + '(VACUUM command)' => '(komenda VACUUM)', + '(Gzip compressed Sqlite file)' => '(skompresowany gzip-em plik bazy danych sqlite)', + 'Close a task' => 'ZakoÅ„cz zadanie', + 'Column' => 'Kolumna', + 'Color' => 'Kolor', + 'Assignee' => 'Odpowiedzialny', + 'Create another task' => 'Dodaj kolejne zadanie', + 'New task' => 'Nowe zadanie', + 'Open a task' => 'Otwórz zadanie', + 'Do you really want to open this task: "%s"?' => 'Na pewno chcesz otworzyć zadanie: "%s"?', + 'Back to the board' => 'Powrót do tablicy', + 'There is nobody assigned' => 'Nikt nie jest przypisany', + 'Column on the board:' => 'Kolumna na tablicy:', + 'Close this task' => 'Zamknij zadanie', + 'Open this task' => 'Otwórz zadanie', + 'There is no description.' => 'Brak opisu.', + 'Add a new task' => 'Dodaj zadanie', + 'The username is required' => 'Nazwa użytkownika jest wymagana', + 'The maximum length is %d characters' => 'Maksymalna dÅ‚ugość wynosi %d znaków', + 'The minimum length is %d characters' => 'Minimalna dÅ‚ugość wynosi %d znaków', + 'The password is required' => 'HasÅ‚o jest wymagane', + 'This value must be an integer' => 'Wartość musi być liczbÄ… caÅ‚kowitÄ…', + 'The username must be unique' => 'Nazwa użytkownika musi być unikalna', + 'The user id is required' => 'ID użytkownika jest wymagane', + 'Passwords don\'t match' => 'HasÅ‚a nie pasujÄ… do siebie', + 'The confirmation is required' => 'Wymagane jest potwierdzenie', + 'The project is required' => 'Projekt jest wymagany', + 'The id is required' => 'ID jest wymagane', + 'The project id is required' => 'ID projektu jest wymagane', + 'The project name is required' => 'Nazwa projektu jest wymagana', + 'The title is required' => 'TytuÅ‚ jest wymagany', + 'Settings saved successfully.' => 'Ustawienia zapisane.', + 'Unable to save your settings.' => 'Nie udaÅ‚o siÄ™ zapisać ustawieÅ„.', + 'Database optimization done.' => 'Optymalizacja bazy danych zakoÅ„czona.', + 'Your project has been created successfully.' => 'Projekt zostaÅ‚ pomyÅ›lnie utworzony.', + 'Unable to create your project.' => 'Nie udaÅ‚o siÄ™ stworzyć projektu.', + 'Project updated successfully.' => 'Projekt zaktualizowany.', + 'Unable to update this project.' => 'Nie można zaktualizować projektu.', + 'Unable to remove this project.' => 'Nie można usunąć projektu.', + 'Project removed successfully.' => 'Projekt usuniÄ™ty.', + 'Project activated successfully.' => 'Projekt aktywowany.', + 'Unable to activate this project.' => 'Nie można aktywować projektu.', + 'Project disabled successfully.' => 'Projekt wyłączony.', + 'Unable to disable this project.' => 'Nie można wyłączyć projektu.', + 'Unable to open this task.' => 'Nie można otworzyć tego zadania.', + 'Task opened successfully.' => 'Zadanie otwarte.', + 'Unable to close this task.' => 'Nie można zamknąć tego zadania.', + 'Task closed successfully.' => 'Zadanie zamkniÄ™te.', + 'Unable to update your task.' => 'Nie można zaktualizować tego zadania.', + 'Task updated successfully.' => 'Zadanie zaktualizowane.', + 'Unable to create your task.' => 'Nie można dodać zadania.', + 'Task created successfully.' => 'Zadanie zostaÅ‚o utworzone.', + 'User created successfully.' => 'Użytkownik dodany', + 'Unable to create your user.' => 'Nie udaÅ‚o siÄ™ dodać użytkownika.', + 'User updated successfully.' => 'Profil użytkownika zostaÅ‚ zaaktualizowany.', + 'User removed successfully.' => 'Użytkownik usuniÄ™ty.', + 'Unable to remove this user.' => 'Nie udaÅ‚o siÄ™ usunąć użytkownika.', + 'Board updated successfully.' => 'Tablica zostaÅ‚a zaktualizowana.', + 'Ready' => 'Gotowe', + 'Backlog' => 'Log', + 'Work in progress' => 'W trakcie', + 'Done' => 'ZakoÅ„czone', + 'Application version:' => 'Wersja aplikacji:', + 'Id' => 'Id', + 'Public link' => 'Link publiczny', + 'Timezone' => 'Strefa czasowa', + 'Sorry, I didn\'t find this information in my database!' => 'Niestety nie znaleziono tej informacji w bazie danych', + 'Page not found' => 'Strona nie istnieje', + 'Complexity' => 'Poziom trudnoÅ›ci', + 'Task limit' => 'Limit zadaÅ„', + 'Task count' => 'Liczba zadaÅ„', + 'User' => 'Użytkownik', + 'Comments' => 'Komentarze', + 'Comment is required' => 'Komentarz jest wymagany', + 'Comment added successfully.' => 'Komentarz dodany', + 'Unable to create your comment.' => 'Nie udaÅ‚o siÄ™ dodać komentarza', + 'Due Date' => 'Termin', + 'Invalid date' => 'Błędna data', + 'Automatic actions' => 'Akcje automatyczne', + 'Your automatic action has been created successfully.' => 'Twoja akcja zostaÅ‚a dodana', + 'Unable to create your automatic action.' => 'Nie udaÅ‚o siÄ™ utworzyć akcji', + 'Remove an action' => 'UsuÅ„ akcjÄ™', + 'Unable to remove this action.' => 'Nie można usunąć akcji', + 'Action removed successfully.' => 'Akcja usuniÄ™ta', + 'Automatic actions for the project "%s"' => 'Akcje automatyczne dla projektu "%s"', + 'Add an action' => 'Nowa akcja', + 'Event name' => 'Nazwa zdarzenia', + 'Action' => 'Akcja', + 'Event' => 'Zdarzenie', + 'When the selected event occurs execute the corresponding action.' => 'Gdy nastÄ™puje wybrane zdarzenie, uruchom odpowiedniÄ… akcjÄ™', + 'Next step' => 'NastÄ™pny krok', + 'Define action parameters' => 'Zdefiniuj parametry akcji', + 'Do you really want to remove this action: "%s"?' => 'Na pewno chcesz usunąć akcjÄ™ "%s"?', + 'Remove an automatic action' => 'UsuÅ„ akcjÄ™ automatycznÄ…', + 'Assign the task to a specific user' => 'Przypisz zadanie do wybranego użytkownika', + 'Assign the task to the person who does the action' => 'Przypisz zadanie do osoby wykonujÄ…cej akcjÄ™', + 'Duplicate the task to another project' => 'Kopiuj zadanie do innego projektu', + 'Move a task to another column' => 'Przeniesienie zadania do innej kolumny', + 'Task modification' => 'Modyfikacja zadania', + 'Task creation' => 'Tworzenie zadania', + 'Closing a task' => 'ZamkniÄ™cie zadania', + 'Assign a color to a specific user' => 'Przypisz kolor do wybranego użytkownika', + 'Position' => 'Pozycja', + 'Duplicate to project' => 'Skopiuj do innego projektu', + 'Duplicate' => 'Utwórz kopiÄ™', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Komentarz zostaÅ‚ zapisany.', + 'Unable to update your comment.' => 'Nie udaÅ‚o siÄ™ zapisać komentarza.', + 'Remove a comment' => 'UsuÅ„ komentarz', + 'Comment removed successfully.' => 'Komentarz zostaÅ‚ usuniÄ™ty.', + 'Unable to remove this comment.' => 'Nie udaÅ‚o siÄ™ usunąć komentarza.', + 'Do you really want to remove this comment?' => 'Czy na pewno usunąć ten komentarz?', + 'Current password for the user "%s"' => 'Aktualne hasÅ‚o dla użytkownika "%s"', + 'The current password is required' => 'Wymanage jest aktualne hasÅ‚o', + 'Wrong password' => 'Błędne hasÅ‚o', + 'Unknown' => 'Nieznany', + 'Last logins' => 'Ostatnie logowania', + 'Login date' => 'Data logowania', + 'Authentication method' => 'Sposób uwierzytelnienia', + 'IP address' => 'Adres IP', + 'User agent' => 'PrzeglÄ…darka', + 'Persistent connections' => 'StaÅ‚e połączenia', + 'No session.' => 'Brak sesji.', + 'Expiration date' => 'Data zakoÅ„czenia', + 'Remember Me' => 'PamiÄ™taj mnie', + 'Creation date' => 'Data utworzenia', + 'Everybody' => 'Wszyscy', + 'Open' => 'Otwarto', + 'Closed' => 'ZamkniÄ™to', + 'Search' => 'Szukaj', + 'Nothing found.' => 'Nic nie znaleziono', + 'Due date' => 'Termin', + 'Description' => 'Opis', + '%d comments' => '%d Komentarzy', + '%d comment' => '%d Komentarz', + 'Email address invalid' => 'Błędny adres email', + 'Your external account is not linked anymore to your profile.' => 'Twoje zewnÄ™trzne konto nie jest już połączone z profilem', + 'Unable to unlink your external account.' => 'Nie można odłączyć zewnÄ™trznego konta', + 'External authentication failed' => 'Uwierzytelnianie zewnÄ™trzne zakoÅ„czyÅ‚o siÄ™ niepowodzeniem', + 'Your external account is linked to your profile successfully.' => 'Twoje zewnÄ™trzne konto zostaÅ‚o pomyÅ›lnie połączone z profilem', + 'Email' => 'Email', + 'Task removed successfully.' => 'Zadanie usuniÄ™to pomyÅ›lnie.', + 'Unable to remove this task.' => 'Nie można usunąć tego zadania.', + 'Remove a task' => 'UsuÅ„ zadanie', + 'Do you really want to remove this task: "%s"?' => 'Czy na pewno chcesz usunąć zadanie "%s"?', + 'Assign automatically a color based on a category' => 'Przypisz kolor automatycznie na podstawie kategori', + 'Assign automatically a category based on a color' => 'Przypisz kategoriÄ™ automatycznie na podstawie koloru', + 'Task creation or modification' => 'Tworzenie lub usuwanie zadania', + 'Category' => 'Kategoria', + 'Category:' => 'Kategoria:', + 'Categories' => 'Kategorie', + 'Your category has been created successfully.' => 'PomyÅ›lnie utworzono kategoriÄ™.', + 'This category has been updated successfully.' => 'PomyÅ›lnie zaktualizowano kategoriÄ™', + 'Unable to update this category.' => 'Nie można zaktualizować kategorii', + 'Remove a category' => 'UsuÅ„ kategoriÄ™', + 'Category removed successfully.' => 'PomyÅ›lnie usuniÄ™to kategoriÄ™.', + 'Unable to remove this category.' => 'Nie można usunąć tej kategorii.', + 'Category modification for the project "%s"' => 'Zmiana kategorii projektu "%s"', + 'Category Name' => 'Nazwa kategorii', + 'Add a new category' => 'Utwórz nowÄ… kategoriÄ™', + 'Do you really want to remove this category: "%s"?' => 'Czy na pewno chcesz usunąć kategoriÄ™: "%s"?', + 'All categories' => 'Wszystkie kategorie', + 'No category' => 'Brak kategorii', + 'The name is required' => 'Nazwa jest wymagana', + 'Remove a file' => 'UsuÅ„ plik', + 'Unable to remove this file.' => 'Nie można usunąć tego pliku.', + 'File removed successfully.' => 'Plik UsuniÄ™ty pomyÅ›lnie.', + 'Attach a document' => 'Dołącz plik', + 'Do you really want to remove this file: "%s"?' => 'Czy na pewno chcesz usunąć plik: "%s"?', + 'Attachments' => 'Załączniki', + 'Edit the task' => 'Edytuj zadanie', + 'Add a comment' => 'Dodaj komentarz', + 'Edit a comment' => 'Edytuj komentarz', + 'Summary' => 'Podsumowanie', + 'Time tracking' => 'Åšledzenie czasu', + 'Estimate:' => 'Szacowany:', + 'Spent:' => 'Przeznaczony:', + 'Do you really want to remove this sub-task?' => 'Czy na pewno chcesz usunąć to pod-zadanie?', + 'Remaining:' => 'PozostaÅ‚o:', + 'hours' => 'godzin(y)', + 'estimated' => 'szacowany', + 'Sub-Tasks' => 'Pod-zadania', + 'Add a sub-task' => 'Dodaj pod-zadanie', + 'Original estimate' => 'Szacowanie poczÄ…tkowe', + 'Create another sub-task' => 'Dodaj kolejne pod-zadanie', + 'Time spent' => 'SpÄ™dzony czas', + 'Edit a sub-task' => 'Edytuj pod-zadanie', + 'Remove a sub-task' => 'UsuÅ„ pod-zadanie', + 'The time must be a numeric value' => 'Czas musi być wartoÅ›ciÄ… liczbowÄ…', + 'Todo' => 'Do zrobienia', + 'In progress' => 'W trakcie', + 'Sub-task removed successfully.' => 'Pod-zadanie usuniÄ™te pomyÅ›lnie.', + 'Unable to remove this sub-task.' => 'Nie można usunąć tego pod-zadania.', + 'Sub-task updated successfully.' => 'Pod-zadanie zaktualizowane pomyÅ›lnie.', + 'Unable to update your sub-task.' => 'Nie można zaktualizować tego pod-zadania.', + 'Unable to create your sub-task.' => 'Nie można utworzyć tego pod-zadania.', + 'Maximum size: ' => 'Maksymalny rozmiar: ', + 'Display another project' => 'WyÅ›wietl inny projekt', + 'Created by %s' => 'Utworzone przez %s', + 'Tasks Export' => 'Eksport zadaÅ„', + 'Start Date' => 'Data poczÄ…tkowa', + 'Execute' => 'Wykonaj', + 'Task Id' => 'Identyfikator Zadania', + 'Creator' => 'Autor', + 'Modification date' => 'Data modyfikacji', + 'Completion date' => 'Data ukoÅ„czenia', + 'Clone' => 'Sklonuj', + 'Project cloned successfully.' => 'Projekt sklonowany pomyÅ›lnie.', + 'Unable to clone this project.' => 'Nie można sklonować projektu.', + 'Enable email notifications' => 'Włącz powiadomienia email', + 'Task position:' => 'Pozycja zadania:', + 'The task #%d has been opened.' => 'Zadania #%d zostaÅ‚y otwarte.', + 'The task #%d has been closed.' => 'Zadania #%d zostaÅ‚y zamkniÄ™te.', + 'Sub-task updated' => 'Pod-zadanie zaktualizowane', + 'Title:' => 'Nazwa:', + 'Status:' => 'Status:', + 'Assignee:' => 'Przypisano do:', + 'Time tracking:' => 'Åšledzenie czasu: ', + '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 comment' => 'Nowy Komentarz', + 'Comment updated' => 'Komentarz zaktualizowany', + 'New subtask' => 'Nowe pod-zadanie', + 'I only want to receive notifications for these projects:' => 'ChcÄ™ otrzymywać powiadomienia tylko dla poniższych projektów:', + 'view the task on Kanboard' => 'Zobacz zadanie', + 'Public access' => 'DostÄ™p publiczny', + 'Disable public access' => 'Zablokuj dostÄ™p publiczny', + 'Enable public access' => 'Odblokuj dostÄ™p publiczny', + 'Public access disabled' => 'DostÄ™p publiczny zablokowany', + 'Move the task to another project' => 'PrzenieÅ› zadanie do innego projektu', + 'Move to project' => 'PrzenieÅ› do innego projektu', + 'Do you really want to duplicate this task?' => 'Czy na pewno chcesz zduplikować to zadanie?', + 'Duplicate a task' => 'Zduplikuj zadanie', + 'External accounts' => 'Konta zewnÄ™trzne', + 'Account type' => 'Typ konta', + 'Local' => 'Lokalne', + 'Remote' => 'Zdalne', + 'Enabled' => 'Odblokowane', + 'Disabled' => 'Zablokowane', + 'Login:' => 'Nazwa Użytkownika (login):', + 'Full Name:' => 'ImiÄ™ i Nazwisko', + 'Email:' => 'Email: ', + 'Notifications:' => 'Powiadomienia: ', + 'Notifications' => 'Powiadomienia', + 'Account type:' => 'Typ konta:', + 'Edit profile' => 'Edytuj profil', + 'Change password' => 'ZmieÅ„ hasÅ‚o', + 'Password modification' => 'Zmiana hasÅ‚a', + 'External authentications' => 'Uwierzytelnienia zewnÄ™trzne', + 'Never connected.' => 'Nigdy nie połączone.', + 'No external authentication enabled.' => 'Brak włączonych uwierzytelnieÅ„ zewnÄ™trznych.', + 'Password modified successfully.' => 'HasÅ‚o zmienione pomyÅ›lne.', + 'Unable to change the password.' => 'Nie można zmienić hasÅ‚a.', + 'Change category' => 'ZmieÅ„ kategoriÄ™', + '%s updated the task %s' => '%s zaktualizowaÅ‚ zadanie %s', + '%s opened the task %s' => '%s otworzyÅ‚ zadanie %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s przeniósÅ‚ zadanie %s na pozycjÄ™ #%d w kolumnie "%s"', + '%s moved the task %s to the column "%s"' => '%s przeniósÅ‚ zadanie %s do kolumny "%s"', + '%s created the task %s' => '%s utworzyÅ‚ zadanie %s', + '%s closed the task %s' => '%s zamknÄ…Å‚ zadanie %s', + '%s created a subtask for the task %s' => '%s utworzyÅ‚ pod-zadanie dla zadania %s', + '%s updated a subtask for the task %s' => '%s zaktualizowaÅ‚ pod-zadanie dla zadania %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Przypisano do %s z szacowanym czasem wykonania %s/%sh', + 'Not assigned, estimate of %sh' => 'Nie przypisane, szacowany czas wykonania %sh', + '%s updated a comment on the task %s' => '%s zaktualizowaÅ‚ komentarz do zadania %s', + '%s commented the task %s' => '%s skomentowaÅ‚ zadanie %s', + '%s\'s activity' => 'Aktywność %s', + 'RSS feed' => 'KanaÅ‚ RSS', + '%s updated a comment on the task #%d' => '%s zaktualizowaÅ‚ komentarz do zadania #%d', + '%s commented on the task #%d' => '%s skomentowaÅ‚ zadanie #%d', + '%s updated a subtask for the task #%d' => '%s zaktualizowaÅ‚ pod-zadanie dla zadania #%d', + '%s created a subtask for the task #%d' => '%s utworzyÅ‚ pod-zadanie dla zadania #%d', + '%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 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 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', + 'Create a task from an external provider' => 'Utwórz zadanie z dostawcy zewnÄ™trznego', + 'Change the assignee based on an external username' => 'ZmieÅ„ osobÄ™ odpowiedzialnÄ… na podstawie zewnÄ™trznej nazwy użytkownika', + 'Change the category based on an external label' => 'ZmieÅ„ kategoriÄ™ na podstawie zewnÄ™trznej etykiety', + 'Reference' => 'Odniesienie', + 'Label' => 'Etykieta', + 'Database' => 'Baza danych', + 'About' => 'Informacje', + 'Database driver:' => 'Silnik bazy danych:', + 'Board settings' => 'Ustawienia tablicy', + 'Webhook settings' => 'Ustawienia webhook', + 'Reset token' => 'Resetuj token', + 'API endpoint:' => 'Endpoint API', + 'Refresh interval for personal board' => 'CzÄ™stotliwość odÅ›wieżania dla tablicy prywatnej', + 'Refresh interval for public board' => 'CzÄ™stotliwość odÅ›wieżania dla tablicy publicznej', + 'Task highlight period' => 'Okres wyróżniania zadaÅ„', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Okres (w sekundach) wymagany do uznania zadania za niedawno zmienione (0 ab zablokować, domyÅ›lnie 2 dni)', + 'Frequency in second (60 seconds by default)' => 'CzÄ™stotliwość w sekundach (domyÅ›lnie 60 sekund)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'CzÄ™stotliwość w sekundach (0 aby zablokować, domyÅ›lnie 10 sekund)', + 'Application URL' => 'Adres URL aplikacji', + 'Token regenerated.' => 'Token wygenerowany ponownie.', + 'Date format' => 'Format daty', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Format ISO jest zawsze akceptowany, przykÅ‚ady: "%s", "%s"', + 'New personal project' => 'Nowy projekt prywatny', + 'This project is personal' => 'Ten projekt jest prywatny', + 'Add' => 'Dodaj', + 'Start date' => 'Data rozpoczÄ™cia', + 'Time estimated' => 'Szacowany czas', + 'There is nothing assigned to you.' => 'Nie ma przypisanych zadaÅ„', + 'My tasks' => 'Moje zadania', + 'Activity stream' => 'StrumieÅ„ aktywnoÅ›ci', + 'Dashboard' => 'Dashboard', + 'Confirmation' => 'Potwierdzenie', + 'Webhooks' => 'Webhooki', + 'API' => 'API', + 'Create a comment from an external provider' => 'Utwórz komentarz od zewnÄ™trznego dostawcy', + 'Project management' => 'Menadżer projektu', + 'Columns' => 'Kolumny', + 'Task' => 'Zadanie', + 'Percentage' => 'Procent', + 'Number of tasks' => 'Liczba zadaÅ„', + 'Task distribution' => 'Rozmieszczenie zadaÅ„', + 'Analytics' => 'Analizy', + 'Subtask' => 'Pod-zadanie', + 'User repartition' => 'PrzydziaÅ‚ użytkownika', + '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.', + 'Previous' => 'Poprzedni', + 'The id must be an integer' => 'ID musi być liczbÄ… caÅ‚kowitÄ…', + 'The project id must be an integer' => 'ID projektu musi być liczbÄ… caÅ‚kowitÄ…', + 'The status must be an integer' => 'Status musi być liczbÄ… caÅ‚kowitÄ…', + 'The subtask id is required' => 'ID pod-zadanie jest wymagane', + 'The subtask id must be an integer' => 'ID pod-zadania musi być liczbÄ… caÅ‚kowitÄ…', + 'The task id is required' => 'ID zadania jest wymagane', + 'The task id must be an integer' => 'ID zadania musi być liczbÄ… caÅ‚kowitÄ…', + 'The user id must be an integer' => 'ID użytkownika musi być liczbÄ… caÅ‚kowitÄ…', + 'This value is required' => 'Wymagana wartość', + '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', + 'Daily project summary' => 'Dzienne raport z projektu', + 'Daily project summary export' => 'Eksport dziennego podsumowania projektu', + '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', + 'Add a new swimlane' => 'Dodaj tor', + 'Default swimlane' => 'DomyÅ›lny tor', + 'Do you really want to remove this swimlane: "%s"?' => 'Czy na pewno chcesz usunąć tor: "%s"?', + 'Inactive swimlanes' => 'Nieaktywne tory', + 'Remove a swimlane' => 'UsuÅ„ tor', + 'Swimlane modification for the project "%s"' => 'Edycja torów dla projektu "%s"', + 'Swimlane removed successfully.' => 'Tor usuniÄ™ty pomyÅ›lnie.', + 'Swimlanes' => 'Tory', + 'Swimlane updated successfully.' => 'Zaktualizowano tor.', + 'Unable to remove this swimlane.' => 'Nie można usunąć toru.', + 'Unable to update this swimlane.' => 'Nie można zaktualizować toru.', + 'Your swimlane has been created successfully.' => 'Tor utworzony pomyÅ›lnie.', + 'Example: "Bug, Feature Request, Improvement"' => 'PrzykÅ‚ad: "Błąd, Żądanie FunkcjonalnoÅ›ci, Udoskonalenia"', + 'Default categories for new projects (Comma-separated)' => 'DomyÅ›lne kategorie dla nowych projektów (oddzielone przecinkiem)', + 'Integrations' => 'Integracje', + 'Integration with third-party services' => 'Integracja z usÅ‚ugami firm trzecich', + 'Subtask Id' => 'ID pod-zadania', + 'Subtasks' => 'Pod-zadania', + 'Subtasks Export' => 'Eksport pod-zadaÅ„', + 'Task Title' => 'Nazwa zadania', + 'Untitled' => 'Bez nazwy', + 'Application default' => 'DomyÅ›lne dla aplikacji', + 'Language:' => 'JÄ™zyk:', + 'Timezone:' => 'Strefa czasowa:', + 'All columns' => 'Wszystkie kolumny', + 'Next' => 'NastÄ™pny', + '#%d' => 'nr %d', + 'All swimlanes' => 'Wszystkie tory', + 'All colors' => 'Wszystkie kolory', + 'Moved to column %s' => 'Przeniosiono do kolumny %s', + 'User dashboard' => 'Panel użytkownika', + 'Allow only one subtask in progress at the same time for a user' => 'Zezwalaj na tylko jedno pod-zadanie o statusie "w trakcie" jednoczeÅ›nie', + 'Edit column "%s"' => 'ZmieÅ„ kolumnÄ™ "%s"', + 'Select the new status of the subtask: "%s"' => 'Wybierz nowy status dla pod-zadania: "%s"', + 'Subtask timesheet' => 'OÅ› czasu pod-zadania', + 'There is nothing to show.' => 'Nie nic do wyÅ›wietlenia', + 'Time Tracking' => 'Åšledzenie czasu', + 'You already have one subtask in progress' => 'Masz już zadanie o statusie "w trakcie"', + 'Which parts of the project do you want to duplicate?' => 'Które elementy projektu chcesz zduplikować?', + 'Disallow login form' => 'Zablokuj możliwość logowania', + 'Start' => 'PoczÄ…tek', + 'End' => 'Koniec', + 'Task age in days' => 'Wiek zadania w dniach', + 'Days in this column' => 'Dni w tej kolumnie', + '%dd' => '%d dni', + 'Add a new link' => 'Dodaj nowy link', + 'Do you really want to remove this link: "%s"?' => 'Czy na pewno chcesz usunąć ten link: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Czy na pewno chcesz usunąć ten link razem z zadaniem nr %d?', + 'Field required' => 'Pole wymagane', + 'Link added successfully.' => 'Link dodany', + 'Link updated successfully.' => 'Link zaktualizowany', + 'Link removed successfully.' => 'Link usuniÄ™ty', + 'Link labels' => 'Etykiety linku', + 'Link modification' => 'Modyfikuj link', + 'Opposite label' => 'Etykieta odwrotna', + 'Remove a link' => 'UsuÅ„ link', + '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', + 'Unable to create your link.' => 'Nie można utworzyć linku.', + 'Unable to update your link.' => 'Nie można zaktualizować linku,', + 'Unable to remove this link.' => 'Nie można usunąć linku,', + 'relates to' => 'odnosi siÄ™ do', + 'blocks' => 'blokuje', + 'is blocked by' => 'jest blokowane przez', + 'duplicates' => 'duplikuje', + 'is duplicated by' => 'jest duplikowane przez', + 'is a child of' => 'jest dzieckiem', + 'is a parent of' => 'jest rodzicem', + 'targets milestone' => 'oznacza krok milowy', + 'is a milestone of' => 'jest krokiem milowym', + 'fixes' => 'naprawia', + 'is fixed by' => 'zostaÅ‚o naprawione przez', + 'This task' => 'To zadanie', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'RozwiÅ„ zadania', + 'Collapse tasks' => 'ZwiÅ„ zadania', + 'Expand/collapse tasks' => 'ZwiÅ„/RozwiÅ„ zadania', + 'Close dialog box' => 'Zamknij okno', + 'Submit a form' => 'WyÅ›lij formularz', + 'Board view' => 'Widok tablicy', + 'Keyboard shortcuts' => 'Skróty klawiszowe', + 'Open board switcher' => 'Przełącz tablice', + 'Application' => 'Aplikacja', + 'Compact view' => 'Widok kompaktowy', + 'Horizontal scrolling' => 'Przewijanie poziome', + 'Compact/wide view' => 'PeÅ‚ny/Kompaktowy widok', + 'Currency' => 'Waluta', + 'Personal project' => 'Projekt prywatny', + 'AUD - Australian Dollar' => 'AUD - Dolar australijski', + 'CAD - Canadian Dollar' => 'CAD - Dolar kanadyjski', + 'CHF - Swiss Francs' => 'CHF - Frank szwajcarski', + 'Custom Stylesheet' => 'Niestandardowy arkusz stylów', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Funt brytyjski', + 'INR - Indian Rupee' => 'INR - Rupia indyjska', + 'JPY - Japanese Yen' => 'JPY - Jen japoÅ„ski', + 'NZD - New Zealand Dollar' => 'NZD - Dolar nowozelandzki', + 'PEN - Peruvian Sol' => 'PEN - Sol peruwiaÅ„ski', + 'RSD - Serbian dinar' => 'RSD - Dinar serbski', + 'CNY - Chinese Yuan' => 'CNY - Juan chiÅ„ski', + 'USD - US Dollar' => 'USD - Dolar amerykaÅ„ski', + 'VES - Venezuelan Bolívar' => 'VES - Boliwar wenezuelski', + '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', + 'Move the task to another column when assignee is cleared' => 'PrzenieÅ› zadanie do innej kolumny gdy osoba odpowiedzialna zostanie usuniÄ™ta', + 'Source column' => 'Kolumna źródÅ‚owa', + 'Transitions' => 'Przeniesienia', + 'Executer' => 'WykonaÅ‚', + 'Time spent in the column' => 'Czas spÄ™dzony w tej kolumnie', + 'Task transitions' => 'Przeniesienia zadaÅ„', + 'Task transitions export' => 'Wygeneruj raport z przeniesieÅ„ zadaÅ„', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ten raport zawiera wszystkie przeniesienia pomiÄ™dzy kolumnami wraz z datÄ… oraz osobÄ… odpowiedzialnÄ…', + 'Currency rates' => 'Kursy walut', + 'Rate' => 'Kurs', + 'Change reference currency' => 'ZmieÅ„ walutÄ™ referencyjnÄ…', + 'Reference currency' => 'Waluta referencyjna', + 'The currency rate has been added successfully.' => 'Dodano kurs waluty', + 'Unable to add this currency rate.' => 'Nie można dodać kursu waluty', + 'Webhook URL' => 'Adres webhooka', + '%s removed the assignee of the task %s' => '%s usunÄ…Å‚ osobÄ™ przypisanÄ… do zadania %s', + 'Information' => 'Informacje', + 'Check two factor authentication code' => 'Sprawdź kod weryfikujÄ…cy', + 'The two factor authentication code is not valid.' => 'Kod weryfikujÄ…cy niepoprawny', + 'The two factor authentication code is valid.' => 'Kod weryfikujÄ…cy poprawny', + 'Code' => 'Kod', + 'Two factor authentication' => 'Dwustopniowe uwierzytelnianie', + 'This QR code contains the key URI: ' => 'Ten kod QR zawiera URI klucza: ', + 'Check my code' => 'Sprawdź kod', + 'Secret key: ' => 'Tajny kod: ', + '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' => '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', + 'Add a screenshot' => 'Dołącz zrzut ekranu', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Zrób zrzut ekranu i wciÅ›nij CTRL+V by dodać go tutaj.', + 'Screenshot uploaded successfully.' => 'Zrzut ekranu dodany.', + 'SEK - Swedish Krona' => 'SEK - Korona szwedzka', + 'Identifier' => 'Identyfikator', + 'Disable two factor authentication' => 'Wyłącz dwustopniowe uwierzytelnianie', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Czy na pewno chcesz wyłączyć dwustopniowe uwierzytelnianie dla tego użytkownika: "%s"?', + 'Edit link' => 'Edytuj link', + 'Start to type task title...' => 'Rozpocznij wpisywanie tytuÅ‚u zadania...', + 'A task cannot be linked to itself' => 'Link do zadania nie może wskazywać na samego siebie', + 'The exact same link already exists' => 'Taki link już istnieje', + 'Recurrent task is scheduled to be generated' => 'Zaplanowano zadanie cykliczne', + 'Score' => 'Wynik', + 'The identifier must be unique' => 'Identyfikator musi być unikatowy', + 'This linked task id doesn\'t exists' => 'Id zadania nie istnieje', + 'This value must be alphanumeric' => 'Ta wartość musi być alfanumeryczna', + 'Edit recurrence' => 'Edytuj rekurencje', + 'Generate recurrent task' => 'Włącz rekurencje', + 'Trigger to generate recurrent task' => 'Wyzwalacz tworzÄ…cy zadanie cykliczne', + 'Factor to calculate new due date' => 'Czynnik wyliczajÄ…cy nowy termin', + 'Timeframe to calculate new due date' => 'Ramy czasowe do wyliczenia nowego terminu', + 'Base date to calculate new due date' => 'Data bazowa do wyliczenia nowego terminu', + 'Action date' => 'Data akcji', + 'Base date to calculate new due date: ' => 'Data bazowa do wyliczenia nowego terminu: ', + 'This task has created this child task: ' => 'Zadanie utworzyÅ‚o zadanie pokrewne: ', + 'Day(s)' => 'Dni', + 'Existing due date' => 'IstniejÄ…cy termin', + 'Factor to calculate new due date: ' => 'Czynnik wyliczajÄ…cy nowy termin: ', + 'Month(s)' => 'MiesiÄ™cy', + 'This task has been created by: ' => 'Zadanie utworzone przez: ', + 'Recurrent task has been generated:' => 'Zadanie cykliczne zostaÅ‚o utworzone: ', + 'Timeframe to calculate new due date: ' => 'Ramy czasowe do wyliczenia nowego terminu: ', + 'Trigger to generate recurrent task: ' => 'Wyzwalacz tworzÄ…cy zadanie cykliczne: ', + 'When task is closed' => 'ZamkniÄ™cie zadania', + 'When task is moved from first column' => 'Przeniesienie zadania z pierwszej kolumny', + 'When task is moved to last column' => 'Przeniesienie zadania do ostatniej kolumny', + 'Year(s)' => 'Lat', + 'Project settings' => 'Ustawienia Projektu', + 'Automatically update the start date' => 'Automatycznie aktualizuj datÄ™ rozpoczÄ™cia', + 'iCal feed' => 'kanaÅ‚ iCal', + 'Preferences' => 'Ustawienia', + 'Security' => 'Zabezpieczenia', + 'Two factor authentication disabled' => 'Dwustopniowe uwierzytelnianie wyłączone', + 'Two factor authentication enabled' => 'Dwustopniowe uwierzytelnianie włączone', + 'Unable to update this user.' => 'Nie można zaktualizować tego użytkownika', + 'There is no user management for personal projects.' => 'Projekty prywatne nie wspierajÄ… zarzÄ…dzania użytkownikami. Projekt prywatny ma tylko jednego użytkownika.', + 'User that will receive the email' => 'Adresat', + 'Email subject' => 'Temat', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'Wygeneruj komentarz pod zadaniem podczas przenoszenia miÄ™dzy kolumnami', + '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', + 'Notification' => 'Powiadomienie', + '%s moved the task #%d to the first swimlane' => '%s przeniosÅ‚ zadanie #%d na pierwszy tor', + 'Swimlane' => 'Tor', + '%s moved the task %s to the first swimlane' => '%s przeniosÅ‚ zadanie %s na pierwszy tor', + '%s moved the task %s to the swimlane "%s"' => '%s przeniosÅ‚ zadanie %s na tor "%s"', + 'This report contains all subtasks information for the given date range.' => 'Niniejszy raport zawiera wszystkie informacje o pod-zadaniach dla podanego zakresu dat.', + 'This report contains all tasks information for the given date range.' => 'Niniejszy raport zawiera wszystkie informacje o zadaniach dla podanego zakresu dat.', + 'Project activities for %s' => 'AktywnoÅ›ci w ramach projektu dla %s', + 'view the board on Kanboard' => 'Zobacz tablice', + 'The task has been moved to the first swimlane' => 'Zadanie zostaÅ‚o przeniesione do piewszego toru', + 'The task has been moved to another swimlane:' => 'Zadanie zostaÅ‚o przeniesione do innego toru:', + 'New title: %s' => 'Nowy tytuÅ‚: %s', + 'The task is not assigned anymore' => 'Brak osoby odpowiedzialnej za zadanie', + 'New assignee: %s' => 'Nowy odpowiedzialny: %s', + 'There is no category now' => 'Aktualnie zadanie nie posiada kategorii', + 'New category: %s' => 'Nowa kategoria: %s', + 'New color: %s' => 'Nowy kolor: %s', + 'New complexity: %d' => 'Nowa zÅ‚ożoność: %d', + 'The due date has been removed' => 'Termin zostaÅ‚ usuniÄ™ty', + 'There is no description anymore' => 'Nie ma już opisu', + 'Recurrence settings has been modified' => 'Ustawienia cyklu zostaÅ‚y zmienione', + 'Time spent changed: %sh' => 'SpÄ™dzony czas ulegÅ‚ zmianie: %sh', + 'Time estimated changed: %sh' => 'Szacowany czas ulegÅ‚ zmianie: %sh', + 'The field "%s" has been updated' => 'Pole "%s" zostaÅ‚o zaktualizowane', + 'The description has been modified:' => 'Opis zostaÅ‚ zmodyfikowany:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'NaprawdÄ™ chcesz zamknąć zadanie "%s" wraz z wszystkimi pod-zadaniami?', + 'I want to receive notifications for:' => 'WysyÅ‚aj powiadomienia dla:', + 'All tasks' => 'Wszystkich zadaÅ„', + 'Only for tasks assigned to me' => 'Tylko zadaÅ„ przypisanych do mnie', + 'Only for tasks created by me' => 'Tylko zadaÅ„ utworzonych przeze mnie', + 'Only for tasks created by me and tasks assigned to me' => 'Tylko zadaÅ„ przypisanych lub utworzonych przeze mnie', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Ogółem dla wszystkich kolumn', + 'You need at least 2 days of data to show the chart.' => 'Potrzebujesz przynajmniej 2 dni by wyÅ›wietlić wykres', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Zatrzymaj pomiar czasu', + 'Start timer' => 'Uruchom pomiar czasu', + 'My activity stream' => 'Moja aktywność', + 'Search tasks' => 'Szukaj zadaÅ„', + 'Reset filters' => 'Resetuj zastosowane filtry', + 'My tasks due tomorrow' => 'Moje zadania do jutra', + 'Tasks due today' => 'Zadania do dzisiaj', + 'Tasks due tomorrow' => 'Zadania do jutra', + 'Tasks due yesterday' => 'Zadania na wczoraj', + 'Closed tasks' => 'ZamkniÄ™te zadania', + 'Open tasks' => 'Otwarte zadania', + 'Not assigned' => 'Nieprzypisane zadania', + 'View advanced search syntax' => 'Pomoc dotyczÄ…ca budowania filtrów', + 'Overview' => 'Podsumowanie', + 'Board/Calendar/List view' => 'Widok: Tablica/Kalendarz/Lista', + 'Switch to the board view' => 'Przełącz na tablicÄ™', + 'Switch to the list view' => 'Przełącz na listÄ™', + 'Go to the search/filter box' => 'Użyj pola wyszukiwania/filtrów', + 'There is no activity yet.' => 'Brak powiadomieÅ„', + 'No tasks found.' => 'Nie znaleziono zadaÅ„', + 'Keyboard shortcut: "%s"' => 'Skrót klawiaturowy: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtr', + 'Advanced search' => 'Zaawansowane wyszukiwanie', + 'Example of query: ' => 'PrzykÅ‚adowe zapytanie:', + 'Search by project: ' => 'Szukaj wg projektów:', + 'Search by column: ' => 'Szukaj wg kolumn:', + 'Search by assignee: ' => 'Szukaj wg użytkownika:', + 'Search by color: ' => 'Szukaj wg koloru:', + 'Search by category: ' => 'Szukaj wg kategorii:', + 'Search by description: ' => 'Szukaj wg opisu:', + 'Search by due date: ' => 'Szukaj wg terminu:', + 'Average time spent in each column' => 'Åšredni czas spÄ™dzony w każdej z kolumn', + 'Average time spent' => 'Åšredni spÄ™dzony czas', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Niniejszy wykres pokazuje Å›redni czas spÄ™dzony w każdej z kolumn dla ostatnich %d zadaÅ„.', + 'Average Lead and Cycle time' => 'Åšredni czas cyklu i realizacji', + 'Average lead time: ' => 'Åšredni czas realizacji:', + 'Average cycle time: ' => 'Åšredni czas cyklu:', + 'Cycle Time' => 'Czas cyklu', + 'Lead Time' => 'Czas realizacji', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Niniejszy wykres pokazuje Å›redni czas cyklu i realizacji dla ostatnich %d zadaÅ„ na przestrzeni czasu.', + 'Average time into each column' => 'Åšredni czas dla każdej kolumny', + 'Lead and cycle time' => 'Czas cyklu i realizacji', + 'Lead time: ' => 'Czas realizacji:', + 'Cycle time: ' => 'Czas cyklu:', + 'Time spent in each column' => 'Czas spÄ™dzony przez zadanie w każdej z kolumn', + 'The lead time is the duration between the task creation and the completion.' => 'Czas realizacji pomiÄ™dzy utworzeniem a ukoÅ„czeniem zadania.', + 'The cycle time is the duration between the start date and the completion.' => 'Czas cyklu pomiÄ™dzy datÄ… rozpoczÄ™cia a ukoÅ„czeniem zadania.', + 'If the task is not closed the current time is used instead of the completion date.' => 'JeÅ›li zadanie nie jest zamkniÄ™te, bieżący czas zostaje użyty zamiast daty ukoÅ„czenia.', + 'Set the start date automatically' => 'Ustaw automatycznie datÄ™ rozpoczÄ™cia', + 'Edit Authentication' => 'Edycja uwierzytelnienia', + '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.', + '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.', + 'Trigger automatically subtask time tracking' => 'Ustaw automatyczne Å›ledzenie czasu dla pod-zadaÅ„', + 'Include closed tasks in the cumulative flow diagram' => 'Obejmuj zamkniÄ™te zadania w zbiorczym diagramie przepÅ‚ywu', + 'Current swimlane: %s' => 'Bieżący tor: %s', + 'Current column: %s' => 'Bieżąca kolumna: %s', + 'Current category: %s' => 'Bieżąca kategoria: %s', + 'no category' => 'brak kategorii', + 'Current assignee: %s' => 'Aktualnie odpowiedzialna osoba: %s', + 'not assigned' => 'Brak osoby odpowiedzialnej', + 'Author:' => 'Autor', + 'contributors' => 'współautorzy', + 'License:' => 'Licencja:', + 'License' => 'Licencja', + 'Enter the text below' => 'Wpisz tekst poniżej', + 'Start date:' => 'Data rozpoczÄ™cia:', + 'Due date:' => 'Termin', + 'People who are project managers' => 'Użytkownicy bÄ™dÄ…cy menedżerami projektu', + 'People who are project members' => 'Użytkownicy bÄ™dÄ…cy uczestnikami projektu', + 'NOK - Norwegian Krone' => 'NOK - Korona norweska', + 'Show this column' => 'Pokaż tÄ… kolumnÄ™', + 'Hide this column' => 'Ukryj tÄ… kolumnÄ™', + 'End date' => 'Data zakoÅ„czenia', + 'Users overview' => 'PrzeglÄ…d użytkowników', + 'Members' => 'CzÅ‚onkowie', + 'Shared project' => 'Projekt udostÄ™pniony', + 'Project managers' => 'Menedżerowie projektu', + 'Projects list' => 'Lista projektów', + 'End date:' => 'Data zakoÅ„czenia:', + 'Change task color when using a specific task link' => 'ZmieÅ„ kolor zadania używajÄ…c specjalnego adresu URL', + 'Task link creation or modification' => 'Adres URL do utworzenia zadania lub modyfikacji', + 'Milestone' => 'KamieÅ„ milowy', + 'Reset the search/filter box' => 'Zresetuj pole wyszukiwania/filtrowania', + 'Documentation' => 'Dokumentacja', + 'Author' => 'Autor', + 'Version' => 'Wersja', + 'Plugins' => 'Wtyczki', + 'There is no plugin loaded.' => 'Nie wykryto żadnych wtyczek.', + 'My notifications' => 'Powiadomienia', + 'Custom filters' => 'Dostosuj filtry', + 'Your custom filter has been created successfully.' => 'Niestandardowy filtr zostaÅ‚ utworzony.', + 'Unable to create your custom filter.' => 'Nie można utworzyć niestandardowego filtra.', + 'Custom filter removed successfully.' => 'Niestandardowy filtr zostaÅ‚ usuniÄ™ty.', + 'Unable to remove this custom filter.' => 'Nie można usunąć niestandardowego filtra.', + 'Edit custom filter' => 'Edytuj niestandardowy filtr', + 'Your custom filter has been updated successfully.' => 'Niestandardowy filtr zostaÅ‚ zaktualizowany', + 'Unable to update custom filter.' => 'Nie można zaktualizować niestandardowego filtra.', + 'Web' => 'Sieć', + 'New attachment on task #%d: %s' => 'Nowy załącznik do zadania #%d: %s', + 'New comment on task #%d' => 'Nowy komentarz do zadania #%d', + 'Comment updated on task #%d' => 'Aktualizacja komentarza do zadania #%d', + 'New subtask on task #%d' => 'Nowe pod-zadanie dla zadania #%d', + 'Subtask updated on task #%d' => 'Aktualizacja pod-zadania w zadaniu #%d', + 'New task #%d: %s' => 'Nowe zadanie #%d: %s', + 'Task updated #%d' => 'Aktualizacja zadania #%d', + 'Task #%d closed' => 'ZamkniÄ™to zadanie #%d', + 'Task #%d opened' => 'Otwarto zadanie #%d', + 'Column changed for task #%d' => 'Zmieniono kolumnÄ™ zadania #%d', + 'New position for task #%d' => 'Ustalono nowÄ… pozycjÄ™ zadania #%d', + 'Swimlane changed for task #%d' => 'Zmieniono tor dla zadania #%d', + 'Assignee changed on task #%d' => 'Zmieniono osobÄ™ odpowiedzialnÄ… dla zadania #%d', + '%d overdue tasks' => '%d zalegÅ‚ych zadaÅ„', + 'No notification.' => 'Brak nowych powiadomieÅ„.', + 'Mark all as read' => 'Oznacz wszystkie jako przeczytane', + 'Mark as read' => 'Oznacz jako przeczytane', + 'Total number of tasks in this column across all swimlanes' => 'CaÅ‚kowita liczba zadaÅ„ z tej kolumny z wszystkich torów', + 'Collapse swimlane' => 'ZwiÅ„ tor', + 'Expand swimlane' => 'RozwiÅ„ tor', + 'Add a new filter' => 'Dodaj nowy filtr', + 'Share with all project members' => 'UdostÄ™pnij wszystkim uczestnikom projektu', + 'Shared' => 'UdostÄ™pnione', + 'Owner' => 'WÅ‚aÅ›ciciel', + 'Unread notifications' => 'Nieprzeczytane powiadomienia', + 'Notification methods:' => 'Metody powiadomieÅ„:', + 'Unable to read your file' => 'Nie można odczytać pliku', + '%d task(s) have been imported successfully.' => '%d zadaÅ„ zostaÅ‚o zaimportowanych.', + 'Nothing has been imported!' => 'Nic nie zostaÅ‚o zaimportowane!', + 'Import users from CSV file' => 'Importuj użytkowników z pliku CSV', + '%d user(s) have been imported successfully.' => '%d użytkowników zostaÅ‚o zaimportowanych.', + 'Comma' => 'Przecinek', + 'Semi-colon' => 'Åšrednik', + 'Tab' => 'Tabulacja', + 'Vertical bar' => 'Kreska pionowa', + 'Double Quote' => 'Cudzysłów', + 'Single Quote' => 'Apostrof', + '%s attached a file to the task #%d' => '%s dołączyÅ‚(a) plik do zadania #%d', + 'There is no column or swimlane activated in your project!' => 'Å»aden tor badź kolumna nie zostaÅ‚a aktywowana!', + 'Append filter (instead of replacement)' => 'Dołączaj filtr do zastosowanego filtru(zamiast przełączać)', + 'Append/Replace' => 'Dołącz/ZastÄ…p', + 'Append' => 'Dołącz', + 'Replace' => 'ZastÄ…p', + 'Import' => 'Importuj', + 'Change sorting' => 'odwróć sortowanie', + 'Tasks Importation' => 'Import zadaÅ„', + 'Delimiter' => 'Separator pola', + 'Enclosure' => 'Separator tekstu', + 'CSV File' => 'Plik CSV', + 'Instructions' => 'Instrukcje', + 'Your file must use the predefined CSV format' => 'Twój plik musi być zgodny z predefiniowanym formatem CSV (pobierz szablon)', + 'Your file must be encoded in UTF-8' => 'Twój plik musi być kodowany w UTF-8', + 'The first row must be the header' => 'Pierwszy wiersz pliku musi definiować nagłówki', + 'Duplicates are not verified for you' => 'Duplikaty nie bÄ™dÄ… weryfikowane', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Data musi być w formacie ISO: YYYY-MM-DD', + 'Download CSV template' => 'Pobierz szablon pliku CSV', + 'No external integration registered.' => 'Å»adna zewnÄ™trzna integracja nie zostaÅ‚a zarejestrowana.', + 'Duplicates are not imported' => 'Duplikaty nie zostanÄ… zaimportowane', + 'Usernames must be lowercase and unique' => 'Nazwy użytkowników muszÄ… być unikalne i skÅ‚adać siÄ™ z maÅ‚ych liter', + 'Passwords will be encrypted if present' => 'HasÅ‚a zostanÄ… zaszyfrowane jeÅ›li wystÄ™pujÄ…', + '%s attached a new file to the task %s' => '%s załączyÅ‚ nowy plik do zadania %s', + 'Link type' => 'Rodzaj link\'u', + 'Assign automatically a category based on a link' => 'Przypisz kategoriÄ™ automatycznie na podstawie linku', + 'BAM - Konvertible Mark' => 'BAM - BoÅ›nia i Hercegowina Cabrio Marka', + 'Assignee Username' => 'Przypisz nazwÄ™ użytkownika', + 'Assignee Name' => 'Przypisz imiÄ™', + 'Groups' => 'Grupy', + 'Members of %s' => 'CzÅ‚onkowie %s', + 'New group' => 'Nowa grupa', + 'Group created successfully.' => 'Grupa zostaÅ‚a utworzona.', + 'Unable to create your group.' => 'Nie można utworzyć grupy.', + 'Edit group' => 'Edytuj grupÄ™', + 'Group updated successfully.' => 'Grupa zostaÅ‚a zaaktualizowana.', + 'Unable to update your group.' => 'Nie można zaaktualizować grupy.', + 'Add group member to "%s"' => 'Dodaj czÅ‚onka do grupy "%s"', + 'Group member added successfully.' => 'Użytkownik zostaÅ‚ dodany do grupy.', + 'Unable to add group member.' => 'Nie można dodać użytkownika do grupy.', + 'Remove user from group "%s"' => 'UsuÅ„ użytkownika z grupy "%s"', + 'User removed successfully from this group.' => 'Użytkownik zostaÅ‚ usuniÄ™ty z grupy.', + 'Unable to remove this user from the group.' => 'Nie można usunąć użytkownika z grupy.', + 'Remove group' => 'UsuÅ„ grupÄ™', + 'Group removed successfully.' => 'Grupa zostaÅ‚a usuniÄ™ta.', + 'Unable to remove this group.' => 'Nie można usunąć grupy.', + 'Project Permissions' => 'Prawa dostÄ™powe projektu', + 'Manager' => 'Menedżer', + 'Project Manager' => 'Menedżer projektu', + 'Project Member' => 'Uczestnik projektu', + 'Project Viewer' => 'Obserwator projektu', + 'Your account is locked for %d minutes' => 'Twoje konto zostaÅ‚o zablokowane na %d minut', + 'Invalid captcha' => 'Błędny kod z obrazka (captcha)', + 'The name must be unique' => 'Nazwa musi być unikatowa', + 'View all groups' => 'WyÅ›wietl wszystkie 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.', + 'Add group member' => 'Dodaj czÅ‚onka grupy', + 'Do you really want to remove this group: "%s"?' => 'Czy na pewno chcesz usunąć grupÄ™ "%s"?', + 'There is no user in this group.' => 'Wybrana grupa nie posiada czÅ‚onków.', + 'Permissions' => 'Prawa dostÄ™pu', + 'Allowed Users' => 'Użytkownicy, którzy majÄ… dostÄ™p', + 'No specific user has been allowed.' => 'Å»aden użytkownik nie ma przyznanego dostÄ™pu.', + 'Role' => 'Rola', + 'Enter user name...' => 'Wprowadź nazwÄ™ użytkownika...', + 'Allowed Groups' => 'Grupy, które majÄ… dostÄ™p', + 'No group has been allowed.' => 'Å»adna grupa nie ma przyznanego dostÄ™pu.', + 'Group' => 'Grupa', + 'Group Name' => 'Nazwa grupy', + 'Enter group name...' => 'Wprowadź nazwÄ™ grupy...', + 'Role:' => 'Rola:', + 'Project members' => 'Uczestnicy projektu', + '%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', + 'Estimated hours: ' => 'Szacowane godziny: ', + 'Actual hours: ' => 'Rzeczywiste godziny: ', + 'Hours Spent' => 'SpÄ™dzone godziny', + 'Hours Estimated' => 'Szacowane godziny', + 'Estimated Time' => 'Szacowany czas', + 'Actual Time' => 'Rzeczywisty czas', + 'Estimated vs actual time' => 'Szacowany vs rzeczywisty czas', + 'RUB - Russian Ruble' => 'RUB - Rosyjskie Ruble', + 'Assign the task to the person who does the action when the column is changed' => 'Przypisz zadanie do osoby wykonujÄ…cej akcjÄ™ gdy kolumna zostanie zmodyfikowana', + 'Close a task in a specific column' => 'Zamknij zadanie w okreÅ›lonej kolumnie', + 'Time-based One-time Password Algorithm' => 'Algorytm hasÅ‚a jednorazowego bazujÄ…cego na czasie', + 'Two-Factor Provider: ' => 'Dostawca: ', + 'Disable two-factor authentication' => 'Wyłącz dwustopniowe uwierzytelnianie', + 'Enable two-factor authentication' => 'Włącz dwustopniowe uwierzytelnianie', + 'There is no integration registered at the moment.' => 'W chwili obecnej nie ma zarejestrowanej żadnej integracji.', + 'Password Reset for Kanboard' => 'Resetuj hasÅ‚o do Kanboarda', + 'Forgot password?' => 'Nie pamiÄ™tasz hasÅ‚a?', + 'Enable "Forget Password"' => 'Włącz "Nie pamiÄ™tasz hasÅ‚a?"', + 'Password Reset' => 'Resetuj hasÅ‚o', + 'New password' => 'Nowe hasÅ‚o', + 'Change Password' => 'ZmieÅ„ hasÅ‚o', + 'To reset your password click on this link:' => 'Kliknij w poniższy link, aby zresetować hasÅ‚o:', + 'Last Password Reset' => 'Ostatnie odzyskiwanie hasÅ‚a', + 'The password has never been reinitialized.' => 'HasÅ‚o nigdy nie byÅ‚o odzyskiwane.', + 'Creation' => 'Utworzenie', + 'Expiration' => 'WygaÅ›niÄ™cie', + 'Password reset history' => 'Historia resetowania hasÅ‚a', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Wszystkie zadania z kolumny "%s" i toru "%s" zostaÅ‚y zamkniÄ™te.', + 'Do you really want to close all tasks of this column?' => 'Na pewno chcesz zamknąć wszystkie zadania z tej kolumny?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d zadania z kolumny "%s" i toru "%s" zostanÄ… zamkniÄ™te.', + 'Close all tasks in this column and this swimlane' => 'Zamknij wszystkie zadania w tej kolumnie', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Wtyczki obsÅ‚ugujÄ…ce dodatkowe powiadomienia nie zostaÅ‚y zainstalowane. Dalej jednak możesz korzystać z standardowych powiadomieÅ„ (sprawdź w ustawieniach Twojego profilu).', + 'My dashboard' => 'Mój dashboard', + 'My profile' => 'Mój profil', + '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', + 'Personal 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', + 'Task priority' => 'Priorytety zadaÅ„', + 'General' => 'Ogólne', + 'Dates' => 'Czas życia projektu', + 'Default priority' => 'DomyÅ›lny priorytet', + 'Lowest priority' => 'Najniższy priorytet', + 'Highest priority' => 'Najwyższy priorytet', + 'Close a task when there is no activity' => 'Zamknij zadanie gdy nie jest aktywne', + 'Duration in days' => 'Czas trwania w dniach', + 'Send email when there is no activity on a task' => 'WyÅ›lij email gdy zadanie nie jest aktywne', + 'Unable to fetch link information.' => 'Nie można pobrać informacji o połączeniach.', + 'Daily background job for tasks' => 'Codzienne zadanie w tle', + 'Auto' => 'Automatyczny', + 'Related' => 'PowiÄ…zanie', + 'Attachment' => 'Załącznik', + 'Web Link' => 'Link URL', + 'External links' => 'Linki zewnÄ™trzne', + 'Add external link' => 'Dodaj link zewnÄ™trzny', + 'Type' => 'Typ', + 'Dependency' => 'Zależność', + 'Add internal link' => 'Dodaj link do innego zadania', + 'Add a new external link' => 'Dodaj nowy link zewnÄ™trzny', + 'Edit external link' => 'Edytuj link zewnÄ™trzny', + 'External link' => 'Link zewnÄ™trzny', + 'Copy and paste your link here...' => 'Skopiuj i wklej link tutaj ...', + 'URL' => 'URL', + 'Internal links' => 'Linki do innych zadaÅ„', + 'Assign to me' => 'Przypisz do mnie', + 'Me' => 'Ja', + 'Do not duplicate anything' => 'Nie kopiuj żadnego projektu', + 'Projects management' => 'ZarzÄ…dzanie projektami', + 'Users management' => 'ZarzÄ…dzanie użytkownikami', + 'Groups management' => 'ZarzÄ…dzanie grupami', + 'Create from another project' => 'Utwórz na podstawie innego projektu', + 'open' => 'otwarty', + 'closed' => 'zamkniÄ™ty', + 'Priority:' => 'Priorytet:', + 'Reference:' => 'OdnoÅ›nik:', + 'Complexity:' => 'ZÅ‚ożoność:', + 'Swimlane:' => 'Proces:', + 'Column:' => 'Kolumna:', + 'Position:' => 'Pozycja:', + 'Creator:' => 'UtworzyÅ‚:', + 'Time estimated:' => 'Szacowany czas:', + '%s hours' => '%s godzin', + 'Time spent:' => 'SpÄ™dzony czas:', + 'Created:' => 'Utworzone:', + 'Modified:' => 'Zmodyfikowane:', + 'Completed:' => 'UkoÅ„czone:', + 'Started:' => 'RozpoczÄ™te:', + 'Moved:' => 'Przeniesione:', + 'Task #%d' => 'Zadanie #%d', + 'Time format' => 'Format czasu', + 'Start date: ' => 'Data rozpoczÄ™cia: ', + 'End date: ' => 'Data zakoÅ„czenia: ', + 'New due date: ' => 'Nowy termin: ', + 'Start date changed: ' => 'Data rozpoczÄ™cia zostaÅ‚a zmieniona: ', + 'Disable personal projects' => 'Wyłącz prywatne projekty', + 'Do you really want to remove this custom filter: "%s"?' => 'Na pewno usunąć niestandardowy filtr: "%s"?', + 'Remove a custom filter' => 'UsuÅ„ niestandardowy filtr', + 'User activated successfully.' => 'Użytkownik zostaÅ‚ aktywowany.', + 'Unable to enable this user.' => 'Nie można włączyć użytkownika.', + '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.', + 'The maximum allowed file size is %sB.' => 'Maksymalny rozmiar pliku to %sB.', + 'Drag and drop your files here' => 'PrzeciÄ…gnij i upuść pliki tutaj', + 'choose files' => 'wybierz pliki', + 'View profile' => 'Zobacz profil', + 'Two Factor' => 'Dwustopniowe', + 'Disable user' => 'Wyłącz użytkownika', + 'Do you really want to disable this user: "%s"?' => 'Na pewno wyłączyć użytkownika: "%s"?', + 'Enable user' => 'Włącz użytkownika', + 'Do you really want to enable this user: "%s"?' => 'Na pewno włączyć użytkownika: "%s"?', + 'Download' => 'Pobierz', + 'Uploaded: %s' => 'Data załączenia: %s', + 'Size: %s' => 'Rozmiar: %s', + 'Uploaded by %s' => 'ZaÅ‚adowany przez %s', + 'Filename' => 'Nazwa pliku', + 'Size' => 'Rozmiar', + 'Column created successfully.' => 'Utworzono kolumnÄ™.', + 'Another column with the same name exists in the project' => 'Inna kolumna o tej samej nazwie już istnieje w projekcie', + 'Default filters' => 'DomyÅ›lne filtry', + 'Your board doesn\'t have any columns!' => 'Twoja tablica nie ma żadnej kolumny!', + 'Change column position' => 'ZmieÅ„ pozycjÄ™ kolumny', + 'Switch to the project overview' => 'Przełącz do podsumowania projektu', + 'User filters' => 'Filtry użytkownika', + 'Category filters' => 'Filtry kategorii', + 'Upload a file' => 'PrzeÅ›lij plik', + 'View file' => 'WyÅ›wietl plik', + 'Last activity' => 'Ostatnia aktywność', + 'Change subtask position' => 'ZmieÅ„ pozycjÄ™ pod-zadania', + 'This value must be greater than %d' => 'Wartość musi być wiÄ™ksza niż %d', + 'Another swimlane with the same name exists in the project' => 'Inny tor o tej samej nazwie już istnieje w projekcie', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'PrzykÅ‚ad: https://example.kanboard.org/ (użyty do wygenerowania bezwzglÄ™dnych adresów URL)', + 'Actions duplicated successfully.' => 'PomyÅ›lnie zduplikowano akcje.', + 'Unable to duplicate actions.' => 'Nie można zduplikować akcji.', + 'Add a new action' => 'Dodaj nowÄ… akcjÄ™', + 'Import from another project' => 'Importuj z innego projektu', + 'There is no action at the moment.' => 'W chwili obecnej nie dodano żadnych akcji.', + 'Import actions from another project' => 'Importuj akcje z innego projektu', + 'There is no available project.' => 'Brak dostÄ™pnego projektu.', + 'Local File' => 'Plik lokalny', + 'Configuration' => 'Konfiguracja', + 'PHP version:' => 'Wersja PHP:', + 'PHP SAPI:' => 'API serwera PHP:', + 'OS version:' => 'Wersja OS:', + 'Database version:' => 'Wersja bazy danych:', + 'Browser:' => 'PrzeglÄ…darka', + 'Task view' => 'Widok zadaÅ„', + 'Edit task' => 'Edytuj zadanie', + 'Edit description' => 'Edytuj opis', + 'New internal link' => 'Nowy wewnÄ™trzny link', + 'Display list of keyboard shortcuts' => 'WyÅ›wietl skróty klawiszowe', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'PrzeÅ›lij avatar', + 'Remove my image' => 'UsuÅ„', + 'The OAuth2 state parameter is invalid' => 'Parametr stanu OAuth2 jest niepoprawny', + 'User not found.' => 'Nie znaleziono użytkownika', + 'Search in activity stream' => 'Szukaj w strumieniu aktywnoÅ›ci', + 'My activities' => 'Moje aktywnoÅ›ci', + 'Activity until yesterday' => 'AktywnoÅ›ci do wczoraj', + 'Activity until today' => 'AktywnoÅ›ci do dzisiaj', + 'Search by creator: ' => 'Wyszukaj wedÅ‚ug twórcy: ', + 'Search by creation date: ' => 'Wyszukaj wedÅ‚ug daty utworzenia: ', + 'Search by task status: ' => 'Wyszukaj wedÅ‚ug statusu zadania: ', + 'Search by task title: ' => 'Wyszukaj po tytule zadania: ', + 'Activity stream search' => 'Wyszukaj w strumieniu aktywnoÅ›ci', + 'Projects where "%s" is manager' => 'Projekty gdzie "%s" jest menedżerem', + 'Projects where "%s" is member' => 'Projekty gdzie "%s" jest uczestnikiem', + 'Open tasks assigned to "%s"' => 'Otwarte zadania przypisane do "%s"', + 'Closed tasks assigned to "%s"' => 'ZamkniÄ™te zadania przypisane do "%s"', + 'Assign automatically a color based on a priority' => 'Przypisz kolor automatycznie, w zależnoÅ›ci od priorytetu', + 'Overdue tasks for the project(s) "%s"' => 'ZalegÅ‚e zadania dla projektu/projektów "%s"', + 'Upload files' => 'Wgraj pliki', + 'Installed Plugins' => 'Zainstalowane wtyczki', + 'Plugin Directory' => 'Folder z wtyczkami', + 'Plugin installed successfully.' => 'Wtyczka zainstalowana poprawnie.', + 'Plugin updated successfully.' => 'Wtyczka wgrana poprawnie.', + 'Plugin removed successfully.' => 'Wtyczka usuniÄ™ta poprawnie.', + 'Subtask converted to task successfully.' => 'Zadanie podrzÄ™dne skonwertowane poprawnie do zadania.', + 'Unable to convert the subtask.' => 'Skonwertowanie do zadania podrzÄ™dnego niemożliwe.', + 'Unable to extract plugin archive.' => 'Rozpakowanie archiwum wtyczki niemożliwe.', + 'Plugin not found.' => 'Wtyczka nie znaleziona.', + 'You don\'t have the permission to remove this plugin.' => 'Nie masz uprawnieÅ„ do usuniÄ™cia wtyczki.', + 'Unable to download plugin archive.' => 'Pobranie wtyczki niemożliwe.', + 'Unable to write temporary file for plugin.' => 'Zapisanie plików tymczasowych dla wtyczki niemożliwe.', + 'Unable to open plugin archive.' => 'Rozpakowanie archiwum wtyczki niemożliwe.', + 'There is no file in the plugin archive.' => 'Brak plików w archiwum wtyczki.', + 'Create tasks in bulk' => 'Utwórz zadania zbiorczo', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Ta instancja Kanboarda nie jest sknfigurowana do instalowania wtyczek z poziomu interfejsu użytkownika.', + 'There is no plugin available.' => 'Brak dostÄ™pnej wtyczki.', + 'Install' => 'Instaluj', + 'Update' => 'Aktualizuj', + 'Up to date' => 'Aktualny', + 'Not available' => 'NiedostÄ™pny', + 'Remove plugin' => 'UsuÅ„ wtyczkÄ™', + 'Do you really want to remove this plugin: "%s"?' => 'Na pewno chcesz usunąć tÄ™ wtyczkÄ™: "%s"?', + 'Uninstall' => 'UsuÅ„', + 'Listing' => 'Lista', + 'Metadata' => 'Metadane', + 'Manage projects' => 'ZarzÄ…dzaj projektami', + 'Convert to task' => 'Konwertuj do zadania', + 'Convert sub-task to task' => 'Konwertuj zadanie podrzÄ™dne do zadania', + 'Do you really want to convert this sub-task to a task?' => 'Na pewno chcesz skonwertować zadanie podrzÄ™dne do zadania?', + 'My task title' => 'TytuÅ‚ mojego zadania', + 'Enter one task by line.' => 'Wprowadź każde zadanie w osobnej linii.', + 'Number of failed login:' => 'Ilość nieudanych logowaÅ„:', + 'Account locked until:' => 'Konto zablokowane do:', + 'Email settings' => 'Ustawienia e-maila', + 'Email sender address' => 'Adres e-mail nadawcy', + 'Email transport' => 'Protokół transportowy e-mail', + 'Webhook token' => 'Token', + 'Project tags management' => 'ZarzÄ…dzanie tagami w projekcie', + 'Tag created successfully.' => 'Tag utworzony poprawnie.', + 'Unable to create this tag.' => 'Utworzenie taga niemożliwe.', + 'Tag updated successfully.' => 'Tag zaktualizowany poprawnie', + 'Unable to update this tag.' => 'Aktualizacja taga niemożliwa.', + 'Tag removed successfully.' => 'Tag usuniÄ™ty poprawnie.', + 'Unable to remove this tag.' => 'UsuniÄ™cie taga niemożliwe.', + 'Global tags management' => 'Globalne zarzÄ…dzanie tagami', + 'Tags' => 'Tagi', + 'Tags management' => 'ZarzÄ…dzanie tagami', + 'Add new tag' => 'Utwórz taga', + 'Edit a tag' => 'Edytuj taga', + 'Project tags' => 'Tagi w projekcie', + 'There is no specific tag for this project at the moment.' => 'Brak wskazanego taga w projekcie.', + 'Tag' => 'Tag', + 'Remove a tag' => 'UsuÅ„ taga', + 'Do you really want to remove this tag: "%s"?' => 'Na pewno chcesz usunąć taga: "%s"?', + 'Global tags' => 'Globalne tagi', + 'There is no global tag at the moment.' => 'Brak globalnych tagów.', + 'This field cannot be empty' => 'To pole nie może być puste', + 'Close a task when there is no activity in a specific column' => 'Zamknij zadanie, jeżeli nie ma aktywnoÅ›ci we wskazanej kolumnie', + '%s removed a subtask for the task #%d' => '%s usunÄ…Å‚ zadanie podrzÄ™dne do zadania nr %d', + '%s removed a comment on the task #%d' => '%s usunÄ…Å‚ komentarz do zadania nr %d', + 'Comment removed on task #%d' => 'Komentarz do zadania nr %d usuniÄ™ty', + 'Subtask removed on task #%d' => 'Zadanie podrzÄ™dne do zadania nr %d usuniÄ™te', + 'Hide tasks in this column in the dashboard' => 'Ukryj zadania dla wskazanej kolumny na tablicy', + '%s removed a comment on the task %s' => '%s usunÄ…Å‚ komentarz do zadania %s', + '%s removed a subtask for the task %s' => '%s usunÄ…Å‚ zadanie podrzÄ™dne do zadania %s', + 'Comment removed' => 'Komentarz usuniÄ™ty', + 'Subtask removed' => 'Zadanie podrzÄ™dne usuniÄ™te', + '%s set a new internal link for the task #%d' => '%s dodaÅ‚ odnoÅ›nik do zadania nr %d', + '%s removed an internal link for the task #%d' => '%s usunÄ…Å‚ odnoÅ›nik do zadania nr %d', + 'A new internal link for the task #%d has been defined' => 'Nowy odnoÅ›nik do zadania nr %d zostaÅ‚ zdefiniowany', + 'Internal link removed for the task #%d' => 'OdnoÅ›nik do zadania nr %d zostaÅ‚ usuniÄ™ty', + '%s set a new internal link for the task %s' => '%s dodaÅ‚ odnoÅ›nik do zadania %s', + '%s removed an internal link for the task %s' => '%s usunÄ…Å‚ odnoÅ›nik do zadania %s', + 'Automatically set the due date on task creation' => 'Automatycznie ustaw datÄ™ zakoÅ„czenia przy tworzeniu zadania', + 'Move the task to another column when closed' => 'Przy zamkniÄ™ciu zadania przenieÅ› je do innej kolumny', + 'Move the task to another column when not moved during a given period' => 'Jeżeli zadanie nie byÅ‚o przenoszone przez okreÅ›lony interwaÅ‚, to przenieÅ› je do innej kolumny', + 'Dashboard for %s' => 'Tablica dla %s', + 'Tasks overview for %s' => 'PodglÄ…d zadaÅ„ dla %s', + 'Subtasks overview for %s' => 'PodglÄ…d zadaÅ„ podrzÄ™dnych dla %s', + 'Projects overview for %s' => 'PodglÄ…d projektów dla %s', + 'Activity stream for %s' => 'StrumieÅ„ aktywnoÅ›ci dla %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Ustaw kolor kiedy zadanie jest przenoszone do swimlane', + 'Assign a priority when the task is moved to a specific swimlane' => 'Ustaw priorytet kiedy zadanie jest przenoszone do swimlane', + 'User unlocked successfully.' => 'Użytkownik odblokowany poprawnie.', + 'Unable to unlock the user.' => 'Odblokowanie użytkownika niemożliwe.', + 'Move a task to another swimlane' => 'PrzenieÅ› zadanie do innego swimlane', + 'Creator Name' => 'Autor', + 'Time spent and estimated' => 'Czas spÄ™dzony i planowany', + 'Move position' => 'PrzenieÅ›', + 'Move task to another position on the board' => 'PrzenieÅ› zadanie w inne miejsce', + 'Insert before this task' => 'Wstaw przed tym zadaniem', + 'Insert after this task' => 'Wstaw za tym zadaniem', + 'Unlock this user' => 'Odblokuj użytkownika', + 'Custom Project Roles' => 'Role w projekcie', + 'Add a new custom role' => 'Dodaj rolÄ™', + 'Restrictions for the role "%s"' => 'Ograniczenia dla roli "%s"', + 'Add a new project restriction' => 'Dodaj ograniczenie w projekcie', + 'Add a new drag and drop restriction' => 'Dodaj ograniczenie drag & drop', + 'Add a new column restriction' => 'Dodaj ograniczenie kolumny', + 'Edit this role' => 'Edytuj rolÄ™', + 'Remove this role' => 'UsuÅ„ rolÄ™', + 'There is no restriction for this role.' => 'Brak ograniczeÅ„ dla roli', + 'Only moving task between those columns is permitted' => 'Jedynie przenoszenie zadaÅ„ pomiÄ™dzy kolumnami jest dozwolone', + 'Close a task in a specific column when not moved during a given period' => 'Zamknij zadanie w kolumnie, jeżeli nie byÅ‚o przeniesione przez okreÅ›lony czas', + 'Edit columns' => 'Edytuj kolumny', + 'The column restriction has been created successfully.' => 'Ograniczenie dla kolumny zostaÅ‚o utworzone poprawnie.', + 'Unable to create this column restriction.' => 'Utworzenie ograniczenia dla kolumny niemożliwe.', + 'Column restriction removed successfully.' => 'Ograniczenie dla kolumny zostaÅ‚o usuniÄ™te poprawnie.', + 'Unable to remove this restriction.' => 'UsuniÄ™cie ograniczenia niemożliwe.', + 'Your custom project role has been created successfully.' => 'Rola w projekcie zostaÅ‚a utworzona.', + 'Unable to create custom project role.' => 'Utworzenie roli w projekcie niemożliwe.', + 'Your custom project role has been updated successfully.' => 'Rola w projekcie zostaÅ‚a zaktualizowana.', + 'Unable to update custom project role.' => 'Aktualizacja roli w projekcie niemożliwa.', + 'Custom project role removed successfully.' => 'Rola w projekcie zostaÅ‚a usuniÄ™ta.', + 'Unable to remove this project role.' => 'UsuniÄ™cie roli w projekcie niemożliwe.', + 'The project restriction has been created successfully.' => 'Ograniczenie w projekcie zostaÅ‚o utworzone.', + 'Unable to create this project restriction.' => 'UsuniÄ™cie ograniczenia w projekcie niemożliwe.', + 'Project restriction removed successfully.' => 'Organiczenie w projekcie zostaÅ‚o usuniÄ™te.', + 'You cannot create tasks in this column.' => 'Nie możesz utworzyć zadania w tej kolumnie.', + 'Task creation is permitted for this column' => 'Dodawanie zadania w tej kolumnie jest niedozwolone', + 'Closing or opening a task is permitted for this column' => 'Otwieranie lub zamykanie zadania w tej kolumnie jest dozwolone', + 'Task creation is blocked for this column' => 'Tworzenie zadania w tej kolumnie jest zablokowane', + 'Closing or opening a task is blocked for this column' => 'Otwieranie lub zamykanie zadania w tej kolumnie jest zablokowane', + 'Task creation is not permitted' => 'Tworzenie zadania jest niedozwolone', + 'Closing or opening a task is not permitted' => 'Otwierania lub zamykanie zadania jest niedozwolone', + 'New drag and drop restriction for the role "%s"' => 'Now ograniczenie drag & drop dla roli "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Osoby przypisane do tej roli bÄ™dÄ… mogÅ‚y przemieszczać zadania jedynie pomiÄ™dzy kolumnÄ… źródÅ‚owÄ… i docelowÄ….', + 'Remove a column restriction' => 'UsuÅ„ ograniczenie dla kolumny', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Na pewno chcesz usunąć organiczenie "%s", dla kolumny "%s"?', + 'New column restriction for the role "%s"' => 'Nowe organiczenie dla kolumny, dla roli "%s"', + 'Rule' => 'ReguÅ‚a', + 'Do you really want to remove this column restriction?' => 'Na pewno chcesz usunąć organiczenie dla kolumny?', + 'Custom roles' => 'WÅ‚asne role', + 'New custom project role' => 'Nowa rola w projekcie', + 'Edit custom project role' => 'Edytuj rolÄ™ w projekcie', + 'Remove a custom role' => 'UsuÅ„ rolÄ™', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Na pewno chcesz usunąć rolÄ™: "%s"? Wszyscy użytkownicy przypisani do tej roli zostanÄ… czÅ‚onkami projektu.', + 'There is no custom role for this project.' => 'Brak wskazanej roli w projekcie.', + 'New project restriction for the role "%s"' => 'Nowe ograniczenie w projekcie dla roli "%s"', + 'Restriction' => 'Organiczenie', + 'Remove a project restriction' => 'UsuÅ„ organiczenie projektu', + 'Do you really want to remove this project restriction: "%s"?' => 'Na pewno chcesz usunąć organiczenie projektu: "%s"?', + 'Duplicate to multiple projects' => 'Zduplikuj do innych projektów', + 'This field is required' => 'To pole jest wymagane', + 'Moving a task is not permitted' => 'Przenoszenie zadaÅ„ jest niedozwolone', + 'This value must be in the range %d to %d' => 'Ta wartosć musi być w zakresie od %d do %d', + 'You are not allowed to move this task.' => 'Nie masz uprawnieÅ„ do przeniesienia zadania.', + 'API User Access' => 'DostÄ™p użytkownika po API', + 'Preview' => 'PodglÄ…d', + 'Write' => 'Zapisz', + 'Write your text in Markdown' => 'Zapisz tekst jako znaczniki Markdown', + 'No personal API access token registered.' => 'Brak zarejestrowanego tokenu osobistego API.', + 'Your personal API access token is "%s"' => 'Osobisty token API to "%s"', + 'Remove your token' => 'UsuÅ„ token', + 'Generate a new token' => 'Wygeneruj token', + 'Showing %d-%d of %d' => 'PokazujÄ™ %d-%d z %d', + 'Outgoing Emails' => 'WychodzÄ…ce e-maile', + 'Add or change currency rate' => 'Dodaj kursy wymiany walut', + 'Reference currency: %s' => 'Waluta referencyjna: %s', + 'Add custom filters' => 'Dodaj filtr', + 'Export' => 'Eksport', + 'Add link label' => 'Dodaj etykietÄ™ linku', + 'Incompatible Plugins' => 'Niekompatybilne wtyczki', + 'Compatibility' => 'Kompatybilność', + 'Permissions and ownership' => 'Zezwolenia i wÅ‚asnoÅ›ci', + 'Priorities' => 'Priorytety', + 'Close this window' => 'Zamknij okno', + 'Unable to upload this file.' => 'Wgranie pliku niemożliwe.', + 'Import tasks' => 'Importuj zadania', + 'Choose a project' => 'Wybierz projekt', + 'Profile' => 'Profil', + 'Application role' => 'Rola w aplikacji', + '%d invitations were sent.' => '%d zaproszeÅ„ zostaÅ‚o wysÅ‚anych.', + '%d invitation was sent.' => '%d zaproszenie zostaÅ‚o wysÅ‚ane.', + 'Unable to create this user.' => 'Utworzenie użytkownika niemożliwe.', + 'Kanboard Invitation' => 'Zaproszenie do Kanboarda', + 'Visible on dashboard' => 'Widoczny na tablicy', + 'Created at:' => 'Utworzono:', + 'Updated at:' => 'Zaktualizowano:', + 'There is no custom filter.' => 'Brak filtra.', + 'New User' => 'Nowy użytkownik', + 'Authentication' => 'AUtentykacja', + 'If checked, this user will use a third-party system for authentication.' => 'Użytkownik bÄ™dzie używaÅ‚ zewnÄ™trznego systemu dla autentykacji.', + 'The password is necessary only for local users.' => 'HasÅ‚o jest wymagane jedynie dla lokalnych użytkowników.', + 'You have been invited to register on Kanboard.' => 'ZostaÅ‚eÅ› zaproszony do rejestracji w Kanboardzie.', + 'Click here to join your team' => 'Kliknij, aby dołączyć do zespoÅ‚u', + 'Invite people' => 'ZaproÅ› osoby', + 'Emails' => 'E-maile', + 'Enter one email address by line.' => 'Wprowadź jeden adres e-mail w każdej linii.', + 'Add these people to this project' => 'Dodaj ludzi do projektu', + 'Add this person to this project' => 'Dodaj osobÄ™ do projektu', + 'Sign-up' => 'Zaloguj', + 'Credentials' => 'Dane logowania', + 'New user' => 'Nowy użytkownik', + 'This username is already taken' => 'Ta nazwa użytkownika jest już zajÄ™ta', + 'Your profile must have a valid email address.' => 'Profil musi mieć przypisany prawidÅ‚owy adres e-mail.', + 'TRL - Turkish Lira' => 'TRL - Lira turecka', + 'The project email is optional and could be used by several plugins.' => 'Adres e-mail projektu jest opcjonalny, ale może być używany przez niektóre wtyczki.', + 'The project email must be unique across all projects' => 'Adres e-mail projektu musi być unikalny poÅ›ród wszystkich projektów', + 'The email configuration has been disabled by the administrator.' => 'Konfiguracja e-maila zostaÅ‚a zablokowana przez administratora.', + 'Close this project' => 'Zamknij projekt', + 'Open this project' => 'Otwórz projekt', + 'Close a project' => 'Zamknij projekt', + 'Do you really want to close this project: "%s"?' => 'Na pewno chcesz zamknąć projekt: "%s"?', + 'Reopen a project' => 'Otwórz ponownie projekt', + 'Do you really want to reopen this project: "%s"?' => 'Na pewno chcesz otworzyć ponownie projekt: "%s"?', + 'This project is open' => 'Ten projekt jest otwarty', + 'This project is closed' => 'Ten projekt jest zamkniÄ™ty', + 'Unable to upload files, check the permissions of your data folder.' => 'Wgranie plików niemożliwe, sprawdź uprawnienia katalogu z danymi.', + 'Another category with the same name exists in this project' => 'Istnieje już taka kategoria w tym projekcie', + 'Comment sent by email successfully.' => 'Komentarz zostaÅ‚ wysÅ‚any poprzez e-maila.', + 'Sent by email to "%s" (%s)' => 'WysÅ‚ano poprzez e-maila do "%s" (%s)', + 'Unable to read uploaded file.' => 'Odczytanie wgranego pliku niemożliwe.', + 'Database uploaded successfully.' => 'Baza danych wgrana poprawnie.', + 'Task sent by email successfully.' => 'Zadanie zostaÅ‚o wysÅ‚ane poprzez e-maila.', + 'There is no category in this project.' => 'Brak kategorii w projekcie.', + 'Send by email' => 'WysÅ‚ano poprzez e-maila', + 'Create and send a comment by email' => 'Utwórz komentarz i wyÅ›lij poprze e-maila', + 'Subject' => 'Temat', + 'Upload the database' => 'Wgraj bazÄ™ danych', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Możesz wgrać poprzednio wyeksportowanÄ… bazÄ™ danych sqlite (w formacie gzip).', + 'Database file' => 'Plik bazy danych', + 'Upload' => 'Wgraj', + 'Your project must have at least one active swimlane.' => 'Projekt musi mieć chociaż jednego aktywnego swimlane', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatyczna akcja nie znaleziona: "%s"', + '%d projects' => '%d projektów', + '%d project' => '%d projekt', + 'There is no project.' => 'Brak projektu.', + 'Sort' => 'Sortuj', + 'Project ID' => 'ID projektu', + 'Project name' => 'Nazwa projektu', + 'Public' => 'Publiczny', + 'Personal' => 'Prywatny', + '%d tasks' => '%d zadaÅ„', + '%d task' => '%d zadanie', + 'Task ID' => 'ID zadania', + 'Assign automatically a color when due date is expired' => 'Automatycznie przydziel kolor, jeżeli data zakoÅ„czenia zostaÅ‚a przekroczona', + 'Total score in this column across all swimlanes' => 'Sumaryczna ilość punktów w kolumnie, poprzez wszystkie swimlane', + 'HRK - Kuna' => 'HRK - Kuna chorwacka', + 'ARS - Argentine Peso' => 'ARS - Peso argentyÅ„skie', + 'COP - Colombian Peso' => 'COP - Peso kolumbijskie', + '%d groups' => '%d grup', + '%d group' => '%d grupa', + 'Group ID' => 'ID grupy', + 'External ID' => 'ZewnÄ™trzne ID', + '%d users' => '%d użytkowników', + '%d user' => '%d użytkownik', + 'Hide subtasks' => 'Ukryj zadania podrzÄ™dne', + 'Show subtasks' => 'Pokaż zadania podrzÄ™dne', + 'Authentication Parameters' => 'Parametry autentykacji', + 'API Access' => 'DostÄ™p po API', + 'No users found.' => 'Nie znaleziono użytkowników.', + 'User ID' => 'ID użytkownika', + 'Notifications are activated' => 'Powiadomienia sÄ… aktywne', + 'Notifications are disabled' => 'Powiadomienia sÄ… nieaktywne', + 'User disabled' => 'Użytkownik zablokowany', + '%d notifications' => '%d powiadomieÅ„', + '%d notification' => '%d powiadomienie', + 'There is no external integration installed.' => 'Brak zainstalowanych zewnÄ™trznych integracji', + 'You are not allowed to update tasks assigned to someone else.' => 'Nie masz uprawnieÅ„ do uaktualnienia zadania przypisanego do innego użytkownika.', + 'You are not allowed to change the assignee.' => 'Nie masz uprawnieÅ„ do zmiany użytkownika przypisanego do zadania.', + 'Task suppression is not permitted' => 'Zablokowanie zadania jest niedozwolone', + 'Changing assignee is not permitted' => 'Zmiana osoby przypisanej do zadania jest niedozwolona', + 'Update only assigned tasks is permitted' => 'Dozwolone jest jedynie uaktualnienie już przypisanego zadania', + 'Only for tasks assigned to the current user' => 'Tylko dla zadaÅ„ przypisanych dla aktualnego użytkownika', + 'My projects' => 'Moje projekty', + 'You are not a member of any project.' => 'Nie jesteÅ› czÅ‚onkiem żadnego projektu.', + 'My subtasks' => 'Moje zadania podrzÄ™dne', + '%d subtasks' => '%d zadaÅ„ podrzÄ™dnych', + '%d subtask' => '%d zadanie podrzÄ™dne', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Przesuwanie zadaÅ„ pomiÄ™dzy kolumnami jest dozwolone jedynie dla użytkownika przypisanego do tych zadaÅ„', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Korona duÅ„ska', + 'Remove user from group' => 'UsuÅ„ użytkownika z grupy', + 'Assign the task to its creator' => 'Przypisz zadanie do autora', + 'This task was sent by email to "%s" with subject "%s".' => 'Zadanie zostaÅ‚o wysÅ‚ane poprzez e-maila do "%s" z tematem "%s".', + 'Predefined Email Subjects' => 'Predefiniowane tematy e-maila', + 'Write one subject by line.' => 'Wpisz jeden temat na liniÄ™.', + 'Create another link' => 'Utwórz kolejnego linka', + 'BRL - Brazilian Real' => 'BRL - Real brazylijski', + 'Add a new Kanboard task' => 'Dodaj nowe zadanie Kanboard', + 'Subtask not started' => 'Zadanie podrzÄ™dne nie rozpoczÄ™te', + 'Subtask currently in progress' => 'Zadanie podrzÄ™dne w trakcie', + 'Subtask completed' => 'Zadanie podrzÄ™dne zakoÅ„czone', + 'Subtask added successfully.' => 'Zadanie podrzÄ™dne dodane', + '%d subtasks added successfully.' => '%d zadaÅ„ podrzÄ™dnych dodanych prawidÅ‚owo', + 'Enter one subtask by line.' => 'Dodaj jedno zadanie podrzÄ™dne na liniÄ™.', + 'Predefined Contents' => 'Predefiniowana zawartość', + 'Predefined contents' => 'Predefiniowana zawartość', + 'Predefined Task Description' => 'Predefiniowany opis zadania', + 'Do you really want to remove this template? "%s"' => 'Na pewno chcesz usunąć szablon? "%s"', + 'Add predefined task description' => 'Dodaj predefiniowany opis zadania', + 'Predefined Task Descriptions' => 'Predefiniowany opis zadania', + 'Template created successfully.' => 'Szablon utworzony poprawnie.', + 'Unable to create this template.' => 'Utworzenie szablonu niemożliwe.', + 'Template updated successfully.' => 'Szablon uaktualniony poprawnie.', + 'Unable to update this template.' => 'Uaktualnienie szablonu niemożliwe.', + 'Template removed successfully.' => 'Szablon usuniÄ™ty poprawnie.', + 'Unable to remove this template.' => 'UsuniÄ™cie szablonu niemożliwe.', + 'Template for the task description' => 'Szablon dla opisu zadania', + 'The start date is greater than the end date' => 'Data startu jest późniejsza niż data zakoÅ„czenia', + 'Tags must be separated by a comma' => 'Tagi muszÄ… być oddzielone przecinkiem', + 'Only the task title is required' => 'Wymagana jest jedynie nazwa zadania', + 'Creator Username' => 'Nazwa autora', + 'Color Name' => 'Kolor', + 'Column Name' => 'Kolumna', + 'Swimlane Name' => 'Swimlane', + 'Time Estimated' => 'Szacowany czas', + 'Time Spent' => 'SpÄ™dzony czas', + 'External Link' => 'ZewnÄ™trzny odnoÅ›nik', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Ta opcja aktywuje kanaÅ‚ iCal, kanaÅ‚ RSS oraz publiczny widok tablicy.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Zatrzymaj odliczanie czasu we wszystkich zadaniach podrzÄ™dnych, kiedy przenosisz zadanie do innej kolumny', + 'Subtask Title' => 'TytuÅ‚ zadania podrzÄ™dnego', + 'Add a subtask and activate the timer when moving a task to another column' => 'Dodaj zadanie podrzÄ™dne i aktywuj odliczanie czasu, kiedy przenosisz zadanie do innej kolumny', + 'days' => 'dni', + 'minutes' => 'minuty', + 'seconds' => 'sekundy', + 'Assign automatically a color when preset start date is reached' => 'Ustaw kolor automatycznie, kiedy data rozpoczÄ™cia zadania zostaÅ‚a osiÄ…gniÄ™ta', + 'Move the task to another column once a predefined start date is reached' => 'PrzenieÅ› zadanie do innej kolumny, kiedy data rozpoczÄ™cia zadania zostaÅ‚a osiÄ…gniÄ™ta', + 'This task is now linked to the task %s with the relation "%s"' => 'Zadanie jest teraz podlinkowane do zadania %s w relacji "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'OdnoÅ›nik w relacji "%s" do zadania %s zostaÅ‚ usuniÄ™ty', + 'Custom Filter:' => 'Filtr:', + 'Unable to find this group.' => 'Nie można znaleźć grupy.', + '%s moved the task #%d to the column "%s"' => '%s przeniósÅ‚ zadanie nr %d do kolumny "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s przeniósÅ‚ zadanie nr %d w pozycÄ™ %d do kolumny "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s przeniósÅ‚ zadanie nr %d do swimlane "%s"', + '%sh spent' => '%sh upÅ‚ynęło', + '%sh estimated' => '%sh planowane', + 'Select All' => 'Zaznacz wszystko', + 'Unselect All' => 'Odznacz wszystko', + 'Apply action' => 'Zastosuj akcjÄ™', + 'Move selected tasks to another column or swimlane' => 'PrzenieÅ› wybrane zadania do innej kolumny', + 'Edit tasks in bulk' => 'Edytuj zadania grupowo', + 'Choose the properties that you would like to change for the selected tasks.' => 'Wybierz wÅ‚aÅ›ciwość wskazanego zadania, które chciaÅ‚byÅ› zmienić.', + 'Configure this project' => 'Skonfiguruj projekt', + 'Start now' => 'Wystartuj', + '%s removed a file from the task #%d' => '%s usunÄ…Å‚ plik z zadania nr %d', + 'Attachment removed from task #%d: %s' => 'Załącznik usuniÄ™ty z zadania nr %d: %s', + 'No color' => 'Brak koloru', + 'Attachment removed "%s"' => 'Załącznik usuniÄ™ty "%s"', + '%s removed a file from the task %s' => '%s usunÄ…Å‚ plik z zadania %s', + 'Move the task to another swimlane when assigned to a user' => 'PrzenieÅ› zadanie do innego swimlane, kiedy zostanie przydzielone do użytkownika', + 'Destination swimlane' => 'Docelowy swimlane', + 'Assign a category when the task is moved to a specific swimlane' => 'Przydziel kategoriÄ™, kiedy zadanie zostanie przeniesione do swimlane', + 'Move the task to another swimlane when the category is changed' => 'PrzenieÅ› zadanie do innego swimlane, kiedy kategoria zostanie zmieniona', + 'Reorder this column by priority (ASC)' => 'Sortuj kolumnÄ™ po priorytecie (rosnÄ…co)', + 'Reorder this column by priority (DESC)' => 'Sortuj kolumnÄ™ po priorytecie (malejÄ…co)', + 'Reorder this column by assignee and priority (ASC)' => 'Sortuj kolumnÄ™ po priorytecie i osobie przydzielonej do zadania (rosnÄ…co)', + 'Reorder this column by assignee and priority (DESC)' => 'Sortuj kolumnÄ™ po priorytecie i osobie przydzielonej do zadania (malejÄ…co)', + 'Reorder this column by assignee (A-Z)' => 'Sortuj kolumnÄ™ po osobie przydzielonej do zadania (rosnÄ…co)', + 'Reorder this column by assignee (Z-A)' => 'Sortuj kolumnÄ™ po osobie przydzielonej do zadania (malejÄ…co)', + 'Reorder this column by due date (ASC)' => 'Sortuj kolumnÄ™ po dacie zakoÅ„czenia (rosnÄ…co)', + 'Reorder this column by due date (DESC)' => 'Sortuj kolumnÄ™ po dacie zakoÅ„czenia (malejÄ…co)', + 'Reorder this column by id (ASC)' => 'Sortuj tÄ™ kolumnÄ™ wedÅ‚ug ID (rosnÄ…co)', + 'Reorder this column by id (DESC)' => 'Sortuj tÄ™ kolumnÄ™ wedÅ‚ug ID (malejÄ…co)', + '%s moved the task #%d "%s" to the project "%s"' => '%s przeniósÅ‚ zadanie nr %d "%s" do projektu "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Zadanie nr %d "%s" zostaÅ‚o przeniesione do projektu "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'PrzenieÅ› zadanie do innej kolumny, kiedy data zakoÅ„czenia jest oddalona o okreÅ›lonÄ… liczbÄ™ dni od aktualnej daty', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automatycznie uaktualnij datÄ™ startu, kiedy zadanie jest przeniesione z danej kolumny', + 'HTTP Client:' => 'Klient HTTP:', + 'Assigned' => 'Przydzielony', + 'Task limits apply to each swimlane individually' => 'Limity zadaÅ„ odnoszÄ… siÄ™ do każdego toru indywidualnie', + 'Column task limits apply to each swimlane individually' => 'Limity zadaÅ„ dla kolumn dotyczÄ… każdego toru indywidualnie', + 'Column task limits are applied to each swimlane individually' => 'Kolumnowe limity zadaÅ„ sÄ… stosowane do każdego toru indywidualnie', + 'Column task limits are applied across swimlanes' => 'Limity zadaÅ„ dla kolumn sÄ… stosowane na wszystkich torów', + 'Task limit: ' => 'Limit zadaÅ„: ', + 'Change to global tag' => 'ZmieÅ„ na tag globalny', + 'Do you really want to make the tag "%s" global?' => 'Czy naprawdÄ™ chcesz, aby tag "%s" byÅ‚ globalny?', + 'Enable global tags for this project' => 'Włącz globalne tagi dla tego projektu', + 'Group membership(s):' => 'CzÅ‚onkostwo w grupie (grupach):', + '%s is a member of the following group(s): %s' => '%s jest czÅ‚onkiem nastÄ™pujÄ…cej(ych) grupy(grup): %s', + '%d/%d group(s) shown' => '%d/%d wykazanych grup(y)', + 'Subtask creation or modification' => 'Tworzenie lub modyfikacja podzadania', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Przypisz zadanie do konkretnego użytkownika, gdy zadanie zostanie przeniesione na konkretny tor', + 'Comment' => 'Komentarz', + 'Collapse vertically' => 'ZwiÅ„ w pionie', + 'Expand vertically' => 'RozwiÅ„ w pionie', + 'MXN - Mexican Peso' => 'MXN - peso meksykaÅ„skie', + 'Estimated vs actual time per column' => 'Szacowany vs rzeczywisty czas na kolumnÄ™', + 'HUF - Hungarian Forint' => 'HUF - Forint wÄ™gierski', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Musisz wybrać plik, który chcesz przesÅ‚ać jako swój awatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'PrzesÅ‚any plik nie jest prawidÅ‚owym obrazem! (Tylko *.gif, *.jpg, *.jpeg i *.png sÄ… dozwolone!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Automatycznie ustaw datÄ™ wykonania, gdy zadanie zostanie przeniesione z okreÅ›lonej kolumny', + 'No other projects found.' => 'Nie znaleziono innych projektów.', + 'Tasks copied successfully.' => 'Zadania skopiowane pomyÅ›lnie.', + 'Unable to copy tasks.' => 'Nie udaÅ‚o siÄ™ skopiować zadaÅ„.', + 'Theme' => 'Motyw', + 'Theme:' => 'Motyw:', + 'Light theme' => 'Jasny motyw', + 'Dark theme' => 'Ciemny motyw', + 'Automatic theme - Sync with system' => 'Motyw automatyczny – synchronizuj z systemem', + 'Application managers or more' => 'Menedżerowie aplikacji lub wyżej', + 'Administrators' => 'Administratorzy', + 'Visibility:' => 'Widoczność:', + 'Standard users' => 'Użytkownicy standardowi', + 'Visibility is required' => 'Widoczność jest wymagana', + 'The visibility should be an app role' => 'Widoczność powinna odpowiadać roli w aplikacji', + 'Reply' => 'Odpowiedz', + '%s wrote: ' => '%s napisaÅ‚(a): ', + 'Number of visible tasks in this column and swimlane' => 'Liczba widocznych zadaÅ„ w tej kolumnie i torze', + 'Number of tasks in this swimlane' => 'Liczba zadaÅ„ w tym torze', + 'Unable to find another subtask in progress, you can close this window.' => 'Nie znaleziono innego podzadania w toku, możesz zamknąć to okno.', + 'This theme is invalid' => 'Ten motyw jest nieprawidÅ‚owy', + 'This role is invalid' => 'Ta rola jest nieprawidÅ‚owa', + 'This timezone is invalid' => 'Ta strefa czasowa jest nieprawidÅ‚owa', + 'This language is invalid' => 'Ten jÄ™zyk jest nieprawidÅ‚owy', + 'This URL is invalid' => 'Ten adres URL jest nieprawidÅ‚owy', + 'Date format invalid' => 'NieprawidÅ‚owy format daty', + 'Time format invalid' => 'NieprawidÅ‚owy format czasu', + 'Invalid Mail transport' => 'NieprawidÅ‚owy sposób przesyÅ‚ania poczty', + 'Color invalid' => 'NieprawidÅ‚owy kolor', + 'This value must be greater or equal to %d' => 'Ta wartość musi być wiÄ™ksza lub równa %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Dodaj BOM na poczÄ…tku pliku (wymagane dla Microsoft Excel)', + 'Just add these tag(s)' => 'Po prostu dodaj te tagi', + 'Remove internal link(s)' => 'UsuÅ„ wewnÄ™trzne odnoÅ›niki', + 'Import tasks from another project' => 'Importuj zadania z innego projektu', + 'Select the project to copy tasks from' => 'Wybierz projekt, z którego chcesz skopiować zadania', + 'The total maximum allowed attachments size is %sB.' => 'Maksymalny dozwolony rozmiar załączników to %sB.', + 'Add attachments' => 'Dodaj załączniki', + 'Task #%d "%s" is overdue' => 'Zadanie nr %d "%s" jest przeterminowane', + 'Enable notifications by default for all new users' => 'Włącz powiadomienia domyÅ›lnie dla wszystkich nowych użytkowników', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Przypisz zadanie do jego twórcy dla okreÅ›lonych kolumn, jeÅ›li nie ustawiono rÄ™cznie osoby odpowiedzialnej', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Przypisz zadanie do zalogowanego użytkownika przy zmianie kolumny na wskazanÄ…, jeÅ›li nie ma przypisanego użytkownika', +]; diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php new file mode 100644 index 0000000..4defcb7 --- /dev/null +++ b/app/Locale/pt_BR/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Nenhum', + 'Edit' => 'Editar', + 'Remove' => 'Remover', + 'Yes' => 'Sim', + 'No' => 'Não', + 'cancel' => 'cancelar', + 'or' => 'ou', + 'Yellow' => 'Amarelo', + 'Blue' => 'Azul', + 'Green' => 'Verde', + 'Purple' => 'Roxo', + 'Red' => 'Vermelho', + 'Orange' => 'Laranja', + 'Grey' => 'Cinza', + 'Brown' => 'Marrom', + 'Deep Orange' => 'Laranja escuro', + 'Dark Grey' => 'Cinza escuro', + 'Pink' => 'Rosa', + 'Teal' => 'Turquesa', + 'Cyan' => 'Azul intenso', + 'Lime' => 'Verde limão', + 'Light Green' => 'Verde claro', + 'Amber' => 'Âmbar', + 'Save' => 'Salvar', + 'Login' => 'Login', + 'Official website:' => 'Site oficial:', + 'Unassigned' => 'Não Atribuída', + 'View this task' => 'Ver esta tarefa', + 'Remove user' => 'Remover usuário', + 'Do you really want to remove this user: "%s"?' => 'Você realmente deseja remover este usuário: "%s"?', + 'All users' => 'Todos os usuários', + 'Username' => 'Nome de usuário', + 'Password' => 'Senha', + 'Administrator' => 'Administrador', + 'Sign in' => 'Entrar', + 'Users' => 'Usuários', + 'Forbidden' => 'Proibido', + 'Access Forbidden' => 'Acesso negado', + 'Edit user' => 'Editar usuário', + 'Logout' => 'Sair', + 'Bad username or password' => 'Usuário ou senha inválidos', + 'Edit project' => 'Editar projeto', + 'Name' => 'Nome', + 'Projects' => 'Projetos', + 'No project' => 'Nenhum projeto', + 'Project' => 'Projeto', + 'Status' => 'Status', + 'Tasks' => 'Tarefas', + 'Board' => 'Quadro', + 'Actions' => 'Ações', + 'Inactive' => 'Inativo', + 'Active' => 'Ativo', + 'Unable to update this board.' => 'Não foi possível atualizar este quadro.', + '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 quadro para "%s"', + 'Add a new column' => 'Adicionar uma nova coluna', + 'Title' => 'Título', + 'Assigned to %s' => 'Designado para %s', + 'Remove a column' => 'Remover uma coluna', + '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"?', + 'Settings' => 'Configurações', + 'Application settings' => 'Configurações da aplicação', + 'Language' => 'Idioma', + 'Webhook token:' => 'Token de webhooks:', + 'API token:' => 'Token de API:', + 'Database size:' => 'Tamanho do banco de dados:', + 'Download the database' => 'Baixar o banco de dados', + 'Optimize the database' => 'Otimizar o banco de dados', + '(VACUUM command)' => '(Comando VACUUM)', + '(Gzip compressed Sqlite file)' => '(Arquivo Sqlite comprimido com Gzip)', + 'Close a task' => 'Finalizar uma tarefa', + 'Column' => 'Coluna', + 'Color' => 'Cor', + 'Assignee' => 'Designação', + 'Create another task' => 'Criar outra tarefa', + 'New task' => 'Nova tarefa', + 'Open a task' => 'Abrir uma tarefa', + 'Do you really want to open this task: "%s"?' => 'Você realmente deseja abrir esta tarefa: "%s"?', + 'Back to the board' => 'Voltar ao quadro', + 'There is nobody assigned' => 'Não há ninguém designado', + 'Column on the board:' => 'Coluna no quadro:', + 'Close this task' => 'Finalizar esta tarefa', + 'Open this task' => 'Abrir esta tarefa', + 'There is no description.' => 'Não há descrição.', + 'Add a new task' => 'Adicionar uma nova tarefa', + 'The username is required' => 'O nome de usuário é obrigatório', + 'The maximum length is %d characters' => 'O tamanho máximo é %d caracteres', + 'The minimum length is %d characters' => 'O tamanho mínimo é %d caracteres', + 'The password is required' => 'A senha é obrigatória', + 'This value must be an integer' => 'O valor deve ser um número inteiro', + 'The username must be unique' => 'O nome de usuário deve ser único', + 'The user id is required' => 'O ID de usuário é 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 projeto é obrigatório', + 'The id is required' => 'O ID é 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 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 concluída.', + 'Your project has 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.', + 'Unable to update this project.' => 'Não é possível atualizar 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 ativado com sucesso.', + 'Unable to activate this project.' => 'Não é possível ativar este projeto.', + 'Project disabled successfully.' => 'Projeto desativado com sucesso.', + 'Unable to disable this project.' => 'Não é possível desativar 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.', + 'Task closed successfully.' => 'Tarefa finalizada com sucesso.', + 'Unable to update your task.' => 'Não é possível atualizar a sua tarefa.', + 'Task updated successfully.' => 'Tarefa atualizada com sucesso.', + 'Unable to create your task.' => 'Não é possível criar a sua tarefa.', + 'Task created successfully.' => 'Tarefa criada com sucesso.', + 'User created successfully.' => 'Usuário criado com sucesso.', + 'Unable to create your user.' => 'Não é possível criar o seu usuário.', + 'User updated successfully.' => 'Usuário atualizado com sucesso.', + 'User removed successfully.' => 'Usuário removido com sucesso.', + 'Unable to remove this user.' => 'Não é possível remover este usuário.', + 'Board updated successfully.' => 'Quadro atualizado com sucesso.', + 'Ready' => 'A fazer', + 'Backlog' => 'Backlog', + 'Work in progress' => 'Em andamento', + 'Done' => 'Feito', + 'Application version:' => 'Versão da aplicação:', + 'Id' => 'Id', + '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 no meu banco de dados!', + 'Page not found' => 'Página não encontrada', + 'Complexity' => 'Complexidade', + 'Task limit' => 'Limite de tarefas', + 'Task count' => 'Número de tarefas', + 'User' => 'Usuário', + 'Comments' => 'Comentários', + 'Comment is required' => 'Comentário é obrigatório', + '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 fim estimada', + 'Invalid date' => 'Data inválida', + 'Automatic actions' => 'Ações automáticas', + 'Your automatic action has been created successfully.' => 'Sua ação automática foi criada com sucesso.', + 'Unable to create your automatic action.' => 'Não é possível criar sua ação automática.', + 'Remove an action' => 'Remover uma ação', + 'Unable to remove this action.' => 'Não é possível remover esta ação.', + 'Action removed successfully.' => 'Ação removida com sucesso.', + 'Automatic actions for the project "%s"' => 'Ações automáticas para o projeto "%s"', + 'Add an action' => 'Adicionar Ação', + 'Event name' => 'Nome do evento', + 'Action' => 'Ação', + 'Event' => 'Evento', + 'When the selected event occurs execute the corresponding action.' => 'Quando o evento selecionado ocorrer execute a ação correspondente.', + 'Next step' => 'Próximo passo', + 'Define action parameters' => 'Definir parâmetros da ação', + 'Do you really want to remove this action: "%s"?' => 'Você realmente deseja remover esta ação: "%s"?', + 'Remove an automatic action' => 'Remover uma ação automática', + 'Assign the task to a specific user' => 'Atribuir a tarefa para um usuário específico', + 'Assign the task to the person who does the action' => 'Atribuir a tarefa para a pessoa que executa a ação', + 'Duplicate the task to another project' => 'Duplicar a tarefa para 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', + 'Closing a task' => 'Finalizando uma tarefa', + 'Assign a color to a specific user' => 'Atribuir uma cor para um usuário específico', + 'Position' => 'Posição', + 'Duplicate to project' => 'Duplicar para outro projeto', + 'Duplicate' => 'Duplicar', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Comentário atualizado com sucesso.', + 'Unable to update your comment.' => 'Não é possível atualizar o seu comentário.', + 'Remove a comment' => 'Remover um comentário', + 'Comment removed successfully.' => 'Comentário removido com sucesso.', + 'Unable to remove this comment.' => 'Não é possível remover este comentário.', + 'Do you really want to remove this comment?' => 'Você realmente deseja remover este comentário?', + 'Current password for the user "%s"' => 'Senha atual para o usuário "%s"', + 'The current password is required' => 'A senha atual é obrigatória', + 'Wrong password' => 'Senha incorreta', + 'Unknown' => 'Desconhecido', + 'Last logins' => 'Últimos logins', + 'Login date' => 'Data de login', + 'Authentication method' => 'Método de autenticação', + 'IP address' => 'Endereço IP', + 'User agent' => 'User Agent', + 'Persistent connections' => 'Conexões persistentes', + 'No session.' => 'Nenhuma sessão.', + 'Expiration date' => 'Data de expiração', + 'Remember Me' => 'Lembre-se de mim', + 'Creation date' => 'Data de criação', + 'Everybody' => 'Todos', + 'Open' => 'Abrir', + 'Closed' => 'Finalizado', + 'Search' => 'Pesquisar', + 'Nothing found.' => 'Nada foi encontrado.', + 'Due date' => 'Data fim estimada', + 'Description' => 'Descrição', + '%d comments' => '%d comentários', + '%d comment' => '%d comentário', + 'Email address invalid' => 'Endereço de e-mail inválido', + 'Your external account is not linked anymore to your profile.' => 'Sua conta externa não está mais ligada ao seu perfil.', + 'Unable to unlink your external account.' => 'Impossível de remover a sua conta externa.', + 'External authentication failed' => 'Autenticação externa falhou', + 'Your external account is linked to your profile successfully.' => 'Sua conta externa está agora ligada ao seu perfil.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Tarefa removida com sucesso.', + 'Unable to remove this task.' => 'Não foi possível remover esta tarefa.', + 'Remove a task' => 'Remover uma tarefa', + 'Do you really want to remove this task: "%s"?' => 'Você realmente deseja remover esta tarefa: "%s"?', + 'Assign automatically a color based on a category' => 'Atribuir automaticamente uma cor com base em uma categoria', + 'Assign automatically a category based on a color' => 'Atribuir automaticamente uma categoria com base em uma cor', + 'Task creation or modification' => 'Criação ou modificação de tarefa', + 'Category' => 'Categoria', + 'Category:' => 'Categoria:', + 'Categories' => 'Categorias', + 'Your category has been created successfully.' => 'Sua categoria foi criada com sucesso.', + 'This category has been updated successfully.' => 'A sua categoria foi atualizada com sucesso.', + 'Unable to update this category.' => 'Não foi possível atualizar a sua categoria.', + '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 projeto "%s"', + 'Category Name' => 'Nome da categoria', + 'Add a new category' => 'Adicionar uma nova categoria', + 'Do you really want to remove this category: "%s"?' => 'Você realmente deseja remover esta categoria: "%s"?', + 'All categories' => 'Todas as categorias', + 'No category' => 'Nenhuma categoria', + 'The name is required' => 'O nome é obrigatório', + 'Remove a file' => 'Remover um arquivo', + 'Unable to remove this file.' => 'Não foi possível remover este arquivo.', + 'File removed successfully.' => 'Arquivo removido com sucesso.', + 'Attach a document' => 'Anexar um documento', + 'Do you really want to remove this file: "%s"?' => 'Você realmente deseja remover este arquivo: "%s"?', + 'Attachments' => 'Anexos', + 'Edit the task' => 'Editar a tarefa', + 'Add a comment' => 'Adicionar um comentário', + 'Edit a comment' => 'Editar um comentário', + 'Summary' => 'Resumo', + 'Time tracking' => 'Rastreamento de tempo', + 'Estimate:' => 'Estimado:', + 'Spent:' => 'Gasto:', + 'Do you really want to remove this sub-task?' => 'Você realmente deseja remover esta subtarefa?', + 'Remaining:' => 'Restante:', + 'hours' => 'horas', + 'estimated' => 'estimado', + 'Sub-Tasks' => 'Subtarefas', + 'Add a sub-task' => 'Adicionar uma subtarefa', + 'Original estimate' => 'Estimativa original', + 'Create another sub-task' => 'Criar uma outra subtarefa', + 'Time spent' => 'Tempo gasto', + 'Edit a sub-task' => 'Editar uma subtarefa', + 'Remove a sub-task' => 'Remover uma subtarefa', + 'The time must be a numeric value' => 'O tempo deve ser um valor numérico', + 'Todo' => 'A fazer', + 'In progress' => 'Em andamento', + 'Sub-task removed successfully.' => 'Subtarefa removida com sucesso.', + 'Unable to remove this sub-task.' => 'Não foi possível remover esta subtarefa.', + 'Sub-task updated successfully.' => 'Subtarefa atualizada com sucesso.', + 'Unable to update your sub-task.' => 'Não foi possível atualizar a sua subtarefa.', + 'Unable to create your sub-task.' => 'Não é possível criar a sua subtarefa.', + 'Maximum size: ' => 'Tamanho máximo: ', + 'Display another project' => 'Exibir outro projeto', + 'Created by %s' => 'Criado por %s', + 'Tasks Export' => 'Exportar Tarefas', + 'Start Date' => 'Data inicial', + '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.' => 'Projeto clonado com sucesso.', + 'Unable to clone this project.' => 'Não foi possível clonar este projeto.', + 'Enable email notifications' => 'Habilitar notificações por e-mail', + 'Task position:' => 'Posição da tarefa:', + 'The task #%d has been opened.' => 'A tarefa #%d foi aberta.', + 'The task #%d has been closed.' => 'A tarefa #%d foi finalizada.', + 'Sub-task updated' => 'Subtarefa atualizada', + 'Title:' => 'Título:', + 'Status:' => 'Status:', + 'Assignee:' => 'Designado:', + 'Time tracking:' => 'Controle de tempo:', + '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 comment' => 'Novo comentário', + 'Comment updated' => 'Comentário atualizado', + 'New subtask' => 'Nova subtarefa', + 'I only want to receive notifications for these projects:' => 'Quero receber notificações apenas destes projetos:', + 'view the task on Kanboard' => 'ver a tarefa no Kanboard', + 'Public access' => 'Acesso público', + 'Disable public access' => 'Desabilitar o acesso público', + 'Enable public access' => 'Habilitar o acesso público', + 'Public access disabled' => 'Acesso público desabilitado', + 'Move the task to another project' => 'Mover a tarefa para outro projeto', + 'Move to project' => 'Mover para outro projeto', + 'Do you really want to duplicate this task?' => 'Você realmente deseja duplicar esta tarefa?', + 'Duplicate a task' => 'Duplicar uma tarefa', + 'External accounts' => 'Contas externas', + 'Account type' => 'Tipo de conta', + 'Local' => 'Local', + 'Remote' => 'Remoto', + 'Enabled' => 'Habilitado', + 'Disabled' => 'Desabilitado', + 'Login:' => 'Usuário:', + 'Full Name:' => 'Nome:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Notificações:', + 'Notifications' => 'Notificações', + 'Account type:' => 'Tipo de conta:', + 'Edit profile' => 'Editar perfil', + 'Change password' => 'Alterar senha', + 'Password modification' => 'Alteração de senha', + 'External authentications' => 'Autenticação externa', + 'Never connected.' => 'Nunca conectado.', + 'No external authentication enabled.' => 'Nenhuma autenticação externa habilitada.', + 'Password modified successfully.' => 'Senha alterada com sucesso.', + 'Unable to change the password.' => 'Não foi possível alterar a senha.', + 'Change category' => 'Mudar categoria', + '%s updated the task %s' => '%s atualizou a tarefa %s', + '%s opened the task %s' => '%s abriu a tarefa %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s moveu a tarefa %s para a posição #%d na coluna "%s"', + '%s moved the task %s to the column "%s"' => '%s moveu a tarefa %s para a coluna "%s"', + '%s created the task %s' => '%s criou a tarefa %s', + '%s closed the task %s' => '%s finalizou a tarefa %s', + '%s created a subtask for the task %s' => '%s criou uma subtarefa para a tarefa %s', + '%s updated a subtask for the task %s' => '%s atualizou uma subtarefa da tarefa %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Designado para %s com tempo estimado de %s/%sh', + 'Not assigned, estimate of %sh' => 'Não designado, estimado em %sh', + '%s updated a comment on the task %s' => '%s atualizou o comentário na tarefa %s', + '%s commented the task %s' => '%s comentou a tarefa %s', + '%s\'s activity' => 'Atividades de %s', + 'RSS feed' => 'Feed RSS', + '%s updated a comment on the task #%d' => '%s atualizou um comentário sobre a tarefa #%d', + '%s commented on the task #%d' => '%s comentou sobre a tarefa #%d', + '%s updated a subtask for the task #%d' => '%s atualizou uma subtarefa para a tarefa #%d', + '%s created a subtask for the task #%d' => '%s criou uma subtarefa para a tarefa #%d', + '%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 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 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', + 'Create a task from an external provider' => 'Criar uma tarefa por meio de um serviço externo', + 'Change the assignee based on an external username' => 'Alterar designação com base em um usuário externo', + 'Change the category based on an external label' => 'Alterar categoria com base em um rótulo externo', + 'Reference' => 'Referência', + 'Label' => 'Rótulo', + 'Database' => 'Banco de dados', + 'About' => 'Sobre', + 'Database driver:' => 'Driver do banco de dados:', + 'Board settings' => 'Configurações do quadro', + 'Webhook settings' => 'Configurações do Webhook', + 'Reset token' => 'Resetar token', + 'API endpoint:' => 'API endpoint:', + 'Refresh interval for personal board' => 'Intervalo de atualização para um quadro privado', + 'Refresh interval for public board' => 'Intervalo de atualização para um quadro público', + 'Task highlight period' => 'Período de Tarefa em destaque', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Período (em segundos) para considerar que uma tarefa foi modificada recentemente (0 para desativar, 2 dias por padrão)', + 'Frequency in second (60 seconds by default)' => 'Frequência em segundos (60 segundos por padrão)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequência em segundos (0 para desativar este recurso, 10 segundos por padrão)', + 'Application URL' => 'URL da Aplicação', + 'Token regenerated.' => 'Novo token gerado.', + 'Date format' => 'Formato de data', + 'ISO format is always accepted, example: "%s" and "%s"' => 'O formato ISO é sempre aceito, exemplo: "%s" e "%s"', + 'New personal project' => 'Novo projeto pessoal', + 'This project is personal' => 'Este projeto é pessoal', + 'Add' => 'Adicionar', + 'Start date' => 'Data de início', + 'Time estimated' => 'Tempo estimado', + 'There is nothing assigned to you.' => 'Não há nada designado a você.', + 'My tasks' => 'Minhas tarefas', + 'Activity stream' => 'Atividades Recentes', + 'Dashboard' => 'Painel de Controle', + 'Confirmation' => 'Confirmação', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Criar um comentário por meio de um serviço externo', + 'Project management' => 'Gerenciamento de projetos', + 'Columns' => 'Colunas', + 'Task' => 'Tarefas', + 'Percentage' => 'Porcentagem', + 'Number of tasks' => 'Número de tarefas', + 'Task distribution' => 'Distribuição de tarefas', + 'Analytics' => 'Estatísticas', + 'Subtask' => 'Subtarefa', + 'User repartition' => 'Redistribuição de usuário', + '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 projeto deve ser um inteiro', + 'The status must be an integer' => 'O status 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', + 'The task id is required' => 'O ID da tarefa é obrigatório', + 'The task id must be an integer' => 'O ID da tarefa deve ser um número inteiro', + 'The user id must be an integer' => 'O ID do usuário deve ser um número inteiro', + 'This value is required' => 'Este valor é obrigatório', + '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', + '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' => 'Raias ativas', + 'Add a new swimlane' => 'Adicionar uma nova raia', + 'Default swimlane' => 'Raia padrão', + 'Do you really want to remove this swimlane: "%s"?' => 'Você realmente deseja remover esta raia: "%s"?', + 'Inactive swimlanes' => 'Raias inativas', + 'Remove a swimlane' => 'Remover uma raia', + 'Swimlane modification for the project "%s"' => 'Modificação de raia para o projeto "%s"', + 'Swimlane removed successfully.' => 'Raia removida com sucesso.', + 'Swimlanes' => 'Raias', + 'Swimlane updated successfully.' => 'Raia atualizada com sucesso.', + 'Unable to remove this swimlane.' => 'Não foi possível remover esta raia.', + 'Unable to update this swimlane.' => 'Não foi possível atualizar esta raia.', + 'Your swimlane has been created successfully.' => 'Sua raia foi criada com sucesso.', + 'Example: "Bug, Feature Request, Improvement"' => 'Exemplo: "Bug, Solicitação de Recurso, Melhoria"', + 'Default categories for new projects (Comma-separated)' => 'Categorias padrões 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', + 'Task Title' => 'Título da Tarefa', + 'Untitled' => 'Sem título', + 'Application default' => 'Padrão da aplicação', + 'Language:' => 'Idioma', + 'Timezone:' => 'Fuso horário', + 'All columns' => 'Todas as colunas', + 'Next' => 'Próximo', + '#%d' => '#%d', + 'All swimlanes' => 'Todas as raias', + 'All colors' => 'Todas as cores', + 'Moved to column %s' => 'Movido para a coluna %s', + 'User dashboard' => 'Painel de Controle do usuário', + 'Allow only one subtask in progress at the same time for a user' => 'Permitir apenas uma subtarefa em andamento ao mesmo tempo para um usuário', + 'Edit column "%s"' => 'Editar a coluna "%s"', + 'Select the new status of the subtask: "%s"' => 'Selecionar um novo status para a subtarefa: "%s"', + 'Subtask timesheet' => 'Gestão de tempo das subtarefas', + 'There is nothing to show.' => 'Não há nada para mostrar.', + 'Time Tracking' => 'Gestão de tempo', + 'You already have one subtask in progress' => 'Você já tem um subtarefa em andamento', + 'Which parts of the project do you want to duplicate?' => 'Quais partes do projeto você deseja duplicar?', + 'Disallow login form' => 'Proibir o formulário de login', + 'Start' => 'Início', + 'End' => 'Fim', + 'Task age in days' => 'Idade da tarefa em dias', + 'Days in this column' => 'Dias nesta coluna', + '%dd' => '%dd', + 'Add a new link' => 'Adicionar uma nova associação', + 'Do you really want to remove this link: "%s"?' => 'Você realmente deseja remover esta associação: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Você realmente deseja remover esta associação com a tarefa #%d?', + 'Field required' => 'Campo requerido', + 'Link added successfully.' => 'Associação criada com sucesso.', + 'Link updated successfully.' => 'Associação atualizada com sucesso.', + 'Link removed successfully.' => 'Associação removida com sucesso.', + 'Link labels' => 'Rótulos das associações', + 'Link modification' => 'Modificação de uma associação', + 'Opposite label' => 'Nome do rótulo oposto', + 'Remove a link' => 'Remover uma associação', + 'The labels must be different' => 'Os rótulos devem ser diferentes', + 'There is no link.' => 'Não há nenhuma associação.', + 'This label must be unique' => 'Este rótulo deve ser único', + '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' => 'é associada com', + 'blocks' => 'bloqueia', + 'is blocked by' => 'está bloqueada por', + 'duplicates' => 'duplica', + 'is duplicated by' => 'é duplicada por', + 'is a child of' => 'é filha de', + 'is a parent of' => 'é pai de', + 'targets milestone' => 'marcos alvos', + 'is a milestone of' => 'é um marco de', + 'fixes' => 'corrige', + 'is fixed by' => 'foi corrigida por', + 'This task' => 'Esta tarefa', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Expandir tarefas', + 'Collapse tasks' => 'Contrair tarefas', + 'Expand/collapse tasks' => 'Expandir/Contrair tarefas', + 'Close dialog box' => 'Fechar a caixa de diálogo', + 'Submit a form' => 'Envia o formulário', + 'Board view' => 'Visão do quadro', + 'Keyboard shortcuts' => 'Atalhos de teclado', + 'Open board switcher' => 'Abrir opções de quadros', + 'Application' => 'Aplicação', + 'Compact view' => 'Vista reduzida', + 'Horizontal scrolling' => 'Rolagem horizontal', + 'Compact/wide view' => 'Alternar entre a vista compacta e ampliada', + 'Currency' => 'Moeda', + 'Personal project' => 'Projeto pessoal', + 'AUD - Australian Dollar' => 'AUD - Dólar australiano', + 'CAD - Canadian Dollar' => 'CAD - Dólar canadense', + 'CHF - Swiss Francs' => 'CHF - Francos Suíços', + 'Custom Stylesheet' => 'Folha de estilo personalizado', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Libra esterlina', + 'INR - Indian Rupee' => 'INR - Rúpia indiana', + 'JPY - Japanese Yen' => 'JPY - Iene japonês', + 'NZD - New Zealand Dollar' => 'NZD - Dólar neozelandês', + 'PEN - Peruvian Sol' => 'PEN - Sol peruano', + 'RSD - Serbian dinar' => 'RSD - Dinar sérvio', + 'CNY - Chinese Yuan' => 'CNY - Yuan chinês', + 'USD - US Dollar' => 'USD - Dólar norte-americano', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar venezuelano', + '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', + 'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída', + 'Source column' => 'Coluna de origem', + 'Transitions' => 'Transições', + 'Executer' => 'Executor(a)', + 'Time spent in the column' => 'Tempo gasto na coluna', + 'Task transitions' => 'Transições das tarefas', + 'Task transitions export' => 'Exportação das transições das tarefas', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este relatório contém todos os movimentos de coluna para cada tarefa com a data, o usuário e o tempo gasto para cada transição.', + 'Currency rates' => 'Taxas de câmbio das moedas estrangeiras', + 'Rate' => 'Taxa', + 'Change reference currency' => 'Mudar a moeda de referência', + 'Reference currency' => 'Moeda de Referência', + 'The currency rate has 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 removed the assignee of the task %s' => '%s removeu a pessoa designada para a tarefa %s', + 'Information' => 'Informações', + 'Check two factor authentication code' => 'Verifique o código de autenticação em duas etapas', + 'The two factor authentication code is not valid.' => 'O código de autenticação em duas etapas não é válido.', + 'The two factor authentication code is valid.' => 'O código de autenticação em duas etapas é válido.', + 'Code' => 'Código', + 'Two factor authentication' => 'Autenticação em duas etapas', + 'This QR code contains the key URI: ' => 'Este Código QR contém a chave URI:', + 'Check my code' => 'Verifique o meu código', + 'Secret key: ' => 'Chave secreta:', + '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 for movida para uma coluna específica', + '%s via Kanboard' => '%s via Kanboard', + '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', + 'Add a screenshot' => 'Adicionar uma captura de tela', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tire uma captura de tela e pressione CTRL+V ou ⌘+V para colar aqui.', + 'Screenshot uploaded successfully.' => 'Captura de tela enviada com sucesso.', + 'SEK - Swedish Krona' => 'SEK - Coroa sueca', + 'Identifier' => 'Identificador', + 'Disable two factor authentication' => 'Desativar autenticação em duas etapas', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Você realmente deseja desativar a autenticação em duas etapas para este usuário: "%s"?', + 'Edit link' => 'Editar um link', + 'Start to type task title...' => 'Digite o título da tarefa...', + 'A task cannot be linked to itself' => 'Uma tarefa não pode ser vinculada a si própria', + 'The exact same link already exists' => 'Um link idêntico já existe', + 'Recurrent task is scheduled to be generated' => 'A tarefa recorrente está programada para ser criada', + 'Score' => 'Complexidade', + 'The identifier must be unique' => 'O identificador deve ser único', + 'This linked task id doesn\'t exists' => 'O identificador da tarefa associada não existe', + 'This value must be alphanumeric' => 'Este valor deve ser alfanumérico', + '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 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 fim estimada: ', + 'This task has created this child task: ' => 'Esta tarefa criou a tarefa filha: ', + 'Day(s)' => 'Dia(s)', + '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)', + '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 fim estimada: ', + 'Trigger to generate recurrent task: ' => 'Trigger para gerar tarefa recorrente: ', + '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)', + 'Project settings' => 'Configurações dos projetos', + 'Automatically update the start date' => 'Atualizar automaticamente a data de início', + 'iCal feed' => 'Subscrição iCal', + 'Preferences' => 'Preferências', + 'Security' => 'Segurança', + 'Two factor authentication disabled' => 'Autenticação em duas etapas desativada', + 'Two factor authentication enabled' => 'Autenticação em duas etapas ativada', + 'Unable to update this user.' => 'Impossível de atualizar esse usuário.', + 'There is no user management for personal projects.' => 'Não há gerenciamento de usuários para projetos pessoais.', + 'User that will receive the email' => 'O usuário que vai receber o e-mail', + 'Email subject' => 'Assunto do e-mail', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'Adicionar um comentário de log quando uma tarefa é movida para uma outra coluna', + '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', + 'Notification' => 'Notificação', + '%s moved the task #%d to the first swimlane' => '%s moveu a tarefa #%d para a primeira raia', + 'Swimlane' => 'Raia', + '%s moved the task %s to the first swimlane' => '%s moveu a tarefa %s para a primeira raia', + '%s moved the task %s to the swimlane "%s"' => '%s moveu a tarefa %s para a raia "%s"', + 'This report contains all subtasks information for the given date range.' => 'Este relatório contém informações de todas as subtarefas 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' => 'Atividade do projeto "%s"', + 'view the board on Kanboard' => 'ver o quadro no Kanboard', + 'The task has been moved to the first swimlane' => 'A tarefa foi movida para a primeira raia', + 'The task has been moved to another swimlane:' => 'A tarefa foi movida para outra raia:', + 'New title: %s' => 'Novo título: %s', + 'The task is not assigned anymore' => 'Agora a tarefa não está mais atribuída', + 'New assignee: %s' => 'Novo designado: %s', + 'There is no category now' => 'Agora não tem mais categoria', + 'New category: %s' => 'Nova categoria: %s', + 'New color: %s' => 'Nova cor: %s', + 'New complexity: %d' => 'Nova complexidade: %d', + 'The due date has been removed' => 'A data fim estimada foi retirada', + 'There is no description anymore' => 'Agora não tem mais descrição', + 'Recurrence settings has been modified' => 'As configurações da recorrência foram modificadas', + 'Time spent changed: %sh' => 'O tempo despendido foi mudado: %sh', + 'Time estimated changed: %sh' => 'O tempo estimado foi mudado/ %sh', + 'The field "%s" has been updated' => 'O campo "%s" foi atualizada', + 'The description has been modified:' => 'A descrição foi modificada', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Você realmente deseja finalizar a tarefa "%s" e todas as suas subtarefas?', + 'I want to receive notifications for:' => 'Eu quero receber as notificações para:', + 'All tasks' => 'Todas as tarefas', + 'Only for tasks assigned to me' => 'Somente as tarefas atribuídas a mim', + 'Only for tasks created by me' => 'Apenas as tarefas que eu criei', + 'Only for tasks created by me and tasks assigned to me' => 'Apenas as tarefas que eu criei e aquelas atribuídas a mim', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Total para todas as colunas', + 'You need at least 2 days of data to show the chart.' => 'Você precisa de pelo menos 2 dias de dados para visualizar o gráfico.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Parar temporizador', + 'Start timer' => 'Iniciar temporizador', + 'My activity stream' => 'Meu feed de atividades', + 'Search tasks' => 'Pesquisar tarefas', + 'Reset filters' => 'Redefinir os filtros', + 'My tasks due tomorrow' => 'Minhas tarefas que expirarão amanhã', + 'Tasks due today' => 'Tarefas que expiram hoje', + 'Tasks due tomorrow' => 'Tarefas que expirarão amanhã', + 'Tasks due yesterday' => 'Tarefas que expiraram ontem', + 'Closed tasks' => 'Tarefas finalizadas', + 'Open tasks' => 'Tarefas abertas', + 'Not assigned' => 'Não designada', + 'View advanced search syntax' => 'Ver a sintaxe para pesquisa avançada', + 'Overview' => 'Visão global', + 'Board/Calendar/List view' => 'Quadro/Calendário/Lista', + 'Switch to the board view' => 'Mudar para a visão de quadro', + 'Switch to the list view' => 'Mudar par o modo Lista', + 'Go to the search/filter box' => 'Ir para o campo de filtro/pesquisa', + 'There is no activity yet.' => 'Não há nenhuma atividade ainda.', + 'No tasks found.' => 'Nenhuma tarefa encontrada.', + 'Keyboard shortcut: "%s"' => 'Tecla de atalho: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtro', + 'Advanced search' => 'Pesquisa avançada', + 'Example of query: ' => 'Exemplo de consulta: ', + 'Search by project: ' => 'Pesquisar por projeto: ', + 'Search by column: ' => 'Pesquisar por coluna: ', + 'Search by assignee: ' => 'Pesquisar por designado: ', + '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 fim estimada: ', + 'Average time spent in each column' => 'Tempo médio gasto em cada coluna', + 'Average time spent' => 'Tempo médio gasto', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Este gráfico mostra o tempo médio gasto em cada coluna para as %d últimas tarefas.', + 'Average Lead and Cycle time' => 'Tempo médio do tempo de condução e tempo de ciclo', + 'Average lead time: ' => 'Tempo de condução médio: ', + 'Average cycle time: ' => 'Tempo de ciclo médio: ', + 'Cycle Time' => 'Tempo de ciclo', + 'Lead Time' => 'Tempo de condução', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico mostra o tempo médio do tempo de condução e tempo de ciclo das últimas %d tarefas.', + 'Average time into each column' => 'Tempo médio de cada coluna', + 'Lead and cycle time' => 'Tempo de condução e tempo de ciclo', + 'Lead time: ' => 'Tempo de condução: ', + 'Cycle time: ' => 'Tempo de ciclo: ', + 'Time spent in each column' => 'Tempo gasto em cada coluna', + 'The lead time is the duration between the task creation and the completion.' => 'O tempo de condução é 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 Tempo de ciclo é 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á finalizada, a hora atual é usada no lugar da data de conclusão.', + 'Set the start date automatically' => '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 "Impedir o formulário de autenticação", os identificadores entrados no formulário de login serão ignorado.', + '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 finalizadas no diagrama de fluxo acumulado', + 'Current swimlane: %s' => 'Raia atual: %s', + 'Current column: %s' => 'Coluna atual: %s', + 'Current category: %s' => 'Categoria atual: %s', + 'no category' => 'nenhuma categoria', + 'Current assignee: %s' => 'Designação atual: %s', + 'not assigned' => 'não designado', + 'Author:' => 'Autor:', + 'contributors' => 'contribuidores', + 'License:' => 'Licença:', + 'License' => 'Licença', + 'Enter the text below' => 'Digite o texto abaixo', + 'Start date:' => 'Data de início:', + 'Due date:' => 'Data fim estimada:', + 'People who are project managers' => 'Pessoas que são gerente de projeto', + 'People who are project members' => 'Pessoas que são membro de projeto', + 'NOK - Norwegian Krone' => 'NOK - Coroa Norueguesa', + 'Show this column' => 'Mostrar esta coluna', + 'Hide this column' => 'Esconder esta coluna', + 'End date' => 'Data de término', + 'Users overview' => 'Visão geral dos usuários', + 'Members' => 'Membros', + 'Shared project' => 'Projeto compartilhado', + 'Project managers' => 'Gerentes de projeto', + 'Projects list' => 'Lista dos projetos', + 'End date:' => 'Data de término:', + 'Change task color when using a specific task link' => 'Mudar a cor da tarefa quando um link específico é utilizado', + 'Task link creation or modification' => 'Criação ou modificação de um link em uma tarefa', + 'Milestone' => 'Marco', + 'Reset the search/filter box' => 'Reiniciar o campo de pesquisa', + 'Documentation' => 'Documentação', + 'Author' => 'Autor', + 'Version' => 'Versão', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Não há plugins carregados.', + 'My notifications' => 'Minhas notificações', + 'Custom filters' => 'Filtros personalizados', + 'Your custom filter has been created successfully.' => 'Seu filtro personalizado foi criado com sucesso.', + 'Unable to create your custom filter.' => 'Não foi possível criar seu filtro personalizado.', + 'Custom filter removed successfully.' => 'Filtro personalizado removido com sucesso.', + 'Unable to remove this custom filter.' => 'Não foi possível remover este filtro personalizado.', + 'Edit custom filter' => 'Editar filtro personalizado', + 'Your custom filter has been updated successfully.' => 'Seu filtro personalizado foi atualizado com sucesso.', + 'Unable to update custom filter.' => 'Não foi possível atualizar o filtro personalizado.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Novo anexo na tarefa #%d: %s', + 'New comment on task #%d' => 'Novo comentário na tarefa #%d', + 'Comment updated on task #%d' => 'Comentário atualizado na tarefa #%d', + 'New subtask on task #%d' => 'Nova subtarefa na tarefa #%d', + '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 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', + 'Swimlane changed for task #%d' => 'Raia alterada para a tarefa #%d', + 'Assignee changed on task #%d' => 'Designação alterada na tarefa #%d', + '%d overdue tasks' => '%d tarefas atrasadas', + 'No notification.' => 'Nenhuma notificação nova.', + 'Mark all as read' => 'Marcar todas como lidas', + 'Mark as read' => 'Marcar como lida', + 'Total number of tasks in this column across all swimlanes' => 'Número total de tarefas nesta coluna através de todas as raias', + 'Collapse swimlane' => 'Contrair raia', + 'Expand swimlane' => 'Expandir raia', + 'Add a new filter' => 'Adicionar filtro', + 'Share with all project members' => 'Compartilhar com todos os membros do projeto', + 'Shared' => 'Compartilhado', + 'Owner' => 'Líder', + 'Unread notifications' => 'Notificações não lidas', + 'Notification methods:' => 'Métodos de notificação:', + '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 has been imported!' => 'Nada foi importado!', + 'Import users from CSV file' => 'Importar usuários a partir de arquivo CSV', + '%d user(s) have been imported successfully.' => '%d usuário(s) importado(s) com sucesso.', + 'Comma' => 'Vírgula', + 'Semi-colon' => 'Ponto e vírgula', + 'Tab' => 'Tab', + 'Vertical bar' => 'Barra vertical', + 'Double Quote' => 'Aspas duplas', + 'Single Quote' => 'Aspas simples', + '%s attached a file to the task #%d' => '%s anexou um arquivo à tarefa #%d', + 'There is no column or swimlane activated in your project!' => 'Não há coluna ou raia ativa em seu projeto!', + 'Append filter (instead of replacement)' => 'Adicionar filtro (em vez de substituir)', + 'Append/Replace' => 'Adicionar/Substituir', + 'Append' => 'Adicionar', + 'Replace' => 'Substituir', + 'Import' => 'Importar', + 'Change sorting' => 'alterar ordenação', + 'Tasks Importation' => 'Importação de Tarefas', + 'Delimiter' => 'Separador', + 'Enclosure' => 'Delimitador de campos', + 'CSV File' => 'Arquivo CSV', + 'Instructions' => 'Instruções', + 'Your file must use the predefined CSV format' => 'Seu arquivo deve utilizar o formato CSV pré-definido', + '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 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', + 'Usernames must be lowercase and unique' => 'Nomes de usuário devem ser únicos e em letras minúsculas', + '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 arquivo a tarefa %s', + 'Link type' => 'Tipo de link', + 'Assign automatically a category based on a link' => 'Atribuir automaticamente uma categoria baseada num link', + 'BAM - Konvertible Mark' => 'BAM - Mark conversível', + 'Assignee Username' => 'Usuário designado', + 'Assignee Name' => 'Nome do designado', + 'Groups' => 'Grupos', + 'Members of %s' => 'Membros do %s', + 'New group' => 'Novo grupo', + 'Group created successfully.' => 'Grupo criado com sucesso.', + 'Unable to create your group.' => 'Não foi possível de criar o seu grupo.', + 'Edit group' => 'Editar o grupo', + 'Group updated successfully.' => 'Grupo atualizado com sucesso.', + 'Unable to update your group.' => 'Não foi possível atualizar o seu grupo.', + 'Add group member to "%s"' => 'Adicionar um membro ao grupo "%s"', + 'Group member added successfully.' => 'Membro adicionado com sucesso ao o grupo.', + 'Unable to add group member.' => 'Não foi possível adicionar esse membro ao grupo.', + 'Remove user from group "%s"' => 'Remover usuário do grupo "%s"', + 'User removed successfully from this group.' => 'Usuário removido com sucesso deste grupo.', + 'Unable to remove this user from the group.' => 'Não foi possível remover este Usuário do grupo.', + 'Remove group' => 'Remover o grupo', + 'Group removed successfully.' => 'Grupo removido com sucesso.', + 'Unable to remove this group.' => 'Não foi possível remover este grupo.', + 'Project Permissions' => 'Permissões do projeto', + 'Manager' => 'Gerente', + 'Project Manager' => 'Gerente 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', + '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.', + 'Add group member' => 'Adicionar um membro ao grupo', + 'Do you really want to remove this group: "%s"?' => 'Você realmente deseja excluir este grupo: "%s"?', + 'There is no user in this group.' => 'Não há usuários neste grupo.', + 'Permissions' => 'Permissões', + 'Allowed Users' => 'Usuários autorizados', + 'No specific user has been allowed.' => 'Nenhum usuário foi especificamente autorizado.', + 'Role' => 'Função', + 'Enter user name...' => 'Digite o nome do usuário...', + 'Allowed Groups' => 'Grupos autorizados', + 'No group has been allowed.' => 'Nenhum grupo foi especificamente autorizado.', + 'Group' => 'Grupo', + 'Group Name' => 'Nome do grupo', + 'Enter group name...' => 'Digite o nome do grupo', + 'Role:' => 'Função:', + 'Project members' => 'Membros de projeto', + '%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', + 'Estimated hours: ' => 'Horas estimadas: ', + 'Actual hours: ' => 'Horas reais: ', + 'Hours Spent' => 'Horas gastas', + 'Hours Estimated' => 'Horas estimadas', + 'Estimated Time' => 'Tempo estimado', + 'Actual Time' => 'Tempo real', + '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' => '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', + 'Enable two-factor authentication' => 'Ativar a autenticação a dois fatores', + 'There is no integration registered at the moment.' => 'Não há nenhuma integração registrada por enquanto.', + 'Password Reset for Kanboard' => 'Redefinir a senha para Kanboard', + 'Forgot password?' => 'Esqueceu a senha?', + 'Enable "Forget Password"' => 'Ativar a funcionalidade "Esqueceu a senha"', + 'Password Reset' => 'Redefinir a senha', + 'New password' => 'Nova senha', + 'Change Password' => 'Alterar a senha', + 'To reset your password click on this link:' => 'Para redefinir a sua senha, clique neste link:', + 'Last Password Reset' => 'Última redefinição de senha', + 'The password has never been reinitialized.' => 'A senha nunca foi redefinida.', + '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 raia "%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) da coluna "%s" e da raia "%s" serão finalizadas.', + 'Close all tasks in this column and this swimlane' => 'Finalizar todas as tarefas desta coluna', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nenhum plugin registrou método de notificação de projeto. Você ainda pode definir notificações individuais em seu perfil de usuário.', + 'My dashboard' => 'Meu Painel de Controle', + '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', + 'Personal projects do not have users and groups management.' => 'Projetos pessoais 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', + 'Task priority' => 'Prioridade das tarefas', + 'General' => 'Geral', + 'Dates' => 'Datas', + 'Default priority' => 'Prioridade padrão', + 'Lowest priority' => 'Prioridade baixa', + 'Highest priority' => 'Prioridade alta', + '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.', + 'Daily background job for tasks' => 'Tarefa agendada diariamente para as tarefas', + 'Auto' => 'Auto', + 'Related' => 'Relacionado', + 'Attachment' => 'Anexo', + 'Web Link' => 'Link web', + 'External links' => 'Links externos', + 'Add external link' => 'Adicionar um link externo', + 'Type' => 'Tipo', + 'Dependency' => 'Dependência', + 'Add internal link' => 'Adicionar um link interno', + 'Add a new external link' => 'Adicionar um novo link externo', + 'Edit external link' => 'Editar um link externo', + 'External link' => 'Link externo', + 'Copy and paste your link here...' => 'Copie e cole o link aqui...', + 'URL' => 'URL', + 'Internal links' => 'Link interno', + 'Assign to me' => 'Atribuir-me', + 'Me' => 'Eu', + 'Do not duplicate anything' => 'Não duplique nada', + 'Projects management' => 'Gestão de projetos', + 'Users management' => 'Gestão dos usuários', + 'Groups management' => 'Gestão dos grupos', + 'Create from another project' => 'Criar a partir de outro projeto', + 'open' => 'aberto', + 'closed' => 'finalizado', + 'Priority:' => 'Prioridade:', + 'Reference:' => 'Referência:', + 'Complexity:' => 'Complexidade:', + 'Swimlane:' => 'Raia:', + 'Column:' => 'Coluna:', + 'Position:' => 'Posição:', + 'Creator:' => 'Criador:', + 'Time estimated:' => 'Tempo estimado:', + '%s hours' => '%s horas', + 'Time spent:' => 'Tempo gasto:', + 'Created:' => 'Criado:', + 'Modified:' => 'Modificado:', + 'Completed:' => 'Finalizado:', + 'Started:' => 'Começado:', + 'Moved:' => 'Movido:', + 'Task #%d' => 'Tarefa #%d', + 'Time format' => 'Formato da hora', + 'Start date: ' => 'Data de início: ', + 'End date: ' => 'Data final: ', + 'New due date: ' => 'Nova data fim estimada: ', + 'Start date changed: ' => 'Data de início alterada: ', + 'Disable personal projects' => 'Desativar projetos pessoais', + 'Do you really want to remove this custom filter: "%s"?' => 'Você realmente quer remover este filtro personalizado: "%s"?', + 'Remove a custom filter' => 'Remover um filtro personalizado', + 'User activated successfully.' => 'Usuário ativado com sucesso.', + 'Unable to enable this user.' => 'Impossível de ativar esse usuário.', + '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.', + 'The maximum allowed file size is %sB.' => 'O tamanho máximo dos arquivos é %sB.', + 'Drag and drop your files here' => 'Arraste e solte os arquivos aqui', + 'choose files' => 'selecione os arquivos', + 'View profile' => 'Ver o perfil', + 'Two Factor' => 'Dois fatores', + 'Disable user' => 'Desativar o usuário', + 'Do you really want to disable this user: "%s"?' => 'Você realmente quer desativar este usuário: "%s"?', + 'Enable user' => 'Ativar um usuário', + 'Do you really want to enable this user: "%s"?' => 'Você realmente quer ativar este usuário: "%s"?', + 'Download' => 'Baixar', + 'Uploaded: %s' => 'Enviado: %s', + 'Size: %s' => 'Tamanho: %s', + 'Uploaded by %s' => 'Enviado por %s', + 'Filename' => 'Nome do arquivo', + 'Size' => 'Tamanho', + 'Column created successfully.' => 'A coluna criada com sucesso.', + 'Another column with the same name exists in the project' => 'Uma outra coluna com o mesmo nome já existe 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' => 'Alterar a posição da coluna', + 'Switch to the project overview' => 'Mudar para a vista geral do projeto', + 'User filters' => 'Filtros dos usuários', + 'Category filters' => 'Filtros das categorias', + 'Upload a file' => 'Enviar um arquivo', + 'View file' => 'Ver arquivo', + 'Last activity' => 'Últimas atividades', + 'Change subtask position' => 'Alterar a posição da subtarefa', + 'This value must be greater than %d' => 'Este valor deve ser maior que %d', + 'Another swimlane with the same name exists in the project' => 'Outra raia existe com o mesmo nome no projeto', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Exemplo: https://exemplo.kanboard.org/ (usado para gerar URLs absolutos)', + 'Actions duplicated successfully.' => 'Ações duplicadas com sucesso.', + 'Unable to duplicate actions.' => 'Não foi possível duplicar as ações.', + 'Add a new action' => 'Adicionar uma nova ação', + 'Import from another project' => 'Importar a partir de outro projeto', + 'There is no action at the moment.' => 'Não há nenhuma ação atualmente.', + 'Import actions from another project' => 'Importar ações a partir de outro projeto', + 'There is no available project.' => 'Não há projetos disponíveis.', + 'Local File' => 'Arquivo local', + 'Configuration' => 'Configuração', + 'PHP version:' => 'Versão do PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versão do sistema operacional:', + 'Database version:' => 'Versão do banco de dados:', + 'Browser:' => 'Browser:', + 'Task view' => 'Vista detalhada de uma tarefa', + 'Edit task' => 'Editar a tarefa', + 'Edit description' => 'Editar a descrição', + 'New internal link' => 'Novo link interno', + 'Display list of keyboard shortcuts' => 'Ver a lista dos atalhos de teclado', + 'Avatar' => 'Avatar', + '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.' => '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: ' => 'Pesquisar por criador: ', + 'Search by creation date: ' => 'Pesquisar por data de criação: ', + 'Search by task status: ' => 'Pesquisar por estado da tarefa: ', + 'Search by task title: ' => 'Pesquisar por título da tarefa: ', + 'Activity stream search' => 'Pesquisa do 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 possível converter a subtarefa.', + 'Unable to extract plugin archive.' => 'Não foi possível 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 possível transferir o arquivo do plugin.', + 'Unable to write temporary file for plugin.' => 'Não foi possível escrever o arquivo temporário para o plugin.', + 'Unable to open plugin archive.' => 'Não foi possível 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 da 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 disponível', + '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 subtarefa para tarefa', + 'Do you really want to convert this sub-task to a task?' => 'Tem a certeza que pretende converter esta subtarefa para tarefa?', + 'My task title' => 'Título 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' => 'Configurações de e-mail', + 'Email sender address' => 'Endereço de envio de e-mail', + 'Email transport' => 'Transportador de e-mail', + '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 possível criar esta etiqueta.', + 'Tag updated successfully.' => 'Etiqueta atualizada com sucesso.', + 'Unable to update this tag.' => 'Não foi possível atualizar esta etiqueta.', + 'Tag removed successfully.' => 'Etiqueta removida com sucesso.', + 'Unable to remove this tag.' => 'Não foi possível 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 a specific column' => 'Fechar a tarefa quando não houver atividade numa coluna especifica', + '%s removed a subtask for the task #%d' => '%s removeu uma subtarefa 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 de Controle', + '%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 subtarefa 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 has 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 período', + 'Dashboard for %s' => 'Painel de Controle de %s', + 'Tasks overview for %s' => 'Visão geral das tarefas de %s', + 'Subtasks overview for %s' => 'Visão geral das subtarefas de %s', + 'Projects overview for %s' => 'Visão geral dos projetos de %s', + 'Activity stream for %s' => 'Fluxo de atividade de %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Atribuir uma cor quando a tarefa for movida para uma raia especifica', + 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuir uma prioridade quando a tarefa for movida para uma raia especifica', + 'User unlocked successfully.' => 'Usuário desbloqueado com sucesso.', + 'Unable to unlock the user.' => 'Não foi possível desbloquear o usuário.', + 'Move a task to another swimlane' => 'Mover a tarefa para outra raia', + '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 soltar', + '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 período', + '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 possível 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 possível 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 possível 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 possível 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 possível 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 possível 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 soltar 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 poderão mover apenas tarefas entre essas 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.' => 'Você não está autorizado a mover esta tarefa.', + 'API User Access' => 'Acesso à API do usuário', + 'Preview' => 'Pré-visualizar', + 'Write' => 'Escrever', + 'Write your text in Markdown' => 'Escreva seu texto em Markdown', + 'No personal API access token registered.' => 'Nenhum token de acesso pessoal à API registrado.', + 'Your personal API access token is "%s"' => 'Seu token de acesso pessoal à API é "%s"', + 'Remove your token' => 'Remover seu token', + 'Generate a new token' => 'Gerar um novo token', + 'Showing %d-%d of %d' => 'Mostrando %d-%d de %d', + 'Outgoing Emails' => 'E-mails de saída', + 'Add or change currency rate' => 'Adicionar ou alterar taxa de câmbio', + 'Reference currency: %s' => 'Câmbio de referência: %s', + 'Add custom filters' => 'Adicionar filtros personalizados', + 'Export' => 'Exportar', + 'Add link label' => 'Adicionar rótulo de link', + 'Incompatible Plugins' => 'Plugin incompatível', + 'Compatibility' => 'Compatibilidade', + 'Permissions and ownership' => 'Permissões e propriedade', + 'Priorities' => 'Propriedades', + 'Close this window' => 'Fechar esta janela', + 'Unable to upload this file.' => 'Não é possível carregar este arquivo', + 'Import tasks' => 'Importar tarefas', + 'Choose a project' => 'Escolher um projeto', + 'Profile' => 'Perfil', + 'Application role' => 'Regra de 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 usuário.', + 'Kanboard Invitation' => 'Convite do Kanboard', + 'Visible on dashboard' => 'Visível no painel de controle', + 'Created at:' => 'Criado em:', + 'Updated at:' => 'Atualizado em:', + 'There is no custom filter.' => 'Não existe nenhum filtro personalizado.', + 'New User' => 'Novo Usuário', + 'Authentication' => 'Autenticação', + 'If checked, this user will use a third-party system for authentication.' => 'Se marcado, este usuário usará um sistema de autenticação de terceiros.', + 'The password is necessary only for local users.' => 'A senha é necessária somente para usuários locais.', + 'You have been invited to register on Kanboard.' => 'Você foi convidado a se registrar no Kanboard', + 'Click here to join your team' => 'Clique aqui para se unir ao seu time', + 'Invite people' => 'Convidar pessoas', + 'Emails' => 'E-mails', + 'Enter one email address by line.' => 'Digite um e-mail por linha. ', + 'Add these people to this project' => 'Adicionar estas pessoas para este projeto', + 'Add this person to this project' => 'Adicionar esta pessoa para este projeto', + 'Sign-up' => 'Inscrever-se', + 'Credentials' => 'Credenciais', + 'New user' => 'Novo usuário', + 'This username is already taken' => 'Este nome de usuário já está em uso', + 'Your profile must have a valid email address.' => 'Seu perfil precisa ter um endereço de e-mail válido.', + 'TRL - Turkish Lira' => 'TRL - Lira turca', + 'The project email is optional and could be used by several plugins.' => 'O e-mail do projeto é opcional e pode ser usado por diversos plugins.', + 'The project email must be unique across all projects' => 'O e-mail do projeto deve ser exclusivo em relação aos demais os projetos', + 'The email configuration has been disabled by the administrator.' => 'A configuração de e-mail foi desabilitada pelo administrador.', + 'Close this project' => 'Fechar este projeto', + 'Open this project' => 'Abrir este projeto', + 'Close a project' => 'Fechar um projeto', + 'Do you really want to close this project: "%s"?' => 'Deseja realmente fechar este projeto: "%s"?', + 'Reopen a project' => 'Reabrir um projeto', + 'Do you really want to reopen this project: "%s"?' => 'Deseja realmente reabrir este projeto: "%s"?', + 'This project is open' => 'Este projeto está aberto', + 'This project is closed' => 'Este projeto está fechado', + 'Unable to upload files, check the permissions of your data folder.' => 'Incapaz de enviar arquivos. Verifique as permissões da sua pasta de dados.', + 'Another category with the same name exists in this project' => 'Outra categoria com o mesmo nome existe neste projeto', + 'Comment sent by email successfully.' => 'Comentário enviado por e-mail com sucesso.', + 'Sent by email to "%s" (%s)' => 'Enviar por e-mail para "%s" (%s)', + 'Unable to read uploaded file.' => 'Incapaz de ler arquivos enviados.', + 'Database uploaded successfully.' => 'Base de dados enviado com sucesso.', + 'Task sent by email successfully.' => 'Tarefa enviada por e-mail com sucesso.', + 'There is no category in this project.' => 'Não há categoria neste projeto.', + 'Send by email' => 'Enviar por e-mail', + 'Create and send a comment by email' => 'Criar e enviar um comentário por e-mail', + 'Subject' => 'Assunto', + 'Upload the database' => 'Enviar uma base de dados', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Você pode enviar uma base de dados Sqlite baixada anteriormente (formato Gzip).', + 'Database file' => 'Arquivo de base de dados', + 'Upload' => 'Enviar', + 'Your project must have at least one active swimlane.' => 'Seu projeto precisa ter pelo menos uma raia ativa.', + 'Project: %s' => 'Projeto: %s', + 'Automatic action not found: "%s"' => 'Ação automática não encontrada: "%s"', + '%d projects' => '%d projetos', + '%d project' => '%d projeto', + 'There is no project.' => 'Não há projeto.', + 'Sort' => 'Ordenar', + 'Project ID' => 'ID do projeto', + 'Project name' => 'Nome do projeto', + 'Public' => 'Público', + 'Personal' => 'Privado', + '%d tasks' => '%d tarefas', + '%d task' => '%d tarefa', + 'Task ID' => 'ID da tarefa', + 'Assign automatically a color when due date is expired' => 'Atribuir automaticamente uma cor quando a data de vencimento está expirada', + 'Total score in this column across all swimlanes' => 'Pontuação total nesta coluna através de todas as raias', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso argentino', + 'COP - Colombian Peso' => 'COP - Peso colombiano', + '%d groups' => '%d grupos', + '%d group' => '%d grupo', + 'Group ID' => 'ID do grupo', + 'External ID' => 'ID externo', + '%d users' => '%d usuários', + '%d user' => '%d usuário', + 'Hide subtasks' => 'Esconder subtarefa', + 'Show subtasks' => 'Mostrar subtarefa', + 'Authentication Parameters' => 'Parâmetros de autenticação', + 'API Access' => 'Acesso à API', + 'No users found.' => 'Usuários não encontrados.', + 'User ID' => 'ID do usuário', + 'Notifications are activated' => 'As notificações estão ativadas', + 'Notifications are disabled' => 'As notificações estão desativadas', + 'User disabled' => 'Usuário desativado', + '%d notifications' => '%d notificações', + '%d notification' => '%d notificação', + 'There is no external integration installed.' => 'Não existe integração externa instalada.', + 'You are not allowed to update tasks assigned to someone else.' => 'Você não está autorizado a atualizar tarefas designadas para outros.', + 'You are not allowed to change the assignee.' => 'Você não está autorizado a mudar a designação.', + 'Task suppression is not permitted' => 'Supressão de tarefa não é permitida', + 'Changing assignee is not permitted' => 'Mudança de designação não é permitida', + 'Update only assigned tasks is permitted' => 'Atualizar somente tarefas designadas é permitida', + 'Only for tasks assigned to the current user' => 'Somente para tarefas designadas para o usuário atual', + 'My projects' => 'Meus projetos', + 'You are not a member of any project.' => 'Você não é membro de nenhum projeto.', + 'My subtasks' => 'Minhas subtarefas', + '%d subtasks' => '%d subtarefas', + '%d subtask' => '%d subtarefa', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Somente movimentação de tarefas entre aquelas colunas são permitidas para tarefas designadas para o usuário atual', + '[DUPLICATE]' => '[DUPLICADO]', + 'DKK - Danish Krona' => 'DKK - Coroa Dinamarquesa', + 'Remove user from group' => 'Remover usuário do grupo', + 'Assign the task to its creator' => 'Atribuir a tarefa ao seu criador', + 'This task was sent by email to "%s" with subject "%s".' => 'Esta tarefa foi enviada por e-mail para "%s" com o assunto "%s".', + 'Predefined Email Subjects' => 'Assuntos predefinidos de e-mail', + 'Write one subject by line.' => 'Escreva um assunto por linha.', + 'Create another link' => 'Criar outro link', + 'BRL - Brazilian Real' => 'BRL - Real Brasileiro', + 'Add a new Kanboard task' => 'Adicionar uma nova tarefa do Kanboard', + 'Subtask not started' => 'Subtarefa não iniciada', + 'Subtask currently in progress' => 'Subtarefa atualmente em progresso', + 'Subtask completed' => 'Subtarefa finalizada', + 'Subtask added successfully.' => 'Subtarefa adicionada com sucesso.', + '%d subtasks added successfully.' => '%d subtarefas adicionadas com sucesso.', + 'Enter one subtask by line.' => 'Escreva uma subtarefa por linha.', + 'Predefined Contents' => 'Conteúdos predefinidos', + 'Predefined contents' => 'Conteúdos predefinidos', + 'Predefined Task Description' => 'Descrição de tarefa pré-definida', + 'Do you really want to remove this template? "%s"' => 'Deseja realmente remover este modelo? "%s"', + 'Add predefined task description' => 'Adicionar descrição de tarefa pré-definida', + 'Predefined Task Descriptions' => 'Descrições de tarefas pré-definidas', + 'Template created successfully.' => 'Modelo criado com sucesso.', + 'Unable to create this template.' => 'Incapaz de criar este modelo.', + 'Template updated successfully.' => 'Modelo atualizado com sucesso.', + 'Unable to update this template.' => 'Incapaz de atualizar este modelo.', + 'Template removed successfully.' => 'Modelo removido com sucesso.', + 'Unable to remove this template.' => 'Incapaz de remover este modelo.', + 'Template for the task description' => 'Modelo para a descrição de tarefa', + 'The start date is greater than the end date' => 'A data de início é maior que a data final', + 'Tags must be separated by a comma' => 'As tags precisam ser separadas por vírgula', + 'Only the task title is required' => 'Somente o título da tarefa é requerida', + 'Creator Username' => 'Usuário criador', + 'Color Name' => 'Nome da cor', + 'Column Name' => 'Nome da coluna', + 'Swimlane Name' => 'Nome da raia', + 'Time Estimated' => 'Tempo estimado', + 'Time Spent' => 'Tempo gasto', + 'External Link' => 'Link externo', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Essa funcionalidade habilita o alimentador de iCal, RSS e visualização pública do quadro.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Parar o temporizador de todas as subtarefas quando mover a tarefa para outra coluna', + 'Subtask Title' => 'Título da subtarefa', + 'Add a subtask and activate the timer when moving a task to another column' => 'Adicionar uma subtarefa e ativar o temporizador quando mover a tarefa para outra coluna', + 'days' => 'dias', + 'minutes' => 'minutos', + 'seconds' => 'segundos', + 'Assign automatically a color when preset start date is reached' => 'Atribuir automaticamente uma cor quando a data de início for atingida', + 'Move the task to another column once a predefined start date is reached' => 'Mover a tarefa para outra coluna quando a data de início for atingida', + 'This task is now linked to the task %s with the relation "%s"' => 'Esta tarefa agora está vinculada à tarefa %s com a relação "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'O vínculo com a relação "%s" para a tarefa %s foi removido', + 'Custom Filter:' => 'Filtro personalizado:', + 'Unable to find this group.' => 'Incapaz de encontrar este grupo.', + '%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 moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa #%d para a raia "%s"', + '%sh spent' => '%sh gasto', + '%sh estimated' => '%sh estimado', + 'Select All' => 'Selecionar todos', + 'Unselect All' => 'Desmarcar todos', + 'Apply action' => 'Aplicar ação', + 'Move selected tasks to another column or swimlane' => 'Mover tarefas selecionadas para outra coluna', + 'Edit tasks in bulk' => 'Editar tarefas em massa', + 'Choose the properties that you would like to change for the selected tasks.' => 'Escolher as propriedades que deseja mudar para as tarefas selecionadas.', + 'Configure this project' => 'Configurar este projeto', + 'Start now' => 'Iniciar agora', + '%s removed a file from the task #%d' => '%s removeu um arquivo da tarefa #%d', + 'Attachment removed from task #%d: %s' => 'Anexo removido da tarefa #%d: %s', + 'No color' => 'Nenhuma cor', + 'Attachment removed "%s"' => 'Anexo removido "%s"', + '%s removed a file from the task %s' => '%s removeu um arquivo da tarefa %s', + 'Move the task to another swimlane when assigned to a user' => 'Mover a tarefa para uma outra raia quando esta está atribuída a um usuário', + 'Destination swimlane' => 'Raia de destino', + 'Assign a category when the task is moved to a specific swimlane' => 'Atribuir uma categoria quando a tarefa for movida para uma raia específica', + 'Move the task to another swimlane when the category is changed' => 'Mover a tarefa para outra raia quando a categoria é alterada', + 'Reorder this column by priority (ASC)' => 'Reordenar esta coluna por prioridade (crescente)', + 'Reorder this column by priority (DESC)' => 'Reordenar esta coluna por prioridade (decrescente)', + 'Reorder this column by assignee and priority (ASC)' => 'Reordenar esta coluna por designado e prioridade (crescente)', + 'Reorder this column by assignee and priority (DESC)' => 'Reordenar esta coluna por designado e prioridade (decrescente)', + 'Reorder this column by assignee (A-Z)' => 'Reordenar esta coluna por designado (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Reordenar esta coluna por designado (Z-A)', + 'Reorder this column by due date (ASC)' => 'Reordenar esta coluna por data fim estimada (crescente)', + 'Reorder this column by due date (DESC)' => 'Reordenar esta coluna por data fim estimada (decrescente)', + 'Reorder this column by id (ASC)' => 'Reordenar esta coluna por id (ASC)', + 'Reorder this column by id (DESC)' => 'Reordenar esta coluna por id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s moveu a tarefa #%d "%s" para o projeto "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Tarefa #%d "%s" foi movida para o projeto "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Mover a tarefa para outra coluna quando a data fim estimada for menor que uma quantidade de dias', + 'Automatically update the start date when the task is moved away from a specific column' => 'Atualizar automaticamente a data de início quando a tarefa sair de determinada coluna', + 'HTTP Client:' => 'Cliente HTTP:', + 'Assigned' => 'Atribuído', + 'Task limits apply to each swimlane individually' => 'Limites de tarefas aplicam-se a cada raia individualmente', + 'Column task limits apply to each swimlane individually' => 'Limites de tarefas da coluna aplicam-se a cada raia individualmente', + 'Column task limits are applied to each swimlane individually' => 'Limites de tarefas da coluna estão aplicados a cada raia individualmente', + 'Column task limits are applied across swimlanes' => 'Limites de tarefas da coluna estão aplicados por todas as raias', + 'Task limit: ' => 'Limite de tarefas:', + 'Change to global tag' => 'Transformar em etiqueta global', + 'Do you really want to make the tag "%s" global?' => 'Você realmente deseja transformar a etiqueta "%s" em etiqueta global?', + 'Enable global tags for this project' => 'Habilitar etiquetas globais para este projeto', + 'Group membership(s):' => 'Filiação a grupo(s):', + '%s is a member of the following group(s): %s' => '%s é um membro do(s) seguinte(s) grupo(s): %s', + '%d/%d group(s) shown' => '%d/%d grupo(s) exibidos', + 'Subtask creation or modification' => 'Criação ou edição de subtarefas', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Atribuir a tarefa a um usuário específico quando a tarefa for movida para uma raia especifica', + 'Comment' => 'Comentário', + 'Collapse vertically' => 'Contrair verticalmente', + 'Expand vertically' => 'Expandir verticalmente', + 'MXN - Mexican Peso' => 'MXN - Peso mexicano', + 'Estimated vs actual time per column' => 'Tempo estimado vs real por coluna', + 'HUF - Hungarian Forint' => 'HUF - Forint húngaro', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Você deve selecionar um arquivo para enviar como seu avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'O arquivo enviado não é uma imagem válida! (Apenas *.gif, *.jpg, *.jpeg e *.png são permitidos!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Definir automaticamente a data de vencimento quando a tarefa for movida de uma coluna específica', + 'No other projects found.' => 'Nenhum outro projeto encontrado.', + 'Tasks copied successfully.' => 'Tarefas copiadas com sucesso.', + 'Unable to copy tasks.' => 'Não foi possível copiar as tarefas.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema claro', + 'Dark theme' => 'Tema escuro', + 'Automatic theme - Sync with system' => 'Tema automático - Sincronizar com o sistema', + 'Application managers or more' => 'Gerentes de aplicação ou mais', + 'Administrators' => 'Administradores', + 'Visibility:' => 'Visibilidade:', + 'Standard users' => 'Usuários padrão', + 'Visibility is required' => 'A visibilidade é obrigatória', + 'The visibility should be an app role' => 'A visibilidade deve ser um papel do aplicativo', + 'Reply' => 'Responder', + '%s wrote: ' => '%s escreveu: ', + 'Number of visible tasks in this column and swimlane' => 'Número de tarefas visíveis nesta coluna e raia', + 'Number of tasks in this swimlane' => 'Número de tarefas nesta raia', + 'Unable to find another subtask in progress, you can close this window.' => 'Não foi possível encontrar outra subtarefa em andamento, você pode fechar esta janela.', + 'This theme is invalid' => 'Este tema é inválido', + 'This role is invalid' => 'Este papel é inválido', + 'This timezone is invalid' => 'Este fuso horário é inválido', + 'This language is invalid' => 'Este idioma é inválido', + 'This URL is invalid' => 'Esta URL é inválida', + 'Date format invalid' => 'Formato de data inválido', + 'Time format invalid' => 'Formato de hora inválido', + 'Invalid Mail transport' => 'Transporte de e-mail inválido', + 'Color invalid' => 'Cor inválida', + 'This value must be greater or equal to %d' => 'Este valor deve ser maior ou igual a %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Adicione um BOM no início do arquivo (necessário para o Microsoft Excel)', + 'Just add these tag(s)' => 'Adicione apenas estas etiquetas', + 'Remove internal link(s)' => 'Remova os links internos', + 'Import tasks from another project' => 'Importe tarefas de outro projeto', + 'Select the project to copy tasks from' => 'Selecione o projeto do qual deseja copiar tarefas', + 'The total maximum allowed attachments size is %sB.' => 'O tamanho máximo total permitido para anexos é %sB.', + 'Add attachments' => 'Adicionar anexos', + 'Task #%d "%s" is overdue' => 'Tarefa #%d "%s" está atrasada', + 'Enable notifications by default for all new users' => 'Habilitar notificações por padrão para todos os novos usuários', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Atribuir a tarefa ao seu criador para colunas específicas se nenhum responsável for definido manualmente', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Atribuir a tarefa ao usuário logado ao mudar de coluna para a coluna especificada se nenhum usuário estiver atribuído', +]; diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php new file mode 100644 index 0000000..3cb955c --- /dev/null +++ b/app/Locale/pt_PT/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Nenhum', + 'Edit' => 'Editar', + 'Remove' => 'Remover', + 'Yes' => 'Sim', + 'No' => 'Não', + 'cancel' => 'cancelar', + 'or' => 'ou', + 'Yellow' => 'Amarelo', + 'Blue' => 'Azul', + 'Green' => 'Verde', + 'Purple' => 'Roxo', + 'Red' => 'Vermelho', + 'Orange' => 'Laranja', + 'Grey' => 'Cinza', + 'Brown' => 'Castanho', + 'Deep Orange' => 'Laranja escuro', + 'Dark Grey' => 'Cinza escuro', + 'Pink' => 'Rosa', + 'Teal' => 'Turquesa', + 'Cyan' => 'Azul intenso', + 'Lime' => 'Verde limão', + 'Light Green' => 'Verde claro', + 'Amber' => 'Âmbar', + 'Save' => 'Guardar', + 'Login' => 'Login', + 'Official website:' => 'Site oficial:', + 'Unassigned' => 'Não Atribuída', + 'View this task' => 'Ver esta tarefa', + 'Remove user' => 'Remover utilizador', + 'Do you really want to remove this user: "%s"?' => 'Pretende mesmo remover este utilizador: "%s"?', + 'All users' => 'Todos os utilizadores', + 'Username' => 'Nome de utilizador', + 'Password' => 'Senha', + 'Administrator' => 'Administrador', + 'Sign in' => 'Entrar', + 'Users' => 'Utilizadores', + 'Forbidden' => 'Proibido', + 'Access Forbidden' => 'Acesso negado', + 'Edit user' => 'Editar utilizador', + 'Logout' => 'Sair', + 'Bad username or password' => 'Utilizador ou senha inválidos', + 'Edit project' => 'Editar projeto', + 'Name' => 'Nome', + 'Projects' => 'Projetos', + 'No project' => 'Nenhum projeto', + 'Project' => 'Projeto', + 'Status' => 'Estado', + 'Tasks' => 'Tarefas', + 'Board' => 'Quadro', + 'Actions' => 'Acções', + 'Inactive' => 'Inactivo', + 'Active' => 'Activo', + 'Unable to update this board.' => 'Não foi possível actualizar este quadro.', + 'Disable' => 'Desactivar', + 'Enable' => 'Activar', + '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"', + 'Add a new column' => 'Adicionar uma nova coluna', + 'Title' => 'Título', + 'Assigned to %s' => 'Designado para %s', + 'Remove a column' => 'Remover uma coluna', + '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"?', + 'Settings' => 'Configurações', + 'Application settings' => 'Configurações da aplicação', + 'Language' => 'Idioma', + 'Webhook token:' => 'Token de webhooks:', + 'API token:' => 'API Token:', + 'Database size:' => 'Tamanho da base de dados:', + 'Download the database' => 'Download da base de dados', + 'Optimize the database' => 'Otimizar a base de dados', + '(VACUUM command)' => '(Comando VACUUM)', + '(Gzip compressed Sqlite file)' => '(Arquivo Sqlite comprimido com Gzip)', + 'Close a task' => 'Finalizar uma tarefa', + 'Column' => 'Coluna', + 'Color' => 'Cor', + 'Assignee' => 'Assignado', + 'Create another task' => 'Criar outra tarefa', + 'New task' => 'Nova tarefa', + 'Open a task' => 'Abrir uma tarefa', + 'Do you really want to open this task: "%s"?' => 'Tem a certeza que quer abrir esta tarefa: "%s"?', + 'Back to the board' => 'Voltar ao quadro', + 'There is nobody assigned' => 'Não há ninguém assignado', + 'Column on the board:' => 'Coluna no quadro:', + 'Close this task' => 'Finalizar esta tarefa', + 'Open this task' => 'Abrir esta tarefa', + 'There is no description.' => 'Não há descrição.', + 'Add a new task' => 'Adicionar uma nova tarefa', + 'The username is required' => 'O nome de utilizador é obrigatório', + 'The maximum length is %d characters' => 'O tamanho máximo é %d caracteres', + 'The minimum length is %d characters' => 'O tamanho mínimo é %d caracteres', + 'The password is required' => 'A senha é obrigatória', + 'This value must be an integer' => 'O valor deve ser um número inteiro', + 'The username must be unique' => 'O nome de utilizador deve ser único', + '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 projeto é obrigatório', + 'The id is required' => 'O ID é 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 has 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.', + 'Task closed successfully.' => 'Tarefa finalizada com sucesso.', + 'Unable to update your task.' => 'Não é possível actualizar a sua tarefa.', + 'Task updated successfully.' => 'Tarefa actualizada com sucesso.', + 'Unable to create your task.' => 'Não é possível criar a sua tarefa.', + 'Task created successfully.' => 'Tarefa criada com sucesso.', + 'User created successfully.' => 'Utilizador criado com sucesso.', + 'Unable to create your user.' => 'Não é possível criar o seu Utilizador.', + 'User updated successfully.' => 'Utilizador actualizado com sucesso.', + 'User removed successfully.' => 'Utilizador removido com sucesso.', + 'Unable to remove this user.' => 'Não é possível remover este Utilizador.', + 'Board updated successfully.' => 'Quadro actualizado com sucesso.', + 'Ready' => 'Pronto', + 'Backlog' => 'Backlog', + 'Work in progress' => 'Em andamento', + 'Done' => 'Finalizado', + 'Application version:' => 'Versão da aplicação:', + 'Id' => 'Id', + '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!', + 'Page not found' => 'Página não encontrada', + 'Complexity' => 'Complexidade', + 'Task limit' => 'Limite da tarefa', + 'Task count' => 'Número de tarefas', + 'User' => 'Utilizador', + 'Comments' => 'Comentários', + 'Comment is required' => 'Comentário é obrigatório', + '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', + 'Invalid date' => 'Data inválida', + 'Automatic actions' => 'Acções automáticas', + 'Your automatic action has been created successfully.' => 'A sua acção automática foi criada com sucesso.', + 'Unable to create your automatic action.' => 'Não é possível criar a sua acção automática.', + '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 projeto "%s"', + 'Add an action' => 'Adicionar Acção', + 'Event name' => 'Nome do evento', + 'Action' => 'Acção', + 'Event' => 'Evento', + 'When the selected event occurs execute the corresponding action.' => 'Quando o evento selecionado ocorrer execute a acção correspondente.', + 'Next step' => 'Próximo passo', + 'Define action parameters' => 'Definir parêmetros da acção', + 'Do you really want to remove this action: "%s"?' => 'Tem a certeza que quer remover esta acção: "%s"?', + '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 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', + 'Closing a task' => 'A finalizar uma tarefa', + 'Assign a color to a specific user' => 'Designar uma cor para um utilizador específico', + 'Position' => 'Posição', + 'Duplicate to project' => 'Duplicar para outro projeto', + 'Duplicate' => 'Duplicar', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Comentário actualizado com sucesso.', + 'Unable to update your comment.' => 'Não é possível actualizar o seu comentário.', + 'Remove a comment' => 'Remover um comentário', + 'Comment removed successfully.' => 'Comentário removido com sucesso.', + 'Unable to remove this comment.' => 'Não é possível remover este comentário.', + 'Do you really want to remove this comment?' => 'Tem a certeza que quer remover este comentário?', + 'Current password for the user "%s"' => 'Senha atual para o utilizador "%s"', + 'The current password is required' => 'A senha atual é obrigatória', + 'Wrong password' => 'Senha incorreta', + 'Unknown' => 'Desconhecido', + 'Last logins' => 'Últimos logins', + 'Login date' => 'Data de login', + 'Authentication method' => 'Método de autenticação', + 'IP address' => 'Endereço IP', + 'User agent' => 'User Agent', + 'Persistent connections' => 'Conexões persistentes', + 'No session.' => 'Nenhuma sessão.', + 'Expiration date' => 'Data de expiração', + 'Remember Me' => 'Lembre-se de mim', + 'Creation date' => 'Data de criação', + 'Everybody' => 'Todos', + 'Open' => 'Aberto', + 'Closed' => 'Finalizado', + 'Search' => 'Pesquisar', + 'Nothing found.' => 'Nada encontrado.', + 'Due date' => 'Data de vencimento', + 'Description' => 'Descrição', + '%d comments' => '%d comentários', + '%d comment' => '%d comentário', + 'Email address invalid' => 'Endereço de e-mail inválido', + 'Your external account is not linked anymore to your profile.' => 'A sua conta externa já não se encontra vinculada a este perfil', + 'Unable to unlink your external account.' => 'Não foi possivel desvincular a sua conta externa', + 'External authentication failed' => 'Autenticação externa falhou', + 'Your external account is linked to your profile successfully.' => 'A sua conta externa foi vinculada com sucesso ao seu perfil', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Tarefa removida com sucesso.', + 'Unable to remove this task.' => 'Não foi possível remover esta tarefa.', + 'Remove a task' => 'Remover uma tarefa', + 'Do you really want to remove this task: "%s"?' => 'Tem a certeza que quer remover esta tarefa: "%s"', + 'Assign automatically a color based on a category' => 'Atribuir automaticamente uma cor com base numa categoria', + 'Assign automatically a category based on a color' => 'Atribuir automaticamente uma categoria com base numa cor', + 'Task creation or modification' => 'Criação ou modificação de tarefa', + 'Category' => 'Categoria', + 'Category:' => 'Categoria:', + 'Categories' => 'Categorias', + 'Your category has been created successfully.' => 'A sua categoria foi criada com sucesso.', + 'This category has been updated successfully.' => 'A sua categoria foi actualizada com sucesso.', + 'Unable to update this category.' => 'Não foi possível actualizar a sua categoria.', + '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 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"', + 'All categories' => 'Todas as categorias', + 'No category' => 'Nenhuma categoria', + 'The name is required' => 'O nome é obrigatório', + 'Remove a file' => 'Remover um arquivo', + 'Unable to remove this file.' => 'Não foi possível remover este arquivo.', + 'File removed successfully.' => 'Arquivo removido com sucesso.', + 'Attach a document' => 'Anexar um documento', + 'Do you really want to remove this file: "%s"?' => 'Tem a certeza que quer remover este arquivo: "%s"', + 'Attachments' => 'Anexos', + 'Edit the task' => 'Editar a tarefa', + 'Add a comment' => 'Adicionar um comentário', + 'Edit a comment' => 'Editar um comentário', + 'Summary' => 'Resumo', + 'Time tracking' => 'Rastreamento de tempo', + 'Estimate:' => 'Estimado:', + 'Spent:' => 'Gasto:', + 'Do you really want to remove this sub-task?' => 'Tem a certeza que quer remover esta subtarefa?', + 'Remaining:' => 'Restante:', + 'hours' => 'horas', + 'estimated' => 'estimado', + 'Sub-Tasks' => 'Subtarefas', + 'Add a sub-task' => 'Adicionar uma subtarefa', + 'Original estimate' => 'Estimativa original', + 'Create another sub-task' => 'Criar uma outra subtarefa', + 'Time spent' => 'Tempo gasto', + 'Edit a sub-task' => 'Editar uma subtarefa', + 'Remove a sub-task' => 'Remover uma subtarefa', + 'The time must be a numeric value' => 'O tempo deve ser um valor numérico', + 'Todo' => 'A fazer', + 'In progress' => 'Em andamento', + 'Sub-task removed successfully.' => 'Subtarefa removida com sucesso.', + 'Unable to remove this sub-task.' => 'Não foi possível remover esta subtarefa.', + 'Sub-task updated successfully.' => 'Subtarefa atualizada com sucesso.', + 'Unable to update your sub-task.' => 'Não foi possível atualizar a sua subtarefa.', + 'Unable to create your sub-task.' => 'Não é possível criar a sua subtarefa.', + 'Maximum size: ' => 'Tamanho máximo: ', + 'Display another project' => 'Mostrar outro projeto', + 'Created by %s' => 'Criado por %s', + 'Tasks Export' => 'Exportar Tarefas', + 'Start Date' => 'Data inicial', + '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.' => '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 has been opened.' => 'A tarefa #%d foi aberta.', + 'The task #%d has been closed.' => 'A tarefa #%d foi finalizada.', + 'Sub-task updated' => 'Subtarefa atualizada', + 'Title:' => 'Título:', + 'Status:' => 'Estado:', + 'Assignee:' => 'Assignado:', + 'Time tracking:' => 'Controle de tempo:', + 'New sub-task' => 'Nova subtarefa', + 'New attachment added "%s"' => 'Novo anexo adicionado "%s"', + 'New comment posted by %s' => 'Novo comentário por %s', + 'New comment' => 'Novo comentário', + 'Comment updated' => 'Comentário actualizado', + 'New subtask' => 'Nova subtarefa', + 'I only want to receive notifications for these projects:' => 'Quero receber notificações apenas destes projetos:', + 'view the task on Kanboard' => 'ver a tarefa no Kanboard', + 'Public access' => 'Acesso público', + 'Disable public access' => 'Desactivar o acesso público', + 'Enable public access' => 'Activar o acesso público', + 'Public access disabled' => 'Acesso público desactivado', + 'Move the task to another project' => 'Mover a tarefa para outro projeto', + 'Move to 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', + 'Account type' => 'Tipo de conta', + 'Local' => 'Local', + 'Remote' => 'Remoto', + 'Enabled' => 'Activado', + 'Disabled' => 'Desactivado', + 'Login:' => 'Utilizador:', + 'Full Name:' => 'Nome:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Notificações:', + 'Notifications' => 'Notificações', + 'Account type:' => 'Tipo de conta:', + 'Edit profile' => 'Editar perfil', + 'Change password' => 'Alterar senha', + 'Password modification' => 'Alteração de senha', + 'External authentications' => 'Autenticação externa', + 'Never connected.' => 'Nunca conectado.', + 'No external authentication enabled.' => 'Nenhuma autenticação externa activa.', + 'Password modified successfully.' => 'Senha alterada com sucesso.', + 'Unable to change the password.' => 'Não foi possível alterar a senha.', + 'Change category' => 'Mudar categoria', + '%s updated the task %s' => '%s actualizou a tarefa %s', + '%s opened the task %s' => '%s abriu a tarefa %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s moveu a tarefa %s para a posição #%d na coluna "%s"', + '%s moved the task %s to the column "%s"' => '%s moveu a tarefa %s para a coluna "%s"', + '%s created the task %s' => '%s criou a tarefa %s', + '%s closed the task %s' => '%s finalizou a tarefa %s', + '%s created a subtask for the task %s' => '%s criou uma subtarefa para a tarefa %s', + '%s updated a subtask for the task %s' => '%s actualizou uma subtarefa da tarefa %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Designado para %s com tempo estimado de %s/%sh', + '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 %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', + '%s updated a subtask for the task #%d' => '%s actualizou uma subtarefa para a tarefa #%d', + '%s created a subtask for the task #%d' => '%s criou uma subtarefa para a tarefa #%d', + '%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 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 projetos (Separado por vírgula)', + 'Task assignee change' => 'Mudar assignação da tarefa', + '%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', + 'Create a task from an external provider' => 'Criar uma tarefa por meio de um serviço externo', + 'Change the assignee based on an external username' => 'Alterar assignação com base num utilizador externo', + 'Change the category based on an external label' => 'Alterar categoria com base num rótulo externo', + 'Reference' => 'Referência', + 'Label' => 'Rótulo', + 'Database' => 'Base de dados', + 'About' => 'Sobre', + 'Database driver:' => 'Driver da base de dados:', + 'Board settings' => 'Configurações do Quadro', + 'Webhook settings' => 'Configurações do Webhook', + 'Reset token' => 'Redefinir token', + 'API endpoint:' => 'API endpoint:', + 'Refresh interval for personal board' => 'Intervalo de actualização para um quadro privado', + 'Refresh interval for public board' => 'Intervalo de actualização para um quadro público', + 'Task highlight period' => 'Período de Tarefa em destaque', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Período (em segundos) para considerar que uma tarefa foi modificada recentemente (0 para desactivar, 2 dias por defeito)', + 'Frequency in second (60 seconds by default)' => 'Frequência em segundos (60 segundos por defeito)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frequência em segundos (0 para desactivar este recurso, 10 segundos por defeito)', + 'Application URL' => 'URL da Aplicação', + '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 personal project' => 'Novo projeto pessoal', + 'This project is personal' => 'Este projeto é pessoal', + 'Add' => 'Adicionar', + 'Start date' => 'Data de início', + 'Time estimated' => 'Tempo estimado', + 'There is nothing assigned to you.' => 'Não há nada assignado a si.', + 'My tasks' => 'As minhas tarefas', + 'Activity stream' => 'Atividades Recentes', + 'Dashboard' => 'Painel de Controlo', + 'Confirmation' => 'Confirmação', + '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 projetos', + 'Columns' => 'Colunas', + 'Task' => 'Tarefas', + 'Percentage' => 'Percentagem', + 'Number of tasks' => 'Número de tarefas', + 'Task distribution' => 'Distribuição de tarefas', + 'Analytics' => 'Estatísticas', + 'Subtask' => 'Subtarefa', + 'User repartition' => 'Redistribuição de utilizador', + '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 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', + 'The task id is required' => 'O ID da tarefa é obrigatório', + 'The task id must be an integer' => 'O ID da tarefa deve ser um número inteiro', + 'The user id must be an integer' => 'O ID do utilizador deve ser um número inteiro', + 'This value is required' => 'Este valor é obrigatório', + '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', + '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', + 'Add a new swimlane' => 'Adicionar novo swimlane', + 'Default swimlane' => 'Swimlane padrão', + 'Do you really want to remove this swimlane: "%s"?' => 'Tem a certeza que quer remover este swimlane: "%s"?', + 'Inactive swimlanes' => 'Desactivar swimlanes', + 'Remove a swimlane' => 'Remover um swimlane', + '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.', + 'Unable to remove this swimlane.' => 'Não foi possível remover este swimlane.', + 'Unable to update this swimlane.' => 'Não foi possível atualizar este swimlane.', + 'Your swimlane has 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 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', + 'Task Title' => 'Título da Tarefa', + 'Untitled' => 'Sem título', + 'Application default' => 'Aplicação padrão', + 'Language:' => 'Idioma:', + 'Timezone:' => 'Fuso horário:', + 'All columns' => 'Todas as colunas', + 'Next' => 'Próximo', + '#%d' => '#%d', + 'All swimlanes' => 'Todos os swimlane', + 'All colors' => 'Todas as cores', + 'Moved to column %s' => 'Mover para a coluna %s', + 'User dashboard' => 'Painel de Controlo do utilizador', + 'Allow only one subtask in progress at the same time for a user' => 'Permitir apenas uma subtarefa em andamento ao mesmo tempo para um utilizador', + 'Edit column "%s"' => 'Editar a coluna "%s"', + 'Select the new status of the subtask: "%s"' => 'Selecionar um novo estado para a subtarefa: "%s"', + 'Subtask timesheet' => 'Gestão de tempo das subtarefas', + '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 projeto que deseja duplicar?', + 'Disallow login form' => 'Desactivar login', + 'Start' => 'Inicio', + 'End' => 'Fim', + 'Task age in days' => 'Idade da tarefa em dias', + 'Days in this column' => 'Dias nesta coluna', + '%dd' => '%dd', + 'Add a new link' => 'Adicionar uma nova associação', + 'Do you really want to remove this link: "%s"?' => 'Tem a certeza que quer remover esta associação: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Tem a certeza que quer remover esta associação com a tarefa n°%d?', + 'Field required' => 'Campo requerido', + 'Link added successfully.' => 'Associação criada com sucesso.', + 'Link updated successfully.' => 'Associação atualizada com sucesso.', + 'Link removed successfully.' => 'Associação removida com sucesso.', + 'Link labels' => 'Etiquetas das associações', + 'Link modification' => 'Modificação de uma associação', + 'Opposite label' => 'Nome da etiqueta oposta', + 'Remove a link' => 'Remover uma associação', + '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' => 'é associada a', + 'blocks' => 'bloqueia', + 'is blocked by' => 'está bloqueada por', + 'duplicates' => 'duplica', + '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 corrigida por', + 'This task' => 'Esta tarefa', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Expandir tarefas', + 'Collapse tasks' => 'Contrair tarefas', + 'Expand/collapse tasks' => 'Expandir/Contrair tarefas', + 'Close dialog box' => 'Fechar a caixa de diálogo', + 'Submit a form' => 'Envia o formulário', + 'Board view' => 'Vista do Quadro', + 'Keyboard shortcuts' => 'Atalhos de teclado', + 'Open board switcher' => 'Abrir o comutador de painel', + 'Application' => 'Aplicação', + 'Compact view' => 'Vista reduzida', + 'Horizontal scrolling' => 'Deslocamento horizontal', + 'Compact/wide view' => 'Alternar entre a vista compacta e ampliada', + 'Currency' => 'Moeda', + 'Personal project' => 'Projeto pessoal', + 'AUD - Australian Dollar' => 'AUD - Dólar Australiano', + 'CAD - Canadian Dollar' => 'CAD - Dólar Canadense', + 'CHF - Swiss Francs' => 'CHF - Francos Suíços', + 'Custom Stylesheet' => 'Folha de estilos personalizada', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Libra Esterlina', + 'INR - Indian Rupee' => 'INR - Rúpia Indiana', + 'JPY - Japanese Yen' => 'JPY - Iene Japonês', + 'NZD - New Zealand Dollar' => 'NZD - Dólar Neozelandês', + 'PEN - Peruvian Sol' => 'PEN – Sol peruano', + 'RSD - Serbian dinar' => 'RSD - Dinar Sérvio', + 'CNY - Chinese Yuan' => 'CNY - Yuan Chinês', + 'USD - US Dollar' => 'USD - Dólar Norte-americano', + 'VES - Venezuelan Bolívar' => 'VES – Bolívar venezuelano', + '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', + 'Move the task to another column when assignee is cleared' => 'Mover a tarefa para uma outra coluna quando esta não está atribuída', + 'Source column' => 'Coluna de origem', + 'Transitions' => 'Transições', + 'Executer' => 'Executor(a)', + 'Time spent in the column' => 'Tempo gasto na coluna', + 'Task transitions' => 'Transições das tarefas', + 'Task transitions export' => 'Exportação das transições das tarefas', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Este relatório contém todos os movimentos de coluna para cada tarefa com a data, o utilizador e o tempo gasto para cada transição.', + 'Currency rates' => 'Taxas de câmbio das moedas estrangeiras', + 'Rate' => 'Taxa', + 'Change reference currency' => 'Mudar a moeda de referência', + 'Reference currency' => 'Moeda de Referência', + 'The currency rate has 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 removed the assignee of the task %s' => '%s removeu a pessoa assignada à tarefa %s', + 'Information' => 'Informações', + 'Check two factor authentication code' => 'Verificação do código de autenticação com factor duplo', + 'The two factor authentication code is not valid.' => 'O código de autenticação com factor duplo não é válido', + 'The two factor authentication code is valid.' => 'O código de autenticação com factor duplo é válido', + 'Code' => 'Código', + 'Two factor authentication' => 'Autenticação com factor duplo', + 'This QR code contains the key URI: ' => 'Este Código QR contém a chave URI: ', + 'Check my code' => 'Verificar o meu código', + 'Secret key: ' => 'Chave secreta: ', + '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' => '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', + 'Add a screenshot' => 'Adicionar uma Screenshot', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Tire um screenshot e pressione CTRL + V ou ⌘ + V para colar aqui.', + 'Screenshot uploaded successfully.' => 'Screenshot enviada com sucesso.', + 'SEK - Swedish Krona' => 'SEK - Coroa Sueca', + 'Identifier' => 'Identificador', + 'Disable two factor authentication' => 'Desactivar autenticação com dois factores', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Tem a certeza que quer desactivar a autenticação com dois factores para esse utilizador: "%s"?', + 'Edit link' => 'Editar um link', + 'Start to type task title...' => 'Escreva o título do trabalho...', + 'A task cannot be linked to itself' => 'Uma tarefa não pode ser ligada a si própria', + 'The exact same link already exists' => 'Um link idêntico jà existe', + 'Recurrent task is scheduled to be generated' => 'A tarefa recorrente está programada para ser criada', + 'Score' => 'Complexidade', + 'The identifier must be unique' => 'O identificador deve ser único', + 'This linked task id doesn\'t exists' => 'O identificador da tarefa associada não existe', + 'This value must be alphanumeric' => 'Este valor deve ser alfanumérico', + 'Edit recurrence' => 'Modificar a recorrência', + 'Generate recurrent task' => 'Gerar uma tarefa recorrente', + 'Trigger to generate recurrent task' => 'Activador para gerar tarefa recorrente', + 'Factor to calculate new due date' => 'Factor para o cálculo do nova data de vencimento', + 'Timeframe to calculate new due date' => 'Escala de tempo para o cálculo da nova data de vencimento', + 'Base date to calculate new due date' => 'Data a ser utilizada para calcular a nova data de vencimento', + 'Action date' => 'Data da acção', + 'Base date to calculate new due date: ' => 'Data a ser utilizada para calcular a nova data de vencimento: ', + 'This task has created this child task: ' => 'Esta tarefa criou a tarefa filha: ', + 'Day(s)' => 'Dia(s)', + 'Existing due date' => 'Data de vencimento existente', + 'Factor to calculate new due date: ' => 'Factor para calcular a nova data de vencimento: ', + 'Month(s)' => 'Mês(es)', + '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 de vencimento: ', + 'Trigger to generate recurrent task: ' => 'Activador para gerar tarefa recorrente: ', + 'When task is closed' => 'Quando a tarefa é fechada', + '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)', + 'Project settings' => 'Configurações dos projetos', + 'Automatically update the start date' => 'Actualizar automaticamente a data de início', + 'iCal feed' => 'Subscrição iCal', + 'Preferences' => 'Preferências', + 'Security' => 'Segurança', + '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 personal projects.' => 'Não há gestão de utilizadores para projetos pessoais.', + 'User that will receive the email' => 'O utilizador que vai receber o e-mail', + 'Email subject' => 'Assunto do e-mail', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'Adicionar um comentário de log quando uma tarefa é movida para uma outra coluna', + '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', + 'Notification' => 'Notificação', + '%s moved the task #%d to the first swimlane' => '%s moveu a tarefa n° %d no primeiro swimlane', + 'Swimlane' => 'Swimlane', + '%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 projeto "%s"', + 'view the board on Kanboard' => 'ver o painel no Kanboard', + 'The task has been moved to the first swimlane' => 'A tarefa foi movida para o primeiro Swimlane', + 'The task has been moved to another swimlane:' => 'A tarefa foi movida para outro Swimlane:', + 'New title: %s' => 'Novo título: %s', + 'The task is not assigned anymore' => 'Tarefa já não está atribuída', + 'New assignee: %s' => 'Novo assignado: %s', + 'There is no category now' => 'Já não existe categoria', + 'New category: %s' => 'Nova categoria: %s', + 'New color: %s' => 'Nova cor: %s', + 'New complexity: %d' => 'Nova complexidade: %d', + 'The due date has been removed' => 'A data de vencimento foi retirada', + 'There is no description anymore' => 'Já não há descrição', + 'Recurrence settings has been modified' => 'As configurações da recorrência foram modificadas', + 'Time spent changed: %sh' => 'O tempo despendido foi mudado: %sh', + 'Time estimated changed: %sh' => 'O tempo estimado foi mudado/ %sh', + 'The field "%s" has been updated' => 'O campo "%s" foi actualizada', + 'The description has been modified:' => 'A descrição foi modificada', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Tem a certeza que quer fechar a tarefa "%s" e todas as suas sub-tarefas?', + 'I want to receive notifications for:' => 'Eu quero receber as notificações para:', + 'All tasks' => 'Todas as tarefas', + 'Only for tasks assigned to me' => 'Somente as tarefas atribuídas a mim', + 'Only for tasks created by me' => 'Apenas as tarefas que eu criei', + 'Only for tasks created by me and tasks assigned to me' => 'Apenas as tarefas que eu criei e aquelas atribuídas a mim', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Total para todas as colunas', + 'You need at least 2 days of data to show the chart.' => 'Precisa de pelo menos 2 dias de dados para visualizar o gráfico.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Parar temporizador', + 'Start timer' => 'Iniciar temporizador', + 'My activity stream' => 'O meu feed de actividade', + 'Search tasks' => 'Pesquisar tarefas', + 'Reset filters' => 'Redefinir os filtros', + 'My tasks due tomorrow' => 'A minhas tarefas que expiram amanhã', + 'Tasks due today' => 'Tarefas que expiram hoje', + 'Tasks due tomorrow' => 'Tarefas que expiram amanhã', + 'Tasks due yesterday' => 'Tarefas que expiraram ontem', + 'Closed tasks' => 'Tarefas fechadas', + 'Open tasks' => 'Tarefas abertas', + 'Not assigned' => 'Não assignada', + 'View advanced search syntax' => 'Ver sintaxe avançada de pesquisa', + 'Overview' => 'Visão global', + 'Board/Calendar/List view' => 'Vista Painel/Calendário/Lista', + 'Switch to the board view' => 'Mudar para o modo Painel', + 'Switch to the list view' => 'Mudar para o modo Lista', + 'Go to the search/filter box' => 'Ir para o campo de pesquisa', + 'There is no activity yet.' => 'Ainda não há nenhuma actividade.', + 'No tasks found.' => 'Nenhuma tarefa encontrada', + 'Keyboard shortcut: "%s"' => 'Tecla de atalho: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filtro', + 'Advanced search' => 'Pesquisa avançada', + 'Example of query: ' => 'Exemplo de consulta: ', + '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: ', + 'Average time spent in each column' => 'Tempo médio gasto por coluna', + 'Average time spent' => 'Tempo médio gasto', + 'This chart shows the average time spent in 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', + 'Average lead time: ' => 'Tempo médio de Espera: ', + 'Average cycle time: ' => 'Tempo médio de Ciclo: ', + 'Cycle Time' => 'Tempo de Ciclo', + 'Lead Time' => 'Tempo de Espera', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Este gráfico mostra o tempo médio de espera e ciclo para as últimas %d tarefas realizadas.', + 'Average time into each column' => 'Tempo médio em cada coluna', + 'Lead and cycle time' => 'Tempo de Espera e Ciclo', + 'Lead time: ' => 'Tempo de Espera: ', + 'Cycle time: ' => 'Tempo de Ciclo: ', + 'Time spent in each column' => 'Tempo gasto em cada coluna', + 'The lead time is the duration between the task creation and the completion.' => 'O tempo de espera é a duração entre a criação e o fim da tarefa', + 'The cycle time is the duration between the start date and the completion.' => 'O tempo de ciclo é a duração entre a data de inicio e o fim da tarefa', + 'If the task is not closed the current time is used instead of the completion date.' => 'Se a tarefa não estiver fechada o hora actual será usada em vez da hora de conclusão', + 'Set the start date automatically' => 'Definir data de inicio automáticamente', + 'Edit Authentication' => 'Editar Autenticação', + '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.', + '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á 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', + 'Current column: %s' => 'Coluna actual: %s', + 'Current category: %s' => 'Categoria actual: %s', + 'no category' => 'sem categoria', + 'Current assignee: %s' => 'Assignado a: %s', + 'not assigned' => 'não assignado', + 'Author:' => 'Autor:', + 'contributors' => 'contribuidores', + 'License:' => 'Licença:', + 'License' => 'Licença', + 'Enter the text below' => 'Escreva o texto em baixo', + 'Start date:' => 'Data de inicio:', + 'Due date:' => 'Data de vencimento:', + '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', + 'End date' => 'Data de fim', + 'Users overview' => 'Visão geral de Utilizadores', + 'Members' => 'Membros', + 'Shared project' => 'Projeto partilhado', + 'Project managers' => 'Gestores do projeto', + 'Projects list' => 'Lista de projetos', + 'End date:' => 'Data de fim:', + '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', + 'Reset the search/filter box' => 'Repor caixa de procura/filtro', + 'Documentation' => 'Documentação', + 'Author' => 'Autor', + 'Version' => 'Versão', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Não existem extras carregados', + 'My notifications' => 'As minhas notificações', + 'Custom filters' => 'Filtros personalizados', + 'Your custom filter has been created successfully.' => 'O seu filtro personalizado foi criado com sucesso.', + 'Unable to create your custom filter.' => 'Não foi possivel criar o seu filtro personalizado.', + 'Custom filter removed successfully.' => 'Filtro personalizado removido com sucesso.', + 'Unable to remove this custom filter.' => 'Não foi possivel remover este filtro personalizado.', + 'Edit custom filter' => 'Editar filtro personalizado', + 'Your custom filter has been updated successfully.' => 'O seu filtro personalizado foi actualizado com sucesso.', + 'Unable to update custom filter.' => 'Não foi possivel actualizar o filtro personalizado.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Novo anexo na tarefa #%d: %s', + 'New comment on task #%d' => 'Novo comentário na tarefa #%d', + 'Comment updated on task #%d' => 'Comentário actualizado na tarefa #%d', + 'New subtask on task #%d' => 'Nova sub-tarefa na tarefa #%d', + 'Subtask updated on task #%d' => 'Sub-tarefa actualizada na tarefa #%d', + 'New task #%d: %s' => 'Nova tarefa #%d: %s', + 'Task updated #%d' => 'Tarefa actualizada #%d', + 'Task #%d closed' => 'Tarefa #%d fechada', + 'Task #%d opened' => 'Tarefa #%d aberta', + 'Column changed for task #%d' => 'Coluna alterada para tarefa #%d', + 'New position for task #%d' => 'Nova posição para tarefa #%d', + 'Swimlane changed for task #%d' => 'Swimlane alterado na tarefa #%d', + 'Assignee changed on task #%d' => 'Assignado alterado na tarefa #%d', + '%d overdue tasks' => '%d tarefas em atraso', + 'No notification.' => 'Sem novas notificações.', + 'Mark all as read' => 'Marcar tudo como lido', + 'Mark as read' => 'Marcar como lido', + 'Total number of tasks in this column across all swimlanes' => 'Número total de tarefas nesta coluna em todas as swimlanes', + '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 projeto', + 'Shared' => 'Partilhado', + 'Owner' => 'Dono', + 'Unread notifications' => 'Notificações por ler', + 'Notification methods:' => 'Métodos de notificação:', + '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 has been imported!' => 'Nada foi importado', + 'Import users from CSV file' => 'Importar utilizadores de um ficheiro CSV', + '%d user(s) have been imported successfully.' => '%d utilizadore(s) importados com successo.', + 'Comma' => 'Vírgula', + 'Semi-colon' => 'Ponto e Vírgula', + 'Tab' => 'Tabulação', + 'Vertical bar' => 'Barra vertical', + '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 projeto!', + 'Append filter (instead of replacement)' => 'Acrescentar filtro (em vez de substituir)', + 'Append/Replace' => 'Acrescentar/Substituir', + 'Append' => 'Acrescentar', + 'Replace' => 'Substituir', + 'Import' => 'Importar', + 'Change sorting' => 'alterar ordernação', + 'Tasks Importation' => 'Importação de Tarefas', + 'Delimiter' => 'Delimitador', + 'Enclosure' => 'Clausura', + 'CSV File' => 'Ficheiro CSV', + 'Instructions' => 'Instruções', + 'Your file must use the predefined CSV format' => 'O seu ficheiro tem de usar um formato CSV pre-definido', + 'Your file must be encoded in UTF-8' => 'O seu ficheiro tem de estar codificado como UTF-8', + 'The first row must be the header' => 'A primeira linha tem de ser o cabeçalho', + 'Duplicates are not verified for you' => 'Duplicados não são verificados por si', + 'The due date must use the ISO format: YYYY-MM-DD' => 'A data de expiração tem de estar no formato ISO: AAAA-MM-DD', + 'Download CSV template' => 'Descarregar template CSV', + 'No external integration registered.' => 'Nenhuma integração externa registrada.', + 'Duplicates are not imported' => 'Duplicados não são importados', + 'Usernames must be lowercase and unique' => 'Utilizadores tem de estar em letra pequena e ser unicos', + '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' => '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', + 'Groups' => 'Grupos', + 'Members of %s' => 'Membros de %s', + 'New group' => 'Novo grupo', + 'Group created successfully.' => 'Grupo criado com sucesso.', + 'Unable to create your group.' => 'Não foi possivel criar o seu grupo.', + 'Edit group' => 'Editar grupo', + 'Group updated successfully.' => 'Grupo actualizado com sucesso.', + 'Unable to update your group.' => 'Não foi possivel actualizar o seu grupo.', + 'Add group member to "%s"' => 'Adicionar membro do grupo a "%s"', + 'Group member added successfully.' => 'Membro de grupo adicionado com sucesso.', + 'Unable to add group member.' => 'Não foi possivel adicionar membro de grupo.', + 'Remove user from group "%s"' => 'Remover utilizador do grupo "%s"', + 'User removed successfully from this group.' => 'Utilizador removido com sucesso deste grupo.', + 'Unable to remove this user from the group.' => 'Não foi possivel remover este utilizador do grupo.', + '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 Projeto', + 'Manager' => 'Gestor', + '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', + '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.', + 'Add group member' => 'Adicionar membro de grupo', + 'Do you really want to remove this group: "%s"?' => 'Tem a certeza que quer remover este grupo: "%s"?', + 'There is no user in this group.' => 'Não existe utilizadores neste grupo.', + 'Permissions' => 'Permissões', + 'Allowed Users' => 'Utilizadores Permitidos', + 'No specific user has been allowed.' => 'Nenhum utilizador foi especificamente permitido.', + 'Role' => 'Função', + 'Enter user name...' => 'Escreva o nome do utilizador...', + 'Allowed Groups' => 'Grupos Permitidos', + 'No group has been allowed.' => 'Nenhum grupo foi especificamente permitido.', + 'Group' => 'Grupo', + 'Group Name' => 'Nome do Grupo', + 'Enter group name...' => 'Escreva o nome do Grupo', + 'Role:' => 'Função:', + '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', + 'Estimated hours: ' => 'Horas estimadas: ', + 'Actual hours: ' => 'Horas reais: ', + 'Hours Spent' => 'Horas Gastas', + 'Hours Estimated' => 'Horas Estimadas', + 'Estimated Time' => 'Tempo Estimado', + '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' => '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: ', + 'Disable two-factor authentication' => 'Desactivar autenticação de dois passos', + 'Enable two-factor authentication' => 'Activar autenticação de dois passos', + 'There is no integration registered at the moment.' => 'Não existe nenhuma integração registrada até ao momento.', + 'Password Reset for Kanboard' => 'Redefinir Password para Kanboard', + 'Forgot password?' => 'Esqueceu a password?', + 'Enable "Forget Password"' => 'Activar "Esqueceu a password"', + 'Password Reset' => 'Redefinir a Password', + 'New password' => 'Nova Password', + 'Change Password' => 'Alterar Password', + 'To reset your password click on this link:' => 'Para redefinir a sua password click nesta ligação:', + 'Last Password Reset' => 'Última Redefinição da Password', + 'The password has never been reinitialized.' => 'A password nunca foi redefinida.', + 'Creation' => 'Criação', + 'Expiration' => 'Expiração', + 'Password reset history' => 'Histórico da redefinição da password', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Todas as tarefas na coluna "%s" e na swimlane "%s" foram fechadas com successo.', + '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 in this column and this swimlane' => '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 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 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', + 'Personal projects do not have users and groups management.' => 'Projetos pessoais 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', + 'Dates' => 'Datas', + 'Default priority' => 'Prioridade por defeito', + 'Lowest priority' => 'Prioridade mais baixa', + 'Highest priority' => 'Prioridade mais alta', + '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 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', + 'Related' => 'Relacionado', + 'Attachment' => 'Anexo', + 'Web Link' => 'Ligação Web', + 'External links' => 'Ligações externas', + 'Add external link' => 'Adicionar ligação externa', + 'Type' => 'Tipo', + 'Dependency' => 'Dependencia', + 'Add internal link' => 'Adicionar ligação interna', + 'Add a new external link' => 'Adicionar nova ligação externa', + 'Edit external link' => 'Editar ligação externa', + 'External link' => 'Ligação externa', + 'Copy and paste your link here...' => 'Copie e cole a sua ligação aqui...', + 'URL' => 'URL', + 'Internal links' => 'Ligações internas', + 'Assign to me' => 'Atribuir a mim', + 'Me' => 'Eu', + 'Do not duplicate anything' => 'Não duplicar nada', + '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 projeto', + 'open' => 'aberto', + 'closed' => 'fechado', + 'Priority:' => 'Prioridade:', + 'Reference:' => 'Referência:', + 'Complexity:' => 'Complexidade:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Coluna:', + 'Position:' => 'Posição:', + 'Creator:' => 'Criador:', + 'Time estimated:' => 'Tempo estimado:', + '%s hours' => '%s horas', + 'Time spent:' => 'Tempo gasto:', + 'Created:' => 'Criado:', + 'Modified:' => 'Modificado:', + 'Completed:' => 'Completado:', + 'Started:' => 'Iniciado:', + 'Moved:' => 'Movido:', + 'Task #%d' => 'Tarefa #%d', + '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 personal projects' => 'Desactivar projetos pessoais', + '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.', + 'Unable to enable this user.' => 'Não foi possivel activar este utilizador.', + '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.', + 'The maximum allowed file size is %sB.' => 'O tamanho máximo permitido é %sB.', + 'Drag and drop your files here' => 'Arraste e deixe os ficheiros para aqui', + 'choose files' => 'escolher ficheiros', + 'View profile' => 'Ver perfil', + 'Two Factor' => 'Dois Factores', + 'Disable user' => 'Desactivar utilizador', + 'Do you really want to disable this user: "%s"?' => 'Tem a certeza que quer desactivar este utilizador: "%s"?', + 'Enable user' => 'Activar utilizador', + 'Do you really want to enable this user: "%s"?' => 'Tem a certeza que quer activar este utilizador: "%s"?', + 'Download' => 'Transferir', + 'Uploaded: %s' => 'Enviado: %s', + 'Size: %s' => 'Tamanho: %s', + 'Uploaded by %s' => 'Enviado por %s', + '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 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 projeto', + 'User filters' => 'Filtros de utilizador', + 'Category filters' => 'Filtros de categoria', + 'Upload a file' => 'Enviar um ficheiro', + 'View file' => 'Ver ficheiro', + '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 projeto', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Exemplo: https://example.kanboard.org/ (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 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 projeto', + 'There is no available project.' => 'Não existe projeto disponivel.', + 'Local File' => 'Ficheiro Local', + 'Configuration' => 'Configuração', + 'PHP version:' => 'Versão PHP:', + 'PHP SAPI:' => 'SAPI PHP:', + 'OS version:' => 'Versão SO:', + 'Database version:' => 'Versão base de dados:', + 'Browser:' => 'Navegador:', + 'Task view' => 'Vista de Tarefas', + 'Edit task' => 'Editar tarefa', + 'Edit description' => 'Editar descrição', + 'New internal link' => 'Nova ligação interna', + 'Display list of keyboard shortcuts' => 'Mostrar lista de atalhos do teclado', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Enviar a minha imagem de avatar', + 'Remove my image' => 'Remover a minha imagem', + 'The OAuth2 state parameter is invalid' => 'O parametro de estado do OAuth2 é inválido', + 'User not found.' => 'Utilizador não encontrado.', + 'Search in activity stream' => 'Procurar no fluxo de atividade', + 'My activities' => 'Minhas actividades', + 'Activity until yesterday' => 'Actividade até ontem', + 'Activity until today' => 'Actividade até hoje', + 'Search by creator: ' => 'Procurar por criador: ', + 'Search by creation date: ' => 'Procurar por data de criação: ', + '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' => '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' => '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', + 'Plugin installed successfully.' => 'Plugin instalado com sucesso.', + 'Plugin updated successfully.' => 'Plugin actualizado 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.' => '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.', + '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 utilizador.', + 'There is no plugin available.' => 'Não existe nenhum plugin instalado.', + 'Install' => 'Instalar', + 'Update' => 'Actualizar', + 'Up to date' => 'Actualizado', + 'Not available' => 'Não disponivel', + 'Remove plugin' => 'Remover plugin', + 'Do you really want to remove this plugin: "%s"?' => 'Tem a certeza que pretende remover este plugin: "%s%"?', + 'Uninstall' => 'Desinstalar', + 'Listing' => 'A Listar', + '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 E-mail', + 'Email sender address' => 'Endereço de envido de E-mail', + 'Email transport' => 'Transportador de E-mail', + '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 actualizada com sucesso.', + 'Unable to update this tag.' => 'Não foi possivel actualizar esta etiqueta.', + 'Tag removed successfully.' => 'Etiqueta removida com sucesso.', + 'Unable to remove this tag.' => 'Não foi possivel remover esta etiqueta.', + 'Global tags management' => 'Gestão de etiquetas globais', + 'Tags' => 'Etiquetas', + 'Tags management' => 'Gestão de Etiquetas', + 'Add new tag' => 'Adicionar etiqueta nova', + 'Edit a tag' => 'Editar a etiqueta', + 'Project tags' => 'Etiquetas do 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' => 'Este campo não pode ficar vazio', + 'Close a task when there is no activity in a 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 has 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', + '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', + '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 utilizador já está a ser utilizado', + 'Your profile must have a valid email address.' => 'O seu perfil deve ter um endereço de email válido.', + 'TRL - Turkish Lira' => 'TRL - Lira Turca', + 'The project email is optional and could be used by several plugins.' => 'O e-mail do projeto é opcional e pode ser usado por vários plugins.', + 'The project email must be unique across all projects' => 'O e-mail do projeto tem de ser único em todos os projetos', + 'The email configuration has been disabled by the administrator.' => 'A configuração de e-mail foi desativada pelo administrador.', + 'Close this project' => 'Fechar este projeto', + 'Open this project' => 'Abrir este projeto', + 'Close a project' => 'Fechar um projeto', + 'Do you really want to close this project: "%s"?' => 'Deseja realmente fechar este projeto: "%s"?', + 'Reopen a project' => 'Reabrir um projeto', + 'Do you really want to reopen this project: "%s"?' => 'Deseja mesmo reabrir este projecto?: "%s"?', + 'This project is open' => 'Este projeto está aberto', + 'This project is closed' => 'Este projecto está fechado', + 'Unable to upload files, check the permissions of your data folder.' => 'Impossivel enviar ficheiros, verifique as permissões da pasta data', + 'Another category with the same name exists in this project' => 'Outra categoria com o mesmo nome já existe neste projecto', + 'Comment sent by email successfully.' => 'Comentário enviado por email com sucesso.', + 'Sent by email to "%s" (%s)' => 'Enviado por email para "%s" (%s)', + 'Unable to read uploaded file.' => 'Não foi possivel ler ficheiro enviado.', + 'Database uploaded successfully.' => 'Base de dados enviada com sucesso.', + 'Task sent by email successfully.' => 'Tarefa enviada por email com sucesso.', + 'There is no category in this project.' => 'Não existe categorias neste projecto.', + 'Send by email' => 'Enviar por email', + 'Create and send a comment by email' => 'Criar e enviar um comentário por email', + 'Subject' => 'Assunto', + 'Upload the database' => 'Enviar a base de dados', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Poderia enviar a base de dados Sqlite transferida anteriormente (formato Gzip).', + 'Database file' => 'Ficheiro base de dados', + 'Upload' => 'Enviar', + 'Your project must have at least one active swimlane.' => 'O seu projecto deve ter pelo menos uma swimlane activa.', + 'Project: %s' => 'Projecto: %s', + 'Automatic action not found: "%s"' => 'Acção automática não encontrada: "%s"', + '%d projects' => '%d projetos', + '%d project' => '%d projecto', + 'There is no project.' => 'Não existe projecto.', + 'Sort' => 'Ordenar', + 'Project ID' => 'ID do Projecto', + 'Project name' => 'Nome do Projecto', + 'Public' => 'Público', + 'Personal' => 'Privado', + '%d tasks' => '%d tarefas', + '%d task' => '%d tarefa', + 'Task ID' => 'ID da Tarefa', + 'Assign automatically a color when due date is expired' => 'Atribuir automaticamente uma cor quando a data de vencimento expirar', + 'Total score in this column across all swimlanes' => 'Pontuação total nesta coluna em todos os swimlanes', + 'HRK - Kuna' => 'HRK - Kuna Croata', + 'ARS - Argentine Peso' => 'ARS - Peso Argentino', + 'COP - Colombian Peso' => 'COP - Peso Colombiano', + '%d groups' => '%d grupos', + '%d group' => '%d grupo', + 'Group ID' => 'ID do Grupo', + 'External ID' => 'ID Externo', + '%d users' => '%d utilizadores', + '%d user' => '%d utilizador', + 'Hide subtasks' => 'Esconder subtarefas', + 'Show subtasks' => 'Mostrar subtarefas', + 'Authentication Parameters' => 'Parâmetros de autenticação', + 'API Access' => 'Acesso à API', + 'No users found.' => 'Não foram encontrados utilizadores.', + 'User ID' => 'ID de Utilizador', + 'Notifications are activated' => 'Notificações ativadas', + 'Notifications are disabled' => 'Notificações desativadas', + 'User disabled' => 'Utilizador desactivado', + '%d notifications' => '%d notificações', + '%d notification' => '%d notificação', + 'There is no external integration installed.' => 'Não há integração externa instalada.', + 'You are not allowed to update tasks assigned to someone else.' => 'Não tem permissão para atualizar tarefas atribuídas a outra pessoa.', + 'You are not allowed to change the assignee.' => 'Não tem permissão para alterar a atribuição', + 'Task suppression is not permitted' => 'A supressão de tarefas não é permitida', + 'Changing assignee is not permitted' => 'Não é permitido alterar a atribuição', + 'Update only assigned tasks is permitted' => 'Apenas é permitido atualizar as tarefas atribuídas', + 'Only for tasks assigned to the current user' => 'Apenas para tarefas atribuídas ao utilizador atual', + 'My projects' => 'Meus projetos', + 'You are not a member of any project.' => 'Não é membro de nenhum projeto.', + 'My subtasks' => 'Minhas subtarefas', + '%d subtasks' => '%d subtarefas', + '%d subtask' => '%d subtarefa', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Apenas é permitido mover a tarefa entre essas colunas para tarefas atribuídas ao utilizador atual', + '[DUPLICATE]' => '[DUPLICADA]', + 'DKK - Danish Krona' => 'DKK - Coroa Dinamarquesa', + 'Remove user from group' => 'Remover utilizador do grupo', + 'Assign the task to its creator' => 'Atribuir a tarefa ao seu criador', + 'This task was sent by email to "%s" with subject "%s".' => 'Esta tarefa foi enviada por email para "%s" com o assunto "%s".', + 'Predefined Email Subjects' => 'Assuntos de email predefinidos', + 'Write one subject by line.' => 'Escreva um assunto por linha.', + 'Create another link' => 'Criar outro link', + 'BRL - Brazilian Real' => 'BRL - Real Brasileiro', + 'Add a new Kanboard task' => 'Adicionar nova tarefa Kanboard', + 'Subtask not started' => 'Sub-tarefa não iniciada', + 'Subtask currently in progress' => 'Sub-tarefa em progresso', + 'Subtask completed' => 'Sub-tarefa concluida', + 'Subtask added successfully.' => 'Sub-tarefa adicionada com sucesso.', + '%d subtasks added successfully.' => '%d sub-tarefas adicionadas com sucesso.', + 'Enter one subtask by line.' => 'Escreva uma sub-tarefa por linha.', + 'Predefined Contents' => 'Conteudos Predefinidos', + 'Predefined contents' => 'Conteudos predefinidos', + 'Predefined Task Description' => 'Descricção de Tarefa Predefinida', + 'Do you really want to remove this template? "%s"' => 'Tem a certeza que pretende remover este template? "%s"', + 'Add predefined task description' => 'Adicionar descrição predefinida de tarefa', + 'Predefined Task Descriptions' => 'Descrições Predefinidas de Tarefa', + 'Template created successfully.' => 'Template criado com sucesso.', + 'Unable to create this template.' => 'Não foi possivel criar este template.', + 'Template updated successfully.' => 'Template actualizado com sucesso.', + 'Unable to update this template.' => 'Não foi possivel actualizar este template.', + 'Template removed successfully.' => 'Template removido com sucesso.', + 'Unable to remove this template.' => 'Não foi possivel remover este template.', + 'Template for the task description' => 'Template para descrição de tarefa', + 'The start date is greater than the end date' => 'A data de inicio é maior que a data de fim', + 'Tags must be separated by a comma' => 'Etiquetas tem de ser separadas por virgula', + 'Only the task title is required' => 'Apenas o titulo da tarefa é necessário', + 'Creator Username' => 'Utilizador do Criador', + 'Color Name' => 'Nome da Cor', + 'Column Name' => 'Noma da Coluna', + 'Swimlane Name' => 'Nome da Swimlane', + 'Time Estimated' => 'Tempo Estimado', + 'Time Spent' => 'Tempo Gasto', + 'External Link' => 'Ligação Externa', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Esta característica activa a feed do iCal, a feed do RSS e a vista publica do quadro.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Parar o temporizador de todas as sub-tarefas quando a tarefa for movida para outra coluna', + 'Subtask Title' => 'Titulo de sub-tarefa', + 'Add a subtask and activate the timer when moving a task to another column' => 'Adicionar uma sub-tarefa e activar o temporizador quando a tarefa for movida para outra coluna', + 'days' => 'dias', + 'minutes' => 'minutos', + 'seconds' => 'segundos', + 'Assign automatically a color when preset start date is reached' => 'Atribuir automaticamente uma cor quando a data de início for atingida', + 'Move the task to another column once a predefined start date is reached' => 'Mover a tarefa para outra coluna quando a data de início for atingida', + 'This task is now linked to the task %s with the relation "%s"' => 'Esta tarefa está agora ligada à tarefa %s com a relação "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'A ligação com a relação "%s" à tarefa %s foi removida', + 'Custom Filter:' => 'Filtro Personalizado:', + 'Unable to find this group.' => 'Não foi possivel encontrar este grupo.', + '%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 moved the task #%d to the swimlane "%s"' => '%s moveu a tarefa #%d para a swimlane "%s"', + '%sh spent' => '%sh gastas', + '%sh estimated' => '%sh estimadas', + 'Select All' => 'Selecionar Tudo', + 'Unselect All' => 'Desmarcar Tudo', + 'Apply action' => 'Aplicar ação', + 'Move selected tasks to another column or swimlane' => 'Mover tarefas selecionadas para outra coluna', + 'Edit tasks in bulk' => 'Editar tarefas em massa', + 'Choose the properties that you would like to change for the selected tasks.' => 'Escolha as propriedades que pretente modificar nas tarefas selecionadas.', + 'Configure this project' => 'Configurar este projeto', + 'Start now' => 'Começar agora', + '%s removed a file from the task #%d' => '%s removeu um ficheiro da tarefa #%d', + 'Attachment removed from task #%d: %s' => 'Anexo removido da tarefa #%d: %s', + 'No color' => 'Sem cor', + 'Attachment removed "%s"' => 'Anexo removido "%s"', + '%s removed a file from the task %s' => '%s removeu um ficheiro da tarefa %s', + 'Move the task to another swimlane when assigned to a user' => 'Mover a tarefa para outra swimlane quando assignada a um utilizador', + 'Destination swimlane' => 'Swimlane de destino', + 'Assign a category when the task is moved to a specific swimlane' => 'Assignar uma categoria quando a tarefa é movida para um swimlane especifica', + 'Move the task to another swimlane when the category is changed' => 'Mover a tarefa para outra swimlane quando a categoria é alterada', + 'Reorder this column by priority (ASC)' => 'Reordenar esta coluna por prioridade (ASC)', + 'Reorder this column by priority (DESC)' => 'Reordenar esta coluna por prioridade (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'Reordenar esta coluna por assignado e prioridade (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'Reordenar esta coluna por assignado e prioridade (DESC)', + 'Reorder this column by assignee (A-Z)' => 'Reordenar esta coluna por assignado (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Reordenar esta coluna por assignado (Z-A)', + 'Reorder this column by due date (ASC)' => 'Reordenar esta coluna por data de vencimento (ASC)', + 'Reorder this column by due date (DESC)' => 'Reordenar esta coluna por data de vencimento (DESC)', + 'Reorder this column by id (ASC)' => 'Reordenar esta coluna por id (ASC)', + 'Reorder this column by id (DESC)' => 'Reordenar esta coluna por id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s moveu a tarefa #%d "%s" para o projeto "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Tarefa #%d "%s" foi movida para o projeto "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Mover a tarefa para outra coluna quando a data de vencimento é menos que um certo numero de dias', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automaticamente atualizar a data de inicio quando a tarefa for movida de uma certa coluna', + 'HTTP Client:' => 'Cliente HTTP:', + 'Assigned' => 'Assignado', + 'Task limits apply to each swimlane individually' => 'Limites de tarefa aplicam-se a cada swimlane individualmente', + 'Column task limits apply to each swimlane individually' => 'Limites de tarefas da coluna aplicam-se a cada swimlane individualmente', + 'Column task limits are applied to each swimlane individually' => 'Limites de tarefas da coluna são aplicadas a cada swimlane individualmente', + 'Column task limits are applied across swimlanes' => 'Limites de tarefas da coluna são aplicados a todas a swimlanes', + 'Task limit: ' => 'Limite da tarefa: ', + 'Change to global tag' => 'Alterar para etiqueta global', + 'Do you really want to make the tag "%s" global?' => 'Tem a certeza que pretende por a etiqueta "%s" como global?', + 'Enable global tags for this project' => 'Activar etiquetas globais para este projeto', + 'Group membership(s):' => 'Membro(s) do grupo:', + '%s is a member of the following group(s): %s' => '%s é membro do(s) seguinte(s) grupo(s): %s', + '%d/%d group(s) shown' => '%d/%d grupo(s) mostrado(s)', + 'Subtask creation or modification' => 'Alteração ou criação de sub-tarefa', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Assignar a tarefa a um utilizador especificado quando a tarefa é movida para uma swimlane especificada', + 'Comment' => 'Comentário', + 'Collapse vertically' => 'Colapsar verticalmente', + 'Expand vertically' => 'Expandir verticalmente', + 'MXN - Mexican Peso' => 'MXN - Peso Mexicano', + 'Estimated vs actual time per column' => 'Tempo estimado vs real por coluna', + 'HUF - Hungarian Forint' => 'HUF - Florim Húngaro', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Tem selecionar um ficheiro para carregar como seu avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'O ficheiro que carregou não uma imagem válida! (Apenas *.gif, *.jpg, *.jpeg e *.png são permitidas!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Definir automaticamente a data de vencimento quando a tarefa for arrastada de uma coluna específica', + 'No other projects found.' => 'Nenhum outro projeto encontrado.', + 'Tasks copied successfully.' => 'Tarefas copiadas com sucesso.', + 'Unable to copy tasks.' => 'Não foi possível copiar as tarefas.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Tema claro', + 'Dark theme' => 'Tema escuro', + 'Automatic theme - Sync with system' => 'Tema automático – Sincronizar com o sistema', + 'Application managers or more' => 'Gestores da aplicação ou superior', + 'Administrators' => 'Administradores', + 'Visibility:' => 'Visibilidade:', + 'Standard users' => 'Utilizadores padrão', + 'Visibility is required' => 'A visibilidade é obrigatória', + 'The visibility should be an app role' => 'A visibilidade deve ser um papel da aplicação', + 'Reply' => 'Responder', + '%s wrote: ' => '%s escreveu: ', + 'Number of visible tasks in this column and swimlane' => 'Número de tarefas visíveis nesta coluna e faixa', + 'Number of tasks in this swimlane' => 'Número de tarefas nesta faixa', + 'Unable to find another subtask in progress, you can close this window.' => 'Não foi possível encontrar outra subtarefa em progresso, pode fechar esta janela.', + 'This theme is invalid' => 'Este tema é inválido', + 'This role is invalid' => 'Este papel é inválido', + 'This timezone is invalid' => 'Este fuso horário é inválido', + 'This language is invalid' => 'Este idioma é inválido', + 'This URL is invalid' => 'Este URL é inválido', + 'Date format invalid' => 'Formato de data inválido', + 'Time format invalid' => 'Formato de hora inválido', + 'Invalid Mail transport' => 'Transporte de e-mail inválido', + 'Color invalid' => 'Cor inválida', + 'This value must be greater or equal to %d' => 'Este valor deve ser maior ou igual a %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Adicionar um BOM no início do ficheiro (necessário para Microsoft Excel)', + 'Just add these tag(s)' => 'Apenas adicione estas etiquetas', + 'Remove internal link(s)' => 'Remover ligações internas', + 'Import tasks from another project' => 'Importar tarefas de outro projeto', + 'Select the project to copy tasks from' => 'Selecionar o projeto de onde copiar as tarefas', + 'The total maximum allowed attachments size is %sB.' => 'O tamanho máximo total permitido para anexos é de %sB.', + 'Add attachments' => 'Adicionar anexos', + 'Task #%d "%s" is overdue' => 'Tarefa #%d "%s" está atrasada', + 'Enable notifications by default for all new users' => 'Ativar notificações por padrão para todos os novos usuários', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Atribuir a tarefa ao seu criador para colunas específicas se nenhum responsável for definido manualmente', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Atribuir a tarefa ao utilizador com sessão iniciada ao mudar de coluna para a coluna especificada se nenhum utilizador estiver atribuído', +]; diff --git a/app/Locale/ro_RO/translations.php b/app/Locale/ro_RO/translations.php new file mode 100644 index 0000000..9abd233 --- /dev/null +++ b/app/Locale/ro_RO/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ' ', + 'None' => 'Nimic', + 'Edit' => 'Modifică', + 'Remove' => 'Șterge', + 'Yes' => 'Da', + 'No' => 'Nu', + 'cancel' => 'anulare', + 'or' => 'sau', + 'Yellow' => 'Galben', + 'Blue' => 'Albastru', + 'Green' => 'Verde', + 'Purple' => 'Violet', + 'Red' => 'RoÈ™u', + 'Orange' => 'Portocaliu', + 'Grey' => 'Gri', + 'Brown' => 'Maro', + 'Deep Orange' => 'Portocaliu închis', + 'Dark Grey' => 'Gri închis', + 'Pink' => 'Roz', + 'Teal' => 'Turcoaz', + 'Cyan' => 'Bleu', + 'Lime' => 'Lime', + 'Light Green' => 'Verde deschis', + 'Amber' => 'Ambră', + 'Save' => 'Salvează', + 'Login' => 'Conectare', + 'Official website:' => 'Site web oficial:', + 'Unassigned' => 'Neatribuit', + 'View this task' => 'Vezi sarcina aceasta', + 'Remove user' => 'Șterge utilizator', + 'Do you really want to remove this user: "%s"?' => 'Vrei să È™tergi acest utilizator: "%s" ?', + 'All users' => 'ToÈ›i utilizatorii', + 'Username' => 'Nume utilizator', + 'Password' => 'Parolă', + 'Administrator' => 'Administrator', + 'Sign in' => 'Conectare', + 'Users' => 'Utilizatori', + 'Forbidden' => 'Interzis', + 'Access Forbidden' => 'Acces interzis', + 'Edit user' => 'Modifică utilizator', + 'Logout' => 'Deconectare', + 'Bad username or password' => 'Utilizator sau parolă incorectă', + 'Edit project' => 'Modifică proiect', + 'Name' => 'Nume', + 'Projects' => 'Proiecte', + 'No project' => 'Fără proiect', + 'Project' => 'Proiect', + 'Status' => 'Stare', + 'Tasks' => 'Sarcini', + 'Board' => 'Bord', + 'Actions' => 'Actiuni', + 'Inactive' => 'Inactiv', + 'Active' => 'Activ', + 'Unable to update this board.' => 'Nu am putut actualiza acest bord.', + 'Disable' => 'Dezactivează', + 'Enable' => 'Activează', + 'New project' => 'Proiect nou', + 'Do you really want to remove this project: "%s"?' => 'Vrei să È™tergi acest proiect: "%s" ?', + 'Remove project' => 'Șterge proiect', + 'Edit the board for "%s"' => 'Modifică bordul pentru "%s"', + 'Add a new column' => 'Adaugă o coloană nouă', + 'Title' => 'Titlu', + 'Assigned to %s' => 'Atribuie lui %s', + 'Remove a column' => 'Șterge o coloană', + 'Unable to remove this column.' => 'Nu pot È™terge această coloană.', + 'Do you really want to remove this column: "%s"?' => 'Vrei să È™tergi acestă coloană: "%s" ?', + 'Settings' => 'PreferinÈ›e', + 'Application settings' => 'PreferinÈ›ele aplicaÈ›iei', + 'Language' => 'Limbă', + 'Webhook token:' => 'Token de securitate pentru webhook:', + 'API token:' => 'Token de securitate pentru API:', + 'Database size:' => 'Dimensiune bază de date:', + 'Download the database' => 'Descarcă baza de date', + 'Optimize the database' => 'Optimizează baza de date', + '(VACUUM command)' => '(Comanda VACUUM)', + '(Gzip compressed Sqlite file)' => '(FiÈ™ier Sqlite comprimat cu Gzip)', + 'Close a task' => 'ÃŽnchide o sarcină', + 'Column' => 'Coloană', + 'Color' => 'Culoare', + 'Assignee' => 'Persoană desemnată', + 'Create another task' => 'Creează altă sarcină', + 'New task' => 'Sarcină nouă', + 'Open a task' => 'Deschide o sarcină', + 'Do you really want to open this task: "%s"?' => 'Vrei să deschizi sarcina: "%s" ?', + 'Back to the board' => 'ÃŽnapoi la bord', + 'There is nobody assigned' => 'Nu este desemnată o persoană', + 'Column on the board:' => 'Coloană pe bord: ', + 'Close this task' => 'ÃŽnchide sarcina', + 'Open this task' => 'Deschide Sarcina', + 'There is no description.' => 'Nu există o descriere.', + 'Add a new task' => 'Adaugă o sarcină nouă', + 'The username is required' => 'Numele este obligatoriu', + 'The maximum length is %d characters' => 'Lungimea maximă este de %d caractere', + 'The minimum length is %d characters' => 'Lungimea minimă este de %d caractere', + 'The password is required' => 'Parola este obligatorie', + 'This value must be an integer' => 'Valoarea trebuie să fie număr întreg', + 'The username must be unique' => 'Utilizatorul trebuie să fie unic', + 'The user id is required' => 'ID-ul de utilizator este obligatoriu', + 'Passwords don\'t match' => 'Parolele nu corespund', + 'The confirmation is required' => 'Confirmarea este obligatorie', + 'The project is required' => 'Proiectul este obligatoriu', + 'The id is required' => 'Identificatorul este obligatoriu', + 'The project id is required' => 'ID-ul proiectului este obligatoriu', + 'The project name is required' => 'Numele proiectului este obligatoriu', + 'The title is required' => 'Titlul este obligatoriu', + 'Settings saved successfully.' => 'PreferinÈ›ele au fost salvate.', + 'Unable to save your settings.' => 'Nu am putut salva preferinÈ›ele.', + 'Database optimization done.' => 'Optimizarea bazei de date s-a încheiat.', + 'Your project has been created successfully.' => 'Proiectul dumneavoastră a fost creat cu succes.', + 'Unable to create your project.' => 'Nu am putut crea proiectul dumneavoastră.', + 'Project updated successfully.' => 'Proiectul a fost actualizat.', + 'Unable to update this project.' => 'Nu am putut actualiza proiectul.', + 'Unable to remove this project.' => 'Nu am putut È™terge proiectul.', + 'Project removed successfully.' => 'Proiectul a fost È™ters.', + 'Project activated successfully.' => 'Proiectul a fost activat.', + 'Unable to activate this project.' => 'Nu am putut activa proiectul.', + 'Project disabled successfully.' => 'Proiectul a fost dezactivat.', + 'Unable to disable this project.' => 'Nu am putut dezactiva proiectul', + 'Unable to open this task.' => 'Nu am putut deschide sarcina.', + 'Task opened successfully.' => 'Sarcina a fost deschisă', + 'Unable to close this task.' => 'Nu am putut închide sarcina.', + 'Task closed successfully.' => 'Sarcina a fost închisă.', + 'Unable to update your task.' => 'Nu am putut actualiza sarcina.', + 'Task updated successfully.' => 'Sarcina fost actualizată.', + 'Unable to create your task.' => 'Nu am putut crea sarcina.', + 'Task created successfully.' => 'Sarcina a fost creată.', + 'User created successfully.' => 'Utilizatorul a fost creat.', + 'Unable to create your user.' => 'Nu am putut crea utilizatorul.', + 'User updated successfully.' => 'Utilizatorul a fost actualizat.', + 'User removed successfully.' => 'Utilizatorul a fost È™ters.', + 'Unable to remove this user.' => 'Nu am putut È™terge utilizatorul.', + 'Board updated successfully.' => 'Bordul a fost actualizat.', + 'Ready' => 'Pregătit', + 'Backlog' => 'Restant', + 'Work in progress' => 'ÃŽn desfășurare', + 'Done' => 'Finalizat', + 'Application version:' => 'Versiunea aplicaÈ›iei:', + 'Id' => 'Id', + 'Public link' => 'Legătură publică', + 'Timezone' => 'Fus orar', + 'Sorry, I didn\'t find this information in my database!' => 'Scuze, nu am găsit informaÈ›ia asta în baza mea de date!', + 'Page not found' => 'Pagina nu a fost găsită', + 'Complexity' => 'Complexitate', + 'Task limit' => 'Limită sarcini.', + 'Task count' => 'Număr de sarcini', + 'User' => 'Utilizator', + 'Comments' => 'Comentarii', + 'Comment is required' => 'Comentariul este obligatoriu', + 'Comment added successfully.' => 'Comentariul a fost adăugat', + 'Unable to create your comment.' => 'Nu am putut crea comentariul.', + 'Due Date' => 'Data scadentă', + 'Invalid date' => 'Dată invalidă', + 'Automatic actions' => 'AcÈ›iuni automatizate', + 'Your automatic action has been created successfully.' => 'AcÈ›iunea automatizată a fost creată.', + 'Unable to create your automatic action.' => 'Nu am putut crea acÈ›iunea automatizată.', + 'Remove an action' => 'Șterge o acÈ›iune', + 'Unable to remove this action.' => 'Nu am putut È™terge acÈ›iunea', + 'Action removed successfully.' => 'AcÈ›iunea a fost È™tearsă.', + 'Automatic actions for the project "%s"' => 'AcÈ›iuni automatizate pentru proiectul "%s"', + 'Add an action' => 'Adaugă o acÈ›iune', + 'Event name' => 'Nume eveniment', + 'Action' => 'AcÈ›iune', + 'Event' => 'Eveniment', + 'When the selected event occurs execute the corresponding action.' => 'Când are loc evenimentul ales execută acÈ›iunea corespunzătoare.', + 'Next step' => 'Pasul următor', + 'Define action parameters' => 'DefineÈ™te parametrii acÈ›iunii', + 'Do you really want to remove this action: "%s"?' => 'Vrei să È™tergi acÈ›iunea: "%s" ?', + 'Remove an automatic action' => 'Șterge o acÈ›iune automatizată', + 'Assign the task to a specific user' => 'Atribuie sarcina unui utilizator specific', + 'Assign the task to the person who does the action' => 'Atribuie sarcina persoanei care îndeplineÈ™te acÈ›iunea', + 'Duplicate the task to another project' => 'Duplichează sarcina în alt proiect', + 'Move a task to another column' => 'Mută sarcina în altă coloană', + 'Task modification' => 'Modificare sarcină', + 'Task creation' => 'Creare sarcină', + 'Closing a task' => 'ÃŽnchidere sarcină', + 'Assign a color to a specific user' => 'Atribuie o culoare unui utilizator specific', + 'Position' => 'PoziÈ›ie', + 'Duplicate to project' => 'Duplichează în alt proiect', + 'Duplicate' => 'Duplicare', + 'Link' => 'Legătură', + 'Comment updated successfully.' => 'Comentariu actualizat.', + 'Unable to update your comment.' => 'Nu am putut actualiza comentariul.', + 'Remove a comment' => 'Șterge un comentariu', + 'Comment removed successfully.' => 'Comentariul a fost È™ters.', + 'Unable to remove this comment.' => 'Nu am putut È™terge comentariul.', + 'Do you really want to remove this comment?' => 'Vrei să È™tergi acest comentariu?', + 'Current password for the user "%s"' => 'Parola actuală pentru utilizatorul "%s"', + 'The current password is required' => 'Parola actuală este obligatorie', + 'Wrong password' => 'Parolă greÈ™ită', + 'Unknown' => 'Necunoscut', + 'Last logins' => 'Ultimele conectări', + 'Login date' => 'Data conectării', + 'Authentication method' => 'Metodă de autentificare', + 'IP address' => 'Adresă IP', + 'User agent' => 'Agent utilizator', + 'Persistent connections' => 'Conexiuni persistente', + 'No session.' => 'Fără sesiune.', + 'Expiration date' => 'Data expirării', + 'Remember Me' => 'AminteÈ™te-È›i de mine', + 'Creation date' => 'Data creării', + 'Everybody' => 'Toată lumea', + 'Open' => 'Deschis', + 'Closed' => 'ÃŽnchis', + 'Search' => 'Caută', + 'Nothing found.' => 'Nimic găsit.', + 'Due date' => 'Dată scadentă', + 'Description' => 'Descriere', + '%d comments' => '%d comentarii', + '%d comment' => '%d comentariu', + 'Email address invalid' => 'Adresă e-mail invalidă', + 'Your external account is not linked anymore to your profile.' => 'Contul tău extern nu mai este legat cu profilul.', + 'Unable to unlink your external account.' => 'Nu am putut dezlega contul tău extern.', + 'External authentication failed' => 'Autentificarea externă a eÈ™uat', + 'Your external account is linked to your profile successfully.' => 'Contul tău extern a fost legat de profil.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Sarcină È™tearsă.', + 'Unable to remove this task.' => 'Nu pot È™terge sarcina.', + 'Remove a task' => 'Șterge o sarcină', + 'Do you really want to remove this task: "%s"?' => 'Vrei să È™tergi sarcina: "%s" ?', + 'Assign automatically a color based on a category' => 'Atribuie automat o culoare bazat pe categorie', + 'Assign automatically a category based on a color' => 'Atribuie automat o categorie bazat pe culoare', + 'Task creation or modification' => 'Creare sau modificare sarcină', + 'Category' => 'Categorie', + 'Category:' => 'Categorie:', + 'Categories' => 'Categorii', + 'Your category has been created successfully.' => 'Categoria a fost creată.', + 'This category has been updated successfully.' => 'Categoria a fost actualizată.', + 'Unable to update this category.' => 'Nu am putut actualiza categoria.', + 'Remove a category' => 'Șterge o categorie', + 'Category removed successfully.' => 'Categoria a fost È™tearsă.', + 'Unable to remove this category.' => 'Nu am putut È™terge categoria.', + 'Category modification for the project "%s"' => 'Modificarea categoriei pentru proiectul "%s"', + 'Category Name' => 'Numele categoriei', + 'Add a new category' => 'Adaugă o categorie nouă', + 'Do you really want to remove this category: "%s"?' => 'Vrei să È™tergi categoria: "%s" ?', + 'All categories' => 'Toate categoriile', + 'No category' => 'Fără categorie', + 'The name is required' => 'Numele este obligatoriu', + 'Remove a file' => 'Șterge un fiÈ™ier', + 'Unable to remove this file.' => 'Nu am putut È™terge fiÈ™ierul.', + 'File removed successfully.' => 'FiÈ™ierul a fost È™ters.', + 'Attach a document' => 'AtaÈ™ează un document', + 'Do you really want to remove this file: "%s"?' => 'Vrei să È™tergi acest fiÈ™ier: "%s" ?', + 'Attachments' => 'AtaÈ™amente', + 'Edit the task' => 'Modifică sarcina', + 'Add a comment' => 'Adaugă un comentariu', + 'Edit a comment' => 'Modifică un comentariu', + 'Summary' => 'Sumar', + 'Time tracking' => 'Urmărirea timpului', + 'Estimate:' => 'Estimat:', + 'Spent:' => 'Petrecut:', + 'Do you really want to remove this sub-task?' => 'Vrei să È™tergi această sub-sarcină?', + 'Remaining:' => 'Rămas:', + 'hours' => 'ore', + 'estimated' => 'estimate', + 'Sub-Tasks' => 'Sub-sarcini', + 'Add a sub-task' => 'Adaugă o sub-sarcină', + 'Original estimate' => 'Estimat original', + 'Create another sub-task' => 'Creează altă sub-sarcină', + 'Time spent' => 'Timp petrecut', + 'Edit a sub-task' => 'Modifică o sub-sarcină', + 'Remove a sub-task' => 'Șterge o sub-sarcină', + 'The time must be a numeric value' => 'Timpul trebuie să fie o valoare numerică', + 'Todo' => 'De făcut', + 'In progress' => 'ÃŽn curs', + 'Sub-task removed successfully.' => 'Sub-sarcină È™tearsă.', + 'Unable to remove this sub-task.' => 'Nu am putut È™terge sub-sarcina.', + 'Sub-task updated successfully.' => 'Sub-sarcină actualizată.', + 'Unable to update your sub-task.' => 'Nu am putut actualiza sub-sarcina.', + 'Unable to create your sub-task.' => 'Nu am putut crea sub-sarcina.', + 'Maximum size: ' => 'Dimensiune maximă: ', + 'Display another project' => 'AfiÈ™ează alt proiect', + 'Created by %s' => 'Creat de %s', + 'Tasks Export' => 'Exportă sarcini', + 'Start Date' => 'Data pornirii', + 'Execute' => 'Execută', + 'Task Id' => 'ID Sarcină', + 'Creator' => 'Creator', + 'Modification date' => 'Data modificării', + 'Completion date' => 'Data finalizării', + 'Clone' => 'Clonează', + 'Project cloned successfully.' => 'Proiectul a fost clonat.', + 'Unable to clone this project.' => 'Nu am putut clona proiectul.', + 'Enable email notifications' => 'Activează notificarile pe e-mail', + 'Task position:' => 'PoziÈ›ia sarcinii:', + 'The task #%d has been opened.' => 'Sarcina #%d a fost deschisă.', + 'The task #%d has been closed.' => 'Sarcina #%d a fost închisă.', + 'Sub-task updated' => 'Sub-sarcină actualizată', + 'Title:' => 'Titlu:', + 'Status:' => 'Stare:', + 'Assignee:' => 'Atribuire:', + 'Time tracking:' => 'Gestionarea timpului:', + 'New sub-task' => 'Sub-sarcină nouă', + 'New attachment added "%s"' => 'AtaÈ™ament nou adăugat "%s"', + 'New comment posted by %s' => 'Comentariu nou postat de "%s"', + 'New comment' => 'Comentariu nou', + 'Comment updated' => 'Comentariu actualizat', + 'New subtask' => 'Sub-sarcină nouă', + 'I only want to receive notifications for these projects:' => 'Vreau să primesc notificări numai pentru acele proiecte:', + 'view the task on Kanboard' => 'vezi sarcina pe Kanboard', + 'Public access' => 'Acces public', + 'Disable public access' => 'Dezactivează accesul public', + 'Enable public access' => 'Activează accesul public', + 'Public access disabled' => 'Accesul public dezactivat', + 'Move the task to another project' => 'Mută sarcina în alt proiect', + 'Move to project' => 'Mută în alt proiect', + 'Do you really want to duplicate this task?' => 'Vrei să duplichezi această sarcină?', + 'Duplicate a task' => 'Duplichează o sarcină', + 'External accounts' => 'Conturi externe', + 'Account type' => 'Tip de cont', + 'Local' => 'Local', + 'Remote' => 'La distanță', + 'Enabled' => 'Activat', + 'Disabled' => 'Dezactivat', + 'Login:' => 'Utilizator:', + 'Full Name:' => 'Nume :', + 'Email:' => 'E-mail :', + 'Notifications:' => 'Notificări:', + 'Notifications' => 'Notificări', + 'Account type:' => 'Tip de cont:', + 'Edit profile' => 'Modifică profilul', + 'Change password' => 'Schimbă parola', + 'Password modification' => 'Modificare parolă', + 'External authentications' => 'Autentificări externe', + 'Never connected.' => 'Niciodată conectat.', + 'No external authentication enabled.' => 'Nici o autentificare externă activă.', + 'Password modified successfully.' => 'Parolă modificată.', + 'Unable to change the password.' => 'Nu am putut modifica parola.', + 'Change category' => 'Schimbă categoria', + '%s updated the task %s' => '%s a actualizat sarcina %s', + '%s opened the task %s' => '%s a deschis sarcina %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s a mutat sarcina %s la poziÈ›ia #%d în coloana "%s"', + '%s moved the task %s to the column "%s"' => '%s a mutat sarcina %s în coloana "%s"', + '%s created the task %s' => '%s a creat sarcina %s', + '%s closed the task %s' => '%s a închis sarcina %s', + '%s created a subtask for the task %s' => '%s a creat o sub-sarcină pentru sarcina %s', + '%s updated a subtask for the task %s' => '%s a actualizat o sub-sarcină pentru sarcina %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Atribuit lui %s cu o estimare de %s/%s h', + 'Not assigned, estimate of %sh' => 'Nimeni atribuit, estimare de %s h', + '%s updated a comment on the task %s' => '%s a actualizat u comentariu în sarcina %s', + '%s commented the task %s' => '%s a comentat în sarcina %s', + '%s\'s activity' => 'Activitatea pentru %s', + 'RSS feed' => 'Flux RSS', + '%s updated a comment on the task #%d' => '%s a actualizat un comentariu în sarcina #%d', + '%s commented on the task #%d' => '%s a comentat în sarcina #%d', + '%s updated a subtask for the task #%d' => '%s a actualizat o sub-sarcină în sarcina #%d', + '%s created a subtask for the task #%d' => '%s a creat o sub-sarcină în sarcina #%d', + '%s updated the task #%d' => '%s a actualizat sarcina #%d', + '%s created the task #%d' => '%s a creat sarcina #%d', + '%s closed the task #%d' => '%s a închis sarcina #%d', + '%s opened the task #%d' => '%s a deschis sarcina #%d', + 'Activity' => 'Activitate', + 'Default values are "%s"' => 'Valorile implicite sunt "%s"', + 'Default columns for new projects (Comma-separated)' => 'Coloane implicite pentru proiectele noi (Separate prin virgulă)', + 'Task assignee change' => 'Modificare persoană desemnată sarcinii', + '%s changed the assignee of the task #%d to %s' => '%s a schimbat persoana desemnată sarcinii #%d lui %s', + '%s changed the assignee of the task %s to %s' => '%s a schimbat persoana desemnată sarcinii %s lui %s', + 'New password for the user "%s"' => 'Parolă nouă pentru utilizatorul "%s"', + 'Choose an event' => 'Alege un eveniment', + 'Create a task from an external provider' => 'Creează o sarcină de la un furnizor extern', + 'Change the assignee based on an external username' => 'Schimbă persoana desemnată bazat pe un nume de utilizator extern', + 'Change the category based on an external label' => 'Schimbă categoria bazat pe o etichetă externă', + 'Reference' => 'Referință', + 'Label' => 'Etichetă', + 'Database' => 'Bază de date', + 'About' => 'Despre', + 'Database driver:' => 'Tip de bază de date:', + 'Board settings' => 'PreferinÈ›e bord', + 'Webhook settings' => 'PreferinÈ›e webhook', + 'Reset token' => 'Resetare token de securitate', + 'API endpoint:' => 'URL-ul pentru API:', + 'Refresh interval for personal board' => 'Interval de reîmprospătare pentru bord privat', + 'Refresh interval for public board' => 'Interval de reîmprospătare pentru bord public', + 'Task highlight period' => 'Perioada de evidenÈ›iere a sarcinii', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Durata în secunde pentru a considera o sarcină recent modificată (0 pentru dezactivare, 2 zile implicit)', + 'Frequency in second (60 seconds by default)' => 'Frecvență în secunde (60 secunde implicit)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frecvență în secunde (0 pentru dezactivare, 10 secunde implicit)', + 'Application URL' => 'URL-ul aplicaÈ›iei', + 'Token regenerated.' => 'Token de securitate regenerat.', + 'Date format' => 'Formatare dată', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Formatul ISO este întotdeauna acceptat, exemplu: "%s" È™i "%s"', + 'New personal project' => 'Proiect privat nou', + 'This project is personal' => 'Acest proiect este privat', + 'Add' => 'Adaugă', + 'Start date' => 'Dată pornire', + 'Time estimated' => 'Timp estimat', + 'There is nothing assigned to you.' => 'Nu îți este atribuit nimic.', + 'My tasks' => 'Sarcinile mele', + 'Activity stream' => 'Flux de activitate', + 'Dashboard' => 'Bord', + 'Confirmation' => 'Confirmare', + 'Webhooks' => 'Webhook-uri', + 'API' => 'API', + 'Create a comment from an external provider' => 'Creează un comentariu de la un furnizor extern', + 'Project management' => 'Gestionare proiect', + 'Columns' => 'Coloane', + 'Task' => 'Sarcini', + 'Percentage' => 'Procentaj', + 'Number of tasks' => 'Număr de sarcini', + 'Task distribution' => 'DistribuÈ›ia sarcinilor', + 'Analytics' => 'Analitică', + 'Subtask' => 'Sub-sarcină', + 'User repartition' => 'Repartizare utilizatori', + 'Clone this project' => 'Clonează proiectul', + 'Column removed successfully.' => 'Coloana a fost È™tearsă.', + 'Not enough data to show the graph.' => 'Nu sunt destule date pentru afiÈ™area graficului.', + 'Previous' => 'Anterior', + 'The id must be an integer' => 'ID-ul trebuie să fie un număr întreg', + 'The project id must be an integer' => 'ID-ul proiectului trebuie să fie un număr întreg', + 'The status must be an integer' => 'Starea trebuie să fie un număr întreg', + 'The subtask id is required' => 'ID-ul sub-sarcinii este obligatoriu', + 'The subtask id must be an integer' => 'ID-ul sub-sarcinii trebuie sa fie un număr întreg', + 'The task id is required' => 'ID-ul sarcinii este obligatoriu', + 'The task id must be an integer' => 'ID-ul sarcinii trebuie să fie un număr întreg', + 'The user id must be an integer' => 'ID-ul utilizatorului trebuie să fie un număr întreg', + 'This value is required' => 'Valoarea aceasta este obligatorie', + 'This value must be numeric' => 'Valoarea aceasta trebuie să fie numerică', + 'Unable to create this task.' => 'Nu am putut crea sarcina', + 'Cumulative flow diagram' => 'Diagrama fluxului cumulat', + 'Daily project summary' => 'Rezumatul zilnic al proiectului', + 'Daily project summary export' => 'Export rezumat zilnic al proiectului', + 'Exports' => 'Exporturi', + 'This export contains the number of tasks per column grouped per day.' => 'Acest export conÈ›ine numărul de sarcini per coloană grupat pe zile.', + 'Active swimlanes' => 'Culoare active', + 'Add a new swimlane' => 'Adaugă culoar nou', + 'Default swimlane' => 'Culoar implicit', + 'Do you really want to remove this swimlane: "%s"?' => 'Vrei să È™tergi culoarul: "%s" ?', + 'Inactive swimlanes' => 'Culoare inactive', + 'Remove a swimlane' => 'Șterge un culoar', + 'Swimlane modification for the project "%s"' => 'Modificarea culoarului pentru proiectul "%s"', + 'Swimlane removed successfully.' => 'Culoarul a fost È™ters', + 'Swimlanes' => 'Culoare', + 'Swimlane updated successfully.' => 'Culoar actualizat.', + 'Unable to remove this swimlane.' => 'Nu am putut È™terge culoarul.', + 'Unable to update this swimlane.' => 'Nu am putut actualiza culoarul.', + 'Your swimlane has been created successfully.' => 'Culoarul a fost creat.', + 'Example: "Bug, Feature Request, Improvement"' => 'Exemplu: "Incident, Cere caracteristică, ÃŽmbunătățire"', + 'Default categories for new projects (Comma-separated)' => 'Categorii implicite pentru proiecte noi (Separate prin virgulă)', + 'Integrations' => 'Integrări', + 'Integration with third-party services' => 'Integrări cu servicii externe', + 'Subtask Id' => 'ID sub-sarcină', + 'Subtasks' => 'Sub-sarcini', + 'Subtasks Export' => 'Export sub-sarcini', + 'Task Title' => 'Titlu sarcină', + 'Untitled' => 'Fără titlu', + 'Application default' => 'Implicit din aplicaÈ›ie', + 'Language:' => 'Limbă:', + 'Timezone:' => 'Fus orar:', + 'All columns' => 'Toate coloanele', + 'Next' => 'Următor', + '#%d' => '#%d', + 'All swimlanes' => 'Toate culoarele', + 'All colors' => 'Toate culorile', + 'Moved to column %s' => 'Mutat în coloana %s', + 'User dashboard' => 'Bordul utilizatorului', + 'Allow only one subtask in progress at the same time for a user' => 'Permite o singură sub-sarcină în derulare simultană pentru un utilizator', + 'Edit column "%s"' => 'Modifică coloana "%s"', + 'Select the new status of the subtask: "%s"' => 'Alege noua stare a sarcinii: "%s"', + 'Subtask timesheet' => 'Pontajul sub-sarcinii', + 'There is nothing to show.' => 'Nimic de afiÈ™at', + 'Time Tracking' => 'Urmărirea timpului', + 'You already have one subtask in progress' => 'Deja ai o sub-sarcină în desfășurare', + 'Which parts of the project do you want to duplicate?' => 'Care părÈ›i ale proiectului vrei să fie duplicate?', + 'Disallow login form' => 'Interzice formularul de autentificare', + 'Start' => 'Start', + 'End' => 'Final', + 'Task age in days' => 'Vârsta sarcinii în zile', + 'Days in this column' => 'Zile în această coloană', + '%dd' => '%d z', + 'Add a new link' => 'Adaugă legătură nouă', + 'Do you really want to remove this link: "%s"?' => 'Vrei să È™tergi legătura: "%s" ?', + 'Do you really want to remove this link with task #%d?' => 'Vrei să È™tergi legătura cu sarcina #%d ?', + 'Field required' => 'Câmp obligatoriu', + 'Link added successfully.' => 'Legătură adăugată.', + 'Link updated successfully.' => 'Legătură actualizată.', + 'Link removed successfully.' => 'Legătură È™tearsă.', + 'Link labels' => 'Etichete de legături', + 'Link modification' => 'Modificare legătură', + 'Opposite label' => 'Etichetă opusă', + 'Remove a link' => 'Șterge o legătură', + 'The labels must be different' => 'Etichetele trebuie să difere', + 'There is no link.' => 'Nu există legătură.', + 'This label must be unique' => 'Eticheta trebuie să fie unică', + 'Unable to create your link.' => 'Nu pot crea legătura.', + 'Unable to update your link.' => 'Nu pot actualiza legătura.', + 'Unable to remove this link.' => 'Nu pot È™terge legătura.', + 'relates to' => 'se leagă de', + 'blocks' => 'blochează', + 'is blocked by' => 'este blocat de', + 'duplicates' => 'duplichează', + 'is duplicated by' => 'este duplicat de', + 'is a child of' => 'este element moÈ™tenitor al', + 'is a parent of' => 'este element parental al', + 'targets milestone' => 'vizează È›elul', + 'is a milestone of' => 'este un È›el a', + 'fixes' => 'corectează', + 'is fixed by' => 'este corectat de', + 'This task' => 'Această sarcină', + '<1h' => '< 1 h', + '%dh' => '%d h', + 'Expand tasks' => 'Extinde sarcinile', + 'Collapse tasks' => 'Restrânge sarcinile', + 'Expand/collapse tasks' => 'Extinde/restrânge sarcinile', + 'Close dialog box' => 'ÃŽnchide dialogul', + 'Submit a form' => 'Depune un formular', + 'Board view' => 'Vizualizare bord', + 'Keyboard shortcuts' => 'Scurtături de tastatură', + 'Open board switcher' => 'Deschide comutatorul de bord', + 'Application' => 'AplicaÈ›ie', + 'Compact view' => 'Vizualizare restrânsă', + 'Horizontal scrolling' => 'Derulare orizontală', + 'Compact/wide view' => 'Vizualizare compactă/lată', + 'Currency' => 'Monedă', + 'Personal project' => 'Proiect privat', + 'AUD - Australian Dollar' => 'AUD - Dolar australian', + 'CAD - Canadian Dollar' => 'CAD - Dolar canadian', + 'CHF - Swiss Francs' => 'CHF - Franc elvetian', + 'Custom Stylesheet' => 'Stylesheet personalizat', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Liră sterlină', + 'INR - Indian Rupee' => 'INR - Rupie indiană', + 'JPY - Japanese Yen' => 'JPY - Yen japonez', + 'NZD - New Zealand Dollar' => 'NZD - Dolar neo-zeelandez', + 'PEN - Peruvian Sol' => 'PEN - Sol Peruvian', + 'RSD - Serbian dinar' => 'RSD - Dinar sârbesc', + 'CNY - Chinese Yuan' => 'CNY - Yuan chinez', + 'USD - US Dollar' => 'USD - Dolar american', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar Venezuelan', + 'Destination column' => 'Coloana destinaÈ›ie', + 'Move the task to another column when assigned to a user' => 'Mută sarcina în altă coloană când este atribuită unui utilizator', + 'Move the task to another column when assignee is cleared' => 'Mută sarcina în altă coloană când este eliberată atribuirea', + 'Source column' => 'Coloana sursă', + 'Transitions' => 'Tranzitii', + 'Executer' => 'Executant', + 'Time spent in the column' => 'Timp petrecut în coloană', + 'Task transitions' => 'TranziÈ›iile sarcinilor', + 'Task transitions export' => 'Exportă tranziÈ›iile sarcinilor', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Acest raport conÈ›ine toate mutările de coloană pentru sarcinile cu data, utilizatorul È™i timpul petrecut pentru fiecare tranziÈ›ie.', + 'Currency rates' => 'Rate de schimb', + 'Rate' => 'Rată', + 'Change reference currency' => 'Schimbă moneda de referință', + 'Reference currency' => 'Moneda de referință', + 'The currency rate has been added successfully.' => 'Rata de schimb a fost adăugată.', + 'Unable to add this currency rate.' => 'Nu am putut adăuga rata de schimb', + 'Webhook URL' => 'URL pentru webhook', + '%s removed the assignee of the task %s' => '%s a eliberat persoana atribuită sarcinii %s', + 'Information' => 'InformaÈ›ii', + 'Check two factor authentication code' => 'Verificare cod autentificare în doi factori', + 'The two factor authentication code is not valid.' => 'Codul de autentificare în doi factori nu este valid.', + 'The two factor authentication code is valid.' => 'Codul de autentificare în doi factori este valid.', + 'Code' => 'Cod', + 'Two factor authentication' => 'Autentificare în doi factori', + 'This QR code contains the key URI: ' => 'Codul QR contine URI-ul cheii: ', + 'Check my code' => 'Verifică codul meu', + 'Secret key: ' => 'Cheia secretă: ', + 'Test your device' => 'Testează dispozitivul tău', + 'Assign a color when the task is moved to a specific column' => 'Atribuie o culoare când sarcina este mutată într-o anumită coloană', + '%s via Kanboard' => '%s via Kanboard', + 'Burndown chart' => 'Graficul progresului', + 'This chart show the task complexity over the time (Work Remaining).' => 'Acest grafic arată complexitatea sarcinii de-a lungul timpului (munca rămasă).', + 'Screenshot taken %s' => 'Captură de ecran %s', + 'Add a screenshot' => 'Adaugă captură de ecran', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'FaceÈ›i o captura de ecran si apăsaÈ›i CTRL+V sau ⌘+V pentru a lipi aici.', + 'Screenshot uploaded successfully.' => 'Captura de ecran a fost încărcată.', + 'SEK - Swedish Krona' => 'SEK - coroană suedeză', + 'Identifier' => 'Identificator', + 'Disable two factor authentication' => 'Dezactivează autentificarea în doi factori', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Vrei să dezactivezi autentificarea în doi factori pentru utilizatorul: "%s" ?', + 'Edit link' => 'Modifică o legătură', + 'Start to type task title...' => 'Scrie titlul sarcinii…', + 'A task cannot be linked to itself' => 'O sarcină nu se poate lega de ea însăși', + 'The exact same link already exists' => 'O legătură identică există deja', + 'Recurrent task is scheduled to be generated' => 'Sarcina recurentă este programată să fie generată', + 'Score' => 'Complexitate', + 'The identifier must be unique' => 'Identificatorul trebuie să fie unic', + 'This linked task id doesn\'t exists' => 'ID-ul de sarcină legat nu există', + 'This value must be alphanumeric' => 'Valoarea trebuie să fie alfanumerică', + 'Edit recurrence' => 'Modifică recurenÈ›a', + 'Generate recurrent task' => 'Generează sarcină recurentă', + 'Trigger to generate recurrent task' => 'DeclanÈ™ator pentru generarea sarcinii recurente', + 'Factor to calculate new due date' => 'Factor de calculare a noii date scadente', + 'Timeframe to calculate new due date' => 'Interval de timp pentru calcularea noii date scadente', + 'Base date to calculate new due date' => 'Data de bază pentru calcularea noii date scadente', + 'Action date' => 'Data acÈ›iunii', + 'Base date to calculate new due date: ' => 'Data de bază pentru calcularea noii date scadente: ', + 'This task has created this child task: ' => 'Sarcina aceasta a creat această sarcină-moÈ™tenitor: ', + 'Day(s)' => 'Zi(le)', + 'Existing due date' => 'Dată scadentă existentă', + 'Factor to calculate new due date: ' => 'Factor de calculare a noii date scadente: ', + 'Month(s)' => 'Lună(/i)', + 'This task has been created by: ' => 'Această sarcină a fost creată de:', + 'Recurrent task has been generated:' => 'Sarcina recurentă a fost generată:', + 'Timeframe to calculate new due date: ' => 'Interval de timp pentru calcularea noii date scadente: ', + 'Trigger to generate recurrent task: ' => 'DeclanÈ™ator pentru generarea sarcinii recurente: ', + 'When task is closed' => 'Când sarcina este închisă', + 'When task is moved from first column' => 'Când sarcina este mutată din prima coloană', + 'When task is moved to last column' => 'Când sarcina este mutată în ultima coloană', + 'Year(s)' => 'An(i)', + 'Project settings' => 'PreferinÈ›e de proiect', + 'Automatically update the start date' => 'Actualizează automat data de start', + 'iCal feed' => 'Flux iCal', + 'Preferences' => 'PreferinÈ›e', + 'Security' => 'Securitate', + 'Two factor authentication disabled' => 'Autentificare în doi factori dezactivată', + 'Two factor authentication enabled' => 'Autentificare în doi factori activată', + 'Unable to update this user.' => 'Nu am putut actualiza utilizatorul.', + 'There is no user management for personal projects.' => 'Nu poÈ›i gestiona utilizatori pentru proiecte private.', + 'User that will receive the email' => 'Utilizatorul care va primi e-mail-ul', + 'Email subject' => 'Subiectul e-mail-ului', + 'Date' => 'Data', + 'Add a comment log when moving the task between columns' => 'ÃŽnregistrează un comentariu în jurnal când se mută sarcina între coloane', + 'Move the task to another column when the category is changed' => 'Mută sarcina în altă coloană când se schimbă categoria', + 'Send a task by email to someone' => 'Trimite o sarcină prin e-mail cuiva', + 'Reopen a task' => 'Redeschide o sarcină', + 'Notification' => 'Notificare', + '%s moved the task #%d to the first swimlane' => '%s a mutat sarcina #%d în primul culoar', + 'Swimlane' => 'Culoar', + '%s moved the task %s to the first swimlane' => '%s a mutat sarcina %s în primul culoar', + '%s moved the task %s to the swimlane "%s"' => '%s a mutat sarcina %s în culoarul "%s"', + 'This report contains all subtasks information for the given date range.' => 'Raportul conÈ›ine toate informaÈ›iile sub-sarcinilor pentru intervalul dat.', + 'This report contains all tasks information for the given date range.' => 'Raportul conÈ›ine toate informaÈ›iile sarcinilor pentru intervalul dat.', + 'Project activities for %s' => 'Activități de proiect pentru "%s"', + 'view the board on Kanboard' => 'vezi bordul în Kanboard', + 'The task has been moved to the first swimlane' => 'Sarcina a fost mutată în primul culoar', + 'The task has been moved to another swimlane:' => 'Sarcina a fost mutată în alt culoar:', + 'New title: %s' => 'Titlu nou: %s', + 'The task is not assigned anymore' => 'Sarcina nu mai este desemnată', + 'New assignee: %s' => 'Desemnat nou: %s', + 'There is no category now' => 'Nu mai există categorii acum', + 'New category: %s' => 'Categorie nouă: %s', + 'New color: %s' => 'Culoare nouă: %s', + 'New complexity: %d' => 'Complexitate nouă: %d', + 'The due date has been removed' => 'Data scadentă a fost È™tearsă', + 'There is no description anymore' => 'Nu mai există o descriere', + 'Recurrence settings has been modified' => 'PreferinÈ›ele de recurență au fost modificate', + 'Time spent changed: %sh' => 'Timpul petrecut s-a schimbat: %s h', + 'Time estimated changed: %sh' => 'Timpul estimat s-a schimbat: %s h', + 'The field "%s" has been updated' => 'Câmpul "%s" a fost actualizat', + 'The description has been modified:' => 'Descrierea a fost modificată', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vrei să închizi sarcina "%s" inclusiv toate sub-sarcinile?', + 'I want to receive notifications for:' => 'Vreau sa primesc notificări pentru:', + 'All tasks' => 'Toate Sarcinile', + 'Only for tasks assigned to me' => 'Numai sarcinile atribuite mie', + 'Only for tasks created by me' => 'Numai sarcinile create de mine', + 'Only for tasks created by me and tasks assigned to me' => 'Numai sarcinile create de mine si atribuite mie.', + '%%Y-%%m-%%d' => '%%d/%%m/%%Y', + 'Total for all columns' => 'Totalul tuturor coloanelor', + 'You need at least 2 days of data to show the chart.' => 'Ai nevoie de cel putin 2 zile de date pentru afiÈ™area graficului.', + '<15m' => '< 15 min', + '<30m' => '< 30 min', + 'Stop timer' => 'OpreÈ™te cronometrul', + 'Start timer' => 'PorneÈ›e cronometrul', + 'My activity stream' => 'Fluxul meu de activități', + 'Search tasks' => 'Caută sarcini', + 'Reset filters' => 'Resetează filtre', + 'My tasks due tomorrow' => 'Sarcinile mele scadente mâine', + 'Tasks due today' => 'Sarcini scadente astăzi', + 'Tasks due tomorrow' => 'Sarcini scadente mâine', + 'Tasks due yesterday' => 'Sarcini scadente ieri', + 'Closed tasks' => 'Sarcini închise', + 'Open tasks' => 'Sarcini deschise', + 'Not assigned' => 'Fără atribuire', + 'View advanced search syntax' => 'Vezi sintaxa de căutare avansată', + 'Overview' => 'ÃŽn ansamblu', + 'Board/Calendar/List view' => 'Bord/Calendar/Listă', + 'Switch to the board view' => 'Schimbă la bord', + 'Switch to the list view' => 'Schimbă la listă', + 'Go to the search/filter box' => 'Mergi la câmpul de căutare/filtre', + 'There is no activity yet.' => 'Nu există activitate încă.', + 'No tasks found.' => 'Nu s-au găsit sarcini.', + 'Keyboard shortcut: "%s"' => 'Scurtătură tastatură : "%s"', + 'List' => 'Listă', + 'Filter' => 'Filtru', + 'Advanced search' => 'Căutare avansată', + 'Example of query: ' => 'Exemplu de interogare: ', + 'Search by project: ' => 'Caută după proiect: ', + 'Search by column: ' => 'Caută după coloană: ', + 'Search by assignee: ' => 'Caută după desemnat: ', + 'Search by color: ' => 'Caută după culoare: ', + 'Search by category: ' => 'Caută după categorie: ', + 'Search by description: ' => 'Caută după descriere: ', + 'Search by due date: ' => 'Caută după dată scadentă: ', + 'Average time spent in each column' => 'Timp mediu petrecut în fiecare coloană', + 'Average time spent' => 'Timp mediu utilizat', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Acest grafic arată timpul mediu petrecut în fiecare coloană pentru ultimele %d sarcini.', + 'Average Lead and Cycle time' => 'Durată medie de Avans si Ciclu', + 'Average lead time: ' => 'Medie durată avans: ', + 'Average cycle time: ' => 'Medie durata ciclu: ', + 'Cycle Time' => 'Timp ciclu', + 'Lead Time' => 'Timp avans', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Acest grafic arată media duratelor de avans È™i ciclu pentru ultimele %d sarcini de-a lungul timpului.', + 'Average time into each column' => 'Durată medie în fiecare coloană', + 'Lead and cycle time' => 'Timpuri de avans È™i ciclu', + 'Lead time: ' => 'Timp avans: ', + 'Cycle time: ' => 'Timp ciclu: ', + 'Time spent in each column' => 'Timp petrecut prin fiecare coloană', + 'The lead time is the duration between the task creation and the completion.' => 'Timpul de avans este durata între crearea sarcinii È™i finalizarea.', + 'The cycle time is the duration between the start date and the completion.' => 'Timpul de ciclu este durata între data de pornire È™i finalizare.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Dacă sarcina nu este închisă data curentă este folosită în locul dății de finalizare.', + 'Set the start date automatically' => 'Setează automat data pornirii.', + 'Edit Authentication' => 'Modifică autentificarea', + 'Remote user' => 'Utilizator la distanță', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Utilizatorii la distanță nu păstrează parola în baza de date locală, de exemplu : conturi LDAP, GitHub sau Google.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Dacă bifezi "Interzice formularul de autentificare", acreditările introduse în dialogul de conectare vor fi ignorate.', + 'Default task color' => 'Culoarea implicită a sarcinii', + 'This feature does not work with all browsers.' => 'Această funcÈ›ionalitate nu funcÈ›ionează cu toate browserele.', + 'There is no destination project available.' => 'Nu este disponibil un proiect destinaÈ›ie.', + 'Trigger automatically subtask time tracking' => 'DeclanÈ™ează automat cronometrarea timpului în sub-sarcini.', + 'Include closed tasks in the cumulative flow diagram' => 'Include sarcini închise în graficul fluxurilor cumulate', + 'Current swimlane: %s' => 'Culoarul curent: %s', + 'Current column: %s' => 'Coloana curentă: %s', + 'Current category: %s' => 'Categoria curentă: %s', + 'no category' => 'fără categorie', + 'Current assignee: %s' => 'Desemnat curent: %s', + 'not assigned' => 'fără desemnare', + 'Author:' => 'Autor:', + 'contributors' => 'contribuitori', + 'License:' => 'Licență:', + 'License' => 'Licență', + 'Enter the text below' => 'Introdu textul mai jos', + 'Start date:' => 'Data pornirii:', + 'Due date:' => 'Data scadentă:', + 'People who are project managers' => 'Persoane care gestionează proiectul', + 'People who are project members' => 'Persoane care participă la proiect', + 'NOK - Norwegian Krone' => 'NOK - Coroană norvegiană', + 'Show this column' => 'Arată această coloană', + 'Hide this column' => 'Ascunde această coloană', + 'End date' => 'Dată finalizare', + 'Users overview' => 'Prezentare generală utilizatori', + 'Members' => 'Membrii', + 'Shared project' => 'Proiect partajat', + 'Project managers' => 'Manageri de proiect', + 'Projects list' => 'Listă proiecte', + 'End date:' => 'Dată finalizare:', + 'Change task color when using a specific task link' => 'Schimbă culoarea sarcinii când se foloseÈ™te o anumită legătură în sarcină', + 'Task link creation or modification' => 'Creare sau modificare legături sarcină', + 'Milestone' => 'Èšel', + 'Reset the search/filter box' => 'Resetează dialogul de căutare/filtre', + 'Documentation' => 'DocumentaÈ›ie', + 'Author' => 'Autor', + 'Version' => 'Versiuni', + 'Plugins' => 'Extensii', + 'There is no plugin loaded.' => 'Nu sunt încărcate extensii.', + 'My notifications' => 'Notificările mele', + 'Custom filters' => 'Filtre personalizate', + 'Your custom filter has been created successfully.' => 'Filtrul tău personalizat a fost creat.', + 'Unable to create your custom filter.' => 'Nu am putut crea filtrul tău personalizat.', + 'Custom filter removed successfully.' => 'Filtrul personalizat a fost È™ters.', + 'Unable to remove this custom filter.' => 'Nu am putut È™terge filtrul presonalizat.', + 'Edit custom filter' => 'Modifică un filtru personalizat', + 'Your custom filter has been updated successfully.' => 'Filtrul tău personalizat a fost actualizat.', + 'Unable to update custom filter.' => 'Nu am putut actualiza filtrul personalizat.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'AtaÈ™ament nou în sarcina #%d: %s', + 'New comment on task #%d' => 'Comentariu nou în sarcina #%d', + 'Comment updated on task #%d' => 'Comentariu actualizat în sarcina #%d', + 'New subtask on task #%d' => 'Sub-sarcină nouă în sarcina #%d', + 'Subtask updated on task #%d' => 'Sub-sarcină actualizată în sarcina #%d', + 'New task #%d: %s' => 'Sarcină nouă #%d: %s', + 'Task updated #%d' => 'Sarcina #%d a fost actualizată', + 'Task #%d closed' => 'Sarcina #%d a fost închisă', + 'Task #%d opened' => 'Sarcina #%d a fost deschisă', + 'Column changed for task #%d' => 'Coloană schimbată pentru sarcina #%d', + 'New position for task #%d' => 'PoziÈ›ie nouă pentru sarcina #%d', + 'Swimlane changed for task #%d' => 'Culoarul schimbat pentru sarcina #%d', + 'Assignee changed on task #%d' => 'Persoana desemnată a fost schimbată în sarcina #%d', + '%d overdue tasks' => '%d sarcini întârziate', + 'No notification.' => 'Fără notificări.', + 'Mark all as read' => 'Marchează toate ca citite', + 'Mark as read' => 'Marchează citit', + 'Total number of tasks in this column across all swimlanes' => 'Număr total de sarcini în această coloană peste toate culoarele', + 'Collapse swimlane' => 'Restrânge culoarul', + 'Expand swimlane' => 'Extinde culoarul', + 'Add a new filter' => 'Adaugă un filtru nou', + 'Share with all project members' => 'Partajează cu toÈ›i membrii proiectului', + 'Shared' => 'Partajat', + 'Owner' => 'Proprietar', + 'Unread notifications' => 'Notificări necitite', + 'Notification methods:' => 'Metode de notificare:', + 'Unable to read your file' => 'Nu am putut citi fiÈ™ierul', + '%d task(s) have been imported successfully.' => 'Sarcini importate cu succes: %d.', + 'Nothing has been imported!' => 'Nu s-a importat nimic!', + 'Import users from CSV file' => 'Importă utilizatori dintr-un fiÈ™ier CSV', + '%d user(s) have been imported successfully.' => 'Utilizatori importaÈ›i cu succes: %d.', + 'Comma' => 'Virgulă', + 'Semi-colon' => 'Punct È™i virgulă', + 'Tab' => 'Tab', + 'Vertical bar' => 'Bară verticală', + 'Double Quote' => 'Ghilimele duble', + 'Single Quote' => 'Ghilimele simple', + '%s attached a file to the task #%d' => '%s a ataÈ™at un fiÈ™ier sarcinii #%d', + 'There is no column or swimlane activated in your project!' => 'Nu există vreo coloană sau culoar activ în proiectul tău!', + 'Append filter (instead of replacement)' => 'Adaugă filtru (în loc de înlocuire)', + 'Append/Replace' => 'Adaugă/ÃŽnlocuieÈ™te', + 'Append' => 'Adaugă', + 'Replace' => 'ÃŽnlocuieÈ™te', + 'Import' => 'Import', + 'Change sorting' => 'Schimbă ordonarea', + 'Tasks Importation' => 'Import de sarcini', + 'Delimiter' => 'Delimitare', + 'Enclosure' => 'Caractere de încadrare', + 'CSV File' => 'FiÈ™ier CSV', + 'Instructions' => 'InstrucÈ›iuni', + 'Your file must use the predefined CSV format' => 'Trebuie să foloseÈ™ti formatarea CSV predefinită', + 'Your file must be encoded in UTF-8' => 'FiÈ™ierul trebuie să fie codificat în UTF-8', + 'The first row must be the header' => 'Primul rând trebuie să fie antetul', + 'Duplicates are not verified for you' => 'Nu este verificată existenÈ›a duplicatelor', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Data scadentă trebuie să folosească formatarea ISO : AAAA-LL-ZZ', + 'Download CSV template' => 'Descarcă modelul CSV', + 'No external integration registered.' => 'Nu a fost înregistrată o integrare externă.', + 'Duplicates are not imported' => 'Duplicatele nu sunt importate', + 'Usernames must be lowercase and unique' => 'Numele de utilizator trebuie să aibă caractere minuscule È™i să fie unice', + 'Passwords will be encrypted if present' => 'Parolele vor fi criptate dacă există', + '%s attached a new file to the task %s' => '%s a ataÈ™at un fiÈ™ier nou la sarcina %s', + 'Link type' => 'Tip de legătură', + 'Assign automatically a category based on a link' => 'Desemnează categoria automat în funcÈ›ie de legătură', + 'BAM - Konvertible Mark' => 'BAM - Marcă bosniană convertibilă', + 'Assignee Username' => 'Utilizatorul desemnat', + 'Assignee Name' => 'Numele desemnatului', + 'Groups' => 'Groupuri', + 'Members of %s' => 'Membri al %s', + 'New group' => 'Grup nou', + 'Group created successfully.' => 'Grupul a fost creat.', + 'Unable to create your group.' => 'Nu am putut crea grupul.', + 'Edit group' => 'Modifică grup', + 'Group updated successfully.' => 'Grupul a fost actualizat.', + 'Unable to update your group.' => 'Nu am putut actualiza grupul.', + 'Add group member to "%s"' => 'Adaugă membru de grup în "%s"', + 'Group member added successfully.' => 'Membru de grup adăugat.', + 'Unable to add group member.' => 'Nu am putut adăuga membrul de grup.', + 'Remove user from group "%s"' => 'Șterge utilizatorul din grupul "%s"', + 'User removed successfully from this group.' => 'Utilizatorul a fost È™ters din grup.', + 'Unable to remove this user from the group.' => 'Nu am putut È™terge utilizatorul din grup.', + 'Remove group' => 'Șterge grupul', + 'Group removed successfully.' => 'Grupul a fost È™ters.', + 'Unable to remove this group.' => 'Nu am putut È™terge grupul.', + 'Project Permissions' => 'Permisiunile proiectului', + 'Manager' => 'Gestionar', + 'Project Manager' => 'Șef de proiect', + 'Project Member' => 'Membru de proiect', + 'Project Viewer' => 'Vizualizator de proiect', + 'Your account is locked for %d minutes' => 'Contul tău este blocat %d minute', + 'Invalid captcha' => 'Captcha invalid', + 'The name must be unique' => 'Numele trebuie să fie unic', + 'View all groups' => 'Vezi toate grupurile', + 'There is no user available.' => 'Nu există utilizator disponibil', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Vrei să È™tergi utilizatorul "%s" din grupul "%s" ?', + 'There is no group.' => 'Nu există grup.', + 'Add group member' => 'Adaugă membru în grup', + 'Do you really want to remove this group: "%s"?' => 'Vrei să È™tergi acest grup: "%s" ?', + 'There is no user in this group.' => 'Nu există utilizatori în acest grup', + 'Permissions' => 'Permisiuni', + 'Allowed Users' => 'Utilizatori autorizaÈ›i', + 'No specific user has been allowed.' => 'Nu a fost autorizat vreun utilizator.', + 'Role' => 'Rol', + 'Enter user name...' => 'Introdu numele de utilizator…', + 'Allowed Groups' => 'Grupuri autorizate', + 'No group has been allowed.' => 'Nu a fost autorizat vreun grup.', + 'Group' => 'Grup', + 'Group Name' => 'Nume grup', + 'Enter group name...' => 'Introdu numele de grup…', + 'Role:' => 'Rol:', + 'Project members' => 'Membri de proiect', + '%s mentioned you in the task #%d' => '%s te-a menÈ›ionat în sarcina #%d', + '%s mentioned you in a comment on the task #%d' => '%s te-a menÈ›ionat într-un comentariu în sarcina #%d', + 'You were mentioned in the task #%d' => 'Ai fost menÈ›ionat în sarcina #%d', + 'You were mentioned in a comment on the task #%d' => 'Ai fost menÈ›ionat într-un comentariu în sarcina #%d', + 'Estimated hours: ' => 'Ore estimate: ', + 'Actual hours: ' => 'Ore actuale: ', + 'Hours Spent' => 'Ore petrecute', + 'Hours Estimated' => 'Ore estimate', + 'Estimated Time' => 'Timp estimat', + 'Actual Time' => 'Timp actual', + 'Estimated vs actual time' => 'Timp estimat vs actual', + 'RUB - Russian Ruble' => 'RUB - Rublă rusească', + 'Assign the task to the person who does the action when the column is changed' => 'Atribuie sarcina persoanei care acÈ›ionează când este schimbată coloana', + 'Close a task in a specific column' => 'ÃŽnchide o sarcină într-o anumită coloană', + 'Time-based One-time Password Algorithm' => 'Parolă de unică folosință bazată pe timp', + 'Two-Factor Provider: ' => 'Furnizor autentificare în doi factori: ', + 'Disable two-factor authentication' => 'Dezactivează autentificarea în doi factori', + 'Enable two-factor authentication' => 'Activează autentificarea în doi factori', + 'There is no integration registered at the moment.' => 'Nu este vreo integrare înregistrată momentan.', + 'Password Reset for Kanboard' => 'Resetare parolă pentru Kanboard', + 'Forgot password?' => 'Parolă uitată?', + 'Enable "Forget Password"' => 'Activează "Parolă Uitată"', + 'Password Reset' => 'Resetare de parolă', + 'New password' => 'Parolă nouă', + 'Change Password' => 'Schimbă parola', + 'To reset your password click on this link:' => 'Pentru a reseta parola apasă pe acest link:', + 'Last Password Reset' => 'Ultima resetare de parolă', + 'The password has never been reinitialized.' => 'Parola nu a fost resetată vreodată.', + 'Creation' => 'Creare', + 'Expiration' => 'Expirare', + 'Password reset history' => 'Istoricul resetărilor de parolă', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Toate sarcinile coloanei "%s" si a culoarului "%s" au fost închise.', + 'Do you really want to close all tasks of this column?' => 'Vrei să închizi toate sarcinile acestei coloane?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d sarcini în coloana "%s" si culoarul "%s" vor fi închise.', + 'Close all tasks in this column and this swimlane' => 'ÃŽnchide toate sarcinile acestei coloane', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nici o extensie nu a înregistrat o metodă de notificare a proiectelor. Mai poÈ›i însă configura notificări individuale în profilul tău de utilizator.', + 'My dashboard' => 'Bordul meu', + 'My profile' => 'Profilul meu', + 'Project owner: ' => 'Responsabil de proiect: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identificatorul de proiect este opÈ›ional È™i trebuie să fie alfanumeric, exemplu: PROIECTULMEU.', + 'Project owner' => 'Responsabil de proiect', + 'Personal projects do not have users and groups management.' => 'Proiectele private nu au utilizatori È™i gestionare de grupuri.', + 'There is no project member.' => 'Nu există membri de proiect.', + 'Priority' => 'Prioritate', + 'Task priority' => 'Priorități de sarcini', + 'General' => 'General', + 'Dates' => 'Date', + 'Default priority' => 'Prioritate implicită', + 'Lowest priority' => 'Prioritate mică', + 'Highest priority' => 'Prioritate maximă', + 'Close a task when there is no activity' => 'ÃŽnchide o sarcină când nu are activitate', + 'Duration in days' => 'Durată în zile', + 'Send email when there is no activity on a task' => 'Trimite e-mail când o sarcină nu are activitate', + 'Unable to fetch link information.' => 'Nu am putut aduce informaÈ›ia legăturii.', + 'Daily background job for tasks' => 'FuncÈ›ia zilnică de fundal pentru sarcini', + 'Auto' => 'Auto', + 'Related' => 'Asociat', + 'Attachment' => 'AtaÈ™ament', + 'Web Link' => 'Link web', + 'External links' => 'Legături externe', + 'Add external link' => 'Adaugă legătură externă', + 'Type' => 'Tip', + 'Dependency' => 'Dependență', + 'Add internal link' => 'Adaugă legătură internă', + 'Add a new external link' => 'Adaugă o legătură externă nouă', + 'Edit external link' => 'Modifică legătura externă', + 'External link' => 'Legătură externă', + 'Copy and paste your link here...' => 'LipeÈ™te link-ul tău aici…', + 'URL' => 'URL', + 'Internal links' => 'Legături interne', + 'Assign to me' => 'Atribuie mie', + 'Me' => 'Eu', + 'Do not duplicate anything' => 'Nu duplica nimic', + 'Projects management' => 'Gestionează proiecte', + 'Users management' => 'Gestionează utilizatori', + 'Groups management' => 'Gestionează grupuri', + 'Create from another project' => 'Creează din alt proiect', + 'open' => 'deschis', + 'closed' => 'închis', + 'Priority:' => 'Prioritate:', + 'Reference:' => 'Referință:', + 'Complexity:' => 'Complexitate:', + 'Swimlane:' => 'Culoar:', + 'Column:' => 'Coloană:', + 'Position:' => 'PoziÈ›ie:', + 'Creator:' => 'Creator:', + 'Time estimated:' => 'Timp estimat:', + '%s hours' => '%s ore', + 'Time spent:' => 'Timp petrecut:', + 'Created:' => 'Creat de:', + 'Modified:' => 'Modificat:', + 'Completed:' => 'Finalizat:', + 'Started:' => 'Pornit:', + 'Moved:' => 'Mutat: ', + 'Task #%d' => 'Sarcina #%d', + 'Time format' => 'Format oră', + 'Start date: ' => 'Data pornirii: ', + 'End date: ' => 'Data finalizării: ', + 'New due date: ' => 'Dată scadentă nouă: ', + 'Start date changed: ' => 'Data pornirii modificată: ', + 'Disable personal projects' => 'Dezactivează proiectele private', + 'Do you really want to remove this custom filter: "%s"?' => 'Vrei să È™tergi filtrul personalizat: "%s" ?', + 'Remove a custom filter' => 'Șterge un filtru personalizat', + 'User activated successfully.' => 'Utilizatorul a fost activat.', + 'Unable to enable this user.' => 'Nu am putut activa utilizatorul.', + 'User disabled successfully.' => 'Utilizatorul a fost dezactivat.', + 'Unable to disable this user.' => 'Nu am putut dezactiva utilizatorul.', + 'All files have been uploaded successfully.' => 'Toate fiÈ™ierele au fost încărcate.', + 'The maximum allowed file size is %sB.' => 'Dimensiunea maximă a fiÈ™ierului este %sB.', + 'Drag and drop your files here' => 'Trage fiÈ™ierele tale aici', + 'choose files' => 'alege fiÈ™iere', + 'View profile' => 'Vezi profilul', + 'Two Factor' => 'Doi Factori', + 'Disable user' => 'Dezactivează utilizator', + 'Do you really want to disable this user: "%s"?' => 'Vrei să dezactivezi utilizatorul: "%s" ?', + 'Enable user' => 'Activează utilizator', + 'Do you really want to enable this user: "%s"?' => 'Vrei să activezi utilizatorul: "%s" ?', + 'Download' => 'Descarcă', + 'Uploaded: %s' => 'ÃŽncărcat: %s', + 'Size: %s' => 'Dimensiune: %s', + 'Uploaded by %s' => 'ÃŽncărcat de %s', + 'Filename' => 'Nume fiÈ™ier', + 'Size' => 'Dimensiune', + 'Column created successfully.' => 'Coloana a fost creată.', + 'Another column with the same name exists in the project' => 'Există o coloană cu acelaÈ™i nume în proiect', + 'Default filters' => 'Filtre implicite', + 'Your board doesn\'t have any columns!' => 'Bordul tău nu are coloane!', + 'Change column position' => 'Schimbă poziÈ›ia coloanei', + 'Switch to the project overview' => 'Treci la vederea de ansamblu a proiectului', + 'User filters' => 'Filtre utilizatori', + 'Category filters' => 'Filtre categorii', + 'Upload a file' => 'ÃŽncarcă un fiÈ™ier', + 'View file' => 'Vezi fiÈ™ier', + 'Last activity' => 'Ultima activitate', + 'Change subtask position' => 'Schimbă poziÈ›ia sub-sarcinii', + 'This value must be greater than %d' => 'Valoarea aceasta trebuie să fie mai mare decât %d', + 'Another swimlane with the same name exists in the project' => 'Există un culoar cu acelaÈ™i nume în proiect', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Exemplu : https://exemplu.kanboard.org/ (utilizat în generarea URL-urilor absolute)', + 'Actions duplicated successfully.' => 'AcÈ›iuni duplicate cu succes.', + 'Unable to duplicate actions.' => 'Nu am putut duplica acÈ›iunile.', + 'Add a new action' => 'Adaugă o nouă acÈ›iune', + 'Import from another project' => 'Importă din alt proiect', + 'There is no action at the moment.' => 'Nu există vreo acÈ›iune momentan.', + 'Import actions from another project' => 'Importă acÈ™iuni din alt proiect', + 'There is no available project.' => 'Nu există un proiect disponibil.', + 'Local File' => 'FiÈ™ier local', + 'Configuration' => 'ConfiguraÈ›ie', + 'PHP version:' => 'Versiune de PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Versiune sistem de operare:', + 'Database version:' => 'Versiune bază de date:', + 'Browser:' => 'Browser:', + 'Task view' => 'Detaliere sarcină', + 'Edit task' => 'Modifică sarcina', + 'Edit description' => 'Modifică descrierea', + 'New internal link' => 'Legătură internă nouă', + 'Display list of keyboard shortcuts' => 'AfiÈ™ează lista de scurtături pe tastatură', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'ÃŽncarcă o imagine de avatar', + 'Remove my image' => 'Șterge imaginea mea', + 'The OAuth2 state parameter is invalid' => 'Parametrul "Stare" din OAuth2 este invalid', + 'User not found.' => 'Utilizatorul nu a fost găsit.', + 'Search in activity stream' => 'Caută în fluxul de activități', + 'My activities' => 'Activitățile mele', + 'Activity until yesterday' => 'Activitate până ieri', + 'Activity until today' => 'Activitate până azi', + 'Search by creator: ' => 'Caută după creator: ', + 'Search by creation date: ' => 'Caută după data creării: ', + 'Search by task status: ' => 'Caută după starea sarcinii: ', + 'Search by task title: ' => 'Caută după titlul sarcinii: ', + 'Activity stream search' => 'Căutare în fluxul de activități', + 'Projects where "%s" is manager' => 'Proiecte unde "%s" este gestionar', + 'Projects where "%s" is member' => 'Proiecte unde "%s" este membru', + 'Open tasks assigned to "%s"' => 'Sarcini deschise atribuite lui "%s"', + 'Closed tasks assigned to "%s"' => 'Sarcini închise atribuite lui "%s"', + 'Assign automatically a color based on a priority' => 'Atribuie în mod automat o culoare în funcÈ›ie de prioritate', + 'Overdue tasks for the project(s) "%s"' => 'Sarcini întârziate pentru proiect "%s"', + 'Upload files' => 'ÃŽncarcă fiÈ™iere', + 'Installed Plugins' => 'Extensii instalate', + 'Plugin Directory' => 'Director extensii', + 'Plugin installed successfully.' => 'Extensie instalată cu succes.', + 'Plugin updated successfully.' => 'Extensie actualizată cu succes.', + 'Plugin removed successfully.' => 'Extensie dezinstalată cu succes.', + 'Subtask converted to task successfully.' => 'Sub-sarcina a fost transformată în sarcină.', + 'Unable to convert the subtask.' => 'Nu am putut transforma sub-sarcina.', + 'Unable to extract plugin archive.' => 'Nu am putut extrage arhiva extensiei.', + 'Plugin not found.' => 'Extensia nu a fost găsită.', + 'You don\'t have the permission to remove this plugin.' => 'Nu ai dreptul să È™tergi această extensie.', + 'Unable to download plugin archive.' => 'Nu am putut descărca arhiva extensiei.', + 'Unable to write temporary file for plugin.' => 'Nu am putut scrie fiÈ™ierul temporar pentru extensie.', + 'Unable to open plugin archive.' => 'Nu am putut deschide arhiva extensiei.', + 'There is no file in the plugin archive.' => 'Nu există fiÈ™iere în arhiva extensiei.', + 'Create tasks in bulk' => 'Creează sarcini în vrac', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'InstanÈ›a ta de Kanboard nu a fost configurată pentru a instala extensii din interfaÈ›a de utilizator.', + 'There is no plugin available.' => 'Nu există extensii disponibile.', + 'Install' => 'Instalează', + 'Update' => 'Actualizează', + 'Up to date' => 'La zi', + 'Not available' => 'Indisponibil', + 'Remove plugin' => 'Șterge extensia', + 'Do you really want to remove this plugin: "%s"?' => 'Vrei să È™tergi această extensie: "%s" ?', + 'Uninstall' => 'Dezinstalare', + 'Listing' => 'Listare', + 'Metadata' => 'Metadate', + 'Manage projects' => 'Gestionează proiecte', + 'Convert to task' => 'Transformă în sarcină', + 'Convert sub-task to task' => 'Transformă sub-sarcina în sarcină', + 'Do you really want to convert this sub-task to a task?' => 'Vrei să transformi sub-sarcina în sarcină?', + 'My task title' => 'Titlul pentru sarcină', + 'Enter one task by line.' => 'Introdu o sarcină per linie.', + 'Number of failed login:' => 'Număr de autentificări eÈ™uate:', + 'Account locked until:' => 'Cont blocat până la:', + 'Email settings' => 'PreferinÈ›e e-mail', + 'Email sender address' => 'Adresa e-mail expeditor', + 'Email transport' => 'Transport e-mail', + 'Webhook token' => 'Token de securitate webhook', + 'Project tags management' => 'Gestionare etichete de proiect', + 'Tag created successfully.' => 'Etichetă creată.', + 'Unable to create this tag.' => 'Nu am putut crea eticheta.', + 'Tag updated successfully.' => 'Eticheta a fost actualizată.', + 'Unable to update this tag.' => 'Nu am putut actualiza eticheta.', + 'Tag removed successfully.' => 'Eticheta a fost È™tearsă.', + 'Unable to remove this tag.' => 'Impossible de supprimer ce libellé.', + 'Global tags management' => 'Gestionare etichete globale', + 'Tags' => 'Etichete', + 'Tags management' => 'Gestionare etichete', + 'Add new tag' => 'Adaugă etichetă nouă', + 'Edit a tag' => 'Modifică o etichetă', + 'Project tags' => 'Etichetele proiectului', + 'There is no specific tag for this project at the moment.' => 'Proiectul nu are vreo etichetă specifică momentan.', + 'Tag' => 'Etichetă', + 'Remove a tag' => 'Șterge o etichetă', + 'Do you really want to remove this tag: "%s"?' => 'Vrei să È™tergi eticheta: "%s" ?', + 'Global tags' => 'Etichete globale', + 'There is no global tag at the moment.' => 'Nu există etichete globale momentan.', + 'This field cannot be empty' => 'Acest câmp nu poate fi gol', + 'Close a task when there is no activity in a specific column' => 'ÃŽnchide o sarcină când nu există activitate într-o anumită coloană', + '%s removed a subtask for the task #%d' => '%s a È™ters o sub-sarcină din sarcina#%d', + '%s removed a comment on the task #%d' => '%s a È™ters un comentariu din sarcina #%d', + 'Comment removed on task #%d' => 'Comentariu È™ters în sarcina #%d', + 'Subtask removed on task #%d' => 'Sub-sarcină È™tearsă din sarcina #%d', + 'Hide tasks in this column in the dashboard' => 'Ascunde sarcinile din această coloană în bord', + '%s removed a comment on the task %s' => '%s a È™ters un comentariu din sarcina %s', + '%s removed a subtask for the task %s' => '%s a È™ters o sub-sarcină din sarcina %s', + 'Comment removed' => 'Comentariu È™ters', + 'Subtask removed' => 'Sub-sarcină È™tearsă', + '%s set a new internal link for the task #%d' => '%s a definit o legătură internă nouă pentru sarcina #%d', + '%s removed an internal link for the task #%d' => '%s a È™ters o legătură internă pentru sarcina #%d', + 'A new internal link for the task #%d has been defined' => 'O nouă legătură internă a fost definită pentru sarcina #%d', + 'Internal link removed for the task #%d' => 'Legătură internă È™tearsă pentru sarcina #%d', + '%s set a new internal link for the task %s' => '%s a definit o nouă legătură internă pentru sarcina %s', + '%s removed an internal link for the task %s' => '%s a È™ters o legătură internă pentru sarcina %s', + 'Automatically set the due date on task creation' => 'DefineÈ™te automat data scadentă la crearea sarcinii', + 'Move the task to another column when closed' => 'Mută sarcina în altă coloană când este închisă', + 'Move the task to another column when not moved during a given period' => 'Mută sarcina în altă coloană dacă nu a fost mutată într-o anumită perioadă', + 'Dashboard for %s' => 'Bordul pentru %s', + 'Tasks overview for %s' => 'Prezentare generală sarcini pentru %s', + 'Subtasks overview for %s' => 'Prezentare generală sub-sarcini pentru %s', + 'Projects overview for %s' => 'Prezentare generală proiecte pentru %s', + 'Activity stream for %s' => 'Flux de activități pentru %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Atribuie o culoare când sarcina este mutată într-un anumit culoar', + 'Assign a priority when the task is moved to a specific swimlane' => 'Atribuie o prioritate când sarcina este mutată într-un anumit culoar', + 'User unlocked successfully.' => 'Utilizatorul a fost deblocat.', + 'Unable to unlock the user.' => 'Nu am putut debloca utilizatorul.', + 'Move a task to another swimlane' => 'Mută sarcina în alt culoar', + 'Creator Name' => 'Nume creator', + 'Time spent and estimated' => 'Timp petrecut si estimat', + 'Move position' => 'Mută poziÈ›ia', + 'Move task to another position on the board' => 'Mută sarcina pe altă poziÈ›ie pe bord', + 'Insert before this task' => 'Inserează înaintea sarcinii', + 'Insert after this task' => 'Inserează după sarcină', + 'Unlock this user' => 'Deblochează utilizatorul', + 'Custom Project Roles' => 'Roluri personalizate în proiect', + 'Add a new custom role' => 'Adaugă rol personalizat nou', + 'Restrictions for the role "%s"' => 'RestricÈ›ii pentru rolul "%s"', + 'Add a new project restriction' => 'Adaugă o restricÈ›ie nouă de proiect', + 'Add a new drag and drop restriction' => 'Adaugă o restricÈ›ie nouă de mutare', + 'Add a new column restriction' => 'Adaugă o restricÈ›ie nouă de coloană', + 'Edit this role' => 'Modifică rolul', + 'Remove this role' => 'Șterge rolul', + 'There is no restriction for this role.' => 'Nu există restricÈ›ii pe acest rol.', + 'Only moving task between those columns is permitted' => 'Sarcina poate fi mutată numai între aceste coloane', + 'Close a task in a specific column when not moved during a given period' => 'ÃŽnchide o sarcină într-o anumită coloană când nu a fost mutată într-o anumită perioadă', + 'Edit columns' => 'Modifică coloane', + 'The column restriction has been created successfully.' => 'RestricÈ›ia pe coloane a fost creată.', + 'Unable to create this column restriction.' => 'Nu am putut crea restricÈ›ia pe coloană.', + 'Column restriction removed successfully.' => 'RestricÈ›ia pe coloană a fost È™tearsă.', + 'Unable to remove this restriction.' => 'Nu am putut È™terge restricÈ›ia.', + 'Your custom project role has been created successfully.' => 'Rolul personalizat a fost creat.', + 'Unable to create custom project role.' => 'Nu am putut crea rolul personalizat.', + 'Your custom project role has been updated successfully.' => 'Rolul personalizat a fost actualizat.', + 'Unable to update custom project role.' => 'Nu am putut actualiza rolul personalizat.', + 'Custom project role removed successfully.' => 'Rolul personalizat a fost È™ters.', + 'Unable to remove this project role.' => 'Nu am putut È™terge rolul.', + 'The project restriction has been created successfully.' => 'RestricÈ›ia pe proiect a fost creată.', + 'Unable to create this project restriction.' => 'Nu am putut crea restricÈ›ia pe proiect.', + 'Project restriction removed successfully.' => 'RestricÈ›ia pe proiect a fost È™tearsă.', + 'You cannot create tasks in this column.' => 'Nu poÈ›i crea sarcini în această coloană.', + 'Task creation is permitted for this column' => 'Crearea sarcinilor este permisă în această coloană', + 'Closing or opening a task is permitted for this column' => 'ÃŽnchiderea sau deschiderea sarcinilor este permisă în această coloană', + 'Task creation is blocked for this column' => 'Crearea sarcinilor este blocată în această coloană', + 'Closing or opening a task is blocked for this column' => 'ÃŽnchiderea sau deschiderea sarcinilor este blocată în această coloană', + 'Task creation is not permitted' => 'Crearea sarcinilor nu este permisă', + 'Closing or opening a task is not permitted' => 'ÃŽnchiderea sau deschiderea sarcinilor nu este permisă', + 'New drag and drop restriction for the role "%s"' => 'RestricÈ›ie nouă de mutare pentru rolul "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Persoanele cu acest rol vor putea muta sarcini numai între coloana sursă È™i destinaÈ›ie.', + 'Remove a column restriction' => 'Șterge o restricÈ›ie pe coloană', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Vrei să È™tergi această restricÈ›ie pe coloană: "%s" la "%s" ?', + 'New column restriction for the role "%s"' => 'RestricÈ›ie pe coloană nouă pentru rolul "%s"', + 'Rule' => 'Reguli', + 'Do you really want to remove this column restriction?' => 'Vrei să È™tergi această restricÈ›ie pe coloană?', + 'Custom roles' => 'Roluri personalizate', + 'New custom project role' => 'Nou rol personalizat pe proiect', + 'Edit custom project role' => 'Modifică rol personalizat pe proiect', + 'Remove a custom role' => 'Șterge rol personalizat pe proiect', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Vrei să È™tergi acest rol personalizat "%s" ? ToÈ›i membrii cu acest rol vor deveni membri de proiect.', + 'There is no custom role for this project.' => 'Nu există roluri personalizate în acest proiect.', + 'New project restriction for the role "%s"' => 'RestricÈ›ie de proiect nouă pentru rolul "%s"', + 'Restriction' => 'RestricÈ›ie', + 'Remove a project restriction' => 'Șterge o restricÈ›ie de proiect', + 'Do you really want to remove this project restriction: "%s"?' => 'Vrei să È™tergi această restricÈ›ie de proiect: "%s" ?', + 'Duplicate to multiple projects' => 'Duplichează în mai multe proiecte', + 'This field is required' => 'Câmpul este obligatoriu', + 'Moving a task is not permitted' => 'Nu este permisă mutarea sarcinii', + 'This value must be in the range %d to %d' => 'Valoarea trebuie să se afle între %d È™i %d', + 'You are not allowed to move this task.' => 'Nu ai permisiunea să muÈ›i această sarcină.', + 'API User Access' => 'Acces utilizator in API', + 'Preview' => 'Previzualizare', + 'Write' => 'Scrie', + 'Write your text in Markdown' => 'Scrie-È›i textul în Markdown', + 'No personal API access token registered.' => 'Nu este înregistrat vreun token personal de acces API.', + 'Your personal API access token is "%s"' => 'Token-ul tău personal de acces API este "%s"', + 'Remove your token' => 'Șterge token-ul tău', + 'Generate a new token' => 'Generează un nou token', + 'Showing %d-%d of %d' => 'AfiÈ™ez %d - %d din %d', + 'Outgoing Emails' => 'E-mail-uri trimise', + 'Add or change currency rate' => 'Adaugă sau modifică rată de schimb', + 'Reference currency: %s' => 'Moneda de referință: %s', + 'Add custom filters' => 'Adaugă filtre personalizate', + 'Export' => 'Export', + 'Add link label' => 'Adaugă etichetă de legătură', + 'Incompatible Plugins' => 'Extensii incompatibile', + 'Compatibility' => 'Compatibilitate', + 'Permissions and ownership' => 'Permisii si proprietate', + 'Priorities' => 'Priorități', + 'Close this window' => 'ÃŽnchide fereastra', + 'Unable to upload this file.' => 'Nu pot încărca fiÈ™ierul acesta.', + 'Import tasks' => 'Importă sarcini', + 'Choose a project' => 'Alege un proiect', + 'Profile' => 'Profil', + 'Application role' => 'Rolul aplicaÈ›iei', + '%d invitations were sent.' => '%d invitaÈ›ii au fost trimise.', + '%d invitation was sent.' => '%d invitaÈ›ie a fost trimisă.', + 'Unable to create this user.' => 'Nu am putut crea acest utilizator.', + 'Kanboard Invitation' => 'InvitaÈ›ie în Kanboard', + 'Visible on dashboard' => 'Vizibil pe bord', + 'Created at:' => 'Creat la:', + 'Updated at:' => 'Actualizat la:', + 'There is no custom filter.' => 'Nu există filtru personalizat.', + 'New User' => 'Utilizator nou', + 'Authentication' => 'Autentificare', + 'If checked, this user will use a third-party system for authentication.' => 'Dacă este bifat, acest utilizator va folosi un sistem terÈ› pentru autentificare.', + 'The password is necessary only for local users.' => 'Parola este obligatorie pentru membrii locali.', + 'You have been invited to register on Kanboard.' => 'Ai fost invitat să te înregistrezi pe Kanboard.', + 'Click here to join your team' => 'Apasă aici pentru a te alătura echipei tale', + 'Invite people' => 'Invită persoane', + 'Emails' => 'E-mail-uri', + 'Enter one email address by line.' => 'Introdu o adresă de e-mail per linie.', + 'Add these people to this project' => 'Adaugă aceste persoane în proiect', + 'Add this person to this project' => 'Adaugă această persoană în proiect', + 'Sign-up' => 'ÃŽnregistrare', + 'Credentials' => 'Acreditări', + 'New user' => 'Utilizator nou', + 'This username is already taken' => 'Acest nume de utilizator a fost luat deja', + 'Your profile must have a valid email address.' => 'Profilul tău trebuie să aibă o adresă de e-mail validă.', + 'TRL - Turkish Lira' => 'TRL - Liră turcească', + 'The project email is optional and could be used by several plugins.' => 'E-mail-ul proiectului este opÈ›ional si ar putea fi folosit de mai multe extensii.', + 'The project email must be unique across all projects' => 'E-mail-ul proiectului trebuie să fie diferit de toate celelalte proiecte.', + 'The email configuration has been disabled by the administrator.' => 'Configurarea de e-mail-uri a fost dezactivată de administrator.', + 'Close this project' => 'ÃŽnchide acest proiect', + 'Open this project' => 'Deschide acest proiect', + 'Close a project' => 'ÃŽnchide un proiect', + 'Do you really want to close this project: "%s"?' => 'Vrei să închizi proiectul: "%s" ?', + 'Reopen a project' => 'Redeschide un proiect', + 'Do you really want to reopen this project: "%s"?' => 'Vrei să redeschizi proiectul: "%s" ?', + 'This project is open' => 'Proiectul este deschis', + 'This project is closed' => 'Proiectul este închis', + 'Unable to upload files, check the permissions of your data folder.' => 'Nu am putut încărca fiÈ™ierele, verifică permisiunile directorului de date.', + 'Another category with the same name exists in this project' => 'Altă categorie cu acelaÈ™i nume există deja în proiect', + 'Comment sent by email successfully.' => 'Comentariul a fost trimis prin e-mail.', + 'Sent by email to "%s" (%s)' => 'Trimite prin e-mail la "%s" (%s)', + 'Unable to read uploaded file.' => 'Nu pot citi fiÈ™ierul încărcat.', + 'Database uploaded successfully.' => 'Baza de date a fost încărcată cu succes.', + 'Task sent by email successfully.' => 'Sarcina a fost trimisă prin e-mail.', + 'There is no category in this project.' => 'Nu există categorii în acest proiect.', + 'Send by email' => 'Trimis prin e-mail', + 'Create and send a comment by email' => 'Creat È™i trimis un comentariu prin e-mail', + 'Subject' => 'Subiect', + 'Upload the database' => 'ÃŽncarcă baza de date', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Ai putea încărca baza de date Sqlite descărcată anterior (format Gzip).', + 'Database file' => 'FiÈ™ierul bazei de date', + 'Upload' => 'ÃŽncarcă', + 'Your project must have at least one active swimlane.' => 'Proiectul tău trebuie să aibă măcar un culoar activ.', + 'Project: %s' => 'Proiect: %s', + 'Automatic action not found: "%s"' => 'AcÈ›iunea automatizată nu a fost gasită: "%s"', + '%d projects' => '%d proiecte', + '%d project' => '%d proiect', + 'There is no project.' => 'Nu există proiect.', + 'Sort' => 'Sortare', + 'Project ID' => 'ID-ul proiectului', + 'Project name' => 'Numele proiectului', + 'Public' => 'Public', + 'Personal' => 'Privat', + '%d tasks' => '%d sarcini', + '%d task' => '%d sarcină', + 'Task ID' => 'ID-ul sarcinii', + 'Assign automatically a color when due date is expired' => 'Atribuie automat o culoare când data scadentă a expirat', + 'Total score in this column across all swimlanes' => 'Scorul total în această coloană pe toate culoarele', + 'HRK - Kuna' => 'HRK - Kuna croată', + 'ARS - Argentine Peso' => 'ARS - Peso argentinian', + 'COP - Colombian Peso' => 'COP - Peso columbian', + '%d groups' => '%d grupuri', + '%d group' => '%d grup', + 'Group ID' => 'ID-ul grupului', + 'External ID' => 'ID extern', + '%d users' => '%d utilizatori', + '%d user' => '%d utilizator', + 'Hide subtasks' => 'Ascunde sub-sarcinile', + 'Show subtasks' => 'Arată sub-sarcinile', + 'Authentication Parameters' => 'Parametri de autentificare', + 'API Access' => 'Acces API', + 'No users found.' => 'Nici un utilizator găsit.', + 'User ID' => 'ID utilizator', + 'Notifications are activated' => 'Notificările sunt activate', + 'Notifications are disabled' => 'Notificările sunt dezactivate', + 'User disabled' => 'Utilizator dezactivat', + '%d notifications' => '%d notificări', + '%d notification' => '%d notificare', + 'There is no external integration installed.' => 'Nu este instalată vreo integrare externă.', + 'You are not allowed to update tasks assigned to someone else.' => 'Nu ai voie să actualizezi sarcini atribuite altcuiva.', + 'You are not allowed to change the assignee.' => 'Nu ai voie să schimbi persoana atribuită.', + 'Task suppression is not permitted' => 'Suprimarea sarcinii nu este permisă', + 'Changing assignee is not permitted' => 'Schimbarea persoanei atribuite nu este permisă', + 'Update only assigned tasks is permitted' => 'Numai actualizarea sarcinilor atribuite este permisă', + 'Only for tasks assigned to the current user' => 'Numai pentru sarcini atribuite utilizatorului curent', + 'My projects' => 'Proiectele mele', + 'You are not a member of any project.' => 'Nu faci parte din vreun proiect', + 'My subtasks' => 'Sub-sarcinile mele', + '%d subtasks' => '%d sub-sarcini', + '%d subtask' => '%d sub-sarcină', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Este permisă mutarea sarcinii între acele coloane pentru sarcinile atribuite utilizatorului curent', + '[DUPLICATE]' => '[DUPLICAT]', + 'DKK - Danish Krona' => 'DKK - Coroană daneză', + 'Remove user from group' => 'Șterge utilizatorul din grup', + 'Assign the task to its creator' => 'Atribuie sarcina creatorului ei', + 'This task was sent by email to "%s" with subject "%s".' => 'Această sarcină a fost trimisă prin e-mail lui "%s" cu subiectul "%s".', + 'Predefined Email Subjects' => 'Subiecte predefinite in e-mail', + 'Write one subject by line.' => 'Scrie un singur subiect per linie.', + 'Create another link' => 'Creează altă legătură', + 'BRL - Brazilian Real' => 'BRL - Real brazilian', + 'Add a new Kanboard task' => 'Adaugă o nouă sarcină Kanboard', + 'Subtask not started' => 'Sub-sarcina nu este pornită', + 'Subtask currently in progress' => 'Sub-sarcină în derulare', + 'Subtask completed' => 'Sub-sarcină finalizată', + 'Subtask added successfully.' => 'Sub-sarcină adăugată.', + '%d subtasks added successfully.' => '%d sub-sarcini au fost adăugate.', + 'Enter one subtask by line.' => 'Introdu o sub-sarcină pe linie.', + 'Predefined Contents' => 'ConÈ›inut predefinit', + 'Predefined contents' => 'ConÈ›inut predefinit', + 'Predefined Task Description' => 'Descrierea predefinită a sarcinii', + 'Do you really want to remove this template? "%s"' => 'Vrei să È™tergi acest model? "%s"', + 'Add predefined task description' => 'Adaugă descriere predefinită a sarcinii', + 'Predefined Task Descriptions' => 'Descrieri predefinite ale sarcinilor', + 'Template created successfully.' => 'Model creat.', + 'Unable to create this template.' => 'Nu am putut crea modelul.', + 'Template updated successfully.' => 'Model actualizat.', + 'Unable to update this template.' => 'Nu am putut actualiza modelul.', + 'Template removed successfully.' => 'Model È™ters.', + 'Unable to remove this template.' => 'Nu am putut È™terge modelul.', + 'Template for the task description' => 'Șablon pentru descrierea sarcinii', + 'The start date is greater than the end date' => 'Data de început este mai mare decât data de sfârÈ™it', + 'Tags must be separated by a comma' => 'Etichetele trebuie separate prin virgulă', + 'Only the task title is required' => 'Doar titlul sarcinii este obligatoriu', + 'Creator Username' => 'Nume de utilizator creator', + 'Color Name' => 'Nume culoare', + 'Column Name' => 'Nume coloană', + 'Swimlane Name' => 'Nume culoar', + 'Time Estimated' => 'Timp estimat', + 'Time Spent' => 'Timp petrecut', + 'External Link' => 'Link extern', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Această funcÈ›ie activează fluxul iCal, fluxul RSS È™i vizualizarea publică a tabelului.', + 'Stop the timer of all subtasks when moving a task to another column' => 'OpriÈ›i cronometrul tuturor subsarcinilor când mutaÈ›i o sarcină într-o altă coloană', + 'Subtask Title' => 'Titlu subsarcină', + 'Add a subtask and activate the timer when moving a task to another column' => 'Adaugă o subsarcină È™i activează cronometrul când muÈ›i o sarcină într-o altă coloană', + 'days' => 'zile', + 'minutes' => 'minute', + 'seconds' => 'secunde', + 'Assign automatically a color when preset start date is reached' => 'AtribuiÈ›i automat o culoare când se atinge data de începere prestabilită', + 'Move the task to another column once a predefined start date is reached' => 'MutaÈ›i sarcina într-o altă coloană odată ce se atinge o dată de începere predefinită', + 'This task is now linked to the task %s with the relation "%s"' => 'Această sarcină este acum legată de sarcina %s cu relaÈ›ia "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Legătura cu relaÈ›ia "%s" la sarcina %s a fost eliminată', + 'Custom Filter:' => 'Filtru personalizat:', + 'Unable to find this group.' => 'Nu găsesc acest grup.', + '%s moved the task #%d to the column "%s"' => '%s a mutat sarcina #%d în coloana "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s a mutat sarcina #%d pe poziÈ›ia %d în coloana "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s a mutat sarcina #%d în culoarul "%s"', + '%sh spent' => '%sh petrecute', + '%sh estimated' => '%sh estimate', + 'Select All' => 'Selectează tot', + 'Unselect All' => 'Deselectează tot', + 'Apply action' => 'Aplică acÈ›iunea', + 'Move selected tasks to another column or swimlane' => 'MutaÈ›i sarcinile selectate într-o altă coloană sau un alt culoar', + 'Edit tasks in bulk' => 'EditaÈ›i sarcinile în masă', + 'Choose the properties that you would like to change for the selected tasks.' => 'AlegeÈ›i proprietățile pe care doriÈ›i să le schimbaÈ›i pentru sarcinile selectate.', + 'Configure this project' => 'Configurează acest proiect', + 'Start now' => 'ÃŽncepe acum', + '%s removed a file from the task #%d' => '%s a eliminat un fiÈ™ier de la sarcina #%d', + 'Attachment removed from task #%d: %s' => 'AtaÈ™ament eliminat de la sarcina #%d: %s', + 'No color' => 'Fără culoare', + 'Attachment removed "%s"' => 'AtaÈ™ament eliminat "%s"', + '%s removed a file from the task %s' => '%s a eliminat un fiÈ™ier de la sarcina %s', + 'Move the task to another swimlane when assigned to a user' => 'MutaÈ›i sarcina într-un alt culoar când este atribuită unui utilizator', + 'Destination swimlane' => 'Culoar destinaÈ›ie', + 'Assign a category when the task is moved to a specific swimlane' => 'AtribuiÈ›i o categorie când sarcina este mutată într-un anumit culoar', + 'Move the task to another swimlane when the category is changed' => 'MutaÈ›i sarcina într-un alt culoar când categoria este schimbată', + 'Reorder this column by priority (ASC)' => 'RearanjaÈ›i această coloană după prioritate (ASC)', + 'Reorder this column by priority (DESC)' => 'RearanjaÈ›i această coloană după prioritate (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'RearanjaÈ›i această coloană după responsabil È™i prioritate (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'RearanjaÈ›i această coloană după responsabil È™i prioritate (DESC)', + 'Reorder this column by assignee (A-Z)' => 'RearanjaÈ›i această coloană după responsabil (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'RearanjaÈ›i această coloană după responsabil (Z-A)', + 'Reorder this column by due date (ASC)' => 'RearanjaÈ›i această coloană după data scadenÈ›ei (ASC)', + 'Reorder this column by due date (DESC)' => 'RearanjaÈ›i această coloană după data scadenÈ›ei (DESC)', + 'Reorder this column by id (ASC)' => 'RearanjaÈ›i această coloană după id (ASC)', + 'Reorder this column by id (DESC)' => 'RearanjaÈ›i această coloană după id (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s a mutat sarcina #%d "%s" la proiectul "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Sarcina #%d "%s" a fost mutată la proiectul "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'MutaÈ›i sarcina într-o altă coloană când data scadenÈ›ei este mai mică de un anumit număr de zile', + 'Automatically update the start date when the task is moved away from a specific column' => 'ActualizaÈ›i automat data de începere când sarcina este mutată din o anumită coloană', + 'HTTP Client:' => 'Client HTTP:', + 'Assigned' => 'Atribuit', + 'Task limits apply to each swimlane individually' => 'Limite sarcini se aplică fiecărui culoar individual', + 'Column task limits apply to each swimlane individually' => 'Limite sarcini coloană se aplică fiecărui culoar individual', + 'Column task limits are applied to each swimlane individually' => 'Limite sarcini coloană sunt aplicate fiecărui culoar individual', + 'Column task limits are applied across swimlanes' => 'Limite sarcini coloană sunt aplicate pe toate culoarele', + 'Task limit: ' => 'Limita sarcini:', + 'Change to global tag' => 'Schimbă în etichetă globală', + 'Do you really want to make the tag "%s" global?' => 'Sigur doriÈ›i să faceÈ›i eticheta "%s" globală?', + 'Enable global tags for this project' => 'Activează etichetele globale pentru acest proiect', + 'Group membership(s):' => 'ApartenenÈ›a la grup(uri):', + '%s is a member of the following group(s): %s' => '%s este membru al următorului(lor) grup(uri): %s', + '%d/%d group(s) shown' => '%d/%d grup(uri) afiÈ™ate', + 'Subtask creation or modification' => 'Creare sau modificare subsarcină', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'AtribuiÈ›i sarcina unui utilizator specific când sarcina este mutată într-un anumit culoar', + 'Comment' => 'Comentariu', + 'Collapse vertically' => 'Strânge vertical', + 'Expand vertically' => 'Extinde vertical', + 'MXN - Mexican Peso' => 'MXN - Peso Mexican', + 'Estimated vs actual time per column' => 'Timp estimat vs timp real pe coloană', + 'HUF - Hungarian Forint' => 'HUF - Forint Maghiar', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Trebuie să selectaÈ›i un fiÈ™ier pentru a-l încărca drept avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'FiÈ™ierul încărcat nu este o imagine validă! (Doar *.gif, *.jpg, *.jpeg È™i *.png sunt permise!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Setează automat data scadenÈ›ei când sarcina este mutată dintr-o anumită coloană', + 'No other projects found.' => 'Nu s-au găsit alte proiecte.', + 'Tasks copied successfully.' => 'Sarcini copiate cu succes.', + 'Unable to copy tasks.' => 'Nu se pot copia sarcinile.', + 'Theme' => 'Temă', + 'Theme:' => 'Temă:', + 'Light theme' => 'Temă deschisă', + 'Dark theme' => 'Temă închisă', + 'Automatic theme - Sync with system' => 'Temă automată - Sincronizare cu sistemul', + 'Application managers or more' => 'Manageri aplicaÈ›ie sau mai mult', + 'Administrators' => 'Administratori', + 'Visibility:' => 'Vizibilitate:', + 'Standard users' => 'Utilizatori standard', + 'Visibility is required' => 'Vizibilitatea este obligatorie', + 'The visibility should be an app role' => 'Vizibilitatea ar trebui să fie o rolă a aplicaÈ›iei', + 'Reply' => 'Răspunde', + '%s wrote: ' => '%s a scris: ', + 'Number of visible tasks in this column and swimlane' => 'Număr de sarcini vizibile în această coloană È™i culoar', + 'Number of tasks in this swimlane' => 'Număr de sarcini în acest culoar', + 'Unable to find another subtask in progress, you can close this window.' => 'Nu s-a găsit altă subsarcină în progres, puteÈ›i închide această fereastră.', + 'This theme is invalid' => 'Această temă este nevalidă', + 'This role is invalid' => 'Acest rol este nevalid', + 'This timezone is invalid' => 'Acest fus orar este nevalid', + 'This language is invalid' => 'Această limbă este nevalidă', + 'This URL is invalid' => 'Această adresă URL este nevalidă', + 'Date format invalid' => 'Format dată nevalid', + 'Time format invalid' => 'Format oră nevalid', + 'Invalid Mail transport' => 'Transport Mail nevalid', + 'Color invalid' => 'Culoare nevalidă', + 'This value must be greater or equal to %d' => 'Această valoare trebuie să fie mai mare sau egală cu %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'AdăugaÈ›i un BOM la începutul fiÈ™ierului (necesar pentru Microsoft Excel)', + 'Just add these tag(s)' => 'Doar adăugaÈ›i aceste etichetă(e)', + 'Remove internal link(s)' => 'Elimină legătură(i) internă(e)', + 'Import tasks from another project' => 'Importă sarcini dintr-un alt proiect', + 'Select the project to copy tasks from' => 'SelectaÈ›i proiectul din care să copiaÈ›i sarcinile', + 'The total maximum allowed attachments size is %sB.' => 'Dimensiunea totală maximă permisă pentru ataÈ™amente este %sB.', + 'Add attachments' => 'Adaugă ataÈ™amente', + 'Task #%d "%s" is overdue' => 'Sarcina #%d "%s" este întârziată', + 'Enable notifications by default for all new users' => 'Activează notificările implicit pentru toÈ›i utilizatorii noi', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Atribuie sarcina creatorului ei pentru coloane specifice dacă nu este setat manual niciun responsabil', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Atribuie sarcina utilizatorului autentificat la schimbarea coloanei către coloana specificată dacă nu este atribuit niciun utilizator', +]; diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php new file mode 100644 index 0000000..f5486ca --- /dev/null +++ b/app/Locale/ru_RU/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'ОтÑутÑтвует', + 'Edit' => 'Изменить', + 'Remove' => 'Удалить', + 'Yes' => 'Да', + 'No' => 'Ðет', + 'cancel' => 'Отменить', + 'or' => 'или', + 'Yellow' => 'Желтый', + 'Blue' => 'Синий', + 'Green' => 'Зеленый', + 'Purple' => 'Фиолетовый', + 'Red' => 'КраÑный', + 'Orange' => 'Оранжевый', + 'Grey' => 'Серый', + 'Brown' => 'Коричневый', + 'Deep Orange' => 'Темно-оранжевый', + 'Dark Grey' => 'Темно-Ñерый', + 'Pink' => 'Розовый', + 'Teal' => 'Бирюзовый', + 'Cyan' => 'Голубой', + 'Lime' => 'Лимонный', + 'Light Green' => 'Светло-зеленый', + 'Amber' => 'Янтарный', + 'Save' => 'Сохранить', + 'Login' => 'Вход', + 'Official website:' => 'Официальный Ñайт:', + 'Unassigned' => 'Ðе назначена', + 'View this task' => 'ПоÑмотреть задачу', + 'Remove user' => 'Удалить пользователÑ', + 'Do you really want to remove this user: "%s"?' => 'Ð’Ñ‹ хотите удалить пользователÑ: «%s»?', + 'All users' => 'Ð’Ñе пользователи', + 'Username' => 'Логин', + 'Password' => 'Пароль', + 'Administrator' => 'ÐдминиÑтратор', + 'Sign in' => 'Войти', + 'Users' => 'Пользователи', + 'Forbidden' => 'Запрещено', + 'Access Forbidden' => 'ДоÑтуп запрещён', + 'Edit user' => 'Изменить пользователÑ', + 'Logout' => 'Выйти', + 'Bad username or password' => 'Ðеверное Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð»Ð¸ пароль', + 'Edit project' => 'Изменить проект', + 'Name' => 'ИмÑ', + 'Projects' => 'Проекты', + 'No project' => 'Ðет проекта', + 'Project' => 'Проект', + 'Status' => 'СтатуÑ', + 'Tasks' => 'Задачи', + 'Board' => 'ДоÑка', + 'Actions' => 'ДейÑтвиÑ', + 'Inactive' => 'Ðеактивен', + 'Active' => 'Ðктивен', + 'Unable to update this board.' => 'Ðе удалоÑÑŒ обновить Ñту доÑку.', + 'Disable' => 'Отключить', + 'Enable' => 'Включить', + 'New project' => 'Ðовый проект', + 'Do you really want to remove this project: "%s"?' => 'Ð’Ñ‹ точно хотите удалить проект: "%s"?', + 'Remove project' => 'Удалить проект', + 'Edit the board for "%s"' => 'Изменить доÑку Ð´Ð»Ñ "%s"', + 'Add a new column' => 'Добавить новую колонку', + 'Title' => 'Ðазвание', + 'Assigned to %s' => 'Ðазначено %s', + 'Remove a column' => 'Удалить колонку', + 'Unable to remove this column.' => 'Ðе удалоÑÑŒ удалить Ñту колонку.', + 'Do you really want to remove this column: "%s"?' => 'Ð’Ñ‹ точно хотите удалить Ñту колонку: "%s" ?', + 'Settings' => 'ÐаÑтройки', + 'Application settings' => 'ÐаÑтройки приложениÑ', + 'Language' => 'Язык', + 'Webhook token:' => 'Webhooks токен :', + 'API token:' => 'API токен :', + 'Database size:' => 'Размер базы данных :', + 'Download the database' => 'Скачать базу данных', + 'Optimize the database' => 'Оптимизировать базу данных', + '(VACUUM command)' => '(Команда VACUUM)', + '(Gzip compressed Sqlite file)' => '(Сжать GZip файл SQLite)', + 'Close a task' => 'Закрыть задачу', + 'Column' => 'Колонка', + 'Color' => 'Цвет', + 'Assignee' => 'Ðазначена', + 'Create another task' => 'Создать другую задачу', + 'New task' => 'ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ð´Ð°Ñ‡Ð°', + 'Open a task' => 'Открыть задачу', + 'Do you really want to open this task: "%s"?' => 'Ð’Ñ‹ уверены, что хотите открыть задачу: "%s" ?', + 'Back to the board' => 'ВернутьÑÑ Ð½Ð° доÑку', + 'There is nobody assigned' => 'Ðикто не назначен', + 'Column on the board:' => 'Колонка на доÑке: ', + 'Close this 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 знаков', + 'The password is required' => 'Ðеобходим пароль', + 'This value must be an integer' => 'Это значение должно быть целым чиÑлом', + 'The username must be unique' => 'Ð˜Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ быть уникально', + 'The user id is required' => 'Ðеобходим ID пользователÑ', + 'Passwords don\'t match' => 'Пароли не Ñовпадают', + 'The confirmation is required' => 'Ðеобходимо подтверждение', + 'The project is required' => 'Ðеобходимо указать проект', + 'The id is required' => 'Ðеобходим ID', + 'The project id is required' => 'Ðеобходим ID проекта', + 'The project name is required' => 'Ðеобходимо Ð¸Ð¼Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°', + 'The title is required' => 'Ðеобходим заголовок', + 'Settings saved successfully.' => 'Параметры уÑпешно Ñохранены.', + 'Unable to save your settings.' => 'Ðевозможно Ñохранить параметры.', + 'Database optimization done.' => 'База данных оптимизирована.', + 'Your project has been created successfully.' => 'Ваш проект уÑпешно Ñоздан.', + 'Unable to create your project.' => 'Ðе удалоÑÑŒ Ñоздать проект.', + 'Project updated successfully.' => 'Проект уÑпешно обновлен.', + 'Unable to update this project.' => 'Ðе удалоÑÑŒ обновить проект.', + 'Unable to remove this project.' => 'Ðе удалоÑÑŒ удалить проект.', + 'Project removed successfully.' => 'Проект удалён.', + 'Project activated successfully.' => 'Проект активирован.', + 'Unable to activate this project.' => 'Ðевозможно активировать проект.', + 'Project disabled successfully.' => 'Проект уÑпешно деактивирован.', + 'Unable to disable this project.' => 'Ðе удалоÑÑŒ деактивировать проект.', + 'Unable to open this task.' => 'Ðе удалоÑÑŒ открыть задачу.', + 'Task opened successfully.' => 'Задача открыта.', + 'Unable to close this task.' => 'Ðе удалоÑÑŒ закрыть задачу.', + 'Task closed successfully.' => 'Задача закрыта.', + 'Unable to update your task.' => 'Ðе удалоÑÑŒ обновить задачу.', + 'Task updated successfully.' => 'Задача обновлена.', + 'Unable to create your task.' => 'Ðе удалоÑÑŒ Ñоздать задачу.', + 'Task created successfully.' => 'Задача Ñоздана.', + 'User created successfully.' => 'Пользователь Ñоздан.', + 'Unable to create your user.' => 'Ðе удалоÑÑŒ Ñоздать пользователÑ.', + 'User updated successfully.' => 'Пользователь обновлён.', + 'User removed successfully.' => 'Пользователь удалён.', + 'Unable to remove this user.' => 'Ðе удалоÑÑŒ удалить пользователÑ.', + 'Board updated successfully.' => 'ДоÑка уÑпешно обновлена.', + 'Ready' => 'СоглаÑованные', + 'Backlog' => 'Ожидающие', + 'Work in progress' => 'Ð’ процеÑÑе', + 'Done' => 'Выполнено', + 'Application version:' => 'ВерÑÐ¸Ñ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ:', + 'Id' => 'ID', + 'Public link' => 'СÑылка Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñмотра', + 'Timezone' => 'ЧаÑовой поÑÑ', + 'Sorry, I didn\'t find this information in my database!' => 'К Ñожалению, Ð¸Ð½Ñ„Ð¾Ñ€Ð¼Ð°Ñ†Ð¸Ñ Ð² базе данных не найдена!', + 'Page not found' => 'Страница не найдена', + 'Complexity' => 'СложноÑть', + 'Task limit' => 'Лимит задач', + 'Task count' => 'КоличеÑтво задач', + 'User' => 'Пользователь', + 'Comments' => 'Комментарии', + 'Comment is required' => 'Ðужен комментарий', + 'Comment added successfully.' => 'Комментарий уÑпешно добавлен.', + 'Unable to create your comment.' => 'Ðевозможно Ñоздать комментарий.', + 'Due Date' => 'Дата завершениÑ', + 'Invalid date' => 'ÐÐµÐ²ÐµÑ€Ð½Ð°Ñ Ð´Ð°Ñ‚Ð°', + 'Automatic actions' => 'ÐвтоматичеÑкие дейÑтвиÑ', + 'Your automatic action has been created successfully.' => 'Ðвтоматизированное дейÑтвие уÑпешно наÑтроено.', + 'Unable to create your automatic action.' => 'Ðе удалоÑÑŒ Ñоздать автоматизированное дейÑтвие.', + 'Remove an action' => 'Удалить дейÑтвие', + 'Unable to remove this action.' => 'Ðе удалоÑÑŒ удалить дейÑтвие', + 'Action removed successfully.' => 'ДейÑтвие удалено.', + 'Automatic actions for the project "%s"' => 'ÐвтоматичеÑкие дейÑÑ‚Ð²Ð¸Ñ Ð´Ð»Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð° « %s »', + 'Add an action' => 'Добавить дейÑтвие', + 'Event name' => 'Ð˜Ð¼Ñ ÑобытиÑ', + 'Action' => 'ДейÑтвие', + 'Event' => 'Событие', + 'When the selected event occurs execute the corresponding action.' => 'Когда ÑлучитÑÑ Ð’Ð«Ð‘Ð ÐÐÐОЕ Ñобытие выполнÑетÑÑ Ð¡ÐžÐžÐ¢Ð’Ð•Ð¢Ð¡Ð¢Ð’Ð£Ð®Ð©Ð•Ð• дейÑтвие.', + 'Next step' => 'Следующий шаг', + '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 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' => 'Ðазначить определённый цвет пользователю', + 'Position' => 'РаÑположение', + 'Duplicate to project' => 'Клонировать в другой проект', + 'Duplicate' => 'Клонировать', + 'Link' => 'CÑылка', + 'Comment updated successfully.' => 'Комментарий обновлён.', + 'Unable to update your comment.' => 'Ðе удалоÑÑŒ обновить ваш комментарий.', + 'Remove a comment' => 'Удалить комментарий', + 'Comment removed successfully.' => 'Комментарий удалён.', + 'Unable to remove this comment.' => 'Ðе удалоÑÑŒ удалить Ñтот комментарий.', + 'Do you really want to remove this comment?' => 'Ð’Ñ‹ точно хотите удалить Ñтот комментарий?', + 'Current password for the user "%s"' => 'Текущий пароль Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ "%s"', + 'The current password is required' => 'ТребуетÑÑ Ñ‚ÐµÐºÑƒÑ‰Ð¸Ð¹ пароль', + 'Wrong password' => 'Ðеверный пароль', + 'Unknown' => 'ÐеизвеÑтно', + 'Last logins' => 'ПоÑледние поÑещениÑ', + 'Login date' => 'Дата входа', + 'Authentication method' => 'СпоÑоб аутентификации', + 'IP address' => 'IP адреÑ', + 'User agent' => 'User agent', + 'Persistent connections' => 'ПоÑтоÑнные ÑоединениÑ', + 'No session.' => 'Ðет ÑеанÑа', + 'Expiration date' => 'Дата окончаниÑ', + 'Remember Me' => 'Запомнить менÑ', + 'Creation date' => 'Дата ÑозданиÑ', + 'Everybody' => 'Ð’Ñе', + 'Open' => 'Открытый', + 'Closed' => 'Закрытый', + 'Search' => 'ПоиÑк', + 'Nothing found.' => 'Ðичего не найдено.', + 'Due date' => 'Срок', + 'Description' => 'ОпиÑание', + '%d comments' => '%d комментариев', + '%d comment' => '%d комментарий', + 'Email address invalid' => 'Ðекорректный e-mail адреÑ', + '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.' => 'Ваш внешний аккаунт уÑпешно подключён к профилю.', + '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' => 'ÐвтоматичеÑки назначить категорию по цвету', + 'Task creation or modification' => 'Создание или изменение задачи', + 'Category' => 'КатегориÑ', + 'Category:' => 'КатегориÑ:', + 'Categories' => 'Категории', + 'Your category has been created successfully.' => 'ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ñоздана.', + 'This category has been updated successfully.' => 'ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ð¾Ð±Ð½Ð¾Ð²Ð»ÐµÐ½Ð°.', + 'Unable to update this 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' => 'Ðет категории', + 'The name is required' => 'ТребуетÑÑ Ð½Ð°Ð·Ð²Ð°Ð½Ð¸Ðµ', + 'Remove a file' => 'Удалить файл', + 'Unable to remove this file.' => 'Ðе удалоÑÑŒ удалить файл.', + 'File removed successfully.' => 'Файл удалён.', + 'Attach a document' => 'Прикрепить файл', + 'Do you really want to remove this file: "%s"?' => 'Ð’Ñ‹ точно хотите удалить Ñтот файл « %s » ?', + 'Attachments' => 'ВложениÑ', + 'Edit the task' => 'Изменить задачу', + 'Add a comment' => 'Добавить комментарий', + 'Edit a comment' => 'Изменить комментарий', + 'Summary' => 'Сводка', + 'Time tracking' => 'ОтÑлеживание времени', + 'Estimate:' => 'Запланировано:', + 'Spent:' => 'Затрачено:', + 'Do you really want to remove this sub-task?' => 'Ð’Ñ‹ точно хотите удалить подзадачу?', + 'Remaining:' => 'ОÑталоÑÑŒ:', + 'hours' => 'чаÑов', + 'estimated' => 'раÑчётное', + 'Sub-Tasks' => 'Подзадачи', + 'Add a sub-task' => 'Добавить подзадачу', + 'Original estimate' => 'Запланировано', + 'Create another sub-task' => 'Создать другую подзадачу', + 'Time spent' => 'Времени затрачено', + 'Edit a sub-task' => 'Изменить подзадачу', + 'Remove a sub-task' => 'Удалить подзадачу', + 'The time must be a numeric value' => 'Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ быть чиÑлом!', + 'Todo' => 'К иÑполнению', + 'In progress' => 'Ð’ процеÑÑе', + 'Sub-task removed successfully.' => 'Подзадача удалена.', + 'Unable to remove this sub-task.' => 'Ðе удалоÑÑŒ удалить подзадачу.', + 'Sub-task updated successfully.' => 'Подзадача обновлена.', + 'Unable to update your sub-task.' => 'Ðе удалоÑÑŒ обновить подзадачу.', + 'Unable to create your sub-task.' => 'Ðе удалоÑÑŒ Ñоздать подзадачу.', + 'Maximum size: ' => 'МакÑимальный размер: ', + 'Display another project' => 'Показать другой проект', + 'Created by %s' => 'Создано %s', + 'Tasks Export' => 'ЭкÑпорт задач', + 'Start Date' => 'Дата начала', + 'Execute' => 'Выполнить', + 'Task Id' => 'ID задачи', + 'Creator' => 'Ðвтор', + 'Modification date' => 'Дата изменениÑ', + 'Completion date' => 'Дата завершениÑ', + 'Clone' => 'Клонировать', + 'Project cloned successfully.' => 'Проект клонирован.', + 'Unable to clone this project.' => 'Ðе удалоÑÑŒ клонировать проект.', + 'Enable email notifications' => 'Включить ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾ e-mail', + 'Task position:' => 'ÐŸÐ¾Ð·Ð¸Ñ†Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸:', + 'The task #%d has been opened.' => 'Задача #%d была открыта.', + 'The task #%d has been closed.' => 'Задача #%d была закрыта.', + 'Sub-task updated' => 'Подзадача обновлена', + 'Title:' => 'Ðазвание:', + 'Status:' => 'СтатуÑ:', + 'Assignee:' => 'Ðазначена:', + 'Time tracking:' => 'ОтÑлеживание времени:', + 'New sub-task' => 'ÐÐ¾Ð²Ð°Ñ Ð¿Ð¾Ð´Ð·Ð°Ð´Ð°Ñ‡Ð°', + 'New attachment added "%s"' => 'Добавлено вложение « %s »', + 'New comment posted by %s' => 'Ðовый комментарий добавлен « %s »', + 'New comment' => 'Ðовый комментарий', + 'Comment updated' => 'Комментарий обновлён', + 'New subtask' => 'ÐÐ¾Ð²Ð°Ñ Ð¿Ð¾Ð´Ð·Ð°Ð´Ð°Ñ‡Ð°', + 'I only want to receive notifications for these projects:' => 'Я хочу получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ñ‚Ð¾Ð»ÑŒÐºÐ¾ по Ñтим проектам:', + 'view the task on Kanboard' => 'поÑмотреть задачу на Kanboard', + 'Public access' => 'Общий доÑтуп', + 'Disable public access' => 'Отключить общий доÑтуп', + 'Enable public access' => 'Включить общий доÑтуп', + 'Public access disabled' => 'Общий доÑтуп отключён', + 'Move the task to another project' => 'ПеремеÑтить задачу в другой проект', + 'Move to project' => 'ПеремеÑтить в другой проект', + 'Do you really want to duplicate this task?' => 'Ð’Ñ‹ точно хотите клонировать задачу?', + 'Duplicate a task' => 'Клонировать задачу', + 'External accounts' => 'ВнешнÑÑ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ', + 'Account type' => 'Тип профилÑ', + 'Local' => 'Локальный', + 'Remote' => 'Удалённый', + 'Enabled' => 'Включён', + 'Disabled' => 'Выключены', + 'Login:' => 'Логин:', + 'Full Name:' => 'ИмÑ:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'УведомлениÑ:', + 'Notifications' => 'УведомлениÑ', + 'Account type:' => 'Тип профилÑ:', + 'Edit profile' => 'Редактировать профиль', + 'Change password' => 'Сменить пароль', + 'Password modification' => 'Изменение паролÑ', + 'External authentications' => 'ВнешнÑÑ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ', + 'Never connected.' => 'Ранее не ÑоединÑлоÑÑŒ.', + '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' => 'Ðе назначено, окончание %sh', + '%s updated a comment on the task %s' => '%s обновил комментарий к задаче %s', + '%s commented the task %s' => '%s прокомментировал задачу %s', + '%s\'s activity' => '%s активноÑть', + 'RSS feed' => 'RSS лента', + '%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)' => 'Колонки по умолчанию Ð´Ð»Ñ Ð½Ð¾Ð²Ñ‹Ñ… проектов (разделÑть запÑтой)', + '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' => 'Выберите Ñобытие', + 'Create a task from an external provider' => 'Создать задачу из внешнего иÑточника', + 'Change the assignee based on an external username' => 'Изменить назначенного оÑновываÑÑÑŒ на внешнем имени пользователÑ', + 'Change the category based on an external label' => 'Изменить категорию оÑновываÑÑÑŒ на внешнем Ñрлыке', + 'Reference' => 'СÑылка', + 'Label' => 'Ярлык', + 'Database' => 'База данных', + 'About' => 'ИнформациÑ', + 'Database driver:' => 'Драйвер базы данных', + 'Board settings' => 'ÐаÑтройки доÑки', + 'Webhook settings' => 'Параметры Webhook', + 'Reset token' => 'Перезагрузить токен', + 'API endpoint:' => 'API endpoint:', + 'Refresh interval for personal 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 Ñекунд по умолчанию)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'ЧаÑтота в Ñекундах (0 Ð´Ð»Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ð¸Ñ, 10 Ñекунд по умолчанию)', + 'Application URL' => 'URL приложениÑ', + 'Token regenerated.' => 'Токен переÑоздан', + 'Date format' => 'Формат даты', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Ð’Ñ€ÐµÐ¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ быть в ISO-формате, например: "%s" или "%s"', + 'New personal project' => 'Ðовый перÑональный проект', + 'This project is personal' => 'Это перÑональный проект', + 'Add' => 'Добавить', + 'Start date' => 'Дата начала', + 'Time estimated' => 'Запланировано', + 'There is nothing assigned to you.' => 'Вам ничего не назначено', + 'My tasks' => 'Мои задачи', + 'Activity stream' => 'Ð¢ÐµÐºÑƒÑ‰Ð°Ñ Ð°ÐºÑ‚Ð¸Ð²Ð½Ð¾Ñть', + 'Dashboard' => 'Панель управлениÑ', + 'Confirmation' => 'Подтверждение паролÑ', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Создать комментарий из внешнего иÑточника', + 'Project management' => 'Управление проектами', + 'Columns' => 'Колонки', + 'Task' => 'Задача', + 'Percentage' => 'Процент', + 'Number of tasks' => 'КоличеÑтво задач', + 'Task distribution' => 'РаÑпределение задач', + 'Analytics' => 'Ðналитика', + 'Subtask' => 'Подзадача', + 'User repartition' => 'ПерераÑпределение пользователей', + 'Clone this project' => 'Клонировать проект', + 'Column removed successfully.' => 'Колонка уÑпешно удалена.', + 'Not enough data to show the graph.' => 'ÐедоÑтаточно данных, чтобы показать график.', + 'Previous' => 'Предыдущий', + 'The id must be an integer' => 'Этот id должен быть целочиÑленным', + 'The project id must be an integer' => 'Id проекта должен быть целочиÑленным', + 'The status must be an integer' => 'Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть целочиÑленным', + 'The subtask id is required' => 'Id подзадачи обÑзателен', + 'The subtask id must be an integer' => 'Id подзадачи должен быть целочиÑленным', + 'The task id is required' => 'Id задачи обÑзателен', + 'The task id must be an integer' => 'Id задачи должен быть целочиÑленным', + 'The user id must be an integer' => 'Id Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð´Ð¾Ð»Ð¶ÐµÐ½ быть целочиÑленным', + 'This value is required' => 'Это значение обÑзательно', + 'This value must be numeric' => 'Это значение должно быть цифровым', + 'Unable to create this task.' => 'Ðевозможно Ñоздать задачу.', + 'Cumulative flow diagram' => 'ÐÐ°ÐºÐ¾Ð¿Ð¸Ñ‚ÐµÐ»ÑŒÐ½Ð°Ñ Ð´Ð¸Ð°Ð³Ñ€Ð°Ð¼Ð¼Ð°', + 'Daily project summary' => 'Ежедневное ÑоÑтоÑние проекта', + 'Daily project summary export' => 'ЭкÑпорт ежедневного резюме проекта', + 'Exports' => 'ЭкÑпорт', + 'This export contains the number of tasks per column grouped per day.' => 'Этот ÑкÑпорт Ñодержит Ñ€Ñд задач в колонках, Ñгруппированные по днÑм.', + 'Active swimlanes' => 'Ðктивные дорожки', + 'Add a new swimlane' => 'Добавить новую дорожку', + 'Default swimlane' => 'Ð¡Ñ‚Ð°Ð½Ð´Ð°Ñ€Ñ‚Ð½Ð°Ñ Ð´Ð¾Ñ€Ð¾Ð¶ÐºÐ°', + 'Do you really want to remove this swimlane: "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите удалить дорожку "%s"?', + 'Inactive swimlanes' => 'Ðеактивные дорожки', + 'Remove a swimlane' => 'Удалить дорожку', + 'Swimlane modification for the project "%s"' => 'Редактирование дорожки Ð´Ð»Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð° "%s"', + 'Swimlane removed successfully.' => 'Дорожка уÑпешно удалена', + 'Swimlanes' => 'Дорожки', + 'Swimlane updated successfully.' => 'Дорожка уÑпешно обновлена.', + 'Unable to remove this swimlane.' => 'Ðевозможно удалить дорожку.', + 'Unable to update this swimlane.' => 'Ðевозможно обновить дорожку.', + 'Your swimlane has been created successfully.' => 'Ваша дорожка была уÑпешно Ñоздана.', + 'Example: "Bug, Feature Request, Improvement"' => 'Ðапример: "Баг, Фича, Улучшение"', + 'Default categories for new projects (Comma-separated)' => 'Стандартные категории Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð³Ð¾ проекта (разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтыми)', + 'Integrations' => 'Интеграции', + 'Integration with third-party services' => 'Ð˜Ð½Ñ‚ÐµÐ³Ñ€Ð°Ñ†Ð¸Ñ Ñо Ñторонними ÑервиÑами', + 'Subtask Id' => 'Id подзадачи', + 'Subtasks' => 'Подзадачи', + 'Subtasks Export' => 'ЭкÑпортировать подзадачи', + 'Task Title' => 'Заголовок задачи', + 'Untitled' => 'Заголовок отÑутÑтвует', + 'Application default' => 'Значение по умолчанию из приложениÑ', + 'Language:' => 'Язык:', + 'Timezone:' => 'Ð’Ñ€ÐµÐ¼ÐµÐ½Ð½Ð°Ñ Ð·Ð¾Ð½Ð°:', + 'All columns' => 'Ð’Ñе колонки', + 'Next' => 'Следующий', + '#%d' => '#%d', + 'All swimlanes' => 'Ð’Ñе дорожки', + 'All colors' => 'Ð’Ñе цвета', + 'Moved to column %s' => 'Перемещена в колонку %s', + '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' => 'Учёт времени', + 'You already have one subtask in progress' => 'У Ð²Ð°Ñ ÑƒÐ¶Ðµ еÑть одна задача в разработке', + 'Which parts of the project do you want to duplicate?' => 'Какие чаÑти проекта должны быть дублированы?', + 'Disallow login form' => 'Запретить форму входа', + 'Start' => 'Ðачало', + 'End' => 'Конец', + 'Task age in days' => 'ВозраÑÑ‚ задачи в днÑÑ…', + 'Days in this column' => 'Дней в Ñтой колонке', + '%dd' => '%dd', + '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' => 'Поле обÑзательно Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ', + 'Link added successfully.' => 'СÑылка уÑпешно добавлена', + 'Link updated successfully.' => 'СÑылка уÑпешно обновлена', + 'Link removed successfully.' => 'СÑылка уÑпешно удалена', + 'Link labels' => 'Метки Ð´Ð»Ñ ÑÑылки', + 'Link modification' => 'Обновление ÑÑылки', + 'Opposite label' => 'Ярлык напротив', + 'Remove a link' => 'Удалить ÑÑылку', + '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.' => 'Ðе удаётÑÑ ÑƒÐ´Ð°Ð»Ð¸Ñ‚ÑŒ Ñту ÑÑылку.', + 'relates to' => 'отноÑитÑÑ Ðº', + 'blocks' => 'блокирует', + 'is blocked by' => 'заблокирована', + 'duplicates' => 'дублирована', + 'is duplicated by' => 'дублирует', + 'is a child of' => 'ÑвлÑетÑÑ Ð¿Ñ€Ð¾Ð´Ð¾Ð»Ð¶ÐµÐ½Ð¸ÐµÐ¼', + 'is a parent of' => 'ÑвлÑетÑÑ Ð½Ð°Ñ‡Ð°Ð»Ð¾Ð¼ длÑ', + 'targets milestone' => 'нацелена на веху', + 'is a milestone of' => 'ÑвлÑетÑÑ Ð²ÐµÑ…Ð¾Ð¹ длÑ', + 'fixes' => 'иÑправлено', + 'is fixed by' => 'иÑправлÑет', + 'This task' => 'Эта задача', + '<1h' => '<1ч', + '%dh' => '%dh', + 'Expand tasks' => 'Развернуть задачи', + 'Collapse tasks' => 'Свернуть задачи', + 'Expand/collapse tasks' => 'Развернуть/Ñвернуть задачи', + 'Close dialog box' => 'Закрыть диалог', + 'Submit a form' => 'Отправить форму', + 'Board view' => 'ПроÑмотр доÑки', + 'Keyboard shortcuts' => 'ГорÑчие клавиши', + 'Open board switcher' => 'Открыть переключатель доÑки', + 'Application' => 'Приложение', + 'Compact view' => 'Компактный вид', + 'Horizontal scrolling' => 'Широкий вид', + 'Compact/wide view' => 'Компактный/широкий вид', + 'Currency' => 'Валюта', + 'Personal project' => 'ПерÑональный проект', + 'AUD - Australian Dollar' => 'AUD - ÐвÑтралийÑкий доллар', + 'CAD - Canadian Dollar' => 'CAD - КанадÑкий доллар', + 'CHF - Swiss Francs' => 'CHF - ШвейцарÑкий франк', + 'Custom Stylesheet' => 'ПользовательÑкий Ñтиль', + 'EUR - Euro' => 'EUR - Евро', + 'GBP - British Pound' => 'GBP - БританÑкий фунт', + 'INR - Indian Rupee' => 'INR - ИндийÑкий рупий', + 'JPY - Japanese Yen' => 'JPY - ЯпонÑÐºÐ°Ñ Ð¹ÐµÐ½Ð°', + 'NZD - New Zealand Dollar' => 'NZD - ÐовозеландÑкий доллар', + 'PEN - Peruvian Sol' => 'PEN - ПеруанÑкий Ñоль', + 'RSD - Serbian dinar' => 'RSD - СербÑкий динар', + 'CNY - Chinese Yuan' => 'CNY - КитайÑкий юань', + 'USD - US Dollar' => 'USD - ÐмериканÑкий доллар', + 'VES - Venezuelan Bolívar' => 'VES - ВенеÑуÑльÑкий боливар', + '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' => 'ВремÑ, проведенное в колонке', + '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.' => 'Этот отчёт Ñодержит вÑе Ð¿ÐµÑ€ÐµÐ¼ÐµÑ‰ÐµÐ½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡ в колонках Ñ Ð´Ð°Ñ‚Ð¾Ð¹, пользователем и времени, затраченным Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ перемещениÑ.', + 'Currency rates' => 'КурÑÑ‹ валют', + 'Rate' => 'КурÑ', + 'Change reference currency' => 'Изменить Ñправочник валют', + 'Reference currency' => 'Справочник валют', + 'The currency rate has been added successfully.' => 'ÐšÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚Ñ‹ был уÑпешно добавлен.', + 'Unable to add this currency rate.' => 'Ðевозможно добавить Ñтот ÐºÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚Ñ‹.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s удалил назначенного на задачу %s', + 'Information' => 'ИнформациÑ', + 'Check two factor authentication code' => 'Проверка кода двухфакторной авторизации', + 'The two factor authentication code is not valid.' => 'Код двухфакторной авторизации не валиден', + 'The two factor authentication code is valid.' => 'Код двухфакторной авторизации валиден', + 'Code' => 'Код', + 'Two factor authentication' => 'Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°Ð²Ñ‚Ð¾Ñ€Ð¸Ð·Ð°Ñ†Ð¸Ñ', + 'This QR code contains the key URI: ' => 'Это QR-код Ñодержит ключевую URI:', + 'Check my code' => 'Проверить мой код', + 'Secret key: ' => 'Секретный ключ: ', + 'Test your device' => 'Проверьте Ñвое уÑтройÑтво', + 'Assign a color when the task is moved to a specific column' => 'Ðазначить цвет, когда задача перемещаетÑÑ Ð² определенную колонку', + '%s via Kanboard' => '%s через Kanboard', + 'Burndown chart' => 'Диаграмма ÑгораниÑ', + '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 - ШведÑÐºÐ°Ñ ÐºÑ€Ð¾Ð½Ð°', + 'Identifier' => 'Идентификатор', + 'Disable two factor authentication' => 'Выключить двухфакторную авторизацию', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите выключить двухфакторную авторизацию Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ "%s"?', + 'Edit link' => 'Редактировать ÑÑылку', + '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' => 'ПериодичеÑÐºÐ°Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° запланирована к Ñозданию', + 'Score' => 'Оценка', + 'The identifier must be unique' => 'Идентификатор должен быть уникальным', + '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' => 'Временные рамки Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты завершениÑ', + '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: ' => 'КоÑффициент Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты завершениÑ: ', + 'Month(s)' => 'МеÑÑц(а)', + 'This task has been created by: ' => 'Эта задача была Ñоздана: ', + 'Recurrent task has been generated:' => 'ПериодичеÑÐºÐ°Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° была Ñформирована:', + 'Timeframe to calculate new due date: ' => 'Временные рамки Ð´Ð»Ñ Ñ€Ð°Ñчёта новой даты завершениÑ: ', + 'Trigger to generate recurrent task: ' => 'Триггер Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ периодичеÑкой задачи: ', + 'When task is closed' => 'Когда задача закрываетÑÑ', + 'When task is moved from first column' => 'Когда задача перемещаетÑÑ Ð¸Ð· первой колонки', + 'When task is moved to last column' => 'Когда задача перемещаетÑÑ Ð² поÑледнюю колонку', + 'Year(s)' => 'Год(а)', + 'Project settings' => 'ÐаÑтройки проекта', + 'Automatically update the start date' => 'ÐвтоматичеÑкое обновление даты начала', + 'iCal feed' => 'iCal данные', + 'Preferences' => 'ПредпочтениÑ', + 'Security' => 'БезопаÑноÑть', + 'Two factor authentication disabled' => 'Ð”Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ Ð¾Ñ‚ÐºÐ»ÑŽÑ‡ÐµÐ½Ð°', + 'Two factor authentication enabled' => 'Включена Ð´Ð²ÑƒÑ…Ñ„Ð°ÐºÑ‚Ð¾Ñ€Ð½Ð°Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ñ', + 'Unable to update this user.' => 'Ðе удаётÑÑ Ð¾Ð±Ð½Ð¾Ð²Ð¸Ñ‚ÑŒ Ñтого пользователÑ.', + 'There is no user management for personal projects.' => 'Ð”Ð»Ñ Ð¿ÐµÑ€Ñональных проектов управление пользователÑми не предуÑмотрено.', + 'User that will receive the email' => 'Пользователь, который будет получать e-mail', + 'Email subject' => 'Тема e-mail', + 'Date' => 'Дата', + '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' => 'Отправить задачу по email', + 'Reopen a task' => 'Переоткрыть задачу', + 'Notification' => 'УведомлениÑ', + '%s moved the task #%d to the first swimlane' => '%s перемеÑтил задачу #%d на первую дорожку', + 'Swimlane' => 'Дорожки', + '%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.' => 'Этот отчёт Ñодержит вÑÑŽ информацию Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸ в заданном диапазоне дат.', + 'Project activities for %s' => 'ÐктивноÑть проекта Ð´Ð»Ñ %s', + 'view the board on Kanboard' => 'поÑмотреть доÑку на Kanboard', + 'The task has been moved to the first swimlane' => 'Эта задача была перемещена в первую дорожку', + 'The task has been moved to another swimlane:' => 'Эта задача была перемещена в другую дорожку:', + 'New title: %s' => 'Ðовый заголовок: %s', + 'The task is not assigned anymore' => 'Задача больше не назначена', + 'New assignee: %s' => 'Ðовый назначенный: %s', + 'There is no category now' => 'Ð’ наÑтоÑщее Ð²Ñ€ÐµÐ¼Ñ Ð·Ð´ÐµÑÑŒ нет категорий', + 'New category: %s' => 'ÐÐ¾Ð²Ð°Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ: %s', + 'New color: %s' => 'Ðовый цвет: %s', + 'New complexity: %d' => 'ÐÐ¾Ð²Ð°Ñ ÑложноÑть: %d', + 'The due date has been removed' => 'Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð±Ñ‹Ð»Ð° удалена', + 'There is no description anymore' => 'ЗдеÑÑŒ больше нет опиÑаниÑ', + 'Recurrence settings has been modified' => 'ÐаÑтройки повтора были изменены', + 'Time spent changed: %sh' => 'Изменение количеÑтва затраченного времени: %sh', + 'Time estimated changed: %sh' => 'Ожидаемый Ñрок изменён: %sh', + 'The field "%s" has 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:' => 'Я хочу получать ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð´Ð»Ñ:', + 'All tasks' => 'Ð’Ñе задачи', + 'Only for tasks assigned to me' => 'Только Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡, назначенных мне', + 'Only for tasks created by me' => 'Только Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡, Ñозданных мной', + 'Only for tasks created by me and tasks assigned to me' => 'Только Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡, Ñозданных мной и назначенных мне', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Суммарно Ð´Ð»Ñ Ð²Ñех колонок', + 'You need at least 2 days of data to show the chart.' => 'Ð”Ð»Ñ Ð¾Ñ‚Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð¸Ñ Ð´Ð¸Ð°Ð³Ñ€Ð°Ð¼Ð¼Ñ‹ нужно по крайней мере 2 днÑ.', + '<15m' => '<15м', + '<30m' => '<30м', + 'Stop timer' => 'ОÑтановить таймер', + 'Start timer' => 'ЗапуÑтить таймер', + 'My activity stream' => 'Лента моей активноÑти', + 'Search tasks' => 'ПоиÑк задачи', + 'Reset filters' => 'СброÑить фильтры', + 'My tasks due tomorrow' => 'Мои задачи на завтра', + 'Tasks due today' => 'Задачи, завершающиеÑÑ ÑегоднÑ', + 'Tasks due tomorrow' => 'Задачи, завершающиеÑÑ Ð·Ð°Ð²Ñ‚Ñ€Ð°', + 'Tasks due yesterday' => 'Задачи, завершившиеÑÑ Ð²Ñ‡ÐµÑ€Ð°', + 'Closed tasks' => 'Закрытые задачи', + 'Open tasks' => 'Открытые задачи', + 'Not assigned' => 'Ðе назначенные', + 'View advanced search syntax' => 'ПроÑмотр раÑширенного ÑинтакÑиÑа поиÑка', + 'Overview' => 'Обзор', + 'Board/Calendar/List view' => 'ПроÑмотр ДоÑка/Календарь/СпиÑок', + 'Switch to the board view' => 'ПереключитьÑÑ Ð² режим доÑки', + 'Switch to the list view' => 'ПереключитьÑÑ Ð² режим ÑпиÑка', + 'Go to the search/filter box' => 'Перейти в поиÑк/фильтр', + 'There is no activity yet.' => 'ÐктивноÑти еще не было', + 'No tasks found.' => 'Задач не найдено.', + 'Keyboard shortcut: "%s"' => 'Сочетание клавиш: "%s"', + 'List' => 'СпиÑок', + 'Filter' => 'Фильтр', + 'Advanced search' => 'РаÑширенный поиÑк', + 'Example of query: ' => 'Пример запроÑа: ', + 'Search by project: ' => 'ПоиÑк по проекту: ', + 'Search by column: ' => 'ПоиÑк по колонкам: ', + 'Search by assignee: ' => 'ПоиÑк по назначенному: ', + 'Search by color: ' => 'ПоиÑк по цвету: ', + 'Search by category: ' => 'ПоиÑк по категориÑм: ', + 'Search by description: ' => 'ПоиÑк по опиÑанию: ', + 'Search by due date: ' => 'ПоиÑк по дате завершениÑ: ', + 'Average time spent in each column' => 'Затрачено времени в Ñреднем в каждой колонке', + 'Average time spent' => 'Затрачено времени в Ñреднем', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Эта диаграмма показывает Ñреднее времÑ, проведенное в каждой колонке Ð´Ð»Ñ Ð¿Ð¾Ñледних %d задач.', + 'Average Lead and Cycle time' => 'Среднее Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¸ цикла', + 'Average lead time: ' => 'Среднее Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ: ', + 'Average cycle time: ' => 'Среднее Ð²Ñ€ÐµÐ¼Ñ Ñ†Ð¸ÐºÐ»Ð°: ', + 'Cycle Time' => 'Ð’Ñ€ÐµÐ¼Ñ Ñ†Ð¸ÐºÐ»Ð°', + 'Lead Time' => 'Ð’Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Эта диаграмма показывает Ñреднее Ð²Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¸ цикла Ð´Ð»Ñ Ð¿Ð¾Ñледних %d задач.', + 'Average time into each column' => 'Среднее Ð²Ñ€ÐµÐ¼Ñ Ð² каждом Ñтолбце', + 'Lead and cycle time' => 'Ð’Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ Ð¸ цикла', + 'Lead time: ' => 'Ð’Ñ€ÐµÐ¼Ñ Ð²Ñ‹Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ:', + 'Cycle time: ' => 'Ð’Ñ€ÐµÐ¼Ñ Ñ†Ð¸ÐºÐ»Ð°:', + 'Time spent in 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 the start date automatically' => 'УÑтановить дату начала автоматичеÑки', + '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.' => 'ЕÑли вы уÑтановите флажок "Запретить форму входа", учётные данные, введенные в форму входа будет игнорироватьÑÑ.', + '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 category: %s' => 'Ð¢ÐµÐºÑƒÑ‰Ð°Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ: %s', + 'no category' => 'без категории', + 'Current assignee: %s' => 'Текущее назначенное лицо: %s', + 'not assigned' => 'не назначен', + 'Author:' => 'Ðвтор:', + 'contributors' => 'Ñоавторы', + 'License:' => 'ЛицензиÑ:', + 'License' => 'ЛицензиÑ', + 'Enter the text below' => 'Введите текÑÑ‚ ниже', + 'Start date:' => 'Дата начала:', + 'Due date:' => 'Дата завершениÑ:', + 'People who are project managers' => 'Люди, которые ÑвлÑÑŽÑ‚ÑÑ Ð¼ÐµÐ½ÐµÐ´Ð¶ÐµÑ€Ð°Ð¼Ð¸ проекта', + 'People who are project members' => 'Люди, которые ÑвлÑÑŽÑ‚ÑÑ ÑƒÑ‡Ð°Ñтниками проекта', + 'NOK - Norwegian Krone' => 'ÐК - ÐорвежÑÐºÐ°Ñ ÐºÑ€Ð¾Ð½Ð°', + 'Show this column' => 'Показать Ñту колонку', + 'Hide this column' => 'СпрÑтать Ñту колонку', + 'End date' => 'Дата завершениÑ', + 'Users overview' => 'Обзор пользователей', + 'Members' => 'УчаÑтники', + 'Shared project' => 'Общие/публичные проекты', + 'Project managers' => 'Менеджер проекта', + 'Projects list' => 'СпиÑок проектов', + 'End date:' => 'Дата завершениÑ:', + 'Change task color when using a specific task link' => 'Изменение цвета задач при иÑпользовании ÑÑылки на определенные задачи', + 'Task link creation or modification' => 'СÑылка на Ñоздание или модификацию задачи', + 'Milestone' => 'Веха', + 'Reset the search/filter box' => 'СброÑить поиÑк/фильтр', + 'Documentation' => 'ДокументациÑ', + 'Author' => 'Ðвтор', + 'Version' => 'ВерÑиÑ', + 'Plugins' => 'Плагины', + 'There is no plugin loaded.' => 'Ðет уÑтановленных плагинов.', + 'My notifications' => 'Мои уведомлениÑ', + 'Custom filters' => 'ПользовательÑкие фильтры', + 'Your custom filter has 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 has been updated successfully.' => 'ПользовательÑкий фильтр был уÑпешно обновлен.', + 'Unable to update custom filter.' => 'Ðевозможно обновить фильтр.', + 'Web' => 'Интернет', + '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', + 'New task #%d: %s' => 'ÐÐ¾Ð²Ð°Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° #%d: %s', + 'Task updated #%d' => 'Обновлена задача #%d', + 'Task #%d closed' => 'Задача #%d закрыта', + 'Task #%d opened' => 'Задача #%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', + '%d overdue tasks' => '%d проÑроченных задач', + 'No notification.' => 'Ðет новых уведомлений.', + '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:' => 'СпоÑобы уведомлениÑ:', + 'Unable to read your file' => 'Ðевозможно прочитать файл', + '%d task(s) have been imported successfully.' => '%d задач было уÑпешно импортировано.', + 'Nothing has been imported!' => 'Ðичего не было импортировано!', + 'Import users from CSV file' => 'Импорт пользователей из CSV-файла', + '%d user(s) have been imported successfully.' => '%d пользователей было уÑпешно импортировано.', + 'Comma' => 'ЗапÑтаÑ', + 'Semi-colon' => 'Точка Ñ Ð·Ð°Ð¿Ñтой', + 'Tab' => 'Пробел (Tab)', + 'Vertical bar' => 'Ð’ÐµÑ€Ñ‚Ð¸ÐºÐ°Ð»ÑŒÐ½Ð°Ñ Ñ‡ÐµÑ€Ñ‚Ð° (|)', + '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)' => 'ДобавлÑющий фильтр (не заменÑющий)', + 'Append/Replace' => 'Добавление/Замена', + 'Append' => 'Добавление', + 'Replace' => 'Замена', + 'Import' => 'Импорт', + 'Change sorting' => 'изменить Ñортировку', + 'Tasks Importation' => 'Импортирование задач', + 'Delimiter' => 'Разделитель', + 'Enclosure' => 'Тип кавычек', + 'CSV File' => 'CSV-файл', + 'Instructions' => 'ИнÑтрукции', + 'Your file must use the predefined CSV format' => 'Ваш файл должен иÑпользовать Ñтруктуру формата CSV', + 'Your file must be encoded in UTF-8' => 'Ваш файл должен иметь кодировку 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' => 'Дата Ð·Ð°Ð²ÐµÑ€ÑˆÐµÐ½Ð¸Ñ Ð´Ð¾Ð»Ð¶Ð½Ð° быть в формате ISO: ГГГГ-ММ-ДД', + 'Download CSV template' => 'Скачать шаблон CSV-файла', + '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' => '%s добавил новый файл к задаче %s', + 'Link type' => 'Тип ÑÑылки', + 'Assign automatically a category based on a link' => 'ÐвтоматичеÑки назначить категории на оÑнове ÑÑылки', + 'BAM - Konvertible Mark' => 'BAM - ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð¸Ñ€ÑƒÐµÐ¼Ð°Ñ Ð¼Ð°Ñ€ÐºÐ°', + 'Assignee Username' => 'Логин назначенного', + 'Assignee Name' => 'Ð˜Ð¼Ñ Ð½Ð°Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ð¾Ð³Ð¾', + 'Groups' => 'Группы', + 'Members of %s' => 'УчаÑтник группы %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"' => 'Добавить учаÑтника в "%s"', + 'Group member added successfully.' => 'УчаÑтник группы уÑпешно добавлен.', + 'Unable to add group member.' => 'Ðевозможно добавить учаÑтника.', + 'Remove user from group "%s"' => 'Удалить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð· группы "%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' => 'Ваш аккаунт заблокирован на %d минут', + 'Invalid captcha' => 'Ðеверный код подтверждениÑ', + 'The name must be unique' => 'Ð˜Ð¼Ñ Ð´Ð¾Ð»Ð¶Ð½Ð¾ быть уникальным', + 'View all groups' => 'ПроÑмотр вÑех групп', + 'There is no user available.' => 'Ðет доÑтупных пользователей.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите удалить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ "%s" из группы "%s"?', + 'There is no group.' => 'Ðет Ñозданных групп.', + 'Add group member' => 'Добавить учаÑтника в группу', + 'Do you really want to remove this group: "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите удалить группу "%s"?', + 'There is no user in this group.' => 'Ð’ Ñтой группе нет учаÑтников.', + 'Permissions' => 'РазрешениÑ', + 'Allowed Users' => 'Разрешенные пользователи', + 'No specific user has been allowed.' => 'Ðет заданных разрешений Ð´Ð»Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»ÐµÐ¹.', + 'Role' => 'Роль', + 'Enter user name...' => 'Введите логин пользователÑ...', + 'Allowed Groups' => 'Разрешенные группы', + 'No group has been allowed.' => 'Ðет заданных разрешений Ð´Ð»Ñ Ð³Ñ€ÑƒÐ¿Ð¿.', + 'Group' => 'Группа', + 'Group Name' => 'Ð˜Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹', + 'Enter group name...' => 'Введите Ð¸Ð¼Ñ Ð³Ñ€ÑƒÐ¿Ð¿Ñ‹...', + 'Role:' => 'Роль:', + 'Project members' => 'УчаÑтники проекта', + '%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', + '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' => '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' => 'Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð´Ð»Ñ Kanboard', + 'Forgot password?' => 'Забыли пароль?', + 'Enable "Forget Password"' => 'Включить возможноÑть воÑÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ', + 'Password Reset' => 'Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ', + 'New password' => 'Ðовый пароль', + 'Change Password' => 'Изменить пароль', + 'To reset your password click on this link:' => 'Чтобы изменить пароль нажмите на Ñту ÑÑылку:', + 'Last Password Reset' => 'ПоÑледний ÑÐ±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ', + 'The password has never been reinitialized.' => 'Пароль никогда не был Ñброшен.', + 'Creation' => 'Создан', + 'Expiration' => 'ИÑтекает', + 'Password reset history' => 'ИÑÑ‚Ð¾Ñ€Ð¸Ñ ÑброÑа паролÑ', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Ð’Ñе задачи Ð´Ð»Ñ ÐºÐ¾Ð»Ð¾Ð½ÐºÐ¸ "%s" и дорожки "%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.' => '%d задач в колонке "%s" и дорожке "%s" будут закрыты.', + 'Close all tasks in this column and this swimlane' => 'Закрыть вÑе задачи в Ñтой колонке', + '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.' => 'Идентификатор проекта не обÑзателен и должен Ñодержать буквенно-цифровые Ñимволы, пример: MYPROJECT', + 'Project owner' => 'Владелец проекта', + 'Personal projects do not have users and groups management.' => 'ПерÑональные проекты не имеют ÑƒÐ¿Ñ€Ð°Ð²Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñми и группами.', + 'There is no project member.' => 'Ðет учаÑтников проекта.', + 'Priority' => 'Приоритет', + 'Task priority' => 'Приоритет задачи', + 'General' => 'Общее', + 'Dates' => 'Даты', + 'Default priority' => 'Приоритет по умолчанию', + 'Lowest priority' => 'Ðаименьший приоритет', + 'Highest priority' => 'ÐаивыÑший приоритет', + 'Close a task when there is no activity' => 'Закрывать задачу, когда нет активноÑти', + 'Duration in days' => 'ДлительноÑть в днÑÑ…', + 'Send email when there is no activity on a task' => 'ОтправлÑть email, когда активноÑть по задаче отÑутÑтвует', + 'Unable to fetch link information.' => 'Ðе удалоÑÑŒ получить информацию о ÑÑылке', + 'Daily background job for tasks' => 'Ежедневные фоновые работы Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡', + 'Auto' => 'Ðвто', + 'Related' => 'СвÑзано', + 'Attachment' => 'Вложение', + 'Web Link' => 'Web-ÑÑылка', + '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' => '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' => '%s чаÑов', + 'Time spent:' => 'Времени потрачено:', + 'Created:' => 'Создана:', + 'Modified:' => 'Изменена:', + 'Completed:' => 'Завершена:', + 'Started:' => 'Ðачата:', + 'Moved:' => 'Перемещена:', + 'Task #%d' => 'Задача #%d', + 'Time format' => 'Формат времени', + 'Start date: ' => 'Дата начала: ', + 'End date: ' => 'Дата окончаниÑ: ', + 'New due date: ' => 'ÐÐ¾Ð²Ð°Ñ Ð´Ð°Ñ‚Ð° завершениÑ: ', + 'Start date changed: ' => 'Дата начала изменена:', + 'Disable personal projects' => 'Выключить перÑональные проекты', + 'Do you really want to remove this custom filter: "%s"?' => 'Ð’Ñ‹ точно ходите удалить Ñтот пользовательÑкий фильтр: "%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.' => 'Ð’Ñе файлы были уÑпешно загружены.', + 'The maximum allowed file size is %sB.' => 'МакÑимально допуÑтимый размер файла: %sB.', + 'Drag and drop your files here' => 'ПеремеÑтите ваши файлы Ñюда', + 'choose files' => 'выбор файлов', + 'View profile' => 'ПроÑмотр профилÑ', + 'Two Factor' => 'Двухфакторный', + 'Disable user' => 'Выключить пользователÑ', + 'Do you really want to disable this user: "%s"?' => 'Ð’Ñ‹ точно хотите выключить Ñтого пользователÑ: "%s"?', + 'Enable user' => 'Включить пользователÑ', + 'Do you really want to enable this user: "%s"?' => 'Ð’Ñ‹ точно хотите включить Ñтого пользователÑ: "%s"?', + 'Download' => 'Загрузка', + 'Uploaded: %s' => 'Загружено: %s', + 'Size: %s' => 'Размер: %s', + 'Uploaded by %s' => 'Загружено пользователем: %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' => 'Это значение должно быть больше, чем %d', + 'Another swimlane with the same name exists in the project' => 'Ð”Ñ€ÑƒÐ³Ð°Ñ Ð´Ð¾Ñ€Ð¾Ð¶ÐºÐ° Ñ Ñ‚Ð°ÐºÐ¸Ð¼ же именем уже ÑущеÑтвует в Ñтом проекте', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Пример: https://example.kanboard.org/ (иÑпользуетÑÑ Ð´Ð»Ñ Ð³ÐµÐ½ÐµÑ€Ð°Ñ†Ð¸Ð¸ абÑолютных 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:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'ВерÑÐ¸Ñ ÐžÐ¡:', + 'Database version:' => 'ВерÑÐ¸Ñ Ð‘Ð”:', + 'Browser:' => 'Браузер:', + 'Task view' => 'ПроÑмотр задачи', + 'Edit task' => 'Изменение задачи', + 'Edit description' => 'Изменение опиÑаниÑ', + 'New internal link' => 'ÐÐ¾Ð²Ð°Ñ Ð²Ð½ÑƒÑ‚Ñ€ÐµÐ½Ð½ÑÑ ÑÑылка', + 'Display list of keyboard shortcuts' => 'Показать ÑпиÑок клавиатурных Ñокращений', + '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' => '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 a 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 has 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', + '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', + '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 приглашений было отправлено.', + '%d invitation was sent.' => '%d приглашение было отправлено.', + 'Unable to create this user.' => 'Ðе удалоÑÑŒ Ñоздать Ñтого пользователÑ.', + 'Kanboard Invitation' => 'Приглашение в Kanboard', + '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.' => 'Ð’Ñ‹ были приглашены зарегиÑтрироватьÑÑ Ð² 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' => 'Это Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð·Ð°Ð½Ñто', + 'Your profile must have a valid email address.' => 'Ð’ вашем профиле должен быть указан корректный Ð°Ð´Ñ€ÐµÑ e-mail.', + 'TRL - Turkish Lira' => 'TRL - Ð¢ÑƒÑ€ÐµÑ†ÐºÐ°Ñ Ð»Ð¸Ñ€Ð°', + 'The project email is optional and could be used by several plugins.' => 'E-mail проекта ÑвлÑетÑÑ Ð½ÐµÐ¾Ð±Ñзательным атрибутом и может быть иÑпользован некоторыми плагинами', + 'The project email must be unique across all projects' => 'E-mail проекта должен быть уникальным Ð´Ð»Ñ ÐºÐ°Ð¶Ð´Ð¾Ð³Ð¾ проекта', + 'The email configuration has been disabled by the administrator.' => 'ÐаÑтройка e-mail была отключена админиÑтратором.', + 'Close this project' => 'Закрыть Ñтот проект', + 'Open this project' => 'Открыть Ñтот проект', + 'Close a project' => 'Закрыть проект', + 'Do you really want to close this project: "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите закрыть проект: "%s"?', + 'Reopen a project' => 'Открыть проект', + 'Do you really want to reopen this project: "%s"?' => 'Ð’Ñ‹ дейÑтвительно хотите открыть проект: "%s"?', + 'This project is open' => 'Этот проект открыт', + 'This project is closed' => 'Этот проект закрыт', + 'Unable to upload files, check the permissions of your data folder.' => 'Ðевозможно загрузить файлы, проверьте права доÑтупа на папку "data".', + 'Another category with the same name exists in this project' => 'Ð”Ñ€ÑƒÐ³Ð°Ñ ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ñ Ñ‚Ð°ÐºÐ¸Ð¼ же именем уже ÑущеÑтвует в Ñтом проекте', + 'Comment sent by email successfully.' => 'Комментарий уÑпешно отправлен по e-mail.', + 'Sent by email to "%s" (%s)' => 'Отправлен по e-mail Ð´Ð»Ñ "%s" (%s)', + 'Unable to read uploaded file.' => 'Ðе удаётÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚ÑŒ загруженный файл.', + 'Database uploaded successfully.' => 'База данных уÑпешно импортирована.', + 'Task sent by email successfully.' => 'Задача уÑпешно отправлена по e-mail.', + 'There is no category in this project.' => 'Ð”Ð»Ñ Ñтого проекта не задана категориÑ.', + 'Send by email' => 'Отправить по e-mail', + 'Create and send a comment by email' => 'Создать и отправить комментарий на e-mail', + 'Subject' => 'Тема', + 'Upload the database' => 'Импорт базы данных', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Ð’Ñ‹ можете импортировать предварительно Ñозданный файл выгрузки базы данных SQLite (формат Gzip).', + 'Database file' => 'Файл выгрузки БД', + 'Upload' => 'Импорт', + 'Your project must have at least one active swimlane.' => 'Ваш проект должен иметь Ñ…Ð¾Ñ‚Ñ Ð±Ñ‹ одну активную дорожку.', + 'Project: %s' => 'Проект: %s', + 'Automatic action not found: "%s"' => 'ÐвтоматичеÑкое дейÑтвие не найдено: "%s"', + '%d projects' => '%d проектов', + '%d project' => '%d проект', + 'There is no project.' => 'Ðет проектов.', + 'Sort' => 'Сортировка', + 'Project ID' => 'ID проекта', + 'Project name' => 'Ð˜Ð¼Ñ Ð¿Ñ€Ð¾ÐµÐºÑ‚Ð°', + 'Public' => 'Публичный', + 'Personal' => 'ПерÑональный', + '%d tasks' => '%d задач', + '%d task' => '%d задачу', + 'Task ID' => 'ID задачи', + 'Assign automatically a color when due date is expired' => 'ÐвтоматичеÑки назначать цвет поÑле иÑÑ‚ÐµÑ‡ÐµÐ½Ð¸Ñ Ñрока задачи', + 'Total score in this column across all swimlanes' => 'ÐžÐ±Ñ‰Ð°Ñ Ð¾Ñ†ÐµÐ½ÐºÐ° в Ñтой колонке Ñреди вÑех дорожек', + 'HRK - Kuna' => 'HRK - ХорватÑÐºÐ°Ñ ÐºÑƒÐ½Ð°', + 'ARS - Argentine Peso' => 'ARS - ÐргентинÑкий пеÑо', + 'COP - Colombian Peso' => 'COP - КолумбийÑкий пеÑо', + '%d groups' => '%d групп', + '%d group' => '%d группа', + 'Group ID' => 'ID группы', + 'External ID' => 'Внешний ID', + '%d users' => '%d пользователей', + '%d user' => '%d пользователь', + 'Hide subtasks' => 'Скрыть подзадачи', + 'Show subtasks' => 'Показать подзадачи', + 'Authentication Parameters' => 'Параметры аутентификации', + 'API Access' => 'ДоÑтуп к API', + 'No users found.' => 'Пользователи не найдены.', + 'User ID' => 'ID пользователÑ', + 'Notifications are activated' => 'Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð°ÐºÑ‚Ð¸Ð²Ð¸Ñ€Ð¾Ð²Ð°Ð½Ñ‹', + 'Notifications are disabled' => 'Ð£Ð²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð²Ñ‹ÐºÐ»ÑŽÑ‡ÐµÐ½Ñ‹', + 'User disabled' => 'Пользователь выключен', + '%d notifications' => '%d уведомлений', + '%d notification' => '%d уведомление', + 'There is no external integration installed.' => 'Внешние интеграции не уÑтановлены.', + 'You are not allowed to update tasks assigned to someone else.' => 'Ð’Ñ‹ не можете обновлÑть задачи, назначенные другому пользователю.', + 'You are not allowed to change the assignee.' => 'Ð’Ñ‹ не можете изменить назначенного на Ñту задачу.', + 'Task suppression is not permitted' => 'Удаление задач не разрешено', + 'Changing assignee is not permitted' => 'Изменение назначенного не разрешено', + 'Update only assigned tasks is permitted' => 'Разрешено обновление только назначенных задач', + 'Only for tasks assigned to the current user' => 'Только Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡, назначенных текущему пользователю', + 'My projects' => 'Мои проекты', + 'You are not a member of any project.' => 'Ð’Ñ‹ не ÑвлÑетеÑÑŒ учаÑтником какого-либо проекта.', + 'My subtasks' => 'Мои подзадачи', + '%d subtasks' => '%d подзадач', + '%d subtask' => '%d подзадача', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Текущий пользователь может перемещать назначенные ему задачи только между Ñтими колонками', + '[DUPLICATE]' => '[КОПИЯ]', + 'DKK - Danish Krona' => 'DKK - ДатÑÐºÐ°Ñ ÐºÑ€Ð¾Ð½Ð°', + 'Remove user from group' => 'Удалить Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ð¸Ð· группы', + 'Assign the task to its creator' => 'Ðазначать задачу её Ñоздателю', + 'This task was sent by email to "%s" with subject "%s".' => 'Эта задача была отправлена по e-mail на "%s" Ñ Ñ‚ÐµÐ¼Ð¾Ð¹ "%s".', + 'Predefined Email Subjects' => 'ПредуÑтановленные темы Ð´Ð»Ñ e-mail', + 'Write one subject by line.' => 'ЗапиÑываютÑÑ Ð¿Ð¾ одной теме на Ñтроку.', + 'Create another link' => 'Создать другую ÑÑылку', + 'BRL - Brazilian Real' => 'BRL - бразильÑкий реал', + 'Add a new Kanboard task' => 'Добавить новую задачу на Kanboard', + 'Subtask not started' => 'Подзадача не начата', + 'Subtask currently in progress' => 'Подзадача в процеÑÑе выполнениÑ', + 'Subtask completed' => 'Подзадача завершена', + 'Subtask added successfully.' => 'Подзадача уÑпешно добавлена.', + '%d subtasks added successfully.' => '%d подзадач(а) уÑпешно добавлено.', + 'Enter one subtask by line.' => 'ЗапиÑывайте по одной подзадаче на Ñтроку.', + 'Predefined Contents' => 'Шаблоны Ñодержимого', + 'Predefined contents' => 'Шаблоны Ñодержимого', + 'Predefined Task Description' => 'Шаблон опиÑÐ°Ð½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸', + 'Do you really want to remove this template? "%s"' => 'Ð’Ñ‹ дейÑтвительно хотите удалить Ñтот шаблон? "%s"', + 'Add predefined task description' => 'Добавить шаблон опиÑÐ°Ð½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸', + 'Predefined Task Descriptions' => 'Шаблоны опиÑÐ°Ð½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡', + 'Template created successfully.' => 'Шаблон Ñоздан уÑпешно.', + 'Unable to create this template.' => 'Произошла ошибка при Ñоздании шаблона.', + 'Template updated successfully.' => 'Шаблон уÑпешно обновлен.', + 'Unable to update this template.' => 'Произошла ошибка при обновлении шаблона.', + 'Template removed successfully.' => 'Шаблон уÑпешно удалён.', + 'Unable to remove this template.' => 'Произошла ошибка при удалении шаблона.', + 'Template for the task description' => 'Шаблон Ð´Ð»Ñ Ð¾Ð¿Ð¸ÑÐ°Ð½Ð¸Ñ Ð·Ð°Ð´Ð°Ñ‡Ð¸', + 'The start date is greater than the end date' => 'Дата начала не должна быть позже даты завершениÑ', + 'Tags must be separated by a comma' => 'Метки разделÑÑŽÑ‚ÑÑ Ð·Ð°Ð¿Ñтой', + 'Only the task title is required' => 'Ðазвание задачи обÑзательно Ð´Ð»Ñ Ð·Ð°Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸Ñ', + 'Creator Username' => 'Логин ÑоздателÑ', + 'Color Name' => 'Цвет', + 'Column Name' => 'Колонка', + 'Swimlane Name' => 'Дорожка', + 'Time Estimated' => 'РаÑчётное времÑ', + 'Time Spent' => 'Времени потрачено', + 'External Link' => 'ВнешнÑÑ ÑÑылка', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Эта Ð¾Ð¿Ñ†Ð¸Ñ Ð²ÐºÐ»ÑŽÑ‡Ð¸Ñ‚ ленту iCal и RSS-поток, а также публичный доÑтуп к доÑке.', + 'Stop the timer of all subtasks when moving a task to another column' => 'ОÑтановить таймеры вÑех подзадач при перемещении задачи в другую колонку', + 'Subtask Title' => 'Ðазвание подзадачи', + 'Add a subtask and activate the timer when moving a task to another column' => 'ДобавлÑть подзадачу и активировать таймер при перемещении задачи в другую колонку', + 'days' => 'дней', + 'minutes' => 'минут', + 'seconds' => 'Ñекунд', + 'Assign automatically a color when preset start date is reached' => 'ÐвтоматичеÑки назначить цвет, когда дата начала доÑтигнута', + 'Move the task to another column once a predefined start date is reached' => 'ПеремеÑтить задачу в другую колонку, когда дата начала доÑтигнута', + 'This task is now linked to the task %s with the relation "%s"' => 'Эта задача теперь ÑвÑзана Ñ %s Ñ Ð¾Ñ‚Ð½Ð¾ÑˆÐµÐ½Ð¸ÐµÐ¼ "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Удалена ÑвÑзь Ñ Ð¾Ñ‚Ð½Ð¾ÑˆÐµÐ½Ð¸ÐµÐ¼ "%s" к задаче %s', + 'Custom Filter:' => 'ÐаÑтраиваемый фильтр:', + 'Unable to find this group.' => 'Ðевозможно найти Ñту группу.', + '%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 moved the task #%d to the swimlane "%s"' => '%s Ð¿ÐµÑ€ÐµÐ½Ñ‘Ñ Ð·Ð°Ð´Ð°Ñ‡Ñƒ #%d на дорожку "%s"', + '%sh spent' => '%sч затрачено', + '%sh estimated' => '%sч запланировано', + 'Select All' => 'Выделить вÑе', + 'Unselect All' => 'СнÑть выделение Ñо вÑех', + 'Apply action' => 'Применить дейÑтвие', + 'Move selected tasks to another column or swimlane' => 'ПеремеÑтить выбранные задачи в другую колонку', + 'Edit tasks in bulk' => 'Пакетное редактирование задач', + 'Choose the properties that you would like to change for the selected tasks.' => 'Выберите ÑвойÑтва, которые вы хотите изменить Ð´Ð»Ñ Ð²Ñ‹Ð±Ñ€Ð°Ð½Ð½Ñ‹Ñ… задач', + 'Configure this project' => 'ÐаÑтроить Ñтот проект', + 'Start now' => 'Ðачать ÑейчаÑ', + '%s removed a file from the task #%d' => '%s удалил файл из задачи #%d', + 'Attachment removed from task #%d: %s' => 'Вложение удалено из задачи #%d: %s', + 'No color' => 'Без цвета', + 'Attachment removed "%s"' => 'Вложение удалено "%s"', + '%s removed a file from the task %s' => '%s удалил файл из задачи %s', + 'Move the task to another swimlane when assigned to a user' => 'ПеремеÑтить задачу в другую дорожку, когда она приÑваиваетÑÑ Ð´Ñ€ÑƒÐ³Ð¾Ð¼Ñƒ пользователю', + 'Destination swimlane' => 'Ð¦ÐµÐ»ÐµÐ²Ð°Ñ Ð´Ð¾Ñ€Ð¾Ð¶ÐºÐ°', + 'Assign a category when the task is moved to a specific swimlane' => 'ПриÑвоить категорию, еÑли задача перемещена в определенную дорожку', + 'Move the task to another swimlane when the category is changed' => 'ПеремеÑтить задачу в другую дорожку, еÑли ÐºÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ð¸Ñ Ð¸Ð·Ð¼ÐµÐ½ÐµÐ½Ð°', + 'Reorder this column by priority (ASC)' => 'УпорÑдочить колонку по приоритету (ASC)', + 'Reorder this column by priority (DESC)' => 'УпорÑдочить колонку по приоритету (DESC)', + 'Reorder this column by assignee and priority (ASC)' => 'УпорÑдочить колонку по назначенному пользователю и приоритету (ASC)', + 'Reorder this column by assignee and priority (DESC)' => 'УпорÑдочить колонку по назначенному пользователю и приоритету (DESC)', + 'Reorder this column by assignee (A-Z)' => 'УпорÑдочить колонку по назначенному пользователю (Ð-Я)', + 'Reorder this column by assignee (Z-A)' => 'УпорÑдочить колонку по назначенному пользователю (Я-Ð)', + 'Reorder this column by due date (ASC)' => 'УпорÑдочить колонку по дате Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ (ASC)', + 'Reorder this column by due date (DESC)' => 'УпорÑдочить колонку по дате Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ (DESC)', + 'Reorder this column by id (ASC)' => 'УпорÑдочить колонку по ID (ASC)', + 'Reorder this column by id (DESC)' => 'УпорÑдочить колонку по ID (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s перемеÑтил задачу #%d "%s" в проект "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Задача #%d "%s" перемещена в проект "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'ПеремеÑтить задачу в другую колонку, еÑли дата Ð¾ÐºÐ¾Ð½Ñ‡Ð°Ð½Ð¸Ñ Ð¼ÐµÐ½ÑŒÑˆÐµ заданного количеÑтва дней', + 'Automatically update the start date when the task is moved away from a specific column' => 'ÐвтоматичеÑки обновить дату начала, еÑли задача перемещена из указанной колонки', + 'HTTP Client:' => 'HTTP Клиент:', + 'Assigned' => 'Ðазначенные', + 'Task limits apply to each swimlane individually' => 'Лимиты задач применÑÑŽÑ‚ÑÑ Ðº каждой дорожке отдельно', + 'Column task limits apply to each swimlane individually' => 'Лимиты задач в колонках применÑÑŽÑ‚ÑÑ Ðº каждой дорожке отдельно', + 'Column task limits are applied to each swimlane individually' => 'Лимиты задач в колонках применены к каждой дорожке отдельно', + 'Column task limits are applied across swimlanes' => 'Лимиты задач применены по вÑем дорожкам', + 'Task limit: ' => 'Лимит задач:', + 'Change to global tag' => 'Сменить на глобальную метку', + 'Do you really want to make the tag "%s" global?' => 'Ð’Ñ‹ дейÑтвительно хотите Ñделать метку "%s" глобальной?', + 'Enable global tags for this project' => 'Разрешить глобальные метки в Ñтом проекте', + 'Group membership(s):' => 'СоÑтоит в группах:', + '%s is a member of the following group(s): %s' => '%s ÑвлÑетÑÑ Ñ‡Ð»ÐµÐ½Ð¾Ð¼ Ñледующих групп: %s', + '%d/%d group(s) shown' => '%d/%d групп показано', + 'Subtask creation or modification' => 'Создание или изменение подзадач', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Ðазначить задачу определённому пользователю, еÑли задача перемещена в указанную дорожку', + 'Comment' => 'Комментарий', + 'Collapse vertically' => 'Свернуть вертикально', + 'Expand vertically' => 'Развернуть по вертикали', + 'MXN - Mexican Peso' => 'MXN - МекÑиканÑкое пеÑо', + 'Estimated vs actual time per column' => 'Оценочное и фактичеÑкое Ð²Ñ€ÐµÐ¼Ñ Ð¿Ð¾ колонке', + 'HUF - Hungarian Forint' => 'HUF - ВенгерÑкий форинт', + 'XBT - Bitcoin' => 'XBT - Биткоин', + 'You must select a file to upload as your avatar!' => 'Ð’Ñ‹ должны выбрать файл Ð´Ð»Ñ Ð·Ð°Ð³Ñ€ÑƒÐ·ÐºÐ¸ в качеÑтве аватара!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Загруженный вами файл не ÑвлÑетÑÑ Ð´Ð¾Ð¿ÑƒÑтимым изображением! (Разрешены только *.gif, *.jpg, *.jpeg и *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'ÐвтоматичеÑки уÑтанавливать Ñрок, когда задача перемещаетÑÑ Ð¸Ð· определенной колонки', + 'No other projects found.' => 'Другие проекты не найдены.', + 'Tasks copied successfully.' => 'Задачи уÑпешно Ñкопированы.', + 'Unable to copy tasks.' => 'Ðе удалоÑÑŒ Ñкопировать задачи.', + 'Theme' => 'Тема', + 'Theme:' => 'Тема:', + 'Light theme' => 'Ð¡Ð²ÐµÑ‚Ð»Ð°Ñ Ñ‚ÐµÐ¼Ð°', + 'Dark theme' => 'Ð¢ÐµÐ¼Ð½Ð°Ñ Ñ‚ÐµÐ¼Ð°', + 'Automatic theme - Sync with system' => 'ÐвтоматичеÑÐºÐ°Ñ Ñ‚ÐµÐ¼Ð° — Ñинхронизировать Ñ ÑиÑтемой', + 'Application managers or more' => 'Менеджеры Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð¸Ð»Ð¸ выше', + 'Administrators' => 'ÐдминиÑтраторы', + 'Visibility:' => 'ВидимоÑть:', + 'Standard users' => 'Обычные пользователи', + 'Visibility is required' => 'ТребуетÑÑ Ð²Ð¸Ð´Ð¸Ð¼Ð¾Ñть', + 'The visibility should be an app role' => 'ВидимоÑть должна быть ролью приложениÑ', + 'Reply' => 'Ответить', + '%s wrote: ' => '%s напиÑал: ', + 'Number of visible tasks in this column and swimlane' => 'КоличеÑтво видимых задач в Ñтой колонке и дорожке', + 'Number of tasks in this swimlane' => 'КоличеÑтво задач в Ñтой дорожке', + 'Unable to find another subtask in progress, you can close this window.' => 'Ðе удалоÑÑŒ найти другую подзадачу в процеÑÑе, вы можете закрыть Ñто окно.', + 'This theme is invalid' => 'Эта тема недейÑтвительна', + 'This role is invalid' => 'Эта роль недейÑтвительна', + 'This timezone is invalid' => 'Этот чаÑовой поÑÑ Ð½ÐµÐ´ÐµÐ¹Ñтвителен', + 'This language is invalid' => 'Этот Ñзык недейÑтвителен', + 'This URL is invalid' => 'Этот URL недейÑтвителен', + 'Date format invalid' => 'Ðеверный формат даты', + 'Time format invalid' => 'Ðеверный формат времени', + 'Invalid Mail transport' => 'ÐедопуÑтимый почтовый транÑпорт', + 'Color invalid' => 'ÐедопуÑтимый цвет', + 'This value must be greater or equal to %d' => 'Это значение должно быть больше или равно %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Добавьте BOM в начало файла (требуетÑÑ Ð´Ð»Ñ Microsoft Excel)', + 'Just add these tag(s)' => 'Добавьте только Ñти теги', + 'Remove internal link(s)' => 'Удалить внутренние ÑÑылки', + 'Import tasks from another project' => 'Импортировать задачи из другого проекта', + 'Select the project to copy tasks from' => 'Выберите проект, из которого нужно Ñкопировать задачи', + 'The total maximum allowed attachments size is %sB.' => 'Общий макÑимально допуÑтимый размер вложений — %sB.', + 'Add attachments' => 'Добавить вложениÑ', + 'Task #%d "%s" is overdue' => 'Задача #%d "%s" проÑрочена', + 'Enable notifications by default for all new users' => 'Включить ÑƒÐ²ÐµÐ´Ð¾Ð¼Ð»ÐµÐ½Ð¸Ñ Ð¿Ð¾ умолчанию Ð´Ð»Ñ Ð²Ñех новых пользователей', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Ðазначать задачу её Ñоздателю Ð´Ð»Ñ Ð¾Ð¿Ñ€ÐµÐ´ÐµÐ»Ñ‘Ð½Ð½Ñ‹Ñ… колонок, еÑли иÑполнитель не задан вручную', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Ðазначать задачу текущему пользователю при перемещении в указанную колонку, еÑли никто не назначен', +]; diff --git a/app/Locale/sk_SK/translations.php b/app/Locale/sk_SK/translations.php new file mode 100644 index 0000000..fd183e6 --- /dev/null +++ b/app/Locale/sk_SK/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Žiadne', + 'Edit' => 'UpraviÅ¥', + 'Remove' => 'OdstrániÅ¥', + 'Yes' => 'Ãno', + 'No' => 'Nie', + 'cancel' => 'zruÅ¡iÅ¥', + 'or' => 'alebo', + 'Yellow' => 'Žltá', + 'Blue' => 'Modrá', + 'Green' => 'Zelená', + 'Purple' => 'Fialová', + 'Red' => 'ÄŒervená', + 'Orange' => 'Oranžová', + 'Grey' => 'Sivá', + 'Brown' => 'Hnedá', + 'Deep Orange' => 'Sýto oranžová', + 'Dark Grey' => 'Tmavo sivá', + 'Pink' => 'Ružová', + 'Teal' => 'Modro zelená', + 'Cyan' => 'Azúrová', + 'Lime' => 'Limetková', + 'Light Green' => 'Svetlo zelená', + 'Amber' => 'Jantárová', + 'Save' => 'UložiÅ¥', + 'Login' => 'PrihlásiÅ¥', + 'Official website:' => 'Oficiálna stránka:', + 'Unassigned' => 'Nepridelené', + 'View this task' => 'ZobraziÅ¥ túto úlohu', + 'Remove user' => 'OdstrániÅ¥ používateľa', + 'Do you really want to remove this user: "%s"?' => 'Naozaj chcete odstrániÅ¥ používateľa: „%sâ€?', + 'All users' => 'VÅ¡etci používatelia', + 'Username' => 'Prihlasovacie meno', + 'Password' => 'Heslo', + 'Administrator' => 'Administrátor', + 'Sign in' => 'PrihlásiÅ¥ sa', + 'Users' => 'Používatelia', + 'Forbidden' => 'Zakázané', + 'Access Forbidden' => 'Prístup zakázaný', + 'Edit user' => 'UpraviÅ¥ používateľa', + 'Logout' => 'OdhlásiÅ¥', + 'Bad username or password' => 'Zlé používateľské meno alebo heslo', + 'Edit project' => 'UpraviÅ¥ projekt', + 'Name' => 'Meno', + 'Projects' => 'Projekty', + 'No project' => 'Žiadne projekty', + 'Project' => 'Projekt', + 'Status' => 'Stav', + 'Tasks' => 'Úlohy', + 'Board' => 'Nástenka', + 'Actions' => 'Akcie', + 'Inactive' => 'Neaktívne', + 'Active' => 'Aktívne', + 'Unable to update this board.' => 'Nemožno aktualizovaÅ¥ nástenku.', + 'Disable' => 'ZakázaÅ¥', + 'Enable' => 'Zapnúť', + 'New project' => 'Nový projekt', + 'Do you really want to remove this project: "%s"?' => 'Naozaj chcete odstrániÅ¥ projekt: „%sâ€?', + 'Remove project' => 'OdstrániÅ¥ projekt', + 'Edit the board for "%s"' => 'UpraviÅ¥ nástenku „%sâ€', + 'Add a new column' => 'PridaÅ¥ nový stĺpec', + 'Title' => 'Názov', + 'Assigned to %s' => 'Pridelené používateľovi %s', + 'Remove a column' => 'OdstrániÅ¥ stĺpec', + 'Unable to remove this column.' => 'Nemožno odstrániÅ¥ tento stĺpec.', + 'Do you really want to remove this column: "%s"?' => 'Naozaj chcete odstrániÅ¥ stĺpec „%sâ€?', + 'Settings' => 'Nastavenia', + 'Application settings' => 'Nastavenia aplikácie', + 'Language' => 'Jazyk', + 'Webhook token:' => 'Token webového háku:', + 'API token:' => 'Token API:', + 'Database size:' => 'VeľkosÅ¥ databázy:', + 'Download the database' => 'StiahnuÅ¥ databázu', + 'Optimize the database' => 'OptimalizovaÅ¥ databázu', + '(VACUUM command)' => '(príkaz VACUUM)', + '(Gzip compressed Sqlite file)' => 'Súbor SQLite, komprimovaný gzip)', + 'Close a task' => 'UkonÄiÅ¥ úlohu', + 'Column' => 'Stĺpec', + 'Color' => 'Farba', + 'Assignee' => 'Pridelené', + 'Create another task' => 'VytvoriÅ¥ ÄalÅ¡iu úlohu', + 'New task' => 'Nová úloha', + 'Open a task' => 'OtvoriÅ¥ úlohu', + 'Do you really want to open this task: "%s"?' => 'Naozaj chcete otvoriÅ¥ úlohu: „%sâ€?', + 'Back to the board' => 'Späť na nástenku', + 'There is nobody assigned' => 'Nie je pridelené nikomu', + 'Column on the board:' => 'Stĺpec nástenky:', + 'Close this task' => 'UkonÄiÅ¥ túto úlohu', + 'Open this task' => 'OtvoriÅ¥ túto úlohu', + 'There is no description.' => 'Nemá popis.', + 'Add a new task' => 'PridaÅ¥ novú úlohu', + 'The username is required' => 'Prihl. meno je povinné', + 'The maximum length is %d characters' => 'Maximálna dĺžka je %d znakov', + 'The minimum length is %d characters' => 'Minimálna dĺžka je %d znakov', + 'The password is required' => 'Heslo je povinné', + 'This value must be an integer' => 'Táto hodnota musí byÅ¥ celé Äíslo', + 'The username must be unique' => 'Prihl. meno musí byÅ¥ jedineÄné', + 'The user id is required' => 'ID používateľa je povinné', + 'Passwords don\'t match' => 'Heslá sa nezhodujú', + 'The confirmation is required' => 'Potvrdenie je povinné', + 'The project is required' => 'Projekt je povinný', + 'The id is required' => 'ID je povinné', + 'The project id is required' => 'ID projektu je povinné', + 'The project name is required' => 'Meno projektu je povinné', + 'The title is required' => 'Názov je povinný', + 'Settings saved successfully.' => 'Nastavenia úspeÅ¡ne uložené.', + 'Unable to save your settings.' => 'Nemožno uložiÅ¥ nastavenia.', + 'Database optimization done.' => 'Optimalizácia databázy dokonÄená.', + 'Your project has been created successfully.' => 'Projekt úspeÅ¡ne vytvorený.', + 'Unable to create your project.' => 'Nemožno vytvoriÅ¥ projekt.', + 'Project updated successfully.' => 'Projekt úspeÅ¡ne aktualizovaný.', + 'Unable to update this project.' => 'Nemožno aktualizovaÅ¥ projekt.', + 'Unable to remove this project.' => 'Nemožno odstrániÅ¥ projekt.', + 'Project removed successfully.' => 'Projekt úspeÅ¡ne odstránený.', + 'Project activated successfully.' => 'Projekt úspeÅ¡ne aktivovaný.', + 'Unable to activate this project.' => 'Nemožno aktivovaÅ¥ projekt.', + 'Project disabled successfully.' => 'Projekt úspeÅ¡ne vypnutý.', + 'Unable to disable this project.' => 'Nemožno vypnúť projekt.', + 'Unable to open this task.' => 'Nemožno otvoriÅ¥ úlohu.', + 'Task opened successfully.' => 'Úloha úspeÅ¡ne otvorená.', + 'Unable to close this task.' => 'Nemožno ukonÄiÅ¥ túto úlohu.', + 'Task closed successfully.' => 'Úloha úspeÅ¡ne ukonÄená.', + 'Unable to update your task.' => 'Nemožno aktualizovaÅ¥ úlohu.', + 'Task updated successfully.' => 'Úloha úspeÅ¡ne aktualizovaná.', + 'Unable to create your task.' => 'Nemožno vytvoriÅ¥ úlohu.', + 'Task created successfully.' => 'Úloha úspeÅ¡ne vytvorená.', + 'User created successfully.' => 'Používateľ úspeÅ¡ne vytvorený.', + 'Unable to create your user.' => 'Nemožno vytvoriÅ¥ používateľa.', + 'User updated successfully.' => 'Používateľ úspeÅ¡ne aktualizovaný.', + 'User removed successfully.' => 'Používateľ úspeÅ¡ne odstránený.', + 'Unable to remove this user.' => 'Nemožno odstrániÅ¥ používateľa.', + 'Board updated successfully.' => 'Nástenka úspeÅ¡ne aktualizovaná.', + 'Ready' => 'Pripravené', + 'Backlog' => 'Nevybavené', + 'Work in progress' => 'V rieÅ¡ení', + 'Done' => 'DokonÄené', + 'Application version:' => 'Verzia aplikácie:', + 'Id' => 'ID', + 'Public link' => 'Verejný odkaz', + 'Timezone' => 'ÄŒasové pásmo', + 'Sorry, I didn\'t find this information in my database!' => 'PrepáÄte, túto informáciu som v databáze nenaÅ¡iel!', + 'Page not found' => 'Stránka neexistuje', + 'Complexity' => 'ZložitosÅ¥', + 'Task limit' => 'Limit úloh', + 'Task count' => 'PoÄet úloh', + 'User' => 'Používateľ', + 'Comments' => 'Komentáre', + 'Comment is required' => 'Komentár je povinný', + 'Comment added successfully.' => 'Komentár úspeÅ¡ne pridaný.', + 'Unable to create your comment.' => 'Nemožno vytvoriÅ¥ komentár.', + 'Due Date' => 'Dátum splnenia', + 'Invalid date' => 'Neplatný dátum', + 'Automatic actions' => 'Automatické akcie', + 'Your automatic action has been created successfully.' => 'Automatická akcia úspeÅ¡ne vytvorená.', + 'Unable to create your automatic action.' => 'Nemožno vytvoriÅ¥ automatickú akciu.', + 'Remove an action' => 'OdstrániÅ¥ akciu', + 'Unable to remove this action.' => 'Nemožno odstrániÅ¥ akciu.', + 'Action removed successfully.' => 'Akcia úspeÅ¡ne odstránená.', + 'Automatic actions for the project "%s"' => 'Automatické akcie projektu „%sâ€', + 'Add an action' => 'PridaÅ¥ akciu', + 'Event name' => 'Názov udalosti', + 'Action' => 'Akcia', + 'Event' => 'UdalosÅ¥', + 'When the selected event occurs execute the corresponding action.' => 'KeÄ nastane zvolená udalosÅ¥, vykonaÅ¥ prísluÅ¡nú akciu.', + 'Next step' => 'ÄŽalší krok', + 'Define action parameters' => 'Definujte parametre akcie', + 'Do you really want to remove this action: "%s"?' => 'Naozaj chcete odstrániÅ¥ akciu: „%sâ€?', + 'Remove an automatic action' => 'OdstrániÅ¥ automatickú akciu', + 'Assign the task to a specific user' => 'PrideliÅ¥ úlohu zadanému používateľovi', + 'Assign the task to the person who does the action' => 'PrideliÅ¥ úlohu osobe, ktorá vykonala akciu', + 'Duplicate the task to another project' => 'DuplikovaÅ¥ akciu do iného projektu', + 'Move a task to another column' => 'Presun úlohy do iného stĺpca', + 'Task modification' => 'Úprava úlohy', + 'Task creation' => 'Vytvorenie úlohy', + 'Closing a task' => 'UkonÄenie úlohy', + 'Assign a color to a specific user' => 'PriradiÅ¥ farbu zadanému používateľovi', + 'Position' => 'Pozícia', + 'Duplicate to project' => 'DuplikovaÅ¥ do iného projektu', + 'Duplicate' => 'DuplikovaÅ¥', + 'Link' => 'Odkaz', + 'Comment updated successfully.' => 'Komentár úspeÅ¡ne aktualizovaný.', + 'Unable to update your comment.' => 'Nemožno aktualizovaÅ¥ komentár.', + 'Remove a comment' => 'OdstrániÅ¥ komentár', + 'Comment removed successfully.' => 'Komentár úspeÅ¡ne odstránený.', + 'Unable to remove this comment.' => 'Nemožno odstrániÅ¥ tento komentár.', + 'Do you really want to remove this comment?' => 'Naozaj chcete odstrániÅ¥ tento komentár?', + 'Current password for the user "%s"' => 'Aktuálne heslo používateľa „%sâ€', + 'The current password is required' => 'Aktuálne heslo je povinné', + 'Wrong password' => 'Zlé heslo', + 'Unknown' => 'Neznáme', + 'Last logins' => 'Posledné prihlásenia', + 'Login date' => 'Dátum prihlásenia', + 'Authentication method' => 'Metóda autentifikácie', + 'IP address' => 'Adresa IP', + 'User agent' => 'User-Agent', + 'Persistent connections' => 'Trvalé spojenia', + 'No session.' => 'Bez relácie.', + 'Expiration date' => 'Dátum platnosti', + 'Remember Me' => 'ZapamätaÅ¥ si ma', + 'Creation date' => 'Dátum vytvorenia', + 'Everybody' => 'Každý', + 'Open' => 'Otvorené', + 'Closed' => 'UkonÄené', + 'Search' => 'HľadaÅ¥', + 'Nothing found.' => 'NiÄ nenájdené.', + 'Due date' => 'Termín splnenia', + 'Description' => 'Popis', + '%d comments' => '%d komentáre(ov)', + '%d comment' => '%d komentár', + 'Email address invalid' => 'Neplatná emailová adresa', + 'Your external account is not linked anymore to your profile.' => 'Externý úÄet už nie je prepojený s VaÅ¡im profilom.', + 'Unable to unlink your external account.' => 'Nemožno zruÅ¡iÅ¥ prepojenie s externým úÄtom.', + 'External authentication failed' => 'Externá autentifikácia zlyhala', + 'Your external account is linked to your profile successfully.' => 'Externý úÄet úspeÅ¡ne prepojený s profilom.', + 'Email' => 'Email', + 'Task removed successfully.' => 'Úloha úspeÅ¡ne odstránená.', + 'Unable to remove this task.' => 'Nemožno odstrániÅ¥ úlohu.', + 'Remove a task' => 'OdstrániÅ¥ úlohu', + 'Do you really want to remove this task: "%s"?' => 'Naozaj chcete odstrániÅ¥ úlohu: „%sâ€?', + 'Assign automatically a color based on a category' => 'Automaticky nastaviÅ¥ farbu na základe kategórie', + 'Assign automatically a category based on a color' => 'Automaticky nastaviÅ¥ kategóriu na základe farby', + 'Task creation or modification' => 'Vytvorenie alebo úprava úlohy', + 'Category' => 'Kategória', + 'Category:' => 'Kategória:', + 'Categories' => 'Kategórie', + 'Your category has been created successfully.' => 'Kategória úspeÅ¡ne vytvorená.', + 'This category has been updated successfully.' => 'Kategória úspeÅ¡ne aktualizovaná.', + 'Unable to update this category.' => 'Nemožno aktualizovaÅ¥ kategóriu.', + 'Remove a category' => 'OdstrániÅ¥ kategóriu', + 'Category removed successfully.' => 'Kategória úspeÅ¡ne odstránená.', + 'Unable to remove this category.' => 'Nemožno odstrániÅ¥ kategóriu.', + 'Category modification for the project "%s"' => 'Úprava kategórie projektu „%sâ€', + 'Category Name' => 'Meno kategórie', + 'Add a new category' => 'PridaÅ¥ novú kategóriu', + 'Do you really want to remove this category: "%s"?' => 'Naozaj chcete odstrániÅ¥ kategóriu: „%sâ€?', + 'All categories' => 'VÅ¡etky kategórie', + 'No category' => 'Bez kategórie', + 'The name is required' => 'Meno je povinné', + 'Remove a file' => 'OdstrániÅ¥ súbor', + 'Unable to remove this file.' => 'Nemožno odstrániÅ¥ súbor.', + 'File removed successfully.' => 'Súbor úspeÅ¡ne odstránený.', + 'Attach a document' => 'PripojiÅ¥ dokument', + 'Do you really want to remove this file: "%s"?' => 'Naozaj chcete odstrániÅ¥ súbor: „%sâ€?', + 'Attachments' => 'Prílohy', + 'Edit the task' => 'UpraviÅ¥ úlohu', + 'Add a comment' => 'PridaÅ¥ komentár', + 'Edit a comment' => 'UpraviÅ¥ komentár', + 'Summary' => 'Zhrnutie', + 'Time tracking' => 'Sledovanie Äasu', + 'Estimate:' => 'OÄakávané:', + 'Spent:' => 'Strávené:', + 'Do you really want to remove this sub-task?' => 'Naozaj chcete odstrániÅ¥ túto podúlohu?', + 'Remaining:' => 'Ostáva:', + 'hours' => 'hodiny', + 'estimated' => 'oÄakávané', + 'Sub-Tasks' => 'Podúlohy', + 'Add a sub-task' => 'PridaÅ¥ podúlohu', + 'Original estimate' => 'Predpokladaný Äas', + 'Create another sub-task' => 'VytvoriÅ¥ ÄalÅ¡iu podúlohu', + 'Time spent' => 'Strávený Äas', + 'Edit a sub-task' => 'UpraviÅ¥ podúlohu', + 'Remove a sub-task' => 'OdstrániÅ¥ podúlohu', + 'The time must be a numeric value' => 'ÄŒas musí byÅ¥ Äíselná hodnota', + 'Todo' => 'ÄŒaká', + 'In progress' => 'V rieÅ¡ení', + 'Sub-task removed successfully.' => 'Podúloha úspeÅ¡ne odstránená.', + 'Unable to remove this sub-task.' => 'Nemožno odstrániÅ¥ podúlohu.', + 'Sub-task updated successfully.' => 'Podúloha úspeÅ¡ne aktualizovaná.', + 'Unable to update your sub-task.' => 'Nemožno aktualizovaÅ¥ podúlohu.', + 'Unable to create your sub-task.' => 'Nemožno vytvoriÅ¥ podúlohu.', + 'Maximum size: ' => 'Maximálna veľkosÅ¥: ', + 'Display another project' => 'ZobraziÅ¥ iný projekt', + 'Created by %s' => 'Vytvoril %s', + 'Tasks Export' => 'Export úloh', + 'Start Date' => 'Dátum zaÄiatku', + 'Execute' => 'VykonaÅ¥', + 'Task Id' => 'ID úlohy', + 'Creator' => 'Vytvoril', + 'Modification date' => 'Dátum úpravy', + 'Completion date' => 'Dátum ukonÄenia', + 'Clone' => 'NaklonovaÅ¥', + 'Project cloned successfully.' => 'Projekt úspeÅ¡ne naklonovaný.', + 'Unable to clone this project.' => 'Nemožno naklonovaÅ¥ tento projekt.', + 'Enable email notifications' => 'Zapnúť upozornenia emailom', + 'Task position:' => 'Pozícia úlohy:', + 'The task #%d has been opened.' => 'Úloha #%d bola otvorená.', + 'The task #%d has been closed.' => 'Úloha #%d bola ukonÄená.', + 'Sub-task updated' => 'Podúloha aktualizovaná', + 'Title:' => 'Názov:', + 'Status:' => 'Stav:', + 'Assignee:' => 'Pridelené:', + 'Time tracking:' => 'Sledovanie Äasu:', + 'New sub-task' => 'Nová podúloha', + 'New attachment added "%s"' => 'Nová príloha pridaná %s', + 'New comment posted by %s' => 'Nový komentár poslaný %s', + 'New comment' => 'Nový komentár', + 'Comment updated' => 'Komentár aktualizovaný', + 'New subtask' => 'Nová podúloha', + 'I only want to receive notifications for these projects:' => 'Chcem dostávaÅ¥ upozornenia len z týchto projektov:', + 'view the task on Kanboard' => 'zobraziÅ¥ úlohu v Kanboard', + 'Public access' => 'Verejný prístup', + 'Disable public access' => 'ZakázaÅ¥ verejný prístup', + 'Enable public access' => 'Zapnúť verejný prístup', + 'Public access disabled' => 'Verejný prístup zakázaný', + 'Move the task to another project' => 'Presunúť úlohu do iného projektu', + 'Move to project' => 'Presunúť do projektu', + 'Do you really want to duplicate this task?' => 'Naozaj chcete duplikovaÅ¥ túto úlohu?', + 'Duplicate a task' => 'DuplikovaÅ¥ úlohu', + 'External accounts' => 'Externé úÄty', + 'Account type' => 'Typ úÄtu', + 'Local' => 'Lokálny', + 'Remote' => 'Vzdialený', + 'Enabled' => 'Vypnuté', + 'Disabled' => 'Zapnuté', + 'Login:' => 'Prihl. meno:', + 'Full Name:' => 'Celé meno:', + 'Email:' => 'Email:', + 'Notifications:' => 'Upozornenia:', + 'Notifications' => 'Upozornenia', + 'Account type:' => 'Typ úÄtu:', + 'Edit profile' => 'UpraviÅ¥ profil', + 'Change password' => 'ZmeniÅ¥ heslo', + 'Password modification' => 'Úprava hesla', + 'External authentications' => 'Externé autentifikácie', + 'Never connected.' => 'Nikdy neprihlásený.', + 'No external authentication enabled.' => 'Žiadne povolené externé autentifikácie.', + 'Password modified successfully.' => 'Heslo úspeÅ¡ne zmenené.', + 'Unable to change the password.' => 'Nemožno zmeniÅ¥ heslo.', + 'Change category' => 'ZmeniÅ¥ kategóriu', + '%s updated the task %s' => '%s upravil úlohu %s', + '%s opened the task %s' => '%s otvoril úlohu %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s presunul úlohu %s na pozíciu #%d v stĺpci „%sâ€', + '%s moved the task %s to the column "%s"' => '%s presunul úlohu %s do stĺpca „%sâ€', + '%s created the task %s' => '%s vytvoril úlohu %s', + '%s closed the task %s' => '%s ukonÄil úlohu %s', + '%s created a subtask for the task %s' => '%s vytvoril podúlohu úlohy %s', + '%s updated a subtask for the task %s' => '%s upravil podúlohu úlohy %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Pridelené %s s oÄakávaním %s/%s h', + 'Not assigned, estimate of %sh' => 'Nepridelené, oÄakávané %s h', + '%s updated a comment on the task %s' => '%s aktualizoval komentár úlohy %s', + '%s commented the task %s' => '%s komentoval úlohu %s', + '%s\'s activity' => 'Aktivita používateľa %s', + 'RSS feed' => 'Kanál RSS', + '%s updated a comment on the task #%d' => '%s upravil komentár úlohy #%d', + '%s commented on the task #%d' => '%s pridal komentár úlohy #%d', + '%s updated a subtask for the task #%d' => '%s upravil podúlohu úlohy #%d', + '%s created a subtask for the task #%d' => '%s vytvoril podúlohu úlohy #%d', + '%s updated the task #%d' => '%s upravil úlohu #%d', + '%s created the task #%d' => '%s vytvoril úlohu #%d', + '%s closed the task #%d' => '%s ukonÄil úlohu #%d', + '%s opened the task #%d' => '%s otvoril úlohu #%d', + 'Activity' => 'Aktivita', + 'Default values are "%s"' => 'Predvolené hodnoty sú „%sâ€', + 'Default columns for new projects (Comma-separated)' => 'Predvolené stĺpce nových projektov (oddelené Äiarkami)', + 'Task assignee change' => 'Zmena pridelenia úlohy', + '%s changed the assignee of the task #%d to %s' => '%s zmenil pridelenie úlohy #%d na %s', + '%s changed the assignee of the task %s to %s' => '%s zmenil pridelenie úlohy %s na %s', + 'New password for the user "%s"' => 'Nové heslo používateľa „%sâ€', + 'Choose an event' => 'Zvoľte udalosÅ¥', + 'Create a task from an external provider' => 'VytvoriÅ¥ úlohu od externého poskytovateľa', + 'Change the assignee based on an external username' => 'ZmeniÅ¥ pridelenie na základe externého používateľa', + 'Change the category based on an external label' => 'ZmeniÅ¥ kategóriu na základe externej menovky', + 'Reference' => 'Odkaz', + 'Label' => 'Menovka', + 'Database' => 'Databázy', + 'About' => 'O aplikácii', + 'Database driver:' => 'OvládaÄ databázy:', + 'Board settings' => 'Nastavenia nástenky', + 'Webhook settings' => 'Nastavenia webových hákov', + 'Reset token' => 'ObnoviÅ¥ token', + 'API endpoint:' => 'Prípojný bod API:', + 'Refresh interval for personal board' => 'Interval obnovenia súkromných násteniek', + 'Refresh interval for public board' => 'Interval obnovenia verejných násteniek', + 'Task highlight period' => 'Doba zvýraznenia úlohy', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Doba (v sekundách), poÄas ktorej považovaÅ¥ úlohu za nedávno upravenú (0 na vypnutie, predvolene 2 dni)', + 'Frequency in second (60 seconds by default)' => 'Frekvencia v sekundách (predvolene 60 sekúnd)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvencia v sekundách (predvolene 10 sekúnd)', + 'Application URL' => 'URL aplikácie', + 'Token regenerated.' => 'Token obnovený.', + 'Date format' => 'Formát dátumu', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Formát ISO je vždy prijímaný, napr.: „%s†a %sâ€', + 'New personal project' => 'Nový súkromný projekt', + 'This project is personal' => 'Tento projekt je súkromný', + 'Add' => 'PridaÅ¥', + 'Start date' => 'Dátum zaÄiatku', + 'Time estimated' => 'OÄakávaný Äas', + 'There is nothing assigned to you.' => 'Nemáte niÄ pridelené.', + 'My tasks' => 'Moje úlohy', + 'Activity stream' => 'Prehľad aktivity', + 'Dashboard' => 'Nástenka', + 'Confirmation' => 'Potvrdenie', + 'Webhooks' => 'Webové háky', + 'API' => 'API', + 'Create a comment from an external provider' => 'VytvoriÅ¥ komentár od externého poskytovateľa', + 'Project management' => 'Správa projektov', + 'Columns' => 'Stĺpce', + 'Task' => 'Úloha', + 'Percentage' => 'Percentá', + 'Number of tasks' => 'PoÄet úloh', + 'Task distribution' => 'Distribúcia úloh', + 'Analytics' => 'Analytika', + 'Subtask' => 'Podúloha', + 'User repartition' => 'Rozdelenie používateľov', + 'Clone this project' => 'NaklonovaÅ¥ tento projekt', + 'Column removed successfully.' => 'Stĺpec úspeÅ¡ne odstránený.', + 'Not enough data to show the graph.' => 'Nedostatok dát na zobrazenie grafu.', + 'Previous' => 'PredoÅ¡lé', + 'The id must be an integer' => 'ID musí byÅ¥ celé Äíslo', + 'The project id must be an integer' => 'ID projektu musí byÅ¥ celé Äíslo', + 'The status must be an integer' => 'Stav musí byÅ¥ celé Äíslo', + 'The subtask id is required' => 'ID podúlohy je povinné', + 'The subtask id must be an integer' => 'ID podúlohy musí byÅ¥ celé Äíslo', + 'The task id is required' => 'ID úlohy je povinné', + 'The task id must be an integer' => 'ID úlohy musí byÅ¥ celé Äíslo', + 'The user id must be an integer' => 'ID používateľa musí byÅ¥ celé Äíslo', + 'This value is required' => 'Táto hodnota je povinná', + 'This value must be numeric' => 'Táto hodnota musí byÅ¥ Äíselná', + 'Unable to create this task.' => 'Nemožno vytvoriÅ¥ túto úlohu.', + 'Cumulative flow diagram' => 'Kumulatívny diagram toku', + 'Daily project summary' => 'Senný sumár projektu', + 'Daily project summary export' => 'Export denného sumáru projektu', + 'Exports' => 'Exporty', + 'This export contains the number of tasks per column grouped per day.' => 'Tento export obsahuje poÄet úloh na stĺpec, zoskupené pod dňoch.', + 'Active swimlanes' => 'Aktívne dráhy', + 'Add a new swimlane' => 'PridaÅ¥ novú dráhu', + 'Default swimlane' => 'Hlavná dráha', + 'Do you really want to remove this swimlane: "%s"?' => 'Naozaj chcete odstrániÅ¥ dráhu: „%sâ€?', + 'Inactive swimlanes' => 'Neaktívne dráhy', + 'Remove a swimlane' => 'OdstrániÅ¥ dráhu', + 'Swimlane modification for the project "%s"' => 'Úpravy dráhy projektu „%sâ€', + 'Swimlane removed successfully.' => 'Dráha úspeÅ¡ne odstránená.', + 'Swimlanes' => 'Dráhy', + 'Swimlane updated successfully.' => 'Dráha úspeÅ¡ne upravená.', + 'Unable to remove this swimlane.' => 'Nemožno odstrániÅ¥ túto dráhu.', + 'Unable to update this swimlane.' => 'Nemožno upraviÅ¥ túto dráhu.', + 'Your swimlane has been created successfully.' => 'Dráha úspeÅ¡ne vytvorená.', + 'Example: "Bug, Feature Request, Improvement"' => 'Príklad: "Chyba, Nová vlastnosÅ¥, VylepÅ¡enie"', + 'Default categories for new projects (Comma-separated)' => 'Predvolené kategórie nových projektov (oddelené Äiarkami)', + 'Integrations' => 'Integrácie', + 'Integration with third-party services' => 'Integrácia so službami tretích strán', + 'Subtask Id' => 'ID podúlohy', + 'Subtasks' => 'Podúlohy', + 'Subtasks Export' => 'Export podúloh', + 'Task Title' => 'Názov úlohy', + 'Untitled' => 'Bez názvu', + 'Application default' => 'Predvolené', + 'Language:' => 'Jazyk:', + 'Timezone:' => 'ÄŒasové pásmo:', + 'All columns' => 'VÅ¡etky stĺpce', + 'Next' => 'ÄŽalÅ¡ie', + '#%d' => '# %d', + 'All swimlanes' => 'VÅ¡etky dráhy', + 'All colors' => 'VÅ¡etky farby', + 'Moved to column %s' => 'Presunuté do stĺpca %s', + 'User dashboard' => 'Nástenka používateľa', + 'Allow only one subtask in progress at the same time for a user' => 'PovoliÅ¥ len jednu spracovávanú podúlohu v rovnakom Äase na používateľa', + 'Edit column "%s"' => 'UpraviÅ¥ stĺpec „%sâ€', + 'Select the new status of the subtask: "%s"' => 'Vyberte nový stav podúlohy: „%sâ€', + 'Subtask timesheet' => 'Výkaz podúloh', + 'There is nothing to show.' => 'NiÄ na zobrazenie.', + 'Time Tracking' => 'Sledovanie Äasu', + 'You already have one subtask in progress' => 'Už máte jednu podúlohu v rieÅ¡ení', + 'Which parts of the project do you want to duplicate?' => 'Ktoré Äasti projektu chcete duplikovaÅ¥?', + 'Disallow login form' => 'ZakázaÅ¥ prihlasovací formulár', + 'Start' => 'ZaÄiatok', + 'End' => 'Koniec', + 'Task age in days' => 'Vek úlohy v dňoch', + 'Days in this column' => 'Dní v tomto stĺpci', + '%dd' => '%d d', + 'Add a new link' => 'PridaÅ¥ nový odkaz', + 'Do you really want to remove this link: "%s"?' => 'Naozaj chcete odstrániÅ¥ odkaz: „%sâ€?', + 'Do you really want to remove this link with task #%d?' => 'Naozaj chcete odstrániÅ¥ odkaz s úlohou #%d?', + 'Field required' => 'Pole je povinné', + 'Link added successfully.' => 'Odkaz úspeÅ¡ne pridaný.', + 'Link updated successfully.' => 'Odkaz úspeÅ¡ne upravený.', + 'Link removed successfully.' => 'Odkaz úspeÅ¡ne odstránený.', + 'Link labels' => 'Menovky odkazov', + 'Link modification' => 'Úprava odkazu', + 'Opposite label' => 'OpaÄná menovka', + 'Remove a link' => 'OdstrániÅ¥ odkaz', + 'The labels must be different' => 'Menovky sa musia líšiÅ¥', + 'There is no link.' => 'Žiadny odkaz.', + 'This label must be unique' => 'Menovka musí byÅ¥ jedineÄná', + 'Unable to create your link.' => 'Nemožno vytvoriÅ¥ odkaz.', + 'Unable to update your link.' => 'Nemožno aktualizovaÅ¥ odkaz.', + 'Unable to remove this link.' => 'Nemožno odstrániÅ¥ odkaz.', + 'relates to' => 'súvisí s', + 'blocks' => 'blokuje', + 'is blocked by' => 'je blokovaná', + 'duplicates' => 'duplikuje', + 'is duplicated by' => 'je duplikovaná', + 'is a child of' => 'je potomkom', + 'is a parent of' => 'je rodiÄom', + 'targets milestone' => 'cieľový míľnik', + 'is a milestone of' => 'je míľnikom', + 'fixes' => 'opravuje', + 'is fixed by' => 'je opravená', + 'This task' => 'Táto úloha', + '<1h' => '<1 h', + '%dh' => '%d h', + 'Expand tasks' => 'RozbaliÅ¥ úlohy', + 'Collapse tasks' => 'ZbaliÅ¥ úlohy', + 'Expand/collapse tasks' => 'RozbaliÅ¥/ZbaliÅ¥ úlohy', + 'Close dialog box' => 'ZatvoriÅ¥ dialógové okno', + 'Submit a form' => 'OdoslaÅ¥ formulár', + 'Board view' => 'Zobrazenie nástenky', + 'Keyboard shortcuts' => 'Klávesové skratky', + 'Open board switcher' => 'PrepínaÄ otvorených násteniek', + 'Application' => 'Aplikácia', + 'Compact view' => 'Kompaktné zobrazenie', + 'Horizontal scrolling' => 'Vodorovný posun', + 'Compact/wide view' => 'Kompaktné/Å¡iroké zobrazenie', + 'Currency' => 'Mena', + 'Personal project' => 'Súkromný projekt', + 'AUD - Australian Dollar' => 'AUD – Austrálsky dolár', + 'CAD - Canadian Dollar' => 'CAD – Kanadský dolár', + 'CHF - Swiss Francs' => 'CHF – Å vajÄiarsky frank', + 'Custom Stylesheet' => 'Vlastné CSS', + 'EUR - Euro' => 'EUR – Euro', + 'GBP - British Pound' => 'GBP – Britská libra', + 'INR - Indian Rupee' => 'INR – Indická rupia', + 'JPY - Japanese Yen' => 'JPY – Japonský jen', + 'NZD - New Zealand Dollar' => 'NZD – Novozélandský dolár', + 'PEN - Peruvian Sol' => 'PEN – peruánsky sol', + 'RSD - Serbian dinar' => 'RSD – Srbský dinár', + 'CNY - Chinese Yuan' => 'CNY – Čínsky juan', + 'USD - US Dollar' => 'USD – Americký dolár', + 'VES - Venezuelan Bolívar' => 'Venezuelský bolívar', + 'Destination column' => 'Cieľový stĺpec', + 'Move the task to another column when assigned to a user' => 'Presunúť úlohu do iného stĺpca, keÄ je pridelená používateľovi', + 'Move the task to another column when assignee is cleared' => 'Presunúť úlohu do iného stĺpca, keÄ je pridelenie vymazané', + 'Source column' => 'Zdrojový stĺpec', + 'Transitions' => 'Prechody', + 'Executer' => 'SpúšťaÄ', + 'Time spent in the column' => 'ÄŒas strávený v stĺpci', + 'Task transitions' => 'Prechody úloh', + 'Task transitions export' => 'Export prechodov úloh', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Tento výstup obsahuje presuny stĺpcov vÅ¡etkých úloh s dátumom, používateľom a Äasom stráveným v každom prechode.', + 'Currency rates' => 'Kurzy meny', + 'Rate' => 'Kurz', + 'Change reference currency' => 'ZmeniÅ¥ referenÄnú menu', + 'Reference currency' => 'ReferenÄná mena', + 'The currency rate has been added successfully.' => 'Kurz meny bol úspeÅ¡ne pridaný.', + 'Unable to add this currency rate.' => 'Nemožno pridaÅ¥ tento kurz meny.', + 'Webhook URL' => 'URL webového háku', + '%s removed the assignee of the task %s' => '%s odstránil pridelenie úlohy %s', + 'Information' => 'Informácie', + 'Check two factor authentication code' => 'OveriÅ¥ dvojfaktorový autentifikaÄný kód', + 'The two factor authentication code is not valid.' => 'Dvojfaktorový autentifikaÄný kód nie je platný.', + 'The two factor authentication code is valid.' => 'Dvojfaktorový autentifikaÄný kód je platný.', + 'Code' => 'Kód', + 'Two factor authentication' => 'Dvojfaktorová autentifikácia', + 'This QR code contains the key URI: ' => 'Tento kód QR obsahuje URI kľúÄa: ', + 'Check my code' => 'SkontrolovaÅ¥ svoj kód', + 'Secret key: ' => 'Tajný kľúÄ: ', + 'Test your device' => 'Otestujte svoje zariadenie', + 'Assign a color when the task is moved to a specific column' => 'NastaviÅ¥ farbu, keÄ je úloha presunutá do zadaného stĺpca', + '%s via Kanboard' => '%s cez Kanboard', + 'Burndown chart' => 'Burndown diagram', + 'This chart show the task complexity over the time (Work Remaining).' => 'Tento diagram zobrazuje komplexnosÅ¥ úloh v Äase (Zostávajúca práca).', + 'Screenshot taken %s' => 'Vytvorená snímka obrazovky %s', + 'Add a screenshot' => 'PridaÅ¥ snímku obrazovky', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Vytvorte snímku obrazovky a stlaÄte CTRL+V alebo ⌘+V na jej vloženie tu.', + 'Screenshot uploaded successfully.' => 'Snímka úspeÅ¡ne nahraná.', + 'SEK - Swedish Krona' => 'SEK – Å védska koruna', + 'Identifier' => 'Identifikátor', + 'Disable two factor authentication' => 'Vypnúť dvojfaktorovú autentifikáciu', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Naozaj chcete zakázaÅ¥ dvojfaktorovú autentifikáciu používateľa: „%sâ€?', + 'Edit link' => 'UpraviÅ¥ odkaz', + 'Start to type task title...' => 'ZaÄnite písaÅ¥ názov úlohy...', + 'A task cannot be linked to itself' => 'Úloha nemôže odkazovaÅ¥ sama na seba', + 'The exact same link already exists' => 'Rovnaký odkaz už existuje', + 'Recurrent task is scheduled to be generated' => 'Opakovaná úloha je naplánovaná na vygenerovanie', + 'Score' => 'Skóre', + 'The identifier must be unique' => 'Identifikátor musí byÅ¥ jedineÄný', + 'This linked task id doesn\'t exists' => 'ID odkazovanej úlohy neexistuje', + 'This value must be alphanumeric' => 'Hodnota musí byÅ¥ alfanumerická', + 'Edit recurrence' => 'UpraviÅ¥ opakovanie', + 'Generate recurrent task' => 'GenerovaÅ¥ opakovanú úlohu', + 'Trigger to generate recurrent task' => 'SpúšťaÄ generovania opakovanej úlohy', + 'Factor to calculate new due date' => 'Faktor výpoÄtu nového termínu splnenia', + 'Timeframe to calculate new due date' => 'ÄŒasový rozvrh výpoÄtu nového termínu splnenia', + 'Base date to calculate new due date' => 'Základný dátum výpoÄtu nového termínu splnenia', + 'Action date' => 'Dátum akcie', + 'Base date to calculate new due date: ' => 'Základný dátum výpoÄtu nového termínu splnenia: ', + 'This task has created this child task: ' => 'Úloha vytvorila túto dcérsku úlohu: ', + 'Day(s)' => 'Deň(dni)', + 'Existing due date' => 'Existujúci termín splnenia', + 'Factor to calculate new due date: ' => 'Faktor výpoÄtu nového termínu splnenia: ', + 'Month(s)' => 'Mesiac(e)', + 'This task has been created by: ' => 'Túto úlohu vytvoril: ', + 'Recurrent task has been generated:' => 'Opakovaná úloha bola vygenerovaná:', + 'Timeframe to calculate new due date: ' => 'ÄŒasový rozvrh výpoÄtu nového termínu splnenia: ', + 'Trigger to generate recurrent task: ' => 'SpúšťaÄ generovania opakovanej úlohy: ', + 'When task is closed' => 'KeÄ je úloha ukonÄená', + 'When task is moved from first column' => 'KeÄ je úloha presunutá z prvého stĺpca', + 'When task is moved to last column' => 'KeÄ je úloha presunutá do posledného stĺpca', + 'Year(s)' => 'Rok(y)', + 'Project settings' => 'Nastavenia projektu', + 'Automatically update the start date' => 'Automaticky aktualizovaÅ¥ dátum zaÄiatku', + 'iCal feed' => 'Kanál iCal', + 'Preferences' => 'Nastavenia', + 'Security' => 'BezpeÄnosÅ¥', + 'Two factor authentication disabled' => 'Dvojfaktorová autentifikácia zakázaná', + 'Two factor authentication enabled' => 'Dvojfaktorová autentifikácia povolená', + 'Unable to update this user.' => 'Nemožno aktualizovaÅ¥ tohoto používateľa.', + 'There is no user management for personal projects.' => 'Súkromné projekty nemajú správu používateľov.', + 'User that will receive the email' => 'Používateľ, ktorý dostane email', + 'Email subject' => 'Predmet emailu', + 'Date' => 'Dátum', + 'Add a comment log when moving the task between columns' => 'PridaÅ¥ do záznamu komentár, keÄ je úloha presunutá medzi stĺpcami', + 'Move the task to another column when the category is changed' => 'Presunúť úlohu do iného stĺpca, keÄ je zmenená kategória', + 'Send a task by email to someone' => 'PoslaÅ¥ úlohu niekomu emailom', + 'Reopen a task' => 'Znova tvoriÅ¥ úlohu', + 'Notification' => 'Upozornenie', + '%s moved the task #%d to the first swimlane' => '%s presunul úlohu #%d do prvej dráhy', + 'Swimlane' => 'Dráha', + '%s moved the task %s to the first swimlane' => '%s presunul úlohu %s do prvej dráhy', + '%s moved the task %s to the swimlane "%s"' => '%s presunul úlohu %s do dráhy „%sâ€', + 'This report contains all subtasks information for the given date range.' => 'Tento výstup obsahuje informácie o vÅ¡etkých podúlohách v zadanom rozsahu dátumov.', + 'This report contains all tasks information for the given date range.' => 'Tento výstup obsahuje vÅ¡etky úlohy v zadanom rozsahu dátumov.', + 'Project activities for %s' => 'Aktivity projektu %s', + 'view the board on Kanboard' => 'zobraziÅ¥ v nástenke Kanboard', + 'The task has been moved to the first swimlane' => 'Úloha bola presunutá do prvej dráhy', + 'The task has been moved to another swimlane:' => 'Úloha bola presunutá do inej dráhy:', + 'New title: %s' => 'Nový názov: %s', + 'The task is not assigned anymore' => 'Úloha už viac nie je pridelená', + 'New assignee: %s' => 'Nové pridelenie: %s', + 'There is no category now' => 'Teraz žiadna kategória', + 'New category: %s' => 'Nová kategória. %s', + 'New color: %s' => 'Nová farba: %s', + 'New complexity: %d' => 'Nová zložitosÅ¥: %d', + 'The due date has been removed' => 'Termín splnenia odstránený', + 'There is no description anymore' => 'Už viac nemá popis', + 'Recurrence settings has been modified' => 'Nastavenia opakovania boli zmenené', + 'Time spent changed: %sh' => 'Strávený Äas zmenený: %s hod', + 'Time estimated changed: %sh' => 'Predpokladaný Äas zmenený: %s hod', + 'The field "%s" has been updated' => 'Pole „%s†bolo aktualizované', + 'The description has been modified:' => 'Popis bol zmenený:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Naozaj chcete ukonÄiÅ¥ úlohu „%s†spolu so vÅ¡etkými podúlohami?', + 'I want to receive notifications for:' => 'Chcem dostávaÅ¥ upozornenia na:', + 'All tasks' => 'VÅ¡etky úlohy', + 'Only for tasks assigned to me' => 'Len úlohy pridelené mne', + 'Only for tasks created by me' => 'Len mnou vytvorené úlohy', + 'Only for tasks created by me and tasks assigned to me' => 'Len mnou vytvorené a mne pridelené úlohy', + '%%Y-%%m-%%d' => '%%d. %%m. %%Y', + 'Total for all columns' => 'Celkom vÅ¡etky stĺpce', + 'You need at least 2 days of data to show the chart.' => 'Na zobrazenie diagramu sú potrebné dáta aspoň za dva dni.', + '<15m' => '<15 m', + '<30m' => '<30 m', + 'Stop timer' => 'ZastaviÅ¥ ÄasovaÄ', + 'Start timer' => 'SpustiÅ¥ ÄasovaÄ', + 'My activity stream' => 'Moja aktivita', + 'Search tasks' => 'HľadaÅ¥ úlohy', + 'Reset filters' => 'VymazaÅ¥ filtre', + 'My tasks due tomorrow' => 'Moje zajtrajÅ¡ie úlohy', + 'Tasks due today' => 'DneÅ¡né úlohy', + 'Tasks due tomorrow' => 'ZajtrajÅ¡ie úlohy', + 'Tasks due yesterday' => 'VÄerajÅ¡ie úlohy', + 'Closed tasks' => 'UkonÄené úlohy', + 'Open tasks' => 'Otvorené úlohy', + 'Not assigned' => 'Nepridelené', + 'View advanced search syntax' => 'ZobraziÅ¥ syntax pokroÄilého hľadania', + 'Overview' => 'Prehľad', + 'Board/Calendar/List view' => 'Zobrazenie Nástenky/Kalendára/Zoznamu', + 'Switch to the board view' => 'Prepnúť do zobrazenia nástenky', + 'Switch to the list view' => 'Prepnúť do zobrazenia zoznamu', + 'Go to the search/filter box' => 'PrejsÅ¥ do poľa hľadania/filtra', + 'There is no activity yet.' => 'Zatiaľ žiadna aktivita.', + 'No tasks found.' => 'Nenájdené žiadne úlohy.', + 'Keyboard shortcut: "%s"' => 'Klávesová skratka: „%sâ€', + 'List' => 'Zoznam', + 'Filter' => 'Filter', + 'Advanced search' => 'PokroÄilé hľadanie', + 'Example of query: ' => 'Príklad dopytu: ', + 'Search by project: ' => 'HľadaÅ¥ podľa projektu: ', + 'Search by column: ' => 'HľadaÅ¥ podľa stĺpca: ', + 'Search by assignee: ' => 'HľadaÅ¥ podľa pridelenia: ', + 'Search by color: ' => 'HľadaÅ¥ podľa farby: ', + 'Search by category: ' => 'HľadaÅ¥ podľa kategórie: ', + 'Search by description: ' => 'HľadaÅ¥ podľa popisu: ', + 'Search by due date: ' => 'HľadaÅ¥ podľa dátumu splnenia: ', + 'Average time spent in each column' => 'Priemerný Äas strávený v každom stĺpci', + 'Average time spent' => 'Priemerne strávený Äas', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Tento diagram zobrazuje priemerný Äas cyklu a trvania posledných %d úloh.', + 'Average Lead and Cycle time' => 'Priemerný Äas trvania a cyklu', + 'Average lead time: ' => 'Priemerný Äas trvania: ', + 'Average cycle time: ' => 'Priemerný Äas cyklu: ', + 'Cycle Time' => 'ÄŒas cyklu', + 'Lead Time' => 'ÄŒas trvania', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Tento diagram zobrazuje priemerný Äas cyklu a trvania posledných %d úloh v Äase.', + 'Average time into each column' => 'Priemerný Äas v stĺpci', + 'Lead and cycle time' => 'ÄŒas trvania a cyklu', + 'Lead time: ' => 'ÄŒas trvania: ', + 'Cycle time: ' => 'ÄŒas cyklu: ', + 'Time spent in each column' => 'ÄŒas strávený v každom stĺpci', + 'The lead time is the duration between the task creation and the completion.' => 'ÄŒas trvania je doba medzi vytvorením úlohy a jej ukonÄením.', + 'The cycle time is the duration between the start date and the completion.' => 'ÄŒas cyklu je doba medzi zaÄatím úlohy a jej ukonÄením.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Ak úloha nie je ukonÄená, je namiesto Äasu ukonÄenia použitý aktuálny Äas.', + 'Set the start date automatically' => 'Automaticky nastaviÅ¥ dátum zaÄiatku', + 'Edit Authentication' => 'UpraviÅ¥ Autentifikáciu', + 'Remote user' => 'Vzdialený používateľ', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Vzdialení používatelia nemajú uložené svoje heslo v databáze Kanboard, príklady: LDAP, úÄty Google a Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ak oznaÄíte zaÅ¡krtávacie pole „ZakázaÅ¥ prihlasovací formulárâ€, prihlasovacie údaje zadané v prihlasovacom formulári budú ignorované.', + 'Default task color' => 'Predvolená farba úlohy', + 'This feature does not work with all browsers.' => 'Táto vlastnosÅ¥ nefunguje vo vÅ¡etkých prehliadaÄoch.', + 'There is no destination project available.' => 'Cieľový projekt nie je dostupný.', + 'Trigger automatically subtask time tracking' => 'Automaticky spustiÅ¥ sledovanie Äasu podúlohy', + 'Include closed tasks in the cumulative flow diagram' => 'V kumulatívnom diagrame toku zahrnúť aj ukonÄené úlohy', + 'Current swimlane: %s' => 'Aktuálna dráha: %s', + 'Current column: %s' => 'Aktuálny stĺpec: %s', + 'Current category: %s' => 'Aktuálna kategória: %s', + 'no category' => 'žiadna kategória', + 'Current assignee: %s' => 'Aktuálne pridelené: %s', + 'not assigned' => 'nepridelené', + 'Author:' => 'Autor:', + 'contributors' => 'prispievatelia', + 'License:' => 'Licencia:', + 'License' => 'Licencia', + 'Enter the text below' => 'Zadajte nasledujúci text', + 'Start date:' => 'Dátum zaÄiatku:', + 'Due date:' => 'Termín ukonÄenia:', + 'People who are project managers' => 'Ľudia, ktorí sú správcovia projektu', + 'People who are project members' => 'Ľudia, ktorí sú Älenovia projektu', + 'NOK - Norwegian Krone' => 'NOK – Nórska koruna', + 'Show this column' => 'ZobraziÅ¥ tento stĺpec', + 'Hide this column' => 'SkryÅ¥ tento stĺpec', + 'End date' => 'Dátum ukonÄenia', + 'Users overview' => 'Prehľad používateľov', + 'Members' => 'ÄŒlenovia', + 'Shared project' => 'Zdieľané projekty', + 'Project managers' => 'Správcovia projektu', + 'Projects list' => 'Zoznam projektov', + 'End date:' => 'Dátum ukonÄenia:', + 'Change task color when using a specific task link' => 'ZmeniÅ¥ farbu úlohy, pri použití daného odkazu na úlohu', + 'Task link creation or modification' => 'Vytvorenie alebo úprava odkazu na úlohu', + 'Milestone' => 'Míľnik', + 'Reset the search/filter box' => 'VymazaÅ¥ pole hľadania/filtra', + 'Documentation' => 'Dokumentácia', + 'Author' => 'Autor', + 'Version' => 'Verzia', + 'Plugins' => 'Zásuvné moduly', + 'There is no plugin loaded.' => 'Žiadne naÄítané zásuvné moduly.', + 'My notifications' => 'Moje upozornenia', + 'Custom filters' => 'Vlastné filtre', + 'Your custom filter has been created successfully.' => 'Váš vlastný filter bol úspeÅ¡ne vytvorený.', + 'Unable to create your custom filter.' => 'Nemožno vytvoriÅ¥ Váš vlastný filter.', + 'Custom filter removed successfully.' => 'Vlastný filter úspeÅ¡ne odstránený.', + 'Unable to remove this custom filter.' => 'Nemožno odstrániÅ¥ vlastný filter.', + 'Edit custom filter' => 'UpraviÅ¥ vlastný filter', + 'Your custom filter has been updated successfully.' => 'Vlastný filter bol úspeÅ¡ne aktualizovaný.', + 'Unable to update custom filter.' => 'Nemožno aktualizovaÅ¥ vlastný filter.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Nová príloha úlohy #%d: %s', + 'New comment on task #%d' => 'Nový komentár úlohy #%d', + 'Comment updated on task #%d' => 'Aktualizovaný komentár úlohy #%d', + 'New subtask on task #%d' => 'Nová podúloha úlohy #%d', + 'Subtask updated on task #%d' => 'Aktualizovaná podúloha úlohy #%d', + 'New task #%d: %s' => 'Nová úloha #%d: %s', + 'Task updated #%d' => 'Aktualizovaná úloha #%d', + 'Task #%d closed' => 'Úloha #%d ukonÄená', + 'Task #%d opened' => 'Úloha #%d otvorená', + 'Column changed for task #%d' => 'Zmenený stĺpec úlohy #%d', + 'New position for task #%d' => 'Nová pozícia úlohy #%d', + 'Swimlane changed for task #%d' => 'Zmenená dráha úlohy #%d', + 'Assignee changed on task #%d' => 'Zmenené pridelenie úlohy #%d', + '%d overdue tasks' => '%d úloh po termíne', + 'No notification.' => 'Žiadne upozornenia.', + 'Mark all as read' => 'OznaÄiÅ¥ vÅ¡etky ako preÄítané', + 'Mark as read' => 'OznaÄiÅ¥ ako preÄítané', + 'Total number of tasks in this column across all swimlanes' => 'Celkový poÄet úloh tohoto stĺpca vo vÅ¡etkých dráhach', + 'Collapse swimlane' => 'ZbaliÅ¥ cestu', + 'Expand swimlane' => 'RozbaliÅ¥ cestu', + 'Add a new filter' => 'PridaÅ¥ nový filter', + 'Share with all project members' => 'ZdieľaÅ¥ so vÅ¡etkými Älenmi projektu', + 'Shared' => 'Zdieľané', + 'Owner' => 'Vlastník', + 'Unread notifications' => 'NepreÄítané upozornenia', + 'Notification methods:' => 'Metódy upozornení:', + 'Unable to read your file' => 'Nemožno ÄítaÅ¥ zadaný súbor', + '%d task(s) have been imported successfully.' => '%d úloha(y) úspeÅ¡ne importovaná/é.', + 'Nothing has been imported!' => 'Nebolo importované niÄ', + 'Import users from CSV file' => 'ImportovaÅ¥ používateľov zo súboru CSV', + '%d user(s) have been imported successfully.' => '%d požívateľ(ia) úspeÅ¡ne importovaní.', + 'Comma' => 'Äiarka', + 'Semi-colon' => 'bodkoÄiarka', + 'Tab' => 'tabulátor', + 'Vertical bar' => 'zvislá Äiara', + 'Double Quote' => 'úvodzovky', + 'Single Quote' => 'apostrofy', + '%s attached a file to the task #%d' => '%s pripojil súbor k úlohe #%d', + 'There is no column or swimlane activated in your project!' => 'V tomto projekte nie sú aktivované stĺpce alebo dráhy', + 'Append filter (instead of replacement)' => 'PridaÅ¥ filter (namiesto nahradenia)', + 'Append/Replace' => 'PridaÅ¥/NahradiÅ¥', + 'Append' => 'PridaÅ¥', + 'Replace' => 'NahradiÅ¥', + 'Import' => 'ImportovaÅ¥', + 'Change sorting' => 'ZmeniÅ¥ radenie', + 'Tasks Importation' => 'Import úloh', + 'Delimiter' => 'OddeľovaÄ', + 'Enclosure' => 'Uzavretie', + 'CSV File' => 'Súbor CSV', + 'Instructions' => 'Pokyny', + 'Your file must use the predefined CSV format' => 'Súbor musí používaÅ¥ prednastavený formát CSV', + 'Your file must be encoded in UTF-8' => 'Súbor musí byÅ¥ kódovaný v UTF-8', + 'The first row must be the header' => 'Prvý riadok musí byÅ¥ hlaviÄka', + 'Duplicates are not verified for you' => 'Duplikáty nie sú kontrolované', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Dátum ukonÄenia musí byÅ¥ vo formáte ISO: RRRR-MM-DD', + 'Download CSV template' => 'StiahnuÅ¥ Å¡ablónu CSV', + 'No external integration registered.' => 'Nie je zaregistrovaná externá integrácia.', + 'Duplicates are not imported' => 'Duplikáta nie sú importované', + 'Usernames must be lowercase and unique' => 'Mená používateľov musia byÅ¥ malými písmenami a jedineÄné', + 'Passwords will be encrypted if present' => 'Heslá, ak existujú, budú Å¡ifrované', + '%s attached a new file to the task %s' => '%s pripojil nový súbor k úlohe %s', + 'Link type' => 'Typ odkazu', + 'Assign automatically a category based on a link' => 'Automaticky nastaviÅ¥ kategóriu na základe odkazu', + 'BAM - Konvertible Mark' => 'BAM – Konvertibilná marka', + 'Assignee Username' => 'Prihl. meno pridelenia', + 'Assignee Name' => 'Meno pridelenia', + 'Groups' => 'Skupiny', + 'Members of %s' => 'ÄŒlenovia %s', + 'New group' => 'Nová skupina', + 'Group created successfully.' => 'Skupina úspeÅ¡ne vytvorená.', + 'Unable to create your group.' => 'Nemožno vytvoriÅ¥ skupinu.', + 'Edit group' => 'UpraviÅ¥ skupinu', + 'Group updated successfully.' => 'Skupina úspeÅ¡ne aktualizovaná.', + 'Unable to update your group.' => 'Nemožno aktualizovaÅ¥ skupinu.', + 'Add group member to "%s"' => 'PridaÅ¥ Älena skupiny do „%sâ€', + 'Group member added successfully.' => 'ÄŒlen skupiny úspeÅ¡ne pridaný.', + 'Unable to add group member.' => 'Nemožno pridaÅ¥ Älena skupiny.', + 'Remove user from group "%s"' => 'OdstrániÅ¥ používateľa zo skupiny „%sâ€', + 'User removed successfully from this group.' => 'Používateľ úspeÅ¡ne odstránený zo skupiny.', + 'Unable to remove this user from the group.' => 'Nemožno odstrániÅ¥ používateľa zo skupiny.', + 'Remove group' => 'OdstrániÅ¥ skupinu', + 'Group removed successfully.' => 'Skupina úspeÅ¡ne odstránená.', + 'Unable to remove this group.' => 'Nemožno odstrániÅ¥ skupinu.', + 'Project Permissions' => 'Povolenia projektu', + 'Manager' => 'Správca', + 'Project Manager' => 'Správca projektu', + 'Project Member' => 'ÄŒlen projektu', + 'Project Viewer' => 'ÄŒitateľ projektu', + 'Your account is locked for %d minutes' => 'Váš úÄet je zamknutý na %d min', + 'Invalid captcha' => 'Neplatná CAPTCHA', + 'The name must be unique' => 'Meno musí byÅ¥ jedineÄné', + 'View all groups' => 'ZobraziÅ¥ vÅ¡etky skupiny', + 'There is no user available.' => 'Nie je dostupný žiadny používateľ.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Naozaj chcete odstrániÅ¥ používateľa „%s†zo skupiny „%sâ€?', + 'There is no group.' => 'Nie je tu žiadna skupina.', + 'Add group member' => 'PridaÅ¥ Älena skupiny', + 'Do you really want to remove this group: "%s"?' => 'Naozaj chcete odstrániÅ¥ skupinu: „%sâ€?', + 'There is no user in this group.' => 'Žiadny používatelia tejto skupiny.', + 'Permissions' => 'Povolenia', + 'Allowed Users' => 'Povolení používatelia', + 'No specific user has been allowed.' => 'Žiadni výslovne povolení používatelia.', + 'Role' => 'Rola', + 'Enter user name...' => 'Zadajte prihlasovacie meno...', + 'Allowed Groups' => 'Povolené skupiny', + 'No group has been allowed.' => 'Žiadne výslovne povolené skupiny.', + 'Group' => 'Skupiny', + 'Group Name' => 'Meno skupiny', + 'Enter group name...' => 'Zadajte meno skupiny...', + 'Role:' => 'Rola:', + 'Project members' => 'ÄŒlenovia projektu', + '%s mentioned you in the task #%d' => '%s Vás spomenul v úlohe #%d', + '%s mentioned you in a comment on the task #%d' => '%s Vás spomenul v komentári úlohy #%d', + 'You were mentioned in the task #%d' => 'Boli ste spomenutý v úlohe #%d', + 'You were mentioned in a comment on the task #%d' => 'Boli ste spomenutý v komentári úlohy #%d', + 'Estimated hours: ' => 'OÄakávané hodiny: ', + 'Actual hours: ' => 'Aktuálne hodiny: ', + 'Hours Spent' => 'Strávených hodín', + 'Hours Estimated' => 'Plánovaných hodín', + 'Estimated Time' => 'Plánovaný Äas', + 'Actual Time' => 'Aktuálny Äas', + 'Estimated vs actual time' => 'OÄakávaný a aktuálny Äas', + 'RUB - Russian Ruble' => 'RUB – Ruský rubeľ', + 'Assign the task to the person who does the action when the column is changed' => 'PrideliÅ¥ úlohu osobe, ktorá vykonala akciu so zmenou stĺpca', + 'Close a task in a specific column' => 'UkonÄiÅ¥ úlohu v zadanom stĺpci', + 'Time-based One-time Password Algorithm' => 'Algoritmus Äasovo obmedzeného jednoúÄelového hesla', + 'Two-Factor Provider: ' => 'Poskytovateľ dvojfaktoru: ', + 'Disable two-factor authentication' => 'Vypnúť dvojfaktorovú autentifikáciu', + 'Enable two-factor authentication' => 'Zapnúť dvojfaktorovú autentifikáciu', + 'There is no integration registered at the moment.' => 'Momentálne žiadna registrovaná integrácia.', + 'Password Reset for Kanboard' => 'Obnova hesla pre Kanboard', + 'Forgot password?' => 'Zabudli ste heslo?', + 'Enable "Forget Password"' => 'Zapnúť „Zabudnuté hesloâ€', + 'Password Reset' => 'Obnovenie hesla', + 'New password' => 'Nové heslo', + 'Change Password' => 'ZmeniÅ¥ heslo', + 'To reset your password click on this link:' => 'Na obnovu hesla kliknite na nasledujúci odkaz:', + 'Last Password Reset' => 'Posledná obnova hesla', + 'The password has never been reinitialized.' => 'Heslo nebolo nikdy obnovované.', + 'Creation' => 'Vytvorenie', + 'Expiration' => 'VyprÅ¡anie', + 'Password reset history' => 'História obnovenia hesla', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'VÅ¡etky úlohy stĺpca „%s†a dráhy „%s†boli úspeÅ¡ne ukonÄené.', + 'Do you really want to close all tasks of this column?' => 'Naozaj chcete ukonÄiÅ¥ vÅ¡etky úlohy tohoto stĺpca?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d úloha(y) v stĺpci „%s†a dráhy „%s†budú ukonÄené.', + 'Close all tasks in this column and this swimlane' => 'UkonÄiÅ¥ vÅ¡etky úlohy tohoto stĺpca', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Žiadny zásuvný modul nemá zaregistrovanú metódu upozornení projektu. I tak môžete nastaviÅ¥ jednotlivé upozornenia vo svojom profile.', + 'My dashboard' => 'Moja nástenka', + 'My profile' => 'Môj profil', + 'Project owner: ' => 'Vlastník projektu: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identifikátor projektu je voliteľný a musí byÅ¥ alfanumerický, napr. MOJPORJEKT.', + 'Project owner' => 'Vlastník projektu', + 'Personal projects do not have users and groups management.' => 'Súkromné projekty nemajú správu používateľov a skupín.', + 'There is no project member.' => 'Žiadny Älen projektu.', + 'Priority' => 'Priorita', + 'Task priority' => 'Priorita úlohy', + 'General' => 'VÅ¡eobecné', + 'Dates' => 'Dátumy', + 'Default priority' => 'Predvolená priorita', + 'Lowest priority' => 'Najnižšia priorita', + 'Highest priority' => 'Najvyššia priorita', + 'Close a task when there is no activity' => 'UkonÄiÅ¥ úlohu, ak je bez aktivity', + 'Duration in days' => 'Trvanie v dňoch', + 'Send email when there is no activity on a task' => 'PoslaÅ¥ email, keÄ nenastala aktivita s úlohou', + 'Unable to fetch link information.' => 'Nemožno získaÅ¥ informácie odkazu.', + 'Daily background job for tasks' => 'Denná úloha na pozadí pre úlohy', + 'Auto' => 'Auto', + 'Related' => 'Súvisí', + 'Attachment' => 'Prílohy', + 'Web Link' => 'Webový odkaz', + 'External links' => 'Externé odkazy', + 'Add external link' => 'PridaÅ¥ externý odkaz', + 'Type' => 'Typ', + 'Dependency' => 'ZávislosÅ¥', + 'Add internal link' => 'PridaÅ¥ interný odkaz', + 'Add a new external link' => 'PridaÅ¥ nový externý odkaz', + 'Edit external link' => 'UpraviÅ¥ externý odkaz', + 'External link' => 'Externý odkaz', + 'Copy and paste your link here...' => 'Prekopírujte a vložte svoj odkaz tu...', + 'URL' => 'URL', + 'Internal links' => 'Interné odkazy', + 'Assign to me' => 'PrideliÅ¥ mne', + 'Me' => 'mne', + 'Do not duplicate anything' => 'NiÄ neduplikovaÅ¥', + 'Projects management' => 'Správa projektov', + 'Users management' => 'Správa používateľov', + 'Groups management' => 'Správa skupín', + 'Create from another project' => 'VytvoriÅ¥ z iného projektu', + 'open' => 'otvorený', + 'closed' => 'ukonÄený', + 'Priority:' => 'Priorita:', + 'Reference:' => 'Odkaz:', + 'Complexity:' => 'ZložitosÅ¥:', + 'Swimlane:' => 'Dráha:', + 'Column:' => 'Stĺpec:', + 'Position:' => 'Pozícia:', + 'Creator:' => 'Autor:', + 'Time estimated:' => 'Predpokladaný Äas:', + '%s hours' => '%s hod', + 'Time spent:' => 'Strávený Äas:', + 'Created:' => 'Vytvorené:', + 'Modified:' => 'Zmenené:', + 'Completed:' => 'DokonÄené:', + 'Started:' => 'ZaÄaté:', + 'Moved:' => 'Presunuté:', + 'Task #%d' => 'Úloha #%d', + 'Time format' => 'Formát Äasu', + 'Start date: ' => 'Dátum zaÄiatku: ', + 'End date: ' => 'Dátum ukonÄenia: ', + 'New due date: ' => 'Nový termín splnenia: ', + 'Start date changed: ' => 'Dátum zaÄiatku zmenený: ', + 'Disable personal projects' => 'Vypnúť súkromné projekty', + 'Do you really want to remove this custom filter: "%s"?' => 'Naozaj chcete odstrániÅ¥ vlastný filter: „%sâ€?', + 'Remove a custom filter' => 'OdstrániÅ¥ vlastný filter', + 'User activated successfully.' => 'Používateľ úspeÅ¡ne aktivovaný.', + 'Unable to enable this user.' => 'Nemožno povoliÅ¥ tohoto používateľa.', + 'User disabled successfully.' => 'Používateľ úspeÅ¡ne zakázaný.', + 'Unable to disable this user.' => 'Nemožno zakázaÅ¥ tohoto používateľa.', + 'All files have been uploaded successfully.' => 'VÅ¡etky súbory úspeÅ¡ne nahrané.', + 'The maximum allowed file size is %sB.' => 'Maximálna povolená veľkosÅ¥ súboru je %sB.', + 'Drag and drop your files here' => 'Pretiahnite svoje súbory tu', + 'choose files' => 'zvoľte súbory', + 'View profile' => 'ZobraziÅ¥ profil', + 'Two Factor' => 'Dvojfaktor', + 'Disable user' => 'ZakázaÅ¥ používateľa', + 'Do you really want to disable this user: "%s"?' => 'Naozaj chcete zakázaÅ¥ používateľa: „%sâ€?', + 'Enable user' => 'PovoliÅ¥ používateľa', + 'Do you really want to enable this user: "%s"?' => 'Naozaj chcete povoliÅ¥ používateľa: „%sâ€?', + 'Download' => 'StiahnuÅ¥', + 'Uploaded: %s' => 'Nahrané: %s', + 'Size: %s' => 'VeľkosÅ¥: %s', + 'Uploaded by %s' => 'Nahral %s', + 'Filename' => 'Meno súboru', + 'Size' => 'VeľkosÅ¥', + 'Column created successfully.' => 'Stĺpec úspeÅ¡ne vytvorený.', + 'Another column with the same name exists in the project' => 'V projekte už existuje iný stĺpec s rovnakým menom', + 'Default filters' => 'Predvolené filtre', + 'Your board doesn\'t have any columns!' => 'VaÅ¡a nástenka nemá žiadne stĺpce!', + 'Change column position' => 'ZmeniÅ¥ pozíciu stĺpca', + 'Switch to the project overview' => 'Prepnúť na prehľad projektu', + 'User filters' => 'Filtre používateľa', + 'Category filters' => 'Filtre kategórie', + 'Upload a file' => 'NahraÅ¥ súbor', + 'View file' => 'ZobraziÅ¥ súbor', + 'Last activity' => 'Nedávna aktivita', + 'Change subtask position' => 'ZmeniÅ¥ pozíciu podúlohy', + 'This value must be greater than %d' => 'Táto hodnota musí byÅ¥ väÄÅ¡ia ako %d', + 'Another swimlane with the same name exists in the project' => 'V projekte už existuje iná dráha s rovnakým menom', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Príklad: https://kanboard.example.org/ (slúži na generovanie absolútnych URL)', + 'Actions duplicated successfully.' => 'Akcie úspeÅ¡ne duplikované.', + 'Unable to duplicate actions.' => 'Nemožno duplikovaÅ¥ akciu.', + 'Add a new action' => 'PridaÅ¥ novú akciu', + 'Import from another project' => 'ImportovaÅ¥ z iného projektu', + 'There is no action at the moment.' => 'Momentálne žiadne akcie.', + 'Import actions from another project' => 'ImportovaÅ¥ akcie z iného projektu', + 'There is no available project.' => 'Žiadny dostupný projekt.', + 'Local File' => 'Lokálny súbor', + 'Configuration' => 'Nastavenia', + 'PHP version:' => 'Verzia PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Verzia OS:', + 'Database version:' => 'Verzia databázy:', + 'Browser:' => 'PrehliadaÄ:', + 'Task view' => 'Zobrazenie úlohy', + 'Edit task' => 'UpraviÅ¥ úlohu', + 'Edit description' => 'UpraviÅ¥ popis', + 'New internal link' => 'Nový interný odkaz', + 'Display list of keyboard shortcuts' => 'ZobraziÅ¥ zoznam klávesových skratiek', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'NahraÅ¥ obrázok svojho avatara', + 'Remove my image' => 'OdstrániÅ¥ svoj obrázok', + 'The OAuth2 state parameter is invalid' => 'Parameter stavu OAuth2 je neplatný', + 'User not found.' => 'Používateľ nenájdený.', + 'Search in activity stream' => 'HľadaÅ¥ v prehľade aktivity', + 'My activities' => 'Moje aktivity', + 'Activity until yesterday' => 'Aktivita do vÄera', + 'Activity until today' => 'Aktivita do dnes', + 'Search by creator: ' => 'HľadaÅ¥ podľa autora: ', + 'Search by creation date: ' => 'HľadaÅ¥ podľa dátumu vytvorenia: ', + 'Search by task status: ' => 'HľadaÅ¥ podľa stavu úlohy: ', + 'Search by task title: ' => 'HľadaÅ¥ podľa názvu úlohy: ', + 'Activity stream search' => 'Hľadanie v prehľade aktivity', + 'Projects where "%s" is manager' => 'Projekty, kde je správcom „%sâ€', + 'Projects where "%s" is member' => 'Projekty, kde je Älenom „%sâ€', + 'Open tasks assigned to "%s"' => 'Otvorené úlohy pridelené „%sâ€', + 'Closed tasks assigned to "%s"' => 'UkonÄené úlohy pridelené „%sâ€', + 'Assign automatically a color based on a priority' => 'Automaticky nastaviÅ¥ farbu na základe priority', + 'Overdue tasks for the project(s) "%s"' => 'Úlohy po termíne projektu(ov) „%sâ€', + 'Upload files' => 'NahraÅ¥ súbory', + 'Installed Plugins' => 'NainÅ¡talované zásuvné moduly', + 'Plugin Directory' => 'Zložka zásuvných modulov', + 'Plugin installed successfully.' => 'Zásuvný modul úspeÅ¡ne nainÅ¡talovaný.', + 'Plugin updated successfully.' => 'Zásuvný modul úspeÅ¡ne aktualizovaný.', + 'Plugin removed successfully.' => 'Zásuvný modul úspeÅ¡ne odstránený.', + 'Subtask converted to task successfully.' => 'Podúloha úspeÅ¡ne konvertovaná na úlohu.', + 'Unable to convert the subtask.' => 'Nemožno konvertovaÅ¥ podúlohu.', + 'Unable to extract plugin archive.' => 'Nemožno rozbaliÅ¥ archív zásuvného modulu.', + 'Plugin not found.' => 'Zásuvný modul nenájdený.', + 'You don\'t have the permission to remove this plugin.' => 'Nemáte práva na odstránenie tohoto zásuvného modulu.', + 'Unable to download plugin archive.' => 'Nemožno stiahnuÅ¥ archív zásuvného modulu.', + 'Unable to write temporary file for plugin.' => 'Nemožno zapísaÅ¥ doÄasný súbor zásuvného modulu.', + 'Unable to open plugin archive.' => 'Nemožno otvoriÅ¥ archív zásuvného modulu.', + 'There is no file in the plugin archive.' => 'V archíve zásuvného modulu nie je súbor.', + 'Create tasks in bulk' => 'Hromadné vytvorenie úloh', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Táto inÅ¡talácia Kanboard nie je nastavená na inÅ¡taláciu zásuvných modulov z používateľského rozhrania.', + 'There is no plugin available.' => 'Žiadne dostupné zásuvné moduly.', + 'Install' => 'InÅ¡talovaÅ¥', + 'Update' => 'AktualizovaÅ¥', + 'Up to date' => 'Aktuálny', + 'Not available' => 'Nedostupný', + 'Remove plugin' => 'OdstrániÅ¥ zásuvný modul', + 'Do you really want to remove this plugin: "%s"?' => 'Naozaj chcete odstrániÅ¥ zásuvný modul: „%sâ€?', + 'Uninstall' => 'OdinÅ¡talovaÅ¥', + 'Listing' => 'Zoznam', + 'Metadata' => 'Metadáta', + 'Manage projects' => 'Správa projektov', + 'Convert to task' => 'KonvertovaÅ¥ na úlohu', + 'Convert sub-task to task' => 'KonvertovaÅ¥ podúlohu na úlohu', + 'Do you really want to convert this sub-task to a task?' => 'Naozaj chcete konvertovaÅ¥ túto podúlohu na úlohu?', + 'My task title' => 'Názov mojej úlohy', + 'Enter one task by line.' => 'Zadajte jednu úlohu na riadok.', + 'Number of failed login:' => 'PoÄet zlyhaných prihlásení:', + 'Account locked until:' => 'ÚÄet uzamknutý do:', + 'Email settings' => 'Nastavenia emailu', + 'Email sender address' => 'Adresa odosielateľa', + 'Email transport' => 'Emailový transport', + 'Webhook token' => 'Token webového háku', + 'Project tags management' => 'Správa znaÄiek projektu', + 'Tag created successfully.' => 'ZnaÄka úspeÅ¡ne vytvorená.', + 'Unable to create this tag.' => 'Nemožno vytvoriÅ¥ túto znaÄku.', + 'Tag updated successfully.' => 'ZnaÄka úspeÅ¡ne zmenená.', + 'Unable to update this tag.' => 'Nemožno upraviÅ¥ túto znaÄku.', + 'Tag removed successfully.' => 'ZnaÄka úspeÅ¡ne odstránená.', + 'Unable to remove this tag.' => 'Nemožno odstrániÅ¥ túto znaÄku.', + 'Global tags management' => 'Správa globálnych znaÄiek', + 'Tags' => 'ZnaÄky', + 'Tags management' => 'Správa znaÄiek', + 'Add new tag' => 'PridaÅ¥ novú znaÄku', + 'Edit a tag' => 'UpraviÅ¥ znaÄku', + 'Project tags' => 'ZnaÄky projektu', + 'There is no specific tag for this project at the moment.' => 'Momentálne tento projekt nemá žiadnu konkrétnu znaÄku.', + 'Tag' => 'ZnaÄka', + 'Remove a tag' => 'OdstrániÅ¥ znaÄku', + 'Do you really want to remove this tag: "%s"?' => 'Naozaj chcete odstrániÅ¥ znaÄku: „%sâ€?', + 'Global tags' => 'Globálne znaÄky', + 'There is no global tag at the moment.' => 'Momentálne žiadne globálne znaÄky.', + 'This field cannot be empty' => 'Toto pole nemôže byÅ¥ prázdne', + 'Close a task when there is no activity in a specific column' => 'UkonÄiÅ¥ úlohu, ak nebola aktivita v zadanom stĺpci', + '%s removed a subtask for the task #%d' => '%s odstránil podúlohu úlohy #%d', + '%s removed a comment on the task #%d' => '%s odstránil komentár úlohy #%d', + 'Comment removed on task #%d' => 'Odstránený komentár úlohy #%d', + 'Subtask removed on task #%d' => 'Odstránená podúloha úlohy #%d', + 'Hide tasks in this column in the dashboard' => 'SkryÅ¥ úlohy tohoto stĺpca v nástenke', + '%s removed a comment on the task %s' => '%s odstránil komentár úlohy %s', + '%s removed a subtask for the task %s' => '%s odstránil podúlohu úlohy %s', + 'Comment removed' => 'Komentár odstránený', + 'Subtask removed' => 'Podúloha odstránená', + '%s set a new internal link for the task #%d' => '%s nastavil nový interný odkaz úlohy #%d', + '%s removed an internal link for the task #%d' => '%s odstránil interný odkaz úlohy #%d', + 'A new internal link for the task #%d has been defined' => 'Bol definovaný nový interný odkaz úlohy #%d', + 'Internal link removed for the task #%d' => 'Odstránený interný odkaz úlohy #%d', + '%s set a new internal link for the task %s' => '%s vytvoril nový interný odkaz úlohy %s', + '%s removed an internal link for the task %s' => '%s odstránil interný odkaz úlohy %s', + 'Automatically set the due date on task creation' => 'Automaticky nastaviÅ¥ termín splnenia pri vytvorení úlohy', + 'Move the task to another column when closed' => 'Presunúť úlohu do iného stĺpca pri ukonÄení', + 'Move the task to another column when not moved during a given period' => 'Presunúť úlohu do iného stĺpca, ak nebola presunutá po zadanú dobu', + 'Dashboard for %s' => 'Nástenka %s', + 'Tasks overview for %s' => 'Prehľad úloh %s', + 'Subtasks overview for %s' => 'Prehľad podúloh %s', + 'Projects overview for %s' => 'Prehľad projektov %s', + 'Activity stream for %s' => 'Prúd aktivity %s', + 'Assign a color when the task is moved to a specific swimlane' => 'NastaviÅ¥ farbu, keÄ je úloha presunutá do zadanej dráhy', + 'Assign a priority when the task is moved to a specific swimlane' => 'NastaviÅ¥ prioritu, keÄ je úloha presunutá do zadanej dráhy', + 'User unlocked successfully.' => 'Používateľ úspeÅ¡ne odomknutý.', + 'Unable to unlock the user.' => 'Nemožno odomknúť používateľa.', + 'Move a task to another swimlane' => 'Presunúť úlohu do inej dráhy', + 'Creator Name' => 'Meno autora', + 'Time spent and estimated' => 'Strávený a oÄakávaný Äas', + 'Move position' => 'Presunúť pozíciu', + 'Move task to another position on the board' => 'Presunúť úlohu na inú pozíciu v nástenke', + 'Insert before this task' => 'VložiÅ¥ pred túto úlohu', + 'Insert after this task' => 'VložiÅ¥ za túto úlohu', + 'Unlock this user' => 'Odomknúť tohoto používateľa', + 'Custom Project Roles' => 'Vlastné role projektu', + 'Add a new custom role' => 'PridaÅ¥ novú vlastnú rolu', + 'Restrictions for the role "%s"' => 'Obmedzenia roly „%sâ€', + 'Add a new project restriction' => 'PridaÅ¥ nové obmedzenie projektu', + 'Add a new drag and drop restriction' => 'PridaÅ¥ obmedzenie Ťahaj a PusÅ¥', + 'Add a new column restriction' => 'PridaÅ¥ obmedzenie nového stĺpca', + 'Edit this role' => 'UpraviÅ¥ túto rolu', + 'Remove this role' => 'OdstrániÅ¥ túto rolu', + 'There is no restriction for this role.' => 'Táto rola nemá žiadne obmedzenia.', + 'Only moving task between those columns is permitted' => 'Povolený je len presun úloh medzi týmito stĺpcami', + 'Close a task in a specific column when not moved during a given period' => 'UkonÄiÅ¥ úlohu v zadanom stĺpci, ak nebola presunutá poÄas zadanej doby', + 'Edit columns' => 'UpraviÅ¥ stĺpce', + 'The column restriction has been created successfully.' => 'Obmedzenie stĺpca úspeÅ¡ne vytvorené.', + 'Unable to create this column restriction.' => 'Nemožno vytvoriÅ¥ obmedzenie stĺpca.', + 'Column restriction removed successfully.' => 'Obmedzenie stĺpca úspeÅ¡ne vytvorené.', + 'Unable to remove this restriction.' => 'Nemožno odstrániÅ¥ obmedzenie.', + 'Your custom project role has been created successfully.' => 'Vlastná rola projektu úspeÅ¡ne vytvorená.', + 'Unable to create custom project role.' => 'Nemožno vytvoriÅ¥ vlastnú rolu projektu.', + 'Your custom project role has been updated successfully.' => 'Vlastná rola projektu úspeÅ¡ne aktualizovaná.', + 'Unable to update custom project role.' => 'Nemožno aktualizovaÅ¥ vlastnú rolu projektu.', + 'Custom project role removed successfully.' => 'Vlastná rola projektu úspeÅ¡ne vytvorená.', + 'Unable to remove this project role.' => 'Nemožno odstrániÅ¥ rolu projektu.', + 'The project restriction has been created successfully.' => 'Obmedzenie projektu úspeÅ¡ne vytvorené.', + 'Unable to create this project restriction.' => 'Nemožno vytvoriÅ¥ obmedzenie projektu.', + 'Project restriction removed successfully.' => 'Obmedzenie projektu úspeÅ¡ne odstránené.', + 'You cannot create tasks in this column.' => 'Nemôžete vytvoriÅ¥ úlohu v tomto stĺpci.', + 'Task creation is permitted for this column' => 'Vytvorenie úloh povolené pre tento stĺpec', + 'Closing or opening a task is permitted for this column' => 'Otvorenie a ukonÄenie úloh je povolené pre tento stĺpec', + 'Task creation is blocked for this column' => 'Vytvorenie úloh je zakázané pre tento stĺpec', + 'Closing or opening a task is blocked for this column' => 'Otvorenie a ukonÄenie úloh je zakázané pre tento stĺpec', + 'Task creation is not permitted' => 'Vytvorenie úlohy nie je dovolené', + 'Closing or opening a task is not permitted' => 'Otvorenie a ukonÄenie úlohy nie je dovolené', + 'New drag and drop restriction for the role "%s"' => 'Nové obmedzenie Ťahaj a PusÅ¥ pre rolu „%sâ€', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Ľudia, ktorí patria do tejto roly, budú môcÅ¥ presúvaÅ¥ úlohy len medzi zdrojovým a cieľovým stĺpcom.', + 'Remove a column restriction' => 'OdstrániÅ¥ obmedzenie stĺpca', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Naozaj chcete odstrániÅ¥ obmedzenie stĺpca: „%s†do „%sâ€?', + 'New column restriction for the role "%s"' => 'Nové obmedzenie stĺpca roly „%sâ€', + 'Rule' => 'Pravidlo', + 'Do you really want to remove this column restriction?' => 'Naozaj chcete odstrániÅ¥ obmedzenie stĺpca?', + 'Custom roles' => 'Vlastné roly', + 'New custom project role' => 'Nová vlastná rola projektu', + 'Edit custom project role' => 'UpraviÅ¥ vlastnú rolu projektu', + 'Remove a custom role' => 'OdstrániÅ¥ vlastnú rolu projektu', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Naozaj chcete odstrániÅ¥ vlastnú rolu: „%sâ€? VÅ¡etci priradení k tejto role sa stanú Älenmi projektu.', + 'There is no custom role for this project.' => 'Tento projekt nemá vlastné roly.', + 'New project restriction for the role "%s"' => 'Nové obmedzenie roly projektu „%sâ€', + 'Restriction' => 'Obmedzenia', + 'Remove a project restriction' => 'OdstrániÅ¥ obmedzenie projektu', + 'Do you really want to remove this project restriction: "%s"?' => 'naozaj chcete odstrániÅ¥ obmedzenie projektu: „%sâ€?', + 'Duplicate to multiple projects' => 'DuplikovaÅ¥ do viacerých', + 'This field is required' => 'Toto pole je povinné', + 'Moving a task is not permitted' => 'Presun úlohy nie je povolený', + 'This value must be in the range %d to %d' => 'Táto hodnota musí byÅ¥ v rozsahu %d – %d', + 'You are not allowed to move this task.' => 'Nemáte dovolené presunúť túto úlohu.', + 'API User Access' => 'Prístup API používateľa', + 'Preview' => 'Ukážka', + 'Write' => 'PísaÅ¥', + 'Write your text in Markdown' => 'Napíšte svoj text v Markdown', + 'No personal API access token registered.' => 'Nie sú zaregistrované žiadne prístupové tokeny API.', + 'Your personal API access token is "%s"' => 'Váš osobný prístupový token API je „%sâ€', + 'Remove your token' => 'OdstrániÅ¥ svoj token', + 'Generate a new token' => 'GenerovaÅ¥ nový token', + 'Showing %d-%d of %d' => 'Zobrazené %d – %d z %d', + 'Outgoing Emails' => 'Odchádzajúce emaily', + 'Add or change currency rate' => 'PridaÅ¥ alebo zmeniÅ¥ konverzný kurz', + 'Reference currency: %s' => 'ReferenÄná mena: %s', + 'Add custom filters' => 'PridaÅ¥ vlastné filtre', + 'Export' => 'ExportovaÅ¥', + 'Add link label' => 'PridaÅ¥ menovku odkazu', + 'Incompatible Plugins' => 'Nekompatibilné zásuvné moduly', + 'Compatibility' => 'Kompatibilita', + 'Permissions and ownership' => 'Povolenia a vlastníctvo', + 'Priorities' => 'Priority', + 'Close this window' => 'ZatvoriÅ¥ okno', + 'Unable to upload this file.' => 'Nemožno nahraÅ¥ tento súbor.', + 'Import tasks' => 'ImportovaÅ¥ úlohy', + 'Choose a project' => 'Zvoľte projekt', + 'Profile' => 'Profile', + 'Application role' => 'Rola aplikácie', + '%d invitations were sent.' => '%d pozvaní odoslaných.', + '%d invitation was sent.' => '%d pozvanie odoslané.', + 'Unable to create this user.' => 'Nemožno vytvoriÅ¥ používateľa.', + 'Kanboard Invitation' => 'Pozvánka Kanboard', + 'Visible on dashboard' => 'Viditeľné v nástenke', + 'Created at:' => 'Vytvorené:', + 'Updated at:' => 'Upravené:', + 'There is no custom filter.' => 'Žiadny vlastný filter.', + 'New User' => 'Nový používateľ', + 'Authentication' => 'Autentifikácia', + 'If checked, this user will use a third-party system for authentication.' => 'Ak je zvolené, tento používateľ bude používaÅ¥ na autentifikáciu systém tretej strany.', + 'The password is necessary only for local users.' => 'Heslo je potrebné len pre lokálnych používateľov.', + 'You have been invited to register on Kanboard.' => 'Boli ste pozvaný na registráciu v Kanboard.', + 'Click here to join your team' => 'Kliknite tu na vstup do tímu', + 'Invite people' => 'PozvaÅ¥', + 'Emails' => 'Emaily', + 'Enter one email address by line.' => 'Zadajte jednu emailovú adresu na riadok.', + 'Add these people to this project' => 'PridaÅ¥ týchto ľudí do projektu', + 'Add this person to this project' => 'PridaÅ¥ tohoto Äloveka do projektu', + 'Sign-up' => 'RegistrovaÅ¥', + 'Credentials' => 'Prihl. údaje', + 'New user' => 'Nový používateľ', + 'This username is already taken' => 'Prihlasovacie meno už je použité', + 'Your profile must have a valid email address.' => 'Váš profil musí maÅ¥ platnú emailovú adresu.', + 'TRL - Turkish Lira' => 'TRL – Turecká líra', + 'The project email is optional and could be used by several plugins.' => 'Email projektu je voliteľný a môže byÅ¥ použitý viacerými zásuvnými modulmi.', + 'The project email must be unique across all projects' => 'Email projektu musí byÅ¥ jedineÄný vo vÅ¡etkých projektoch', + 'The email configuration has been disabled by the administrator.' => 'Nastavenie emailu bolo zakázané administrátorom.', + 'Close this project' => 'UkonÄiÅ¥ tento projekt', + 'Open this project' => 'OtvoriÅ¥ tento projekt', + 'Close a project' => 'UkonÄiÅ¥ projekt', + 'Do you really want to close this project: "%s"?' => 'Naozaj chcete ukonÄiÅ¥ projekt „%sâ€?', + 'Reopen a project' => 'Znova otvoriÅ¥ projekt', + 'Do you really want to reopen this project: "%s"?' => 'Naozaj chcete znova otvoriÅ¥ projekt: „%sâ€?', + 'This project is open' => 'Tento projekt je otvorený', + 'This project is closed' => 'Tento projekt je ukonÄený', + 'Unable to upload files, check the permissions of your data folder.' => 'Nemožno nahraÅ¥ súbory, skontrolujte prístupové práva zložky „dataâ€.', + 'Another category with the same name exists in this project' => 'Iná kategória s rovnakým menom existuje v toto projekte', + 'Comment sent by email successfully.' => 'Komentár úspeÅ¡ne odoslaný emailom.', + 'Sent by email to "%s" (%s)' => 'Poslané emailom „%s†(%s)', + 'Unable to read uploaded file.' => 'Nemožno ÄítaÅ¥ nahratý súbor.', + 'Database uploaded successfully.' => 'Databáza úspeÅ¡ne nahratá.', + 'Task sent by email successfully.' => 'Úloha úspeÅ¡ne odoslaná emailom.', + 'There is no category in this project.' => 'Tento projekt nemá kategórie.', + 'Send by email' => 'OdoslaÅ¥ emailom', + 'Create and send a comment by email' => 'VytvoriÅ¥ a poslaÅ¥ komentár emailom', + 'Subject' => 'Predmet', + 'Upload the database' => 'NahraÅ¥ databázu', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Môžete nahraÅ¥ predtým stiahnutú databázu SQLite (formát Gzip).', + 'Database file' => 'Súbor databázy', + 'Upload' => 'NahraÅ¥', + 'Your project must have at least one active swimlane.' => 'Projekt musí maÅ¥ aspoň jednu aktívnu cestu.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatická akcia nenájdená: „%sâ€', + '%d projects' => '%d projekty(ov)', + '%d project' => '%d projekt', + 'There is no project.' => 'Nie je tu žiadny projekt.', + 'Sort' => 'RadiÅ¥', + 'Project ID' => 'ID projektu', + 'Project name' => 'Názov projektu', + 'Public' => 'Verejný', + 'Personal' => 'Súkromný', + '%d tasks' => '%d úloh(y)', + '%d task' => '%d úloha', + 'Task ID' => 'ID úlohy', + 'Assign automatically a color when due date is expired' => 'Automaticky nastaviÅ¥ farbu, po vyprÅ¡aní termínu splnenia', + 'Total score in this column across all swimlanes' => 'Celkové skóre tohoto stĺpca vo vÅ¡etkých dráhach', + 'HRK - Kuna' => 'HRK – Chorvátska kuna', + 'ARS - Argentine Peso' => 'ARS – Argentínske peso', + 'COP - Colombian Peso' => 'COP – Kolumbijské peso', + '%d groups' => '%d skupiny(ín)', + '%d group' => '%d skupina', + 'Group ID' => 'ID skupiny', + 'External ID' => 'Externé ID', + '%d users' => '%d používatelia(ľov)', + '%d user' => '%d používateľ', + 'Hide subtasks' => 'SkryÅ¥ podúlohy', + 'Show subtasks' => 'ZobraziÅ¥ podúlohy', + 'Authentication Parameters' => 'AutentifikaÄné parametre', + 'API Access' => 'Prístup API', + 'No users found.' => 'Nenájdení žiadny používatelia.', + 'User ID' => 'ID používateľa', + 'Notifications are activated' => 'Upozornenia sú aktivovaná', + 'Notifications are disabled' => 'Upozornenia sú zakázané', + 'User disabled' => 'Používateľ zakázaný', + '%d notifications' => '%d upozornení', + '%d notification' => '%d upozornenie', + 'There is no external integration installed.' => 'Nie je nainÅ¡talovaná žiadna externá integrácia.', + 'You are not allowed to update tasks assigned to someone else.' => 'Nemáte povolené upraviÅ¥ úlohu pridelenú niekomu inému.', + 'You are not allowed to change the assignee.' => 'Nemáte dovolené zmeniÅ¥ pridelenie.', + 'Task suppression is not permitted' => 'PotlaÄenie úlohy nie je povolené', + 'Changing assignee is not permitted' => 'Zmena pridelenia nie je dovolená', + 'Update only assigned tasks is permitted' => 'Povolená je len aktualizácia pridelených úloh', + 'Only for tasks assigned to the current user' => 'Len úlohy pridelené aktuálnemu používateľovi', + 'My projects' => 'Moje projekty', + 'You are not a member of any project.' => 'Nie ste Älenom žiadneho projektu.', + 'My subtasks' => 'Moje podúlohy', + '%d subtasks' => '%d podúloh(y)', + '%d subtask' => '%d podúloha', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Presun úlohy medzi týmito stĺpcami je povolený len pre úlohy pridelené aktuálnemu používateľovi', + '[DUPLICATE]' => '[DUPLIKÃT]', + 'DKK - Danish Krona' => 'DKK – Dánska koruna', + 'Remove user from group' => 'OdstrániÅ¥ používateľa zo skupiny', + 'Assign the task to its creator' => 'PrideliÅ¥ úlohu jej autorovi', + 'This task was sent by email to "%s" with subject "%s".' => 'Táto úloha bude odoslaná emailom „%s†s predmetom „%sâ€.', + 'Predefined Email Subjects' => 'Preddefinované predmety emailu', + 'Write one subject by line.' => 'Napíšte jeden predmet na riadok.', + 'Create another link' => 'VytvoriÅ¥ Äalší odkaz', + 'BRL - Brazilian Real' => 'BRL – Brazílsky real', + 'Add a new Kanboard task' => 'PridaÅ¥ novú úlohu Kanboard', + 'Subtask not started' => 'Podúloha nezaÄatá', + 'Subtask currently in progress' => 'Podúloha v rieÅ¡ení', + 'Subtask completed' => 'Podúloha dokonÄená', + 'Subtask added successfully.' => 'Podúloha úspeÅ¡ne pridaná.', + '%d subtasks added successfully.' => '%d podúloh úspeÅ¡ne pridaných.', + 'Enter one subtask by line.' => 'Zadajte jednu podúlohu na riadok.', + 'Predefined Contents' => 'Preddefinovaný obsah', + 'Predefined contents' => 'Preddefinovaný obsah', + 'Predefined Task Description' => 'Preddefinované popisy úloh', + 'Do you really want to remove this template? "%s"' => 'Naozaj chcete odstrániÅ¥ Å¡ablónu: „%sâ€?', + 'Add predefined task description' => 'Pridaný preddefinovaný popis úlohy', + 'Predefined Task Descriptions' => 'Preddefinované popisy úloh', + 'Template created successfully.' => 'Å ablóna úspeÅ¡ne pridaná.', + 'Unable to create this template.' => 'Nemožno vytvoriÅ¥ Å¡ablónu.', + 'Template updated successfully.' => 'Å ablóna úspeÅ¡ne aktualizovaná.', + 'Unable to update this template.' => 'Nemožno aktualizovaÅ¥ Å¡ablónu.', + 'Template removed successfully.' => 'Å ablóna úspeÅ¡ne odstránená.', + 'Unable to remove this template.' => 'Nemožno odstrániÅ¥ Å¡ablónu.', + 'Template for the task description' => 'Å ablóna popisu úlohy', + 'The start date is greater than the end date' => 'Dátum zaÄiatku je až po dátume ukonÄenia', + 'Tags must be separated by a comma' => 'ZnaÄky musia byÅ¥ oddelené Äiarkou', + 'Only the task title is required' => 'Len názov úlohy je povinný', + 'Creator Username' => 'Prihl. meno autora', + 'Color Name' => 'Meno farby', + 'Column Name' => 'Meno stĺpca', + 'Swimlane Name' => 'Meno dráhy', + 'Time Estimated' => 'OÄakávaný Äas', + 'Time Spent' => 'Strávený Äas', + 'External Link' => 'Externý odkaz', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Táto vlastnosÅ¥ zapína kanály iCal, kanály RSS a verejné zobrazenie nástenky.', + 'Stop the timer of all subtasks when moving a task to another column' => 'ZastaviÅ¥ ÄasovaÄ vÅ¡etkých podúloh, keÄ je úloha presunutá do iného stĺpca', + 'Subtask Title' => 'Názov podúlohy', + 'Add a subtask and activate the timer when moving a task to another column' => 'PridaÅ¥ podúlohu a spustiÅ¥ ÄasovaÄ, keÄ je úloha presunutá do iného stĺpca', + 'days' => 'dni', + 'minutes' => 'minúty', + 'seconds' => 'sekundy', + 'Assign automatically a color when preset start date is reached' => 'Automaticky nastaviÅ¥ farbu, keÄ nastane prednastavený dátum zaÄiatku', + 'Move the task to another column once a predefined start date is reached' => 'Presunúť úlohu do iného stĺpca, keÄ nastane prednastavený dátum zaÄiatku', + 'This task is now linked to the task %s with the relation "%s"' => 'Táto úloha je teraz prepojená s úlohou %s so vzÅ¥ahom „%sâ€', + 'The link with the relation "%s" to the task %s has been removed' => 'Odkaz so vzÅ¥ahom „%s†na úlohu %s bol odstránený', + 'Custom Filter:' => 'Vlastný filter:', + 'Unable to find this group.' => 'Nemožno nájsÅ¥ túto skupinu.', + '%s moved the task #%d to the column "%s"' => '%s presunul úlohu #%d do stĺpca „%sâ€', + '%s moved the task #%d to the position %d in the column "%s"' => '%s presunul úlohu #%d na pozíciu %d v stĺpci „%sâ€', + '%s moved the task #%d to the swimlane "%s"' => '%s presunul úlohu #%d do dráhy „%sâ€', + '%sh spent' => '%s h strávené', + '%sh estimated' => '%s h oÄakávané', + 'Select All' => 'VybraÅ¥ vÅ¡etky', + 'Unselect All' => 'ZruÅ¡iÅ¥ výber', + 'Apply action' => 'PoužiÅ¥ akciu', + 'Move selected tasks to another column or swimlane' => 'Presunúť zvolené do iného stĺpca', + 'Edit tasks in bulk' => 'Hromadná úprava úloh', + 'Choose the properties that you would like to change for the selected tasks.' => 'Zvoľte vlastnosti, ktoré chcete zmeniÅ¥ vo vÅ¡etkých zvolených úlohách.', + 'Configure this project' => 'NastaviÅ¥ projekt', + 'Start now' => 'ZaÄaÅ¥ teraz', + '%s removed a file from the task #%d' => '%s odstránil súbor z úlohy #%d', + 'Attachment removed from task #%d: %s' => 'Príloha odstránená z úlohy #%d: %s', + 'No color' => 'Bez farby', + 'Attachment removed "%s"' => 'Príloha odstránená „%sâ€', + '%s removed a file from the task %s' => '%s odstránil súbor z úlohy %s', + 'Move the task to another swimlane when assigned to a user' => 'Presunúť úlohu do inej dráhy, keÄ je pridelená používateľovi', + 'Destination swimlane' => 'Cieľová dráha', + 'Assign a category when the task is moved to a specific swimlane' => 'NastaviÅ¥ kategóriu, keÄ je úloha presunutá do zadanej dráhy', + 'Move the task to another swimlane when the category is changed' => 'Presunúť úlohu do inej dráhy, keÄ je zmenená kategória', + 'Reorder this column by priority (ASC)' => 'ZoradiÅ¥ stĺpec podľa priority (VZOST)', + 'Reorder this column by priority (DESC)' => 'ZoradiÅ¥ stĺpec podľa priority (ZOST)', + 'Reorder this column by assignee and priority (ASC)' => 'ZoradiÅ¥ stĺpec podľa pridelenia a priority (VZOST)', + 'Reorder this column by assignee and priority (DESC)' => 'ZoradiÅ¥ stĺpec podľa pridelenia a priority (ZOST)', + 'Reorder this column by assignee (A-Z)' => 'ZoradiÅ¥ stĺpec podľa pridelenia (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'ZoradiÅ¥ stĺpec podľa pridelenia (Z-A)', + 'Reorder this column by due date (ASC)' => 'ZoradiÅ¥ stĺpec podľa termínu ukonÄenia (VZOST)', + 'Reorder this column by due date (DESC)' => 'ZoradiÅ¥ stĺpec podľa termínu ukonÄenia (ZOST)', + 'Reorder this column by id (ASC)' => 'ZoradiÅ¥ tento stĺpec podľa ID (vzostupne)', + 'Reorder this column by id (DESC)' => 'ZoradiÅ¥ tento stĺpec podľa ID (zostupne)', + '%s moved the task #%d "%s" to the project "%s"' => '%s presunul/a úlohu Ä. %d „%s†do projektu „%sâ€', + 'Task #%d "%s" has been moved to the project "%s"' => 'Úloha Ä. %d „%s†bola presunutá do projektu „%sâ€', + 'Move the task to another column when the due date is less than a certain number of days' => 'Presunúť úlohu do iného stĺpca, keÄ je do termínu ukonÄenia menej ako zadaný poÄet dní', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automaticky aktualizovaÅ¥ dátum zaÄiatku, keÄ je úloha presunutá zo zadaného stĺpca', + 'HTTP Client:' => 'Klient HTTP', + 'Assigned' => 'Priadené', + 'Task limits apply to each swimlane individually' => 'Limit úloh použiÅ¥ na každú dráhu samostatne', + 'Column task limits apply to each swimlane individually' => 'Limit úloh stĺpca použiÅ¥ na každú dráhu samostatne', + 'Column task limits are applied to each swimlane individually' => 'Limit úloh stĺpca je použitý na každú dráhu samostatne', + 'Column task limits are applied across swimlanes' => 'Limit úloh stĺpca je použitý na vÅ¡etky dráhy spolu', + 'Task limit: ' => 'Limit úloh', + 'Change to global tag' => 'ZmeniÅ¥ globálnu znaÄku', + 'Do you really want to make the tag "%s" global?' => 'Naozaj má byÅ¥ znaÄka „%s†globálna?', + 'Enable global tags for this project' => 'Zapnúť globálne znaÄky v tomto projekte', + 'Group membership(s):' => 'ÄŒlenstvo v skupine(ách)', + '%s is a member of the following group(s): %s' => '%s je Älenom tejto skupiny(n): %s', + '%d/%d group(s) shown' => 'zobrazených %d/%d skupíny(ín)', + 'Subtask creation or modification' => 'Vytvorenie alebo úprava podúlohy', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'PrideliÅ¥ úlohu zadanému používateľovi, keÄ je úloha presunutá do zadanej dráhy', + 'Comment' => 'Komentár', + 'Collapse vertically' => 'ZbaliÅ¥ vertikálne', + 'Expand vertically' => 'RozbaliÅ¥ vertikálne', + 'MXN - Mexican Peso' => 'Mexické peso', + 'Estimated vs actual time per column' => 'OÄakávaný a aktuálny Äas na stĺpec', + 'HUF - Hungarian Forint' => 'HUF – MaÄarský forint', + 'XBT - Bitcoin' => 'XBT – Bitcoin', + 'You must select a file to upload as your avatar!' => 'Musíte zvoliÅ¥ súbor na nahratie ako svoj avatar', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'nahraný súbor nie je paltný obrázok! (Povolené sú len *.gif, *.jpg, *.jpeg a *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Automaticky nastaviÅ¥ termín úlohy po presunutí z urÄitého stĺpca', + 'No other projects found.' => 'NenaÅ¡li sa žiadne ÄalÅ¡ie projekty.', + 'Tasks copied successfully.' => 'Úlohy boli úspeÅ¡ne skopírované.', + 'Unable to copy tasks.' => 'Úlohy sa nepodarilo skopírovaÅ¥.', + 'Theme' => 'Téma', + 'Theme:' => 'Téma:', + 'Light theme' => 'Svetlá téma', + 'Dark theme' => 'Tmavá téma', + 'Automatic theme - Sync with system' => 'Automatická téma – synchronizácia so systémom', + 'Application managers or more' => 'Správcovia aplikácie alebo vyššie', + 'Administrators' => 'Administrátori', + 'Visibility:' => 'ViditeľnosÅ¥:', + 'Standard users' => 'Å tandardní používatelia', + 'Visibility is required' => 'ViditeľnosÅ¥ je povinná', + 'The visibility should be an app role' => 'ViditeľnosÅ¥ musí byÅ¥ rolou v aplikácii', + 'Reply' => 'OdpovedaÅ¥', + '%s wrote: ' => '%s napísal(a): ', + 'Number of visible tasks in this column and swimlane' => 'PoÄet viditeľných úloh v tomto stĺpci a plaveckom pruhu', + 'Number of tasks in this swimlane' => 'PoÄet úloh v tomto plaveckom pruhu', + 'Unable to find another subtask in progress, you can close this window.' => 'Nepodarilo sa nájsÅ¥ ÄalÅ¡iu podúlohu v priebehu, môžete zavrieÅ¥ toto okno.', + 'This theme is invalid' => 'Táto téma je neplatná', + 'This role is invalid' => 'Táto rola je neplatná', + 'This timezone is invalid' => 'Toto Äasové pásmo je neplatné', + 'This language is invalid' => 'Tento jazyk je neplatný', + 'This URL is invalid' => 'Tento URL je neplatný', + 'Date format invalid' => 'Neplatný formát dátumu', + 'Time format invalid' => 'Neplatný formát Äasu', + 'Invalid Mail transport' => 'Neplatný spôsob odosielania e-mailov', + 'Color invalid' => 'Neplatná farba', + 'This value must be greater or equal to %d' => 'Táto hodnota musí byÅ¥ väÄÅ¡ia alebo rovná %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'PridaÅ¥ BOM na zaÄiatok súboru (vyžaduje Microsoft Excel)', + 'Just add these tag(s)' => 'Len pridajte tieto znaÄky', + 'Remove internal link(s)' => 'OdstrániÅ¥ interné odkazy', + 'Import tasks from another project' => 'ImportovaÅ¥ úlohy z iného projektu', + 'Select the project to copy tasks from' => 'Vyberte projekt, z ktorého chcete kopírovaÅ¥ úlohy', + 'The total maximum allowed attachments size is %sB.' => 'Celková maximálna povolená veľkosÅ¥ príloh je %sB.', + 'Add attachments' => 'PridaÅ¥ prílohy', + 'Task #%d "%s" is overdue' => 'Úloha #%d „%s†je po termíne', + 'Enable notifications by default for all new users' => 'Predvolene zapnúť upozornenia pre vÅ¡etkých nových používateľov', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'PriradiÅ¥ úlohu jej tvorcovi pre vybrané stĺpce, ak nie je ruÄne nastavený rieÅ¡iteľ', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'PriradiÅ¥ úlohu prihlásenému používateľovi pri presune do zadaného stĺpca, ak nie je priradený žiadny používateľ', +]; diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php new file mode 100644 index 0000000..4832759 --- /dev/null +++ b/app/Locale/sr_Latn_RS/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => '.', + 'None' => 'Nijedan', + 'Edit' => 'Izmeni', + 'Remove' => 'Ukloni', + 'Yes' => 'Da', + 'No' => 'Ne', + 'cancel' => 'odustani', + 'or' => 'ili', + 'Yellow' => 'Žuta', + 'Blue' => 'Plava', + 'Green' => 'Zelena', + 'Purple' => 'LjubiÄasta', + 'Red' => 'Crvena', + 'Orange' => 'Narandžasta', + 'Grey' => 'Siva', + 'Brown' => 'Braon', + 'Deep Orange' => 'Tamno naradžasta', + 'Dark Grey' => 'Tamno siva', + 'Pink' => 'Roze', + 'Teal' => 'Tirkizna', + 'Cyan' => 'Cijan', + 'Lime' => 'Limeta', + 'Light Green' => 'Svetlo zelena', + 'Amber' => 'Ćilibar', + 'Save' => 'Snimi', + 'Login' => 'Prijava', + 'Official website:' => 'ZvaniÄna strana:', + 'Unassigned' => 'Nedodeljen', + 'View this task' => 'Pregledaj zadatak', + 'Remove user' => 'Ukloni korisnika', + 'Do you really want to remove this user: "%s"?' => 'Da li zaista želite da uklonite korisnika: "%s"?', + 'All users' => 'Svi korisnici', + 'Username' => 'Korisnik', + 'Password' => 'Lozinka', + 'Administrator' => 'Administrator', + 'Sign in' => 'Prijava', + 'Users' => 'Korisnici', + 'Forbidden' => 'Zabranjeno', + 'Access Forbidden' => 'Zabranjen pristup', + 'Edit user' => 'Izmeni korisnika', + 'Logout' => 'Odjava', + 'Bad username or password' => 'LoÅ¡e korisniÄko ime ili lozinka', + 'Edit project' => 'Izmeni projekat', + 'Name' => 'Naziv', + 'Projects' => 'Projekti', + 'No project' => 'Bez projekta', + 'Project' => 'Projekat', + 'Status' => 'Status', + 'Tasks' => 'Zadatak', + 'Board' => 'Tabla', + 'Actions' => 'Akcije', + 'Inactive' => 'Neaktivno', + 'Active' => 'Aktivno', + 'Unable to update this board.' => 'Ažuriranje ove table nije uspelo', + 'Disable' => 'Onemogući', + 'Enable' => 'Omogući', + 'New project' => 'Novi projekat', + 'Do you really want to remove this project: "%s"?' => 'Da li zaista želite da uklonite projekat: "%s"?', + 'Remove project' => 'Ukloni projekat', + 'Edit the board for "%s"' => 'Izmeni tablu za "%s"', + 'Add a new column' => 'Dodaj novu kolonu', + 'Title' => 'Naslov', + 'Assigned to %s' => 'Dodeljen korisniku %s', + 'Remove a column' => 'Ukloni kolonu', + 'Unable to remove this column.' => 'Nije moguće ukloniti ovu kolonu.', + 'Do you really want to remove this column: "%s"?' => 'Da li zaista želite da uklonite ovu kolonu: "%s"?', + 'Settings' => 'PodeÅ¡avanja', + 'Application settings' => 'PodeÅ¡avanja aplikacije', + 'Language' => 'Jezik', + 'Webhook token:' => 'Webhook token :', + 'API token:' => 'API token', + 'Database size:' => 'VeliÄina baze :', + 'Download the database' => 'Preuzmi bazu', + 'Optimize the database' => 'Optimizuj bazu', + '(VACUUM command)' => '(VACUUM komanda)', + '(Gzip compressed Sqlite file)' => '(Sqlite baza spakovana Gzip-om)', + 'Close a task' => 'Zatvori zadatak', + 'Column' => 'Kolona', + 'Color' => 'Boja', + 'Assignee' => 'IzvrÅ¡ilac', + 'Create another task' => 'Dodaj zadatak', + 'New task' => 'Novi zadatak', + 'Open a task' => 'Otvori zadatak', + 'Do you really want to open this task: "%s"?' => 'Da li zaista želite da otvorite ovaj zadatak: "%s"?', + 'Back to the board' => 'Nazad na tablu', + 'There is nobody assigned' => 'Nikome nije dodeljeno', + 'Column on the board:' => 'Kolona na tabli:', + 'Close this task' => 'Zatvori ovaj zadatak', + 'Open this task' => 'Otvori ovaj zadatak', + 'There is no description.' => 'Bez opisa.', + 'Add a new task' => 'Dodaj zadatak', + 'The username is required' => 'KorisniÄko ime je obavezno', + 'The maximum length is %d characters' => 'Maksimalna dužina je %d znakova', + 'The minimum length is %d characters' => 'Minimalna dužina je %d znakova', + 'The password is required' => 'Lozinka je obavezna', + 'This value must be an integer' => 'Mora biti celobrojna vrednost', + 'The username must be unique' => 'KorisniÄko ime mora biti jedinstveno', + 'The user id is required' => 'ID korisnika je obavezan', + 'Passwords don\'t match' => 'Lozinke se ne podudaraju', + 'The confirmation is required' => 'Potvrda je obavezna', + 'The project is required' => 'Projekat je obavezan', + 'The id is required' => 'ID je obavezan', + 'The project id is required' => 'ID projekta je obavezan', + 'The project name is required' => 'Naziv projekta je obavezan', + 'The title is required' => 'Naslov je obavezan', + 'Settings saved successfully.' => 'PodeÅ¡avanja uspeÅ¡no snimljena.', + 'Unable to save your settings.' => 'Nije mogućue saÄuvati podeÅ¡avanja.', + 'Database optimization done.' => 'Optimizacija baze je zavrÅ¡ena.', + 'Your project has been created successfully.' => 'Projekat je uspeÅ¡no napravljen.', + 'Unable to create your project.' => 'Nije mogućue kreirati projekat.', + 'Project updated successfully.' => 'Projekat je uspeÅ¡no ažuriran.', + 'Unable to update this project.' => 'Nije mogućue ažurirati ovaj projekat.', + 'Unable to remove this project.' => 'Nije mogućue ukloniti ovaj projekat.', + 'Project removed successfully.' => 'Projekat uspeÅ¡no uklonjen.', + 'Project activated successfully.' => 'Projekat uspeÅ¡no aktiviran.', + 'Unable to activate this project.' => 'Nije mogućue aktivirti ovaj projekat.', + 'Project disabled successfully.' => 'Projekat uspeÅ¡no deaktiviran.', + 'Unable to disable this project.' => 'Nije moguće onemogućiti ovaj projekat.', + 'Unable to open this task.' => 'Nije moguće otvoriti ovaj zadatak', + 'Task opened successfully.' => 'Zadatak uspeÅ¡no otvoren.', + 'Unable to close this task.' => 'Nije moguće zatvoriti ovaj zadatak.', + 'Task closed successfully.' => 'Zadatak uspeÅ¡no zatvoren.', + 'Unable to update your task.' => 'Nije moguće ažuriranje zadatka.', + 'Task updated successfully.' => 'Zadatak uspeÅ¡no ažuriran.', + 'Unable to create your task.' => 'Nije moguće kreiranje zadatka.', + 'Task created successfully.' => 'Zadatak uspeÅ¡no kreiran.', + 'User created successfully.' => 'Korisnik uspeÅ¡no kreiran', + 'Unable to create your user.' => 'Nije uspelo kreiranje korisnika.', + 'User updated successfully.' => 'Korisnik uspeÅ¡no ažuriran.', + 'User removed successfully.' => 'Korisnik uspeÅ¡no uklonjen.', + 'Unable to remove this user.' => 'Nije moguće uklanjanje korisnika.', + 'Board updated successfully.' => 'Tabla uspeÅ¡no ažurirana.', + 'Ready' => 'Spreman', + 'Backlog' => 'Log', + 'Work in progress' => 'U radu', + 'Done' => 'Gotovo', + 'Application version:' => 'Verzija aplikacije:', + 'Id' => 'Id', + 'Public link' => 'Javni link', + 'Timezone' => 'Vremenska zona', + 'Sorry, I didn\'t find this information in my database!' => 'Na žalost, nije pronaÄ‘ena informacija u bazi', + 'Page not found' => 'Strana nije pronaÄ‘ena', + 'Complexity' => 'Složenost', + 'Task limit' => 'OgraniÄenje zadatka', + 'Task count' => 'Broj zadataka', + 'User' => 'Korisnik', + 'Comments' => 'Komentari', + 'Comment is required' => 'Komentar je obavezan', + 'Comment added successfully.' => 'Komentar uspeÅ¡no ostavljen', + 'Unable to create your comment.' => 'Nemoguće kreiranje komentara', + 'Due Date' => 'Rok zavrÅ¡etka', + 'Invalid date' => 'LoÅ¡ datum', + 'Automatic actions' => 'Automatske akcije', + 'Your automatic action has been created successfully.' => 'UspeÅ¡no kreirana automatska akcija', + 'Unable to create your automatic action.' => 'Nemoguće kreiranje automatske akcije', + 'Remove an action' => 'ObriÅ¡i akciju', + 'Unable to remove this action.' => 'Nije moguće obrisati akciju', + 'Action removed successfully.' => 'Akcija obrisana', + 'Automatic actions for the project "%s"' => 'Akcije za automatizaciju projekta "%s"', + 'Add an action' => 'dodaj akciju', + 'Event name' => 'Naziv dogaÄ‘aja', + 'Action' => 'Akcija', + 'Event' => 'DogaÄ‘aj', + 'When the selected event occurs execute the corresponding action.' => 'Kad se dogaÄ‘aj desi izvrÅ¡i odgovarajuću akciju', + 'Next step' => 'Sledeći korak', + 'Define action parameters' => 'DefiniÅ¡i parametre akcije', + 'Do you really want to remove this action: "%s"?' => 'Da li da obriÅ¡em akciju "%s"?', + 'Remove an automatic action' => 'ObriÅ¡i automatsku akciju', + 'Assign the task to a specific user' => 'Dodeli zadatak odreÄ‘enom korisniku', + 'Assign the task to the person who does the action' => 'Dodeli zadatak korisniku koji je izvrÅ¡io akciju', + 'Duplicate the task to another project' => 'Kopiraj akciju u drugi projekat', + 'Move a task to another column' => 'Premesti zadatak u drugu kolonu', + 'Task modification' => 'Izmena zadatka', + 'Task creation' => 'Kreiranje zadatka', + 'Closing a task' => 'Zatvaranja zadatka', + 'Assign a color to a specific user' => 'Dodeli boju korisniku', + 'Position' => 'Pozicija', + 'Duplicate to project' => 'Kopiraj u drugi projekat', + 'Duplicate' => 'Napravi kopiju', + 'Link' => 'Link', + 'Comment updated successfully.' => 'Komentar uspeÅ¡no ažuriran.', + 'Unable to update your comment.' => 'NeuspeÅ¡no ažuriranje komentara.', + 'Remove a comment' => 'ObriÅ¡i komentar', + 'Comment removed successfully.' => 'Komentar je uspeÅ¡no obrisan.', + 'Unable to remove this comment.' => 'NeuspeÅ¡no brisanje komentara.', + 'Do you really want to remove this comment?' => 'Da li da obriÅ¡em ovaj komentar?', + 'Current password for the user "%s"' => 'Trenutna lozinka za korisnika "%s"', + 'The current password is required' => 'Trenutna lozinka je obavezna', + 'Wrong password' => 'PogreÅ¡na lozinka', + 'Unknown' => 'Nepoznat', + 'Last logins' => 'Poslednja prijava', + 'Login date' => 'Datum prijave', + 'Authentication method' => 'Metod autentifikacije', + 'IP address' => 'IP adresa', + 'User agent' => 'PregledaÄ', + 'Persistent connections' => 'Stalna konekcija', + 'No session.' => 'Bez sesije', + 'Expiration date' => 'IstiÄe', + 'Remember Me' => 'Zapamti me', + 'Creation date' => 'Datum kreiranja', + 'Everybody' => 'Svi', + 'Open' => 'Otvoreni', + 'Closed' => 'Zatvoreni', + 'Search' => 'Traži', + 'Nothing found.' => 'NiÅ¡ta nije pronaÄ‘eno', + 'Due date' => 'Rok zavrÅ¡etka', + 'Description' => 'Opis', + '%d comments' => '%d komentara', + '%d comment' => '%d komentar', + 'Email address invalid' => 'Email adresa nije validna', + 'Your external account is not linked anymore to your profile.' => 'VaÅ¡ spoljni nalog viÅ¡e nije povezan sa vaÅ¡im profilom.', + 'Unable to unlink your external account.' => 'Nije moguće prekinuti vezu sa spoljnim nalogom.', + 'External authentication failed' => 'Spoljna autentifikacija nije uspela', + 'Your external account is linked to your profile successfully.' => 'VaÅ¡ spoljni nalog je uspeÅ¡no povezan sa vaÅ¡im profilom.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Zadatak uspeÅ¡no uklonjen.', + 'Unable to remove this task.' => 'Nije moguće ukoliniti zadatak.', + 'Remove a task' => 'Ukloni zadatak', + 'Do you really want to remove this task: "%s"?' => 'Da li zaista želite da uklonite ovaj zadatak "%s"?', + 'Assign automatically a color based on a category' => 'Automatski dodeli boju po kategoriji', + 'Assign automatically a category based on a color' => 'Automatski dodeli kategoriju po boji', + 'Task creation or modification' => 'Kreiranje ili izmena zadatka', + 'Category' => 'Kategorija', + 'Category:' => 'Kategorija:', + 'Categories' => 'Kategorije', + 'Your category has been created successfully.' => 'UspeÅ¡no kreirana kategorija.', + 'This category has been updated successfully.' => 'Kategorija je uspeÅ¡no izmenjena', + 'Unable to update this category.' => 'Nije moguće izmeniti kategoriju', + 'Remove a category' => 'ObriÅ¡i kategoriju', + 'Category removed successfully.' => 'Kategorija uspeÅ¡no uklonjena.', + 'Unable to remove this category.' => 'Nije moguće ukloniti kategoriju.', + 'Category modification for the project "%s"' => 'Izmena kategorije za projekat "%s"', + 'Category Name' => 'Naziv kategorije', + 'Add a new category' => 'Dodaj novu kategoriju', + 'Do you really want to remove this category: "%s"?' => 'Da li zaista želiÅ¡ da ukloniÅ¡ kategoriju: "%s"?', + 'All categories' => 'Sve kategorije', + 'No category' => 'Bez kategorije', + 'The name is required' => 'Naziv je obavezan', + 'Remove a file' => 'Ukloni fajl', + 'Unable to remove this file.' => 'Fajl nije moguće ukloniti.', + 'File removed successfully.' => 'UspeÅ¡no uklonjen fajl.', + 'Attach a document' => 'Priloži dokument', + 'Do you really want to remove this file: "%s"?' => 'Da li zaista želite da ukolnite fajl: "%s"?', + 'Attachments' => 'Prilozi', + 'Edit the task' => 'Izmena zadatka', + 'Add a comment' => 'Dodaj komentar', + 'Edit a comment' => 'Izmeni komentar', + 'Summary' => 'Pregled', + 'Time tracking' => 'Praćenje vremena', + 'Estimate:' => 'Procena:', + 'Spent:' => 'PotroÅ¡eno:', + 'Do you really want to remove this sub-task?' => 'Da li zaista želite da uklonite podzadatak?', + 'Remaining:' => 'Preostalo:', + 'hours' => 'sati', + 'estimated' => 'procenjeno', + 'Sub-Tasks' => 'Podzadaci', + 'Add a sub-task' => 'Dodaj podzadatak', + 'Original estimate' => 'Originalna procena', + 'Create another sub-task' => 'Dodaj novi podzadatak', + 'Time spent' => 'UtroÅ¡eno vreme', + 'Edit a sub-task' => 'Izmeni podzadatak', + 'Remove a sub-task' => 'Ukloni podzadatak', + 'The time must be a numeric value' => 'Vreme mora biti numeriÄka vrednost', + 'Todo' => 'Uraditi', + 'In progress' => 'U radu', + 'Sub-task removed successfully.' => 'Podzadatak uspeÅ¡no uklonjen.', + 'Unable to remove this sub-task.' => 'Nie moguće ukloniti ovaj podzadatak', + 'Sub-task updated successfully.' => 'Podzadatak uspeÅ¡no ažuriran', + 'Unable to update your sub-task.' => 'Ažuriranje podzadatka nije uspelo.', + 'Unable to create your sub-task.' => 'Nije moguće kreirati podzadatak.', + 'Maximum size: ' => 'Maksimalna veliÄina: ', + 'Display another project' => 'Prikaži drugi projekat', + 'Created by %s' => 'Napravio/la %s', + 'Tasks Export' => 'Izvoz zadataka', + 'Start Date' => 'PoÄetni datum', + 'Execute' => 'IzvrÅ¡i', + 'Task Id' => 'Identifikator zadatka', + 'Creator' => 'Autor', + 'Modification date' => 'Datum izmene', + 'Completion date' => 'Datum kompletiranja', + 'Clone' => 'Kloniraj', + 'Project cloned successfully.' => 'Projekat uspeÅ¡no kloniran.', + 'Unable to clone this project.' => 'Nije moguće klonirati projekat.', + 'Enable email notifications' => 'Omogući obaveÅ¡tenja email-om', + 'Task position:' => 'Pozicija zadatka:', + 'The task #%d has been opened.' => 'Zadatak #%d je otvoren.', + 'The task #%d has been closed.' => 'Zadatak #%d je zatvoren.', + 'Sub-task updated' => 'Podzadatak izmenjen', + 'Title:' => 'Naslov:', + 'Status:' => 'Status', + 'Assignee:' => 'IzvrÅ¡ilac:', + 'Time tracking:' => 'Praćenje vremena: ', + 'New sub-task' => 'Novi podzadatak', + 'New attachment added "%s"' => 'Novi prilog ubaÄen "%s"', + 'New comment posted by %s' => 'Novi komentar ostavio %s', + 'New comment' => 'Novi komentar', + 'Comment updated' => 'Komentar ažuriran', + 'New subtask' => 'Novi podzadatak', + 'I only want to receive notifications for these projects:' => 'Želim obaveÅ¡tenja samo za projekate:', + 'view the task on Kanboard' => 'pregled zadataka u Kanboard-u', + 'Public access' => 'Javni pristup', + 'Disable public access' => 'Zabrani javni pristup', + 'Enable public access' => 'Dozvoli javni pristup', + 'Public access disabled' => 'Javni pristup onemogućen!', + 'Move the task to another project' => 'Premesti zadatak u drugi projekat', + 'Move to project' => 'Premesti u drugi projekat', + 'Do you really want to duplicate this task?' => 'Da li zaista želite da kopirate ovaj zadatak?', + 'Duplicate a task' => 'Kopiraj zadatak', + 'External accounts' => 'Spoljni nalozi', + 'Account type' => 'Tip naloga', + 'Local' => 'Lokalno', + 'Remote' => 'Udaljno', + 'Enabled' => 'Omogućeno', + 'Disabled' => 'Onemogućeno', + 'Login:' => 'Prijava:', + 'Full Name:' => 'Ime i prezime', + 'Email:' => 'Email: ', + 'Notifications:' => 'ObaveÅ¡tenja: ', + 'Notifications' => 'ObaveÅ¡tenja', + 'Account type:' => 'Vrsta naloga:', + 'Edit profile' => 'Izmeni profil', + 'Change password' => 'Izmeni lozinku', + 'Password modification' => 'Izmena lozinke', + 'External authentications' => 'Spoljne akcije', + 'Never connected.' => 'Nikad povezan.', + 'No external authentication enabled.' => 'Nema omogućenih spoljnih autentifikacija.', + 'Password modified successfully.' => 'UspeÅ¡na izmena lozinke.', + 'Unable to change the password.' => 'Nije moguće izmeniti lozinku.', + 'Change category' => 'Izmeni kategoriju', + '%s updated the task %s' => '%s je ažurirao/la zadatak %s', + '%s opened the task %s' => '%s je otvorio/la zadatak %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s je premestio/la zadatak %s na poziciju #%d u koloni "%s"', + '%s moved the task %s to the column "%s"' => '%s je premestio/la zadatak %s u kolonu "%s"', + '%s created the task %s' => '%s je kreirao/la zadatak %s', + '%s closed the task %s' => '%s je zatvorio/la zadatak %s', + '%s created a subtask for the task %s' => '%s je kreirao/la podzadatak za zadatka %s', + '%s updated a subtask for the task %s' => '%s je izmenio/la podzadatak za zadatka %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Dodeljen korisniku %s uz procenu vremena %s/%sh', + 'Not assigned, estimate of %sh' => 'Nedodeljen, procenjeno vreme %sh', + '%s updated a comment on the task %s' => '%s je ažurirao/la komentar za zadatka %s', + '%s commented the task %s' => '%s je komentarisao/la zadatak %s', + '%s\'s activity' => '%s - aktivnosti', + 'RSS feed' => 'RSS kanal', + '%s updated a comment on the task #%d' => '%s ažurirao/la komentar za zadatka #%d', + '%s commented on the task #%d' => '%s komentarisao/la zadatak #%d', + '%s updated a subtask for the task #%d' => '%s ažurirao/la podzadatak za zadatka #%d', + '%s created a subtask for the task #%d' => '%s kreirao/la podzadatak za zadatka #%d', + '%s updated the task #%d' => '%s ažurirao/la zadatak #%d', + '%s created the task #%d' => '%s kreirao/la zadatak #%d', + '%s closed the task #%d' => '%s zatvorio/la zadatak #%d', + '%s opened the task #%d' => '%s otvorio/la zadatak #%d', + 'Activity' => 'Aktivnosti', + 'Default values are "%s"' => 'Podrazumevane vrednosti su: "%s"', + 'Default columns for new projects (Comma-separated)' => 'Podrazumevane kolone za novi projekat (odvojeno zarezom)', + 'Task assignee change' => 'Promena izvrÅ¡ioca zadatka', + '%s changed the assignee of the task #%d to %s' => '%s je promenio/la izvrÅ¡ioca zadataka #%d na %s', + '%s changed the assignee of the task %s to %s' => '%s je promenio/la izvrÅ¡ioca zadatak %s na %s', + 'New password for the user "%s"' => 'Nova lozinka za korisnika "%s"', + 'Choose an event' => 'Izaberi dogaÄ‘aj', + 'Create a task from an external provider' => 'Napravi zadatak preko posrednika', + 'Change the assignee based on an external username' => 'Promeni izvrÅ¡ioca bazirano na spoljnom korisniÄkom imenu', + 'Change the category based on an external label' => 'Promeni kategoriju bazirano na spoljnoj etiketi', + 'Reference' => 'Referenca', + 'Label' => 'Etikieta', + 'Database' => 'Baza', + 'About' => 'Informacje', + 'Database driver:' => 'Drajver baze podataka:', + 'Board settings' => 'PodeÅ¡avanje table', + 'Webhook settings' => 'Webhook podeÅ¡avanja', + 'Reset token' => 'Resetuj token', + 'API endpoint:' => 'Krajnja taÄka API-a:', + 'Refresh interval for personal board' => 'Interval osvežavanja liÄnih tabli', + 'Refresh interval for public board' => 'Interval osvežavanja javnih tabli', + 'Task highlight period' => 'Period naznaÄavanja zadatka', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Period (u sekundama) za koji se smatra da je bilo izmena na zadatku (0 je onemogućeno, 2 dana je podrazumevano)', + 'Frequency in second (60 seconds by default)' => 'UÄestalost u sekundama (60 je podrazumevano)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'UÄestalost u sekundama (0 gasi ovu funkecionalnost, 10 je podrazumevano)', + 'Application URL' => 'URL adresa aplikacije', + 'Token regenerated.' => 'Token regenerisan', + 'Date format' => 'Format datuma', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO format je uvek prihvatljiv, primer: "%s", "%s"', + 'New personal project' => 'Novi liÄni projekat', + 'This project is personal' => 'Ovaj projekat je liÄan', + 'Add' => 'Dodaj', + 'Start date' => 'Datum poÄetka', + 'Time estimated' => 'Procenjeno vreme', + 'There is nothing assigned to you.' => 'NiÅ¡ta Vam nije dodeljeno', + 'My tasks' => 'Moji zadaci', + 'Activity stream' => 'Spisak aktinosti', + 'Dashboard' => 'Komandna tabla', + 'Confirmation' => 'Potvrda', + 'Webhooks' => 'Webhook-ovi', + 'API' => 'API', + 'Create a comment from an external provider' => 'Napravi komentar putem spoljnog posrednika', + 'Project management' => 'UreÄ‘ivanje projekata', + 'Columns' => 'Kolone', + 'Task' => 'Zadatak', + 'Percentage' => 'Procenat', + 'Number of tasks' => 'Broj zadataka', + 'Task distribution' => 'Podela zadataka', + 'Analytics' => 'Analiza', + 'Subtask' => 'Podzadatak', + 'User repartition' => 'Zaduženja korisnika', + 'Clone this project' => 'Kloniraj projekat', + 'Column removed successfully.' => 'Kolona usepÅ¡no uklonjena.', + 'Not enough data to show the graph.' => 'Nedovoljno podataka za grafikon.', + 'Previous' => 'Prethodni', + 'The id must be an integer' => 'ID mora biti celobrojna vrednost', + 'The project id must be an integer' => 'ID projekta mora biti celobrojna vrednost', + 'The status must be an integer' => 'Status mora biti celobrojna vrednost', + 'The subtask id is required' => 'ID podzadataka je obavezan', + 'The subtask id must be an integer' => 'ID podzadatka mora biti celobrojna vrednost', + 'The task id is required' => 'ID zadatka je obavezan', + 'The task id must be an integer' => 'ID zadatka mora biti celobrojna vrednost', + 'The user id must be an integer' => 'ID korisnika mora biti celobrojna vrednost', + 'This value is required' => 'Ova vrednost je obavezna', + 'This value must be numeric' => 'Ova vrednost mora biti broj', + 'Unable to create this task.' => 'Nije moguće kreirati ovaj zadatak.', + 'Cumulative flow diagram' => 'Kumulativni dijagram toka', + 'Daily project summary' => 'Zbirni pregled po danima', + 'Daily project summary export' => 'Izvoz zbirnog pregleda po danima', + 'Exports' => 'Izvozi', + 'This export contains the number of tasks per column grouped per day.' => 'Ovaj izvoz sadrži broj zadataka po koloni grupisano po danima.', + 'Active swimlanes' => 'Aktivna staza', + 'Add a new swimlane' => 'Dodaj novu stazu', + 'Default swimlane' => 'Podrazumevana staza', + 'Do you really want to remove this swimlane: "%s"?' => 'Da li zasita želite da da uklonite stazu: "%s"?', + 'Inactive swimlanes' => 'Neaktivne staze', + 'Remove a swimlane' => 'Ukloni stazu', + 'Swimlane modification for the project "%s"' => 'Izmena staze za projekat "%s"', + 'Swimlane removed successfully.' => 'Staza uspeÅ¡no uklonjen.', + 'Swimlanes' => 'Staze', + 'Swimlane updated successfully.' => 'Staza uspeÅ¡no ažuriran.', + 'Unable to remove this swimlane.' => 'Nije moguće ukloniti ovu stazu', + 'Unable to update this swimlane.' => 'Nije moguće ažuriranje ove staze', + 'Your swimlane has been created successfully.' => 'VaÅ¡a staza je uspeÅ¡no napravljena.', + 'Example: "Bug, Feature Request, Improvement"' => 'Na primer: "GreÅ¡ka, Zahtev za funkcionalnost, PoboljÅ¡anje"', + 'Default categories for new projects (Comma-separated)' => 'Podrazumevane kategorije za nove projekate', + 'Integrations' => 'Integracje', + 'Integration with third-party services' => 'Integracja sa uslugama spoljnih servisa', + 'Subtask Id' => 'ID podzadatka', + 'Subtasks' => 'Podzadaci', + 'Subtasks Export' => 'Izvoz podzdataka', + 'Task Title' => 'Naslov zadatka', + 'Untitled' => 'Bez naslova', + 'Application default' => 'Aplikacijski podrazumevano', + 'Language:' => 'Jezik:', + 'Timezone:' => 'Vremenska zona:', + 'All columns' => 'Sve kolone', + 'Next' => 'Sledeće', + '#%d' => '#%d', + 'All swimlanes' => 'Sve staze', + 'All colors' => 'Sve boje', + 'Moved to column %s' => 'PremeÅ¡ten u kolonu %s', + 'User dashboard' => 'KorisniÄka komandna tabla', + 'Allow only one subtask in progress at the same time for a user' => 'Dozvoli samo jedan podzadatak "u radu" po korisniku', + 'Edit column "%s"' => 'Uredi kolonu "%s"', + 'Select the new status of the subtask: "%s"' => 'Izaberi novi status za podzadatak: "%s"', + 'Subtask timesheet' => 'Tabela vremena za podzadatke', + 'There is nothing to show.' => 'Nema podataka', + 'Time Tracking' => 'Praćenje vremena', + 'You already have one subtask in progress' => 'Već imate jedan podzadatak "u radu"', + 'Which parts of the project do you want to duplicate?' => 'Koje delove projekta želite da duplirate', + 'Disallow login form' => 'Zabrani prijavnu formu', + 'Start' => 'PoÄetak', + 'End' => 'Kraj', + 'Task age in days' => 'Starost zadatka u danima', + 'Days in this column' => 'Dana u ovoj koloni', + '%dd' => '%dd', + 'Add a new link' => 'Dodaj novu vezu', + 'Do you really want to remove this link: "%s"?' => 'Da li zaista želite da uklonite ovu vezu: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Da li zaista želite da uklonite ovu vezu sa zadatkom #%d?', + 'Field required' => 'Polje je obavezno', + 'Link added successfully.' => 'Veza je uspeÅ¡no dodata.', + 'Link updated successfully.' => 'Veza je uspeÅ¡no ažurirana.', + 'Link removed successfully.' => 'Veza je uspeÅ¡no uklonjena.', + 'Link labels' => 'Veza etiketa', + 'Link modification' => 'Veza modifikacija', + 'Opposite label' => 'Suprotna etiketa', + 'Remove a link' => 'Ukloni vezu', + 'The labels must be different' => 'Etikete moraju biti razliÄite', + 'There is no link.' => 'Ne postoji veza', + 'This label must be unique' => 'Ova etiketa mora biti jedinstvena', + 'Unable to create your link.' => 'Nije moguće napraviti vezu.', + 'Unable to update your link.' => 'Nije moguće ažurirati vezu.', + 'Unable to remove this link.' => 'Nije moguće ukloniti vezu.', + 'relates to' => 'relacija sa', + 'blocks' => 'blokira', + 'is blocked by' => 'je blokiran od', + 'duplicates' => 'duplira', + 'is duplicated by' => 'je dupliran od', + 'is a child of' => 'je dete od', + 'is a parent of' => 'je roditelj od', + 'targets milestone' => 'cilj prekretnice', + 'is a milestone of' => 'je od prekretnice', + 'fixes' => 'popravlja', + 'is fixed by' => 'je popravljen od', + 'This task' => 'Ovaj zadatak', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'ProÅ¡iri zadatke', + 'Collapse tasks' => 'Skupi zadatke', + 'Expand/collapse tasks' => 'ProÅ¡iri/skupi zadatke', + 'Close dialog box' => 'Skupi dijalog', + 'Submit a form' => 'PoÅ¡alji obrazac', + 'Board view' => 'Pregled table', + 'Keyboard shortcuts' => 'PreÄice tastature', + 'Open board switcher' => 'Otvori prekidaÄe table', + 'Application' => 'Aplikacija', + 'Compact view' => 'Kompaktan pregled', + 'Horizontal scrolling' => 'Horizontalno listanje', + 'Compact/wide view' => 'Skupi/raÅ¡iri pregled', + 'Currency' => 'Valuta', + 'Personal project' => 'LiÄni projekat', + 'AUD - Australian Dollar' => 'AUD - Australijski dolar', + 'CAD - Canadian Dollar' => 'CAD - Kanadski dolar', + 'CHF - Swiss Francs' => 'CHF - Å vajcarski franak', + 'Custom Stylesheet' => 'PrilagoÄ‘eni stil', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Britanska funta', + 'INR - Indian Rupee' => 'INR - Indijski rupi', + 'JPY - Japanese Yen' => 'JPY - Japanski jen', + 'NZD - New Zealand Dollar' => 'NZD - Novozelandski dolar', + 'PEN - Peruvian Sol' => 'PEN - peruanski sol', + 'RSD - Serbian dinar' => 'RSD - Srpski dinar', + 'CNY - Chinese Yuan' => 'CNY - Kineski jen', + 'USD - US Dollar' => 'USD - AmeriÄki dolar', + 'VES - Venezuelan Bolívar' => 'VES - Venecuelanski bolivar', + 'Destination column' => 'OdrediÅ¡na kolona', + 'Move the task to another column when assigned to a user' => 'Premesti zadatak u neku drugu kolonu kada se dodeli izvrÅ¡iocu', + 'Move the task to another column when assignee is cleared' => 'Premesti zadatak u neku drugu kolonu kada se ukloni izvrÅ¡iocu', + 'Source column' => 'Izvorna kolona', + 'Transitions' => 'Prelaz', + 'Executer' => 'IzvrÅ¡ilac', + 'Time spent in the column' => 'Vreme provedeno u koloni', + 'Task transitions' => 'Prelazi zadatka', + 'Task transitions export' => 'Izvezi prelaze zadatka', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Ovaj izveÅ¡taj sadrži sva premeÅ¡tanja unutar kolona za svaki zadatak sa datumom, korisnika i utroÅ¡eno vreme za svaki premeÅ¡taj.', + 'Currency rates' => 'Stopa valute', + 'Rate' => 'Stopa', + 'Change reference currency' => 'Promeni referentnu valutu', + 'Reference currency' => 'Referentna valuta', + 'The currency rate has been added successfully.' => 'Stopa valute je uspeÅ¡no dodata.', + 'Unable to add this currency rate.' => 'Nije moguće dodati ovu stopu valute.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s je uklonio/la izvrÅ¡ioca zadatka %s', + 'Information' => 'Informacije', + 'Check two factor authentication code' => 'Proverite kod za dvofaktorsku autentifikaciju', + 'The two factor authentication code is not valid.' => 'Kod dvofaktorsku autentifikaciju nije važeći.', + 'The two factor authentication code is valid.' => 'Kod za dvofaktorsku autentifikaciju je važeći', + 'Code' => 'Kod', + 'Two factor authentication' => 'Dvofaktorska autentifikacija', + 'This QR code contains the key URI: ' => 'Ovaj QR kod sadrži kljuÄni URL:', + 'Check my code' => 'Proveri moj kod', + 'Secret key: ' => 'Tajni kljuÄ: ', + 'Test your device' => 'Testiraj svoj ureÄ‘aj', + 'Assign a color when the task is moved to a specific column' => 'Dodeli boju kada je zadatak pomeren u odabranu kolonu', + '%s via Kanboard' => '%s putem Kanboard-a', + 'Burndown chart' => 'Preostalo posla / vreme', + 'This chart show the task complexity over the time (Work Remaining).' => 'Ovaj grafikon pokazuje kompleksnost zadatka tokom vremena (Preostalo posla)', + 'Screenshot taken %s' => 'Slika ekrana uzeta %s', + 'Add a screenshot' => 'Dodaj sliku ekrana', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Uzmi sliku ekrana i pritisni CTRL+V ili ⌘+V da zalepiÅ¡ ovde.', + 'Screenshot uploaded successfully.' => 'Slika ekrana uspeÅ¡no dodata.', + 'SEK - Swedish Krona' => 'SEK - Å vedska kruna', + 'Identifier' => 'Identifikator', + 'Disable two factor authentication' => 'Onemogućite dvofaktorsku autnetifikaciju', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Da li zaista želite da onemogućite dvofaktorsku autentifikaciju za ovog korisnika: "%s"?', + 'Edit link' => 'Uredi vezu', + 'Start to type task title...' => 'PoÄni pisanje naslova zadatka...', + 'A task cannot be linked to itself' => 'Zadatak ne može biti povezan sa samim sobom', + 'The exact same link already exists' => 'IdentiÄna veza već postoji', + 'Recurrent task is scheduled to be generated' => 'Ponavljajući zadatak je pripremljen da bude kreiran', + 'Score' => 'Ocena', + 'The identifier must be unique' => 'Identifikator mora biti jedinstven', + 'This linked task id doesn\'t exists' => 'Povezani ID zadatka ne postoji', + 'This value must be alphanumeric' => 'Ova vrednost mora biti alfanumeriÄka', + 'Edit recurrence' => 'Izmena ponavljanja', + 'Generate recurrent task' => 'Napravi ponavljajući zadatak', + 'Trigger to generate recurrent task' => 'OkidaÄ koji pravi ponavljajući zadatak', + 'Factor to calculate new due date' => 'Faktor za raÄunanje novog roka zavrÅ¡etka', + 'Timeframe to calculate new due date' => 'Vremenski okvir za raÄunanje novog roka zavrÅ¡etka', + 'Base date to calculate new due date' => 'PoÄetni datum za raÄunanje novog roka zavrÅ¡etka', + 'Action date' => 'Datum akcije', + 'Base date to calculate new due date: ' => 'PoÄetni datum za raÄunanje novog roka zavrÅ¡etka: ', + 'This task has created this child task: ' => 'Ovaj zadatak je napravio dete zadatka: ', + 'Day(s)' => 'Dan/i', + 'Existing due date' => 'Postojeći rok zavrÅ¡etka', + 'Factor to calculate new due date: ' => 'Faktor za raÄunanje novog roka zavrÅ¡etka: ', + 'Month(s)' => 'Mesec(i)', + 'This task has been created by: ' => 'Ovaj zadatak je napravio: ', + 'Recurrent task has been generated:' => 'Ponavljajući zadatak je napravio:', + 'Timeframe to calculate new due date: ' => 'Vremenski okvir za raÄunanje novog roka zavrÅ¡etka:', + 'Trigger to generate recurrent task: ' => 'OkidaÄ za pravljenje ponavljajućeg zadatka', + 'When task is closed' => 'Kada je zadatak zatvoren', + 'When task is moved from first column' => 'Kada je zadatak premeÅ¡ten iz prve kolone', + 'When task is moved to last column' => 'Kada je zadatak premeÅ¡ten u poslednju kolonu', + 'Year(s)' => 'Godina/e', + 'Project settings' => 'Postavke projekta', + 'Automatically update the start date' => 'Automatski ažuriraj poÄetni datum', + 'iCal feed' => 'iCal kanal', + 'Preferences' => 'Postavke', + 'Security' => 'Sigurnost', + 'Two factor authentication disabled' => 'Dvofaktorska autentifikacija je onemogućena', + 'Two factor authentication enabled' => 'Dvofaktorska autentifikacija je omogućena', + 'Unable to update this user.' => 'Nije moguće ažurirati ovog korisnika', + 'There is no user management for personal projects.' => 'Nema mehanizma za upravljanje korisnicima kod liÄnih projekata.', + 'User that will receive the email' => 'Korisnik koji će primiti email', + 'Email subject' => 'Predmet email-a', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'Dodaj komentar u dnevnik kada se zadatak premesti iz jedne kolonu u drugu', + 'Move the task to another column when the category is changed' => 'Pomeri zadatak u drugu kolonu kada je kategorija promenjena', + 'Send a task by email to someone' => 'PoÅ¡alji zadatak nekome putem email-a', + 'Reopen a task' => 'Ponovo otvori zadatak', + 'Notification' => 'ObaveÅ¡tenja', + '%s moved the task #%d to the first swimlane' => '%s je premestio/la zadatak #%d u prvu stazu', + 'Swimlane' => 'Staza', + '%s moved the task %s to the first swimlane' => '%s je premestio/la zadatak %s u prvu stazu', + '%s moved the task %s to the swimlane "%s"' => '%s je premestio/la zadatak %s u stazu "%s"', + 'This report contains all subtasks information for the given date range.' => 'Ovaj izveÅ¡taj sadrži sve informacije o podzadacima u datom periodu', + 'This report contains all tasks information for the given date range.' => 'Ovaj izveÅ¡taj sadrži sve informacije o zadacima u datom periodu', + 'Project activities for %s' => 'Aktivnosti projekta za %s', + 'view the board on Kanboard' => 'pregled table na Kanboard-u', + 'The task has been moved to the first swimlane' => 'Zadatak je premeÅ¡ten u prvu stazu', + 'The task has been moved to another swimlane:' => 'Zadatak je premeÅ¡ten u drugu stazu:', + 'New title: %s' => 'Novi naslov: %s', + 'The task is not assigned anymore' => 'Ovaj zadatak viÅ¡e nema izvrÅ¡ioca', + 'New assignee: %s' => 'Novi izvrÅ¡ilac: %s', + 'There is no category now' => 'Sada nema kategorije', + 'New category: %s' => 'Nova kategorija: %s', + 'New color: %s' => 'Nova boja: %s', + 'New complexity: %d' => 'Nova složenost: %d', + 'The due date has been removed' => 'Rok zavrÅ¡etka je ukloljen', + 'There is no description anymore' => 'Nema viÅ¡e opisa', + 'Recurrence settings has been modified' => 'Promenjene postavke za ponavljajuće zadatke', + 'Time spent changed: %sh' => 'UtroÅ¡eno vreme je promenjeno: %sh', + 'Time estimated changed: %sh' => 'OÄekivano vreme je promenjeno: %sh', + 'The field "%s" has been updated' => 'Polje "%s" je ažurirano', + 'The description has been modified:' => 'Promenjen opis:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Da li zaista želite da zatvorite zadatak "%s" kao i sve podzadatke?', + 'I want to receive notifications for:' => 'Želim da dobijam obaveÅ¡tenja za:', + 'All tasks' => 'Sve zadatke', + 'Only for tasks assigned to me' => 'Samo za zadatke na kojima sam ja izvrÅ¡ilac', + 'Only for tasks created by me' => 'Samo za zadatke koje sam ja napravio/la', + 'Only for tasks created by me and tasks assigned to me' => 'Samo za zadatke koje sam ja napravio/la ili sam izvrÅ¡ilac zadatka', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Ukupno za sve kolone', + 'You need at least 2 days of data to show the chart.' => 'Da bi se prikazao ovaj grafik neophodno je da postoje podaci barem dva dana unazad.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Zaustavi tajmer', + 'Start timer' => 'Pokreni tajmer', + 'My activity stream' => 'Tok mojih aktivnosti', + 'Search tasks' => 'Pretraga zadataka', + 'Reset filters' => 'Resetuj filtere', + 'My tasks due tomorrow' => 'Moji zadaci koje treba zavrÅ¡iti sutra', + 'Tasks due today' => 'Zadaci koje treba zavrÅ¡iti danas', + 'Tasks due tomorrow' => 'Zadaci koje treba zavrÅ¡iti sutra', + 'Tasks due yesterday' => 'Zadaci koje je trebalo zavrÅ¡iti juÄe', + 'Closed tasks' => 'Zatvoreni zadaci', + 'Open tasks' => 'Otvoreni zadaci', + 'Not assigned' => 'Bez izvrÅ¡ioca', + 'View advanced search syntax' => 'Vidi naprednu sintaksu pretrage', + 'Overview' => 'Pregled', + 'Board/Calendar/List view' => 'Pregled Table/Kalendara/Liste', + 'Switch to the board view' => 'Prebacivanje na prikaz table', + 'Switch to the list view' => 'Prebacivanje na prikaz liste', + 'Go to the search/filter box' => 'Idite na polje za pretragu / filter', + 'There is no activity yet.' => 'Nema aktivnosti joÅ¡ uvek.', + 'No tasks found.' => 'Zadaci nisu pronaÄ‘eni.', + 'Keyboard shortcut: "%s"' => 'PreÄica tastature: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filter', + 'Advanced search' => 'Napredna pretraga', + 'Example of query: ' => 'Primer za upit: ', + 'Search by project: ' => 'Pretraga po projektu: ', + 'Search by column: ' => 'Pretraga po koloni: ', + 'Search by assignee: ' => 'Pretraga po izvrÅ¡iocu: ', + 'Search by color: ' => 'Pretraga po boji: ', + 'Search by category: ' => 'Pretraga po kategoriji: ', + 'Search by description: ' => 'Pretraga po opisu: ', + 'Search by due date: ' => 'Pretraga po roku zavrÅ¡etka: ', + 'Average time spent in each column' => 'Prosek utroÅ¡enog vrmena u svakoj koloni', + 'Average time spent' => 'Prosek utroÅ¡enog vremena', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Ovaj grafikon pokazuje prosek utroÅ¡enog vremena u svakoj koloni za posljednjih %d zadataka.', + 'Average Lead and Cycle time' => 'ProseÄno vreme realizacije(lead) i izvoÄ‘enja(cycle)', + 'Average lead time: ' => 'ProseÄno vreme realizacije: ', + 'Average cycle time: ' => 'ProseÄno vreme ciklusa: ', + 'Cycle Time' => 'Vreme ciklusa', + 'Lead Time' => 'Vreme realizacije', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Ovaj grafikon prikazuje proseÄno vreme realizacije i ciklusa za posljednjih %d zadataka tokom vremena.', + 'Average time into each column' => 'ProseÄno vreme u svakoj koloni', + 'Lead and cycle time' => 'Vreme realizacije i ciklusa', + 'Lead time: ' => 'Vreme realizacije: ', + 'Cycle time: ' => 'Vreme ciklusa: ', + 'Time spent in each column' => 'UtroÅ¡eno vreme u svakoj koloni', + 'The lead time is the duration between the task creation and the completion.' => 'Vreme realizacije je vreme koje je proteklo izmeÄ‘u otvaranja i zatvaranja zadatka.', + 'The cycle time is the duration between the start date and the completion.' => 'Vreme ciklusa je vreme koje je proteklo izmeÄ‘u poÄetka i zavrÅ¡etka rada na zadatku.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Ako zadatak nije zatvoren trenutno vreme se koristi umesto datuma zavrÅ¡etka.', + 'Set the start date automatically' => 'Automatski postavi poÄetno vreme', + 'Edit Authentication' => 'Uredi autentifikaciju', + 'Remote user' => 'Udaljeni korisnik', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Udaljeni korisnik ne Äuva lozinku u Kanboard bazi, npr: LDAP, Google i Github korisniÄki nalozi.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Ako ste Å¡tiklirali polje "Zabrani prijavnu formu" unos pristupnih podataka u prijavnoj formi će biti ignorisan.', + 'Default task color' => 'Podrazumijevana boja zadatka', + 'This feature does not work with all browsers.' => 'Ova funckionalnost ne radi na svim pregledaÄima.', + 'There is no destination project available.' => 'Nije dostupan nijedan odrediÅ¡ni projekat.', + 'Trigger automatically subtask time tracking' => 'OkidaÄ za automatsko vremensko praćenje za podzadatke', + 'Include closed tasks in the cumulative flow diagram' => 'Obuhvati zatvorene zadatke u kumulativnom dijagramu toka', + 'Current swimlane: %s' => 'Trenutna staza: %s', + 'Current column: %s' => 'Trenutna kolona: %s', + 'Current category: %s' => 'Trenutna kategorija: %s', + 'no category' => 'bez kategorije', + 'Current assignee: %s' => 'Trenutni izvrÅ¡ilac: %s', + 'not assigned' => 'nije dodeljeno', + 'Author:' => 'Autor:', + 'contributors' => 'saradnici', + 'License:' => 'Licenca:', + 'License' => 'Licenca', + 'Enter the text below' => 'Unesite tekst ispod', + 'Start date:' => 'PoÄetno vreme:', + 'Due date:' => 'Rok zavrÅ¡etka:', + 'People who are project managers' => 'Osobe koji su menadžeri projekta', + 'People who are project members' => 'Osobe koje su Älanovi projekta', + 'NOK - Norwegian Krone' => 'NOK - NorveÅ¡ka kruna', + 'Show this column' => 'Prikaži ovu kolonu', + 'Hide this column' => 'Sakrij ovu kolonu', + 'End date' => 'Datum zavrÅ¡etka', + 'Users overview' => 'Pregled korisnika', + 'Members' => 'ÄŒlanovi', + 'Shared project' => 'Deljeni projekat', + 'Project managers' => 'Menadžeri projekta', + 'Projects list' => 'Lista projekata', + 'End date:' => 'Datum zavrÅ¡etka:', + 'Change task color when using a specific task link' => 'Promeni boju zadatka kada se koristi odreÄ‘ena veza na zadatku', + 'Task link creation or modification' => 'Veza na zadatku je napravljena ili izmenjena', + 'Milestone' => 'Prekretnica', + 'Reset the search/filter box' => 'Resetuj plje za pretragu/filtere', + 'Documentation' => 'Dokumentacija', + 'Author' => 'Autor', + 'Version' => 'Verzija', + 'Plugins' => 'Dodaci', + 'There is no plugin loaded.' => 'Nema uÄitanih dodataka.', + 'My notifications' => 'Moja obaveÅ¡tenja', + 'Custom filters' => 'PrilagoÄ‘eni filteri', + 'Your custom filter has been created successfully.' => 'Tvoj prilagoÄ‘eni filter je uspeÅ¡no napravljen.', + 'Unable to create your custom filter.' => 'Nije moguće napraviti prilagoÄ‘eni filter.', + 'Custom filter removed successfully.' => 'PrilagoÄ‘eni filter uspeÅ¡no uklonjen.', + 'Unable to remove this custom filter.' => 'Nije moguće ukloniti prilagoÄ‘eni filter.', + 'Edit custom filter' => 'Uredi prilagoÄ‘eni filter', + 'Your custom filter has been updated successfully.' => 'PrilagoÄ‘eni filter uspeÅ¡no ažuriran.', + 'Unable to update custom filter.' => 'Nije moguće ažurirati prilagoÄ‘eni filter', + 'Web' => 'Veb', + 'New attachment on task #%d: %s' => 'Novi priložak na zadatku #%d: %s', + 'New comment on task #%d' => 'Novi komentar na zadatku #%d', + 'Comment updated on task #%d' => 'Ažuriran komentar na zadatku #%d', + 'New subtask on task #%d' => 'Novi podzadatak na zadatku #%d', + 'Subtask updated on task #%d' => 'Podzadatak ažuriran na zadatku #%d', + 'New task #%d: %s' => 'Novi zadatak #%d: %s', + 'Task updated #%d' => 'Zadatak ažuriran #%d', + 'Task #%d closed' => 'Zadatak #%d zatvoren', + 'Task #%d opened' => 'Zadatak #%d otvoren', + 'Column changed for task #%d' => 'Promenjena kolona za zadatak #%d', + 'New position for task #%d' => 'Nova pozicija za zadatak #%d', + 'Swimlane changed for task #%d' => 'Staza je promenjena za zadatak #%d', + 'Assignee changed on task #%d' => 'IzvrÅ¡ilac je promenjen na zadatku #%d', + '%d overdue tasks' => '%d zadataka kasni', + 'No notification.' => 'Nema novih obaveÅ¡tenja.', + 'Mark all as read' => 'OznaÄi sve kao proÄitano', + 'Mark as read' => 'OznaÄi kao proÄitano', + 'Total number of tasks in this column across all swimlanes' => 'Ukupan broj zadataka u ovoj koloni u svim stazama', + 'Collapse swimlane' => 'Skupi stazu', + 'Expand swimlane' => 'ProÅ¡iri stazu', + 'Add a new filter' => 'Dodaj novi filter', + 'Share with all project members' => 'Podeli sa svim Älanovima projekta', + 'Shared' => 'Podeljeno', + 'Owner' => 'Vlasnik', + 'Unread notifications' => 'NeproÄitana obaveÅ¡tenja', + 'Notification methods:' => 'Metode obaveÅ¡tenja:', + 'Unable to read your file' => 'Nije moguće proÄitati datoteku', + '%d task(s) have been imported successfully.' => '%d zadataka uspeÅ¡no uvezeno.', + 'Nothing has been imported!' => 'NiÅ¡ta nije uvezeno!', + 'Import users from CSV file' => 'Uvezi korisnike putem CSV datoteke', + '%d user(s) have been imported successfully.' => '%d korisnika uspeÅ¡no uvezeno.', + 'Comma' => 'Zarez', + 'Semi-colon' => 'TaÄka i zarez', + 'Tab' => 'Tab', + 'Vertical bar' => 'Vertikalna traka', + 'Double Quote' => 'Dvostruki navodnici', + 'Single Quote' => 'Jednostruki navodnici', + '%s attached a file to the task #%d' => '%s je dodao/la novu datoteku u zadatak %d', + 'There is no column or swimlane activated in your project!' => 'Nema kolone ili aktivne staze u VaÅ¡em projektu!', + 'Append filter (instead of replacement)' => 'Dodaj filter (umesto zamene postojećeg)', + 'Append/Replace' => 'Dodaj/Zameni', + 'Append' => 'Dodaj', + 'Replace' => 'Zameni', + 'Import' => 'Uvoz', + 'Change sorting' => 'Promeni sortiranje', + 'Tasks Importation' => 'Uvoz zadataka', + 'Delimiter' => 'Delimter', + 'Enclosure' => 'Prilog', + 'CSV File' => 'CSV datoteka', + 'Instructions' => 'Uputstva', + 'Your file must use the predefined CSV format' => 'VaÅ¡a datoteka mora koristiti predefinisani CSV format', + 'Your file must be encoded in UTF-8' => 'VaÅ¡a datoteka mora koristiti UTF-8 enkodovana', + 'The first row must be the header' => 'Prvi red mora biti zaglavlje', + 'Duplicates are not verified for you' => 'Dupliranja neće biti provervana (morate sami uraditi)', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Rok zavrÅ¡etka mora biti u ISO formatu: YYYY-MM-DD', + 'Download CSV template' => 'Preuzmi CSV Å¡ablon', + 'No external integration registered.' => 'Nema registrovanih spoljnih integracija.', + 'Duplicates are not imported' => 'Duplikati nisu uveženi', + 'Usernames must be lowercase and unique' => 'KorisniÄka imena moraju biti napisana malim slovima i jedinstvena', + 'Passwords will be encrypted if present' => 'Lozinke (ako postoje) će biti Å¡iforvane', + '%s attached a new file to the task %s' => '%s je dodao/la novu datoteku u zadatak %s', + 'Link type' => 'Tip veze', + 'Assign automatically a category based on a link' => 'Automatsko dodeljivanje kategorije bazirano na vezi', + 'BAM - Konvertible Mark' => 'BAM - Konvertibilna marka', + 'Assignee Username' => 'KorisniÄko ime izvrÅ¡ioca', + 'Assignee Name' => 'Ime izvrÅ¡ioca', + 'Groups' => 'Grupe', + 'Members of %s' => 'ÄŒlanovi %s', + 'New group' => 'Nova grupa', + 'Group created successfully.' => 'Grupa uspeÅ¡no kreirana.', + 'Unable to create your group.' => 'Nije moguće kreirati grupu.', + 'Edit group' => 'Uredi grupu', + 'Group updated successfully.' => 'Grupa uspeÅ¡no ažurirana.', + 'Unable to update your group.' => 'Nije moguće ažurirati grupu', + 'Add group member to "%s"' => 'Dodaj Älana u grupu "%s"', + 'Group member added successfully.' => 'UspjeÅ¡no dodat Älan grupe.', + 'Unable to add group member.' => 'Nije moguće dodati Älana grupi.', + 'Remove user from group "%s"' => 'Ukloni korisnika iz grupe "%s"', + 'User removed successfully from this group.' => 'Korisnik uspeÅ¡no uklonjen iz grupe.', + 'Unable to remove this user from the group.' => 'Nije moguće ukloniti korisnika iz grupe.', + 'Remove group' => 'Ukloni grupu', + 'Group removed successfully.' => 'Grupa uspeÅ¡no uklonjena.', + 'Unable to remove this group.' => 'Nije moguće ukloniti grupu.', + 'Project Permissions' => 'Dozvole na projektu', + 'Manager' => 'Menadžer', + 'Project Manager' => 'Menadžer projekta', + 'Project Member' => 'ÄŒlan projekta', + 'Project Viewer' => 'PosmatraÄ projekta', + 'Your account is locked for %d minutes' => 'Tvoj korisniÄki nalog je zakljuÄan narednih %d minuta', + 'Invalid captcha' => 'PogreÅ¡an captcha', + 'The name must be unique' => 'Ime mora biti jedinstveno', + 'View all groups' => 'Pregledaj sve grupe', + 'There is no user available.' => 'Nema dostupnih korisnika.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Da li zaista želite da uklonite korisnika "%s" iz grupe "%s"?', + 'There is no group.' => 'Nema grupe', + 'Add group member' => 'Dodaj Älana grupe', + 'Do you really want to remove this group: "%s"?' => 'Da li zaista želite da ukloniti ovu grupu: "%s"?', + 'There is no user in this group.' => 'Trenutno nema korisnika u grupi.', + 'Permissions' => 'Dozvole', + 'Allowed Users' => 'Dozvoljeni korisnici', + 'No specific user has been allowed.' => 'Nijednom specifiiÄnom korisniku nije dozvoljeno.', + 'Role' => 'Uloge', + 'Enter user name...' => 'Unesi korisniÄko ime...', + 'Allowed Groups' => 'Dozvoljene grupe', + 'No group has been allowed.' => 'Nijednoj grupi nije dozvoljeno.', + 'Group' => 'Grupa', + 'Group Name' => 'Ime grupe', + 'Enter group name...' => 'Unesi ime grupe...', + 'Role:' => 'Uloga:', + 'Project members' => 'ÄŒlanovi projekta', + '%s mentioned you in the task #%d' => '%s Vas je spomenuo/la u zadatku #%d', + '%s mentioned you in a comment on the task #%d' => '%s Vas je spomenuo/la u komentaru zadatka #%d', + 'You were mentioned in the task #%d' => 'Spomenuti ste u zadatku #%d', + 'You were mentioned in a comment on the task #%d' => 'Spomenuti ste u komentaru zadatka #%d', + 'Estimated hours: ' => 'Procenjeno sati:', + 'Actual hours: ' => 'Stvarno sati:', + 'Hours Spent' => 'UtroÅ¡eno sati:', + 'Hours Estimated' => 'Sati procenjeno', + 'Estimated Time' => 'Procenjeno vreme', + 'Actual Time' => 'Stvarno vreme', + 'Estimated vs actual time' => 'Procenjeno / stvarno vreme', + 'RUB - Russian Ruble' => 'RUB - Ruski rubij', + 'Assign the task to the person who does the action when the column is changed' => 'Dodeli zadatak osobi koja izvrÅ¡i akciju promene kolone', + 'Close a task in a specific column' => 'Zatvori zadatak u odreÄ‘enoj koloni', + 'Time-based One-time Password Algorithm' => 'Algoritam jednokratne lozinke zasnovan na vremenu', + 'Two-Factor Provider: ' => 'Dvofaktorski dobavljaÄ:', + 'Disable two-factor authentication' => 'Onemogući dvofaktorsku autentifikaciju', + 'Enable two-factor authentication' => 'Omogućite dvofaktorsku autentifikaciju', + 'There is no integration registered at the moment.' => 'Trenutno nije registrovana nijedna integracija.', + 'Password Reset for Kanboard' => 'Promena lozinke za Kanboard', + 'Forgot password?' => 'Zaboravljena lozinka?', + 'Enable "Forget Password"' => 'Omogući "Zaboravljena lozinka"', + 'Password Reset' => 'Promena lozinke', + 'New password' => 'Nova lozinka', + 'Change Password' => 'Promeni lozinku', + 'To reset your password click on this link:' => 'Da biste promenili lozinku kliknite na ovaj link:', + 'Last Password Reset' => 'Posljednja promena lozinke', + 'The password has never been reinitialized.' => 'Lozinka nije nikada menjana.', + 'Creation' => 'Napravljena', + 'Expiration' => 'IstiÄe', + 'Password reset history' => 'Istorija promena lozinki', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Svi zadaci kolone "%s" i staze "%s" uspeÅ¡no su zatvoreni.', + 'Do you really want to close all tasks of this column?' => 'Da li zaista želite da zatvorite sve zadatke ove kolone?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d zadatak/zadaci u koloni "%s" i stazi "%s" će biti zatvoreni.', + 'Close all tasks in this column and this swimlane' => 'Zatvori sve zadatke u ovoj koloni i ovoj stazi', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Nijedan dodatak nije registrovao metodu obaveÅ¡tavanja o projektu. U korisniÄkom profilu i dalje možete da konfiguriÅ¡ete pojedinaÄna obaveÅ¡tenja.', + 'My dashboard' => 'Moja komandna tabla', + 'My profile' => 'Moj profil', + 'Project owner: ' => 'Vlasnik projekta: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identifikator projekta je opcion, mora biti alfanumeriÄki, na primer: MOJPROJEKAT.', + 'Project owner' => 'Vlasnik projekta', + 'Personal projects do not have users and groups management.' => 'LiÄni projekti nemaju upravljanje korisnicima i grupama.', + 'There is no project member.' => 'Nema Älanova projekta.', + 'Priority' => 'Prioritet', + 'Task priority' => 'Prioritet zadatka', + 'General' => 'OpÅ¡te', + 'Dates' => 'Datumi', + 'Default priority' => 'Podrazumijevani prioritet', + 'Lowest priority' => 'Najmanji prioritet', + 'Highest priority' => 'Najveći prioritet', + 'Close a task when there is no activity' => 'Zatvori zadatak kada nema aktivnosti', + 'Duration in days' => 'Dužina trajanja u danima', + 'Send email when there is no activity on a task' => 'PoÅ¡alji email kada nema aktivnosti na zadatku', + 'Unable to fetch link information.' => 'Ne mogu da pribavim informacije o vezi.', + 'Daily background job for tasks' => 'Dnevni pozadinski poslovi za zadatke', + 'Auto' => 'Automatski', + 'Related' => 'Povezan', + 'Attachment' => 'Priložak', + 'Web Link' => 'Veb link', + 'External links' => 'Spoljne veze', + 'Add external link' => 'Dodaj spoljnu vezu', + 'Type' => 'Vrsta', + 'Dependency' => 'Zavisnost', + 'Add internal link' => 'Dodaj unutraÅ¡nju vezu', + 'Add a new external link' => 'Dodaj novu spoljnu vezu', + 'Edit external link' => 'Uredi spoljnu vezu', + 'External link' => 'Spoljna veza', + 'Copy and paste your link here...' => 'Kopirajte i zalepite VaÅ¡u vezu ovde...', + 'URL' => 'URL', + 'Internal links' => 'UnutraÅ¡nje veze', + 'Assign to me' => 'Dodeli meni', + 'Me' => 'Ja', + 'Do not duplicate anything' => 'NiÅ¡ta ne dupliraj', + 'Projects management' => 'Upravljanje projektima', + 'Users management' => 'Upravljanje korisnicima', + 'Groups management' => 'Upravljanje grupama', + 'Create from another project' => 'Napravi iz drugog projekta', + 'open' => 'otvoreno', + 'closed' => 'zatvoreno', + 'Priority:' => 'Prioritet:', + 'Reference:' => 'Referenca:', + 'Complexity:' => 'Složenost:', + 'Swimlane:' => 'Staza:', + 'Column:' => 'Kolona:', + 'Position:' => 'Pozicija:', + 'Creator:' => 'Kreator:', + 'Time estimated:' => 'OÄekivano vreme:', + '%s hours' => '%s sati', + 'Time spent:' => 'UtroÅ¡eno vreme:', + 'Created:' => 'Kreirano:', + 'Modified:' => 'UreÄ‘eno:', + 'Completed:' => 'ZavrÅ¡eno:', + 'Started:' => 'ZapoÄeto:', + 'Moved:' => 'Pomereno:', + 'Task #%d' => 'Zadatak #%d', + 'Time format' => 'Format vremena', + 'Start date: ' => 'PoÄetni datum:', + 'End date: ' => 'Krajnji datum:', + 'New due date: ' => 'Novi rok zavrÅ¡etka:', + 'Start date changed: ' => 'PoÄetni datum promenjen: ', + 'Disable personal projects' => 'Onemogući liÄne projekte', + 'Do you really want to remove this custom filter: "%s"?' => 'Da li zaista želite da uklonite ovaj prilagoÄ‘eni filter "%s"?', + 'Remove a custom filter' => 'Ukloni prilagoÄ‘eni filter', + 'User activated successfully.' => 'Korisnik uspeÅ¡no aktiviran.', + 'Unable to enable this user.' => 'Nije moguće omogućiti ovog korisnika.', + 'User disabled successfully.' => 'Korisnik uspeÅ¡no onemogućen.', + 'Unable to disable this user.' => 'Nije moguće onemogućiti ovog korisnika.', + 'All files have been uploaded successfully.' => 'Sve datoteke su uspeÅ¡no otpremljene.', + 'The maximum allowed file size is %sB.' => 'Maksimalna dozvoljena veliÄina datoteke je %sB.', + 'Drag and drop your files here' => 'Prevucite i spustite VaÅ¡e datoteke ovde', + 'choose files' => 'izaberite datoteke', + 'View profile' => 'Pregledaj profil', + 'Two Factor' => 'Dva faktora', + 'Disable user' => 'Onemogući korisnika', + 'Do you really want to disable this user: "%s"?' => 'Da li zaista želite da onemogućite ovog korisnika: "%s"?', + 'Enable user' => 'Omogući korisnika', + 'Do you really want to enable this user: "%s"?' => 'Da li zaista želite omogućite ovog korisnika: "%s"?', + 'Download' => 'Preuzeto', + 'Uploaded: %s' => 'Otpremljeno: %s', + 'Size: %s' => 'VeliÄina: %s', + 'Uploaded by %s' => 'Otpremio %s', + 'Filename' => 'Ime datoteke', + 'Size' => 'VeliÄina', + 'Column created successfully.' => 'Kolona uspeÅ¡no napravljena.', + 'Another column with the same name exists in the project' => 'Već postoji kolona sa istim imenom u ovom projektu.', + 'Default filters' => 'Podrazumevani filteri', + 'Your board doesn\'t have any columns!' => 'Tvoja tabla nema ni jednu kolonu!', + 'Change column position' => 'Promeni poziciju kolone', + 'Switch to the project overview' => 'Prebacivanje na pregled projekta', + 'User filters' => 'Fliteri korisnika', + 'Category filters' => 'Fliteri kategorija', + 'Upload a file' => 'Otpremi datoteku', + 'View file' => 'Pogledaj datoteku', + 'Last activity' => 'Posljednja aktivnost', + 'Change subtask position' => 'Promijeni poziciju podzadatka', + 'This value must be greater than %d' => 'Ova vrednost mora biti veća od %d', + 'Another swimlane with the same name exists in the project' => 'Već postoji staza sa istim imenom u ovom projektu', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Na primer: https://example.kanboard.org/ (koristi se za generisanje apsolutnog URL-a)', + 'Actions duplicated successfully.' => 'Akcije uspjeÅ¡no duplirane.', + 'Unable to duplicate actions.' => 'Nije moguće duplirati akcije.', + 'Add a new action' => 'Dodaj novu akciju', + 'Import from another project' => 'Uvezi iz drugog projekta', + 'There is no action at the moment.' => 'Trenutno nema akcija.', + 'Import actions from another project' => 'Uvezi akcije iz drugog projekta', + 'There is no available project.' => 'Trenutno nema dostupnih projekata.', + 'Local File' => 'Lokalna datoteka', + 'Configuration' => 'Konfiguracija', + 'PHP version:' => 'Verzija PHP-a:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Verzija OS-a:', + 'Database version:' => 'Verzija baze podataka:', + 'Browser:' => 'PregledaÄ:', + 'Task view' => 'Pregled zadatka', + 'Edit task' => 'Uredi zadatak', + 'Edit description' => 'Uredi opis', + 'New internal link' => 'Nova unutraÅ¡nja veza', + 'Display list of keyboard shortcuts' => 'Prikaži listu preÄica na tastaturi', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Otpremi sliku za avatar', + 'Remove my image' => 'Ukloni moju sliku', + 'The OAuth2 state parameter is invalid' => 'Parametar stanja OAuth2 je nevažeći', + 'User not found.' => 'Korisnik nije pronaÄ‘en.', + 'Search in activity stream' => 'Pretraži aktivnosti', + 'My activities' => 'Moje aktivnosti', + 'Activity until yesterday' => 'Aktivnosti do juÄe', + 'Activity until today' => 'Aktivnosti do danas', + 'Search by creator: ' => 'Pretraga po kreatoru: ', + 'Search by creation date: ' => 'Pretraga po datumu kreiranja: ', + 'Search by task status: ' => 'Pretraga po statusu zadatka: ', + 'Search by task title: ' => 'Pretraga po naslovu zadatka: ', + 'Activity stream search' => 'Pretraga aktivnosti', + 'Projects where "%s" is manager' => 'Projekti gde je "%s" menadžer', + 'Projects where "%s" is member' => 'Projekti gde je "%s" Älan', + 'Open tasks assigned to "%s"' => 'Otvoreni zadaci dodeljeni "%s"', + 'Closed tasks assigned to "%s"' => 'Zatvoreni zadaci dodijeljeni "%s"', + 'Assign automatically a color based on a priority' => 'Dodeli boju automatski bazirano na prioritetu', + 'Overdue tasks for the project(s) "%s"' => 'Zadaci koji kasne za projekat/projekte "%s"', + 'Upload files' => 'Otpremi datoteke', + 'Installed Plugins' => 'Instalirani dodaci', + 'Plugin Directory' => 'Direktorijum dodataka', + 'Plugin installed successfully.' => 'Dodatak je uspeÅ¡no instaliran.', + 'Plugin updated successfully.' => 'Dodatak je uspeÅ¡no ažuriran.', + 'Plugin removed successfully.' => 'Dodatak uspeÅ¡no uklonjen.', + 'Subtask converted to task successfully.' => 'Podzadatak uspeÅ¡no pretvoren u zadatak.', + 'Unable to convert the subtask.' => 'Nije moguće pretvoriti podzadatak.', + 'Unable to extract plugin archive.' => 'Nije moguće otpakovati arhivu dodatka.', + 'Plugin not found.' => 'Dodatak nije pronaÄ‘en.', + 'You don\'t have the permission to remove this plugin.' => 'Nemate dozvolu za uklanjanje ovog dodatka.', + 'Unable to download plugin archive.' => 'Nije moguće preuzeti arhivu dodatka.', + 'Unable to write temporary file for plugin.' => 'Nije moguće zapisati privremenu datoteku za dodatak.', + 'Unable to open plugin archive.' => 'Nije moguće otvoriti arhivu dodatka.', + 'There is no file in the plugin archive.' => 'Nema datoteke u arhivi dodatka.', + 'Create tasks in bulk' => 'Masovno kreiranja zadataka', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Ova Kanboard nije konfigurisan tako da podržava instalaciju dodataka kroz korisniÄko okruženje.', + 'There is no plugin available.' => 'Nema dostupnih dodataka.', + 'Install' => 'Instaliraj', + 'Update' => 'Ažuriraj', + 'Up to date' => 'Ažurno', + 'Not available' => 'Nije dostupno', + 'Remove plugin' => 'Ukloni dodatak', + 'Do you really want to remove this plugin: "%s"?' => 'Da li zaista želite da uklonite ovaj dodatak: "%s"?', + 'Uninstall' => 'Deinstaliraj', + 'Listing' => 'Spisak', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Upravljanje projektima', + 'Convert to task' => 'Pretvori u zadatak', + 'Convert sub-task to task' => 'Pretvori podzadatak u zadatak', + 'Do you really want to convert this sub-task to a task?' => 'Da li zaista želite da pretvorite podzadatak u zadatak?', + 'My task title' => 'Naslov zadatka', + 'Enter one task by line.' => 'Unesi jedan zadatak po liniji.', + 'Number of failed login:' => 'Broj neuspeÅ¡nih prijava:', + 'Account locked until:' => 'KorisniÄki nalog zakljuÄan do:', + 'Email settings' => 'Postavke email-a', + 'Email sender address' => 'Email adresa poÅ¡iljaoca', + 'Email transport' => 'Transport email-a', + 'Webhook token' => 'Webhook token', + 'Project tags management' => 'Upravljanje oznakama projekta', + 'Tag created successfully.' => 'Oznaka uspeÅ¡no kreirana.', + 'Unable to create this tag.' => 'Nije moguće kreirati oznaku.', + 'Tag updated successfully.' => 'Oznaka uspeÅ¡no ažurirana.', + 'Unable to update this tag.' => 'Nije moguće ažurirati ovu oznaku.', + 'Tag removed successfully.' => 'Oznaka uspeÅ¡no uklonjena.', + 'Unable to remove this tag.' => 'Nije moguće ukloniti ovu oznaku.', + 'Global tags management' => 'Globalno upravljanje oznakama.', + 'Tags' => 'Oznake', + 'Tags management' => 'Upravljanje oznakama', + 'Add new tag' => 'Dodaj novu oznaku', + 'Edit a tag' => 'Uredi oznaku', + 'Project tags' => 'Oznake projekta', + 'There is no specific tag for this project at the moment.' => 'Trenutno nema oznaka za ovaj projekat.', + 'Tag' => 'Oznaka', + 'Remove a tag' => 'Ukloni oznaku', + 'Do you really want to remove this tag: "%s"?' => 'Da li zaista želite da uklonite ovu oznaku: "%s"?', + 'Global tags' => 'Globalne oznake', + 'There is no global tag at the moment.' => 'Trenutno nema globalnih oznaka.', + 'This field cannot be empty' => 'Ovo polje ne može biti prazno', + 'Close a task when there is no activity in a specific column' => 'Zatvori zadatak kada nema aktivnosti u odabranoj koloni', + '%s removed a subtask for the task #%d' => '%s je uklonio/la podzadatak za zadatak #%d', + '%s removed a comment on the task #%d' => '%s je uklonio/la komentar na zadatku #%d', + 'Comment removed on task #%d' => 'Komentar ukloljen na zadatku #%d', + 'Subtask removed on task #%d' => 'Podzadatak ukloljen na zadatku #%d', + 'Hide tasks in this column in the dashboard' => 'Sakrijte zadatke u ovoj koloni na kontrolnoj tabli', + '%s removed a comment on the task %s' => '%s je uklonio/la komentar za zadatak %s', + '%s removed a subtask for the task %s' => '%s je uklonio/la podzadatak za zadatak %s', + 'Comment removed' => 'Komentar uklonjen', + 'Subtask removed' => 'Podzadatak uklonjen', + '%s set a new internal link for the task #%d' => '%s je postavio/la novu unutraÅ¡nju vezu za zadatak #%d', + '%s removed an internal link for the task #%d' => '%s je uklonio/la unutraÅ¡nju vezu za zadatak #%d', + 'A new internal link for the task #%d has been defined' => 'Nova unutraÅ¡nja veza za zadatak #%d je definisana', + 'Internal link removed for the task #%d' => 'UnutraÅ¡nja veza je uklonjena za zadatak #%d', + '%s set a new internal link for the task %s' => '%s je dodao/la novu unutraÅ¡nju vezu za zadatak %s', + '%s removed an internal link for the task %s' => '%s je uklonio/la unutraÅ¡nju vezu za zadatak %s', + 'Automatically set the due date on task creation' => 'Automatski dodaj rok zavrÅ¡etka na kreiranom zadatku', + 'Move the task to another column when closed' => 'Pomeri zadatak u drugu kolonu kada je zatvoren', + 'Move the task to another column when not moved during a given period' => 'Pomeri zadatak u drugu kolonu kada nema pomeranja zadatka za zadati period', + 'Dashboard for %s' => 'Komandna tabla - %s', + 'Tasks overview for %s' => 'Pregled zadataka za %s', + 'Subtasks overview for %s' => 'Pregled podzadataka za %s', + 'Projects overview for %s' => 'Pregled projekata za %s', + 'Activity stream for %s' => 'Tok aktivnosti za %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Dodeli boju kada je zadatak pomeren u odreÄ‘enu stazu', + 'Assign a priority when the task is moved to a specific swimlane' => 'Dodeli prioritet kada je zadatak pomeren u odreÄ‘enu stazu', + 'User unlocked successfully.' => 'Korisnik je uspeÅ¡no otkljuÄan.', + 'Unable to unlock the user.' => 'Nije moguće otkljuÄati korisnika.', + 'Move a task to another swimlane' => 'Pomeri zadatak u drugu stazu', + 'Creator Name' => 'Ime kreatora', + 'Time spent and estimated' => 'UtroÅ¡eno i procenjeno vrieme', + 'Move position' => 'Mesto pomeranja', + 'Move task to another position on the board' => 'Pomeri zadataka na drugo mesto na tabli', + 'Insert before this task' => 'Umetni pre ovog zadatka', + 'Insert after this task' => 'Umetni posle ovog zadatka', + 'Unlock this user' => 'OtkljuÄaj ovog korisnika', + 'Custom Project Roles' => 'PrilagoÄ‘ne uloge projekta', + 'Add a new custom role' => 'Dodaj novu prilagoÄ‘enu ulogu', + 'Restrictions for the role "%s"' => 'OgraniÄenja za ulogu "%s"', + 'Add a new project restriction' => 'Dodaj nova ograniÄenja na projektu', + 'Add a new drag and drop restriction' => 'Dodaj nova "prevuci i spusti" ograniÄenja', + 'Add a new column restriction' => 'Dodaj nova ograniÄenja za kolonu', + 'Edit this role' => 'Uredi ovu ulogu', + 'Remove this role' => 'Ukloni ovu ulogu', + 'There is no restriction for this role.' => 'Nema ograniÄenja za ovu ulogu.', + 'Only moving task between those columns is permitted' => 'Samo izmeÄ‘u ovih kolona je dozvoljeno pomeranje', + 'Close a task in a specific column when not moved during a given period' => 'Zatvori zadatak u odabranoj koloni kada nema pomeranja za zadati period', + 'Edit columns' => 'Uredi kolone', + 'The column restriction has been created successfully.' => 'OgraniÄenje za kolonu su uspeÅ¡no kreiranao.', + 'Unable to create this column restriction.' => 'Nemoguće kreirati ograniÄenja za kolonu.', + 'Column restriction removed successfully.' => 'OgraniÄenje za kolonu su uspeÅ¡no uklonjeno.', + 'Unable to remove this restriction.' => 'Nije moguće ukloniti ovo ograniÄenje.', + 'Your custom project role has been created successfully.' => 'Tvoja prilagoÄ‘ena uloga na projekatu je uspeÅ¡no kreirana.', + 'Unable to create custom project role.' => 'Nije moguće kreirati prilagoÄ‘enu ulogu na projektu.', + 'Your custom project role has been updated successfully.' => 'Tvoja prilagoÄ‘ena uloga na projekatu je uspeÅ¡no ažurirana.', + 'Unable to update custom project role.' => 'Nije moguće ažurirati prilagoÄ‘enu ulogu na projektu.', + 'Custom project role removed successfully.' => 'PrilagoÄ‘ena uloga na projektu uspeÅ¡no uklonjena.', + 'Unable to remove this project role.' => 'Nije moguće ukloniti ovu ulogu na projektu.', + 'The project restriction has been created successfully.' => 'OgraniÄenje na projektu uspeÅ¡no kreirano.', + 'Unable to create this project restriction.' => 'Nije moguće kreirati ovo ograniÄenje na projektu.', + 'Project restriction removed successfully.' => 'OgraniÄenje na projektu uspeÅ¡no uklonjeno.', + 'You cannot create tasks in this column.' => 'Ne možete kreirati zadatak u ovoj koloni.', + 'Task creation is permitted for this column' => 'Kreiranje zadatka u ovoj koloni je zabranjeno', + 'Closing or opening a task is permitted for this column' => 'Zatvaranje ili otvaranje zadatka u ovoj koloni je zabranjeno', + 'Task creation is blocked for this column' => 'Kreiranje zadatka je onemogućeno za ovu kolonu', + 'Closing or opening a task is blocked for this column' => 'Zatvaranje ili otvaranje zadatka u ovoj koloni je onemogućeno', + 'Task creation is not permitted' => 'Kreiranje zadatka nije dozvoljeno', + 'Closing or opening a task is not permitted' => 'Zatvarnaje ili otvaranje zadatka nije dozvoljeno', + 'New drag and drop restriction for the role "%s"' => 'Novo "prevuci i spusti" ograniÄenje za ulogu "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Osobe kojima se dodeli ova uloga će biti u mogućnosti da pomeraju zadatke samo imeÄ‘u izvorne i odrediÅ¡ne kolone.', + 'Remove a column restriction' => 'Ukloni ograniÄenje za kolonu', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Da li zaista želite da uklonite ovo ograniÄenje za kolonu: "%s" u "%s"?', + 'New column restriction for the role "%s"' => 'Novo ograniÄenje za kolonu za ulogu "%s"', + 'Rule' => 'Pravilo', + 'Do you really want to remove this column restriction?' => 'Da li zaista želite daukloniti ovo ograniÄenje za kolonu?', + 'Custom roles' => 'PrilagoÄ‘ene uloge', + 'New custom project role' => 'Nova prilagoÄ‘ena uloga za projekat', + 'Edit custom project role' => 'Uredi prilagoÄ‘enu ulogu za projekat', + 'Remove a custom role' => 'Ukloni prilagoÄ‘enu ulogu', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Da li zaista želite da uklonite ovu prilagoÄ‘enu ulogu: "%s"? Sve osobe kojima je dodeljena ova uloga će postati Älanovi projekta.', + 'There is no custom role for this project.' => 'Nema prilagoÄ‘enih uloga za ovaj projekat.', + 'New project restriction for the role "%s"' => 'Novo ograniÄenje na projektu za ulogu "%s"', + 'Restriction' => 'OgraniÄenje', + 'Remove a project restriction' => 'Ukloni ograniÄenje na projektu', + 'Do you really want to remove this project restriction: "%s"?' => 'Da li zaista želite da ukloniti ovo ograniÄenje na projektu: "%s"?', + 'Duplicate to multiple projects' => 'Dupliraj u viÅ¡e projekata', + 'This field is required' => 'Ovo polje je obavezno', + 'Moving a task is not permitted' => 'Pomeranje zadatka nije dozvoljeno', + 'This value must be in the range %d to %d' => 'Ova vrednost mora biti u rasponu od %d do %d', + 'You are not allowed to move this task.' => 'NemaÅ¡ dozvolu za pomeranje ovog zadatka.', + 'API User Access' => 'API Pristup korisnika', + 'Preview' => 'Pregled', + 'Write' => 'PiÅ¡i', + 'Write your text in Markdown' => 'UpiÅ¡i tekst u Markdown-u', + 'No personal API access token registered.' => 'Nije registrovan liÄni API pristupni token', + 'Your personal API access token is "%s"' => 'VaÅ¡ liÄni API token za pristup je: "%s"', + 'Remove your token' => 'Uklonite VaÅ¡ token', + 'Generate a new token' => 'GeneriÅ¡i novi token', + 'Showing %d-%d of %d' => 'Prikaži %d-%d od %d', + 'Outgoing Emails' => 'Odlazni email-ovi', + 'Add or change currency rate' => 'Dodajte ili promenite kurs valute', + 'Reference currency: %s' => 'Referentna valuta: %s', + 'Add custom filters' => 'Dodaj prilagoÄ‘ene filtere', + 'Export' => 'Izvezi', + 'Add link label' => 'Dodaj etiketu na vezu', + 'Incompatible Plugins' => 'Nekompatibilni dodaci', + 'Compatibility' => 'Kompatibilnost', + 'Permissions and ownership' => 'Dozvole i vlasniÅ¡tvo', + 'Priorities' => 'Prioriteti', + 'Close this window' => 'Zatvori ovaj prozor', + 'Unable to upload this file.' => 'Nije moguće optremiti ovu datoteku.', + 'Import tasks' => 'Uvezi zadatke', + 'Choose a project' => 'Izaberi projekat', + 'Profile' => 'Profil', + 'Application role' => 'Uloga u aplikaciji', + '%d invitations were sent.' => '%d poslato pozivnica.', + '%d invitation was sent.' => '%d pozivnica poslata.', + 'Unable to create this user.' => 'Nije moguće kreirati ovog korisnika.', + 'Kanboard Invitation' => 'Kanboard pozivnica', + 'Visible on dashboard' => 'Vidljivo na komandnoj tabli', + 'Created at:' => 'Kreirano:', + 'Updated at:' => 'Ažurirano:', + 'There is no custom filter.' => 'Nema prilagoÄ‘enih filtera.', + 'New User' => 'Novi korisnik', + 'Authentication' => 'Autentifikacija', + 'If checked, this user will use a third-party system for authentication.' => 'Ako je oznaÄeno, ovaj korisnik će koristiti nezavisni sistem za autentifikaciju.', + 'The password is necessary only for local users.' => 'Lozinka je neophodna samo za lokalne korisnike.', + 'You have been invited to register on Kanboard.' => 'Pozvani ste da se registrujete na Kanboard.', + 'Click here to join your team' => 'Kliknite ovde da se prodružite VaÅ¡em timu', + 'Invite people' => 'Pozovi ljude', + 'Emails' => 'email-ovi', + 'Enter one email address by line.' => 'Unesi jednu email adresu po redu.', + 'Add these people to this project' => 'Dodaj ove ljude u ovaj projekat', + 'Add this person to this project' => 'Dodaj ovu osobu u ovaj projekat', + 'Sign-up' => 'Registracija', + 'Credentials' => 'Pristupni podaci', + 'New user' => 'Novi korisnik', + 'This username is already taken' => 'Ovo korisniÄko ime je zauzeto', + 'Your profile must have a valid email address.' => 'Profil mora imati validnu email adresu.', + 'TRL - Turkish Lira' => 'TRL - Turska Lira', + 'The project email is optional and could be used by several plugins.' => 'email projekta je opcion i može je koristiti nekoliko dodataka.', + 'The project email must be unique across all projects' => 'email projekta mora biti jedinstven za svaki projekat', + 'The email configuration has been disabled by the administrator.' => 'UreÄ‘ivanje email-a je onemogućeno od strane administratora.', + 'Close this project' => 'Zatvori ovaj projekat', + 'Open this project' => 'Otvori ovaj projekat', + 'Close a project' => 'Zatvori projekat', + 'Do you really want to close this project: "%s"?' => 'Da li zaista želite da zatvorite projekat: "%s"?', + 'Reopen a project' => 'Ponovo otvori projekat', + 'Do you really want to reopen this project: "%s"?' => 'Da li zaista želite da ponovo otvoriti projekat: "%s"?', + 'This project is open' => 'Ovaj projekat je otvoren', + 'This project is closed' => 'Ovaj projekat je zatvoren', + 'Unable to upload files, check the permissions of your data folder.' => 'Nije moguće otpremiti datoteke, proverite prava na direktorijum servera.', + 'Another category with the same name exists in this project' => 'Kategorija sa ovim imenom već postoji na ovom projektu', + 'Comment sent by email successfully.' => 'Komentar je uspeÅ¡no poslat email-om.', + 'Sent by email to "%s" (%s)' => 'Poslato email-om na "%s" (%s)', + 'Unable to read uploaded file.' => 'Nije moguće proÄitati otpremljenu datoteku.', + 'Database uploaded successfully.' => 'Baza podataka je uspeÅ¡no otpremljena.', + 'Task sent by email successfully.' => 'Zadatak je uspeÅ¡no poslat email-om.', + 'There is no category in this project.' => 'Nema kategorija u ovom projektu.', + 'Send by email' => 'PoÅ¡alji email-om', + 'Create and send a comment by email' => 'Napravi i poÅ¡alji komentar email-om', + 'Subject' => 'Predmet', + 'Upload the database' => 'Otpremi bazu podataka', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Možete otpremiti prethodno preuzetu Sqlite bazu podataka (Gzip format).', + 'Database file' => 'Datoteka baze podataka', + 'Upload' => 'Otpremi', + 'Your project must have at least one active swimlane.' => 'Projekat mora imati barem jednu aktivnu stazu.', + 'Project: %s' => 'Projekat: %s', + 'Automatic action not found: "%s"' => 'Automatska akcija nije pronaÄ‘ena: "%s"', + '%d projects' => '%d projekata', + '%d project' => '%d projekat', + 'There is no project.' => 'Nema projekta.', + 'Sort' => 'Sortiraj', + 'Project ID' => 'ID projekta', + 'Project name' => 'Naziv projekta', + 'Public' => 'Javno', + 'Personal' => 'LiÄno', + '%d tasks' => '%d zadataka', + '%d task' => '%d zadatak', + 'Task ID' => 'ID zadatka', + 'Assign automatically a color when due date is expired' => 'Automatski postavi boju kada je rok zavrÅ¡etka istekao', + 'Total score in this column across all swimlanes' => 'Ukupni rezultat u ovoj koloni za sve staze', + 'HRK - Kuna' => 'HRK - Hrvatska Kuna', + 'ARS - Argentine Peso' => 'ARS - Argentiski pezos', + 'COP - Colombian Peso' => 'COP - Kolumbijski pezos', + '%d groups' => '%d grupe', + '%d group' => '%d grupa', + 'Group ID' => 'ID grupe', + 'External ID' => 'Spoljni ID', + '%d users' => '%d korisnika', + '%d user' => '%d korisnik', + 'Hide subtasks' => 'Sakrij podzadatke', + 'Show subtasks' => 'Prikaži podzadatke', + 'Authentication Parameters' => 'Parametri za atentifikaciju', + 'API Access' => 'API pristup', + 'No users found.' => 'Nema ponaÄ‘enih korisnika.', + 'User ID' => 'ID korisnika', + 'Notifications are activated' => 'Notifikacije su omogućene', + 'Notifications are disabled' => 'Notifikacije su onemogućene', + 'User disabled' => 'Korisnik je onemogućen', + '%d notifications' => '%d notifkacija/e', + '%d notification' => '%d notifikacija', + 'There is no external integration installed.' => 'Nije instalirana spoljna integracija.', + 'You are not allowed to update tasks assigned to someone else.' => 'Nije vam dozvoljeno da ažurirate zadatke dodeljene nekom drugom.', + 'You are not allowed to change the assignee.' => 'Nije vam dozvoljeno da promenite izvrÅ¡ioca.', + 'Task suppression is not permitted' => 'Suzbijanje zadataka nije dozvoljeno', + 'Changing assignee is not permitted' => 'Menjanje izvrÅ¡ioca nije dozvoljeno', + 'Update only assigned tasks is permitted' => 'Dozvoljeno je ažuriranje samo dodeljenih zadataka', + 'Only for tasks assigned to the current user' => 'Samo za zadatke dodeljene trenutnom korisniku', + 'My projects' => 'Moji projekti', + 'You are not a member of any project.' => 'Niste Älan nijednog projekta.', + 'My subtasks' => 'Moji podzadaci.', + '%d subtasks' => '%d podzadataka', + '%d subtask' => '%d podzadatak', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Samo premeÅ¡tanja zadatka izmeÄ‘u tih kolona je dozvoljeno za zadatke dodeljene trenutnom korisniku', + '[DUPLICATE]' => '[DUPLIKAT]', + 'DKK - Danish Krona' => 'DKK - Danska kruna', + 'Remove user from group' => 'Ukloni korisnika iz grupe', + 'Assign the task to its creator' => 'Dodeli zadatak njegovom kreatoru', + 'This task was sent by email to "%s" with subject "%s".' => 'Ovaj zadatak je poslat email-om na "%s" sa predmetom "%s".', + 'Predefined Email Subjects' => 'Unapred definisani predmeti e-poÅ¡te', + 'Write one subject by line.' => 'Unesite jedan predmet po liniji', + 'Create another link' => 'Napravi joÅ¡ jednu vezu', + 'BRL - Brazilian Real' => 'BRL - Brazilski real', + 'Add a new Kanboard task' => 'Dodajte novi Kanboard zadatak', + 'Subtask not started' => 'Podzadatak nije zapoÄet', + 'Subtask currently in progress' => 'Podzadatak je trenutno u toku', + 'Subtask completed' => 'Podzadatak zavrÅ¡en', + 'Subtask added successfully.' => 'Podzadatak uspeÅ¡no dodat.', + '%d subtasks added successfully.' => '%d podzadataka uspeÅ¡no dodato.', + 'Enter one subtask by line.' => 'Unesite jedan podzadatak po liniji.', + 'Predefined Contents' => 'Predefnisani sadržaj', + 'Predefined contents' => 'Predefnisani sadržaj', + 'Predefined Task Description' => 'Predefinisan opis zadatka', + 'Do you really want to remove this template? "%s"' => 'Da li zaista želite da uklonite ovaj Å¡ablon? "%s"', + 'Add predefined task description' => 'Dodat predefinsani opis zadatka', + 'Predefined Task Descriptions' => 'Predefinisani opis zadatka', + 'Template created successfully.' => 'Å ablon uspeÅ¡no kreiran', + 'Unable to create this template.' => 'Nije moguće kreirati ovaj Å¡ablon', + 'Template updated successfully.' => 'Å ablon uspeÅ¡no ažuriran', + 'Unable to update this template.' => 'Nije moguće ažurirati ovaj Å¡ablon', + 'Template removed successfully.' => 'Å ablon uspeÅ¡no uklonjen', + 'Unable to remove this template.' => 'Nije moguće ukloniti ovaj Å¡ablon', + 'Template for the task description' => 'Å ablon za opis zadatka', + 'The start date is greater than the end date' => 'PoÄetni datum je veći od kranjeg datuma', + 'Tags must be separated by a comma' => 'Oznake moraju biti razdvojene zarezom', + 'Only the task title is required' => 'Samo naslov zadatka je obavezan', + 'Creator Username' => 'KorisniÄko ime kreatora', + 'Color Name' => 'Boja kolone', + 'Column Name' => 'Naziv kolone', + 'Swimlane Name' => 'Naziv staze', + 'Time Estimated' => 'Procenjeno vreme', + 'Time Spent' => 'UtroÅ¡eno vreme', + 'External Link' => 'Spoljna veza', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Ova funckionalnost omogućava iCal kanal, RSS kanal i prikaz javne table.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Zaustavi tajmer na svim podzadacima prilikom premeÅ¡tanja zadatka u drugu kolonu', + 'Subtask Title' => 'Naslov podzadatka', + 'Add a subtask and activate the timer when moving a task to another column' => 'Dodaj podzadatak i aktiviraj tajmer prilikom premeÅ¡tanja zadatka u drugu kolonu', + 'days' => 'dana', + 'minutes' => 'minuta', + 'seconds' => 'sekundi', + 'Assign automatically a color when preset start date is reached' => 'Automatski postavi boju kada se dostigne unapred zadati datum poÄetka', + 'Move the task to another column once a predefined start date is reached' => 'Premestite zadatak u drugu kolonu kada se dostigne unapred definisani datum poÄetka', + 'This task is now linked to the task %s with the relation "%s"' => 'Zadatak je sada povezan sa zadatkom %s sa relacijom "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Veza sa relacijom "%s" sa zadatkom %s je uklonjena', + 'Custom Filter:' => 'PrilagoÄ‘eni filter', + 'Unable to find this group.' => 'Nije moguće pronaći ovu grupu.', + '%s moved the task #%d to the column "%s"' => '%s je pomerio/la zadatak #%d u kolonu "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s je pomerio/la zadatakk #%d na poziciju %d u koloni "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s je pomerio/la zadatak #%d u stazu "%s"', + '%sh spent' => '%sÄ potroÅ¡eno', + '%sh estimated' => '%sÄ procenjeno', + 'Select All' => 'Izaberi sve', + 'Unselect All' => 'PoniÅ¡ti sve', + 'Apply action' => 'Primeni akciju', + 'Move selected tasks to another column or swimlane' => 'Pomerei odabrani zadatak u neku drugu kolonu ili stazu', + 'Edit tasks in bulk' => 'Masovno ureÄ‘ivanje zadataka', + 'Choose the properties that you would like to change for the selected tasks.' => 'Izaberite svojstva koja biste želeli da promenite za izabrane zadatke.', + 'Configure this project' => 'KonfiguriÅ¡ite ovaj projekat', + 'Start now' => 'ZapoÄni sada', + '%s removed a file from the task #%d' => '%s je uklonio/la dataoteku iz zadatka #%d', + 'Attachment removed from task #%d: %s' => 'Dodatak je uklonjen iz zadatka #%d: %s', + 'No color' => 'Nema boju', + 'Attachment removed "%s"' => 'Priložak uklonjen "%s"', + '%s removed a file from the task %s' => '%s je uklonio/la datoteku iz zadatka %s', + 'Move the task to another swimlane when assigned to a user' => 'Premesti zadatak u drugu stazu kada je zadatak dodeljen korisniku', + 'Destination swimlane' => 'OdrediÅ¡na staza', + 'Assign a category when the task is moved to a specific swimlane' => 'Postavi kategoriju kada je zadatak premeÅ¡ten u odreÄ‘enu stazu', + 'Move the task to another swimlane when the category is changed' => 'Premesti zadatak u drugu stazu kada se promeni kategorija', + 'Reorder this column by priority (ASC)' => 'Promenite redosled ove kolone prema prioritetu (UZLAZNO)', + 'Reorder this column by priority (DESC)' => 'Promenite redosled ove kolone prema prioritetu (SILAZNO)', + 'Reorder this column by assignee and priority (ASC)' => 'Promenite redosled ove kolone prema izvrÅ¡iocu (UZLAZNO)', + 'Reorder this column by assignee and priority (DESC)' => 'Promenite redosled ove kolone prema izvrÅ¡iocu (SILAZNO)', + 'Reorder this column by assignee (A-Z)' => 'Promenite redosled ove kolone prema izvrÅ¡iocu (A-Ž)', + 'Reorder this column by assignee (Z-A)' => 'Promenite redosled ove kolone prema izvrÅ¡iocu (Ž-A)', + 'Reorder this column by due date (ASC)' => 'Promenite redosled ove kolone prema roku zavrÅ¡etka (UZLAZNO)', + 'Reorder this column by due date (DESC)' => 'Promenite redosled ove kolone prema roku zavrÅ¡etka (SILAZNO)', + 'Reorder this column by id (ASC)' => 'Promenite redosled ove kolone prema ID-u (UZLAZNO)', + 'Reorder this column by id (DESC)' => 'Promenite redosled ove kolone prema ID-u (SILAZNO)', + '%s moved the task #%d "%s" to the project "%s"' => '%s je pomerio/la zadatak #%d "%s" u projekat "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Zadatak #%d "%s" je premeÅ¡ten u projekat "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Pomeri zadatak u neku drugu kolonu kada je datum roka zavrÅ¡etka kraći od odreÄ‘enog broja dana', + 'Automatically update the start date when the task is moved away from a specific column' => 'Automatski ažuriraj datum poÄetka kada se zadatak ukloni iz odreÄ‘ene kolone', + 'HTTP Client:' => 'HTTP klijent', + 'Assigned' => 'Dodeljeno', + 'Task limits apply to each swimlane individually' => 'OgraniÄenje broja zadataka važi za svaku stazu zasebno', + 'Column task limits apply to each swimlane individually' => 'OgraniÄenje broja zadataka u koloni važi za svaku stazu zasebno', + 'Column task limits are applied to each swimlane individually' => 'OgraniÄenje broja zadataka u koloni se primenjuje na svaku stazu zasebno', + 'Column task limits are applied across swimlanes' => 'OgraniÄenje broja zadataka u koloni se primenjuje na viÅ¡e staza', + 'Task limit: ' => 'OgraniÄenje broja zadataka: ', + 'Change to global tag' => 'Promeni u globalnu oznaku', + 'Do you really want to make the tag "%s" global?' => 'Da li zaista želite da oznaku "%s" uÄinite globalnom?', + 'Enable global tags for this project' => 'Omogući globalne oznake za ovaj projekat', + 'Group membership(s):' => 'ÄŒlanstvo u grupi/grupama:', + '%s is a member of the following group(s): %s' => '%s je Älan grupa(e): %s', + '%d/%d group(s) shown' => '%d/%d prikazane grupa(e)', + 'Subtask creation or modification' => 'Kreiranje ili modifikacija podzadatka', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Dodeli zadatak odreÄ‘enom korisnika kada je zadatak prebaÄen u odreÄ‘enu stazu', + 'Comment' => 'Komentar', + 'Collapse vertically' => 'Skupi vertikalno', + 'Expand vertically' => 'RaÅ¡iri vertikalno', + 'MXN - Mexican Peso' => 'MXN - MeksiÄki pezos', + 'Estimated vs actual time per column' => 'Procenjeno vreme naspram stvarnog vremena po koloni', + 'HUF - Hungarian Forint' => 'HUF - MaÄ‘arska forinta', + 'XBT - Bitcoin' => 'XBT - Bitkoin', + 'You must select a file to upload as your avatar!' => 'Morate izabrati datoteku koju ćete postaviti kao avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Datoteka koju ste otpremili nije validna slika! (Dozvoljeni su samo *.gif, *.jpg, *.jpeg i *.png formati!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Automatski postavite rok zavrÅ¡etka kada se zadatak premesti iz odreÄ‘ene kolone', + 'No other projects found.' => 'Nisu pronaÄ‘eni drugi projekti.', + 'Tasks copied successfully.' => 'Zadaci su uspeÅ¡no iskopirani.', + 'Unable to copy tasks.' => 'Nije moguće kopirati zadatke.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Svetla tema', + 'Dark theme' => 'Tamna tema', + 'Automatic theme - Sync with system' => 'Automatska tema - sinhronizacija sa sistemom', + 'Application managers or more' => 'Menadžeri aplikacije ili viÅ¡e', + 'Administrators' => 'Administratori', + 'Visibility:' => 'Vidljivost:', + 'Standard users' => 'Standardni korisnici', + 'Visibility is required' => 'Vidljivost je obavezna', + 'The visibility should be an app role' => 'Vidljivost treba da bude uloga aplikacije', + 'Reply' => 'Odgovori', + '%s wrote: ' => '%s je napisao/la: ', + 'Number of visible tasks in this column and swimlane' => 'Broj vidljivih zadataka u ovoj koloni i plivaÄkoj stazi', + 'Number of tasks in this swimlane' => 'Broj zadataka u ovoj plivaÄkoj stazi', + 'Unable to find another subtask in progress, you can close this window.' => 'Nije moguće pronaći drugi podzadatak u toku, možete zatvoriti ovaj prozor.', + 'This theme is invalid' => 'Ova tema nije validna', + 'This role is invalid' => 'Ova uloga nije validna', + 'This timezone is invalid' => 'Ova vremenska zona nije validna', + 'This language is invalid' => 'Ovaj jezik nije validan', + 'This URL is invalid' => 'Ovaj URL nije validan', + 'Date format invalid' => 'Format datuma nije validan', + 'Time format invalid' => 'Format vremena nije validan', + 'Invalid Mail transport' => 'Neispravan naÄin slanja poÅ¡te', + 'Color invalid' => 'Neispravna boja', + 'This value must be greater or equal to %d' => 'Ova vrednost mora biti veća ili jednaka %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Dodaj BOM na poÄetak fajla (potrebno za Microsoft Excel)', + 'Just add these tag(s)' => 'Samo dodaj ove oznake', + 'Remove internal link(s)' => 'Ukloni interne linkove', + 'Import tasks from another project' => 'Uvezi zadatke iz drugog projekta', + 'Select the project to copy tasks from' => 'Izaberi projekat iz kog želiÅ¡ da kopiraÅ¡ zadatke', + 'The total maximum allowed attachments size is %sB.' => 'Ukupna dozvoljena veliÄina priloga je %sB.', + 'Add attachments' => 'Dodaj priloge', + 'Task #%d "%s" is overdue' => 'Zadatak #%d "%s" je s izteÄen rok', + 'Enable notifications by default for all new users' => 'Omogući notifikacije po difoltu za sve nove korisnike', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Dodeli zadatak njegovom kreatoru za odreÄ‘ene kolone ako izvrÅ¡ilac nije ruÄno postavljen', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Dodeli zadatak prijavljenom korisniku pri promeni kolone u zadatu kolonu ako niko nije dodeljen', +]; diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php new file mode 100644 index 0000000..68d3131 --- /dev/null +++ b/app/Locale/sv_SE/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Ingen', + 'Edit' => 'Redigera', + 'Remove' => 'Ta bort', + 'Yes' => 'Ja', + 'No' => 'Nej', + 'cancel' => 'avbryt', + 'or' => 'eller', + 'Yellow' => 'Gul', + 'Blue' => 'BlÃ¥', + 'Green' => 'Grön', + 'Purple' => 'Lila', + 'Red' => 'Röd', + 'Orange' => 'Orange', + 'Grey' => 'GrÃ¥', + 'Brown' => 'Brun', + 'Deep Orange' => 'Mörkorange', + 'Dark Grey' => 'MörkgrÃ¥', + 'Pink' => 'Rosa', + 'Teal' => 'GrönblÃ¥', + 'Cyan' => 'Cyan', + 'Lime' => 'Lime', + 'Light Green' => 'Ljusgrön', + 'Amber' => 'Bärnsten', + 'Save' => 'Spara', + 'Login' => 'Login', + 'Official website:' => 'Officiell webbsida:', + 'Unassigned' => 'Ej tilldelad', + 'View this task' => 'Se denna uppgift', + 'Remove user' => 'Ta bort användare', + 'Do you really want to remove this user: "%s"?' => 'Vill du verkligen ta bort användaren: "%s"?', + 'All users' => 'Alla användare', + 'Username' => 'Användarnamn', + 'Password' => 'Lösenord', + 'Administrator' => 'Administratör', + 'Sign in' => 'Logga in', + 'Users' => 'Användare', + 'Forbidden' => 'Ej tillÃ¥ten', + 'Access Forbidden' => 'Ej tillÃ¥ten', + 'Edit user' => 'Ändra användare', + 'Logout' => 'Logga ut', + 'Bad username or password' => 'Fel användarnamn eller lösenord', + 'Edit project' => 'Ändra projekt', + 'Name' => 'Namn', + 'Projects' => 'Projekt', + 'No project' => 'Inget projekt', + 'Project' => 'Projekt', + 'Status' => 'Status', + 'Tasks' => 'Uppgifter', + 'Board' => 'Tavla', + 'Actions' => 'Ã…tgärder', + 'Inactive' => 'Inaktiv', + 'Active' => 'Aktiv', + 'Unable to update this board.' => 'Kunde inte uppdatera 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"', + 'Add a new column' => 'Lägg till ny kolumn', + 'Title' => 'Titel', + 'Assigned to %s' => 'Tilldelad %s', + 'Remove a column' => 'Ta bort en kolumn', + '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"?', + 'Settings' => 'Inställningar', + 'Application settings' => 'Applikationsinställningar', + 'Language' => 'SprÃ¥k', + 'Webhook token:' => 'Token för webhooks:', + 'API token:' => 'API token:', + 'Database size:' => 'Databasstorlek:', + 'Download the database' => 'Ladda ner databasen', + 'Optimize the database' => 'Optimera databasen', + '(VACUUM command)' => '(Vacuum-kommando)', + '(Gzip compressed Sqlite file)' => '(Gzip-komprimera Sqlite-filen)', + 'Close a task' => 'Stäng en uppgift', + 'Column' => 'Kolumn', + 'Color' => 'Färg', + 'Assignee' => 'Uppdragsinnehavare', + 'Create another task' => 'Skapa ännu en uppgift', + 'New task' => 'Ny uppgift', + 'Open a task' => 'Öppna en uppgift', + 'Do you really want to open this task: "%s"?' => 'Vill du verkligen öppna denna uppgift: "%s"?', + 'Back to the board' => 'Tillbaka till tavlan', + 'There is nobody assigned' => 'Det finns ingen tilldelad', + 'Column on the board:' => 'Kolumn pÃ¥ tavlan:', + 'Close this task' => 'Stäng uppgiften', + 'Open this task' => 'Öppna uppgiften', + 'There is no description.' => 'Det finns ingen beskrivning.', + 'Add a new task' => 'Lägg till en ny uppgift', + 'The username is required' => 'Användarnamnet mÃ¥ste anges', + 'The maximum length is %d characters' => 'Max antal bokstäver är %d', + 'The minimum length is %d characters' => 'Minst antal bokstäver är %d', + 'The password is required' => 'Lösenordet mÃ¥ste anges.', + 'This value must be an integer' => 'Värdet mÃ¥ste vara ett heltal.', + 'The username must be unique' => 'Användarnamnet mÃ¥ste vara unikt', + 'The user id is required' => 'Användar-ID mÃ¥ste anges', + 'Passwords don\'t match' => 'Lösenorden matchar inte', + 'The confirmation is required' => 'Bekräftelse behövs.', + 'The project is required' => 'Projektet mÃ¥ste anges', + 'The id is required' => 'Aktuellt ID mÃ¥ste anges', + 'The project id is required' => 'Projekt-ID mÃ¥ste anges', + 'The project name is required' => 'Ett projektnamn mÃ¥ste anges', + 'The title is required' => 'En titel mÃ¥ste anges.', + 'Settings saved successfully.' => 'Inställningarna har sparats.', + 'Unable to save your settings.' => 'Kunde inte spara dina ändringar', + 'Database optimization done.' => 'Databasen har optimerats.', + 'Your project has been created successfully.' => 'Ditt projekt har skapats.', + 'Unable to create your project.' => 'Kunde inte skapa ditt projekt.', + 'Project updated successfully.' => 'Projektet har uppdaterats.', + 'Unable to update this project.' => 'Kunde inte uppdatera detta projekt.', + 'Unable to remove this project.' => 'Kunde inte ta bort detta projekt.', + 'Project removed successfully.' => 'Projektet har tagits bort.', + 'Project activated successfully.' => 'Projektet har aktiverats.', + 'Unable to activate this project.' => 'Kunde inte aktivera detta projekt.', + 'Project disabled successfully.' => 'Projektet har inaktiverats.', + 'Unable to disable this project.' => 'Kunde inte inaktivera detta projekt.', + 'Unable to open this task.' => 'Kunde inte öppna denna uppgift.', + 'Task opened successfully.' => 'Uppgiften har öppnats.', + 'Unable to close this task.' => 'Kunde inte stänga denna uppgift.', + 'Task closed successfully.' => 'Uppgiften har stängts.', + 'Unable to update your task.' => 'Kunde inte uppdatera din uppgift.', + 'Task updated successfully.' => 'Uppgiften har uppdaterats.', + 'Unable to create your task.' => 'Kunde inte skapa din uppgift.', + 'Task created successfully.' => 'Uppgiften har skapats.', + 'User created successfully.' => 'Användaren har skapats.', + 'Unable to create your user.' => 'Kunde inte skapa din användare.', + 'User updated successfully.' => 'Användaren har updaterats.', + 'User removed successfully.' => 'Användaren har tagits bort.', + 'Unable to remove this user.' => 'Kunde inte ta bort denna användare.', + 'Board updated successfully.' => 'Tavlan uppdaterad.', + 'Ready' => 'Denna mÃ¥nad', + 'Backlog' => 'Att göra', + 'Work in progress' => 'PÃ¥gÃ¥ende', + 'Done' => 'Slutfört', + 'Application version:' => 'Version:', + 'Id' => 'ID', + 'Public link' => 'Publik länk', + 'Timezone' => 'Tidszon', + 'Sorry, I didn\'t find this information in my database!' => 'Informationen kunde inte hittas i databasen.', + 'Page not found' => 'Sidan hittas inte', + 'Complexity' => 'Komplexitet', + 'Task limit' => 'Uppgiftsbegränsning', + 'Task count' => 'Antal uppgifter', + 'User' => 'Användare', + 'Comments' => 'Kommentarer', + 'Comment is required' => 'En kommentar mÃ¥ste lämnas', + 'Comment added successfully.' => 'Kommentaren har lagts till.', + 'Unable to create your comment.' => 'Kommentaren kunde inte laddas upp.', + 'Due Date' => 'MÃ¥ldatum', + 'Invalid date' => 'Ej tillÃ¥tet datum', + 'Automatic actions' => 'Automatiska Ã¥tgärder', + 'Your automatic action has been created successfully.' => 'Din automatiska Ã¥tgärd har skapats.', + 'Unable to create your automatic action.' => 'Kunde inte skapa din automatiska Ã¥tgärd.', + 'Remove an action' => 'Ta bort en Ã¥tgärd', + 'Unable to remove this action.' => 'Kunde inte ta bort denna Ã¥tgärd.', + 'Action removed successfully.' => 'Ã…tgärden har tagits bort.', + 'Automatic actions for the project "%s"' => 'Automatiska Ã¥tgärder för projektet "%s"', + 'Add an action' => 'Lägg till en Ã¥tgärd', + 'Event name' => 'Händelsenamn', + 'Action' => 'Ã…tgärd', + 'Event' => 'Händelse', + 'When the selected event occurs execute the corresponding action.' => 'När händelsen inträffar, kör inställd Ã¥tgärd.', + 'Next step' => 'Nästa steg', + 'Define action parameters' => 'Definiera händelseparametrar', + 'Do you really want to remove this action: "%s"?' => 'Vill du verkligen ta bort denna Ã¥tgärd: "%s"?', + 'Remove an automatic action' => 'Ta bort en automatiskt Ã¥tgärd', + 'Assign the task to a specific user' => 'Tilldela uppgiften till en viss användare', + 'Assign the task to the person who does the action' => 'Tilldela uppgiften till personen som skapar den', + 'Duplicate the task to another project' => 'Kopiera uppgiften till ett annat projekt', + 'Move a task to another column' => 'Flytta en uppgift till en annan kolumn', + 'Task modification' => 'Ändra uppgift', + 'Task creation' => 'Skapa uppgift', + 'Closing a task' => 'Stänger en uppgift', + 'Assign a color to a specific user' => 'Tilldela en färg till en viss användare', + 'Position' => 'Position', + 'Duplicate to project' => 'Kopiera till ett annat projekt', + 'Duplicate' => 'Kopiera uppgiften', + 'Link' => 'Länk', + 'Comment updated successfully.' => 'Kommentaren har uppdaterats.', + 'Unable to update your comment.' => 'Kunde inte uppdatera din kommentar.', + 'Remove a comment' => 'Ta bort en kommentar', + 'Comment removed successfully.' => 'Kommentaren har tagits bort.', + 'Unable to remove this comment.' => 'Kunde inte ta bort denna kommentar.', + 'Do you really want to remove this comment?' => 'Är du säker pÃ¥ att du vill ta bort denna kommentar?', + 'Current password for the user "%s"' => 'Nuvarande lösenord för användaren "%s"', + 'The current password is required' => 'Nuvarande lösenord mÃ¥ste anges', + 'Wrong password' => 'Fel lösenord', + 'Unknown' => 'Okänd', + 'Last logins' => 'Senaste inloggningarna', + 'Login date' => 'Inloggningsdatum', + 'Authentication method' => 'Autentiseringsmetod', + 'IP address' => 'IP-adress', + 'User agent' => 'Användaragent/webbläsare', + 'Persistent connections' => 'Beständiga anslutningar', + 'No session.' => 'Ingen session.', + 'Expiration date' => 'Förfallodatum', + 'Remember Me' => 'Kom ihÃ¥g mig', + 'Creation date' => 'Skapandedatum', + 'Everybody' => 'Alla', + 'Open' => 'Öppen', + 'Closed' => 'Stängd', + 'Search' => 'Sök', + 'Nothing found.' => 'Inget kunde hittas.', + 'Due date' => 'MÃ¥ldatum', + 'Description' => 'Beskrivning', + '%d comments' => '%d kommentarer', + '%d comment' => '%d kommentar', + 'Email address invalid' => 'Epost-adressen ogiltig', + 'Your external account is not linked anymore to your profile.' => 'Ditt externa konto är inte längre länkat till din profil.', + 'Unable to unlink your external account.' => 'Kunde inte koppla frÃ¥n ditt externa konto.', + 'External authentication failed' => 'Extern autentisering misslyckades', + 'Your external account is linked to your profile successfully.' => 'Ditt externa konto länkades till din profil.', + 'Email' => 'Epost', + 'Task removed successfully.' => 'Uppgiften har tagits bort.', + 'Unable to remove this task.' => 'Kunde inte ta bort denna uppgift.', + 'Remove a task' => 'Ta bort en uppgift', + 'Do you really want to remove this task: "%s"?' => 'Vill du verkligen ta bort denna uppgift: "%s"?', + 'Assign automatically a color based on a category' => 'Tilldela automatiskt en färg baserat pÃ¥ en kategori', + 'Assign automatically a category based on a color' => 'Tilldela automatiskt en kategori baserat pÃ¥ en färg', + 'Task creation or modification' => 'Skapa eller ändra uppgift', + 'Category' => 'Kategori', + 'Category:' => 'Kategori:', + 'Categories' => 'Ange kategorier', + 'Your category has been created successfully.' => 'Din kategori har skapats.', + 'This category has been updated successfully.' => 'Din kategori har uppdaterats.', + 'Unable to update this category.' => 'Kunde inte uppdatera din kategori.', + 'Remove a category' => 'Ta bort en kategori', + 'Category removed successfully.' => 'Kategorin har tagits bort.', + 'Unable to remove this category.' => 'Kunde inte ta bort denna kategori.', + 'Category modification for the project "%s"' => 'Ändring av kategori för projektet "%s"', + 'Category Name' => 'Kategorinamn', + 'Add a new category' => 'Lägg till en kategori', + 'Do you really want to remove this category: "%s"?' => 'Vill du verkligen ta bort denna kategori: "%s"?', + 'All categories' => 'Alla kategorier', + 'No category' => 'Ingen kategori', + 'The name is required' => 'Namnet mÃ¥ste anges', + 'Remove a file' => 'Ta bort en fil', + 'Unable to remove this file.' => 'Kunde inte ta bort denna fil.', + 'File removed successfully.' => 'Filen har tagits bort.', + 'Attach a document' => 'Bifoga ett dokument', + 'Do you really want to remove this file: "%s"?' => 'Vill du verkligen ta bort denna fil: "%s"?', + 'Attachments' => 'Bifogade filer', + 'Edit the task' => 'Ändra uppgiften', + 'Add a comment' => 'Lägg till kommentar', + 'Edit a comment' => 'Ändra en kommentar', + 'Summary' => 'Sammanfattning', + 'Time tracking' => 'TidsÃ¥tgÃ¥ng', + 'Estimate:' => 'Uppskattning', + 'Spent:' => 'Nedlagd tid', + 'Do you really want to remove this sub-task?' => 'Vill du verkligen ta bort deluppgiften?', + 'Remaining:' => 'Ã…terstÃ¥ende:', + 'hours' => 'timmar', + 'estimated' => 'uppskattat', + 'Sub-Tasks' => 'Deluppgifter', + 'Add a sub-task' => 'Lägg till deluppgift', + 'Original estimate' => 'Ursprunglig uppskattning', + 'Create another sub-task' => 'Skapa en till deluppgift', + 'Time spent' => 'Nedlagd tid', + 'Edit a sub-task' => 'Ändra en deluppgift', + 'Remove a sub-task' => 'Ta bort en deluppgift', + 'The time must be a numeric value' => 'Tiden mÃ¥ste ha ett numeriskt värde', + 'Todo' => 'Att göra', + 'In progress' => 'PÃ¥gÃ¥ende', + 'Sub-task removed successfully.' => 'Deluppgiften har tagits bort.', + 'Unable to remove this sub-task.' => 'Kunde inte ta bort denna deluppgift.', + 'Sub-task updated successfully.' => 'Deluppgiften har uppdaterats.', + 'Unable to update your sub-task.' => 'Kunde inte uppdatera din deluppgift.', + 'Unable to create your sub-task.' => 'Kunde inte skapa din deluppgift.', + 'Maximum size: ' => 'Maxstorlek: ', + 'Display another project' => 'Visa ett annat projekt', + 'Created by %s' => 'Skapad av %s', + 'Tasks Export' => 'Exportera uppgifter', + 'Start Date' => 'Startdatum', + 'Execute' => 'Utför', + 'Task Id' => 'Uppgift-ID', + 'Creator' => 'Skapare', + 'Modification date' => 'Ändringsdatum', + 'Completion date' => 'Slutfört datum', + 'Clone' => 'Klona', + 'Project cloned successfully.' => 'Projektet har klonats.', + 'Unable to clone this project.' => 'Kunde inte klona projektet.', + 'Enable email notifications' => 'Aktivera e-postnotiser', + 'Task position:' => 'Uppgiftsposition:', + 'The task #%d has been opened.' => 'Uppgiften #%d har öppnats.', + 'The task #%d has been closed.' => 'Uppgiften #%d har stängts.', + 'Sub-task updated' => 'Deluppgift uppdaterad', + 'Title:' => 'Titel:', + 'Status:' => 'Status:', + 'Assignee:' => 'Tilldelad:', + 'Time tracking:' => 'TidsspÃ¥rning', + 'New sub-task' => 'Ny deluppgift', + 'New attachment added "%s"' => 'Ny bifogning tillagd "%s"', + 'New comment posted by %s' => 'Ny kommentar postad av %s', + 'New comment' => 'Ny kommentar', + 'Comment updated' => 'Kommentaren har uppdaterats', + 'New subtask' => 'Ny deluppgift', + 'I only want to receive notifications for these projects:' => 'Jag vill endast fÃ¥ notiser för dessa projekt:', + 'view the task on Kanboard' => 'Visa uppgiften pÃ¥ Kanboard', + 'Public access' => 'Publik Ã¥tkomst', + 'Disable public access' => 'Inaktivera publik Ã¥tkomst', + 'Enable public access' => 'Aktivera publik Ã¥tkomst', + 'Public access disabled' => 'Publik Ã¥tkomst har inaktiverats', + 'Move the task to another project' => 'Flytta uppgiften till ett annat projekt', + 'Move to project' => 'Flytta till ett annat projekt', + 'Do you really want to duplicate this task?' => 'Vill du verkligen kopiera denna uppgift?', + 'Duplicate a task' => 'Kopiera en uppgift', + 'External accounts' => 'Externa konton', + 'Account type' => 'Kontotyp', + 'Local' => 'Lokal', + 'Remote' => 'Fjärr', + 'Enabled' => 'Aktiverad', + 'Disabled' => 'Inaktiverad', + 'Login:' => 'Användarnam:', + 'Full Name:' => 'Namn:', + 'Email:' => 'E-post:', + 'Notifications:' => 'Notiser:', + 'Notifications' => 'Notiser', + 'Account type:' => 'Kontotyp:', + 'Edit profile' => 'Ändra profil', + 'Change password' => 'Byt lösenord', + 'Password modification' => 'Ändra lösenord', + 'External authentications' => 'Extern autentisering', + 'Never connected.' => 'Inte ansluten.', + 'No external authentication enabled.' => 'Ingen extern autentisering aktiverad.', + 'Password modified successfully.' => 'Lösenordet har ändrats.', + 'Unable to change the password.' => 'Kunde inte byta lösenord.', + 'Change category' => 'Byt kategori', + '%s updated the task %s' => '%s uppdaterade uppgiften %s', + '%s opened the task %s' => '%s öppna uppgiften %s', + '%s moved the task %s to the position #%d in the column "%s"' => '%s flyttade uppgiften %s till positionen #%d i kolumnen "%s"', + '%s moved the task %s to the column "%s"' => '%s flyttade uppgiften %s till kolumnen "%s"', + '%s created the task %s' => '%s skapade uppgiften %s', + '%s closed the task %s' => '%s stängde uppgiften %s', + '%s created a subtask for the task %s' => '%s skapade en deluppgift för uppgiften %s', + '%s updated a subtask for the task %s' => '%s uppdaterade en deluppgift för uppgiften %s', + 'Assigned to %s with an estimate of %s/%sh' => 'Tilldelades %s med en uppskattning pÃ¥ %s/%sh', + 'Not assigned, estimate of %sh' => 'Inte tilldelade, uppskattat %sh', + '%s updated a comment on the task %s' => '%s uppdaterade en kommentar till uppgiften %s', + '%s commented the task %s' => '%s kommenterade uppgiften %s', + '%s\'s activity' => '%ss aktivitet', + 'RSS feed' => 'RSS-flöde', + '%s updated a comment on the task #%d' => '%s uppdaterade en kommentar pÃ¥ uppgiften #%d', + '%s commented on the task #%d' => '%s kommenterade uppgiften #%d', + '%s updated a subtask for the task #%d' => '%s uppdaterade en deluppgift för uppgiften #%d', + '%s created a subtask for the task #%d' => '%s skapade en deluppgift för uppgiften #%d', + '%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 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 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', + 'Create a task from an external provider' => 'Skapa en uppgift frÃ¥n en extern leverantör', + 'Change the assignee based on an external username' => 'Ändra tilldelning baserat pÃ¥ ett externt användarnamn', + 'Change the category based on an external label' => 'Ändra kategori baserat pÃ¥ en extern etikett', + 'Reference' => 'Referens', + 'Label' => 'Etikett', + 'Database' => 'Databas', + 'About' => 'Om', + 'Database driver:' => 'Databasdrivrutin:', + 'Board settings' => 'Inställningar för tavla', + 'Webhook settings' => 'Webhook-inställningar', + 'Reset token' => 'Nollställ token', + 'API endpoint:' => 'API-ändpunkt:', + 'Refresh interval for personal board' => 'Uppdateringsintervall för privat tavla', + 'Refresh interval for public board' => 'Uppdateringsintervall för publik tavla', + 'Task highlight period' => 'Period att framhäva ny uppgift', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Period (i sekunder) att betrakta en uppgifts ändring som ny (ange 0 för att inaktivera, 2 dagar är standard)', + 'Frequency in second (60 seconds by default)' => 'Frekvens i sekunder (60 sekunder är standard)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Frekvens i sekunder (ange 0 för att inaktivera, 10 sekunder är standard)', + 'Application URL' => 'Applikations-URL', + 'Token regenerated.' => 'Token nyskapad.', + 'Date format' => 'Datumformat', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO-format är alltid tillÃ¥tet, exempel: "%s" och "%s"', + 'New personal project' => 'Nytt privat projekt', + 'This project is personal' => 'Det här projektet är privat', + 'Add' => 'Lägg till', + 'Start date' => 'Startdatum', + 'Time estimated' => 'Uppskattad tid', + 'There is nothing assigned to you.' => 'Du har inget tilldelat till dig.', + 'My tasks' => 'Mina uppgifter', + 'Activity stream' => 'Aktivitetsström', + 'Dashboard' => 'Översiktspanel', + 'Confirmation' => 'Bekräftelse', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Skapa en kommentar frÃ¥n en extern leverantör', + 'Project management' => 'Projekthantering', + 'Columns' => 'Kolumner', + 'Task' => 'Uppgift', + 'Percentage' => 'Procent', + 'Number of tasks' => 'Antal uppgifter', + 'Task distribution' => 'Uppgiftsfördelning', + 'Analytics' => 'Analyser', + 'Subtask' => 'Deluppgift', + 'User repartition' => 'Användardeltagande', + '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', + 'Previous' => 'FöregÃ¥ende', + 'The id must be an integer' => 'ID mÃ¥ste vara ett heltal', + 'The project id must be an integer' => 'Projekt-ID mÃ¥ste vara ett heltal', + 'The status must be an integer' => 'Status mÃ¥ste vara ett heltal', + 'The subtask id is required' => 'Deluppgifts-ID behövs', + 'The subtask id must be an integer' => 'Deluppgifts-ID mÃ¥ste vara ett heltal', + 'The task id is required' => 'Uppgifts-ID behövs', + 'The task id must be an integer' => 'Uppgifts-ID mÃ¥ste vara ett heltal', + 'The user id must be an integer' => 'Användar-ID mÃ¥ste vara ett heltal', + 'This value is required' => 'Värdet behövs', + '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', + 'Daily project summary' => 'Daglig projektsummering', + 'Daily project summary export' => 'Export av daglig projektsummering', + '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 simbanor', + 'Add a new swimlane' => 'Lägg till en ny simbana', + 'Default swimlane' => 'Standard-simbana', + 'Do you really want to remove this swimlane: "%s"?' => 'Vill du verkligen ta bort simbanan "%s"?', + 'Inactive swimlanes' => 'Inaktiv simbana', + 'Remove a swimlane' => 'Ta bort en simbana', + 'Swimlane modification for the project "%s"' => 'Ändra simbana för projekt "%s"', + 'Swimlane removed successfully.' => 'Simbanan togs bort', + 'Swimlanes' => 'Simbanor', + 'Swimlane updated successfully.' => 'Simbana uppdaterad', + 'Unable to remove this swimlane.' => 'Kunde inte ta bort simbanan', + 'Unable to update this swimlane.' => 'Kunde inte uppdatera simbanan', + 'Your swimlane has been created successfully.' => 'Din simbana har skapats', + 'Example: "Bug, Feature Request, Improvement"' => 'Exempel: "Bug, ny funktionalitet, förbättringar"', + 'Default categories for new projects (Comma-separated)' => 'Standardkategorier för nya projekt (komma-separerade)', + 'Integrations' => 'Integrationer', + 'Integration with third-party services' => 'Integration med tredjepartstjänster', + 'Subtask Id' => 'Deluppgifts-ID', + 'Subtasks' => 'Deluppgift', + 'Subtasks Export' => 'Exportering av deluppgifter', + 'Task Title' => 'Uppgiftstitel', + 'Untitled' => 'Titel saknas', + 'Application default' => 'Applikationsstandard', + 'Language:' => 'SprÃ¥k', + 'Timezone:' => 'Tidszon', + 'All columns' => 'Alla kolumner', + 'Next' => 'Nästa', + '#%d' => '#%d', + 'All swimlanes' => 'Alla simbanor', + 'All colors' => 'Alla färger', + 'Moved to column %s' => 'Flyttad till kolumn %s', + 'User dashboard' => 'Användarens översiktspanel', + 'Allow only one subtask in progress at the same time for a user' => 'TillÃ¥t endast en deluppgift igÃ¥ng samtidigt för en användare', + 'Edit column "%s"' => 'Ändra kolumn "%s"', + 'Select the new status of the subtask: "%s"' => 'Välj ny status för deluppgiften: "%s"', + 'Subtask timesheet' => 'Tidrapport för deluppgiften', + 'There is nothing to show.' => 'Det finns inget att visa.', + 'Time Tracking' => 'Tidsbevakning', + 'You already have one subtask in progress' => 'Du har redan en deluppgift igÃ¥ng', + 'Which parts of the project do you want to duplicate?' => 'Vilka delar av projektet vill du duplicera?', + 'Disallow login form' => 'Förbjud inloggningsformulär', + 'Start' => 'Start', + 'End' => 'Slut', + 'Task age in days' => 'UppgiftsÃ¥lder i dagar', + 'Days in this column' => 'Dagar i denna kolumn', + '%dd' => '%dd', + 'Add a new link' => 'Lägg till ny länk', + 'Do you really want to remove this link: "%s"?' => 'Vill du verkligen ta bort länken: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Vill du verkligen ta bort länken till uppgiften #%d?', + 'Field required' => 'Fältet krävs', + 'Link added successfully.' => 'Länken har lagts till', + 'Link updated successfully.' => 'Länken har uppdaterats', + 'Link removed successfully.' => 'Länken har tagits bort', + 'Link labels' => 'Länketiketter', + 'Link modification' => 'Länkändring', + 'Opposite label' => 'Motpartslänk', + 'Remove a link' => 'Ta bort en länk', + 'The labels must be different' => 'Etiketterna mÃ¥ste vara olika', + 'There is no link.' => 'Det finns ingen länk', + 'This label must be unique' => 'Etiketten mÃ¥ste vara unik', + 'Unable to create your link.' => 'Kunde inte skapa din länk', + 'Unable to update your link.' => 'Kunde inte uppdatera din länk', + 'Unable to remove this link.' => 'Kunde inte ta bort länken', + 'relates to' => 'relaterar till', + 'blocks' => 'blockerar', + 'is blocked by' => 'blockeras av', + 'duplicates' => 'duplicerar', + 'is duplicated by' => 'är duplicerad av', + 'is a child of' => 'är underliggande till', + 'is a parent of' => 'är överliggande till', + 'targets milestone' => 'milstolpemÃ¥l', + 'is a milestone of' => 'är en milstolpe för', + 'fixes' => 'Ã¥tgärdar', + 'is fixed by' => 'Ã¥tgärdas av', + 'This task' => 'Denna uppgift', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => 'Fäll ut uppgifter', + 'Collapse tasks' => 'Fäll ihop uppgifter', + 'Expand/collapse tasks' => 'Fäll ut/fäll ihop uppgifter', + 'Close dialog box' => 'Stäng dialogruta', + 'Submit a form' => 'Sänd formulär', + 'Board view' => 'Tavelvy', + 'Keyboard shortcuts' => 'Tangentbordsgenvägar', + 'Open board switcher' => 'Växling av öppen tavla', + 'Application' => 'Applikation', + 'Compact view' => 'Kompakt vy', + 'Horizontal scrolling' => 'Horisontell scroll', + 'Compact/wide view' => 'Kompakt/bred vy', + 'Currency' => 'Valuta', + 'Personal project' => 'Privat projekt', + 'AUD - Australian Dollar' => 'AUD - Australiska dollar', + 'CAD - Canadian Dollar' => 'CAD - Kanadensiska dollar', + 'CHF - Swiss Francs' => 'CHF - Schweiziska franc', + 'Custom Stylesheet' => 'Anpassad stilmall', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Brittiska pund', + 'INR - Indian Rupee' => 'INR - Indiska rupier', + 'JPY - Japanese Yen' => 'JPY - Japanska yen', + 'NZD - New Zealand Dollar' => 'NZD - Nya Zeeländska dollar', + 'PEN - Peruvian Sol' => 'PEN – Peruansk sol', + 'RSD - Serbian dinar' => 'RSD - Serbiska dinarer', + 'CNY - Chinese Yuan' => 'CNY - Kinesisk yuan', + 'USD - US Dollar' => 'USD - Amerikanska dollar', + 'VES - Venezuelan Bolívar' => 'VES - Venezuelanska bolívar', + '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', + 'Move the task to another column when assignee is cleared' => 'Flytta uppgiften till en annan kolumn när tilldelningen tas bort.', + 'Source column' => 'Källkolumn', + 'Transitions' => 'ÖvergÃ¥ngar', + 'Executer' => 'Verkställare', + 'Time spent in the column' => 'Tid i kolumnen.', + 'Task transitions' => 'UppgiftsövergÃ¥ngar', + 'Task transitions export' => 'Export av uppgiftsövergÃ¥ngar', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Denna rapport innehÃ¥ller alla kolumnförflyttningar för varje uppgift med datum, användare och nedlagd tid vid varje övergÃ¥ng.', + 'Currency rates' => 'Valutakurser', + 'Rate' => 'Kurs', + 'Change reference currency' => 'Ändra referenskurs', + 'Reference currency' => 'Referensvaluta', + 'The currency rate has been added successfully.' => 'Valutakursen har lagts till.', + 'Unable to add this currency rate.' => 'Kunde inte lägga till valutakursen.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s ta bort tilldelningen av uppgiften %s', + 'Information' => 'Information', + 'Check two factor authentication code' => 'Kolla tvÃ¥faktorsautentiseringsngskod', + 'The two factor authentication code is not valid.' => 'TvÃ¥faktorsautentiseringskoden är inte giltig.', + 'The two factor authentication code is valid.' => 'TvÃ¥faktorsautentiseringskoden är giltig.', + 'Code' => 'Kod', + 'Two factor authentication' => 'TvÃ¥faktorsautentisering', + 'This QR code contains the key URI: ' => 'Denna QR-kod innehÃ¥ller nyckel-URI:n', + 'Check my code' => 'Verifiera min kod', + 'Secret key: ' => 'Säkerhetsnyckel:', + '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 viss kolumn', + '%s via Kanboard' => '%s via Kanboard', + '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', + 'Add a screenshot' => 'Lägg till en skärmdump', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Ta en skärmdump och tryck CTRL+V för att klistra in här.', + 'Screenshot uploaded successfully.' => 'Skärmdumpen laddades upp.', + 'SEK - Swedish Krona' => 'SEK - Svensk krona', + 'Identifier' => 'Identifierare', + 'Disable two factor authentication' => 'Inaktivera tvÃ¥faktorsautentisering', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Vill du verkligen inaktivera tvÃ¥faktorsautentisering för denna användare: "%s"?', + 'Edit link' => 'Ändra länk', + 'Start to type task title...' => 'Börja skriv uppgiftstitel...', + 'A task cannot be linked to itself' => 'En uppgift kan inte länkas till sig själv', + 'The exact same link already exists' => 'Länken existerar redan', + 'Recurrent task is scheduled to be generated' => 'Ã…terkommande uppgift är schemalagd att genereras', + 'Score' => 'Poäng', + 'The identifier must be unique' => 'Identifieraren mÃ¥ste vara unik', + 'This linked task id doesn\'t exists' => 'Detta länkade uppgifts-ID existerar inte', + 'This value must be alphanumeric' => 'Värdet mÃ¥ste vara alfanumeriskt', + 'Edit recurrence' => 'Ändra Ã¥terkommande', + 'Generate recurrent task' => 'Generera Ã¥terkommande uppgift', + 'Trigger to generate recurrent task' => 'Aktivera att generera Ã¥terkommande uppgift', + 'Factor to calculate new due date' => 'Faktor för att beräkna nytt förfallodatum', + 'Timeframe to calculate new due date' => 'Tidsram för att beräkna nytt förfallodatum', + 'Base date to calculate new due date' => 'Basdatum för att beräkna nytt förfallodatum', + 'Action date' => 'Händelsedatum', + 'Base date to calculate new due date: ' => 'Basdatum för att beräkna nytt förfallodatum: ', + 'This task has created this child task: ' => 'Uppgiften har skapat denna underliggande uppgift: ', + 'Day(s)' => 'Dag(ar)', + 'Existing due date' => 'Existerande förfallodatum', + 'Factor to calculate new due date: ' => 'Faktor för att beräkna nytt förfallodatum: ', + 'Month(s)' => 'MÃ¥nad(er)', + 'This task has been created by: ' => 'Uppgiften har skapats av: ', + 'Recurrent task has been generated:' => 'Ã…terkommande uppgift har genererats:', + 'Timeframe to calculate new due date: ' => 'Tidsram för att beräkna nytt förfallodatum: ', + 'Trigger to generate recurrent task: ' => 'Aktivera att generera Ã¥terkommande uppgift: ', + 'When task is closed' => 'När uppgiften är stängd', + 'When task is moved from first column' => 'När uppgiften flyttas frÃ¥n första kolumnen', + 'When task is moved to last column' => 'När uppgiften flyttas till sista kolumnen', + 'Year(s)' => 'Ã…r', + 'Project settings' => 'Projektinställningar', + 'Automatically update the start date' => 'Automatisk uppdatering av startdatum', + 'iCal feed' => 'iCal-flöde', + 'Preferences' => 'Preferenser', + 'Security' => 'Säkerhet', + 'Two factor authentication disabled' => 'TvÃ¥faktorsautentisering inaktiverad', + 'Two factor authentication enabled' => 'TvÃ¥faktorsautentisering aktiverad', + 'Unable to update this user.' => 'Kunde inte uppdatera användaren.', + 'There is no user management for personal projects.' => 'Det finns ingen användarhantering för privata projekt.', + 'User that will receive the email' => 'Användare som kommer att ta emot e-brevet', + 'Email subject' => 'E-postämne', + 'Date' => 'Datum', + 'Add a comment log when moving the task between columns' => 'Lägg till en kommentarslogg när en uppgift flyttas mellan kolumner', + '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 till nÃ¥gon via e-post', + 'Reopen a task' => 'Ã…teröppna en uppgift', + 'Notification' => 'Notis', + '%s moved the task #%d to the first swimlane' => '%s flyttade uppgiften #%d till första simbanan', + 'Swimlane' => 'Simbana', + '%s moved the task %s to the first swimlane' => '%s flyttade uppgiften %s till första simbanan', + '%s moved the task %s to the swimlane "%s"' => '%s flyttade uppgiften %s till simbana "%s"', + 'This report contains all subtasks information for the given date range.' => 'Denna rapport innehÃ¥ller all deluppgiftsinformation för det givna datumintervallet.', + 'This report contains all tasks information for the given date range.' => 'Denna rapport innehÃ¥ller all uppgiftsinformation för det givna datumintervallet.', + 'Project activities for %s' => 'Projektaktiviteter för %s', + 'view the board on Kanboard' => 'visa tavlan pÃ¥ Kanboard', + 'The task has been moved to the first swimlane' => 'Uppgiften har flyttats till första simbanan', + 'The task has been moved to another swimlane:' => 'Uppgiften har flyttats till en annan simbana::', + 'New title: %s' => 'Ny titel: %s', + 'The task is not assigned anymore' => 'Uppgiften är inte länge tilldelad', + 'New assignee: %s' => 'Ny tilldelning: %s', + 'There is no category now' => 'Det finns ingen kategori nu', + 'New category: %s' => 'Ny kategori: %s', + 'New color: %s' => 'Ny färg: %s', + 'New complexity: %d' => 'Ny komplexitet: %d', + 'The due date has been removed' => 'Förfallodatumet har tagits bort', + 'There is no description anymore' => 'Det finns ingen beskrivning längre', + 'Recurrence settings has been modified' => 'Ã…terkommande inställning har ändrats', + 'Time spent changed: %sh' => 'Spenderad tid har ändrats: %sh', + 'Time estimated changed: %sh' => 'Tidsuppskattning ändrad: %sh', + 'The field "%s" has been updated' => 'Fältet "%s" har uppdaterats', + 'The description has been modified:' => 'Beskrivningen har modifierats', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Vill du verkligen stänga uppgiften "%s" och alla deluppgifter?', + 'I want to receive notifications for:' => 'Jag vill fÃ¥ notiser för:', + 'All tasks' => 'Alla uppgifter', + 'Only for tasks assigned to me' => 'Bara för uppgifter tilldelade mig', + 'Only for tasks created by me' => 'Bara för uppgifter skapade av mig', + 'Only for tasks created by me and tasks assigned to me' => 'Bara för uppgifter skapade av mig och tilldelade till mig', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Totalt för alla kolumner', + 'You need at least 2 days of data to show the chart.' => 'Du behöver minst tvÃ¥ dagars data för att visa diagrammet.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Stoppa timer', + 'Start timer' => 'Starta timer', + 'My activity stream' => 'Min aktivitetsström', + 'Search tasks' => 'Sök uppgifter', + 'Reset filters' => 'Ã…terställ filter', + 'My tasks due tomorrow' => 'Mina uppgifter förfaller imorgon', + 'Tasks due today' => 'Uppgifter förfaller idag', + 'Tasks due tomorrow' => 'Uppgifter förfaller imorgon', + 'Tasks due yesterday' => 'Uppgifter förföll igÃ¥r', + 'Closed tasks' => 'Stängda uppgifter', + 'Open tasks' => 'Öppna uppgifter', + 'Not assigned' => 'Inte tilldelad', + 'View advanced search syntax' => 'Visa avancerad söksyntax', + 'Overview' => 'Översikt', + 'Board/Calendar/List view' => 'Tavelvy/kalendervy/listvy', + 'Switch to the board view' => 'Växla till tavelvy', + 'Switch to the list view' => 'Växla till listvy', + 'Go to the search/filter box' => 'GÃ¥ till sök/filter box', + 'There is no activity yet.' => 'Det finns ingen aktivitet ännu.', + 'No tasks found.' => 'Inga uppgifter hittades.', + 'Keyboard shortcut: "%s"' => 'Tangentbordsgenväg: "%s"', + 'List' => 'Lista', + 'Filter' => 'Filter', + 'Advanced search' => 'Avancerad sök', + 'Example of query: ' => 'Exempel pÃ¥ frÃ¥ga', + 'Search by project: ' => 'Sök efter projekt:', + 'Search by column: ' => 'Sök efter kolumn:', + 'Search by assignee: ' => 'Sök efter tilldelad:', + 'Search by color: ' => 'Sök efter färg:', + 'Search by category: ' => 'Sök efter kategori:', + 'Search by description: ' => 'Sök efter beskrivning', + 'Search by due date: ' => 'Sök efter förfallodatum', + 'Average time spent in each column' => 'MedeltidsÃ¥tgÃ¥ng i varje kolumn', + 'Average time spent' => 'MedeltidsÃ¥tgÃ¥ng', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Diagramet visar medeltidsÃ¥tgÃ¥ng i varje kolumn för de senaste %d uppgifterna.', + 'Average Lead and Cycle time' => 'Medel av led och cykeltid', + 'Average lead time: ' => 'Medel av ledtid', + 'Average cycle time: ' => 'Medel av cykeltid', + 'Cycle Time' => 'Cykeltid', + 'Lead Time' => 'Ledtid', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Diagramet visar medel av led och cykeltid för de senaste %d uppgifterna över tiden.', + 'Average time into each column' => 'MedeltidsÃ¥tgÃ¥ng i varje kolumn', + 'Lead and cycle time' => 'Led och cykeltid', + 'Lead time: ' => 'Ledtid', + 'Cycle time: ' => 'Cykeltid', + 'Time spent in each column' => 'TidsÃ¥tgÃ¥ng per kolumn', + 'The lead time is the duration between the task creation and the completion.' => 'Ledtiden är varaktigheten mellan uppgiftens skapande och slutförande.', + 'The cycle time is the duration between the start date and the completion.' => 'Cykeltiden är varaktigheten mellan uppgiftens startdatum och slut.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Om uppgiften inte är stängd används nuvarande tid istället för slutförandedatum.', + 'Set the start date automatically' => 'Sätt startdatum automatiskt', + 'Edit Authentication' => 'Ändra autentisering', + 'Remote user' => 'Fjärranvändare', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Fjärranvä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.', + '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.', + 'Trigger automatically subtask time tracking' => 'Aktivera automatisk tidsbevakning av deluppgifter', + 'Include closed tasks in the cumulative flow diagram' => 'Inkludera stängda uppgifter i digrammet med kumulativt flöde', + 'Current swimlane: %s' => 'Nuvarande simbana: %s', + 'Current column: %s' => 'Nuvarande kolumn: %s', + 'Current category: %s' => 'Nuvarande kategori: %s', + 'no category' => 'ingen kategori', + 'Current assignee: %s' => 'Nuvarande tilldelning: %s', + 'not assigned' => 'inte tilldelad', + 'Author:' => 'Upphovsman:', + 'contributors' => 'medarbetare', + 'License:' => 'Licens:', + 'License' => 'Licens', + 'Enter the text below' => 'Fyll i texten nedan', + 'Start date:' => 'Startdatum:', + 'Due date:' => 'Slutdatum:', + 'People who are project managers' => 'Personer som är projektledare', + 'People who are project members' => 'Personer som är projektdeltagare', + 'NOK - Norwegian Krone' => 'NOK - Norsk krona', + 'Show this column' => 'Visa kolumn', + 'Hide this column' => 'Dölj kolumn', + 'End date' => 'Slutdatum', + 'Users overview' => 'Användaröversikt', + 'Members' => 'Medlemmar', + 'Shared project' => 'Delat projekt', + 'Project managers' => 'Projektledare', + 'Projects list' => 'Projektlista', + 'End date:' => 'Slutdatum:', + 'Change task color when using a specific task link' => 'Ändra uppgiftsfärg när en viss uppgiftslänk används', + 'Task link creation or modification' => 'Uppgiftslänk skapad eller ändrad', + 'Milestone' => 'Milstolpe', + 'Reset the search/filter box' => 'Ã…terställ sökning/fitrering', + 'Documentation' => 'Dokumentation', + 'Author' => 'Skapad av', + 'Version' => 'Version', + 'Plugins' => 'Insticksprogram', + 'There is no plugin loaded.' => 'Inget insticksprogram har laddats', + 'My notifications' => 'Mina notifieringar', + 'Custom filters' => 'Anpassade filter', + 'Your custom filter has been created successfully.' => 'Ditt anpassade filter har skapats.', + 'Unable to create your custom filter.' => 'Ditt anpassade filter kunde inte skapas.', + 'Custom filter removed successfully.' => 'Ditt anpassade filter har raderats.', + 'Unable to remove this custom filter.' => 'Ditt anpassade filter kunde inte raderas.', + 'Edit custom filter' => 'Redigera anpassat filter', + 'Your custom filter has been updated successfully.' => 'Ditt anpassade filter har uppdaterats', + 'Unable to update custom filter.' => 'Kunde inte uppdatera anpassat filter', + 'Web' => 'Webb', + 'New attachment on task #%d: %s' => 'Ny bilaga i uppgift #%d: %s', + 'New comment on task #%d' => 'Ny kommentar i uppgift #%d', + 'Comment updated on task #%d' => 'Kommentar uppdaterad i uppgift #%d', + 'New subtask on task #%d' => 'Ny deluppgift till uppgift #%d', + 'Subtask updated on task #%d' => 'Deluppgift till uppgift #%d uppdaterad', + 'New task #%d: %s' => 'Ny uppgift #%d: %s', + 'Task updated #%d' => 'Uppgift uppdaterad #%d', + 'Task #%d closed' => 'Uppgift #%d stängd', + 'Task #%d opened' => 'Uppgift #%d öppnad', + 'Column changed for task #%d' => 'Byte av kolumn för uppgift #%d', + 'New position for task #%d' => 'Ny plats för uppgift #%d', + 'Swimlane changed for task #%d' => 'Simbana uppdaterad för uppgift #%d', + 'Assignee changed on task #%d' => 'Uppgiftsinnehavare ändrades pÃ¥ uppgift #%d', + '%d overdue tasks' => '%d försenade uppgifter', + 'No notification.' => 'Ingen notifiering', + 'Mark all as read' => 'Markera alla som lästa', + 'Mark as read' => 'Markera som läst', + 'Total number of tasks in this column across all swimlanes' => 'Totalt antal uppgifter i denna kolumnen över samtliga simbanor', + 'Collapse swimlane' => 'Fäll ihop simbana', + 'Expand swimlane' => 'Fäll ut simbana', + 'Add a new filter' => 'Lägg till nytt filter', + 'Share with all project members' => 'Dela med alla projektmedlemmar', + 'Shared' => 'Delad', + 'Owner' => 'Ägare', + 'Unread notifications' => 'Olästa notiser', + 'Notification methods:' => 'Notifieringsmetoder:', + 'Unable to read your file' => 'Kunde inte läsa din fil', + '%d task(s) have been imported successfully.' => '%d uppgift(er) har importerats.', + 'Nothing has been imported!' => 'Inget har importerats!', + 'Import users from CSV file' => 'Importera användare frÃ¥n CSV-fil', + '%d user(s) have been imported successfully.' => '%d användare har importerats.', + 'Comma' => 'Kommatecken', + 'Semi-colon' => 'Semikolon', + 'Tab' => 'Tabb', + 'Vertical bar' => 'Lodstreck', + 'Double Quote' => 'Dubbla citattecken', + 'Single Quote' => 'Enkla citattecken', + '%s attached a file to the task #%d' => '%s bifogade en fil till uppgift #%d', + 'There is no column or swimlane activated in your project!' => 'Ingen kolumn eller simbana har aktiverats i ditt projekt!', + 'Append filter (instead of replacement)' => 'Lägg till filter (istället för ersättning)', + 'Append/Replace' => 'Lägg till/ersätt', + 'Append' => 'Lägg till', + 'Replace' => 'Ersätt', + 'Import' => 'Importera', + 'Change sorting' => 'Ändra sortering', + 'Tasks Importation' => 'Importera uppgifter', + 'Delimiter' => 'Avskiljare', + 'Enclosure' => 'Bilaga', + 'CSV File' => 'CSV-fil', + 'Instructions' => 'Instruktioner', + 'Your file must use the predefined CSV format' => 'Din fil mÃ¥ste använda fördefinierat CSV-format', + 'Your file must be encoded in UTF-8' => 'Din fil mÃ¥ste teckenkodas med UTF-8', + 'The first row must be the header' => 'Första raden mÃ¥ste vara rubrik.', + 'Duplicates are not verified for you' => 'Dupliceringar verifieras inte för din användare', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Förfallodatum mÃ¥ste använda ISO-datumformat: Ã…Ã…Ã…Ã…-MM-DD', + 'Download CSV template' => 'Ladda ner CSV-mall', + 'No external integration registered.' => 'Ingen extern integration registrerad.', + 'Duplicates are not imported' => 'Dubletter importeras inte', + 'Usernames must be lowercase and unique' => 'Användarnamn mÃ¥ste vara unika och endast bestÃ¥ av gemener ', + 'Passwords will be encrypted if present' => 'Eventuella lösenord kommer krypteras', + '%s attached a new file to the task %s' => '%s bifogade en ny fil till uppgiften %s', + 'Link type' => 'Länktyp', + 'Assign automatically a category based on a link' => 'Tilldela kategori automatiskt baserat pÃ¥ en länk', + 'BAM - Konvertible Mark' => 'BAM - Konvertibla mark', + 'Assignee Username' => 'Uppdragsinnehavares användarnamn', + 'Assignee Name' => 'Uppdragsinnehavares namn', + 'Groups' => 'Grupper', + 'Members of %s' => 'Medlemmar i %s', + 'New group' => 'Ny grupp', + 'Group created successfully.' => 'Grupp skapades', + 'Unable to create your group.' => 'Kunde inte skapa gruppen', + 'Edit group' => 'Redigera grupp', + 'Group updated successfully.' => 'Gruppen uppdaterades.', + 'Unable to update your group.' => 'Kunde inte uppdatera gruppen', + 'Add group member to "%s"' => 'Lägg till gruppmedlem till "%s"', + 'Group member added successfully.' => 'Gruppmedlem lades till.', + 'Unable to add group member.' => 'Kunde inte lägga till gruppmedlem.', + 'Remove user from group "%s"' => 'Ta bort användare frÃ¥n grupp "%s"', + 'User removed successfully from this group.' => 'Användaren har tagits bort frÃ¥n gruppen.', + 'Unable to remove this user from the group.' => 'Kunde inte ta bort användaren frÃ¥n gruppen.', + 'Remove group' => 'Ta bort grupp', + 'Group removed successfully.' => 'Gruppen har raderats', + 'Unable to remove this group.' => 'Kunde inte radera gruppen.', + 'Project Permissions' => 'Projektbehörighet', + 'Manager' => 'Ledare', + 'Project Manager' => 'Projektägare', + 'Project Member' => 'Projektmedlem', + 'Project Viewer' => 'Projektläsare', + 'Your account is locked for %d minutes' => 'Ditt konto har lÃ¥sts i %d minuter', + 'Invalid captcha' => 'Ogiltig captcha', + 'The name must be unique' => 'Namnet mÃ¥ste vara unikt', + 'View all groups' => 'Visa alla grupper', + 'There is no user available.' => 'Ingen användare tillgänglig', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Vill du verkligen ta bort användaren "%s" frÃ¥n gruppen "%s"?', + 'There is no group.' => 'Inga grupper existerar.', + 'Add group member' => 'Lägg till gruppmedlem', + 'Do you really want to remove this group: "%s"?' => 'Vill du verkligen ta bort gruppen: "%s"?', + 'There is no user in this group.' => 'Det finns inga användare i gruppen.', + 'Permissions' => 'Behörigheter', + 'Allowed Users' => 'Behöriga användare', + 'No specific user has been allowed.' => 'Ingen särskild användare har beviljats.', + 'Role' => 'Roll', + 'Enter user name...' => 'Mata in användarnamn...', + 'Allowed Groups' => 'TillÃ¥tna grupper', + 'No group has been allowed.' => 'Ingen grupp har beviljats.', + 'Group' => 'Grupp', + 'Group Name' => 'Gruppnamn', + 'Enter group name...' => 'Mata in gruppnamn...', + 'Role:' => 'Roll:', + 'Project members' => 'Projektmedlemmar', + '%s mentioned you in the task #%d' => '%s nämnde dig i uppgift #%d', + '%s mentioned you in a comment on the task #%d' => '%s nämnde dig i en kommentar till uppgift #%d', + 'You were mentioned in the task #%d' => 'Du nämndes i uppgift #%d', + 'You were mentioned in a comment on the task #%d' => 'Du nämndes i en kommentar till uppgift #%d', + 'Estimated hours: ' => 'Uppskattat antal timmar', + 'Actual hours: ' => 'Verkligt antal timmar', + 'Hours Spent' => 'Upparbetade timmar', + 'Hours Estimated' => 'Uppskattat antal timmar', + 'Estimated Time' => 'Estimerad tid', + 'Actual Time' => 'Faktisk tid', + 'Estimated vs actual time' => 'Estimerad vs faktisk tid', + 'RUB - Russian Ruble' => 'RUB - Rysk rubel', + 'Assign the task to the person who does the action when the column is changed' => 'Tilldela uppgiften till personen som utför Ã¥tgärden när kolumnen ändras', + 'Close a task in a specific column' => 'Stäng en uppgift i en viss kolumn', + 'Time-based One-time Password Algorithm' => 'Tidsbaserade engÃ¥ngslösenord', + 'Two-Factor Provider: ' => 'TvÃ¥faktorsleverantör:', + 'Disable two-factor authentication' => 'Inaktivera tvÃ¥faktorsautentisering', + 'Enable two-factor authentication' => 'Aktivera tvÃ¥faktorsautentisering', + 'There is no integration registered at the moment.' => 'Ingen integration har registrerats.', + 'Password Reset for Kanboard' => 'LösenordsÃ¥terställning för Kanboard', + 'Forgot password?' => 'Glömt lösenordet?', + 'Enable "Forget Password"' => 'Aktivera "Glömt lösenord"', + 'Password Reset' => 'Ã…terställ lösenord', + 'New password' => 'Nytt lösenord', + 'Change Password' => 'Ändra lösenord', + 'To reset your password click on this link:' => 'För att ändra lösenord klicka pÃ¥ denna länk:', + 'Last Password Reset' => 'Senaste lösenordsÃ¥terställning', + 'The password has never been reinitialized.' => 'Lösenordet har aldrig nollställts.', + 'Creation' => 'Skapat', + 'Expiration' => 'UtgÃ¥ng', + 'Password reset history' => 'Historik för lösenordsÃ¥terställning', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Alla uppgifter i kolumnen "%s" samt simbanan "%s" har stängts.', + 'Do you really want to close all tasks of this column?' => 'Vill du verkligen stänga alla uppgifter i denna kolumn?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d uppgift(er) i kolumnen "%s" och simbanan "%s" kommer stängas.', + 'Close all tasks in this column and this swimlane' => 'Stäng alla uppgifter i denna kolumn', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Inget insticksprogram har registrerat notifieringsmetod för projektet. Du kan fortfarande konfigurera personliga notifikationer i din användarprofil.', + 'My dashboard' => 'Min översiktspanel', + 'My profile' => 'Min profil', + 'Project owner: ' => 'Projektägare: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Identifierare för projektet är inte obligatorisk men mÃ¥ste vara alfanumerisk, t.ex.: AVDELNINGX', + 'Project owner' => 'Projektägare', + 'Personal projects do not have users and groups management.' => 'Privata projekt saknar hantering för användare och grupper.', + 'There is no project member.' => 'Det finns ingen projekmedlem.', + 'Priority' => 'Prioritet', + 'Task priority' => 'Uppgiftsprioritet', + 'General' => 'Allmänt', + 'Dates' => 'Datu', + 'Default priority' => 'Standardprioritet', + 'Lowest priority' => 'LÃ¥g prioritet', + 'Highest priority' => 'Hög prioritet', + 'Close a task when there is no activity' => 'Stäng uppgift utan aktivitet', + 'Duration in days' => 'Varaktighet i dagar', + 'Send email when there is no activity on a task' => 'Skicka e-post när aktivitet i en uppgift upphör', + 'Unable to fetch link information.' => 'Kunde inte hämta länkinformation.', + 'Daily background job for tasks' => 'Dagligt bakgrundsjobb för uppgifter', + 'Auto' => 'Auto', + 'Related' => 'Relaterat', + 'Attachment' => 'Bilaga', + 'Web Link' => 'Webblänk', + 'External links' => 'Externa länkar', + 'Add external link' => 'Skapa extern länk', + 'Type' => 'Typ', + 'Dependency' => 'Beroende', + 'Add internal link' => 'Lägg till intern länk', + 'Add a new external link' => 'Lägg till extern länk', + 'Edit external link' => 'Redigera extern länk', + 'External link' => 'Extern länk', + 'Copy and paste your link here...' => 'Klistra in länken här...', + 'URL' => 'URL', + 'Internal links' => 'Intern länk', + 'Assign to me' => 'Tilldela mig', + 'Me' => 'Jag', + 'Do not duplicate anything' => 'Kopiera inte', + 'Projects management' => 'Projekthantering', + 'Users management' => 'Användarhantering', + 'Groups management' => 'Grupphantering', + 'Create from another project' => 'Skapa frÃ¥n annat projekt', + 'open' => 'öppen', + 'closed' => 'stängd', + 'Priority:' => 'Prioritet:', + 'Reference:' => 'Referens:', + 'Complexity:' => 'Komplexitet:', + 'Swimlane:' => 'Simbana:', + 'Column:' => 'Kolumn', + 'Position:' => 'Position:', + 'Creator:' => 'Skapad av:', + 'Time estimated:' => 'Uppskattad tid:', + '%s hours' => '%s timmar', + 'Time spent:' => 'Spenderad tid:', + 'Created:' => 'Skapad:', + 'Modified:' => 'Ändrad', + 'Completed:' => 'Färdig:', + 'Started:' => 'PÃ¥börjad:', + 'Moved:' => 'Flyttad:', + 'Task #%d' => 'Uppgift #%d', + 'Time format' => 'Tidsformat', + 'Start date: ' => 'Startdatum:', + 'End date: ' => 'Slutdatum: ', + 'New due date: ' => 'Nytt förfallodatum: ', + 'Start date changed: ' => 'Startdatum ändrat:', + 'Disable personal projects' => 'Inaktivera personliga projekt', + 'Do you really want to remove this custom filter: "%s"?' => 'Vill du verkligen ta bort anpassat filter: "%s"?', + 'Remove a custom filter' => 'Ta bort ett anpassat filter', + 'User activated successfully.' => 'Användare aktiverad.', + 'Unable to enable this user.' => 'Kunde inte aktivera användaren.', + 'User disabled successfully.' => 'Användaren inaktiverad.', + 'Unable to disable this user.' => 'Kunde inte inaktivera användaren.', + 'All files have been uploaded successfully.' => 'Samtliga filer har laddats upp.', + 'The maximum allowed file size is %sB.' => 'Maximal filstorlek är %sB.', + 'Drag and drop your files here' => 'Dra och släpp dina filer här', + 'choose files' => 'välj filer', + 'View profile' => 'Visa profil', + 'Two Factor' => 'TvÃ¥faktor', + 'Disable user' => 'Inaktivera användare', + 'Do you really want to disable this user: "%s"?' => 'Vill du verkligen inaktivera följande användare: "%s"?', + 'Enable user' => 'Aktivera användare', + 'Do you really want to enable this user: "%s"?' => 'Vill du verkligen aktivera följande användare: "%s"?', + 'Download' => 'Ladda ner', + 'Uploaded: %s' => 'Laddat upp: %s', + 'Size: %s' => 'Storlek: %s', + 'Uploaded by %s' => 'Uppladdat av %s', + 'Filename' => 'Filnamn', + 'Size' => 'Storlek', + 'Column created successfully.' => 'Kolumnen skapades.', + 'Another column with the same name exists in the project' => 'En annan kolumn med samma namn finns redan i projektet', + 'Default filters' => 'Standardfilter', + 'Your board doesn\'t have any columns!' => 'Tavlan har inga kolumner!', + 'Change column position' => 'Ändra kolumnposition', + 'Switch to the project overview' => 'GÃ¥ till projektöversikt', + 'User filters' => 'Användarfilter', + 'Category filters' => 'Kategorifilter', + 'Upload a file' => 'Ladda upp en fil', + 'View file' => 'Visa fil', + 'Last activity' => 'Senaste aktivitet', + 'Change subtask position' => 'Ändra position pÃ¥ deluppgift', + 'This value must be greater than %d' => 'Detta värde mÃ¥ste vara större än %d', + 'Another swimlane with the same name exists in the project' => 'En simbana med samma namn finns redan i projektet', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Exempel: https://example.kanboard.org/ (används för att generera absoluta URL:er)', + 'Actions duplicated successfully.' => 'Ã…tgärder duplicerades.', + 'Unable to duplicate actions.' => 'Kunde inte duplicera Ã¥tgärder.', + 'Add a new action' => 'Skapa en ny Ã¥tgärd', + 'Import from another project' => 'Importera frÃ¥n ett annat projekt', + 'There is no action at the moment.' => 'Det finns ingen Ã¥tgärd.', + 'Import actions from another project' => 'Importera Ã¥tgärder frÃ¥n ett annat projekt', + 'There is no available project.' => 'Inget tillgängligt projekt.', + 'Local File' => 'Lokal fil', + 'Configuration' => 'Konfiguration', + 'PHP version:' => 'PHP-version:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'OS-version:', + 'Database version:' => 'Databas-version:', + 'Browser:' => 'Webbläsare:', + 'Task view' => 'Uppgiftsvy', + 'Edit task' => 'Ändra uppgift', + 'Edit description' => 'Ändra beskrivning', + 'New internal link' => 'Ny intern länk', + 'Display list of keyboard shortcuts' => 'Visa lista av kortkommandon', + 'Avatar' => 'Avatar', + 'Upload my avatar image' => 'Ladda upp min avatar', + 'Remove my image' => 'Radera min bild', + 'The OAuth2 state parameter is invalid' => 'TillstÃ¥ndsparametern till OAuth2 är ogiltig', + 'User not found.' => 'Användaren hittades inte.', + 'Search in activity stream' => 'Sök i aktivitetsström', + 'My activities' => 'Mina aktiviteter', + 'Activity until yesterday' => 'Aktivitet fram till igÃ¥r', + 'Activity until today' => 'Aktivitet fram till idag', + 'Search by creator: ' => 'Sök pÃ¥ skapare: ', + 'Search by creation date: ' => 'Sök pÃ¥ skapandedatum: ', + 'Search by task status: ' => 'Sök pÃ¥ uppgiftsstatus: ', + 'Search by task title: ' => 'Sök pÃ¥ uppgiftstitel: ', + 'Activity stream search' => 'Sök i aktivitetsström', + 'Projects where "%s" is manager' => 'Projekt där "%s" är ledare', + 'Projects where "%s" is member' => 'Projekt där "%s" är medlem', + 'Open tasks assigned to "%s"' => 'Öppna uppgifter tilldelade till "%s"', + 'Closed tasks assigned to "%s"' => 'Stängda uppgifter tilldelade till "%s"', + 'Assign automatically a color based on a priority' => 'Tilldela färg baserat pÃ¥ prioritet automatiskt', + 'Overdue tasks for the project(s) "%s"' => 'Försenade uppgifter för projekt(en) "%s"', + 'Upload files' => 'Ladda upp filer', + 'Installed Plugins' => 'Installerade insticksprogram', + 'Plugin Directory' => 'Katalog för insticksprogram', + 'Plugin installed successfully.' => 'Insticksprogram installerat.', + 'Plugin updated successfully.' => 'Insticksprogram uppdaterat.', + 'Plugin removed successfully.' => 'Insticksprogram borttaget.', + 'Subtask converted to task successfully.' => 'Deluppgift konverterades till uppgift.', + 'Unable to convert the subtask.' => 'Kunde inte konvertera deluppgift.', + 'Unable to extract plugin archive.' => 'Kunde inte packa upp arkivet med insticksprogram.', + 'Plugin not found.' => 'Insticksprogram kunde inte hittas.', + 'You don\'t have the permission to remove this plugin.' => 'Du har inte behörighet att radera insticksprogrammet.', + 'Unable to download plugin archive.' => 'Kunde inte ladda ner arkiv med insticksprogram.', + 'Unable to write temporary file for plugin.' => 'Kunde inte skriva temporär fil för insticksprogram.', + 'Unable to open plugin archive.' => 'Kunde inte öppna arkiv med insticksprogram.', + 'There is no file in the plugin archive.' => 'Filer saknas i insticksprogramarkivet.', + 'Create tasks in bulk' => 'Skapa uppgifter i bulk', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Din instans av Kanboard är inte konfigurerad för att installera insticksprogram frÃ¥n användergränssnittet.', + 'There is no plugin available.' => 'Inget insticksprogram tillgängligt.', + 'Install' => 'Installera', + 'Update' => 'Uppdatera', + 'Up to date' => 'Uppdaterad', + 'Not available' => 'Inte tillgänglig', + 'Remove plugin' => 'Radera insticksprogram', + 'Do you really want to remove this plugin: "%s"?' => 'Vill du verkligen radera insticksprogram: "%s"?', + 'Uninstall' => 'Avinstallera', + 'Listing' => 'Listar', + 'Metadata' => 'Metadata', + 'Manage projects' => 'Hantera projekt', + 'Convert to task' => 'Konvertera till uppgift', + 'Convert sub-task to task' => 'Konvertera deluppgift till uppgift', + 'Do you really want to convert this sub-task to a task?' => 'Vill du verkligen konvertera deluppgiften till en uppgift?', + 'My task title' => 'Titel pÃ¥ min uppgift', + 'Enter one task by line.' => 'Fyll i en uppgift per rad.', + 'Number of failed login:' => 'Antal misslyckade inloggningar:', + 'Account locked until:' => 'Kontot lÃ¥st till:', + 'Email settings' => 'E-postinställningar', + 'Email sender address' => 'E-postavsändare', + 'Email transport' => 'Transportval för e-post', + 'Webhook token' => 'Token för webhooks', + 'Project tags management' => 'Projektmärkningshantering', + 'Tag created successfully.' => 'Märkning skapades.', + 'Unable to create this tag.' => 'Kunde inte skapa märkning.', + 'Tag updated successfully.' => 'Märkning uppdaterad.', + 'Unable to update this tag.' => 'Kunde inte uppdatera märkning.', + 'Tag removed successfully.' => 'Märkning har tagits bort.', + 'Unable to remove this tag.' => 'Kunde inte ta bort märkningen.', + 'Global tags management' => 'Global märkningshantering', + 'Tags' => 'Märkningar', + 'Tags management' => 'Märkningshantering', + 'Add new tag' => 'Lägg till ny märkning', + 'Edit a tag' => 'Ändra en märkning', + 'Project tags' => 'Projektmärkningar', + 'There is no specific tag for this project at the moment.' => 'Det finns ingen märkning i projektet ännu.', + 'Tag' => 'Märkning', + 'Remove a tag' => 'Ta bort en märkning', + 'Do you really want to remove this tag: "%s"?' => 'Vill du verkligen ta bort märkningen "%s"?', + 'Global tags' => 'Globala märkningar', + 'There is no global tag at the moment.' => 'Det finns ingen global märkning ännu.', + 'This field cannot be empty' => 'Detta fältet kan inte lämnas tomt', + 'Close a task when there is no activity in a specific column' => 'Stäng en uppgift om aktivitet saknas i en viss kolumn', + '%s removed a subtask for the task #%d' => '%s tog bort en deluppgift i aktivitet #%d', + '%s removed a comment on the task #%d' => '%s tog bort en kommentar pÃ¥ aktivitet #%d', + 'Comment removed on task #%d' => 'Kommentar borttagen frÃ¥n aktivitet #%d', + 'Subtask removed on task #%d' => 'Deluppgift borttagen frÃ¥n aktivitet #%d', + 'Hide tasks in this column in the dashboard' => 'Göm uppgifter i denna kolumn pÃ¥ översiktspanelen', + '%s removed a comment on the task %s' => '%s tog bort en kommentar frÃ¥n uppgiften %s', + '%s removed a subtask for the task %s' => '%s tog bort en deluppgift frÃ¥n uppgiften %s', + 'Comment removed' => 'Kommentar borttagen', + 'Subtask removed' => 'Deluppgift borttagen', + '%s set a new internal link for the task #%d' => '%s tilldelade en ny intern länk till uppgift #%d', + '%s removed an internal link for the task #%d' => '%s tog bort en intern länk frÃ¥n uppgift #%d', + 'A new internal link for the task #%d has been defined' => 'En ny intern länk har skapats till uppgift #%d', + 'Internal link removed for the task #%d' => 'Intern länk togs bort frÃ¥n uppgift #%d', + '%s set a new internal link for the task %s' => '%s tilldelade en ny intern länk till uppgiften %s', + '%s removed an internal link for the task %s' => '%s tog bort en intern länk frÃ¥n uppgiften %s', + 'Automatically set the due date on task creation' => 'Sätt förfallodatum automatiskt vid skapande av uppgift', + 'Move the task to another column when closed' => 'Flytta uppgiften till en annan kolumn när den stängs', + 'Move the task to another column when not moved during a given period' => 'Flytta uppgiften till en annan kolumn när den inte flyttats under en given tidsperiod', + 'Dashboard for %s' => 'Översiktspanel för %s', + 'Tasks overview for %s' => 'Uppgiftsöversikt för %s', + 'Subtasks overview for %s' => 'Deluppgiftsöversikt för %s', + 'Projects overview for %s' => 'Projektöversikt för %s', + 'Activity stream for %s' => 'Aktivitetsström för %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Tilldela en färg till uppgiften när den flyttas till en viss simbana', + 'Assign a priority when the task is moved to a specific swimlane' => 'Tilldela en prioritet till uppgiften när den flyttas till en viss simbana', + 'User unlocked successfully.' => 'Användare upplÃ¥st.', + 'Unable to unlock the user.' => 'Kunde inte lÃ¥sa upp användare.', + 'Move a task to another swimlane' => 'Flytta en uppgift till en annan simbana', + 'Creator Name' => 'Namn pÃ¥ skapare', + 'Time spent and estimated' => 'Förbrukad och uppskattad tid', + 'Move position' => 'Flytta position', + 'Move task to another position on the board' => 'Flytta uppgiften till en annan position pÃ¥ tavlan', + 'Insert before this task' => 'Infoga före denna uppgift', + 'Insert after this task' => 'Infoga efter denna uppgift', + 'Unlock this user' => 'LÃ¥s upp denna användare', + 'Custom Project Roles' => 'Anpassade projektroller', + 'Add a new custom role' => 'Skapa ny anpassad roll', + 'Restrictions for the role "%s"' => 'Restriktioner för roll "%s"', + 'Add a new project restriction' => 'Lägg till ny projektrestriktion', + 'Add a new drag and drop restriction' => 'Lägg till ny dra-och-släpprestriktion', + 'Add a new column restriction' => 'Lägg till ny kolumnrestriktion', + 'Edit this role' => 'Ändra denna rollen', + 'Remove this role' => 'Ta bort denna rollen', + 'There is no restriction for this role.' => 'Det finns inga restriktioner för denna rollen', + 'Only moving task between those columns is permitted' => 'Endast tillÃ¥tet att flytta uppgifter mellan dessa kolumner', + 'Close a task in a specific column when not moved during a given period' => 'Stäng en uppgift i en specifik kolumn om den inte flyttas under en viss period', + 'Edit columns' => 'Redigera kolumner', + 'The column restriction has been created successfully.' => 'Kolumnbegränsningen har skapats.', + 'Unable to create this column restriction.' => 'Det gick inte att skapa denna kolumnbegränsning.', + 'Column restriction removed successfully.' => 'Kolumnbegränsningen har tagits bort.', + 'Unable to remove this restriction.' => 'Det gick inte att ta bort denna begränsning.', + 'Your custom project role has been created successfully.' => 'Din anpassade projektroll har skapats.', + 'Unable to create custom project role.' => 'Det gick inte att skapa anpassad projektroll.', + 'Your custom project role has been updated successfully.' => 'Din anpassade projektroll har uppdaterats.', + 'Unable to update custom project role.' => 'Det gick inte att uppdatera anpassad projektroll.', + 'Custom project role removed successfully.' => 'Anpassad projektroll har tagits bort.', + 'Unable to remove this project role.' => 'Det gick inte att ta bort denna projektroll.', + 'The project restriction has been created successfully.' => 'Projektbegränsningen har skapats.', + 'Unable to create this project restriction.' => 'Det gick inte att skapa denna projektbegränsning.', + 'Project restriction removed successfully.' => 'Projektbegränsningen har tagits bort.', + 'You cannot create tasks in this column.' => 'Du kan inte skapa uppgifter i denna kolumn.', + 'Task creation is permitted for this column' => 'Uppgiftsskapande är tillÃ¥tet för denna kolumn', + 'Closing or opening a task is permitted for this column' => 'Att stänga eller öppna en uppgift är tillÃ¥tet för denna kolumn', + 'Task creation is blocked for this column' => 'Uppgiftsskapande är blockerat för denna kolumn', + 'Closing or opening a task is blocked for this column' => 'Att stänga eller öppna en uppgift är blockerat för denna kolumn', + 'Task creation is not permitted' => 'Uppgiftsskapande är inte tillÃ¥tet', + 'Closing or opening a task is not permitted' => 'Att stänga eller öppna en uppgift är inte tillÃ¥tet', + 'New drag and drop restriction for the role "%s"' => 'Ny dra-och-släpp-begränsning för rollen "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Personer med denna roll kan endast flytta uppgifter mellan käll- och destinationskolumnen.', + 'Remove a column restriction' => 'Ta bort en kolumnbegränsning', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Vill du verkligen ta bort denna kolumnbegränsning: "%s" till "%s"?', + 'New column restriction for the role "%s"' => 'Ny kolumnbegränsning för rollen "%s"', + 'Rule' => 'Regel', + 'Do you really want to remove this column restriction?' => 'Vill du verkligen ta bort denna kolumnbegränsning?', + 'Custom roles' => 'Anpassade roller', + 'New custom project role' => 'Ny anpassad projektroll', + 'Edit custom project role' => 'Redigera anpassad projektroll', + 'Remove a custom role' => 'Ta bort en anpassad roll', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Vill du verkligen ta bort denna anpassade roll: "%s"? Alla personer tilldelade denna roll kommer att bli projektmedlemmar.', + 'There is no custom role for this project.' => 'Det finns ingen anpassad roll för detta projekt.', + 'New project restriction for the role "%s"' => 'Ny projektrestriktion för roll "%s"', + 'Restriction' => 'Restriktion', + 'Remove a project restriction' => 'Ta bort en projektrestriktion', + 'Do you really want to remove this project restriction: "%s"?' => 'Vill du verkligen ta bort projektrestriktion: "%s"?', + 'Duplicate to multiple projects' => 'Duplicera till multipla projekt', + 'This field is required' => 'Detta fält mÃ¥ste vara ifyllt', + 'Moving a task is not permitted' => 'Flytt av uppgift ej tillÃ¥ten', + 'This value must be in the range %d to %d' => 'Värdet mÃ¥ste vara mellan %d och %d', + 'You are not allowed to move this task.' => 'Du har inte behörighet att flytta uppgiften.', + 'API User Access' => 'Användarbehörighet för API', + 'Preview' => 'Förhandsvisning', + 'Write' => 'Skriv', + 'Write your text in Markdown' => 'Uppgiftsbeskrivning i Markdown-format', + 'No personal API access token registered.' => 'Ingen personlig API-token registrerad.', + 'Your personal API access token is "%s"' => 'Din personliga API-token är "%s"', + 'Remove your token' => 'Radera din token', + 'Generate a new token' => 'Generera ny token', + 'Showing %d-%d of %d' => 'Visar %d-%d av %d', + 'Outgoing Emails' => 'UtgÃ¥ende e-post', + 'Add or change currency rate' => 'Lägg till eller ändra valutakurs', + 'Reference currency: %s' => 'Referensvaluta: %s', + 'Add custom filters' => 'Lägg till anpassade filter', + 'Export' => 'Exportera', + 'Add link label' => 'Lägg till länketikett', + 'Incompatible Plugins' => 'Okompatibla insticksprogram', + 'Compatibility' => 'Kompatibilitet', + 'Permissions and ownership' => 'Behörighet och ägarskap', + 'Priorities' => 'Prioriteringar', + 'Close this window' => 'Stäng detta fönster', + 'Unable to upload this file.' => 'Kunde inte ladda upp filen.', + 'Import tasks' => 'Importera uppgifter', + 'Choose a project' => 'Välj ett projekt', + 'Profile' => 'Profil', + 'Application role' => 'Applikationsroll', + '%d invitations were sent.' => '%d inbjudningar skickades.', + '%d invitation was sent.' => '%d inbjudan skickades.', + 'Unable to create this user.' => 'Kunde inte skapa användaren.', + 'Kanboard Invitation' => 'Kanboard-inbjudan', + 'Visible on dashboard' => 'Synlig pÃ¥ översiktspanel', + 'Created at:' => 'Skapad vid:', + 'Updated at:' => 'Uppdaterad vid;', + 'There is no custom filter.' => 'Det finns inget anpassat filter.', + 'New User' => 'Ny användare', + 'Authentication' => 'Autentisering', + 'If checked, this user will use a third-party system for authentication.' => 'Vid ikryssning fÃ¥r denna användare använda ett tredjepartssystem för autentisering.', + 'The password is necessary only for local users.' => 'Lösenordet är endast nödvändigt för lokala användare.', + 'You have been invited to register on Kanboard.' => 'Du har blivit inbjuden att registrera dig pÃ¥ Kanboard', + 'Click here to join your team' => 'Klicka här för att gÃ¥ med i gruppen', + 'Invite people' => 'Bjud in', + 'Emails' => 'E-brev', + 'Enter one email address by line.' => 'Skriv en e-postadress per rad.', + 'Add these people to this project' => 'Lägg till dessa personer till projektet', + 'Add this person to this project' => 'Lägg till denna person till projektet', + 'Sign-up' => 'Registrera', + 'Credentials' => 'Inloggningsuppgifter', + 'New user' => 'Ny användare', + 'This username is already taken' => 'Detta användarnamnet är redan upptaget', + 'Your profile must have a valid email address.' => 'Din profil mÃ¥ste ha en giltig e-postadress.', + 'TRL - Turkish Lira' => 'TRL - Turkisk lira', + 'The project email is optional and could be used by several plugins.' => 'Att ange en e-post för projektet är frivilligt och kan användas av flera insticksmoduler.', + 'The project email must be unique across all projects' => 'Projektets e-post mÃ¥ste vara unik för projektet', + 'The email configuration has been disabled by the administrator.' => 'E-postkonfigurationen har inaktiverats av administratören.', + 'Close this project' => 'Stäng projektet', + 'Open this project' => 'Öppna projektet', + 'Close a project' => 'Stäng ett projekt', + 'Do you really want to close this project: "%s"?' => 'Vill du verkligen stänga projektet "%s"?', + 'Reopen a project' => 'Ã…teröppna ett projekt', + 'Do you really want to reopen this project: "%s"?' => 'Vill du verkligen Ã¥teröppna projektet "%s"?', + 'This project is open' => 'Projektet är öppet', + 'This project is closed' => 'Projektet är stängt', + 'Unable to upload files, check the permissions of your data folder.' => 'Kunde inte ladda upp filer. Kontrollera rättigheterna pÃ¥ din datakatalog.', + 'Another category with the same name exists in this project' => 'En annan kategori med samma namn finns redan i projektet', + 'Comment sent by email successfully.' => 'Kommentar skickad via e-post.', + 'Sent by email to "%s" (%s)' => 'Skickad via e-post till "%s" (%s)', + 'Unable to read uploaded file.' => 'Kan inte läsa uppladdad fil.', + 'Database uploaded successfully.' => 'Databas uppladdad.', + 'Task sent by email successfully.' => 'Uppgifter skickade via mail.', + 'There is no category in this project.' => 'Det finns ingen kategori i detta projektet.', + 'Send by email' => 'Skicka via e-post', + 'Create and send a comment by email' => 'Skapa och skicka en kommentar via e-post', + 'Subject' => 'Ärende', + 'Upload the database' => 'Ladda upp databasen', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Du kan ladda upp den tidigare nedladdade Sqlite-databasen (i Gzip-format).', + 'Database file' => 'Databasfil', + 'Upload' => 'Ladda upp', + 'Your project must have at least one active swimlane.' => 'Ditt projekt mÃ¥ste ha Ã¥tminstone en aktiv simbana.', + 'Project: %s' => 'Projekt: %s', + 'Automatic action not found: "%s"' => 'Automatisk Ã¥tgärd kunde inte hittas: "%s"', + '%d projects' => '%d projekt', + '%d project' => '%d projekt', + 'There is no project.' => 'Det finns inget projekt', + 'Sort' => 'Sortera', + 'Project ID' => 'Projekt-id', + 'Project name' => 'Projektnamn', + 'Public' => 'Offentlig', + 'Personal' => 'Personlig', + '%d tasks' => '%d uppgifter', + '%d task' => '%d uppgift', + 'Task ID' => 'Uppgift-id', + 'Assign automatically a color when due date is expired' => 'Tilldela en färg automatiskt när förfallodatum har passerat', + 'Total score in this column across all swimlanes' => 'Total poäng i denna kolumn över samtliga simbanor', + 'HRK - Kuna' => 'HRK - kuna', + 'ARS - Argentine Peso' => 'ARS - Argentisk peso', + 'COP - Colombian Peso' => 'COP - Colombiansk peso', + '%d groups' => '%d grupper', + '%d group' => '%d grupp', + 'Group ID' => 'Grupp-id', + 'External ID' => 'Externt id', + '%d users' => '%d användare', + '%d user' => '%d användare', + 'Hide subtasks' => 'Göm deluppgifter', + 'Show subtasks' => 'Visa deluppgifter', + 'Authentication Parameters' => 'Autentiseringsparametrar', + 'API Access' => 'API-tillgÃ¥ng', + 'No users found.' => 'Inga användare hittades.', + 'User ID' => 'Användar-id', + 'Notifications are activated' => 'Notifieringar är aktiverade', + 'Notifications are disabled' => 'Notifieringar är inaktiverade', + 'User disabled' => 'Användare inaktiverad', + '%d notifications' => '%d notifieringar', + '%d notification' => '%d notifiering', + 'There is no external integration installed.' => 'Ingen extern integration har installerats.', + 'You are not allowed to update tasks assigned to someone else.' => 'Du har inte tillÃ¥telse att uppdatera uppgifter som är tilldelade till nÃ¥gon annan.', + 'You are not allowed to change the assignee.' => 'Du har inte behörighet att ändra uppgiftstilldelning.', + 'Task suppression is not permitted' => 'Borttagning av uppgift ej tillÃ¥ten', + 'Changing assignee is not permitted' => 'Ändring av uppgiftstilldelning är inte tillÃ¥ten', + 'Update only assigned tasks is permitted' => 'Endast ändring av tilldelade uppgifter tillÃ¥ten', + 'Only for tasks assigned to the current user' => 'Endast för uppgifter tilldelade till nuvarande användare', + 'My projects' => 'Mina projekt', + 'You are not a member of any project.' => 'Du är inte medlem i nÃ¥got projekt.', + 'My subtasks' => 'Mina deluppgifter', + '%d subtasks' => '%d deluppgifter', + '%d subtask' => '%d deluppgift', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Endast förflyttning mellan dessa kolumner är tillÃ¥ten för uppgifter som är tilldelade den nuvarande användaren', + '[DUPLICATE]' => '[DUPLICERAD]', + 'DKK - Danish Krona' => 'DKK - Dansk krona', + 'Remove user from group' => 'Ta bort användare frÃ¥n grupp', + 'Assign the task to its creator' => 'Tilldela uppgiften till sin skapare', + 'This task was sent by email to "%s" with subject "%s".' => 'Uppgiften skickades per e-post till "%s" med titeln "%s".', + 'Predefined Email Subjects' => 'Fördefinierade e-postmottagare', + 'Write one subject by line.' => 'Skriv en titel per rad.', + 'Create another link' => 'Skapa ytterligare en länk', + 'BRL - Brazilian Real' => 'BRL - Brasiliansk real', + 'Add a new Kanboard task' => 'Skapa en ny Kanboard-uppgift', + 'Subtask not started' => 'Deluppgift ej pÃ¥börjad', + 'Subtask currently in progress' => 'Deluppgift pÃ¥gÃ¥ende', + 'Subtask completed' => 'Deluppgift färdig', + 'Subtask added successfully.' => 'Deluppgift tillagd.', + '%d subtasks added successfully.' => '%d deluppgifter tillagda.', + 'Enter one subtask by line.' => 'Skriv en deluppgift per rad.', + 'Predefined Contents' => 'Fördefinierat innehÃ¥ll', + 'Predefined contents' => 'Fördefinierat innehÃ¥ll', + 'Predefined Task Description' => 'Förderinierad uppgiftsbeskrivning', + 'Do you really want to remove this template? "%s"' => 'Vill du verkligen ta bort mallen "%s"?', + 'Add predefined task description' => 'Skapa fördefinierad uppgiftsbeskrivning', + 'Predefined Task Descriptions' => 'Fördefinierad uppgiftsbeskrivningar', + 'Template created successfully.' => 'Mallen skapades.', + 'Unable to create this template.' => 'Kunde inte skapa mallen.', + 'Template updated successfully.' => 'Mallen uppdaterades.', + 'Unable to update this template.' => 'Kunde inte uppdatera mallen.', + 'Template removed successfully.' => 'Mallen togs bort.', + 'Unable to remove this template.' => 'Kunde inte ta bort mallen.', + 'Template for the task description' => 'Mall för uppgiftsbeskrivning', + 'The start date is greater than the end date' => 'Startdatum är senare än slutdatum', + 'Tags must be separated by a comma' => 'Märkningar mÃ¥ste vara kommaseparerade', + 'Only the task title is required' => 'Endast märkningens titel krävs', + 'Creator Username' => 'Skapares användarnamn', + 'Color Name' => 'Färgnamn', + 'Column Name' => 'Kolumnnamn', + 'Swimlane Name' => 'Simbanans namn', + 'Time Estimated' => 'Estimerad tid', + 'Time Spent' => 'Spenderad tid', + 'External Link' => 'Extern länk', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Denna funktion aktiverar iCal-flöde, RSS-flöde, samt offentlig tavelvy.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Stoppa timern för samtliga deluppgifter när en uppgift flyttas till en annan kolumn', + 'Subtask Title' => 'Deluppgiftstitel', + 'Add a subtask and activate the timer when moving a task to another column' => 'Lägg till en deluppgift och aktivera timern när en uppgift flyttas till en annan kolumn', + 'days' => 'dagar', + 'minutes' => 'minuter', + 'seconds' => 'sekunder', + 'Assign automatically a color when preset start date is reached' => 'Tilldela automatiskt en färg när förvalt startdatum infaller', + 'Move the task to another column once a predefined start date is reached' => 'Flytta uppgiften till en annan kolumn när ett fördefinierat startdatum infaller', + 'This task is now linked to the task %s with the relation "%s"' => 'Denna uppgift är nu länkad till uppgiften "%s" via relationen "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Länken med relationen "%s" till uppgiften "%s" har tagits bort', + 'Custom Filter:' => 'Anpassat filter:', + 'Unable to find this group.' => 'Kunde inte hitta gruppen.', + '%s moved the task #%d to the column "%s"' => '%s flyttade uppgift #%d till kolumnen "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s flyttade uppgift #%d till till position %d i kolumnen "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s flyttade uppgift #%d till simbanan "%s"', + '%sh spent' => '%stim. förbrukat', + '%sh estimated' => '%stim. uppskattat', + 'Select All' => 'Välj alla', + 'Unselect All' => 'Avmarkera alla', + 'Apply action' => 'Genomför Ã¥tgärd', + 'Move selected tasks to another column or swimlane' => 'Flytta markerade uppgifter till en annan kolumn', + 'Edit tasks in bulk' => 'Massredigera uppgifer', + 'Choose the properties that you would like to change for the selected tasks.' => 'Välj vilka egenskaper som du vill ändra för de valda uppgifterna.', + 'Configure this project' => 'Konfigurera projekt', + 'Start now' => 'Börja nu', + '%s removed a file from the task #%d' => '%s tog bort en fil frÃ¥n uppgift #%d', + 'Attachment removed from task #%d: %s' => 'Bilaga borttagen frÃ¥n uppgift #%d: %s', + 'No color' => 'Ingen färg', + 'Attachment removed "%s"' => 'Bilaga "%s" borttagen', + '%s removed a file from the task %s' => '%s tog bort en fil frÃ¥n uppgiften %s', + 'Move the task to another swimlane when assigned to a user' => 'Flytta uppgiften till en annan simbana när den tilldelas en användare', + 'Destination swimlane' => 'Destinationssimbana', + 'Assign a category when the task is moved to a specific swimlane' => 'Tilldela en kategori när uppgiften flyttas till en specifik simbana', + 'Move the task to another swimlane when the category is changed' => 'Flytta uppgiften till en annan simbana när kategorin ändras', + 'Reorder this column by priority (ASC)' => 'Ändra ordning i denna kolumnen enligt prioritet (stigande)', + 'Reorder this column by priority (DESC)' => 'Ändra ordning i denna kolumnen enligt prioritet (fallande)', + 'Reorder this column by assignee and priority (ASC)' => 'Ändra ordning i denna kolumnen enligt tilldelning och prioritet (stigande)', + 'Reorder this column by assignee and priority (DESC)' => 'Ändra ordning i denna kolumnen enligt tilldelning och prioritet (fallande)', + 'Reorder this column by assignee (A-Z)' => 'Ändra ordning i denna kolumnen enligt tilldelning (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Ändra ordning i denna kolumnen enligt tilldelning (Z-A)', + 'Reorder this column by due date (ASC)' => 'Ändra ordning i denna kolumnen enligt förfallodatum (stigande)', + 'Reorder this column by due date (DESC)' => 'Ändra ordning i denna kolumnen enligt förfallodatum (fallande)', + 'Reorder this column by id (ASC)' => 'Sortera denna kolumn efter ID (stigande)', + 'Reorder this column by id (DESC)' => 'Sortera denna kolumn efter ID (fallande)', + '%s moved the task #%d "%s" to the project "%s"' => '%s flyttade uppgift #%d "%s" till projektet "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Uppgift #%d "%s" har flyttats till projektet "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Flytta uppgiften till en annan kolumn när förfallodatum är närmare än ett visst antal dagar', + 'Automatically update the start date when the task is moved away from a specific column' => 'Uppdatera startdatum automatiskt när en uppgift flyttas frÃ¥n en specifik kolumn', + 'HTTP Client:' => 'HTTP-klient;', + 'Assigned' => 'Tilldelad', + 'Task limits apply to each swimlane individually' => 'Uppgiftsbegränsningar tillämpas pÃ¥ varje simbana individuellt', + 'Column task limits apply to each swimlane individually' => 'Kolumners uppgiftsbegränsning tillämpas pÃ¥ varje simbana individuellt', + 'Column task limits are applied to each swimlane individually' => 'Kolumners uppgiftsbegränsning tillämpas pÃ¥ varje simbana individuellt', + 'Column task limits are applied across swimlanes' => 'Kolumners uppgiftsbegränsning tillämpas över simbanor', + 'Task limit: ' => 'Uppgiftsgräns: ', + 'Change to global tag' => 'Ändra till global märkning', + 'Do you really want to make the tag "%s" global?' => 'Vill du verkligen göra märkningen "%s" global?', + 'Enable global tags for this project' => 'Aktivera global märkning för detta projekt', + 'Group membership(s):' => 'Gruppmedlemskap:', + '%s is a member of the following group(s): %s' => '%s är medlem i följande grupp(er): %s', + '%d/%d group(s) shown' => '%d/%d grupp(er) visade', + 'Subtask creation or modification' => 'Delupupgifts skapande eller ändrande', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Tilldela uppgiften till en viss användare när uppgiften flyttas till en viss swimlane', + 'Comment' => 'Kommentar', + 'Collapse vertically' => 'Fäll ihop vertikalt', + 'Expand vertically' => 'Fäll ut vertikalt', + 'MXN - Mexican Peso' => 'MXN - Mexikansk peso', + 'Estimated vs actual time per column' => 'Uppskattad kontra faktisk tid per kolumn', + 'HUF - Hungarian Forint' => 'HUF - Ungersk forint', + 'XBT - Bitcoin' => 'XBT – Bitcoin', + 'You must select a file to upload as your avatar!' => 'Du mÃ¥ste välja en fil att uppladda som din avatar!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Filen du laddade upp är inte ett giltigt bildformat! (Endast *.gif *.jpg *.jpeg samt *.png är tillÃ¥tet!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Ställ automatiskt in förfallodatum när uppgiften flyttas frÃ¥n en specifik kolumn', + 'No other projects found.' => 'Inga andra projekt hittades.', + 'Tasks copied successfully.' => 'Uppgifter kopierades framgÃ¥ngsrikt.', + 'Unable to copy tasks.' => 'Det gick inte att kopiera uppgifter.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Ljust tema', + 'Dark theme' => 'Mörkt tema', + 'Automatic theme - Sync with system' => 'Automatiskt tema – synkronisera med systemet', + 'Application managers or more' => 'Applikationsansvariga eller högre', + 'Administrators' => 'Administratörer', + 'Visibility:' => 'Synlighet:', + 'Standard users' => 'Standardanvändare', + 'Visibility is required' => 'Synlighet krävs', + 'The visibility should be an app role' => 'Synligheten bör vara en applikationsroll', + 'Reply' => 'Svara', + '%s wrote: ' => '%s skrev: ', + 'Number of visible tasks in this column and swimlane' => 'Antal synliga uppgifter i denna kolumn och simlinje', + 'Number of tasks in this swimlane' => 'Antal uppgifter i denna simlinje', + 'Unable to find another subtask in progress, you can close this window.' => 'Kunde inte hitta en annan deluppgift i gÃ¥ng, du kan stänga detta fönster.', + 'This theme is invalid' => 'Detta tema är ogiltigt', + 'This role is invalid' => 'Denna roll är ogiltig', + 'This timezone is invalid' => 'Denna tidszon är ogiltig', + 'This language is invalid' => 'Detta sprÃ¥k är ogiltigt', + 'This URL is invalid' => 'Denna URL är ogiltig', + 'Date format invalid' => 'Ogiltigt datumformat', + 'Time format invalid' => 'Ogiltigt tidsformat', + 'Invalid Mail transport' => 'Ogiltig e-posttransport', + 'Color invalid' => 'Ogiltig färg', + 'This value must be greater or equal to %d' => 'Detta värde mÃ¥ste vara större än eller lika med %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Lägg till en BOM i början av filen (krävs för Microsoft Excel)', + 'Just add these tag(s)' => 'Lägg bara till dessa taggar', + 'Remove internal link(s)' => 'Ta bort interna länkar', + 'Import tasks from another project' => 'Importera uppgifter frÃ¥n ett annat projekt', + 'Select the project to copy tasks from' => 'Välj projektet att kopiera uppgifter frÃ¥n', + 'The total maximum allowed attachments size is %sB.' => 'Den totala maximalt tillÃ¥tna bilagestorleken är %sB.', + 'Add attachments' => 'Lägg till bilagor', + 'Task #%d "%s" is overdue' => 'Uppgift #%d "%s" är försenad', + 'Enable notifications by default for all new users' => 'Aktivera notifieringar som standard för alla nya användare', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Tilldela uppgiften till dess skapare för specifika kolumner om ingen ansvarig är satt manuellt', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Tilldela en uppgift till den inloggade användaren vid kolumnbyte till angiven kolumn om ingen användare är tilldelad', +]; diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php new file mode 100644 index 0000000..600ba6a --- /dev/null +++ b/app/Locale/th_TH/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'ไม่มี', + 'Edit' => 'à¹à¸à¹‰à¹„ข', + 'Remove' => 'ลบ', + 'Yes' => 'ใช่', + 'No' => 'ไม่', + 'cancel' => 'ยà¸à¹€à¸¥à¸´à¸', + 'or' => 'หรือ', + 'Yellow' => 'สีเหลือง', + 'Blue' => 'สีน้ำเงิน', + 'Green' => 'สีเขียว', + 'Purple' => 'สีม่วง', + 'Red' => 'สีà¹à¸”ง', + 'Orange' => 'สีส้ม', + 'Grey' => 'สีเทา', + 'Brown' => 'สีน้ำตาล', + 'Deep Orange' => 'สีส้มเข้ม', + 'Dark Grey' => 'สีเทาเข้ม', + 'Pink' => 'สีชมพู', + 'Teal' => 'สีเขียวหัวเป็ด', + 'Cyan' => 'สีฟ้า', + 'Lime' => 'สีมะนาว', + 'Light Green' => 'สีเขียวสว่าง', + 'Amber' => 'สีเหลืองอำพัน', + 'Save' => 'บันทึà¸', + 'Login' => 'เข้าสู่ระบบ', + 'Official website:' => 'เวบไซต์อย่างเป็นทางà¸à¸²à¸£:', + 'Unassigned' => 'ไม่à¸à¸³à¸«à¸™à¸”', + 'View this task' => 'รายละเอียดงานนี้', + 'Remove user' => 'เอาผู้ใช้ออà¸', + 'Do you really want to remove this user: "%s"?' => 'คุณต้องà¸à¸²à¸£à¹€à¸­à¸²à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ « %s » ออà¸à¹ƒà¸Šà¹ˆà¸«à¸£à¸·à¸­à¹„ม่?', + 'All users' => 'ผู้ใช้ทั้งหมด', + 'Username' => 'ชื่อผู้ใช้', + 'Password' => 'รหัสผ่าน', + 'Administrator' => 'ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š', + 'Sign in' => 'เข้าสู่ระบบ', + 'Users' => 'ผู้ใช้', + 'Forbidden' => 'ไม่อนุà¸à¸²à¸•', + 'Access Forbidden' => 'ไม่อนุà¸à¸²à¸•ให้เข้า', + 'Edit user' => 'à¹à¸à¹‰à¹„ขผู้ใช้', + 'Logout' => 'ออà¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸š', + 'Bad username or password' => 'ชื่อผู้ใช้หรือรหัสผ่านผิด', + 'Edit project' => 'à¹à¸à¹‰à¹„ขโปรเจค', + 'Name' => 'ชื่อ', + 'Projects' => 'โปรเจค', + 'No project' => 'ไม่มีโปรเจค', + 'Project' => 'โปรเจค', + 'Status' => 'สถานะ', + 'Tasks' => 'งาน', + 'Board' => 'บอร์ด', + 'Actions' => 'à¸à¸²à¸£à¸à¸£à¸°à¸—ำ', + 'Inactive' => 'ไม่เปิดใช้งาน', + 'Active' => 'เปิดใช้งาน', + 'Unable to update this board.' => 'ไม่สามารถปรับปรุงบอร์ดได้.', + 'Disable' => 'ปิดà¸à¸²à¸£à¸—ำงาน', + 'Enable' => 'เปิดà¸à¸²à¸£à¸—ำงาน', + 'New project' => 'โปรเจคใหม่', + 'Do you really want to remove this project: "%s"?' => 'คุณต้องà¸à¸²à¸£à¹€à¸­à¸²à¹‚ปรเจค « %s » ออà¸à¹ƒà¸Šà¹ˆà¸«à¸£à¸·à¸­à¹„ม่?', + 'Remove project' => 'ลบโปรเจค', + 'Edit the board for "%s"' => 'à¹à¸à¹‰à¹„ขบอร์ดสำหรับ « %s »', + 'Add a new column' => 'เพิ่มคอลัมน์ใหม่', + 'Title' => 'หัวเรื่อง', + 'Assigned to %s' => 'à¸à¸³à¸«à¸™à¸”ให้ %s', + 'Remove a column' => 'ลบคอลัมน์', + 'Unable to remove this column.' => 'ไม่สามารถลบคอลัมน์นี้', + 'Do you really want to remove this column: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¸„อลัมน์ « %s » ออà¸à¹ƒà¸Šà¹ˆà¸«à¸£à¸·à¸­à¹„ม่?', + 'Settings' => 'ตั้งค่า', + 'Application settings' => 'ตั้งค่าà¸à¸²à¸£à¸—ำงาน', + 'Language' => 'ภาษา', + 'Webhook token:' => 'โทเค็น Webhook:', + 'API token:' => 'API token:', + 'Database size:' => 'ขนาดà¸à¸²à¸™à¸‚้อมูล:', + 'Download the database' => 'ดาวน์โหลดà¸à¸²à¸™à¸‚้อมูล', + 'Optimize the database' => 'ปรับปรุงà¸à¸²à¸™à¸‚้อมูล', + '(VACUUM command)' => '(VACUUM command)', + '(Gzip compressed Sqlite file)' => '(Gzip compressed Sqlite file)', + 'Close a task' => 'ปิดงาน', + 'Column' => 'คอลัมน์', + 'Color' => 'สี', + 'Assignee' => 'à¸à¸³à¸«à¸™à¸”ให้', + 'Create another task' => 'สร้างงานอื่น', + 'New task' => 'งานใหม่', + 'Open a task' => 'เปิดงาน', + 'Do you really want to open this task: "%s"?' => 'คุณต้องà¸à¸²à¸£à¹€à¸›à¸´à¸”งาน: « %s » ใช่หรือไม่?', + 'Back to the board' => 'à¸à¸¥à¸±à¸šà¹„ปที่บอร์ด', + 'There is nobody assigned' => 'ไม่มีใครถูà¸à¸à¸³à¸«à¸™à¸”', + 'Column on the board:' => 'คอลัมน์บนบอร์ด:', + 'Close this 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 ตัวอัà¸à¸©à¸£', + 'The password is required' => 'ต้องà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™', + 'This value must be an integer' => 'ต้องเป็นตัวเลข', + 'The username must be unique' => 'ชื่อผู้ใช้ต้องไม่ซ้ำ', + 'The user id is required' => 'ต้องà¸à¸²à¸£à¹„อดีผู้ใช้', + 'Passwords don\'t match' => 'รหัสผ่านไม่ถูà¸à¸•้อง', + 'The confirmation is required' => 'ต้องà¸à¸²à¸£à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™', + 'The project is required' => 'ต้องà¸à¸²à¸£à¹‚ปรเจค', + 'The id is required' => 'ต้องà¸à¸²à¸£à¹„อดี', + 'The project id is required' => 'ต้องà¸à¸²à¸£à¹„อดีโปรเจค', + 'The project name is required' => 'ต้องà¸à¸²à¸£à¸Šà¸·à¹ˆà¸­à¹‚ปรเจค', + 'The title is required' => 'ต้องà¸à¸²à¸£à¸«à¸±à¸§à¹€à¸£à¸·à¹ˆà¸­à¸‡', + 'Settings saved successfully.' => 'บันทึà¸à¸à¸²à¸£à¸•ั้งค่าเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to save your settings.' => 'ไม่สามารถบันทึà¸à¸à¸²à¸£à¸•ั้งค่าได้', + 'Database optimization done.' => 'ปรับปรุงà¸à¸²à¸™à¸‚้อมูลเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Your project has been created successfully.' => 'สร้างโปรเจคเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to create your project.' => 'ไม่สามารถสร้างโปรเจคได้', + 'Project updated successfully.' => 'ปรับปรุงโปรเจคเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to update this project.' => 'ไม่สามารถปรับปรุงโปรเจคได้', + 'Unable to remove this project.' => 'ไม่สามารถลบโปรเจคได้', + 'Project removed successfully.' => 'ลบโปรเจคเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Project activated successfully.' => 'เปิดใช้งานโปรเจคเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to activate this project.' => 'ไม่สามารถเปิดใช้งานโปรเจคได้', + 'Project disabled successfully.' => 'ปิดโปรเจคเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to disable this project.' => 'ไม่สามารถปิดโปรเจคได้', + 'Unable to open this task.' => 'ไม่สามารถเปิดงานนี้', + 'Task opened successfully.' => 'เปิดงานเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to close this task.' => 'ไม่สามารถปิดงานนี้', + 'Task closed successfully.' => 'ปิดงานเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to update your task.' => 'ไม่สามารถปรับปรุงงานได้', + 'Task updated successfully.' => 'ปรับปรุงงานเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to create your task.' => 'ไม่สามารถสร้างงานได้', + 'Task created successfully.' => 'สร้างงานเรียบร้อยà¹à¸¥à¹‰à¸§', + 'User created successfully.' => 'สร้างผู้ใช้เรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to create your user.' => 'ไม่สามารถสร้างผู้ใช้ได้', + 'User updated successfully.' => 'ปรับปรุงผู้ใช้เรียบร้อยà¹à¸¥à¹‰à¸§', + 'User removed successfully.' => 'ลบผู้ใช้เรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to remove this user.' => 'ไม่สามารถลบผู้ใช้ได้', + 'Board updated successfully.' => 'ปรับปรุงบอร์ดเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Ready' => 'พร้อม', + 'Backlog' => 'งานค้าง', + 'Work in progress' => 'à¸à¸³à¸¥à¸±à¸‡à¸—ำ', + 'Done' => 'เสร็จ', + 'Application version:' => 'à¹à¸­à¸žà¹€à¸§à¸­à¸£à¹Œà¸Šà¸±à¸™:', + 'Id' => 'ไอดี', + 'Public link' => 'ลิงค์สาธารณะ', + 'Timezone' => 'เขตเวลา', + 'Sorry, I didn\'t find this information in my database!' => 'เสียใจด้วย ไม่สามารถหาข้อมูลในà¸à¸²à¸™à¸‚้อมูลได้', + 'Page not found' => 'ไม่พบหน้า', + 'Complexity' => 'ความซับซ้อน', + 'Task limit' => 'จำà¸à¸±à¸”งาน', + 'Task count' => 'นับงาน', + 'User' => 'ผู้ใช้', + 'Comments' => 'ความคิดเห็น', + 'Comment is required' => 'ต้องà¸à¸²à¸£à¸„วามคิดเห็น', + 'Comment added successfully.' => 'เพิ่มความคิดเห็นเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to create your comment.' => 'ไม่สามารถสร้างความคิดเห็น', + 'Due Date' => 'วันที่ครบà¸à¸³à¸«à¸™à¸”', + 'Invalid date' => 'วันที่ผิด', + 'Automatic actions' => 'à¸à¸²à¸£à¸à¸£à¸°à¸—ำอัตโนมัติ', + 'Your automatic action has been created successfully.' => 'à¸à¸²à¸£à¸à¸£à¸°à¸—ำอัตโนมัติสร้างเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to create your automatic action.' => 'ไม่สามารถสร้างà¸à¸²à¸£à¸à¸£à¸°à¸—ำอัตโนมัติได้', + 'Remove an action' => 'ลบà¸à¸²à¸£à¸à¸£à¸°à¸—ำ', + 'Unable to remove this action.' => 'ไม่สามารถลบà¸à¸²à¸£à¸à¸£à¸°à¸—ำ', + 'Action removed successfully.' => 'ลบà¸à¸²à¸£à¸à¸£à¸°à¸—ำเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Automatic actions for the project "%s"' => 'à¸à¸²à¸£à¸à¸£à¸°à¸—ำอัตโนมัติสำหรับโปรเจค « %s »', + 'Add an action' => 'เพิ่มà¸à¸²à¸£à¸à¸£à¸°à¸—ำ', + 'Event name' => 'ชื่อเหตุà¸à¸²à¸“์', + 'Action' => 'à¸à¸²à¸£à¸à¸£à¸°à¸—ำ', + 'Event' => 'เหตุà¸à¸²à¸£à¸“์', + 'When the selected event occurs execute the corresponding action.' => 'เหตุà¸à¸²à¸£à¹Œà¸—ี่เลือà¸à¸ˆà¸°à¹€à¸à¸´à¸”ขึ้นเมื่อมีà¸à¸²à¸£à¸à¸£à¸°à¸—ำที่สอดคล้องà¸à¸±à¸™', + 'Next step' => 'ขั้นตอนต่อไป', + '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 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' => 'à¸à¸³à¸«à¸™à¸”สีให้ผู้ใช้à¹à¸šà¸šà¹€à¸ˆà¸²à¸°à¸ˆà¸‡', + 'Position' => 'ตำà¹à¸«à¸™à¹ˆà¸‡', + 'Duplicate to project' => 'ทำซ้ำในโปรเจคอื่น', + 'Duplicate' => 'ทำซ้ำ', + 'Link' => 'ลิงค์', + 'Comment updated successfully.' => 'ปรับปรุงความคิดเห็นเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to update your comment.' => 'ไม่สามารถปรับปรุงความคิดเห็นได้', + 'Remove a comment' => 'ลบความคิดเห็น', + 'Comment removed successfully.' => 'ลบความคิดเห็นเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to remove this comment.' => 'ไม่สามารถลบความคิดเห็นได้', + 'Do you really want to remove this comment?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¸„วามคิดเห็น', + 'Current password for the user "%s"' => 'รหัสผ่านปัจจุบันของผู้ใช้ « %s »', + 'The current password is required' => 'ต้องà¸à¸²à¸£à¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¸›à¸±à¸ˆà¸ˆà¸¸à¸šà¸±à¸™', + 'Wrong password' => 'รหัสผ่านผิด', + 'Unknown' => 'ไม่ทราบ', + 'Last logins' => 'เข้าใช้ล่าสุด', + 'Login date' => 'วันที่เข้าใข้', + 'Authentication method' => 'วิธีà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸•ัวตน', + 'IP address' => 'ที่อยู่ไอพี', + 'User agent' => 'User agent', + 'Persistent connections' => 'Persistent connections', + 'No session.' => 'No session.', + 'Expiration date' => 'หมดอายุวันที่', + 'Remember Me' => 'จดจำฉัน', + 'Creation date' => 'สร้างวันที่', + 'Everybody' => 'ทุà¸à¸„น', + 'Open' => 'เปิด', + 'Closed' => 'ปิด', + 'Search' => 'ค้นหา', + 'Nothing found.' => 'ค้นหาไม่พบ.', + 'Due date' => 'วันที่ครบà¸à¸³à¸«à¸™à¸”', + 'Description' => 'คำอธิบาย', + '%d comments' => '%d ความคิดเห็น', + '%d comment' => '%d ความคิดเห็น', + '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.' => 'ทำà¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¹‚ยงบัà¸à¸Šà¸µà¸ à¸²à¸¢à¸™à¸­à¸à¸‚องคุณà¸à¸±à¸šà¹‚ปรไฟล์ของคุณเรียบร้อย', + 'Email' => 'อีเมล', + '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' => 'à¸à¸³à¸«à¸™à¸”หมวดอัตโนมัติขึ้นอยู่à¸à¸±à¸šà¸ªà¸µ', + 'Task creation or modification' => 'สร้างหรือà¹à¸à¹‰à¹„ขงาน', + 'Category' => 'หมวดหมู่', + 'Category:' => 'หมวดหมู่:', + 'Categories' => 'หมวดหมู่', + 'Your category has been created successfully.' => 'สร้างหมวดหมู่เรียบร้อยà¹à¸¥à¹‰à¸§', + 'This category has been updated successfully.' => 'ปรับปรุงหมวดเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to update this 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' => 'ไม่มีหมวดหมู่', + 'The name is required' => 'ต้องà¸à¸²à¸£à¸Šà¸·à¹ˆà¸­', + 'Remove a file' => 'ลบไฟล์', + 'Unable to remove this file.' => 'ไม่สามารถลบไฟล์ได้', + 'File removed successfully.' => 'ลบไฟล์เรียบร้อยà¹à¸¥à¹‰à¸§', + 'Attach a document' => 'à¹à¸™à¸šà¹€à¸­à¸à¸ªà¸²à¸£', + 'Do you really want to remove this file: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¹„ฟล์ "%s" ใช่หรือไม่?', + 'Attachments' => 'à¹à¸™à¸š', + 'Edit the task' => 'à¹à¸à¹‰à¹„ขงาน', + 'Add a comment' => 'เพิ่มความคิดเห็น', + 'Edit a comment' => 'à¹à¸à¹‰à¹„ขความคิดเห็น', + 'Summary' => 'สรุป', + 'Time tracking' => 'à¸à¸²à¸£à¸•ิดตามเวลา', + 'Estimate:' => 'ประมาณ:', + 'Spent:' => 'ใช้:', + 'Do you really want to remove this sub-task?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¸‡à¸²à¸™à¸¢à¹ˆà¸­à¸¢à¹ƒà¸Šà¹ˆà¸«à¸£à¸·à¸­à¹„ม่?', + 'Remaining:' => 'เหลือ:', + 'hours' => 'ชั่วโมง', + 'estimated' => 'ประมาณ', + 'Sub-Tasks' => 'งานย่อย', + 'Add a sub-task' => 'เพิ่มงานย่อย', + 'Original estimate' => 'ประมาณà¸à¸²à¸£à¹€à¸”ิม', + 'Create another sub-task' => 'สร้างงานย่อยอื่น', + 'Time spent' => 'ใช้เวลา', + 'Edit a sub-task' => 'à¹à¸à¹‰à¹„ขงานย่อย', + 'Remove a sub-task' => 'ลบงานย่อย', + 'The time must be a numeric value' => 'เวลาที่ต้องเป็นตัวเลข', + 'Todo' => 'สิ่งที่ต้องทำ', + 'In progress' => 'à¸à¸³à¸¥à¸±à¸‡à¸”ำเนินà¸à¸²à¸£', + 'Sub-task removed successfully.' => 'ลบงานย่อยเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to remove this sub-task.' => 'ไม่สามารถลบงานย่อยได้', + 'Sub-task updated successfully.' => 'ปรับปรุงงานย่อย่่เรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to update your sub-task.' => 'ไม่สามารถปรับปรุงานย่อยได้', + 'Unable to create your sub-task.' => 'ไม่สามารถสร้างงานย่อยได้', + 'Maximum size: ' => 'ขนาดไฟล์สูงสุด:', + 'Display another project' => 'à¹à¸ªà¸”งโปรเจคอื่น', + 'Created by %s' => 'สร้างโดย %s', + 'Tasks Export' => 'ส่งออà¸à¸‡à¸²à¸™', + 'Start Date' => 'เริ่มวันที่', + 'Execute' => 'ประมวลผล', + 'Task Id' => 'งาน ไอดี', + 'Creator' => 'ผู้สร้าง', + 'Modification date' => 'วันที่à¹à¸à¹‰à¹„ข', + 'Completion date' => 'วันที่เสร็จสิ้น', + 'Clone' => 'เลียนà¹à¸šà¸š', + 'Project cloned successfully.' => 'เลียนà¹à¸šà¸šà¹‚ปรเจคเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to clone this project.' => 'ไม่สามารถเลียบà¹à¸šà¸šà¹‚ปรเจคได้', + 'Enable email notifications' => 'เปิดอีเมลà¹à¸ˆà¹‰à¸‡à¹€à¸•ือน', + 'Task position:' => 'ตำà¹à¸«à¸™à¹ˆà¸‡à¸‡à¸²à¸™', + 'The task #%d has been opened.' => 'งานที่ #%d ถุà¸à¹€à¸›à¸´à¸”', + 'The task #%d has been closed.' => 'งานที่ #%d ถูà¸à¸›à¸´à¸”', + 'Sub-task updated' => 'ปรับปรุงงานย่อย', + 'Title:' => 'หัวเรื่อง:', + 'Status:' => 'สถานะ:', + 'Assignee:' => 'à¸à¸³à¸«à¸™à¸”ให้:', + 'Time tracking:' => 'à¸à¸²à¸£à¸•ิดตามเวลา:', + 'New sub-task' => 'งานย่อยใหม่', + 'New attachment added "%s"' => 'เพิ่มà¸à¸²à¸£à¹à¸™à¸šà¹ƒà¸«à¸¡à¹ˆ "%s"', + 'New comment posted by %s' => 'ความคิดเห็นใหม่จาภ%s', + 'New comment' => 'ความคิดเห็นใหม่', + 'Comment updated' => 'ปรับปรุงความคิดเห็น', + 'New subtask' => 'งานย่อยใหม่', + 'I only want to receive notifications for these projects:' => 'ฉันต้องà¸à¸²à¸£à¸£à¸±à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนสำหรับโปรเจค:', + 'view the task on Kanboard' => 'à¹à¸ªà¸”งงานบน Kanboard', + 'Public access' => 'à¸à¸²à¸£à¹€à¸‚้าถึงสาธารณะ', + 'Disable public access' => 'ปิดà¸à¸²à¸£à¹€à¸‚้าถึงสาธารณะ', + 'Enable public access' => 'เปิดà¸à¸²à¸£à¹€à¸‚้าถึงสาธารณะ', + 'Public access disabled' => 'à¸à¸²à¸£à¹€à¸‚้าถึงสาธารณะถูà¸à¸›à¸´à¸”', + 'Move the task to another project' => 'ย้ายงานไปโปรเจคอื่น', + 'Move to project' => 'ย้ายไปโปรเจคอื่น', + 'Do you really want to duplicate this task?' => 'คุณต้องà¸à¸²à¸£à¸—ำซ้ำงานนี้ใช่หรือไม่?', + 'Duplicate a task' => 'ทำซ้ำงาน', + 'External accounts' => 'บัà¸à¸Šà¸µà¸ à¸²à¸¢à¸™à¸­à¸', + 'Account type' => 'ประเภทบัà¸à¸Šà¸µ', + 'Local' => 'ท้องถิ่น', + 'Remote' => 'รีโมท', + 'Enabled' => 'เปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰', + 'Disabled' => 'ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰', + 'Login:' => 'ชื่อผู้ใช้:', + 'Full Name:' => 'ชื่อ:', + 'Email:' => 'อีเมล:', + 'Notifications:' => 'à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน:', + 'Notifications' => 'à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน', + 'Account type:' => 'ประเภทบัà¸à¸Šà¸µ:', + 'Edit profile' => 'à¹à¸à¹‰à¹„ขประวัติ', + 'Change password' => 'เปลี่ยนรหัสผ่าน', + 'Password modification' => 'à¹à¸à¹‰à¹„ขรหัสผ่าน', + 'External authentications' => 'à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸ à¸²à¸¢à¸™à¸­à¸', + 'Never connected.' => 'ไม่เชื่อมต่อ', + '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', + '%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 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 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' => 'สร้างงานจาà¸à¸šà¸£à¸´à¸à¸²à¸£à¸ à¸²à¸¢à¸™à¸­à¸', + 'Change the assignee based on an external username' => 'เปลี่ยนผู้รับผิดชอบขึ้นอยู่à¸à¸±à¸šà¸Šà¸·à¹ˆà¸­à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¸ à¸²à¸¢à¸™à¸­à¸', + 'Change the category based on an external label' => 'เปลี่ยนหมวดขึ้นอยู่à¸à¸±à¸šà¸›à¹‰à¸²à¸¢à¸Šà¸·à¹ˆà¸­à¸ à¸²à¸¢à¸™à¸­à¸', + 'Reference' => 'อ้างถึง', + 'Label' => 'ป้ายชื่อ', + 'Database' => 'à¸à¸²à¸™à¸‚้อมูล', + 'About' => 'เà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸š', + 'Database driver:' => 'เครื่องมือà¸à¸²à¸™à¸‚ข้อมูล', + 'Board settings' => 'ตั้งค่าบอร์ด', + 'Webhook settings' => 'à¸à¸²à¸£à¸•ั้งค่า Webhook', + 'Reset token' => 'รีเซ็ตโทเค็น', + 'API endpoint:' => 'ปลายทาง API:', + 'Refresh interval for personal 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 วินาที) ', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'ความถี่ (0 ไม่ใช้คุณลัà¸à¸©à¸“ะนี้, ค่าเริ่มต้นทุภ10 วินาที)', + 'Application URL' => 'URL à¹à¸­à¸›à¸žà¸¥à¸´à¹€à¸„ชัน', + 'Token regenerated.' => 'สร้างโทเค็นใหม่à¹à¸¥à¹‰à¸§', + 'Date format' => 'รูปà¹à¸šà¸šà¸§à¸±à¸™à¸—ี่', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ยอมรับรูปà¹à¸šà¸š ISO ตัวอย่าง: "%s" à¹à¸¥à¸° "%s"', + 'New personal project' => 'เพิ่มโปรเจคส่วนตัวใหม่', + 'This project is personal' => 'โปรเจคนี้เป็นโปรเจคส่วนตัว', + 'Add' => 'เพิ่ม', + 'Start date' => 'เริ่มวันที่', + 'Time estimated' => 'เวลาโดยประมาณ', + 'There is nothing assigned to you.' => 'ไม่มีอะไรà¸à¸³à¸«à¸™à¸”ให้คุณ', + 'My tasks' => 'งานของฉัน', + 'Activity stream' => 'à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่เà¸à¸´à¸”ขึ้น', + 'Dashboard' => 'à¹à¸”ชบอร์ด', + 'Confirmation' => 'ยืนยันรหัสผ่าน', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'สร้างความคิดเห็นจาà¸à¸šà¸£à¸´à¸à¸²à¸£à¸ à¸²à¸¢à¸™à¸­à¸', + 'Project management' => 'à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¹‚ปรเจค', + 'Columns' => 'คอลัมน์', + 'Task' => 'งาน', + 'Percentage' => 'เปอร์เซ็นต์', + 'Number of tasks' => 'จำนวนงาน', + 'Task distribution' => 'à¸à¸²à¸£à¸à¸£à¸°à¸ˆà¸²à¸¢à¸‡à¸²à¸™', + 'Analytics' => 'à¸à¸²à¸£à¸§à¸´à¹€à¸„ราะห์', + 'Subtask' => 'งานย่อย', + 'User repartition' => 'à¸à¸²à¸£à¹à¸šà¹ˆà¸‡à¸‡à¸²à¸™à¸‚องผู้ใช้', + 'Clone this project' => 'สำเนาโปรเจคนี้', + 'Column removed successfully.' => 'ลบคอลัมน์สำเร็จ', + '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 subtask id is required' => 'ต้องà¸à¸²à¸£à¸‡à¸²à¸™à¸¢à¹ˆà¸­à¸¢', + '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' => 'ไอดีผู้ใช้ต้องเป็นตัวเลขเท่านั้น', + 'This value is required' => 'ต้องà¸à¸²à¸£à¸„่านี้', + 'This value must be numeric' => 'ค่านี้ต้องเป็นตัวเลข', + 'Unable to create this task.' => 'ไม่สามารถสร้างงานนี้', + 'Cumulative flow diagram' => 'à¹à¸œà¸™à¸ à¸²à¸žà¸‡à¸²à¸™à¸ªà¸°à¸ªà¸¡', + 'Daily project summary' => 'สรุปโปรเจครายวัน', + 'Daily project summary export' => 'ส่งออà¸à¸ªà¸£à¸¸à¸›à¹‚ปรเจครายวัน', + 'Exports' => 'ส่งออà¸', + 'This export contains the number of tasks per column grouped per day.' => 'à¸à¸²à¸£à¸ªà¹ˆà¸‡à¸­à¸­à¸à¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸à¸²à¸£à¸™à¸±à¸šà¸ˆà¸³à¸™à¸§à¸™à¸‡à¸²à¸™à¹ƒà¸™à¹à¸•่ละคอลัมน์ในà¹à¸•่ละวัน', + 'Active swimlanes' => 'สวิมเลนพร้อมใช้งาน', + 'Add a new swimlane' => 'เพิ่มสวิมเลนใหม่', + 'Default swimlane' => 'สวิมเลนเริ่มต้น', + 'Do you really want to remove this swimlane: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¸ªà¸§à¸´à¸¡à¹€à¸¥à¸™à¸™à¸µà¹‰ : "%s"?', + 'Inactive swimlanes' => 'สวิมเลนไม่ทำงาน', + 'Remove a swimlane' => 'ลบสวิมเลน', + 'Swimlane modification for the project "%s"' => 'à¹à¸à¹‰à¹„ขสวิมเลนสำหรับโปรเจค "%s"', + 'Swimlane removed successfully.' => 'ลบสวิมเลนเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Swimlanes' => 'สวิมเลน', + 'Swimlane updated successfully.' => 'ปรับปรุงสวิมเลนเรียบร้อยà¹à¸¥à¹‰à¸§', + 'Unable to remove this swimlane.' => 'ไม่สามารถลบสวิมเลนนี้', + 'Unable to update this swimlane.' => 'ไม่สามารถปรับปรุงสวิมเลนนี้', + 'Your swimlane has been created successfully.' => 'สวิมเลนของคุณถูà¸à¸ªà¸£à¹‰à¸²à¸‡à¹€à¸£à¸µà¸¢à¸šà¸£à¹‰à¸­à¸¢à¹à¸¥à¹‰à¸§', + 'Example: "Bug, Feature Request, Improvement"' => 'ตัวอย่าง: "Bug, Feature Request, Improvement"', + 'Default categories for new projects (Comma-separated)' => 'ค่าเริ่มต้นหมวดสำหรับโปรเจคใหม่ (Comma-separated)', + 'Integrations' => 'à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸£à¹ˆà¸§à¸¡à¸à¸±à¸™', + 'Integration with third-party services' => 'à¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™à¸£à¹ˆà¸§à¸¡à¸à¸±à¸šà¸šà¸£à¸´à¸à¸²à¸£ third-party', + 'Subtask Id' => 'ไอดีของงานย่อย', + 'Subtasks' => 'งานย่อย', + 'Subtasks Export' => 'ส่งออภงานย่อย', + 'Task Title' => 'ชื่องาน', + 'Untitled' => 'ไม่มีชื่อ', + 'Application default' => 'à¹à¸­à¸žà¸žà¸¥à¸´à¹€à¸„ชันเริ่มต้น', + 'Language:' => 'ภาษา:', + 'Timezone:' => 'เขตเวลา:', + 'All columns' => 'คอลัมน์ทั้งหมด', + 'Next' => 'ต่อไป', + '#%d' => '#%d', + 'All swimlanes' => 'สวิมเลนทั้งหมด', + 'All colors' => 'สีทั้งหมด', + 'Moved to column %s' => 'เคลื่อนไปคอลัมน์ %s', + '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' => 'ติดตามเวลา', + 'You already have one subtask in progress' => 'คุณมีหนึ่งงานย่อยที่à¸à¸³à¸¥à¸±à¸‡à¸—ำงาน', + 'Which parts of the project do you want to duplicate?' => 'เลือà¸à¸ªà¹ˆà¸§à¸™à¸‚องโปรเจคที่คุณต้องà¸à¸²à¸£à¸—ำซ้ำ?', + 'Disallow login form' => 'ไม่อนุà¸à¸²à¸•ให้ใช้à¹à¸šà¸šà¸Ÿà¸­à¸£à¹Œà¸¡à¸à¸²à¸£à¹€à¸‚้าสู่ระบบ', + 'Start' => 'เริ่ม', + 'End' => 'จบ', + 'Task age in days' => 'อายุงาน', + 'Days in this column' => 'วันในคอลัมน์นี้', + '%dd' => '%d วัน', + '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' => 'จำเป็นต้องใส่', + 'Link added successfully.' => 'เพิ่มลิงค์เรียบร้อยà¹à¸¥à¹‰à¸§', + 'Link updated successfully.' => 'ปรับปรุงลิงค์เรียบร้อยà¹à¸¥à¹‰à¸§', + 'Link removed successfully.' => 'ลบลิงค์เรียบร้อยà¹à¸¥à¹‰à¸§', + 'Link labels' => 'ป้ายลิงค์', + 'Link modification' => 'à¹à¸à¹‰à¹„ขลิงค์', + 'Opposite label' => 'ป้ายชื่อตรงข้าม', + 'Remove a link' => 'ลบลิงค์', + '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.' => 'ไม่สามารถลบลิงค์นี้', + 'relates to' => 'เà¸à¸µà¹ˆà¸¢à¸§à¸‚้องà¸à¸±à¸š', + 'blocks' => 'ห้าม', + 'is blocked by' => 'ถูà¸à¸«à¹‰à¸²à¸¡à¸”้วย', + 'duplicates' => 'ซ้ำà¸à¸±à¸™', + 'is duplicated by' => 'ถูà¸à¸—ำซ้ำโดย', + 'is a child of' => 'เป็นลูà¸à¸‚อง', + 'is a parent of' => 'เป็นพ่อà¹à¸¡à¹ˆà¸‚อง', + 'targets milestone' => 'เป้าหมาย', + 'is a milestone of' => 'เป็นเป้าหมายของ', + 'fixes' => 'เจาะจง', + 'is fixed by' => 'ถูà¸à¹€à¸ˆà¸²à¸°à¸ˆà¸‡à¸”้วย', + 'This task' => 'งานนี้', + '<1h' => '<1 ชม.', + '%dh' => '%d ชม.', + 'Expand tasks' => 'ขยายงาน', + 'Collapse tasks' => 'ย่องาน', + 'Expand/collapse tasks' => 'ขยาย/ย่อ งาน', + 'Close dialog box' => 'ปิดà¸à¸¥à¹ˆà¸­à¸‡à¸‚้อความ', + 'Submit a form' => 'ยอมรับฟอร์ม', + 'Board view' => 'มุมมองบอร์ด', + 'Keyboard shortcuts' => 'คีย์ลัด', + 'Open board switcher' => 'เปิดà¸à¸²à¸£à¸ªà¸¥à¸±à¸šà¸šà¸­à¸£à¹Œà¸”', + 'Application' => 'à¹à¸­à¸žà¸žà¸¥à¸´à¹€à¸„ชัน', + 'Compact view' => 'มุมมองพอดี', + 'Horizontal scrolling' => 'เลื่อนตามà¹à¸™à¸§à¸™à¸­à¸™', + 'Compact/wide view' => 'พอดี/à¸à¸§à¹‰à¸²à¸‡ มุมมอง', + 'Currency' => 'สà¸à¸¸à¸¥à¹€à¸‡à¸´à¸™', + 'Personal project' => 'โปรเจคส่วนตัว', + 'AUD - Australian Dollar' => 'AUD - ดอลลาร์ออสเตรเลีย', + 'CAD - Canadian Dollar' => 'CAD - ดอลลาร์à¹à¸„นาดา', + 'CHF - Swiss Francs' => 'CHF - ฟรังà¸à¹Œà¸ªà¸§à¸´à¸ª', + 'Custom Stylesheet' => 'สไตล์ที่à¸à¸³à¸«à¸™à¸”เอง', + 'EUR - Euro' => 'EUR - ยูโร', + 'GBP - British Pound' => 'GBP - ปอนด์อังà¸à¸¤à¸©', + 'INR - Indian Rupee' => 'INR - รูปี', + 'JPY - Japanese Yen' => 'JPY - เยน', + 'NZD - New Zealand Dollar' => 'NZD - ดอลลาร์นิวซีà¹à¸¥à¸™à¸”์', + 'PEN - Peruvian Sol' => 'PEN - โซลเปรู', + 'RSD - Serbian dinar' => 'RSD - ดีนาร์เซอร์เบีย', + 'CNY - Chinese Yuan' => 'CNY - หยวนจีน', + 'USD - US Dollar' => 'USD - ดอลลาร์สหรัà¸', + 'VES - Venezuelan Bolívar' => 'VES - โบลีวาร์เวเนซุเอลา', + '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' => 'เวลาที่ใช้ในคอลัมน์', + '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.' => 'รายงานนี้มีà¸à¸²à¸£à¹€à¸„ลื่อนไหวคอลัมน์ทั้งหมดของงานà¹à¸•่ละงานมีวันที่ผู้ใช้à¹à¸¥à¸°à¹€à¸§à¸¥à¸²à¸—ี่ใช้สำหรับà¹à¸•่ละà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¹à¸›à¸¥à¸‡', + 'Currency rates' => 'อัตราค่าเงิน', + 'Rate' => 'อัตรา', + 'Change reference currency' => 'เปลี่ยนà¸à¸²à¸£à¸­à¹‰à¸²à¸‡à¸–ึงค่าเงิน', + 'Reference currency' => 'อ้างถึงค่าเงิน', + 'The currency rate has been added successfully.' => 'เพิ่มอัตราค่าเงินเรียบร้อย', + 'Unable to add this currency rate.' => 'ไม่สามารถเพิ่มค่าเงินนี้', + 'Webhook URL' => 'URL Webhook', + '%s removed the assignee of the task %s' => '%s เอาผู้รับผิดชอบออà¸à¸ˆà¸²à¸à¸‡à¸²à¸™ %s', + 'Information' => 'ข้อมูลสารสนเทศ', + 'Check two factor authentication code' => 'ตรวจสอบรหัสà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอน', + 'The two factor authentication code is not valid.' => 'รหัสà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอนไม่ถูà¸à¸•้อง', + 'The two factor authentication code is valid.' => 'รหัสà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอนถูà¸à¸•้อง', + 'Code' => 'รหัส', + 'Two factor authentication' => 'à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอน', + 'This QR code contains the key URI: ' => 'รหัส QR นี้มี URI คีย์:', + 'Check my code' => 'ตรวจสอบรหัสของฉัน', + 'Secret key: ' => 'à¸à¸¸à¸à¹à¸ˆà¸¥à¸±à¸š', + 'Test your device' => 'ทดสอบอุปà¸à¸£à¸“์ของคุณ', + 'Assign a color when the task is moved to a specific column' => 'à¸à¸³à¸«à¸™à¸”สีเมื่องานถูà¸à¸¢à¹‰à¸²à¸¢à¹„ปคอลัมน์ที่à¸à¸³à¸«à¸™à¸”ไว้', + '%s via Kanboard' => '%s ผ่าน Kanboard', + 'Burndown chart' => 'à¹à¸œà¸™à¸ à¸¹à¸¡à¸´à¸‡à¸²à¸™à¸à¸±à¸šà¹€à¸§à¸¥à¸²', + 'This chart show the task complexity over the time (Work Remaining).' => 'à¹à¸œà¸™à¸ à¸¹à¸¡à¸´à¹à¸ªà¸”งความซับซ้อนของงานตามเวลา (งานที่เหลือ)', + 'Screenshot taken %s' => 'จับภาพหน้าจอ %s', + 'Add a screenshot' => 'เพิ่ม screenshot', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'จับภาพหน้าจอ (screenshot) à¹à¸¥à¸°à¸à¸” CTRL+V หรือ ⌘+V เพื่อวางที่นี้', + 'Screenshot uploaded successfully.' => 'อัพโหลด screenshot เรียบร้อยà¹à¸¥à¹‰à¸§', + 'SEK - Swedish Krona' => 'SEK - โครนสวีเดน', + 'Identifier' => 'ตัวบ่งชี้', + 'Disable two factor authentication' => 'ปิดใช้งานà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอน', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸¢à¸à¹€à¸¥à¸´à¸ the two factor authentication สำหรับผู้ใช้นีั: "%s"?', + 'Edit link' => 'à¹à¸à¹‰à¹„ขลิงค์', + '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' => 'งานà¹à¸šà¸šà¸§à¸™à¸¥à¸¹à¸›à¸–ูà¸à¸ªà¸£à¹‰à¸²à¸‡à¸•ามที่à¸à¸³à¸«à¸™à¸”ไว้', + 'Score' => 'คะà¹à¸™à¸™', + 'The identifier must be unique' => 'ตัวบ่งชี้ต้องไม่ซ้ำ', + 'This linked task id doesn\'t exists' => 'รหัสงานที่เชื่อมโยงนี้ไม่มีอยู่', + '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' => 'ระยะเวลาà¸à¸²à¸£à¸„ำนวณวันครบà¸à¸³à¸«à¸™à¸”ใหม่', + '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: ' => 'ปัจจัยà¸à¸²à¸£à¸„ำนวณวันครบà¸à¸³à¸«à¸™à¸”ใหม่: ', + 'Month(s)' => 'เดือน', + 'This task has been created by: ' => 'งานนี้ถูà¸à¸ªà¸£à¹‰à¸²à¸‡à¹‚ดย', + 'Recurrent task has been generated:' => 'งานà¹à¸šà¸šà¸§à¸™à¸¥à¸¹à¸›à¸–ูà¸à¸ªà¸£à¹‰à¸²à¸‡', + 'Timeframe to calculate new due date: ' => 'ระยะเวลาà¸à¸²à¸£à¸„ำนวณวันครบà¸à¸³à¸«à¸™à¸”ใหม่: ', + 'Trigger to generate recurrent task: ' => 'จะสร้างงานà¹à¸šà¸šà¸§à¸™à¸¥à¸¹à¸›', + 'When task is closed' => 'เมื่อปิดงาน', + 'When task is moved from first column' => 'เมื่องานถูà¸à¸¢à¹‰à¸²à¸¢à¸ˆà¸²à¸à¸„อลัมน์à¹à¸£à¸', + 'When task is moved to last column' => 'เมื่องานถูà¸à¸¢à¹‰à¸²à¸¢à¹„ปคอลัมน์สุดท้าย', + 'Year(s)' => 'ปี', + 'Project settings' => 'ตั้งค่าโปรเจค', + 'Automatically update the start date' => 'ปรับปรุงวันที่เริ่มอัตโนมมัติ', + 'iCal feed' => 'ฟีด iCal', + 'Preferences' => 'à¸à¸²à¸£à¸•ั้งค่า', + 'Security' => 'ความปลอดภัย', + 'Two factor authentication disabled' => 'ปิดใช้งานà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอนà¹à¸¥à¹‰à¸§', + 'Two factor authentication enabled' => 'เปิดใช้งานà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอนà¹à¸¥à¹‰à¸§', + 'Unable to update this user.' => 'ไม่สามารถปรับปรุงผู้ใช้นี้', + 'There is no user management for personal 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' => 'ย้ายงานไปคอลัมน์อื่นเมื่อหมวดถูà¸à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™', + 'Send a task by email to someone' => 'ส่งงานโดยถึงบางคน', + 'Reopen a task' => 'เปิดงานอีà¸à¸„รั้ง', + 'Notification' => 'à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน', + '%s moved the task #%d to the first swimlane' => '%s ย้ายงาน #%d ไปสวินเลนà¹à¸£à¸', + 'Swimlane' => 'สวิมเลน', + '%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.' => 'รายงานนี้มีข้อมูลงานทั้งหมดสำหรับช่วงวันที่ที่à¸à¸³à¸«à¸™à¸”', + 'Project activities for %s' => 'à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¹‚ปรเจคสำหรับ %s', + 'view the board on Kanboard' => 'à¹à¸ªà¸”งบอร์ดบนคังบอร์ด', + 'The task has been moved to the first swimlane' => 'งานถูà¸à¸¢à¹‰à¸²à¸™à¹„ปสวิมเลนà¹à¸£à¸', + 'The task has been moved to another swimlane:' => 'งานถูà¸à¸¢à¹‰à¸²à¸™à¹„ปสวิมเลนอื่น:', + 'New title: %s' => 'ชื่อเรื่องใหม่: %s', + 'The task is not assigned anymore' => 'ไม่à¸à¸³à¸«à¸™à¸”ผู้รับผิดชอบ', + 'New assignee: %s' => 'ผู้รับผิดชอบใหม่: %s', + 'There is no category now' => 'ปัจจุบันไม่มีหมวด', + 'New category: %s' => 'หมวดใหม่: %s', + 'New color: %s' => 'สีใหม่: %s', + 'New complexity: %d' => 'ความซับซ้อนใหม่: %d', + 'The due date has been removed' => 'วันครบà¸à¸³à¸«à¸™à¸”ถูà¸à¸¥à¸š', + 'There is no description anymore' => 'ไม่มีคำอธิบาย', + 'Recurrence settings has been modified' => 'à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸•ั้งค่าà¸à¸²à¸£à¸§à¸™à¸¥à¸¹à¸›', + 'Time spent changed: %sh' => 'เวลาที่ใช้ในà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™: %s ชม.', + 'Time estimated changed: %sh' => 'เวลาโดยประมาณในà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™: %s ชม.', + 'The field "%s" has 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:' => 'ฉันต้องà¸à¸²à¸£à¸£à¸±à¸šà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนสำหรับ:', + 'All tasks' => 'ทุà¸à¸‡à¸²à¸™', + 'Only for tasks assigned to me' => 'เฉพาะงานที่ฉันรับผิดชอบ', + 'Only for tasks created by me' => 'เฉพาะงานที่ฉันสร้าง', + 'Only for tasks created by me and tasks assigned to me' => 'เฉพาะงานที่ฉันสร้างà¹à¸¥à¸°à¸‰à¸±à¸™à¸£à¸±à¸šà¸œà¸´à¸”ชอบ', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'จำนวนคอลัมน์ทั้งหมด', + 'You need at least 2 days of data to show the chart.' => 'คุณต้องà¸à¸²à¸£à¸­à¸¢à¹ˆà¸²à¸‡à¸™à¹‰à¸­à¸¢ 2 วันในà¸à¸²à¸£à¹à¸ªà¸”งà¹à¸œà¸™à¸ à¸¹à¸¡à¸´', + '<15m' => '<15นาที', + '<30m' => '<30นาที', + 'Stop timer' => 'หยุดจับเวลา', + 'Start timer' => 'เริ่มจับเวลา', + 'My activity stream' => 'à¸à¸´à¸ˆà¸à¸£à¸£à¸¡à¸—ี่เà¸à¸´à¸”ขึ้นของฉัน', + 'Search tasks' => 'ค้นหางาน', + 'Reset filters' => 'ล้างตัวà¸à¸£à¸­à¸‡', + 'My tasks due tomorrow' => 'งานถึงà¸à¸³à¸«à¸™à¸”ของฉันวันพรุ่งนี้', + 'Tasks due today' => 'งานถึงà¸à¸³à¸«à¸™à¸”วันนี้', + 'Tasks due tomorrow' => 'งานถึงà¸à¸³à¸«à¸™à¸”พรุ่งนี้', + 'Tasks due yesterday' => 'งานถึงà¸à¸³à¸«à¸™à¸”เมื่อวาน', + 'Closed tasks' => 'งานปิด', + 'Open tasks' => 'งานเปิด', + 'Not assigned' => 'ไม่à¸à¸³à¸«à¸™à¸”ใคร', + 'View advanced search syntax' => 'à¹à¸ªà¸”งรูปà¹à¸šà¸šà¸à¸²à¸£à¸„้นหาขั้นสูง', + 'Overview' => 'ภาพรวม', + 'Board/Calendar/List view' => 'มุมมอง บอร์ด/ปฎิทิน/ลิสต์', + 'Switch to the board view' => 'เปลี่ยนเป็นมุมมองบอร์ด', + 'Switch to the list view' => 'เปลี่ยนเป็นมุมมองลิสต์', + 'Go to the search/filter box' => 'ไปที่à¸à¸¥à¹ˆà¸­à¸‡à¸„้นหา/ตัวà¸à¸£à¸­à¸‡', + 'There is no activity yet.' => 'ตอนนี้ไม่มีà¸à¸´à¸ˆà¸à¸£à¸£à¸¡', + 'No tasks found.' => 'ไม่พบงาน', + 'Keyboard shortcut: "%s"' => 'คีย์ลัด: %s', + 'List' => 'ลิสต์', + 'Filter' => 'ตัวà¸à¸£à¸­à¸‡', + 'Advanced search' => 'ค้นหาขั้นสูง', + 'Example of query: ' => 'ตัวอย่างคิวรี: ', + 'Search by project: ' => 'ค้นหาตามโปรเจค: ', + 'Search by column: ' => 'ค้นหาตามคอลัมน์: ', + 'Search by assignee: ' => 'ค้นหาตามผู้รับผิดชอบ: ', + 'Search by color: ' => 'ค้นหาตามสี: ', + 'Search by category: ' => 'ค้นหาตามหมวด: ', + 'Search by description: ' => 'ค้นหาตามคำอธิบาย: ', + 'Search by due date: ' => 'ค้นหาตามวันครบà¸à¸³à¸«à¸™à¸”: ', + 'Average time spent in each column' => 'ค่าเฉลี่ยเวลาที่ใช้à¹à¸•่ละคอลัมน์', + 'Average time spent' => 'ค่าเฉลี่ยเวลาที่ใช้', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'à¹à¸œà¸™à¸ à¸¹à¸¡à¸´à¹à¸ªà¸”งค่าเฉลี่ยเวลาที่ใช้à¹à¸•่ละคอลัมน์สำหรับ %d งานล่าสุด', + 'Average Lead and Cycle time' => 'ค่าเฉลี่ยเวลานำà¹à¸¥à¸°à¸£à¸­à¸šà¹€à¸§à¸¥à¸²', + 'Average lead time: ' => 'ค่าเฉลี่ยเวลานำ: ', + 'Average cycle time: ' => 'ค่าเฉลี่ยรอบเวลา: ', + 'Cycle Time' => 'รอบเวลา', + 'Lead Time' => 'เวลานำ', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'à¹à¸œà¸™à¸ à¸¹à¸¡à¸´à¹à¸ªà¸”งค่าเฉลี่ยเวลานำà¹à¸¥à¸°à¸£à¸­à¸šà¹€à¸§à¸¥à¸²à¸ªà¸³à¸«à¸£à¸±à¸š %d งานล่าสุดเมื่อเวลาผ่านไป', + 'Average time into each column' => 'ค่าเฉลี่ยเวลาà¹à¸•่ละคอลัมน์', + 'Lead and cycle time' => 'เวลานำà¹à¸¥à¸°à¸£à¸­à¸šà¹€à¸§à¸¥à¸²', + 'Lead time: ' => 'เวลานำ: ', + 'Cycle time: ' => 'รอบเวลา: ', + 'Time spent in 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 the start date automatically' => 'ตั้งค่าวันที่เริ่มต้นอัตโนมัติ', + 'Edit Authentication' => 'à¹à¸à¹‰à¹„ขà¸à¸²à¸£à¸•รวจสอบ', + 'Remote user' => 'ผู้ใช้รีโมท', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'ผู้ใช้ระยะไà¸à¸¥à¸ˆà¸°à¹„ม่เà¸à¹‡à¸šà¸£à¸«à¸±à¸ªà¸œà¹ˆà¸²à¸™à¹„ว้ในà¸à¸²à¸™à¸‚้อมูล Kanboard ตัวอย่าง: บัà¸à¸Šà¸µ LDAP, Google à¹à¸¥à¸° Github', + '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.' => 'ไม่มีโครงà¸à¸²à¸£à¸›à¸¥à¸²à¸¢à¸—างที่ใช้งานได้', + 'Trigger automatically subtask time tracking' => 'เรียà¸à¹‚ดยอัตโนมัติà¸à¸²à¸£à¸•ิดตามเวลางานย่อย', + 'Include closed tasks in the cumulative flow diagram' => 'รวมงานที่ปิดà¹à¸¥à¹‰à¸§à¹ƒà¸™à¹à¸œà¸™à¸ à¸²à¸žà¸à¸£à¸°à¹à¸ªà¸ªà¸°à¸ªà¸¡', + 'Current swimlane: %s' => 'สวิมเลนปัจจุบัน: %s', + 'Current column: %s' => 'คอลัมน์ปัจจุบัน: %s', + 'Current category: %s' => 'หมวดปัจจุบัน: %s', + 'no category' => 'ไม่มีหมวด', + 'Current assignee: %s' => 'ผู้รับผิดชอบปัจจุบัน: %s', + 'not assigned' => 'ไม่à¸à¸³à¸«à¸™à¸”', + 'Author:' => 'ผู้à¹à¸•่ง:', + 'contributors' => 'ผู้ให้à¸à¸³à¹€à¸™à¸´à¸”', + 'License:' => 'สัà¸à¸à¸²à¸­à¸™à¸¸à¸à¸²à¸•:', + 'License' => 'สัà¸à¸à¸²à¸­à¸™à¸¸à¸à¸²à¸•', + 'Enter the text below' => 'พิมพ์ข้อความด้านล่าง', + 'Start date:' => 'วันที่เริ่ม:', + 'Due date:' => 'วันครบà¸à¸³à¸«à¸™à¸”:', + 'People who are project managers' => 'คนที่เป็นผู้จัดà¸à¸²à¸£à¹‚ปรเจค', + 'People who are project members' => 'คนที่เป็นสมาชิà¸à¹‚ปรเจค', + 'NOK - Norwegian Krone' => 'NOK - โครนนอร์เวย์', + 'Show this column' => 'à¹à¸ªà¸”งคอลัมนี้', + 'Hide this column' => 'ซ่อนคอลัมน์นี้', + 'End date' => 'วันจบ', + 'Users overview' => 'ภาพรวมผู้ใช้', + 'Members' => 'สมาชิà¸', + 'Shared project' => 'à¹à¸Šà¸£à¹Œà¹‚ปรเจค', + 'Project managers' => 'ผู้จัดà¸à¸²à¸£à¹‚ปรเจค', + 'Projects list' => 'รายà¸à¸²à¸£à¹‚ปรเจค', + 'End date:' => 'วันที่จบ:', + 'Change task color when using a specific task link' => 'เปลี่ยนสีงานเมื่อมีà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¹‚ยงงาน', + 'Task link creation or modification' => 'à¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸à¸²à¸£à¹€à¸Šà¸·à¹ˆà¸­à¸¡à¹‚ยงงานหรือà¸à¸²à¸£à¸›à¸£à¸±à¸šà¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™', + 'Milestone' => 'ขั้น', + 'Reset the search/filter box' => 'รีเซตà¸à¸¥à¹ˆà¸­à¸‡à¸„้นหา/ตัวà¸à¸£à¸­à¸‡', + 'Documentation' => 'เอà¸à¸ªà¸²à¸£', + 'Author' => 'ผู้à¹à¸•่ง', + 'Version' => 'เวอร์ชัน', + 'Plugins' => 'ปลั๊à¸à¸­à¸´à¸™', + 'There is no plugin loaded.' => 'ไม่มีปลั๊à¸à¸­à¸´à¸™à¸–ูà¸à¹‚หลดไว้', + 'My notifications' => 'à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนของฉัน', + 'Custom filters' => 'ตัวà¸à¸£à¸­à¸‡à¸à¸³à¸«à¸™à¸”เอง', + 'Your custom filter has 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 has been updated successfully.' => 'ตัวà¸à¸£à¸­à¸‡à¸à¸³à¸«à¸™à¸”เองของคุณà¹à¸à¹‰à¹„ขเรียบร้อย', + 'Unable to update custom filter.' => 'ไม่สามารถà¹à¸à¹‰à¹„ขตัวà¸à¸£à¸­à¸‡à¸à¸³à¸«à¸™à¸”เอง', + 'Web' => 'เวบ', + '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', + 'New task #%d: %s' => 'งานใหม่ #%d: %s', + 'Task updated #%d' => 'à¹à¸à¹‰à¹„ขงาน #%d', + 'Task #%d closed' => 'ปิดงาน #%d', + 'Task #%d opened' => 'เปิดงาน #%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', + '%d overdue tasks' => '%d งานเà¸à¸´à¸™à¸à¸³à¸«à¸™à¸”', + 'No notification.' => 'ไม่มีà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนใหม่', + '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:' => 'ลัà¸à¸©à¸“ะà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน:', + 'Unable to read your file' => 'ไม่สามารถอ่านไฟล์ของคุณ', + '%d task(s) have been imported successfully.' => '%d งานนำเข้าเรียบร้อย', + 'Nothing has been imported!' => 'ไม่มีอะไรถูà¸à¸™à¸³à¹€à¸‚้า', + 'Import users from CSV file' => 'นำเข้าผู้ใช้จาà¸à¹„ฟล์ CSV', + '%d user(s) have been imported successfully.' => '%d ผู้ใช้นำเข้าเรียบร้อย', + 'Comma' => ', - Comma', + 'Semi-colon' => '; - Semi-colon', + 'Tab' => 'Tab - Tab', + 'Vertical bar' => '| - Vertical bar', + 'Double Quote' => '" " - Double Quote', + 'Single 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)' => 'เพิ่มตัวà¸à¸£à¸­à¸‡ (à¹à¸—นที่à¸à¸²à¸£à¹à¸—นที่)', + 'Append/Replace' => 'เพิ่มเติม/à¹à¸—นที่', + 'Append' => 'เพิ่มเติม', + 'Replace' => 'à¹à¸—นที่', + 'Import' => 'นำเข้า', + 'Change sorting' => 'เปลี่ยนà¸à¸²à¸£à¹€à¸£à¸µà¸¢à¸‡', + 'Tasks Importation' => 'à¸à¸²à¸£à¸™à¸³à¹€à¸‚้างาน', + 'Delimiter' => 'คั่น', + 'Enclosure' => 'à¸à¸³à¸«à¸™à¸”ข้อความ', + 'CSV File' => 'ไฟล์ CSV', + 'Instructions' => 'คำสั่ง', + 'Your file must use the predefined CSV format' => 'ไฟล์ของคุณจะต้องใช้รูปà¹à¸šà¸š CSV ที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'Your file must be encoded in UTF-8' => 'ไฟล์ของคุณต้องเอนโค้ดด้วย 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' => 'วันที่ต้องอยู่ในรูปà¹à¸šà¸š ISO: YYYY-MM-DD', + 'Download CSV template' => 'ดาวน์โหลด CSV ต้นฉบับ', + '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' => '%s à¹à¸™à¸šà¹„ฟล์ใหม่ในงาน %s', + 'Link type' => 'ประเภทลิงค์', + 'Assign automatically a category based on a link' => 'à¸à¸³à¸«à¸™à¸”หมวดอัตโนมัติตามลิงค์', + 'BAM - Konvertible Mark' => 'BAM - มาร์คà¹à¸›à¸¥à¸‡à¸ªà¸ à¸²à¸ž', + 'Assignee Username' => 'à¸à¸³à¸«à¸™à¸”ชื่อผู้ใช้', + 'Assignee Name' => 'à¸à¸³à¸«à¸™à¸”ชื่อ', + 'Groups' => 'à¸à¸¥à¸¸à¹ˆà¸¡', + 'Members of %s' => 'สมาชิà¸à¸‚อง %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"' => 'เพิ่มสมาชิà¸à¹ƒà¸™à¸à¸¥à¸¸à¹ˆà¸¡ %s', + 'Group member added successfully.' => 'เพิ่มสมาชิà¸à¸à¸¥à¸¸à¹ˆà¸¡à¹€à¸£à¸µà¸¢à¸šà¸£à¹‰à¸­à¸¢', + 'Unable to add group member.' => 'ไม่สามารถเพิ่มสมาชิà¸à¸à¸¥à¸¸à¹ˆà¸¡', + 'Remove user from group "%s"' => 'เอาผู้ใช้ออà¸à¸ˆà¸²à¸à¸à¸¥à¸¸à¹ˆà¸¡ %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' => 'บัà¸à¸Šà¸µà¸‚องคุณถูà¸à¸¥à¹‡à¸­à¸ %d นาที', + 'Invalid captcha' => 'captcha ไม่ถูà¸à¸•้อง', + 'The name must be unique' => 'ชื่อต้องไม่ซ้ำ', + 'View all groups' => 'à¹à¸ªà¸”งà¸à¸¥à¸¸à¹ˆà¸¡à¸—ั้งหมด', + 'There is no user available.' => 'ไม่มีผู้ใช้ที่ใช้งานได้', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ "%s" ออà¸à¸ˆà¸²à¸à¸à¸¥à¸¸à¹ˆà¸¡ "%s"?', + 'There is no group.' => 'ไม่มีà¸à¸¥à¸¸à¹ˆà¸¡', + 'Add group member' => 'เพิ่มสมาชิà¸à¸à¸¥à¸¸à¹ˆà¸¡', + 'Do you really want to remove this group: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¸à¸¥à¸¸à¹ˆà¸¡à¸™à¸µà¹‰: "%s"?', + 'There is no user in this group.' => 'ไม่มีผู้ใช้ในà¸à¸¥à¸¸à¹ˆà¸¡à¸™à¸µà¹‰', + 'Permissions' => 'à¸à¸²à¸£à¸­à¸™à¸¸à¸à¸²à¸•ใช้งาน', + 'Allowed Users' => 'à¸à¸²à¸£à¸­à¸™à¸¸à¸à¸²à¸•ผู้ใช้', + 'No specific user has been allowed.' => 'ไม่มีผู้ใช้ได้รับอนุà¸à¸²à¸•เป็นพิเศษ', + 'Role' => 'บทบาท', + 'Enter user name...' => 'พิมพ์ชื่อผู้ใช้...', + 'Allowed Groups' => 'อนุà¸à¸²à¸•à¸à¸¥à¸¸à¹ˆà¸¡', + 'No group has been allowed.' => 'ไม่มีà¸à¸¥à¸¸à¹ˆà¸¡à¹„ด้รับอนุà¸à¸²à¸•เป็นพิเศษ', + 'Group' => 'à¸à¸¥à¸¸à¹ˆà¸¡', + 'Group Name' => 'ชื่อà¸à¸¥à¸¸à¹ˆà¸¡', + 'Enter group name...' => 'พิมพ์ชื่อà¸à¸¥à¸¸à¹ˆà¸¡...', + 'Role:' => 'บทบาท:', + 'Project members' => 'สมาชิà¸à¹‚ปรเจค', + '%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', + 'Estimated hours: ' => 'เวลาโดยประมาณ:', + 'Actual hours: ' => 'เวลาที่เà¸à¸´à¸”ขึ้นจริง:', + 'Hours Spent' => 'เวลาที่ใช้', + 'Hours Estimated' => 'เวลาโดยประมาณ', + 'Estimated Time' => 'เวลาโดยประมาณ', + 'Actual Time' => 'เวลาที่ใช้', + 'Estimated vs actual time' => 'เวลาโดยประมาณà¸à¸±à¸šà¹€à¸§à¸¥à¸²à¸ˆà¸£à¸´à¸‡', + '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' => 'อัลà¸à¸­à¸£à¸´à¸—ึมรหัสผ่านà¹à¸šà¸šà¹ƒà¸Šà¹‰à¸„รั้งเดียวตามเวลา', + 'Two-Factor Provider: ' => 'ผู้ให้บริà¸à¸²à¸£à¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอน:', + 'Disable two-factor authentication' => 'ปิดใช้งานà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอน', + 'Enable two-factor authentication' => 'เปิดใช้งานà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¹à¸šà¸šà¸ªà¸­à¸‡à¸‚ั้นตอน', + 'There is no integration registered at the moment.' => 'ขณะนี้ไม่มีà¸à¸²à¸£à¸¥à¸‡à¸—ะเบียนà¸à¸²à¸£à¸£à¸§à¸¡à¸£à¸°à¸šà¸š', + 'Password Reset for Kanboard' => 'รีเซตรหัสผ่านสำหรับคังบอร์ด', + 'Forgot password?' => 'ลืมรหัสผ่าน?', + 'Enable "Forget Password"' => 'เปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™ "ลืมรหัสผ่าน"', + 'Password Reset' => 'รีเซตรหัสผ่าน', + 'New password' => 'รหัสผ่านใหม่', + 'Change Password' => 'เปลี่ยนรหัสผ่าน', + 'To reset your password click on this link:' => 'ในà¸à¸²à¸£à¸£à¸µà¹€à¸‹à¸•รหัสผ่านของคุณคลิ๊à¸à¸—ี่ลิงค์นี้:', + 'Last Password Reset' => 'รีเซตรหัสผ่านครั้งล่าสุด', + 'The password has never been reinitialized.' => 'รหัสผ่านไม่เคยเริ่มใหม่อีà¸à¸„รั้ง', + 'Creation' => 'สร้าง', + 'Expiration' => 'สิ้นสุด', + 'Password reset history' => 'ประวัติà¸à¸²à¸£à¸£à¸µà¹€à¸‹à¸•รหัสผ่าน', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'ทุà¸à¸‡à¸²à¸™à¸‚องคอลัมน์ "%s" à¹à¸¥à¸°à¸ªà¸§à¸´à¸¡à¹€à¸¥à¸™ "%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.' => '%d งานในคอลัมน์ "%s" à¹à¸¥à¸°à¸ªà¸§à¸´à¸¡à¹€à¸¥à¸™ "%s" จะปิด', + 'Close all tasks in this column and this swimlane' => 'ปิดทุà¸à¸‡à¸²à¸™à¹ƒà¸™à¸„อลัมน์นี้', + '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.' => 'ตัวบ่งชี้โปรโจคเป็นตัวเลือà¸à¹€à¸ªà¸£à¸´à¸¡à¹à¸¥à¸°à¸•้องเป็นตัวอัà¸à¸©à¸£à¸«à¸£à¸·à¸­à¸•ัวเลข ตัวอย่าง: MYPROJECT', + 'Project owner' => 'เจ้าของโปรเจค', + 'Personal projects do not have users and groups management.' => 'โปรเจคส่วนตัวไม่มีà¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰à¹à¸¥à¸°à¸à¸¥à¸¸à¹ˆà¸¡', + 'There is no project member.' => 'ไม่มีสมาชิà¸à¹‚ปรเจค', + 'Priority' => 'ความสำคัà¸', + 'Task priority' => 'ความสำคัà¸à¸‚องงาน', + 'General' => 'ทั่วไป', + 'Dates' => 'วันที่', + 'Default priority' => 'ความสำคัà¸à¹€à¸£à¸´à¹ˆà¸¡à¸•้น', + 'Lowest priority' => 'ความสำคัà¸à¸•่ำสุด', + 'Highest priority' => 'ความสำคัà¸à¸ªà¸¹à¸‡à¸ªà¸¸à¸”', + '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' => 'à¹à¸™à¸š', + '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' => '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' => '%s ชั่วโมง', + 'Time spent:' => 'ใช้เวลา:', + 'Created:' => 'สร้าง:', + 'Modified:' => 'à¹à¸à¹‰à¹„ข:', + 'Completed:' => 'เสร็จสิ้น:', + 'Started:' => 'เริ่ม:', + 'Moved:' => 'ย้าย:', + 'Task #%d' => 'งานที่ #%d', + 'Time format' => 'รูปà¹à¸šà¸šà¸‚องเวลา', + 'Start date: ' => 'เริ่มวันที่:', + 'End date: ' => 'จบวันที่:', + 'New due date: ' => 'วันครบà¸à¸³à¸«à¸™à¸”ใหม่', + 'Start date changed: ' => 'เปลี่ยนวันที่เริ่ม', + 'Disable personal projects' => 'ปิดใช้งานโครงà¸à¸²à¸£à¸ªà¹ˆà¸§à¸™à¸•ัว', + 'Do you really want to remove this custom filter: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¸•ัวà¸à¸£à¸­à¸‡à¸—ี่à¸à¸³à¸«à¸™à¸”เองนี้จริง ๆ หรือไม่: "%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.' => 'อัปโหลดไฟล์ทั้งหมดสำเร็จà¹à¸¥à¹‰à¸§', + 'The maximum allowed file size is %sB.' => 'ขนาดไฟล์สูงสุดที่อนุà¸à¸²à¸•คือ %sB', + 'Drag and drop your files here' => 'ลาà¸à¹à¸¥à¸°à¸§à¸²à¸‡à¹„ฟล์ของคุณที่นี่', + 'choose files' => 'เลือà¸à¹„ฟล์', + 'View profile' => 'ดูโปรไฟล์', + 'Two Factor' => 'สองปัจจัย', + 'Disable user' => 'ปิดใช้งานผู้ใช้', + 'Do you really want to disable this user: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸›à¸´à¸”ใช้งานผู้ใช้นี้จริง ๆ หรือไม่: "%s"?', + 'Enable user' => 'เปิดใช้งานผู้ใช้', + 'Do you really want to enable this user: "%s"?' => 'คุณต้องà¸à¸²à¸£à¹€à¸›à¸´à¸”ใช้งานผู้ใช้นี้จริง ๆ หรือไม่: "%s"?', + 'Download' => 'ดาวน์โหลด', + 'Uploaded: %s' => 'อัปโหลด: %s', + 'Size: %s' => 'ขนาด: %s', + 'Uploaded by %s' => 'อัปโหลดโดย %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' => 'ค่านี้นต้องมาà¸à¸à¸§à¹ˆà¸² %d', + 'Another swimlane with the same name exists in the project' => 'มีเลนอื่นที่มีชื่อเดียวà¸à¸±à¸™à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™à¹‚ครงà¸à¸²à¸£', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'ตัวอย่าง: https://example.kanboard.org/ (ใช้สร้าง URL à¹à¸šà¸šà¹€à¸•็ม)', + '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:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'เวอร์ชัน OS:', + 'Database version:' => 'เวอร์ชันà¸à¸²à¸™à¸‚้อมูล:', + 'Browser:' => 'เบราว์เซอร์:', + 'Task view' => 'มุมมองงาน', + 'Edit task' => 'à¹à¸à¹‰à¹„ขงาน', + 'Edit description' => 'à¹à¸à¹‰à¹„ขคำอธิบาย', + 'New internal link' => 'ลิงà¸à¹Œà¸ à¸²à¸¢à¹ƒà¸™à¹ƒà¸«à¸¡à¹ˆ', + 'Display list of keyboard shortcuts' => 'à¹à¸ªà¸”งรายà¸à¸²à¸£à¹à¸›à¹‰à¸™à¸žà¸´à¸¡à¸žà¹Œà¸¥à¸±à¸”', + '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' => 'โทเค็น 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 a 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 has 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', + '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', + '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' => 'อีเมลขาออà¸', + '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 ฉบับà¹à¸¥à¹‰à¸§', + '%d invitation was sent.' => 'ส่งคำเชิภ%d ฉบับà¹à¸¥à¹‰à¸§', + 'Unable to create this user.' => 'ไม่สามารถสร้างผู้ใช้นี้ได้', + 'Kanboard Invitation' => 'คำเชิภKanboard', + '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.' => 'คุณได้รับเชิà¸à¹ƒà¸«à¹‰à¸¥à¸‡à¸—ะเบียนบน 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' => 'ชื่อผู้ใช้นี้ถูà¸à¹ƒà¸Šà¹‰à¹„ปà¹à¸¥à¹‰à¸§', + 'Your profile must have a valid email address.' => 'โปรไฟล์ของคุณต้องมีที่อยู่อีเมลที่ถูà¸à¸•้อง', + 'TRL - Turkish Lira' => 'TRL - ลีราตุรà¸à¸µ', + 'The project email is optional and could be used by several plugins.' => 'อีเมลโครงà¸à¸²à¸£à¹€à¸›à¹‡à¸™à¸•ัวเลือà¸à¹à¸¥à¸°à¸ªà¸²à¸¡à¸²à¸£à¸–ใช้โดยปลั๊à¸à¸­à¸´à¸™à¸«à¸¥à¸²à¸¢à¸•ัว', + 'The project email must be unique across all projects' => 'อีเมลโครงà¸à¸²à¸£à¸•้องไม่ซ้ำà¸à¸±à¸™à¹ƒà¸™à¸—ุà¸à¹‚ครงà¸à¸²à¸£', + 'The email configuration has been disabled by the administrator.' => 'ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸šà¹„ด้ปิดใช้งานà¸à¸²à¸£à¸à¸³à¸«à¸™à¸”ค่าอีเมลà¹à¸¥à¹‰à¸§', + 'Close this project' => 'ปิดโครงà¸à¸²à¸£à¸™à¸µà¹‰', + 'Open this project' => 'เปิดโครงà¸à¸²à¸£à¸™à¸µà¹‰', + 'Close a project' => 'ปิดโครงà¸à¸²à¸£', + 'Do you really want to close this project: "%s"?' => 'คุณต้องà¸à¸²à¸£à¸›à¸´à¸”โครงà¸à¸²à¸£à¸™à¸µà¹‰à¸ˆà¸£à¸´à¸‡ ๆ หรือไม่: "%s"?', + 'Reopen a project' => 'เปิดโครงà¸à¸²à¸£à¸­à¸µà¸à¸„รั้ง', + 'Do you really want to reopen this project: "%s"?' => 'คุณต้องà¸à¸²à¸£à¹€à¸›à¸´à¸”โครงà¸à¸²à¸£à¸™à¸µà¹‰à¸­à¸µà¸à¸„รั้งจริง ๆ หรือไม่: "%s"?', + 'This project is open' => 'โครงà¸à¸²à¸£à¸™à¸µà¹‰à¹€à¸›à¸´à¸”อยู่', + 'This project is closed' => 'โครงà¸à¸²à¸£à¸™à¸µà¹‰à¸›à¸´à¸”อยู่', + 'Unable to upload files, check the permissions of your data folder.' => 'ไม่สามารถอัปโหลดไฟล์ได้ ตรวจสอบสิทธิ์ของโฟลเดอร์ข้อมูลของคุณ', + 'Another category with the same name exists in this project' => 'มีหมวดหมู่อื่นที่มีชื่อเดียวà¸à¸±à¸™à¸­à¸¢à¸¹à¹ˆà¹ƒà¸™à¹‚ครงà¸à¸²à¸£à¸™à¸µà¹‰', + 'Comment sent by email successfully.' => 'ส่งความคิดเห็นทางอีเมลสำเร็จà¹à¸¥à¹‰à¸§', + 'Sent by email to "%s" (%s)' => 'ส่งทางอีเมลถึง "%s" (%s)', + 'Unable to read uploaded file.' => 'ไม่สามารถอ่านไฟล์ที่อัปโหลดได้', + 'Database uploaded successfully.' => 'อัปโหลดà¸à¸²à¸™à¸‚้อมูลสำเร็จà¹à¸¥à¹‰à¸§', + 'Task sent by email successfully.' => 'ส่งงานทางอีเมลสำเร็จà¹à¸¥à¹‰à¸§', + 'There is no category in this project.' => 'ไม่มีหมวดหมู่ในโครงà¸à¸²à¸£à¸™à¸µà¹‰', + 'Send by email' => 'ส่งทางอีเมล', + 'Create and send a comment by email' => 'สร้างà¹à¸¥à¸°à¸ªà¹ˆà¸‡à¸„วามคิดเห็นทางอีเมล', + 'Subject' => 'หัวข้อ', + 'Upload the database' => 'อัปโหลดà¸à¸²à¸™à¸‚้อมูล', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'คุณสามารถอัปโหลดà¸à¸²à¸™à¸‚้อมูล Sqlite ที่ดาวน์โหลดà¸à¹ˆà¸­à¸™à¸«à¸™à¹‰à¸²à¸™à¸µà¹‰ (รูปà¹à¸šà¸š Gzip) ได้', + 'Database file' => 'ไฟล์à¸à¸²à¸™à¸‚้อมูล', + 'Upload' => 'อัปโหลด', + 'Your project must have at least one active swimlane.' => 'โครงà¸à¸²à¸£à¸‚องคุณต้องมีอย่างน้อยหนึ่งเลนที่ใช้งานอยู่', + 'Project: %s' => 'โครงà¸à¸²à¸£: %s', + 'Automatic action not found: "%s"' => 'ไม่พบà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¸­à¸±à¸•โนมัติ: "%s"', + '%d projects' => '%d โครงà¸à¸²à¸£', + '%d project' => '%d โครงà¸à¸²à¸£', + 'There is no project.' => 'ไม่มีโครงà¸à¸²à¸£', + 'Sort' => 'เรียงลำดับ', + 'Project ID' => 'รหัสโครงà¸à¸²à¸£', + 'Project name' => 'ชื่อโครงà¸à¸²à¸£', + 'Public' => 'สาธารณะ', + 'Personal' => 'ส่วนตัว', + '%d tasks' => '%d งาน', + '%d task' => '%d งาน', + 'Task ID' => 'รหัสงาน', + 'Assign automatically a color when due date is expired' => 'à¸à¸³à¸«à¸™à¸”สีโดยอัตโนมัติเมื่อครบà¸à¸³à¸«à¸™à¸”', + 'Total score in this column across all swimlanes' => 'คะà¹à¸™à¸™à¸£à¸§à¸¡à¹ƒà¸™à¸„อลัมน์นี้ในทุà¸à¹€à¸¥à¸™', + 'HRK - Kuna' => 'HRK - คูนาโครเอเชีย', + 'ARS - Argentine Peso' => 'ARS - เปโซอาร์เจนตินา', + 'COP - Colombian Peso' => 'COP - เปโซโคลอมเบีย', + '%d groups' => '%d à¸à¸¥à¸¸à¹ˆà¸¡', + '%d group' => '%d à¸à¸¥à¸¸à¹ˆà¸¡', + 'Group ID' => 'รหัสà¸à¸¥à¸¸à¹ˆà¸¡', + 'External ID' => 'รหัสภายนอà¸', + '%d users' => '%d ผู้ใช้', + '%d user' => '%d ผู้ใช้', + 'Hide subtasks' => 'ซ่อนงานย่อย', + 'Show subtasks' => 'à¹à¸ªà¸”งงานย่อย', + 'Authentication Parameters' => 'พารามิเตอร์à¸à¸²à¸£à¸£à¸±à¸šà¸£à¸­à¸‡à¸„วามถูà¸à¸•้อง', + 'API Access' => 'à¸à¸²à¸£à¹€à¸‚้าถึง API', + 'No users found.' => 'ไม่พบผู้ใช้', + 'User ID' => 'รหัสผู้ใช้', + 'Notifications are activated' => 'เปิดใช้งานà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนà¹à¸¥à¹‰à¸§', + 'Notifications are disabled' => 'ปิดใช้งานà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนà¹à¸¥à¹‰à¸§', + 'User disabled' => 'ปิดใช้งานผู้ใช้à¹à¸¥à¹‰à¸§', + '%d notifications' => '%d à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน', + '%d notification' => '%d à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน', + 'There is no external integration installed.' => 'ไม่ได้ติดตั้งà¸à¸²à¸£à¸£à¸§à¸¡à¸£à¸°à¸šà¸šà¸ à¸²à¸¢à¸™à¸­à¸', + 'You are not allowed to update tasks assigned to someone else.' => 'คุณไม่ได้รับอนุà¸à¸²à¸•ให้อัปเดตงานที่มอบหมายให้ผู้อื่น', + 'You are not allowed to change the assignee.' => 'คุณไม่ได้รับอนุà¸à¸²à¸•ให้เปลี่ยนผู้รับมอบหมาย', + 'Task suppression is not permitted' => 'ไม่อนุà¸à¸²à¸•ให้ระงับงาน', + 'Changing assignee is not permitted' => 'ไม่อนุà¸à¸²à¸•ให้เปลี่ยนผู้รับมอบหมาย', + 'Update only assigned tasks is permitted' => 'อนุà¸à¸²à¸•ให้อัปเดตเฉพาะงานที่ได้รับมอบหมาย', + 'Only for tasks assigned to the current user' => 'สำหรับงานที่มอบหมายให้ผู้ใช้ปัจจุบันเท่านั้น', + 'My projects' => 'โครงà¸à¸²à¸£à¸‚องฉัน', + 'You are not a member of any project.' => 'คุณไม่ใช่สมาชิà¸à¸‚องโครงà¸à¸²à¸£à¹ƒà¸”ๆ', + 'My subtasks' => 'งานย่อยของฉัน', + '%d subtasks' => '%d งานย่อย', + '%d subtask' => '%d งานย่อย', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'อนุà¸à¸²à¸•ให้ย้ายงานระหว่างคอลัมน์เหล่านั้นเท่านั้นสำหรับงานที่มอบหมายให้ผู้ใช้ปัจจุบัน', + '[DUPLICATE]' => '[ทำซ้ำ]', + 'DKK - Danish Krona' => 'DKK - โครนเดนมาร์à¸', + 'Remove user from group' => 'ลบผู้ใช้ออà¸à¸ˆà¸²à¸à¸à¸¥à¸¸à¹ˆà¸¡', + 'Assign the task to its creator' => 'มอบหมายงานให้ผู้สร้าง', + 'This task was sent by email to "%s" with subject "%s".' => 'งานนี้ถูà¸à¸ªà¹ˆà¸‡à¸—างอีเมลถึง "%s" พร้อมหัวข้อ "%s"', + 'Predefined Email Subjects' => 'หัวข้ออีเมลที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'Write one subject by line.' => 'เขียนหัวข้อหนึ่งบรรทัดต่อหนึ่งรายà¸à¸²à¸£', + 'Create another link' => 'สร้างลิงà¸à¹Œà¸­à¸·à¹ˆà¸™', + 'BRL - Brazilian Real' => 'BRL - เรียลบราซิล', + 'Add a new Kanboard task' => 'เพิ่มงาน Kanboard ใหม่', + 'Subtask not started' => 'งานย่อยยังไม่เริ่ม', + 'Subtask currently in progress' => 'งานย่อยà¸à¸³à¸¥à¸±à¸‡à¸”ำเนินà¸à¸²à¸£à¸­à¸¢à¸¹à¹ˆ', + 'Subtask completed' => 'งานย่อยเสร็จสมบูรณ์à¹à¸¥à¹‰à¸§', + 'Subtask added successfully.' => 'เพิ่มงานย่อยสำเร็จà¹à¸¥à¹‰à¸§', + '%d subtasks added successfully.' => 'เพิ่มงานย่อย %d รายà¸à¸²à¸£à¸ªà¸³à¹€à¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§', + 'Enter one subtask by line.' => 'ป้อนงานย่อยหนึ่งบรรทัดต่อหนึ่งรายà¸à¸²à¸£', + 'Predefined Contents' => 'เนื้อหาที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'Predefined contents' => 'เนื้อหาที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'Predefined Task Description' => 'คำอธิบายงานที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'Do you really want to remove this template? "%s"' => 'คุณต้องà¸à¸²à¸£à¸¥à¸šà¹à¸¡à¹ˆà¹à¸šà¸šà¸™à¸µà¹‰à¸ˆà¸£à¸´à¸‡ ๆ หรือไม่: "%s"?', + 'Add predefined task description' => 'เพิ่มคำอธิบายงานที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'Predefined Task Descriptions' => 'คำอธิบายงานที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'Template created successfully.' => 'สร้างà¹à¸¡à¹ˆà¹à¸šà¸šà¸ªà¸³à¹€à¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§', + 'Unable to create this template.' => 'ไม่สามารถสร้างà¹à¸¡à¹ˆà¹à¸šà¸šà¸™à¸µà¹‰à¹„ด้', + 'Template updated successfully.' => 'อัปเดตà¹à¸¡à¹ˆà¹à¸šà¸šà¸ªà¸³à¹€à¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§', + 'Unable to update this template.' => 'ไม่สามารถอัปเดตà¹à¸¡à¹ˆà¹à¸šà¸šà¸™à¸µà¹‰à¹„ด้', + 'Template removed successfully.' => 'ลบà¹à¸¡à¹ˆà¹à¸šà¸šà¸ªà¸³à¹€à¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§', + 'Unable to remove this template.' => 'ไม่สามารถลบà¹à¸¡à¹ˆà¹à¸šà¸šà¸™à¸µà¹‰à¹„ด้', + 'Template for the task description' => 'à¹à¸¡à¹ˆà¹à¸šà¸šà¸ªà¸³à¸«à¸£à¸±à¸šà¸„ำอธิบายงาน', + 'The start date is greater than the end date' => 'วันที่เริ่มต้นมาà¸à¸à¸§à¹ˆà¸²à¸§à¸±à¸™à¸—ี่สิ้นสุด', + 'Tags must be separated by a comma' => 'ต้องใช้เครื่องหมายจุลภาคคั่นà¹à¸—็à¸', + 'Only the task title is required' => 'จำเป็นต้องมีเพียงชื่อเรื่องงานเท่านั้น', + 'Creator Username' => 'ชื่อผู้ใช้ผู้สร้าง', + 'Color Name' => 'ชื่อสี', + 'Column Name' => 'ชื่อคอลัมน์', + 'Swimlane Name' => 'ชื่อเลน', + 'Time Estimated' => 'เวลาที่ประมาณ', + 'Time Spent' => 'เวลาที่ใช้ไป', + 'External Link' => 'ลิงà¸à¹Œà¸ à¸²à¸¢à¸™à¸­à¸', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'คุณลัà¸à¸©à¸“ะนี้เปิดใช้งานฟีด iCal, ฟีด RSS à¹à¸¥à¸°à¸¡à¸¸à¸¡à¸¡à¸­à¸‡à¸šà¸­à¸£à¹Œà¸”สาธารณะ', + 'Stop the timer of all subtasks when moving a task to another column' => 'หยุดตัวจับเวลาของงานย่อยทั้งหมดเมื่อย้ายงานไปยังคอลัมน์อื่น', + 'Subtask Title' => 'ชื่อเรื่องงานย่อย', + 'Add a subtask and activate the timer when moving a task to another column' => 'เพิ่มงานย่อยà¹à¸¥à¸°à¹€à¸›à¸´à¸”ใช้งานตัวจับเวลาเมื่อย้ายงานไปยังคอลัมน์อื่น', + 'days' => 'วัน', + 'minutes' => 'นาที', + 'seconds' => 'วินาที', + 'Assign automatically a color when preset start date is reached' => 'à¸à¸³à¸«à¸™à¸”สีโดยอัตโนมัติเมื่อถึงวันที่เริ่มต้นที่ตั้งไว้ล่วงหน้า', + 'Move the task to another column once a predefined start date is reached' => 'ย้ายงานไปยังคอลัมน์อื่นเมื่อถึงวันที่เริ่มต้นที่à¸à¸³à¸«à¸™à¸”ไว้ล่วงหน้า', + 'This task is now linked to the task %s with the relation "%s"' => 'งานนี้เชื่อมโยงà¸à¸±à¸šà¸‡à¸²à¸™ %s โดยใช้ความสัมพันธ์ "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'ลบลิงà¸à¹Œà¸—ี่มีความสัมพันธ์ "%s" ไปยังงาน %s à¹à¸¥à¹‰à¸§', + 'Custom Filter:' => 'ตัวà¸à¸£à¸­à¸‡à¸—ี่à¸à¸³à¸«à¸™à¸”เอง:', + 'Unable to find this group.' => 'ไม่สามารถหาà¸à¸¥à¸¸à¹ˆà¸¡à¸™à¸µà¹‰à¹„ด้', + '%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 moved the task #%d to the swimlane "%s"' => '%s ย้ายงาน #%d ไปยังเลน "%s"', + '%sh spent' => 'ใช้ไป %sh', + '%sh estimated' => 'ประมาณ %sh', + 'Select All' => 'เลือà¸à¸—ั้งหมด', + 'Unselect All' => 'ยà¸à¹€à¸¥à¸´à¸à¸à¸²à¸£à¹€à¸¥à¸·à¸­à¸à¸—ั้งหมด', + 'Apply action' => 'ใช้à¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£', + 'Move selected tasks to another column or swimlane' => 'ย้ายงานที่เลือà¸à¹„ปยังคอลัมน์หรือเลนอื่น', + 'Edit tasks in bulk' => 'à¹à¸à¹‰à¹„ขงานจำนวนมาà¸', + 'Choose the properties that you would like to change for the selected tasks.' => 'เลือà¸à¸„ุณสมบัติที่คุณต้องà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸ªà¸³à¸«à¸£à¸±à¸šà¸‡à¸²à¸™à¸—ี่เลือà¸', + 'Configure this project' => 'à¸à¸³à¸«à¸™à¸”ค่าโครงà¸à¸²à¸£à¸™à¸µà¹‰', + 'Start now' => 'เริ่มตอนนี้', + '%s removed a file from the task #%d' => '%s ลบไฟล์ออà¸à¸ˆà¸²à¸à¸‡à¸²à¸™ #%d', + 'Attachment removed from task #%d: %s' => 'ลบไฟล์à¹à¸™à¸šà¸ˆà¸²à¸à¸‡à¸²à¸™ #%d: %s', + 'No color' => 'ไม่มีสี', + 'Attachment removed "%s"' => 'ลบไฟล์à¹à¸™à¸š "%s" à¹à¸¥à¹‰à¸§', + '%s removed a file from the task %s' => '%s ลบไฟล์ออà¸à¸ˆà¸²à¸à¸‡à¸²à¸™ %s', + 'Move the task to another swimlane when assigned to a user' => 'ย้ายงานไปยังเลนอื่นเมื่อมอบหมายให้ผู้ใช้', + 'Destination swimlane' => 'เลนปลายทาง', + 'Assign a category when the task is moved to a specific swimlane' => 'à¸à¸³à¸«à¸™à¸”หมวดหมู่เมื่อย้ายงานไปยังเลนเฉพาะ', + 'Move the task to another swimlane when the category is changed' => 'ย้ายงานไปยังเลนอื่นเมื่อมีà¸à¸²à¸£à¹€à¸›à¸¥à¸µà¹ˆà¸¢à¸™à¸«à¸¡à¸§à¸”หมู่', + 'Reorder this column by priority (ASC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามลำดับความสำคัภ(จาà¸à¸™à¹‰à¸­à¸¢à¹„ปมาà¸)', + 'Reorder this column by priority (DESC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามลำดับความสำคัภ(จาà¸à¸¡à¸²à¸à¹„ปน้อย)', + 'Reorder this column by assignee and priority (ASC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามผู้รับมอบหมายà¹à¸¥à¸°à¸¥à¸³à¸”ับความสำคัภ(จาà¸à¸™à¹‰à¸­à¸¢à¹„ปมาà¸)', + 'Reorder this column by assignee and priority (DESC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามผู้รับมอบหมายà¹à¸¥à¸°à¸¥à¸³à¸”ับความสำคัภ(จาà¸à¸¡à¸²à¸à¹„ปน้อย)', + 'Reorder this column by assignee (A-Z)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามผู้รับมอบหมาย (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามผู้รับมอบหมาย (Z-A)', + 'Reorder this column by due date (ASC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามวันครบà¸à¸³à¸«à¸™à¸” (จาà¸à¸™à¹‰à¸­à¸¢à¹„ปมาà¸)', + 'Reorder this column by due date (DESC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตามวันครบà¸à¸³à¸«à¸™à¸” (จาà¸à¸¡à¸²à¸à¹„ปน้อย)', + 'Reorder this column by id (ASC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตาม ID (จาà¸à¸™à¹‰à¸­à¸¢à¹„ปมาà¸)', + 'Reorder this column by id (DESC)' => 'จัดเรียงคอลัมน์นี้ใหม่ตาม ID (จาà¸à¸¡à¸²à¸à¹„ปน้อย)', + '%s moved the task #%d "%s" to the project "%s"' => '%s ย้ายงาน #%d "%s" ไปยังโครงà¸à¸²à¸£ "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'งาน #%d "%s" ถูà¸à¸¢à¹‰à¸²à¸¢à¹„ปยังโครงà¸à¸²à¸£ "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'ย้ายงานไปยังคอลัมน์อื่นเมื่อวันครบà¸à¸³à¸«à¸™à¸”น้อยà¸à¸§à¹ˆà¸²à¸ˆà¸³à¸™à¸§à¸™à¸§à¸±à¸™à¸—ี่à¸à¸³à¸«à¸™à¸”', + 'Automatically update the start date when the task is moved away from a specific column' => 'อัปเดตวันที่เริ่มต้นโดยอัตโนมัติเมื่อย้ายงานออà¸à¸ˆà¸²à¸à¸„อลัมน์ที่ระบุ', + 'HTTP Client:' => 'ไคลเอนต์ HTTP:', + 'Assigned' => 'ได้รับมอบหมาย', + 'Task limits apply to each swimlane individually' => 'ขีดจำà¸à¸±à¸”งานจะใช้à¸à¸±à¸šà¹à¸•่ละเลนà¹à¸¢à¸à¸à¸±à¸™', + 'Column task limits apply to each swimlane individually' => 'ขีดจำà¸à¸±à¸”งานคอลัมน์จะใช้à¸à¸±à¸šà¹à¸•่ละเลนà¹à¸¢à¸à¸à¸±à¸™', + 'Column task limits are applied to each swimlane individually' => 'ขีดจำà¸à¸±à¸”งานคอลัมน์จะใช้à¸à¸±à¸šà¹à¸•่ละเลนà¹à¸¢à¸à¸à¸±à¸™', + 'Column task limits are applied across swimlanes' => 'ขีดจำà¸à¸±à¸”งานคอลัมน์จะใช้à¸à¸±à¸šà¸—ุà¸à¹€à¸¥à¸™', + 'Task limit: ' => 'ขีดจำà¸à¸±à¸”งาน:', + 'Change to global tag' => 'เปลี่ยนเป็นà¹à¸—็à¸à¸ªà¸²à¸à¸¥', + 'Do you really want to make the tag "%s" global?' => 'คุณต้องà¸à¸²à¸£à¸—ำให้à¹à¸—็ภ"%s" เป็นสาà¸à¸¥à¸ˆà¸£à¸´à¸‡ ๆ หรือไม่?', + 'Enable global tags for this project' => 'เปิดใช้งานà¹à¸—็à¸à¸ªà¸²à¸à¸¥à¸ªà¸³à¸«à¸£à¸±à¸šà¹‚ครงà¸à¸²à¸£à¸™à¸µà¹‰', + 'Group membership(s):' => 'สมาชิà¸à¸à¸¥à¸¸à¹ˆà¸¡:', + '%s is a member of the following group(s): %s' => '%s เป็นสมาชิà¸à¸‚องà¸à¸¥à¸¸à¹ˆà¸¡à¸•่อไปนี้: %s', + '%d/%d group(s) shown' => 'à¹à¸ªà¸”งà¸à¸¥à¸¸à¹ˆà¸¡ %d/%d', + 'Subtask creation or modification' => 'à¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸«à¸£à¸·à¸­à¹à¸à¹‰à¹„ขงานย่อย', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'มอบหมายงานให้ผู้ใช้ที่ระบุเมื่อย้ายงานไปยังเลนที่ระบุ', + 'Comment' => 'ความคิดเห็น', + 'Collapse vertically' => 'ยุบà¹à¸™à¸§à¸•ั้ง', + 'Expand vertically' => 'ขยายà¹à¸™à¸§à¸•ั้ง', + 'MXN - Mexican Peso' => 'MXN - เปโซเม็à¸à¸‹à¸´à¹‚à¸', + 'Estimated vs actual time per column' => 'เวลาที่ประมาณ vs เวลาจริงต่อคอลัมน์', + 'HUF - Hungarian Forint' => 'HUF - โฟรินต์ฮังà¸à¸²à¸£à¸µ', + 'XBT - Bitcoin' => 'XBT - บิตคอยน์', + 'You must select a file to upload as your avatar!' => 'คุณต้องเลือà¸à¹„ฟล์เพื่ออัปโหลดเป็นอวตารของคุณ!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'ไฟล์ที่คุณอัปโหลดไม่ใช่รูปภาพที่ถูà¸à¸•้อง! (อนุà¸à¸²à¸•เฉพาะ *.gif, *.jpg, *.jpeg à¹à¸¥à¸° *.png เท่านั้น!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'ตั้งค่าวันครบà¸à¸³à¸«à¸™à¸”โดยอัตโนมัติเมื่อย้ายงานออà¸à¸ˆà¸²à¸à¸„อลัมน์ที่ระบุ', + 'No other projects found.' => 'ไม่พบโครงà¸à¸²à¸£à¸­à¸·à¹ˆà¸™', + 'Tasks copied successfully.' => 'คัดลอà¸à¸‡à¸²à¸™à¸ªà¸³à¹€à¸£à¹‡à¸ˆà¹à¸¥à¹‰à¸§', + 'Unable to copy tasks.' => 'ไม่สามารถคัดลอà¸à¸‡à¸²à¸™à¹„ด้', + 'Theme' => 'ธีม', + 'Theme:' => 'ธีม:', + 'Light theme' => 'ธีมสว่าง', + 'Dark theme' => 'ธีมมืด', + 'Automatic theme - Sync with system' => 'ธีมอัตโนมัติ - ซิงค์à¸à¸±à¸šà¸£à¸°à¸šà¸š', + 'Application managers or more' => 'ผู้จัดà¸à¸²à¸£à¹à¸­à¸›à¸žà¸¥à¸´à¹€à¸„ชันหรือมาà¸à¸à¸§à¹ˆà¸²', + 'Administrators' => 'ผู้ดูà¹à¸¥à¸£à¸°à¸šà¸š', + 'Visibility:' => 'à¸à¸²à¸£à¸¡à¸­à¸‡à¹€à¸«à¹‡à¸™:', + 'Standard users' => 'ผู้ใช้มาตรà¸à¸²à¸™', + 'Visibility is required' => 'จำเป็นต้องมีà¸à¸²à¸£à¸¡à¸­à¸‡à¹€à¸«à¹‡à¸™', + 'The visibility should be an app role' => 'à¸à¸²à¸£à¸¡à¸­à¸‡à¹€à¸«à¹‡à¸™à¸„วรเป็นบทบาทà¹à¸­à¸›à¸žà¸¥à¸´à¹€à¸„ชัน', + 'Reply' => 'ตอบà¸à¸¥à¸±à¸š', + '%s wrote: ' => '%s เขียน:', + 'Number of visible tasks in this column and swimlane' => 'จำนวนงานที่มองเห็นได้ในคอลัมน์à¹à¸¥à¸°à¹€à¸¥à¸™à¸™à¸µà¹‰', + 'Number of tasks in this swimlane' => 'จำนวนงานในเลนนี้', + 'Unable to find another subtask in progress, you can close this window.' => 'ไม่สามารถหางานย่อยอื่นที่à¸à¸³à¸¥à¸±à¸‡à¸”ำเนินà¸à¸²à¸£à¸­à¸¢à¸¹à¹ˆà¹„ด้ คุณสามารถปิดหน้าต่างนี้ได้', + 'This theme is invalid' => 'ธีมนี้ไม่ถูà¸à¸•้อง', + 'This role is invalid' => 'บทบาทนี้ไม่ถูà¸à¸•้อง', + 'This timezone is invalid' => 'เขตเวลานี้ไม่ถูà¸à¸•้อง', + 'This language is invalid' => 'ภาษานี้ไม่ถูà¸à¸•้อง', + 'This URL is invalid' => 'URL นี้ไม่ถูà¸à¸•้อง', + 'Date format invalid' => 'รูปà¹à¸šà¸šà¸§à¸±à¸™à¸—ี่ไม่ถูà¸à¸•้อง', + 'Time format invalid' => 'รูปà¹à¸šà¸šà¹€à¸§à¸¥à¸²à¹„ม่ถูà¸à¸•้อง', + 'Invalid Mail transport' => 'à¸à¸²à¸£à¸‚นส่งอีเมลไม่ถูà¸à¸•้อง', + 'Color invalid' => 'สีไม่ถูà¸à¸•้อง', + 'This value must be greater or equal to %d' => 'ค่านี้นต้องมาà¸à¸à¸§à¹ˆà¸²à¸«à¸£à¸·à¸­à¹€à¸—่าà¸à¸±à¸š %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'เพิ่ม BOM ที่ต้นไฟล์ (จำเป็นสำหรับ Microsoft Excel)', + 'Just add these tag(s)' => 'เพียงเพิ่มà¹à¸—็à¸à¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰', + 'Remove internal link(s)' => 'ลบลิงà¸à¹Œà¸ à¸²à¸¢à¹ƒà¸™', + 'Import tasks from another project' => 'นำเข้างานจาà¸à¹‚ครงà¸à¸²à¸£à¸­à¸·à¹ˆà¸™', + 'Select the project to copy tasks from' => 'เลือà¸à¹‚ครงà¸à¸²à¸£à¸—ี่จะคัดลอà¸à¸‡à¸²à¸™à¸ˆà¸²à¸', + 'The total maximum allowed attachments size is %sB.' => 'ขนาดไฟล์à¹à¸™à¸šà¸ªà¸¹à¸‡à¸ªà¸¸à¸”ที่อนุà¸à¸²à¸•ทั้งหมดคือ %sB', + 'Add attachments' => 'เพิ่มไฟล์à¹à¸™à¸š', + 'Task #%d "%s" is overdue' => 'งาน #%d "%s" เà¸à¸´à¸™à¸à¸³à¸«à¸™à¸”', + 'Enable notifications by default for all new users' => 'เปิดใช้งานà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนตามค่าเริ่มต้นสำหรับผู้ใช้ใหม่ทั้งหมด', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'à¸à¸³à¸«à¸™à¸”งานให้ผู้สร้างสำหรับคอลัมน์ที่ระบุ หาà¸à¹„ม่มีผู้รับมอบหมายที่ตั้งค่าเอง', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'à¸à¸³à¸«à¸™à¸”งานให้ผู้ใช้ที่ล็อà¸à¸­à¸´à¸™à¹€à¸¡à¸·à¹ˆà¸­à¸¢à¹‰à¸²à¸¢à¸„อลัมน์ไปยังคอลัมน์ที่ระบุ หาà¸à¸¢à¸±à¸‡à¹„ม่มีผู้ใช้ที่ถูà¸à¸à¸³à¸«à¸™à¸”', +]; diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php new file mode 100644 index 0000000..c876638 --- /dev/null +++ b/app/Locale/tr_TR/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'Hiçbiri', + 'Edit' => 'Düzenle', + 'Remove' => 'Sil', + 'Yes' => 'Evet', + 'No' => 'Hayır', + 'cancel' => 'İptal', + 'or' => 'veya', + 'Yellow' => 'Sarı', + 'Blue' => 'Mavi', + 'Green' => 'YeÅŸil', + 'Purple' => 'Mor', + 'Red' => 'Kırmızı', + 'Orange' => 'Turuncu', + 'Grey' => 'Gri', + 'Brown' => 'K.rengi', + 'Deep Orange' => 'Kavuniçi', + 'Dark Grey' => 'Koyu Gri', + 'Pink' => 'Pembe', + 'Teal' => 'Turkuaz', + 'Cyan' => 'Cam GöbeÄŸi', + 'Lime' => 'Limoni', + 'Light Green' => 'Açık YeÅŸil', + 'Amber' => 'Koyu Sarı', + 'Save' => 'Kaydet', + 'Login' => 'GiriÅŸ', + 'Official website:' => 'Resmi internet sitesi:', + 'Unassigned' => 'Atanmamış', + 'View this task' => 'Bu görevi görüntüle', + 'Remove user' => 'Kullanıcıyı kaldır', + 'Do you really want to remove this user: "%s"?' => 'Bu kullanıcıyı gerçekten silmek istiyor musunuz: "%s"?', + 'All users' => 'Tüm kullanıcılar', + 'Username' => 'Kullanıcı adı', + 'Password' => 'Åžifre', + 'Administrator' => 'Yönetici', + 'Sign in' => 'GiriÅŸ yap', + 'Users' => 'Kullanıcılar', + 'Forbidden' => 'Yasak', + 'Access Forbidden' => 'EriÅŸim yasaklandı', + 'Edit user' => 'Kullanıcıyı düzenle', + 'Logout' => 'Çıkış yap', + 'Bad username or password' => 'Hatalı kullanıcı adı veya ÅŸifre', + 'Edit project' => 'Projeyi düzenle', + 'Name' => 'İsim', + 'Projects' => 'Projeler', + 'No project' => 'Proje yok', + 'Project' => 'Proje', + 'Status' => 'Durum', + 'Tasks' => 'Görevler', + 'Board' => 'Pano', + 'Actions' => 'İşlemler', + 'Inactive' => 'Aktif deÄŸil', + 'Active' => 'Aktif', + '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"' => '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', + '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"?', + 'Settings' => 'Ayarlar', + 'Application settings' => 'Uygulama ayarları', + 'Language' => 'Dil', + 'Webhook token:' => 'Web kancası anahtarı:', + 'API token:' => 'API anahtarı:', + 'Database size:' => 'Veritabanı boyutu:', + 'Download the database' => 'Veritabanını indir', + 'Optimize the database' => 'Veritabanını optimize et', + '(VACUUM command)' => '(VACUUM komutu)', + '(Gzip compressed Sqlite file)' => '(Gzip ile sıkıştırılmış Sqlite dosyası)', + 'Close a task' => 'Bir görevi kapat', + 'Column' => 'Sütun', + 'Color' => 'Renk', + 'Assignee' => 'Atanan', + 'Create another task' => 'BaÅŸka bir görev oluÅŸtur', + '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' => 'Panoya dön', + 'There is nobody assigned' => 'Kimse atanmamış', + '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.', + 'Add a new task' => 'Yeni görev ekle', + 'The username is required' => 'Kullanıcı adı gerekli', + 'The maximum length is %d characters' => 'Maksimum uzunluk %d karakterdir', + 'The minimum length is %d characters' => 'Minimum uzunluk %d karakterdir', + 'The password is required' => 'Åžifre gerekli', + 'This value must be an integer' => 'Bu deÄŸer bir rakam olmak zorunda', + 'The username must be unique' => 'Kullanıcı adı daha önceden var', + 'The user id is required' => 'Kullanıcı kodu gerekli', + 'Passwords don\'t match' => 'Åžifreler uyuÅŸmuyor', + 'The confirmation is required' => 'Onay gerekli', + 'The project is required' => 'Proje gerekli', + 'The id is required' => 'Kod gerekli', + 'The project id is required' => 'Proje kodu gerekli', + 'The project name is required' => 'Proje adı gerekli', + 'The title is required' => 'BaÅŸlık gerekli', + 'Settings saved successfully.' => 'Ayarlar baÅŸarıyla kaydedildi.', + 'Unable to save your settings.' => 'Ayarlarınız kaydedilemedi.', + 'Database optimization done.' => 'Veritabanı optimizasyonu tamamlandı.', + 'Your project has been created successfully.' => 'Projeniz baÅŸarıyla oluÅŸturuldu.', + 'Unable to create your project.' => 'Proje oluÅŸturulamadı.', + 'Project updated successfully.' => 'Proje baÅŸarıyla güncellendi.', + 'Unable to update this project.' => 'Bu proje güncellenemedi.', + 'Unable to remove this project.' => 'Bu proje silinemedi.', + 'Project removed successfully.' => 'Proje baÅŸarıyla silindi.', + 'Project activated successfully.' => 'Proje baÅŸarıyla aktif edildi.', + 'Unable to activate this project.' => 'Bu proje aktif edilemedi.', + 'Project disabled successfully.' => 'Proje baÅŸarıyla devre dışı bırakıldı.', + 'Unable to disable this project.' => 'Bu proje devre dışı bırakılamadı.', + 'Unable to open this task.' => 'Bu görev açılamıyor.', + 'Task opened successfully.' => 'Görev baÅŸarıyla açıldı.', + 'Unable to close this task.' => 'Bu görev kapatılamıyor.', + 'Task closed successfully.' => 'Görev baÅŸarıyla kapatıldı.', + 'Unable to update your task.' => 'Görev güncellenemiyor.', + 'Task updated successfully.' => 'Görev baÅŸarıyla güncellendi.', + 'Unable to create your task.' => 'Görev oluÅŸturulamadı.', + 'Task created successfully.' => 'Görev baÅŸarıyla oluÅŸturuldu.', + 'User created successfully.' => 'Kullanıcı baÅŸarıyla oluÅŸturuldu', + 'Unable to create your user.' => 'Kullanıcı oluÅŸturulamadı.', + 'User updated successfully.' => 'Kullanıcı baÅŸarıyla güncellendi.', + 'User removed successfully.' => 'Kullanıcı baÅŸarıyla silindi.', + 'Unable to remove this user.' => 'Bu kullanıcı silinemiyor.', + 'Board updated successfully.' => 'Pano baÅŸarıyla güncellendi.', + 'Ready' => 'Hazır', + 'Backlog' => 'Bekleme listesi', + 'Work in progress' => 'İşlemde', + 'Done' => 'Tamamlandı', + 'Application version:' => 'Uygulama versiyonu:', + 'Id' => 'Kod', + 'Public link' => 'Dışa açık link', + 'Timezone' => 'Saat dilimi', + 'Sorry, I didn\'t find this information in my database!' => 'Üzgünüm, bu bilgiyi veri tabanımda bulamadım.', + 'Page not found' => 'Sayfa bulunamadı', + 'Complexity' => 'Zorluk seviyesi', + 'Task limit' => 'Görev limiti', + 'Task count' => 'Görev sayısı', + 'User' => 'Kullanıcı', + 'Comments' => 'Yorumlar', + 'Comment is required' => 'Yorum gerekli', + 'Comment added successfully.' => 'Yorum eklendi', + 'Unable to create your comment.' => 'Yorumunuz oluÅŸturulamadı', + 'Due Date' => 'BitiÅŸ Tarihi', + 'Invalid date' => 'Geçersiz tarihi', + 'Automatic actions' => 'Otomatik iÅŸlemler', + 'Your automatic action has been created successfully.' => 'Otomatik iÅŸlem baÅŸarıyla oluÅŸturuldu', + 'Unable to create your automatic action.' => 'Otomatik iÅŸleminiz oluÅŸturulamadı', + 'Remove an action' => 'Bir iÅŸlemi sil', + 'Unable to remove this action.' => 'Bu iÅŸlem silinemedi', + 'Action removed successfully.' => 'İşlem baÅŸarıyla silindi', + 'Automatic actions for the project "%s"' => '"%s" projesi için otomatik iÅŸlemler', + 'Add an action' => 'İşlem ekle', + 'Event name' => 'Durum adı', + 'Action' => 'İşlem', + 'Event' => 'Durum', + 'When the selected event occurs execute the corresponding action.' => 'Seçilen durum oluÅŸtuÄŸunda ilgili eylemi gerçekleÅŸtir.', + 'Next step' => 'Sonraki adım', + 'Define action parameters' => 'İşlem parametrelerini düzenle', + 'Do you really want to remove this action: "%s"?' => 'Bu iÅŸlemi silmek istediÄŸinize emin misiniz: "%s"?', + 'Remove an automatic action' => 'Bir otomatik iÅŸlemi sil', + 'Assign the task to a specific user' => 'Görevi bir kullanıcıya ata', + 'Assign the task to the person who does the action' => 'Görevi, iÅŸlemi gerçekleÅŸtiren kullanıcıya ata', + 'Duplicate the task to another project' => 'Görevi bir baÅŸka projeye kopyala', + 'Move a task to another column' => 'Bir görevi baÅŸka bir sütuna taşı', + 'Task modification' => 'Görev düzenleme', + 'Task creation' => 'Görev oluÅŸturma', + 'Closing a task' => 'Bir görev kapatılıyor', + 'Assign a color to a specific user' => 'Bir kullanıcıya renk tanımla', + 'Position' => 'Pozisyon', + 'Duplicate to project' => 'BaÅŸka bir projeye kopyala', + 'Duplicate' => 'Kopya oluÅŸtur', + 'Link' => 'BaÄŸlantı', + 'Comment updated successfully.' => 'Yorum güncellendi.', + 'Unable to update your comment.' => 'Yorum güncellenemedi.', + 'Remove a comment' => 'Bir yorumu sil', + 'Comment removed successfully.' => 'Yorum silindi.', + 'Unable to remove this comment.' => 'Bu yorum silinemiyor.', + 'Do you really want to remove this comment?' => 'Bu yorumu silmek istediÄŸinize emin misiniz?', + 'Current password for the user "%s"' => 'Kullanıcı için mevcut ÅŸifre "%s"', + 'The current password is required' => 'Mevcut ÅŸifre gerekli', + 'Wrong password' => 'Yanlış ÅŸifre', + 'Unknown' => 'Bilinmeyen', + 'Last logins' => 'Son kullanıcı giriÅŸleri', + 'Login date' => 'GiriÅŸ tarihi', + 'Authentication method' => 'DoÄŸrulama yöntemi', + 'IP address' => 'IP adresi', + 'User agent' => 'Kullanıcı Arayüzü', + 'Persistent connections' => 'Kalıcı baÄŸlantılar', + 'No session.' => 'Oturum yok.', + 'Expiration date' => 'Geçerlilik sonu', + 'Remember Me' => 'Beni hatırla', + 'Creation date' => 'OluÅŸturulma tarihi', + 'Everybody' => 'Herkes', + 'Open' => 'Açık', + 'Closed' => 'Kapalı', + 'Search' => 'Ara', + 'Nothing found.' => 'Hiçbir ÅŸey bulunamadı.', + 'Due date' => 'BitiÅŸ tarihi', + 'Description' => 'Açıklama', + '%d comments' => '%d yorum', + '%d comment' => '%d yorum', + 'Email address invalid' => 'E-posta adresi geçersiz', + 'Your external account is not linked anymore to your profile.' => 'Harici hesabınız artık profilinize baÄŸlı deÄŸil', + 'Unable to unlink your external account.' => 'Harici hesabınızla baÄŸlantı koparılamadı', + 'External authentication failed' => 'Harici hesap doÄŸrulaması baÅŸarısız', + 'Your external account is linked to your profile successfully.' => 'Harici hesabınız profilinizle baÅŸarıyla baÄŸlandı.', + 'Email' => 'E-posta', + 'Task removed successfully.' => 'Görev baÅŸarıyla silindi.', + 'Unable to remove this task.' => 'Görev silinemiyor.', + 'Remove a task' => 'Bir görevi sil', + 'Do you really want to remove this task: "%s"?' => 'Bu görevi silmek istediÄŸinize emin misiniz: "%s"?', + 'Assign automatically a color based on a category' => 'Kategoriye göre otomatik renk ata', + 'Assign automatically a category based on a color' => 'Rengine göre otomatik kategori ata', + 'Task creation or modification' => 'Görev oluÅŸturma veya düzenleme', + 'Category' => 'Kategori', + 'Category:' => 'Kategori:', + 'Categories' => 'Kategoriler', + 'Your category has been created successfully.' => 'Kategori baÅŸarıyla oluÅŸturuldu.', + 'This category has been updated successfully.' => 'Kategori baÅŸarıyla güncellendi.', + 'Unable to update this category.' => 'Kategori güncellenemedi.', + 'Remove a category' => 'Bir kategoriyi sil', + 'Category removed successfully.' => 'Kategori baÅŸarıyla silindi.', + 'Unable to remove this category.' => 'Bu kategori silinemedi.', + 'Category modification for the project "%s"' => '"%s" projesi için kategori düzenleme', + 'Category Name' => 'Kategori adı', + 'Add a new category' => 'Yeni kategori ekle', + 'Do you really want to remove this category: "%s"?' => 'Bu kategoriyi silmek istediÄŸinize emin misiniz: "%s"?', + 'All categories' => 'Tüm kategoriler', + 'No category' => 'Kategori Yok', + 'The name is required' => 'İsim gerekli', + 'Remove a file' => 'Dosya sil', + 'Unable to remove this file.' => 'Dosya silinemedi', + 'File removed successfully.' => 'Dosya silindi', + 'Attach a document' => 'Dosya ekle', + 'Do you really want to remove this file: "%s"?' => 'Bu dosyayı silmek istediÄŸinize emin misiniz: "%s"?', + 'Attachments' => 'Ekler', + 'Edit the task' => 'Görevi deÄŸiÅŸtir', + 'Add a comment' => 'Yorum ekle', + 'Edit a comment' => 'Yorum deÄŸiÅŸtir', + 'Summary' => 'Özet', + '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', + 'Remaining:' => 'Kalan', + 'hours' => 'saat', + 'estimated' => 'tahmini', + 'Sub-Tasks' => 'Alt Görev', + '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' => '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ı', + 'Todo' => 'Yapılacaklar', + 'In progress' => 'Devam etmekte', + 'Sub-task removed successfully.' => 'Alt görev baÅŸarıyla silindi.', + 'Unable to remove this sub-task.' => 'Alt görev silinemedi.', + 'Sub-task updated successfully.' => 'Alt görev güncellendi.', + 'Unable to update your sub-task.' => 'Alt görev güncellenemiyor.', + 'Unable to create your sub-task.' => 'Alt görev oluÅŸturulamadı.', + 'Maximum size: ' => 'Maksimum boyutu', + 'Display another project' => 'BaÅŸka bir proje göster', + 'Created by %s' => '%s tarafından oluÅŸturuldu', + 'Tasks Export' => 'Görevleri dışa aktar', + 'Start Date' => 'BaÅŸlangıç tarihi', + 'Execute' => 'GerçekleÅŸtir', + 'Task Id' => 'Görev KimliÄŸi', + 'Creator' => 'OluÅŸturan', + 'Modification date' => 'DeÄŸiÅŸiklik tarihi', + 'Completion date' => 'Tamamlanma tarihi', + 'Clone' => 'Kopya oluÅŸtur', + 'Project cloned successfully.' => 'Proje kopyası baÅŸarıyla oluÅŸturuldu.', + 'Unable to clone this project.' => 'Proje kopyası oluÅŸturulamıyor.', + 'Enable email notifications' => 'E-posta bilgilendirmesini aç', + 'Task position:' => 'Görev pozisyonu', + 'The task #%d has been opened.' => '#%d numaralı görev açıldı.', + 'The task #%d has been closed.' => '#%d numaralı görev kapatıldı.', + 'Sub-task updated' => 'Alt görev güncellendi', + 'Title:' => 'BaÅŸlık', + 'Status:' => 'Durum', + 'Assignee:' => 'Sorumlu:', + 'Time tracking:' => 'Zaman takibi', + '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 comment' => 'Yeni yorum', + 'Comment updated' => 'Yorum güncellendi', + 'New subtask' => 'Yeni alt görev', + 'I only want to receive notifications for these 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', + 'Disable public access' => 'Dışa açık eriÅŸimi kapat', + 'Enable public access' => 'Dışa açık eriÅŸimi aç', + 'Public access disabled' => 'Dışa açık eriÅŸim kapatıldı', + 'Move the task to another project' => 'Görevi baÅŸka projeye taşı', + 'Move to project' => 'BaÅŸka projeye taşı', + 'Do you really want to duplicate this task?' => 'Bu görevin kopyasını oluÅŸturmak istediÄŸinize emin misiniz?', + 'Duplicate a task' => 'Görevin kopyasını oluÅŸtur', + 'External accounts' => 'Dış hesaplar', + 'Account type' => 'Hesap türü', + 'Local' => 'Yerel', + 'Remote' => 'Uzak', + 'Enabled' => 'EtkinleÅŸtirildi', + 'Disabled' => 'Devre dışı bırakıldı', + 'Login:' => 'Kullanıcı adı', + 'Full Name:' => 'Ad', + 'Email:' => 'E-posta', + 'Notifications:' => 'Bildirimler:', + 'Notifications' => 'Bildirimler', + 'Account type:' => 'Hesap türü:', + 'Edit profile' => 'Profili deÄŸiÅŸtir', + 'Change password' => 'Åžifre deÄŸiÅŸtir', + 'Password modification' => 'Åžifre deÄŸiÅŸimi', + 'External authentications' => 'Dış kimlik doÄŸrulamaları', + 'Never connected.' => 'Hiç baÄŸlanmamış.', + 'No external authentication enabled.' => 'Dış kimlik doÄŸrulama kapalı.', + 'Password modified successfully.' => 'Åžifre baÅŸarıyla deÄŸiÅŸtirildi.', + 'Unable to change the password.' => 'Åžifre deÄŸiÅŸtirilemiyor.', + 'Change category' => 'Kategori deÄŸiÅŸtirme', + '%s updated the task %s' => '%s kullanıcısı %s görevini güncelledi', + '%s opened the task %s' => '%s kullanıcısı %s görevini açtı', + '%s moved the task %s to the position #%d in the column "%s"' => '%s kullanıcısı %s görevini #%d pozisyonu "%s" sütununa taşıdı', + '%s moved the task %s to the column "%s"' => '%s kullanıcısı %s görevini "%s" sütununa taşıdı', + '%s created the task %s' => '%s kullanıcısı %s görevini oluÅŸturdu', + '%s closed the task %s' => '%s kullanıcısı %s görevini kapattı', + '%s created a subtask for the task %s' => '%s kullanıcısı %s görevi için bir alt görev oluÅŸturdu', + '%s updated a subtask for the task %s' => '%s kullanıcısı %s görevinin bir alt görevini güncelledi', + 'Assigned to %s with an estimate of %s/%sh' => '%s kullanıcısına tahmini %s/%s saat tamamlanma süresi ile atanmış', + 'Not assigned, estimate of %sh' => 'Kimseye atanmamış, tahmini süre %s saat', + '%s updated a comment on the task %s' => '%s kullanıcısı %s görevinde bir yorumu güncelledi', + '%s commented the task %s' => '%s kullanıcısı %s görevine yorum ekledi', + '%s\'s activity' => '%s\'in aktivitesi', + 'RSS feed' => 'RSS kaynağı', + '%s updated a comment on the task #%d' => '%s kullanıcısı #%d nolu görevde bir yorumu güncelledi', + '%s commented on the task #%d' => '%s kullanıcısı #%d nolu göreve yorum ekledi', + '%s updated a subtask for the task #%d' => '%s kullanıcısı #%d nolu görevin bir alt görevini güncelledi', + '%s created a subtask for the task #%d' => '%s kullanıcısı #%d nolu göreve bir alt görev ekledi', + '%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 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 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', + 'Create a task from an external provider' => 'Dış saÄŸlayıcı ile bir görev oluÅŸtur', + 'Change the assignee based on an external username' => 'Dış kaynaklı kullanıcı adı ile göreve atananı deÄŸiÅŸtir', + 'Change the category based on an external label' => 'Dış kaynaklı bir etiket ile kategori deÄŸiÅŸtir', + 'Reference' => 'Referans', + 'Label' => 'Etiket', + 'Database' => 'Veritabanı', + 'About' => 'Hakkında', + 'Database driver:' => 'Veritabanı sürücüsü:', + 'Board settings' => 'Pano ayarları', + 'Webhook settings' => 'Webhook ayarları', + 'Reset token' => 'Anahtarı sıfırla', + 'API endpoint:' => 'API bitiÅŸ noktası:', + 'Refresh interval for personal 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)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Saniye olarak frekans (Bu özelliÄŸi iptal etmek için 0, varsayılan deÄŸer 10 saniye)', + 'Application URL' => 'Uygulama URL', + 'Token regenerated.' => 'Anahtar yeniden oluÅŸturuldu.', + 'Date format' => 'Tarih formatı', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO formatı her zaman kabul edilir, örneÄŸin: "%s" ve "%s"', + 'New personal project' => 'Yeni özel proje', + 'This project is personal' => 'Bu proje özel', + 'Add' => 'Ekle', + 'Start date' => 'BaÅŸlangıç tarihi', + 'Time estimated' => 'Tahmini süre', + 'There is nothing assigned to you.' => 'Size atanan hiçbir ÅŸey yok.', + 'My tasks' => 'Görevlerim', + 'Activity stream' => 'Güncel olay akışı', + 'Dashboard' => 'Pano', + 'Confirmation' => 'Onay', + 'Webhooks' => 'Web Kancaları', + 'API' => 'API', + 'Create a comment from an external provider' => 'Dış saÄŸlayıcı ile bir yorum oluÅŸtur', + 'Project management' => 'Proje yönetimi', + 'Columns' => 'Sütunlar', + 'Task' => 'Görev', + 'Percentage' => 'Yüzde', + 'Number of tasks' => 'Görev sayısı', + 'Task distribution' => 'Görev dağılımı', + 'Analytics' => 'Analiz', + 'Subtask' => 'Alt görev', + 'User repartition' => '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.', + 'Previous' => 'Önceki', + 'The id must be an integer' => 'ID bir tamsayı olmalı', + 'The project id must be an integer' => 'Proje numarası bir tam sayı olmalı', + 'The status must be an integer' => 'Durum bir tam sayı olmalı', + 'The subtask id is required' => 'Alt görev numarası gerekli', + 'The subtask id must be an integer' => 'Alt görev numarası bir tam sayı olmalı', + 'The task id is required' => 'Görev numarası gerekli', + 'The task id must be an integer' => 'Görev numarası bir tam sayı olmalı', + 'The user id must be an integer' => 'Kullanıcı numarası bir tam sayı olmalı', + 'This value is required' => 'Bu deÄŸer gerekli', + '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ı', + 'Daily project summary' => 'Günlük proje özeti', + 'Daily project summary export' => 'Günlük proje özetini dışa aktar', + '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', + 'Add a new swimlane' => 'Yeni bir Kulvar ekle', + 'Default swimlane' => 'Varsayılan Kulvar', + 'Do you really want to remove this swimlane: "%s"?' => 'Bu Kulvarı silmek istediÄŸinize emin misiniz?: "%s"?', + 'Inactive swimlanes' => 'Pasif Kulvarlar', + 'Remove a swimlane' => 'Kulvarı sil', + 'Swimlane modification for the project "%s"' => '"%s" Projesi için Kulvar deÄŸiÅŸikliÄŸi', + 'Swimlane removed successfully.' => 'Kulvar baÅŸarıyla kaldırıldı.', + 'Swimlanes' => 'Kulvarlar', + 'Swimlane updated successfully.' => 'Kulvar baÅŸarıyla güncellendi.', + 'Unable to remove this swimlane.' => 'Bu Kulvarı silmek mümkün deÄŸil.', + 'Unable to update this swimlane.' => 'Bu Kulvarı deÄŸiÅŸtirmek mümkün deÄŸil.', + 'Your swimlane has been created successfully.' => 'Kulvar baÅŸarıyla oluÅŸturuldu.', + 'Example: "Bug, Feature Request, Improvement"' => 'Örnek: "Sorun, Özellik talebi, İyileÅŸtirme"', + 'Default categories for new projects (Comma-separated)' => 'Yeni projeler için varsayılan kategoriler (Virgül ile ayrılmış)', + 'Integrations' => 'Entegrasyon', + 'Integration with third-party services' => 'Dış servislerle entegrasyon', + 'Subtask Id' => 'Alt görev No:', + 'Subtasks' => 'Alt görevler', + 'Subtasks Export' => 'Alt görevleri dışa aktar', + 'Task Title' => 'Görev BaÅŸlığı', + 'Untitled' => 'BaÅŸlıksız', + 'Application default' => 'Uygulama varsayılanları', + 'Language:' => 'Dil:', + 'Timezone:' => 'Saat dilimi:', + 'All columns' => 'Tüm sütunlar', + 'Next' => 'Sonraki', + '#%d' => '#%d', + 'All swimlanes' => 'Tüm Kulvarlar', + 'All colors' => 'Tüm Renkler', + 'Moved to column %s' => '%s Sütununa taşındı', + 'User dashboard' => 'Kullanıcı Anasayfası', + '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 süre tablosu', + 'There is nothing to show.' => 'Gösterilecek hiçbir ÅŸey yok.', + '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' => 'BitiÅŸ', + 'Task age in days' => 'Görev yaşı gün olarak', + 'Days in this column' => 'Bu sütunda geçirilen gün', + '%dd' => '%dG', + 'Add a new link' => 'Yeni link ekle', + 'Do you really want to remove this link: "%s"?' => '"%s" linkini gerçekten silmek istiyor musunuz?', + 'Do you really want to remove this link with task #%d?' => '#%d numaralı görev ile linki gerçekten silmek istiyor musunuz?', + 'Field required' => 'Bu alan gerekli', + 'Link added successfully.' => 'Link baÅŸarıyla eklendi.', + 'Link updated successfully.' => 'Link baÅŸarıyla güncellendi.', + 'Link removed successfully.' => 'Link baÅŸarıyla silindi.', + 'Link labels' => 'Link etiketleri', + 'Link modification' => 'Link deÄŸiÅŸtirme', + 'Opposite label' => 'Zıt etiket', + 'Remove a link' => 'Bir link silmek', + '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ı', + 'Unable to create your link.' => 'Link oluÅŸturulamadı.', + 'Unable to update your link.' => 'Link güncellenemiyor.', + 'Unable to remove this link.' => 'Link kaldırılamıyor', + 'relates to' => 'ÅŸununla ilgili', + 'blocks' => 'ÅŸunu engelliyor', + 'is blocked by' => 'ÅŸunun tarafından engelleniyor', + 'duplicates' => 'ÅŸunun kopyasını oluÅŸturuyor', + 'is duplicated by' => 'ÅŸunun tarafından kopyası oluÅŸturuluyor', + 'is a child of' => 'ÅŸunun astı', + 'is a parent of' => 'ÅŸunun üstü', + 'targets milestone' => 'ÅŸu kilometre taşını hedefliyor', + 'is a milestone of' => 'ÅŸunun için bir kilometre taşı', + 'fixes' => 'düzeltiyor', + 'is fixed by' => 'ÅŸunun tarafından düzeltildi', + 'This task' => 'Bu görev', + '<1h' => '<1s', + '%dh' => '%ds', + 'Expand tasks' => 'Görevleri geniÅŸlet', + 'Collapse tasks' => 'Görevleri daralt', + 'Expand/collapse tasks' => 'Görevleri geniÅŸlet/daralt', + 'Close dialog box' => 'İletiyi kapat', + 'Submit a form' => 'Formu gönder', + 'Board view' => 'Pano görünümü', + 'Keyboard shortcuts' => 'Klavye kısayolları', + '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', + 'Currency' => 'Para birimi', + 'Personal project' => 'Özel proje', + 'AUD - Australian Dollar' => 'AUD - Avustralya Doları', + 'CAD - Canadian Dollar' => 'CAD - Kanada Doları', + 'CHF - Swiss Francs' => 'CHF - İsviçre Frangı', + 'Custom Stylesheet' => 'Özel Sitil Css', + 'EUR - Euro' => 'EUR - Euro', + '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ı', + 'PEN - Peruvian Sol' => 'PEN - Peru Solü', + 'RSD - Serbian dinar' => 'Sırp Dinarı', + 'CNY - Chinese Yuan' => 'CNY - Çin Yuanı', + 'USD - US Dollar' => 'USD Amerikan Doları', + 'VES - Venezuelan Bolívar' => 'VES - Venezuela Bolivarı', + '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 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', + 'Reference currency' => 'Referans kur', + 'The currency rate has been added successfully.' => 'Kur baÅŸarıyla eklendi', + 'Unable to add this currency rate.' => 'Bu kur eklenemedi', + 'Webhook URL' => 'Web kancası URL', + '%s removed the assignee of the task %s' => '%s, %s görevinin atanan bilgisini kaldırdı', + 'Information' => 'Bilgi', + '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' => 'Ç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' => '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 - İsveç Kronu', + 'Identifier' => 'Kimlik', + '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', + 'The exact same link already exists' => 'Birebir aynı link zaten var', + 'Recurrent task is scheduled to be generated' => 'Tekrarlanan görev oluÅŸturulması zamanlandı', + 'Score' => 'Skor', + 'The identifier must be unique' => 'Kimlik özgün olmalı', + 'This linked task id doesn\'t exists' => 'Link oluÅŸturulan görec kodu mevcut deÄŸil', + 'This value must be alphanumeric' => 'Bu deÄŸer alfanumerik olmalı', + 'Edit recurrence' => 'Tekrarlanmayı düzenle', + 'Generate recurrent task' => 'Tekrarlanan görev oluÅŸtur', + 'Trigger to generate recurrent task' => 'Tekrarlanan görev oluÅŸturmak için tetikleyici', + 'Factor to calculate new due date' => 'Yeni tamamlanma tarihi için hesaplama faktörü', + 'Timeframe to calculate new due date' => 'Yeni tamamlanma tarihinin hesaplanması için zaman dilimi', + 'Base date to calculate new due date' => 'Yeni tamamlanma tarihinin hesaplanması için baz alınacak tarih', + 'Action date' => 'Hareket tarihi', + 'Base date to calculate new due date: ' => 'Yeni tamamlanma tarihinin hesaplanması için baz alınacak tarih', + 'This task has created this child task: ' => 'Bu görev ÅŸu alt görevi oluÅŸturdu:', + 'Day(s)' => 'Gün(ler)', + 'Existing due date' => 'Mevcut tamamlanma tarihi', + 'Factor to calculate new due date: ' => 'Yeni tamamlanma tarihi için hesaplama faktörü', + 'Month(s)' => 'Ay(lar)', + 'This task has been created by: ' => 'Bu görev ÅŸunun tarafından oluÅŸturuldu:', + 'Recurrent task has been generated:' => 'Tekrarlanan görev oluÅŸturuldu:', + 'Timeframe to calculate new due date: ' => 'Yeni tamamlanma tarihinin hesaplanması için zaman dilimi', + 'Trigger to generate recurrent task: ' => 'Tekrarlanan görev oluÅŸturmak için tetikleyici', + 'When task is closed' => 'Görev kapatıldığı zaman', + 'When task is moved from first column' => 'Görev ilk sütundan taşındığı zaman', + 'When task is moved to last column' => 'Görev son sütuna taşındığı zaman', + 'Year(s)' => 'Yıl(lar)', + 'Project settings' => 'Proje ayarları', + 'Automatically update the start date' => 'BaÅŸlangıç tarihini otomatik olarak güncelle', + 'iCal feed' => 'iCal akışı', + 'Preferences' => 'Ayarlar', + 'Security' => 'Güvenlik', + '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 personal projects.' => 'Özel projeler için kullanıcı yönetimi yoktur.', + 'User that will receive the email' => 'Email alacak kullanıcı', + 'Email subject' => 'Email baÅŸlığı', + 'Date' => 'Tarih', + 'Add a comment log when moving the task between columns' => 'Görevi sütunlar arasında taşırken yorum kaydı ekle', + '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ç', + 'Notification' => 'Uyarılar', + '%s moved the task #%d to the first swimlane' => '%s, #%d görevini birinci kulvara taşıdı', + 'Swimlane' => 'Kulvar', + '%s moved the task %s to the first swimlane' => '%s, %s görevini ilk kulvara taşıdı', + '%s moved the task %s to the swimlane "%s"' => '%s, %s görevini "%s" kulvarına taşıdı', + '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' => 'Pano yu Kanboard\'da görüntüle', + 'The task has been moved to the first swimlane' => 'Görev birinci kulvara taşındı', + 'The task has been moved to another swimlane:' => 'Görev baÅŸka bir kulvara taşındı:', + 'New title: %s' => 'Yeni baÅŸlık: %s', + 'The task is not assigned anymore' => 'Görev artık atanmamış', + 'New assignee: %s' => 'Yeni atanan: %s', + 'There is no category now' => 'Åžu anda kategori yok', + 'New category: %s' => 'Yeni kategori: %s', + 'New color: %s' => 'Yeni renk: %s', + 'New complexity: %d' => 'Yeni zorluk seviyesi: %d', + 'The due date has been removed' => 'Tamamlanma tarihi silindi', + 'There is no description anymore' => 'Artık açıklama yok', + 'Recurrence settings has been modified' => 'Tekrarlanma ayarları deÄŸiÅŸtirildi', + '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" has been updated' => '"%s" hanesi deÄŸiÅŸtirildi', + 'The description has been modified:' => 'Açıklama deÄŸiÅŸtirildi', + 'Do you really want to close the task "%s" as well as all subtasks?' => '"%s" görevini ve tüm alt görevlerini kapatmak istediÄŸinize emin misiniz?', + 'I want to receive notifications for:' => 'Bununla ilgili bildirimler almak istiyorum:', + 'All tasks' => 'Tüm görevler', + 'Only for tasks assigned to me' => 'Yalnızca bana atanmış görevler için', + 'Only for tasks created by me' => 'Yalnızca benim oluÅŸturduÄŸum görevler için', + 'Only for tasks created by me and tasks assigned to me' => 'Yalnızca benim oluÅŸturduÄŸum ve bana atanmış görevler için', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => 'Tüm sütunlar için toplam', + 'You need at least 2 days of data to show the chart.' => 'GrafiÄŸi göstermek için en az iki günlük veriye ihtiyaç var.', + '<15m' => '<15dk', + '<30m' => '<30dk', + 'Stop timer' => 'Zamanlayıcıyı durdur', + 'Start timer' => 'Zamanlayıcıyı baÅŸlat', + 'My activity stream' => 'Olay akışım', + 'Search tasks' => 'Görevleri ara', + 'Reset filters' => 'Filtreleri sıfırla', + 'My tasks due tomorrow' => 'Yarına tamamlanması gereken görevlerim', + 'Tasks due today' => 'Bugün tamamlanması gereken görevler', + 'Tasks due tomorrow' => 'Yarına tamamlanması gereken görevler', + 'Tasks due yesterday' => 'Dün tamamlanmış olması gereken görevler', + 'Closed tasks' => 'Kapatılmış görevler', + 'Open tasks' => 'Açık görevler', + 'Not assigned' => 'Atanmamış', + 'View advanced search syntax' => 'GeliÅŸmiÅŸ arama kodlarını göster', + '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 list view' => 'Liste görünümüne geç', + 'Go to the search/filter box' => 'Arama/Filtreleme kutusuna git', + 'There is no activity yet.' => 'Henüz bir aktivite yok.', + 'No tasks found.' => 'Hiç görev bulunamadı.', + 'Keyboard shortcut: "%s"' => 'Klavye kısayolu: "%s"', + 'List' => 'Liste', + 'Filter' => 'Filtre', + 'Advanced search' => 'GeliÅŸmiÅŸ arama', + 'Example of query: ' => 'Sorgu örneÄŸi', + 'Search by project: ' => 'Projeye göre ara', + 'Search by column: ' => 'Sütuna göre ara', + 'Search by assignee: ' => 'Atanana göre ara', + 'Search by color: ' => 'Renge göre ara', + 'Search by category: ' => 'Kategoriye göre ara', + 'Search by description: ' => 'Açıklamaya göre ara', + 'Search by due date: ' => 'Tamamlanma tarihine göre ara', + 'Average time spent in each column' => 'Her bir sütunda geçirilen ortalama süre', + 'Average time spent' => 'Geçen ortalama süre', + 'This chart shows the average time spent in 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', + 'Cycle Time' => 'Çevrim süresi', + 'Lead Time' => 'Teslim süresi', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Bu grafik son %d görev için zaman içinde gerçekleÅŸen ortalama teslim ve çevrim sürelerini gösterir.', + 'Average time into each column' => 'Her bir sütunda ortalama zaman', + 'Lead and cycle time' => 'Teslim ve çevrim süresi', + 'Lead time: ' => 'Teslim süresi: ', + 'Cycle time: ' => 'Çevrim süresi: ', + 'Time spent in 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.', + 'Set the start date automatically' => 'BaÅŸlangıç tarihini otomatik olarak belirle', + 'Edit Authentication' => 'DoÄŸrulamayı düzenle', + '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.', + '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.', + 'Trigger automatically subtask time tracking' => 'Altgörev zamanlayıcıyı otomatik olarak baÅŸlat', + 'Include closed tasks in the cumulative flow diagram' => 'Kümülatif akış diyagramında kapatılmış görevleri de dahil et', + 'Current swimlane: %s' => 'Geçerli kulvar: %s', + 'Current column: %s' => 'Geçerli sütun: %s', + 'Current category: %s' => 'Geçerli kategori: %s', + 'no category' => 'kategori yok', + 'Current assignee: %s' => 'Geçerli atanan: %s', + 'not assigned' => 'atanmamış', + 'Author:' => 'Yazar', + 'contributors' => 'Katkı saÄŸlayanlar', + 'License:' => 'Lisans:', + 'License' => 'Lisans', + 'Enter the text below' => 'AÅŸağıdaki metni girin', + 'Start date:' => 'BaÅŸlangıç tarihi:', + 'Due date:' => 'Tamamlanması gereken tarih:', + '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 - Norveç Kronu', + 'Show this column' => 'Bu sütunu göster', + 'Hide this column' => 'Bu sütunu gizle', + 'End date' => 'BitiÅŸ tarihi', + 'Users overview' => 'Kullanıcılara genel bakış', + 'Members' => 'Üyeler', + 'Shared project' => 'Paylaşılan proje', + 'Project managers' => 'Proje müdürleri', + 'Projects list' => 'Proje listesi', + 'End date:' => 'BitiÅŸ tarihi:', + 'Change task color when using a specific task link' => 'Belirli bir görev baÄŸlantısı kullanıldığında görevin rengini deÄŸiÅŸtir', + 'Task link creation or modification' => 'Görev baÄŸlantısı oluÅŸturulması veya deÄŸiÅŸtirilmesi', + 'Milestone' => 'Kilometre taşı', + 'Reset the search/filter box' => 'Arama/Filtre kutusunu sıfırla', + 'Documentation' => 'Dokümantasyon', + 'Author' => 'Yazar', + 'Version' => 'Versiyon', + 'Plugins' => 'Eklentiler', + 'There is no plugin loaded.' => 'YüklenmiÅŸ bir eklendi yok', + 'My notifications' => 'Bildirimlerim', + 'Custom filters' => 'Özel filtreler', + 'Your custom filter has been created successfully.' => 'Özel filtreleriniz baÅŸarıyla oluÅŸturuldu.', + 'Unable to create your custom filter.' => 'Özel filtreniz oluÅŸturulamadı.', + 'Custom filter removed successfully.' => 'Özel filtreniz baÅŸarıyla silindi.', + 'Unable to remove this custom filter.' => 'Bu özel filtre silinemiyor.', + 'Edit custom filter' => 'Özel filtreyi düzenle', + 'Your custom filter has been updated successfully.' => 'Özel filtreleriniz baÅŸarıyla güncellendi.', + 'Unable to update custom filter.' => 'Özel filtre güncellenemiyor.', + 'Web' => 'İnternet', + 'New attachment on task #%d: %s' => '#%d görevinde yeni dosya: %s', + 'New comment on task #%d' => '#%d görevinde yeni yorum', + 'Comment updated on task #%d' => '#%d görevinde yorum güncellendi', + 'New subtask on task #%d' => '#%d görevinde yeni alt görev', + 'Subtask updated on task #%d' => '#%d görevinde alt görev güncellendi', + 'New task #%d: %s' => 'Yeni #%d görevi: %s', + 'Task updated #%d' => '#%d görevi güncellendi', + 'Task #%d closed' => '#%d görevi kapatıldı', + 'Task #%d opened' => '#%d görevi oluÅŸturuldu', + 'Column changed for task #%d' => '#%d görevinin sütunu deÄŸiÅŸti', + 'New position for task #%d' => '#%d görevinin konumu deÄŸiÅŸti', + 'Swimlane changed for task #%d' => '#%d görevinin kulvarı deÄŸiÅŸti', + 'Assignee changed on task #%d' => '#%d görevine atanan deÄŸiÅŸti', + '%d overdue tasks' => '%d gecikmiÅŸ görev', + 'No notification.' => 'Yeni bildirim yok.', + 'Mark all as read' => 'Tümünü okunmuÅŸ olarak iÅŸaretle', + 'Mark as read' => 'OkunmuÅŸ olarak iÅŸaretle', + 'Total number of tasks in this column across all swimlanes' => 'Bu sutündaki görev sayısının tüm kulvarlardaki toplamı', + 'Collapse swimlane' => 'Kulvarı daralt', + 'Expand swimlane' => 'Kulvarı geniÅŸlet', + 'Add a new filter' => 'Yeni bir filtre ekle', + 'Share with all project members' => 'Tüm proje üyeleriyle paylaÅŸ', + 'Shared' => 'Paylaşılan', + 'Owner' => 'Sahibi', + 'Unread notifications' => 'Okunmamış bildirimler', + 'Notification methods:' => 'Bildirim yöntemleri:', + '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 has been imported!' => 'Hiçbir ÅŸey içeri aktarılamadı!', + 'Import users from CSV file' => 'CSV dosyasından kullanıcıları içeri aktar', + '%d user(s) have been imported successfully.' => '%d kullanıcı baÅŸarıyla içeri aktarıldı.', + 'Comma' => 'Virgül', + 'Semi-colon' => 'Noktalı virgül', + 'Tab' => 'Tab', + 'Vertical bar' => 'Dikey çizgi', + 'Double Quote' => 'Tırnak iÅŸareti', + 'Single Quote' => 'Kesme iÅŸareti', + '%s attached a file to the task #%d' => '%s, #%d görevine bir dosya ekledi.', + 'There is no column or swimlane activated in your project!' => 'Projenizde etkinleÅŸtirilmiÅŸ hiç bir sütun veya kulvar yok!', + 'Append filter (instead of replacement)' => 'Fıltreye ekle (üzerine yazmak yerine)', + 'Append/Replace' => 'Ekle/Üzerine yaz', + 'Append' => 'Ekle', + 'Replace' => 'Üzerine yaz', + 'Import' => 'İçeri aktar', + 'Change sorting' => 'sıralamayı deÄŸiÅŸtir', + 'Tasks Importation' => 'Görevleri içeri aktar', + 'Delimiter' => 'Ayırıcı', + 'Enclosure' => 'Enclosure', + 'CSV File' => 'CSV Dosyası', + 'Instructions' => 'Yönergeler', + 'Your file must use the predefined CSV format' => 'Dosyanız önceden belirlenmiÅŸ CSV formatını kullanmalı', + 'Your file must be encoded in UTF-8' => 'Dosyanız UTF-8 kodlamasında olmalı', + 'The first row must be the header' => 'İlk satır baÅŸlık olmalı', + 'Duplicates are not verified for you' => 'Çift giriÅŸler sizin için onaylanmamış', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Tamamlanma tarihi ISO formatını kullanmalı: YYYY-MM-DD', + 'Download CSV template' => 'CSV taslağını indir', + 'No external integration registered.' => 'Hiç dış entegrasyon kaydedilmemiÅŸ.', + 'Duplicates are not imported' => 'Çift giriÅŸler içeri aktarılmaz', + 'Usernames must be lowercase and unique' => 'Kullanıcı adları küçük harf ve tekil olmalı', + 'Passwords will be encrypted if present' => 'Åžifreler (eÄŸer varsa) kriptolanır', + '%s attached a new file to the task %s' => '%s, %s görevine yeni dosya ekledi', + 'Link type' => 'BaÄŸlantı türü', + 'Assign automatically a category based on a link' => 'Bir baÄŸlantıya göre otomatik olarak kategori ata', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Atanan kullanıcı adı', + 'Assignee Name' => 'Atanan İsmi', + 'Groups' => 'Gruplar', + 'Members of %s' => '%s in üyeleri', + 'New group' => 'Yeni grup', + 'Group created successfully.' => 'Grup baÅŸarıyla oluÅŸturuldu.', + 'Unable to create your group.' => 'Grup oluÅŸturulamadı.', + 'Edit group' => 'Grubu düzenle', + 'Group updated successfully.' => 'Grup baÅŸarıyla güncellendi.', + 'Unable to update your group.' => 'Grup güncellenemedi.', + 'Add group member to "%s"' => '"%s" e grup üyesi ekle', + 'Group member added successfully.' => 'Grup üyesi baÅŸarıyla eklendi.', + 'Unable to add group member.' => 'Grup üyesi eklenemedi.', + 'Remove user from group "%s"' => '"%s" grubundan kullanıcı çıkar', + 'User removed successfully from this group.' => 'Kullanıcı bu gruptan baÅŸarıyla çıkarıldı.', + 'Unable to remove this user from the group.' => 'Bu kullanıcı bu grubtan çıkarılamadı', + 'Remove group' => 'Grubu sil', + 'Group removed successfully.' => 'Grup baÅŸarıyla silindi.', + 'Unable to remove this group.' => 'Grup silinemedi.', + 'Project Permissions' => 'Proje izimleri', + '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', + '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.', + 'Add group member' => 'Grup üyesi ekle', + 'Do you really want to remove this group: "%s"?' => '"%s" grubunu silmek istediÄŸinize emin misiniz?', + 'There is no user in this group.' => 'Bu grupta hiç kullanıcı yok.', + 'Permissions' => 'İzinler', + 'Allowed Users' => 'İzin verilen kullanıcı', + 'No specific user has been allowed.' => 'Hiç bir kullanıcıya özel olarak izin verilmemiÅŸ.', + 'Role' => 'Rol', + 'Enter user name...' => 'Kullanıcı adını girin...', + 'Allowed Groups' => 'İzinli gruplar', + 'No group has been allowed.' => 'Hiç bir gruba özel olarak izin verilmemiÅŸ', + 'Group' => 'Grup', + 'Group Name' => 'Grup adı', + 'Enter group name...' => 'Grup adını girin...', + 'Role:' => 'Rol:', + 'Project members' => 'Proje üyeleri', + '%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', + 'Estimated hours: ' => 'Tahmini saat:', + 'Actual hours: ' => 'GerçekleÅŸen saat:', + 'Hours Spent' => 'Harcanan saat', + 'Hours Estimated' => 'Tahmini saat', + '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 - 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', + '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?', + 'Enable "Forget Password"' => '"Åžifremi unuttum" komutunu etkinleÅŸtir', + 'Password Reset' => 'Åžifre sıfırlama', + 'New password' => 'Yeni ÅŸifre', + 'Change Password' => 'Åžifreyi deÄŸiÅŸtir', + 'To reset your password click on this link:' => 'Åžifrenizi sıfırlamak için bu linke tıklayın:', + 'Last Password Reset' => 'Son ÅŸifre sıfırlama', + 'The password has never been reinitialized.' => 'Åžifre hiç bir zaman tekrar baÅŸlatılmamış.', + 'Creation' => 'OluÅŸturulma', + 'Expiration' => 'Sona erme', + 'Password reset history' => 'Åžifre sıfırlama geçmiÅŸi', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '"%s" sütunu ve "%s" kulvarındaki tüm görevler baÅŸarıyla kapatıldı.', + '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 in this column and this swimlane' => '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.' => 'Proje bildirimleri için hiçbir plugin kaydedilmedi. Yine de profil sayfanızdan bildirim ayarları yapabilirsiniz.', + 'My dashboard' => 'Panom', + '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', + 'Personal 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', + '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', + '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 projeyi örnek alma', + '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 personal 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: https://example.kanboard.org/ (used to generate absolute URLs)' => 'ÖrneÄŸin: https://ornek.kanboard.org/ (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', + 'Avatar' => 'Simge', + '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ÅŸ Eklentiler', + 'Plugin Directory' => 'Eklenti Klasörü', + 'Plugin installed successfully.' => 'Eklenti baÅŸarıyla kuruldu.', + 'Plugin updated successfully.' => 'Eklenti baÅŸarıyla güncellendi.', + 'Plugin removed successfully.' => 'Eklenti 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.' => 'Eklenti 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.' => 'Eklenti dosyası indirilemedi.', + 'Unable to write temporary file for plugin.' => 'Eklenti için geçici dosya oluÅŸturulamadı.', + 'Unable to open plugin archive.' => 'Eklenti dosyası açılamadı.', + 'There is no file in the plugin archive.' => 'Eklenti (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 eklenti kurulacak ÅŸekilde ayarlanmamış.', + 'There is no plugin available.' => 'Uygun eklenti yok.', + 'Install' => 'Kur', + 'Update' => 'Güncelle', + 'Up to date' => 'Güncel', + 'Not available' => 'Uygun deÄŸil', + 'Remove plugin' => 'Eklentiyi kaldır', + 'Do you really want to remove this plugin: "%s"?' => '"%s" eklentiyi 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' => 'Web kancası anahtarı', + '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 a 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 has 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ış', + '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', + 'No personal API access token registered.' => 'KiÅŸisel API eriÅŸim anahtarınız kaydedilmedi.', + 'Your personal API access token is "%s"' => 'KiÅŸisel API eriÅŸim anahtarınız "%s"', + 'Remove your token' => 'Anahtarı kaldır', + 'Generate a new token' => 'Yeni bir anahtar oluÅŸtur', + 'Showing %d-%d of %d' => 'Gösterilen %d-%d / %d', + 'Outgoing Emails' => 'Gönderilen ePostalar', + 'Add or change currency rate' => 'Kur Oranını ekle veya deÄŸiÅŸtir', + 'Reference currency: %s' => 'Referans parabirimi: %s', + 'Add custom filters' => 'Özel filtre ekle', + 'Export' => 'Dışa aktar', + 'Add link label' => 'BaÄŸlantı etiketi ekle', + 'Incompatible Plugins' => 'Uyumsuz Eklentiler', + 'Compatibility' => 'Uyumluluk', + 'Permissions and ownership' => 'İzinler ve sahiplik', + 'Priorities' => 'Öncelikler', + 'Close this window' => 'Bu pencereyi kapat', + 'Unable to upload this file.' => 'Bu dosya yüklenemiyor', + 'Import tasks' => 'Görevleri içeri aktar', + 'Choose a project' => 'Proje seçin', + 'Profile' => 'Profil', + 'Application role' => 'Uygulama rolü', + '%d invitations were sent.' => '%d Davetiye gönderildi', + '%d invitation was sent.' => '%d Davetiye gönderildi', + 'Unable to create this user.' => 'Bu kullanıcı oluÅŸturulamadı.', + 'Kanboard Invitation' => 'Kanboard Daveti', + 'Visible on dashboard' => 'Panoda görünür', + 'Created at:' => 'OluÅŸturulma: ', + 'Updated at:' => 'Güncellenme: ', + 'There is no custom filter.' => 'Uygulanmış özel filtre yok', + 'New User' => 'Yeni Kullanıcı', + 'Authentication' => 'Yetkilendirme', + 'If checked, this user will use a third-party system for authentication.' => 'İşaretlenirse, bu kullanıcı kimlik doÄŸrulama için üçüncü parti bir sistem kullanacaktır.', + 'The password is necessary only for local users.' => 'Parola yalnızca yerel kullanıcılar için gereklidir', + 'You have been invited to register on Kanboard.' => 'Kanboarda kayıt yaptırmak için davet edildiniz.', + 'Click here to join your team' => 'Takımınıza katılmak için buraya tıklayın', + 'Invite people' => 'İnsanları davet et', + 'Emails' => 'ePostalar', + 'Enter one email address by line.' => 'Her satıra bir adet email girin.', + 'Add these people to this project' => 'KiÅŸileri bu projeye ekle', + 'Add this person to this project' => 'KiÅŸiyi bu projeye ekle', + 'Sign-up' => 'Kayıt', + 'Credentials' => 'Kimlik bilgileri', + 'New user' => 'Yeni kullanıcı', + 'This username is already taken' => 'Bu kullanıcı adı zaten alınmış', + 'Your profile must have a valid email address.' => 'Profiliniz için geçerli bir ePosta adresi gerekiyor.', + 'TRL - Turkish Lira' => 'TRL - Türk Lirası', + 'The project email is optional and could be used by several plugins.' => 'Proje ePostası isteÄŸe baÄŸlıdır ve eklentiler tarafından kullanılabilir', + 'The project email must be unique across all projects' => 'Proje e-postası tüm projelerde benzersiz olmalıdır.', + 'The email configuration has been disabled by the administrator.' => 'ePosta ayarları yönetici tarafından devre dışı bırakılmış.', + 'Close this project' => 'Bu projeyi kapat', + 'Open this project' => 'Bu projeyi aç', + 'Close a project' => 'Proje kapat', + 'Do you really want to close this project: "%s"?' => 'Bu projeyi kapatmak istediÄŸinize emin misiniz? : "%s"', + 'Reopen a project' => 'Projeyi tekrar aç', + 'Do you really want to reopen this project: "%s"?' => 'Bu projeyi tekrar açmak istediÄŸinizi onaylıyor musunuz? : "%s"', + 'This project is open' => 'Bu proje açıktır', + 'This project is closed' => 'Bu proje kapatılmış', + 'Unable to upload files, check the permissions of your data folder.' => 'Dosya yüklenemedi. Lütfen yükleme dizine yazma izninizi kontrol ediniz.', + 'Another category with the same name exists in this project' => 'Bu projede aynı ada sahip baÅŸka bir kategori var', + 'Comment sent by email successfully.' => 'Yorum ePosta ile baÅŸarıyla gönderildi.', + 'Sent by email to "%s" (%s)' => 'ePostayı gönder "%s" (%s)', + 'Unable to read uploaded file.' => 'Yüklenen dosya okunamadı.', + 'Database uploaded successfully.' => 'Veritabanı baÅŸarıyla yüklendi.', + 'Task sent by email successfully.' => 'Görev ePosta ile baÅŸarıyla gönderildi.', + 'There is no category in this project.' => 'Bu projenin kategorisi yok', + 'Send by email' => 'ePosta ile gönder', + 'Create and send a comment by email' => 'Yorum oluÅŸtur ve ePosta ile gönder', + 'Subject' => 'Konu', + 'Upload the database' => 'Veritabanına yükle', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Daha önce indirdiÄŸiniz Sqlite Veritabanı dosyasını yükleyebilirsiniz (Gzip formatında)', + 'Database file' => 'Veritabanı dosyası', + 'Upload' => 'Yükle', + 'Your project must have at least one active swimlane.' => 'Projenizin en az bir aktif kulvara sahip olması gerekir', + 'Project: %s' => 'Proje: %s', + 'Automatic action not found: "%s"' => 'Otomatik iÅŸlem bulunamadı: "%s"', + '%d projects' => '%d proje', + '%d project' => '%d proje', + 'There is no project.' => 'Proje yok.', + 'Sort' => 'Sırala', + 'Project ID' => 'Proje No', + 'Project name' => 'Proje Adı', + 'Public' => 'Herkese açık', + 'Personal' => 'Gizli', + '%d tasks' => '%d görev', + '%d task' => '%d görev', + 'Task ID' => 'Görev No', + 'Assign automatically a color when due date is expired' => 'Son günü geçtiÄŸinde otomatik olarak bir renk ata', + 'Total score in this column across all swimlanes' => 'Tüm kulvarlara göre bu kulvarın toplam puanı', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Arjantin Pezosu', + 'COP - Colombian Peso' => 'COP - Kolombiya Pezosu', + '%d groups' => '%d grup', + '%d group' => '%d grup', + 'Group ID' => 'Grup No', + 'External ID' => 'Harici No', + '%d users' => '%d kullanıcı', + '%d user' => '%d kullanıcı', + 'Hide subtasks' => 'Alt görevleri gizler', + 'Show subtasks' => 'Alt görevleri göster', + 'Authentication Parameters' => 'Yetkilendirme Parametreleri', + 'API Access' => 'API EriÅŸimi', + 'No users found.' => 'Kullanıcı yok', + 'User ID' => 'Kullanıcı No', + 'Notifications are activated' => 'Bildirimler etkinleÅŸtirildi', + 'Notifications are disabled' => 'Bildirimler devre dışı', + 'User disabled' => 'Kullanıcı devre dışı', + '%d notifications' => '%d bildirim', + '%d notification' => '%d bildirim', + 'There is no external integration installed.' => 'YüklenmiÅŸ harici entegrasyon yok.', + 'You are not allowed to update tasks assigned to someone else.' => 'BaÅŸkasına atanmış görevi güncelleme izniniz yok.', + 'You are not allowed to change the assignee.' => 'Atanmış kiÅŸiyi deÄŸiÅŸtirme yetkiniz yok.', + 'Task suppression is not permitted' => 'Görev silmeye izniniz yok', + 'Changing assignee is not permitted' => 'Atanmışı deÄŸiÅŸtirme izniniz yok', + 'Update only assigned tasks is permitted' => 'Yalnızca atandığınız görevleri güncelleyebilirsiniz', + 'Only for tasks assigned to the current user' => 'Yalnızca geçerli kullanıcıya atanan görevler için', + 'My projects' => 'Projelerim', + 'You are not a member of any project.' => 'Herhangi bir projenin üyesi deÄŸilsiniz.', + 'My subtasks' => 'Altgörevlerim', + '%d subtasks' => '%d alt görev', + '%d subtask' => '%d alt görev', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Geçerli kullanıcıya atanan görevler için yalnızca o sütunlar arasında hareket eden bir göreve izin verilir.', + '[DUPLICATE]' => '[ÇİFT]', + 'DKK - Danish Krona' => 'DKK - Danimarka Kronu', + 'Remove user from group' => 'Kullanıcıyı gruptan çıkar', + 'Assign the task to its creator' => 'OluÅŸturucusuna görev ata', + 'This task was sent by email to "%s" with subject "%s".' => 'Bu görev "%s" konusuyla "%s" ePosta adresine gönderildi.', + 'Predefined Email Subjects' => 'Öntanımlı ePosta konuları', + 'Write one subject by line.' => 'Her satıra bir konu yazınız', + 'Create another link' => 'BaÅŸka baÄŸlantı oluÅŸtur', + 'BRL - Brazilian Real' => 'BRL - Brezilya Reali', + 'Add a new Kanboard task' => 'Yeni bir Kanboard Görevi ekle', + 'Subtask not started' => 'Alt Görev baÅŸlatılmamış', + 'Subtask currently in progress' => 'Alt Görev iÅŸlemde', + 'Subtask completed' => 'Alt Görev tamamlandı', + 'Subtask added successfully.' => 'Alt Görev baÅŸarıyla eklendi.', + '%d subtasks added successfully.' => '%d Alt Görev baÅŸarıyla eklendi.', + 'Enter one subtask by line.' => 'Her satıra bir alt görev giriniz.', + 'Predefined Contents' => 'Ön tanımlı İçerik', + 'Predefined contents' => 'Ön tanımlı içerik', + 'Predefined Task Description' => 'Ön tanımlı Görev Açıklaması', + 'Do you really want to remove this template? "%s"' => 'Bu ÅŸablonu kaldırmayı gerçekten istiyor musunuz? "%s"', + 'Add predefined task description' => 'Ön tanımlı görev açıklaması ekle', + 'Predefined Task Descriptions' => 'Ön Tanımlı Görev Açıklamaları', + 'Template created successfully.' => 'Åžablon baÅŸarıyla oluÅŸturuldu.', + 'Unable to create this template.' => 'Åžablon oluÅŸturulamadı.', + 'Template updated successfully.' => 'Åžablon baÅŸarıyla güncellendi.', + 'Unable to update this template.' => 'Åžablon güncellenemedi.', + 'Template removed successfully.' => 'Åžablon baÅŸarıyla kaldırıldı.', + 'Unable to remove this template.' => 'Åžablon kaldırılamıyor.', + 'Template for the task description' => 'Görev Açıklaması için Åžablon', + 'The start date is greater than the end date' => 'BaÅŸlama Tarihi BitiÅŸ Tarihinden büyük', + 'Tags must be separated by a comma' => 'Etiketler virgül ile ayrılmalı', + 'Only the task title is required' => 'Sadece görev baÅŸlığı gereklidir', + 'Creator Username' => 'OluÅŸturan Kullanıcı', + 'Color Name' => 'Renk Adı', + 'Column Name' => 'Kolon Adı', + 'Swimlane Name' => 'Kulvar Adı', + 'Time Estimated' => 'Tahmini Zaman', + 'Time Spent' => 'Harcanan Zaman', + 'External Link' => 'Harici BaÄŸlantı', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Bu özellik iCal beslemesini, RSS beslemesini ve genel pano görünümünü aktifleÅŸtirir.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Görev baÅŸka bir kolona taşındığında, tüm alt görevlerin zaman sayaçlarını durdur.', + 'Subtask Title' => 'Alt Görev BaÅŸlığı', + 'Add a subtask and activate the timer when moving a task to another column' => 'Görev baÅŸka bir kolona taşındığında, bir alt görev ekle ve zaman sayacını baÅŸlat', + 'days' => 'gün', + 'minutes' => 'dakika', + 'seconds' => 'saniye', + 'Assign automatically a color when preset start date is reached' => 'BaÅŸlangıç Tarihi geldiÄŸinde, otomatik olarak bir renk ata', + 'Move the task to another column once a predefined start date is reached' => 'BaÅŸlangıç Tarihi geldiÄŸinde, görevi baÅŸka bir kolona taşı', + 'This task is now linked to the task %s with the relation "%s"' => 'Bu görev %s görevine baÄŸlandı, iliÅŸki %s', + 'The link with the relation "%s" to the task %s has been removed' => '%s görevindeki %s baÄŸlantısı kaldırıldı', + 'Custom Filter:' => 'Özel Filtre', + 'Unable to find this group.' => 'Bu grup bulunamıyor', + '%s moved the task #%d to the column "%s"' => '%s , #%d görevini "%s" kolonuna taşıdı', + '%s moved the task #%d to the position %d in the column "%s"' => '%s , #%d görevini "%s" kolonundaki %d pozisyonuna taşıdı', + '%s moved the task #%d to the swimlane "%s"' => '%s , #%d görevini "%s" kulvarına taşıdı', + '%sh spent' => '%sh harcandı', + '%sh estimated' => '%sh tahmin edildi', + 'Select All' => 'Tümünü Seç', + 'Unselect All' => 'Tüm Seçimi Kaldır', + 'Apply action' => 'Komutu Uygula', + 'Move selected tasks to another column or swimlane' => 'Seçili görevleri baÅŸka kolona taşı', + 'Edit tasks in bulk' => 'Görevleri toplu olarak güncelle', + 'Choose the properties that you would like to change for the selected tasks.' => 'Seçili görevler için deÄŸiÅŸtirilecek özellikleri seç', + 'Configure this project' => 'Proje Ayarları', + 'Start now' => 'Åžimdi baÅŸla', + '%s removed a file from the task #%d' => '%s %d görevinden bir dosya çıkardı', + 'Attachment removed from task #%d: %s' => '%s görevinden bir ek dosya çıkarıldı: %d', + 'No color' => 'Renksiz', + 'Attachment removed "%s"' => 'Ek dosya kaldırıldız"%s"', + '%s removed a file from the task %s' => '%s %s görevinden bir dosya çıkardı', + 'Move the task to another swimlane when assigned to a user' => 'Görev birine atandığında bir kulvara taşı', + 'Destination swimlane' => 'Hedef Kulvar', + 'Assign a category when the task is moved to a specific swimlane' => 'Görev bir kulvara taşındığında bir kategori ata', + 'Move the task to another swimlane when the category is changed' => 'Katgorisi deÄŸiÅŸtiÄŸinde görevi baÅŸka bir kulvara taşı', + 'Reorder this column by priority (ASC)' => 'Kolonu artan önceliÄŸe göre sırala', + 'Reorder this column by priority (DESC)' => 'Kolonu azalan önceliÄŸe göre sırala', + 'Reorder this column by assignee and priority (ASC)' => 'Kolonu atanan ve önceliÄŸe göre artan sırala', + 'Reorder this column by assignee and priority (DESC)' => 'Kolonu atanan ve önceliÄŸe göre azalan sırala', + 'Reorder this column by assignee (A-Z)' => 'Kolonu atanana göre artan sırala (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Kolonu atanana göre azalan sırala (Z-A)', + 'Reorder this column by due date (ASC)' => 'Kolonu hedef tarihe göre artan sırala', + 'Reorder this column by due date (DESC)' => 'Kolonu hedef tarihe göre azalan sırala', + 'Reorder this column by id (ASC)' => 'Kolonu id ye göre artan sırala', + 'Reorder this column by id (DESC)' => 'Kolonu id ye göre azalan sırala', + '%s moved the task #%d "%s" to the project "%s"' => '%s görevi taşıdı #%d "%s" "%s" projesine', + 'Task #%d "%s" has been moved to the project "%s"' => 'Görev #%d "%s" tarafından "%s" projesine taşındı', + 'Move the task to another column when the due date is less than a certain number of days' => 'Hedef tarihe belirli bir gün sayısından az kaldığında görevi baÅŸka kolona taşı', + 'Automatically update the start date when the task is moved away from a specific column' => 'Görev bir kolondan taşındığında baÅŸlangıç tarihini otomatik olarak güncelle', + 'HTTP Client:' => 'HTTP İstemci', + 'Assigned' => 'Atanmış', + 'Task limits apply to each swimlane individually' => 'Görev limitini her kulvara ayrı uygula', + 'Column task limits apply to each swimlane individually' => 'Kolon görev limitleri her kulvara ayrı uygulansın', + 'Column task limits are applied to each swimlane individually' => 'Kolon görev limitleri her kulvara ayrı uygulandı', + 'Column task limits are applied across swimlanes' => 'Kolon görev limitleri tüm kulvarlara uygulandı', + 'Task limit: ' => 'Görev limiti', + 'Change to global tag' => 'Global etikete deÄŸiÅŸtir', + 'Do you really want to make the tag "%s" global?' => '"%s" etiketini global yapmak istediÄŸinize emin misiniz?', + 'Enable global tags for this project' => 'Bu iÅŸ grubu için global etiketleri etkinleÅŸtir', + 'Group membership(s):' => 'Grup üyelikleri', + '%s is a member of the following group(s): %s' => '"%s" ÅŸu grupların üyesidir:"%s"', + '%d/%d group(s) shown' => '%d/%dgrupları göster', + 'Subtask creation or modification' => 'Alt görev oluÅŸturma veya deÄŸiÅŸtirme', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Görev belirli bir kulvara taşındığında belirli bir kullanıcıya ata', + 'Comment' => 'Yorum', + 'Collapse vertically' => 'Dikey olarak daralt', + 'Expand vertically' => 'Dikey olarak geniÅŸlet', + 'MXN - Mexican Peso' => 'MXN - Meksika Pesosu', + 'Estimated vs actual time per column' => 'Sütun başına tahmini ve gerçek zaman', + 'HUF - Hungarian Forint' => 'HUF - Macar Forinti', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Avatarınız olarak yüklemek için bir dosya seçmelisiniz!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'YüklediÄŸiniz dosya geçerli bir resim deÄŸil! (Yalnızca *.gif, *.jpg, *.jpeg ve *.png dosyalarına izin verilir!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Görev belirli bir sütundan taşındığında son tarihi otomatik olarak ayarla', + 'No other projects found.' => 'BaÅŸka proje bulunamadı.', + 'Tasks copied successfully.' => 'Görevler baÅŸarıyla kopyalandı.', + 'Unable to copy tasks.' => 'Görevler kopyalanamıyor.', + 'Theme' => 'Tema', + 'Theme:' => 'Tema:', + 'Light theme' => 'Açık tema', + 'Dark theme' => 'Koyu tema', + 'Automatic theme - Sync with system' => 'Otomatik tema - Sistemle senkronize et', + 'Application managers or more' => 'Uygulama yöneticileri veya daha fazlası', + 'Administrators' => 'Yöneticiler', + 'Visibility:' => 'Görünürlük:', + 'Standard users' => 'Standart kullanıcılar', + 'Visibility is required' => 'Görünürlük gereklidir', + 'The visibility should be an app role' => 'Görünürlük bir uygulama rolü olmalıdır', + 'Reply' => 'Yanıtla', + '%s wrote: ' => '%s yazdı: ', + 'Number of visible tasks in this column and swimlane' => 'Bu sütun ve yüzme yolunda görünen görev sayısı', + 'Number of tasks in this swimlane' => 'Bu yüzme yolundaki görev sayısı', + 'Unable to find another subtask in progress, you can close this window.' => 'Devam eden baÅŸka bir alt görev bulunamadı, bu pencereyi kapatabilirsiniz.', + 'This theme is invalid' => 'Bu tema geçersiz', + 'This role is invalid' => 'Bu rol geçersiz', + 'This timezone is invalid' => 'Bu saat dilimi geçersiz', + 'This language is invalid' => 'Bu dil geçersiz', + 'This URL is invalid' => 'Bu URL geçersiz', + 'Date format invalid' => 'Tarih biçimi geçersiz', + 'Time format invalid' => 'Saat biçimi geçersiz', + 'Invalid Mail transport' => 'Geçersiz posta aktarımı', + 'Color invalid' => 'Geçersiz renk', + 'This value must be greater or equal to %d' => 'Bu deÄŸer %d veya daha büyük olmalıdır', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Dosyanın başına BOM ekle (Microsoft Excel için gereklidir)', + 'Just add these tag(s)' => 'Sadece bu etiketleri ekle', + 'Remove internal link(s)' => 'Dahili baÄŸlantıları kaldır', + 'Import tasks from another project' => 'BaÅŸka bir projeden görevleri içe aktar', + 'Select the project to copy tasks from' => 'Görevleri kopyalamak için projeyi seçin', + 'The total maximum allowed attachments size is %sB.' => 'Ekler için izin verilen toplam maksimum boyut %sB.', + 'Add attachments' => 'Ekleri ekle', + 'Task #%d "%s" is overdue' => 'Görev #%d "%s" son tarihi geçti', + 'Enable notifications by default for all new users' => 'Tüm yeni kullanıcılar için bildirimleri varsayılan olarak etkinleÅŸtir', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Sorumlu elle ayarlanmadıysa, belirli sütunlar için görevi oluÅŸturan kiÅŸiye ata', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Hiç kullanıcı atanmadıysa, sütun deÄŸiÅŸikliÄŸinde görev belirtilen sütuna taşındığında görevi oturum açmış kullanıcıya ata', +]; diff --git a/app/Locale/uk_UA/translations.php b/app/Locale/uk_UA/translations.php new file mode 100644 index 0000000..c0c691e --- /dev/null +++ b/app/Locale/uk_UA/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'ВідÑутній', + 'Edit' => 'Редагувати', + 'Remove' => 'Видалити', + 'Yes' => 'Так', + 'No' => 'ÐÑ–', + 'cancel' => 'ÑкаÑувати', + 'or' => 'або', + 'Yellow' => 'Жовтий', + 'Blue' => 'Синій', + 'Green' => 'Зелений', + 'Purple' => 'Фіолетовий', + 'Red' => 'Червоний', + 'Orange' => 'Помаранчевий', + 'Grey' => 'Сірий', + 'Brown' => 'Коричневий', + 'Deep Orange' => 'Темно-помаранчевий', + 'Dark Grey' => 'Темно-Ñірий', + 'Pink' => 'Рожевий', + 'Teal' => 'Бірюзовий', + 'Cyan' => 'Синьо-зелений', + 'Lime' => 'Лайм', + 'Light Green' => 'Світло-зелений', + 'Amber' => 'Бурштиновий', + 'Save' => 'Зберегти', + 'Login' => 'Увійти', + 'Official website:' => 'Офіційний веб-Ñайт:', + 'Unassigned' => 'Ðепризначена', + 'View this task' => 'ПереглÑнути задачу', + 'Remove user' => 'Видалити кориÑтувача', + 'Do you really want to remove this user: "%s"?' => 'ДійÑно видалити кориÑтувача "%s"?', + 'All users' => 'Ð’ÑÑ– кориÑтувачі', + 'Username' => 'Логін', + 'Password' => 'Пароль', + 'Administrator' => 'ÐдмініÑтратор', + 'Sign in' => 'Увійти', + 'Users' => 'КориÑтувачі', + 'Forbidden' => 'Заборонено', + 'Access Forbidden' => 'Відмовлено у доÑтупі', + 'Edit user' => 'Редагувати кориÑтувача', + 'Logout' => 'Вийти', + 'Bad username or password' => 'Ðеправильний логін або пароль', + 'Edit project' => 'Редагувати проєкт', + 'Name' => 'Ім\'Ñ', + 'Projects' => 'Проєкти', + 'No project' => 'Проєкти відÑутні', + 'Project' => 'Проєкт', + 'Status' => 'СтатуÑ', + 'Tasks' => 'Задачі', + 'Board' => 'Дошка', + 'Actions' => 'Дії', + 'Inactive' => 'Ðеактивний', + 'Active' => 'Ðктивний', + 'Unable to update this board.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ дошку.', + 'Disable' => 'Заблокувати', + 'Enable' => 'Розблокувати', + 'New project' => 'Ðовий проєкт', + 'Do you really want to remove this project: "%s"?' => 'ДійÑно видалити проєкт "%s"?', + 'Remove project' => 'Видалити проєкт', + 'Edit the board for "%s"' => 'Редагувати дошку проєкту "%s"', + 'Add a new column' => 'Додати нову колонку', + 'Title' => 'Заголовок', + 'Assigned to %s' => 'Доручена %s', + 'Remove a column' => 'Видалити колонку', + 'Unable to remove this column.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ колонку.', + 'Do you really want to remove this column: "%s"?' => 'ДійÑно видалити колонку "%s"?', + 'Settings' => 'ÐалаштуваннÑ', + 'Application settings' => 'Параметри додатку', + 'Language' => 'Мова', + 'Webhook token:' => 'Ключ доÑтупу webhook:', + 'API token:' => 'Ключ доÑтупу API:', + 'Database size:' => 'Розмір бази даних:', + 'Download the database' => 'Завантажити базу даних', + 'Optimize the database' => 'Оптимізувати базу даних', + '(VACUUM command)' => '(Команда VACUUM)', + '(Gzip compressed Sqlite file)' => '(Файл SQLite ÑтиÑнутий Gzip)', + 'Close a task' => 'Закрити задачу', + 'Column' => 'Колонка', + 'Color' => 'Колір', + 'Assignee' => 'Доручено', + 'Create another task' => 'Створити ще задачу', + 'New task' => 'Ðова задача', + 'Open a task' => 'Відкрити задачу', + 'Do you really want to open this task: "%s"?' => 'ДійÑно відкрити "%s"?', + 'Back to the board' => 'Ðазад до дошки', + 'There is nobody assigned' => 'Ðе доручена нікому', + 'Column on the board:' => 'Колонка на дошці:', + 'Close this 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 Ñимволів', + 'The password is required' => 'Пароль Ñ” обов\'Ñзковим', + 'This value must be an integer' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¼Ð°Ñ” бути цілим чиÑлом', + 'The username must be unique' => 'Логін має бути унікальним', + 'The user id is required' => 'Ідентифікатор кориÑтувача Ñ” обов\'Ñзковим', + 'Passwords don\'t match' => 'Паролі не Ñпівпадають', + 'The confirmation is required' => 'Ðеобхідно підтвердити', + 'The project is required' => 'Слід вказати проєкт', + 'The id is required' => 'Слід вказати ідентифікатор', + 'The project id is required' => 'Слід вказати ідентифікатор проєкту', + 'The project name is required' => 'Слід вказати ім\'Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ', + 'The title is required' => 'Слід вказати заголовок', + 'Settings saved successfully.' => 'ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÑпішно збережено.', + 'Unable to save your settings.' => 'Ðе вдалоÑÑ Ð·Ð±ÐµÑ€ÐµÐ³Ñ‚Ð¸ налаштуваннÑ.', + 'Database optimization done.' => 'База даних оптимізована.', + 'Your project has been created successfully.' => 'Ваш проєкт уÑпішно Ñтворено.', + 'Unable to create your project.' => 'Ðе вдалоÑÑ Ñтворити проєкт.', + 'Project updated successfully.' => 'Проєкт уÑпішно оновлено.', + 'Unable to update this project.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ проєкт.', + 'Unable to remove this project.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ проєкт.', + 'Project removed successfully.' => 'Проєкт уÑпішно видалено.', + 'Project activated successfully.' => 'Проєкт уÑпішно активовано.', + 'Unable to activate this project.' => 'Ðе вдалоÑÑ Ð°ÐºÑ‚Ð¸Ð²ÑƒÐ²Ð°Ñ‚Ð¸ проєкт.', + 'Project disabled successfully.' => 'Проєкт уÑпішно деактивовано.', + 'Unable to disable this project.' => 'Ðе вдалоÑÑ Ð´ÐµÐ°ÐºÑ‚Ð¸Ð²ÑƒÐ²Ð°Ñ‚Ð¸ проєкт.', + 'Unable to open this task.' => 'Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ задачу.', + 'Task opened successfully.' => 'Задачу уÑпішно відкрито.', + 'Unable to close this task.' => 'Ðе вдалоÑÑ Ð·Ð°ÐºÑ€Ð¸Ñ‚Ð¸ задачу.', + 'Task closed successfully.' => 'Задачу уÑпішно закрито.', + 'Unable to update your task.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ задачу.', + 'Task updated successfully.' => 'Задачу уÑпішно оновлено.', + 'Unable to create your task.' => 'Ðе вдалоÑÑ Ñтворити задачу.', + 'Task created successfully.' => 'Задачу уÑпішно Ñтворено.', + 'User created successfully.' => 'КориÑтувача уÑпішно Ñтворено.', + 'Unable to create your user.' => 'Ðе вдалоÑÑ Ñтворити кориÑтувача.', + 'User updated successfully.' => 'КориÑтувача уÑпішно оновлено.', + 'User removed successfully.' => 'КориÑтувача уÑпішно видалено.', + 'Unable to remove this user.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ кориÑтувача.', + 'Board updated successfully.' => 'Дошку уÑпішно оновлено.', + 'Ready' => 'Відібрані', + 'Backlog' => 'Заплановані', + 'Work in progress' => 'Ð’ роботі', + 'Done' => 'Завершені', + 'Application version:' => 'ВерÑÑ–Ñ Ð´Ð¾Ð´Ð°Ñ‚ÐºÑƒ:', + 'Id' => 'ID', + 'Public link' => 'Публічне поÑиланнÑ', + 'Timezone' => 'ЧаÑовий поÑÑ', + 'Sorry, I didn\'t find this information in my database!' => 'Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ цю інформацію в базі даних :(', + 'Page not found' => 'Сторінку не знайдено', + 'Complexity' => 'СкладніÑть', + 'Task limit' => 'Ліміт задач', + 'Task count' => 'ЧиÑло задач', + 'User' => 'КориÑтувач', + 'Comments' => 'Коментарі', + 'Comment is required' => 'ТекÑÑ‚ ÐºÐ¾Ð¼ÐµÐ½Ñ‚Ð°Ñ€Ñ Ñ” обов\'Ñзковим', + 'Comment added successfully.' => 'Коментар уÑпішно додано.', + 'Unable to create your comment.' => 'Ðе вдалоÑÑ Ñтворити коментар.', + 'Due Date' => 'Термін виконаннÑ', + 'Invalid date' => 'Ðеправильна дата', + 'Automatic actions' => 'Ðвтоматичні дії', + 'Your automatic action has been created successfully.' => 'Ðвтоматичну дію уÑпішно Ñтворено.', + 'Unable to create your automatic action.' => 'Ðе вдалоÑÑ Ñтворити автоматичну дію.', + 'Remove an action' => 'Видалити дію', + 'Unable to remove this action.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ дію.', + 'Action removed successfully.' => 'Дію уÑпішно видалено.', + 'Automatic actions for the project "%s"' => 'Ðвтоматичні дії Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ "%s"', + 'Add an action' => 'Додати дію', + 'Event name' => 'Ðазва події', + 'Action' => 'ДіÑ', + 'Event' => 'ПодіÑ', + 'When the selected event occurs execute the corresponding action.' => 'Виконати вказану дію при наÑтанні обнаної події.', + 'Next step' => 'ÐаÑтупний крок', + '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 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' => 'Призначити колір певному кориÑтувачу', + 'Position' => 'ПозиціÑ', + 'Duplicate to project' => 'Дублювати до іншого проєкту', + 'Duplicate' => 'Дублювати', + 'Link' => 'ПоÑиланнÑ', + 'Comment updated successfully.' => 'Комент уÑпішно оновлено.', + 'Unable to update your comment.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ коментар.', + 'Remove a comment' => 'Видалити коментар', + 'Comment removed successfully.' => 'Коментар уÑпішно видалено.', + 'Unable to remove this comment.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ коментар.', + 'Do you really want to remove this comment?' => 'ДійÑно видалити цей коментар?', + 'Current password for the user "%s"' => 'Поточний пароль кориÑтувача "%s"', + 'The current password is required' => 'Слід вказати поточний пароль', + 'Wrong password' => 'Пароль неправильний', + 'Unknown' => 'Ðевідомо', + 'Last logins' => 'ОÑтанні підключеннÑ', + 'Login date' => 'Дата входу', + 'Authentication method' => 'СпоÑіб автентифікації', + 'IP address' => 'IP-адреÑа', + 'User agent' => 'Клієнт', + 'Persistent connections' => 'ПоÑтійні з\'єднаннÑ', + 'No session.' => 'З\'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ñутні.', + 'Expiration date' => 'ДійÑне до', + 'Remember Me' => 'Запам\'Ñтати мене', + 'Creation date' => 'Дата ÑтвореннÑ', + 'Everybody' => 'Ð’ÑÑ–', + 'Open' => 'Відкритий', + 'Closed' => 'Закритий', + 'Search' => 'Пошук', + 'Nothing found.' => 'Ðічого не знайдено.', + 'Due date' => 'Термін виконаннÑ', + 'Description' => 'ОпиÑ', + '%d comments' => '%d коментарів', + '%d comment' => '%d коментар', + 'Email address invalid' => 'Ðеправильна адреÑа e-mail', + '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.' => 'Профіль уÑпішно зв\'Ñзано із зовнішнім обліковим запиÑом.', + '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' => 'Ðвтоматично призначити категорію за кольором', + 'Task creation or modification' => 'Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ зміна задачі', + 'Category' => 'КатегоріÑ', + 'Category:' => 'КатегоріÑ:', + 'Categories' => 'Категорії', + 'Your category has been created successfully.' => 'Категорію уÑпішно Ñтворено.', + 'This category has been updated successfully.' => 'Категорію уÑпішно оновлено.', + 'Unable to update this 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' => 'Без категорії', + 'The name is required' => 'Слід вказати назву', + 'Remove a file' => 'Видалити файл', + 'Unable to remove this file.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ файл.', + 'File removed successfully.' => 'Файл уÑпішно видалено.', + 'Attach a document' => 'Прикріпити файл', + 'Do you really want to remove this file: "%s"?' => 'ДійÑно видалити файл "%s"?', + 'Attachments' => 'Прикріплені файли', + 'Edit the task' => 'Редагувати задачу', + 'Add a comment' => 'Додати коментар', + 'Edit a comment' => 'Редагувати коментар', + 'Summary' => 'ВідомоÑті', + 'Time tracking' => 'Облік чаÑу', + 'Estimate:' => 'Оцінка:', + 'Spent:' => 'Витрачено:', + 'Do you really want to remove this sub-task?' => 'ДійÑно видалити цю підзадачу?', + 'Remaining:' => 'ЗалишилоÑÑ:', + 'hours' => 'годин', + 'estimated' => 'оцінено', + 'Sub-Tasks' => 'Підзадачі', + 'Add a sub-task' => 'Додати підзадачу', + 'Original estimate' => 'Початкова оцінка', + 'Create another sub-task' => 'Створити ще одну підзадачу', + 'Time spent' => 'Витрачений чаÑ', + 'Edit a sub-task' => 'Редагувати відзадачу', + 'Remove a sub-task' => 'Видалити підзадачу', + 'The time must be a numeric value' => 'Ð§Ð°Ñ Ð¼Ð°Ñ” бути чиÑловим значеннÑм', + 'Todo' => 'Зробити', + 'In progress' => 'ВиконуєтьÑÑ', + 'Sub-task removed successfully.' => 'Підзадачу уÑпішно видалено.', + 'Unable to remove this sub-task.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ підзадачу.', + 'Sub-task updated successfully.' => 'Підзадачу уÑпішно оновлено.', + 'Unable to update your sub-task.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ підзадачу.', + 'Unable to create your sub-task.' => 'Ðе вдалоÑÑ Ñтворити підзадачу.', + 'Maximum size: ' => 'МакÑимальний розмір: ', + 'Display another project' => 'ПереглÑнути інший проєкт', + 'Created by %s' => 'Створена %s', + 'Tasks Export' => 'ЕкÑпорт задач', + 'Start Date' => 'Дата початку', + 'Execute' => 'Виконати', + 'Task Id' => 'ID задачі', + 'Creator' => 'Ðвтор', + 'Modification date' => 'Дата зміни', + 'Completion date' => 'Дата завершеннÑ', + 'Clone' => 'КопіÑ', + 'Project cloned successfully.' => 'Проєкт уÑпішно Ñклоновано.', + 'Unable to clone this project.' => 'Ðе вдалоÑÑ Ñклонувати проєкт.', + 'Enable email notifications' => 'Увімкнути ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ð¾ e-mail', + 'Task position:' => 'ÐŸÐ¾Ð·Ð¸Ñ†Ñ–Ñ Ð·Ð°Ð´Ð°Ñ‡Ñ–:', + 'The task #%d has been opened.' => 'Відкрито задачу #%d.', + 'The task #%d has been closed.' => 'Закрито задачу #%d.', + 'Sub-task updated' => 'Підзадачу оновлено', + 'Title:' => 'Заголовок:', + 'Status:' => 'СтатуÑ:', + 'Assignee:' => 'Відповідальний:', + 'Time tracking:' => 'Облік чаÑу:', + 'New sub-task' => 'Ðова підзадача', + 'New attachment added "%s"' => 'Прикріплено "%s"', + 'New comment posted by %s' => 'Ðовий коментар від "%s"', + 'New comment' => 'Ðовий коментар', + 'Comment updated' => 'Коментар оновлено', + 'New subtask' => 'Ðова підзадача', + 'I only want to receive notifications for these projects:' => 'Отримувати Ð¿Ð¾Ð²Ñ–Ð´Ð¾Ð¼Ð»ÐµÐ½Ð½Ñ Ð»Ð¸ÑˆÐµ Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñ–Ð²:', + 'view the task on Kanboard' => 'переглÑнути задачу в Kanboard', + 'Public access' => 'Публічний доÑтуп', + 'Disable public access' => 'Вимкнути публічний доÑтуп', + 'Enable public access' => 'Увімкнути публічний доÑтуп', + 'Public access disabled' => 'Публічний доÑтуп вимкнуто', + 'Move the task to another project' => 'ПереміÑтити задачу до іншого проєкту', + 'Move to project' => 'ПереміÑтити до іншого проєкту', + 'Do you really want to duplicate this task?' => 'Ви дійÑно бажаєте Ñтворити копію цієї задачі?', + 'Duplicate a task' => 'Дублювати задачу', + 'External accounts' => 'Зовнішні облікові запиÑи', + 'Account type' => 'Тип облікового запиÑу', + 'Local' => 'Локальний', + 'Remote' => 'Віддалений', + 'Enabled' => 'Увімкнені', + 'Disabled' => 'Вимкнуті', + 'Login:' => 'Логін:', + 'Full Name:' => 'Повне ім\'Ñ:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'СповіщеннÑ:', + 'Notifications' => 'СповіщеннÑ', + 'Account type:' => 'Тип облікового запиÑу:', + 'Edit profile' => 'Редагувати профіль', + 'Change password' => 'Змінити пароль', + 'Password modification' => 'Зміна паролÑ', + 'External authentications' => 'Ð—Ð¾Ð²Ð½Ñ–ÑˆÐ½Ñ Ð°Ð²Ñ‚ÐµÐ½Ñ‚Ð¸Ñ„Ñ–ÐºÐ°Ñ†Ñ–Ñ', + 'Never connected.' => 'ІÑÑ‚Ð¾Ñ€Ñ–Ñ Ð¿Ñ–Ð´ÐºÐ»ÑŽÑ‡ÐµÐ½ÑŒ відÑутнÑ.', + '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/%s год', + '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', + '%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)' => 'Стандартні колонки Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… проєктів (розділені комами)', + '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' => 'Оберіть подію', + 'Create a task from an external provider' => 'Створити задачу із зовнішнього джерела', + 'Change the assignee based on an external username' => 'Змінити відповідального виходÑчи з зовнішнього логіна', + 'Change the category based on an external label' => 'Змінити категорію на оÑнові мітки зв\'Ñзку', + 'Reference' => 'Зовнішній ID', + 'Label' => 'Мітка', + 'Database' => 'База даних', + 'About' => 'Про додаток', + 'Database driver:' => 'Драйвер бази даних:', + 'Board settings' => 'ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾ÑˆÐºÐ¸', + 'Webhook settings' => 'Параметри webhook', + 'Reset token' => 'Скинути ключ доÑтупу', + 'API endpoint:' => 'URL API:', + 'Refresh interval for personal 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 Ñекунд)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'ЧаÑтота в Ñекундах (0 – вимкнути цю функцію, Ñтандартно – 10 Ñекунд)', + 'Application URL' => 'URL додатку', + 'Token regenerated.' => 'Згенеровано новий ключ доÑтупу.', + 'Date format' => 'Формат дати', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Також підтримуєтьÑÑ Ñ„Ð¾Ñ€Ð¼Ð°Ñ‚ ISO: "%s" та "%s"', + 'New personal project' => 'Ðовий перÑональний проєкт', + 'This project is personal' => 'Цей проєкт перÑональний', + 'Add' => 'Додати', + 'Start date' => 'Дата початку', + 'Time estimated' => 'Оцінка чаÑу', + 'There is nothing assigned to you.' => 'Ðемає призначених вам задач.', + 'My tasks' => 'Мої задачі', + 'Activity stream' => 'ÐктивніÑть', + 'Dashboard' => 'Панель', + 'Confirmation' => 'ПідтвердженнÑ', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Створити коментар із зовнішнього джерела', + 'Project management' => 'ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ð°Ð¼Ð¸', + 'Columns' => 'Колонки', + 'Task' => 'Задача', + 'Percentage' => 'Процент', + 'Number of tasks' => 'ЧиÑло задач', + 'Task distribution' => 'Колонки', + 'Analytics' => 'Ðналітика', + 'Subtask' => 'Підзадача', + 'User repartition' => 'Відповідальні', + 'Clone this project' => 'Створити клон проєкту', + 'Column removed successfully.' => 'Колонку уÑпішно видалено.', + 'Not enough data to show the graph.' => 'ÐедоÑтатньо даних Ð´Ð»Ñ Ð¿Ð¾ÐºÐ°Ð·Ñƒ графіка.', + 'Previous' => 'Ðазад', + 'The id must be an integer' => 'ID має бути цілим чиÑлом', + 'The project id must be an integer' => 'ID проєкту має бути цілим чиÑлом', + 'The status must be an integer' => 'Ð¡Ñ‚Ð°Ñ‚ÑƒÑ Ð¼Ð°Ñ” бути цілим чиÑлом', + 'The subtask id is required' => 'ID підзадачі Ñ” обов\'Ñзковим', + 'The subtask id must be an integer' => 'ID підзадачі має бути цілим чиÑлом', + 'The task id is required' => 'ID задачі Ñ” обов\'Ñзковим', + 'The task id must be an integer' => 'ID задачі має бути цілим чиÑлом', + 'The user id must be an integer' => 'ID кориÑтувача має бути цілим чиÑлом', + 'This value is required' => 'Це Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ñ” обов\'Ñзковим', + 'This value must be numeric' => 'Це Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¼Ð°Ñ” бути чиÑловим', + 'Unable to create this task.' => 'Ðе вдалоÑÑ Ñтворити задачу', + 'Cumulative flow diagram' => 'Ðакопичувальна діаграма', + 'Daily project summary' => 'Щоденний звіт проєкту', + 'Daily project summary export' => 'ЕкÑпорт щоденного звіту проєкту', + 'Exports' => 'ЕкÑпорт', + 'This export contains the number of tasks per column grouped per day.' => 'Звіт міÑтить згруповані по днÑм кількоÑті задач в кожній з колонок.', + 'Active swimlanes' => 'Ðктивні доріжки', + 'Add a new swimlane' => 'Додати доріжку', + 'Default swimlane' => 'Стандартна доріжка', + 'Do you really want to remove this swimlane: "%s"?' => 'ДійÑно видалити доріжку "%s"?', + 'Inactive swimlanes' => 'Ðеактивні доріжки', + 'Remove a swimlane' => 'Видалити доріжку', + 'Swimlane modification for the project "%s"' => 'Ð ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð´Ð¾Ñ€Ñ–Ð¶ÐºÐ¸ в проєкті "%s"', + 'Swimlane removed successfully.' => 'Доріжку уÑпішно видалено.', + 'Swimlanes' => 'Доріжки', + 'Swimlane updated successfully.' => 'Доріжку уÑпішно оновлено.', + 'Unable to remove this swimlane.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ доріжку.', + 'Unable to update this swimlane.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ доріжку.', + 'Your swimlane has been created successfully.' => 'Доріжку уÑпішно Ñтворено.', + 'Example: "Bug, Feature Request, Improvement"' => 'Ðаприклад, "Дефект, Ðова функціÑ, ВдоÑконаленнÑ"', + 'Default categories for new projects (Comma-separated)' => 'Стандартні категорії Ð´Ð»Ñ Ð½Ð¾Ð²Ð¸Ñ… проєктів (розділені комами)', + 'Integrations' => 'Інтеграції', + 'Integration with third-party services' => 'Інтеграції із Ñторонніми ÑервіÑами', + 'Subtask Id' => 'ID підзадачі', + 'Subtasks' => 'Підзадачі', + 'Subtasks Export' => 'ЕкÑпорт підзадач', + 'Task Title' => 'Заголовок задачі', + 'Untitled' => 'Без назви', + 'Application default' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм із додатка', + 'Language:' => 'Мова:', + 'Timezone:' => 'ЧаÑовий поÑÑ:', + 'All columns' => 'Ð’ÑÑ– колонки', + 'Next' => 'Далі', + '#%d' => '#%d', + 'All swimlanes' => 'Ð’ÑÑ– доріжки', + 'All colors' => 'Ð’ÑÑ– кольори', + 'Moved to column %s' => 'Переміщено до колонки %s', + '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' => 'Облік чаÑу', + 'You already have one subtask in progress' => 'У Ð²Ð°Ñ Ð²Ð¶Ðµ Ñ” одна підзадача в роботі', + 'Which parts of the project do you want to duplicate?' => 'Який вміÑÑ‚ цього проєкту Ñлід Ñкопіювати?', + 'Disallow login form' => 'Вимкнути форму входу', + 'Start' => 'Початок', + 'End' => 'ЗавершеннÑ', + 'Task age in days' => 'Вік задачі в днÑÑ…', + 'Days in this column' => 'Днів у поточній колонці', + '%dd' => '%d д', + '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' => 'Поле Ñ” обов\'Ñзковим', + 'Link added successfully.' => 'ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ ÑƒÑпішно додано.', + 'Link updated successfully.' => 'ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ ÑƒÑпішно оновлено.', + 'Link removed successfully.' => 'ПоÑÐ¸Ð»Ð°Ð½Ð½Ñ ÑƒÑпішно видалено.', + 'Link labels' => 'Мітки зв\'Ñзків', + 'Link modification' => 'Змінити мітку зв\'Ñзку', + 'Opposite label' => 'Протилежна мітка', + 'Remove a link' => 'Видалити поÑиланнÑ', + '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.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ поÑиланнÑ.', + 'relates to' => 'пов\'Ñзана з', + 'blocks' => 'блокує', + 'is blocked by' => 'заблокована', + 'duplicates' => 'дублює', + 'is duplicated by' => 'дубльована', + 'is a child of' => 'Ñ” дочірньою длÑ', + 'is a parent of' => 'Ñ” батьківÑькою длÑ', + 'targets milestone' => 'належить до віхи', + 'is a milestone of' => 'Ñ” віхою длÑ', + 'fixes' => 'виправлÑÑ”', + 'is fixed by' => 'виправлÑєтьÑÑ', + 'This task' => 'Ð¦Ñ Ð·Ð°Ð´Ð°Ñ‡Ð°', + '<1h' => '< 1 год', + '%dh' => '%d год', + 'Expand tasks' => 'Розгорнути задачі', + 'Collapse tasks' => 'Згорнути задачі', + 'Expand/collapse tasks' => 'Згорнути/розгорнути задачі', + 'Close dialog box' => 'Закрити діалогове вікно', + 'Submit a form' => 'ÐадіÑлати форму', + 'Board view' => 'ПереглÑд дошки', + 'Keyboard shortcuts' => 'Комбінації клавіш', + 'Open board switcher' => 'Відкрити перемикач проєкту', + 'Application' => 'Додаток', + 'Compact view' => 'СтиÑлий виглÑд', + 'Horizontal scrolling' => 'Горизонтальне гортаннÑ', + 'Compact/wide view' => 'СтиÑлий/широкий виглÑд', + 'Currency' => 'Валюта', + 'Personal project' => 'ПерÑональний проєкт', + 'AUD - Australian Dollar' => 'AUD – ÐвÑтралійÑький долар', + 'CAD - Canadian Dollar' => 'CAD – КанадÑький долар', + 'CHF - Swiss Francs' => 'CHF – ШвейцарÑький франк', + 'Custom Stylesheet' => 'Додаткові Ñтилі', + 'EUR - Euro' => 'EUR – Євро', + 'GBP - British Pound' => 'GBP – Фунт Ñтерлінгів', + 'INR - Indian Rupee' => 'INR – ІндійÑька рупіÑ', + 'JPY - Japanese Yen' => 'JPY – ЯпонÑька йєна', + 'NZD - New Zealand Dollar' => 'NZD – ÐовозеландÑький долар', + 'PEN - Peruvian Sol' => 'PEN - ПеруанÑький Ñоль', + 'RSD - Serbian dinar' => 'RSD – СербÑький динар', + 'CNY - Chinese Yuan' => 'CNY – КитайÑький юань', + 'USD - US Dollar' => 'USD – ÐмериканÑький долар', + 'VES - Venezuelan Bolívar' => 'VES – ВенеÑуельÑький болівар', + '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' => 'Ð§Ð°Ñ Ð² колонці', + '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.' => 'Цей звіт міÑтить вÑÑ– Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¼Ñ–Ð¶ колонками Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ñ— задачі разом із датою, кориÑтувачем та чаÑом витраченим на кожне з переміщень.', + 'Currency rates' => 'ÐšÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚', + 'Rate' => 'КурÑ', + 'Change reference currency' => 'Змінити оÑновну валюту', + 'Reference currency' => 'ОÑновна валюта', + 'The currency rate has been added successfully.' => 'ÐšÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚ уÑпішно додано.', + 'Unable to add this currency rate.' => 'Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ ÐºÑƒÑ€Ñ Ð²Ð°Ð»ÑŽÑ‚.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s знÑв відповідального з задачі %s', + 'Information' => 'ІнформаціÑ', + 'Check two factor authentication code' => 'Перевірка коду двофакторної автентифікації', + 'The two factor authentication code is not valid.' => 'Код двофакторної автентифікації неправильний.', + 'The two factor authentication code is valid.' => 'Код двофакторної автентифікації правильний.', + 'Code' => 'Код', + 'Two factor authentication' => 'Двофакторна автентифікаціÑ', + 'This QR code contains the key URI: ' => 'Цей QR-код міÑтить URL ключа: ', + 'Check my code' => 'Перевірити код', + 'Secret key: ' => 'Секретний ключ: ', + 'Test your device' => 'Перевірте ваш приÑтрій', + 'Assign a color when the task is moved to a specific column' => 'Призначити колір, коли задачу переміщено до певної колонки', + '%s via Kanboard' => '%s через Kanboard', + 'Burndown chart' => 'Діаграма Ð·Ð³Ð¾Ñ€Ð°Ð½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡', + '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 – ШведÑька крона', + 'Identifier' => 'Ідентифікатор', + 'Disable two factor authentication' => 'Вимкнути двоетапну перевірку', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Ви дійÑно бажаєте вимкнути двоетапну перевірку Ð´Ð»Ñ ÐºÐ¾Ñ€Ð¸Ñтувача "%s"?', + 'Edit link' => 'Змінити зв\'Ñзок', + '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' => 'Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÑŽÐ²Ð°Ð½Ð¾Ñ— задачі заплановано', + 'Score' => 'СкладніÑть', + 'The identifier must be unique' => 'Ідентифікатор повинен бути унікальним', + 'This linked task id doesn\'t exists' => 'Ідентифікатор пов\'Ñзаної задачі не Ñ–Ñнує', + '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' => 'Проміжок чаÑу Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð°Ñ…ÑƒÐ½ÐºÑƒ нового терміну виконаннÑ', + '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: ' => 'Коефіцієнт Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð°Ñ…ÑƒÐ½ÐºÑƒ нового терміну виконаннÑ: ', + 'Month(s)' => 'МіÑÑці', + 'This task has been created by: ' => 'Ð¦Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° Ñтворена:', + 'Recurrent task has been generated:' => 'Була Ñтворена повторювана задача:', + 'Timeframe to calculate new due date: ' => 'Період чаÑу Ð´Ð»Ñ Ñ€Ð¾Ð·Ñ€Ð°Ñ…ÑƒÐ½ÐºÑƒ нового терміну виконаннÑ: ', + 'Trigger to generate recurrent task: ' => 'ПодіÑ, що викликає ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÑŽÐ²Ð°Ð½Ð¾Ñ— задачі: ', + 'When task is closed' => 'Задачу закрито', + 'When task is moved from first column' => 'Задачу переміщено до першої колонки', + 'When task is moved to last column' => 'Задачу переміщено до оÑтанньої колонки', + 'Year(s)' => 'Роки', + 'Project settings' => 'ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ', + 'Automatically update the start date' => 'Ðвтоматично оновити дату початку', + 'iCal feed' => 'Стрічка iCal', + 'Preferences' => 'ÐалаштуваннÑ', + 'Security' => 'Безпека', + 'Two factor authentication disabled' => 'Двофакторну автентифікацію вимкнуто', + 'Two factor authentication enabled' => 'Двофакторну автентифікацію увімкнено', + 'Unable to update this user.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ цього кориÑтувача.', + 'There is no user management for personal projects.' => 'ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувачами відÑутнє Ð´Ð»Ñ Ð¿ÐµÑ€Ñональних проєктів.', + 'User that will receive the email' => 'КориÑтувач, Ñкий отримає e-mail', + 'Email subject' => 'Тема e-mail', + 'Date' => 'Дата', + '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' => 'ÐадіÑлати задачу на вказаний E-mail', + 'Reopen a task' => 'Відкрити задачу', + 'Notification' => 'СповіщеннÑ', + '%s moved the task #%d to the first swimlane' => '%s переміÑтив задачу #%d на першу доріжку', + 'Swimlane' => 'Доріжка', + '%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.' => 'Цей звіт надає інформацію по задачам Ð´Ð»Ñ Ð²ÐºÐ°Ð·Ð°Ð½Ð¾Ð³Ð¾ проміжку чаÑу.', + 'Project activities for %s' => 'ДіÑльніÑть в проєкті Ð´Ð»Ñ "%s"', + 'view the board on Kanboard' => 'переглÑнути дошку в Kanboard', + 'The task has been moved to the first swimlane' => 'Задачу переміщено на першу доріжку', + 'The task has been moved to another swimlane:' => 'Задачу переміщено на іншу доріжку:', + 'New title: %s' => 'Ðовий заголовок: %s', + 'The task is not assigned anymore' => 'Задача не має відповідального', + 'New assignee: %s' => 'Ðовий відповідальний: %s', + 'There is no category now' => 'ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ñ–Ñ Ð²Ñ–Ð´ÑутнÑ', + 'New category: %s' => 'Ðова категоріÑ: %s', + 'New color: %s' => 'Ðовий колір: %s', + 'New complexity: %d' => 'Ðова ÑкладніÑть: %d', + 'The due date has been removed' => 'Термін Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾', + 'There is no description anymore' => 'ÐžÐ¿Ð¸Ñ Ñ‚ÐµÐ¿ÐµÑ€ відÑутній', + 'Recurrence settings has been modified' => 'Змінено Ð½Ð°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÑŽÐ²Ð°Ð½Ð¾Ñті', + 'Time spent changed: %sh' => 'Змінений витрачений чаÑ: %s год', + 'Time estimated changed: %sh' => 'Змінено оцінку чаÑу: %s год', + 'The field "%s" has 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:' => 'Отримувати ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð¿Ñ€Ð¾:', + 'All tasks' => 'Ð’ÑÑ– задачі', + 'Only for tasks assigned to me' => 'Задачі призначені мені', + 'Only for tasks created by me' => 'Задачі Ñтворені мною', + 'Only for tasks created by me and tasks assigned to me' => 'Задачі Ñтворені мною або призначені мені', + '%%Y-%%m-%%d' => '%%d.%%m.%%Y', + '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' => 'ЗапуÑтити таймер', + 'My activity stream' => 'Cтрічка подій', + 'Search tasks' => 'Пошук задач', + 'Reset filters' => 'Скинути фільтри', + 'My tasks due tomorrow' => 'Мої задачі з терміном Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ "завтра"', + 'Tasks due today' => 'Задачі з терміном Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ "Ñьогодні"', + 'Tasks due tomorrow' => 'Задачі з термімном Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ "завтра"', + 'Tasks due yesterday' => 'Задачі з терміном Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ "учора"', + 'Closed tasks' => 'Закриті задачі', + 'Open tasks' => 'Відкриті задачі', + 'Not assigned' => 'Ðе призначено нікому', + 'View advanced search syntax' => 'ПереглÑнути розширений ÑинтакÑÐ¸Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ', + 'Overview' => 'ОглÑд', + 'Board/Calendar/List view' => 'ПереглÑд Дошки/КалендарÑ/СпиÑку', + 'Switch to the board view' => 'ПеремкнутиÑÑ Ð½Ð° дошку', + 'Switch to the list view' => 'ПеремкнутиÑÑ Ð½Ð° ÑпиÑок', + 'Go to the search/filter box' => 'Перейти до Ð¿Ð¾Ð»Ñ Ð¿Ð¾ÑˆÑƒÐºÑƒ/фільтрів', + 'There is no activity yet.' => 'ДіÑльніÑть поки що відÑутнÑ.', + 'No tasks found.' => 'Задачі відÑутні.', + 'Keyboard shortcut: "%s"' => 'ÐšÐ¾Ð¼Ð±Ñ–Ð½Ð°Ñ†Ñ–Ñ ÐºÐ»Ð°Ð²Ñ–Ñˆ: "%s"', + 'List' => 'СпиÑок', + 'Filter' => 'Фільтр', + 'Advanced search' => 'Розширений пошук', + 'Example of query: ' => 'Приклад запиту: ', + 'Search by project: ' => 'Пошук за проєктом: ', + 'Search by column: ' => 'Пошук за колонкою: ', + 'Search by assignee: ' => 'Пошук за відповідальним: ', + 'Search by color: ' => 'Пошук за кольором: ', + 'Search by category: ' => 'Пошук за категорією: ', + 'Search by description: ' => 'Пошук за опиÑом: ', + 'Search by due date: ' => 'Пошук за терміном виконаннÑ: ', + 'Average time spent in each column' => 'Середній витрачений Ñ‡Ð°Ñ Ð´Ð»Ñ ÐºÐ¾Ð¶Ð½Ð¾Ñ— з колонок', + 'Average time spent' => 'Середній витрачений чаÑ', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Графік показує Ñередній витрачений Ñ‡Ð°Ñ Ð² кожній колонці Ð´Ð»Ñ Ð¾Ñтанніх %d задач.', + 'Average Lead and Cycle time' => 'Середній Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ñ‚Ð° циклу', + 'Average lead time: ' => 'Середній Ñ‡Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ: ', + 'Average cycle time: ' => 'Середній Ñ‡Ð°Ñ Ñ†Ð¸ÐºÐ»Ñƒ: ', + 'Cycle Time' => 'Ð§Ð°Ñ Ñ†Ð¸ÐºÐ»Ñƒ', + 'Lead Time' => 'Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Графік показує зміну з чаÑом Ñереднього чаÑу Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ (з очікуваннÑм) та циклу Ð´Ð»Ñ Ð¾Ñтанніх %d задач.', + 'Average time into each column' => 'Середній Ñ‡Ð°Ñ Ð² колонках', + 'Lead and cycle time' => 'Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ñ‚Ð° циклу', + 'Lead time: ' => 'Ð§Ð°Ñ Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ: ', + 'Cycle time: ' => 'Ð§Ð°Ñ Ñ†Ð¸ÐºÐ»Ñƒ: ', + 'Time spent in 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 the start date automatically' => 'Ðвтоматично вÑтановити дату початку', + 'Edit Authentication' => 'Змінити автентифікацію', + 'Remote user' => 'Віддалений кориÑтувач', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Віддалені кориÑтувачі не зберігають Ñвої паролі в базі даних Kanboard. Приклади: облікові запиÑи LDAP, GitHub та Google.', + '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.' => 'Ðемає доÑтупних проєктів.', + 'Trigger automatically subtask time tracking' => 'Ðвтоматично запуÑкати таймер підзадачі при зміні ÑтатуÑу', + 'Include closed tasks in the cumulative flow diagram' => 'Враховувати закриті задачі в накопичувальній діаграмі', + 'Current swimlane: %s' => 'Поточна доріжка: %s', + 'Current column: %s' => 'Поточна колонка: %s', + 'Current category: %s' => 'Поточна категоріÑ: %s', + 'no category' => 'без категорії', + 'Current assignee: %s' => 'Поточний відповідальний: %s', + 'not assigned' => 'відÑутній', + 'Author:' => 'Ðвтор:', + 'contributors' => 'учаÑники', + 'License:' => 'ЛіцензіÑ:', + 'License' => 'ЛіцензіÑ', + 'Enter the text below' => 'Введіть текÑÑ‚ нижче', + 'Start date:' => 'Дата початку:', + 'Due date:' => 'Термін виконаннÑ:', + 'People who are project managers' => 'КориÑтувачі, що Ñ” керівниками проєктів', + 'People who are project members' => 'КориÑтувачі, що Ñ” учаÑниками проєктів', + 'NOK - Norwegian Krone' => 'NOK – Ðорвезька крона', + 'Show this column' => 'Показувати цю колонку', + 'Hide this column' => 'Приховати цю колонку', + 'End date' => 'Дата завершеннÑ', + 'Users overview' => 'КориÑтувачі в проєктах', + 'Members' => 'УчаÑники', + 'Shared project' => 'Спільний проєкт', + 'Project managers' => 'Керівники проєктів', + 'Projects list' => 'СпиÑок проєктів', + 'End date:' => 'Дата завершеннÑ:', + 'Change task color when using a specific task link' => 'Змінити колір задачі, Ñкщо викориÑтовуєтьÑÑ Ð¿ÐµÐ²Ð½Ð¸Ð¹ тип зв\'Ñзків задач', + 'Task link creation or modification' => 'Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð°Ð±Ð¾ зміна зв\'Ñзків задачі', + 'Milestone' => 'Віха', + 'Reset the search/filter box' => 'Скинути пошук/фільтр', + 'Documentation' => 'ДокументаціÑ', + 'Author' => 'Ðвтор', + 'Version' => 'ВерÑÑ–Ñ', + 'Plugins' => 'РозширеннÑ', + 'There is no plugin loaded.' => 'Ð Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñутні.', + 'My notifications' => 'Мої ÑповіщеннÑ', + 'Custom filters' => 'Додаткові фільтри', + 'Your custom filter has 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 has been updated successfully.' => 'Додатковий фільтр уÑпішно оновлено.', + 'Unable to update custom filter.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ додатковий фільтр.', + 'Web' => 'Web', + '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 оновлено', + 'New task #%d: %s' => 'Ðова задача #%d: %s', + 'Task updated #%d' => 'Задачу #%d оновлено', + 'Task #%d closed' => 'Задачу #%d закрито', + 'Task #%d opened' => 'Задачу #%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', + '%d overdue tasks' => '%d проÑтрочених задач', + 'No notification.' => 'Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ñ–Ð´Ñутні.', + '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:' => 'СпоÑоби ÑповіщеннÑ:', + 'Unable to read your file' => 'Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ ваш файл', + '%d task(s) have been imported successfully.' => '%d задач(Ñ–) уÑпішно імпортовано.', + 'Nothing has been imported!' => 'Ðічого не було імпортовано!', + 'Import users from CSV file' => 'Імпортувати кориÑтувачів з CSV-файлу', + '%d user(s) have been imported successfully.' => '%d кориÑтувач(ів) уÑпішно імпортовано.', + 'Comma' => 'Кома', + 'Semi-colon' => 'Крапка з комою', + 'Tab' => 'ТабулÑціÑ', + 'Vertical bar' => 'Вертикальна риÑка', + '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)' => 'Додавати фільтр заміÑть заміни наÑвного', + 'Append/Replace' => 'Додати/Замінити', + 'Append' => 'Додати', + 'Replace' => 'Замінити', + 'Import' => 'Імпорт', + 'Change sorting' => 'Змінити ÑортуваннÑ', + 'Tasks Importation' => 'Імпорт задач', + 'Delimiter' => 'Розділювач', + 'Enclosure' => 'ÐžÐ±Ñ€Ð°Ð¼Ð»ÐµÐ½Ð½Ñ Ñ‚ÐµÐºÑту', + 'CSV File' => 'Файл CSV', + 'Instructions' => 'ІнÑтрукції', + 'Your file must use the predefined CSV format' => 'Ваш файл має бути у відповідному форматі CSV', + 'Your file must be encoded in UTF-8' => 'ВміÑÑ‚ файлу повинен бути у кодуванні 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' => 'Термін Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð¼Ð°Ñ” бути датою у форматі ISO: РРРР-ММ-ДД', + 'Download CSV template' => 'Завантажити шаблон CSV', + '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' => '%s прикріпив файл до задачі %s', + 'Link type' => 'Тип поÑиланнÑ', + 'Assign automatically a category based on a link' => 'Ð’Ñтановити категорію на оÑнові зв\'Ñзку', + 'BAM - Konvertible Mark' => 'BAM – Конвертовна марка', + 'Assignee Username' => 'Логін відповідального', + 'Assignee Name' => 'Ім\'Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð°Ð»ÑŒÐ½Ð¾Ð³Ð¾', + 'Groups' => 'Групи', + 'Members of %s' => 'УчаÑники %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"' => 'Додати до групи "%s"', + 'Group member added successfully.' => 'УÑпішно додано учаÑника групи.', + 'Unable to add group member.' => 'Ðе вдалоÑÑ Ð´Ð¾Ð´Ð°Ñ‚Ð¸ учаÑника групи.', + 'Remove user from group "%s"' => 'Вилучити кориÑтувача із групи "%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' => 'Ваш обліковий Ð·Ð°Ð¿Ð¸Ñ Ð·Ð°Ð±Ð»Ð¾ÐºÐ¾Ð²Ð°Ð½Ð¾ на %d хвилин(и)', + 'Invalid captcha' => 'Ðеправильний код перевірки', + 'The name must be unique' => 'Ðазва має бути унікальною', + 'View all groups' => 'ПереглÑнути вÑÑ– групи', + 'There is no user available.' => 'Ðемає доÑтупних кориÑтувачів.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'ДійÑно вилучити кориÑтувача "%s" із групи "%s"?', + 'There is no group.' => 'Ðемає Ñтворених груп.', + 'Add group member' => 'Додати учаÑника групи', + 'Do you really want to remove this group: "%s"?' => 'ДійÑно видалити групу "%s"?', + 'There is no user in this group.' => 'Ð’ цій групі відÑутні учаÑники.', + 'Permissions' => 'Дозволи', + 'Allowed Users' => 'Дозволи кориÑтувачів', + 'No specific user has been allowed.' => 'Дозволів кориÑтувачів не задано.', + 'Role' => 'Роль', + 'Enter user name...' => 'Вкажіть ім\'Ñ ÐºÐ¾Ñ€Ð¸Ñтувача…', + 'Allowed Groups' => 'Дозволи груп', + 'No group has been allowed.' => 'Дозволів груп не задано.', + 'Group' => 'Група', + 'Group Name' => 'Ðазва групи', + 'Enter group name...' => 'Вказіть назву групи…', + 'Role:' => 'Роль:', + 'Project members' => 'УчаÑники проєкту', + '%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', + 'Estimated hours: ' => 'Оцінено годин: ', + 'Actual hours: ' => 'Витрачено годин: ', + 'Hours Spent' => 'Витрачено годин', + 'Hours Estimated' => 'Оцінено годин', + 'Estimated Time' => 'Оцінка чаÑу', + 'Actual Time' => 'Витрачено чаÑу', + 'Estimated vs actual time' => 'Оцінений та витрачений чаÑ', + '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' => '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' => 'Ð¡ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Kanboard', + 'Forgot password?' => 'Забули пароль?', + 'Enable "Forget Password"' => 'Увімкнути "Забув пароль"', + 'Password Reset' => 'Ð¡ÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ', + 'New password' => 'Ðовий пароль', + 'Change Password' => 'Змінити пароль', + 'To reset your password click on this link:' => 'Ð”Ð»Ñ ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ Ð¿ÐµÑ€ÐµÐ¹Ð´Ñ–Ñ‚ÑŒ за поÑиланнÑм:', + 'Last Password Reset' => 'ОÑтаннє ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ', + 'The password has never been reinitialized.' => 'Пароль ніколи не ÑкидавÑÑ.', + 'Creation' => 'Ð’Ñтановлено', + 'Expiration' => 'ДійÑний до', + 'Password reset history' => 'ІÑÑ‚Ð¾Ñ€Ñ–Ñ ÑÐºÐ¸Ð´Ð°Ð½Ð½Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Ð’ÑÑ– задачі в колонці "%s" на доріжці "%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.' => 'буде закрито %d задач(Ñ–) у колонці "%s" на доріжці "%s".', + 'Close all tasks in this column and this swimlane' => 'Закрити вÑÑ– задачі в цій колонці', + '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.' => 'Ідентифікатор проєкта Ñ” необов\'Ñзковим Ñ– може ÑкладатиÑÑ Ð»Ð¸ÑˆÐµ з латиниці та цифр: MIYPROEKT.', + 'Project owner' => 'ВлаÑник проєкту', + 'Personal projects do not have users and groups management.' => 'ÐšÐµÑ€ÑƒÐ²Ð°Ð½Ð½Ñ ÐºÐ¾Ñ€Ð¸Ñтувачами та групами відÑутнє в перÑональних проєктах.', + 'There is no project member.' => 'УчаÑники проєкту відÑутні.', + 'Priority' => 'Пріорітет', + 'Task priority' => 'Пріорітет задачі', + 'General' => 'ОÑновне', + 'Dates' => 'Дати', + 'Default priority' => 'Стандартний пріорітет', + 'Lowest priority' => 'Ðайнижчий пріорітет', + 'Highest priority' => 'Ðайвищий пріорітет', + 'Close a task when there is no activity' => 'Закрити задачу, Ñкщо немає діÑльноÑті', + 'Duration in days' => 'ТриваліÑть в годинах', + 'Send email when there is no activity on a task' => 'ÐадіÑлати E-mail, Ñкщо діÑльніÑть в задачі відÑутнÑ', + 'Unable to fetch link information.' => 'Ðе вдалоÑÑ Ð¾Ñ‚Ñ€Ð¸Ð¼Ð°Ñ‚Ð¸ інформацію про поÑиланнÑ.', + 'Daily background job for tasks' => 'Щоденна фонова задача планувальника Ð´Ð»Ñ Ð·Ð°Ð´Ð°Ñ‡', + 'Auto' => 'Ðвто', + 'Related' => 'Пов\'Ñзаний', + 'Attachment' => 'ВкладеннÑ', + 'Web Link' => 'Web-поÑиланнÑ', + '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' => '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:' => 'Зовнішній ID:', + 'Complexity:' => 'СкладніÑть:', + 'Swimlane:' => 'Доріжка:', + 'Column:' => 'Колонка:', + 'Position:' => 'ПозиціÑ:', + 'Creator:' => 'Ðвтор:', + 'Time estimated:' => 'Оцінка чаÑу:', + '%s hours' => '%s годин', + 'Time spent:' => 'Витрачений чаÑ:', + 'Created:' => 'Створена:', + 'Modified:' => 'Редагована:', + 'Completed:' => 'Завершена:', + 'Started:' => 'Розпочата:', + 'Moved:' => 'Переміщена: ', + 'Task #%d' => 'Задача #%d', + 'Time format' => 'Формат чаÑу', + 'Start date: ' => 'Дата початку: ', + 'End date: ' => 'Дата завершеннÑ: ', + 'New due date: ' => 'Ðовий термін виконаннÑ: ', + 'Start date changed: ' => 'Дату початку змінено: ', + 'Disable personal projects' => 'Вимкнути перÑональні проєкти', + 'Do you really want to remove this custom filter: "%s"?' => 'ДійÑно видалити додатковий фільтр "%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.' => 'Ð’ÑÑ– файли уÑпішно завантажено.', + 'The maximum allowed file size is %sB.' => 'МакÑимально дозволений розмір файлу – %s байт.', + 'Drag and drop your files here' => 'ПеретÑгніть ваші файли Ñюди', + 'choose files' => 'оберіть файли', + 'View profile' => 'ПереглÑнути профіль', + 'Two Factor' => 'Двофакторна автентифікаціÑ', + 'Disable user' => 'Заблокувати кориÑтувача', + 'Do you really want to disable this user: "%s"?' => 'Ви дійÑно бажаєте заблокувати кориÑтувача "%s"?', + 'Enable user' => 'Розблокувати кориÑтувача', + 'Do you really want to enable this user: "%s"?' => 'Ви дійÑно бажаєте розблокувати кориÑтувача "%s"?', + 'Download' => 'Завантажити', + 'Uploaded: %s' => 'Дата завантаженнÑ: %s', + 'Size: %s' => 'Розмір: %s', + 'Uploaded by %s' => 'Завантажений %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' => 'Ð—Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¼Ð°Ñ” бути більшим за %d', + 'Another swimlane with the same name exists in the project' => 'Ð’ проєкті вже Ñ–Ñнує доріжка з такою назвою', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Ðаприклад : https://example.kanboard.org/ (викориÑтовуєтьÑÑ Ð´Ð»Ñ ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð°Ð±Ñолютних URL)', + '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:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'ВерÑÑ–Ñ ÐžÐ¡:', + 'Database version:' => 'ВерÑÑ–Ñ Ð±Ð°Ð·Ð¸ даних:', + 'Browser:' => 'Браузер:', + 'Task view' => 'ПереглÑд задачі', + 'Edit task' => 'Редагувати задачу', + 'Edit description' => 'Редагувати опиÑ', + 'New internal link' => 'Ðовий зв\'Ñзок', + 'Display list of keyboard shortcuts' => 'Показати перелік клавіатурних комбінацій', + 'Avatar' => 'Ðватар', + 'Upload my avatar image' => 'Завантажити Ð·Ð¾Ð±Ñ€Ð°Ð¶ÐµÐ½Ð½Ñ Ð°Ð²Ð°Ñ‚Ð°Ñ€Ð°', + 'Remove my image' => 'Видалити зображеннÑ', + 'The OAuth2 state parameter is invalid' => 'Параметр OAuth2 "state" неправильний', + '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' => 'ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ E-mail', + 'Email sender address' => 'ÐдреÑа E-mail відправника', + 'Email transport' => 'ТранÑпорт E-Mail', + '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 a 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 has 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', + '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', + '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 запрошень надіÑлано.', + '%d invitation was sent.' => '%d Ð·Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Ð½Ð°Ð´Ñ–Ñлано.', + 'Unable to create this user.' => 'Ðе вдалоÑÑ Ñтворити кориÑтувача.', + 'Kanboard Invitation' => 'Ð—Ð°Ð¿Ñ€Ð¾ÑˆÐµÐ½Ð½Ñ Kanboard', + '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.' => 'Ð’Ð°Ñ Ð·Ð°Ð¿Ñ€Ð¾Ñили зареєÑтруватиÑÑ Ð² 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' => 'Цей логін вже зайнÑто', + 'Your profile must have a valid email address.' => 'Ваш профіль повинен мати правильний E-mail.', + 'TRL - Turkish Lira' => 'TRL – Турецька ліра', + 'The project email is optional and could be used by several plugins.' => 'E-mail проєкту не Ñ” обов\'Ñзковим Ñ– може викориÑтовуватиÑÑ Ñ€Ð¾Ð·ÑˆÐ¸Ñ€ÐµÐ½Ð½Ñми.', + 'The project email must be unique across all projects' => 'E-mail проєкту має бути унікальним Ð´Ð»Ñ Ð²ÑÑ–Ñ… проєктів', + 'The email configuration has been disabled by the administrator.' => 'ÐÐ°Ð»Ð°ÑˆÑ‚ÑƒÐ²Ð°Ð½Ð½Ñ E-mail вимкнуто адмініÑтратором.', + 'Close this project' => 'Закрити цей проєкт', + 'Open this project' => 'Відкрити цей проєкт', + 'Close a project' => 'Закрити проєкт', + 'Do you really want to close this project: "%s"?' => 'ДійÑно закрити проєкт "%s"?', + 'Reopen a project' => 'Повторне Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ñ‚Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ñƒ', + 'Do you really want to reopen this project: "%s"?' => 'Повторно відкрити проєкт "%s"?', + 'This project is open' => 'Проєкт відкритий', + 'This project is closed' => 'Проєкт закритий', + 'Unable to upload files, check the permissions of your data folder.' => 'Ðе вдалоÑÑ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶Ð¸Ñ‚Ð¸ файли, перевірте дозволи вÑтановлені на директорію даних.', + 'Another category with the same name exists in this project' => 'ÐšÐ°Ñ‚ÐµÐ³Ð¾Ñ€Ñ–Ñ Ð· таким іменем вже Ñ–Ñнує в цьому проєкті', + 'Comment sent by email successfully.' => 'Коментар уÑпішно надіÑлано на E-mail.', + 'Sent by email to "%s" (%s)' => 'ÐадіÑлано на E-mail "%s" (%s)', + 'Unable to read uploaded file.' => 'Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ завантажений файл.', + 'Database uploaded successfully.' => 'Базу даних уÑпішно завантажено.', + 'Task sent by email successfully.' => 'Задачу уÑпішно надіÑлано на E-mail.', + 'There is no category in this project.' => 'Категорії в проєкті відÑутні.', + 'Send by email' => 'ÐадіÑлати на e-mail', + 'Create and send a comment by email' => 'Створити та надіÑлати коментар на E-mail', + 'Subject' => 'Тема', + 'Upload the database' => 'Завантажити базу даних', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Ви можете завантажити попередньо збережену базу даних SQLite (ÑтиÑнуту Gzip).', + 'Database file' => 'Файл бази даних', + 'Upload' => 'Завантажити', + 'Your project must have at least one active swimlane.' => 'Проєкт має мати хоча б одну активну доріжку.', + 'Project: %s' => 'Проєкт: %s', + 'Automatic action not found: "%s"' => 'Ðвтоматичну дію не знайдено: "%s"', + '%d projects' => '%d проєктів', + '%d project' => '%d проєкт', + 'There is no project.' => 'Проєкти відÑутні.', + 'Sort' => 'Сортувати', + 'Project ID' => 'ID проєкту', + 'Project name' => 'Ðазва проєкту', + 'Public' => 'Публічний', + 'Personal' => 'ПерÑональний', + '%d tasks' => '%d задач', + '%d task' => '%d задача', + 'Task ID' => 'ID задачі', + 'Assign automatically a color when due date is expired' => 'Призначити колір автоматично при порушенні терміну виконаннÑ', + 'Total score in this column across all swimlanes' => 'Сумарна ÑкладніÑть задач в цій колонці на вÑÑ–Ñ… доріжках', + 'HRK - Kuna' => 'HRK – ХорватÑька куна', + 'ARS - Argentine Peso' => 'ARS – ÐргентинÑький пеÑо', + 'COP - Colombian Peso' => 'COP – КолумбійÑький пеÑо', + '%d groups' => '%d груп(и)', + '%d group' => '%d група', + 'Group ID' => 'ID групи', + 'External ID' => 'Зовнішній ID', + '%d users' => '%d кориÑтувачі(в)', + '%d user' => '%d кориÑтувач', + 'Hide subtasks' => 'Приховати підзадачі', + 'Show subtasks' => 'Показати підзадачі', + 'Authentication Parameters' => 'Параметри автентифікації', + 'API Access' => 'ДоÑтуп до API', + 'No users found.' => 'КориÑтувачі відÑутні.', + 'User ID' => 'ID кориÑтувача', + 'Notifications are activated' => 'Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ ÑƒÐ²Ñ–Ð¼ÐºÐ½ÐµÐ½Ð¾', + 'Notifications are disabled' => 'Ð¡Ð¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð²Ð¸Ð¼ÐºÐ½ÑƒÑ‚Ð¾', + 'User disabled' => 'КориÑтувача заблоковано', + '%d notifications' => '%d Ñповіщень', + '%d notification' => '%d ÑповіщеннÑ', + 'There is no external integration installed.' => 'Зовнішніх інтеграцій не вÑтановлено.', + 'You are not allowed to update tasks assigned to someone else.' => 'Ви не маєте дозволу змінювати задачі призначені комуÑÑŒ іншому.', + 'You are not allowed to change the assignee.' => 'Ви не маєте дозволу змінювати відповідального.', + 'Task suppression is not permitted' => 'Ð’Ð¸Ð´Ð°Ð»ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡ заборонене', + 'Changing assignee is not permitted' => 'Зміна відповідального заборонена', + 'Update only assigned tasks is permitted' => 'Дозволене Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð»Ð¸ÑˆÐµ призначених задач', + 'Only for tasks assigned to the current user' => 'Лише задачі призначені поточному кориÑтувачу', + 'My projects' => 'Мої проєкти', + 'You are not a member of any project.' => 'Ви не Ñ” учаÑником жодного з проєктів.', + 'My subtasks' => 'Мої підзадачі', + '%d subtasks' => '%d підзадач', + '%d subtask' => '%d підзадача', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'ÐŸÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð·Ð°Ð´Ð°Ñ‡ обмежити до призначених поточному кориÑтувачу та між колоноками', + '[DUPLICATE]' => '[КОПІЯ]', + 'DKK - Danish Krona' => 'DKK – ДатÑька крона', + 'Remove user from group' => 'Вилучити кориÑтувача з групи', + 'Assign the task to its creator' => 'Призначити задачу автору', + 'This task was sent by email to "%s" with subject "%s".' => 'Задачу відправлено на E-mail "%s" з темою лиÑта "%s".', + 'Predefined Email Subjects' => 'Шаблонні теми E-mail', + 'Write one subject by line.' => 'По одній темі на Ñ€Ñдок.', + 'Create another link' => 'Створити ще один зв\'Ñзок', + 'BRL - Brazilian Real' => 'BRL – БразильÑький реал', + 'Add a new Kanboard task' => 'Додати нову задачу в Kanboard', + 'Subtask not started' => 'Підзадача в очікуванні', + 'Subtask currently in progress' => 'Йде робота над підзадачею', + 'Subtask completed' => 'Підзадачу завершено', + 'Subtask added successfully.' => 'Підзадачу уÑпішно додано.', + '%d subtasks added successfully.' => '%d підзадач уÑпішно додано.', + 'Enter one subtask by line.' => 'По одній підзадачі на Ñ€Ñдок.', + 'Predefined Contents' => 'Шаблони вміÑту', + 'Predefined contents' => 'Шаблони вміÑту', + 'Predefined Task Description' => 'Шаблон опиÑу задачі', + 'Do you really want to remove this template? "%s"' => 'ДійÑно видалити шаблон "%s"?', + 'Add predefined task description' => 'Додати шаблон опиÑу задачі', + 'Predefined Task Descriptions' => 'Шаблонні опиÑи задач', + 'Template created successfully.' => 'Шаблон уÑпішно Ñтворено.', + 'Unable to create this template.' => 'Ðе вдалоÑÑ Ñтворити шаблон.', + 'Template updated successfully.' => 'Шаблон уÑпішно оновлено.', + 'Unable to update this template.' => 'Ðе вдалоÑÑ Ð¾Ð½Ð¾Ð²Ð¸Ñ‚Ð¸ шаблон.', + 'Template removed successfully.' => 'Шаблон уÑпішно видалено.', + 'Unable to remove this template.' => 'Ðе вдалоÑÑ Ð²Ð¸Ð´Ð°Ð»Ð¸Ñ‚Ð¸ шаблон.', + 'Template for the task description' => 'ÐžÐ¿Ð¸Ñ Ð· шаблона', + 'The start date is greater than the end date' => 'Дата початку не може бути пізніше дати завершеннÑ', + 'Tags must be separated by a comma' => 'Мітки розділÑютьÑÑ ÐºÐ¾Ð¼Ð¾ÑŽ', + 'Only the task title is required' => 'Обов\'Ñзковим полем Ñ” лише заголовок задачі', + 'Creator Username' => 'Логін автора', + 'Color Name' => 'Ðазва кольору', + 'Column Name' => 'Ðазва колонки', + 'Swimlane Name' => 'Ðазва доріжки', + 'Time Estimated' => 'Оцінка чаÑу', + 'Time Spent' => 'Витрачений чаÑ', + 'External Link' => 'Зовнішнє поÑиланнÑ', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Ð¦Ñ Ñ„ÑƒÐ½ÐºÑ†Ñ–Ñ Ð²Ð¼Ð¸ÐºÐ°Ñ” Ð´Ð»Ñ Ð¿Ñ€Ð¾Ñ”ÐºÑ‚Ð° Ñтрічки iCal та RSS, а також публічний переглÑд дошки.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Зупинити таймери вÑÑ–Ñ… підзадач при переміщенні до іншої колонки', + 'Subtask Title' => 'Заголовок підзадачі', + 'Add a subtask and activate the timer when moving a task to another column' => 'Додати підзадачу та увімкнути таймер при переміщенні задачі до іншої колонки', + 'days' => 'днів', + 'minutes' => 'хвилин', + 'seconds' => 'Ñекунд', + 'Assign automatically a color when preset start date is reached' => 'Ðвтоматично вÑтановити колір при наÑтанні вказаної дати', + 'Move the task to another column once a predefined start date is reached' => 'ПереміÑтити задачу до іншої колонки при наÑтанні вказаної дати', + 'This task is now linked to the task %s with the relation "%s"' => 'Задача зв\'Ñзана з задачею %s відношеннÑм "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Зв\'Ñз відношеннÑм "%s" із задачею %s вилучено', + 'Custom Filter:' => 'ВлаÑний фільтр:', + 'Unable to find this group.' => 'Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ таку групу.', + '%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 moved the task #%d to the swimlane "%s"' => '%s переміÑтив задачу #%d на доріжку "%s"', + '%sh spent' => '%sh витрачено', + '%sh estimated' => '%sh за оцінкою', + 'Select All' => 'Вибрати вÑе', + 'Unselect All' => 'Прибрати вибір', + 'Apply action' => 'ЗаÑтоÑувати дію', + 'Move selected tasks to another column or swimlane' => 'ПереміÑтити вибрані задачі до іншої колонки', + 'Edit tasks in bulk' => 'Відредагувати багато задач одразу', + 'Choose the properties that you would like to change for the selected tasks.' => 'Оберіть влаÑтивоÑті, Ñкі ви бажаєте змінити в обраних задачах.', + 'Configure this project' => 'Ðалаштувати цей проєкт', + 'Start now' => 'Почати зараз', + '%s removed a file from the task #%d' => '%s видалив файл із задачі #%d', + 'Attachment removed from task #%d: %s' => 'Ð’ÐºÐ»Ð°Ð´ÐµÐ½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾ із задачі #%d: %s', + 'No color' => 'Без кольору', + 'Attachment removed "%s"' => 'Ð’ÐºÐ»Ð°Ð´ÐµÐ½Ð½Ñ Ð²Ð¸Ð´Ð°Ð»ÐµÐ½Ð¾ "%s"', + '%s removed a file from the task %s' => '%s видалив файл із задачі %s', + 'Move the task to another swimlane when assigned to a user' => 'ПереміÑтити цю задачу на іншу доріжку, коли призначено кориÑтувачу', + 'Destination swimlane' => 'Доріжка призначеннÑ', + 'Assign a category when the task is moved to a specific swimlane' => 'Призначити категорію, коли Ñ†Ñ Ð·Ð°Ð´Ð°Ñ‡Ð° мереміщена на вказану доріжку', + 'Move the task to another swimlane when the category is changed' => 'ПереміÑтити цю задачу на іншу доріжку при зміні категорії', + 'Reorder this column by priority (ASC)' => 'ВпорÑдкувати цю колонку за зроÑтаннÑм пріоритету', + 'Reorder this column by priority (DESC)' => 'ВпорÑдкувати цю колонку за ÑпаданнÑм пріоритету', + 'Reorder this column by assignee and priority (ASC)' => 'ВпорÑдкувати цю колонку за відповідальним та зроÑтаннÑм пріоритету', + 'Reorder this column by assignee and priority (DESC)' => 'ВпорÑдкувати цю колонку за відповідальним та ÑпаданнÑм пріоритету', + 'Reorder this column by assignee (A-Z)' => 'ВпорÑдкувати цю колонку за відповідальним (Ð-Я)', + 'Reorder this column by assignee (Z-A)' => 'ВпорÑдкувати цю колонку за відповідальним (Я-Ð)', + 'Reorder this column by due date (ASC)' => 'ВпорÑдкувати цю колонку за зроÑтаннÑм терміну виконаннÑ', + 'Reorder this column by due date (DESC)' => 'ВпорÑдкувати цю колонку за ÑпаданнÑм терміну виконаннÑ', + 'Reorder this column by id (ASC)' => 'ВпорÑдкувати цю колонку за ID (ASC)', + 'Reorder this column by id (DESC)' => 'ВпорÑдкувати цю колонку за ID (DESC)', + '%s moved the task #%d "%s" to the project "%s"' => '%s переміÑтив задачу #%d "%s" до проєкту "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Задача #%d "%s" переміщено до проєкту "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'ПереміÑтити цю задачу до іншої колонки, коли до терміну Ð²Ð¸ÐºÐ¾Ð½Ð°Ð½Ð½Ñ Ð·Ð°Ð»Ð¸ÑˆÐ¸Ð»Ð¾ÑÑ Ð¼ÐµÐ½ÑˆÐµ вказаного чиÑла днів', + 'Automatically update the start date when the task is moved away from a specific column' => 'Ðвтоматично оновлювати дату початку, коли задачу переміщено із вказаної колонки', + 'HTTP Client:' => 'HTTP-клієнт:', + 'Assigned' => 'Призначені будь-кому', + 'Task limits apply to each swimlane individually' => 'Ліміт задач заÑтоÑовуєтьÑÑ Ð´Ð¾ кожної доріжки окремо', + 'Column task limits apply to each swimlane individually' => 'Ліміт задач у колонці заÑтоÑовуєтьÑÑ Ð´Ð¾ кожної доріжки окремо', + 'Column task limits are applied to each swimlane individually' => 'Ліміт задач у колонці заÑтоÑовано до кожної доріжки окремо', + 'Column task limits are applied across swimlanes' => 'Ліміт задач у колонці заÑтоÑовуєтьÑÑ Ð´Ð¾ вÑÑ–Ñ… доріжок разом', + 'Task limit: ' => 'Ліміт задач:', + 'Change to global tag' => 'Перетворити на Ñпільну мітку', + 'Do you really want to make the tag "%s" global?' => 'ДійÑно зробити мітку "%s" Ñпільною?', + 'Enable global tags for this project' => 'Увімкнути Ñпільні мітки Ð´Ð»Ñ Ñ†ÑŒÐ¾Ð³Ð¾ проєкту', + 'Group membership(s):' => 'ÐалежніÑть до груп:', + '%s is a member of the following group(s): %s' => '%s належить до наÑтупних груп: %s', + '%d/%d group(s) shown' => '%d/%d груп показано', + 'Subtask creation or modification' => 'Ð¡Ñ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ‡Ð¸ Ñ€ÐµÐ´Ð°Ð³ÑƒÐ²Ð°Ð½Ð½Ñ Ð¿Ñ–Ð´Ð·Ð°Ð´Ð°Ñ‡', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Призначити виконавцем задачі заданого кориÑтувача, коли задачу переміщено до вказаної доріжки', + 'Comment' => 'Коментар', + 'Collapse vertically' => 'Згорнути вертикально', + 'Expand vertically' => 'Розгорнути вертикально', + 'MXN - Mexican Peso' => 'MXN - МекÑиканÑький пеÑо', + 'Estimated vs actual time per column' => 'Запланований та реально витрачений Ñ‡Ð°Ñ Ð¿Ð¾ колонках', + 'HUF - Hungarian Forint' => 'HUF - УгорÑький форинт', + 'XBT - Bitcoin' => 'XBT – Біткоїн', + 'You must select a file to upload as your avatar!' => 'Ви повинні вибрати файл Ð´Ð»Ñ Ð·Ð°Ð²Ð°Ð½Ñ‚Ð°Ð¶ÐµÐ½Ð½Ñ Ñƒ ÑкоÑті вашого аватару!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Вибраний файл не Ñ” зображеннÑм! (ДопуÑтимі розширеннÑ: *.gif, *.jpg, *.jpeg, *.png)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Ðвтоматично призначити дату завершеннÑ, Ñкщо Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ðµ із зазначеної колонки', + 'No other projects found.' => 'Інших проектів не знайдено.', + 'Tasks copied successfully.' => 'Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ ÑƒÑпішно Ñкопійовано.', + 'Unable to copy tasks.' => 'Ðе вдалоÑÑ Ñкопіювати завданнÑ.', + 'Theme' => 'Тема', + 'Theme:' => 'Тема:', + 'Light theme' => 'Світла тема', + 'Dark theme' => 'Темна тема', + 'Automatic theme - Sync with system' => 'Ðвтоматичний вибір - викориÑтовувати ÑиÑтемні налаштуваннÑ', + 'Application managers or more' => 'Менеджери додатків або більше', + 'Administrators' => 'ÐдмініÑтратори', + 'Visibility:' => 'ВидиміÑть:', + 'Standard users' => 'Стандартні кориÑтувачі', + 'Visibility is required' => 'ВидиміÑть обов’Ñзкова', + 'The visibility should be an app role' => 'ВидиміÑть має бути роллю додатка', + 'Reply' => 'ВідповіÑти', + '%s wrote: ' => '%s напиÑав: ', + 'Number of visible tasks in this column and swimlane' => 'КількіÑть видимих ​​завдань у цьому Ñтовпці та доріжці', + 'Number of tasks in this swimlane' => 'КількіÑть завдань у цій доріжці', + 'Unable to find another subtask in progress, you can close this window.' => 'Ðе вдаєтьÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ інше Ð¿Ñ–Ð´Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ñƒ процеÑÑ–, можна закрити це вікно.', + 'This theme is invalid' => 'Ð¦Ñ Ñ‚ÐµÐ¼Ð° недійÑна', + 'This role is invalid' => 'Ð¦Ñ Ñ€Ð¾Ð»ÑŒ недійÑна', + 'This timezone is invalid' => 'Цей чаÑовий поÑÑ Ð½ÐµÐ´Ñ–Ð¹Ñний', + 'This language is invalid' => 'Ð¦Ñ Ð¼Ð¾Ð²Ð° недійÑна', + 'This URL is invalid' => 'Ð¦Ñ URL-адреÑа недійÑна', + 'Date format invalid' => 'ÐедійÑний формат дати', + 'Time format invalid' => 'ÐедійÑний формат чаÑу', + 'Invalid Mail transport' => 'ÐедійÑний транÑпорт пошти', + 'Color invalid' => 'Колір недійÑний', + 'This value must be greater or equal to %d' => 'Це Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð¼Ð°Ñ” бути більше або дорівнювати %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Додайте BOM на початку файлу (обов’Ñзково Ð´Ð»Ñ Microsoft Excel)', + 'Just add these tag(s)' => 'ПроÑто додайте ці теги', + 'Remove internal link(s)' => 'Видалити внутрішні поÑиланнÑ', + 'Import tasks from another project' => 'Імпортувати Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð· іншого проекту', + 'Select the project to copy tasks from' => 'Виберіть проект, з Ñкого потрібно Ñкопіювати завданнÑ', + 'The total maximum allowed attachments size is %sB.' => 'Загальний макÑимально дозволений розмір вкладень Ñтановить %sБ.', + 'Add attachments' => 'Додати вкладеннÑ', + 'Task #%d "%s" is overdue' => 'Задача #%d "%s" проÑтрочена', + 'Enable notifications by default for all new users' => 'Увімкнути ÑÐ¿Ð¾Ð²Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð·Ð° замовчуваннÑм Ð´Ð»Ñ Ð²ÑÑ–Ñ… нових кориÑтувачів', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Призначати Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¹Ð¾Ð³Ð¾ автору Ð´Ð»Ñ Ð²Ð¸Ð·Ð½Ð°Ñ‡ÐµÐ½Ð¸Ñ… колонок, Ñкщо виконавець не вÑтановлений вручну', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Призначати Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð¿Ð¾Ñ‚Ð¾Ñ‡Ð½Ð¾Ð¼Ñƒ кориÑтувачу під Ñ‡Ð°Ñ Ð¿ÐµÑ€ÐµÐ¼Ñ–Ñ‰ÐµÐ½Ð½Ñ Ð² указану колонку, Ñкщо нікого не призначено', +]; diff --git a/app/Locale/vi_VN/translations.php b/app/Locale/vi_VN/translations.php new file mode 100644 index 0000000..3985a27 --- /dev/null +++ b/app/Locale/vi_VN/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => ',', + 'number.thousands_separator' => ' ', + 'None' => 'Chưa có', + 'Edit' => 'Chỉnh sá»­a', + 'Remove' => 'Xóa bá»', + 'Yes' => 'Có', + 'No' => 'Không', + 'cancel' => 'há»§y bá»', + 'or' => 'hoặc là', + 'Yellow' => 'Màu vàng', + 'Blue' => 'Màu xanh da trá»i', + 'Green' => 'Màu xanh lá', + 'Purple' => 'Màu tím', + 'Red' => 'Äá»', + 'Orange' => 'Trái cam', + 'Grey' => 'Xám', + 'Brown' => 'Nâu', + 'Deep Orange' => 'Deep Orange', + 'Dark Grey' => 'Màu xám Ä‘en', + 'Pink' => 'Hồng', + 'Teal' => 'Teal', + 'Cyan' => ' Cyan', + 'Lime' => 'Vôi', + 'Light Green' => 'Màu xanh lợt', + 'Amber' => 'Amber', + 'Save' => 'Lưu', + 'Login' => 'Äăng nhập', + 'Official website:' => 'Trang web chinh thưc:', + 'Unassigned' => 'Chưa được gán', + 'View this task' => 'Xem công việc này', + 'Remove user' => 'Xóa ngưá»i dùng', + 'Do you really want to remove this user: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» ngưá»i dùng này: "%s"?', + 'All users' => 'Tất cả ngưá»i dùng', + 'Username' => 'Tên đăng nhập', + 'Password' => 'Mật khẩu', + 'Administrator' => 'Ngưá»i quản lý', + 'Sign in' => 'Äăng nhập', + 'Users' => 'Ngưá»i dùng', + 'Forbidden' => 'Forbidden', + 'Access Forbidden' => 'Truy cập bị cấm', + 'Edit user' => 'Chỉnh sá»­a ngưá»i dùng', + 'Logout' => 'Äăng xuất', + 'Bad username or password' => 'Tên đăng nhập hoặc mật khẩu xâu', + 'Edit project' => 'Chỉnh sá»­a dá»± án', + 'Name' => 'Tên', + 'Projects' => 'Dá»± án', + 'No project' => 'Không có dá»± án', + 'Project' => 'Dá»± án', + 'Status' => 'Trạng thái', + 'Tasks' => 'Nhiệm vụ', + 'Board' => 'Bảng', + 'Actions' => 'Hành động', + 'Inactive' => 'Không hoạt động', + 'Active' => 'Hoạt động', + 'Unable to update this board.' => 'Không thể cập nhật bảng này.', + 'Disable' => 'Vô hiệu hóa', + 'Enable' => 'Bật', + 'New project' => 'Dá»± án má»›i', + 'Do you really want to remove this project: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» dá»± án này: "%s"?', + 'Remove project' => 'Loại bá» dá»± án', + 'Edit the board for "%s"' => 'Chỉnh sá»­a bảng cho "%s"', + 'Add a new column' => 'Thêm cá»™t má»›i', + 'Title' => 'Chức vụ', + 'Assigned to %s' => 'ÄÆ°á»£c giao cho %s', + 'Remove a column' => 'Loại bá» má»™t cá»™t', + 'Unable to remove this column.' => 'Không thể xoá cá»™t này.', + 'Do you really want to remove this column: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» cá»™t này: "%s"?', + 'Settings' => 'Cài đặt', + 'Application settings' => 'Cài đặt ứng dụng', + 'Language' => 'Ngôn ngữ', + 'Webhook token:' => 'Mã thông báo Webhook:', + 'API token:' => 'Mã thông báo API:', + 'Database size:' => 'Kích thước cÆ¡ sở dữ liệu:', + 'Download the database' => 'Tải vá» cÆ¡ sở dữ liệu', + 'Optimize the database' => 'Tối ưu hóa cÆ¡ sở dữ liệu', + '(VACUUM command)' => '(Lệnh VACUUM)', + '(Gzip compressed Sqlite file)' => '(Gzip nén tập tin Sqlite)', + 'Close a task' => 'Äóng má»™t nhiệm vụ', + 'Column' => 'Cá»™t', + 'Color' => 'Màu', + 'Assignee' => 'Ngưá»i được chuyển nhượng', + 'Create another task' => 'Tạo má»™t nhiệm vụ khác', + 'New task' => 'Nhiệm vụ má»›i', + 'Open a task' => 'Mở má»™t nhiệm vụ', + 'Do you really want to open this task: "%s"?' => 'Bạn có thá»±c sá»± muốn mở công việc này: "%s"?', + 'Back to the board' => 'Trở lại bảng', + 'There is nobody assigned' => 'Không ai được chỉ định', + 'Column on the board:' => 'Cá»™t trên bảng:', + 'Close this task' => 'Äóng nhiệm vụ này', + 'Open this task' => 'Mở nhiệm vụ này', + 'There is no description.' => 'Không có mô tả.', + 'Add a new task' => 'Thêm má»™t nhiệm vụ má»›i', + 'The username is required' => 'Tên ngưá»i dùng được yêu cầu', + 'The maximum length is %d characters' => 'Chiá»u dài tối Ä‘a là %d ký tá»±', + 'The minimum length is %d characters' => 'Chiá»u dài tối thiểu là %d ký tá»±', + 'The password is required' => 'Mật khẩu là bắt buá»™c', + 'This value must be an integer' => 'Giá trị này phải là má»™t số nguyên', + 'The username must be unique' => 'Tên ngưá»i dùng phải là duy nhất', + 'The user id is required' => 'Ngưá»i sá»­ dụng id là bắt buá»™c', + 'Passwords don\'t match' => 'Mật khẩu don \'t match', + 'The confirmation is required' => 'Cần xác nhận', + 'The project is required' => 'Dá»± án được yêu cầu', + 'The id is required' => 'Id là bắt buá»™c', + 'The project id is required' => 'Id dá»± án được yêu cầu', + 'The project name is required' => 'Tên cá»§a dá»± án được yêu cầu', + 'The title is required' => 'Tiêu đỠlà bắt buá»™c', + 'Settings saved successfully.' => 'Cài đặt đã lưu thành công.', + 'Unable to save your settings.' => 'Không thể lưu cài đặt cá»§a bạn.', + 'Database optimization done.' => 'Tối ưu hóa cÆ¡ sở dữ liệu được thá»±c hiện.', + 'Your project has been created successfully.' => 'Dá»± án cá»§a bạn đã được tạo thành công.', + 'Unable to create your project.' => 'Không thể tạo dá»± án cá»§a bạn.', + 'Project updated successfully.' => 'Dá»± án được cập nhật thành công.', + 'Unable to update this project.' => 'Không thể cập nhật dá»± án này.', + 'Unable to remove this project.' => 'Không thể xóa dá»± án này.', + 'Project removed successfully.' => 'Dá»± án đã được loại bá» thành công.', + 'Project activated successfully.' => 'Dá»± án được kích hoạt thành công.', + 'Unable to activate this project.' => 'Không thể kích hoạt dá»± án này.', + 'Project disabled successfully.' => 'Dá»± án bị vô hiệu thành công.', + 'Unable to disable this project.' => 'Không thể vô hiệu hoá dá»± án này.', + 'Unable to open this task.' => 'Không thể mở nhiệm vụ này.', + 'Task opened successfully.' => 'Nhiệm vụ thành công.', + 'Unable to close this task.' => 'Không thể đóng nhiệm vụ này.', + 'Task closed successfully.' => 'Nhiệm vụ đóng thành công.', + 'Unable to update your task.' => 'Không thể cập nhật công việc cá»§a bạn.', + 'Task updated successfully.' => 'Nhiệm vụ được cập nhật thành công.', + 'Unable to create your task.' => 'Không thể tạo ra nhiệm vụ cá»§a bạn.', + 'Task created successfully.' => 'Nhiệm vụ thành công.', + 'User created successfully.' => 'Ngưá»i dùng đã thành công.', + 'Unable to create your user.' => 'Không thể tạo ngưá»i dùng cá»§a bạn.', + 'User updated successfully.' => 'Ngưá»i dùng cập nhật thành công.', + 'User removed successfully.' => 'Ngưá»i dùng đã xoá thành công.', + 'Unable to remove this user.' => 'Không thể xóa ngưá»i dùng này.', + 'Board updated successfully.' => 'Bảng đã được cập nhật thành công.', + 'Ready' => 'Sẳn sàng', + 'Backlog' => 'Backlog', + 'Work in progress' => 'Công việc Ä‘ang tiến hành', + 'Done' => 'Làm xong', + 'Application version:' => 'Phiên bản ứng dụng:', + 'Id' => 'ID', + 'Public link' => 'Liên kết công cá»™ng', + 'Timezone' => 'Múi giá»', + 'Sorry, I didn\'t find this information in my database!' => 'Xin lá»—i, tôi không tìm thấy thông tin này trong cÆ¡ sở dữ liệu cá»§a tôi!', + 'Page not found' => 'Trang không tìm thấy', + 'Complexity' => 'Sá»± phức tạp', + 'Task limit' => 'Nhiệm vụ giá»›i hạn', + 'Task count' => 'Nhiệm vụ', + 'User' => 'Ngưá»i dùng', + 'Comments' => 'Bình luận', + 'Comment is required' => 'Bình luận là bắt buá»™c', + 'Comment added successfully.' => 'Bình luận được thêm thành công.', + 'Unable to create your comment.' => 'Không thể tạo bình luận cá»§a bạn.', + 'Due Date' => 'Ngày đáo hạn', + 'Invalid date' => 'Ngày không hợp lệ', + 'Automatic actions' => 'Hành động tá»± động', + 'Your automatic action has been created successfully.' => 'Hành động tá»± động cá»§a bạn đã được tạo thành công.', + 'Unable to create your automatic action.' => 'Không thể tạo hành động tá»± động cá»§a bạn.', + 'Remove an action' => 'Loại bá» má»™t hành động', + 'Unable to remove this action.' => 'Không thể loại bá» hành động này.', + 'Action removed successfully.' => 'Hành động đã được xoá thành công.', + 'Automatic actions for the project "%s"' => 'Hành động tá»± động cho dá»± án "%s"', + 'Add an action' => 'Thêm má»™t hành động', + 'Event name' => 'Tên sá»± kiện', + 'Action' => 'Hoạt động', + 'Event' => 'Biến cố', + 'When the selected event occurs execute the corresponding action.' => 'Khi sá»± kiện đã chá»n xảy ra, hãy thá»±c hiện hành động tương ứng.', + 'Next step' => 'Bước tiếp theo', + 'Define action parameters' => 'Xác định các tham số hành động', + 'Do you really want to remove this action: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» hành động này: "%s"?', + 'Remove an automatic action' => 'Há»§y bá» má»™t hành động tá»± động', + 'Assign the task to a specific user' => 'Chỉ định nhiệm vụ cho má»™t ngưá»i dùng cụ thể', + 'Assign the task to the person who does the action' => 'Chỉ định nhiệm vụ cho ngưá»i thá»±c hiện hành động', + 'Duplicate the task to another project' => 'Nhân đôi công việc vá»›i má»™t dá»± án khác', + 'Move a task to another column' => 'Di chuyển má»™t nhiệm vụ tá»›i má»™t cá»™t khác', + 'Task modification' => 'Sá»­a đổi tác vụ', + 'Task creation' => 'Nhiệm vụ sáng tạo', + 'Closing a task' => 'Äóng má»™t nhiệm vụ', + 'Assign a color to a specific user' => 'Chỉ định màu cho má»™t ngưá»i dùng cụ thể', + 'Position' => 'Chức vụ', + 'Duplicate to project' => 'Nhân bản vá»›i má»™t dá»± án khác', + 'Duplicate' => 'Bản sao', + 'Link' => 'Liên kết', + 'Comment updated successfully.' => 'Bình luận cập nhật thành công.', + 'Unable to update your comment.' => 'Không thể cập nhật nhận xét cá»§a bạn.', + 'Remove a comment' => 'Xóa má»™t bình luận', + 'Comment removed successfully.' => 'Äã xóa nhận xét thành công.', + 'Unable to remove this comment.' => 'Không thể xóa nhận xét này.', + 'Do you really want to remove this comment?' => 'Bạn có thật sá»± muốn xóa nhận xét này?', + 'Current password for the user "%s"' => 'Mật khẩu hiện tại cho ngưá»i dùng "%s"', + 'The current password is required' => 'Mật khẩu hiện tại là bắt buá»™c', + 'Wrong password' => 'Sai mật khẩu', + 'Unknown' => 'Không xác định', + 'Last logins' => 'Äăng nhập lần cuối', + 'Login date' => 'Ngày đăng nhập', + 'Authentication method' => 'Phương pháp xác thá»±c', + 'IP address' => 'Äịa chỉ IP', + 'User agent' => 'Äại lý ngưá»i dùng', + 'Persistent connections' => 'Kết nối liên tục', + 'No session.' => 'Không có phiên.', + 'Expiration date' => 'Ngày hết hạn', + 'Remember Me' => 'Nhá»› tôi', + 'Creation date' => 'Ngày thành lập', + 'Everybody' => 'Má»i ngưá»i', + 'Open' => 'Mở', + 'Closed' => 'Äóng', + 'Search' => 'Tìm kiếm', + 'Nothing found.' => 'Không kết quả.', + 'Due date' => 'Ngày đáo hạn', + 'Description' => 'Mô tả', + '%d comments' => '%d nhận xét', + '%d comment' => '%d nhận xét', + 'Email address invalid' => 'Äịa chỉ email không hợp lệ', + 'Your external account is not linked anymore to your profile.' => 'Tài khoản bên ngoài cá»§a bạn không được liên kết vá»›i hồ sÆ¡ cá»§a bạn nữa.', + 'Unable to unlink your external account.' => 'Không thể há»§y liên kết tài khoản bên ngoài cá»§a bạn.', + 'External authentication failed' => 'Xác thá»±c bên ngoài không thành công', + 'Your external account is linked to your profile successfully.' => 'Tài khoản bên ngoài cá»§a bạn được liên kết vá»›i hồ sÆ¡ cá»§a bạn thành công.', + 'Email' => 'E-mail', + 'Task removed successfully.' => 'Nhiệm vụ đã được gỡ bá» thành công.', + 'Unable to remove this task.' => 'Không thể xóa tác vụ này.', + 'Remove a task' => 'Loại bá» má»™t nhiệm vụ', + 'Do you really want to remove this task: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» công việc này: "%s"?', + 'Assign automatically a color based on a category' => 'Chỉ định tá»± động má»™t màu dá»±a trên má»™t loại', + 'Assign automatically a category based on a color' => 'Chỉ định tá»± động má»™t thể loại dá»±a trên màu', + 'Task creation or modification' => 'Tạo tác hoặc sá»­a đổi nhiệm vụ', + 'Category' => 'Thể loại', + 'Category:' => 'Thể loại:', + 'Categories' => 'Thể loại', + 'Your category has been created successfully.' => 'Danh mục cá»§a bạn đã được tạo thành công.', + 'This category has been updated successfully.' => 'Danh mục này đã được cập nhật thành công.', + 'Unable to update this category.' => 'Không thể cập nhật danh mục này.', + 'Remove a category' => 'Loại bá» má»™t danh mục', + 'Category removed successfully.' => 'Loại bá» thành công.', + 'Unable to remove this category.' => 'Không thể xóa loại này.', + 'Category modification for the project "%s"' => 'Sá»­a đổi loại cho dá»± án "%s"', + 'Category Name' => 'Tên danh mục', + 'Add a new category' => 'Thêm má»™t loại má»›i', + 'Do you really want to remove this category: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» loại này: "%s"?', + 'All categories' => 'Tất cả danh mục', + 'No category' => 'Không có loại', + 'The name is required' => 'Tên là bắt buá»™c', + 'Remove a file' => 'Há»§y bá» má»™t tập tin', + 'Unable to remove this file.' => 'Không thể xóa tệp này.', + 'File removed successfully.' => 'Äã xoá tệp thành công.', + 'Attach a document' => 'Äính kèm má»™t tài liệu', + 'Do you really want to remove this file: "%s"?' => 'Bạn có thá»±c sá»± muốn xóa tệp này: "%s"?', + 'Attachments' => 'File đính kèm', + 'Edit the task' => 'Chỉnh sá»­a nhiệm vụ', + 'Add a comment' => 'Thêm nhận xét', + 'Edit a comment' => 'Chỉnh sá»­a má»™t nhận xét', + 'Summary' => 'Tóm lược', + 'Time tracking' => 'Theo dõi thá»i gian', + 'Estimate:' => 'Ước tính:', + 'Spent:' => 'Spent:', + 'Do you really want to remove this sub-task?' => 'Bạn có thật sá»± muốn loại bá» nhiệm vụ phụ này?', + 'Remaining:' => 'Còn lại:', + 'hours' => 'giá»', + 'estimated' => 'ước tính', + 'Sub-Tasks' => 'Nhiệm vụ phụ', + 'Add a sub-task' => 'Thêm má»™t nhiệm vụ phụ', + 'Original estimate' => 'Ước tính ban đầu', + 'Create another sub-task' => 'Tạo má»™t nhiệm vụ phụ khác', + 'Time spent' => 'Thá»i gian đã bá» ra', + 'Edit a sub-task' => 'Chỉnh sá»­a má»™t tiểu nhiệm vụ', + 'Remove a sub-task' => 'Loại bá» má»™t nhiệm vụ phụ', + 'The time must be a numeric value' => 'Thá»i gian phải là má»™t giá trị số', + 'Todo' => 'Làm', + 'In progress' => 'Trong tiến trình', + 'Sub-task removed successfully.' => 'Nhiệm vụ phụ được xoá thành công.', + 'Unable to remove this sub-task.' => 'Không thể loại bá» nhiệm vụ phụ này.', + 'Sub-task updated successfully.' => 'Tiểu nhiệm vụ được cập nhật thành công.', + 'Unable to update your sub-task.' => 'Không thể cập nhật tiểu nhiệm vụ cá»§a bạn.', + 'Unable to create your sub-task.' => 'Không thể tạo ra nhiệm vụ phụ cá»§a bạn.', + 'Maximum size: ' => 'Kích thước tối Ä‘a: ', + 'Display another project' => 'Hiển thị má»™t dá»± án khác', + 'Created by %s' => 'Äã tạo bởi %s', + 'Tasks Export' => 'Nhiệm vụ xuất khẩu', + 'Start Date' => 'Ngày bắt đầu', + 'Execute' => 'Thá»±c hiện', + 'Task Id' => 'Nhiệm vụ Id', + 'Creator' => 'Ngưá»i sáng tạo', + 'Modification date' => 'Ngày sá»­a đổi', + 'Completion date' => 'Ngày kết thúc', + 'Clone' => 'Clone', + 'Project cloned successfully.' => 'Dá»± án nhân bản thành công.', + 'Unable to clone this project.' => 'Không thể nhân bản dá»± án này.', + 'Enable email notifications' => 'Bật thông báo qua email', + 'Task position:' => 'Nhiệm vụ:', + 'The task #%d has been opened.' => 'Nhiệm vụ #%d đã được mở ra.', + 'The task #%d has been closed.' => 'Nhiệm vụ #%d đã được đóng lại.', + 'Sub-task updated' => 'Tiểu nhiệm vụ cập nhật', + 'Title:' => 'Chức vụ:', + 'Status:' => 'Trạng thái:', + 'Assignee:' => 'Ngưá»i được chuyển nhượng:', + 'Time tracking:' => 'Theo dõi thá»i gian:', + 'New sub-task' => 'Nhiệm vụ phụ má»›i', + 'New attachment added "%s"' => 'Tập tin đính kèm má»›i được thêm vào "%s"', + 'New comment posted by %s' => 'Bình luận má»›i được đăng bởi %s', + 'New comment' => 'Bình luận má»›i', + 'Comment updated' => 'Bình luận cập nhật', + 'New subtask' => 'Bổ sung phụ', + 'I only want to receive notifications for these projects:' => 'Tôi chỉ muốn nhận thông báo cho các dá»± án đó:', + 'view the task on Kanboard' => 'xem công việc trên Kanboard', + 'Public access' => 'Truy cập công cá»™ng', + 'Disable public access' => 'Vô hiệu hoá quyá»n truy cập công cá»™ng', + 'Enable public access' => 'Bật quyá»n truy cập công cá»™ng', + 'Public access disabled' => 'Truy cập công cá»™ng bị vô hiệu hóa', + 'Move the task to another project' => 'Di chuyển công việc sang dá»± án khác', + 'Move to project' => 'Chuyển sang dá»± án khác', + 'Do you really want to duplicate this task?' => 'Bạn có thá»±c sá»± muốn lặp lại nhiệm vụ này?', + 'Duplicate a task' => 'Nhân đôi má»™t nhiệm vụ', + 'External accounts' => 'Tài khoản bên ngoài', + 'Account type' => 'Kiểu tài khoản', + 'Local' => 'Äịa phương', + 'Remote' => 'Xa', + 'Enabled' => 'Bật', + 'Disabled' => 'Vô hiệu', + 'Login:' => 'Äăng nhập:', + 'Full Name:' => 'Há» và tên:', + 'Email:' => 'E-mail:', + 'Notifications:' => 'Thông báo:', + 'Notifications' => 'Thông báo', + 'Account type:' => 'Kiểu tài khoản:', + 'Edit profile' => 'Chỉnh sá»­a hồ sÆ¡', + 'Change password' => 'Äổi mật khẩu', + 'Password modification' => 'Sá»­a đổi mật khẩu', + 'External authentications' => 'Xác thá»±c bên ngoài', + 'Never connected.' => 'Không bao giá» kết nối.', + 'No external authentication enabled.' => 'Không bật xác thá»±c bên ngoài.', + 'Password modified successfully.' => 'Äã sá»­a đổi mật khẩu thành công.', + 'Unable to change the password.' => 'Không thể thay đổi mật khẩu.', + 'Change category' => 'Thay đổi loại', + '%s updated the task %s' => ' %s đã cập nhật công việc %s', + '%s opened the task %s' => ' %s mở nhiệm vụ %s', + '%s moved the task %s to the position #%d in the column "%s"' => ' %s di chuyển nhiệm vụ %s đến vị trí #%d trong cá»™t "%s"', + '%s moved the task %s to the column "%s"' => ' %s di chuyển công việc %s đến cá»™t "%s"', + '%s created the task %s' => ' %s đã tạo ra nhiệm vụ %s', + '%s closed the task %s' => ' %s đã đóng công việc %s', + '%s created a subtask for the task %s' => ' %s tạo ra má»™t phụ cho nhiệm vụ %s', + '%s updated a subtask for the task %s' => ' %s cập nhật má»™t công việc phụ cho nhiệm vụ %s', + 'Assigned to %s with an estimate of %s/%sh' => 'ÄÆ°á»£c giao cho %s vá»›i ước tính %s / %sh', + 'Not assigned, estimate of %sh' => 'Không được chỉ định, ước tính %sh', + '%s updated a comment on the task %s' => ' %s đã cập nhật má»™t bình luận vá» nhiệm vụ %s', + '%s commented the task %s' => ' %s bình luận vá» nhiệm vụ %s', + '%s\'s activity' => 'Hoạt động cá»§a %s', + 'RSS feed' => 'Nguồn cấp dữ liệu RSS', + '%s updated a comment on the task #%d' => ' %s đã cập nhật má»™t bình luận vá» nhiệm vụ #%d', + '%s commented on the task #%d' => ' %s bình luận vá» nhiệm vụ #%d', + '%s updated a subtask for the task #%d' => ' %s đã cập nhật má»™t công việc phụ cho nhiệm vụ #%d', + '%s created a subtask for the task #%d' => ' %s tạo ra má»™t phụ cho nhiệm vụ #%d', + '%s updated the task #%d' => ' %s cập nhật công việc #%d', + '%s created the task #%d' => ' %s đã tạo ra nhiệm vụ #%d', + '%s closed the task #%d' => ' %s đã đóng công việc #%d', + '%s opened the task #%d' => ' %s mở nhiệm vụ #%d', + 'Activity' => 'Hoạt động', + 'Default values are "%s"' => 'Giá trị mặc định là "%s"', + 'Default columns for new projects (Comma-separated)' => 'Cá»™t mặc định cho các dá»± án má»›i (tách bằng dấu phẩy)', + 'Task assignee change' => 'Thay đổi nhiệm vụ chuyển nhượng', + '%s changed the assignee of the task #%d to %s' => ' %s đã thay đổi ngưá»i được giao nhiệm vụ #%d thành %s', + '%s changed the assignee of the task %s to %s' => ' %s đã thay đổi ngưá»i được giao nhiệm vụ %s sang %s', + 'New password for the user "%s"' => 'Mật khẩu má»›i cho ngưá»i dùng "%s"', + 'Choose an event' => 'Chá»n má»™t sá»± kiện', + 'Create a task from an external provider' => 'Tạo má»™t nhiệm vụ từ má»™t nhà cung cấp bên ngoài', + 'Change the assignee based on an external username' => 'Thay đổi ngưá»i được chuyển nhượng dá»±a trên tên ngưá»i dùng bên ngoài', + 'Change the category based on an external label' => 'Thay đổi thể loại dá»±a trên má»™t nhãn bên ngoài', + 'Reference' => 'Tài liệu tham khảo', + 'Label' => 'Nhãn', + 'Database' => 'CÆ¡ sở dữ liệu', + 'About' => 'Vá»', + 'Database driver:' => 'Trình Ä‘iá»u khiển cÆ¡ sở dữ liệu:', + 'Board settings' => 'Cài đặt Bảng', + 'Webhook settings' => 'Cài đặt Webhook', + 'Reset token' => 'Äặt lại mã thông báo', + 'API endpoint:' => 'Äiểm cuối API:', + 'Refresh interval for personal board' => 'Khoảng thá»i gian làm má»›i cho bảng cá nhân', + 'Refresh interval for public board' => 'Khoảng thá»i gian làm má»›i cho há»™i đồng quản trị', + 'Task highlight period' => 'Nhiệm vụ thá»i gian nổi bật', + 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => 'Thá»i gian (thứ hai) để xem xét má»™t tác vụ đã được sá»­a đổi gần đây (0 để vô hiệu hoá, 2 ngày theo mặc định)', + 'Frequency in second (60 seconds by default)' => 'Tần số trong giây (mặc định 60 giây)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => 'Tần số trong giây (0 để tắt tính năng này, mặc định là 10 giây)', + 'Application URL' => 'URL ứng dụng', + 'Token regenerated.' => 'Token tái tạo.', + 'Date format' => 'Äịnh dạng ngày tháng', + 'ISO format is always accepted, example: "%s" and "%s"' => 'Äịnh dạng ISO luôn được chấp nhận, ví dụ: "%s" và "%s"', + 'New personal project' => 'Dá»± án riêng tư má»›i', + 'This project is personal' => 'Dá»± án này là riêng tư', + 'Add' => 'Thêm vào', + 'Start date' => 'Ngày bắt đầu', + 'Time estimated' => 'Thá»i gian ước tính', + 'There is nothing assigned to you.' => 'Không có gì được giao cho bạn.', + 'My tasks' => 'Nhiệm vụ cá»§a tôi', + 'Activity stream' => 'Luồng hoạt động', + 'Dashboard' => 'Bảng Ä‘iá»u khiển', + 'Confirmation' => 'Xác nhận', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => 'Tạo má»™t bình luận từ má»™t nhà cung cấp bên ngoài', + 'Project management' => 'Quản lý dá»± án', + 'Columns' => 'Cá»™t', + 'Task' => 'Bài tập', + 'Percentage' => 'Phần trăm', + 'Number of tasks' => 'Số nhiệm vụ', + 'Task distribution' => 'Phân phát nhiệm vụ', + 'Analytics' => 'Phân tích', + 'Subtask' => 'Subtask', + 'User repartition' => 'Phân vùng ngưá»i dùng', + 'Clone this project' => 'Nhân bản dá»± án này', + 'Column removed successfully.' => 'Bá» cá»™t thành công.', + 'Not enough data to show the graph.' => 'Không đủ dữ liệu để hiển thị đồ thị.', + 'Previous' => 'Trước', + 'The id must be an integer' => 'Id phải là má»™t số nguyên', + 'The project id must be an integer' => 'Id dá»± án phải là má»™t số nguyên', + 'The status must be an integer' => 'Trạng thái phải là má»™t số nguyên', + 'The subtask id is required' => 'Cần phải có id phụ Ä‘á»', + 'The subtask id must be an integer' => 'Số thứ tá»± subtask phải là má»™t số nguyên', + 'The task id is required' => 'ID công việc được yêu cầu', + 'The task id must be an integer' => 'ID công việc phải là má»™t số nguyên', + 'The user id must be an integer' => 'ID ngưá»i dùng phải là má»™t số nguyên', + 'This value is required' => 'Giá trị này là bắt buá»™c', + 'This value must be numeric' => 'Giá trị này phải là số', + 'Unable to create this task.' => 'Không thể tạo ra nhiệm vụ này.', + 'Cumulative flow diagram' => 'SÆ¡ đồ luồng tích luỹ', + 'Daily project summary' => 'Tóm tắt dá»± án hàng ngày', + 'Daily project summary export' => 'Tóm tắt dá»± án hàng ngày xuất khẩu', + 'Exports' => 'Xuất khẩu', + 'This export contains the number of tasks per column grouped per day.' => 'Xuất này có chứa số nhiệm vụ cho má»—i cá»™t được nhóm trong má»™t ngày.', + 'Active swimlanes' => 'ÄÆ°á»ng phố hoạt động', + 'Add a new swimlane' => 'Thêm má»™t đưá»ng swimlane má»›i', + 'Default swimlane' => 'Mặc định swimlane', + 'Do you really want to remove this swimlane: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» swimlane này: "%s"?', + 'Inactive swimlanes' => 'Bể swimlane không hoạt động', + 'Remove a swimlane' => 'Há»§y bá» swimlane lá»™i', + 'Swimlane modification for the project "%s"' => 'Sá»­a đổi Swimlane cho dá»± án %s', + 'Swimlane removed successfully.' => 'Swimlane đã được gỡ bá» thành công.', + 'Swimlanes' => 'Swimlanes', + 'Swimlane updated successfully.' => 'Swimlane được cập nhật thành công.', + 'Unable to remove this swimlane.' => 'Không thể tháo dải swimlane ra.', + 'Unable to update this swimlane.' => 'Không thể cập nhật swimlane lá»™i này.', + 'Your swimlane has been created successfully.' => 'Làn sóng cá»§a bạn đã được tạo ra thành công.', + 'Example: "Bug, Feature Request, Improvement"' => 'Ví dụ: "Lá»—i, yêu cầu tính năng, cải tiến"', + 'Default categories for new projects (Comma-separated)' => 'Danh mục mặc định cho các dá»± án má»›i (Phân cách bằng dấu phẩy)', + 'Integrations' => 'Tích hợp', + 'Integration with third-party services' => 'Tích hợp vá»›i các dịch vụ cá»§a bên thứ ba', + 'Subtask Id' => 'Id phụ Ä‘á»', + 'Subtasks' => 'Công việc', + 'Subtasks Export' => 'Nhiệm vụ xuất khẩu', + 'Task Title' => 'Nhiệm vụ', + 'Untitled' => 'Không có tiêu Ä‘á»', + 'Application default' => 'Mặc định ứng dụng', + 'Language:' => 'Ngôn ngữ:', + 'Timezone:' => 'Múi giá»:', + 'All columns' => 'Tất cả các cá»™t', + 'Next' => 'Kế tiếp', + '#%d' => '#%d', + 'All swimlanes' => 'Tất cả swimlane', + 'All colors' => 'Äá»§ màu sắc', + 'Moved to column %s' => 'Äã chuyển đến cá»™t %s', + 'User dashboard' => 'Trang tổng quan cá»§a ngưá»i dùng', + 'Allow only one subtask in progress at the same time for a user' => 'Cho phép chỉ có má»™t phụ đỠđang được tiến hành đồng thá»i cho má»™t ngưá»i dùng', + 'Edit column "%s"' => 'Chỉnh sá»­a cá»™t %s ', + 'Select the new status of the subtask: "%s"' => 'Chá»n trạng thái má»›i cá»§a phụ Ä‘á»: "%s"', + 'Subtask timesheet' => 'Lịch biểu phụ Ä‘á»', + 'There is nothing to show.' => 'Không có gì để hiển thị.', + 'Time Tracking' => 'Theo dõi Thá»i gian', + 'You already have one subtask in progress' => 'Bạn đã có má»™t phụ trong tiến trình', + 'Which parts of the project do you want to duplicate?' => 'Bạn muốn nhân bản phần nào cá»§a dá»± án?', + 'Disallow login form' => 'Disallow form đăng nhập', + 'Start' => 'Khởi đầu', + 'End' => 'Kết thúc', + 'Task age in days' => 'Nhiệm vụ tuổi tác trong ngày', + 'Days in this column' => 'Ngày trong cá»™t này', + '%dd' => '%dd', + 'Add a new link' => 'Thêm má»™t liên kết má»›i', + 'Do you really want to remove this link: "%s"?' => 'Bạn có thá»±c sá»± muốn xoá liên kết này: "%s"?', + 'Do you really want to remove this link with task #%d?' => 'Bạn có thá»±c sá»± muốn loại bá» liên kết này vá»›i công việc #%d?', + 'Field required' => 'Trưá»ng bắt buá»™c', + 'Link added successfully.' => 'Liên kết thành công.', + 'Link updated successfully.' => 'Liên kết được cập nhật thành công.', + 'Link removed successfully.' => 'Liên kết đã xoá thành công.', + 'Link labels' => 'Liên kết nhãn', + 'Link modification' => 'Liên kết sá»­a đổi', + 'Opposite label' => 'Ngược lại nhãn', + 'Remove a link' => 'Há»§y liên kết', + 'The labels must be different' => 'Các nhãn phải khác nhau', + 'There is no link.' => 'Không có liên kết.', + 'This label must be unique' => 'Nhãn này phải là duy nhất', + 'Unable to create your link.' => 'Không thể tạo liên kết cá»§a bạn.', + 'Unable to update your link.' => 'Không thể cập nhật liên kết cá»§a bạn.', + 'Unable to remove this link.' => 'Không thể xóa liên kết này.', + 'relates to' => 'liên quan tá»›i', + 'blocks' => 'khối', + 'is blocked by' => 'bị chặn bởi', + 'duplicates' => 'bản sao', + 'is duplicated by' => 'được nhân đôi bởi', + 'is a child of' => 'là má»™t đứa trẻ', + 'is a parent of' => 'là cha mẹ cá»§a', + 'targets milestone' => 'mục tiêu cá»™t mốc', + 'is a milestone of' => 'là má»™t mốc quan trá»ng cá»§a', + 'fixes' => 'sá»­a lá»—i', + 'is fixed by' => 'được sá»­a bởi', + 'This task' => 'Nhiệm vụ này', + '<1h' => '<1 giá»', + '%dh' => '%dh', + 'Expand tasks' => 'Mở rá»™ng các nhiệm vụ', + 'Collapse tasks' => 'Thu hẹp nhiệm vụ', + 'Expand/collapse tasks' => 'Mở rá»™ng / thu gá»n nhiệm vụ', + 'Close dialog box' => 'Äóng há»™p thoại', + 'Submit a form' => 'Gá»­i má»™t mẫu', + 'Board view' => 'Quan Ä‘iểm cá»§a há»™i đồng quản trị', + 'Keyboard shortcuts' => 'Các phím tắt bàn phím', + 'Open board switcher' => 'Open board switcher', + 'Application' => 'Ứng dụng', + 'Compact view' => 'Chế độ xem nhá» gá»n', + 'Horizontal scrolling' => 'Cuá»™n ngang', + 'Compact/wide view' => 'Chế độ xem nhá» gá»n / rá»™ng', + 'Currency' => 'Tiá»n tệ', + 'Personal project' => 'Dá»± án riêng tư', + 'AUD - Australian Dollar' => 'AUD - Äô la Úc', + 'CAD - Canadian Dollar' => 'CAD - Äô la Canada', + 'CHF - Swiss Francs' => 'CHF - Swiss Francs', + 'Custom Stylesheet' => 'Biểu định kiểu Tuỳ chỉnh', + 'EUR - Euro' => 'EUR - Euro', + 'GBP - British Pound' => 'GBP - Bảng Anh', + 'INR - Indian Rupee' => 'INR - Rupi Ấn Äá»™', + 'JPY - Japanese Yen' => 'JPY - Yên Nhật', + 'NZD - New Zealand Dollar' => 'NZD - Äô la New Zealand', + 'PEN - Peruvian Sol' => 'PEN - Sol Peru', + 'RSD - Serbian dinar' => 'RSD - dinar Serbia', + 'CNY - Chinese Yuan' => 'CNY - Yuan Trung Quốc', + 'USD - US Dollar' => 'Äô la Mỹ - Äô la Mỹ', + 'VES - Venezuelan Bolívar' => 'VES - Bolívar Venezuela', + 'Destination column' => 'Cá»™t đích', + 'Move the task to another column when assigned to a user' => 'Chuyển nhiệm vụ sang cá»™t khác khi được chỉ định cho ngưá»i dùng', + 'Move the task to another column when assignee is cleared' => 'Di chuyển nhiệm vụ sang cá»™t khác khi ngưá»i nhận chuyển giao bị xóa', + 'Source column' => 'Cá»™t nguồn', + 'Transitions' => 'Chuyển tiếp', + 'Executer' => 'Executer', + 'Time spent in the column' => 'Thá»i gian trong cá»™t', + 'Task transitions' => 'Nhiệm vụ chuyển tiếp', + 'Task transitions export' => 'Nhiệm vụ chuyển tiếp xuất khẩu', + 'This report contains all column moves for each task with the date, the user and the time spent for each transition.' => 'Báo cáo này chứa tất cả các cá»™t di chuyển cho má»—i nhiệm vụ vá»›i ngày, ngưá»i sá»­ dụng và thá»i gian dành cho má»—i quá trình chuyển đổi.', + 'Currency rates' => 'Tá»· giá tiá»n tệ', + 'Rate' => 'Tá»· lệ', + 'Change reference currency' => 'Thay đổi đồng tiá»n tham chiếu', + 'Reference currency' => 'Tiá»n tệ tham khảo', + 'The currency rate has been added successfully.' => 'Tá»· giá đã được thêm thành công.', + 'Unable to add this currency rate.' => 'Không thể thêm tá»· giá tiá»n tệ này.', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => ' %s loại bá» ngưá»i nhận chuyển nhượng cá»§a nhiệm vụ %s', + 'Information' => 'Thông tin', + 'Check two factor authentication code' => 'Kiểm tra mã xác thá»±c hai lá»›p', + 'The two factor authentication code is not valid.' => 'Mã xác thá»±c hai lá»›p không hợp lệ.', + 'The two factor authentication code is valid.' => 'Mã xác thá»±c hai lá»›p là hợp lệ.', + 'Code' => 'Mã', + 'Two factor authentication' => 'Xác thá»±c hai lá»›p', + 'This QR code contains the key URI: ' => 'Mã QR này có chứa URI chính:', + 'Check my code' => 'Kiểm tra mã cá»§a tôi', + 'Secret key: ' => 'Chìa khoá bí mật: ', + 'Test your device' => 'Kiểm tra thiết bị cá»§a bạn', + 'Assign a color when the task is moved to a specific column' => 'Chỉ định má»™t màu khi công việc được chuyển đến má»™t cá»™t cụ thể', + '%s via Kanboard' => ' %s qua Kanboard', + 'Burndown chart' => 'Biểu đồ Burndown', + 'This chart show the task complexity over the time (Work Remaining).' => 'Biểu đồ này cho thấy sá»± phức tạp cá»§a công việc qua thá»i gian (Work Remaining).', + 'Screenshot taken %s' => 'Äã chụp ảnh chụp màn hình %s', + 'Add a screenshot' => 'Thêm ảnh chụp màn hình', + 'Take a screenshot and press CTRL+V or ⌘+V to paste here.' => 'Chụp ảnh màn hình và nhấn CTRL + V hoặc ⌘ + V để dán vào đây.', + 'Screenshot uploaded successfully.' => 'Ảnh chụp màn hình được tải lên thành công.', + 'SEK - Swedish Krona' => 'SEK - Krona Thu Swedish Äiển', + 'Identifier' => 'Bá»™ nhận dạng', + 'Disable two factor authentication' => 'Vô hiệu hoá hai lá»›p xác thá»±c', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => 'Bạn có thá»±c sá»± muốn vô hiệu hóa xác thá»±c hai lá»›p cho ngưá»i dùng này: "%s"?', + 'Edit link' => 'Chỉnh sá»­a liên kết', + 'Start to type task title...' => 'Bắt đầu gõ tên tác vụ ...', + 'A task cannot be linked to itself' => 'Má»™t nhiệm vụ không thể được liên kết vá»›i chính nó', + 'The exact same link already exists' => 'Liên kết chính xác đã tồn tại', + 'Recurrent task is scheduled to be generated' => 'Nhiệm vụ lặp lại được dá»± kiến ​​sẽ được tạo ra', + 'Score' => 'Ghi bàn', + 'The identifier must be unique' => 'Mã nhận diện phải là duy nhất', + 'This linked task id doesn\'t exists' => 'Không có id công việc liên kết nào tồn tại', + 'This value must be alphanumeric' => 'Giá trị này phải là chữ số', + 'Edit recurrence' => 'Chỉnh sá»­a sá»± tái phát', + 'Generate recurrent task' => 'Tạo ra công việc lặp Ä‘i lặp lại', + 'Trigger to generate recurrent task' => 'Trigger để tạo ra công việc lặp Ä‘i lặp lại', + 'Factor to calculate new due date' => 'Yếu tố tính ngày đáo hạn má»›i', + 'Timeframe to calculate new due date' => 'Khung thá»i gian để tính toán ngày đáo hạn má»›i', + 'Base date to calculate new due date' => 'Base date để tính ngày đáo hạn má»›i', + 'Action date' => 'Ngày hành động', + 'Base date to calculate new due date: ' => 'Căn cứ để tính ngày đáo hạn má»›i:', + 'This task has created this child task: ' => 'Nhiệm vụ này đã tạo ra nhiệm vụ con này:', + 'Day(s)' => 'Ngày', + 'Existing due date' => 'Ngày hết hạn', + 'Factor to calculate new due date: ' => 'Yếu tố tính ngày đáo hạn má»›i:', + 'Month(s)' => 'Tháng)', + 'This task has been created by: ' => 'Nhiệm vụ này đã được tạo ra bằng cách:', + 'Recurrent task has been generated:' => 'Nhiệm vụ lặp lại đã được tạo ra:', + 'Timeframe to calculate new due date: ' => 'Khung thá»i gian tính ngày đáo hạn má»›i:', + 'Trigger to generate recurrent task: ' => 'Kích hoạt để tạo ra công việc lặp Ä‘i lặp lại:', + 'When task is closed' => 'Khi công việc được đóng lại', + 'When task is moved from first column' => 'Khi công việc được di chuyển từ cá»™t đầu tiên', + 'When task is moved to last column' => 'Khi công việc được chuyển đến cá»™t cuối cùng', + 'Year(s)' => 'Năm (s)', + 'Project settings' => 'Cài đặt dá»± án', + 'Automatically update the start date' => 'Tá»± động cập nhật ngày bắt đầu', + 'iCal feed' => 'nguồn cấp dữ liệu iCal', + 'Preferences' => 'Sở thích', + 'Security' => 'An ninh', + 'Two factor authentication disabled' => 'Xác thá»±c hai lá»›p bị vô hiệu', + 'Two factor authentication enabled' => 'Kích hoạt chứng thá»±c hai lá»›p', + 'Unable to update this user.' => 'Không thể cập nhật ngưá»i dùng này.', + 'There is no user management for personal projects.' => 'Không có ngưá»i quản lý ngưá»i dùng cho các dá»± án riêng tư.', + 'User that will receive the email' => 'Ngưá»i dùng sẽ nhận được email', + 'Email subject' => 'Chá»§ đỠemail', + 'Date' => 'Ngày', + 'Add a comment log when moving the task between columns' => 'Thêm nhật ký nhận xét khi di chuyển nhiệm vụ giữa các cá»™t', + 'Move the task to another column when the category is changed' => 'Di chuyển công việc sang cá»™t khác khi danh mục được thay đổi', + 'Send a task by email to someone' => 'Gá»­i má»™t nhiệm vụ qua email cho ai đó', + 'Reopen a task' => 'Mở lại má»™t nhiệm vụ', + 'Notification' => 'Thông báo', + '%s moved the task #%d to the first swimlane' => ' %s di chuyển nhiệm vụ #%d đến ngưá»i đầu tiên swimlane', + 'Swimlane' => 'Swimlane', + '%s moved the task %s to the first swimlane' => ' %s di chuyển nhiệm vụ %s tá»›i swimlane đầu tiên', + '%s moved the task %s to the swimlane "%s"' => ' %s di chuyển nhiệm vụ %s tá»›i swimlane lá»™i "%s"', + 'This report contains all subtasks information for the given date range.' => 'Báo cáo này chứa tất cả các thông tin phụ cho má»™t phạm vi ngày.', + 'This report contains all tasks information for the given date range.' => 'Báo cáo này chứa tất cả các nhiệm vụ thông tin cho phạm vi ngày nhất định.', + 'Project activities for %s' => 'Các hoạt động dá»± án cho %s', + 'view the board on Kanboard' => 'xem bảng trên Kanboard', + 'The task has been moved to the first swimlane' => 'Nhiệm vụ đã được dá»i đến sân vận động đầu tiên', + 'The task has been moved to another swimlane:' => 'Nhiệm vụ đã được chuyển sang má»™t đội swimlane khác: ', + 'New title: %s' => 'Tiêu đỠmá»›i: %s', + 'The task is not assigned anymore' => 'Nhiệm vụ không được giao nữa', + 'New assignee: %s' => 'Ngưá»i được chuyển nhượng má»›i: %s', + 'There is no category now' => 'Không có danh mục bây giá»', + 'New category: %s' => 'Thể loại má»›i: %s', + 'New color: %s' => 'Màu má»›i: %s', + 'New complexity: %d' => 'Sá»± phức tạp má»›i: %d', + 'The due date has been removed' => 'Ngày đáo hạn đã bị xoá', + 'There is no description anymore' => 'Không còn mô tả nữa', + 'Recurrence settings has been modified' => 'Cài đặt tái lập đã được sá»­a đổi', + 'Time spent changed: %sh' => 'Thá»i gian thay đổi: %sh', + 'Time estimated changed: %sh' => 'Thá»i gian ước tính thay đổi: %sh', + 'The field "%s" has been updated' => 'Trưá»ng "%s" đã được cập nhật', + 'The description has been modified:' => 'Mô tả đã được sá»­a đổi:', + 'Do you really want to close the task "%s" as well as all subtasks?' => 'Bạn có thá»±c sá»± muốn đóng nhiệm vụ "%s" cÅ©ng như tất cả các nhiệm vụ phụ?', + 'I want to receive notifications for:' => 'Tôi muốn nhận thông báo cho:', + 'All tasks' => 'Tất cả các nhiệm vụ', + 'Only for tasks assigned to me' => 'Chỉ đối vá»›i những nhiệm vụ được giao cho tôi', + 'Only for tasks created by me' => 'Chỉ cho những công việc do tôi tạo ra', + 'Only for tasks created by me and tasks assigned to me' => 'Chỉ cho những công việc mà tôi tạo ra và được giao cho tôi', + '%%Y-%%m-%%d' => '%%Y - %%m - %%d', + 'Total for all columns' => 'Tổng cho tất cả các cá»™t', + 'You need at least 2 days of data to show the chart.' => 'Bạn cần ít nhất 2 ngày dữ liệu để hiển thị biểu đồ.', + '<15m' => '<15m', + '<30m' => '<30m', + 'Stop timer' => 'Ngừng hẹn giá»', + 'Start timer' => 'Bắt đầu hẹn giá»', + 'My activity stream' => 'Dòng hoạt động cá»§a tôi', + 'Search tasks' => 'Các nhiệm vụ tìm kiếm', + 'Reset filters' => 'Äặt lại bá»™ lá»c', + 'My tasks due tomorrow' => 'Nhiệm vụ cá»§a tôi vào ngày mai', + 'Tasks due today' => 'Nhiệm vụ hôm nay', + 'Tasks due tomorrow' => 'Nhiệm vụ vào ngày mai', + 'Tasks due yesterday' => 'Nhiệm vụ do ngày hôm qua', + 'Closed tasks' => 'Các nhiệm vụ đã đóng', + 'Open tasks' => 'Mở nhiệm vụ', + 'Not assigned' => 'Không được chỉ định', + 'View advanced search syntax' => 'Xem cú pháp tìm kiếm nâng cao', + 'Overview' => 'Tổng quan', + 'Board/Calendar/List view' => 'Chế độ xem há»™i đồng quản trị / lịch / danh sách', + 'Switch to the board view' => 'Chuyển sang chế độ xem bảng', + 'Switch to the list view' => 'Chuyển sang chế độ xem danh sách', + 'Go to the search/filter box' => 'Äi tá»›i há»™p tìm kiếm / bá»™ lá»c', + 'There is no activity yet.' => 'Không có hoạt động nào.', + 'No tasks found.' => 'Không tìm thấy công việc nào.', + 'Keyboard shortcut: "%s"' => 'Các phím tắt bàn phím "%s"', + 'List' => 'Danh sách', + 'Filter' => 'Lá»c', + 'Advanced search' => 'Tìm kiếm nâng cao', + 'Example of query: ' => 'Ví dụ vá» truy vấn:', + 'Search by project: ' => 'Tìm kiếm theo dá»± án:', + 'Search by column: ' => 'Tìm kiếm theo cá»™t:', + 'Search by assignee: ' => 'Tìm kiếm bởi ngưá»i được chuyển nhượng:', + 'Search by color: ' => 'Tìm kiếm theo màu sắc:', + 'Search by category: ' => 'Tìm kiếm theo thể loại:', + 'Search by description: ' => 'Tìm theo mô tả:', + 'Search by due date: ' => 'Tìm kiếm theo ngày đáo hạn:', + 'Average time spent in each column' => 'Thá»i gian trung bình dành cho từng cá»™t', + 'Average time spent' => 'Thá»i gian trung bình dành', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'Biểu đồ này cho thấy thá»i gian trung bình dành cho má»—i cá»™t cho các tác vụ %d cuối cùng.', + 'Average Lead and Cycle time' => 'Thá»i gian trung bình và thá»i gian chu kỳ', + 'Average lead time: ' => 'Thá»i gian dẫn trung bình:', + 'Average cycle time: ' => 'Thá»i gian chu kỳ trung bình:', + 'Cycle Time' => 'Thá»i gian chu kỳ', + 'Lead Time' => 'Chì Thá»i gian', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'Biểu đồ này cho thấy thá»i gian dẫn và thá»i gian trung bình cho các tác vụ %d cuối cùng qua thá»i gian.', + 'Average time into each column' => 'Thá»i gian trung bình vào má»—i cá»™t', + 'Lead and cycle time' => 'Chì và thá»i gian chu kỳ', + 'Lead time: ' => 'Chì thá»i gian:', + 'Cycle time: ' => 'Thá»i gian chu kỳ: ', + 'Time spent in each column' => 'Thá»i gian dành cho từng cá»™t', + 'The lead time is the duration between the task creation and the completion.' => 'Thá»i gian dẫn là khoảng thá»i gian giữa nhiệm vụ sáng tạo và hoàn thành.', + 'The cycle time is the duration between the start date and the completion.' => 'Thá»i gian chu kỳ là khoảng thá»i gian giữa ngày bắt đầu và ngày hoàn thành.', + 'If the task is not closed the current time is used instead of the completion date.' => 'Nếu nhiệm vụ không đóng thì thá»i gian hiện tại được sá»­ dụng thay vì ngày hoàn thành.', + 'Set the start date automatically' => 'Äặt tá»± động ngày bắt đầu', + 'Edit Authentication' => 'Chỉnh sá»­a Xác thá»±c', + 'Remote user' => 'Ngưá»i dùng từ xa', + 'Remote users do not store their password in Kanboard database, examples: LDAP, Google and Github accounts.' => 'Ngưá»i dùng từ xa không lưu mật khẩu cá»§a há» trong cÆ¡ sở dữ liệu Kanboard, ví dụ: LDAP, Google và Github.', + 'If you check the box "Disallow login form", credentials entered in the login form will be ignored.' => 'Nếu bạn đánh dấu ô "Disallow form đăng nhập", các thông tin đăng nhập vào mẫu đăng nhập sẽ bị bá» qua.', + 'Default task color' => 'Màu nhiệm vụ mặc định', + 'This feature does not work with all browsers.' => 'Tính năng này không hoạt động vá»›i tất cả các trình duyệt.', + 'There is no destination project available.' => 'Không có dá»± án Ä‘iểm đến có sẵn.', + 'Trigger automatically subtask time tracking' => 'Trigger tá»± động theo dõi thá»i gian theo dõi', + 'Include closed tasks in the cumulative flow diagram' => 'Bao gồm các nhiệm vụ đã đóng trong sÆ¡ đồ luồng tích lÅ©y', + 'Current swimlane: %s' => 'Dòng swimlane hiện tại: %s', + 'Current column: %s' => 'Cá»™t hiện tại: %s', + 'Current category: %s' => 'Thể loại hiện tại: %s', + 'no category' => 'không có loại', + 'Current assignee: %s' => 'Äang chuyển nhượng hiện tại: %s', + 'not assigned' => 'không được chỉ định', + 'Author:' => 'Tác giả:', + 'contributors' => 'ngưá»i đóng góp', + 'License:' => 'Giấy phép:', + 'License' => 'Giấy phép', + 'Enter the text below' => 'Nhập văn bản dưới đây', + 'Start date:' => 'Ngày bắt đầu:', + 'Due date:' => 'Ngày đáo hạn:', + 'People who are project managers' => 'Những ngưá»i quản lý dá»± án', + 'People who are project members' => 'Những ngưá»i là thành viên cá»§a dá»± án', + 'NOK - Norwegian Krone' => 'NOK - Krone Na Uy', + 'Show this column' => 'Hiển thị cá»™t này', + 'Hide this column' => 'Ẩn cá»™t này', + 'End date' => 'Ngày cuối', + 'Users overview' => 'Tổng quan ngưá»i dùng', + 'Members' => 'Các thành viên', + 'Shared project' => 'Dá»± án được chia sẻ', + 'Project managers' => 'Quản lý dá»± án', + 'Projects list' => 'Danh sách các dá»± án', + 'End date:' => 'Ngày cuối:', + 'Change task color when using a specific task link' => 'Thay đổi màu nhiệm vụ khi sá»­ dụng liên kết nhiệm vụ cụ thể', + 'Task link creation or modification' => 'Tạo hoặc sá»­a đổi liên kết nhiệm vụ', + 'Milestone' => 'Milestone', + 'Reset the search/filter box' => 'Äặt lại há»™p tìm kiếm / bá»™ lá»c', + 'Documentation' => 'Tài liệu hướng dẫn', + 'Author' => 'Tác giả', + 'Version' => 'Phiên bản', + 'Plugins' => 'Plugins', + 'There is no plugin loaded.' => 'Không có plugin nạp.', + 'My notifications' => 'Thông báo cá»§a tôi', + 'Custom filters' => 'Bá»™ lá»c tùy chỉnh', + 'Your custom filter has been created successfully.' => 'Bá»™ lá»c tuỳ chỉnh cá»§a bạn đã được tạo thành công.', + 'Unable to create your custom filter.' => 'Không thể tạo bá»™ lá»c tuỳ chỉnh cá»§a bạn.', + 'Custom filter removed successfully.' => 'Äã xoá bá»™ lá»c tùy chỉnh.', + 'Unable to remove this custom filter.' => 'Không thể xóa bá»™ lá»c tùy chỉnh này.', + 'Edit custom filter' => 'Chỉnh sá»­a bá»™ lá»c tùy chỉnh', + 'Your custom filter has been updated successfully.' => 'Bá»™ lá»c tuỳ chỉnh cá»§a bạn đã được cập nhật thành công.', + 'Unable to update custom filter.' => 'Không thể cập nhật bá»™ lá»c tuỳ chỉnh.', + 'Web' => 'Web', + 'New attachment on task #%d: %s' => 'Tập tin đính kèm má»›i trên task #%d: %s', + 'New comment on task #%d' => 'à kiến ​​má»›i vá» nhiệm vụ #%d', + 'Comment updated on task #%d' => 'Bình luận cập nhật vá» nhiệm vụ #%d', + 'New subtask on task #%d' => 'Bổ sung phụ vá» nhiệm vụ #%d', + 'Subtask updated on task #%d' => 'Không cập nhật được nhiệm vụ #%d', + 'New task #%d: %s' => 'Nhiệm vụ má»›i %d: %s', + 'Task updated #%d' => 'Äã cập nhật nhiệm vụ #%d', + 'Task #%d closed' => 'Nhiệm vụ #%d đóng', + 'Task #%d opened' => 'Nhiệm vụ #%d mở', + 'Column changed for task #%d' => 'Äã thay đổi cá»™t cho tác vụ #%d', + 'New position for task #%d' => 'Vị trí má»›i cho nhiệm vụ #%d', + 'Swimlane changed for task #%d' => 'Swimlane đã thay đổi cho nhiệm vụ #%d', + 'Assignee changed on task #%d' => 'Ngưá»i được chỉ định thay đổi vá» nhiệm vụ #%d', + '%d overdue tasks' => '%d quá hạn tác vụ', + 'No notification.' => 'Không có thông báo.', + 'Mark all as read' => 'Äánh dấu tất cả như đã Ä‘á»c', + 'Mark as read' => 'Äánh dấu là đã Ä‘á»c', + 'Total number of tasks in this column across all swimlanes' => 'Tổng số công việc trong cá»™t này trên tất cả các đưá»ng swimlane', + 'Collapse swimlane' => 'Thu gá»n swimlane', + 'Expand swimlane' => 'Mở rá»™ng swimlane lá»™i', + 'Add a new filter' => 'Thêm má»™t bá»™ lá»c má»›i', + 'Share with all project members' => 'Chia sẻ vá»›i tất cả các thành viên dá»± án', + 'Shared' => 'Chia sẻ', + 'Owner' => 'Chá»§ nhân', + 'Unread notifications' => 'Thông báo chưa Ä‘á»c', + 'Notification methods:' => 'Phương pháp thông báo:', + 'Unable to read your file' => 'Không thể Ä‘á»c tập tin cá»§a bạn', + '%d task(s) have been imported successfully.' => '%d nhiệm vụ đã được nhập thành công.', + 'Nothing has been imported!' => 'Không có gì đã được nhập khẩu!', + 'Import users from CSV file' => 'Nhập khẩu ngưá»i dùng từ tệp CSV', + '%d user(s) have been imported successfully.' => '%d ngưá»i dùng đã được nhập thành công.', + 'Comma' => 'Comma', + 'Semi-colon' => 'Bán kết', + 'Tab' => 'Chuyển hướng', + 'Vertical bar' => 'Thanh dá»c', + 'Double Quote' => 'Double Quote', + 'Single Quote' => 'Trích dẫn đơn', + '%s attached a file to the task #%d' => ' %s gắn má»™t tập tin vào tác vụ #%d', + 'There is no column or swimlane activated in your project!' => 'Không có cá»™t hoặc swimlane kích hoạt trong dá»± án cá»§a bạn!', + 'Append filter (instead of replacement)' => 'Thêm bá»™ lá»c (thay vì thay thế)', + 'Append/Replace' => 'Thêm / Thay thế', + 'Append' => 'Thêm', + 'Replace' => 'Thay thế', + 'Import' => 'Nhập khẩu', + 'Change sorting' => 'Thay đổi phân loại', + 'Tasks Importation' => 'Nhiệm vụ Nhập khẩu', + 'Delimiter' => 'Delimiter', + 'Enclosure' => 'Bao vây', + 'CSV File' => 'CSV File', + 'Instructions' => 'Hướng dẫn', + 'Your file must use the predefined CSV format' => 'Tệp cá»§a bạn phải sá»­ dụng định dạng CSV được xác định trước', + 'Your file must be encoded in UTF-8' => 'Tập tin cá»§a bạn phải được mã hoá bằng UTF-8', + 'The first row must be the header' => 'Hàng đầu tiên phải là tiêu Ä‘á»', + 'Duplicates are not verified for you' => 'Bản sao không được xác minh cho bạn', + 'The due date must use the ISO format: YYYY-MM-DD' => 'Ngày đáo hạn phải sá»­ dụng định dạng ISO: YYYY-MM-DD', + 'Download CSV template' => 'Tải xuống mẫu CSV', + 'No external integration registered.' => 'Không có tích hợp bên ngoài đăng ký.', + 'Duplicates are not imported' => 'Bản sao không được nhập', + 'Usernames must be lowercase and unique' => 'Tên ngưá»i dùng phải là chữ thưá»ng và', + 'Passwords will be encrypted if present' => 'Mật khẩu sẽ được mã hóa nếu có', + '%s attached a new file to the task %s' => ' %s gắn má»™t tập tin má»›i vào công việc %s', + 'Link type' => 'Loại liên kết', + 'Assign automatically a category based on a link' => 'Chỉ định tá»± động má»™t thể loại dá»±a trên liên kết', + 'BAM - Konvertible Mark' => 'BAM - Konvertible Mark', + 'Assignee Username' => 'Assignee Username', + 'Assignee Name' => 'Ngưá»i được chuyển nhượng', + 'Groups' => 'Các nhóm', + 'Members of %s' => 'Thành viên cá»§a %s', + 'New group' => 'Nhóm má»›i', + 'Group created successfully.' => 'Nhóm được tạo thành công.', + 'Unable to create your group.' => 'Không thể tạo nhóm cá»§a bạn.', + 'Edit group' => 'Chỉnh sá»­a nhóm', + 'Group updated successfully.' => 'Nhóm được cập nhật thành công.', + 'Unable to update your group.' => 'Không thể cập nhật nhóm cá»§a bạn.', + 'Add group member to "%s"' => 'Thêm thành viên nhóm vào "%s"', + 'Group member added successfully.' => 'Thành viên nhóm đã thành công.', + 'Unable to add group member.' => 'Không thể thêm thành viên nhóm', + 'Remove user from group "%s"' => 'Xóa ngưá»i dùng khá»i nhóm %s', + 'User removed successfully from this group.' => 'Ngưá»i dùng đã xóa thành công khá»i nhóm này.', + 'Unable to remove this user from the group.' => 'Không thể xóa ngưá»i dùng này khá»i nhóm.', + 'Remove group' => 'Loại bá» nhóm', + 'Group removed successfully.' => 'Nhóm đã xoá thành công.', + 'Unable to remove this group.' => 'Không thể xóa nhóm này.', + 'Project Permissions' => 'Quyá»n dá»± án', + 'Manager' => 'Giám đốc', + 'Project Manager' => 'Quản lý dá»± án', + 'Project Member' => 'Thành viên Dá»± án', + 'Project Viewer' => 'Trình xem dá»± án', + 'Your account is locked for %d minutes' => 'Tài khoản cá»§a bạn bị khóa trong %d phút', + 'Invalid captcha' => 'Captcha không hợp lệ', + 'The name must be unique' => 'Cái tên phải là duy nhất', + 'View all groups' => 'Xem tất cả các nhóm', + 'There is no user available.' => 'Không có ngưá»i dùng nào có sẵn.', + 'Do you really want to remove the user "%s" from the group "%s"?' => 'Bạn có thá»±c sá»± muốn xóa ngưá»i dùng "%s" khá»i nhóm "%s"?', + 'There is no group.' => 'Không có nhóm.', + 'Add group member' => 'Thêm thành viên nhóm', + 'Do you really want to remove this group: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» nhóm này: "%s"?', + 'There is no user in this group.' => 'Không có ngưá»i dùng trong nhóm này.', + 'Permissions' => 'Quyá»n', + 'Allowed Users' => 'Ngưá»i dùng được phép', + 'No specific user has been allowed.' => 'Không ngưá»i dùng nào được cho phép cụ thể.', + 'Role' => 'Vai trò', + 'Enter user name...' => 'Äiá»n tên đăng nhập...', + 'Allowed Groups' => 'Nhóm được phép', + 'No group has been allowed.' => 'Không có nhóm nào được cho phép cụ thể.', + 'Group' => 'Nhóm', + 'Group Name' => 'Tên nhóm', + 'Enter group name...' => 'Nhập tên nhóm ...', + 'Role:' => 'Vai trò:', + 'Project members' => 'Thành viên dá»± án', + '%s mentioned you in the task #%d' => ' %s đỠcập đến bạn trong công việc #%d', + '%s mentioned you in a comment on the task #%d' => ' %s đã đỠcập đến bạn trong má»™t nhận xét vá» nhiệm vụ #%d', + 'You were mentioned in the task #%d' => 'Bạn đã được đỠcập trong nhiệm vụ #%d', + 'You were mentioned in a comment on the task #%d' => 'Bạn đã được đỠcập trong má»™t nhận xét vá» nhiệm vụ #%d', + 'Estimated hours: ' => 'GiỠước tính:', + 'Actual hours: ' => 'Giá» thá»±c tế:', + 'Hours Spent' => 'Thá»i gian Nghỉ ngÆ¡i', + 'Hours Estimated' => 'Số giỠƯớc tính', + 'Estimated Time' => 'ThÆ¡i gian dự Ä‘iÌ£nh', + 'Actual Time' => 'Thá»i gian Thá»±c', + 'Estimated vs actual time' => 'Ước tính thá»i gian thá»±c', + 'RUB - Russian Ruble' => 'RUB - Nga Ruble', + 'Assign the task to the person who does the action when the column is changed' => 'Chỉ định nhiệm vụ cho ngưá»i thá»±c hiện hành động khi cá»™t được thay đổi', + 'Close a task in a specific column' => 'Äóng má»™t nhiệm vụ trong má»™t cá»™t cụ thể', + 'Time-based One-time Password Algorithm' => 'Thuật toán mật khẩu má»™t lần dá»±a trên thá»i gian', + 'Two-Factor Provider: ' => 'Nhà cung cấp hai nhân tố:', + 'Disable two-factor authentication' => 'Tắt xác thá»±c hai lá»›p', + 'Enable two-factor authentication' => 'Kích hoạt xác thá»±c hai lá»›p', + 'There is no integration registered at the moment.' => 'Hiện tại không có há»™i nhập nào được đăng ký.', + 'Password Reset for Kanboard' => 'Äặt lại mật khẩu cho Kanboard', + 'Forgot password?' => 'Quên mật khẩu?', + 'Enable "Forget Password"' => 'Bật Quên mật khẩu ', + 'Password Reset' => 'Äặt lại mật khẩu', + 'New password' => 'Mật khẩu má»›i', + 'Change Password' => 'Äổi mật khẩu', + 'To reset your password click on this link:' => 'Äể đặt lại mật khẩu cá»§a bạn bấm vào liên kết này:', + 'Last Password Reset' => 'Thiết lập lại mật khẩu lần cuối', + 'The password has never been reinitialized.' => 'Mật khẩu chưa bao giỠđược khởi tạo lại.', + 'Creation' => 'Sá»± sáng tạo', + 'Expiration' => 'Hết hạn', + 'Password reset history' => 'Lịch sá»­ đặt lại mật khẩu', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'Tất cả nhiệm vụ cá»§a cá»™t "%s" và swimlane "%s" đã được đóng thành công.', + 'Do you really want to close all tasks of this column?' => 'Bạn có thá»±c sá»± muốn đóng tất cả các nhiệm vụ cá»§a cá»™t này?', + '%d task(s) in the column "%s" and the swimlane "%s" will be closed.' => '%d nhiệm vụ trong cá»™t "%s" và swimlane "%s" sẽ được đóng lại.', + 'Close all tasks in this column and this swimlane' => 'Äóng tất cả các tác vụ cá»§a cá»™t này', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => 'Không có plugin đã đăng ký má»™t phương pháp thông báo dá»± án. Bạn vẫn có thể định cấu hình các thông báo riêng trong hồ sÆ¡ ngưá»i dùng cá»§a bạn. ', + 'My dashboard' => 'Bảng Ä‘iá»u khiển cá»§a tôi', + 'My profile' => 'Thông tin cá»§a tôi', + 'Project owner: ' => 'Chá»§ dá»± án: ', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => 'Äịnh danh dá»± án là tùy chá»n và phải là chữ số, ví dụ: MYPROJECT.', + 'Project owner' => 'Chá»§ dá»± án', + 'Personal projects do not have users and groups management.' => 'Các dá»± án riêng tư không có ngưá»i dùng và quản lý nhóm.', + 'There is no project member.' => 'Không có thành viên dá»± án.', + 'Priority' => 'Sá»± ưu tiên', + 'Task priority' => 'Nhiệm vụ ưu tiên', + 'General' => 'Chung', + 'Dates' => 'Ngày', + 'Default priority' => 'Mức độ ưu tiên mặc định', + 'Lowest priority' => 'Mức ưu tiên thấp nhất', + 'Highest priority' => 'Mức ưu tiên cao nhất', + 'Close a task when there is no activity' => 'Äóng má»™t nhiệm vụ khi không có hoạt động', + 'Duration in days' => 'Thá»i lượng theo ngày', + 'Send email when there is no activity on a task' => 'Gá»­i email khi không có hoạt động nào trên má»™t tác vụ', + 'Unable to fetch link information.' => 'Không thể tìm nạp thông tin liên kết.', + 'Daily background job for tasks' => 'Công việc hàng ngày cho các nhiệm vụ', + 'Auto' => 'Tự động', + 'Related' => 'Liên quan', + 'Attachment' => 'Tập tin đính kèm', + 'Web Link' => 'Liên kết Web', + 'External links' => 'Liện kết ngoại', + 'Add external link' => 'Thêm liên kết bên ngoài', + 'Type' => 'Kiểu', + 'Dependency' => 'Sá»± phụ thuá»™c', + 'Add internal link' => 'Thêm liên kết ná»™i bá»™', + 'Add a new external link' => 'Thêm má»™t liên kết bên ngoài má»›i', + 'Edit external link' => 'Chỉnh sá»­a liên kết bên ngoài', + 'External link' => 'Liên kết bên ngoài', + 'Copy and paste your link here...' => 'Sao chép và dán liên kết cá»§a bạn ở đây ...', + 'URL' => 'URL', + 'Internal links' => 'Liên kết ná»™i bá»™', + 'Assign to me' => 'Chỉ định cho tôi', + 'Me' => 'Tôi', + 'Do not duplicate anything' => 'Äừng trùng lặp bất cứ Ä‘iá»u gì', + 'Projects management' => 'Quản lý dá»± án', + 'Users management' => 'Quản lý ngưá»i dùng', + 'Groups management' => 'Quản lý nhóm', + 'Create from another project' => 'Tạo ra từ má»™t dá»± án khác', + 'open' => 'mở', + 'closed' => 'đóng cá»­a', + 'Priority:' => 'Sá»± ưu tiên:', + 'Reference:' => 'Tài liệu tham khảo:', + 'Complexity:' => 'Sá»± phức tạp:', + 'Swimlane:' => 'Swimlane:', + 'Column:' => 'Cá»™t:', + 'Position:' => 'Chức vụ:', + 'Creator:' => 'Ngưá»i sáng tạo:', + 'Time estimated:' => 'Thá»i gian ước tính:', + '%s hours' => ' %s hours', + 'Time spent:' => 'Thá»i gian dành:', + 'Created:' => 'Tạo:', + 'Modified:' => 'Sá»­a đổi:', + 'Completed:' => 'Äã hoàn thành:', + 'Started:' => 'Äã bắt đầu:', + 'Moved:' => 'Moved:', + 'Task #%d' => 'Nhiệm vụ #%d', + 'Time format' => 'Äịnh dạng thá»i gian', + 'Start date: ' => 'Ngày bắt đầu:', + 'End date: ' => 'Ngày cuối: ', + 'New due date: ' => 'Ngày đáo hạn má»›i:', + 'Start date changed: ' => 'Ngày bắt đầu thay đổi:', + 'Disable personal projects' => 'Vô hiệu hoá các dá»± án riêng tư', + 'Do you really want to remove this custom filter: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» bá»™ lá»c tuỳ chỉnh này: "%s"?', + 'Remove a custom filter' => 'Xóa má»™t bá»™ lá»c tuỳ chỉnh', + 'User activated successfully.' => 'Ngưá»i dùng đã kích hoạt thành công.', + 'Unable to enable this user.' => 'Không thể kích hoạt ngưá»i dùng này.', + 'User disabled successfully.' => 'Ngưá»i dùng đã vô hiệu thành công.', + 'Unable to disable this user.' => 'Không thể vô hiệu hóa ngưá»i dùng này.', + 'All files have been uploaded successfully.' => 'Tất cả các tập tin đã được tải lên thành công.', + 'The maximum allowed file size is %sB.' => 'Kích thước tập tin tối Ä‘a cho phép là %sB.', + 'Drag and drop your files here' => 'Kéo và thả tệp cá»§a bạn ở đây', + 'choose files' => 'chá»n tập tin', + 'View profile' => 'Xem hồ sÆ¡', + 'Two Factor' => 'hai lá»›p', + 'Disable user' => 'Vô hiệu hoá ngưá»i dùng', + 'Do you really want to disable this user: "%s"?' => 'Bạn thá»±c sá»± muốn vô hiệu hóa ngưá»i dùng này: "%s"?', + 'Enable user' => 'Enable user', + 'Do you really want to enable this user: "%s"?' => 'Bạn có thá»±c sá»± muốn cho phép ngưá»i dùng này: "%s"?', + 'Download' => 'Tải vá»', + 'Uploaded: %s' => 'Äã tải lên: %s', + 'Size: %s' => 'Kích thước: %s', + 'Uploaded by %s' => 'Äã tải lên bởi %s', + 'Filename' => 'Filename', + 'Size' => 'Kích thước', + 'Column created successfully.' => 'Cá»™t được tạo thành công.', + 'Another column with the same name exists in the project' => 'Má»™t cá»™t có cùng tên tồn tại trong dá»± án', + 'Default filters' => 'Bá»™ lá»c mặc định', + 'Your board doesn\'t have any columns!' => 'Bảng cá»§a bạn không có bất kỳ cá»™t nào!', + 'Change column position' => 'Thay đổi vị trí cá»™t', + 'Switch to the project overview' => 'Chuyển sang tổng quan vá» dá»± án', + 'User filters' => 'Bá»™ lá»c ngưá»i dùng', + 'Category filters' => 'Bá»™ lá»c danh mục', + 'Upload a file' => 'Tải lên má»™t tài liệu', + 'View file' => 'Xem tài liệu', + 'Last activity' => 'Hoạt động cuối', + 'Change subtask position' => 'Thay đổi vị trí phụ', + 'This value must be greater than %d' => 'Giá trị này phải lá»›n hÆ¡n %d', + 'Another swimlane with the same name exists in the project' => 'Má»™t con tàu khác có cùng tên tồn tại trong dá»± án ', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => 'Ví dụ: https://example.kanboard.org/ (dùng để tạo URL tuyệt đối)', + 'Actions duplicated successfully.' => 'Hành động trùng lặp thành công.', + 'Unable to duplicate actions.' => 'Không thể lặp lại hành động.', + 'Add a new action' => 'Thêm má»™t hành động má»›i', + 'Import from another project' => 'Nhập khẩu từ má»™t dá»± án khác', + 'There is no action at the moment.' => 'Hiện tại không có hành động nào.', + 'Import actions from another project' => 'Nhập khẩu các hành động từ má»™t dá»± án khác', + 'There is no available project.' => 'Không có dá»± án sẵn có.', + 'Local File' => 'Local File', + 'Configuration' => 'Cấu hình', + 'PHP version:' => 'Phiên bản PHP:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => 'Phiên bản hệ Ä‘iá»u hành:', + 'Database version:' => 'Phiên bản cÆ¡ sở dữ liệu:', + 'Browser:' => 'Trình duyệt:', + 'Task view' => 'Công việc xem', + 'Edit task' => 'Chỉnh sá»­a nhiệm vụ', + 'Edit description' => 'Chỉnh sá»­a mô tả', + 'New internal link' => 'Liên kết ná»™i bá»™ má»›i', + 'Display list of keyboard shortcuts' => 'Hiển thị danh sách các phím tắt', + 'Avatar' => 'Hình đại diện', + 'Upload my avatar image' => 'Tải lên hình ảnh đại diện cá»§a tôi', + 'Remove my image' => 'Há»§y bá» hình ảnh cá»§a tôi', + 'The OAuth2 state parameter is invalid' => 'Tham số trạng thái OAuth2 không hợp lệ', + 'User not found.' => 'Ngưá»i dùng không tìm thấy.', + 'Search in activity stream' => 'Tìm kiếm trong luồng hoạt động', + 'My activities' => 'Hoạt động cá»§a tôi', + 'Activity until yesterday' => 'Hoạt động cho đến ngày hôm qua', + 'Activity until today' => 'Hoạt động cho đến ngày hôm nay', + 'Search by creator: ' => 'Tìm kiếm theo ngưá»i sáng tạo:', + 'Search by creation date: ' => 'Tìm kiếm theo ngày tạo:', + 'Search by task status: ' => 'Tìm theo tình trạng công việc:', + 'Search by task title: ' => 'Tìm theo tên công việc:', + 'Activity stream search' => 'Tìm kiếm luồng hoạt động', + 'Projects where "%s" is manager' => 'Dá»± án nÆ¡i "%s" là ngưá»i quản lý', + 'Projects where "%s" is member' => 'Dá»± án nÆ¡i "%s" là thành viên', + 'Open tasks assigned to "%s"' => 'Mở nhiệm vụ được giao cho "%s"', + 'Closed tasks assigned to "%s"' => 'Äã đóng nhiệm vụ được giao cho "%s"', + 'Assign automatically a color based on a priority' => 'Chỉ định tá»± động má»™t màu dá»±a trên má»™t ưu tiên', + 'Overdue tasks for the project(s) "%s"' => 'Các nhiệm vụ quá hạn cho (các) dá»± án %s', + 'Upload files' => 'Tải tệp lên', + 'Installed Plugins' => 'Plugin cài đặt', + 'Plugin Directory' => 'Plugin Directory', + 'Plugin installed successfully.' => 'Plugin được cài đặt thành công.', + 'Plugin updated successfully.' => 'Plugin được cập nhật thành công.', + 'Plugin removed successfully.' => 'Plugin được xoá thành công.', + 'Subtask converted to task successfully.' => 'Äã thá»±c hiện thành công nhiệm vụ phụ đỠđã được chuyển thành.', + 'Unable to convert the subtask.' => 'Không thể chuyển đổi phụ Ä‘á».', + 'Unable to extract plugin archive.' => 'Không thể trích xuất kho lưu trữ plugin.', + 'Plugin not found.' => 'Plugin không tìm thấy.', + 'You don\'t have the permission to remove this plugin.' => 'Bạn không có quyá»n xóa plugin này.', + 'Unable to download plugin archive.' => 'Không thể tải tệp lưu trữ plugin.', + 'Unable to write temporary file for plugin.' => 'Không thể ghi tập tin tạm thá»i cho plugin.', + 'Unable to open plugin archive.' => 'Không thể mở tệp lưu trữ plugin.', + 'There is no file in the plugin archive.' => 'Không có tệp nào trong kho lưu trữ trình cắm.', + 'Create tasks in bulk' => 'Tạo nhiệm vụ hàng loạt', + 'Your Kanboard instance is not configured to install plugins from the user interface.' => 'Trưá»ng hợp Kanboard cá»§a bạn không được định cấu hình để cài đặt plugin từ giao diện ngưá»i dùng.', + 'There is no plugin available.' => 'Không có plugin nào sẵn có.', + 'Install' => 'Cài đặt, dá»±ng lên', + 'Update' => 'Cập nhật', + 'Up to date' => 'Cập nhật', + 'Not available' => 'Không có sẵn', + 'Remove plugin' => 'Xoá plugin', + 'Do you really want to remove this plugin: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» plugin này: "%s"?', + 'Uninstall' => 'Gỡ cài đặt', + 'Listing' => 'Liệt kê', + 'Metadata' => 'Siêu dữ liệu', + 'Manage projects' => 'Quản lý dá»± án', + 'Convert to task' => 'Chuyển đổi sang tác vụ', + 'Convert sub-task to task' => 'Chuyển tiểu nhiệm vụ thành nhiệm vụ', + 'Do you really want to convert this sub-task to a task?' => 'Bạn có thá»±c sá»± muốn chuyển đổi tiểu nhiệm vụ này sang má»™t nhiệm vụ?', + 'My task title' => 'Nhiệm vụ cá»§a tôi', + 'Enter one task by line.' => 'Nhập má»™t tác vụ theo dòng.', + 'Number of failed login:' => 'Số lần đăng nhập không thành công:', + 'Account locked until:' => 'Tài khoản bị khoá cho đến khi:', + 'Email settings' => 'Cài đặt email', + 'Email sender address' => 'Äịa chỉ ngưá»i gá»­i email', + 'Email transport' => 'Vận chuyển email', + 'Webhook token' => 'Mã thông báo cá»§a Webhook', + 'Project tags management' => 'Quản lý thẻ dá»± án', + 'Tag created successfully.' => 'Thẻ được tạo thành công.', + 'Unable to create this tag.' => 'Không thể tạo thẻ này.', + 'Tag updated successfully.' => 'Thẻ được cập nhật thành công.', + 'Unable to update this tag.' => 'Không thể cập nhật thẻ này.', + 'Tag removed successfully.' => 'Thẻ đã xoá thành công.', + 'Unable to remove this tag.' => 'Không thể xóa thẻ này.', + 'Global tags management' => 'Quản lý thẻ toàn cầu', + 'Tags' => 'Thẻ', + 'Tags management' => 'Quản lý thẻ', + 'Add new tag' => 'Thêm thẻ má»›i', + 'Edit a tag' => 'Chỉnh sá»­a thẻ', + 'Project tags' => 'Thẻ dá»± án', + 'There is no specific tag for this project at the moment.' => 'Hiện tại không có thẻ cụ thể cho dá»± án này.', + 'Tag' => 'Nhãn', + 'Remove a tag' => 'Loại bá» má»™t thẻ', + 'Do you really want to remove this tag: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» thẻ này: "%s"?', + 'Global tags' => 'Thẻ toàn cầu', + 'There is no global tag at the moment.' => 'Hiện tại không có nhãn toàn cầu. ', + 'This field cannot be empty' => 'Trưá»ng này không thể để trống', + 'Close a task when there is no activity in a specific column' => 'Äóng má»™t nhiệm vụ khi không có hoạt động trong má»™t cá»™t cụ thể', + '%s removed a subtask for the task #%d' => ' %s loại bá» má»™t subtask cho nhiệm vụ #%d', + '%s removed a comment on the task #%d' => ' %s xóa má»™t nhận xét vá» nhiệm vụ #%d', + 'Comment removed on task #%d' => 'Äã xóa nhận xét vá» công việc #%d', + 'Subtask removed on task #%d' => 'Huá»· bá» các nhiệm vụ #%d', + 'Hide tasks in this column in the dashboard' => 'Ẩn nhiệm vụ trong cá»™t này trong bảng Ä‘iá»u khiển', + '%s removed a comment on the task %s' => ' %s xóa má»™t nhận xét vá» nhiệm vụ %s', + '%s removed a subtask for the task %s' => ' %s loại bá» má»™t phụ cho nhiệm vụ %s', + 'Comment removed' => 'Äã xóa nhận xét', + 'Subtask removed' => 'Huá»· bá» bá»', + '%s set a new internal link for the task #%d' => ' %s thiết lập má»™t liên kết ná»™i bá»™ má»›i cho nhiệm vụ #%d', + '%s removed an internal link for the task #%d' => ' %s đã xóa liên kết ná»™i bá»™ cho tác vụ #%d', + 'A new internal link for the task #%d has been defined' => 'Má»™t liên kết ná»™i bá»™ má»›i cho nhiệm vụ #%d đã được xác định', + 'Internal link removed for the task #%d' => 'Äã loại bá» liên kết ná»™i bá»™ cho tác vụ #%d', + '%s set a new internal link for the task %s' => ' %s thiết lập má»™t liên kết ná»™i bá»™ má»›i cho nhiệm vụ %s', + '%s removed an internal link for the task %s' => ' %s đã xóa liên kết ná»™i bá»™ cho tác vụ %s', + 'Automatically set the due date on task creation' => 'Tá»± động thiết lập ngày đáo hạn khi tạo tác vụ', + 'Move the task to another column when closed' => 'Di chuyển nhiệm vụ sang cá»™t khác khi đóng', + 'Move the task to another column when not moved during a given period' => 'Di chuyển công việc sang cá»™t khác khi không di chuyển trong má»™t khoảng thá»i gian nhất định', + 'Dashboard for %s' => 'Bảng Ä‘iá»u khiển cho %s', + 'Tasks overview for %s' => 'Tổng quan vá» các nhiệm vụ cho %s', + 'Subtasks overview for %s' => 'Tổng quan vá» các công việc cho %s', + 'Projects overview for %s' => 'Tổng quan vá» dá»± án cho %s', + 'Activity stream for %s' => 'Luồng hoạt động cho %s', + 'Assign a color when the task is moved to a specific swimlane' => 'Chỉ định má»™t màu sắc khi nhiệm vụ được chuyển đến má»™t swimlane cụ thể', + 'Assign a priority when the task is moved to a specific swimlane' => 'Chỉ định má»™t ưu tiên khi nhiệm vụ được chuyển đến má»™t con swimlane cụ thể', + 'User unlocked successfully.' => 'Ngưá»i dùng đã mở khóa thành công.', + 'Unable to unlock the user.' => 'Không thể mở khóa ngưá»i dùng.', + 'Move a task to another swimlane' => 'Di chuyển má»™t nhiệm vụ đến má»™t swimlane khác', + 'Creator Name' => 'Tên ngưá»i tạo', + 'Time spent and estimated' => 'Thá»i gian chi tiêu và ước tính', + 'Move position' => 'Di chuyển vị trí', + 'Move task to another position on the board' => 'Chuyển nhiệm vụ sang vị trí khác trên bảng', + 'Insert before this task' => 'Chèn trước khi tác vụ này', + 'Insert after this task' => 'Chèn sau khi tác vụ này', + 'Unlock this user' => 'Mở khóa ngưá»i dùng này', + 'Custom Project Roles' => 'Vai trò dá»± án tùy chỉnh', + 'Add a new custom role' => 'Thêm má»™t vai trò tùy chỉnh má»›i', + 'Restrictions for the role "%s"' => 'Hạn chế đối vá»›i vai trò %s', + 'Add a new project restriction' => 'Thêm má»™t hạn chế dá»± án má»›i', + 'Add a new drag and drop restriction' => 'Thêm má»™t hạn chế kéo và thả má»›i', + 'Add a new column restriction' => 'Thêm má»™t cá»™t má»›i', + 'Edit this role' => 'Chỉnh sá»­a vai trò này', + 'Remove this role' => 'Loại bá» vai trò này', + 'There is no restriction for this role.' => 'Không có sá»± hạn chế đối vá»›i vai trò này.', + 'Only moving task between those columns is permitted' => 'Chỉ di chuyển nhiệm vụ giữa các cá»™t đó được phép', + 'Close a task in a specific column when not moved during a given period' => 'Äóng má»™t nhiệm vụ trong má»™t cá»™t cụ thể khi không di chuyển trong má»™t khoảng thá»i gian nhất định', + 'Edit columns' => 'Chỉnh sá»­a cá»™t', + 'The column restriction has been created successfully.' => 'Hạn chế cá»™t đã được tạo thành công.', + 'Unable to create this column restriction.' => 'Không thể tạo giá»›i hạn cá»™t này.', + 'Column restriction removed successfully.' => 'Hạn chế cá»™t đã được xoá thành công.', + 'Unable to remove this restriction.' => 'Không thể xóa bá» hạn chế này.', + 'Your custom project role has been created successfully.' => 'Vai trò dá»± án tùy chỉnh cá»§a bạn đã được tạo thành công.', + 'Unable to create custom project role.' => 'Không thể tạo vai trò dá»± án tùy chỉnh.', + 'Your custom project role has been updated successfully.' => 'Vai trò dá»± án tùy chỉnh cá»§a bạn đã được cập nhật thành công.', + 'Unable to update custom project role.' => 'Không thể cập nhật vai trò dá»± án tùy chỉnh.', + 'Custom project role removed successfully.' => 'Vai trò dá»± án đã được gỡ bá» thành công.', + 'Unable to remove this project role.' => 'Không thể xóa vai trò dá»± án này.', + 'The project restriction has been created successfully.' => 'Sá»± hạn chế cá»§a dá»± án đã được tạo ra thành công.', + 'Unable to create this project restriction.' => 'Không thể tạo ra sá»± hạn chế cá»§a dá»± án.', + 'Project restriction removed successfully.' => 'Hạn chế dá»± án đã được loại bá» thành công.', + 'You cannot create tasks in this column.' => 'Bạn không thể tạo ra các nhiệm vụ trong cá»™t này.', + 'Task creation is permitted for this column' => 'Tạo tác vụ được cho phép cho cá»™t này', + 'Closing or opening a task is permitted for this column' => 'Cho phép Äóng hay mở má»™t nhiệm vụ cho cá»™t này', + 'Task creation is blocked for this column' => 'Tạo tác vụ bị chặn cho cá»™t này', + 'Closing or opening a task is blocked for this column' => 'Äóng hoặc mở má»™t tác vụ bị chặn cho cá»™t này', + 'Task creation is not permitted' => 'Không được phép tạo công việc', + 'Closing or opening a task is not permitted' => 'Không được phép đóng hay mở má»™t nhiệm vụ', + 'New drag and drop restriction for the role "%s"' => 'Hạn chế kéo và thả má»›i cho vai trò "%s"', + 'People belonging to this role will be able to move tasks only between the source and the destination column.' => 'Những ngưá»i thuá»™c vai trò này sẽ chỉ có thể di chuyển nhiệm vụ giữa cá»™t nguồn và cá»™t đích.', + 'Remove a column restriction' => 'Há»§y bá» má»™t hạn chế vá» cá»™t', + 'Do you really want to remove this column restriction: "%s" to "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» giá»›i hạn cá»™t này: "%s" thành "%s"?', + 'New column restriction for the role "%s"' => 'Hạn chế cá»™t má»›i cho vai trò %s', + 'Rule' => 'Qui định', + 'Do you really want to remove this column restriction?' => 'Bạn có thá»±c sá»± muốn loại bá» hạn chế cá»™t này?', + 'Custom roles' => 'Vai trò tùy chỉnh', + 'New custom project role' => 'Vai trò dá»± án má»›i tùy chỉnh', + 'Edit custom project role' => 'Chỉnh sá»­a vai trò dá»± án tùy chỉnh', + 'Remove a custom role' => 'Loại bá» má»™t vai trò tùy chỉnh', + 'Do you really want to remove this custom role: "%s"? All people assigned to this role will become project member.' => 'Bạn có thá»±c sá»± muốn loại bá» vai trò tùy chỉnh này: "%s"? Tất cả má»i ngưá»i được giao nhiệm vụ này sẽ trở thành thành viên cá»§a dá»± án.', + 'There is no custom role for this project.' => 'Không có vai trò tùy chỉnh cho dá»± án này.', + 'New project restriction for the role "%s"' => 'Hạn chế dá»± án má»›i cho vai trò "%s"', + 'Restriction' => 'Sá»± hạn chế', + 'Remove a project restriction' => 'Há»§y bá» má»™t dá»± án hạn chế', + 'Do you really want to remove this project restriction: "%s"?' => 'Bạn có thá»±c sá»± muốn loại bá» hạn chế dá»± án này: "%s"?', + 'Duplicate to multiple projects' => 'Nhân đôi vá»›i nhiá»u dá»± án', + 'This field is required' => 'Trưá»ng này là bắt buá»™c', + 'Moving a task is not permitted' => 'Không được phép di chuyển má»™t nhiệm vụ', + 'This value must be in the range %d to %d' => 'Giá trị này phải nằm trong khoảng từ %d đến %d', + 'You are not allowed to move this task.' => 'Bạn không được phép di chuyển nhiệm vụ này.', + 'API User Access' => 'Truy cập Ngưá»i dùng API', + 'Preview' => 'Xem trước', + 'Write' => 'Viết', + 'Write your text in Markdown' => 'Viết văn bản cá»§a bạn trong Markdown', + 'No personal API access token registered.' => 'Không có đăng nhập truy cập API cá nhân.', + 'Your personal API access token is "%s"' => 'Mã thông báo truy cập API cá nhân cá»§a bạn là "%s"', + 'Remove your token' => 'Há»§y bá» mã thông báo cá»§a bạn', + 'Generate a new token' => 'Tạo mã thông báo má»›i', + 'Showing %d-%d of %d' => 'Hiển thị %d-%d cá»§a %d', + 'Outgoing Emails' => 'Email gá»­i Ä‘i', + 'Add or change currency rate' => 'Thêm hoặc thay đổi tá»· lệ tiá»n tệ', + 'Reference currency: %s' => 'Tiá»n tệ tham khảo: %s', + 'Add custom filters' => 'Thêm bá»™ lá»c tùy chỉnh', + 'Export' => 'Xuất khẩu', + 'Add link label' => 'Thêm nhãn liên kết', + 'Incompatible Plugins' => 'Plugin không tương thích', + 'Compatibility' => 'Khả năng tương thích', + 'Permissions and ownership' => 'Quyá»n và quyá»n sở hữu', + 'Priorities' => 'Ưu tiên', + 'Close this window' => 'Äóng cá»­a sổ này', + 'Unable to upload this file.' => 'Không thể tải tệp này lên.', + 'Import tasks' => 'Nhập khẩu các nhiệm vụ', + 'Choose a project' => 'Chá»n má»™t dá»± án', + 'Profile' => 'Hồ sÆ¡', + 'Application role' => 'Vai trò ứng dụng', + '%d invitations were sent.' => '%d lá»i má»i đã được gá»­i.', + '%d invitation was sent.' => 'Thư má»i %d đã được gá»­i.', + 'Unable to create this user.' => 'Không thể tạo ngưá»i dùng này.', + 'Kanboard Invitation' => 'Lá»i má»i cá»§a Kanboard', + 'Visible on dashboard' => 'Hiển thị trên bảng Ä‘iá»u khiển', + 'Created at:' => 'ÄÆ°á»£c tạo vào:', + 'Updated at:' => 'Cập nhật tại:', + 'There is no custom filter.' => 'Không có bá»™ lá»c tuỳ chỉnh.', + 'New User' => 'Ngưá»i dùng má»›i', + 'Authentication' => 'Xác thá»±c', + 'If checked, this user will use a third-party system for authentication.' => 'Nếu được chá»n, ngưá»i dùng này sẽ sá»­ dụng hệ thống cá»§a bên thứ ba để xác thá»±c.', + 'The password is necessary only for local users.' => 'Mật khẩu là cần thiết chỉ dành cho ngưá»i dùng cục bá»™.', + 'You have been invited to register on Kanboard.' => 'Bạn đã được má»i đăng ký trên Kanboard.', + 'Click here to join your team' => 'Nhấp vào đây để tham gia nhóm cá»§a bạn', + 'Invite people' => 'Má»i má»i ngưá»i', + 'Emails' => 'Email', + 'Enter one email address by line.' => 'Nhập má»™t địa chỉ email theo dòng.', + 'Add these people to this project' => 'Thêm những ngưá»i này vào dá»± án này', + 'Add this person to this project' => 'Thêm ngưá»i này vào dá»± án này', + 'Sign-up' => 'Äăng ký', + 'Credentials' => 'Thông tin xác thá»±c', + 'New user' => 'Ngưá»i dùng má»›i', + 'This username is already taken' => 'Tên ngưá»i dùng này đã được dùng', + 'Your profile must have a valid email address.' => 'Tiểu sá»­ cá»§a bạn phải có địa chỉ email hợp lệ.', + 'TRL - Turkish Lira' => 'TRL - Lira Thổ NhÄ© Kỳ', + 'The project email is optional and could be used by several plugins.' => 'Email dá»± án là tùy chá»n và có thể được sá»­ dụng bởi má»™t số plugin.', + 'The project email must be unique across all projects' => 'Email cá»§a dá»± án phải là duy nhất trong tất cả các dá»± án', + 'The email configuration has been disabled by the administrator.' => 'Quản trị viên đã vô hiệu cấu hình email.', + 'Close this project' => 'Äóng dá»± án này', + 'Open this project' => 'Mở dá»± án này', + 'Close a project' => 'Äóng má»™t dá»± án', + 'Do you really want to close this project: "%s"?' => 'Bạn có thá»±c sá»± muốn đóng dá»± án này: "%s"?', + 'Reopen a project' => 'Mở lại má»™t dá»± án', + 'Do you really want to reopen this project: "%s"?' => 'Bạn có thá»±c sá»± muốn mở lại dá»± án này: "%s"?', + 'This project is open' => 'Dá»± án này Ä‘ang mở', + 'This project is closed' => 'Dá»± án này đã kết thúc', + 'Unable to upload files, check the permissions of your data folder.' => 'Không thể tải tệp lên, hãy kiểm tra quyá»n cá»§a thư mục dữ liệu cá»§a bạn.', + 'Another category with the same name exists in this project' => 'Má»™t thể loại khác có cùng tên tồn tại trong dá»± án này', + 'Comment sent by email successfully.' => 'Thảo luận gá»­i qua email thành công.', + 'Sent by email to "%s" (%s)' => 'Gá»­i qua email tá»›i "%s" ( %s)', + 'Unable to read uploaded file.' => 'Không thể Ä‘á»c tập tin được tải lên.', + 'Database uploaded successfully.' => 'CÆ¡ sở dữ liệu được tải lên thành công.', + 'Task sent by email successfully.' => 'Nhiệm vụ gá»­i qua email thành công.', + 'There is no category in this project.' => 'Không có hạng mục trong dá»± án này.', + 'Send by email' => 'Gá»­i bằng thư Ä‘iện tá»­', + 'Create and send a comment by email' => 'Tạo và gá»­i má»™t nhận xét qua email', + 'Subject' => 'Môn há»c', + 'Upload the database' => 'Tải lên cÆ¡ sở dữ liệu', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'Bạn có thể tải lên cÆ¡ sở dữ liệu Sqlite đã tải xuống trước đây (định dạng Gzip)', + 'Database file' => 'Tập tin cÆ¡ sở dữ liệu', + 'Upload' => 'Tải lên', + 'Your project must have at least one active swimlane.' => 'Dá»± án cá»§a bạn phải có ít nhất má»™t swimlane hoạt động.', + 'Project: %s' => 'Dá»± án: %s', + 'Automatic action not found: "%s"' => 'Không tìm thấy hành động tá»± động: "%s"', + '%d projects' => '%d dá»± án', + '%d project' => '%d dá»± án', + 'There is no project.' => 'Không có dá»± án.', + 'Sort' => 'Sắp xếp', + 'Project ID' => 'ID dá»± án', + 'Project name' => 'Tên dá»± án', + 'Public' => 'Công cá»™ng', + 'Personal' => 'Riêng tư', + '%d tasks' => '%d nhiệm vụ', + '%d task' => '%d công việc', + 'Task ID' => 'ID công việc', + 'Assign automatically a color when due date is expired' => 'Chỉ định màu tá»± động khi ngày hết hạn hết hạn', + 'Total score in this column across all swimlanes' => 'Tổng số Ä‘iểm trong cá»™t này trên tất cả các đưá»ng swimlane', + 'HRK - Kuna' => 'HRK - Kuna', + 'ARS - Argentine Peso' => 'ARS - Peso Argentina', + 'COP - Colombian Peso' => 'COP - Colombia Peso', + '%d groups' => '%d nhóm', + '%d group' => '%d nhóm', + 'Group ID' => 'ID nhóm', + 'External ID' => 'ID bên ngoài', + '%d users' => '%d ngưá»i dùng', + '%d user' => '%d ngưá»i dùng', + 'Hide subtasks' => 'Ẩn các nhiệm vụ phụ', + 'Show subtasks' => 'Hiện các nhiệm vụ phụ', + 'Authentication Parameters' => 'Thông số xác thá»±c', + 'API Access' => 'Truy cập API', + 'No users found.' => 'Không tìm thấy ngưá»i dùng.', + 'User ID' => 'Tên ngưá»i dùng', + 'Notifications are activated' => 'Thông báo được kích hoạt', + 'Notifications are disabled' => 'Thông báo bị vô hiệu hóa', + 'User disabled' => 'Ngưá»i dùng đã vô hiệu hóa', + '%d notifications' => '%d thông báo', + '%d notification' => '%d thông báo', + 'There is no external integration installed.' => 'Không có tích hợp bên ngoài được cài đặt.', + 'You are not allowed to update tasks assigned to someone else.' => 'Bạn không được phép cập nhật các nhiệm vụ được giao cho ngưá»i khác.', + 'You are not allowed to change the assignee.' => 'Bạn không được phép thay đổi ngưá»i được chuyển nhượng.', + 'Task suppression is not permitted' => 'Không được phép loại bá» công việc', + 'Changing assignee is not permitted' => 'Không được phép thay đổi ngưá»i được chuyển nhượng', + 'Update only assigned tasks is permitted' => 'Chỉ được phép cập nhật các nhiệm vụ được giao', + 'Only for tasks assigned to the current user' => 'Chỉ cho các nhiệm vụ được gán cho ngưá»i dùng hiện tại', + 'My projects' => 'Dá»± án cá»§a tôi', + 'You are not a member of any project.' => 'Bạn không phải là thành viên cá»§a bất kỳ dá»± án nào.', + 'My subtasks' => 'Nhiệm vụ phụ cá»§a tôi', + '%d subtasks' => '%d nhiệm vụ phụ', + '%d subtask' => '%d nhiệm vụ phụ', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'Chỉ di chuyển nhiệm vụ giữa các cá»™t được phép cho các nhiệm vụ được giao cho ngưá»i sá»­ dụng hiện tại', + '[DUPLICATE]' => '[BẢN SAO]', + 'DKK - Danish Krona' => 'DKK - Äan Mạch Äan Mạch', + 'Remove user from group' => 'Xóa ngưá»i dùng khá»i nhóm', + 'Assign the task to its creator' => 'Chỉ định nhiệm vụ cho ngưá»i sáng tạo', + 'This task was sent by email to "%s" with subject "%s".' => 'Nhiệm vụ này được gá»­i qua email tá»›i "%s" vá»›i chá»§ đỠ"%s".', + 'Predefined Email Subjects' => 'Äối tượng Email được Xác định trước', + 'Write one subject by line.' => 'Viết má»™t chá»§ đỠtheo dòng.', + 'Create another link' => 'Tạo liên kết khác', + 'BRL - Brazilian Real' => 'BRL - Brazilian Real', + 'Add a new Kanboard task' => 'Thêm nhiệm vụ Kanboard má»›i', + 'Subtask not started' => 'Nhiệm vụ con chưa bắt đầu', + 'Subtask currently in progress' => 'Nhiệm vụ con Ä‘ang tiến hành', + 'Subtask completed' => 'Nhiệm vụ con đã hoàn thành', + 'Subtask added successfully.' => 'Nhiệm vụ con đã được thêm thành công.', + '%d subtasks added successfully.' => '%d nhiệm vụ con đã được thêm thành công.', + 'Enter one subtask by line.' => 'Nhập má»—i nhiệm vụ con trên má»™t dòng.', + 'Predefined Contents' => 'Ná»™i dung được xác định trước', + 'Predefined contents' => 'Ná»™i dung được xác định trước', + 'Predefined Task Description' => 'Mô tả nhiệm vụ được xác định trước', + 'Do you really want to remove this template? "%s"' => 'Bạn có thá»±c sá»± muốn xóa mẫu này? "%s"', + 'Add predefined task description' => 'Thêm mô tả nhiệm vụ được xác định trước', + 'Predefined Task Descriptions' => 'Mô tả nhiệm vụ được xác định trước', + 'Template created successfully.' => 'Mẫu đã được tạo thành công.', + 'Unable to create this template.' => 'Không thể tạo mẫu này.', + 'Template updated successfully.' => 'Mẫu đã được cập nhật thành công.', + 'Unable to update this template.' => 'Không thể cập nhật mẫu này.', + 'Template removed successfully.' => 'Mẫu đã được xóa thành công.', + 'Unable to remove this template.' => 'Không thể xóa mẫu này.', + 'Template for the task description' => 'Mẫu cho mô tả nhiệm vụ', + 'The start date is greater than the end date' => 'Ngày bắt đầu lá»›n hÆ¡n ngày kết thúc', + 'Tags must be separated by a comma' => 'Các thẻ phải được phân tách bằng dấu phẩy', + 'Only the task title is required' => 'Chỉ tiêu đỠnhiệm vụ là bắt buá»™c', + 'Creator Username' => 'Tên ngưá»i dùng ngưá»i tạo', + 'Color Name' => 'Tên màu', + 'Column Name' => 'Tên cá»™t', + 'Swimlane Name' => 'Tên làn', + 'Time Estimated' => 'Thá»i gian ước tính', + 'Time Spent' => 'Thá»i gian đã sá»­ dụng', + 'External Link' => 'Liên kết ngoài', + 'This feature enables the iCal feed, RSS feed and the public board view.' => 'Tính năng này cho phép nguồn cấp dữ liệu iCal, nguồn cấp dữ liệu RSS và chế độ xem bảng công khai.', + 'Stop the timer of all subtasks when moving a task to another column' => 'Dừng bá»™ hẹn giá» cá»§a tất cả các nhiệm vụ con khi di chuyển má»™t nhiệm vụ sang cá»™t khác', + 'Subtask Title' => 'Tiêu đỠnhiệm vụ con', + 'Add a subtask and activate the timer when moving a task to another column' => 'Thêm nhiệm vụ con và kích hoạt bá»™ hẹn giá» khi di chuyển má»™t nhiệm vụ sang cá»™t khác', + 'days' => 'ngày', + 'minutes' => 'phút', + 'seconds' => 'giây', + 'Assign automatically a color when preset start date is reached' => 'Tá»± động gán màu khi đạt đến ngày bắt đầu cài đặt trước', + 'Move the task to another column once a predefined start date is reached' => 'Di chuyển nhiệm vụ sang cá»™t khác sau khi đạt đến ngày bắt đầu được xác định trước', + 'This task is now linked to the task %s with the relation "%s"' => 'Nhiệm vụ này hiện được liên kết vá»›i nhiệm vụ %s vá»›i quan hệ "%s"', + 'The link with the relation "%s" to the task %s has been removed' => 'Liên kết vá»›i quan hệ "%s" đến nhiệm vụ %s đã bị xóa', + 'Custom Filter:' => 'Bá»™ lá»c tùy chỉnh:', + 'Unable to find this group.' => 'Không tìm thấy nhóm này.', + '%s moved the task #%d to the column "%s"' => '%s đã di chuyển nhiệm vụ #%d sang cá»™t "%s"', + '%s moved the task #%d to the position %d in the column "%s"' => '%s đã di chuyển nhiệm vụ #%d đến vị trí %d trong cá»™t "%s"', + '%s moved the task #%d to the swimlane "%s"' => '%s đã di chuyển nhiệm vụ #%d đến làn "%s"', + '%sh spent' => '%sh đã dùng', + '%sh estimated' => '%sh ước tính', + 'Select All' => 'Chá»n tất cả', + 'Unselect All' => 'Bá» chá»n tất cả', + 'Apply action' => 'Ãp dụng hành động', + 'Move selected tasks to another column or swimlane' => 'Di chuyển các nhiệm vụ đã chá»n sang cá»™t hoặc làn khác', + 'Edit tasks in bulk' => 'Chỉnh sá»­a hàng loạt nhiệm vụ', + 'Choose the properties that you would like to change for the selected tasks.' => 'Chá»n các thuá»™c tính mà bạn muốn thay đổi cho các nhiệm vụ đã chá»n.', + 'Configure this project' => 'Cấu hình dá»± án này', + 'Start now' => 'Bắt đầu ngay', + '%s removed a file from the task #%d' => '%s đã xóa má»™t tệp khá»i nhiệm vụ #%d', + 'Attachment removed from task #%d: %s' => 'Äã xóa tệp đính kèm khá»i nhiệm vụ #%d: %s', + 'No color' => 'Không màu', + 'Attachment removed "%s"' => 'Äã xóa tệp đính kèm "%s"', + '%s removed a file from the task %s' => '%s đã xóa má»™t tệp khá»i nhiệm vụ %s', + 'Move the task to another swimlane when assigned to a user' => 'Di chuyển nhiệm vụ sang làn khác khi được giao cho ngưá»i dùng', + 'Destination swimlane' => 'Làn đích', + 'Assign a category when the task is moved to a specific swimlane' => 'Chỉ định danh mục khi nhiệm vụ được di chuyển đến má»™t làn cụ thể', + 'Move the task to another swimlane when the category is changed' => 'Di chuyển nhiệm vụ sang làn khác khi danh mục được thay đổi', + 'Reorder this column by priority (ASC)' => 'Sắp xếp lại cá»™t này theo mức ưu tiên (Tăng dần)', + 'Reorder this column by priority (DESC)' => 'Sắp xếp lại cá»™t này theo mức ưu tiên (Giảm dần)', + 'Reorder this column by assignee and priority (ASC)' => 'Sắp xếp lại cá»™t này theo ngưá»i được giao và mức ưu tiên (Tăng dần)', + 'Reorder this column by assignee and priority (DESC)' => 'Sắp xếp lại cá»™t này theo ngưá»i được giao và mức ưu tiên (Giảm dần)', + 'Reorder this column by assignee (A-Z)' => 'Sắp xếp lại cá»™t này theo ngưá»i được giao (A-Z)', + 'Reorder this column by assignee (Z-A)' => 'Sắp xếp lại cá»™t này theo ngưá»i được giao (Z-A)', + 'Reorder this column by due date (ASC)' => 'Sắp xếp lại cá»™t này theo ngày đến hạn (Tăng dần)', + 'Reorder this column by due date (DESC)' => 'Sắp xếp lại cá»™t này theo ngày đến hạn (Giảm dần)', + 'Reorder this column by id (ASC)' => 'Sắp xếp lại cá»™t này theo id (Tăng dần)', + 'Reorder this column by id (DESC)' => 'Sắp xếp lại cá»™t này theo id (Giảm dần)', + '%s moved the task #%d "%s" to the project "%s"' => '%s đã di chuyển nhiệm vụ #%d "%s" sang dá»± án "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => 'Nhiệm vụ #%d "%s" đã được di chuyển sang dá»± án "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => 'Di chuyển nhiệm vụ sang cá»™t khác khi ngày đến hạn còn ít hÆ¡n má»™t số ngày nhất định', + 'Automatically update the start date when the task is moved away from a specific column' => 'Tá»± động cập nhật ngày bắt đầu khi nhiệm vụ được di chuyển khá»i má»™t cá»™t cụ thể', + 'HTTP Client:' => 'Máy khách HTTP:', + 'Assigned' => 'Äã giao', + 'Task limits apply to each swimlane individually' => 'Giá»›i hạn nhiệm vụ áp dụng cho từng làn riêng lẻ', + 'Column task limits apply to each swimlane individually' => 'Giá»›i hạn nhiệm vụ cá»§a cá»™t áp dụng cho từng làn riêng lẻ', + 'Column task limits are applied to each swimlane individually' => 'Giá»›i hạn nhiệm vụ cá»§a cá»™t được áp dụng cho từng làn riêng lẻ', + 'Column task limits are applied across swimlanes' => 'Giá»›i hạn nhiệm vụ cá»§a cá»™t được áp dụng trên các làn', + 'Task limit: ' => 'Giá»›i hạn nhiệm vụ: ', + 'Change to global tag' => 'Thay đổi thành thẻ toàn cầu', + 'Do you really want to make the tag "%s" global?' => 'Bạn có thá»±c sá»± muốn đặt thẻ "%s" thành toàn cầu không?', + 'Enable global tags for this project' => 'Bật thẻ toàn cầu cho dá»± án này', + 'Group membership(s):' => 'Thành viên nhóm:', + '%s is a member of the following group(s): %s' => '%s là thành viên cá»§a (các) nhóm sau: %s', + '%d/%d group(s) shown' => 'Hiển thị %d/%d nhóm', + 'Subtask creation or modification' => 'Tạo hoặc sá»­a đổi nhiệm vụ con', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'Giao nhiệm vụ cho ngưá»i dùng cụ thể khi nhiệm vụ được di chuyển đến má»™t làn cụ thể', + 'Comment' => 'Bình luận', + 'Collapse vertically' => 'Thu gá»n theo chiá»u dá»c', + 'Expand vertically' => 'Mở rá»™ng theo chiá»u dá»c', + 'MXN - Mexican Peso' => 'MXN - Peso Mexico', + 'Estimated vs actual time per column' => 'Thá»i gian ước tính so vá»›i thá»i gian thá»±c tế trên má»—i cá»™t', + 'HUF - Hungarian Forint' => 'HUF - Forint Hungary', + 'XBT - Bitcoin' => 'XBT - Bitcoin', + 'You must select a file to upload as your avatar!' => 'Bạn phải chá»n má»™t tệp để tải lên làm ảnh đại diện cá»§a mình!', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'Tệp bạn tải lên không phải là hình ảnh hợp lệ! (Chỉ cho phép các định dạng *.gif, *.jpg, *.jpeg và *.png!)', + 'Automatically set the due date when the task is moved away from a specific column' => 'Tá»± động đặt ngày đến hạn khi nhiệm vụ được di chuyển khá»i má»™t cá»™t cụ thể', + 'No other projects found.' => 'Không tìm thấy dá»± án nào khác.', + 'Tasks copied successfully.' => 'Các nhiệm vụ đã được sao chép thành công.', + 'Unable to copy tasks.' => 'Không thể sao chép các nhiệm vụ.', + 'Theme' => 'Chá»§ Ä‘á»', + 'Theme:' => 'Chá»§ Ä‘á»:', + 'Light theme' => 'Chá»§ đỠsáng', + 'Dark theme' => 'Chá»§ đỠtối', + 'Automatic theme - Sync with system' => 'Chá»§ đỠtá»± động - Äồng bá»™ hóa vá»›i hệ thống', + 'Application managers or more' => 'Ngưá»i quản lý ứng dụng trở lên', + 'Administrators' => 'Quản trị viên', + 'Visibility:' => 'Hiển thị:', + 'Standard users' => 'Ngưá»i dùng tiêu chuẩn', + 'Visibility is required' => 'Yêu cầu hiển thị', + 'The visibility should be an app role' => 'Hiển thị phải là má»™t vai trò ứng dụng', + 'Reply' => 'Trả lá»i', + '%s wrote: ' => '%s đã viết: ', + 'Number of visible tasks in this column and swimlane' => 'Số lượng nhiệm vụ hiển thị trong cá»™t và làn này', + 'Number of tasks in this swimlane' => 'Số lượng nhiệm vụ trong làn này', + 'Unable to find another subtask in progress, you can close this window.' => 'Không tìm thấy nhiệm vụ con nào khác Ä‘ang xá»­ lý, bạn có thể đóng cá»­a sổ này.', + 'This theme is invalid' => 'Chá»§ đỠnày không hợp lệ', + 'This role is invalid' => 'Vai trò này không hợp lệ', + 'This timezone is invalid' => 'Múi giá» này không hợp lệ', + 'This language is invalid' => 'Ngôn ngữ này không hợp lệ', + 'This URL is invalid' => 'URL này không hợp lệ', + 'Date format invalid' => 'Äịnh dạng ngày không hợp lệ', + 'Time format invalid' => 'Äịnh dạng thá»i gian không hợp lệ', + 'Invalid Mail transport' => 'Vận chuyển thư không hợp lệ', + 'Color invalid' => 'Màu không hợp lệ', + 'This value must be greater or equal to %d' => 'Giá trị này phải lá»›n hÆ¡n hoặc bằng %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => 'Thêm BOM vào đầu tệp (bắt buá»™c đối vá»›i Microsoft Excel)', + 'Just add these tag(s)' => 'Chỉ cần thêm (các) thẻ này', + 'Remove internal link(s)' => 'Xóa (các) liên kết ná»™i bá»™', + 'Import tasks from another project' => 'Nhập nhiệm vụ từ dá»± án khác', + 'Select the project to copy tasks from' => 'Chá»n dá»± án để sao chép nhiệm vụ từ đó', + 'The total maximum allowed attachments size is %sB.' => 'Tổng kích thước tệp đính kèm tối Ä‘a được phép là %sB.', + 'Add attachments' => 'Thêm tệp đính kèm', + 'Task #%d "%s" is overdue' => 'Nhiệm vụ #%d "%s" đã quá hạn', + 'Enable notifications by default for all new users' => 'Kích hoạt thông báo theo mặc định cho tất cả ngưá»i dùng má»›i', + 'Assign the task to its creator for specific columns if no assignee is set manually' => 'Gán công việc cho ngưá»i tạo nó đối vá»›i các cá»™t cụ thể nếu chưa đặt ngưá»i phụ trách thá»§ công', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => 'Gán công việc cho ngưá»i dùng Ä‘ang đăng nhập khi chuyển cá»™t sang cá»™t được chỉ định nếu chưa có ngưá»i dùng được gán', +]; diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php new file mode 100644 index 0000000..34f7503 --- /dev/null +++ b/app/Locale/zh_CN/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'æ— ', + 'Edit' => '编辑', + 'Remove' => '移除', + 'Yes' => '是', + 'No' => 'å¦', + 'cancel' => 'å–æ¶ˆ', + 'or' => '或者', + 'Yellow' => '黄色', + 'Blue' => 'è“色', + 'Green' => '绿色', + 'Purple' => '紫色', + 'Red' => '红色', + 'Orange' => '橘色', + 'Grey' => 'ç°è‰²', + 'Brown' => 'è¤è‰²', + 'Deep Orange' => '橘红色', + 'Dark Grey' => 'æ·±ç°è‰²', + 'Pink' => '粉红色', + 'Teal' => 'è“绿色', + 'Cyan' => 'é’色', + 'Lime' => '黄绿色', + 'Light Green' => '浅绿色', + 'Amber' => '黄è¤è‰²', + 'Save' => 'ä¿å­˜', + 'Login' => '登录', + 'Official website:' => '官方网站:', + 'Unassigned' => '未指派', + 'View this task' => '查看该任务', + 'Remove user' => '移除用户', + 'Do you really want to remove this user: "%s"?' => '确定è¦åˆ é™¤ç”¨æˆ·"%s"å—?', + 'All users' => '所有用户', + 'Username' => '用户å', + 'Password' => '密ç ', + 'Administrator' => '超级管ç†å‘˜', + 'Sign in' => '登录', + 'Users' => '用户', + 'Forbidden' => 'ç¦æ­¢', + 'Access Forbidden' => 'ç¦æ­¢è¿›å…¥', + 'Edit user' => '修改用户', + 'Logout' => '退出', + 'Bad username or password' => 'ç”¨æˆ·åæˆ–密ç é”™è¯¯', + 'Edit project' => '修改项目', + 'Name' => 'åç§°', + 'Projects' => '项目', + 'No project' => '无项目', + 'Project' => '项目', + 'Status' => '状æ€', + 'Tasks' => '任务', + 'Board' => '看æ¿', + 'Actions' => '动作', + 'Inactive' => '未激活', + 'Active' => '激活', + 'Unable to update this board.' => '无法更新该看æ¿ã€‚', + 'Disable' => 'åœç”¨', + 'Enable' => 'å¯ç”¨', + 'New project' => '新建团队项目', + 'Do you really want to remove this project: "%s"?' => '确定è¦ç§»é™¤é¡¹ç›®"%s"å—?', + 'Remove project' => '移除项目', + 'Edit the board for "%s"' => '为"%s"修改看æ¿', + 'Add a new column' => '添加新æ ç›®', + 'Title' => '标题', + 'Assigned to %s' => '指派给 %s', + 'Remove a column' => '移除一个æ ç›®', + 'Unable to remove this column.' => '无法移除该æ ç›®ã€‚', + 'Do you really want to remove this column: "%s"?' => '确定è¦ç§»é™¤æ ç›®"%s"å—?', + 'Settings' => '设置', + 'Application settings' => '应用设置', + 'Language' => '语言', + 'Webhook token:' => '页é¢é’©å­ä»¤ç‰Œï¼š', + 'API token:' => 'API 令牌:', + 'Database size:' => 'æ•°æ®åº“大å°ï¼š', + 'Download the database' => '下载数æ®åº“', + 'Optimize the database' => '优化数æ®åº“', + '(VACUUM command)' => '(VACUUM 指令)', + '(Gzip compressed Sqlite file)' => '(用Gzip压缩的Sqlite文件)', + 'Close a task' => '关闭一个任务', + 'Column' => 'æ ç›®', + 'Color' => '颜色', + 'Assignee' => '指派给', + 'Create another task' => '创建å¦ä¸€ä¸ªä»»åŠ¡', + 'New task' => '新建任务', + 'Open a task' => 'å¼€å¯ä¸€ä¸ªä»»åŠ¡', + 'Do you really want to open this task: "%s"?' => '你确定è¦å¼€è¿™ä¸ªä»»åŠ¡å—?"%s"', + 'Back to the board' => '回到看æ¿', + 'There is nobody assigned' => '当剿— äººè¢«æŒ‡æ´¾', + 'Column on the board:' => '看æ¿ä¸Šçš„æ ç›®ï¼š', + 'Close this 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个英文字符', + 'The password is required' => '需è¦å¯†ç ', + 'This value must be an integer' => '该值必须为整数', + 'The username must be unique' => '用户å必须唯一', + 'The user id is required' => '用户id是必须的', + 'Passwords don\'t match' => '密ç ä¸åŒ¹é…', + 'The confirmation is required' => '需è¦ç¡®è®¤', + 'The project is required' => 'éœ€è¦æŒ‡å®šé¡¹ç›®', + 'The id is required' => 'éœ€è¦æŒ‡å®šid', + 'The project id is required' => 'éœ€è¦æŒ‡å®šé¡¹ç›®id', + 'The project name is required' => 'éœ€è¦æŒ‡å®šé¡¹ç›®åç§°', + 'The title is required' => 'éœ€è¦æŒ‡å®šæ ‡é¢˜', + 'Settings saved successfully.' => '设置æˆåŠŸä¿å­˜ã€‚', + 'Unable to save your settings.' => '无法ä¿å­˜ä½ çš„设置。', + 'Database optimization done.' => 'æ•°æ®åº“优化完æˆã€‚', + 'Your project has been created successfully.' => 'æ‚¨çš„é¡¹ç›®å·²ç»æˆåŠŸåˆ›å»ºã€‚', + 'Unable to create your project.' => '无法为您创建项目。', + 'Project updated successfully.' => 'æˆåŠŸæ›´æ–°é¡¹ç›®ã€‚', + 'Unable to update this project.' => '无法更新该项目。', + 'Unable to remove this project.' => '无法移除该项目。', + 'Project removed successfully.' => 'æˆåŠŸç§»é™¤é¡¹ç›®ã€‚', + 'Project activated successfully.' => '项目æˆåŠŸæ¿€æ´»ã€‚', + 'Unable to activate this project.' => '无法激活该项目。', + 'Project disabled successfully.' => 'æˆåŠŸåœç”¨é¡¹ç›®ã€‚', + 'Unable to disable this project.' => '无法åœç”¨è¯¥é¡¹ç›®ã€‚', + 'Unable to open this task.' => '无法开å¯è¯¥ä»»åŠ¡ã€‚', + 'Task opened successfully.' => 'ä»»åŠ¡å¼€å¯æˆåŠŸã€‚', + 'Unable to close this task.' => '无法关闭该任务。', + 'Task closed successfully.' => 'æˆåŠŸå…³é—­ä»»åŠ¡ã€‚', + 'Unable to update your task.' => '无法更新您的任务。', + 'Task updated successfully.' => 'æˆåŠŸæ›´æ–°ä»»åŠ¡ã€‚', + 'Unable to create your task.' => '无法为您创建任务。', + 'Task created successfully.' => 'æˆåŠŸåˆ›å»ºä»»åŠ¡ã€‚', + 'User created successfully.' => 'æˆåŠŸåˆ›å»ºç”¨æˆ·ã€‚', + 'Unable to create your user.' => '无法创建用户。', + 'User updated successfully.' => 'æˆåŠŸæ›´æ–°ç”¨æˆ·ã€‚', + 'User removed successfully.' => 'æˆåŠŸç§»é™¤ç”¨æˆ·ã€‚', + 'Unable to remove this user.' => '无法移除该用户。', + 'Board updated successfully.' => 'çœ‹æ¿æˆåŠŸæ›´æ–°ã€‚', + 'Ready' => '预备', + 'Backlog' => '待办', + 'Work in progress' => '进行中', + 'Done' => '完æˆ', + 'Application version:' => '应用程åºç‰ˆæœ¬ï¼š', + 'Id' => 'ç¼–å·', + 'Public link' => '公开链接', + 'Timezone' => '时区', + 'Sorry, I didn\'t find this information in my database!' => '抱歉,无法在数æ®åº“中找到该信æ¯ï¼', + 'Page not found' => '页颿œªæ‰¾åˆ°', + 'Complexity' => '夿‚度', + 'Task limit' => '任务é™åˆ¶', + 'Task count' => '任务数', + 'User' => '用户', + 'Comments' => '评论', + 'Comment is required' => '评论ä¸èƒ½ä¸ºç©º', + 'Comment added successfully.' => '评论æˆåŠŸæ·»åŠ ã€‚', + 'Unable to create your comment.' => '无法创建评论。', + 'Due Date' => '到期时间', + 'Invalid date' => '无效日期', + 'Automatic actions' => '自动动作', + 'Your automatic action has been created successfully.' => '您的自动动作已æˆåŠŸåˆ›å»º', + 'Unable to create your automatic action.' => '无法为您创建自动动作。', + 'Remove an action' => '移除一个动作。', + 'Unable to remove this action.' => '无法移除该动作', + 'Action removed successfully.' => 'æˆåŠŸç§»é™¤åŠ¨ä½œã€‚', + 'Automatic actions for the project "%s"' => '项目"%s"的自动动作', + 'Add an action' => '添加动作', + 'Event name' => '事件åç§°', + 'Action' => '动作', + 'Event' => '事件', + 'When the selected event occurs execute the corresponding action.' => '当所选事件å‘生时执行相应动作。', + 'Next step' => '下一步', + '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 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' => '为特定用户指派颜色', + 'Position' => 'ä½ç½®', + 'Duplicate to project' => 'å¤åˆ¶åˆ°å¦ä¸€é¡¹ç›®', + 'Duplicate' => 'å¤åˆ¶', + 'Link' => '连接', + 'Comment updated successfully.' => '评论æˆåŠŸæ›´æ–°ã€‚', + 'Unable to update your comment.' => '无法更新您的评论。', + 'Remove a comment' => '移除评论', + 'Comment removed successfully.' => '评论æˆåŠŸç§»é™¤ã€‚', + 'Unable to remove this comment.' => '无法移除该评论。', + 'Do you really want to remove this comment?' => '确定è¦ç§»é™¤è¯„论å—?', + 'Current password for the user "%s"' => '用户"%s"的当å‰å¯†ç ', + 'The current password is required' => '需è¦è¾“入当å‰å¯†ç ', + 'Wrong password' => '密ç é”™è¯¯', + 'Unknown' => '未知', + 'Last logins' => '上次登录', + 'Login date' => '登录日期', + 'Authentication method' => 'è®¤è¯æ–¹å¼', + 'IP address' => 'IP地å€', + 'User agent' => 'æµè§ˆå™¨æ ‡è¯†', + 'Persistent connections' => '登录会è¯', + 'No session.' => '无会è¯', + 'Expiration date' => '过期日期', + 'Remember Me' => 'è®°ä½æˆ‘', + 'Creation date' => '创建日期', + 'Everybody' => '所有人', + 'Open' => '打开', + 'Closed' => '关闭', + 'Search' => '查找', + 'Nothing found.' => '没找到。', + 'Due date' => '到期时间', + 'Description' => 'æè¿°', + '%d comments' => '%d个评论', + '%d comment' => '%d个评论', + '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.' => 'ä½ å·²æˆåŠŸå…³è”到你的外部账户', + 'Email' => '邮件', + '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' => '基于一ç§é¢œè‰²è‡ªåŠ¨æŒ‡æ´¾åˆ†ç±»', + 'Task creation or modification' => '任务创建或修改', + 'Category' => '分类', + 'Category:' => '分类:', + 'Categories' => '分类', + 'Your category has been created successfully.' => 'æˆåŠŸä¸ºæ‚¨åˆ›å»ºåˆ†ç±»ã€‚', + 'This category has been updated successfully.' => 'æˆåŠŸä¸ºæ‚¨æ›´æ–°åˆ†ç±»ã€‚', + 'Unable to update this 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' => '无分类', + 'The name is required' => 'å¿…é¡»è¦æœ‰åå­—', + 'Remove a file' => '移除一个文件', + 'Unable to remove this file.' => '无法移除该文件。', + 'File removed successfully.' => '文件æˆåŠŸç§»é™¤ã€‚', + 'Attach a document' => '附加文档', + 'Do you really want to remove this file: "%s"?' => '确定è¦ç§»é™¤æ–‡ä»¶"%s"å—?', + 'Attachments' => '附件', + 'Edit the task' => '修改任务', + 'Add a comment' => '添加评论', + 'Edit a comment' => '编辑评论', + 'Summary' => '概è¦', + 'Time tracking' => '时间记录', + 'Estimate:' => '评估:', + 'Spent:' => '花费:', + 'Do you really want to remove this sub-task?' => '请确认是å¦è¦åˆ é™¤æ­¤å­ä»»åŠ¡ï¼Ÿ', + 'Remaining:' => '剩余:', + 'hours' => 'å°æ—¶', + 'estimated' => '评估', + 'Sub-Tasks' => 'å­ä»»åŠ¡', + 'Add a sub-task' => '添加å­ä»»åŠ¡', + 'Original estimate' => 'åˆæ­¥é¢„ä¼°', + 'Create another sub-task' => '创建å¦ä¸€ä¸ªå­ä»»åŠ¡', + 'Time spent' => '时间花费', + 'Edit a sub-task' => '编辑å­ä»»åŠ¡', + 'Remove a sub-task' => '删除å­ä»»åŠ¡', + 'The time must be a numeric value' => '时间必须为数字', + 'Todo' => '待完æˆ', + 'In progress' => '正在进行', + 'Sub-task removed successfully.' => 'æˆåŠŸåˆ é™¤å­ä»»åŠ¡', + 'Unable to remove this sub-task.' => '无法删除此任务', + 'Sub-task updated successfully.' => 'æˆåŠŸåˆ›å»ºå­ä»»åŠ¡', + 'Unable to update your sub-task.' => '无法更新å­ä»»åŠ¡', + 'Unable to create your sub-task.' => '无法创建å­ä»»åŠ¡', + 'Maximum size: ' => '大å°ä¸Šé™ï¼š', + 'Display another project' => '显示其它项目', + 'Created by %s' => '创建者:%s', + 'Tasks Export' => '任务导出', + 'Start Date' => '开始时间', + 'Execute' => '执行', + 'Task Id' => '任务ID', + 'Creator' => '创建者', + 'Modification date' => '修改日期', + 'Completion date' => 'å®Œæˆæ—¥æœŸ', + 'Clone' => '克隆', + 'Project cloned successfully.' => 'æˆåŠŸå¤åˆ¶é¡¹ç›®ã€‚', + 'Unable to clone this project.' => '无法å¤åˆ¶æ­¤é¡¹ç›®', + 'Enable email notifications' => 'å¯ç”¨é‚®ä»¶é€šçŸ¥', + 'Task position:' => '任务ä½ç½®ï¼š', + 'The task #%d has been opened.' => '任务#%då·²ç»è¢«å¼€å¯.', + 'The task #%d has been closed.' => '任务#%då·²ç»è¢«å…³é—­.', + 'Sub-task updated' => 'å­ä»»åŠ¡æ›´æ–°', + 'Title:' => '标题:', + 'Status:' => '状æ€ï¼š', + 'Assignee:' => '指派给', + 'Time tracking:' => '时间记录', + 'New sub-task' => '新建å­ä»»åŠ¡', + 'New attachment added "%s"' => '新附件已添加"%s"', + 'New comment posted by %s' => '%s 的新评论', + 'New comment' => '新建评论', + 'Comment updated' => '更新了评论', + 'New subtask' => '新建å­ä»»åŠ¡', + 'I only want to receive notifications for these projects:' => 'æˆ‘ä»…éœ€è¦æ”¶åˆ°ä¸‹é¢é¡¹ç›®çš„通知:', + 'view the task on Kanboard' => '在看æ¿ä¸­æŸ¥çœ‹æ­¤ä»»åŠ¡', + 'Public access' => '公开访问', + 'Disable public access' => 'åœæ­¢å…¬å¼€è®¿é—®', + 'Enable public access' => 'å¼€å¯å…¬å¼€è®¿é—®', + 'Public access disabled' => 'å·²ç»ç¦æ­¢å…¬å¼€è®¿é—®', + 'Move the task to another project' => '移动任务到其它项目', + 'Move to project' => '移动到其它项目', + 'Do you really want to duplicate this task?' => '确定è¦å¤åˆ¶æ­¤ä»»åŠ¡å—?', + 'Duplicate a task' => 'å¤åˆ¶ä»»åŠ¡', + 'External accounts' => '外部账户', + 'Account type' => '账户类型', + 'Local' => '本地', + 'Remote' => '远程', + 'Enabled' => 'å¯ç”¨', + 'Disabled' => 'åœç”¨', + 'Login:' => '用户å:', + 'Full Name:' => 'å§“å:', + 'Email:' => '邮件:', + 'Notifications:' => '通知:', + 'Notifications' => '通知设定', + 'Account type:' => '账户类型:', + 'Edit profile' => '编辑属性', + 'Change password' => '修改密ç ', + 'Password modification' => '修改密ç ', + 'External authentications' => '外部认è¯', + 'Never connected.' => '从未连接。', + '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 移动到了"%s"的第#%d个ä½ç½®', + '%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/%s å°æ—¶', + '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 链接', + '%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)' => '新建项目的默认æ ç›®(用逗å·åˆ†å¼€)', + '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' => '选择一个事件', + 'Create a task from an external provider' => '从外部创建任务', + 'Change the assignee based on an external username' => 'æ ¹æ®å¤–部用户å修改任务å—ç†äºº', + 'Change the category based on an external label' => 'æ ¹æ®å¤–部标签修改分类', + 'Reference' => 'å‚考', + 'Label' => '标签', + 'Database' => 'æ•°æ®åº“', + 'About' => '关于', + 'Database driver:' => 'æ•°æ®åº“驱动:', + 'Board settings' => '看æ¿è®¾ç½®', + 'Webhook settings' => 'Webhook 设置', + 'Reset token' => 'é‡ç½®ä»¤ç‰Œ', + 'API endpoint:' => 'API 端点:', + 'Refresh interval for personal 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ç§’)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '频率,å•ä½ä¸ºç§’(设置为0åœç”¨æ­¤åŠŸèƒ½ï¼Œé»˜è®¤æ˜¯10ç§’)', + 'Application URL' => '应用URL', + 'Token regenerated.' => '釿–°ç”Ÿæˆä»¤ç‰Œ', + 'Date format' => '日期格å¼', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO æ ¼å¼æ€»æ˜¯å…许的,例如:"%s" å’Œ "%s"', + 'New personal project' => 'æ–°å»ºç§æœ‰é¡¹ç›®', + 'This project is personal' => 'æ­¤é¡¹ç›®ä¸ºç§æœ‰é¡¹ç›®', + 'Add' => '添加', + 'Start date' => 'å¯åŠ¨æ—¥æœŸ', + 'Time estimated' => '预计时间', + 'There is nothing assigned to you.' => '当剿— ä»»åŠ¡æŒ‡æ´¾ç»™ä½ ã€‚', + 'My tasks' => '我的任务', + 'Activity stream' => '动æ€è®°å½•', + 'Dashboard' => '颿¿', + 'Confirmation' => '确认', + 'Webhooks' => '网络钩å­', + 'API' => 'åº”ç”¨ç¨‹åºæŽ¥å£', + 'Create a comment from an external provider' => '从外部创建一个评论', + 'Project management' => '项目管ç†', + 'Columns' => 'æ ç›®', + 'Task' => '任务', + 'Percentage' => '百分比', + 'Number of tasks' => '任务数', + 'Task distribution' => '任务分布', + 'Analytics' => '统计分æž', + 'Subtask' => 'å­ä»»åŠ¡', + 'User repartition' => '用户分æž', + 'Clone this project' => 'å¤åˆ¶æ­¤é¡¹ç›®', + 'Column removed successfully.' => 'æˆåŠŸåˆ é™¤äº†æ ç›®ã€‚', + '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 subtask id is required' => 'å¿…é¡»æä¾›å­ä»»åŠ¡ç¼–å·', + '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' => '用户编å·å¿…须为整数', + 'This value is required' => '必须给出这个值', + 'This value must be numeric' => '这个值必须为数字', + 'Unable to create this task.' => '无法创建此任务。', + 'Cumulative flow diagram' => '累积æµå›¾è¡¨', + 'Daily project summary' => 'æ¯æ—¥é¡¹ç›®æ±‡æ€»', + 'Daily project summary export' => 'å¯¼å‡ºæ¯æ—¥é¡¹ç›®æ±‡æ€»', + 'Exports' => '导出任务', + 'This export contains the number of tasks per column grouped per day.' => 'æ­¤å¯¼å‡ºåŒ…å«æ¯åˆ—的任务数,按天分组', + 'Active swimlanes' => '活动泳é“', + 'Add a new swimlane' => '添加新泳é“', + 'Default swimlane' => '默认泳é“', + 'Do you really want to remove this swimlane: "%s"?' => '确定è¦åˆ é™¤æ³³é“:"%s"?', + 'Inactive swimlanes' => 'éžæ´»åŠ¨æ³³é“', + 'Remove a swimlane' => '删除泳é“', + 'Swimlane modification for the project "%s"' => '项目"%s"的泳é“å˜æ›´', + 'Swimlane removed successfully.' => 'æˆåŠŸåˆ é™¤æ³³é“', + 'Swimlanes' => 'æ³³é“', + 'Swimlane updated successfully.' => 'æˆåŠŸæ›´æ–°äº†æ³³é“。', + 'Unable to remove this swimlane.' => '无法删除此泳é“', + 'Unable to update this swimlane.' => '无法更新此泳é“', + 'Your swimlane has been created successfully.' => 'å·²ç»æˆåŠŸåˆ›å»ºæ³³é“。', + 'Example: "Bug, Feature Request, Improvement"' => '示例:“缺陷,功能需求,æå‡', + 'Default categories for new projects (Comma-separated)' => '新项目的默认分类(用逗å·åˆ†éš”)', + 'Integrations' => 'æ•´åˆ', + 'Integration with third-party services' => '与第三方æœåŠ¡è¿›è¡Œæ•´åˆ', + 'Subtask Id' => 'å­ä»»åŠ¡ Id', + 'Subtasks' => 'å­ä»»åŠ¡', + 'Subtasks Export' => 'å­ä»»åŠ¡å¯¼å‡º', + 'Task Title' => '任务标题', + 'Untitled' => '无标题', + 'Application default' => '程åºé»˜è®¤', + 'Language:' => '语言:', + 'Timezone:' => '时区:', + 'All columns' => '全部æ ç›®', + 'Next' => 'å‰è¿›', + '#%d' => '#%d', + 'All swimlanes' => '全部泳é“', + 'All colors' => '全部颜色', + 'Moved to column %s' => '移动到æ ç›® %s', + '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' => '时间记录', + 'You already have one subtask in progress' => 'ä½ å·²ç»æœ‰äº†ä¸€ä¸ªè¿›è¡Œä¸­çš„å­ä»»åŠ¡', + 'Which parts of the project do you want to duplicate?' => 'è¦å¤åˆ¶é¡¹ç›®çš„哪些内容?', + 'Disallow login form' => 'ç¦æ­¢ç™»é™†', + 'Start' => '开始', + 'End' => '结æŸ', + 'Task age in days' => '任务存在天数', + 'Days in this column' => '在此æ ç›®çš„天数', + '%dd' => '%d天', + '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' => '必须的字段', + 'Link added successfully.' => 'æˆåŠŸæ·»åŠ å…³è”。', + 'Link updated successfully.' => 'æˆåŠŸæ›´æ–°å…³è”。', + 'Link removed successfully.' => 'æˆåŠŸåˆ é™¤å…³è”。', + 'Link labels' => 'å…³è”æ ‡ç­¾', + 'Link modification' => 'å…³è”修改', + 'Opposite label' => 'å呿 ‡ç­¾', + 'Remove a link' => '删除关è”', + '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.' => '无法删除关è”。', + 'relates to' => 'å…³è”到', + 'blocks' => '阻塞', + 'is blocked by' => '阻塞于', + 'duplicates' => 'é‡å¤', + 'is duplicated by' => 'é‡å¤äºŽ', + 'is a child of' => 'å­ä»»åŠ¡è‡ª', + 'is a parent of' => '父任务于', + 'targets milestone' => '里程碑目标', + 'is a milestone of' => '属于里程碑', + 'fixes' => 'ä¿®å¤', + 'is fixed by' => 'ä¿®å¤äºŽ', + 'This task' => '此任务', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => '展开任务', + 'Collapse tasks' => '折å ä»»åŠ¡', + 'Expand/collapse tasks' => '展开/折å ä»»åŠ¡', + 'Close dialog box' => 'å…³é—­å¯¹è¯æ¡†', + 'Submit a form' => 'æäº¤è¡¨å•', + 'Board view' => '颿¿è§†å›¾', + 'Keyboard shortcuts' => 'é”®ç›˜å¿«æ·æ–¹å¼', + 'Open board switcher' => 'æ‰“å¼€é¢æ¿åˆ‡æ¢å™¨', + 'Application' => '应用程åº', + 'Compact view' => '紧凑视图', + 'Horizontal scrolling' => '水平滚动', + 'Compact/wide view' => '紧凑/宽视图', + 'Currency' => 'è´§å¸', + 'Personal project' => 'ç§æœ‰é¡¹ç›®', + 'AUD - Australian Dollar' => '澳元', + 'CAD - Canadian Dollar' => '加元', + 'CHF - Swiss Francs' => '瑞士法郎', + 'Custom Stylesheet' => '自定义样å¼è¡¨', + 'EUR - Euro' => '欧元', + 'GBP - British Pound' => '英镑', + 'INR - Indian Rupee' => 'å°åº¦å¢æ¯”', + 'JPY - Japanese Yen' => '日元', + 'NZD - New Zealand Dollar' => '新西兰元', + 'PEN - Peruvian Sol' => 'PEN - 秘é²ç´¢å°”', + 'RSD - Serbian dinar' => '第纳尔', + 'CNY - Chinese Yuan' => '人民å¸', + 'USD - US Dollar' => '美元', + 'VES - Venezuelan Bolívar' => '委内瑞拉玻利瓦尔', + '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' => 'æ ç›®ä¸­çš„æ—¶é—´æ¶ˆè€—', + '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.' => 'æ­¤æŠ¥å‘Šè®°å½•ä»»åŠ¡çš„å˜æ›´ï¼ŒåŒ…嫿—¥æœŸã€ç”¨æˆ·å’Œæ—¶é—´æ¶ˆè€—。', + 'Currency rates' => '汇率', + 'Rate' => '汇率', + 'Change reference currency' => '修改å‚考货å¸', + 'Reference currency' => 'å‚考货å¸', + 'The currency rate has been added successfully.' => 'æˆåŠŸæ·»åŠ æ±‡çŽ‡ã€‚', + 'Unable to add this currency rate.' => '无法添加此汇率', + 'Webhook URL' => 'ç½‘ç»œé’©å­ URL', + '%s removed the assignee of the task %s' => '%s删除了任务%sçš„å—ç†äºº', + 'Information' => 'ä¿¡æ¯', + 'Check two factor authentication code' => '检查åŒé‡è®¤è¯ç ', + 'The two factor authentication code is not valid.' => 'åŒé‡è®¤è¯ç ä¸æ­£ç¡®ã€‚', + 'The two factor authentication code is valid.' => 'åŒé‡è®¤è¯ç æ­£ç¡®ã€‚', + 'Code' => '认è¯ç ', + 'Two factor authentication' => 'åŒé‡è®¤è¯', + 'This QR code contains the key URI: ' => '此二维ç åŒ…å«å¯†ç  URI:', + 'Check my code' => '检查我的认è¯ç ', + 'Secret key: ' => '密ç ï¼š', + 'Test your device' => '测试设备', + 'Assign a color when the task is moved to a specific column' => '任务移动到指定æ ç›®æ—¶è®¾ç½®é¢œè‰²', + '%s via Kanboard' => '%s 通过KanBoard', + 'Burndown chart' => '燃尽图', + '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' => '瑞郎', + 'Identifier' => '标识符', + 'Disable two factor authentication' => 'ç¦ç”¨åŒé‡è®¤è¯', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '你真的è¦ç¦ç”¨ "%s" çš„åŒé‡è®¤è¯å—?', + 'Edit link' => '编辑链接', + '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' => '循环性任务将按计划生æˆ', + 'Score' => '积分', + 'The identifier must be unique' => '标识符必须唯一', + 'This linked task id doesn\'t exists' => 'å…³è”任务ä¸å­˜åœ¨', + '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' => '新到期时间的计算周期', + '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: ' => '新到期时间的计算因å­ï¼š', + 'Month(s)' => '月', + 'This task has been created by: ' => '此任务被è°åˆ›å»ºï¼š', + 'Recurrent task has been generated:' => '循环任务已生æˆï¼š', + 'Timeframe to calculate new due date: ' => '新到期时间的计算周期:', + 'Trigger to generate recurrent task: ' => '生æˆå¾ªçŽ¯ä»»åŠ¡çš„è§¦å‘器', + 'When task is closed' => '当任务关闭时', + 'When task is moved from first column' => '当任务从第一列任务æ ç§»èµ°æ—¶', + 'When task is moved to last column' => '当任务移动到最åŽä¸€åˆ—ä»»åŠ¡æ æ—¶', + 'Year(s)' => 'å¹´', + 'Project settings' => '项目设置', + 'Automatically update the start date' => '自动更新开始日期', + 'iCal feed' => '日历订阅', + 'Preferences' => 'å好', + 'Security' => '安全', + 'Two factor authentication disabled' => 'åŒé‡è®¤è¯å·²ç¦ç”¨', + 'Two factor authentication enabled' => 'åŒé‡è®¤è¯å·²å¯ç”¨', + 'Unable to update this user.' => '无法更新此用户', + 'There is no user management for personal 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' => 'å½“ä»»åŠ¡åˆ†ç±»æ”¹å˜æ—¶ç§»åŠ¨åˆ°å¦ä¸€æ ', + 'Send a task by email to someone' => 'å‘é€ä»»åŠ¡é‚®ä»¶åˆ°ç”¨æˆ·', + 'Reopen a task' => '釿–°å¼€å§‹ä¸€ä¸ªä»»åŠ¡', + 'Notification' => '通知', + '%s moved the task #%d to the first swimlane' => '%s将任务#%d移动到了首个泳é“', + 'Swimlane' => 'æ³³é“', + '%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.' => '该报告包å«äº†æŒ‡å®šæ—¥æœŸèŒƒå›´å†…的所有任务信æ¯ã€‚', + 'Project activities for %s' => '%s的项目活动记录', + 'view the board on Kanboard' => '在看æ¿ä¸ŠæŸ¥çœ‹é¢æ¿', + 'The task has been moved to the first swimlane' => '该任务已被移动到首个泳é“', + 'The task has been moved to another swimlane:' => '该任务已被移动到别的泳é“:', + 'New title: %s' => '新标题:%s', + 'The task is not assigned anymore' => '该任务没有指派给任何人', + 'New assignee: %s' => '新指派到:%s', + 'There is no category now' => '当剿²¡æœ‰åˆ†ç±»', + 'New category: %s' => '新分类:%s', + 'New color: %s' => '新颜色:%s', + 'New complexity: %d' => 'æ–°çš„å¤æ‚度:%d', + 'The due date has been removed' => '超期时间已被移除', + 'There is no description anymore' => '当剿²¡æœ‰æè¿°', + 'Recurrence settings has been modified' => '循环周期已被更改', + 'Time spent changed: %sh' => 'æ—¶é—´èŠ±è´¹å·²å˜æ›´ï¼š%sh', + 'Time estimated changed: %sh' => 'æ—¶é—´é¢„ä¼°å·²å˜æ›´ï¼š%sh', + 'The field "%s" has 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:' => '我想接收以下相关通知:', + 'All tasks' => '所有任务', + 'Only for tasks assigned to me' => '所有指派给我的任务', + 'Only for tasks created by me' => '所有我创建的任务', + 'Only for tasks created by me and tasks assigned to me' => '所有我创建的和指派给我的任务', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => '所有æ ç›®ä¸‹çš„', + 'You need at least 2 days of data to show the chart.' => '当剿Ÿ±çŠ¶å›¾è‡³å°‘éœ€è¦2天的数æ®ã€‚', + '<15m' => 'å°äºŽ15分钟', + '<30m' => 'å°äºŽ30分钟', + 'Stop timer' => 'åœæ­¢è®¡æ—¶å™¨', + 'Start timer' => 'å¼€å¯è®¡æ—¶å™¨', + 'My activity stream' => '我的动æ€', + 'Search tasks' => 'æœç´¢ä»»åŠ¡', + 'Reset filters' => 'é‡ç½®è¿‡æ»¤å™¨', + 'My tasks due tomorrow' => '我的明天到期的任务', + 'Tasks due today' => '今天到期的任务', + 'Tasks due tomorrow' => '明天到期的任务', + 'Tasks due yesterday' => '昨天到期的任务', + 'Closed tasks' => '已关闭任务', + 'Open tasks' => '打开的任务', + 'Not assigned' => '未指派', + 'View advanced search syntax' => '查看高级æœç´¢è¯­æ³•', + 'Overview' => '概览', + 'Board/Calendar/List view' => '看æ¿/日程/列表视图', + 'Switch to the board view' => '切æ¢åˆ°çœ‹æ¿è§†å›¾', + 'Switch to the list view' => '切æ¢åˆ°åˆ—表视图', + 'Go to the search/filter box' => 'å‰å¾€æœç´¢/过滤箱', + 'There is no activity yet.' => '当剿— ä»»ä½•活动。', + 'No tasks found.' => '没有找到任何任务。', + 'Keyboard shortcut: "%s"' => 'å¿«æ·é”®: "%s"', + 'List' => '列表', + 'Filter' => '过滤器', + 'Advanced search' => '高级æœç´¢', + 'Example of query: ' => '查询示例:', + 'Search by project: ' => '按项目:', + 'Search by column: ' => '按任务æ ï¼š', + 'Search by assignee: ' => '按å—ç†äººï¼š', + 'Search by color: ' => '按颜色: ', + 'Search by category: ' => '按分类:', + 'Search by description: ' => '按æè¿°ï¼š', + 'Search by due date: ' => '按超期时间:', + 'Average time spent in each column' => 'æ¯ä¸ªä»»åŠ¡æ çš„å¹³å‡èŠ±è´¹æ—¶é—´', + 'Average time spent' => 'å¹³å‡èŠ±è´¹æ—¶é—´', + 'This chart shows the average time spent in each column for the last %d tasks.' => '当剿Ÿ±çŠ¶å›¾è¡¨ç¤ºæœ€æ–°%dæ¡ä»»åŠ¡åœ¨æ¯ä¸ªä»»åŠ¡æ ä¸‹çš„å¹³å‡èŠ±è´¹æ—¶é—´', + 'Average Lead and Cycle time' => 'å¹³å‡å·¥ä½œæ—¶é—´å’Œå¹³å‡å‘¨æœŸæ—¶é—´', + 'Average lead time: ' => 'å¹³å‡å·¥ä½œæ—¶é—´', + 'Average cycle time: ' => 'å¹³å‡å‘¨æœŸæ—¶é—´ï¼š', + 'Cycle Time' => '周期时间', + 'Lead Time' => '工作时间', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => '该图表显示了过去 %d 个任务的平å‡å’Œå³°å€¼è€—时。', + 'Average time into each column' => 'æ¯ä¸ªä»»åŠ¡æ çš„平凿—¶é—´', + 'Lead and cycle time' => '工作时间和周期时间', + 'Lead time: ' => '工作时间: ', + 'Cycle time: ' => '周期时间: ', + 'Time spent in 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 the start date automatically' => '设置自动开始日期', + '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.' => 'å¦‚æžœé€‰ä¸­â€œç¦æ­¢ç™»é™†æ¥è‡ªâ€ï¼Œç™»é™†è¡¨å•内的验è¯ä¿¡æ¯å°†è¢«å¿½ç•¥ã€‚', + '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 category: %s' => '当å‰åˆ†ç±»ï¼š%s', + 'no category' => '无分类', + 'Current assignee: %s' => '当å‰å—ç†äºº: %s', + 'not assigned' => '未指派', + 'Author:' => '作者', + 'contributors' => '贡献者', + 'License:' => '授æƒè®¸å¯:', + 'License' => '授æƒè®¸å¯', + 'Enter the text below' => '输入下方的文本', + 'Start date:' => '开始日期', + 'Due date:' => '到期日期', + 'People who are project managers' => '项目管ç†å‘˜', + 'People who are project members' => '项目æˆå‘˜', + 'NOK - Norwegian Krone' => '克朗', + 'Show this column' => '显示任务æ ', + 'Hide this column' => 'éšè—任务æ ', + 'End date' => 'ç»“æŸæ—¥æœŸ', + 'Users overview' => '用户概览', + 'Members' => 'æˆå‘˜', + 'Shared project' => '公开项目', + 'Project managers' => '项目管ç†å‘˜', + 'Projects list' => '项目列表', + 'End date:' => 'ç»“æŸæ—¥æœŸ', + 'Change task color when using a specific task link' => '当任务关è”到指定任务时改å˜é¢œè‰²', + 'Task link creation or modification' => '任务链接创建或更新时间', + 'Milestone' => '里程碑', + 'Reset the search/filter box' => 'é‡ç½®æœç´¢/过滤框', + 'Documentation' => '帮助文档', + 'Author' => '作者', + 'Version' => '版本', + 'Plugins' => 'æ’件管ç†', + 'There is no plugin loaded.' => '当剿²¡æœ‰æ’件载入', + 'My notifications' => '我的通知', + 'Custom filters' => '过滤器', + 'Your custom filter has 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 has been updated successfully.' => '你的过滤器更新æˆåŠŸ', + 'Unable to update custom filter.' => '无法更新过滤器', + 'Web' => 'web', + '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下的å­ä»»åŠ¡å·²æ›´æ–°', + 'New task #%d: %s' => '新任务#%d:%s', + 'Task updated #%d' => '任务#%d已更新', + 'Task #%d closed' => '任务#%d已关闭', + 'Task #%d opened' => '任务#%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的指派人已改å˜', + '%d overdue tasks' => '%dæ¡è¶…期任务', + 'No notification.' => '没有新通知', + '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:' => '通知æé†’æ–¹å¼ï¼š', + 'Unable to read your file' => 'æ— æ³•è¯»å–æ–‡ä»¶', + '%d task(s) have been imported successfully.' => 'æˆåŠŸå¯¼å…¥%dæ¡ä»»åŠ¡ã€‚', + 'Nothing has been imported!' => '没有信æ¯è¢«å¯¼å…¥ï¼', + 'Import users from CSV file' => '从CSV文件导入用户', + '%d user(s) have been imported successfully.' => 'æˆåŠŸå¯¼å…¥%d个用户。', + 'Comma' => '逗å·', + 'Semi-colon' => '分å·', + 'Tab' => '制表符', + 'Vertical bar' => '竖线', + '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)' => '追加过滤', + 'Append/Replace' => '追加/替æ¢', + 'Append' => '追加', + 'Replace' => '替æ¢', + 'Import' => '导入', + 'Change sorting' => 'æ”¹å˜æŽ’åº', + 'Tasks Importation' => '任务é‡è¦æ€§', + 'Delimiter' => '分隔符', + 'Enclosure' => '附件', + 'CSV File' => 'CSV文件', + 'Instructions' => 'æ“作指å—', + 'Your file must use the predefined CSV format' => '文件必须为预定格å¼çš„CSV文件', + 'Your file must be encoded in UTF-8' => '文件编ç å¿…须为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' => '超期日期必须为ISOæ ¼å¼ï¼šYYYY-MM-DD', + 'Download CSV template' => '下载CSV模æ¿', + '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' => '"%s"添加了附件到任务"%s"', + 'Link type' => 'å…³è”类型', + 'Assign automatically a category based on a link' => '基于链接自动关è”分类', + 'BAM - Konvertible Mark' => '波斯尼亚马克', + 'Assignee Username' => '指派用户å', + 'Assignee Name' => '指派åç§°', + 'Groups' => '用户组', + 'Members of %s' => '“%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"' => '添加到用户组"%s"', + 'Group member added successfully.' => 'æˆåŠŸæ·»åŠ ç”¨æˆ·ç»„æˆå‘˜', + 'Unable to add group member.' => '无法添加用户组æˆå‘˜', + 'Remove user from group "%s"' => '从"%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' => '你的账户被é”定%d分钟', + 'Invalid captcha' => '验è¯ç æ— æ•ˆ', + 'The name must be unique' => '请确ä¿ç”¨æˆ·å唯一', + 'View all groups' => '查看所有用户组', + 'There is no user available.' => '当剿²¡æœ‰æœ‰æ•ˆç”¨æˆ·', + 'Do you really want to remove the user "%s" from the group "%s"?' => '你确定把用户"%s"从"%s"中移除?', + 'There is no group.' => '当剿²¡æœ‰ç”¨æˆ·ç»„', + 'Add group member' => '添加用户组æˆå‘˜', + 'Do you really want to remove this group: "%s"?' => '你真的想è¦ç§»é™¤ç”¨æˆ·ç»„:"%s"?', + 'There is no user in this group.' => '当å‰ç”¨æˆ·ç»„下没有æˆå‘˜', + 'Permissions' => 'æƒé™', + 'Allowed Users' => '被å…许的用户', + 'No specific user has been allowed.' => '没有被å…许的用户。', + 'Role' => '角色', + 'Enter user name...' => '输入用户å...', + 'Allowed Groups' => '被å…许的用户组', + 'No group has been allowed.' => '没有被å…许的用户组。', + 'Group' => '用户组', + 'Group Name' => '用户组åç§°', + 'Enter group name...' => '输入用户组åç§°...', + 'Role:' => '角色:', + 'Project members' => '项目æˆå‘˜', + '%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的评论里被æåŠ', + 'Estimated hours: ' => '预估尿—¶æ•°', + 'Actual hours: ' => 'å®žé™…å°æ—¶æ•°', + 'Hours Spent' => 'èŠ±è´¹å°æ—¶æ•°', + 'Hours Estimated' => '预估尿—¶æ•°', + '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' => '关闭指定任务æ ä¸‹çš„任务', + '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' => 'é‡ç½®kanboard密ç ', + 'Forgot password?' => '忘记密ç ï¼Ÿ', + 'Enable "Forget Password"' => 'å¯ç”¨æ‰¾å›žå¯†ç ', + 'Password Reset' => '密ç é‡ç½®', + 'New password' => '新密ç ', + 'Change Password' => '更改密ç ', + 'To reset your password click on this link:' => '点击此链接é‡ç½®ä½ çš„密ç ', + 'Last Password Reset' => '上次密ç é‡ç½®', + 'The password has never been reinitialized.' => '密ç ä»Žæœªè¢«é‡æ–°åˆå§‹åŒ–', + 'Creation' => '创建时间', + 'Expiration' => '过期时间', + 'Password reset history' => '密ç åކå²', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => 'ä»»åŠ¡æ  "%s" å’Œ æ³³é“ "%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 in this column and this swimlane' => '关闭此任务æ ä¸‹çš„æ‰€æœ‰ä»»åŠ¡', + '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.' => '项目标识符是å¯é€‰çš„,且åªèƒ½æ˜¯æ•°å­—或字æ¯ç»„æˆï¼Œä¾‹å¦‚:MYPROJECT。', + 'Project owner' => '项目负责人', + 'Personal projects do not have users and groups management.' => 'ç§æœ‰é¡¹ç›®ä¸‹æ²¡æœ‰æˆå‘˜æˆ–组å¯ç®¡ç†', + 'There is no project member.' => '当剿²¡æœ‰é¡¹ç›®æˆå‘˜', + 'Priority' => '优先级', + 'Task priority' => '任务优先级', + 'General' => '常用', + 'Dates' => 'æ—¶é—´', + 'Default priority' => '默认优先级', + 'Lowest priority' => '最低优先级', + 'Highest priority' => '最高优先级', + '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' => '附件', + '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' => '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' => '%s å°æ—¶', + 'Time spent:' => '时间消耗:', + 'Created:' => '已创建:', + 'Modified:' => '已修改:', + 'Completed:' => '已完æˆï¼š', + 'Started:' => '已开始:', + 'Moved:' => '已移走:', + 'Task #%d' => '任务#%d', + 'Time format' => 'æ—¶é—´æ ¼å¼', + 'Start date: ' => '开始时间:', + 'End date: ' => 'ç»“æŸæ—¶é—´ï¼š', + 'New due date: ' => '新超期时间:', + 'Start date changed: ' => '开始时间已改å˜ï¼š', + 'Disable personal projects' => 'ç¦ç”¨ç§æœ‰é¡¹ç›®', + 'Do you really want to remove this custom filter: "%s"?' => '你确定è¦ç§»é™¤è¿™ä¸ªè‡ªå®šä¹‰è¿‡æ»¤å™¨ï¼š"%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.' => '所有文件已æˆåŠŸä¸Šä¼ ã€‚', + 'The maximum allowed file size is %sB.' => '最大上传尺寸 %sB', + 'Drag and drop your files here' => '拖放文件到这里', + 'choose files' => '选择文件', + 'View profile' => '查看个人信æ¯', + 'Two Factor' => 'åŒé‡è®¤è¯', + 'Disable user' => 'ç¦ç”¨ç”¨æˆ·', + 'Do you really want to disable this user: "%s"?' => '你确定è¦ç¦ç”¨è¯¥ç”¨æˆ·ï¼š"%s"?', + 'Enable user' => 'å¯ç”¨ç”¨æˆ·', + 'Do you really want to enable this user: "%s"?' => '你确定è¦å¯ç”¨è¯¥ç”¨æˆ·ï¼š"%s"?', + 'Download' => '下载', + 'Uploaded: %s' => '上传:%s', + 'Size: %s' => '大å°ï¼š%s', + 'Uploaded by %s' => 'ç”±%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' => '当å‰è¾“入值必须大于%d', + 'Another swimlane with the same name exists in the project' => '当å‰é¡¹ç›®ä¸­é‡åæ³³é“已存在', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => '例如: https://example.kanboard.org/(通常用于生æˆç»å¯¹åœ°å€ï¼‰', + '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 版本:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => '系统:', + 'Database version:' => 'æ•°æ®åº“版本:', + 'Browser:' => 'æµè§ˆå™¨ï¼š', + 'Task view' => '任务æµè§ˆ', + 'Edit task' => '编辑任务', + 'Edit description' => '编辑æè¿°', + 'New internal link' => '新建内部链接', + 'Display list of keyboard shortcuts' => '显示快æ·é”®åˆ—表', + '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 a 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 has 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的活动足迹', + '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 access', + 'Preview' => '预览', + 'Write' => '写入', + 'Write your text in Markdown' => '用markdownæ ¼å¼å†™å…¥æ–‡æœ¬', + 'No personal API access token registered.' => '没有API access token 注册。', + 'Your personal API access token is "%s"' => 'ä½ çš„API access token 是 “%sâ€', + 'Remove your token' => '移除token', + 'Generate a new token' => 'ç”Ÿæˆæ–°çš„token', + 'Showing %d-%d of %d' => '本页显示 %d-%d æ¡ï¼Œå…±æœ‰: %d æ¡', + 'Outgoing Emails' => '目标邮件', + '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个邀请已å‘é€ã€‚', + '%d invitation was sent.' => '%d个邀请已å‘é€ã€‚', + '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' => '该用户å已被å ç”¨', + 'Your profile must have a valid email address.' => '个人资料必须有一个有效email地å€ã€‚', + 'TRL - Turkish Lira' => '土耳其里拉', + 'The project email is optional and could be used by several plugins.' => '项目邮件地å€å¯é€‰ï¼Œé€‚用于æ’件。', + 'The project email must be unique across all projects' => '项目邮件地å€å¿…须全局唯一', + 'The email configuration has been disabled by the administrator.' => '邮件é…置被管ç†å‘˜ç¦ç”¨ã€‚', + 'Close this project' => '关闭项目', + 'Open this project' => '打开项目', + 'Close a project' => '关闭项目', + 'Do you really want to close this project: "%s"?' => '你真的è¦å…³é—­é¡¹ç›®ï¼š"%s"?', + 'Reopen a project' => 'é‡å¼€é¡¹ç›®', + 'Do you really want to reopen this project: "%s"?' => '你真的è¦é‡å¼€é¡¹ç›®ï¼š"%s"?', + 'This project is open' => '项目已打开', + 'This project is closed' => '项目已关闭', + 'Unable to upload files, check the permissions of your data folder.' => '无法上传文件,请检查你的data目录æƒé™ã€‚', + 'Another category with the same name exists in this project' => '该项目下åŒå分类已存在', + 'Comment sent by email successfully.' => '评论已通过邮件å‘逿ˆåŠŸã€‚', + 'Sent by email to "%s" (%s)' => 'å·²å‘é€é‚®ä»¶è‡³"%s" (%s)', + 'Unable to read uploaded file.' => '无法读å–上传文件。', + 'Database uploaded successfully.' => 'æ•°æ®åº“上传æˆåŠŸã€‚', + 'Task sent by email successfully.' => '任务已通过邮件å‘逿ˆåŠŸã€‚', + 'There is no category in this project.' => '当å‰é¡¹ç›®æ²¡æœ‰åˆ†ç±»ã€‚', + 'Send by email' => 'å‘é€é‚®ä»¶', + 'Create and send a comment by email' => '通过电å­é‚®ä»¶åˆ›å»ºå’Œå‘é€è¯„论', + 'Subject' => '主题', + 'Upload the database' => '上传数æ®åº“', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => 'ä½ å¯ä»¥ä¸Šä¼ å…ˆå‰ä¸‹è½½çš„sqliteæ•°æ®åº“(Gzip æ ¼å¼)。', + 'Database file' => 'æ•°æ®åº“文件', + 'Upload' => '上传', + 'Your project must have at least one active swimlane.' => '项目必须包å«è‡³å°‘一个活跃泳é“。', + 'Project: %s' => '项目:%s', + 'Automatic action not found: "%s"' => '自动动作未找到:"%s"', + '%d projects' => '%d 项目', + '%d project' => '%d 项目', + 'There is no project.' => '当剿²¡æœ‰é¡¹ç›®ã€‚', + 'Sort' => '排åº', + 'Project ID' => '项目ID', + 'Project name' => '项目å', + 'Public' => '公开', + 'Personal' => 'ç§æœ‰', + '%d tasks' => '%d 任务', + '%d task' => '%d 任务', + 'Task ID' => '任务ID', + 'Assign automatically a color when due date is expired' => '当任务过期时自动标注颜色', + 'Total score in this column across all swimlanes' => '该æ ç›®æ‰€æœ‰æ³³é“上的总分', + 'HRK - Kuna' => '克罗地亚库纳', + 'ARS - Argentine Peso' => '阿根廷比索', + 'COP - Colombian Peso' => '哥伦比亚比索', + '%d groups' => '%d 用户组', + '%d group' => '%d 用户组', + 'Group ID' => '用户组ID', + 'External ID' => '附属ID', + '%d users' => '%d 用户', + '%d user' => '%d 用户', + 'Hide subtasks' => '显示å­ä»»åŠ¡', + 'Show subtasks' => 'éšè—å­ä»»åŠ¡', + 'Authentication Parameters' => '验è¯å‚æ•°', + 'API Access' => 'API授æƒ', + 'No users found.' => '未找到用户。', + 'User ID' => '用户ID', + 'Notifications are activated' => '通知已å¯ç”¨', + 'Notifications are disabled' => '通知已ç¦ç”¨', + 'User disabled' => '用户已ç¦ç”¨', + '%d notifications' => '%d 通知', + '%d notification' => '%d 通知', + 'There is no external integration installed.' => '当剿²¡æœ‰å¤–部整åˆå®‰è£…。', + 'You are not allowed to update tasks assigned to someone else.' => '你没有æƒé™æ›´æ–°ä»»åŠ¡æŒ‡æ´¾ã€‚', + 'You are not allowed to change the assignee.' => '你没有æƒé™æ›´æ”¹å½“å‰å—ç†äººã€‚', + 'Task suppression is not permitted' => 'ä¸å…许抑制任务', + 'Changing assignee is not permitted' => 'ä¸å…许更改å—ç†äºº', + 'Update only assigned tasks is permitted' => 'åªå…许更新已å—ç†ä»»åŠ¡', + 'Only for tasks assigned to the current user' => 'åªä½œç”¨äºŽå½“å‰ç”¨æˆ·å·²å—ç†ä»»åŠ¡', + 'My projects' => '我的项目', + 'You are not a member of any project.' => '你䏿˜¯ä»»ä½•项目的æˆå‘˜ã€‚', + 'My subtasks' => '我的å­ä»»åŠ¡', + '%d subtasks' => '%d å­ä»»åŠ¡', + '%d subtask' => '%d å­ä»»åŠ¡', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => 'åªå…许在这些æ ç›®é—´ç§»åЍå—ç†ç»™å½“å‰ç”¨æˆ·çš„任务', + '[DUPLICATE]' => '[é‡å¤]', + 'DKK - Danish Krona' => '丹麦克朗', + 'Remove user from group' => '从用户组中移除用户', + 'Assign the task to its creator' => '将任务分é…给创建者', + 'This task was sent by email to "%s" with subject "%s".' => '任务已通过邮件å‘é€ç»™"%s"(标题:"%s")。', + 'Predefined Email Subjects' => '预定义的邮件主题', + 'Write one subject by line.' => '添加主题(æ¯è¡Œï¼‰ã€‚', + 'Create another link' => '创建新链接', + 'BRL - Brazilian Real' => '巴西雷亚尔', + 'Add a new Kanboard task' => '添加新任务', + 'Subtask not started' => 'å­ä»»åŠ¡æœªå¼€å§‹', + 'Subtask currently in progress' => 'å­ä»»åŠ¡è¿›è¡Œä¸­', + 'Subtask completed' => 'å­ä»»åŠ¡å·²å®Œæˆ', + 'Subtask added successfully.' => 'å­ä»»åŠ¡æ·»åŠ æˆåŠŸã€‚', + '%d subtasks added successfully.' => '%då­ä»»åŠ¡æ·»åŠ æˆåŠŸã€‚', + 'Enter one subtask by line.' => '添加å­ä»»åŠ¡ï¼ˆæ¯è¡Œï¼‰ã€‚', + 'Predefined Contents' => '预设内容', + 'Predefined contents' => '预设内容', + 'Predefined Task Description' => '预定义任务æè¿°', + 'Do you really want to remove this template? "%s"' => '你真的è¦ç§»é™¤è¯¥æ¨¡æ¿ï¼Ÿ"%s"', + 'Add predefined task description' => '添加预定义任务æè¿°', + 'Predefined Task Descriptions' => '预定义任务æè¿°', + 'Template created successfully.' => 'æ¨¡æ¿æ·»åŠ æˆåŠŸã€‚', + 'Unable to create this template.' => '无法添加该模æ¿ã€‚', + 'Template updated successfully.' => 'æ¨¡æ¿æ›´æ–°æˆåŠŸã€‚', + 'Unable to update this template.' => '无法更新此模æ¿ã€‚', + 'Template removed successfully.' => '模æ¿åˆ é™¤æˆåŠŸã€‚', + 'Unable to remove this template.' => '无法删除该模æ¿ã€‚', + 'Template for the task description' => '任务æè¿°æ¨¡æ¿', + 'The start date is greater than the end date' => 'å¼€å§‹æ—¥æœŸæ™šäºŽç»“æŸæ—¥æœŸ', + 'Tags must be separated by a comma' => '标签必须以逗å·åˆ†å‰²', + 'Only the task title is required' => 'åªæœ‰ä»»åŠ¡æ ‡é¢˜æ˜¯å¿…é¡»çš„', + 'Creator Username' => '创建人', + 'Color Name' => '颜色åç§°', + 'Column Name' => 'æ ç›®åç§°', + 'Swimlane Name' => 'æ³³é“åç§°', + 'Time Estimated' => '预计耗时', + 'Time Spent' => '时间花费', + 'External Link' => '外部链接', + 'This feature enables the iCal feed, RSS feed and the public board view.' => '该功能将开å¯iCal订阅, RSS订阅以åŠå…¬å…±æŸ¥çœ‹æƒé™ã€‚', + 'Stop the timer of all subtasks when moving a task to another column' => '当移动任务到别的æ ç›®ç»„æ—¶åœæ­¢æ‰€æœ‰å­ä»»åŠ¡è®¡æ—¶å™¨', + 'Subtask Title' => 'å­ä»»åŠ¡æ ‡é¢˜', + 'Add a subtask and activate the timer when moving a task to another column' => '当移动任务到别的æ ç›®ç»„时添加å­ä»»åŠ¡å¹¶å¼€å§‹è®¡æ—¶', + 'days' => '天', + 'minutes' => '分钟', + 'seconds' => 'ç§’', + 'Assign automatically a color when preset start date is reached' => '当到达预设起始日期时自动指派颜色', + 'Move the task to another column once a predefined start date is reached' => '当到达预定起始日期时移动任务到å¦ä¸€æ ç›®', + 'This task is now linked to the task %s with the relation "%s"' => '该任务与任务:%s 已建立"%s"å…³è”', + 'The link with the relation "%s" to the task %s has been removed' => '该任务与任务:%s 已解除"%s"å…³è”', + 'Custom Filter:' => '自定义过滤器:', + 'Unable to find this group.' => '无法找到该用户组。', + '%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 moved the task #%d to the swimlane "%s"' => '%s将任务 #%d ç§»åŠ¨åˆ°äº†æ³³é“ "%s" 下', + '%sh spent' => '花费%så°æ—¶', + '%sh estimated' => '预估%så°æ—¶', + 'Select All' => '全选', + 'Unselect All' => 'å–æ¶ˆ', + 'Apply action' => '应用动作', + 'Move selected tasks to another column or swimlane' => '移动选中任务到å¦ä¸€åˆ—', + 'Edit tasks in bulk' => '批é‡ç¼–辑任务', + 'Choose the properties that you would like to change for the selected tasks.' => 'é€‰æ‹©ä½ æƒ³è¦æ‰¹é‡ç¼–辑的任务属性。', + 'Configure this project' => '项目é…ç½®', + 'Start now' => 'ç«‹å³å¼€å§‹', + '%s removed a file from the task #%d' => '%s 从任务 #%d 删除了文件', + 'Attachment removed from task #%d: %s' => '附件已从任务 #%d: %s 中删除', + 'No color' => '未标色', + 'Attachment removed "%s"' => '附件已删除 "%s"', + '%s removed a file from the task %s' => '%s 从任务 %s 中删除了文件', + 'Move the task to another swimlane when assigned to a user' => '将分é…给用户的任务移动到å¦ä¸€ä¸ªæ³³é“', + 'Destination swimlane' => '目标泳é“', + 'Assign a category when the task is moved to a specific swimlane' => '将移动到指定泳é“的任务设定分类', + 'Move the task to another swimlane when the category is changed' => 'å½“åˆ†ç±»æ”¹å˜æ—¶å°†ä»»åŠ¡ç§»åŠ¨åˆ°å¦ä¸€ä¸ªæ³³é“', + 'Reorder this column by priority (ASC)' => 'æŒ‰ä¼˜å…ˆçº§é‡æŽ’(å‡åº)', + 'Reorder this column by priority (DESC)' => 'æŒ‰ä¼˜å…ˆçº§é‡æŽ’(é™åº)', + 'Reorder this column by assignee and priority (ASC)' => 'æŒ‰æŒ‡æ´¾å’Œä¼˜å…ˆçº§é‡æŽ’(å‡åº)', + 'Reorder this column by assignee and priority (DESC)' => 'æŒ‰æŒ‡æ´¾å’Œä¼˜å…ˆçº§é‡æŽ’(é™åº)', + 'Reorder this column by assignee (A-Z)' => 'æŒ‰æŒ‡æ´¾é‡æŽ’(A-Z)', + 'Reorder this column by assignee (Z-A)' => 'æŒ‰æŒ‡æ´¾é‡æŽ’(Z-A)', + 'Reorder this column by due date (ASC)' => 'æŒ‰é€¾æœŸæ—¶é—´é‡æŽ’(å‡åº)', + 'Reorder this column by due date (DESC)' => 'æŒ‰é€¾æœŸæ—¶é—´é‡æŽ’(é™åº)', + 'Reorder this column by id (ASC)' => '按ID釿–°æŽ’åºæ­¤åˆ— (å‡åº)', + 'Reorder this column by id (DESC)' => '按ID釿–°æŽ’åºæ­¤åˆ— (é™åº)', + '%s moved the task #%d "%s" to the project "%s"' => '%s 已将任务 #%d "%s" 移动到项目"%s" ', + 'Task #%d "%s" has been moved to the project "%s"' => '任务 #%d "%s" 已被移动到项目 "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => '当任务å³å°†åœ¨æŒ‡å®šå¤©æ•°å†…超期时移动到å¦ä¸€æ ', + 'Automatically update the start date when the task is moved away from a specific column' => '当任务离开指定æ ç›®æ—¶è‡ªåŠ¨æ›´æ–°å¼€å§‹æ—¶é—´', + 'HTTP Client:' => 'HTTP客户端', + 'Assigned' => '已分é…', + 'Task limits apply to each swimlane individually' => '任务é™åˆ¶åˆ†åˆ«åº”用于æ¯ä¸ªæ³³é“', + 'Column task limits apply to each swimlane individually' => '列任务é™åˆ¶åˆ†åˆ«åº”用于æ¯ä¸ªæ³³é“', + 'Column task limits are applied to each swimlane individually' => '列任务é™åˆ¶å·²ç»è¢«åˆ†åˆ«åº”用于æ¯ä¸ªæ³³é“', + 'Column task limits are applied across swimlanes' => '列任务é™åˆ¶å·²ç»è¢«è·¨æ³³é“应用', + 'Task limit: ' => '任务é™åˆ¶', + 'Change to global tag' => '更改为全局标签', + 'Do you really want to make the tag "%s" global?' => '您是å¦çœŸçš„æƒ³å°†æ ‡ç­¾ "%s" 设置为全局标签?', + 'Enable global tags for this project' => '为该项目å¯ç”¨å…¨å±€æ ‡ç­¾', + 'Group membership(s):' => '群组æˆå‘˜ï¼š', + '%s is a member of the following group(s): %s' => '%s 是以下组的æˆå‘˜ï¼š %s', + '%d/%d group(s) shown' => '%d/%d 个组已显示', + 'Subtask creation or modification' => 'å­ä»»åŠ¡åˆ›å»ºæˆ–ä¿®æ”¹', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'å°†ä»»åŠ¡ç§»è‡³ç‰¹å®šæ³³é“æ—¶ï¼Œå°†ä»»åŠ¡åˆ†é…给指定用户', + 'Comment' => '评论', + 'Collapse vertically' => 'æ å†…滚动', + 'Expand vertically' => '免屿»šåЍ', + 'MXN - Mexican Peso' => '墨西哥比索', + 'Estimated vs actual time per column' => 'æ¯æ ç›®é¢„计和实际耗时', + 'HUF - Hungarian Forint' => '匈牙利 - ç¦æž—', + 'XBT - Bitcoin' => 'XBT - 比特å¸', + 'You must select a file to upload as your avatar!' => '你必须选择一张图片作为你的头åƒ', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'ä½ æ‰€ä¸Šä¼ çš„æ–‡ä»¶ä¸æ˜¯æœ‰æ•ˆå›¾åƒï¼(åªæœ‰*.gif, *.jpg, *.jpeg and *.pngæ‰è¢«å…许ï¼)', + 'Automatically set the due date when the task is moved away from a specific column' => '当任务离开指定æ ç›®æ—¶è‡ªåŠ¨è®¾ç½®åˆ°æœŸæ—¶é—´', + 'No other projects found.' => '未找到其它项目。', + 'Tasks copied successfully.' => '任务å¤åˆ¶æˆåŠŸã€‚', + 'Unable to copy tasks.' => '无法å¤åˆ¶ä»»åŠ¡ã€‚', + 'Theme' => '主题', + 'Theme:' => '主题:', + 'Light theme' => '浅色主题', + 'Dark theme' => '深色主题', + 'Automatic theme - Sync with system' => '自动主题 - åŒæ­¥ç³»ç»Ÿè®¾ç½®', + 'Application managers or more' => '应用程åºç»ç†æˆ–更多', + 'Administrators' => '管ç†å‘˜', + 'Visibility:' => 'å¯è§æ€§:', + 'Standard users' => '标准用户', + 'Visibility is required' => 'å¯è§æ€§æ˜¯å¿…需的', + 'The visibility should be an app role' => 'å¯è§æ€§åº”为应用角色', + 'Reply' => '回å¤', + '%s wrote: ' => '%s 写é“:', + 'Number of visible tasks in this column and swimlane' => '此列和泳é“中å¯è§ä»»åŠ¡çš„æ•°é‡', + 'Number of tasks in this swimlane' => '此泳é“中的任务数', + 'Unable to find another subtask in progress, you can close this window.' => '找ä¸åˆ°å¦ä¸€ä¸ªæ­£åœ¨è¿›è¡Œçš„å­ä»»åŠ¡ï¼Œæ‚¨å¯ä»¥å…³é—­æ­¤çª—å£ã€‚', + 'This theme is invalid' => '此主题无效', + 'This role is invalid' => '此角色无效', + 'This timezone is invalid' => '此时区无效', + 'This language is invalid' => '此语言无效', + 'This URL is invalid' => 'æ­¤URL无效', + 'Date format invalid' => 'æ—¥æœŸæ ¼å¼æ— æ•ˆ', + 'Time format invalid' => 'æ—¶é—´æ ¼å¼æ— æ•ˆ', + 'Invalid Mail transport' => '无效的邮件传输', + 'Color invalid' => '颜色无效', + 'This value must be greater or equal to %d' => '此值必须大于或等于 %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => '在文件开头添加 BOM(Microsoft Excel 需è¦ï¼‰', + 'Just add these tag(s)' => 'åªéœ€æ·»åŠ è¿™äº›æ ‡ç­¾', + 'Remove internal link(s)' => '移除内部链接', + 'Import tasks from another project' => '从其他项目导入任务', + 'Select the project to copy tasks from' => '选择è¦ä»Žä¸­å¤åˆ¶ä»»åŠ¡çš„é¡¹ç›®', + 'The total maximum allowed attachments size is %sB.' => 'å…许的附件总大å°ä¸Šé™ä¸º %sB。', + 'Add attachments' => '添加附件', + 'Task #%d "%s" is overdue' => '任务 #%d "%s" 已逾期', + 'Enable notifications by default for all new users' => '默认å¯ç”¨æ‰€æœ‰æ–°ç”¨æˆ·çš„通知', + 'Assign the task to its creator for specific columns if no assignee is set manually' => '如果未手动指定负责人,则在指定列中将任务分é…给创建者', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => '如果未分é…ç”¨æˆ·ï¼Œåˆ™åœ¨åˆ—å˜æ›´ç§»åŠ¨åˆ°æŒ‡å®šåˆ—æ—¶å°†ä»»åŠ¡åˆ†é…给已登录用户', +]; diff --git a/app/Locale/zh_TW/translations.php b/app/Locale/zh_TW/translations.php new file mode 100644 index 0000000..2505115 --- /dev/null +++ b/app/Locale/zh_TW/translations.php @@ -0,0 +1,1472 @@ +<?php + +return [ + 'number.decimals_separator' => '.', + 'number.thousands_separator' => ',', + 'None' => 'ç„¡', + 'Edit' => '編輯', + 'Remove' => '移除', + 'Yes' => '是', + 'No' => 'å¦', + 'cancel' => 'å–æ¶ˆ', + 'or' => '或者', + 'Yellow' => '黄色', + 'Blue' => 'è—色', + 'Green' => '綠色', + 'Purple' => '紫色', + 'Red' => '红色', + 'Orange' => '橘色', + 'Grey' => 'ç°è‰²', + 'Brown' => 'è¤è‰²', + 'Deep Orange' => '橘红色', + 'Dark Grey' => 'æ·±ç°è‰²', + 'Pink' => '粉红色', + 'Teal' => 'é’色', + 'Cyan' => 'è—綠色', + 'Lime' => '黃綠色', + 'Light Green' => '淺綠色', + 'Amber' => '黄è¤è‰²', + 'Save' => '儲存', + 'Login' => '登入', + 'Official website:' => '官方網站:', + 'Unassigned' => '未指派', + 'View this task' => '檢視任務', + 'Remove user' => '移除使用者', + 'Do you really want to remove this user: "%s"?' => '確定è¦åˆªé™¤ä½¿ç”¨è€…"%s"嗎?', + 'All users' => '所有使用者', + 'Username' => '使用者å稱', + 'Password' => '密碼', + 'Administrator' => '管ç†å“¡', + 'Sign in' => '登入', + 'Users' => '使用者', + 'Forbidden' => 'ç¦æ­¢', + 'Access Forbidden' => 'ç¦æ­¢é€²å…¥', + 'Edit user' => '修改使用者', + 'Logout' => '登出', + 'Bad username or password' => '使用者å稱或密碼錯誤', + 'Edit project' => '修改專案', + 'Name' => 'å稱', + 'Projects' => '專案', + 'No project' => '無專案', + 'Project' => '專案', + 'Status' => '狀態', + 'Tasks' => '任務', + 'Board' => '看æ¿', + 'Actions' => '動作', + 'Inactive' => '未啟用', + 'Active' => '啟用', + 'Unable to update this board.' => '無法更新看æ¿ã€‚', + 'Disable' => 'åœç”¨', + 'Enable' => '啟用', + 'New project' => '新建專案', + 'Do you really want to remove this project: "%s"?' => '確定è¦ç§»é™¤å°ˆæ¡ˆ"%s"嗎?', + 'Remove project' => '移除專案', + 'Edit the board for "%s"' => '"%s"修改看æ¿', + 'Add a new column' => '新增欄ä½', + 'Title' => '標題', + 'Assigned to %s' => '指派给 %s', + 'Remove a column' => '刪除一個欄ä½', + 'Unable to remove this column.' => '無法刪除此欄ä½ã€‚', + 'Do you really want to remove this column: "%s"?' => '確定è¦ç§»é™¤æ­¤æ¬„ä½"%s"嗎?', + 'Settings' => '設定', + 'Application settings' => '應用程å¼è¨­å®š', + 'Language' => '語言', + 'Webhook token:' => 'Webhook token:', + 'API token:' => 'API token:', + 'Database size:' => '資料庫大å°ï¼š', + 'Download the database' => '下載資料庫', + 'Optimize the database' => '優化資料庫', + '(VACUUM command)' => '(VACUUM 指令)', + '(Gzip compressed Sqlite file)' => '(用Gzip壓縮的Sqlite文件)', + 'Close a task' => '關閉任務', + 'Column' => '欄ä½', + 'Color' => 'é¡è‰²', + 'Assignee' => '負責人', + 'Create another task' => '新增å¦ä¸€å€‹ä»»å‹™', + 'New task' => '新建任務', + 'Open a task' => '打開任務', + 'Do you really want to open this task: "%s"?' => 'ç¢ºå®šè¦æ‰“開這個任務嗎?"%s"', + 'Back to the board' => '回到看æ¿', + 'There is nobody assigned' => '無人被指派', + 'Column on the board:' => '看æ¿ä¸Šçš„æ¬„ä½ï¼š', + 'Close this 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個英文字æ¯', + 'The password is required' => '需è¦å¯†ç¢¼', + 'This value must be an integer' => '必須為整数', + 'The username must be unique' => '使用者å稱é‡è¤‡', + 'The user id is required' => '帳號id是必è¦çš„', + 'Passwords don\'t match' => '密碼ä¸ç¬¦', + 'The confirmation is required' => '需è¦ç¢ºèª', + 'The project is required' => 'éœ€è¦æŒ‡å®šå°ˆæ¡ˆ', + 'The id is required' => 'éœ€è¦æŒ‡å®šç·¨è™Ÿ', + 'The project id is required' => 'éœ€è¦æŒ‡å®šå°ˆæ¡ˆç·¨è™Ÿ', + 'The project name is required' => 'éœ€è¦æŒ‡å®šå°ˆæ¡ˆå稱', + 'The title is required' => 'éœ€è¦æŒ‡å®šæ¨™é¡Œ', + 'Settings saved successfully.' => '設定儲存æˆåŠŸã€‚', + 'Unable to save your settings.' => '無法儲存你的設定。', + 'Database optimization done.' => '資料庫優化完æˆã€‚', + 'Your project has been created successfully.' => '您的專案已經建立完æˆã€‚', + 'Unable to create your project.' => '無法建立您的專案。', + 'Project updated successfully.' => '更新專案æˆåŠŸã€‚', + 'Unable to update this project.' => '無法更新專案。', + 'Unable to remove this project.' => '無法移除專案。', + 'Project removed successfully.' => '移除專案æˆåŠŸã€‚', + 'Project activated successfully.' => '專案啟用æˆåŠŸã€‚', + 'Unable to activate this project.' => '無法啟用專案。', + 'Project disabled successfully.' => 'åœç”¨å°ˆæ¡ˆæˆåŠŸã€‚', + 'Unable to disable this project.' => '無法åœç”¨å°ˆæ¡ˆã€‚', + 'Unable to open this task.' => '無法開啟任務。', + 'Task opened successfully.' => '任務開啟æˆåŠŸã€‚', + 'Unable to close this task.' => '無法關閉任務。', + 'Task closed successfully.' => '關閉任務æˆåŠŸã€‚', + 'Unable to update your task.' => '無法更新您的任務。', + 'Task updated successfully.' => '更新任務æˆåŠŸã€‚', + 'Unable to create your task.' => '無法建立任務。', + 'Task created successfully.' => '任務建立æˆåŠŸã€‚', + 'User created successfully.' => '使用者建立æˆåŠŸã€‚', + 'Unable to create your user.' => '無法建立使用者。', + 'User updated successfully.' => '使用者更新æˆåŠŸã€‚', + 'User removed successfully.' => '使用者移除æˆåŠŸã€‚', + 'Unable to remove this user.' => '無法移除這個使用者。', + 'Board updated successfully.' => 'çœ‹æ¿æ›´æ–°æˆåŠŸã€‚', + 'Ready' => 'é å‚™', + 'Backlog' => '待辦', + 'Work in progress' => '進行中', + 'Done' => '完æˆ', + 'Application version:' => '應用程å¼ç‰ˆæœ¬ï¼š', + 'Id' => '編號', + 'Public link' => '公開連çµ', + 'Timezone' => '時å€', + 'Sorry, I didn\'t find this information in my database!' => '抱歉,找ä¸åˆ°ä»»ä½•訊æ¯ï¼', + 'Page not found' => '找ä¸åˆ°é é¢', + 'Complexity' => '複雜度', + 'Task limit' => '任務上é™', + 'Task count' => '任務數é‡', + 'User' => '使用者', + 'Comments' => 'è©•è«–', + 'Comment is required' => 'è©•è«–ä¸èƒ½ç©ºç™½', + 'Comment added successfully.' => '評論增加完æˆã€‚', + 'Unable to create your comment.' => '無法建立評論。', + 'Due Date' => '到期日期', + 'Invalid date' => '無效日期', + 'Automatic actions' => '自動化動作', + 'Your automatic action has been created successfully.' => '您的自動化動作已建立', + 'Unable to create your automatic action.' => '無法建立自動化動作。', + 'Remove an action' => '移除一個自動化動作。', + 'Unable to remove this action.' => '無法移除自動化動作', + 'Action removed successfully.' => '移除自動化動作æˆåŠŸã€‚', + 'Automatic actions for the project "%s"' => '專案"%s"的自動化動作', + 'Add an action' => '增加一個動作', + 'Event name' => '事件å稱', + 'Action' => '動作', + 'Event' => '事件', + 'When the selected event occurs execute the corresponding action.' => '當所é¸äº‹ä»¶ç™¼ç”Ÿæ™‚執行動作。', + 'Next step' => '下一步', + '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 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' => '指派é¡è‰²çµ¦ç‰¹å®šä½¿ç”¨è€…', + 'Position' => 'ä½ç½®', + 'Duplicate to project' => '複製到å¦ä¸€å°ˆæ¡ˆ', + 'Duplicate' => '複製', + 'Link' => '連接', + 'Comment updated successfully.' => 'è©•è«–æ›´æ–°æˆåŠŸã€‚', + 'Unable to update your comment.' => '無法更新您的評論。', + 'Remove a comment' => '移除一則評論', + 'Comment removed successfully.' => '評論移除æˆåŠŸã€‚', + 'Unable to remove this comment.' => '無法移除評論。', + 'Do you really want to remove this comment?' => '確定è¦ç§»é™¤æ­¤è©•論嗎?', + 'Current password for the user "%s"' => '使用者"%s"的目å‰å¯†ç¢¼', + 'The current password is required' => '需è¦è¼¸å…¥ç›®å‰çš„密碼', + 'Wrong password' => '密碼錯誤', + 'Unknown' => '未知', + 'Last logins' => '上次登入', + 'Login date' => '登入日期', + 'Authentication method' => 'èªè­‰æ–¹å¼', + 'IP address' => 'IPä½å€', + 'User agent' => 'ç€è¦½å™¨æ¨™ç¤º', + 'Persistent connections' => '登入會話', + 'No session.' => '無會話', + 'Expiration date' => 'éŽæœŸæ—¥æœŸ', + 'Remember Me' => 'è¨˜ä½æˆ‘', + 'Creation date' => '建立日期', + 'Everybody' => '所有人', + 'Open' => '打開', + 'Closed' => '關閉', + 'Search' => 'æœå°‹', + 'Nothing found.' => '找ä¸åˆ°ä»»ä½•資料。', + 'Due date' => '到期日期', + 'Description' => 'æè¿°', + '%d comments' => '%d個評論', + '%d comment' => '%d個評論', + '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.' => 'ä½ å·²æˆåŠŸé—œè¯åˆ°ä½ çš„外部帳號', + 'Email' => 'é›»å­ä¿¡ç®±', + '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' => '自動指派分類到一個é¡è‰²', + 'Task creation or modification' => '任務建立或修改', + 'Category' => '分類', + 'Category:' => '分類:', + 'Categories' => '分類', + 'Your category has been created successfully.' => '建立分類æˆåŠŸã€‚', + 'This category has been updated successfully.' => '更新分類æˆåŠŸã€‚', + 'Unable to update this 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' => '無分類', + 'The name is required' => 'å¿…éœ€è¦æœ‰åå­—', + 'Remove a file' => '移除一個文件', + 'Unable to remove this file.' => '無法移除文件。', + 'File removed successfully.' => '文件移除æˆåŠŸã€‚', + 'Attach a document' => '附加文件', + 'Do you really want to remove this file: "%s"?' => '確定è¦ç§»é™¤æ–‡ä»¶"%s"嗎?', + 'Attachments' => '附件', + 'Edit the task' => '修改任務', + 'Add a comment' => '新增評論', + 'Edit a comment' => '編輯評論', + 'Summary' => '摘è¦', + 'Time tracking' => '時間紀錄', + 'Estimate:' => 'é ä¼°ï¼š', + 'Spent:' => '花費:', + 'Do you really want to remove this sub-task?' => '確èªè¦åˆªé™¤æ­¤å­ä»»å‹™ï¼Ÿ', + 'Remaining:' => '剩餘:', + 'hours' => 'å°æ™‚', + 'estimated' => 'é ä¼°', + 'Sub-Tasks' => 'å­ä»»å‹™', + 'Add a sub-task' => '增加å­ä»»å‹™', + 'Original estimate' => 'åˆæ­¥é ä¼°', + 'Create another sub-task' => '建立å¦ä¸€å€‹å­ä»»å‹™', + 'Time spent' => '時間花費', + 'Edit a sub-task' => '編輯å­ä»»å‹™', + 'Remove a sub-task' => '刪除å­ä»»å‹™', + 'The time must be a numeric value' => '時間必需是數字', + 'Todo' => '待完æˆ', + 'In progress' => '正在進行', + 'Sub-task removed successfully.' => '删除å­ä»»å‹™æˆåŠŸ', + 'Unable to remove this sub-task.' => '無法删除此任務', + 'Sub-task updated successfully.' => '建立å­ä»»å‹™æˆåŠŸ', + 'Unable to update your sub-task.' => '無法更新å­ä»»å‹™', + 'Unable to create your sub-task.' => '無法建立å­ä»»å‹™', + 'Maximum size: ' => '大å°ä¸Šé™ï¼š', + 'Display another project' => '顯示其它專案', + 'Created by %s' => '建立者:%s', + 'Tasks Export' => '任務匯出', + 'Start Date' => '開始時間', + 'Execute' => '執行', + 'Task Id' => '任務編號', + 'Creator' => '建立者', + 'Modification date' => '修改日期', + 'Completion date' => 'å®Œæˆæ—¥æœŸ', + 'Clone' => '複製', + 'Project cloned successfully.' => '複製專案æˆåŠŸã€‚', + 'Unable to clone this project.' => '無法複製專案', + 'Enable email notifications' => '啟用郵件通知', + 'Task position:' => '任務ä½ç½®ï¼š', + 'The task #%d has been opened.' => '任務#%d已經被打開.', + 'The task #%d has been closed.' => '任務#%d已經被關閉.', + 'Sub-task updated' => 'å­ä»»å‹™å·²æ›´æ–°', + 'Title:' => '標題:', + 'Status:' => '狀態:', + 'Assignee:' => '負責人:', + 'Time tracking:' => '時間紀錄', + 'New sub-task' => '新建å­ä»»å‹™', + 'New attachment added "%s"' => '加入新附件"%s"', + 'New comment posted by %s' => '%s 的新評論', + 'New comment' => '新增評論', + 'Comment updated' => '更新了評論', + 'New subtask' => '新建å­ä»»å‹™', + 'I only want to receive notifications for these projects:' => 'æˆ‘è¬¹éœ€è¦æ”¶åˆ°ä¸‹é¢å°ˆæ¡ˆçš„通知:', + 'view the task on Kanboard' => '在看æ¿ä¸­æŸ¥çœ‹æ­¤ä»»å‹™', + 'Public access' => '公開ç€è¦½', + 'Disable public access' => 'åœæ­¢å…¬é–‹ç€è¦½', + 'Enable public access' => '開啟公開ç€è¦½', + 'Public access disabled' => 'å·²ç¶“ç¦æ­¢å…¬é–‹ç€è¦½', + 'Move the task to another project' => '移動任務到其它專案', + 'Move to project' => '移動到其它專案', + 'Do you really want to duplicate this task?' => '確èªè¦è¤‡è£½æ­¤ä»»å‹™å—Žï¼Ÿ', + 'Duplicate a task' => '複製任務', + 'External accounts' => '外部帳號', + 'Account type' => '帳號類型', + 'Local' => '本地', + 'Remote' => 'é ç«¯', + 'Enabled' => '啟用', + 'Disabled' => 'åœç”¨', + 'Login:' => '使用者å稱:', + 'Full Name:' => 'å§“å:', + 'Email:' => 'Email:', + 'Notifications:' => '通知:', + 'Notifications' => '通知', + 'Account type:' => '帳號類型:', + 'Edit profile' => '編輯屬性', + 'Change password' => '修改密碼', + 'Password modification' => '修改密碼', + 'External authentications' => '外部èªè­‰', + 'Never connected.' => '從未連接。', + '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 移動到"%s"的第#%d個ä½ç½®', + '%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/%s å°æ™‚', + '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 連çµ', + '%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)' => '新建專案的é è¨­æ¬„ä½(用逗號分開)', + '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' => '鏿“‡ä¸€å€‹äº‹ä»¶', + 'Create a task from an external provider' => '從外部建立任務', + 'Change the assignee based on an external username' => '根據外部使用者å稱修改任務分é…', + 'Change the category based on an external label' => '根據外部標籤修改分類', + 'Reference' => 'åƒè€ƒ', + 'Label' => '標籤', + 'Database' => '資料庫', + 'About' => '關於', + 'Database driver:' => '資料庫驅動:', + 'Board settings' => '看æ¿è¨­å®š', + 'Webhook settings' => 'Webhook 設定', + 'Reset token' => 'é‡è¨­ token', + 'API endpoint:' => 'API endpoint:', + 'Refresh interval for personal 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ç§’)', + 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '頻率,å–®ä½ç‚ºç§’(設為0åœç”¨æ­¤åŠŸèƒ½ï¼Œé è¨­æ˜¯10ç§’)', + 'Application URL' => '應用程å¼URL', + 'Token regenerated.' => 'Token已釿–°ç”¢ç”Ÿ', + 'Date format' => '日期格å¼', + 'ISO format is always accepted, example: "%s" and "%s"' => 'ISO æ ¼å¼ç¸½æ˜¯å…許的,例如:"%s" å’Œ "%s"', + 'New personal project' => '新建ç§äººå°ˆæ¡ˆ', + 'This project is personal' => '此專案為ç§äººå°ˆæ¡ˆ', + 'Add' => '增加', + 'Start date' => '開始日期', + 'Time estimated' => 'é è¨ˆæ™‚é–“', + 'There is nothing assigned to you.' => 'ç›®å‰ç„¡ä»»å‹™æŒ‡æ´¾ç»™ä½ ã€‚', + 'My tasks' => '我的任務', + 'Activity stream' => '動態記錄', + 'Dashboard' => '儀表æ¿', + 'Confirmation' => '確èª', + 'Webhooks' => 'Webhooks', + 'API' => 'API', + 'Create a comment from an external provider' => '從外部建立一個評論', + 'Project management' => '專案管ç†', + 'Columns' => '欄ä½', + 'Task' => '任務', + 'Percentage' => '百分比', + 'Number of tasks' => '任務數', + 'Task distribution' => '任務分佈', + 'Analytics' => '分æž', + 'Subtask' => 'å­ä»»å‹™', + 'User repartition' => '使用者分æž', + 'Clone this project' => '複製此專案', + 'Column removed successfully.' => 'åˆ é™¤æ¬„ä½æˆåŠŸã€‚', + '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 subtask id is required' => 'å¿…é ˆæä¾›å­ä»»å‹™ç·¨è™Ÿ', + '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' => '使用者編號必須為整數', + 'This value is required' => '這個數值是必è¦çš„', + 'This value must be numeric' => '這個值必須為數字', + 'Unable to create this task.' => '無法建立此任務。', + 'Cumulative flow diagram' => 'ç´¯ç©æµç¨‹åœ–', + 'Daily project summary' => 'æ¯æ—¥å°ˆæ¡ˆåŒ¯ç¸½', + 'Daily project summary export' => 'åŒ¯å‡ºæ¯æ—¥å°ˆæ¡ˆåŒ¯ç¸½', + 'Exports' => '匯出', + 'This export contains the number of tasks per column grouped per day.' => 'æ­¤åŒ¯å‡ºåŒ…å«æ¯åˆ—的任務數,按天分组', + 'Active swimlanes' => '活動里程碑', + 'Add a new swimlane' => '增加新里程碑', + 'Default swimlane' => 'é è¨­é‡Œç¨‹ç¢‘', + 'Do you really want to remove this swimlane: "%s"?' => '確定è¦åˆ é™¤é‡Œç¨‹ç¢‘:"%s"?', + 'Inactive swimlanes' => 'éžæ´»å‹•里程碑', + 'Remove a swimlane' => '删除里程碑', + 'Swimlane modification for the project "%s"' => '專案"%s"的里程碑變更', + 'Swimlane removed successfully.' => '删除里程碑æˆåŠŸ', + 'Swimlanes' => '里程碑', + 'Swimlane updated successfully.' => '更新里程碑æˆåŠŸã€‚', + 'Unable to remove this swimlane.' => '無法删除此里程碑', + 'Unable to update this swimlane.' => '無法更新此里程碑', + 'Your swimlane has been created successfully.' => '你的里程碑已æˆåŠŸå»ºç«‹ã€‚', + 'Example: "Bug, Feature Request, Improvement"' => '範例:“缺陷,功能需求,æå‡', + 'Default categories for new projects (Comma-separated)' => '新項目的é è¨­åˆ†é¡ž(用逗號分隔)', + 'Integrations' => 'æ•´åˆ', + 'Integration with third-party services' => '與第三方æœå‹™æ•´åˆ', + 'Subtask Id' => 'å­ä»»å‹™ç·¨è™Ÿ', + 'Subtasks' => 'å­ä»»å‹™', + 'Subtasks Export' => 'å­ä»»å‹™åŒ¯å‡º', + 'Task Title' => '任務標題', + 'Untitled' => '無標題', + 'Application default' => '程åºé è¨­', + 'Language:' => '語言:', + 'Timezone:' => '時å€ï¼š', + 'All columns' => '全部欄ä½', + 'Next' => '下一é ', + '#%d' => '#%d', + 'All swimlanes' => '全部里程碑', + 'All colors' => '全部é¡è‰²', + 'Moved to column %s' => 'ç§»å‹•åˆ°æ¬„ä½ %s', + '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' => '時間追踪', + 'You already have one subtask in progress' => '你已經有一個進行中的å­ä»»å‹™', + 'Which parts of the project do you want to duplicate?' => 'è¦è¤‡è£½å°ˆæ¡ˆçš„哪些内容?', + 'Disallow login form' => 'ç¦æ­¢ç™»å…¥', + 'Start' => 'é–‹å§‹', + 'End' => 'çµæŸ', + 'Task age in days' => '任務存在天數', + 'Days in this column' => '在此欄ä½çš„天數', + '%dd' => '%d天', + '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' => 'å¿…è¦çš„æ¬„ä½', + 'Link added successfully.' => 'å¢žåŠ é€£çµæˆåŠŸã€‚', + 'Link updated successfully.' => 'æ›´æ–°é€£çµæˆåŠŸã€‚', + 'Link removed successfully.' => 'åˆ é™¤é€£çµæˆåŠŸã€‚', + 'Link labels' => 'é€£çµæ¨™ç±¤', + 'Link modification' => '連çµä¿®æ”¹', + 'Opposite label' => 'å呿¨™ç±¤', + 'Remove a link' => '删除連çµ', + '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.' => '無法删除連çµã€‚', + 'relates to' => 'é—œè¯åˆ°', + 'blocks' => '阻塞', + 'is blocked by' => '阻塞於', + 'duplicates' => 'é‡è¤‡', + 'is duplicated by' => 'é‡è¤‡æ–¼', + 'is a child of' => 'å­ä»»å‹™è‡ª', + 'is a parent of' => '父任務於', + 'targets milestone' => '里程碑目標', + 'is a milestone of' => '屬於里程碑', + 'fixes' => '修復', + 'is fixed by' => '修復於', + 'This task' => '此任務', + '<1h' => '<1h', + '%dh' => '%dh', + 'Expand tasks' => '展開任務', + 'Collapse tasks' => 'æ”¶åˆä»»å‹™', + 'Expand/collapse tasks' => '展開/æ”¶åˆä»»å‹™', + 'Close dialog box' => '關閉å°è©±æ¡†', + 'Submit a form' => 'æäº¤è¡¨å–®', + 'Board view' => '颿¿è¦–圖', + 'Keyboard shortcuts' => 'éµç›¤å¿«é€Ÿéµ', + 'Open board switcher' => 'æ‰“é–‹é¢æ¿åˆ‡æ›å™¨', + 'Application' => '應用程åº', + 'Compact view' => '緊湊視圖', + 'Horizontal scrolling' => 'æ°´å¹³æ²å‹•', + 'Compact/wide view' => '緊湊/寬視圖', + 'Currency' => '貨幣', + 'Personal project' => 'ç§äººé …ç›®', + 'AUD - Australian Dollar' => '澳幣', + 'CAD - Canadian Dollar' => '加元', + 'CHF - Swiss Francs' => '瑞士法郎', + 'Custom Stylesheet' => '自定義樣å¼è¡¨', + 'EUR - Euro' => 'æ­å…ƒ', + 'GBP - British Pound' => '英鎊', + 'INR - Indian Rupee' => 'å°åº¦ç›§æ¯”', + 'JPY - Japanese Yen' => '日元', + 'NZD - New Zealand Dollar' => 'ç´è¥¿è˜­å…ƒ', + 'PEN - Peruvian Sol' => 'PEN - 秘é²ç´¢å°”', + 'RSD - Serbian dinar' => '第ç´çˆ¾', + 'CNY - Chinese Yuan' => '人民幣', + 'USD - US Dollar' => '美元', + 'VES - Venezuelan Bolívar' => 'VES - 委內瑞拉玻利瓦爾', + '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' => '欄ä½ä¸­çš„æ™‚間消耗', + '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.' => 'æ­¤å ±å‘Šç´€éŒ„ä»»å‹™çš„è®Šæ›´ï¼ŒåŒ…å«æ—¥æœŸã€ä½¿ç”¨è€…和時間消耗。', + 'Currency rates' => '匯率', + 'Rate' => '匯率', + 'Change reference currency' => '修改å‚考貨幣', + 'Reference currency' => 'å‚考貨幣', + 'The currency rate has been added successfully.' => '增加匯率æˆåŠŸã€‚', + 'Unable to add this currency rate.' => '無法增加此匯率', + 'Webhook URL' => 'Webhook URL', + '%s removed the assignee of the task %s' => '%s删除了任務%s的負責人', + 'Information' => '訊æ¯', + 'Check two factor authentication code' => '檢查雙é‡èªè­‰ç¢¼', + 'The two factor authentication code is not valid.' => 'é›™é‡èªè­‰ç¢¼ä¸æ­£ç¢ºã€‚', + 'The two factor authentication code is valid.' => 'é›™é‡èªè­‰ç¢¼æ­£ç¢ºã€‚', + 'Code' => 'èªè­‰ç¢¼', + 'Two factor authentication' => 'é›™é‡èªè­‰', + 'This QR code contains the key URI: ' => '此二維æ¢ç¢¼åŒ…å«å¯†ç¢¼ URI:', + 'Check my code' => '檢查我的èªè­‰ç¢¼', + 'Secret key: ' => '密碼:', + 'Test your device' => '測試你的設備', + 'Assign a color when the task is moved to a specific column' => 'ä»»å‹™ç§»å‹•åˆ°æŒ‡å®šæ¬„ä½æ™‚設定é¡è‰²', + '%s via Kanboard' => '%s 通éŽKanBoard', + 'Burndown chart' => '燃盡圖', + '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' => '瑞郎', + 'Identifier' => '標示符', + 'Disable two factor authentication' => 'åœç”¨é›™é‡èªè­‰', + 'Do you really want to disable the two factor authentication for this user: "%s"?' => '你真的è¦åœç”¨ "%s" 的雙é‡èªè­‰å—Žï¼Ÿ', + 'Edit link' => '編輯連çµ', + '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' => '循環性任務將按計畫生æˆ', + 'Score' => 'ç©åˆ†', + 'The identifier must be unique' => '標示符必須唯一', + 'This linked task id doesn\'t exists' => '連çµçš„任務ä¸å­˜åœ¨', + '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' => '計算逾期時間表', + '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: ' => '超期因素', + 'Month(s)' => '月', + 'This task has been created by: ' => '此任務建立者:', + 'Recurrent task has been generated:' => '循環任務已建立:', + 'Timeframe to calculate new due date: ' => '計算逾期時間表', + 'Trigger to generate recurrent task: ' => '建立循環任務的觸發器', + 'When task is closed' => '當任務關閉時', + 'When task is moved from first column' => '當任務從第一列任務欄移走時', + 'When task is moved to last column' => '當任務移動到最後一列任務欄時', + 'Year(s)' => 'å¹´', + 'Project settings' => '專案設定', + 'Automatically update the start date' => '自動更新開始日期', + 'iCal feed' => '日曆訂閱', + 'Preferences' => 'å好', + 'Security' => '安全', + 'Two factor authentication disabled' => 'é›™é‡èªè­‰å·²åœç”¨', + 'Two factor authentication enabled' => 'é›™é‡èªè­‰å·²å•Ÿç”¨', + 'Unable to update this user.' => '無法更新此使用者', + 'There is no user management for personal 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' => '當任務分類改變時移動到任務欄', + 'Send a task by email to someone' => '發é€ä»»å‹™éƒµä»¶çµ¦ä½¿ç”¨è€…', + 'Reopen a task' => '釿–°é–‹å§‹ä»»å‹™', + 'Notification' => '通知', + '%s moved the task #%d to the first swimlane' => '%s將任務#%d移動到首個里程碑', + 'Swimlane' => '里程碑', + '%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.' => '該報告包å«äº†æŒ‡å®šæ—¥æœŸç¯„åœå†…的所有任務訊æ¯ã€‚', + 'Project activities for %s' => '%s的項目活動紀錄', + 'view the board on Kanboard' => '在看æ¿ä¸ŠæŸ¥çœ‹é¢æ¿', + 'The task has been moved to the first swimlane' => '該任務已被移動到首個里程碑', + 'The task has been moved to another swimlane:' => '乾任務已被移動到别的里程碑:', + 'New title: %s' => '新標題:%s', + 'The task is not assigned anymore' => '該任務没有指派人', + 'New assignee: %s' => '新指派人:%s', + 'There is no category now' => 'ç›®å‰æ²¡æœ‰åˆ†é¡ž', + 'New category: %s' => '新分類:%s', + 'New color: %s' => 'æ–°é¡è‰²ï¼š%s', + 'New complexity: %d' => '新的複雜度:%d', + 'The due date has been removed' => '到期時間已被移除', + 'There is no description anymore' => 'ç›®å‰æ²¡æœ‰æè¿°', + 'Recurrence settings has been modified' => '循環週期已被更改', + 'Time spent changed: %sh' => '時間花費已變更:%sh', + 'Time estimated changed: %sh' => '時間é ä¼°å·²è®Šæ›´ï¼š%sh', + 'The field "%s" has 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:' => '我想接收以下相關通知:', + 'All tasks' => '所有任務', + 'Only for tasks assigned to me' => '所有指派给我的任務', + 'Only for tasks created by me' => '所有我建立的任務', + 'Only for tasks created by me and tasks assigned to me' => '所有我建立的並且指派给我的任務', + '%%Y-%%m-%%d' => '%%Y-%%m-%%d', + 'Total for all columns' => '所有欄ä½ä¸‹çš„', + 'You need at least 2 days of data to show the chart.' => '柱狀圖至少需è¦2天的資料。', + '<15m' => 'å°æ–¼15分é˜', + '<30m' => 'å°æ–¼30分é˜', + 'Stop timer' => 'åœæ­¢è¨ˆæ™‚器', + 'Start timer' => '啟動計時器', + 'My activity stream' => '我的動態', + 'Search tasks' => 'æœå°‹ä»»å‹™', + 'Reset filters' => 'é‡ç½®éŽæ¿¾å™¨', + 'My tasks due tomorrow' => '我明天到期的任務', + 'Tasks due today' => '今天到期的任務', + 'Tasks due tomorrow' => '明天到期的任務', + 'Tasks due yesterday' => '昨天到期的任務', + 'Closed tasks' => '已關閉的任務', + 'Open tasks' => '打開的任務', + 'Not assigned' => '未指派', + 'View advanced search syntax' => '查看高级æœå°‹èªžæ³•', + 'Overview' => '總覽', + 'Board/Calendar/List view' => '看æ¿/日程/列表視圖', + 'Switch to the board view' => '切æ¢åˆ°çœ‹æ¿è¦–圖', + 'Switch to the list view' => '切æ¢åˆ°åˆ—表視圖', + 'Go to the search/filter box' => 'å‰å¾€æœå°‹/éŽæ¿¾ç®±', + 'There is no activity yet.' => 'ç›®å‰ç„¡ä»»ä½•活動.', + 'No tasks found.' => '找ä¸åˆ°ä»»ä½•任務.', + 'Keyboard shortcut: "%s"' => '快速éµ: "%s"', + 'List' => '列表', + 'Filter' => 'éŽæ¿¾å™¨', + 'Advanced search' => '高级æœå°‹', + 'Example of query: ' => '查詢範例: ', + 'Search by project: ' => 'ä¾å°ˆæ¡ˆ: ', + 'Search by column: ' => 'ä¾ä»»å‹™æ¬„: ', + 'Search by assignee: ' => 'ä¾è¢«æŒ‡æ´¾äºº: ', + 'Search by color: ' => 'ä¾é¢œè‰²: ', + 'Search by category: ' => 'ä¾åˆ†é¡žï¼š', + 'Search by description: ' => 'ä¾æè¿°ï¼š', + 'Search by due date: ' => 'ä¾é€¾æœŸæ™‚間:', + 'Average time spent in each column' => 'æ¯å€‹ä»»å‹™æ¬„的平å‡èŠ±è²»æ™‚é–“', + 'Average time spent' => 'å¹³å‡èŠ±è²»æ™‚é–“', + 'This chart shows the average time spent in each column for the last %d tasks.' => 'ç›®å‰æŸ±ç‹€åœ–表示最新%dæ¢ä»»å‹™åœ¨æ¯å€‹ä»»å‹™æ¬„下的平å‡èŠ±è²»æ™‚é–“', + 'Average Lead and Cycle time' => 'å¹³å‡å·¥ä½œæ™‚間和平å‡é€±æœŸæ™‚é–“', + 'Average lead time: ' => 'å¹³å‡å·¥ä½œæ™‚é–“', + 'Average cycle time: ' => 'å¹³å‡å‘¨æœŸæ™‚é–“: ', + 'Cycle Time' => '週期時間', + 'Lead Time' => '工作時間', + 'This chart shows the average lead and cycle time for the last %d tasks over the time.' => 'ç›®å‰æŸ±ç‹€åœ–表示最新%dæ¢ä»»å‹™çš„å¹³å‡å·¥ä½œæ™‚間和週期時間', + 'Average time into each column' => 'æ¯å€‹ä»»å‹™æ¬„çš„å¹³å‡æ™‚é–“', + 'Lead and cycle time' => '工作時間和週期時間', + 'Lead time: ' => '工作時間: ', + 'Cycle time: ' => '週期時間: ', + 'Time spent in 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 the start date automatically' => '設定自動開始日期', + '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.' => 'å¦‚æžœé¸æ“‡â€œç¦æ­¢ç™»å…¥ä¾†è‡ªâ€ï¼Œç™»å…¥è¡¨å–®å†…的驗證訊æ¯å°‡è¢«å¿½ç•¥ã€‚', + '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 category: %s' => 'ç›®å‰åˆ†é¡žï¼š%s', + 'no category' => '無分類', + 'Current assignee: %s' => 'ç›®å‰è¢«æŒ‡æ´¾äºº: %s', + 'not assigned' => '未指派', + 'Author:' => '作者', + 'contributors' => 'è²¢ç»è€…', + 'License:' => '授權許å¯:', + 'License' => '授權許å¯', + 'Enter the text below' => '輸入下方的文字', + 'Start date:' => '開始日期', + 'Due date:' => '到期日期', + 'People who are project managers' => '專案管ç†å“¡', + 'People who are project members' => '專案æˆå“¡', + 'NOK - Norwegian Krone' => '克朗', + 'Show this column' => '顯示任務欄', + 'Hide this column' => 'éš±è—任務欄', + 'End date' => 'ç»“æŸæ—¥æœŸ', + 'Users overview' => '使用者總覽', + 'Members' => 'æˆå“¡', + 'Shared project' => '公開專案', + 'Project managers' => '專案管ç†å“¡', + 'Projects list' => '專案列表', + 'End date:' => 'ç»“æŸæ—¥æœŸ', + 'Change task color when using a specific task link' => '當任務關è¯åˆ°æŒ‡å®šä»»å‹™æ™‚改變é¡è‰²', + 'Task link creation or modification' => '任務連接建立或更新時間', + 'Milestone' => '里程碑', + 'Reset the search/filter box' => 'é‡è¨­æœå°‹/éŽæ¿¾æ¡†', + 'Documentation' => '文件', + 'Author' => '作者', + 'Version' => '版本', + 'Plugins' => 'æ’ä»¶', + 'There is no plugin loaded.' => 'ç›®å‰æ²¡æœ‰æ’件載入', + 'My notifications' => '我的通知', + 'Custom filters' => 'è‡ªå®šç¾©éŽæ¿¾å™¨', + 'Your custom filter has 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 has been updated successfully.' => 'ä½ çš„éŽæ¿¾å™¨æ›´æ–°æˆåŠŸ', + 'Unable to update custom filter.' => 'ç„¡æ³•æ›´æ–°éŽæ¿¾å™¨', + 'Web' => 'web', + '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下的å­ä»»å‹™å·²æ›´æ–°', + 'New task #%d: %s' => '新任務#%d:%s', + 'Task updated #%d' => '任務#%d已更新', + 'Task #%d closed' => '任務#%d已關閉', + 'Task #%d opened' => '任務#%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的指派人已改變', + '%d overdue tasks' => '%dæ¢é€¾æœŸä»»å‹™', + 'No notification.' => '没有新通知', + '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:' => '通知æé†’æ–¹å¼ï¼š', + 'Unable to read your file' => 'ç„¡æ³•è®€å–æ–‡ä»¶', + '%d task(s) have been imported successfully.' => 'æˆåŠŸåŒ¯å…¥%dæ¢ä»»å‹™ã€‚', + 'Nothing has been imported!' => '没有資料被匯入ï¼', + 'Import users from CSV file' => '從CSV文件匯入帳號', + '%d user(s) have been imported successfully.' => 'æˆåŠŸåŒ¯å…¥%d個帳號。', + 'Comma' => '逗號', + 'Semi-colon' => '分號', + 'Tab' => '製表符', + 'Vertical bar' => '豎線', + '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)' => 'è¿½åŠ éŽæ¿¾', + 'Append/Replace' => '追加/替æ¢', + 'Append' => '追加', + 'Replace' => '替æ¢', + 'Import' => '匯入', + 'Change sorting' => '改變排åº', + 'Tasks Importation' => '任務é‡è¦æ€§', + 'Delimiter' => '分隔符', + 'Enclosure' => '附件', + 'CSV File' => 'CSV文件', + 'Instructions' => 'æ“作指å—', + 'Your file must use the predefined CSV format' => '文件必須為CSVæ ¼å¼', + 'Your file must be encoded in UTF-8' => '文件編碼必須為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' => '到期日期必須為ISOæ ¼å¼ï¼šYYYY-MM-DD', + 'Download CSV template' => '下載CSV範本', + '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' => '"%s"增加了附件到任務"%s"', + 'Link type' => '連çµé¡žåž‹', + 'Assign automatically a category based on a link' => '根據連çµè‡ªå‹•分類', + 'BAM - Konvertible Mark' => '波斯尼亞馬克', + 'Assignee Username' => '指派使用者', + 'Assignee Name' => '指派å稱', + 'Groups' => '使用者群組', + 'Members of %s' => '“%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"' => '增加到群組"%s"', + 'Group member added successfully.' => '增加群組æˆå“¡æˆåŠŸ', + 'Unable to add group member.' => '無法增加群組æˆå‘˜', + 'Remove user from group "%s"' => '從"%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' => '你的帳號被鎖定%d分é˜', + 'Invalid captcha' => '驗證碼無效', + 'The name must be unique' => '帳號å稱ä¸èƒ½é‡è¤‡', + 'View all groups' => '查看所有群組', + 'There is no user available.' => 'ç›®å‰ç„¡æœ‰æ•ˆä½¿ç”¨è€…', + 'Do you really want to remove the user "%s" from the group "%s"?' => '你確定把使用者"%s"從"%s"中移除?', + 'There is no group.' => 'ç›®å‰æ²¡æœ‰ç¾¤çµ„', + 'Add group member' => '增加群組æˆå“¡', + 'Do you really want to remove this group: "%s"?' => '確èªè¦ç§»é™¤ç¾¤çµ„:"%s"?', + 'There is no user in this group.' => 'ç›®å‰ç¾¤çµ„下没有æˆå“¡', + 'Permissions' => '權é™', + 'Allowed Users' => '被å…許的使用者', + 'No specific user has been allowed.' => '没有被å…許的使用者。', + 'Role' => '角色', + 'Enter user name...' => '輸入使用者...', + 'Allowed Groups' => '被å…許的群組', + 'No group has been allowed.' => '没有被å…許的群組。', + 'Group' => '群組', + 'Group Name' => '群組å稱', + 'Enter group name...' => '輸入群組å稱...', + 'Role:' => '角色:', + 'Project members' => '專案æˆå“¡', + '%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評論中被æåˆ°', + 'Estimated hours: ' => 'é ä¼°å°æ™‚數', + 'Actual hours: ' => 'å¯¦éš›å°æ™‚數', + 'Hours Spent' => 'èŠ±è²»å°æ™‚數', + 'Hours Estimated' => 'é ä¼°å°æ™‚數', + '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' => '關閉指定任務欄下的任務', + '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' => 'é‡è¨­kanboard密碼', + 'Forgot password?' => '忘記密碼?', + 'Enable "Forget Password"' => '啟用找回密碼', + 'Password Reset' => '密碼é‡ç½®', + 'New password' => '新密碼', + 'Change Password' => '更改密碼', + 'To reset your password click on this link:' => '點擊此連çµé‡ç½®æ‚¨çš„密碼', + 'Last Password Reset' => '上次密碼é‡ç½®', + 'The password has never been reinitialized.' => 'å¯†ç¢¼å¾žæœªè¢«é‡æ–°åˆå§‹åŒ–', + 'Creation' => '建立時間', + 'Expiration' => 'éŽæœŸæ™‚é–“', + 'Password reset history' => '密碼é‡ç½®ç´€éŒ„', + 'All tasks of the column "%s" and the swimlane "%s" have been closed successfully.' => '任務欄 "%s" å’Œ 里程碑 "%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 in this column and this swimlane' => '關閉此任務欄下的所有任務', + 'No plugin has registered a project notification method. You can still configure individual notifications in your user profile.' => '没有æ’件註冊到專案通知API,你ä»ç„¶å¯ä»¥åœ¨ä½ å€‹äººè¨­å®šè£¡å•Ÿç”¨å–®ç¨çš„通知', + 'My dashboard' => '我的儀表æ¿', + 'My profile' => '我的個人訊æ¯', + 'Project owner: ' => '專案負責人', + 'The project identifier is optional and must be alphanumeric, example: MYPROJECT.' => '專案標示符是å¯é¸çš„,且åªèƒ½æ˜¯æ•¸å­—或字æ¯ç»„æˆï¼Œä¾‹å¦‚:MYPROJECT。', + 'Project owner' => '專案負責人', + 'Personal projects do not have users and groups management.' => 'ç§äººå°ˆæ¡ˆä¸‹æ²’有æˆå“¡æˆ–群組å¯ç®¡ç†', + 'There is no project member.' => 'ç›®å‰æ²¡æœ‰å°ˆæ¡ˆæˆå“¡', + 'Priority' => '優先權', + 'Task priority' => '任務優先權', + 'General' => '常用', + 'Dates' => '時間', + 'Default priority' => 'é è¨­å„ªå…ˆæ¬Š', + 'Lowest priority' => '最低優先權', + 'Highest priority' => '最高優先權', + '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' => '附件', + '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' => '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' => '%s å°æ™‚', + 'Time spent:' => '時間消耗:', + 'Created:' => '已建立:', + 'Modified:' => '已修改:', + 'Completed:' => '已完æˆï¼š', + 'Started:' => '已開始:', + 'Moved:' => '已移走:', + 'Task #%d' => '任務#%d', + 'Time format' => '時間格å¼', + 'Start date: ' => '開始時間:', + 'End date: ' => 'ç»“æŸæ™‚間:', + 'New due date: ' => '新到期時間:', + 'Start date changed: ' => '開始時間已改變:', + 'Disable personal projects' => 'åœç”¨ç§äººå°ˆæ¡ˆ', + 'Do you really want to remove this custom filter: "%s"?' => '你確定è¦ç§»é™¤é€™å€‹è‡ªå®šç¾©éŽæ¿¾å™¨ï¼š"%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.' => '所有文件已æˆåŠŸä¸Šå‚³ã€‚', + 'The maximum allowed file size is %sB.' => '最大上傳尺寸 %sB', + 'Drag and drop your files here' => '拖放文件到這裡', + 'choose files' => '鏿“‡æ–‡ä»¶', + 'View profile' => '查看個人訊æ¯', + 'Two Factor' => 'é›™é‡èªè­‰', + 'Disable user' => 'ç¦ç”¨å¸³è™Ÿ', + 'Do you really want to disable this user: "%s"?' => '你確定è¦ç¦ç”¨å¸³è™Ÿï¼š"%s"?', + 'Enable user' => '啟用帳號', + 'Do you really want to enable this user: "%s"?' => '你確定è¦å•Ÿç”¨å¸³è™Ÿï¼š"%s"?', + 'Download' => '下載', + 'Uploaded: %s' => '上傳:%s', + 'Size: %s' => '大å°ï¼š%s', + 'Uploaded by %s' => 'ç”±%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' => 'ç›®å‰è¼¸å…¥å€¼å¿…須大於%d', + 'Another swimlane with the same name exists in the project' => 'ç›®å‰å°ˆæ¡ˆä¸­ç›¸åŒå稱里程碑已存在', + 'Example: https://example.kanboard.org/ (used to generate absolute URLs)' => '例如: https://example.kanboard.org/(通常用於產生永久網å€ï¼‰', + '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 版本:', + 'PHP SAPI:' => 'PHP SAPI:', + 'OS version:' => '作業系统:', + 'Database version:' => '資料庫版本:', + 'Browser:' => 'ç€è¦½å™¨ï¼š', + 'Task view' => '任務ç€è¦½', + 'Edit task' => '編輯任務', + 'Edit description' => '編輯æè¿°', + 'New internal link' => '新增内部連çµ', + 'Display list of keyboard shortcuts' => '顯示快速éµåˆ—表', + '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' => 'Metadata', + 'Manage projects' => '管ç†å°ˆæ¡ˆ', + 'Convert to task' => '轉為普通任務', + 'Convert sub-task to task' => '轉æ›å­ä»»å‹™ç‚ºæ™®é€šä»»å‹™', + 'Do you really want to convert this sub-task to a task?' => '你真的è¦å°‡æ­¤å­ä»»å‹™è½‰æ›ç‚ºæ™®é€šä»»å‹™ï¼Ÿ', + 'My task title' => '我的任務標題', + 'Enter one task by line.' => '寫入一行任務。', + 'Number of failed login:' => '登入失敗次數:', + 'Account locked until:' => '帳號被鎖定至:', + 'Email settings' => '郵件設定', + 'Email sender address' => '郵件發é€åœ°å€', + 'Email transport' => '郵件轉發', + 'Webhook token' => 'Webhook 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 a 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 has 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的活動紀錄', + '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 access', + 'Preview' => 'é è¦½', + 'Write' => '寫入', + 'Write your text in Markdown' => '使用markdownæ ¼å¼', + 'No personal API access token registered.' => '没有API access token 註冊。', + 'Your personal API access token is "%s"' => 'ä½ çš„API access token 是 “%sâ€', + 'Remove your token' => '移除token', + 'Generate a new token' => '產生新的token', + 'Showing %d-%d of %d' => '本é é¡¯ç¤º %d-%d 筆,共有: %d ç­†', + 'Outgoing Emails' => '目標郵件', + '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個邀請已發é€ã€‚', + '%d invitation was sent.' => '%d個邀請已發é€ã€‚', + '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' => '帳號å稱已被使用', + 'Your profile must have a valid email address.' => '個人資料必須有一個有效emailä½å€ã€‚', + 'TRL - Turkish Lira' => '土耳其里拉', + 'The project email is optional and could be used by several plugins.' => '專案郵件ä½å€å¯é¸ï¼Œä½¿ç”¨æ–¼æ’件。', + 'The project email must be unique across all projects' => '專案郵件地å€å¿…須全域唯一', + 'The email configuration has been disabled by the administrator.' => '郵件設定被管ç†å“¡ç¦ç”¨ã€‚', + 'Close this project' => '關閉專案', + 'Open this project' => '打開專案', + 'Close a project' => '關閉專案', + 'Do you really want to close this project: "%s"?' => '你真的è¦é—œé–‰å°ˆæ¡ˆï¼š"%s"?', + 'Reopen a project' => 'é‡é–‹å°ˆæ¡ˆ', + 'Do you really want to reopen this project: "%s"?' => '你真的è¦é‡é–‹å°ˆæ¡ˆï¼š"%s"?', + 'This project is open' => '專案已打開', + 'This project is closed' => '專案已關閉', + 'Unable to upload files, check the permissions of your data folder.' => '無法上傳文件,請檢查你的data目錄權é™ã€‚', + 'Another category with the same name exists in this project' => '此项目中存在å¦ä¸€ä¸ªåŒå类别', + 'Comment sent by email successfully.' => 'è©•è«–å·²æˆåŠŸé€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚', + 'Sent by email to "%s" (%s)' => '通éŽé›»å­éƒµä»¶ç™¼é€çµ¦ "%s" (%s)', + 'Unable to read uploaded file.' => '無法讀å–上傳的檔案。', + 'Database uploaded successfully.' => '資料庫æˆåŠŸä¸Šå‚³ã€‚', + 'Task sent by email successfully.' => '任務已æˆåŠŸé€šéŽé›»å­éƒµä»¶ç™¼é€ã€‚', + 'There is no category in this project.' => 'ç›®å‰å°ˆæ¡ˆæ²¡æœ‰åˆ†é¡žã€‚', + 'Send by email' => '發é€éƒµä»¶', + 'Create and send a comment by email' => '通éŽé›»å­éƒµä»¶å»ºç«‹å’Œç™¼é€è©•è«–', + 'Subject' => '主旨', + 'Upload the database' => '上傳資料庫', + 'You could upload the previously downloaded Sqlite database (Gzip format).' => '您å¯ä»¥ä¸Šå‚³ä¹‹å‰ä¸‹è¼‰çš„ Sqlite 資料庫 (Gzip æ ¼å¼)。', + 'Database file' => '資料庫檔案', + 'Upload' => '上傳', + 'Your project must have at least one active swimlane.' => '您的專案必須至少有一個作用中的泳é“。', + 'Project: %s' => '專案:%s', + 'Automatic action not found: "%s"' => '找ä¸åˆ°è‡ªå‹•動作: "%s"', + '%d projects' => '%d 專案', + '%d project' => '%d 專案', + 'There is no project.' => 'ç›®å‰æ²¡æœ‰å°ˆæ¡ˆã€‚', + 'Sort' => '排åº', + 'Project ID' => '專案編號', + 'Project name' => '專案å稱', + 'Public' => '公開', + 'Personal' => 'ç§äºº', + '%d tasks' => '%d 任務', + '%d task' => '%d 任務', + 'Task ID' => '任務編號', + 'Assign automatically a color when due date is expired' => 'ç•¶æˆªæ­¢æ—¥æœŸéŽæœŸæ™‚自動分é…é¡è‰²', + 'Total score in this column across all swimlanes' => '此列所有泳é“的總分數', + 'HRK - Kuna' => 'HRK - 克羅埃西亞庫ç´', + 'ARS - Argentine Peso' => 'ARS - 阿根廷比索', + 'COP - Colombian Peso' => 'COP - 哥倫比亞比索', + '%d groups' => '%d 群組', + '%d group' => '%d 群組', + 'Group ID' => '群組編號', + 'External ID' => '外部 ID', + '%d users' => '%d 使用者', + '%d user' => '%d 使用者', + 'Hide subtasks' => '顯示å­ä»»å‹™', + 'Show subtasks' => 'éš±è—å­ä»»å‹™', + 'Authentication Parameters' => 'èªè­‰åƒæ•¸', + 'API Access' => 'API å­˜å–', + 'No users found.' => '找ä¸åˆ°ä½¿ç”¨è€…。', + 'User ID' => '使用者編號', + 'Notifications are activated' => '通知已啟用', + 'Notifications are disabled' => '通知已åœç”¨', + 'User disabled' => '帳號已ç¦ç”¨', + '%d notifications' => '%d 通知', + '%d notification' => '%d 通知', + 'There is no external integration installed.' => '未安è£å¤–部整åˆã€‚', + 'You are not allowed to update tasks assigned to someone else.' => '您ä¸å…許更新分é…給其他人的任務。', + 'You are not allowed to change the assignee.' => '您ä¸å…許更改被分é…人。', + 'Task suppression is not permitted' => 'ä¸å…許壓制任務', + 'Changing assignee is not permitted' => 'ä¸å…許更改被分é…人', + 'Update only assigned tasks is permitted' => 'åªå…許更新分é…的任務', + 'Only for tasks assigned to the current user' => 'åƒ…é™æ–¼åˆ†é…給當å‰ä½¿ç”¨è€…的任務', + 'My projects' => '我的專案', + 'You are not a member of any project.' => '你䏿˜¯ä»»ä½•專案的æˆå“¡ã€‚', + 'My subtasks' => '我的å­ä»»å‹™', + '%d subtasks' => '%d å­ä»»å‹™', + '%d subtask' => '%d å­ä»»å‹™', + 'Only moving task between those columns is permitted for tasks assigned to the current user' => '僅å…許分é…給當å‰ä½¿ç”¨è€…的任務在這些列之間移動', + '[DUPLICATE]' => '[é‡è¤‡]', + 'DKK - Danish Krona' => 'DKK - 丹麥克朗', + 'Remove user from group' => '從群組中移除使用者', + 'Assign the task to its creator' => '將任務分é…给建立者', + 'This task was sent by email to "%s" with subject "%s".' => '此任務已通éŽé›»å­éƒµä»¶ç™¼é€çµ¦ "%s",主旨為 "%s"。', + 'Predefined Email Subjects' => 'é å®šç¾©çš„郵件主旨', + 'Write one subject by line.' => 'æ¯è¡Œå¯«ä¸€å€‹ä¸»æ—¨ã€‚', + 'Create another link' => '建立å¦ä¸€å€‹é€£çµ', + 'BRL - Brazilian Real' => 'BRL - 巴西雷亞爾', + 'Add a new Kanboard task' => '新增 Kanboard 任務', + 'Subtask not started' => 'å­ä»»å‹™æœªé–‹å§‹', + 'Subtask currently in progress' => 'å­ä»»å‹™ç›®å‰æ­£åœ¨é€²è¡Œä¸­', + 'Subtask completed' => 'å­ä»»å‹™å·²å®Œæˆ', + 'Subtask added successfully.' => 'å­ä»»å‹™å·²æˆåŠŸæ–°å¢žã€‚', + '%d subtasks added successfully.' => 'å·²æˆåŠŸæ–°å¢ž %d 個å­ä»»å‹™ã€‚', + 'Enter one subtask by line.' => 'æ¯è¡Œè¼¸å…¥ä¸€å€‹å­ä»»å‹™ã€‚', + 'Predefined Contents' => 'é å®šç¾©å…§å®¹', + 'Predefined contents' => 'é å®šç¾©å…§å®¹', + 'Predefined Task Description' => 'é å®šç¾©ä»»å‹™æè¿°', + 'Do you really want to remove this template? "%s"' => '您確定è¦ç§»é™¤æ­¤ç¯„本嗎? "%s"', + 'Add predefined task description' => '新增é å®šç¾©ä»»å‹™æè¿°', + 'Predefined Task Descriptions' => 'é å®šç¾©ä»»å‹™æè¿°', + 'Template created successfully.' => '範本已æˆåŠŸå»ºç«‹ã€‚', + 'Unable to create this template.' => '無法建立此範本。', + 'Template updated successfully.' => '範本已æˆåŠŸæ›´æ–°ã€‚', + 'Unable to update this template.' => '無法更新此範本。', + 'Template removed successfully.' => '範本已æˆåŠŸç§»é™¤ã€‚', + 'Unable to remove this template.' => '無法移除此範本。', + 'Template for the task description' => '任務æè¿°ç¯„本', + 'The start date is greater than the end date' => 'é–‹å§‹æ—¥æœŸæ™šæ–¼çµæŸæ—¥æœŸ', + 'Tags must be separated by a comma' => '標籤必須用逗號分隔', + 'Only the task title is required' => '僅任務標題為必填', + 'Creator Username' => '建立者使用者å稱', + 'Color Name' => 'é¡è‰²å稱', + 'Column Name' => '欄ä½å稱', + 'Swimlane Name' => 'æ³³é“å稱', + 'Time Estimated' => 'é è¨ˆæ™‚é–“', + 'Time Spent' => '花費時間', + 'External Link' => '外部連çµ', + 'This feature enables the iCal feed, RSS feed and the public board view.' => '此功能啟用 iCal feedã€RSS feed å’Œå…¬é–‹çœ‹æ¿æª¢è¦–。', + 'Stop the timer of all subtasks when moving a task to another column' => '將任務移動到å¦ä¸€åˆ—æ™‚åœæ­¢æ‰€æœ‰å­ä»»å‹™çš„計時器', + 'Subtask Title' => 'å­ä»»å‹™æ¨™é¡Œ', + 'Add a subtask and activate the timer when moving a task to another column' => '新增å­ä»»å‹™ä¸¦åœ¨å°‡ä»»å‹™ç§»å‹•到å¦ä¸€åˆ—時啟用計時器', + 'days' => '天', + 'minutes' => '分é˜', + 'seconds' => 'ç§’', + 'Assign automatically a color when preset start date is reached' => 'é”到é è¨­é–‹å§‹æ—¥æœŸæ™‚自動分é…é¡è‰²', + 'Move the task to another column once a predefined start date is reached' => 'é”到é å®šç¾©é–‹å§‹æ—¥æœŸå¾Œå°‡ä»»å‹™ç§»å‹•到å¦ä¸€åˆ—', + 'This task is now linked to the task %s with the relation "%s"' => '此任務ç¾å·²ä½¿ç”¨é—œä¿‚ "%s" 連çµåˆ°ä»»å‹™ %s', + 'The link with the relation "%s" to the task %s has been removed' => '與任務 %s 的關係 "%s" 的連çµå·²ç§»é™¤', + 'Custom Filter:' => '自訂篩é¸å™¨ï¼š', + 'Unable to find this group.' => '無法找到此群組。', + '%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 moved the task #%d to the swimlane "%s"' => '%s 將任務 #%d ç§»å‹•åˆ°æ³³é“ "%s"', + '%sh spent' => '已花費 %sh', + '%sh estimated' => 'é è¨ˆ %sh', + 'Select All' => 'å…¨é¸', + 'Unselect All' => 'å–æ¶ˆå…¨é¸', + 'Apply action' => '應用動作', + 'Move selected tasks to another column or swimlane' => 'å°‡é¸å®šçš„任務移動到å¦ä¸€åˆ—或泳é“', + 'Edit tasks in bulk' => '批é‡ç·¨è¼¯ä»»å‹™', + 'Choose the properties that you would like to change for the selected tasks.' => '鏿“‡è¦è®Šæ›´æ‰€é¸ä»»å‹™çš„屬性。', + 'Configure this project' => '設定此專案', + 'Start now' => 'ç«‹å³é–‹å§‹', + '%s removed a file from the task #%d' => '%s 從任務 #%d 移除了檔案', + 'Attachment removed from task #%d: %s' => '從任務 #%d 移除的附件:%s', + 'No color' => 'ç„¡é¡è‰²', + 'Attachment removed "%s"' => '附件 "%s" 已移除', + '%s removed a file from the task %s' => '%s 從任務 %s 移除了檔案', + 'Move the task to another swimlane when assigned to a user' => '將任務分é…給使用者時移動到å¦ä¸€å€‹æ³³é“', + 'Destination swimlane' => '目標泳é“', + 'Assign a category when the task is moved to a specific swimlane' => 'ç•¶ä»»å‹™ç§»å‹•åˆ°ç‰¹å®šæ³³é“æ™‚分é…類別', + 'Move the task to another swimlane when the category is changed' => '當類別變更時移動任務到å¦ä¸€å€‹æ³³é“', + 'Reorder this column by priority (ASC)' => 'æŒ‰å„ªå…ˆç´šé‡æŽ’æ­¤åˆ— (å‡åº)', + 'Reorder this column by priority (DESC)' => 'æŒ‰å„ªå…ˆç´šé‡æŽ’æ­¤åˆ— (é™åº)', + 'Reorder this column by assignee and priority (ASC)' => '按被分é…è€…å’Œå„ªå…ˆç´šé‡æŽ’æ­¤åˆ— (å‡åº)', + 'Reorder this column by assignee and priority (DESC)' => '按被分é…è€…å’Œå„ªå…ˆç´šé‡æŽ’æ­¤åˆ— (é™åº)', + 'Reorder this column by assignee (A-Z)' => '按被分é…者 (A-Z) é‡æŽ’æ­¤åˆ—', + 'Reorder this column by assignee (Z-A)' => '按被分é…者 (Z-A) é‡æŽ’æ­¤åˆ—', + 'Reorder this column by due date (ASC)' => 'æŒ‰æˆªæ­¢æ—¥æœŸé‡æŽ’æ­¤åˆ— (å‡åº)', + 'Reorder this column by due date (DESC)' => 'æŒ‰æˆªæ­¢æ—¥æœŸé‡æŽ’æ­¤åˆ— (é™åº)', + 'Reorder this column by id (ASC)' => '按 ID é‡æŽ’æ­¤åˆ— (å‡åº)', + 'Reorder this column by id (DESC)' => '按 ID é‡æŽ’æ­¤åˆ— (é™åº)', + '%s moved the task #%d "%s" to the project "%s"' => '%s 將任務 #%d "%s" 移動到專案 "%s"', + 'Task #%d "%s" has been moved to the project "%s"' => '任務 #%d "%s" 已移至專案 "%s"', + 'Move the task to another column when the due date is less than a certain number of days' => '當截止日期少於特定天數時將任務移動到å¦ä¸€åˆ—', + 'Automatically update the start date when the task is moved away from a specific column' => '當任務從特定欄ä½ç§»é–‹æ™‚,自動更新開始日期', + 'HTTP Client:' => 'HTTP 客戶端:', + 'Assigned' => '已分é…', + 'Task limits apply to each swimlane individually' => '任務é™åˆ¶å€‹åˆ¥æ‡‰ç”¨æ–¼æ¯å€‹æ³³é“', + 'Column task limits apply to each swimlane individually' => '欄ä½ä»»å‹™é™åˆ¶å€‹åˆ¥æ‡‰ç”¨æ–¼æ¯å€‹æ³³é“', + 'Column task limits are applied to each swimlane individually' => '欄ä½ä»»å‹™é™åˆ¶å€‹åˆ¥æ‡‰ç”¨æ–¼æ¯å€‹æ³³é“', + 'Column task limits are applied across swimlanes' => '欄ä½ä»»å‹™é™åˆ¶æ‡‰ç”¨æ–¼æ‰€æœ‰æ³³é“', + 'Task limit: ' => '任務é™åˆ¶ï¼š', + 'Change to global tag' => '變更為全域標籤', + 'Do you really want to make the tag "%s" global?' => '您確定è¦å°‡æ¨™ç±¤ "%s" 設為全域嗎?', + 'Enable global tags for this project' => '啟用此專案的全域標籤', + 'Group membership(s):' => '群組æˆå“¡ï¼š', + '%s is a member of the following group(s): %s' => '%s 是以下群組的æˆå“¡ï¼š%s', + '%d/%d group(s) shown' => '顯示 %d/%d 個群組', + 'Subtask creation or modification' => 'å­ä»»å‹™å»ºç«‹æˆ–修改', + 'Assign the task to a specific user when the task is moved to a specific swimlane' => 'å°‡ä»»å‹™ç§»å‹•åˆ°ç‰¹å®šæ³³é“æ™‚,將任務分é…給特定使用者', + 'Comment' => 'è©•è«–', + 'Collapse vertically' => '垂直收åˆ', + 'Expand vertically' => '垂直展開', + 'MXN - Mexican Peso' => 'MXN - 墨西哥比索', + 'Estimated vs actual time per column' => 'æ¯å€‹æ¬„ä½çš„é ä¼°æ™‚間與實際時間', + 'HUF - Hungarian Forint' => 'HUF - åŒˆç‰™åˆ©ç¦æž—', + 'XBT - Bitcoin' => 'XBT - 比特幣', + 'You must select a file to upload as your avatar!' => 'æ‚¨å¿…é ˆé¸æ“‡è¦ä¸Šå‚³çš„æª”案作為您的頭åƒï¼', + 'The file you uploaded is not a valid image! (Only *.gif, *.jpg, *.jpeg and *.png are allowed!)' => 'æ‚¨ä¸Šå‚³çš„æª”æ¡ˆä¸æ˜¯æœ‰æ•ˆçš„åœ–ç‰‡ï¼ (僅å…許 *.gif, *.jpg, *.jpeg å’Œ *.png)', + 'Automatically set the due date when the task is moved away from a specific column' => '當任務從特定欄ä½ç§»é–‹æ™‚,自動設定截止日期', + 'No other projects found.' => '找ä¸åˆ°å…¶ä»–專案。', + 'Tasks copied successfully.' => '任務已æˆåŠŸè¤‡è£½ã€‚', + 'Unable to copy tasks.' => '無法複製任務。', + 'Theme' => '主題', + 'Theme:' => '主題:', + 'Light theme' => '淺色主題', + 'Dark theme' => '深色主題', + 'Automatic theme - Sync with system' => '自動主題 - èˆ‡ç³»çµ±åŒæ­¥', + 'Application managers or more' => '應用程å¼ç®¡ç†å“¡æˆ–更高權é™', + 'Administrators' => '管ç†å“¡', + 'Visibility:' => 'å¯è¦‹æ€§ï¼š', + 'Standard users' => '標準使用者', + 'Visibility is required' => 'å¯è¦‹æ€§æ˜¯å¿…需的', + 'The visibility should be an app role' => 'å¯è¦‹æ€§æ‡‰è©²æ˜¯æ‡‰ç”¨ç¨‹å¼è§’色', + 'Reply' => '回覆', + '%s wrote: ' => '%s 寫é“:', + 'Number of visible tasks in this column and swimlane' => '此欄ä½å’Œæ³³é“中å¯è¦‹çš„任務數é‡', + 'Number of tasks in this swimlane' => '此泳é“中的任務數é‡', + 'Unable to find another subtask in progress, you can close this window.' => '找ä¸åˆ°å…¶ä»–正在進行的å­ä»»å‹™ï¼Œæ‚¨å¯ä»¥é—œé–‰æ­¤è¦–窗。', + 'This theme is invalid' => '此主題無效', + 'This role is invalid' => '此角色無效', + 'This timezone is invalid' => '此時å€ç„¡æ•ˆ', + 'This language is invalid' => '此語言無效', + 'This URL is invalid' => 'æ­¤ URL 無效', + 'Date format invalid' => '日期格å¼ç„¡æ•ˆ', + 'Time format invalid' => '時間格å¼ç„¡æ•ˆ', + 'Invalid Mail transport' => '無效的郵件傳輸', + 'Color invalid' => 'é¡è‰²ç„¡æ•ˆ', + 'This value must be greater or equal to %d' => '此值必須大於或等於 %d', + 'Add a BOM at the beginning of the file (required for Microsoft Excel)' => '在檔案開頭加入 BOM(Microsoft Excel 需è¦ï¼‰', + 'Just add these tag(s)' => 'åªéœ€åŠ å…¥é€™äº›æ¨™ç±¤', + 'Remove internal link(s)' => '移除內部連çµ', + 'Import tasks from another project' => '從其他專案匯入任務', + 'Select the project to copy tasks from' => '鏿“‡è¦è¤‡è£½ä»»å‹™çš„專案', + 'The total maximum allowed attachments size is %sB.' => 'å…許的附件總大å°ä¸Šé™ç‚º %sB。', + 'Add attachments' => '加入附件', + 'Task #%d "%s" is overdue' => '任務 #%d "%s" 已逾期', + 'Enable notifications by default for all new users' => 'é è¨­å•Ÿç”¨æ‰€æœ‰æ–°ä½¿ç”¨è€…的通知', + 'Assign the task to its creator for specific columns if no assignee is set manually' => '若未手動指定負責人,則在指定欄ä½ä¸­å°‡ä»»å‹™æŒ‡æ´¾çµ¦å»ºç«‹è€…', + 'Assign a task to the logged user on column change to specified column if no user is assigned' => '若未指派使用者,則在變更欄ä½ç§»å‹•åˆ°æŒ‡å®šæ¬„ä½æ™‚將任務指派給已登入使用者', +]; diff --git a/app/Middleware/ApplicationAuthorizationMiddleware.php b/app/Middleware/ApplicationAuthorizationMiddleware.php new file mode 100644 index 0000000..faca2d6 --- /dev/null +++ b/app/Middleware/ApplicationAuthorizationMiddleware.php @@ -0,0 +1,27 @@ +<?php + +namespace Kanboard\Middleware; + +use Kanboard\Core\Controller\AccessForbiddenException; +use Kanboard\Core\Controller\BaseMiddleware; + +/** + * Class ApplicationAuthorizationMiddleware + * + * @package Kanboard\Middleware + * @author Frederic Guillot + */ +class ApplicationAuthorizationMiddleware extends BaseMiddleware +{ + /** + * Execute middleware + */ + public function execute() + { + if (! $this->helper->user->hasAccess($this->router->getController(), $this->router->getAction())) { + throw new AccessForbiddenException(); + } + + $this->next(); + } +} diff --git a/app/Middleware/AuthenticationMiddleware.php b/app/Middleware/AuthenticationMiddleware.php new file mode 100644 index 0000000..18d0ec2 --- /dev/null +++ b/app/Middleware/AuthenticationMiddleware.php @@ -0,0 +1,60 @@ +<?php + +namespace Kanboard\Middleware; + +use Kanboard\Core\Controller\AccessForbiddenException; +use Kanboard\Core\Controller\BaseMiddleware; +use Kanboard\Core\Security\Role; + +/** + * Class AuthenticationMiddleware + * + * @package Kanboard\Middleware + * @author Frederic Guillot + */ +class AuthenticationMiddleware extends BaseMiddleware +{ + /** + * Execute middleware + */ + public function execute() + { + if (! $this->authenticationManager->checkCurrentSession()) { + $this->response->redirect($this->helper->url->to('AuthController', 'login')); + return; + } + + if (! $this->isPublicAccess()) { + $this->handleAuthentication(); + } + + $this->next(); + } + + protected function handleAuthentication() + { + if (! $this->userSession->isLogged() && ! $this->authenticationManager->preAuthentication()) { + $this->nextMiddleware = null; + + if ($this->request->isAjax()) { + $this->response->text('Not Authorized', 401); + } else { + $redirectURI = $this->request->getUri(); + if ($this->request->isSafeRedirectUri($redirectURI)) { + session_set('redirectAfterLogin', $redirectURI); + } + $this->response->redirect($this->helper->url->to('AuthController', 'login')); + } + } + } + + protected function isPublicAccess() + { + if ($this->applicationAuthorization->isAllowed($this->router->getController(), $this->router->getAction(), Role::APP_PUBLIC)) { + $this->nextMiddleware = null; + return true; + } + + return false; + } +} diff --git a/app/Middleware/BootstrapMiddleware.php b/app/Middleware/BootstrapMiddleware.php new file mode 100644 index 0000000..447cc8a --- /dev/null +++ b/app/Middleware/BootstrapMiddleware.php @@ -0,0 +1,46 @@ +<?php + +namespace Kanboard\Middleware; + +use Kanboard\Core\Controller\BaseMiddleware; +use Symfony\Contracts\EventDispatcher\Event; + +/** + * Class BootstrapMiddleware + * + * @package Kanboard\Middleware + * @author Frederic Guillot + */ +class BootstrapMiddleware extends BaseMiddleware +{ + /** + * Execute middleware + */ + public function execute() + { + $this->sessionManager->open(); + $this->dispatcher->dispatch(new Event, 'app.bootstrap'); + $this->sendHeaders(); + $this->next(); + } + + /** + * Send HTTP headers + * + * @access private + */ + private function sendHeaders() + { + $this->response->withContentSecurityPolicy($this->container['cspRules']); + $this->response->withSecurityHeaders(); + $this->response->withP3P(); + + if (ENABLE_XFRAME) { + $this->response->withXframe(); + } + + if (ENABLE_HSTS) { + $this->response->withStrictTransportSecurity(); + } + } +} diff --git a/app/Middleware/PostAuthenticationMiddleware.php b/app/Middleware/PostAuthenticationMiddleware.php new file mode 100644 index 0000000..8ad1f1a --- /dev/null +++ b/app/Middleware/PostAuthenticationMiddleware.php @@ -0,0 +1,36 @@ +<?php + +namespace Kanboard\Middleware; + +use Kanboard\Core\Controller\BaseMiddleware; + +/** + * Class PostAuthenticationMiddleware + * + * @package Kanboard\Middleware + * @author Frederic Guillot + */ +class PostAuthenticationMiddleware extends BaseMiddleware +{ + /** + * Execute middleware + */ + public function execute() + { + $controller = strtolower($this->router->getController()); + $action = strtolower($this->router->getAction()); + $ignore = ($controller === 'twofactorcontroller' && in_array($action, array('code', 'check'))) || ($controller === 'authcontroller' && $action === 'logout'); + + if ($ignore === false && $this->userSession->hasPostAuthentication() && ! $this->userSession->isPostAuthenticationValidated()) { + $this->nextMiddleware = null; + + if ($this->request->isAjax()) { + $this->response->text('Not Authorized', 401); + } else { + $this->response->redirect($this->helper->url->to('TwoFactorController', 'code')); + } + } + + $this->next(); + } +} diff --git a/app/Middleware/ProjectAuthorizationMiddleware.php b/app/Middleware/ProjectAuthorizationMiddleware.php new file mode 100644 index 0000000..704491b --- /dev/null +++ b/app/Middleware/ProjectAuthorizationMiddleware.php @@ -0,0 +1,34 @@ +<?php + +namespace Kanboard\Middleware; + +use Kanboard\Core\Controller\AccessForbiddenException; +use Kanboard\Core\Controller\BaseMiddleware; + +/** + * Class ProjectAuthorizationMiddleware + * + * @package Kanboard\Middleware + * @author Frederic Guillot + */ +class ProjectAuthorizationMiddleware extends BaseMiddleware +{ + /** + * Execute middleware + */ + public function execute() + { + $project_id = $this->request->getIntegerParam('project_id'); + $task_id = $this->request->getIntegerParam('task_id'); + + if ($task_id > 0 && $project_id === 0) { + $project_id = $this->taskFinderModel->getProjectId($task_id); + } + + if ($project_id > 0 && ! $this->helper->user->hasProjectAccess($this->router->getController(), $this->router->getAction(), $project_id)) { + throw new AccessForbiddenException(); + } + + $this->next(); + } +} diff --git a/app/Model/ActionModel.php b/app/Model/ActionModel.php new file mode 100644 index 0000000..b5d2bd0 --- /dev/null +++ b/app/Model/ActionModel.php @@ -0,0 +1,202 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Action Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ActionModel extends Base +{ + /** + * SQL table name for actions + * + * @var string + */ + const TABLE = 'actions'; + + /** + * Return actions and parameters for a given user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getAllByUser($user_id) + { + $project_ids = $this->projectPermissionModel->getActiveProjectIds($user_id); + $actions = array(); + + if (! empty($project_ids)) { + $actions = $this->db->table(self::TABLE)->in('project_id', $project_ids)->findAll(); + $params = $this->actionParameterModel->getAllByActions(array_column($actions, 'id')); + $this->attachParamsToActions($actions, $params); + } + + return $actions; + } + + /** + * Return actions and parameters for a given project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAllByProject($project_id) + { + $actions = $this->db->table(self::TABLE)->eq('project_id', $project_id)->findAll(); + $params = $this->actionParameterModel->getAllByActions(array_column($actions, 'id')); + return $this->attachParamsToActions($actions, $params); + } + + /** + * Return all actions and parameters + * + * @access public + * @return array + */ + public function getAll() + { + $actions = $this->db->table(self::TABLE)->findAll(); + $params = $this->actionParameterModel->getAll(); + return $this->attachParamsToActions($actions, $params); + } + + /** + * Fetch an action + * + * @access public + * @param integer $action_id + * @return array + */ + public function getById($action_id) + { + $action = $this->db->table(self::TABLE)->eq('id', $action_id)->findOne(); + + if (! empty($action)) { + $action['params'] = $this->actionParameterModel->getAllByAction($action_id); + } + + return $action; + } + + /** + * Get the projectId by the actionId + * + * @access public + * @param integer $action_id + * @return integer + */ + public function getProjectId($action_id) + { + return $this->db->table(self::TABLE)->eq('id', $action_id)->findOneColumn('project_id') ?: 0; + } + + /** + * Attach parameters to actions + * + * @access private + * @param array &$actions + * @param array &$params + * @return array + */ + private function attachParamsToActions(array &$actions, array &$params) + { + foreach ($actions as &$action) { + $action['params'] = isset($params[$action['id']]) ? $params[$action['id']] : array(); + } + + return $actions; + } + + /** + * Remove an action + * + * @access public + * @param integer $action_id + * @return bool + */ + public function remove($action_id) + { + return $this->db->table(self::TABLE)->eq('id', $action_id)->remove(); + } + + /** + * Create an action + * + * @access public + * @param array $values Required parameters to save an action + * @return boolean|integer + */ + public function create(array $values) + { + $this->db->startTransaction(); + + $action = array( + 'project_id' => $values['project_id'], + 'event_name' => $values['event_name'], + 'action_name' => $values['action_name'], + ); + + if (! $this->db->table(self::TABLE)->insert($action)) { + $this->db->cancelTransaction(); + return false; + } + + $action_id = $this->db->getLastId(); + + if (! $this->actionParameterModel->create($action_id, $values)) { + $this->db->cancelTransaction(); + return false; + } + + $this->db->closeTransaction(); + + return $action_id; + } + + /** + * Copy actions from a project to another one (skip actions that cannot resolve parameters) + * + * @author Antonio Rabelo + * @param integer $src_project_id Source project id + * @param integer $dst_project_id Destination project id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $actions = $this->actionModel->getAllByProject($src_project_id); + + foreach ($actions as $action) { + $this->db->startTransaction(); + + $values = array( + 'project_id' => $dst_project_id, + 'event_name' => $action['event_name'], + 'action_name' => $action['action_name'], + ); + + if (! $this->db->table(self::TABLE)->insert($values)) { + $this->db->cancelTransaction(); + continue; + } + + $action_id = $this->db->getLastId(); + + if (! $this->actionParameterModel->duplicateParameters($dst_project_id, $action_id, $action['params'])) { + $this->logger->error('Action::duplicate => skip action '.$action['action_name'].' '.$action['id']); + $this->db->cancelTransaction(); + continue; + } + + $this->db->closeTransaction(); + } + + return true; + } +} diff --git a/app/Model/ActionParameterModel.php b/app/Model/ActionParameterModel.php new file mode 100644 index 0000000..8df76a5 --- /dev/null +++ b/app/Model/ActionParameterModel.php @@ -0,0 +1,173 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Action Parameter Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ActionParameterModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'action_has_params'; + + /** + * Get all action params + * + * @access public + * @return array + */ + public function getAll() + { + $params = $this->db->table(self::TABLE)->findAll(); + return $this->toDictionary($params); + } + + /** + * Get all params for a list of actions + * + * @access public + * @param array $action_ids + * @return array + */ + public function getAllByActions(array $action_ids) + { + $params = $this->db->table(self::TABLE)->in('action_id', $action_ids)->findAll(); + return $this->toDictionary($params); + } + + /** + * Build params dictionary + * + * @access private + * @param array $params + * @return array + */ + private function toDictionary(array $params) + { + $result = array(); + + foreach ($params as $param) { + $result[$param['action_id']][$param['name']] = $param['value']; + } + + return $result; + } + + /** + * Get all action params for a given action + * + * @access public + * @param integer $action_id + * @return array + */ + public function getAllByAction($action_id) + { + return $this->db->hashtable(self::TABLE)->eq('action_id', $action_id)->getAll('name', 'value'); + } + + /** + * Insert new parameters for an action + * + * @access public + * @param integer $action_id + * @param array $values + * @return boolean + */ + public function create($action_id, array $values) + { + foreach ($values['params'] as $name => $value) { + $param = array( + 'action_id' => $action_id, + 'name' => $name, + 'value' => $value, + ); + + if (! $this->db->table(self::TABLE)->save($param)) { + return false; + } + } + + return true; + } + + /** + * Duplicate action parameters + * + * @access public + * @param integer $project_id + * @param integer $action_id + * @param array $params + * @return boolean + */ + public function duplicateParameters($project_id, $action_id, array $params) + { + foreach ($params as $name => $value) { + $value = $this->resolveParameter($project_id, $name, $value); + + if ($value === false) { + $this->logger->error('ActionParameter::duplicateParameters => unable to resolve '.$name.'='.$value); + return false; + } + + $values = array( + 'action_id' => $action_id, + 'name' => $name, + 'value' => $value, + ); + + if (! $this->db->table(self::TABLE)->insert($values)) { + return false; + } + } + + return true; + } + + /** + * Resolve action parameter values according to another project + * + * @access private + * @param integer $project_id + * @param string $name + * @param string $value + * @return mixed + */ + private function resolveParameter($project_id, $name, $value) + { + switch ($name) { + case 'project_id': + return $value != $project_id ? $value : false; + case 'category_id': + if ($value == 0) { + return 0; + } + return $this->categoryModel->getIdByName($project_id, $this->categoryModel->getNameById($value)) ?: false; + case 'src_column_id': + case 'dest_column_id': + case 'dst_column_id': + case 'column_id': + $column = $this->columnModel->getById($value); + return empty($column) ? false : ($this->columnModel->getColumnIdByTitle($project_id, $column['title']) ?: false); + case 'user_id': + case 'owner_id': + if ($value == 0) { + return 0; + } + 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/AvatarFileModel.php b/app/Model/AvatarFileModel.php new file mode 100644 index 0000000..923e639 --- /dev/null +++ b/app/Model/AvatarFileModel.php @@ -0,0 +1,159 @@ +<?php + +namespace Kanboard\Model; + +use Exception; +use Kanboard\Core\Base; + +/** + * Avatar File + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class AvatarFileModel extends Base +{ + /** + * Path prefix + * + * @var string + */ + const PATH_PREFIX = 'avatars'; + + /** + * Get image filename + * + * @access public + * @param integer $user_id + * @return string + */ + public function getFilename($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->findOneColumn('avatar_path'); + } + + /** + * Add avatar in the user profile + * + * @access public + * @param integer $user_id Foreign key + * @param string $path Path on the disk + * @return bool + */ + public function create($user_id, $path) + { + $result = $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array( + 'avatar_path' => $path, + )); + + $this->userSession->refresh($user_id); + + return $result; + } + + /** + * Remove avatar from the user profile + * + * @access public + * @param integer $user_id Foreign key + * @return bool + */ + public function remove($user_id) + { + try { + $filename = $this->getFilename($user_id); + + if (! empty($filename)) { + $this->objectStorage->remove($filename); + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array('avatar_path' => '')); + } + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + + return true; + } + + /** + * Upload avatar image file + * + * @access public + * @param integer $user_id + * @param array $file + * @return boolean + */ + public function uploadImageFile($user_id, array $file) + { + try { + if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { + $destinationFilename = $this->generatePath($user_id, $file['name']); + $this->objectStorage->moveUploadedFile($file['tmp_name'], $destinationFilename); + $this->create($user_id, $destinationFilename); + } else { + throw new Exception('File not uploaded: '.var_export($file['error'], true)); + } + + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + + return true; + } + + /** + * Upload avatar image content + * + * @access public + * @param integer $user_id + * @param string $blob + * @return boolean + */ + public function uploadImageContent($user_id, &$blob) + { + try { + $destinationFilename = $this->generatePath($user_id, 'imageContent'); + $this->objectStorage->put($destinationFilename, $blob); + $this->create($user_id, $destinationFilename); + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + + return true; + } + + /** + * Generate the path for a new filename + * + * @access public + * @param integer $user_id + * @param string $filename + * @return string + */ + public function generatePath($user_id, $filename) + { + return implode(DIRECTORY_SEPARATOR, array(self::PATH_PREFIX, $user_id, hash('sha1', $filename.time()))); + } + + /** + * Check if a filename is an image (file types that can be shown as avatar) + * + * @access public + * @param string $filename Filename + * @return bool + */ + public function isAvatarImage($filename) + { + switch (get_file_extension($filename)) { + case 'jpeg': + case 'jpg': + case 'png': + case 'gif': + return true; + } + + return false; + } +} diff --git a/app/Model/BoardModel.php b/app/Model/BoardModel.php new file mode 100644 index 0000000..ad590ff --- /dev/null +++ b/app/Model/BoardModel.php @@ -0,0 +1,97 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Board model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class BoardModel extends Base +{ + /** + * Get Kanboard default columns + * + * @access public + * @return string[] + */ + public function getDefaultColumns() + { + return array(t('Backlog'), t('Ready'), t('Work in progress'), t('Done')); + } + + /** + * Get user default columns + * + * @access public + * @return array + */ + public function getUserColumns() + { + $column_names = array_unique(explode_csv_field($this->configModel->get('board_columns', implode(',', $this->getDefaultColumns())))); + $columns = array(); + + foreach ($column_names as $column_name) { + $columns[] = array( + 'title' => $column_name, + 'task_limit' => 0, + 'description' => '', + 'hide_in_dashboard' => 0, + ); + } + + return $columns; + } + + /** + * Create a board with default columns, must be executed inside a transaction + * + * @access public + * @param integer $project_id Project id + * @param array $columns Column parameters [ 'title' => 'boo', 'task_limit' => 2 ... ] + * @return boolean + */ + public function create($project_id, array $columns) + { + $position = 0; + + foreach ($columns as $column) { + $values = array( + 'title' => $column['title'], + 'position' => ++$position, + 'project_id' => $project_id, + 'task_limit' => $column['task_limit'], + 'description' => $column['description'], + 'hide_in_dashboard' => $column['hide_in_dashboard'] ?: 0, // Avoid SQL error with Postgres + ); + + if (! $this->db->table(ColumnModel::TABLE)->save($values)) { + return false; + } + } + + return true; + } + + /** + * Copy board columns from a project to another one + * + * @author Antonio Rabelo + * @param integer $project_from Project Template + * @param integer $project_to Project that receives the copy + * @return boolean + */ + public function duplicate($project_from, $project_to) + { + $columns = $this->db->table(ColumnModel::TABLE) + ->columns('title', 'task_limit', 'description', 'hide_in_dashboard') + ->eq('project_id', $project_from) + ->asc('position') + ->findAll(); + + return $this->boardModel->create($project_to, $columns); + } +} diff --git a/app/Model/CaptchaModel.php b/app/Model/CaptchaModel.php new file mode 100644 index 0000000..b9e39f1 --- /dev/null +++ b/app/Model/CaptchaModel.php @@ -0,0 +1,63 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Captcha model + * + * @package Kanboard\Model + * + * {"<IP_ADDRESS>": {"failed_login": <COUNT>, "expiration_date": <TIMESTAMP>}} + */ +class CaptchaModel extends Base +{ + public function incrementFailedLogin($ipAddress) + { + $data = $this->getCaptchaData(); + if (!isset($data[$ipAddress])) { + $data[$ipAddress] = ['failed_login' => 0, 'expiration_date' => 0]; + } + + $data[$ipAddress]['failed_login']++; + if ($data[$ipAddress]['failed_login'] >= BRUTEFORCE_CAPTCHA) { + $data[$ipAddress]['lock_expiration_date'] = time() + BRUTEFORCE_LOCKDOWN_DURATION; + } + + $this->setCaptchaData($data); + } + + public function resetFailedLogin($ipAddress) + { + $data = $this->getCaptchaData(); + if (isset($data[$ipAddress])) { + unset($data[$ipAddress]); + $this->setCaptchaData($data); + } + } + + public function isLocked($ipAddress) + { + $data = $this->getCaptchaData(); + if (isset($data[$ipAddress]) && isset($data[$ipAddress]['lock_expiration_date'])) { + return $data[$ipAddress]['lock_expiration_date'] > time(); + } + return false; + } + + protected function getCaptchaData() + { + $rawData = $this->configModel->getOption('captcha_data', '{}'); + $data = json_decode($rawData, true); + if (!is_array($data)) { + $data = []; + } + return $data; + } + + protected function setCaptchaData(array $data) + { + $this->configModel->save(['captcha_data' => json_encode($data)]); + } +} diff --git a/app/Model/CategoryModel.php b/app/Model/CategoryModel.php new file mode 100644 index 0000000..798c8a9 --- /dev/null +++ b/app/Model/CategoryModel.php @@ -0,0 +1,228 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Category model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class CategoryModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_has_categories'; + + /** + * Return true if a category exists for a given project + * + * @access public + * @param integer $category_id Category id + * @return boolean + */ + public function exists($category_id) + { + return $this->db->table(self::TABLE)->eq('id', $category_id)->exists(); + } + + /** + * Get a category by the id + * + * @access public + * @param integer $category_id Category id + * @return array + */ + public function getById($category_id) + { + return $this->db->table(self::TABLE)->eq('id', $category_id)->findOne(); + } + + /** + * Get the category name by the id + * + * @access public + * @param integer $category_id Category id + * @return string + */ + public function getNameById($category_id) + { + return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('name') ?: ''; + } + + /** + * Get the projectId by the category id + * + * @access public + * @param integer $category_id Category id + * @return integer + */ + public function getProjectId($category_id) + { + return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('project_id') ?: 0; + } + + /** + * Get a category id by the category name and project id + * + * @access public + * @param integer $project_id Project id + * @param string $category_name Category name + * @return integer + */ + public function getIdByName($project_id, $category_name) + { + return (int) $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('name', $category_name) + ->findOneColumn('id'); + } + + /** + * Return the list of all categories + * + * @access public + * @param integer $project_id Project id + * @param bool $prepend_none If true, prepend to the list the value 'None' + * @param bool $prepend_all If true, prepend to the list the value 'All' + * @return array + */ + public function getList($project_id, $prepend_none = true, $prepend_all = false) + { + $listing = $this->db->hashtable(self::TABLE) + ->eq('project_id', $project_id) + ->asc('name') + ->getAll('id', 'name'); + + $prepend = array(); + + if ($prepend_all) { + $prepend[-1] = t('All categories'); + } + + if ($prepend_none) { + $prepend[0] = t('No category'); + } + + return $prepend + $listing; + } + + /** + * Return all categories for a given project + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAll($project_id) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->asc('name') + ->findAll(); + } + + /** + * Create default categories during project creation (transaction already started in Project::create()) + * + * @access public + * @param integer $project_id + * @return boolean + */ + public function createDefaultCategories($project_id) + { + $results = array(); + $categories = array_unique(explode_csv_field($this->configModel->get('project_categories'))); + + foreach ($categories as $category) { + $results[] = $this->db->table(self::TABLE)->insert(array( + 'project_id' => $project_id, + 'name' => $category, + )); + } + + return in_array(false, $results, true); + } + + /** + * Create a category (run inside a transaction) + * + * @access public + * @param array $values Form values + * @return bool|integer + */ + public function create(array $values) + { + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Update a category + * + * @access public + * @param array $values Form values + * @return bool + */ + public function update(array $values) + { + $updates = $values; + unset($updates['id']); + return $this->db->table(self::TABLE)->eq('id', $values['id'])->save($updates); + } + + /** + * Remove a category + * + * @access public + * @param integer $category_id Category id + * @return bool + */ + public function remove($category_id) + { + $this->db->startTransaction(); + + $this->db->table(TaskModel::TABLE)->eq('category_id', $category_id)->update(array('category_id' => 0)); + + if (! $this->db->table(self::TABLE)->eq('id', $category_id)->remove()) { + $this->db->cancelTransaction(); + return false; + } + + $this->db->closeTransaction(); + + return true; + } + + /** + * Duplicate categories from a project to another one, must be executed inside a transaction + * + * @author Antonio Rabelo + * @param integer $src_project_id Source project id + * @param integer $dst_project_id Destination project id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $categories = $this->db + ->table(self::TABLE) + ->columns('name', 'description', 'color_id') + ->eq('project_id', $src_project_id) + ->asc('name') + ->findAll(); + + foreach ($categories as $category) { + $category['project_id'] = $dst_project_id; + + if (! $this->db->table(self::TABLE)->save($category)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ColorModel.php b/app/Model/ColorModel.php new file mode 100644 index 0000000..13566fb --- /dev/null +++ b/app/Model/ColorModel.php @@ -0,0 +1,231 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Color model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ColorModel extends Base +{ + /** + * Default colors + * + * @access protected + * @var array + */ + protected $default_colors = array( + 'yellow' => array( + 'name' => 'Yellow', + 'background' => 'rgb(245, 247, 196)', + 'border' => 'rgb(223, 227, 45)', + ), + 'blue' => array( + 'name' => 'Blue', + 'background' => 'rgb(219, 235, 255)', + 'border' => 'rgb(168, 207, 255)', + ), + 'green' => array( + 'name' => 'Green', + 'background' => 'rgb(189, 244, 203)', + 'border' => 'rgb(74, 227, 113)', + ), + 'purple' => array( + 'name' => 'Purple', + 'background' => 'rgb(223, 176, 255)', + 'border' => 'rgb(205, 133, 254)', + ), + 'red' => array( + 'name' => 'Red', + 'background' => 'rgb(255, 187, 187)', + 'border' => 'rgb(255, 151, 151)', + ), + 'orange' => array( + 'name' => 'Orange', + 'background' => 'rgb(255, 215, 179)', + 'border' => 'rgb(255, 172, 98)', + ), + 'grey' => array( + 'name' => 'Grey', + 'background' => 'rgb(238, 238, 238)', + 'border' => 'rgb(204, 204, 204)', + ), + 'brown' => array( + 'name' => 'Brown', + 'background' => '#d7ccc8', + 'border' => '#4e342e', + ), + 'deep_orange' => array( + 'name' => 'Deep Orange', + 'background' => '#ffab91', + 'border' => '#e64a19', + ), + 'dark_grey' => array( + 'name' => 'Dark Grey', + 'background' => '#cfd8dc', + 'border' => '#455a64', + ), + 'pink' => array( + 'name' => 'Pink', + 'background' => '#f48fb1', + 'border' => '#d81b60', + ), + 'teal' => array( + 'name' => 'Teal', + 'background' => '#80cbc4', + 'border' => '#00695c', + ), + 'cyan' => array( + 'name' => 'Cyan', + 'background' => '#b2ebf2', + 'border' => '#00bcd4', + ), + 'lime' => array( + 'name' => 'Lime', + 'background' => '#e6ee9c', + 'border' => '#afb42b', + ), + 'light_green' => array( + 'name' => 'Light Green', + 'background' => '#dcedc8', + 'border' => '#689f38', + ), + 'amber' => array( + 'name' => 'Amber', + 'background' => '#ffe082', + 'border' => '#ffa000', + ), + ); + + /** + * Find a color id from the name or the id + * + * @access public + * @param string $color + * @return string + */ + public function find($color) + { + $color = strtolower($color); + + foreach ($this->default_colors as $color_id => $params) { + if ($color_id === $color) { + return $color_id; + } elseif ($color === strtolower($params['name'])) { + return $color_id; + } + } + + return ''; + } + + /** + * Get color properties + * + * @access public + * @param string $color_id + * @return array + */ + public function getColorProperties($color_id) + { + if (isset($this->default_colors[$color_id])) { + return $this->default_colors[$color_id]; + } + + return $this->default_colors[$this->getDefaultColor()]; + } + + /** + * Get available colors + * + * @access public + * @param bool $prepend + * @return array + */ + public function getList($prepend = false) + { + $listing = $prepend ? array('' => t('All colors')) : array(); + + foreach ($this->default_colors as $color_id => $color) { + $listing[$color_id] = t($color['name']); + } + + $this->hook->reference('model:color:get-list', $listing); + + return $listing; + } + + /** + * Get the default color + * + * @access public + * @return string + */ + public function getDefaultColor() + { + return $this->configModel->get('default_color', 'yellow'); + } + + /** + * Get the default colors + * + * @access public + * @return array + */ + public function getDefaultColors() + { + return $this->default_colors; + } + + /** + * Get border color from string + * + * @access public + * @param string $color_id Color id + * @return string + */ + public function getBorderColor($color_id) + { + $color = $this->getColorProperties($color_id); + return $color['border']; + } + + /** + * Get background color from the color_id + * + * @access public + * @param string $color_id Color id + * @return string + */ + public function getBackgroundColor($color_id) + { + $color = $this->getColorProperties($color_id); + return $color['background']; + } + + /** + * Get CSS stylesheet of all colors + * + * @access public + * @return string + */ + public function getCss() + { + $buffer = ''; + + foreach ($this->default_colors as $color => $values) { + $buffer .= '.task-board.color-'.$color.', .task-summary-container.color-'.$color.', .color-picker-square.color-'.$color.', .task-board-category.color-'.$color.', .table-list-category.color-'.$color.', .task-tag.color-'.$color.' {'; + $buffer .= 'background-color: '.$values['background'].';'; + $buffer .= 'border-color: '.$values['border']; + $buffer .= '}'; + $buffer .= 'td.color-'.$color.' { background-color: '.$values['background'].'}'; + $buffer .= '.table-list-row.color-'.$color.' {border-left: 5px solid '.$values['border'].'}'; + } + + return $buffer; + } +} diff --git a/app/Model/ColumnModel.php b/app/Model/ColumnModel.php new file mode 100644 index 0000000..36bff5e --- /dev/null +++ b/app/Model/ColumnModel.php @@ -0,0 +1,262 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Column Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ColumnModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'columns'; + + /** + * Get a column by the id + * + * @access public + * @param integer $column_id Column id + * @return array + */ + public function getById($column_id) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->findOne(); + } + + /** + * Get projectId by the columnId + * + * @access public + * @param integer $column_id Column id + * @return integer + */ + public function getProjectId($column_id) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('project_id'); + } + + /** + * Get the first column id for a given project + * + * @access public + * @param integer $project_id Project id + * @return integer + */ + public function getFirstColumnId($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findOneColumn('id'); + } + + /** + * Get the last column id for a given project + * + * @access public + * @param integer $project_id Project id + * @return integer + */ + public function getLastColumnId($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->desc('position')->findOneColumn('id'); + } + + /** + * Get the position of the last column for a given project + * + * @access public + * @param integer $project_id Project id + * @return integer + */ + public function getLastColumnPosition($project_id) + { + return (int) $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->desc('position') + ->findOneColumn('position'); + } + + /** + * Get a column id by the name + * + * @access public + * @param integer $project_id + * @param string $title + * @return integer + */ + public function getColumnIdByTitle($project_id, $title) + { + return (int) $this->db->table(self::TABLE)->eq('project_id', $project_id)->eq('title', $title)->findOneColumn('id'); + } + + /** + * Get a column title by the id + * + * @access public + * @param integer $column_id + * @return integer + */ + public function getColumnTitleById($column_id) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('title'); + } + + /** + * Get all columns sorted by position for a given project + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAll($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findAll(); + } + + /** + * Get all columns with opened task count only + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAllWithOpenedTaskCount($project_id) + { + return $this->db->table(self::TABLE) + ->columns('id', 'title', 'position', 'task_limit', 'description', 'hide_in_dashboard', 'project_id') + ->subquery("SELECT COUNT(*) FROM ".TaskModel::TABLE." WHERE column_id=".self::TABLE.".id AND is_active='1'", 'nb_open_tasks') + ->eq('project_id', $project_id) + ->asc('position') + ->findAll(); + } + + /** + * Get all columns with task count + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAllWithTaskCount($project_id) + { + return $this->db->table(self::TABLE) + ->columns('id', 'title', 'position', 'task_limit', 'description', 'hide_in_dashboard', 'project_id') + ->subquery("SELECT COUNT(*) FROM ".TaskModel::TABLE." WHERE column_id=".self::TABLE.".id AND is_active='1'", 'nb_open_tasks') + ->subquery("SELECT COUNT(*) FROM ".TaskModel::TABLE." WHERE column_id=".self::TABLE.".id AND is_active='0'", 'nb_closed_tasks') + ->eq('project_id', $project_id) + ->asc('position') + ->findAll(); + } + + /** + * Get the list of columns sorted by position [ column_id => title ] + * + * @access public + * @param integer $project_id Project id + * @param boolean $prepend Prepend a default value + * @return array + */ + public function getList($project_id, $prepend = false) + { + $listing = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->asc('position')->getAll('id', 'title'); + return $prepend ? array(-1 => t('All columns')) + $listing : $listing; + } + + /** + * 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 + * @param integer $hide_in_dashboard + * @return bool|int + */ + public function create($project_id, $title, $task_limit = 0, $description = '', $hide_in_dashboard = 0) + { + $values = array( + 'project_id' => $project_id, + 'title' => $title, + 'task_limit' => intval($task_limit), + 'position' => $this->getLastColumnPosition($project_id) + 1, + 'hide_in_dashboard' => $hide_in_dashboard, + 'description' => $description, + ); + + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Update a column + * + * @access public + * @param integer $column_id Column id + * @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) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->update(array( + 'title' => $title, + 'task_limit' => intval($task_limit), + 'hide_in_dashboard' => $hide_in_dashboard, + 'description' => $description, + )); + } + + /** + * Remove a column and all tasks associated to this column + * + * @access public + * @param integer $column_id Column id + * @return boolean + */ + public function remove($column_id) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->remove(); + } + + /** + * Change column position + * + * @access public + * @param integer $project_id + * @param integer $column_id + * @param integer $position + * @return boolean + */ + public function changePosition($project_id, $column_id, $position) + { + if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('project_id', $project_id)->count()) { + return false; + } + + $column_ids = $this->db->table(self::TABLE)->eq('project_id', $project_id)->neq('id', $column_id)->asc('position')->findAllByColumn('id'); + $offset = 1; + $results = array(); + + foreach ($column_ids as $current_column_id) { + if ($offset == $position) { + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $current_column_id)->update(array('position' => $offset)); + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $column_id)->eq('project_id', $project_id)->update(array('position' => $position)); + + return !in_array(false, $results, true); + } +} diff --git a/app/Model/ColumnMoveRestrictionModel.php b/app/Model/ColumnMoveRestrictionModel.php new file mode 100644 index 0000000..473b2a7 --- /dev/null +++ b/app/Model/ColumnMoveRestrictionModel.php @@ -0,0 +1,174 @@ +<?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', + self::TABLE.'.only_assigned', + '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', + self::TABLE.'.only_assigned', + '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', self::TABLE.'.only_assigned') + ->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 + * @param bool $only_assigned + * @return bool|int + */ + public function create($project_id, $role_id, $src_column_id, $dst_column_id, $only_assigned = false) + { + 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, + 'only_assigned' => (int) $only_assigned, + )); + } + + /** + * 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(); + } + + /** + * Copy column_move_restriction models from a custome_role in the src project to the dst custom_role of the dst project + * + * @param integer $project_src_id + * @param integer $project_dst_id + * @param integer $role_src_id + * @param integer $role_dst_id + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id, $role_src_id, $role_dst_id) + { + $rows = $this->db->table(self::TABLE) + ->eq('project_id', $project_src_id) + ->eq('role_id', $role_src_id) + ->findAll(); + + foreach ($rows as $row) { + $src_column_title = $this->columnModel->getColumnTitleById($row['src_column_id']); + $dst_column_title = $this->columnModel->getColumnTitleById($row['dst_column_id']); + $src_column_id = $this->columnModel->getColumnIdByTitle($project_dst_id, $src_column_title); + $dst_column_id = $this->columnModel->getColumnIdByTitle($project_dst_id, $dst_column_title); + + if (! $dst_column_id) { + $this->logger->error("The column $dst_column_title is not present in project $project_dst_id"); + return false; + } + + if (! $src_column_id) { + $this->logger->error("The column $src_column_title is not present in project $project_dst_id"); + return false; + } + + $result = $this->db->table(self::TABLE)->persist(array( + 'project_id' => $project_dst_id, + 'role_id' => $role_dst_id, + 'src_column_id' => $src_column_id, + 'dst_column_id' => $dst_column_id, + 'only_assigned' => (int) $row['only_assigned'], + )); + + if (! $result) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ColumnRestrictionModel.php b/app/Model/ColumnRestrictionModel.php new file mode 100644 index 0000000..4023b48 --- /dev/null +++ b/app/Model/ColumnRestrictionModel.php @@ -0,0 +1,192 @@ +<?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( + 'restriction_id', + 'project_id', + 'role_id', + 'column_id', + '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( + 'restriction_id', + 'project_id', + 'role_id', + 'column_id', + '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( + 'restriction_id', + 'project_id', + 'role_id', + 'column_id', + '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(); + } + + /** + * Copy column_restriction models from a custome_role in the src project to the dst custom_role of the dst project + * + * @param integer $project_src_id + * @param integer $project_dst_id + * @param integer $role_src_id + * @param integer $role_dst_id + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id, $role_src_id, $role_dst_id) + { + $rows = $this->db->table(self::TABLE) + ->eq('project_id', $project_src_id) + ->eq('role_id', $role_src_id) + ->findAll(); + + foreach ($rows as $row) { + $column_title = $this->columnModel->getColumnTitleById($row['column_id']); + $dst_column_id = $this->columnModel->getColumnIdByTitle($project_dst_id, $column_title); + + if (! $dst_column_id) { + $this->logger->error("The column $column_title is not present in project $project_dst_id"); + return false; + } + + $result = $this->db->table(self::TABLE)->persist(array( + 'project_id' => $project_dst_id, + 'role_id' => $role_dst_id, + 'column_id' => $dst_column_id, + 'rule' => $row['rule'], + )); + + if (! $result) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/CommentModel.php b/app/Model/CommentModel.php new file mode 100644 index 0000000..299ad77 --- /dev/null +++ b/app/Model/CommentModel.php @@ -0,0 +1,222 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Security\Role; + +/** + * Comment model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class CommentModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'comments'; + + /** + * Events + * + * @var string + */ + const EVENT_UPDATE = 'comment.update'; + const EVENT_CREATE = 'comment.create'; + const EVENT_DELETE = 'comment.delete'; + const EVENT_USER_MENTION = 'comment.user.mention'; + + /** + * Get projectId from commentId + * + * @access public + * @param integer $comment_id + * @return integer + */ + public function getProjectId($comment_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $comment_id) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** + * Get visibility from commentId + * + * @access public + * @param integer $comment_id + * @return string + */ + public function getVisibility($comment_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $comment_id) + ->findOneColumn(self::TABLE . '.visibility') ?: Role::APP_USER; + } + + /** + * Get all comments for a given task + * + * @access public + * @param integer $task_id Task id + * @param string $sorting ASC/DESC + * @return array + */ + public function getAll($task_id, $sorting = 'ASC') + { + return $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.id', + self::TABLE.'.date_creation', + self::TABLE.'.date_modification', + self::TABLE.'.task_id', + self::TABLE.'.user_id', + self::TABLE.'.comment', + self::TABLE.'.visibility', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + UserModel::TABLE.'.avatar_path' + ) + ->join(UserModel::TABLE, 'id', 'user_id') + ->orderBy(self::TABLE.'.date_creation', $sorting) + ->orderBy(self::TABLE.'.id', $sorting) + ->eq(self::TABLE.'.task_id', $task_id) + ->findAll(); + } + + /** + * Get a comment + * + * @access public + * @param integer $comment_id Comment id + * @return array + */ + public function getById($comment_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.id', + self::TABLE.'.task_id', + self::TABLE.'.user_id', + self::TABLE.'.date_creation', + self::TABLE.'.date_modification', + self::TABLE.'.comment', + self::TABLE.'.reference', + self::TABLE.'.visibility', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + UserModel::TABLE.'.avatar_path' + ) + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq(self::TABLE.'.id', $comment_id) + ->findOne(); + } + + /** + * Get the number of comments for a given task + * + * @access public + * @param integer $task_id Task id + * @return integer + */ + public function count($task_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.task_id', $task_id) + ->count(); + } + + /** + * Create a new comment + * + * @access public + * @param array $values Form values + * @return boolean|integer + */ + public function create(array $values) + { + $values = $this->clampVisibility($values); + $values['date_creation'] = time(); + $values['date_modification'] = time(); + $comment_id = $this->db->table(self::TABLE)->persist($values); + + if ($comment_id !== false) { + $this->queueManager->push($this->commentEventJob->withParams($comment_id, self::EVENT_CREATE)); + } + + return $comment_id; + } + + /** + * Update a comment in the database + * + * @access public + * @param array $values Form values + * @return boolean + */ + public function update(array $values) + { + $result = $this->db + ->table(self::TABLE) + ->eq('id', $values['id']) + ->update(array('comment' => $values['comment'], 'date_modification' => time())); + + if ($result) { + $this->queueManager->push($this->commentEventJob->withParams($values['id'], self::EVENT_UPDATE)); + } + + return $result; + } + + /** + * Clamp the visibility field so it never exceeds the current user's role + * + * @access protected + * @param array $values + * @return array + */ + protected function clampVisibility(array $values) + { + if (! $this->userSession->isLogged()) { + return $values; + } + + $visibility = isset($values['visibility']) ? $values['visibility'] : Role::APP_USER; + $userRole = $this->userSession->getRole(); + + if ($userRole === Role::APP_MANAGER && $visibility === Role::APP_ADMIN) { + $values['visibility'] = Role::APP_MANAGER; + } + + if ($userRole === Role::APP_USER && $visibility !== Role::APP_USER) { + $values['visibility'] = Role::APP_USER; + } + + return $values; + } + + /** + * Remove a comment + * + * @access public + * @param integer $comment_id Comment id + * @return boolean + */ + 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/ConfigModel.php b/app/Model/ConfigModel.php new file mode 100644 index 0000000..232932b --- /dev/null +++ b/app/Model/ConfigModel.php @@ -0,0 +1,122 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Security\Token; + +/** + * Config model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ConfigModel extends SettingModel +{ + /** + * Get a config variable with in-memory caching + * + * @access public + * @param string $name Parameter name + * @param string $default_value Default value of the parameter + * @return string + */ + public function get($name, $default_value = '') + { + $options = $this->memoryCache->proxy($this, 'getAll'); + return isset($options[$name]) && $options[$name] !== '' ? $options[$name] : $default_value; + } + + /** + * Optimize the Sqlite database + * + * @access public + * @return boolean + */ + public function optimizeDatabase() + { + return $this->db->getConnection()->exec('VACUUM'); + } + + /** + * Compress the Sqlite database + * + * @access public + * @return string + */ + public function downloadDatabase() + { + return gzencode(file_get_contents(DB_FILENAME)); + } + + /** + * Replace database file with uploaded one + * + * @access public + * @param string $file + * @return bool + */ + public function uploadDatabase($file) + { + $this->db->closeConnection(); + return file_put_contents(DB_FILENAME, gzdecode(file_get_contents($file))) !== false; + } + + /** + * Get the Sqlite database size in bytes + * + * @access public + * @return integer + */ + public function getDatabaseSize() + { + return DB_DRIVER === 'sqlite' ? filesize(DB_FILENAME) : 0; + } + + /** + * Get database extra options + * + * @access public + * @return array + */ + public function getDatabaseOptions() + { + if (DB_DRIVER === 'sqlite') { + return [ + 'journal_mode' => $this->db->getConnection()->query('PRAGMA journal_mode')->fetchColumn(), + 'wal_autocheckpoint' => $this->db->getConnection()->query('PRAGMA wal_autocheckpoint')->fetchColumn(), + 'synchronous' => $this->db->getConnection()->query('PRAGMA synchronous')->fetchColumn(), + 'busy_timeout' => $this->db->getConnection()->query('PRAGMA busy_timeout')->fetchColumn(), + ]; + } + + return []; + } + + /** + * Regenerate a token + * + * @access public + * @param string $option Parameter name + * @return boolean + */ + public function regenerateToken($option) + { + return $this->save(array($option => Token::getToken())); + } + + /** + * Prepare data before save + * + * @access public + * @param array $values + * @return array + */ + public function prepare(array $values) + { + if (! empty($values['application_url']) && substr($values['application_url'], -1) !== '/') { + $values['application_url'] = $values['application_url'].'/'; + } + + return $values; + } +} diff --git a/app/Model/CurrencyModel.php b/app/Model/CurrencyModel.php new file mode 100644 index 0000000..be1d3f6 --- /dev/null +++ b/app/Model/CurrencyModel.php @@ -0,0 +1,123 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Currency + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class CurrencyModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'currencies'; + + /** + * Get available application currencies + * + * @access public + * @return array + */ + public function getCurrencies() + { + return array( + 'ARS' => t('ARS - Argentine Peso'), + 'AUD' => t('AUD - Australian Dollar'), + 'BAM' => t('BAM - Konvertible Mark'), + 'BRL' => t('BRL - Brazilian Real'), + 'CAD' => t('CAD - Canadian Dollar'), + 'CHF' => t('CHF - Swiss Francs'), + 'CNY' => t('CNY - Chinese Yuan'), + 'COP' => t('COP - Colombian Peso'), + 'DKK' => t('DKK - Danish Krona'), + 'EUR' => t('EUR - Euro'), + 'GBP' => t('GBP - British Pound'), + 'HRK' => t('HRK - Kuna'), + 'HUF' => t('HUF - Hungarian Forint'), + 'INR' => t('INR - Indian Rupee'), + 'JPY' => t('JPY - Japanese Yen'), + 'MXN' => t('MXN - Mexican Peso'), + 'NOK' => t('NOK - Norwegian Krone'), + 'NZD' => t('NZD - New Zealand Dollar'), + 'PEN' => t('PEN - Peruvian Sol'), + 'RSD' => t('RSD - Serbian dinar'), + 'RUB' => t('RUB - Russian Ruble'), + 'SEK' => t('SEK - Swedish Krona'), + 'TRL' => t('TRL - Turkish Lira'), + 'USD' => t('USD - US Dollar'), + 'VBL' => t('VES - Venezuelan Bolívar'), + 'XBT' => t('XBT - Bitcoin'), + ); + } + + /** + * Get all currency rates + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->table(self::TABLE)->findAll(); + } + + /** + * Calculate the price for the reference currency + * + * @access public + * @param string $currency + * @param double $price + * @return double + */ + public function getPrice($currency, $price) + { + static $rates = null; + $reference = $this->configModel->get('application_currency', 'USD'); + + if ($reference !== $currency) { + $rates = $rates === null ? $this->db->hashtable(self::TABLE)->getAll('currency', 'rate') : $rates; + $rate = isset($rates[$currency]) ? $rates[$currency] : 1; + + return $rate * $price; + } + + return $price; + } + + /** + * Add a new currency rate + * + * @access public + * @param string $currency + * @param float $rate + * @return boolean|integer + */ + public function create($currency, $rate) + { + if ($this->db->table(self::TABLE)->eq('currency', $currency)->exists()) { + return $this->update($currency, $rate); + } + + return $this->db->table(self::TABLE)->insert(array('currency' => $currency, 'rate' => $rate)); + } + + /** + * Update a currency rate + * + * @access public + * @param string $currency + * @param float $rate + * @return boolean + */ + public function update($currency, $rate) + { + return $this->db->table(self::TABLE)->eq('currency', $currency)->update(array('rate' => $rate)); + } +} diff --git a/app/Model/CustomFilterModel.php b/app/Model/CustomFilterModel.php new file mode 100644 index 0000000..a9c5aae --- /dev/null +++ b/app/Model/CustomFilterModel.php @@ -0,0 +1,141 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Custom Filter model + * + * @package Kanboard\Model + * @author Timo Litzbarski + */ +class CustomFilterModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'custom_filters'; + + /** + * Return the list of all allowed custom filters for a user and project + * + * @access public + * @param integer $project_id Project id + * @param integer $user_id User id + * @return array + */ + public function getAll($project_id, $user_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + UserModel::TABLE.'.name as owner_name', + UserModel::TABLE.'.username as owner_username', + self::TABLE.'.id', + self::TABLE.'.user_id', + self::TABLE.'.project_id', + self::TABLE.'.filter', + self::TABLE.'.name', + self::TABLE.'.is_shared', + self::TABLE.'.append' + ) + ->asc(self::TABLE.'.name') + ->join(UserModel::TABLE, 'id', 'user_id') + ->beginOr() + ->eq('is_shared', 1) + ->eq('user_id', $user_id) + ->closeOr() + ->eq('project_id', $project_id) + ->findAll(); + } + + /** + * Get custom filter by id + * + * @access private + * @param integer $filter_id + * @return array + */ + public function getById($filter_id) + { + return $this->db->table(self::TABLE)->eq('id', $filter_id)->findOne(); + } + + /** + * Create a custom filter + * + * @access public + * @param array $values Form values + * @return bool|integer + */ + public function create(array $values) + { + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Update a custom filter + * + * @access public + * @param array $values Form values + * @return bool + */ + public function update(array $values) + { + $updates = $values; + unset($updates['id']); + return $this->db->table(self::TABLE) + ->eq('id', $values['id']) + ->update($updates); + } + + /** + * Remove a custom filter + * + * @access public + * @param integer $filter_id + * @return bool + */ + public function remove($filter_id) + { + return $this->db->table(self::TABLE)->eq('id', $filter_id)->remove(); + } + + /** + * Duplicate custom filters from a project to another one, must be executed inside a transaction + * + * @param integer $src_project_id Source project id + * @param integer $dst_project_id Destination project id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $filters = $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.user_id', + self::TABLE.'.filter', + self::TABLE.'.name', + self::TABLE.'.is_shared', + self::TABLE.'.append' + ) + ->eq('project_id', $src_project_id) + ->findAll(); + + foreach ($filters as $filter) { + $filter['project_id'] = $dst_project_id; + // Avoid SQL error with Postgres + $filter['is_shared'] = $filter['is_shared'] ?: 0; + $filter['append'] = $filter['append'] ?: 0; + + if (! $this->db->table(self::TABLE)->save($filter)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/FileModel.php b/app/Model/FileModel.php new file mode 100644 index 0000000..122728c --- /dev/null +++ b/app/Model/FileModel.php @@ -0,0 +1,416 @@ +<?php + +namespace Kanboard\Model; + +use Exception; +use Kanboard\Core\Base; +use Kanboard\Core\Thumbnail; +use Kanboard\Core\ObjectStorage\ObjectStorageException; + +/** + * Base File Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +abstract class FileModel extends Base +{ + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getTable(); + + /** + * Define the foreign key + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getForeignKey(); + + /** + * Get the path prefix + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getPathPrefix(); + + /** + * Fire file creation event + * + * @abstract + * @access protected + * @param integer $file_id + */ + abstract protected function fireCreationEvent($file_id); + + /** + * Fire file destruction event + * + * @abstract + * @access protected + * @param integer $file_id + */ + abstract protected function fireDestructionEvent($file_id); + + /** + * Get PicoDb query to get all files + * + * @access protected + * @return \PicoDb\Table + */ + protected function getQuery() + { + return $this->db + ->table($this->getTable()) + ->columns( + $this->getTable().'.id', + $this->getTable().'.name', + $this->getTable().'.path', + $this->getTable().'.is_image', + $this->getTable().'.'.$this->getForeignKey(), + $this->getTable().'.date', + $this->getTable().'.user_id', + $this->getTable().'.size', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name as user_name' + ) + ->join(UserModel::TABLE, 'id', 'user_id') + ->asc($this->getTable().'.name'); + } + + /** + * Get a file by the id + * + * @access public + * @param integer $file_id File id + * @return array + */ + public function getById($file_id) + { + $file = $this->db->table($this->getTable())->eq('id', $file_id)->findOne(); + if ($file) { + $file['etag'] = md5($file['path']); + } + return $file; + } + + /** + * Get all files + * + * @access public + * @param integer $id + * @return array + */ + public function getAll($id) + { + $files = $this->getQuery()->eq($this->getForeignKey(), $id)->findAll(); + foreach ($files as &$file) { + $file['etag'] = md5($file['path']); + } + return $files; + } + + /** + * Get all images + * + * @access public + * @param integer $id + * @return array + */ + public function getAllImages($id) + { + $images = $this->getQuery()->eq($this->getForeignKey(), $id)->eq('is_image', 1)->findAll(); + foreach ($images as &$image) { + $image['etag'] = md5($image['path']); + } + return $images; + } + + /** + * Get all files without images + * + * @access public + * @param integer $id + * @return array + */ + public function getAllDocuments($id) + { + $files = $this->getQuery()->eq($this->getForeignKey(), $id)->eq('is_image', 0)->findAll(); + foreach ($files as &$file) { + $file['etag'] = md5($file['path']); + } + return $files; + } + + /** + * Create a file entry in the database + * + * @access public + * @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($foreign_key_id, $name, $path, $size) + { + $values = array( + $this->getForeignKey() => $foreign_key_id, + 'name' => substr($name, 0, 255), + 'path' => $path, + 'is_image' => $this->isImage($name) ? 1 : 0, + 'size' => $size, + 'user_id' => $this->userSession->getId() ?: 0, + 'date' => time(), + ); + + $result = $this->db->table($this->getTable())->insert($values); + + if ($result) { + $file_id = (int) $this->db->getLastId(); + $this->fireCreationEvent($file_id); + return $file_id; + } + + return false; + } + + /** + * Remove all files + * + * @access public + * @param integer $id + * @return bool + */ + public function removeAll($id) + { + $file_ids = $this->db->table($this->getTable())->eq($this->getForeignKey(), $id)->asc('id')->findAllByColumn('id'); + $results = array(); + + foreach ($file_ids as $file_id) { + $results[] = $this->remove($file_id); + } + + return ! in_array(false, $results, true); + } + + /** + * Remove a file + * + * @access public + * @param integer $file_id File id + * @return bool + */ + public function remove($file_id) + { + try { + $this->fireDestructionEvent($file_id); + + $file = $this->getById($file_id); + + // Only remove files from disk attached to a single task. + $multiple_tasks_count = $this->db->table($this->getTable())->eq('path', $file['path'])->count(); + if ($multiple_tasks_count === 1) { + $this->objectStorage->remove($file['path']); + + if ($file['is_image'] == 1) { + $this->objectStorage->remove($this->getThumbnailPath($file['path'])); + } + } + + return $this->db->table($this->getTable())->eq('id', $file['id'])->remove(); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Check if a filename is an image (file types that can be shown as thumbnail) + * + * @access public + * @param string $filename Filename + * @return bool + */ + public function isImage($filename) + { + switch (get_file_extension($filename)) { + case 'jpeg': + case 'jpg': + case 'png': + case 'gif': + return true; + } + + return false; + } + + /** + * Generate the path for a thumbnails + * + * @access public + * @param string $key Storage key + * @return string + */ + public function getThumbnailPath($key) + { + return 'thumbnails'.DIRECTORY_SEPARATOR.$key; + } + + /** + * Generate the path for a new filename + * + * @access public + * @param integer $id Foreign key + * @param string $filename Filename + * @return string + */ + public function generatePath($id, $filename) + { + if (is_string($id)) { + $id = (int) $id; + } + if (! is_int($id) || $id <= 0) { + throw new Exception('Invalid ID provided for file path generation'); + } + return $this->getPathPrefix().DIRECTORY_SEPARATOR.$id.DIRECTORY_SEPARATOR.hash('sha1', $filename.time()); + } + + /** + * Upload multiple files + * + * @access public + * @param integer $id + * @param array $files + * @return bool + */ + public function uploadFiles($id, array $files) + { + try { + if (empty($files)) { + return false; + } + + foreach (array_keys($files['error']) as $key) { + $file = array( + 'name' => $files['name'][$key], + 'tmp_name' => $files['tmp_name'][$key], + 'size' => $files['size'][$key], + 'error' => $files['error'][$key], + ); + + $this->uploadFile($id, $file); + } + + return true; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Upload a file + * + * @access public + * @param integer $id + * @param array $file + * @throws Exception + */ + public function uploadFile($id, array $file) + { + if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { + $destination_filename = $this->generatePath($id, $file['name']); + + if ($this->isImage($file['name'])) { + $this->generateThumbnailFromFile($file['tmp_name'], $destination_filename); + } + + $this->objectStorage->moveUploadedFile($file['tmp_name'], $destination_filename); + $this->create($id, $file['name'], $destination_filename, $file['size']); + } else { + throw new Exception('File not uploaded: '.var_export($file['error'], true)); + } + } + + /** + * Handle file upload (base64 encoded content) + * + * @access public + * @param integer $id + * @param string $originalFilename + * @param string $data + * @param bool $isEncoded + * @return bool|int + */ + public function uploadContent($id, $originalFilename, $data, $isEncoded = true) + { + try { + if ($isEncoded) { + $data = base64_decode($data); + } + + if (empty($data)) { + $this->logger->error(__METHOD__.': Content upload with no data'); + return false; + } + + $destinationFilename = $this->generatePath($id, $originalFilename); + $this->objectStorage->put($destinationFilename, $data); + + if ($this->isImage($originalFilename)) { + $this->generateThumbnailFromData($destinationFilename, $data); + } + + return $this->create( + $id, + $originalFilename, + $destinationFilename, + strlen($data) + ); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Generate thumbnail from a blob + * + * @access public + * @param string $destination_filename + * @param string $data + */ + public function generateThumbnailFromData($destination_filename, &$data) + { + $blob = Thumbnail::createFromString($data) + ->resize() + ->toString(); + + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); + } + + /** + * Generate thumbnail from a local file + * + * @access public + * @param string $uploaded_filename + * @param string $destination_filename + */ + public function generateThumbnailFromFile($uploaded_filename, $destination_filename) + { + $blob = Thumbnail::createFromFile($uploaded_filename) + ->resize() + ->toString(); + + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); + } +} diff --git a/app/Model/GroupMemberModel.php b/app/Model/GroupMemberModel.php new file mode 100644 index 0000000..ff24777 --- /dev/null +++ b/app/Model/GroupMemberModel.php @@ -0,0 +1,131 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Group Member Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class GroupMemberModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'group_has_users'; + + /** + * Get query to fetch all users + * + * @access public + * @param integer $group_id + * @return \PicoDb\Table + */ + public function getQuery($group_id) + { + return $this->db->table(self::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq('group_id', $group_id); + } + + /** + * Get all users + * + * @access public + * @param integer $group_id + * @return array + */ + public function getMembers($group_id) + { + return $this->getQuery($group_id)->findAll(); + } + + /** + * Get all not members + * + * @access public + * @param integer $group_id + * @return array + */ + public function getNotMembers($group_id) + { + $subquery = $this->db->table(self::TABLE) + ->columns('user_id') + ->eq('group_id', $group_id); + + return $this->db->table(UserModel::TABLE) + ->notInSubquery('id', $subquery) + ->eq('is_active', 1) + ->findAll(); + } + + /** + * Add user to a group + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function addUser($group_id, $user_id) + { + return $this->db->table(self::TABLE)->insert(array( + 'group_id' => $group_id, + 'user_id' => $user_id, + )); + } + + /** + * Remove user from a group + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function removeUser($group_id, $user_id) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('user_id', $user_id) + ->remove(); + } + + /** + * Check if a user is member + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function isMember($group_id, $user_id) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('user_id', $user_id) + ->exists(); + } + + /** + * Get all groups for a given user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getGroups($user_id) + { + return $this->db->table(self::TABLE) + ->columns(GroupModel::TABLE.'.id', GroupModel::TABLE.'.external_id', GroupModel::TABLE.'.name') + ->join(GroupModel::TABLE, 'id', 'group_id') + ->eq(self::TABLE.'.user_id', $user_id) + ->asc(GroupModel::TABLE.'.name') + ->findAll(); + } +} diff --git a/app/Model/GroupModel.php b/app/Model/GroupModel.php new file mode 100644 index 0000000..6fd9c53 --- /dev/null +++ b/app/Model/GroupModel.php @@ -0,0 +1,158 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Group Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class GroupModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'groups'; + + /** + * Get query to fetch all groups + * + * @access public + * @return \PicoDb\Table + */ + public function getQuery() + { + return $this->db->table(self::TABLE) + ->columns('id', 'name', 'external_id') + ->subquery('SELECT COUNT(*) FROM '.GroupMemberModel::TABLE.' WHERE group_id='.self::TABLE.'.id', 'nb_users'); + } + + /** + * Get a specific group by id + * + * @access public + * @param integer $group_id + * @return array + */ + public function getById($group_id) + { + return $this->db->table(self::TABLE)->eq('id', $group_id)->findOne(); + } + + /** + * Get a specific group by externalID + * + * @access public + * @param string $external_id + * @return array + */ + public function getByExternalId($external_id) + { + return $this->db->table(self::TABLE)->eq('external_id', $external_id)->findOne(); + } + + /** + * Get specific groups by externalIDs + * + * @access public + * @param string[] $external_ids + * @return array + */ + public function getByExternalIds(array $external_ids) + { + if (empty($external_ids)) { + return []; + } + + return $this->db->table(self::TABLE)->in('external_id', $external_ids)->findAll(); + } + + /** + * Get all groups + * + * @access public + * @return array + */ + public function getAll() + { + return $this->getQuery()->asc('name')->findAll(); + } + + /** + * Search groups by name + * + * @access public + * @param string $input + * @return array + */ + public function search($input) + { + return $this->db->table(self::TABLE)->ilike('name', '%'.$input.'%')->asc('name')->findAll(); + } + + /** + * Remove a group + * + * @access public + * @param integer $group_id + * @return boolean + */ + public function remove($group_id) + { + return $this->db->table(self::TABLE)->eq('id', $group_id)->remove(); + } + + /** + * Create a new group + * + * @access public + * @param string $name + * @param string $external_id + * @return integer|boolean + */ + public function create($name, $external_id = '') + { + return $this->db->table(self::TABLE)->persist(array( + 'name' => $name, + 'external_id' => $external_id, + )); + } + + /** + * Update existing group + * + * @access public + * @param array $values + * @return boolean + */ + public function update(array $values) + { + $updates = $values; + unset($updates['id']); + return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($updates); + } + + /** + * 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 0000000..13d75f6 --- /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/LanguageModel.php b/app/Model/LanguageModel.php new file mode 100644 index 0000000..74c900b --- /dev/null +++ b/app/Model/LanguageModel.php @@ -0,0 +1,237 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Translator; + +/** + * Class Language + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class LanguageModel extends Base +{ + /** + * Get all language codes + * + * @static + * @access public + * @return string[] + */ + public static function getCodes() + { + return array( + 'id_ID', + 'bg_BG', + 'bs_BA', + 'ca_ES', + 'cs_CZ', + 'da_DK', + 'de_DE', + 'de_DE_du', + 'en_GB', + 'en_US', + 'es_ES', + 'es_VE', + 'fr_FR', + 'el_GR', + 'it_IT', + 'hr_HR', + 'hu_HU', + 'mk_MK', + 'my_MY', + 'nl_NL', + 'nb_NO', + 'pl_PL', + 'pt_PT', + 'pt_BR', + 'ro_RO', + 'ru_RU', + 'sr_Latn_RS', + 'fi_FI', + 'sk_SK', + 'sv_SE', + 'tr_TR', + 'uk_UA', + 'ko_KR', + 'zh_CN', + 'zh_TW', + 'ja_JP', + 'th_TH', + 'vi_VN', + 'fa_IR', + 'ar_SY', + ); + } + + /** + * Find language code + * + * @static + * @access public + * @param string $code + * @return string + */ + public static function findCode($code) + { + $code = str_replace('-', '_', $code); + return in_array($code, self::getCodes()) ? $code : ''; + } + + /** + * Get available languages + * + * @access public + * @param boolean $prepend Prepend a default value + * @return array + */ + public function getLanguages($prepend = false) + { + // Sorted by value + $languages = array( + 'id_ID' => 'Bahasa Indonesia', + 'bg_BG' => 'БългарÑки', + 'bs_BA' => 'Bosanski', + 'ca_ES' => 'Català', + 'cs_CZ' => 'ÄŒeÅ¡tina', + 'da_DK' => 'Dansk', + 'de_DE' => 'Deutsch (Sie)', + 'de_DE_du' => 'Deutsch (du)', + 'en_GB' => 'English (GB)', + 'en_US' => 'English (US)', + 'es_ES' => 'Español (España)', + 'es_VE' => 'Español (Venezuela)', + 'fr_FR' => 'Français', + 'el_GR' => 'Greek (Ελληνικά)', + 'hr_HR' => 'Hrvatski', + 'it_IT' => 'Italiano', + 'hu_HU' => 'Magyar', + 'mk_MK' => 'МакедонÑки', + 'my_MY' => 'Melayu', + 'nl_NL' => 'Nederlands', + 'nb_NO' => 'Norsk', + 'pl_PL' => 'Polski', + 'pt_PT' => 'Português', + 'pt_BR' => 'Português (Brasil)', + 'ro_RO' => 'Română', + 'ru_RU' => 'РуÑÑкий', + 'sr_Latn_RS' => 'Srpski', + 'fi_FI' => 'Suomi', + 'sk_SK' => 'SlovenÄina', + 'sv_SE' => 'Svenska', + 'tr_TR' => 'Türkçe', + 'uk_UA' => 'УкраїнÑька', + 'ko_KR' => '한국어', + 'zh_CN' => '中文(简体)', + 'zh_TW' => '中文(ç¹é«”)', + 'ja_JP' => '日本語', + 'th_TH' => 'ไทย', + 'vi_VN' => 'Tiếng Việt', + 'fa_IR' => 'ÙØ§Ø±Ø³ÛŒ', + 'ar_SY' => 'عربي', + ); + + if ($prepend) { + return array('' => t('Application default')) + $languages; + } + + return $languages; + } + + /** + * Get javascript language code + * + * @access public + * @return string + */ + public function getJsLanguageCode() + { + $languages = array( + 'bg_BG' => 'bg', + 'cs_CZ' => 'cs', + 'ca_ES' => 'ca', + 'da_DK' => 'da', + 'de_DE' => 'de', + 'de_DE_du' => 'de', + 'en_GB' => 'en-GB', + 'en_US' => 'en', + 'es_ES' => 'es', + 'es_VE' => 'es', + 'fr_FR' => 'fr', + 'it_IT' => 'it', + 'hr_HR' => 'hr', + 'hu_HU' => 'hu', + 'nl_NL' => 'nl', + 'nb_NO' => 'no', + 'pl_PL' => 'pl', + 'pt_PT' => 'pt', + 'pt_BR' => 'pt-BR', + 'ro_RO' => 'ro', + 'ru_RU' => 'ru', + 'sr_Latn_RS' => 'sr', + 'fi_FI' => 'fi', + 'sk_SK' => 'sk', + 'sv_SE' => 'sv', + 'tr_TR' => 'tr', + 'uk_UA' => 'uk', + 'ko_KR' => 'ko', + 'zh_CN' => 'zh-CN', + 'zh_TW' => 'zh-TW', + 'ja_JP' => 'ja', + 'th_TH' => 'th', + 'id_ID' => 'id', + 'el_GR' => 'el', + 'fa_IR' => 'fa', + 'vi_VN' => 'vi', + 'bs_BA' => 'bs', + 'mk_MK' => 'mk', + 'my_MY' => 'my', + 'ar_SY' => 'ar', + ); + + $lang = $this->getCurrentLanguage(); + + return isset($languages[$lang]) ? $languages[$lang] : 'en'; + } + + /** + * Check if current language requires RTL direction + * + * @access public + * @return bool + */ + public function isRtlLanguage() + { + $rtlJsLanguageCodes = array( + 'ar', + 'fa', + ); + + $lang = $this->getJsLanguageCode(); + + return in_array($lang, $rtlJsLanguageCodes); + } + + /** + * Get current language + * + * @access public + * @return string + */ + public function getCurrentLanguage() + { + return $this->userSession->getLanguage() ?: $this->configModel->get('application_language', 'en_US'); + } + + /** + * Load translations for the current language + * + * @access public + */ + public function loadCurrentLanguage() + { + Translator::load($this->getCurrentLanguage()); + } +} diff --git a/app/Model/LastLoginModel.php b/app/Model/LastLoginModel.php new file mode 100644 index 0000000..caaa4a8 --- /dev/null +++ b/app/Model/LastLoginModel.php @@ -0,0 +1,92 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * LastLogin model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class LastLoginModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'last_logins'; + + /** + * Number of connections to keep for history + * + * @var integer + */ + const NB_LOGINS = 10; + + /** + * Create a new record + * + * @access public + * @param string $auth_type Authentication method + * @param integer $user_id User id + * @param string $ip IP Address + * @param string $user_agent User Agent + * @return boolean + */ + public function create($auth_type, $user_id, $ip, $user_agent) + { + $this->cleanup($user_id); + + return $this->db + ->table(self::TABLE) + ->insert(array( + 'auth_type' => $auth_type, + 'user_id' => $user_id, + 'ip' => $ip, + 'user_agent' => substr($user_agent, 0, 255), + 'date_creation' => time(), + )); + } + + /** + * Cleanup login history + * + * @access public + * @param integer $user_id + */ + public function cleanup($user_id) + { + $connections = $this->db + ->table(self::TABLE) + ->eq('user_id', $user_id) + ->desc('id') + ->findAllByColumn('id'); + + if (count($connections) >= self::NB_LOGINS) { + $this->db->table(self::TABLE) + ->eq('user_id', $user_id) + ->notIn('id', array_slice($connections, 0, self::NB_LOGINS - 1)) + ->remove(); + } + } + + /** + * Get the last connections for a given user + * + * @access public + * @param integer $user_id User id + * @return array + */ + public function getAll($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('user_id', $user_id) + ->desc('id') + ->columns('id', 'auth_type', 'ip', 'user_agent', 'date_creation') + ->findAll(); + } +} diff --git a/app/Model/LinkModel.php b/app/Model/LinkModel.php new file mode 100644 index 0000000..b72c753 --- /dev/null +++ b/app/Model/LinkModel.php @@ -0,0 +1,178 @@ +<?php + +namespace Kanboard\Model; + +use PDO; +use Kanboard\Core\Base; + +/** + * Link model + * + * @package Kanboard\Model + * @author Olivier Maridat + * @author Frederic Guillot + */ +class LinkModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'links'; + + /** + * Get a link by id + * + * @access public + * @param integer $link_id Link id + * @return array + */ + public function getById($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne(); + } + + /** + * Get a link by name + * + * @access public + * @param string $label + * @return array + */ + public function getByLabel($label) + { + return $this->db->table(self::TABLE)->eq('label', $label)->findOne(); + } + + /** + * Get the opposite link id + * + * @access public + * @param integer $link_id Link id + * @return integer + */ + public function getOppositeLinkId($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->findOneColumn('opposite_id') ?: $link_id; + } + + /** + * Get all links + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->table(self::TABLE)->findAll(); + } + + /** + * Get merged links + * + * @access public + * @return array + */ + public function getMergedList() + { + return $this->db + ->execute(' + SELECT + links.id, links.label, opposite.label as opposite_label + FROM links + LEFT JOIN links AS opposite ON opposite.id=links.opposite_id + ') + ->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Get label list + * + * @access public + * @param integer $exclude_id Exclude this link + * @param boolean $prepend Prepend default value + * @return array + */ + public function getList($exclude_id = 0, $prepend = true) + { + $labels = $this->db->hashtable(self::TABLE)->neq('id', $exclude_id)->asc('id')->getAll('id', 'label'); + + foreach ($labels as &$value) { + $value = t($value); + } + + return $prepend ? array('') + $labels : $labels; + } + + /** + * Create a new link label + * + * @access public + * @param string $label + * @param string $opposite_label + * @return boolean|integer + */ + public function create($label, $opposite_label = '') + { + $this->db->startTransaction(); + + if (! $this->db->table(self::TABLE)->insert(array('label' => $label))) { + $this->db->cancelTransaction(); + return false; + } + + $label_id = $this->db->getLastId(); + + if (! empty($opposite_label)) { + $this->db + ->table(self::TABLE) + ->insert(array( + 'label' => $opposite_label, + 'opposite_id' => $label_id, + )); + + $this->db + ->table(self::TABLE) + ->eq('id', $label_id) + ->update(array( + 'opposite_id' => $this->db->getLastId() + )); + } + + $this->db->closeTransaction(); + + return (int) $label_id; + } + + /** + * Update a link + * + * @access public + * @param array $values + * @return boolean + */ + public function update(array $values) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $values['id']) + ->update(array( + 'label' => $values['label'], + 'opposite_id' => $values['opposite_id'], + )); + } + + /** + * Remove a link a the relation to its opposite + * + * @access public + * @param integer $link_id + * @return boolean + */ + public function remove($link_id) + { + $this->db->table(self::TABLE)->eq('opposite_id', $link_id)->update(array('opposite_id' => 0)); + return $this->db->table(self::TABLE)->eq('id', $link_id)->remove(); + } +} diff --git a/app/Model/MetadataModel.php b/app/Model/MetadataModel.php new file mode 100644 index 0000000..0ab9409 --- /dev/null +++ b/app/Model/MetadataModel.php @@ -0,0 +1,140 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Metadata + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +abstract class MetadataModel extends Base +{ + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getTable(); + + /** + * Define the entity key + * + * @abstract + * @access protected + * @return string + */ + abstract protected function getEntityKey(); + + /** + * Get all metadata for the entity + * + * @access public + * @param integer $entity_id + * @return array + */ + public function getAll($entity_id) + { + return $this->db + ->hashtable($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->asc('name') + ->getAll('name', 'value'); + } + + /** + * Get a metadata for the given entity + * + * @access public + * @param integer $entity_id + * @param string $name + * @param string $default + * @return mixed + */ + public function get($entity_id, $name, $default = '') + { + return $this->db + ->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $name) + ->findOneColumn('value') ?: $default; + } + + /** + * Return true if a metadata exists + * + * @access public + * @param integer $entity_id + * @param string $name + * @return boolean + */ + public function exists($entity_id, $name) + { + return $this->db + ->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $name) + ->exists(); + } + + /** + * Update or insert new metadata + * + * @access public + * @param integer $entity_id + * @param array $values + * @return boolean + */ + public function save($entity_id, array $values) + { + $results = array(); + $user_id = $this->userSession->getId(); + $timestamp = time(); + + $this->db->startTransaction(); + + foreach ($values as $key => $value) { + if ($this->exists($entity_id, $key)) { + $results[] = $this->db->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $key) + ->update(array( + 'value' => $value, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } else { + $results[] = $this->db->table($this->getTable())->insert(array( + 'name' => $key, + 'value' => $value, + $this->getEntityKey() => $entity_id, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } + } + + $this->db->closeTransaction(); + return ! in_array(false, $results, true); + } + + /** + * Remove a metadata + * + * @access public + * @param integer $entity_id + * @param string $name + * @return bool + */ + public function remove($entity_id, $name) + { + return $this->db->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $name) + ->remove(); + } +} diff --git a/app/Model/NotificationModel.php b/app/Model/NotificationModel.php new file mode 100644 index 0000000..803d4f1 --- /dev/null +++ b/app/Model/NotificationModel.php @@ -0,0 +1,100 @@ +<?php + +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 Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class NotificationModel extends Base +{ + /** + * Get the event title with author + * + * @access public + * @param string $eventAuthor + * @param string $eventName + * @param array $eventData + * @return string + */ + public function getTitleWithAuthor($eventAuthor, $eventName, array $eventData) + { + foreach ($this->getIteratorBuilder() as $builder) { + $title = $builder->buildTitleWithAuthor($eventAuthor, $eventName, $eventData); + + if ($title !== '') { + return $title; + } + } + + return e('Notification'); + } + + /** + * Get the event title without author + * + * @access public + * @param string $eventName + * @param array $eventData + * @return string + */ + public function getTitleWithoutAuthor($eventName, array $eventData) + { + foreach ($this->getIteratorBuilder() as $builder) { + $title = $builder->buildTitleWithoutAuthor($eventName, $eventData); + + if ($title !== '') { + return $title; + } + } + + return e('Notification'); + } + + /** + * Get task id from event + * + * @access public + * @param string $eventName + * @param array $eventData + * @return integer + */ + public function getTaskIdFromEvent($eventName, array $eventData) + { + if ($eventName === TaskModel::EVENT_OVERDUE) { + return $eventData['tasks'][0]['id']; + } + + return isset($eventData['task']['id']) ? $eventData['task']['id'] : 0; + } + + /** + * Get iterator builder + * + * @access protected + * @return EventIteratorBuilder + */ + protected function getIteratorBuilder() + { + $iterator = new EventIteratorBuilder(); + $iterator + ->withBuilder(TaskEventBuilder::getInstance($this->container)) + ->withBuilder(CommentEventBuilder::getInstance($this->container)) + ->withBuilder(SubtaskEventBuilder::getInstance($this->container)) + ->withBuilder(TaskFileEventBuilder::getInstance($this->container)) + ->withBuilder(TaskLinkEventBuilder::getInstance($this->container)) + ; + + return $iterator; + } +} diff --git a/app/Model/NotificationTypeModel.php b/app/Model/NotificationTypeModel.php new file mode 100644 index 0000000..432832e --- /dev/null +++ b/app/Model/NotificationTypeModel.php @@ -0,0 +1,128 @@ +<?php + +namespace Kanboard\Model; + +use Pimple\Container; +use Kanboard\Core\Base; + +/** + * Notification Type + * + * @package model + * @author Frederic Guillot + */ +abstract class NotificationTypeModel extends Base +{ + /** + * Container + * + * @access private + * @var \Pimple\Container + */ + private $classes; + + /** + * Notification type labels + * + * @access private + * @var array + */ + private $labels = array(); + + /** + * Hidden notification types + * + * @access private + * @var array + */ + private $hiddens = array(); + + /** + * Constructor + * + * @access public + * @param \Pimple\Container $container + */ + public function __construct(Container $container) + { + parent::__construct($container); + $this->classes = new Container; + } + + /** + * Add a new notification type + * + * @access public + * @param string $type + * @param string $label + * @param string $class + * @param boolean $hidden + * @return NotificationTypeModel + */ + public function setType($type, $label, $class, $hidden = false) + { + $container = $this->container; + + if ($hidden) { + $this->hiddens[] = $type; + } else { + $this->labels[$type] = $label; + } + + $this->classes[$type] = function () use ($class, $container) { + return new $class($container); + }; + + return $this; + } + + /** + * Get mail notification type instance + * + * @access public + * @param string $type + * @return \Kanboard\Core\Notification\NotificationInterface + */ + public function getType($type) + { + return $this->classes[$type]; + } + + /** + * Get all notification types with labels + * + * @access public + * @return array + */ + public function getTypes() + { + return $this->labels; + } + + /** + * Get all hidden notification types + * + * @access public + * @return array + */ + public function getHiddenTypes() + { + return $this->hiddens; + } + + /** + * Keep only loaded notification types + * + * @access public + * @param string[] $types + * @return array + */ + public function filterTypes(array $types) + { + $classes = $this->classes; + + return array_filter($types, function ($type) use ($classes) { + return isset($classes[$type]); + }); + } +} diff --git a/app/Model/PasswordResetModel.php b/app/Model/PasswordResetModel.php new file mode 100644 index 0000000..d7c7496 --- /dev/null +++ b/app/Model/PasswordResetModel.php @@ -0,0 +1,95 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Password Reset Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class PasswordResetModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'password_reset'; + + /** + * Token duration (30 minutes) + * + * @var integer + */ + const DURATION = 1800; + + /** + * Get all tokens + * + * @access public + * @param integer $user_id + * @return array + */ + public function getAll($user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_creation')->limit(100)->findAll(); + } + + /** + * Generate a new reset token for a user + * + * @access public + * @param string $username + * @param integer $expiration + * @return boolean|string + */ + public function create($username, $expiration = 0) + { + $user_id = $this->db->table(UserModel::TABLE)->eq('username', $username)->neq('email', '')->notNull('email')->findOneColumn('id'); + + if (! $user_id) { + return false; + } + + $token = $this->token->getToken(); + + $result = $this->db->table(self::TABLE)->insert(array( + 'token' => $token, + 'user_id' => $user_id, + 'date_expiration' => $expiration ?: time() + self::DURATION, + 'date_creation' => time(), + 'ip' => $this->request->getIpAddress(), + 'user_agent' => $this->request->getUserAgent(), + 'is_active' => 1, + )); + + return $result ? $token : false; + } + + /** + * Get user id from the token + * + * @access public + * @param string $token + * @return integer + */ + public function getUserIdByToken($token) + { + return $this->db->table(self::TABLE)->eq('token', $token)->eq('is_active', 1)->gte('date_expiration', time())->findOneColumn('user_id'); + } + + /** + * Disable all tokens for a user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function disable($user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->update(array('is_active' => 0)); + } +} diff --git a/app/Model/PredefinedTaskDescriptionModel.php b/app/Model/PredefinedTaskDescriptionModel.php new file mode 100644 index 0000000..aaa4d23 --- /dev/null +++ b/app/Model/PredefinedTaskDescriptionModel.php @@ -0,0 +1,52 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +class PredefinedTaskDescriptionModel extends Base +{ + const TABLE = 'predefined_task_descriptions'; + + public function getAll($projectId) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->findAll(); + } + + public function getList($projectId) + { + return array('' => t('None')) + $this->db->hashtable(self::TABLE)->eq('project_id', $projectId)->getAll('id', 'title'); + } + + public function getById($projectId, $id) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->findOne(); + } + + public function getDescriptionById($projectId, $id) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->findOneColumn('description'); + } + + public function create($projectId, $title, $description) + { + return $this->db->table(self::TABLE)->persist(array( + 'project_id' => $projectId, + 'title' => $title, + 'description' => $description, + )); + } + + public function update($projectId, $id, $title, $description) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->update(array( + 'title' => $title, + 'description' => $description, + )); + } + + public function remove($projectId, $id) + { + return $this->db->table(self::TABLE)->eq('project_id', $projectId)->eq('id', $id)->remove(); + } +} diff --git a/app/Model/ProjectActivityModel.php b/app/Model/ProjectActivityModel.php new file mode 100644 index 0000000..739fd78 --- /dev/null +++ b/app/Model/ProjectActivityModel.php @@ -0,0 +1,79 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use PicoDb\Table; + +/** + * Project activity model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectActivityModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_activities'; + + /** + * Add a new event for the project + * + * @access public + * @param integer $project_id Project id + * @param integer $task_id Task id + * @param integer $creator_id User id + * @param string $event_name Event name + * @param array $data Event data (will be serialized) + * @return boolean + */ + public function createEvent($project_id, $task_id, $creator_id, $event_name, array $data) + { + return $this->db->table(self::TABLE)->insert(array( + 'project_id' => $project_id, + 'task_id' => $task_id, + 'creator_id' => $creator_id, + 'event_name' => $event_name, + 'date_creation' => time(), + 'data' => json_encode($data), + )); + } + + /** + * Get query + * + * @access public + * @return Table + */ + public function getQuery() + { + return $this + ->db + ->table(ProjectActivityModel::TABLE) + ->columns( + ProjectActivityModel::TABLE.'.*', + 'uc.username AS author_username', + 'uc.name AS author_name', + 'uc.email', + 'uc.avatar_path' + ) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->left(UserModel::TABLE, 'uc', 'id', ProjectActivityModel::TABLE, 'creator_id'); + } + + /** + * Remove old event entries to avoid large table + * + * @access public + * @param integer $ts Timestamp + */ + public function cleanup($ts) + { + $this->db->table(self::TABLE)->lt('date_creation', $ts)->remove(); + } +} diff --git a/app/Model/ProjectDailyColumnStatsModel.php b/app/Model/ProjectDailyColumnStatsModel.php new file mode 100644 index 0000000..a0f14cf --- /dev/null +++ b/app/Model/ProjectDailyColumnStatsModel.php @@ -0,0 +1,254 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Project Daily Column Stats + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectDailyColumnStatsModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_daily_column_stats'; + + /** + * Update daily totals for the project and for each column + * + * "total" is the number open of tasks in the column + * "score" is the sum of tasks score in the column + * + * @access public + * @param integer $project_id Project id + * @param string $date Record date (YYYY-MM-DD) + * @return boolean + */ + public function updateTotals($project_id, $date) + { + $this->db->startTransaction(); + $this->db->table(self::TABLE)->eq('project_id', $project_id)->eq('day', $date)->remove(); + + foreach ($this->getStatsByColumns($project_id) as $column_id => $column) { + $this->db->table(self::TABLE)->insert(array( + 'day' => $date, + 'project_id' => $project_id, + 'column_id' => $column_id, + 'total' => $column['total'], + 'score' => $column['score'], + )); + } + + $this->db->closeTransaction(); + + return true; + } + + /** + * Count the number of recorded days for the data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return integer + */ + public function countDays($project_id, $from, $to) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->findOneColumn('COUNT(DISTINCT day)'); + } + + /** + * Get aggregated metrics for the project within a data range + * + * [ + * ['Date', 'Column1', 'Column2'], + * ['2014-11-16', 2, 5], + * ['2014-11-17', 20, 15], + * ] + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @param string $field Column to aggregate + * @return array + */ + public function getAggregatedMetrics($project_id, $from, $to, $field = 'total') + { + $columns = $this->columnModel->getList($project_id); + $metrics = $this->getMetrics($project_id, $from, $to); + return $this->buildAggregate($metrics, $columns, $field); + } + + /** + * Fetch metrics + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getMetrics($project_id, $from, $to) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc(self::TABLE.'.day') + ->findAll(); + } + + /** + * Build aggregate + * + * @access private + * @param array $metrics + * @param array $columns + * @param string $field + * @return array + */ + private function buildAggregate(array &$metrics, array &$columns, $field) + { + $column_ids = array_keys($columns); + $days = array_unique(array_column($metrics, 'day')); + $rows = array(array_merge(array(e('Date')), array_values($columns))); + + foreach ($days as $day) { + $rows[] = $this->buildRowAggregate($metrics, $column_ids, $day, $field); + } + + return $rows; + } + + /** + * Build one row of the aggregate + * + * @access private + * @param array $metrics + * @param array $column_ids + * @param string $day + * @param string $field + * @return array + */ + private function buildRowAggregate(array &$metrics, array &$column_ids, $day, $field) + { + $row = array($day); + + foreach ($column_ids as $column_id) { + $row[] = $this->findValueInMetrics($metrics, $day, $column_id, $field); + } + + return $row; + } + + /** + * Find the value in the metrics + * + * @access private + * @param array $metrics + * @param string $day + * @param string $column_id + * @param string $field + * @return integer + */ + private function findValueInMetrics(array &$metrics, $day, $column_id, $field) + { + foreach ($metrics as $metric) { + if ($metric['day'] === $day && $metric['column_id'] == $column_id) { + return (int) $metric[$field]; + } + } + + return 0; + } + + /** + * Get number of tasks and score by columns + * + * @access private + * @param integer $project_id + * @return array + */ + private function getStatsByColumns($project_id) + { + $totals = $this->getTotalByColumns($project_id); + $scores = $this->getScoreByColumns($project_id); + $columns = array(); + + foreach ($totals as $column_id => $total) { + $columns[$column_id] = array('total' => $total, 'score' => 0); + } + + foreach ($scores as $column_id => $score) { + $columns[$column_id]['score'] = (int) $score; + } + + return $columns; + } + + /** + * Get number of tasks and score by columns + * + * @access private + * @param integer $project_id + * @return array + */ + private function getScoreByColumns($project_id) + { + $stats = $this->db->table(TaskModel::TABLE) + ->columns('column_id', 'SUM(score) AS score') + ->eq('project_id', $project_id) + ->eq('is_active', TaskModel::STATUS_OPEN) + ->notNull('score') + ->groupBy('column_id') + ->findAll(); + + return array_column($stats, 'score', 'column_id'); + } + + /** + * Get number of tasks and score by columns + * + * @access private + * @param integer $project_id + * @return array + */ + private function getTotalByColumns($project_id) + { + $stats = $this->db->table(TaskModel::TABLE) + ->columns('column_id', 'COUNT(*) AS total') + ->eq('project_id', $project_id) + ->in('is_active', $this->getTaskStatusConfig()) + ->groupBy('column_id') + ->findAll(); + + return array_column($stats, 'total', 'column_id'); + } + + /** + * Get task status to use for total calculation + * + * @access private + * @return array + */ + private function getTaskStatusConfig() + { + if ($this->configModel->get('cfd_include_closed_tasks') == 1) { + return array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED); + } + + return array(TaskModel::STATUS_OPEN); + } +} diff --git a/app/Model/ProjectDailyStatsModel.php b/app/Model/ProjectDailyStatsModel.php new file mode 100644 index 0000000..0754d26 --- /dev/null +++ b/app/Model/ProjectDailyStatsModel.php @@ -0,0 +1,76 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Project Daily Stats + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectDailyStatsModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_daily_stats'; + + /** + * Update daily totals for the project + * + * @access public + * @param integer $project_id Project id + * @param string $date Record date (YYYY-MM-DD) + * @return boolean + */ + public function updateTotals($project_id, $date) + { + $this->db->startTransaction(); + + $lead_cycle_time = $this->averageLeadCycleTimeAnalytic->build($project_id); + + $this->db->table(self::TABLE)->eq('day', $date)->eq('project_id', $project_id)->remove(); + + $this->db->table(self::TABLE)->insert(array( + 'day' => $date, + 'project_id' => $project_id, + 'avg_lead_time' => $lead_cycle_time['avg_lead_time'], + 'avg_cycle_time' => $lead_cycle_time['avg_cycle_time'], + )); + + $this->db->closeTransaction(); + + return true; + } + + /** + * Get raw metrics for the project within a data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getRawMetrics($project_id, $from, $to) + { + $metrics = $this->db->table(self::TABLE) + ->columns('day', 'avg_lead_time', 'avg_cycle_time') + ->eq('project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc('day') + ->findAll(); + + foreach ($metrics as &$metric) { + $metric['avg_lead_time'] = (int) $metric['avg_lead_time']; + $metric['avg_cycle_time'] = (int) $metric['avg_cycle_time']; + } + + return $metrics; + } +} diff --git a/app/Model/ProjectDuplicationModel.php b/app/Model/ProjectDuplicationModel.php new file mode 100644 index 0000000..a7de64f --- /dev/null +++ b/app/Model/ProjectDuplicationModel.php @@ -0,0 +1,190 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Security\Role; + +/** + * Project Duplication + * + * @package Kanboard\Model + * @author Frederic Guillot + * @author Antonio Rabelo + */ +class ProjectDuplicationModel extends Base +{ + /** + * Get list of optional models to duplicate + * + * @access public + * @return string[] + */ + public function getOptionalSelection() + { + return array( + 'categoryModel', + 'projectRoleModel', + 'projectPermissionModel', + 'actionModel', + 'tagDuplicationModel', + 'customFilterModel', + 'projectMetadataModel', + 'projectTaskDuplicationModel', + ); + } + + /** + * Get list of all possible models to duplicate + * + * @access public + * @return string[] + */ + public function getPossibleSelection() + { + return array( + 'swimlaneModel', + 'boardModel', + 'categoryModel', + 'projectRoleModel', + 'projectPermissionModel', + 'actionModel', + 'swimlaneModel', + 'tagDuplicationModel', + 'customFilterModel', + 'projectMetadataModel', + 'projectTaskDuplicationModel', + ); + } + + /** + * Get a valid project name for the duplication + * + * @access public + * @param string $name Project name + * @param integer $max_length Max length allowed + * @return string + */ + public function getClonedProjectName($name, $max_length = 50) + { + $suffix = ' ('.t('Clone').')'; + + if (strlen($name.$suffix) > $max_length) { + $name = substr($name, 0, $max_length - strlen($suffix)); + } + + return $name.$suffix; + } + + /** + * Clone a project with all settings + * + * @param integer $src_project_id Project Id + * @param array $selection Selection of optional project parts to duplicate + * @param integer $owner_id Owner of the project + * @param string $name Name of the project + * @param boolean $private Force the project to be private + * @param string $identifier Identifier of the project + * @return integer Cloned Project Id + */ + public function duplicate($src_project_id, $selection = array('projectPermissionModel', 'categoryModel', 'actionModel'), $owner_id = 0, $name = null, $private = null, $identifier = null) + { + $this->db->startTransaction(); + + // Get the cloned project Id + $dst_project_id = $this->copy($src_project_id, $owner_id, $name, $private, $identifier); + + if ($dst_project_id === false) { + $this->db->cancelTransaction(); + return false; + } + + // Clone Swimlanes, Columns, Categories, Permissions and Actions + foreach ($this->getPossibleSelection() as $model) { + + // Skip if optional part has not been selected + if (in_array($model, $this->getOptionalSelection()) && ! in_array($model, $selection)) { + continue; + } + + // Skip permissions for private projects + if ($private && $model === 'projectPermissionModel') { + continue; + } + + if (! $this->$model->duplicate($src_project_id, $dst_project_id)) { + $this->db->cancelTransaction(); + return false; + } + } + + if (! $this->makeOwnerManager($dst_project_id, $owner_id)) { + $this->db->cancelTransaction(); + return false; + } + + $this->db->closeTransaction(); + + return (int) $dst_project_id; + } + + /** + * Create a project from another one + * + * @access private + * @param integer $src_project_id + * @param integer $owner_id + * @param string $name + * @param boolean $private + * @param string $identifier + * @return integer + */ + private function copy($src_project_id, $owner_id = 0, $name = null, $private = null, $identifier = null) + { + $project = $this->projectModel->getById($src_project_id); + $is_private = empty($project['is_private']) ? 0 : 1; + + if (! empty($identifier)) { + $identifier = strtoupper($identifier); + } + + $values = array( + 'name' => $name ?: $this->getClonedProjectName($project['name']), + 'is_active' => 1, + 'last_modified' => time(), + 'token' => '', + 'is_public' => 0, + 'is_private' => $private ? 1 : $is_private, + 'owner_id' => $owner_id, + 'priority_default' => $project['priority_default'], + 'priority_start' => $project['priority_start'], + 'priority_end' => $project['priority_end'], + 'per_swimlane_task_limits' => empty($project['per_swimlane_task_limits']) ? 0 : 1, + 'task_limit' => $project['task_limit'], + 'identifier' => $identifier, + ); + + return $this->db->table(ProjectModel::TABLE)->persist($values); + } + + /** + * Make sure that the creator of the duplicated project is also owner + * + * @access private + * @param integer $dst_project_id + * @param integer $owner_id + * @return boolean + */ + private function makeOwnerManager($dst_project_id, $owner_id) + { + if ($owner_id > 0) { + $this->projectUserRoleModel->removeUser($dst_project_id, $owner_id); + + if (! $this->projectUserRoleModel->addUser($dst_project_id, $owner_id, Role::PROJECT_MANAGER)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ProjectFileModel.php b/app/Model/ProjectFileModel.php new file mode 100644 index 0000000..7da5741 --- /dev/null +++ b/app/Model/ProjectFileModel.php @@ -0,0 +1,85 @@ +<?php + +namespace Kanboard\Model; + +/** + * Project File Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectFileModel extends FileModel +{ + /** + * Table name + * + * @var string + */ + const TABLE = 'project_has_files'; + + /** + * Events + * + * @var string + */ + const EVENT_CREATE = 'project.file.create'; + const EVENT_DESTROY = 'project.file.destroy'; + + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + protected function getTable() + { + return self::TABLE; + } + + /** + * Define the foreign key + * + * @abstract + * @access protected + * @return string + */ + protected function getForeignKey() + { + return 'project_id'; + } + + /** + * Define the path prefix + * + * @abstract + * @access protected + * @return string + */ + protected function getPathPrefix() + { + return 'projects'; + } + + /** + * Fire file creation event + * + * @access protected + * @param integer $file_id + */ + protected function fireCreationEvent($file_id) + { + $this->queueManager->push($this->projectFileEventJob->withParams($file_id, self::EVENT_CREATE)); + } + + /** + * Fire file destruction event + * + * @access protected + * @param integer $file_id + */ + protected function fireDestructionEvent($file_id) + { + $this->queueManager->push($this->projectFileEventJob->withParams($file_id, self::EVENT_DESTROY)); + } +} diff --git a/app/Model/ProjectGroupRoleModel.php b/app/Model/ProjectGroupRoleModel.php new file mode 100644 index 0000000..213fee0 --- /dev/null +++ b/app/Model/ProjectGroupRoleModel.php @@ -0,0 +1,197 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Security\Role; + +/** + * Project Group Role + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectGroupRoleModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_has_groups'; + + /** + * Get the list of project visible by the given user according to groups + * + * @access public + * @param integer $user_id + * @param array $status + * @return array + */ + public function getProjectsByUser($user_id, $status = array(ProjectModel::ACTIVE, ProjectModel::INACTIVE)) + { + return $this->db + ->hashtable(ProjectModel::TABLE) + ->join(self::TABLE, 'project_id', 'id') + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) + ->eq(GroupMemberModel::TABLE.'.user_id', $user_id) + ->in(ProjectModel::TABLE.'.is_active', $status) + ->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); + } + + /** + * For a given project get the role of the specified user + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return string + */ + public function getUserRole($project_id, $user_id) + { + $roles = $this->db->table(self::TABLE) + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) + ->eq(GroupMemberModel::TABLE.'.user_id', $user_id) + ->eq(self::TABLE.'.project_id', $project_id) + ->findAllByColumn('role'); + + return $this->projectAccessMap->getHighestRole($roles); + } + + /** + * Get all groups associated directly to the project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getGroups($project_id) + { + return $this->db->table(self::TABLE) + ->columns(GroupModel::TABLE.'.id', GroupModel::TABLE.'.name', self::TABLE.'.role') + ->join(GroupModel::TABLE, 'id', 'group_id') + ->eq('project_id', $project_id) + ->asc('name') + ->findAll(); + } + + /** + * From groups get all users associated to the project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getUsers($project_id) + { + return $this->db->table(self::TABLE) + ->columns( + UserModel::TABLE.'.id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + self::TABLE.'.role' + ) + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', GroupMemberModel::TABLE) + ->eq(self::TABLE.'.project_id', $project_id) + ->asc(UserModel::TABLE.'.username') + ->findAll(); + } + + /** + * From groups get all users assignable to tasks + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAssignableUsers($project_id) + { + return $this->db->table(UserModel::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name') + ->join(GroupMemberModel::TABLE, 'user_id', 'id', UserModel::TABLE) + ->join(self::TABLE, 'group_id', 'group_id', GroupMemberModel::TABLE) + ->eq(self::TABLE.'.project_id', $project_id) + ->eq(UserModel::TABLE.'.is_active', 1) + ->neq(self::TABLE.'.role', Role::PROJECT_VIEWER) + ->asc(UserModel::TABLE.'.username') + ->findAll(); + } + + /** + * Add a group to the project + * + * @access public + * @param integer $project_id + * @param integer $group_id + * @param string $role + * @return boolean + */ + public function addGroup($project_id, $group_id, $role) + { + return $this->db->table(self::TABLE)->insert(array( + 'group_id' => $group_id, + 'project_id' => $project_id, + 'role' => $role, + )); + } + + /** + * Remove a group from the project + * + * @access public + * @param integer $project_id + * @param integer $group_id + * @return boolean + */ + public function removeGroup($project_id, $group_id) + { + return $this->db->table(self::TABLE)->eq('group_id', $group_id)->eq('project_id', $project_id)->remove(); + } + + /** + * Change a group role for the project + * + * @access public + * @param integer $project_id + * @param integer $group_id + * @param string $role + * @return boolean + */ + public function changeGroupRole($project_id, $group_id, $role) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('project_id', $project_id) + ->update(array( + 'role' => $role, + )); + } + + /** + * Copy group access from a project to another one + * + * @param integer $project_src_id Project Template + * @param integer $project_dst_id Project that receives the copy + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id) + { + $rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll(); + + foreach ($rows as $row) { + $result = $this->db->table(self::TABLE)->save(array( + 'project_id' => $project_dst_id, + 'group_id' => $row['group_id'], + 'role' => $row['role'], + )); + + if (! $result) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ProjectMetadataModel.php b/app/Model/ProjectMetadataModel.php new file mode 100644 index 0000000..760acd7 --- /dev/null +++ b/app/Model/ProjectMetadataModel.php @@ -0,0 +1,54 @@ +<?php + +namespace Kanboard\Model; + +/** + * Project Metadata + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectMetadataModel extends MetadataModel +{ + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + protected function getTable() + { + return 'project_has_metadata'; + } + + /** + * Define the entity key + * + * @access protected + * @return string + */ + protected function getEntityKey() + { + return 'project_id'; + } + + /** + * Helper method to duplicate all metadata to another project + * + * @access public + * @param integer $src_project_id + * @param integer $dst_project_id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $metadata = $this->getAll($src_project_id); + + if (! $this->save($dst_project_id, $metadata)) { + return false; + } + + return true; + } +} diff --git a/app/Model/ProjectModel.php b/app/Model/ProjectModel.php new file mode 100644 index 0000000..bb99955 --- /dev/null +++ b/app/Model/ProjectModel.php @@ -0,0 +1,611 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Security\Token; +use Kanboard\Core\Security\Role; +use Kanboard\Model\TaskModel; +use Kanboard\Model\TaskFileModel; + +/** + * Project model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectModel extends Base +{ + /** + * SQL table name for projects + * + * @var string + */ + const TABLE = 'projects'; + + /** + * Value for active project + * + * @var integer + */ + const ACTIVE = 1; + + /** + * Value for inactive project + * + * @var integer + */ + const INACTIVE = 0; + + /** + * Value for private project + * + * @var integer + */ + const TYPE_PRIVATE = 1; + + /** + * Value for team project + * + * @var integer + */ + const TYPE_TEAM = 0; + + /** + * Get a project by the id + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getById($project_id) + { + return $this->db->table(self::TABLE)->eq('id', $project_id)->findOne(); + } + + /** + * Get a project by id with owner name + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getByIdWithOwner($project_id) + { + return $this->db->table(self::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') + ->eq(self::TABLE.'.id', $project_id) + ->join(UserModel::TABLE, 'id', 'owner_id') + ->findOne(); + } + + /** + * Get a project by id with owner name and task count + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getByIdWithOwnerAndTaskCount($project_id) + { + return $this->db->table(self::TABLE) + ->columns( + self::TABLE.'.*', + UserModel::TABLE.'.username AS owner_username', + UserModel::TABLE.'.name AS owner_name', + "(SELECT count(*) FROM tasks WHERE tasks.project_id=projects.id AND tasks.is_active='1') AS nb_active_tasks" + ) + ->eq(self::TABLE.'.id', $project_id) + ->join(UserModel::TABLE, 'id', 'owner_id') + ->join(TaskModel::TABLE, 'project_id', 'id') + ->findOne(); + } + + /** + * Get a project by the name + * + * @access public + * @param string $name Project name + * @return array + */ + public function getByName($name) + { + return $this->db->table(self::TABLE)->eq('name', $name)->findOne(); + } + + /** + * Get a project by the identifier (code) + * + * @access public + * @param string $identifier + * @return array|boolean + */ + public function getByIdentifier($identifier) + { + if (empty($identifier)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('identifier', strtoupper($identifier))->findOne(); + } + + /** + * Get a project by the email address + * + * @access public + * @param string $email + * @return array|boolean + */ + public function getByEmail($email) + { + if (empty($email)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('email', $email)->findOne(); + } + + /** + * Fetch project data by using the token + * + * @access public + * @param string $token Token + * @return array|boolean + */ + public function getByToken($token) + { + if (empty($token)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('token', $token)->eq('is_public', 1)->findOne(); + } + + /** + * Return the first project from the database (no sorting) + * + * @access public + * @return array + */ + public function getFirst() + { + return $this->db->table(self::TABLE)->findOne(); + } + + /** + * Return true if the project is private + * + * @access public + * @param integer $project_id Project id + * @return boolean + */ + public function isPrivate($project_id) + { + return $this->db->table(self::TABLE)->eq('id', $project_id)->eq('is_private', 1)->exists(); + } + + /** + * Get all projects + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->table(self::TABLE)->asc('name')->findAll(); + } + + /** + * Get all projects with given Ids + * + * @access public + * @param integer[] $project_ids + * @return array + */ + public function getAllByIds(array $project_ids) + { + if (empty($project_ids)) { + return array(); + } + + return $this->db->table(self::TABLE)->in('id', $project_ids)->asc('name')->findAll(); + } + + /** + * Get all project ids + * + * @access public + * @return array + */ + public function getAllIds() + { + return $this->db->table(self::TABLE)->asc('name')->findAllByColumn('id'); + } + + /** + * Return the list of all projects + * + * @access public + * @param bool $prependNone + * @param bool $noPrivateProjects + * @return array + */ + public function getList($prependNone = true, $noPrivateProjects = true) + { + if ($noPrivateProjects) { + $projects = $this->db->hashtable(self::TABLE)->eq('is_private', 0)->asc('name')->getAll('id', 'name'); + } else { + $projects = $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); + } + + if ($prependNone) { + return array(t('None')) + $projects; + } + + return $projects; + } + + /** + * Get all projects with all its data for a given status + * + * @access public + * @param integer $status Project status: self::ACTIVE or self:INACTIVE + * @return array + */ + public function getAllByStatus($status) + { + return $this->db + ->table(self::TABLE) + ->asc('name') + ->eq('is_active', $status) + ->findAll(); + } + + /** + * Get a list of project by status + * + * @access public + * @param integer $status Project status: self::ACTIVE or self:INACTIVE + * @return array + */ + public function getListByStatus($status) + { + return $this->db + ->hashtable(self::TABLE) + ->asc('name') + ->eq('is_active', $status) + ->getAll('id', 'name'); + } + + /** + * Return the number of projects by status + * + * @access public + * @param integer $status Status: self::ACTIVE or self:INACTIVE + * @return integer + */ + public function countByStatus($status) + { + return $this->db + ->table(self::TABLE) + ->eq('is_active', $status) + ->count(); + } + + /** + * Get stats for each column of a project + * + * @access public + * @param array $project + * @return array + */ + public function getColumnStats(array &$project) + { + $project['columns'] = $this->columnModel->getAllWithTaskCount($project['id']); + $project['nb_active_tasks'] = 0; + + foreach ($project['columns'] as $column) { + $project['nb_active_tasks'] += $column['nb_open_tasks']; + } + + return $project; + } + + /** + * Apply column stats to a collection of projects (filter callback) + * + * @access public + * @param array $projects + * @return array + */ + public function applyColumnStats(array $projects) + { + foreach ($projects as &$project) { + $this->getColumnStats($project); + } + + return $projects; + } + + /** + * Get project summary for a list of project + * + * @access public + * @param array $project_ids List of project id + * @return \PicoDb\Table + */ + public function getQueryColumnStats(array $project_ids) + { + if (empty($project_ids)) { + return $this->db->table(ProjectModel::TABLE)->eq(ProjectModel::TABLE.'.id', 0); + } + + return $this->db + ->table(ProjectModel::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->in(self::TABLE.'.id', $project_ids) + ->callback(array($this, 'applyColumnStats')); + } + + /** + * Get query for list of project without column statistics + * + * @access public + * @param array $projectIds + * @return \PicoDb\Table + */ + public function getQueryByProjectIds(array $projectIds) + { + if (empty($projectIds)) { + return $this->db->table(ProjectModel::TABLE)->eq(ProjectModel::TABLE.'.id', 0); + } + + return $this->db + ->table(ProjectModel::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->in(self::TABLE.'.id', $projectIds); + } + + /** + * Create a project + * + * @access public + * @param array $values Form values + * @param integer $userId User who creates the project + * @param bool $addUser Whether to automatically add the user to the project + * @return int Project id + */ + public function create(array $values, $userId = 0, $addUser = false) + { + if (! empty($userId) && ! $this->userModel->exists($userId)) { + return false; + } + + $this->db->startTransaction(); + + $values['token'] = ''; + $values['last_modified'] = time(); + $values['is_private'] = empty($values['is_private']) ? 0 : 1; + $values['owner_id'] = $userId; + + if (! empty($values['identifier'])) { + $values['identifier'] = strtoupper($values['identifier']); + } + + $this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end', 'task_limit')); + + if (! $this->db->table(self::TABLE)->save($values)) { + $this->db->cancelTransaction(); + return false; + } + + $project_id = $this->db->getLastId(); + + if (! $this->boardModel->create($project_id, $this->boardModel->getUserColumns())) { + $this->db->cancelTransaction(); + return false; + } + + if (! $this->swimlaneModel->create($project_id, t('Default swimlane'))) { + $this->db->cancelTransaction(); + return false; + } + + if ($addUser && $userId) { + $this->projectUserRoleModel->addUser($project_id, $userId, Role::PROJECT_MANAGER); + } + + $this->categoryModel->createDefaultCategories($project_id); + + $this->db->closeTransaction(); + + return (int) $project_id; + } + + /** + * Check if the project have been modified + * + * @access public + * @param integer $project_id Project id + * @param integer $timestamp Timestamp + * @return bool + */ + public function isModifiedSince($project_id, $timestamp) + { + return (bool) $this->db->table(self::TABLE) + ->eq('id', $project_id) + ->gt('last_modified', $timestamp) + ->count(); + } + + /** + * Update modification date + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function updateModificationDate($project_id) + { + return $this->db->table(self::TABLE)->eq('id', $project_id)->update(array( + 'last_modified' => time() + )); + } + + /** + * Update a project + * + * @access public + * @param array $values Form values + * @return bool + */ + public function update(array $values) + { + if (! empty($values['identifier'])) { + $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']); + } + + if (! empty($values['owner_id']) && ! $this->userModel->exists($values['owner_id'])) { + return false; + } + + $values['per_swimlane_task_limits'] = empty($values['per_swimlane_task_limits']) ? 0 : 1; + + $this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end', 'task_limit')); + + $updates = $values; + unset($updates['id']); + return $this->exists($values['id']) && + $this->db->table(self::TABLE)->eq('id', $values['id'])->save($updates); + } + + /** + * Remove a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function remove($project_id) + { + // Remove all project attachments + $this->projectFileModel->removeAll($project_id); + + // Remove all task attachments + $file_ids = $this->db + ->table(TaskFileModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->join(TaskModel::TABLE, 'id', 'task_id', TaskFileModel::TABLE) + ->findAllByColumn(TaskFileModel::TABLE.'.id'); + + foreach ($file_ids as $file_id) { + $this->taskFileModel->remove($file_id); + } + + // Remove project + $this->db->table(TagModel::TABLE)->eq('project_id', $project_id)->remove(); + return $this->db->table(self::TABLE)->eq('id', $project_id)->remove(); + } + + /** + * Return true if the project exists + * + * @access public + * @param integer $project_id Project id + * @return boolean + */ + public function exists($project_id) + { + return $this->db->table(self::TABLE)->eq('id', $project_id)->exists(); + } + + /** + * Enable a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function enable($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->update(array('is_active' => 1)); + } + + /** + * Disable a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function disable($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->update(array('is_active' => 0)); + } + + /** + * Enable public access for a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function enablePublicAccess($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->save(array('is_public' => 1, 'token' => Token::getToken())); + } + + /** + * Disable public access for a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function disablePublicAccess($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->save(array('is_public' => 0, 'token' => '')); + } + + /** + * Change usage of global tags + * + * @param integer $project_id Project id + * @param bool $global_tags New global tag value + * @return bool + */ + public function changeGlobalTagUsage($project_id, $global_tags) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->save(array('enable_global_tags' => $global_tags)); + } +} diff --git a/app/Model/ProjectNotificationModel.php b/app/Model/ProjectNotificationModel.php new file mode 100644 index 0000000..aeeee4c --- /dev/null +++ b/app/Model/ProjectNotificationModel.php @@ -0,0 +1,67 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Project Notification + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectNotificationModel extends Base +{ + /** + * Send notifications + * + * @access public + * @param integer $project_id + * @param string $event_name + * @param array $event_data + */ + public function sendNotifications($project_id, $event_name, array $event_data) + { + $project = $this->projectModel->getById($project_id); + + $types = array_merge( + $this->projectNotificationTypeModel->getHiddenTypes(), + $this->projectNotificationTypeModel->getSelectedTypes($project_id) + ); + + foreach ($types as $type) { + $this->projectNotificationTypeModel->getType($type)->notifyProject($project, $event_name, $event_data); + } + } + + /** + * Save settings for the given project + * + * @access public + * @param integer $project_id + * @param array $values + */ + public function saveSettings($project_id, array $values) + { + $this->db->startTransaction(); + + $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); + $this->projectNotificationTypeModel->saveSelectedTypes($project_id, $types); + + $this->db->closeTransaction(); + } + + /** + * Read user settings to display the form + * + * @access public + * @param integer $project_id + * @return array + */ + public function readSettings($project_id) + { + return array( + 'notification_types' => $this->projectNotificationTypeModel->getSelectedTypes($project_id), + ); + } +} diff --git a/app/Model/ProjectNotificationTypeModel.php b/app/Model/ProjectNotificationTypeModel.php new file mode 100644 index 0000000..aeec77f --- /dev/null +++ b/app/Model/ProjectNotificationTypeModel.php @@ -0,0 +1,57 @@ +<?php + +namespace Kanboard\Model; + +/** + * Project Notification Type + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectNotificationTypeModel extends NotificationTypeModel +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_has_notification_types'; + + /** + * Get selected notification types for a given project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getSelectedTypes($project_id) + { + $types = $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->asc('notification_type') + ->findAllByColumn('notification_type'); + + return $this->filterTypes($types); + } + + /** + * Save notification types for a given project + * + * @access public + * @param integer $project_id + * @param string[] $types + * @return boolean + */ + public function saveSelectedTypes($project_id, array $types) + { + $results = array(); + $this->db->table(self::TABLE)->eq('project_id', $project_id)->remove(); + + foreach ($types as $type) { + $results[] = $this->db->table(self::TABLE)->insert(array('project_id' => $project_id, 'notification_type' => $type)); + } + + return ! in_array(false, $results, true); + } +} diff --git a/app/Model/ProjectPermissionModel.php b/app/Model/ProjectPermissionModel.php new file mode 100644 index 0000000..c0dc2f5 --- /dev/null +++ b/app/Model/ProjectPermissionModel.php @@ -0,0 +1,199 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Security\Role; +use Kanboard\Filter\ProjectGroupRoleProjectFilter; +use Kanboard\Filter\ProjectGroupRoleUsernameFilter; +use Kanboard\Filter\ProjectUserRoleProjectFilter; +use Kanboard\Filter\ProjectUserRoleUsernameFilter; + +/** + * Project Permission + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectPermissionModel extends Base +{ + /** + * Get query for project users overview + * + * @access public + * @param array $project_ids + * @param string $role + * @return \PicoDb\Table + */ + public function getQueryByRole(array $project_ids, $role) + { + if (empty($project_ids)) { + $project_ids = array(-1); + } + + return $this + ->db + ->table(ProjectUserRoleModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id') + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->eq(ProjectUserRoleModel::TABLE.'.role', $role) + ->eq(ProjectModel::TABLE.'.is_private', 0) + ->in(ProjectModel::TABLE.'.id', $project_ids) + ->columns( + UserModel::TABLE.'.id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + ProjectModel::TABLE.'.name AS project_name', + ProjectModel::TABLE.'.id' + ); + } + + /** + * Get all usernames (fetch users from groups) + * + * @access public + * @param integer $project_id + * @param string $input + * @return array + */ + public function findUsernames($project_id, $input) + { + $userMembers = $this->projectUserRoleQuery + ->withFilter(new ProjectUserRoleProjectFilter($project_id)) + ->withFilter(new ProjectUserRoleUsernameFilter($input)) + ->getQuery() + ->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() + ->columns( + UserModel::TABLE.'.id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + UserModel::TABLE.'.avatar_path' + ) + ->findAll(); + + $userMembers = array_column_index_unique($userMembers, 'username'); + $groupMembers = array_column_index_unique($groupMembers, 'username'); + $members = array_merge($userMembers, $groupMembers); + + ksort($members); + + return $members; + } + + public function getMembers($project_id) + { + $userMembers = $this->projectUserRoleModel->getUsers($project_id); + $groupMembers = $this->projectGroupRoleModel->getUsers($project_id); + + $userMembers = array_column_index_unique($userMembers, 'username'); + $groupMembers = array_column_index_unique($groupMembers, 'username'); + return array_merge($userMembers, $groupMembers); + } + + public function getMembersWithEmail($project_id) + { + $members = $this->getMembers($project_id); + return array_filter($members, function (array $user) { + return ! empty($user['email']); + }); + } + + /** + * Return true if the user is allowed to access a project + * + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function isUserAllowed($project_id, $user_id) + { + if ($this->userSession->isAdmin()) { + return true; + } + + return $this->userModel->isActive($user_id) && + $this->isMember($project_id, $user_id); + } + + /** + * Return true if the user is assignable + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function isAssignable($project_id, $user_id) + { + if ($this->userModel->isActive($user_id)) { + $role = $this->projectUserRoleModel->getUserRole($project_id, $user_id); + + return ! empty($role) && $role !== Role::PROJECT_VIEWER; + } + + return false; + } + + /** + * Return true if the user is member + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function isMember($project_id, $user_id) + { + return ! empty($this->projectUserRoleModel->getUserRole($project_id, $user_id)); + } + + /** + * Get active project ids by user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getActiveProjectIds($user_id) + { + return array_keys($this->projectUserRoleModel->getActiveProjectsByUser($user_id)); + } + + /** + * 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 + * @param integer $project_dst_id Project that receives the copy + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id) + { + return $this->projectUserRoleModel->duplicate($project_src_id, $project_dst_id) && + $this->projectGroupRoleModel->duplicate($project_src_id, $project_dst_id); + } +} diff --git a/app/Model/ProjectRoleModel.php b/app/Model/ProjectRoleModel.php new file mode 100644 index 0000000..c5cd7b0 --- /dev/null +++ b/app/Model/ProjectRoleModel.php @@ -0,0 +1,234 @@ +<?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; + } + + /** + * Copy project custom_roles from a project to another one + * + * @param integer $project_src_id + * @param integer $project_dst_id + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id) + { + $rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll(); + + foreach ($rows as $row) { + $role_src_id = $row['role_id']; + $role_dst_id = $this->db->table(self::TABLE)->persist(array( + 'project_id' => $project_dst_id, + 'role' => $row['role'], + )); + + if (! $role_dst_id) { + return false; + } + + if (! $this->columnRestrictionModel->duplicate($project_src_id, $project_dst_id, $role_src_id, $role_dst_id)) { + return false; + } + + if (! $this->columnMoveRestrictionModel->duplicate($project_src_id, $project_dst_id, $role_src_id, $role_dst_id)) { + return false; + } + + if (! $this->projectRoleRestrictionModel->duplicate($project_src_id, $project_dst_id, $role_src_id, $role_dst_id)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ProjectRoleRestrictionModel.php b/app/Model/ProjectRoleRestrictionModel.php new file mode 100644 index 0000000..ee6e032 --- /dev/null +++ b/app/Model/ProjectRoleRestrictionModel.php @@ -0,0 +1,167 @@ +<?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_SUPPRESSION = 'task_remove'; + const RULE_TASK_OPEN_CLOSE = 'task_open_close'; + const RULE_TASK_MOVE = 'task_move'; + const RULE_TASK_CHANGE_ASSIGNEE = 'task_change_assignee'; + const RULE_TASK_UPDATE_ASSIGNED = 'task_update_assigned'; + + /** + * Get rules + * + * @return array + */ + public function getRules() + { + return array( + self::RULE_TASK_CREATION => t('Task creation is not permitted'), + self::RULE_TASK_SUPPRESSION => t('Task suppression 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'), + self::RULE_TASK_CHANGE_ASSIGNEE => t('Changing assignee is not permitted'), + self::RULE_TASK_UPDATE_ASSIGNED => t('Update only assigned tasks is 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( + 'restriction_id', + 'project_id', + 'role_id', + '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( + 'restriction_id', + 'project_id', + 'role_id', + '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(); + } + + /** + * Copy role restriction models from a custome_role in the src project to the dst custom_role of the dst project + * + * @param integer $project_src_id + * @param integer $project_dst_id + * @param integer $role_src_id + * @param integer $role_dst_id + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id, $role_src_id, $role_dst_id) + { + $rows = $this->db->table(self::TABLE) + ->eq('project_id', $project_src_id) + ->eq('role_id', $role_src_id) + ->findAll(); + + foreach ($rows as $row) { + $result = $this->db->table(self::TABLE)->persist(array( + 'project_id' => $project_dst_id, + 'role_id' => $role_dst_id, + 'rule' => $row['rule'], + )); + + if (! $result) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ProjectTaskDuplicationModel.php b/app/Model/ProjectTaskDuplicationModel.php new file mode 100644 index 0000000..5d2e132 --- /dev/null +++ b/app/Model/ProjectTaskDuplicationModel.php @@ -0,0 +1,35 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Project Task Duplication Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectTaskDuplicationModel extends Base +{ + /** + * Duplicate all tasks to another project + * + * @access public + * @param integer $src_project_id + * @param integer $dst_project_id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $task_ids = $this->taskFinderModel->getAllIds($src_project_id, array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED)); + + foreach ($task_ids as $task_id) { + if (! $this->taskProjectDuplicationModel->duplicateToProject($task_id, $dst_project_id)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ProjectTaskPriorityModel.php b/app/Model/ProjectTaskPriorityModel.php new file mode 100644 index 0000000..c1a0257 --- /dev/null +++ b/app/Model/ProjectTaskPriorityModel.php @@ -0,0 +1,74 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Project Task Priority Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectTaskPriorityModel extends Base +{ + /** + * Get Priority range from a project + * + * @access public + * @param array $project + * @return array + */ + public function getPriorities(array $project) + { + $range = range($project['priority_start'], $project['priority_end']); + return array_combine($range, $range); + } + + /** + * Get task priority settings + * + * @access public + * @param int $project_id + * @return array|null + */ + public function getPrioritySettings($project_id) + { + return $this->db + ->table(ProjectModel::TABLE) + ->columns('priority_default', 'priority_start', 'priority_end') + ->eq('id', $project_id) + ->findOne(); + } + + /** + * Get default task priority + * + * @access public + * @param int $project_id + * @return int + */ + public function getDefaultPriority($project_id) + { + return $this->db->table(ProjectModel::TABLE)->eq('id', $project_id)->findOneColumn('priority_default') ?: 0; + } + + /** + * Get priority for a destination project + * + * @access public + * @param integer $dst_project_id + * @param integer $priority + * @return integer + */ + public function getPriorityForProject($dst_project_id, $priority) + { + $settings = $this->getPrioritySettings($dst_project_id); + + if ($priority >= $settings['priority_start'] && $priority <= $settings['priority_end']) { + return $priority; + } + + return $settings['priority_default']; + } +} diff --git a/app/Model/ProjectUserRoleModel.php b/app/Model/ProjectUserRoleModel.php new file mode 100644 index 0000000..cf4e0de --- /dev/null +++ b/app/Model/ProjectUserRoleModel.php @@ -0,0 +1,275 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Security\Role; + +/** + * Project User Role + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ProjectUserRoleModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'project_has_users'; + + /** + * Get the list of active project for the given user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getActiveProjectsByUser($user_id) + { + return $this->getProjectsByUser($user_id, array(ProjectModel::ACTIVE)); + } + + /** + * Get the list of project visible for the given user + * + * @access public + * @param integer $user_id + * @param array $status + * @return array + */ + public function getProjectsByUser($user_id, $status = array(ProjectModel::ACTIVE, ProjectModel::INACTIVE)) + { + $userProjects = $this->db + ->hashtable(ProjectModel::TABLE) + ->eq(self::TABLE.'.user_id', $user_id) + ->in(ProjectModel::TABLE.'.is_active', $status) + ->join(self::TABLE, 'project_id', 'id') + ->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); + + $groupProjects = $this->projectGroupRoleModel->getProjectsByUser($user_id, $status); + $projects = $userProjects + $groupProjects; + + asort($projects); + + return $projects; + } + + /** + * For a given project get the role of the specified user + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return string + */ + public function getUserRole($project_id, $user_id) + { + $role = $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->findOneColumn('role'); + + if (empty($role)) { + $role = $this->projectGroupRoleModel->getUserRole($project_id, $user_id); + if (empty($role)) { + $role = ""; // force use of the cache + } + } + + return $role; + } + + /** + * Get all users associated directly to the project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getUsers($project_id) + { + return $this->db->table(self::TABLE) + ->columns( + UserModel::TABLE.'.id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + self::TABLE.'.role' + ) + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq('project_id', $project_id) + ->asc(UserModel::TABLE.'.username') + ->asc(UserModel::TABLE.'.name') + ->findAll(); + } + + /** + * Get all users (fetch users from groups) + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAllUsers($project_id) + { + $userMembers = $this->getUsers($project_id); + $groupMembers = $this->projectGroupRoleModel->getUsers($project_id); + $members = array_merge($userMembers, $groupMembers); + + return $this->userModel->prepareList($members); + } + + /** + * Get users grouped by role + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAllUsersGroupedByRole($project_id) + { + $users = array(); + + $userMembers = $this->getUsers($project_id); + $groupMembers = $this->projectGroupRoleModel->getUsers($project_id); + $members = array_merge($userMembers, $groupMembers); + + foreach ($members as $user) { + if (! isset($users[$user['role']])) { + $users[$user['role']] = array(); + } + + $users[$user['role']][$user['id']] = $user['name'] ?: $user['username']; + } + + return $users; + } + + /** + * Get list of users that can be assigned to a task (only Manager and Member) + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAssignableUsers($project_id) + { + $userMembers = $this->db->table(self::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name') + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq(UserModel::TABLE.'.is_active', 1) + ->eq(self::TABLE.'.project_id', $project_id) + ->neq(self::TABLE.'.role', Role::PROJECT_VIEWER) + ->findAll(); + + $groupMembers = $this->projectGroupRoleModel->getAssignableUsers($project_id); + $members = array_merge($userMembers, $groupMembers); + + return $this->userModel->prepareList($members); + } + + /** + * Get list of users that can be assigned to a task (only Manager and Member) + * + * @access public + * @param integer $project_id Project id + * @param bool $unassigned Prepend the 'Unassigned' value + * @param bool $everybody Prepend the 'Everbody' value + * @param bool $singleUser If there is only one user return only this user + * @return array + */ + public function getAssignableUsersList($project_id, $unassigned = true, $everybody = false, $singleUser = false) + { + $users = $this->getAssignableUsers($project_id); + + if ($singleUser && count($users) === 1) { + return $users; + } + + if ($unassigned) { + $users = array(t('Unassigned')) + $users; + } + + if ($everybody) { + $users = array(UserModel::EVERYBODY_ID => t('Everybody')) + $users; + } + + return $users; + } + + /** + * Add a user to the project + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @param string $role + * @return boolean + */ + public function addUser($project_id, $user_id, $role) + { + return $this->db->table(self::TABLE)->insert(array( + 'user_id' => $user_id, + 'project_id' => $project_id, + 'role' => $role, + )); + } + + /** + * Remove a user from the project + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function removeUser($project_id, $user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->remove(); + } + + /** + * Change a user role for the project + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @param string $role + * @return boolean + */ + public function changeUserRole($project_id, $user_id, $role) + { + return $this->db->table(self::TABLE) + ->eq('user_id', $user_id) + ->eq('project_id', $project_id) + ->update(array( + 'role' => $role, + )); + } + + /** + * Copy user access from a project to another one + * + * @param integer $project_src_id + * @param integer $project_dst_id + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id) + { + $rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll(); + + foreach ($rows as $row) { + $result = $this->db->table(self::TABLE)->save(array( + 'project_id' => $project_dst_id, + 'user_id' => $row['user_id'], + 'role' => $row['role'], + )); + + if (! $result) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/RememberMeSessionModel.php b/app/Model/RememberMeSessionModel.php new file mode 100644 index 0000000..75f14b4 --- /dev/null +++ b/app/Model/RememberMeSessionModel.php @@ -0,0 +1,152 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Security\Token; + +/** + * Remember Me Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class RememberMeSessionModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'remember_me'; + + /** + * Expiration (60 days) + * + * @var integer + */ + const EXPIRATION = 5184000; + + /** + * Get a remember me record + * + * @access public + * @param $token + * @param $sequence + * @return mixed + */ + public function find($token, $sequence) + { + return $this->db + ->table(self::TABLE) + ->eq('token', $token) + ->eq('sequence', $sequence) + ->gt('expiration', time()) + ->findOne(); + } + + /** + * Get all sessions for a given user + * + * @access public + * @param integer $user_id User id + * @return array + */ + public function getAll($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('user_id', $user_id) + ->desc('date_creation') + ->columns('id', 'ip', 'user_agent', 'date_creation', 'expiration') + ->findAll(); + } + + /** + * Create a new RememberMe session + * + * @access public + * @param integer $user_id User id + * @param string $ip IP Address + * @param string $user_agent User Agent + * @return array + */ + public function create($user_id, $ip, $user_agent) + { + $token = hash('sha256', $user_id.$user_agent.$ip.Token::getToken()); + $sequence = Token::getToken(); + $expiration = time() + self::EXPIRATION; + + $this->cleanup($user_id); + + $this + ->db + ->table(self::TABLE) + ->insert(array( + 'user_id' => $user_id, + 'ip' => $ip, + 'user_agent' => substr($user_agent, 0, 255), + 'token' => $token, + 'sequence' => $sequence, + 'expiration' => $expiration, + 'date_creation' => time(), + )); + + return array( + 'token' => $token, + 'sequence' => $sequence, + 'expiration' => $expiration, + ); + } + + /** + * Remove a session record + * + * @access public + * @param integer $session_id Session id + * @return mixed + */ + public function remove($session_id) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $session_id) + ->remove(); + } + + /** + * Remove old sessions for a given user + * + * @access public + * @param integer $user_id User id + * @return bool + */ + public function cleanup($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('user_id', $user_id) + ->lt('expiration', time()) + ->remove(); + } + + /** + * Return a new sequence token and update the database + * + * @access public + * @param string $token Session token + * @return string + */ + public function updateSequence($token) + { + $sequence = Token::getToken(); + + $this + ->db + ->table(self::TABLE) + ->eq('token', $token) + ->update(array('sequence' => $sequence)); + + return $sequence; + } +} diff --git a/app/Model/SettingModel.php b/app/Model/SettingModel.php new file mode 100644 index 0000000..e3cd4f9 --- /dev/null +++ b/app/Model/SettingModel.php @@ -0,0 +1,127 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Application Settings + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +abstract class SettingModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'settings'; + + /** + * Prepare data before save + * + * @abstract + * @access public + * @param array $values + * @return array + */ + abstract public function prepare(array $values); + + /** + * Get all settings + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->hashtable(self::TABLE)->getAll('option', 'value'); + } + + /** + * Get a setting value + * + * @access public + * @param string $name + * @param string $default + * @return mixed + */ + public function getOption($name, $default = '') + { + $value = $this->db + ->table(self::TABLE) + ->eq('option', $name) + ->findOneColumn('value'); + + return $value === null || $value === false || $value === '' ? $default : $value; + } + + /** + * Return true if a setting exists + * + * @access public + * @param string $name + * @return boolean + */ + public function exists($name) + { + return $this->db + ->table(self::TABLE) + ->eq('option', $name) + ->exists(); + } + + /** + * Update or insert new settings + * + * @access public + * @param array $values + * @return boolean + */ + public function save(array $values) + { + $results = array(); + $values = $this->prepare($values); + $user_id = $this->userSession->getId(); + $timestamp = time(); + + $this->db->startTransaction(); + + foreach ($values as $option => $value) { + if ($this->exists($option)) { + $results[] = $this->db->table(self::TABLE)->eq('option', $option)->update(array( + 'value' => $value, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } else { + $results[] = $this->db->table(self::TABLE)->insert(array( + 'option' => $option, + 'value' => $value, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } + } + + $this->db->closeTransaction(); + + return ! in_array(false, $results, true); + } + + /** + * Remove a setting + * + * @access public + * @param string $option + * @return bool + */ + public function remove($option) + { + return $this->db->table(self::TABLE) + ->eq('option', $option) + ->remove(); + } +} diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php new file mode 100644 index 0000000..ee86e93 --- /dev/null +++ b/app/Model/SubtaskModel.php @@ -0,0 +1,337 @@ +<?php + +namespace Kanboard\Model; + +use PicoDb\Database; +use Kanboard\Core\Base; + +/** + * Subtask Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class SubtaskModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'subtasks'; + + /** + * Subtask status + * + * @var integer + */ + const STATUS_TODO = 0; + const STATUS_INPROGRESS = 1; + const STATUS_DONE = 2; + + /** + * Events + * + * @var string + */ + const EVENT_UPDATE = 'subtask.update'; + const EVENT_CREATE = 'subtask.create'; + const EVENT_DELETE = 'subtask.delete'; + const EVENT_CREATE_UPDATE = 'subtask.create_update'; + + /** + * Get projectId from subtaskId + * + * @access public + * @param integer $subtaskId + * @return integer + */ + public function getProjectId($subtaskId) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $subtaskId) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** + * Get available status + * + * @access public + * @return string[] + */ + public function getStatusList() + { + return array( + self::STATUS_TODO => 'Todo', + self::STATUS_INPROGRESS => 'In progress', + self::STATUS_DONE => 'Done', + ); + } + + /** + * Get common query + * + * @return \PicoDb\Table + */ + public function getQuery() + { + return $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.*', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name' + ) + ->subquery($this->subtaskTimeTrackingModel->getTimerQuery($this->userSession->getId()), 'timer_start_date') + ->join(UserModel::TABLE, 'id', 'user_id') + ->asc(self::TABLE.'.position'); + } + + /** + * Count by assignee and task status. + * + * @param integer $userId + * @return integer + */ + public function countByAssigneeAndTaskStatus($userId) + { + $query = $this->db->table(self::TABLE) + ->eq('user_id', $userId) + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->join(TaskModel::TABLE, 'id', 'task_id'); + + $this->hook->reference('model:subtask:count:query', $query); + + return $query->count(); + } + + /** + * Get all subtasks for a given task + * + * @access public + * @param integer $taskId + * @return array + */ + public function getAll($taskId) + { + return $this->subtaskListFormatter + ->withQuery($this->getQuery()->eq('task_id', $taskId)) + ->format(); + } + + /** + * Get subtasks for a list of tasks + * + * @param array $taskIds + * @return array + */ + public function getAllByTaskIds(array $taskIds) + { + if (empty($taskIds)) { + return array(); + } + + return $this->subtaskListFormatter + ->withQuery($this->getQuery()->in('task_id', $taskIds)) + ->format(); + } + + /** + * Get subtasks for a list of tasks and a given assignee + * + * @param array $taskIds + * @param integer $userId + * @return array + */ + public function getAllByTaskIdsAndAssignee(array $taskIds, $userId) + { + if (empty($taskIds)) { + return array(); + } + + return $this->subtaskListFormatter + ->withQuery($this->getQuery()->in('task_id', $taskIds)->eq(self::TABLE.'.user_id', $userId)) + ->format(); + } + + /** + * Get a subtask by the id + * + * @access public + * @param integer $subtaskId + * @return array + */ + public function getById($subtaskId) + { + return $this->db->table(self::TABLE)->eq('id', $subtaskId)->findOne(); + } + + /** + * Get subtask with additional information + * + * @param integer $subtaskId + * @return array|null + */ + public function getByIdWithDetails($subtaskId) + { + $subtasks = $this->subtaskListFormatter + ->withQuery($this->getQuery()->eq(self::TABLE.'.id', $subtaskId)) + ->format(); + + if (! empty($subtasks)) { + return $subtasks[0]; + } + + return null; + } + + /** + * Get the position of the last column for a given project + * + * @access public + * @param integer $taskId + * @return integer + */ + public function getLastPosition($taskId) + { + return (int) $this->db + ->table(self::TABLE) + ->eq('task_id', $taskId) + ->desc('position') + ->findOneColumn('position'); + } + + /** + * Create a new subtask + * + * @access public + * @param array $values Form values + * @return bool|integer + */ + public function create(array $values) + { + $this->prepareCreation($values); + $subtaskId = $this->db->table(self::TABLE)->persist($values); + + if ($subtaskId !== false) { + $this->subtaskTimeTrackingModel->updateTaskTimeTracking($values['task_id']); + $this->queueManager->push($this->subtaskEventJob->withParams( + $subtaskId, + array(self::EVENT_CREATE_UPDATE, self::EVENT_CREATE) + )); + } + + return $subtaskId; + } + + /** + * Update a subtask + * + * @access public + * @param array $values + * @param bool $fireEvent + * @return bool + */ + public function update(array $values, $fireEvent = true) + { + $this->prepare($values); + $updates = $values; + unset($updates['id']); + $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($updates); + + if ($result) { + $subtask = $this->getById($values['id']); + $this->subtaskTimeTrackingModel->updateTaskTimeTracking($subtask['task_id']); + + if ($fireEvent) { + $this->queueManager->push($this->subtaskEventJob->withParams( + $subtask['id'], + array(self::EVENT_CREATE_UPDATE, self::EVENT_UPDATE), + $values + )); + } + } + + return $result; + } + + /** + * Remove + * + * @access public + * @param integer $subtaskId + * @return bool + */ + public function remove($subtaskId) + { + $this->subtaskEventJob->execute($subtaskId, array(self::EVENT_DELETE)); + + $subtask = $this->getById($subtaskId); + $result = $this->db->table(self::TABLE)->eq('id', $subtaskId)->remove(); + + $this->subtaskTimeTrackingModel->updateTaskTimeTracking($subtask['task_id']); + + return $result; + } + + /** + * Duplicate all subtasks to another task + * + * @access public + * @param integer $srcTaskId + * @param integer $dstTaskId + * @return bool + */ + public function duplicate($srcTaskId, $dstTaskId) + { + return $this->db->transaction(function (Database $db) use ($srcTaskId, $dstTaskId) { + + $subtasks = $db->table(SubtaskModel::TABLE) + ->columns('title', 'time_estimated', 'position', 'user_id') + ->eq('task_id', $srcTaskId) + ->asc('position') + ->findAll(); + + foreach ($subtasks as &$subtask) { + $subtask['task_id'] = $dstTaskId; + + if (! $db->table(SubtaskModel::TABLE)->save($subtask)) { + return false; + } + } + }); + } + + /** + * Prepare data before insert/update + * + * @access protected + * @param array $values Form values + */ + protected function prepare(array &$values) + { + $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); + } + + /** + * Prepare data before insert + * + * @access protected + * @param array $values Form values + */ + protected 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; + $this->hook->reference('model:subtask:creation:prepare', $values); + } +} diff --git a/app/Model/SubtaskPositionModel.php b/app/Model/SubtaskPositionModel.php new file mode 100644 index 0000000..3c26465 --- /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 0000000..c99d605 --- /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 0000000..f9e8f3e --- /dev/null +++ b/app/Model/SubtaskTaskConversionModel.php @@ -0,0 +1,53 @@ +<?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); + $parent_task = $this->taskFinderModel->getById($subtask['task_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'], + 'swimlane_id' => $parent_task['swimlane_id'], + 'priority' => $parent_task['priority'], + 'column_id' => $parent_task['column_id'], + 'category_id' => $parent_task['category_id'], + 'color_id' => $parent_task['color_id'] + )); + + if ($task_id !== false) { + $link = $this->linkModel->getByLabel('is a child of'); + if ($link) { + $this->taskLinkModel->create($task_id, $subtask['task_id'], $link['id']); + } + + $this->tagDuplicationModel->duplicateTaskTags($parent_task['id'], $task_id); + $this->subtaskModel->remove($subtask_id); + } + + return $task_id; + } +} diff --git a/app/Model/SubtaskTimeTrackingModel.php b/app/Model/SubtaskTimeTrackingModel.php new file mode 100644 index 0000000..a48bb43 --- /dev/null +++ b/app/Model/SubtaskTimeTrackingModel.php @@ -0,0 +1,291 @@ +<?php + +namespace Kanboard\Model; + +use DateTime; +use Kanboard\Core\Base; + +/** + * Subtask time tracking + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class SubtaskTimeTrackingModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'subtask_time_tracking'; + + /** + * Get query to check if a timer is started for the given user and subtask + * + * @access public + * @param integer $user_id User id + * @return string + */ + public function getTimerQuery($user_id) + { + $sql = $this->db + ->table(self::TABLE) + ->columns('start') + ->eq($this->db->escapeIdentifier('user_id', self::TABLE), $user_id) + ->eq($this->db->escapeIdentifier('end', self::TABLE), 0) + ->eq($this->db->escapeIdentifier('subtask_id', self::TABLE), SubtaskModel::TABLE.'.id') + ->limit(1) + ->buildSelectQuery(); + // need to interpolate values into the SQL text for use as a subquery + // in SubtaskModel::getQuery() + $sql = substr_replace($sql, $user_id, strpos($sql, '?'), 1); + $sql = substr_replace($sql, 0, strpos($sql, '?'), 1); + $sql = substr_replace($sql, SubtaskModel::TABLE.'.id', strpos($sql, '?'), 1); + return $sql; + } + + /** + * Get query for user timesheet (pagination) + * + * @access public + * @param integer $user_id User id + * @return \PicoDb\Table + */ + public function getUserQuery($user_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + $this->db->escapeIdentifier('id', self::TABLE), + $this->db->escapeIdentifier('subtask_id', self::TABLE), + $this->db->escapeIdentifier('end', self::TABLE), + $this->db->escapeIdentifier('start', self::TABLE), + $this->db->escapeIdentifier('time_spent', self::TABLE), + $this->db->escapeIdentifier('task_id', SubtaskModel::TABLE), + $this->db->escapeIdentifier('title', SubtaskModel::TABLE).' AS subtask_title', + $this->db->escapeIdentifier('title', TaskModel::TABLE).' AS task_title', + $this->db->escapeIdentifier('project_id', TaskModel::TABLE), + $this->db->escapeIdentifier('color_id', TaskModel::TABLE), + ) + ->join(SubtaskModel::TABLE, 'id', 'subtask_id') + ->join(TaskModel::TABLE, 'id', 'task_id', SubtaskModel::TABLE) + ->eq($this->db->escapeIdentifier('user_id', self::TABLE), $user_id); + } + + /** + * Get query for task timesheet (pagination) + * + * @access public + * @param integer $task_id Task id + * @return \PicoDb\Table + */ + public function getTaskQuery($task_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + $this->db->escapeIdentifier('id', self::TABLE), + $this->db->escapeIdentifier('subtask_id', self::TABLE), + $this->db->escapeIdentifier('end', self::TABLE), + $this->db->escapeIdentifier('start', self::TABLE), + $this->db->escapeIdentifier('time_spent', self::TABLE), + $this->db->escapeIdentifier('user_id', self::TABLE), + $this->db->escapeIdentifier('task_id', SubtaskModel::TABLE), + $this->db->escapeIdentifier('title', SubtaskModel::TABLE).' AS subtask_title', + $this->db->escapeIdentifier('project_id', TaskModel::TABLE), + $this->db->escapeIdentifier('username', UserModel::TABLE), + $this->db->escapeIdentifier('name', UserModel::TABLE).' AS user_fullname', + ) + ->join(SubtaskModel::TABLE, 'id', 'subtask_id') + ->join(TaskModel::TABLE, 'id', 'task_id', SubtaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', self::TABLE) + ->eq($this->db->escapeIdentifier('id', TaskModel::TABLE), $task_id); + } + + /** + * Get all recorded time slots for a given user + * + * @access public + * @param integer $user_id User id + * @return array + */ + public function getUserTimesheet($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('user_id', $user_id) + ->findAll(); + } + + /** + * Return true if a timer is started for this use and subtask + * + * @access public + * @param integer $subtask_id + * @param integer $user_id + * @return boolean + */ + public function hasTimer($subtask_id, $user_id) + { + return $this->db->table(self::TABLE)->eq('subtask_id', $subtask_id)->eq('user_id', $user_id)->eq('end', 0)->exists(); + } + + /** + * 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 + * @param integer $subtask_id + * @param integer $user_id + * @return boolean + */ + public function logStartTime($subtask_id, $user_id) + { + return + ! $this->hasTimer($subtask_id, $user_id) && + $this->db + ->table(self::TABLE) + ->insert(array('subtask_id' => $subtask_id, 'user_id' => $user_id, 'start' => time(), 'end' => 0)); + } + + /** + * Log end time + * + * @access public + * @param integer $subtask_id + * @param integer $user_id + * @return boolean + */ + public function logEndTime($subtask_id, $user_id) + { + $time_spent = $this->getTimeSpent($subtask_id, $user_id); + + if ($time_spent > 0) { + $this->updateSubtaskTimeSpent($subtask_id, $time_spent); + } + + return $this->db + ->table(self::TABLE) + ->eq('subtask_id', $subtask_id) + ->eq('user_id', $user_id) + ->eq('end', 0) + ->update(array( + 'end' => time(), + 'time_spent' => $time_spent, + )); + } + + /** + * Calculate the time spent when the clock is stopped + * + * @access public + * @param integer $subtask_id + * @param integer $user_id + * @return float + */ + public function getTimeSpent($subtask_id, $user_id) + { + $hook = 'model:subtask-time-tracking:calculate:time-spent'; + $start_time = $this->db + ->table(self::TABLE) + ->eq('subtask_id', $subtask_id) + ->eq('user_id', $user_id) + ->eq('end', 0) + ->findOneColumn('start'); + + if (empty($start_time)) { + return 0; + } + + $end = new DateTime; + $start = new DateTime; + $start->setTimestamp($start_time); + + if ($this->hook->exists($hook)) { + return $this->hook->first($hook, array( + 'user_id' => $user_id, + 'start' => $start, + 'end' => $end, + )); + } + + return $this->dateParser->getHours($start, $end); + } + + /** + * Update subtask time spent + * + * @access public + * @param integer $subtask_id + * @param float $time_spent + * @return bool + */ + public function updateSubtaskTimeSpent($subtask_id, $time_spent) + { + $subtask = $this->subtaskModel->getById($subtask_id); + + return $this->subtaskModel->update(array( + 'id' => $subtask['id'], + 'time_spent' => $subtask['time_spent'] + $time_spent, + 'task_id' => $subtask['task_id'], + ), false); + } + + /** + * Update task time tracking based on subtasks time tracking + * + * @access public + * @param integer $task_id Task id + * @return bool + */ + public function updateTaskTimeTracking($task_id) + { + $values = $this->calculateSubtaskTime($task_id); + + return $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->update($values); + } + + /** + * Sum time spent and time estimated for all subtasks + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function calculateSubtaskTime($task_id) + { + return $this->db + ->table(SubtaskModel::TABLE) + ->eq('task_id', $task_id) + ->columns( + 'SUM(time_spent) AS time_spent', + 'SUM(time_estimated) AS time_estimated' + ) + ->findOne(); + } +} diff --git a/app/Model/SwimlaneModel.php b/app/Model/SwimlaneModel.php new file mode 100644 index 0000000..b70a9a8 --- /dev/null +++ b/app/Model/SwimlaneModel.php @@ -0,0 +1,459 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Swimlanes + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class SwimlaneModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'swimlanes'; + + /** + * Value for active swimlanes + * + * @var integer + */ + const ACTIVE = 1; + + /** + * Value for inactive swimlanes + * + * @var integer + */ + const INACTIVE = 0; + + /** + * Get a swimlane by the id + * + * @access public + * @param integer $swimlaneId + * @return array + */ + public function getById($swimlaneId) + { + return $this->db->table(self::TABLE)->eq('id', $swimlaneId)->findOne(); + } + + /** + * Get the swimlane name by the id + * + * @access public + * @param integer $swimlaneId + * @return string + */ + public function getNameById($swimlaneId) + { + return $this->db->table(self::TABLE) + ->eq('id', $swimlaneId) + ->findOneColumn('name'); + } + + /** + * Get a swimlane id by the project and the name + * + * @access public + * @param integer $projectId Project id + * @param string $name Name + * @return integer + */ + public function getIdByName($projectId, $name) + { + return (int) $this->db->table(self::TABLE) + ->eq('project_id', $projectId) + ->eq('name', $name) + ->findOneColumn('id'); + } + + /** + * Get a swimlane by the project and the name + * + * @access public + * @param integer $projectId Project id + * @param string $name Swimlane name + * @return array + */ + public function getByName($projectId, $name) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $projectId) + ->eq('name', $name) + ->findOne(); + } + + /** + * Get first active swimlane for a project + * + * @access public + * @param integer $projectId + * @return array|null + */ + public function getFirstActiveSwimlane($projectId) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $projectId) + ->eq('is_active', 1) + ->asc('position') + ->findOne(); + } + + /** + * Get first active swimlaneId + * + * @access public + * @param int $projectId + * @return int + */ + public function getFirstActiveSwimlaneId($projectId) + { + return (int) $this->db->table(self::TABLE) + ->eq('project_id', $projectId) + ->eq('is_active', 1) + ->asc('position') + ->findOneColumn('id'); + } + + /** + * Get all swimlanes for a given project + * + * @access public + * @param integer $projectId + * @return array + */ + public function getAll($projectId) + { + return $this->db + ->table(self::TABLE) + ->eq('project_id', $projectId) + ->orderBy('position', 'asc') + ->findAll(); + } + + /** + * Get the list of swimlanes by status + * + * @access public + * @param integer $projectId + * @param integer $status + * @return array + */ + public function getAllByStatus($projectId, $status = self::ACTIVE) + { + $query = $this->db + ->table(self::TABLE) + ->eq('project_id', $projectId) + ->eq('is_active', $status); + + if ($status == self::ACTIVE) { + $query->asc('position'); + } else { + $query->asc('name'); + } + + return $query->findAll(); + } + + /** + * Get all swimlanes with task count + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAllWithTaskCount($project_id) + { + $result = array( + 'active' => array(), + 'inactive' => array(), + ); + + $swimlanes = $this->db->table(self::TABLE) + ->columns('id', 'name', 'description', 'project_id', 'position', 'is_active', 'task_limit') + ->subquery("SELECT COUNT(*) FROM ".TaskModel::TABLE." WHERE swimlane_id=".self::TABLE.".id AND is_active='1'", 'nb_open_tasks') + ->subquery("SELECT COUNT(*) FROM ".TaskModel::TABLE." WHERE swimlane_id=".self::TABLE.".id AND is_active='0'", 'nb_closed_tasks') + ->eq('project_id', $project_id) + ->asc('position') + ->asc('name') + ->findAll(); + + foreach ($swimlanes as $swimlane) { + if ($swimlane['is_active']) { + $result['active'][] = $swimlane; + } else { + $result['inactive'][] = $swimlane; + } + } + + return $result; + } + + /** + * Get list of all swimlanes + * + * @access public + * @param integer $projectId Project id + * @param boolean $prepend Prepend default value + * @param boolean $onlyActive Return only active swimlanes + * @return array + */ + public function getList($projectId, $prepend = false, $onlyActive = false) + { + $swimlanes = array(); + + if ($prepend) { + $swimlanes[-1] = t('All swimlanes'); + } + + return $swimlanes + $this->db + ->hashtable(self::TABLE) + ->eq('project_id', $projectId) + ->in('is_active', $onlyActive ? array(self::ACTIVE) : array(self::ACTIVE, self::INACTIVE)) + ->orderBy('position', 'asc') + ->getAll('id', 'name'); + } + + /** + * Add a new swimlane + * + * @access public + * @param int $projectId + * @param string $name + * @param string $description + * @return bool|int + */ + public function create($projectId, $name, $description = '', $task_limit = 0) + { + if (! $this->projectModel->exists($projectId)) { + return 0; + } + + return $this->db->table(self::TABLE)->persist(array( + 'project_id' => $projectId, + 'name' => $name, + 'description' => $description, + 'position' => $this->getLastPosition($projectId), + 'is_active' => 1, + 'task_limit' => intval($task_limit), + )); + } + + /** + * Update a swimlane + * + * @access public + * @param integer $swimlaneId + * @param array $values + * @return bool + */ + public function update($swimlaneId, array $values) + { + unset($values['id']); + unset($values['project_id']); + + return $this->db + ->table(self::TABLE) + ->eq('id', $swimlaneId) + ->update($values); + } + + /** + * Get the last position of a swimlane + * + * @access public + * @param integer $projectId + * @return integer + */ + public function getLastPosition($projectId) + { + return $this->db + ->table(self::TABLE) + ->eq('project_id', $projectId) + ->eq('is_active', 1) + ->count() + 1; + } + + /** + * Disable a swimlane + * + * @access public + * @param integer $projectId + * @param integer $swimlaneId + * @return bool + */ + public function disable($projectId, $swimlaneId) + { + $result = $this->db + ->table(self::TABLE) + ->eq('id', $swimlaneId) + ->eq('project_id', $projectId) + ->update(array( + 'is_active' => self::INACTIVE, + 'position' => 0, + )); + + if ($result) { + $this->updatePositions($projectId); + } + + return $result; + } + + /** + * Enable a swimlane + * + * @access public + * @param integer $projectId + * @param integer $swimlaneId + * @return bool + */ + public function enable($projectId, $swimlaneId) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $swimlaneId) + ->eq('project_id', $projectId) + ->update(array( + 'is_active' => self::ACTIVE, + 'position' => $this->getLastPosition($projectId), + )); + } + + /** + * Remove a swimlane + * + * @access public + * @param integer $projecId + * @param integer $swimlaneId + * @return bool + */ + public function remove($projecId, $swimlaneId) + { + $this->db->startTransaction(); + + if ($this->db->table(TaskModel::TABLE)->eq('swimlane_id', $swimlaneId)->exists()) { + $this->db->cancelTransaction(); + return false; + } + + if (! $this->db->table(self::TABLE)->eq('id', $swimlaneId)->eq('project_id', $projecId)->remove()) { + $this->db->cancelTransaction(); + return false; + } + + $this->updatePositions($projecId); + $this->db->closeTransaction(); + + return true; + } + + /** + * Update swimlane positions after disabling or removing a swimlane + * + * @access public + * @param integer $projectId + * @return boolean + */ + public function updatePositions($projectId) + { + $position = 0; + $swimlanes = $this->db + ->table(self::TABLE) + ->eq('project_id', $projectId) + ->eq('is_active', 1) + ->asc('position') + ->asc('id') + ->findAllByColumn('id'); + + if (! $swimlanes) { + return false; + } + + foreach ($swimlanes as $swimlane_id) { + $this->db->table(self::TABLE) + ->eq('id', $swimlane_id) + ->update(array('position' => ++$position)); + } + + return true; + } + + /** + * Change swimlane position + * + * @access public + * @param integer $projectId + * @param integer $swimlaneId + * @param integer $position + * @return boolean + */ + public function changePosition($projectId, $swimlaneId, $position) + { + if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('project_id', $projectId)->count()) { + return false; + } + + $swimlaneIds = $this->db->table(self::TABLE) + ->eq('is_active', 1) + ->eq('project_id', $projectId) + ->neq('id', $swimlaneId) + ->asc('position') + ->findAllByColumn('id'); + + $offset = 1; + $results = array(); + + foreach ($swimlaneIds as $currentSwimlaneId) { + if ($offset == $position) { + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $currentSwimlaneId)->update(array('position' => $offset)); + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $swimlaneId)->eq('project_id', $projectId)->update(array('position' => $position)); + + return !in_array(false, $results, true); + } + + /** + * Duplicate Swimlane to project + * + * @access public + * @param integer $projectSrcId + * @param integer $projectDstId + * @return boolean + */ + public function duplicate($projectSrcId, $projectDstId) + { + $swimlanes = $this->getAll($projectSrcId); + + foreach ($swimlanes as $swimlane) { + if (! $this->db->table(self::TABLE)->eq('project_id', $projectDstId)->eq('name', $swimlane['name'])->exists()) { + $values = array( + 'name' => $swimlane['name'], + 'description' => $swimlane['description'], + 'position' => $swimlane['position'], + 'is_active' => $swimlane['is_active'] ? self::ACTIVE : self::INACTIVE, // Avoid SQL error with Postgres + 'project_id' => $projectDstId, + ); + + if (! $this->db->table(self::TABLE)->persist($values)) { + return false; + } + } + } + + return true; + } +} diff --git a/app/Model/TagDuplicationModel.php b/app/Model/TagDuplicationModel.php new file mode 100644 index 0000000..ebca395 --- /dev/null +++ b/app/Model/TagDuplicationModel.php @@ -0,0 +1,96 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Tag Duplication + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TagDuplicationModel extends Base +{ + /** + * Duplicate project tags to another project + * + * @access public + * @param integer $src_project_id + * @param integer $dst_project_id + * @return bool + */ + public function duplicate($src_project_id, $dst_project_id) + { + $tags = $this->tagModel->getAllByProject($src_project_id); + $results = array(); + + foreach ($tags as $tag) { + $results[] = $this->tagModel->create($dst_project_id, $tag['name'], $tag['color_id']); + } + + return ! in_array(false, $results, true); + } + + /** + * Link tags to the new tasks + * + * @access public + * @param integer $src_task_id + * @param integer $dst_task_id + * @param integer $dst_project_id + */ + public function duplicateTaskTagsToAnotherProject($src_task_id, $dst_task_id, $dst_project_id) + { + $tags = $this->taskTagModel->getTagsByTask($src_task_id); + + foreach ($tags as $tag) { + $tag_id = $this->tagModel->getIdByName($dst_project_id, $tag['name']); + + if (empty($tag_id)) { + $tag_id = $this->tagModel->create($dst_project_id, $tag['name'], $tag['color_id']); + } + + $this->taskTagModel->associateTag($dst_task_id, $tag_id); + } + } + + /** + * Duplicate tags to the new task + * + * @access public + * @param integer $src_task_id + * @param integer $dst_task_id + */ + public function duplicateTaskTags($src_task_id, $dst_task_id) + { + $tags = $this->taskTagModel->getTagsByTask($src_task_id); + + foreach ($tags as $tag) { + $this->taskTagModel->associateTag($dst_task_id, $tag['id']); + } + } + + /** + * Sync tags that are not available in destination project + * + * @access public + * @param integer $task_id + * @param integer $dst_project_id + */ + public function syncTaskTagsToAnotherProject($task_id, $dst_project_id) + { + $tags = $this->taskTagModel->getTagsByTaskNotAvailableInProject($task_id, $dst_project_id); + + foreach ($tags as $tag) { + $tag_id = $this->tagModel->getIdByName($dst_project_id, $tag['name']); + + if (empty($tag_id)) { + $tag_id = $this->tagModel->create($dst_project_id, $tag['name'], $tag['color_id']); + } + + $this->taskTagModel->dissociateTag($task_id, $tag['id']); + $this->taskTagModel->associateTag($task_id, $tag_id); + } + } +} diff --git a/app/Model/TagModel.php b/app/Model/TagModel.php new file mode 100644 index 0000000..b9d0fd2 --- /dev/null +++ b/app/Model/TagModel.php @@ -0,0 +1,217 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Class TagModel + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TagModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'tags'; + + /** + * Get all tags + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->table(self::TABLE)->asc('name')->findAll(); + } + + /** + * Get all tags by project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAllByProject($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('name')->findAll(); + } + + /** + * Get all global tags and tags for the given project IDs + * + * @access public + * @param array $project_ids + * @return array + */ + public function getAllByProjectIds(array $project_ids) + { + return $this->db->table(self::TABLE) + ->beginOr() + ->eq('project_id', 0) + ->in('project_id', $project_ids) + ->closeOr() + ->asc('name') + ->findAll(); + } + + /** + * Get assignable tags for a project + * + * @param integer $project_id Project Id + * @param bool $include_global_tags Flag to include global tags + * @return array + */ + public function getAssignableList($project_id, $include_global_tags = true) + { + if ($include_global_tags) { + return $this->db->hashtable(self::TABLE) + ->beginOr() + ->eq('project_id', $project_id) + ->eq('project_id', 0) + ->closeOr() + ->asc('name') + ->getAll('id', 'name'); + } else { + return $this->db->hashtable(self::TABLE) + ->beginOr() + ->eq('project_id', $project_id) + ->closeOr() + ->asc('name') + ->getAll('id', 'name'); + } + } + + /** + * Get one tag + * + * @access public + * @param integer $tag_id + * @return array|null + */ + public function getById($tag_id) + { + return $this->db->table(self::TABLE)->eq('id', $tag_id)->findOne(); + } + + /** + * Get tag id from tag name + * + * @access public + * @param int $project_id + * @param string $tag + * @return integer + */ + public function getIdByName($project_id, $tag) + { + return $this->db + ->table(self::TABLE) + ->beginOr() + ->eq('project_id', 0) + ->eq('project_id', $project_id) + ->closeOr() + ->ilike('name', $tag) + ->asc('project_id') + ->findOneColumn('id'); + } + + /** + * Return true if the tag exists + * + * @access public + * @param integer $project_id + * @param string $tag + * @param integer $tag_id + * @return boolean + */ + public function exists($project_id, $tag, $tag_id = 0) + { + return $this->db + ->table(self::TABLE) + ->neq('id', $tag_id) + ->beginOr() + ->eq('project_id', 0) + ->eq('project_id', $project_id) + ->closeOr() + ->ilike('name', $tag) + ->asc('project_id') + ->exists(); + } + + /** + * Return tag id and create a new tag if necessary + * + * @access public + * @param int $project_id + * @param string $tag + * @return bool|int + */ + public function findOrCreateTag($project_id, $tag) + { + $tag_id = $this->getIdByName($project_id, $tag); + + if (empty($tag_id)) { + $tag_id = $this->create($project_id, $tag); + } + + return $tag_id; + } + + /** + * Add a new tag + * + * @access public + * @param int $project_id + * @param string $tag + * @return bool|int + */ + public function create($project_id, $tag, $color_id = null) + { + return $this->db->table(self::TABLE)->persist(array( + 'project_id' => $project_id, + 'name' => $tag, + 'color_id' => $color_id, + )); + } + + /** + * Update a tag + * + * @access public + * @param integer $tag_id + * @param string $tag + * @return bool + */ + public function update($tag_id, $tag, $color_id = null, $project_id = null) + { + if ($project_id !== null) { + return $this->db->table(self::TABLE)->eq('id', $tag_id)->update(array( + 'name' => $tag, + 'color_id' => $color_id, + 'project_id' => $project_id, + )); + } else { + return $this->db->table(self::TABLE)->eq('id', $tag_id)->update(array( + 'name' => $tag, + 'color_id' => $color_id, + )); + } + } + + /** + * Remove a tag + * + * @access public + * @param integer $tag_id + * @return bool + */ + public function remove($tag_id) + { + return $this->db->table(self::TABLE)->eq('id', $tag_id)->remove(); + } +} diff --git a/app/Model/TaskAnalyticModel.php b/app/Model/TaskAnalyticModel.php new file mode 100644 index 0000000..3d6fe8a --- /dev/null +++ b/app/Model/TaskAnalyticModel.php @@ -0,0 +1,72 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Analytic + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskAnalyticModel extends Base +{ + /** + * Get the time between date_creation and date_completed or now if empty + * + * @access public + * @param array $task + * @return integer + */ + public function getLeadTime(array $task) + { + return ($task['date_completed'] ?: time()) - $task['date_creation']; + } + + /** + * Get the time between date_started and date_completed or now if empty + * + * @access public + * @param array $task + * @return integer + */ + public function getCycleTime(array $task) + { + if (empty($task['date_started'])) { + return 0; + } + + return ($task['date_completed'] ?: time()) - $task['date_started']; + } + + /** + * Get the average time spent in each column + * + * @access public + * @param array $task + * @return array + */ + public function getTimeSpentByColumn(array $task) + { + $result = array(); + $columns = $this->columnModel->getList($task['project_id']); + $sums = $this->transitionModel->getTimeSpentByTask($task['id']); + + foreach ($columns as $column_id => $column_title) { + $time_spent = isset($sums[$column_id]) ? $sums[$column_id] : 0; + + if ($task['column_id'] == $column_id) { + $time_spent += ($task['date_completed'] ?: time()) - $task['date_moved']; + } + + $result[] = array( + 'id' => $column_id, + 'title' => $column_title, + 'time_spent' => $time_spent, + ); + } + + return $result; + } +} diff --git a/app/Model/TaskCreationModel.php b/app/Model/TaskCreationModel.php new file mode 100644 index 0000000..8f10ea4 --- /dev/null +++ b/app/Model/TaskCreationModel.php @@ -0,0 +1,94 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Creation + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskCreationModel extends Base +{ + /** + * Create a task + * + * @access public + * @param array $values Form values + * @return integer + */ + public function create(array $values) + { + $position = empty($values['position']) ? 0 : $values['position']; + $tags = array(); + + if (isset($values['tags'])) { + $tags = $values['tags']; + unset($values['tags']); + } + + $this->prepare($values); + $task_id = $this->db->table(TaskModel::TABLE)->persist($values); + + if ($task_id !== false) { + if ($position > 0 && $values['position'] > 1) { + $this->taskPositionModel->movePosition($values['project_id'], $task_id, $values['column_id'], $position, $values['swimlane_id'], false); + } + + if (! empty($tags)) { + $this->taskTagModel->save($values['project_id'], $task_id, $tags); + } + + $this->queueManager->push($this->taskEventJob->withParams( + $task_id, + array(TaskModel::EVENT_CREATE_UPDATE, TaskModel::EVENT_CREATE) + )); + } + + $this->hook->reference('model:task:creation:aftersave', $task_id); + + return (int) $task_id; + } + + /** + * Prepare data + * + * @access protected + * @param array $values Form values + */ + protected function prepare(array &$values) + { + $values = $this->dateParser->convert($values, array('date_due'), true); + $values = $this->dateParser->convert($values, array('date_started'), true); + + $this->helper->model->removeFields($values, array('another_task', 'duplicate_multiple_projects')); + $this->helper->model->resetFields($values, array('creator_id', 'owner_id', 'date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent')); + + if (empty($values['column_id'])) { + $values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']); + } + + if (empty($values['color_id'])) { + $values['color_id'] = $this->colorModel->getDefaultColor(); + } + + if (empty($values['title'])) { + $values['title'] = t('Untitled'); + } + + // Note: Do not override the creator_id if the task is imported + if (empty($values['creator_id']) && $this->userSession->isLogged()) { + $values['creator_id'] = $this->userSession->getId(); + } + + $values['swimlane_id'] = empty($values['swimlane_id']) ? $this->swimlaneModel->getFirstActiveSwimlaneId($values['project_id']) : $values['swimlane_id']; + $values['date_creation'] = time(); + $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; + + $this->hook->reference('model:task:creation:prepare', $values); + } +} diff --git a/app/Model/TaskDuplicationModel.php b/app/Model/TaskDuplicationModel.php new file mode 100644 index 0000000..24845da --- /dev/null +++ b/app/Model/TaskDuplicationModel.php @@ -0,0 +1,169 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Duplication + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskDuplicationModel extends Base +{ + /** + * Fields to copy when duplicating a task + * + * @access protected + * @var string[] + */ + protected $fieldsToDuplicate = array( + 'title', + 'description', + 'date_due', + 'color_id', + 'project_id', + 'column_id', + 'owner_id', + 'score', + 'priority', + 'category_id', + 'time_estimated', + 'swimlane_id', + 'recurrence_status', + 'recurrence_trigger', + 'recurrence_factor', + 'recurrence_timeframe', + 'recurrence_basedate', + 'external_provider', + 'external_uri', + 'reference', + ); + + /** + * Duplicate a task to the same project + * + * @access public + * @param integer $task_id Task id + * @return boolean|integer Duplicated task id + */ + public function duplicate($task_id) + { + $values = $this->copyFields($task_id); + $values['title'] = t('[DUPLICATE]').' '.$values['title']; + + $new_task_id = $this->save($task_id, $values); + + if ($new_task_id !== false) { + $this->tagDuplicationModel->duplicateTaskTags($task_id, $new_task_id); + $this->taskLinkModel->create($new_task_id, $task_id, 4); + + $externalLinks = $this->taskExternalLinkModel->getAll($task_id); + foreach ($externalLinks as $externalLink) { + $this->taskExternalLinkModel->create([ + 'task_id' => $new_task_id, + 'creator_id' => $externalLink['creator_id'], + 'dependency' => $externalLink['dependency'], + 'title' => $externalLink['title'], + 'link_type' => $externalLink['link_type'], + 'url' => $externalLink['url'], + ]); + } + } + + $hook_values = ['source_task_id' => $task_id, 'destination_task_id' => $new_task_id]; + $this->hook->reference('model:task:duplication:aftersave', $hook_values); + + return $new_task_id; + } + + /** + * Check if the assignee and the category are available in the destination project + * + * @access public + * @param array $values + * @return array + */ + public function checkDestinationProjectValues(array &$values) + { + // Check if the assigned user is allowed for the destination project + if ($values['owner_id'] > 0 && ! $this->projectPermissionModel->isUserAllowed($values['project_id'], $values['owner_id'])) { + $values['owner_id'] = 0; + } + + // Check if the category exists for the destination project + if ($values['category_id'] > 0) { + $values['category_id'] = $this->categoryModel->getIdByName( + $values['project_id'], + $this->categoryModel->getNameById($values['category_id']) + ); + } + + // Check if the swimlane exists for the destination project + $values['swimlane_id'] = $this->swimlaneModel->getIdByName( + $values['project_id'], + $this->swimlaneModel->getNameById($values['swimlane_id']) + ); + + if ($values['swimlane_id'] == 0) { + $values['swimlane_id'] = $this->swimlaneModel->getFirstActiveSwimlaneId($values['project_id']); + } + + // Check if the column exists for the destination project + if ($values['column_id'] > 0) { + $values['column_id'] = $this->columnModel->getColumnIdByTitle( + $values['project_id'], + $this->columnModel->getColumnTitleById($values['column_id']) + ); + + $values['column_id'] = $values['column_id'] ?: $this->columnModel->getFirstColumnId($values['project_id']); + } + + // Check if priority exists for destination project + $values['priority'] = $this->projectTaskPriorityModel->getPriorityForProject( + $values['project_id'], + empty($values['priority']) ? 0 : $values['priority'] + ); + + return $values; + } + + /** + * Duplicate fields for the new task + * + * @access protected + * @param integer $task_id Task id + * @return array + */ + protected function copyFields($task_id) + { + $task = $this->taskFinderModel->getById($task_id); + $values = array(); + + foreach ($this->fieldsToDuplicate as $field) { + $values[$field] = $task[$field]; + } + + return $values; + } + + /** + * Create the new task and duplicate subtasks + * + * @access protected + * @param integer $task_id Task id + * @param array $values Form values + * @return boolean|integer + */ + protected function save($task_id, array $values) + { + $new_task_id = $this->taskCreationModel->create($values); + + if ($new_task_id !== false) { + $this->subtaskModel->duplicate($task_id, $new_task_id); + } + + return $new_task_id; + } +} diff --git a/app/Model/TaskExternalLinkModel.php b/app/Model/TaskExternalLinkModel.php new file mode 100644 index 0000000..4d3488f --- /dev/null +++ b/app/Model/TaskExternalLinkModel.php @@ -0,0 +1,103 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task External Link Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskExternalLinkModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'task_has_external_links'; + + /** + * Get all links + * + * @access public + * @param integer $task_id + * @return array + */ + public function getAll($task_id) + { + $types = $this->externalLinkManager->getTypes(); + + $links = $this->db->table(self::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.name AS creator_name', UserModel::TABLE.'.username AS creator_username') + ->eq('task_id', $task_id) + ->asc('title') + ->join(UserModel::TABLE, 'id', 'creator_id') + ->findAll(); + + foreach ($links as &$link) { + $link['dependency_label'] = $this->externalLinkManager->getDependencyLabel($link['link_type'], $link['dependency']); + $link['type'] = isset($types[$link['link_type']]) ? $types[$link['link_type']] : t('Unknown'); + } + + return $links; + } + + /** + * Get link + * + * @access public + * @param integer $link_id + * @return array + */ + public function getById($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne(); + } + + /** + * Add a new link in the database + * + * @access public + * @param array $values Form values + * @return boolean|integer + */ + public function create(array $values) + { + unset($values['id']); + $values['creator_id'] = $this->userSession->getId(); + $values['date_creation'] = time(); + $values['date_modification'] = $values['date_creation']; + + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Modify external link + * + * @access public + * @param array $values Form values + * @return boolean + */ + public function update(array $values) + { + $values['date_modification'] = time(); + $updates = $values; + unset($updates['id']); + return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($updates); + } + + /** + * Remove a link + * + * @access public + * @param integer $link_id + * @return boolean + */ + public function remove($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->remove(); + } +} diff --git a/app/Model/TaskFileModel.php b/app/Model/TaskFileModel.php new file mode 100644 index 0000000..9d44600 --- /dev/null +++ b/app/Model/TaskFileModel.php @@ -0,0 +1,115 @@ +<?php + +namespace Kanboard\Model; + +/** + * Task File Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskFileModel extends FileModel +{ + /** + * Table name + * + * @var string + */ + const TABLE = 'task_has_files'; + + /** + * Events + * + * @var string + */ + const EVENT_CREATE = 'task.file.create'; + const EVENT_DESTROY = 'task.file.destroy'; + + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + protected function getTable() + { + return self::TABLE; + } + + /** + * Define the foreign key + * + * @abstract + * @access protected + * @return string + */ + protected function getForeignKey() + { + return 'task_id'; + } + + /** + * Define the path prefix + * + * @abstract + * @access protected + * @return string + */ + protected function getPathPrefix() + { + return 'tasks'; + } + + /** + * Get projectId from fileId + * + * @access public + * @param integer $file_id + * @return integer + */ + public function getProjectId($file_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $file_id) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** + * Handle screenshot upload + * + * @access public + * @param integer $task_id Task id + * @param string $blob Base64 encoded image + * @return bool|integer + */ + public function uploadScreenshot($task_id, $blob) + { + $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)); + } + + /** + * Fire file destruction event + * + * @access protected + * @param integer $file_id + */ + protected function fireDestructionEvent($file_id) + { + $this->queueManager->push($this->taskFileEventJob->withParams($file_id, self::EVENT_DESTROY)); + } +} diff --git a/app/Model/TaskFinderModel.php b/app/Model/TaskFinderModel.php new file mode 100644 index 0000000..6cf787d --- /dev/null +++ b/app/Model/TaskFinderModel.php @@ -0,0 +1,415 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Finder model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskFinderModel extends Base +{ + /** + * Get query for project user overview + * + * @access public + * @param array $project_ids + * @param integer $is_active + * @return \PicoDb\Table + */ + public function getProjectUserOverviewQuery(array $project_ids, $is_active) + { + if (empty($project_ids)) { + $project_ids = array(-1); + } + + return $this->db + ->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.date_started', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.priority', + TaskModel::TABLE.'.time_spent', + TaskModel::TABLE.'.time_estimated', + ProjectModel::TABLE.'.name AS project_name', + ColumnModel::TABLE.'.title AS column_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->eq(TaskModel::TABLE.'.is_active', $is_active) + ->in(ProjectModel::TABLE.'.id', $project_ids) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE); + } + + /** + * Get query for assigned user tasks + * + * @access public + * @param integer $user_id User id + * @return \PicoDb\Table + */ + public function getUserQuery($user_id) + { + return $this->getExtendedQuery() + ->beginOr() + ->eq(TaskModel::TABLE.'.owner_id', $user_id) + ->inSubquery(TaskModel::TABLE.'.id', $this->db->table(SubtaskModel::TABLE)->columns('task_id')->eq('user_id', $user_id)) + ->closeOr() + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE) + ->eq(ColumnModel::TABLE.'.hide_in_dashboard', 0); + } + + /** + * Extended query + * + * @access public + * @return \PicoDb\Table + */ + public function getExtendedQuery() + { + return $this->db + ->table(TaskModel::TABLE) + ->columns( + '(SELECT COUNT(*) FROM '.CommentModel::TABLE.' WHERE task_id=tasks.id) AS nb_comments', + '(SELECT COUNT(*) FROM '.TaskFileModel::TABLE.' WHERE task_id=tasks.id) AS nb_files', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id) AS nb_subtasks', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks', + '(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.' tl JOIN '.LinkModel::TABLE.' l ON tl.link_id = l.id WHERE tl.task_id = tasks.id AND l.label = '."'is a milestone of') AS is_milestone", + 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', + TaskModel::TABLE.'.reference', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name', + UserModel::TABLE.'.email AS assignee_email', + UserModel::TABLE.'.avatar_path AS assignee_avatar_path', + CategoryModel::TABLE.'.name AS category_name', + CategoryModel::TABLE.'.description AS category_description', + CategoryModel::TABLE.'.color_id AS category_color_id', + ColumnModel::TABLE.'.title AS column_name', + ColumnModel::TABLE.'.position AS column_position', + SwimlaneModel::TABLE.'.name AS swimlane_name', + ProjectModel::TABLE.'.name AS project_name' + ) + ->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); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param integer $status_id Status id + * @return array + */ + public function getAll($project_id, $status_id = TaskModel::STATUS_OPEN) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->eq(TaskModel::TABLE.'.is_active', $status_id) + ->asc(TaskModel::TABLE.'.id') + ->findAll(); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id + * @param array $status + * @return array + */ + public function getAllIds($project_id, array $status = array(TaskModel::STATUS_OPEN)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->in(TaskModel::TABLE.'.is_active', $status) + ->asc(TaskModel::TABLE.'.id') + ->findAllByColumn(TaskModel::TABLE.'.id'); + } + + /** + * Get overdue tasks query + * + * @access public + * @return \PicoDb\Table + */ + public function getOverdueTasksQuery() + { + return $this->db->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.creator_id', + TaskModel::TABLE.'.owner_id', + ProjectModel::TABLE.'.name AS project_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->eq(ProjectModel::TABLE.'.is_active', 1) + ->eq(TaskModel::TABLE.'.is_active', 1) + ->neq(TaskModel::TABLE.'.date_due', 0) + ->lte(TaskModel::TABLE.'.date_due', time()); + } + + /** + * Get a list of overdue tasks for all projects + * + * @access public + * @return array + */ + public function getOverdueTasks() + { + return $this->getOverdueTasksQuery()->findAll(); + } + + /** + * Get a list of overdue tasks by project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getOverdueTasksByProject($project_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.project_id', $project_id)->findAll(); + } + + /** + * Get a list of overdue tasks by user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getOverdueTasksByUser($user_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.owner_id', $user_id)->findAll(); + } + + /** + * Get project id for a given task + * + * @access public + * @param integer $task_id Task id + * @return integer + */ + public function getProjectId($task_id) + { + return (int) $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('project_id') ?: 0; + } + + /** + * Fetch a task by the id + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getById($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOne(); + } + + /** + * Fetch a task by the reference (external id) + * + * @access public + * @param integer $project_id Project id + * @param string $reference Task reference + * @return array + */ + public function getByReference($project_id, $reference) + { + return $this->db->table(TaskModel::TABLE)->eq('project_id', $project_id)->eq('reference', $reference)->findOne(); + } + + /** + * Get task details (fetch more information from other tables) + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getDetails($task_id) + { + 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', + 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' + ) + ->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(); + } + + /** + * Get iCal query + * + * @access public + * @return \PicoDb\Table + */ + public function getICalQuery() + { + return $this->db->table(TaskModel::TABLE) + ->left(UserModel::TABLE, 'ua', 'id', TaskModel::TABLE, 'owner_id') + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->columns( + TaskModel::TABLE.'.*', + 'ua.email AS assignee_email', + 'ua.name AS assignee_name', + 'ua.username AS assignee_username', + 'uc.email AS creator_email', + 'uc.name AS creator_name', + 'uc.username AS creator_username' + ); + } + + /** + * Count all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param array $status List of status id + * @return integer + */ + public function countByProjectId($project_id, array $status = array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->in('is_active', $status) + ->count(); + } + + /** + * Count the number of tasks for a given column and status + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param array $status + * @return int + */ + public function countByColumnId($project_id, $column_id, array $status = array(TaskModel::STATUS_OPEN)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->in('is_active', $status) + ->count(); + } + + /** + * Count the number of tasks for a given column and swimlane + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param integer $swimlane_id Swimlane id + * @return integer + */ + public function countByColumnAndSwimlaneId($project_id, $column_id, $swimlane_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->eq('swimlane_id', $swimlane_id) + ->eq('is_active', 1) + ->count(); + } + + /** + * Return true if the task exists + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function exists($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->exists(); + } + + /** + * Get project token + * + * @access public + * @param integer $task_id + * @return string + */ + public function getProjectToken($task_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.id', $task_id) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->findOneColumn(ProjectModel::TABLE.'.token'); + } +} diff --git a/app/Model/TaskLinkModel.php b/app/Model/TaskLinkModel.php new file mode 100644 index 0000000..116e043 --- /dev/null +++ b/app/Model/TaskLinkModel.php @@ -0,0 +1,305 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * TaskLink model + * + * @package Kanboard\Model + * @author Olivier Maridat + * @author Frederic Guillot + */ +class TaskLinkModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'task_has_links'; + + /** + * Events + * + * @var string + */ + const EVENT_CREATE_UPDATE = 'task_internal_link.create_update'; + const EVENT_DELETE = 'task_internal_link.delete'; + + /** + * Get projectId from $task_link_id + * + * @access public + * @param integer $task_link_id + * @return integer + */ + public function getProjectId($task_link_id) + { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $task_link_id) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->findOneColumn(TaskModel::TABLE . '.project_id') ?: 0; + } + + /** + * Get a task link + * + * @access public + * @param integer $task_link_id Task link id + * @return array + */ + public function getById($task_link_id) + { + 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(); + } + + /** + * Get the opposite task link (use the unique index task_has_links_unique) + * + * @access public + * @param array $task_link + * @return array + */ + public function getOppositeTaskLink(array $task_link) + { + $opposite_link_id = $this->linkModel->getOppositeLinkId($task_link['link_id']); + + return $this->db->table(self::TABLE) + ->eq('opposite_task_id', $task_link['task_id']) + ->eq('task_id', $task_link['opposite_task_id']) + ->eq('link_id', $opposite_link_id) + ->findOne(); + } + + /** + * Get all links attached to a task + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getAll($task_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.id', + self::TABLE.'.opposite_task_id AS task_id', + LinkModel::TABLE.'.label', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.is_active', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.column_id', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.date_completed', + TaskModel::TABLE.'.date_started', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.time_spent AS task_time_spent', + TaskModel::TABLE.'.time_estimated AS task_time_estimated', + TaskModel::TABLE.'.owner_id AS task_assignee_id', + UserModel::TABLE.'.username AS task_assignee_username', + UserModel::TABLE.'.name AS task_assignee_name', + ColumnModel::TABLE.'.title AS column_title', + ProjectModel::TABLE.'.name AS project_name' + ) + ->eq(self::TABLE.'.task_id', $task_id) + ->join(LinkModel::TABLE, 'id', 'link_id') + ->join(TaskModel::TABLE, 'id', 'opposite_task_id') + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE) + ->asc(LinkModel::TABLE.'.id') + ->desc(ColumnModel::TABLE.'.position') + ->desc(TaskModel::TABLE.'.is_active') + ->asc(TaskModel::TABLE.'.position') + ->asc(TaskModel::TABLE.'.id') + ->findAll(); + } + + /** + * Get all links attached to a task grouped by label + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getAllGroupedByLabel($task_id) + { + $links = $this->getAll($task_id); + $result = array(); + + foreach ($links as $link) { + if (! isset($result[$link['label']])) { + $result[$link['label']] = array(); + } + + $result[$link['label']][] = $link; + } + + return $result; + } + + /** + * 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|boolean + */ + public function create($task_id, $opposite_task_id, $link_id) + { + $this->db->startTransaction(); + + $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); + + 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); + + return $task_link_id1; + } + + /** + * Update a task link + * + * @access public + * @param integer $task_link_id Task link id + * @param integer $task_id Task id + * @param integer $opposite_task_id Opposite task id + * @param integer $link_id Link id + * @return boolean + */ + public function update($task_link_id, $task_id, $opposite_task_id, $link_id) + { + $this->db->startTransaction(); + + $task_link = $this->getById($task_link_id); + $opposite_task_link = $this->getOppositeTaskLink($task_link); + $opposite_link_id = $this->linkModel->getOppositeLinkId($link_id); + + $result1 = $this->updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id); + $result2 = $this->updateTaskLink($opposite_task_link['id'], $opposite_task_id, $task_id, $opposite_link_id); + + 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); + + return true; + } + + /** + * Remove a link between two tasks + * + * @access public + * @param integer $task_link_id + * @return boolean + */ + 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']); + + $result1 = $this->db + ->table(self::TABLE) + ->eq('id', $task_link_id) + ->remove(); + + $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(); + + 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/TaskMetadataModel.php b/app/Model/TaskMetadataModel.php new file mode 100644 index 0000000..dc3f56e --- /dev/null +++ b/app/Model/TaskMetadataModel.php @@ -0,0 +1,35 @@ +<?php + +namespace Kanboard\Model; + +/** + * Task Metadata + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskMetadataModel extends MetadataModel +{ + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + protected function getTable() + { + return 'task_has_metadata'; + } + + /** + * Define the entity key + * + * @access protected + * @return string + */ + protected function getEntityKey() + { + return 'task_id'; + } +} diff --git a/app/Model/TaskModel.php b/app/Model/TaskModel.php new file mode 100644 index 0000000..b437eb2 --- /dev/null +++ b/app/Model/TaskModel.php @@ -0,0 +1,156 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'tasks'; + + /** + * Task status + * + * @var integer + */ + const STATUS_OPEN = 1; + const STATUS_CLOSED = 0; + + /** + * Events + * + * @var string + */ + const EVENT_MOVE_PROJECT = 'task.move.project'; + const EVENT_MOVE_COLUMN = 'task.move.column'; + const EVENT_MOVE_POSITION = 'task.move.position'; + const EVENT_MOVE_SWIMLANE = 'task.move.swimlane'; + const EVENT_UPDATE = 'task.update'; + const EVENT_CREATE = 'task.create'; + const EVENT_CLOSE = 'task.close'; + const EVENT_OPEN = 'task.open'; + const EVENT_CREATE_UPDATE = 'task.create_update'; + const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change'; + const EVENT_OVERDUE = 'task.overdue'; + const EVENT_USER_MENTION = 'task.user.mention'; + const EVENT_DAILY_CRONJOB = 'task.cronjob.daily'; + + /** + * Recurrence: status + * + * @var integer + */ + const RECURRING_STATUS_NONE = 0; + const RECURRING_STATUS_PENDING = 1; + const RECURRING_STATUS_PROCESSED = 2; + + /** + * Recurrence: trigger + * + * @var integer + */ + const RECURRING_TRIGGER_FIRST_COLUMN = 0; + const RECURRING_TRIGGER_LAST_COLUMN = 1; + const RECURRING_TRIGGER_CLOSE = 2; + + /** + * Recurrence: timeframe + * + * @var integer + */ + const RECURRING_TIMEFRAME_DAYS = 0; + const RECURRING_TIMEFRAME_MONTHS = 1; + const RECURRING_TIMEFRAME_YEARS = 2; + + /** + * Recurrence: base date used to calculate new due date + * + * @var integer + */ + const RECURRING_BASEDATE_DUEDATE = 0; + const RECURRING_BASEDATE_TRIGGERDATE = 1; + + /** + * Remove a task + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function remove($task_id) + { + if (!$this->taskFinderModel->exists($task_id)) { + return false; + } + + $this->taskFileModel->removeAll($task_id); + + return $this->db->table(self::TABLE)->eq('id', $task_id)->remove(); + } + + /** + * Get a the task id from a text + * + * Example: "Fix bug #1234" will return 1234 + * + * @access public + * @param string $message Text + * @return integer + */ + public function getTaskIdFromText($message) + { + if (preg_match('!#(\d+)!i', $message, $matches) && isset($matches[1])) { + return $matches[1]; + } + + return 0; + } + + /** + * Get task progress based on the column position + * + * @access public + * @param array $task + * @param array $columns + * @return integer + */ + public function getProgress(array $task, array $columns) + { + if ($task['is_active'] == self::STATUS_CLOSED) { + return 100; + } + + $position = 0; + + foreach ($columns as $column_id => $column_title) { + if ($column_id == $task['column_id']) { + break; + } + + $position++; + } + + return round(($position * 100) / count($columns), 1); + } + + public function getOpenTaskCountBySwimlaneAndColumn($project_id) + { + return $this->db->table(self::TABLE) + ->columns('swimlane_id', 'column_id', 'COUNT(*) AS nb_open_tasks') + ->eq('project_id', $project_id) + ->eq('is_active', 1) + ->groupBy('swimlane_id', 'column_id') + ->findAll(); + } +} diff --git a/app/Model/TaskModificationModel.php b/app/Model/TaskModificationModel.php new file mode 100644 index 0000000..f90586a --- /dev/null +++ b/app/Model/TaskModificationModel.php @@ -0,0 +1,134 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Modification + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskModificationModel extends Base +{ + /** + * Update a task + * + * @access public + * @param array $values + * @param boolean $fire_events + * @return boolean + */ + public function update(array $values, $fire_events = true) + { + $task = $this->taskFinderModel->getById($values['id']); + + if (isset($values['tags_only_add_new']) && $values['tags_only_add_new'] == 1) { + $this->updateTags($values, $task, false); + } else { + $this->updateTags($values, $task); + } + + $this->prepare($values); + $result = $this->db->table(TaskModel::TABLE)->eq('id', $task['id'])->update($values); + + if ($fire_events && $result) { + $this->fireEvents($task, $values); + } + + return $result; + } + + /** + * Fire events + * + * @access protected + * @param array $task + * @param array $changes + */ + protected function fireEvents(array $task, array $changes) + { + $events = array(); + + if ($this->isAssigneeChanged($task, $changes)) { + $events[] = TaskModel::EVENT_ASSIGNEE_CHANGE; + } elseif ($this->isModified($task, $changes)) { + $events[] = TaskModel::EVENT_CREATE_UPDATE; + $events[] = TaskModel::EVENT_UPDATE; + } + + 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 protected + * @param array $task + * @param array $changes + * @return bool + */ + protected function isAssigneeChanged(array $task, array $changes) + { + $diff = array_diff_assoc($changes, $task); + unset($diff['date_modification']); + return isset($changes['owner_id']) && $task['owner_id'] != $changes['owner_id'] && count($diff) === 1; + } + + /** + * Prepare data before task modification + * + * @access protected + * @param array $values + */ + protected function prepare(array &$values) + { + $values = $this->dateParser->convert($values, array('date_due'), true); + $values = $this->dateParser->convert($values, array('date_started'), true); + + $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); + } + + /** + * Update tags + * + * @access protected + * @param array $values + * @param array $original_task + */ + protected function updateTags(array &$values, array $original_task, $remove_other_tags = true) + { + if (isset($values['tags'])) { + $this->taskTagModel->save($original_task['project_id'], $values['id'], $values['tags'], $remove_other_tags); + unset($values['tags']); + unset($values['tags_only_add_new']); + } + } +} diff --git a/app/Model/TaskPositionModel.php b/app/Model/TaskPositionModel.php new file mode 100644 index 0000000..56d32c7 --- /dev/null +++ b/app/Model/TaskPositionModel.php @@ -0,0 +1,315 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Position + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskPositionModel extends Base +{ + public function moveBottom($project_id, $task_id, $swimlane_id, $column_id) + { + $this->db->startTransaction(); + + $task = $this->taskFinderModel->getById($task_id); + + $result = $this->db->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('swimlane_id', $swimlane_id) + ->eq('column_id', $column_id) + ->columns('MAX(position) AS pos') + ->findOne(); + + $position = 1; + if (! empty($result)) { + $position = $result['pos'] + 1; + } + + $result = $this->db->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->eq('project_id', $project_id) + ->update([ + 'swimlane_id' => $swimlane_id, + 'column_id' => $column_id, + 'position' => $position, + 'date_moved' => time(), + 'date_modification' => time(), + ]); + + $this->db->closeTransaction(); + + if ($result) { + $this->fireEvents($task, $column_id, $position, $swimlane_id); + } + + return $result; + } + + /** + * 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 + * @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, $onlyOpen = true) + { + if ($position < 1) { + return false; + } + + $task = $this->taskFinderModel->getById($task_id); + + if ($swimlane_id == 0) { + $swimlane_id = $task['swimlane_id']; + } + + if ($onlyOpen && $task['is_active'] == TaskModel::STATUS_CLOSED) { + return true; + } + + $result = false; + + if ($task['swimlane_id'] != $swimlane_id) { + $result = $this->saveSwimlaneChange($project_id, $task_id, $position, $task['column_id'], $column_id, $task['swimlane_id'], $swimlane_id); + } elseif ($task['column_id'] != $column_id) { + $result = $this->saveColumnChange($project_id, $task_id, $position, $swimlane_id, $task['column_id'], $column_id); + } elseif ($task['position'] != $position) { + $result = $this->savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id); + } + + if ($result && $fire_events) { + $this->fireEvents($task, $column_id, $position, $swimlane_id); + } + + return $result; + } + + /** + * Move a task to another swimlane + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $original_column_id + * @param integer $new_column_id + * @param integer $original_swimlane_id + * @param integer $new_swimlane_id + * @return boolean + */ + private function saveSwimlaneChange($project_id, $task_id, $position, $original_column_id, $new_column_id, $original_swimlane_id, $new_swimlane_id) + { + $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 && $r3; + } + + /** + * Move a task to another column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $swimlane_id + * @param integer $original_column_id + * @param integer $new_column_id + * @return boolean + */ + private function saveColumnChange($project_id, $task_id, $position, $swimlane_id, $original_column_id, $new_column_id) + { + $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 && $r3; + } + + /** + * Move a task to another position in the same column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id) + { + $this->db->startTransaction(); + $result = $this->saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id); + $this->db->closeTransaction(); + + return $result; + } + + /** + * Save all task positions for one column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id) + { + $tasks_ids = $this->db->table(TaskModel::TABLE) + ->eq('is_active', 1) + ->eq('swimlane_id', $swimlane_id) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->neq('id', $task_id) + ->asc('position') + ->asc('id') + ->findAllByColumn('id'); + + $offset = 1; + + foreach ($tasks_ids as $current_task_id) { + + // Insert the new task + if ($position == $offset) { + if (! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { + return false; + } + $offset++; + } + + // Rewrite other tasks position + if (! $this->saveTaskPosition($current_task_id, $offset, $column_id, $swimlane_id)) { + return false; + } + + $offset++; + } + + // Insert the new task at the bottom and normalize bad position + if ($position >= $offset && ! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { + 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( + 'date_moved' => $now, + 'date_modification' => $now, + )); + } + + /** + * Save new task position + * + * @access private + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function saveTaskPosition($task_id, $position, $column_id, $swimlane_id) + { + $result = $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(array( + 'position' => $position, + 'column_id' => $column_id, + 'swimlane_id' => $swimlane_id, + )); + + if (! $result) { + $this->db->cancelTransaction(); + return false; + } + + return true; + } + + /** + * Fire events + * + * @access private + * @param array $task + * @param integer $new_column_id + * @param integer $new_position + * @param integer $new_swimlane_id + */ + private function fireEvents(array $task, $new_column_id, $new_position, $new_swimlane_id) + { + $changes = array( + 'project_id' => $task['project_id'], + 'position' => $new_position, + 'column_id' => $new_column_id, + 'swimlane_id' => $new_swimlane_id, + 'src_column_id' => $task['column_id'], + 'dst_column_id' => $new_column_id, + 'date_moved' => $task['date_moved'], + 'recurrence_status' => $task['recurrence_status'], + 'recurrence_trigger' => $task['recurrence_trigger'], + ); + + if ($task['swimlane_id'] != $new_swimlane_id) { + $this->taskEventJob->execute( + $task['id'], + array(TaskModel::EVENT_MOVE_SWIMLANE), + $changes, + $changes + ); + + if ($task['column_id'] != $new_column_id) { + $this->taskEventJob->execute( + $task['id'], + array(TaskModel::EVENT_MOVE_COLUMN), + $changes, + $changes + ); + } + } elseif ($task['column_id'] != $new_column_id) { + $this->taskEventJob->execute( + $task['id'], + array(TaskModel::EVENT_MOVE_COLUMN), + $changes, + $changes + ); + } elseif ($task['position'] != $new_position) { + $this->taskEventJob->execute( + $task['id'], + array(TaskModel::EVENT_MOVE_POSITION), + $changes, + $changes + ); + } + } +} diff --git a/app/Model/TaskProjectDuplicationModel.php b/app/Model/TaskProjectDuplicationModel.php new file mode 100644 index 0000000..c98704e --- /dev/null +++ b/app/Model/TaskProjectDuplicationModel.php @@ -0,0 +1,82 @@ +<?php + +namespace Kanboard\Model; + +/** + * Task Project Duplication + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskProjectDuplicationModel extends TaskDuplicationModel +{ + /** + * Duplicate a task to another project + * + * @access public + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return boolean|integer + */ + public function duplicateToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) + { + $values = $this->prepare($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id); + $this->checkDestinationProjectValues($values); + $new_task_id = $this->save($task_id, $values); + + if ($new_task_id !== false) { + $this->tagDuplicationModel->duplicateTaskTagsToAnotherProject($task_id, $new_task_id, $project_id); + $this->taskLinkModel->create($new_task_id, $task_id, 4); + + $attachments = $this->taskFileModel->getAll($task_id); + $externalLinks = $this->taskExternalLinkModel->getAll($task_id); + + foreach ($attachments as $attachment) { + $this->taskFileModel->create($new_task_id, $attachment['name'], $attachment['path'], $attachment['size']); + } + + foreach ($externalLinks as $externalLink) { + $this->taskExternalLinkModel->create([ + 'task_id' => $new_task_id, + 'creator_id' => $externalLink['creator_id'], + 'dependency' => $externalLink['dependency'], + 'title' => $externalLink['title'], + 'link_type' => $externalLink['link_type'], + 'url' => $externalLink['url'], + ]); + } + } + + $hook_values = ['source_task_id' => $task_id, 'destination_task_id' => $new_task_id]; + $this->hook->reference('model:task:project_duplication:aftersave', $hook_values); + + return $new_task_id; + } + + /** + * Prepare values before duplication + * + * @access protected + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return array + */ + protected function prepare($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id) + { + $values = $this->copyFields($task_id); + $values['project_id'] = $project_id; + $values['column_id'] = $column_id !== null ? $column_id : $values['column_id']; + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $values['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $values['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $values['owner_id']; + return $values; + } +} diff --git a/app/Model/TaskProjectMoveModel.php b/app/Model/TaskProjectMoveModel.php new file mode 100644 index 0000000..ae3ae08 --- /dev/null +++ b/app/Model/TaskProjectMoveModel.php @@ -0,0 +1,65 @@ +<?php + +namespace Kanboard\Model; + +/** + * Task Project Move + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskProjectMoveModel extends TaskDuplicationModel +{ + /** + * Move a task to another project + * + * @access public + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return boolean + */ + public function moveToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) + { + $task = $this->taskFinderModel->getById($task_id); + $values = $this->prepare($project_id, $swimlane_id, $column_id, $category_id, $owner_id, $task); + + $this->checkDestinationProjectValues($values); + $this->tagDuplicationModel->syncTaskTagsToAnotherProject($task_id, $project_id); + + if ($this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update($values)) { + $this->queueManager->push($this->taskEventJob->withParams($task_id, array(TaskModel::EVENT_MOVE_PROJECT), $values)); + } + + return true; + } + + /** + * Prepare new task values + * + * @access protected + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @param array $task + * @return array + */ + protected function prepare($project_id, $swimlane_id, $column_id, $category_id, $owner_id, array $task) + { + $values = array(); + $values['is_active'] = 1; + $values['project_id'] = $project_id; + $values['column_id'] = $column_id !== null ? $column_id : $task['column_id']; + $values['position'] = $this->taskFinderModel->countByColumnId($project_id, $values['column_id']) + 1; + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $task['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $task['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $task['owner_id']; + $values['priority'] = $task['priority']; + return $values; + } +} diff --git a/app/Model/TaskRecurrenceModel.php b/app/Model/TaskRecurrenceModel.php new file mode 100644 index 0000000..201a340 --- /dev/null +++ b/app/Model/TaskRecurrenceModel.php @@ -0,0 +1,159 @@ +<?php + +namespace Kanboard\Model; + +use DateInterval; +use DateTime; + +/** + * Task Recurrence + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskRecurrenceModel extends TaskDuplicationModel +{ + /** + * Return the list user selectable recurrence status + * + * @access public + * @return array + */ + public function getRecurrenceStatusList() + { + return array( + TaskModel::RECURRING_STATUS_NONE => t('No'), + TaskModel::RECURRING_STATUS_PENDING => t('Yes'), + ); + } + + /** + * Return the list recurrence triggers + * + * @access public + * @return array + */ + public function getRecurrenceTriggerList() + { + return array( + TaskModel::RECURRING_TRIGGER_FIRST_COLUMN => t('When task is moved from first column'), + TaskModel::RECURRING_TRIGGER_LAST_COLUMN => t('When task is moved to last column'), + TaskModel::RECURRING_TRIGGER_CLOSE => t('When task is closed'), + ); + } + + /** + * Return the list options to calculate recurrence due date + * + * @access public + * @return array + */ + public function getRecurrenceBasedateList() + { + return array( + TaskModel::RECURRING_BASEDATE_DUEDATE => t('Existing due date'), + TaskModel::RECURRING_BASEDATE_TRIGGERDATE => t('Action date'), + ); + } + + /** + * Return the list recurrence timeframes + * + * @access public + * @return array + */ + public function getRecurrenceTimeframeList() + { + return array( + TaskModel::RECURRING_TIMEFRAME_DAYS => t('Day(s)'), + TaskModel::RECURRING_TIMEFRAME_MONTHS => t('Month(s)'), + TaskModel::RECURRING_TIMEFRAME_YEARS => t('Year(s)'), + ); + } + + /** + * Duplicate recurring task + * + * @access public + * @param integer $task_id Task id + * @return boolean|integer Recurrence task id + */ + public function duplicateRecurringTask($task_id) + { + $values = $this->copyFields($task_id); + + if ($values['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) { + $values['recurrence_parent'] = $task_id; + $values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']); + $this->calculateRecurringTaskDueDate($values); + + $recurring_task_id = $this->save($task_id, $values); + + if ($recurring_task_id !== false) { + $this->tagDuplicationModel->duplicateTaskTags($task_id, $recurring_task_id); + + $externalLinks = $this->taskExternalLinkModel->getAll($task_id); + foreach ($externalLinks as $externalLink) { + $this->taskExternalLinkModel->create([ + 'task_id' => $recurring_task_id, + 'creator_id' => $externalLink['creator_id'], + 'dependency' => $externalLink['dependency'], + 'title' => $externalLink['title'], + 'link_type' => $externalLink['link_type'], + 'url' => $externalLink['url'], + ]); + } + + $parent_update = $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->update(array( + 'recurrence_status' => TaskModel::RECURRING_STATUS_PROCESSED, + 'recurrence_child' => $recurring_task_id, + )); + + if ($parent_update) { + return $recurring_task_id; + } + } + } + + return false; + } + + /** + * Calculate new due date for new recurrence task + * + * @access public + * @param array $values Task fields + */ + public function calculateRecurringTaskDueDate(array &$values) + { + if (! empty($values['date_due']) && $values['recurrence_factor'] != 0) { + if ($values['recurrence_basedate'] == TaskModel::RECURRING_BASEDATE_TRIGGERDATE) { + $values['date_due'] = time(); + } + + $factor = abs($values['recurrence_factor']); + $subtract = $values['recurrence_factor'] < 0; + + switch ($values['recurrence_timeframe']) { + case TaskModel::RECURRING_TIMEFRAME_MONTHS: + $interval = 'P' . $factor . 'M'; + break; + case TaskModel::RECURRING_TIMEFRAME_YEARS: + $interval = 'P' . $factor . 'Y'; + break; + default: + $interval = 'P' . $factor . 'D'; + } + + $date_due = new DateTime(); + $date_due->setTimestamp($values['date_due']); + + $subtract ? $date_due->sub(new DateInterval($interval)) : $date_due->add(new DateInterval($interval)); + + $values['date_due'] = $date_due->getTimestamp(); + } + } +} diff --git a/app/Model/TaskReorderModel.php b/app/Model/TaskReorderModel.php new file mode 100644 index 0000000..d9230c4 --- /dev/null +++ b/app/Model/TaskReorderModel.php @@ -0,0 +1,107 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +class TaskReorderModel extends Base +{ + public function reorderByTaskId($projectID, $swimlaneID, $columnID, $direction) + { + $this->db->startTransaction(); + + $taskIDs = $this->db->table(TaskModel::TABLE) + ->eq('project_id', $projectID) + ->eq('swimlane_id', $swimlaneID) + ->eq('column_id', $columnID) + ->orderBy('id', $direction) + ->findAllByColumn('id'); + + $this->reorderTasks($taskIDs); + + $this->db->closeTransaction(); + } + + public function reorderByPriority($projectID, $swimlaneID, $columnID, $direction) + { + $this->db->startTransaction(); + + $taskIDs = $this->db->table(TaskModel::TABLE) + ->eq('project_id', $projectID) + ->eq('swimlane_id', $swimlaneID) + ->eq('column_id', $columnID) + ->orderBy('priority', $direction) + ->asc('id') + ->findAllByColumn('id'); + + $this->reorderTasks($taskIDs); + + $this->db->closeTransaction(); + } + + public function reorderByAssigneeAndPriority($projectID, $swimlaneID, $columnID, $direction) + { + $this->db->startTransaction(); + + $taskIDs = $this->db->table(TaskModel::TABLE) + ->eq('tasks.project_id', $projectID) + ->eq('tasks.swimlane_id', $swimlaneID) + ->eq('tasks.column_id', $columnID) + ->asc('u.name') + ->asc('u.username') + ->orderBy('tasks.priority', $direction) + ->left(UserModel::TABLE, 'u', 'id', TaskModel::TABLE, 'owner_id') + ->findAllByColumn('tasks.id'); + + $this->reorderTasks($taskIDs); + + $this->db->closeTransaction(); + } + + public function reorderByAssignee($projectID, $swimlaneID, $columnID, $direction) + { + $this->db->startTransaction(); + + $taskIDs = $this->db->table(TaskModel::TABLE) + ->eq('tasks.project_id', $projectID) + ->eq('tasks.swimlane_id', $swimlaneID) + ->eq('tasks.column_id', $columnID) + ->orderBy('u.name', $direction) + ->orderBy('u.username', $direction) + ->orderBy('u.id', $direction) + ->left(UserModel::TABLE, 'u', 'id', TaskModel::TABLE, 'owner_id') + ->findAllByColumn('tasks.id'); + + $this->reorderTasks($taskIDs); + + $this->db->closeTransaction(); + } + + public function reorderByDueDate($projectID, $swimlaneID, $columnID, $direction) + { + $this->db->startTransaction(); + + $taskIDs = $this->db->table(TaskModel::TABLE) + ->eq('project_id', $projectID) + ->eq('swimlane_id', $swimlaneID) + ->eq('column_id', $columnID) + ->orderBy('date_due', $direction) + ->asc('id') + ->findAllByColumn('id'); + + $this->reorderTasks($taskIDs); + + $this->db->closeTransaction(); + } + + protected function reorderTasks(array $taskIDs) + { + $i = 1; + foreach ($taskIDs as $taskID) { + $this->db->table(TaskModel::TABLE) + ->eq('id', $taskID) + ->update(['position' => $i]); + $i++; + } + } +} diff --git a/app/Model/TaskStatusModel.php b/app/Model/TaskStatusModel.php new file mode 100644 index 0000000..bb6725f --- /dev/null +++ b/app/Model/TaskStatusModel.php @@ -0,0 +1,144 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Task Status + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskStatusModel extends Base +{ + /** + * Return true if the task is closed + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function isClosed($task_id) + { + return $this->checkStatus($task_id, TaskModel::STATUS_CLOSED); + } + + /** + * Return true if the task is open + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function isOpen($task_id) + { + return $this->checkStatus($task_id, TaskModel::STATUS_OPEN); + } + + /** + * Mark a task closed + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function close($task_id) + { + $this->subtaskStatusModel->closeAll($task_id); + return $this->changeStatus($task_id, TaskModel::STATUS_CLOSED, time(), TaskModel::EVENT_CLOSE); + } + + /** + * Mark a task open + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function open($task_id) + { + return $this->changeStatus($task_id, TaskModel::STATUS_OPEN, 0, TaskModel::EVENT_OPEN); + } + + /** + * Close multiple tasks + * + * @access public + * @param array $task_ids + */ + public function closeMultipleTasks(array $task_ids) + { + foreach ($task_ids as $task_id) { + $this->close($task_id); + } + } + + /** + * Close all tasks within a column/swimlane + * + * @access public + * @param integer $swimlane_id + * @param integer $column_id + */ + public function closeTasksBySwimlaneAndColumn($swimlane_id, $column_id) + { + $task_ids = $this->db + ->table(TaskModel::TABLE) + ->eq('swimlane_id', $swimlane_id) + ->eq('column_id', $column_id) + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->findAllByColumn('id'); + + $this->closeMultipleTasks($task_ids); + } + + /** + * Common method to change the status of task + * + * @access private + * @param integer $task_id Task id + * @param integer $status Task status + * @param integer $date_completed Timestamp + * @param string $event_name Event name + * @return boolean + */ + private function changeStatus($task_id, $status, $date_completed, $event_name) + { + if (! $this->taskFinderModel->exists($task_id)) { + return false; + } + + $result = $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->update(array( + 'is_active' => $status, + 'date_completed' => $date_completed, + 'date_modification' => time(), + )); + + if ($result) { + $this->queueManager->push($this->taskEventJob->withParams($task_id, array($event_name))); + } + + return $result; + } + + /** + * Check the status of a task + * + * @access private + * @param integer $task_id Task id + * @param integer $status Task status + * @return boolean + */ + private function checkStatus($task_id, $status) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->eq('is_active', $status) + ->exists(); + } +} diff --git a/app/Model/TaskTagModel.php b/app/Model/TaskTagModel.php new file mode 100644 index 0000000..f822a48 --- /dev/null +++ b/app/Model/TaskTagModel.php @@ -0,0 +1,189 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Class TaskTagModel + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TaskTagModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'task_has_tags'; + + /** + * Get all tags not available in a project + * + * @access public + * @param integer $task_id + * @param integer $project_id + * @return array + */ + public function getTagsByTaskNotAvailableInProject($task_id, $project_id) + { + return $this->db->table(TagModel::TABLE) + ->eq(self::TABLE.'.task_id', $task_id) + ->notIn(TagModel::TABLE.'.project_id', array(0, $project_id)) + ->join(self::TABLE, 'tag_id', 'id') + ->findAll(); + } + + /** + * Get all tags associated to a task + * + * @access public + * @param integer $task_id + * @return array + */ + public function getTagsByTask($task_id) + { + return $this->db->table(TagModel::TABLE) + ->columns(TagModel::TABLE.'.id', TagModel::TABLE.'.name', TagModel::TABLE.'.color_id') + ->eq(self::TABLE.'.task_id', $task_id) + ->join(self::TABLE, 'tag_id', 'id') + ->findAll(); + } + + /** + * Get all tags associated to a list of tasks + * + * @access public + * @param integer[] $task_ids + * @return array + */ + public function getTagsByTaskIds($task_ids) + { + if (empty($task_ids)) { + return array(); + } + + $tags = $this->db->table(TagModel::TABLE) + ->columns(TagModel::TABLE.'.id', TagModel::TABLE.'.name', TagModel::TABLE.'.color_id', self::TABLE.'.task_id') + ->in(self::TABLE.'.task_id', $task_ids) + ->join(self::TABLE, 'tag_id', 'id') + ->asc(TagModel::TABLE.'.name') + ->findAll(); + + return array_column_index($tags, 'task_id'); + } + + /** + * Get dictionary of tags + * + * @access public + * @param integer $task_id + * @return array + */ + public function getList($task_id) + { + $tags = $this->getTagsByTask($task_id); + return array_column($tags, 'name', 'id'); + } + + /** + * Add or update a list of tags to a task + * + * @access public + * @param integer $project_id + * @param integer $task_id + * @param string[] $tags + * @return boolean + */ + public function save($project_id, $task_id, array $tags, $remove_other_tags = true) + { + $task_tags = $this->getList($task_id); + $tags = array_filter($tags); + + if ($remove_other_tags) { + return $this->associateTags($project_id, $task_id, $task_tags, $tags) && + $this->dissociateTags($task_id, $task_tags, $tags); + } else { + return $this->associateTags($project_id, $task_id, $task_tags, $tags); + } + } + + /** + * Associate a tag to a task + * + * @access public + * @param integer $task_id + * @param integer $tag_id + * @return boolean + */ + public function associateTag($task_id, $tag_id) + { + return $this->db->table(self::TABLE)->insert(array( + 'task_id' => $task_id, + 'tag_id' => $tag_id, + )); + } + + /** + * Dissociate a tag from a task + * + * @access public + * @param integer $task_id + * @param integer $tag_id + * @return boolean + */ + public function dissociateTag($task_id, $tag_id) + { + return $this->db->table(self::TABLE) + ->eq('task_id', $task_id) + ->eq('tag_id', $tag_id) + ->remove(); + } + + /** + * Associate missing tags + * + * @access protected + * @param integer $project_id + * @param integer $task_id + * @param array $task_tags + * @param string[] $tags + * @return bool + */ + protected function associateTags($project_id, $task_id, $task_tags, $tags) + { + foreach ($tags as $tag) { + $tag_id = $this->tagModel->findOrCreateTag($project_id, $tag); + + if (! isset($task_tags[$tag_id]) && ! $this->associateTag($task_id, $tag_id)) { + return false; + } + } + + return true; + } + + /** + * Dissociate removed tags + * + * @access protected + * @param integer $task_id + * @param array $task_tags + * @param string[] $tags + * @return bool + */ + protected function dissociateTags($task_id, $task_tags, $tags) + { + foreach ($task_tags as $tag_id => $tag) { + if (! in_array($tag, $tags)) { + if (! $this->dissociateTag($task_id, $tag_id)) { + return false; + } + } + } + + return true; + } +} diff --git a/app/Model/ThemeModel.php b/app/Model/ThemeModel.php new file mode 100644 index 0000000..3ec9f95 --- /dev/null +++ b/app/Model/ThemeModel.php @@ -0,0 +1,29 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Class Theme + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class ThemeModel extends Base +{ + /** + * Get available theme + * + * @access public + * @return array + */ + public function getThemes() + { + return [ + 'light' => t('Light theme'), + 'dark' => t('Dark theme'), + 'auto' => t('Automatic theme - Sync with system'), + ]; + } +} diff --git a/app/Model/TimezoneModel.php b/app/Model/TimezoneModel.php new file mode 100644 index 0000000..ef6afc6 --- /dev/null +++ b/app/Model/TimezoneModel.php @@ -0,0 +1,54 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Class Timezone + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TimezoneModel extends Base +{ + /** + * Get available timezones + * + * @access public + * @param boolean $prepend Prepend a default value + * @return array + */ + public function getTimezones($prepend = false) + { + $timezones = timezone_identifiers_list(); + $listing = array_combine(array_values($timezones), $timezones); + + if ($prepend) { + return array('' => t('Application default')) + $listing; + } + + return $listing; + } + + /** + * Get current timezone + * + * @access public + * @return string + */ + public function getCurrentTimezone() + { + return $this->userSession->getTimezone() ?: $this->configModel->get('application_timezone', 'UTC'); + } + + /** + * Set timezone + * + * @access public + */ + public function setCurrentTimezone() + { + date_default_timezone_set($this->getCurrentTimezone()); + } +} diff --git a/app/Model/TransitionModel.php b/app/Model/TransitionModel.php new file mode 100644 index 0000000..a4a5847 --- /dev/null +++ b/app/Model/TransitionModel.php @@ -0,0 +1,130 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * Transition + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class TransitionModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'transitions'; + + /** + * Save transition event + * + * @access public + * @param integer $user_id + * @param array $task_event + * @return bool + */ + public function save($user_id, array $task_event) + { + $time = time(); + + return $this->db->table(self::TABLE)->insert(array( + 'user_id' => $user_id, + 'project_id' => $task_event['project_id'], + 'task_id' => $task_event['task_id'], + 'src_column_id' => $task_event['src_column_id'], + 'dst_column_id' => $task_event['dst_column_id'], + 'date' => $time, + 'time_spent' => $time - $task_event['date_moved'] + )); + } + + /** + * Get time spent by task for each column + * + * @access public + * @param integer $task_id + * @return array + */ + public function getTimeSpentByTask($task_id) + { + return $this->db + ->hashtable(self::TABLE) + ->groupBy('src_column_id') + ->eq('task_id', $task_id) + ->getAll('src_column_id', 'SUM(time_spent) AS time_spent'); + } + + /** + * Get all transitions by task + * + * @access public + * @param integer $task_id + * @return array + */ + public function getAllByTask($task_id) + { + return $this->db->table(self::TABLE) + ->columns( + 'src.title as src_column', + 'dst.title as dst_column', + UserModel::TABLE.'.name', + UserModel::TABLE.'.username', + self::TABLE.'.user_id', + self::TABLE.'.date', + self::TABLE.'.time_spent' + ) + ->eq('task_id', $task_id) + ->desc('date') + ->join(UserModel::TABLE, 'id', 'user_id') + ->join(ColumnModel::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src') + ->join(ColumnModel::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst') + ->findAll(); + } + + /** + * Get all transitions by project + * + * @access public + * @param integer $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 getAllByProjectAndDate($project_id, $from, $to) + { + if (! is_numeric($from)) { + $from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from)); + } + + if (! is_numeric($to)) { + $to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to))); + } + + return $this->db->table(self::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + 'src.title as src_column', + 'dst.title as dst_column', + UserModel::TABLE.'.name', + UserModel::TABLE.'.username', + self::TABLE.'.user_id', + self::TABLE.'.date', + self::TABLE.'.time_spent' + ) + ->gte('date', $from) + ->lte('date', $to) + ->eq(self::TABLE.'.project_id', $project_id) + ->desc('date') + ->desc(self::TABLE.'.id') + ->join(TaskModel::TABLE, 'id', 'task_id') + ->join(UserModel::TABLE, 'id', 'user_id') + ->join(ColumnModel::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src') + ->join(ColumnModel::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst') + ->findAll(); + } +} diff --git a/app/Model/UserLockingModel.php b/app/Model/UserLockingModel.php new file mode 100644 index 0000000..1d4d994 --- /dev/null +++ b/app/Model/UserLockingModel.php @@ -0,0 +1,105 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * User Locking Model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class UserLockingModel extends Base +{ + /** + * Get the number of failed login for the user + * + * @access public + * @param string $username + * @return integer + */ + public function getFailedLogin($username) + { + return (int) $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->findOneColumn('nb_failed_login'); + } + + /** + * Reset to 0 the counter of failed login + * + * @access public + * @param string $username + * @return boolean + */ + public function resetFailedLogin($username) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->update(array( + 'nb_failed_login' => 0, + 'lock_expiration_date' => 0, + )); + } + + /** + * Increment failed login counter + * + * @access public + * @param string $username + * @return boolean + */ + public function incrementFailedLogin($username) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->increment('nb_failed_login', 1); + } + + /** + * Check if the account is locked + * + * @access public + * @param string $username + * @return boolean + */ + public function isLocked($username) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->neq('lock_expiration_date', 0) + ->gte('lock_expiration_date', time()) + ->exists(); + } + + /** + * Lock the account for the specified duration + * + * @access public + * @param string $username Username + * @param integer $duration Duration in minutes + * @return boolean + */ + public function lock($username, $duration = 15) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->update(array( + 'lock_expiration_date' => time() + $duration * 60 + )); + } + + /** + * Return true if the captcha must be shown + * + * @access public + * @param string $username + * @param integer $tries + * @return boolean + */ + public function hasCaptcha($username, $tries = BRUTEFORCE_CAPTCHA) + { + return $this->getFailedLogin($username) >= $tries; + } +} diff --git a/app/Model/UserMetadataModel.php b/app/Model/UserMetadataModel.php new file mode 100644 index 0000000..42fe4c6 --- /dev/null +++ b/app/Model/UserMetadataModel.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Model; + +/** + * User Metadata + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class UserMetadataModel extends MetadataModel +{ + const KEY_COMMENT_SORTING_DIRECTION = 'comment.sorting.direction'; + const KEY_BOARD_COLLAPSED = 'board.collapsed.'; + + /** + * Get the table + * + * @abstract + * @access protected + * @return string + */ + protected function getTable() + { + return 'user_has_metadata'; + } + + /** + * Define the entity key + * + * @access protected + * @return string + */ + protected function getEntityKey() + { + return 'user_id'; + } +} diff --git a/app/Model/UserModel.php b/app/Model/UserModel.php new file mode 100644 index 0000000..f4c4f90 --- /dev/null +++ b/app/Model/UserModel.php @@ -0,0 +1,448 @@ +<?php + +namespace Kanboard\Model; + +use PicoDb\Database; +use Kanboard\Core\Base; +use Kanboard\Core\Security\Token; +use Kanboard\Core\Security\Role; +use InvalidArgumentException; + +/** + * User model + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class UserModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'users'; + + /** + * Id used for everybody (filtering) + * + * @var integer + */ + const EVERYBODY_ID = -1; + + public function isValidSession($userID, $sessionRole) + { + return $this->db->table(self::TABLE) + ->eq('id', $userID) + ->eq('is_active', 1) + ->eq('role', $sessionRole) + ->exists(); + } + + public function has2FA($username) + { + return $this->db->table(self::TABLE) + ->eq('username', $username) + ->eq('is_active', 1) + ->eq('twofactor_activated', 1) + ->exists(); + } + + /** + * Return true if the user exists + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function exists($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->exists(); + } + + /** + * Return true if the user is active + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function isActive($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->eq('is_active', 1)->exists(); + } + + /** + * Get query to fetch all users + * + * @access public + * @return \PicoDb\Table + */ + public function getQuery() + { + return $this->db->table(self::TABLE); + } + + /** + * Return true is the given user id is administrator + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function isAdmin($user_id) + { + return $this->userSession->isAdmin() || // Avoid SQL query if connected + $this->db + ->table(UserModel::TABLE) + ->eq('id', $user_id) + ->eq('role', Role::APP_ADMIN) + ->exists(); + } + + /** + * Get a specific user by id + * + * @access public + * @param integer $user_id User id + * @return array + */ + public function getById($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->findOne(); + } + + /** + * Get a specific user by the Google id + * + * @access public + * @param string $externalIdColumn + * @param string $externalId + * @return array|boolean + */ + public function getByExternalId($externalIdColumn, $externalId) + { + if (empty($externalId)) { + return false; + } + + if (! $this->db->isValidIdentifier($externalIdColumn)) { + throw new InvalidArgumentException('Invalid external id column name'); + } + + return $this->db->table(self::TABLE)->eq($externalIdColumn, $externalId)->findOne(); + } + + /** + * Get a specific user by the username + * + * @access public + * @param string $username Username + * @return array + */ + public function getByUsername($username) + { + return $this->db->table(self::TABLE)->eq('username', $username)->findOne(); + } + + /** + * Get user_id by username + * + * @access public + * @param string $username Username + * @return integer + */ + public function getIdByUsername($username) + { + return $this->db->table(self::TABLE)->eq('username', $username)->findOneColumn('id'); + } + + /** + * Get a specific user by the email address + * + * @access public + * @param string $email Email + * @return array|boolean + */ + public function getByEmail($email) + { + if (empty($email)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('email', $email)->findOne(); + } + + /** + * Fetch user by using the token + * + * @access public + * @param string $token Token + * @return array|boolean + */ + public function getByToken($token) + { + if (empty($token)) { + return false; + } + + return $this->db + ->table(self::TABLE) + ->eq('token', $token) + ->eq('is_active', 1) + ->findOne(); + } + + /** + * Get all users + * + * @access public + * @return array + */ + public function getAll() + { + return $this->getQuery()->asc('username')->findAll(); + } + + /** + * Get the number of users + * + * @access public + * @return integer + */ + public function count() + { + return $this->db->table(self::TABLE)->count(); + } + + /** + * List all users (key-value pairs with id/username) + * + * @access public + * @param boolean $prepend Prepend "All users" + * @return array + */ + public function getActiveUsersList($prepend = false) + { + $users = $this->db->table(self::TABLE)->eq('is_active', 1)->columns('id', 'username', 'name')->findAll(); + $listing = $this->prepareList($users); + + if ($prepend) { + return array(UserModel::EVERYBODY_ID => t('Everybody')) + $listing; + } + + return $listing; + } + + /** + * Common method to prepare a user list + * + * @access public + * @param array $users Users list (from database) + * @return array Formated list + */ + public function prepareList(array $users) + { + $result = array(); + + foreach ($users as $user) { + $result[$user['id']] = $this->helper->user->getFullname($user); + } + + asort($result); + + return $result; + } + + /** + * Prepare values before an update or a create + * + * @access public + * @param array $values Form values + */ + public function prepare(array &$values) + { + if (isset($values['password'])) { + if (! empty($values['password'])) { + $values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT); + } else { + unset($values['password']); + } + } + + if (isset($values['username'])) { + $values['username'] = trim($values['username']); + } + + $this->helper->model->removeFields($values, array('confirmation', 'current_password')); + $this->helper->model->resetFields($values, array('is_ldap_user', 'disable_login_form')); + $this->helper->model->convertNullFields($values, array('gitlab_id')); + $this->helper->model->convertIntegerFields($values, array('gitlab_id')); + } + + /** + * Add a new user in the database + * + * @access public + * @param array $values Form values + * @return boolean|integer + */ + public function create(array $values) + { + $this->prepare($values); + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Modify a new user + * + * @access public + * @param array $values Form values + * @return boolean + */ + public function update(array $values) + { + $this->prepare($values); + $updates = $values; + unset($updates['id']); + $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($updates); + $this->userSession->refresh($values['id']); + return $result; + } + + /** + * Disable a specific user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function disable($user_id) + { + $this->db->startTransaction(); + $result1 = $this->db->table(self::TABLE)->eq('id', $user_id)->update(array( + 'is_active' => 0, + 'token' => '', + )); + $result2 = $this->db->table(ProjectModel::TABLE)->eq('is_private', 1)->eq('owner_id', $user_id)->update(array('is_active' => 0)); + $this->db->closeTransaction(); + return $result1 && $result2; + } + + /** + * Enable a specific user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function enable($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->update(array('is_active' => 1)); + } + + /** + * Remove a specific user + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function remove($user_id) + { + $this->avatarFileModel->remove($user_id); + + return $this->db->transaction(function (Database $db) use ($user_id) { + + // All assigned tasks are now unassigned (no foreign key) + if (! $db->table(TaskModel::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => 0))) { + return false; + } + + // All assigned subtasks are now unassigned (no foreign key) + if (! $db->table(SubtaskModel::TABLE)->eq('user_id', $user_id)->update(array('user_id' => 0))) { + return false; + } + + // All comments are not assigned anymore (no foreign key) + if (! $db->table(CommentModel::TABLE)->eq('user_id', $user_id)->update(array('user_id' => 0))) { + return false; + } + + // All private projects are removed + $project_ids = $db->table(ProjectModel::TABLE) + ->eq('is_private', 1) + ->eq(ProjectUserRoleModel::TABLE.'.user_id', $user_id) + ->join(ProjectUserRoleModel::TABLE, 'project_id', 'id') + ->findAllByColumn(ProjectModel::TABLE.'.id'); + + if (! empty($project_ids)) { + $db->table(ProjectModel::TABLE)->in('id', $project_ids)->remove(); + } + + // Finally remove the user + if (! $db->table(UserModel::TABLE)->eq('id', $user_id)->remove()) { + return false; + } + }); + } + + /** + * Enable public access for a user + * + * @access public + * @param integer $user_id User id + * @return bool + */ + public function enablePublicAccess($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $user_id) + ->save(array('token' => Token::getToken())); + } + + /** + * Disable public access for a user + * + * @access public + * @param integer $user_id User id + * @return bool + */ + public function disablePublicAccess($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $user_id) + ->save(array('token' => '')); + } + + /** + * Get or create user id by using an external id (Google, GitHub, etc.) + * + * @param string $username Username + * @param string $name Full name + * @param string $externalIdColumn Column name for the external id (e.g. google_id, github_id, etc.) + * @param string $externalId External id (e.g. Google id, GitHub id, etc.) + * @return integer User id + */ + public function getOrCreateExternalUserId($username, $name, $externalIdColumn, $externalId) + { + if (! $this->db->isValidIdentifier($externalIdColumn)) { + throw new InvalidArgumentException('Invalid external id column name'); + } + + $userId = $this->db->table(self::TABLE)->eq($externalIdColumn, $externalId)->findOneColumn('id'); + + if (empty($userId)) { + $userId = $this->create(array( + 'username' => $username, + 'name' => $name, + 'is_ldap_user' => 1, + $externalIdColumn => $externalId, + )); + } + + return $userId; + } +} diff --git a/app/Model/UserNotificationFilterModel.php b/app/Model/UserNotificationFilterModel.php new file mode 100644 index 0000000..5cb2da6 --- /dev/null +++ b/app/Model/UserNotificationFilterModel.php @@ -0,0 +1,206 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * User Notification Filter + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class UserNotificationFilterModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const PROJECT_TABLE = 'user_has_notifications'; + + /** + * User filters + * + * @var integer + */ + const FILTER_NONE = 1; + const FILTER_ASSIGNEE = 2; + const FILTER_CREATOR = 3; + const FILTER_BOTH = 4; + + /** + * Get the list of filters + * + * @access public + * @return array + */ + public function getFilters() + { + return array( + self::FILTER_NONE => t('All tasks'), + self::FILTER_ASSIGNEE => t('Only for tasks assigned to me'), + self::FILTER_CREATOR => t('Only for tasks created by me'), + self::FILTER_BOTH => t('Only for tasks created by me and tasks assigned to me'), + ); + } + + /** + * Get user selected filter + * + * @access public + * @param integer $user_id + * @return integer + */ + public function getSelectedFilter($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->findOneColumn('notifications_filter'); + } + + /** + * Save selected filter for a user + * + * @access public + * @param integer $user_id + * @param string $filter + * @return boolean + */ + public function saveFilter($user_id, $filter) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array( + 'notifications_filter' => $filter, + )); + } + + /** + * Get user selected projects + * + * @access public + * @param integer $user_id + * @return array + */ + public function getSelectedProjects($user_id) + { + return $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->findAllByColumn('project_id'); + } + + /** + * Save selected projects for a user + * + * @access public + * @param integer $user_id + * @param integer[] $project_ids + * @return boolean + */ + public function saveSelectedProjects($user_id, array $project_ids) + { + $results = array(); + $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->remove(); + + foreach ($project_ids as $project_id) { + $results[] = $this->db->table(self::PROJECT_TABLE)->insert(array( + 'user_id' => $user_id, + 'project_id' => $project_id, + )); + } + + return !in_array(false, $results, true); + } + + /** + * Return true if the user should receive notification + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function shouldReceiveNotification(array $user, array $event_data) + { + $filters = array( + 'filterNone', + 'filterAssignee', + 'filterCreator', + 'filterBoth', + ); + + foreach ($filters as $filter) { + if ($this->$filter($user, $event_data)) { + return $this->filterProject($user, $event_data); + } + } + + return false; + } + + /** + * Return true if the user will receive all notifications + * + * @access public + * @param array $user + * @return boolean + */ + public function filterNone(array $user) + { + return $user['notifications_filter'] == self::FILTER_NONE; + } + + /** + * Return true if the user is the assignee and selected the filter "assignee" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterAssignee(array $user, array $event_data) + { + return $user['notifications_filter'] == self::FILTER_ASSIGNEE && $event_data['task']['owner_id'] == $user['id']; + } + + /** + * Return true if the user is the creator and enabled the filter "creator" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterCreator(array $user, array $event_data) + { + return $user['notifications_filter'] == self::FILTER_CREATOR && $event_data['task']['creator_id'] == $user['id']; + } + + /** + * Return true if the user is the assignee or the creator and selected the filter "both" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterBoth(array $user, array $event_data) + { + return $user['notifications_filter'] == self::FILTER_BOTH && + ($event_data['task']['creator_id'] == $user['id'] || $event_data['task']['owner_id'] == $user['id']); + } + + /** + * Return true if the user want to receive notification for the selected project + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterProject(array $user, array $event_data) + { + $projects = $this->getSelectedProjects($user['id']); + + if (! empty($projects)) { + return in_array($event_data['task']['project_id'], $projects); + } + + return true; + } +} diff --git a/app/Model/UserNotificationModel.php b/app/Model/UserNotificationModel.php new file mode 100644 index 0000000..6e4adc3 --- /dev/null +++ b/app/Model/UserNotificationModel.php @@ -0,0 +1,183 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; +use Kanboard\Core\Translator; + +/** + * User Notification + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class UserNotificationModel extends Base +{ + /** + * Send notifications to people + * + * @access public + * @param string $event_name + * @param array $event_data + */ + public function sendNotifications($event_name, array $event_data) + { + $users = $this->getUsersWithNotificationEnabled($event_data['task']['project_id'], $this->userSession->getId()); + + foreach ($users as $user) { + if ($this->userNotificationFilterModel->shouldReceiveNotification($user, $event_data)) { + $this->sendUserNotification($user, $event_name, $event_data); + } + } + } + + /** + * Send notification to someone + * + * @access public + * @param array $user User + * @param string $event_name + * @param array $event_data + */ + public function sendUserNotification(array $user, $event_name, array $event_data) + { + $loadedLocales = Translator::$locales; + Translator::unload(); + + // Use the user language otherwise use the application language (do not use the session language) + if (! empty($user['language'])) { + Translator::load($user['language']); + } else { + Translator::load($this->configModel->get('application_language', 'en_US')); + } + + foreach ($this->userNotificationTypeModel->getSelectedTypes($user['id']) as $type) { + $this->userNotificationTypeModel->getType($type)->notifyUser($user, $event_name, $event_data); + } + + // Restore locales + Translator::$locales = $loadedLocales; + } + + /** + * Get a list of people with notifications enabled + * + * @access public + * @param integer $project_id Project id + * @param integer $exclude_user_id User id to exclude + * @return array + */ + public function getUsersWithNotificationEnabled($project_id, $exclude_user_id = 0) + { + $users = array(); + $members = $this->getProjectUserMembersWithNotificationEnabled($project_id, $exclude_user_id); + $groups = $this->getProjectGroupMembersWithNotificationEnabled($project_id, $exclude_user_id); + + foreach (array_merge($members, $groups) as $user) { + if (! isset($users[$user['id']])) { + $users[$user['id']] = $user; + } + } + + return array_values($users); + } + + /** + * Enable notification for someone + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function enableNotification($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array('notifications_enabled' => 1)); + } + + /** + * Disable notification for someone + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function disableNotification($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array('notifications_enabled' => 0)); + } + + /** + * Save settings for the given user + * + * @access public + * @param integer $user_id User id + * @param array $values Form values + */ + public function saveSettings($user_id, array $values) + { + $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); + + if (! empty($types)) { + $this->enableNotification($user_id); + } else { + $this->disableNotification($user_id); + } + + $filter = empty($values['notifications_filter']) ? UserNotificationFilterModel::FILTER_BOTH : $values['notifications_filter']; + $project_ids = empty($values['notification_projects']) ? array() : array_keys($values['notification_projects']); + + $this->userNotificationFilterModel->saveFilter($user_id, $filter); + $this->userNotificationFilterModel->saveSelectedProjects($user_id, $project_ids); + $this->userNotificationTypeModel->saveSelectedTypes($user_id, $types); + } + + /** + * Read user settings to display the form + * + * @access public + * @param integer $user_id User id + * @return array + */ + public function readSettings($user_id) + { + $values = $this->db->table(UserModel::TABLE)->eq('id', $user_id)->columns('notifications_enabled', 'notifications_filter')->findOne(); + $values['notification_types'] = $this->userNotificationTypeModel->getSelectedTypes($user_id); + $values['notification_projects'] = $this->userNotificationFilterModel->getSelectedProjects($user_id); + return $values; + } + + /** + * Get a list of group members with notification enabled + * + * @access private + * @param integer $project_id Project id + * @param integer $exclude_user_id User id to exclude + * @return array + */ + private function getProjectUserMembersWithNotificationEnabled($project_id, $exclude_user_id) + { + return $this->db + ->table(ProjectUserRoleModel::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', UserModel::TABLE.'.email', UserModel::TABLE.'.language', UserModel::TABLE.'.notifications_filter') + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq(ProjectUserRoleModel::TABLE.'.project_id', $project_id) + ->eq(UserModel::TABLE.'.notifications_enabled', '1') + ->eq(UserModel::TABLE.'.is_active', 1) + ->neq(UserModel::TABLE.'.id', $exclude_user_id) + ->findAll(); + } + + private function getProjectGroupMembersWithNotificationEnabled($project_id, $exclude_user_id) + { + return $this->db + ->table(ProjectGroupRoleModel::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', UserModel::TABLE.'.email', UserModel::TABLE.'.language', UserModel::TABLE.'.notifications_filter') + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', ProjectGroupRoleModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', GroupMemberModel::TABLE) + ->eq(ProjectGroupRoleModel::TABLE.'.project_id', $project_id) + ->eq(UserModel::TABLE.'.notifications_enabled', '1') + ->neq(UserModel::TABLE.'.id', $exclude_user_id) + ->eq(UserModel::TABLE.'.is_active', 1) + ->findAll(); + } +} diff --git a/app/Model/UserNotificationTypeModel.php b/app/Model/UserNotificationTypeModel.php new file mode 100644 index 0000000..0f37722 --- /dev/null +++ b/app/Model/UserNotificationTypeModel.php @@ -0,0 +1,52 @@ +<?php + +namespace Kanboard\Model; + +/** + * User Notification Type + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class UserNotificationTypeModel extends NotificationTypeModel +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'user_has_notification_types'; + + /** + * Get selected notification types for a given user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getSelectedTypes($user_id) + { + $types = $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('notification_type')->findAllByColumn('notification_type'); + return $this->filterTypes($types); + } + + /** + * Save notification types for a given user + * + * @access public + * @param integer $user_id + * @param string[] $types + * @return boolean + */ + public function saveSelectedTypes($user_id, array $types) + { + $results = array(); + $this->db->table(self::TABLE)->eq('user_id', $user_id)->remove(); + + foreach ($types as $type) { + $results[] = $this->db->table(self::TABLE)->insert(array('user_id' => $user_id, 'notification_type' => $type)); + } + + return ! in_array(false, $results, true); + } +} diff --git a/app/Model/UserUnreadNotificationModel.php b/app/Model/UserUnreadNotificationModel.php new file mode 100644 index 0000000..6c930ee --- /dev/null +++ b/app/Model/UserUnreadNotificationModel.php @@ -0,0 +1,117 @@ +<?php + +namespace Kanboard\Model; + +use Kanboard\Core\Base; + +/** + * User Unread Notification + * + * @package Kanboard\Model + * @author Frederic Guillot + */ +class UserUnreadNotificationModel extends Base +{ + /** + * SQL table name + * + * @var string + */ + const TABLE = 'user_has_unread_notifications'; + + /** + * Add unread notification to someone + * + * @access public + * @param integer $user_id + * @param string $event_name + * @param array $event_data + */ + public function create($user_id, $event_name, array $event_data) + { + $this->db->table(self::TABLE)->insert(array( + 'user_id' => $user_id, + 'date_creation' => time(), + 'event_name' => $event_name, + 'event_data' => json_encode($event_data), + )); + } + + /** + * Get one notification + * + * @param integer $notification_id + * @return array|null + */ + public function getById($notification_id) + { + $notification = $this->db->table(self::TABLE)->eq('id', $notification_id)->findOne(); + + if (! empty($notification)) { + $this->unserialize($notification); + } + + return $notification; + } + + /** + * Get all notifications for a user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getAll($user_id) + { + $events = $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('date_creation')->findAll(); + + foreach ($events as &$event) { + $this->unserialize($event); + } + + return $events; + } + + /** + * Mark a notification as read + * + * @access public + * @param integer $user_id + * @param integer $notification_id + * @return boolean + */ + public function markAsRead($user_id, $notification_id) + { + return $this->db->table(self::TABLE)->eq('id', $notification_id)->eq('user_id', $user_id)->remove(); + } + + /** + * Mark all notifications as read for a user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function markAllAsRead($user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->remove(); + } + + /** + * Return true if the user as unread notifications + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function hasNotifications($user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->exists(); + } + + private function unserialize(&$event) + { + $event['event_data'] = json_decode($event['event_data'], true); + $event['title'] = $this->notificationModel->getTitleWithoutAuthor($event['event_name'], $event['event_data']); + } +} diff --git a/app/Notification/ActivityStreamNotification.php b/app/Notification/ActivityStreamNotification.php new file mode 100644 index 0000000..9f23c88 --- /dev/null +++ b/app/Notification/ActivityStreamNotification.php @@ -0,0 +1,48 @@ +<?php + +namespace Kanboard\Notification; + +use Kanboard\Core\Base; +use Kanboard\Core\Notification\NotificationInterface; + +/** + * Activity Stream Notification + * + * @package Kanboard\Notification + * @author Frederic Guillot + */ +class ActivityStreamNotification extends Base implements NotificationInterface +{ + /** + * Send notification to a user + * + * @access public + * @param array $user + * @param string $event_name + * @param array $event_data + */ + public function notifyUser(array $user, $event_name, array $event_data) + { + } + + /** + * Send notification to a project + * + * @access public + * @param array $project + * @param string $event_name + * @param array $event_data + */ + public function notifyProject(array $project, $event_name, array $event_data) + { + if ($this->userSession->isLogged()) { + $this->projectActivityModel->createEvent( + $project['id'], + $event_data['task']['id'], + $this->userSession->getId(), + $event_name, + $event_data + ); + } + } +} diff --git a/app/Notification/MailNotification.php b/app/Notification/MailNotification.php new file mode 100644 index 0000000..90ca432 --- /dev/null +++ b/app/Notification/MailNotification.php @@ -0,0 +1,84 @@ +<?php + +namespace Kanboard\Notification; + +use Kanboard\Core\Base; +use Kanboard\Core\Notification\NotificationInterface; + +/** + * Email Notification + * + * @package Kanboard\Notification + * @author Frederic Guillot + */ +class MailNotification extends Base implements NotificationInterface +{ + /** + * Notification type + * + * @var string + */ + const TYPE = 'email'; + + /** + * Send notification to a user + * + * @access public + * @param array $user + * @param string $event_name + * @param array $event_data + */ + public function notifyUser(array $user, $event_name, array $event_data) + { + if (! empty($user['email'])) { + $this->emailClient->send( + $user['email'], + $user['name'] ?: $user['username'], + $this->getMailSubject($event_name, $event_data), + $this->getMailContent($event_name, $event_data) + ); + } + } + + /** + * Send notification to a project + * + * @access public + * @param array $project + * @param string $event_name + * @param array $event_data + */ + public function notifyProject(array $project, $event_name, array $event_data) + { + } + + /** + * Get the mail content for a given template name + * + * @access public + * @param string $event_name Event name + * @param array $event_data Event data + * @return string + */ + public function getMailContent($event_name, array $event_data) + { + return $this->template->render('notification/'.str_replace('.', '_', $event_name), $event_data); + } + + /** + * Get the mail subject for a given template name + * + * @access public + * @param string $eventName Event name + * @param array $eventData Event data + * @return string + */ + public function getMailSubject($eventName, array $eventData) + { + return sprintf( + '[%s] %s', + isset($eventData['project_name']) ? $eventData['project_name'] : $eventData['task']['project_name'], + $this->notificationModel->getTitleWithoutAuthor($eventName, $eventData) + ); + } +} diff --git a/app/Notification/WebNotification.php b/app/Notification/WebNotification.php new file mode 100644 index 0000000..d881882 --- /dev/null +++ b/app/Notification/WebNotification.php @@ -0,0 +1,47 @@ +<?php + +namespace Kanboard\Notification; + +use Kanboard\Core\Base; +use Kanboard\Core\Notification\NotificationInterface; + +/** + * Web Notification + * + * @package Kanboard\Notification + * @author Frederic Guillot + */ +class WebNotification extends Base implements NotificationInterface +{ + /** + * Notification type + * + * @var string + */ + const TYPE = 'web'; + + /** + * Send notification to a user + * + * @access public + * @param array $user + * @param string $event_name + * @param array $event_data + */ + public function notifyUser(array $user, $event_name, array $event_data) + { + $this->userUnreadNotificationModel->create($user['id'], $event_name, $event_data); + } + + /** + * Send notification to a project + * + * @access public + * @param array $project + * @param string $event_name + * @param array $event_data + */ + public function notifyProject(array $project, $event_name, array $event_data) + { + } +} diff --git a/app/Notification/WebhookNotification.php b/app/Notification/WebhookNotification.php new file mode 100644 index 0000000..2d009fc --- /dev/null +++ b/app/Notification/WebhookNotification.php @@ -0,0 +1,62 @@ +<?php + +namespace Kanboard\Notification; + +use Kanboard\Core\Base; +use Kanboard\Core\Notification\NotificationInterface; + +/** + * Webhook Notification + * + * @package Kanboard\Notification + * @author Frederic Guillot + */ +class WebhookNotification extends Base implements NotificationInterface +{ + /** + * Send notification to a user + * + * @access public + * @param array $user + * @param string $event_name + * @param array $event_data + */ + public function notifyUser(array $user, $event_name, array $event_data) + { + } + + /** + * Send notification to a project + * + * @access public + * @param array $project + * @param string $event_name + * @param array $event_data + */ + public function notifyProject(array $project, $event_name, array $event_data) + { + $url = $this->configModel->get('webhook_url'); + $token = $this->configModel->get('webhook_token'); + + if (! empty($url)) { + if (! WEBHOOK_ALLOW_PRIVATE_NETWORKS && $this->httpClient->isPrivateURL($url)) { + $this->logger->info('Blocked webhook request to private network URL: '.$url); + return; + } + + if (strpos($url, '?') !== false) { + $url .= '&token='.$token; + } else { + $url .= '?token='.$token; + } + + $payload = array( + 'event_name' => $event_name, + 'event_data' => $event_data, + 'event_author' => ($this->userSession->isLogged() ? $this->userSession->getUsername() : null), + ); + + $this->httpClient->postJson($url, $payload, [], false, false); + } + } +} diff --git a/app/Pagination/DashboardPagination.php b/app/Pagination/DashboardPagination.php new file mode 100644 index 0000000..9669cb9 --- /dev/null +++ b/app/Pagination/DashboardPagination.php @@ -0,0 +1,54 @@ +<?php + +namespace Kanboard\Pagination; + +use Kanboard\Core\Base; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\TaskModel; + +/** + * Class DashboardPagination + * + * @package Kanboard\Pagination + * @author Frederic Guillot + */ +class DashboardPagination extends Base +{ + /** + * Get user listing pagination + * + * @access public + * @param integer $userId + * @return array + */ + public function getOverview($userId) + { + $paginators = array(); + $projects = $this->projectUserRoleModel->getActiveProjectsByUser($userId); + + foreach ($projects as $projectId => $projectName) { + + $query = $this->taskFinderModel->getUserQuery($userId)->eq(ProjectModel::TABLE.'.id', $projectId); + $this->hook->reference('pagination:dashboard:task:query', $query); + + $paginator = $this->paginator + ->setUrl('DashboardController', 'show', array('user_id' => $userId, 'pagination' => 'tasks-'.$projectId), 'project-tasks-'.$projectId) + ->setMax(15) + ->setOrder(TaskModel::TABLE.'.priority') + ->setDirection('DESC') + ->setFormatter($this->taskListSubtaskAssigneeFormatter->withUserId($userId)) + ->setQuery($query) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks-'.$projectId); + + if ($paginator->getTotal() > 0) { + $paginators[] = array( + 'project_id' => $projectId, + 'project_name' => $projectName, + 'paginator' => $paginator, + ); + } + } + + return $paginators; + } +} diff --git a/app/Pagination/ProjectPagination.php b/app/Pagination/ProjectPagination.php new file mode 100644 index 0000000..563998f --- /dev/null +++ b/app/Pagination/ProjectPagination.php @@ -0,0 +1,38 @@ +<?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) + { + $query = $this->projectModel->getQueryColumnStats($this->projectPermissionModel->getActiveProjectIds($user_id)); + $this->hook->reference('pagination:dashboard:project:query', $query); + + return $this->paginator + ->setUrl('DashboardController', $method, array('pagination' => 'projects', 'user_id' => $user_id)) + ->setMax($max) + ->setOrder(ProjectModel::TABLE.'.name') + ->setQuery($query) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'projects'); + } +} diff --git a/app/Pagination/SubtaskPagination.php b/app/Pagination/SubtaskPagination.php new file mode 100644 index 0000000..51a99a8 --- /dev/null +++ b/app/Pagination/SubtaskPagination.php @@ -0,0 +1,38 @@ +<?php + +namespace Kanboard\Pagination; + +use Kanboard\Core\Base; +use Kanboard\Core\Paginator; +use Kanboard\Model\TaskModel; + +/** + * Class SubtaskPagination + * + * @package Kanboard\Pagination + * @author Frederic Guillot + */ +class SubtaskPagination extends Base +{ + /** + * Get dashboard pagination + * + * @access public + * @param integer $userId + * @return Paginator + */ + public function getDashboardPaginator($userId) + { + $query = $this->taskFinderModel->getUserQuery($userId); + $this->hook->reference('pagination:dashboard:subtask:query', $query); + + return $this->paginator + ->setUrl('DashboardController', 'subtasks', array('user_id' => $userId)) + ->setMax(50) + ->setOrder(TaskModel::TABLE.'.priority') + ->setDirection('DESC') + ->setFormatter($this->taskListSubtaskAssigneeFormatter->withUserId($userId)->withoutEmptyTasks()) + ->setQuery($query) + ->calculate(); + } +} diff --git a/app/Pagination/TaskPagination.php b/app/Pagination/TaskPagination.php new file mode 100644 index 0000000..53e05c1 --- /dev/null +++ b/app/Pagination/TaskPagination.php @@ -0,0 +1,39 @@ +<?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 $userId + * @param string $method + * @param integer $max + * @return Paginator + */ + public function getDashboardPaginator($userId, $method, $max) + { + $query = $this->taskFinderModel->getUserQuery($userId); + $this->hook->reference('pagination:dashboard:task:query', $query); + + return $this->paginator + ->setUrl('DashboardController', $method, array('pagination' => 'tasks', 'user_id' => $userId)) + ->setMax($max) + ->setOrder(TaskModel::TABLE.'.id') + ->setQuery($query) + ->setFormatter($this->taskListFormatter) + ->calculateOnlyIf($this->request->getStringParam('pagination') === 'tasks'); + } +} diff --git a/app/Pagination/UserPagination.php b/app/Pagination/UserPagination.php new file mode 100644 index 0000000..8768857 --- /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 pagination + * + * @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/Migration.php b/app/Schema/Migration.php new file mode 100644 index 0000000..8264523 --- /dev/null +++ b/app/Schema/Migration.php @@ -0,0 +1,79 @@ +<?php + +namespace Schema; + +use PDO; + +function migrate_default_swimlane(PDO $pdo) +{ + $projects = get_all_projects($pdo); + + foreach ($projects as $project) { + if (empty($project['default_swimlane'])) { + $project['default_swimlane'] = 'Default swimlane'; + } + + $rq = $pdo->prepare('SELECT 1 FROM swimlanes WHERE name=? AND project_id=?'); + $rq->execute(array($project['default_swimlane'], $project['id'])); + + if ($rq->fetchColumn()) { + $project['default_swimlane'] = $project['default_swimlane'].' (Default swimlane)'; + } + + // Create new default swimlane + $rq = $pdo->prepare('INSERT INTO swimlanes (project_id, name, is_active, position) VALUES (?, ?, ?, ?)'); + $rq->execute(array( + $project['id'], + $project['default_swimlane'], + (int) $project['show_default_swimlane'], + $project['show_default_swimlane'] == 1 ? 1 : 0, + )); + + $swimlaneId = get_last_insert_id($pdo); + + // Reorder swimlanes if the default one was active + if ($project['show_default_swimlane']) { + $rq = $pdo->prepare("UPDATE swimlanes SET position=position+1 WHERE project_id=? AND is_active='1' AND id!=?"); + $rq->execute(array( + $project['id'], + $swimlaneId, + )); + } + + // Move all tasks to new swimlane + $rq = $pdo->prepare("UPDATE tasks SET swimlane_id=? WHERE swimlane_id='0' AND project_id=?"); + $rq->execute(array( + $swimlaneId, + $project['id'], + )); + + // Migrate automatic actions + $rq = $pdo->prepare("SELECT action_has_params.id FROM action_has_params LEFT JOIN actions ON actions.id=action_has_params.action_id WHERE project_id=? AND name='swimlane_id' AND value='0'"); + $rq->execute(array($project['id'])); + $ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0); + + $rq = $pdo->prepare("UPDATE action_has_params SET value=? WHERE id=?"); + + foreach ($ids as $id) { + $rq->execute(array($swimlaneId, $id)); + } + } +} + +function get_all_projects(PDO $pdo) +{ + $rq = $pdo->prepare('SELECT * FROM projects'); + $rq->execute(); + return $rq->fetchAll(PDO::FETCH_ASSOC); +} + +function get_last_insert_id(PDO $pdo) +{ + if (DB_DRIVER === 'postgres') { + $rq = $pdo->prepare('SELECT LASTVAL()'); + $rq->execute(); + return $rq->fetchColumn(); + } + + return $pdo->lastInsertId(); +} diff --git a/app/Schema/Mssql.php b/app/Schema/Mssql.php new file mode 100644 index 0000000..4d8b268 --- /dev/null +++ b/app/Schema/Mssql.php @@ -0,0 +1,733 @@ +<?php + +namespace Schema; + +require_once __DIR__.'/Migration.php'; + +use PDO; +use Kanboard\Core\Security\Token; +use Kanboard\Core\Security\Role; + +const VERSION = 3; + +function version_3(PDO $pdo) +{ + $pdo->exec("ALTER TABLE dbo.comments ADD visibility nvarchar(25) DEFAULT N'".Role::APP_USER."' NOT NULL"); +} + +function version_2(PDO $pdo) +{ + $pdo->exec("ALTER TABLE dbo.users ADD theme nvarchar(50) DEFAULT N'light' NOT NULL"); +} + +function version_1(PDO $pdo) +{ + // create tables + $pdo->exec(" + CREATE TABLE dbo.users ( + id int identity PRIMARY KEY + , username nvarchar(255) NOT NULL + , password nvarchar(255) + , is_ldap_user bit DEFAULT 0 + , name nvarchar(255) + , email nvarchar(255) + , google_id nvarchar(255) + , github_id nvarchar(30) + , notifications_enabled bit DEFAULT 0 + , timezone nvarchar(50) DEFAULT N'' + , language nvarchar(11) DEFAULT N'' + , disable_login_form bit DEFAULT 0 + , twofactor_activated bit DEFAULT 0 + , twofactor_secret char(16) + , token nvarchar(255) DEFAULT N'' + , notifications_filter int DEFAULT 4 + , nb_failed_login int DEFAULT 0 + , lock_expiration_date bigint DEFAULT 0 + , gitlab_id int + , role nvarchar(25) NOT NULL + , is_active bit DEFAULT 1 + , avatar_path nvarchar(255) + , api_access_token nvarchar(255) + , filter nvarchar(max) DEFAULT N'' + ); + "); + $pdo->exec(" + CREATE TABLE dbo.projects ( + id int identity PRIMARY KEY + , name nvarchar(max) NOT NULL + , is_active bit DEFAULT 1 + , token nvarchar(255) + , last_modified bigint DEFAULT 0 + , is_public bit DEFAULT 0 + , is_private bit DEFAULT 0 + , description nvarchar(max) + , identifier nvarchar(50) DEFAULT N'' + , start_date nvarchar(10) DEFAULT '' + , end_date nvarchar(10) DEFAULT '' + , owner_id int DEFAULT 0 + , priority_default int DEFAULT 0 + , priority_start int DEFAULT 0 + , priority_end int DEFAULT 3 + , email nvarchar(max) + , predefined_email_subjects nvarchar(max) + , per_swimlane_task_limits bit DEFAULT 0 NOT NULL + , task_limit int DEFAULT 0 + , enable_global_tags bit DEFAULT 1 NOT NULL + ); + "); + $pdo->exec(" + CREATE TABLE dbo.columns ( + id int identity PRIMARY KEY + , title nvarchar(255) NOT NULL + , position int + , project_id int NOT NULL + , task_limit int DEFAULT 0 + , description nvarchar(max) + , hide_in_dashboard bit DEFAULT 0 NOT NULL + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , UNIQUE (title, project_id) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_has_users ( + project_id int NOT NULL + , user_id int NOT NULL + , role nvarchar(255) NOT NULL + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + , UNIQUE(project_id, user_id) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.actions ( + id int identity PRIMARY KEY + , project_id int NOT NULL + , event_name nvarchar(max) NOT NULL + , action_name nvarchar(max) NOT NULL + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.action_has_params ( + id int identity PRIMARY KEY + , action_id int NOT NULL + , name nvarchar(max) NOT NULL + , value nvarchar(max) NOT NULL + , FOREIGN KEY(action_id) REFERENCES dbo.actions(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.remember_me ( + id int identity PRIMARY KEY + , user_id int NOT NULL + , ip nvarchar(45) + , user_agent nvarchar(255) + , token nvarchar(255) + , sequence nvarchar(255) + , expiration int + , date_creation bigint + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.last_logins ( + id int identity PRIMARY KEY + , auth_type nvarchar(25) + , user_id int NOT NULL + , ip nvarchar(45) + , user_agent nvarchar(255) + , date_creation bigint + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_has_categories ( + id int identity PRIMARY KEY + , name nvarchar(255) NOT NULL + , project_id int NOT NULL + , description nvarchar(max) + , color_id nvarchar(50) + , UNIQUE (project_id, name) + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.swimlanes ( + id int identity PRIMARY KEY + , name nvarchar(848) NOT NULL /* max size for unique index */ + , position int DEFAULT 1 + , is_active bit DEFAULT 1 + , project_id int NOT NULL + , description nvarchar(max) + , task_limit int DEFAULT 0 + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , UNIQUE (name, project_id) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.tasks + ( + id int identity PRIMARY KEY + , title nvarchar(max) NOT NULL + , description nvarchar(max) + , date_creation bigint + , color_id nvarchar(255) + , project_id int NOT NULL + , column_id int NOT NULL + , owner_id int DEFAULT 0 + , position int + , is_active bit DEFAULT 1 + , date_completed bigint + , score int + , date_due bigint + , category_id int DEFAULT 0 + , creator_id int DEFAULT 0 + , date_modification int DEFAULT 0 + , reference nvarchar(max) DEFAULT '' + , date_started bigint + , time_spent float DEFAULT 0 + , time_estimated float DEFAULT 0 + , swimlane_id int NOT NULL + , date_moved bigint DEFAULT 0 + , recurrence_status int DEFAULT 0 NOT NULL + , recurrence_trigger int DEFAULT 0 NOT NULL + , recurrence_factor int DEFAULT 0 NOT NULL + , recurrence_timeframe int DEFAULT 0 NOT NULL + , recurrence_basedate int DEFAULT 0 NOT NULL + , recurrence_parent int + , recurrence_child int + , priority int DEFAULT 0 + , external_provider nvarchar(255) + , external_uri nvarchar(255) + , FOREIGN KEY (project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , FOREIGN KEY (column_id) REFERENCES dbo.columns(id) ON DELETE NO ACTION /* columns_cascade_delete_trigger */ + , FOREIGN KEY (swimlane_id) REFERENCES dbo.swimlanes(id) ON DELETE NO ACTION /* swimlanes_cascade_delete_trigger */ + ); + "); + $pdo->exec(" + CREATE TABLE dbo.task_has_files ( + id int identity PRIMARY KEY + , name nvarchar(max) NOT NULL + , path nvarchar(max) + , is_image bit DEFAULT 0 + , task_id int NOT NULL + , date bigint NOT NULL DEFAULT 0 + , user_id int NOT NULL DEFAULT 0 + , size int NOT NULL DEFAULT 0 + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.subtasks ( + id int identity PRIMARY KEY + , title nvarchar(max) NOT NULL + , status smallint DEFAULT 0 + , time_estimated float DEFAULT 0 + , time_spent float DEFAULT 0 + , task_id int NOT NULL + , user_id int + , position int DEFAULT 1 + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.user_has_notifications ( + user_id int NOT NULL + , project_id int NOT NULL + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + , UNIQUE(project_id, user_id) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.settings ( + [option] nvarchar(100) PRIMARY KEY + , value nvarchar(max) DEFAULT '' + , changed_by int DEFAULT 0 NOT NULL + , changed_on int DEFAULT 0 NOT NULL + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_daily_column_stats ( + id int identity PRIMARY KEY + , day nchar(10) NOT NULL + , project_id int NOT NULL + , column_id int NOT NULL + , total int NOT NULL DEFAULT 0 + , score int NOT NULL DEFAULT 0 + , FOREIGN KEY(column_id) REFERENCES dbo.columns(id) ON DELETE CASCADE + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + ); + "); + $pdo->exec(" + CREATE TABLE dbo.subtask_time_tracking ( + id int identity PRIMARY KEY + , user_id int NOT NULL + , subtask_id int NOT NULL + , [start] bigint DEFAULT 0 + , [end] bigint DEFAULT 0 + , time_spent real DEFAULT 0 + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + , FOREIGN KEY(subtask_id) REFERENCES dbo.subtasks(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.links ( + id int identity PRIMARY KEY + , label nvarchar(255) NOT NULL + , opposite_id int DEFAULT 0 + , UNIQUE(label) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.task_has_links ( + id int identity PRIMARY KEY + , link_id int NOT NULL + , task_id int NOT NULL + , opposite_task_id int NOT NULL + , FOREIGN KEY(link_id) REFERENCES dbo.links(id) ON DELETE CASCADE + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + , FOREIGN KEY(opposite_task_id) REFERENCES dbo.tasks(id) ON DELETE NO ACTION /* Handled in tasks_cascade_delete_trigger */ + ); + "); + $pdo->exec(" + CREATE TABLE dbo.transitions ( + id int identity PRIMARY KEY + , user_id int NOT NULL + , project_id int NOT NULL + , task_id int NOT NULL + , src_column_id int NOT NULL + , dst_column_id int NOT NULL + , date bigint NOT NULL + , time_spent int DEFAULT 0 + , FOREIGN KEY(src_column_id) REFERENCES dbo.columns(id) ON DELETE NO ACTION /* columns_cascade_delete_trigger */ + , FOREIGN KEY(dst_column_id) REFERENCES dbo.columns(id) ON DELETE NO ACTION /* columns_cascade_delete_trigger */ + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.currencies ( + currency nvarchar(3) NOT NULL UNIQUE + , rate REAL DEFAULT 0 + ); + "); + $pdo->exec(" + CREATE TABLE dbo.comments ( + id int identity PRIMARY KEY + , task_id int NOT NULL + , user_id int DEFAULT 0 + , date_creation bigint NOT NULL + , comment nvarchar(max) NOT NULL + , reference nvarchar(max) DEFAULT N'' + , date_modification bigint + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_daily_stats ( + id int identity PRIMARY KEY + , day nchar(10) NOT NULL + , project_id int NOT NULL + , avg_lead_time int NOT NULL DEFAULT 0 + , avg_cycle_time int NOT NULL DEFAULT 0 + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.plugin_schema_versions ( + plugin nvarchar(80) NOT NULL PRIMARY KEY + , version int NOT NULL DEFAULT 0 + ); + "); + $pdo->exec(" + CREATE TABLE dbo.custom_filters ( + id int identity PRIMARY KEY + , filter nvarchar(max) NOT NULL + , project_id int NOT NULL + , user_id int NOT NULL + , name nvarchar(max) NOT NULL + , is_shared bit DEFAULT 0 + , append bit DEFAULT 0 + ); + "); + $pdo->exec(" + CREATE TABLE dbo.user_has_unread_notifications ( + id int identity PRIMARY KEY + , user_id int NOT NULL + , date_creation bigint NOT NULL + , event_name nvarchar(max) NOT NULL + , event_data nvarchar(max) NOT NULL + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.user_has_notification_types ( + id int identity PRIMARY KEY + , user_id int NOT NULL + , notification_type nvarchar(50) + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_has_notification_types ( + id int identity PRIMARY KEY + , project_id int NOT NULL + , notification_type nvarchar(50) NOT NULL + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + , UNIQUE(project_id, notification_type) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.user_has_metadata ( + user_id int NOT NULL + , name nvarchar(50) NOT NULL + , value nvarchar(255) DEFAULT '' + , changed_by int DEFAULT 0 NOT NULL + , changed_on int DEFAULT 0 NOT NULL /* TODO: should be bigint?? */ + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + , UNIQUE(user_id, name) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_has_metadata ( + project_id int NOT NULL + , name nvarchar(50) NOT NULL + , value nvarchar(255) DEFAULT '' + , changed_by int DEFAULT 0 NOT NULL + , changed_on int DEFAULT 0 NOT NULL /* TODO: should be bigint?? */ + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + , UNIQUE(project_id, name) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.task_has_metadata ( + task_id int NOT NULL + , name nvarchar(50) NOT NULL + , value nvarchar(255) DEFAULT '' + , changed_by int DEFAULT 0 NOT NULL + , changed_on int DEFAULT 0 NOT NULL /* TODO: should be bigint?? */ + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + , UNIQUE(task_id, name) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.groups ( + id int identity PRIMARY KEY + , external_id nvarchar(255) DEFAULT '' + , name nvarchar(850) NOT NULL UNIQUE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.group_has_users ( + group_id int NOT NULL + , user_id int NOT NULL + , FOREIGN KEY(group_id) REFERENCES dbo.groups(id) ON DELETE CASCADE + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + , UNIQUE(group_id, user_id) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_has_groups ( + group_id int NOT NULL + , project_id int NOT NULL + , role nvarchar(255) NOT NULL + , FOREIGN KEY(group_id) REFERENCES dbo.groups(id) ON DELETE CASCADE + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + , UNIQUE(group_id, project_id) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.password_reset ( + token nvarchar(80) PRIMARY KEY + , user_id int NOT NULL + , date_expiration int NOT NULL /* TODO: bigint?? */ + , date_creation int NOT NULL /* TODO: bigint?? */ + , ip nvarchar(45) NOT NULL + , user_agent nvarchar(255) NOT NULL + , is_active bit NOT NULL + , FOREIGN KEY(user_id) REFERENCES dbo.users(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.task_has_external_links ( + id int identity PRIMARY KEY + , link_type nvarchar(100) NOT NULL + , dependency nvarchar(100) NOT NULL + , title nvarchar(max) NOT NULL + , url nvarchar(max) NOT NULL + , date_creation int NOT NULL /* TODO: bigint?? */ + , date_modification int NOT NULL /* TODO: bigint?? */ + , task_id int NOT NULL + , creator_id int DEFAULT 0 + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_has_files ( + id int identity PRIMARY KEY + , project_id int NOT NULL + , name nvarchar(max) NOT NULL + , path nvarchar(max) NOT NULL + , is_image bit DEFAULT 0 + , size int DEFAULT 0 NOT NULL + , user_id int DEFAULT 0 NOT NULL + , date int DEFAULT 0 NOT NULL /* TODO: bigint?? */ + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.tags ( + id int identity PRIMARY KEY + , name nvarchar(255) NOT NULL + , project_id int NOT NULL + , color_id nvarchar(50) DEFAULT NULL + , UNIQUE(project_id, name) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.task_has_tags ( + task_id int NOT NULL + , tag_id int NOT NULL + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + , FOREIGN KEY(tag_id) REFERENCES dbo.tags(id) ON DELETE CASCADE + , UNIQUE(tag_id, task_id) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_has_roles ( + role_id int identity PRIMARY KEY + , role nvarchar(255) NOT NULL + , project_id int NOT NULL + , UNIQUE(project_id, role) + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.column_has_move_restrictions ( + restriction_id int identity PRIMARY KEY + , project_id int NOT NULL + , role_id int NOT NULL + , src_column_id int NOT NULL + , dst_column_id int NOT NULL + , only_assigned bit DEFAULT 0 + , UNIQUE(role_id, src_column_id, dst_column_id) + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , FOREIGN KEY(role_id) REFERENCES dbo.project_has_roles(role_id) ON DELETE CASCADE + , FOREIGN KEY(src_column_id) REFERENCES dbo.columns(id) ON DELETE NO ACTION /* columns_cascade_delete_trigger */ + , FOREIGN KEY(dst_column_id) REFERENCES dbo.columns(id) ON DELETE NO ACTION /* columns_cascade_delete_trigger */ + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_role_has_restrictions ( + restriction_id int identity PRIMARY KEY + , project_id int NOT NULL + , role_id int NOT NULL + , [rule] nvarchar(255) NOT NULL + , UNIQUE(role_id, [rule]) + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , FOREIGN KEY(role_id) REFERENCES dbo.project_has_roles(role_id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.column_has_restrictions ( + restriction_id int identity PRIMARY KEY + , project_id int NOT NULL + , role_id int NOT NULL + , column_id int NOT NULL + , [rule] nvarchar(255) NOT NULL + , UNIQUE(role_id, column_id, [rule]) + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , FOREIGN KEY(role_id) REFERENCES dbo.project_has_roles(role_id) ON DELETE CASCADE + , FOREIGN KEY(column_id) REFERENCES dbo.columns(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.invites ( + email nvarchar(255) NOT NULL + , project_id int NOT NULL + , token nvarchar(255) NOT NULL + , PRIMARY KEY(email, token) + ); + "); + $pdo->exec(" + CREATE TABLE dbo.project_activities ( + id int identity PRIMARY KEY + , date_creation bigint NOT NULL + , event_name nvarchar(max) NOT NULL + , creator_id int NOT NULL + , project_id int NOT NULL + , task_id int NOT NULL + , data nvarchar(max) + , FOREIGN KEY(creator_id) REFERENCES dbo.users(id) ON DELETE CASCADE + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE NO ACTION /* projects_cascade_delete_trigger */ + , FOREIGN KEY(task_id) REFERENCES dbo.tasks(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.predefined_task_descriptions ( + id int identity PRIMARY KEY + , project_id int NOT NULL + , title nvarchar(max) NOT NULL + , description nvarchar(max) NOT NULL + , FOREIGN KEY(project_id) REFERENCES dbo.projects(id) ON DELETE CASCADE + ); + "); + $pdo->exec(" + CREATE TABLE dbo.sessions ( + id nvarchar(450) PRIMARY KEY /* max length for primary key */ + , expire_at int NOT NULL + , data nvarchar(max) DEFAULT '' + ); + "); + + // create triggers -- each of which must be in its own batch + $pdo->exec(" + CREATE TRIGGER dbo.columns_cascade_delete_trigger + ON dbo.columns INSTEAD OF DELETE + AS + SET NOCOUNT ON; + DELETE dbo.column_has_move_restrictions + WHERE src_column_id IN (SELECT id FROM deleted) + OR dst_column_id IN (SELECT id FROM deleted); + DELETE dbo.transitions + WHERE src_column_id IN (SELECT id FROM deleted) + OR dst_column_id IN (SELECT id FROM deleted); + DELETE dbo.tasks + WHERE column_id IN (SELECT id FROM deleted); + DELETE dbo.columns + WHERE id IN (SELECT id FROM deleted); + "); + + $pdo->exec(" + CREATE TRIGGER projects_cascade_delete_trigger + ON dbo.projects INSTEAD OF DELETE + AS + SET NOCOUNT ON; + DELETE dbo.column_has_move_restrictions + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.column_has_restrictions + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.columns + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.project_activities + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.project_daily_column_stats + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.project_role_has_restrictions + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.swimlanes + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.tasks + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.transitions + WHERE project_id IN (SELECT id FROM deleted); + DELETE dbo.projects + WHERE id IN (SELECT id FROM deleted); + "); + + $pdo->exec(" + CREATE TRIGGER dbo.swimlanes_cascade_delete_trigger + ON dbo.swimlanes INSTEAD OF DELETE + AS + SET NOCOUNT ON; + DELETE dbo.tasks + WHERE swimlane_id IN (SELECT id FROM deleted); + DELETE dbo.swimlanes + WHERE id IN (SELECT id FROM deleted); + "); + + $pdo->exec(" + CREATE TRIGGER dbo.tasks_cascade_delete_trigger + ON dbo.tasks INSTEAD OF DELETE + AS + SET NOCOUNT ON; + DELETE dbo.task_has_links + WHERE opposite_task_id IN (SELECT id FROM deleted); + DELETE dbo.tasks + WHERE id IN (SELECT id FROM deleted); + "); + + // set defaults + $pdo->exec(" + ALTER TABLE dbo.project_has_users + ADD DEFAULT N'" .Role::PROJECT_VIEWER. "' FOR role; + "); + $pdo->exec(" + ALTER TABLE dbo.users + ADD DEFAULT N'" .Role::APP_USER. "' FOR role; + "); + + // insert starting data + $aui = $pdo->prepare("INSERT INTO dbo.users (username, password, role) VALUES (?, ?, ?);"); + $aui->execute(array('admin', \password_hash('admin', PASSWORD_BCRYPT), Role::APP_ADMIN)); + + $rq = $pdo->prepare('INSERT INTO dbo.settings ([option],value) VALUES (?, ?);'); + $rq->execute(array('api_token', Token::getToken())); + $rq->execute(array('application_url', defined('KANBOARD_URL') ? KANBOARD_URL : '')); + $rq->execute(array('board_highlight_period', defined('RECENT_TASK_PERIOD') ? RECENT_TASK_PERIOD : 48*60*60)); + $rq->execute(array('board_private_refresh_interval', defined('BOARD_CHECK_INTERVAL') ? BOARD_CHECK_INTERVAL : 10)); + $rq->execute(array('board_public_refresh_interval', defined('BOARD_PUBLIC_CHECK_INTERVAL') ? BOARD_PUBLIC_CHECK_INTERVAL : 60)); + $rq->execute(array('webhook_token', Token::getToken())); + + $pdo->exec(" + INSERT INTO dbo.settings ([option], value) VALUES + ('application_currency','USD'), + ('application_date_format','m/d/Y'), + ('application_language','en_US'), + ('application_stylesheet',''), + ('application_time_format','H:i'), + ('application_timezone','UTC'), + ('board_columns',''), + ('calendar_project_tasks','date_started'), + ('calendar_user_subtasks_time_tracking','0'), + ('calendar_user_tasks','date_started'), + ('cfd_include_closed_tasks','1'), + ('default_color','yellow'), + ('integration_gravatar','0'), + ('password_reset','1'), + ('project_categories',''), + ('subtask_restriction','0'), + ('subtask_time_tracking','1'), + ('webhook_url','') + ; + "); + + $pdo->exec(" + SET IDENTITY_INSERT dbo.links ON; + INSERT INTO dbo.links (id, label, opposite_id) VALUES + (1,'relates to',0), + (2,'blocks',3), + (3,'is blocked by',2), + (4,'duplicates',5), + (5,'is duplicated by',4), + (6,'is a child of',7), + (7,'is a parent of',6), + (8,'targets milestone',9), + (9,'is a milestone of',8), + (10,'fixes',11), + (11,'is fixed by',10) + ; + SET IDENTITY_INSERT dbo.links OFF; + "); + + // create indexes + $pdo->exec(" + CREATE UNIQUE INDEX users_username_idx ON dbo.users(username); + CREATE UNIQUE INDEX project_daily_column_stats_idx ON dbo.project_daily_column_stats(day, project_id, column_id); + CREATE UNIQUE INDEX task_has_links_unique ON dbo.task_has_links(link_id, task_id, opposite_task_id); + CREATE UNIQUE INDEX project_daily_stats_idx ON dbo.project_daily_stats(day, project_id); + CREATE UNIQUE INDEX user_has_notification_types_user_idx ON dbo.user_has_notification_types(user_id, notification_type); + + CREATE INDEX columns_project_idx ON dbo.columns(project_id); + CREATE INDEX swimlanes_project_idx ON dbo.swimlanes(project_id); + CREATE INDEX categories_project_idx ON dbo.project_has_categories(project_id); + CREATE INDEX subtasks_task_idx ON dbo.subtasks(task_id); + CREATE INDEX files_task_idx ON dbo.task_has_files(task_id); + CREATE INDEX task_has_links_task_index ON dbo.task_has_links(task_id); + CREATE INDEX transitions_task_index ON dbo.transitions(task_id); + CREATE INDEX transitions_project_index ON dbo.transitions(project_id); + CREATE INDEX transitions_user_index ON dbo.transitions(user_id); + "); +} diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php new file mode 100644 index 0000000..0b969fa --- /dev/null +++ b/app/Schema/Mysql.php @@ -0,0 +1,1700 @@ +<?php + +namespace Schema; + +require_once __DIR__.'/Migration.php'; + +use PDO; +use Kanboard\Core\Security\Token; +use Kanboard\Core\Security\Role; + +const VERSION = 139; + +function version_139(PDO $pdo) +{ + $pdo->exec("ALTER TABLE `comments` ADD COLUMN `visibility` VARCHAR(25) NOT NULL DEFAULT '".Role::APP_USER."'"); +} + +function version_138(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN theme VARCHAR(50) DEFAULT 'light' NOT NULL"); +} + +function version_137(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `projects` ADD COLUMN `enable_global_tags` TINYINT(1) DEFAULT 1 NOT NULL'); +} + +function version_136(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `swimlanes` ADD COLUMN `task_limit` INT DEFAULT 0'); +} + +function version_135(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `projects` ADD COLUMN `task_limit` INT DEFAULT 0'); +} + +function version_134(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `projects` ADD COLUMN `per_swimlane_task_limits` INT DEFAULT 0 NOT NULL'); +} + +function version_133(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `tags` ADD COLUMN `color_id` VARCHAR(50) DEFAULT NULL'); +} + +function version_132(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `project_has_categories` ADD COLUMN `color_id` VARCHAR(50) DEFAULT NULL'); +} + +function version_131(PDO $pdo) +{ + $pdo->exec("ALTER TABLE `users` MODIFY `language` VARCHAR(11) DEFAULT NULL"); +} + +/* + +This migration convert table encoding to utf8mb4. +You should also convert the database encoding: + +ALTER DATABASE kanboard CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci; + +You might need to run: + +REPAIR TABLE table_name; +OPTIMIZE TABLE table_name; + +The max length for Mysql 5.6 is 191 for varchar unique keys in utf8mb4 + +*/ +function version_130(PDO $pdo) +{ + $pdo->exec("ALTER TABLE `swimlanes` MODIFY `name` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `users` MODIFY `username` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `groups` MODIFY `name` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `links` MODIFY `label` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `tags` MODIFY `name` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `sessions` MODIFY `id` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `project_role_has_restrictions` MODIFY `rule` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `project_has_roles` MODIFY `role` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `project_has_categories` MODIFY `name` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `invites` MODIFY `email` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `invites` MODIFY `token` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `groups` MODIFY `name` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `columns` MODIFY `title` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `column_has_restrictions` MODIFY `rule` VARCHAR(191) NOT NULL"); + $pdo->exec("ALTER TABLE `comments` MODIFY `reference` VARCHAR(191) DEFAULT ''"); + $pdo->exec("ALTER TABLE `tasks` MODIFY `reference` VARCHAR(191) DEFAULT ''"); + + $tables = [ + 'action_has_params', + 'actions', + 'column_has_move_restrictions', + 'column_has_restrictions', + 'columns', + 'comments', + 'currencies', + 'custom_filters', + 'group_has_users', + 'groups', + 'invites', + 'last_logins', + 'links', + 'password_reset', + 'plugin_schema_versions', + 'predefined_task_descriptions', + 'project_activities', + 'project_daily_column_stats', + 'project_daily_stats', + 'project_has_categories', + 'project_has_files', + 'project_has_groups', + 'project_has_metadata', + 'project_has_notification_types', + 'project_has_roles', + 'project_has_users', + 'project_role_has_restrictions', + 'projects', + 'remember_me', + 'sessions', + 'settings', + 'subtask_time_tracking', + 'subtasks', + 'swimlanes', + 'tags', + 'task_has_external_links', + 'task_has_files', + 'task_has_links', + 'task_has_metadata', + 'task_has_tags', + 'tasks', + 'transitions', + 'user_has_metadata', + 'user_has_notification_types', + 'user_has_notifications', + 'user_has_unread_notifications', + 'users', + ]; + + foreach ($tables as $table) { + $pdo->exec('ALTER TABLE `'.$table.'` CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'); + } +} + +function version_129(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `projects` MODIFY `name` TEXT NOT NULL'); + $pdo->exec('ALTER TABLE `projects` MODIFY `email` TEXT'); + $pdo->exec('ALTER TABLE `action_has_params` MODIFY `name` TEXT NOT NULL'); + $pdo->exec('ALTER TABLE `action_has_params` MODIFY `value` TEXT NOT NULL'); + $pdo->exec('ALTER TABLE `actions` MODIFY `event_name` TEXT NOT NULL'); + $pdo->exec('ALTER TABLE `actions` MODIFY `action_name` TEXT NOT NULL'); + $pdo->exec("ALTER TABLE `comments` MODIFY `reference` VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE `custom_filters` MODIFY `filter` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `custom_filters` MODIFY `name` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `groups` MODIFY `name` VARCHAR(255) NOT NULL"); + $pdo->exec("ALTER TABLE `project_activities` MODIFY `event_name` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `project_has_files` MODIFY `name` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `project_has_files` MODIFY `path` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `subtasks` MODIFY `title` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `swimlanes` MODIFY `name` VARCHAR(255) NOT NULL"); + $pdo->exec("ALTER TABLE `task_has_external_links` MODIFY `title` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `task_has_external_links` MODIFY `url` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `task_has_files` MODIFY `name` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `task_has_files` MODIFY `path` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `tasks` MODIFY `title` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `tasks` MODIFY `reference` VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE `user_has_unread_notifications` MODIFY `event_name` TEXT NOT NULL"); + $pdo->exec("ALTER TABLE `users` MODIFY `username` VARCHAR(255) NOT NULL"); + $pdo->exec("ALTER TABLE `users` MODIFY `filter` TEXT"); +} + +function version_128(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `users` ADD COLUMN `filter` VARCHAR(255) DEFAULT NULL'); +} + +function version_127(PDO $pdo) +{ + $pdo->exec("CREATE TABLE sessions ( + id VARCHAR(255) NOT NULL, + expire_at INT NOT NULL, + data LONGTEXT, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8"); +} + +function version_126(PDO $pdo) +{ + $pdo->exec('CREATE TABLE predefined_task_descriptions ( + id INT NOT NULL AUTO_INCREMENT, + project_id INT NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8'); +} + +function version_125(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects DROP COLUMN is_everybody_allowed'); +} + +function version_124(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN predefined_email_subjects TEXT'); +} + +function version_123(PDO $pdo) +{ + $pdo->exec('ALTER TABLE column_has_move_restrictions ADD COLUMN only_assigned TINYINT(1) DEFAULT 0'); +} + +function version_122(PDO $pdo) +{ + migrate_default_swimlane($pdo); + + $pdo->exec('ALTER TABLE `projects` DROP COLUMN `default_swimlane`'); + $pdo->exec('ALTER TABLE `projects` DROP COLUMN `show_default_swimlane`'); + $pdo->exec('ALTER TABLE `tasks` MODIFY `swimlane_id` INT(11) NOT NULL;'); + $pdo->exec('ALTER TABLE tasks ADD CONSTRAINT tasks_swimlane_ibfk_1 FOREIGN KEY (swimlane_id) REFERENCES swimlanes(id) ON DELETE CASCADE'); +} + +function version_121(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN email VARCHAR(255)'); +} + +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) +{ + $pdo->exec('ALTER TABLE columns ADD COLUMN hide_in_dashboard INT DEFAULT 0 NOT NULL'); +} + +function version_111(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE tags ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(255) NOT NULL, + project_id INT NOT NULL, + UNIQUE(project_id, name), + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE task_has_tags ( + task_id INT NOT NULL, + tag_id INT NOT NULL, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE, + UNIQUE(tag_id, task_id) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_110(PDO $pdo) +{ + $pdo->exec("ALTER TABLE user_has_notifications DROP FOREIGN KEY `user_has_notifications_ibfk_1`"); + $pdo->exec("ALTER TABLE user_has_notifications DROP FOREIGN KEY `user_has_notifications_ibfk_2`"); + $pdo->exec("DROP INDEX `project_id` ON user_has_notifications"); + $pdo->exec("ALTER TABLE user_has_notifications DROP KEY `user_id`"); + $pdo->exec("CREATE UNIQUE INDEX `user_has_notifications_unique_idx` ON `user_has_notifications` (`user_id`, `project_id`)"); + $pdo->exec("ALTER TABLE user_has_notifications ADD CONSTRAINT user_has_notifications_ibfk_1 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE"); + $pdo->exec("ALTER TABLE user_has_notifications ADD CONSTRAINT user_has_notifications_ibfk_2 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE"); +} + +function version_109(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN avatar_path VARCHAR(255)"); +} + +function version_108(PDO $pdo) +{ + $pdo->exec("ALTER TABLE user_has_metadata ADD COLUMN changed_by INT DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE user_has_metadata ADD COLUMN changed_on INT DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE project_has_metadata ADD COLUMN changed_by INT DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE project_has_metadata ADD COLUMN changed_on INT DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE task_has_metadata ADD COLUMN changed_by INT DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE task_has_metadata ADD COLUMN changed_on INT DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE settings ADD COLUMN changed_by INT DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE settings ADD COLUMN changed_on INT DEFAULT 0 NOT NULL"); +} + +function version_107(PDO $pdo) +{ + $pdo->exec("UPDATE project_activities SET event_name='task.file.create' WHERE event_name='file.create'"); +} + +function version_106(PDO $pdo) +{ + $pdo->exec('RENAME TABLE files TO task_has_files'); + + $pdo->exec( + " + CREATE TABLE project_has_files ( + `id` INT NOT NULL AUTO_INCREMENT, + `project_id` INT NOT NULL, + `name` VARCHAR(255) NOT NULL, + `path` VARCHAR(255) NOT NULL, + `is_image` TINYINT(1) DEFAULT 0, + `size` INT DEFAULT 0 NOT NULL, + `user_id` INT DEFAULT 0 NOT NULL, + `date` INT DEFAULT 0 NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8" + ); +} + +function version_105(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_active TINYINT(1) DEFAULT 1"); +} + +function version_104(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_external_links ( + id INT NOT NULL AUTO_INCREMENT, + link_type VARCHAR(100) NOT NULL, + dependency VARCHAR(100) NOT NULL, + title VARCHAR(255) NOT NULL, + url VARCHAR(255) NOT NULL, + date_creation INT NOT NULL, + date_modification INT NOT NULL, + task_id INT NOT NULL, + creator_id INT DEFAULT 0, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_103(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_default INT DEFAULT 0"); + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_start INT DEFAULT 0"); + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_end INT DEFAULT 3"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN priority INT DEFAULT 0"); +} + +function version_102(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN owner_id INT DEFAULT 0"); +} + +function version_101(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE password_reset ( + token VARCHAR(80) PRIMARY KEY, + user_id INT NOT NULL, + date_expiration INT NOT NULL, + date_creation INT NOT NULL, + ip VARCHAR(45) NOT NULL, + user_agent VARCHAR(255) NOT NULL, + is_active TINYINT(1) NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec("INSERT INTO settings VALUES ('password_reset', '1')"); +} + +function version_100(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `actions` MODIFY `action_name` VARCHAR(255)'); +} + +function version_99(PDO $pdo) +{ + $rq = $pdo->prepare('SELECT * FROM actions'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE actions SET action_name=? WHERE id=?'); + + foreach ($rows as $row) { + if ($row['action_name'] === 'TaskAssignCurrentUser' && $row['event_name'] === 'task.move.column') { + $row['action_name'] = '\Kanboard\Action\TaskAssignCurrentUserColumn'; + } elseif ($row['action_name'] === 'TaskClose' && $row['event_name'] === 'task.move.column') { + $row['action_name'] = '\Kanboard\Action\TaskCloseColumn'; + } elseif ($row['action_name'] === 'TaskLogMoveAnotherColumn') { + $row['action_name'] = '\Kanboard\Action\CommentCreationMoveTaskColumn'; + } elseif ($row['action_name'][0] !== '\\') { + $row['action_name'] = '\Kanboard\Action\\'.$row['action_name']; + } + + $rq->execute(array($row['action_name'], $row['id'])); + } +} + +function version_98(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `users` MODIFY `language` VARCHAR(5)'); +} + +function version_97(PDO $pdo) +{ + $pdo->exec("ALTER TABLE `users` ADD COLUMN `role` VARCHAR(25) NOT NULL DEFAULT '".Role::APP_USER."'"); + + $rq = $pdo->prepare('SELECT * FROM `users`'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE `users` SET `role`=? WHERE `id`=?'); + + foreach ($rows as $row) { + $role = Role::APP_USER; + + if ($row['is_admin'] == 1) { + $role = Role::APP_ADMIN; + } elseif ($row['is_project_admin']) { + $role = Role::APP_MANAGER; + } + + $rq->execute(array($role, $row['id'])); + } + + $pdo->exec('ALTER TABLE `users` DROP COLUMN `is_admin`'); + $pdo->exec('ALTER TABLE `users` DROP COLUMN `is_project_admin`'); +} + +function version_96(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_has_groups ( + `group_id` INT NOT NULL, + `project_id` INT NOT NULL, + `role` VARCHAR(25) NOT NULL, + FOREIGN KEY(group_id) REFERENCES `groups`(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(group_id, project_id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec("ALTER TABLE `project_has_users` ADD COLUMN `role` VARCHAR(25) NOT NULL DEFAULT '".Role::PROJECT_VIEWER."'"); + + $rq = $pdo->prepare('SELECT * FROM project_has_users'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE `project_has_users` SET `role`=? WHERE `id`=?'); + + foreach ($rows as $row) { + $rq->execute(array( + $row['is_owner'] == 1 ? Role::PROJECT_MANAGER : Role::PROJECT_MEMBER, + $row['id'], + )); + } + + $pdo->exec('ALTER TABLE `project_has_users` DROP COLUMN `is_owner`'); + $pdo->exec('ALTER TABLE `project_has_users` DROP COLUMN `id`'); +} + +function version_95(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE `groups` ( + id INT NOT NULL AUTO_INCREMENT, + external_id VARCHAR(255) DEFAULT '', + name VARCHAR(100) NOT NULL UNIQUE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE group_has_users ( + group_id INT NOT NULL, + user_id INT NOT NULL, + FOREIGN KEY(group_id) REFERENCES `groups`(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(group_id, user_id) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_94(PDO $pdo) +{ + $pdo->exec('ALTER TABLE `projects` DROP INDEX `name`'); + $pdo->exec('ALTER TABLE `projects` DROP INDEX `name_2`'); +} + +function version_93(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE user_has_metadata ( + user_id INT NOT NULL, + name VARCHAR(50) NOT NULL, + value VARCHAR(255) DEFAULT '', + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(user_id, name) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE project_has_metadata ( + project_id INT NOT NULL, + name VARCHAR(50) NOT NULL, + value VARCHAR(255) DEFAULT '', + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, name) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE task_has_metadata ( + task_id INT NOT NULL, + name VARCHAR(50) NOT NULL, + value VARCHAR(255) DEFAULT '', + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + UNIQUE(task_id, name) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec("DROP TABLE project_integrations"); + + $pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_server'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_domain'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_username'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_password'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_nickname'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_jabber_room'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat_api_url'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat_room_id'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_hipchat_room_token'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_slack_webhook'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_slack_webhook_url'"); + $pdo->exec("DELETE FROM settings WHERE `option`='integration_slack_webhook_channel'"); +} + +function version_92(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_has_notification_types ( + id INT NOT NULL AUTO_INCREMENT, + project_id INT NOT NULL, + notification_type VARCHAR(50) NOT NULL, + PRIMARY KEY(id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, notification_type) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_91(PDO $pdo) +{ + $pdo->exec("ALTER TABLE custom_filters ADD COLUMN `append` TINYINT(1) DEFAULT 0"); +} + +function version_90(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks MODIFY date_due BIGINT"); + $pdo->exec("ALTER TABLE tasks MODIFY date_creation BIGINT"); + $pdo->exec("ALTER TABLE tasks MODIFY date_completed BIGINT"); + $pdo->exec("ALTER TABLE tasks MODIFY date_started BIGINT"); + $pdo->exec("ALTER TABLE tasks MODIFY date_moved BIGINT"); + $pdo->exec("ALTER TABLE comments MODIFY date_creation BIGINT"); + $pdo->exec("ALTER TABLE last_logins MODIFY date_creation BIGINT"); + $pdo->exec("ALTER TABLE project_activities MODIFY date_creation BIGINT"); + $pdo->exec("ALTER TABLE projects MODIFY last_modified BIGINT"); + $pdo->exec("ALTER TABLE remember_me MODIFY date_creation BIGINT"); + $pdo->exec('ALTER TABLE files MODIFY `date` BIGINT'); + $pdo->exec('ALTER TABLE transitions MODIFY `date` BIGINT'); + $pdo->exec('ALTER TABLE subtask_time_tracking MODIFY `start` BIGINT'); + $pdo->exec('ALTER TABLE subtask_time_tracking MODIFY `end` BIGINT'); + $pdo->exec('ALTER TABLE users MODIFY `lock_expiration_date` BIGINT'); +} + +function version_89(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE user_has_unread_notifications ( + id INT NOT NULL AUTO_INCREMENT, + user_id INT NOT NULL, + date_creation BIGINT NOT NULL, + event_name VARCHAR(50) NOT NULL, + event_data TEXT NOT NULL, + PRIMARY KEY(id), + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE user_has_notification_types ( + id INT NOT NULL AUTO_INCREMENT, + user_id INT NOT NULL, + notification_type VARCHAR(50), + PRIMARY KEY(id), + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('CREATE UNIQUE INDEX user_has_notification_types_user_idx ON user_has_notification_types(user_id, notification_type)'); + + // Migrate people who have notification enabled before + $rq = $pdo->prepare('SELECT id FROM users WHERE notifications_enabled=1'); + $rq->execute(); + $user_ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0); + + foreach ($user_ids as $user_id) { + $rq = $pdo->prepare('INSERT INTO user_has_notification_types (user_id, notification_type) VALUES (?, ?)'); + $rq->execute(array($user_id, 'email')); + } +} + +function version_88(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE custom_filters ( + id INT NOT NULL AUTO_INCREMENT, + filter VARCHAR(100) NOT NULL, + project_id INT NOT NULL, + user_id INT NOT NULL, + name VARCHAR(100) NOT NULL, + is_shared TINYINT(1) DEFAULT 0, + PRIMARY KEY(id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_87(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE plugin_schema_versions ( + plugin VARCHAR(80) NOT NULL, + version INT NOT NULL DEFAULT 0, + PRIMARY KEY(plugin) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_86(PDO $pdo) +{ + $pdo->exec("ALTER TABLE swimlanes ADD COLUMN description TEXT"); +} + +function version_85(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN gitlab_id INT"); +} + +function version_84(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN start_date VARCHAR(10) DEFAULT ''"); + $pdo->exec("ALTER TABLE projects ADD COLUMN end_date VARCHAR(10) DEFAULT ''"); +} + +function version_83(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_project_admin INT DEFAULT 0"); +} + +function version_82(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN nb_failed_login INT DEFAULT 0"); + $pdo->exec("ALTER TABLE users ADD COLUMN lock_expiration_date INT DEFAULT 0"); +} + +function version_81(PDO $pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('subtask_time_tracking', '1')"); + $pdo->exec("INSERT INTO settings VALUES ('cfd_include_closed_tasks', '1')"); +} + +function version_80(PDO $pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('default_color', 'yellow')"); +} + +function version_79(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id INT NOT NULL AUTO_INCREMENT, + day CHAR(10) NOT NULL, + project_id INT NOT NULL, + avg_lead_time INT NOT NULL DEFAULT 0, + avg_cycle_time INT NOT NULL DEFAULT 0, + PRIMARY KEY(id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('RENAME TABLE project_daily_summaries TO project_daily_column_stats'); +} + +function version_78(PDO $pdo) +{ + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN slack_webhook_channel VARCHAR(255) DEFAULT ''"); + $pdo->exec("INSERT INTO settings VALUES ('integration_slack_webhook_channel', '')"); +} + +function version_77(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users DROP COLUMN `default_project_id`'); +} + +function version_76(PDO $pdo) +{ + $pdo->exec("DELETE FROM `settings` WHERE `option`='subtask_time_tracking'"); +} + +function version_75(PDO $pdo) +{ + $pdo->exec('ALTER TABLE comments DROP FOREIGN KEY comments_ibfk_2'); + $pdo->exec('ALTER TABLE comments MODIFY task_id INT NOT NULL'); + $pdo->exec('ALTER TABLE comments CHANGE COLUMN `user_id` `user_id` INT DEFAULT 0'); + $pdo->exec('ALTER TABLE comments CHANGE COLUMN `date` `date_creation` INT NOT NULL'); +} + +function version_74(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_has_categories MODIFY project_id INT NOT NULL'); + $pdo->exec('ALTER TABLE project_has_categories MODIFY name VARCHAR(255) NOT NULL'); + + $pdo->exec('ALTER TABLE actions MODIFY project_id INT NOT NULL'); + $pdo->exec('ALTER TABLE actions MODIFY event_name VARCHAR(50) NOT NULL'); + $pdo->exec('ALTER TABLE actions MODIFY action_name VARCHAR(50) NOT NULL'); + + $pdo->exec('ALTER TABLE action_has_params MODIFY action_id INT NOT NULL'); + $pdo->exec('ALTER TABLE action_has_params MODIFY name VARCHAR(50) NOT NULL'); + $pdo->exec('ALTER TABLE action_has_params MODIFY value VARCHAR(50) NOT NULL'); + + $pdo->exec('ALTER TABLE files MODIFY name VARCHAR(255) NOT NULL'); + $pdo->exec('ALTER TABLE files MODIFY task_id INT NOT NULL'); + + $pdo->exec('ALTER TABLE subtasks MODIFY title VARCHAR(255) NOT NULL'); + + $pdo->exec('ALTER TABLE tasks MODIFY project_id INT NOT NULL'); + $pdo->exec('ALTER TABLE tasks MODIFY column_id INT NOT NULL'); + + $pdo->exec('ALTER TABLE columns MODIFY title VARCHAR(255) NOT NULL'); + $pdo->exec('ALTER TABLE columns MODIFY project_id INT NOT NULL'); + + $pdo->exec('ALTER TABLE project_has_users MODIFY project_id INT NOT NULL'); + $pdo->exec('ALTER TABLE project_has_users MODIFY user_id INT NOT NULL'); + + $pdo->exec('ALTER TABLE projects MODIFY name VARCHAR(255) NOT NULL UNIQUE'); + + $pdo->exec('ALTER TABLE users MODIFY username VARCHAR(50) NOT NULL'); + + $pdo->exec('ALTER TABLE user_has_notifications MODIFY project_id INT NOT NULL'); + $pdo->exec('ALTER TABLE user_has_notifications MODIFY user_id INT NOT NULL'); +} + +function version_73(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN notifications_filter INT DEFAULT 4"); +} + +function version_72(PDO $pdo) +{ + $pdo->exec('ALTER TABLE files MODIFY name VARCHAR(255)'); +} + +function version_71(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO `settings` VALUES (?, ?)'); + $rq->execute(array('webhook_url', '')); + + $pdo->exec("DELETE FROM `settings` WHERE `option`='webhook_url_task_creation'"); + $pdo->exec("DELETE FROM `settings` WHERE `option`='webhook_url_task_modification'"); +} + +function version_70(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN token VARCHAR(255) DEFAULT ''"); +} + +function version_69(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('calendar_user_subtasks_time_tracking', 0)); + $rq->execute(array('calendar_user_tasks', 'date_started')); + $rq->execute(array('calendar_project_tasks', 'date_started')); + + $pdo->exec("DELETE FROM `settings` WHERE `option`='subtask_forecast'"); +} + +function version_68(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_jabber', '0')); + $rq->execute(array('integration_jabber_server', '')); + $rq->execute(array('integration_jabber_domain', '')); + $rq->execute(array('integration_jabber_username', '')); + $rq->execute(array('integration_jabber_password', '')); + $rq->execute(array('integration_jabber_nickname', 'kanboard')); + $rq->execute(array('integration_jabber_room', '')); + + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber INTEGER DEFAULT '0'"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_server VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_domain VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_username VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_password VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_nickname VARCHAR(255) DEFAULT 'kanboard'"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_room VARCHAR(255) DEFAULT ''"); +} + +function version_67(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_status INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_trigger INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_factor INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_timeframe INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_basedate INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_parent INTEGER'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_child INTEGER'); +} + +function version_66(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN identifier VARCHAR(50) DEFAULT ''"); +} + +function version_65(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_integrations ( + `id` INT NOT NULL AUTO_INCREMENT, + `project_id` INT NOT NULL UNIQUE, + `hipchat` TINYINT(1) DEFAULT 0, + `hipchat_api_url` VARCHAR(255) DEFAULT 'https://api.hipchat.com', + `hipchat_room_id` VARCHAR(255), + `hipchat_room_token` VARCHAR(255), + `slack` TINYINT(1) DEFAULT 0, + `slack_webhook_url` VARCHAR(255), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_64(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_daily_summaries ADD COLUMN score INT NOT NULL DEFAULT 0'); +} + +function version_63(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_has_categories ADD COLUMN description TEXT'); +} + +function version_62(PDO $pdo) +{ + $pdo->exec('ALTER TABLE files ADD COLUMN `date` INT NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE files ADD COLUMN `user_id` INT NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE files ADD COLUMN `size` INT NOT NULL DEFAULT 0'); +} + +function version_61(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated TINYINT(1) DEFAULT 0'); + $pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret CHAR(16)'); +} + +function version_60(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_gravatar', '0')); +} + +function version_59(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_hipchat', '0')); + $rq->execute(array('integration_hipchat_api_url', 'https://api.hipchat.com')); + $rq->execute(array('integration_hipchat_room_id', '')); + $rq->execute(array('integration_hipchat_room_token', '')); +} + +function version_58(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_slack_webhook', '0')); + $rq->execute(array('integration_slack_webhook_url', '')); +} + +function version_57(PDO $pdo) +{ + $pdo->exec('CREATE TABLE currencies (`currency` CHAR(3) NOT NULL UNIQUE, `rate` FLOAT DEFAULT 0) ENGINE=InnoDB CHARSET=utf8'); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_currency', 'USD')); +} + +function version_56(PDO $pdo) +{ + $pdo->exec('CREATE TABLE transitions ( + `id` INT NOT NULL AUTO_INCREMENT, + `user_id` INT NOT NULL, + `project_id` INT NOT NULL, + `task_id` INT NOT NULL, + `src_column_id` INT NOT NULL, + `dst_column_id` INT NOT NULL, + `date` INT NOT NULL, + `time_spent` INT DEFAULT 0, + FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8'); + + $pdo->exec("CREATE INDEX transitions_task_index ON transitions(task_id)"); + $pdo->exec("CREATE INDEX transitions_project_index ON transitions(project_id)"); + $pdo->exec("CREATE INDEX transitions_user_index ON transitions(user_id)"); +} + +function version_55(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('subtask_forecast', '0')); +} + +function version_54(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_stylesheet', '')); +} + +function version_53(PDO $pdo) +{ + $pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent FLOAT DEFAULT 0"); +} + +function version_49(PDO $pdo) +{ + $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1'); + + $task_id = 0; + $position = 1; + $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?'); + + $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC'); + $rq->execute(); + + foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) { + if ($task_id != $subtask['task_id']) { + $position = 1; + $task_id = $subtask['task_id']; + } + + $urq->execute(array($position, $subtask['id'])); + $position++; + } +} + +function version_48(PDO $pdo) +{ + $pdo->exec('RENAME TABLE task_has_files TO files'); + $pdo->exec('RENAME TABLE task_has_subtasks TO subtasks'); +} + +function version_47(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN description TEXT'); +} + +function version_46(PDO $pdo) +{ + $pdo->exec("CREATE TABLE links ( + id INT NOT NULL AUTO_INCREMENT, + label VARCHAR(255) NOT NULL, + opposite_id INT DEFAULT 0, + PRIMARY KEY(id), + UNIQUE(label) + ) ENGINE=InnoDB CHARSET=utf8"); + + $pdo->exec("CREATE TABLE task_has_links ( + id INT NOT NULL AUTO_INCREMENT, + link_id INT NOT NULL, + task_id INT NOT NULL, + opposite_task_id INT NOT NULL, + FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY(id) + ) ENGINE=InnoDB CHARSET=utf8"); + + $pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)"); + $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)"); + + $rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)'); + + # ID cannot be known at time of record creation so we have to update it after the fact + # On MariaDB clusters auto-increment size is normally != 1, so relying on increments of 1 would break + $arq = $pdo->prepare('UPDATE links SET opposite_id=? WHERE label=?'); + + $rq->execute(array('relates to', 0)); + + $rq->execute(array('blocks', 0)); + $rq->execute(array('is blocked by', get_last_insert_id($pdo))); + $arq->execute(array(get_last_insert_id($pdo), 'blocks')); + + $rq->execute(array('duplicates', 0)); + $rq->execute(array('is duplicated by', get_last_insert_id($pdo))); + $arq->execute(array(get_last_insert_id($pdo), 'duplicates')); + + $rq->execute(array('is a parent of', 0)); + $rq->execute(array('is a child of', get_last_insert_id($pdo))); + $arq->execute(array(get_last_insert_id($pdo), 'is a parent of')); + + $rq->execute(array('is a milestone of', 0)); + $rq->execute(array('targets milestone', get_last_insert_id($pdo))); + $arq->execute(array(get_last_insert_id($pdo), 'is a milestone of')); + + $rq->execute(array('is fixed by', 0)); + $rq->execute(array('fixes', get_last_insert_id($pdo))); + $arq->execute(array(get_last_insert_id($pdo), 'is fixed by')); +} + +function version_45(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INT DEFAULT 0'); + + /* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0. + * We take max project_activities.date_creation where event_name in task.create','task.move.column + * since creation date is always less than task moves + */ + $pdo->exec("UPDATE tasks + SET date_moved = ( + SELECT md + FROM ( + SELECT task_id, max(date_creation) md + FROM project_activities + WHERE event_name IN ('task.create', 'task.move.column') + GROUP BY task_id + ) src + WHERE id = src.task_id + ) + WHERE (date_moved IS NULL OR date_moved = 0) AND id IN ( + SELECT task_id + FROM ( + SELECT task_id, max(date_creation) md + FROM project_activities + WHERE event_name IN ('task.create', 'task.move.column') + GROUP BY task_id + ) src + )"); + + // If there is no activities for some tasks use the date_creation + $pdo->exec("UPDATE tasks SET date_moved = date_creation WHERE date_moved IS NULL OR date_moved = 0"); +} + +function version_44(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN disable_login_form TINYINT(1) DEFAULT 0'); +} + +function version_43(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('subtask_restriction', '0')); + $rq->execute(array('subtask_time_tracking', '0')); + + $pdo->exec(" + CREATE TABLE subtask_time_tracking ( + id INT NOT NULL AUTO_INCREMENT, + user_id INT NOT NULL, + subtask_id INT NOT NULL, + start INT DEFAULT 0, + end INT DEFAULT 0, + PRIMARY KEY(id), + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_42(PDO $pdo) +{ + $pdo->exec('ALTER TABLE columns ADD COLUMN description TEXT'); +} + +function version_41(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN timezone VARCHAR(50)'); + $pdo->exec('ALTER TABLE users ADD COLUMN language CHAR(5)'); +} + +function version_40(PDO $pdo) +{ + // Avoid some full table scans + $pdo->exec('CREATE INDEX users_admin_idx ON users(is_admin)'); + $pdo->exec('CREATE INDEX columns_project_idx ON columns(project_id)'); + $pdo->exec('CREATE INDEX tasks_project_idx ON tasks(project_id)'); + $pdo->exec('CREATE INDEX swimlanes_project_idx ON swimlanes(project_id)'); + $pdo->exec('CREATE INDEX categories_project_idx ON project_has_categories(project_id)'); + $pdo->exec('CREATE INDEX subtasks_task_idx ON task_has_subtasks(task_id)'); + $pdo->exec('CREATE INDEX files_task_idx ON task_has_files(task_id)'); + $pdo->exec('CREATE INDEX comments_task_idx ON comments(task_id)'); + + // Set the ownership for all private projects + $rq = $pdo->prepare('SELECT id FROM projects WHERE is_private=1'); + $rq->execute(); + $project_ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0); + + $rq = $pdo->prepare('UPDATE project_has_users SET is_owner=1 WHERE project_id=?'); + + foreach ($project_ids as $project_id) { + $rq->execute(array($project_id)); + } +} + +function version_39(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('project_categories', '')); +} + +function version_38(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE swimlanes ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(200) NOT NULL, + position INT DEFAULT 1, + is_active INT DEFAULT 1, + project_id INT, + PRIMARY KEY(id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE (name, project_id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INT DEFAULT 0'); + $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT 'Default swimlane'"); + $pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane INT DEFAULT 1"); +} + +function version_37(PDO $pdo) +{ + $pdo->exec("ALTER TABLE project_has_users ADD COLUMN is_owner TINYINT(1) DEFAULT '0'"); +} + +function version_36(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks MODIFY title VARCHAR(255) NOT NULL'); +} + +function version_35(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_summaries ( + id INT NOT NULL AUTO_INCREMENT, + day CHAR(10) NOT NULL, + project_id INT NOT NULL, + column_id INT NOT NULL, + total INT NOT NULL DEFAULT 0, + PRIMARY KEY(id), + FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_column_stats_idx ON project_daily_summaries(day, project_id, column_id)'); +} + +function version_34(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_everybody_allowed TINYINT(1) DEFAULT '0'"); +} + +function version_33(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_activities ( + id INT NOT NULL AUTO_INCREMENT, + date_creation INT NOT NULL, + event_name VARCHAR(50) NOT NULL, + creator_id INT, + project_id INT, + task_id INT, + data TEXT, + PRIMARY KEY(id), + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec('DROP TABLE task_has_events'); + $pdo->exec('DROP TABLE comment_has_events'); + $pdo->exec('DROP TABLE subtask_has_events'); +} + +function version_32(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN date_started INTEGER"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN time_spent FLOAT DEFAULT 0"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN time_estimated FLOAT DEFAULT 0"); + + $pdo->exec("ALTER TABLE task_has_subtasks MODIFY time_estimated FLOAT"); + $pdo->exec("ALTER TABLE task_has_subtasks MODIFY time_spent FLOAT"); +} + +function version_31(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_private TINYINT(1) DEFAULT '0'"); +} + +function version_30(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_date_format', 'm/d/Y')); +} + +function version_29(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE settings ( + `option` VARCHAR(100) PRIMARY KEY, + `value` VARCHAR(255) DEFAULT '' + ) + "); + + // Migrate old config parameters + $rq = $pdo->prepare('SELECT * FROM config'); + $rq->execute(); + $parameters = $rq->fetch(PDO::FETCH_ASSOC); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('board_highlight_period', defined('RECENT_TASK_PERIOD') ? RECENT_TASK_PERIOD : 48*60*60)); + $rq->execute(array('board_public_refresh_interval', defined('BOARD_PUBLIC_CHECK_INTERVAL') ? BOARD_PUBLIC_CHECK_INTERVAL : 60)); + $rq->execute(array('board_private_refresh_interval', defined('BOARD_CHECK_INTERVAL') ? BOARD_CHECK_INTERVAL : 10)); + $rq->execute(array('board_columns', $parameters['default_columns'])); + $rq->execute(array('webhook_url_task_creation', $parameters['webhooks_url_task_creation'])); + $rq->execute(array('webhook_url_task_modification', $parameters['webhooks_url_task_modification'])); + $rq->execute(array('webhook_token', $parameters['webhooks_token'])); + $rq->execute(array('api_token', $parameters['api_token'])); + $rq->execute(array('application_language', $parameters['language'])); + $rq->execute(array('application_timezone', $parameters['timezone'])); + $rq->execute(array('application_url', defined('KANBOARD_URL') ? KANBOARD_URL : '')); + + $pdo->exec('DROP TABLE config'); +} + +function version_28(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN reference VARCHAR(50) DEFAULT ''"); + $pdo->exec("ALTER TABLE comments ADD COLUMN reference VARCHAR(50) DEFAULT ''"); + + $pdo->exec('CREATE INDEX tasks_reference_idx ON tasks(reference)'); + $pdo->exec('CREATE INDEX comments_reference_idx ON comments(reference)'); +} + +function version_27(PDO $pdo) +{ + $pdo->exec('CREATE UNIQUE INDEX users_username_idx ON users(username)'); +} + +function version_26(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN default_columns VARCHAR(255) DEFAULT ''"); +} + +function version_25(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_events ( + id INT NOT NULL AUTO_INCREMENT, + date_creation INT NOT NULL, + event_name TEXT NOT NULL, + creator_id INT, + project_id INT, + task_id INT, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY (id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE subtask_has_events ( + id INT NOT NULL AUTO_INCREMENT, + date_creation INT NOT NULL, + event_name TEXT NOT NULL, + creator_id INT, + project_id INT, + subtask_id INT, + task_id INT, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY (id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE comment_has_events ( + id INT NOT NULL AUTO_INCREMENT, + date_creation INT NOT NULL, + event_name TEXT NOT NULL, + creator_id INT, + project_id INT, + comment_id INT, + task_id INT, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(comment_id) REFERENCES comments(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + PRIMARY KEY (id) + ) ENGINE=InnoDB CHARSET=utf8 + "); +} + +function version_24(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_public TINYINT(1) DEFAULT '0'"); +} + +function version_23(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled TINYINT(1) DEFAULT '0'"); + + $pdo->exec(" + CREATE TABLE user_has_notifications ( + user_id INT, + project_id INT, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, user_id) + ); + "); +} + +function version_22(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_modification VARCHAR(255)"); + $pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_creation VARCHAR(255)"); +} + +function version_21(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN creator_id INTEGER DEFAULT '0'"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN date_modification INTEGER DEFAULT '0'"); +} + +function version_20(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN github_id VARCHAR(30)"); +} + +function version_19(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN api_token VARCHAR(255) DEFAULT ''"); + $pdo->exec("UPDATE config SET api_token='".Token::getToken()."'"); +} + +function version_18(PDO $pdo) +{ + $pdo->exec( + " + CREATE TABLE task_has_subtasks ( + id INT NOT NULL AUTO_INCREMENT, + title VARCHAR(255), + status INT DEFAULT 0, + time_estimated INT DEFAULT 0, + time_spent INT DEFAULT 0, + task_id INT NOT NULL, + user_id INT, + PRIMARY KEY (id), + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8" + ); +} + +function version_17(PDO $pdo) +{ + $pdo->exec( + " + CREATE TABLE task_has_files ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(50), + path VARCHAR(255), + is_image TINYINT(1) DEFAULT 0, + task_id INT, + PRIMARY KEY (id), + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8" + ); +} + +function version_16(PDO $pdo) +{ + $pdo->exec( + " + CREATE TABLE project_has_categories ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(255), + project_id INT, + PRIMARY KEY (id), + UNIQUE KEY `idx_project_category` (project_id, name), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8" + ); + + $pdo->exec("ALTER TABLE tasks ADD COLUMN category_id INT DEFAULT 0"); +} + +function version_15(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN last_modified INT DEFAULT 0"); +} + +function version_14(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN name VARCHAR(255)"); + $pdo->exec("ALTER TABLE users ADD COLUMN email VARCHAR(255)"); + $pdo->exec("ALTER TABLE users ADD COLUMN google_id VARCHAR(30)"); +} + +function version_13(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_ldap_user TINYINT(1) DEFAULT 0"); +} + +function version_12(PDO $pdo) +{ + $pdo->exec( + " + CREATE TABLE remember_me ( + id INT NOT NULL AUTO_INCREMENT, + user_id INT, + ip VARCHAR(45), + user_agent VARCHAR(255), + token VARCHAR(255), + sequence VARCHAR(255), + expiration INT, + date_creation INT, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + PRIMARY KEY (id) + ) ENGINE=InnoDB CHARSET=utf8" + ); + + $pdo->exec( + " + CREATE TABLE last_logins ( + id INT NOT NULL AUTO_INCREMENT, + auth_type VARCHAR(25), + user_id INT, + ip VARCHAR(45), + user_agent VARCHAR(255), + date_creation INT, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + PRIMARY KEY (id), + INDEX (user_id) + ) ENGINE=InnoDB CHARSET=utf8" + ); +} + +function version_1(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE config ( + language CHAR(5) DEFAULT 'en_US', + webhooks_token VARCHAR(255) DEFAULT '', + timezone VARCHAR(50) DEFAULT 'UTC' + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE users ( + id INT NOT NULL AUTO_INCREMENT, + username VARCHAR(50), + password VARCHAR(255), + is_admin TINYINT DEFAULT 0, + default_project_id INT DEFAULT 0, + PRIMARY KEY (id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE projects ( + id INT NOT NULL AUTO_INCREMENT, + name VARCHAR(50) UNIQUE, + is_active TINYINT DEFAULT 1, + token VARCHAR(255), + PRIMARY KEY (id) + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE project_has_users ( + id INT NOT NULL AUTO_INCREMENT, + project_id INT, + user_id INT, + PRIMARY KEY (id), + UNIQUE KEY `idx_project_user` (project_id, user_id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE columns ( + id INT NOT NULL AUTO_INCREMENT, + title VARCHAR(255), + position INT NOT NULL, + project_id INT NOT NULL, + task_limit INT DEFAULT '0', + UNIQUE KEY `idx_title_project` (title, project_id), + PRIMARY KEY (id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE tasks ( + id INT NOT NULL AUTO_INCREMENT, + title VARCHAR(255), + description TEXT, + date_creation INT, + date_completed INT, + date_due INT, + color_id VARCHAR(50), + project_id INT, + column_id INT, + owner_id INT DEFAULT '0', + position INT, + score INT, + is_active TINYINT DEFAULT 1, + PRIMARY KEY (id), + INDEX `idx_task_active` (is_active), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE comments ( + id INT NOT NULL AUTO_INCREMENT, + task_id INT, + user_id INT, + `date` INT, + comment TEXT, + PRIMARY KEY (id), + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE actions ( + id INT NOT NULL AUTO_INCREMENT, + project_id INT, + event_name VARCHAR(50), + action_name VARCHAR(50), + PRIMARY KEY (id), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + CREATE TABLE action_has_params ( + id INT NOT NULL AUTO_INCREMENT, + action_id INT, + name VARCHAR(50), + value VARCHAR(50), + PRIMARY KEY (id), + FOREIGN KEY(action_id) REFERENCES actions(id) ON DELETE CASCADE + ) ENGINE=InnoDB CHARSET=utf8 + "); + + $pdo->exec(" + INSERT INTO users + (username, password, is_admin) + VALUES ('admin', '".\password_hash('admin', PASSWORD_BCRYPT)."', '1') + "); + + $pdo->exec(" + INSERT INTO config + (webhooks_token) + VALUES ('".Token::getToken()."') + "); +} diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php new file mode 100644 index 0000000..c17004a --- /dev/null +++ b/app/Schema/Postgres.php @@ -0,0 +1,1473 @@ +<?php + +namespace Schema; + +require_once __DIR__.'/Migration.php'; + +use PDO; +use Kanboard\Core\Security\Token; +use Kanboard\Core\Security\Role; + +const VERSION = 117; + +function version_117(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "comments" ADD COLUMN "visibility" VARCHAR(25) NOT NULL DEFAULT \''.Role::APP_USER."'"); +} + +function version_116(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN theme TEXT DEFAULT 'light' NOT NULL"); +} + +function version_115(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "projects" ADD COLUMN enable_global_tags BOOLEAN DEFAULT TRUE'); +} + +function version_114(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "swimlanes" ADD COLUMN task_limit INTEGER DEFAULT 0'); +} + +function version_113(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "projects" ADD COLUMN task_limit INTEGER DEFAULT 0'); +} + +function version_112(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "projects" ADD COLUMN per_swimlane_task_limits BOOLEAN DEFAULT FALSE'); +} + +function version_111(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "tags" ADD COLUMN "color_id" VARCHAR(50) DEFAULT NULL'); +} + +function version_110(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "project_has_categories" ADD COLUMN "color_id" VARCHAR(50) DEFAULT NULL'); +} + +function version_109(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "users" ALTER COLUMN "language" TYPE VARCHAR(11)'); +} + +function version_108(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "projects" ALTER COLUMN "name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "projects" ALTER COLUMN "email" TYPE TEXT'); + $pdo->exec('ALTER TABLE "action_has_params" ALTER COLUMN "name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "action_has_params" ALTER COLUMN "value" TYPE TEXT'); + $pdo->exec('ALTER TABLE "actions" ALTER COLUMN "event_name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "actions" ALTER COLUMN "action_name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "comments" ALTER COLUMN "reference" TYPE TEXT'); + $pdo->exec('ALTER TABLE "custom_filters" ALTER COLUMN "filter" TYPE TEXT'); + $pdo->exec('ALTER TABLE "custom_filters" ALTER COLUMN "name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "groups" ALTER COLUMN "name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "project_activities" ALTER COLUMN "event_name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "project_has_files" ALTER COLUMN "name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "project_has_files" ALTER COLUMN "path" TYPE TEXT'); + $pdo->exec('ALTER TABLE "subtasks" ALTER COLUMN "title" TYPE TEXT'); + $pdo->exec('ALTER TABLE "swimlanes" ALTER COLUMN "name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "task_has_external_links" ALTER COLUMN "title" TYPE TEXT'); + $pdo->exec('ALTER TABLE "task_has_external_links" ALTER COLUMN "url" TYPE TEXT'); + $pdo->exec('ALTER TABLE "task_has_files" ALTER COLUMN "name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "task_has_files" ALTER COLUMN "path" TYPE TEXT'); + $pdo->exec('ALTER TABLE "tasks" ALTER COLUMN "title" TYPE TEXT'); + $pdo->exec('ALTER TABLE "tasks" ALTER COLUMN "reference" TYPE TEXT'); + $pdo->exec('ALTER TABLE "user_has_unread_notifications" ALTER COLUMN "event_name" TYPE TEXT'); + $pdo->exec('ALTER TABLE "users" ALTER COLUMN "username" TYPE TEXT'); + $pdo->exec('ALTER TABLE "users" ALTER COLUMN "filter" TYPE TEXT'); +} + +function version_107(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "users" ADD COLUMN filter VARCHAR(255) DEFAULT NULL'); +} + +function version_106(PDO $pdo) +{ + $pdo->exec("CREATE TABLE sessions ( + id TEXT PRIMARY KEY, + expire_at INTEGER NOT NULL, + data TEXT DEFAULT '' + )"); +} + +function version_105(PDO $pdo) +{ + $pdo->exec('CREATE TABLE predefined_task_descriptions ( + id SERIAL PRIMARY KEY, + project_id INTEGER NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )'); +} + +function version_104(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects DROP COLUMN is_everybody_allowed'); +} + +function version_103(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN predefined_email_subjects TEXT'); +} + +function version_102(PDO $pdo) +{ + $pdo->exec('ALTER TABLE column_has_move_restrictions ADD COLUMN only_assigned BOOLEAN DEFAULT FALSE'); +} + +function version_101(PDO $pdo) +{ + migrate_default_swimlane($pdo); + + $pdo->exec('ALTER TABLE "projects" DROP COLUMN "default_swimlane"'); + $pdo->exec('ALTER TABLE "projects" DROP COLUMN "show_default_swimlane"'); + $pdo->exec('ALTER TABLE "tasks" ALTER COLUMN "swimlane_id" SET NOT NULL'); + $pdo->exec('ALTER TABLE "tasks" ALTER COLUMN "swimlane_id" DROP DEFAULT'); + $pdo->exec('ALTER TABLE "tasks" ADD FOREIGN KEY (swimlane_id) REFERENCES swimlanes ON DELETE CASCADE'); +} + +function version_100(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "projects" ADD COLUMN email VARCHAR(255)'); +} + +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) +{ + $pdo->exec("ALTER TABLE columns ADD COLUMN hide_in_dashboard BOOLEAN DEFAULT '0'"); +} + +function version_90(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE tags ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + project_id INTEGER NOT NULL, + UNIQUE(project_id, name) + ) + "); + + $pdo->exec(" + CREATE TABLE task_has_tags ( + task_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE, + UNIQUE(tag_id, task_id) + ) + "); +} + +function version_89(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN avatar_path VARCHAR(255)"); +} + +function version_88(PDO $pdo) +{ + $pdo->exec("ALTER TABLE user_has_metadata ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE user_has_metadata ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE project_has_metadata ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE project_has_metadata ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE task_has_metadata ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE task_has_metadata ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE settings ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE settings ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); +} + +function version_87(PDO $pdo) +{ + $pdo->exec("UPDATE project_activities SET event_name='task.file.create' WHERE event_name='file.create'"); +} + +function version_86(PDO $pdo) +{ + $pdo->exec('ALTER TABLE files RENAME TO task_has_files'); + + $pdo->exec( + " + CREATE TABLE project_has_files ( + id SERIAL PRIMARY KEY, + project_id INTEGER NOT NULL, + name VARCHAR(255) NOT NULL, + path VARCHAR(255) NOT NULL, + is_image BOOLEAN DEFAULT '0', + size INTEGER DEFAULT 0 NOT NULL, + user_id INTEGER DEFAULT 0 NOT NULL, + date INTEGER DEFAULT 0 NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )" + ); +} + +function version_85(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT '1'"); +} + +function version_84(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_external_links ( + id SERIAL PRIMARY KEY, + link_type VARCHAR(100) NOT NULL, + dependency VARCHAR(100) NOT NULL, + title VARCHAR(255) NOT NULL, + url VARCHAR(255) NOT NULL, + date_creation INT NOT NULL, + date_modification INT NOT NULL, + task_id INT NOT NULL, + creator_id INT DEFAULT 0, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); +} + +function version_83(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_default INTEGER DEFAULT 0"); + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_start INTEGER DEFAULT 0"); + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_end INTEGER DEFAULT 3"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN priority INTEGER DEFAULT 0"); +} + +function version_82(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN owner_id INTEGER DEFAULT 0"); +} + +function version_81(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE password_reset ( + token VARCHAR(80) PRIMARY KEY, + user_id INTEGER NOT NULL, + date_expiration INTEGER NOT NULL, + date_creation INTEGER NOT NULL, + ip VARCHAR(45) NOT NULL, + user_agent VARCHAR(255) NOT NULL, + is_active BOOLEAN NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) + "); + + $pdo->exec("INSERT INTO settings VALUES ('password_reset', '1')"); +} + +function version_80(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "actions" ALTER COLUMN "action_name" TYPE VARCHAR(255)'); +} + +function version_79(PDO $pdo) +{ + $rq = $pdo->prepare('SELECT * FROM actions'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE actions SET action_name=? WHERE id=?'); + + foreach ($rows as $row) { + if ($row['action_name'] === 'TaskAssignCurrentUser' && $row['event_name'] === 'task.move.column') { + $row['action_name'] = '\Kanboard\Action\TaskAssignCurrentUserColumn'; + } elseif ($row['action_name'] === 'TaskClose' && $row['event_name'] === 'task.move.column') { + $row['action_name'] = '\Kanboard\Action\TaskCloseColumn'; + } elseif ($row['action_name'] === 'TaskLogMoveAnotherColumn') { + $row['action_name'] = '\Kanboard\Action\CommentCreationMoveTaskColumn'; + } elseif ($row['action_name'][0] !== '\\') { + $row['action_name'] = '\Kanboard\Action\\'.$row['action_name']; + } + + $rq->execute(array($row['action_name'], $row['id'])); + } +} + +function version_78(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "users" ALTER COLUMN "language" TYPE VARCHAR(5)'); +} + +function version_77(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "users" ADD COLUMN "role" VARCHAR(25) NOT NULL DEFAULT \''.Role::APP_USER.'\''); + + $rq = $pdo->prepare('SELECT * FROM "users"'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE "users" SET "role"=? WHERE "id"=?'); + + foreach ($rows as $row) { + $role = Role::APP_USER; + + if ($row['is_admin'] == 1) { + $role = Role::APP_ADMIN; + } elseif ($row['is_project_admin']) { + $role = Role::APP_MANAGER; + } + + $rq->execute(array($role, $row['id'])); + } + + $pdo->exec('ALTER TABLE users DROP COLUMN "is_admin"'); + $pdo->exec('ALTER TABLE users DROP COLUMN "is_project_admin"'); +} + +function version_76(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_has_groups ( + group_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + role VARCHAR(25) NOT NULL, + FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(group_id, project_id) + ) + "); + + $pdo->exec("ALTER TABLE project_has_users ADD COLUMN role VARCHAR(25) NOT NULL DEFAULT '".Role::PROJECT_VIEWER."'"); + + $rq = $pdo->prepare('SELECT * FROM project_has_users'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE project_has_users SET "role"=? WHERE "id"=?'); + + foreach ($rows as $row) { + $rq->execute(array( + $row['is_owner'] == 1 ? Role::PROJECT_MANAGER : Role::PROJECT_MEMBER, + $row['id'], + )); + } + + $pdo->exec('ALTER TABLE project_has_users DROP COLUMN "is_owner"'); + $pdo->exec('ALTER TABLE project_has_users DROP COLUMN "id"'); +} + +function version_75(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE groups ( + id SERIAL PRIMARY KEY, + external_id VARCHAR(255) DEFAULT '', + name VARCHAR(100) NOT NULL UNIQUE + ) + "); + + $pdo->exec(" + CREATE TABLE group_has_users ( + group_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(group_id, user_id) + ) + "); +} + +function version_74(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects DROP CONSTRAINT IF EXISTS projects_name_key'); +} + +function version_73(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE user_has_metadata ( + user_id INTEGER NOT NULL, + name VARCHAR(50) NOT NULL, + value VARCHAR(255) DEFAULT '', + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(user_id, name) + ) + "); + + $pdo->exec(" + CREATE TABLE project_has_metadata ( + project_id INTEGER NOT NULL, + name VARCHAR(50) NOT NULL, + value VARCHAR(255) DEFAULT '', + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, name) + ) + "); + + $pdo->exec(" + CREATE TABLE task_has_metadata ( + task_id INTEGER NOT NULL, + name VARCHAR(50) NOT NULL, + value VARCHAR(255) DEFAULT '', + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + UNIQUE(task_id, name) + ) + "); + + $pdo->exec("DROP TABLE project_integrations"); + + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_server'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_domain'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_username'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_password'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_nickname'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_room'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_api_url'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_id'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_token'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_url'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_channel'"); +} + +function version_72(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_has_notification_types ( + id SERIAL PRIMARY KEY, + project_id INTEGER NOT NULL, + notification_type VARCHAR(50) NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, notification_type) + ) + "); +} + +function version_71(PDO $pdo) +{ + $pdo->exec("ALTER TABLE custom_filters ADD COLUMN \"append\" BOOLEAN DEFAULT '0'"); +} + +function version_70(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ALTER COLUMN date_due TYPE BIGINT"); + $pdo->exec("ALTER TABLE tasks ALTER COLUMN date_creation TYPE BIGINT"); + $pdo->exec("ALTER TABLE tasks ALTER COLUMN date_completed TYPE BIGINT"); + $pdo->exec("ALTER TABLE tasks ALTER COLUMN date_started TYPE BIGINT"); + $pdo->exec("ALTER TABLE tasks ALTER COLUMN date_moved TYPE BIGINT"); + $pdo->exec("ALTER TABLE comments ALTER COLUMN date_creation TYPE BIGINT"); + $pdo->exec("ALTER TABLE last_logins ALTER COLUMN date_creation TYPE BIGINT"); + $pdo->exec("ALTER TABLE project_activities ALTER COLUMN date_creation TYPE BIGINT"); + $pdo->exec("ALTER TABLE projects ALTER COLUMN last_modified TYPE BIGINT"); + $pdo->exec("ALTER TABLE remember_me ALTER COLUMN date_creation TYPE BIGINT"); + $pdo->exec('ALTER TABLE files ALTER COLUMN "date" TYPE BIGINT'); + $pdo->exec('ALTER TABLE transitions ALTER COLUMN "date" TYPE BIGINT'); + $pdo->exec('ALTER TABLE subtask_time_tracking ALTER COLUMN "start" TYPE BIGINT'); + $pdo->exec('ALTER TABLE subtask_time_tracking ALTER COLUMN "end" TYPE BIGINT'); + $pdo->exec('ALTER TABLE users ALTER COLUMN "lock_expiration_date" TYPE BIGINT'); +} + +function version_69(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE user_has_unread_notifications ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + date_creation BIGINT NOT NULL, + event_name VARCHAR(50) NOT NULL, + event_data TEXT NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) + "); + + $pdo->exec(" + CREATE TABLE user_has_notification_types ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + notification_type VARCHAR(50), + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX user_has_notification_types_user_idx ON user_has_notification_types(user_id, notification_type)'); + + // Migrate people who have notification enabled before + $rq = $pdo->prepare("SELECT id FROM users WHERE notifications_enabled='1'"); + $rq->execute(); + $user_ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0); + + foreach ($user_ids as $user_id) { + $rq = $pdo->prepare('INSERT INTO user_has_notification_types (user_id, notification_type) VALUES (?, ?)'); + $rq->execute(array($user_id, 'email')); + } +} + +function version_68(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE custom_filters ( + id SERIAL PRIMARY KEY, + filter VARCHAR(100) NOT NULL, + project_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + name VARCHAR(100) NOT NULL, + is_shared BOOLEAN DEFAULT '0' + ) + "); +} + +function version_67(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE plugin_schema_versions ( + plugin VARCHAR(80) NOT NULL PRIMARY KEY, + version INTEGER NOT NULL DEFAULT 0 + ) + "); +} + +function version_66(PDO $pdo) +{ + $pdo->exec("ALTER TABLE swimlanes ADD COLUMN description TEXT"); +} + +function version_65(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN gitlab_id INTEGER"); +} + +function version_64(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN start_date VARCHAR(10) DEFAULT ''"); + $pdo->exec("ALTER TABLE projects ADD COLUMN end_date VARCHAR(10) DEFAULT ''"); +} + +function version_63(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_project_admin BOOLEAN DEFAULT '0'"); +} + +function version_62(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN nb_failed_login INTEGER DEFAULT 0"); + $pdo->exec("ALTER TABLE users ADD COLUMN lock_expiration_date INTEGER DEFAULT 0"); +} + +function version_61(PDO $pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('subtask_time_tracking', '1')"); + $pdo->exec("INSERT INTO settings VALUES ('cfd_include_closed_tasks', '1')"); +} + +function version_60(PDO $pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('default_color', 'yellow')"); +} + +function version_59(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id SERIAL PRIMARY KEY, + day CHAR(10) NOT NULL, + project_id INTEGER NOT NULL, + avg_lead_time INTEGER NOT NULL DEFAULT 0, + avg_cycle_time INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('ALTER TABLE project_daily_summaries RENAME TO project_daily_column_stats'); +} + +function version_58(PDO $pdo) +{ + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN slack_webhook_channel VARCHAR(255) DEFAULT ''"); + $pdo->exec("INSERT INTO settings VALUES ('integration_slack_webhook_channel', '')"); +} + +function version_57(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users DROP COLUMN "default_project_id"'); +} + +function version_56(PDO $pdo) +{ + $pdo->exec('DELETE FROM "settings" WHERE "option"=\'subtask_time_tracking\''); +} + +function version_55(PDO $pdo) +{ + $pdo->exec('ALTER TABLE comments DROP CONSTRAINT IF EXISTS comments_user_id_fkey'); + $pdo->exec("ALTER TABLE comments ALTER COLUMN task_id SET NOT NULL"); + $pdo->exec("ALTER TABLE comments ALTER COLUMN user_id SET DEFAULT 0"); + $pdo->exec('ALTER TABLE comments RENAME COLUMN "date" TO "date_creation"'); + $pdo->exec("ALTER TABLE comments ALTER COLUMN date_creation SET NOT NULL"); +} + +function version_54(PDO $pdo) +{ + $pdo->exec("ALTER TABLE project_has_categories ALTER COLUMN project_id SET NOT NULL"); + $pdo->exec("ALTER TABLE project_has_categories ALTER COLUMN name SET NOT NULL"); + + $pdo->exec("ALTER TABLE actions ALTER COLUMN project_id SET NOT NULL"); + $pdo->exec("ALTER TABLE actions ALTER COLUMN event_name SET NOT NULL"); + $pdo->exec("ALTER TABLE actions ALTER COLUMN action_name SET NOT NULL"); + + $pdo->exec("ALTER TABLE action_has_params ALTER COLUMN action_id SET NOT NULL"); + $pdo->exec("ALTER TABLE action_has_params ALTER COLUMN name SET NOT NULL"); + $pdo->exec("ALTER TABLE action_has_params ALTER COLUMN value SET NOT NULL"); + + $pdo->exec("ALTER TABLE files ALTER COLUMN name SET NOT NULL"); + $pdo->exec("ALTER TABLE files ALTER COLUMN task_id SET NOT NULL"); + + $pdo->exec("ALTER TABLE subtasks ALTER COLUMN title SET NOT NULL"); + + $pdo->exec("ALTER TABLE tasks ALTER COLUMN title SET NOT NULL"); + $pdo->exec("ALTER TABLE tasks ALTER COLUMN project_id SET NOT NULL"); + $pdo->exec("ALTER TABLE tasks ALTER COLUMN column_id SET NOT NULL"); + + $pdo->exec("ALTER TABLE columns ALTER COLUMN title SET NOT NULL"); + $pdo->exec("ALTER TABLE columns ALTER COLUMN project_id SET NOT NULL"); + + $pdo->exec("ALTER TABLE project_has_users ALTER COLUMN project_id SET NOT NULL"); + $pdo->exec("ALTER TABLE project_has_users ALTER COLUMN user_id SET NOT NULL"); + + $pdo->exec("ALTER TABLE projects ALTER COLUMN name SET NOT NULL"); + + $pdo->exec("ALTER TABLE users ALTER COLUMN username SET NOT NULL"); + + $pdo->exec("ALTER TABLE user_has_notifications ALTER COLUMN user_id SET NOT NULL"); + $pdo->exec("ALTER TABLE user_has_notifications ALTER COLUMN user_id SET NOT NULL"); +} + +function version_53(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN notifications_filter INTEGER DEFAULT 4"); +} + +function version_52(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('webhook_url', '')); + + $pdo->exec("DELETE FROM settings WHERE option='webhook_url_task_creation'"); + $pdo->exec("DELETE FROM settings WHERE option='webhook_url_task_modification'"); +} + +function version_51(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN token VARCHAR(255) DEFAULT ''"); +} + +function version_50(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('calendar_user_subtasks_time_tracking', 0)); + $rq->execute(array('calendar_user_tasks', 'date_started')); + $rq->execute(array('calendar_project_tasks', 'date_started')); + + $pdo->exec("DELETE FROM settings WHERE option='subtask_forecast'"); +} + +function version_49(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_jabber', '0')); + $rq->execute(array('integration_jabber_server', '')); + $rq->execute(array('integration_jabber_domain', '')); + $rq->execute(array('integration_jabber_username', '')); + $rq->execute(array('integration_jabber_password', '')); + $rq->execute(array('integration_jabber_nickname', 'kanboard')); + $rq->execute(array('integration_jabber_room', '')); + + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber INTEGER DEFAULT '0'"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_server VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_domain VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_username VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_password VARCHAR(255) DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_nickname VARCHAR(255) DEFAULT 'kanboard'"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_room VARCHAR(255) DEFAULT ''"); +} + +function version_48(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_status INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_trigger INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_factor INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_timeframe INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_basedate INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_parent INTEGER'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_child INTEGER'); +} + +function version_47(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN identifier VARCHAR(50) DEFAULT ''"); +} + +function version_46(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_integrations ( + id SERIAL PRIMARY KEY, + project_id INTEGER NOT NULL UNIQUE, + hipchat BOOLEAN DEFAULT '0', + hipchat_api_url VARCHAR(255) DEFAULT 'https://api.hipchat.com', + hipchat_room_id VARCHAR(255), + hipchat_room_token VARCHAR(255), + slack BOOLEAN DEFAULT '0', + slack_webhook_url VARCHAR(255), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); +} + +function version_45(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_daily_summaries ADD COLUMN score INTEGER NOT NULL DEFAULT 0'); +} + +function version_44(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_has_categories ADD COLUMN description TEXT'); +} + +function version_43(PDO $pdo) +{ + $pdo->exec('ALTER TABLE files ADD COLUMN "date" INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE files ADD COLUMN "user_id" INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE files ADD COLUMN "size" INTEGER NOT NULL DEFAULT 0'); +} + +function version_42(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated BOOLEAN DEFAULT \'0\''); + $pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret CHAR(16)'); +} + +function version_41(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_gravatar', '0')); +} + +function version_40(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_hipchat', '0')); + $rq->execute(array('integration_hipchat_api_url', 'https://api.hipchat.com')); + $rq->execute(array('integration_hipchat_room_id', '')); + $rq->execute(array('integration_hipchat_room_token', '')); +} + +function version_39(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_slack_webhook', '0')); + $rq->execute(array('integration_slack_webhook_url', '')); +} + +function version_38(PDO $pdo) +{ + $pdo->exec('CREATE TABLE currencies ("currency" CHAR(3) NOT NULL UNIQUE, "rate" REAL DEFAULT 0)'); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_currency', 'USD')); +} + +function version_37(PDO $pdo) +{ + $pdo->exec('CREATE TABLE transitions ( + "id" SERIAL PRIMARY KEY, + "user_id" INTEGER NOT NULL, + "project_id" INTEGER NOT NULL, + "task_id" INTEGER NOT NULL, + "src_column_id" INTEGER NOT NULL, + "dst_column_id" INTEGER NOT NULL, + "date" INTEGER NOT NULL, + "time_spent" INTEGER DEFAULT 0, + FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + )'); + + $pdo->exec("CREATE INDEX transitions_task_index ON transitions(task_id)"); + $pdo->exec("CREATE INDEX transitions_project_index ON transitions(project_id)"); + $pdo->exec("CREATE INDEX transitions_user_index ON transitions(user_id)"); +} + +function version_36(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('subtask_forecast', '0')); +} + +function version_35(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_stylesheet', '')); +} + +function version_34(PDO $pdo) +{ + $pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0"); +} + +function version_30(PDO $pdo) +{ + $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1'); + + $task_id = 0; + $position = 1; + $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?'); + + $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC'); + $rq->execute(); + + foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) { + if ($task_id != $subtask['task_id']) { + $position = 1; + $task_id = $subtask['task_id']; + } + + $urq->execute(array($position, $subtask['id'])); + $position++; + } +} + +function version_29(PDO $pdo) +{ + $pdo->exec('ALTER TABLE task_has_files RENAME TO files'); + $pdo->exec('ALTER TABLE task_has_subtasks RENAME TO subtasks'); +} + +function version_28(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN description TEXT'); +} + +function version_27(PDO $pdo) +{ + $pdo->exec('CREATE TABLE links ( + "id" SERIAL PRIMARY KEY, + "label" VARCHAR(255) NOT NULL, + "opposite_id" INTEGER DEFAULT 0, + UNIQUE("label") + )'); + + $pdo->exec("CREATE TABLE task_has_links ( + id SERIAL PRIMARY KEY, + link_id INTEGER NOT NULL, + task_id INTEGER NOT NULL, + opposite_task_id INTEGER NOT NULL, + FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE + )"); + + $pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)"); + $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)"); + + $rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)'); + $rq->execute(array('relates to', 0)); + $rq->execute(array('blocks', 3)); + $rq->execute(array('is blocked by', 2)); + $rq->execute(array('duplicates', 5)); + $rq->execute(array('is duplicated by', 4)); + $rq->execute(array('is a child of', 7)); + $rq->execute(array('is a parent of', 6)); + $rq->execute(array('targets milestone', 9)); + $rq->execute(array('is a milestone of', 8)); + $rq->execute(array('fixes', 11)); + $rq->execute(array('is fixed by', 10)); +} + +function version_26(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INT DEFAULT 0'); + + /* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0. + * We take max project_activities.date_creation where event_name in task.create','task.move.column + * since creation date is always less than task moves + */ + $pdo->exec("UPDATE tasks + SET date_moved = ( + SELECT md + FROM ( + SELECT task_id, max(date_creation) md + FROM project_activities + WHERE event_name IN ('task.create', 'task.move.column') + GROUP BY task_id + ) src + WHERE id = src.task_id + ) + WHERE (date_moved IS NULL OR date_moved = 0) AND id IN ( + SELECT task_id + FROM ( + SELECT task_id, max(date_creation) md + FROM project_activities + WHERE event_name IN ('task.create', 'task.move.column') + GROUP BY task_id + ) src + )"); + + // If there is no activities for some tasks use the date_creation + $pdo->exec("UPDATE tasks SET date_moved = date_creation WHERE date_moved IS NULL OR date_moved = 0"); +} + +function version_25(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN disable_login_form BOOLEAN DEFAULT '0'"); +} + +function version_24(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('subtask_restriction', '0')); + $rq->execute(array('subtask_time_tracking', '0')); + + $pdo->exec(' + CREATE TABLE subtask_time_tracking ( + id SERIAL PRIMARY KEY, + "user_id" INTEGER NOT NULL, + "subtask_id" INTEGER NOT NULL, + "start" INTEGER DEFAULT 0, + "end" INTEGER DEFAULT 0, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE + ) + '); +} + +function version_23(PDO $pdo) +{ + $pdo->exec('ALTER TABLE columns ADD COLUMN description TEXT'); +} + +function version_22(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN timezone VARCHAR(50)'); + $pdo->exec('ALTER TABLE users ADD COLUMN language CHAR(5)'); +} + +function version_21(PDO $pdo) +{ + // Avoid some full table scans + $pdo->exec('CREATE INDEX users_admin_idx ON users(is_admin)'); + $pdo->exec('CREATE INDEX columns_project_idx ON columns(project_id)'); + $pdo->exec('CREATE INDEX tasks_project_idx ON tasks(project_id)'); + $pdo->exec('CREATE INDEX swimlanes_project_idx ON swimlanes(project_id)'); + $pdo->exec('CREATE INDEX categories_project_idx ON project_has_categories(project_id)'); + $pdo->exec('CREATE INDEX subtasks_task_idx ON task_has_subtasks(task_id)'); + $pdo->exec('CREATE INDEX files_task_idx ON task_has_files(task_id)'); + $pdo->exec('CREATE INDEX comments_task_idx ON comments(task_id)'); + + // Set the ownership for all private projects + $rq = $pdo->prepare("SELECT id FROM projects WHERE is_private='1'"); + $rq->execute(); + $project_ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0); + + $rq = $pdo->prepare("UPDATE project_has_users SET is_owner='1' WHERE project_id=?"); + + foreach ($project_ids as $project_id) { + $rq->execute(array($project_id)); + } +} + +function version_20(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('project_categories', '')); +} + +function version_19(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE swimlanes ( + id SERIAL PRIMARY KEY, + name VARCHAR(200) NOT NULL, + position INTEGER DEFAULT 1, + is_active BOOLEAN DEFAULT '1', + project_id INTEGER, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE (name, project_id) + ) + "); + + $pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INTEGER DEFAULT 0'); + $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT 'Default swimlane'"); + $pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane BOOLEAN DEFAULT '1'"); +} + +function version_18(PDO $pdo) +{ + $pdo->exec("ALTER TABLE project_has_users ADD COLUMN is_owner BOOLEAN DEFAULT '0'"); +} + +function version_17(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ALTER COLUMN title SET NOT NULL'); +} + +function version_16(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_summaries ( + id SERIAL PRIMARY KEY, + day CHAR(10) NOT NULL, + project_id INTEGER NOT NULL, + column_id INTEGER NOT NULL, + total INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_column_stats_idx ON project_daily_summaries(day, project_id, column_id)'); +} + +function version_15(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_everybody_allowed BOOLEAN DEFAULT '0'"); +} + +function version_14(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_activities ( + id SERIAL PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name VARCHAR(50) NOT NULL, + creator_id INTEGER, + project_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('DROP TABLE task_has_events'); + $pdo->exec('DROP TABLE comment_has_events'); + $pdo->exec('DROP TABLE subtask_has_events'); +} + +function version_13(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN date_started INTEGER"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN time_spent FLOAT DEFAULT 0"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN time_estimated FLOAT DEFAULT 0"); + + $pdo->exec("ALTER TABLE task_has_subtasks ALTER COLUMN time_estimated TYPE FLOAT"); + $pdo->exec("ALTER TABLE task_has_subtasks ALTER COLUMN time_spent TYPE FLOAT"); +} + +function version_12(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_private BOOLEAN DEFAULT '0'"); +} + +function version_11(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_date_format', 'm/d/Y')); +} + +function version_10(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE settings ( + option VARCHAR(100) PRIMARY KEY, + value VARCHAR(255) DEFAULT '' + ) + "); + + // Migrate old config parameters + $rq = $pdo->prepare('SELECT * FROM config'); + $rq->execute(); + $parameters = $rq->fetch(PDO::FETCH_ASSOC); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('board_highlight_period', defined('RECENT_TASK_PERIOD') ? RECENT_TASK_PERIOD : 48*60*60)); + $rq->execute(array('board_public_refresh_interval', defined('BOARD_PUBLIC_CHECK_INTERVAL') ? BOARD_PUBLIC_CHECK_INTERVAL : 60)); + $rq->execute(array('board_private_refresh_interval', defined('BOARD_CHECK_INTERVAL') ? BOARD_CHECK_INTERVAL : 10)); + $rq->execute(array('board_columns', $parameters['default_columns'])); + $rq->execute(array('webhook_url_task_creation', $parameters['webhooks_url_task_creation'])); + $rq->execute(array('webhook_url_task_modification', $parameters['webhooks_url_task_modification'])); + $rq->execute(array('webhook_token', $parameters['webhooks_token'])); + $rq->execute(array('api_token', $parameters['api_token'])); + $rq->execute(array('application_language', $parameters['language'])); + $rq->execute(array('application_timezone', $parameters['timezone'])); + $rq->execute(array('application_url', defined('KANBOARD_URL') ? KANBOARD_URL : '')); + + $pdo->exec('DROP TABLE config'); +} + +function version_9(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN reference VARCHAR(50) DEFAULT ''"); + $pdo->exec("ALTER TABLE comments ADD COLUMN reference VARCHAR(50) DEFAULT ''"); + + $pdo->exec('CREATE INDEX tasks_reference_idx ON tasks(reference)'); + $pdo->exec('CREATE INDEX comments_reference_idx ON comments(reference)'); +} + +function version_8(PDO $pdo) +{ + $pdo->exec('CREATE UNIQUE INDEX users_username_idx ON users(username)'); +} + +function version_7(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN default_columns VARCHAR(255) DEFAULT ''"); +} + +function version_6(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_events ( + id SERIAL PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name VARCHAR(50) NOT NULL, + creator_id INTEGER, + project_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + "); + + $pdo->exec(" + CREATE TABLE subtask_has_events ( + id SERIAL PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name VARCHAR(50) NOT NULL, + creator_id INTEGER, + project_id INTEGER, + subtask_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + "); + + $pdo->exec(" + CREATE TABLE comment_has_events ( + id SERIAL PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name VARCHAR(50) NOT NULL, + creator_id INTEGER, + project_id INTEGER, + comment_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(comment_id) REFERENCES comments(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + "); +} + +function version_5(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_public BOOLEAN DEFAULT '0'"); +} + +function version_4(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled BOOLEAN DEFAULT '0'"); + + $pdo->exec(" + CREATE TABLE user_has_notifications ( + user_id INTEGER, + project_id INTEGER, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, user_id) + ); + "); +} + +function version_3(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_modification VARCHAR(255)"); + $pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_creation VARCHAR(255)"); +} + +function version_2(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN creator_id INTEGER DEFAULT 0"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN date_modification INTEGER DEFAULT 0"); +} + +function version_1(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE config ( + language CHAR(5) DEFAULT 'en_US', + webhooks_token VARCHAR(255) DEFAULT '', + timezone VARCHAR(50) DEFAULT 'UTC', + api_token VARCHAR(255) DEFAULT '' + ); + + CREATE TABLE users ( + id SERIAL PRIMARY KEY, + username VARCHAR(50), + password VARCHAR(255), + is_admin BOOLEAN DEFAULT '0', + default_project_id INTEGER DEFAULT 0, + is_ldap_user BOOLEAN DEFAULT '0', + name VARCHAR(255), + email VARCHAR(255), + google_id VARCHAR(255), + github_id VARCHAR(30) + ); + + CREATE TABLE remember_me ( + id SERIAL PRIMARY KEY, + user_id INTEGER, + ip VARCHAR(45), + user_agent VARCHAR(255), + token VARCHAR(255), + sequence VARCHAR(255), + expiration INTEGER, + date_creation INTEGER, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ); + + CREATE TABLE last_logins ( + id SERIAL PRIMARY KEY, + auth_type VARCHAR(25), + user_id INTEGER, + ip VARCHAR(45), + user_agent VARCHAR(255), + date_creation INTEGER, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ); + + CREATE TABLE projects ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) UNIQUE, + is_active BOOLEAN DEFAULT '1', + token VARCHAR(255), + last_modified INTEGER DEFAULT 0 + ); + + CREATE TABLE project_has_users ( + id SERIAL PRIMARY KEY, + project_id INTEGER, + user_id INTEGER, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(project_id, user_id) + ); + + CREATE TABLE project_has_categories ( + id SERIAL PRIMARY KEY, + name VARCHAR(255), + project_id INTEGER, + UNIQUE (project_id, name), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ); + + CREATE TABLE columns ( + id SERIAL PRIMARY KEY, + title VARCHAR(255), + position INTEGER, + project_id INTEGER, + task_limit INTEGER DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE (title, project_id) + ); + + CREATE TABLE tasks ( + id SERIAL PRIMARY KEY, + title VARCHAR(255), + description TEXT, + date_creation INTEGER, + color_id VARCHAR(255), + project_id INTEGER, + column_id INTEGER, + owner_id INTEGER DEFAULT 0, + position INTEGER, + is_active BOOLEAN DEFAULT '1', + date_completed INTEGER, + score INTEGER, + date_due INTEGER, + category_id INTEGER DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE + ); + + CREATE TABLE task_has_subtasks ( + id SERIAL PRIMARY KEY, + title VARCHAR(255), + status SMALLINT DEFAULT 0, + time_estimated INTEGER DEFAULT 0, + time_spent INTEGER DEFAULT 0, + task_id INTEGER NOT NULL, + user_id INTEGER, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + + CREATE TABLE task_has_files ( + id SERIAL PRIMARY KEY, + name VARCHAR(255), + path VARCHAR(255), + is_image BOOLEAN DEFAULT '0', + task_id INTEGER, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + + CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + task_id INTEGER, + user_id INTEGER, + date INTEGER, + comment TEXT, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ); + + CREATE TABLE actions ( + id SERIAL PRIMARY KEY, + project_id INTEGER, + event_name VARCHAR(50), + action_name VARCHAR(50), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ); + + CREATE TABLE action_has_params ( + id SERIAL PRIMARY KEY, + action_id INTEGER, + name VARCHAR(50), + value VARCHAR(50), + FOREIGN KEY(action_id) REFERENCES actions(id) ON DELETE CASCADE + ); + "); + + $pdo->exec(" + INSERT INTO users + (username, password, is_admin) + VALUES ('admin', '".\password_hash('admin', PASSWORD_BCRYPT)."', '1') + "); + + $pdo->exec(" + INSERT INTO config + (webhooks_token, api_token) + VALUES ('".Token::getToken()."', '".Token::getToken()."') + "); +} diff --git a/app/Schema/Sql/mysql.sql b/app/Schema/Sql/mysql.sql new file mode 100644 index 0000000..1c46ed5 --- /dev/null +++ b/app/Schema/Sql/mysql.sql @@ -0,0 +1,811 @@ + +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; + SET NAMES utf8mb4 ; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; +DROP TABLE IF EXISTS `action_has_params`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `action_has_params` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `action_id` int(11) NOT NULL, + `name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `value` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `action_id` (`action_id`), + CONSTRAINT `action_has_params_ibfk_1` FOREIGN KEY (`action_id`) REFERENCES `actions` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `actions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `actions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `project_id` int(11) NOT NULL, + `event_name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `action_name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `project_id` (`project_id`), + CONSTRAINT `actions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `column_has_move_restrictions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +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, + `only_assigned` tinyint(1) DEFAULT '0', + 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=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `column_has_restrictions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +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(191) COLLATE utf8mb4_unicode_ci 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=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `columns`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `columns` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `title` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `position` int(11) NOT NULL, + `project_id` int(11) NOT NULL, + `task_limit` int(11) DEFAULT '0', + `description` mediumtext COLLATE utf8mb4_unicode_ci, + `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`), + CONSTRAINT `columns_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `comments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `comments` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `task_id` int(11) NOT NULL, + `user_id` int(11) DEFAULT '0', + `date_creation` bigint(20) DEFAULT NULL, + `comment` mediumtext COLLATE utf8mb4_unicode_ci, + `reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT '', + `date_modification` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `comments_reference_idx` (`reference`), + KEY `comments_task_idx` (`task_id`), + CONSTRAINT `comments_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `currencies`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `currencies` ( + `currency` char(3) COLLATE utf8mb4_unicode_ci NOT NULL, + `rate` float DEFAULT '0', + UNIQUE KEY `currency` (`currency`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `custom_filters`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `custom_filters` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `filter` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `project_id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `is_shared` tinyint(1) DEFAULT '0', + `append` tinyint(1) DEFAULT '0', + PRIMARY KEY (`id`), + KEY `project_id` (`project_id`), + KEY `user_id` (`user_id`), + CONSTRAINT `custom_filters_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE, + CONSTRAINT `custom_filters_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `group_has_users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `group_has_users` ( + `group_id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + UNIQUE KEY `group_id` (`group_id`,`user_id`), + KEY `user_id` (`user_id`), + CONSTRAINT `group_has_users_ibfk_1` FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE, + CONSTRAINT `group_has_users_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `groups` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `external_id` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '', + `name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `invites`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `invites` ( + `email` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `project_id` int(11) NOT NULL, + `token` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`email`,`token`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `last_logins`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `last_logins` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `auth_type` varchar(25) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `user_id` int(11) DEFAULT NULL, + `ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `user_agent` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `date_creation` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `last_logins_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `links`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `links` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `label` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `opposite_id` int(11) DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `label` (`label`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `password_reset`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `password_reset` ( + `token` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL, + `user_id` int(11) NOT NULL, + `date_expiration` int(11) NOT NULL, + `date_creation` int(11) NOT NULL, + `ip` varchar(45) COLLATE utf8mb4_unicode_ci NOT NULL, + `user_agent` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL, + `is_active` tinyint(1) NOT NULL, + PRIMARY KEY (`token`), + KEY `user_id` (`user_id`), + CONSTRAINT `password_reset_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `plugin_schema_versions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `plugin_schema_versions` ( + `plugin` varchar(80) COLLATE utf8mb4_unicode_ci NOT NULL, + `version` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`plugin`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `predefined_task_descriptions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `predefined_task_descriptions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `project_id` int(11) NOT NULL, + `title` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `description` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `project_id` (`project_id`), + CONSTRAINT `predefined_task_descriptions_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_activities`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_activities` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `date_creation` bigint(20) DEFAULT NULL, + `event_name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `creator_id` int(11) DEFAULT NULL, + `project_id` int(11) DEFAULT NULL, + `task_id` int(11) DEFAULT NULL, + `data` mediumtext COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`id`), + KEY `creator_id` (`creator_id`), + KEY `project_id` (`project_id`), + KEY `task_id` (`task_id`), + CONSTRAINT `project_activities_ibfk_1` FOREIGN KEY (`creator_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `project_activities_ibfk_2` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE, + CONSTRAINT `project_activities_ibfk_3` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_daily_column_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_daily_column_stats` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `day` char(10) COLLATE utf8mb4_unicode_ci NOT NULL, + `project_id` int(11) NOT NULL, + `column_id` int(11) NOT NULL, + `total` int(11) NOT NULL DEFAULT '0', + `score` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `project_daily_column_stats_idx` (`day`,`project_id`,`column_id`), + KEY `column_id` (`column_id`), + KEY `project_id` (`project_id`), + CONSTRAINT `project_daily_column_stats_ibfk_1` FOREIGN KEY (`column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE, + CONSTRAINT `project_daily_column_stats_ibfk_2` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_daily_stats`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_daily_stats` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `day` char(10) COLLATE utf8mb4_unicode_ci NOT NULL, + `project_id` int(11) NOT NULL, + `avg_lead_time` int(11) NOT NULL DEFAULT '0', + `avg_cycle_time` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + UNIQUE KEY `project_daily_stats_idx` (`day`,`project_id`), + KEY `project_id` (`project_id`), + CONSTRAINT `project_daily_stats_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_has_categories`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_has_categories` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `project_id` int(11) NOT NULL, + `description` mediumtext COLLATE utf8mb4_unicode_ci, + `color_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_project_category` (`project_id`,`name`), + KEY `categories_project_idx` (`project_id`), + CONSTRAINT `project_has_categories_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_has_files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_has_files` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `project_id` int(11) NOT NULL, + `name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `path` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `is_image` tinyint(1) DEFAULT '0', + `size` int(11) NOT NULL DEFAULT '0', + `user_id` int(11) NOT NULL DEFAULT '0', + `date` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `project_id` (`project_id`), + CONSTRAINT `project_has_files_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_has_groups`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_has_groups` ( + `group_id` int(11) NOT NULL, + `project_id` int(11) NOT NULL, + `role` varchar(255) COLLATE utf8mb4_unicode_ci 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, + CONSTRAINT `project_has_groups_ibfk_2` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_has_metadata`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_has_metadata` ( + `project_id` int(11) NOT NULL, + `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '', + `changed_by` int(11) NOT NULL DEFAULT '0', + `changed_on` int(11) NOT NULL DEFAULT '0', + UNIQUE KEY `project_id` (`project_id`,`name`), + CONSTRAINT `project_has_metadata_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_has_notification_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_has_notification_types` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `project_id` int(11) NOT NULL, + `notification_type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `project_id` (`project_id`,`notification_type`), + CONSTRAINT `project_has_notification_types_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_has_roles`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_has_roles` ( + `role_id` int(11) NOT NULL AUTO_INCREMENT, + `role` varchar(191) COLLATE utf8mb4_unicode_ci 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=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_has_users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `project_has_users` ( + `project_id` int(11) NOT NULL, + `user_id` int(11) NOT NULL, + `role` varchar(255) COLLATE utf8mb4_unicode_ci 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=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `project_role_has_restrictions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +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(191) COLLATE utf8mb4_unicode_ci 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=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `projects`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `projects` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `is_active` tinyint(4) DEFAULT '1', + `token` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `last_modified` bigint(20) DEFAULT NULL, + `is_public` tinyint(1) DEFAULT '0', + `is_private` tinyint(1) DEFAULT '0', + `description` mediumtext COLLATE utf8mb4_unicode_ci, + `identifier` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT '', + `start_date` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT '', + `end_date` varchar(10) COLLATE utf8mb4_unicode_ci DEFAULT '', + `owner_id` int(11) DEFAULT '0', + `priority_default` int(11) DEFAULT '0', + `priority_start` int(11) DEFAULT '0', + `priority_end` int(11) DEFAULT '3', + `email` mediumtext COLLATE utf8mb4_unicode_ci, + `predefined_email_subjects` mediumtext COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `remember_me`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `remember_me` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) DEFAULT NULL, + `ip` varchar(45) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `user_agent` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `token` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `sequence` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `expiration` int(11) DEFAULT NULL, + `date_creation` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `remember_me_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `schema_version`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `schema_version` ( + `version` int(11) DEFAULT '0' +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `sessions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `sessions` ( + `id` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `expire_at` int(11) NOT NULL, + `data` longtext COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `settings`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `settings` ( + `option` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `value` mediumtext COLLATE utf8mb4_unicode_ci, + `changed_by` int(11) NOT NULL DEFAULT '0', + `changed_on` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`option`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `subtask_time_tracking`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `subtask_time_tracking` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `subtask_id` int(11) NOT NULL, + `start` bigint(20) DEFAULT NULL, + `end` bigint(20) DEFAULT NULL, + `time_spent` float DEFAULT '0', + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + KEY `subtask_id` (`subtask_id`), + CONSTRAINT `subtask_time_tracking_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `subtask_time_tracking_ibfk_2` FOREIGN KEY (`subtask_id`) REFERENCES `subtasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `subtasks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `subtasks` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `title` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `status` int(11) DEFAULT '0', + `time_estimated` float DEFAULT NULL, + `time_spent` float DEFAULT NULL, + `task_id` int(11) NOT NULL, + `user_id` int(11) DEFAULT NULL, + `position` int(11) DEFAULT '1', + PRIMARY KEY (`id`), + KEY `subtasks_task_idx` (`task_id`), + CONSTRAINT `subtasks_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `swimlanes`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `swimlanes` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `position` int(11) DEFAULT '1', + `is_active` int(11) DEFAULT '1', + `project_id` int(11) DEFAULT NULL, + `description` mediumtext COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`,`project_id`), + KEY `swimlanes_project_idx` (`project_id`), + CONSTRAINT `swimlanes_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `tags` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `project_id` int(11) NOT NULL, + `color_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `project_id` (`project_id`,`name`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `task_has_external_links`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `task_has_external_links` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `link_type` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `dependency` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL, + `title` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `url` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `date_creation` int(11) NOT NULL, + `date_modification` int(11) NOT NULL, + `task_id` int(11) NOT NULL, + `creator_id` int(11) DEFAULT '0', + PRIMARY KEY (`id`), + KEY `task_id` (`task_id`), + CONSTRAINT `task_has_external_links_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `task_has_files`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `task_has_files` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `path` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `is_image` tinyint(1) DEFAULT '0', + `task_id` int(11) NOT NULL, + `date` bigint(20) DEFAULT NULL, + `user_id` int(11) NOT NULL DEFAULT '0', + `size` int(11) NOT NULL DEFAULT '0', + PRIMARY KEY (`id`), + KEY `files_task_idx` (`task_id`), + CONSTRAINT `task_has_files_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `task_has_links`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `task_has_links` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `link_id` int(11) NOT NULL, + `task_id` int(11) NOT NULL, + `opposite_task_id` int(11) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `task_has_links_unique` (`link_id`,`task_id`,`opposite_task_id`), + KEY `opposite_task_id` (`opposite_task_id`), + KEY `task_has_links_task_index` (`task_id`), + CONSTRAINT `task_has_links_ibfk_1` FOREIGN KEY (`link_id`) REFERENCES `links` (`id`) ON DELETE CASCADE, + CONSTRAINT `task_has_links_ibfk_2` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE, + CONSTRAINT `task_has_links_ibfk_3` FOREIGN KEY (`opposite_task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `task_has_metadata`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `task_has_metadata` ( + `task_id` int(11) NOT NULL, + `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '', + `changed_by` int(11) NOT NULL DEFAULT '0', + `changed_on` int(11) NOT NULL DEFAULT '0', + UNIQUE KEY `task_id` (`task_id`,`name`), + CONSTRAINT `task_has_metadata_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `task_has_tags`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `task_has_tags` ( + `task_id` int(11) NOT NULL, + `tag_id` int(11) NOT NULL, + UNIQUE KEY `tag_id` (`tag_id`,`task_id`), + KEY `task_id` (`task_id`), + CONSTRAINT `task_has_tags_ibfk_1` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE, + CONSTRAINT `task_has_tags_ibfk_2` FOREIGN KEY (`tag_id`) REFERENCES `tags` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `tasks`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `tasks` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `title` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `description` mediumtext COLLATE utf8mb4_unicode_ci, + `date_creation` bigint(20) DEFAULT NULL, + `date_completed` bigint(20) DEFAULT NULL, + `date_due` bigint(20) DEFAULT NULL, + `color_id` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `project_id` int(11) NOT NULL, + `column_id` int(11) NOT NULL, + `owner_id` int(11) DEFAULT '0', + `position` int(11) DEFAULT NULL, + `score` int(11) DEFAULT NULL, + `is_active` tinyint(4) DEFAULT '1', + `category_id` int(11) DEFAULT '0', + `creator_id` int(11) DEFAULT '0', + `date_modification` int(11) DEFAULT '0', + `reference` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT '', + `date_started` bigint(20) DEFAULT NULL, + `time_spent` float DEFAULT '0', + `time_estimated` float DEFAULT '0', + `swimlane_id` int(11) NOT NULL, + `date_moved` bigint(20) DEFAULT NULL, + `recurrence_status` int(11) NOT NULL DEFAULT '0', + `recurrence_trigger` int(11) NOT NULL DEFAULT '0', + `recurrence_factor` int(11) NOT NULL DEFAULT '0', + `recurrence_timeframe` int(11) NOT NULL DEFAULT '0', + `recurrence_basedate` int(11) NOT NULL DEFAULT '0', + `recurrence_parent` int(11) DEFAULT NULL, + `recurrence_child` int(11) DEFAULT NULL, + `priority` int(11) DEFAULT '0', + `external_provider` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `external_uri` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + KEY `idx_task_active` (`is_active`), + KEY `column_id` (`column_id`), + KEY `tasks_reference_idx` (`reference`), + KEY `tasks_project_idx` (`project_id`), + KEY `tasks_swimlane_ibfk_1` (`swimlane_id`), + CONSTRAINT `tasks_ibfk_1` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE, + CONSTRAINT `tasks_ibfk_2` FOREIGN KEY (`column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE, + CONSTRAINT `tasks_swimlane_ibfk_1` FOREIGN KEY (`swimlane_id`) REFERENCES `swimlanes` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `transitions`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `transitions` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `project_id` int(11) NOT NULL, + `task_id` int(11) NOT NULL, + `src_column_id` int(11) NOT NULL, + `dst_column_id` int(11) NOT NULL, + `date` bigint(20) DEFAULT NULL, + `time_spent` int(11) DEFAULT '0', + PRIMARY KEY (`id`), + KEY `src_column_id` (`src_column_id`), + KEY `dst_column_id` (`dst_column_id`), + KEY `transitions_task_index` (`task_id`), + KEY `transitions_project_index` (`project_id`), + KEY `transitions_user_index` (`user_id`), + CONSTRAINT `transitions_ibfk_1` FOREIGN KEY (`src_column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE, + CONSTRAINT `transitions_ibfk_2` FOREIGN KEY (`dst_column_id`) REFERENCES `columns` (`id`) ON DELETE CASCADE, + CONSTRAINT `transitions_ibfk_3` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `transitions_ibfk_4` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE, + CONSTRAINT `transitions_ibfk_5` FOREIGN KEY (`task_id`) REFERENCES `tasks` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_has_metadata`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `user_has_metadata` ( + `user_id` int(11) NOT NULL, + `name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL, + `value` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '', + `changed_by` int(11) NOT NULL DEFAULT '0', + `changed_on` int(11) NOT NULL DEFAULT '0', + UNIQUE KEY `user_id` (`user_id`,`name`), + CONSTRAINT `user_has_metadata_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_has_notification_types`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `user_has_notification_types` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `notification_type` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `user_has_notification_types_user_idx` (`user_id`,`notification_type`), + CONSTRAINT `user_has_notification_types_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_has_notifications`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `user_has_notifications` ( + `user_id` int(11) NOT NULL, + `project_id` int(11) NOT NULL, + UNIQUE KEY `user_has_notifications_unique_idx` (`user_id`,`project_id`), + KEY `user_has_notifications_ibfk_2` (`project_id`), + CONSTRAINT `user_has_notifications_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE, + CONSTRAINT `user_has_notifications_ibfk_2` FOREIGN KEY (`project_id`) REFERENCES `projects` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `user_has_unread_notifications`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `user_has_unread_notifications` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `user_id` int(11) NOT NULL, + `date_creation` bigint(20) NOT NULL, + `event_name` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + `event_data` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL, + PRIMARY KEY (`id`), + KEY `user_id` (`user_id`), + CONSTRAINT `user_has_unread_notifications_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +DROP TABLE IF EXISTS `users`; +/*!40101 SET @saved_cs_client = @@character_set_client */; + SET character_set_client = utf8mb4 ; +CREATE TABLE `users` ( + `id` int(11) NOT NULL AUTO_INCREMENT, + `username` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL, + `password` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `is_ldap_user` tinyint(1) DEFAULT '0', + `name` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `email` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `google_id` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `github_id` varchar(30) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `notifications_enabled` tinyint(1) DEFAULT '0', + `timezone` varchar(50) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `language` varchar(11) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `disable_login_form` tinyint(1) DEFAULT '0', + `twofactor_activated` tinyint(1) DEFAULT '0', + `twofactor_secret` char(16) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `token` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT '', + `notifications_filter` int(11) DEFAULT '4', + `nb_failed_login` int(11) DEFAULT '0', + `lock_expiration_date` bigint(20) DEFAULT NULL, + `gitlab_id` int(11) DEFAULT NULL, + `role` varchar(25) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'app-user', + `is_active` tinyint(1) DEFAULT '1', + `avatar_path` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `api_access_token` varchar(255) COLLATE utf8mb4_unicode_ci DEFAULT NULL, + `filter` mediumtext COLLATE utf8mb4_unicode_ci, + PRIMARY KEY (`id`), + UNIQUE KEY `users_username_idx` (`username`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +/*!40101 SET character_set_client = @saved_cs_client */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +LOCK TABLES `settings` WRITE; +/*!40000 ALTER TABLE `settings` DISABLE KEYS */; +INSERT INTO `settings` VALUES ('api_token','0fde96ab43568a586f9e4ab95e6a38e7a955dcc6124a073f023e951a4197',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','b652b0d2d3e086025f8c3b6797f571b2139ea7bbfdca534f680ba7e41a32',0,0),('webhook_url','',0,0); +/*!40000 ALTER TABLE `settings` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +LOCK TABLES `links` WRITE; +/*!40000 ALTER TABLE `links` DISABLE KEYS */; +INSERT INTO `links` VALUES (1,'relates to',0),(2,'blocks',3),(3,'is blocked by',2),(4,'duplicates',5),(5,'is duplicated by',4),(6,'is a child of',7),(7,'is a parent of',6),(8,'targets milestone',9),(9,'is a milestone of',8),(10,'fixes',11),(11,'is fixed by',10); +/*!40000 ALTER TABLE `links` ENABLE KEYS */; +UNLOCK TABLES; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; + +INSERT INTO users (username, password, role) VALUES ('admin', '$2y$10$GzDCeQl/GdH.pCZfz4fWdO3qmayutRCmxEIY9U9t1k9q9F89VNDCm', 'app-admin'); +INSERT INTO schema_version VALUES ('133'); diff --git a/app/Schema/Sql/postgres.sql b/app/Schema/Sql/postgres.sql new file mode 100644 index 0000000..cd28237 --- /dev/null +++ b/app/Schema/Sql/postgres.sql @@ -0,0 +1,2751 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 10.5 +-- Dumped by pg_dump version 10.5 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Name: SCHEMA "public"; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON SCHEMA "public" IS 'standard public schema'; + + +SET default_tablespace = ''; + +SET default_with_oids = false; + +-- +-- Name: action_has_params; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."action_has_params" ( + "id" integer NOT NULL, + "action_id" integer NOT NULL, + "name" "text" NOT NULL, + "value" "text" NOT NULL +); + + +-- +-- Name: action_has_params_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."action_has_params_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: action_has_params_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."action_has_params_id_seq" OWNED BY "public"."action_has_params"."id"; + + +-- +-- Name: actions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."actions" ( + "id" integer NOT NULL, + "project_id" integer NOT NULL, + "event_name" "text" NOT NULL, + "action_name" "text" NOT NULL +); + + +-- +-- Name: actions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."actions_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: actions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."actions_id_seq" OWNED BY "public"."actions"."id"; + + +-- +-- Name: column_has_move_restrictions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."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, + "only_assigned" boolean DEFAULT false +); + + +-- +-- Name: column_has_move_restrictions_restriction_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."column_has_move_restrictions_restriction_id_seq" + AS integer + 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 "public"."column_has_move_restrictions_restriction_id_seq" OWNED BY "public"."column_has_move_restrictions"."restriction_id"; + + +-- +-- Name: column_has_restrictions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."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 "public"."column_has_restrictions_restriction_id_seq" + AS integer + 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 "public"."column_has_restrictions_restriction_id_seq" OWNED BY "public"."column_has_restrictions"."restriction_id"; + + +-- +-- Name: columns; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."columns" ( + "id" integer NOT NULL, + "title" character varying(255) NOT NULL, + "position" integer, + "project_id" integer NOT NULL, + "task_limit" integer DEFAULT 0, + "description" "text", + "hide_in_dashboard" boolean DEFAULT false +); + + +-- +-- Name: columns_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."columns_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: columns_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."columns_id_seq" OWNED BY "public"."columns"."id"; + + +-- +-- Name: comments; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."comments" ( + "id" integer NOT NULL, + "task_id" integer NOT NULL, + "user_id" integer DEFAULT 0, + "date_creation" bigint NOT NULL, + "comment" "text", + "reference" "text" DEFAULT ''::character varying, + "date_modification" bigint +); + + +-- +-- Name: comments_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."comments_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: comments_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."comments_id_seq" OWNED BY "public"."comments"."id"; + + +-- +-- Name: currencies; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."currencies" ( + "currency" character(3) NOT NULL, + "rate" real DEFAULT 0 +); + + +-- +-- Name: custom_filters; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."custom_filters" ( + "id" integer NOT NULL, + "filter" "text" NOT NULL, + "project_id" integer NOT NULL, + "user_id" integer NOT NULL, + "name" "text" NOT NULL, + "is_shared" boolean DEFAULT false, + "append" boolean DEFAULT false +); + + +-- +-- Name: custom_filters_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."custom_filters_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: custom_filters_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."custom_filters_id_seq" OWNED BY "public"."custom_filters"."id"; + + +-- +-- Name: group_has_users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."group_has_users" ( + "group_id" integer NOT NULL, + "user_id" integer NOT NULL +); + + +-- +-- Name: groups; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."groups" ( + "id" integer NOT NULL, + "external_id" character varying(255) DEFAULT ''::character varying, + "name" "text" NOT NULL +); + + +-- +-- Name: groups_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."groups_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: groups_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."groups_id_seq" OWNED BY "public"."groups"."id"; + + +-- +-- Name: invites; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."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: - +-- + +CREATE TABLE "public"."last_logins" ( + "id" integer NOT NULL, + "auth_type" character varying(25), + "user_id" integer, + "ip" character varying(45), + "user_agent" character varying(255), + "date_creation" bigint +); + + +-- +-- Name: last_logins_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."last_logins_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: last_logins_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."last_logins_id_seq" OWNED BY "public"."last_logins"."id"; + + +-- +-- Name: links; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."links" ( + "id" integer NOT NULL, + "label" character varying(255) NOT NULL, + "opposite_id" integer DEFAULT 0 +); + + +-- +-- Name: links_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."links_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: links_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."links_id_seq" OWNED BY "public"."links"."id"; + + +-- +-- Name: password_reset; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."password_reset" ( + "token" character varying(80) NOT NULL, + "user_id" integer NOT NULL, + "date_expiration" integer NOT NULL, + "date_creation" integer NOT NULL, + "ip" character varying(45) NOT NULL, + "user_agent" character varying(255) NOT NULL, + "is_active" boolean NOT NULL +); + + +-- +-- Name: plugin_schema_versions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."plugin_schema_versions" ( + "plugin" character varying(80) NOT NULL, + "version" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: predefined_task_descriptions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."predefined_task_descriptions" ( + "id" integer NOT NULL, + "project_id" integer NOT NULL, + "title" "text" NOT NULL, + "description" "text" NOT NULL +); + + +-- +-- Name: predefined_task_descriptions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."predefined_task_descriptions_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: predefined_task_descriptions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."predefined_task_descriptions_id_seq" OWNED BY "public"."predefined_task_descriptions"."id"; + + +-- +-- Name: project_activities; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_activities" ( + "id" integer NOT NULL, + "date_creation" bigint NOT NULL, + "event_name" "text" NOT NULL, + "creator_id" integer, + "project_id" integer, + "task_id" integer, + "data" "text" +); + + +-- +-- Name: project_activities_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."project_activities_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: project_activities_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."project_activities_id_seq" OWNED BY "public"."project_activities"."id"; + + +-- +-- Name: project_daily_column_stats; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_daily_column_stats" ( + "id" integer NOT NULL, + "day" character(10) NOT NULL, + "project_id" integer NOT NULL, + "column_id" integer NOT NULL, + "total" integer DEFAULT 0 NOT NULL, + "score" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: project_daily_stats; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_daily_stats" ( + "id" integer NOT NULL, + "day" character(10) NOT NULL, + "project_id" integer NOT NULL, + "avg_lead_time" integer DEFAULT 0 NOT NULL, + "avg_cycle_time" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: project_daily_stats_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."project_daily_stats_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: project_daily_stats_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."project_daily_stats_id_seq" OWNED BY "public"."project_daily_stats"."id"; + + +-- +-- Name: project_daily_summaries_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."project_daily_summaries_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: project_daily_summaries_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."project_daily_summaries_id_seq" OWNED BY "public"."project_daily_column_stats"."id"; + + +-- +-- Name: project_has_categories; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_has_categories" ( + "id" integer NOT NULL, + "name" character varying(255) NOT NULL, + "project_id" integer NOT NULL, + "description" "text", + "color_id" character varying(50) DEFAULT NULL::character varying +); + + +-- +-- Name: project_has_categories_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."project_has_categories_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: project_has_categories_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."project_has_categories_id_seq" OWNED BY "public"."project_has_categories"."id"; + + +-- +-- Name: project_has_files; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_has_files" ( + "id" integer NOT NULL, + "project_id" integer NOT NULL, + "name" "text" NOT NULL, + "path" "text" NOT NULL, + "is_image" boolean DEFAULT false, + "size" integer DEFAULT 0 NOT NULL, + "user_id" integer DEFAULT 0 NOT NULL, + "date" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: project_has_files_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."project_has_files_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: project_has_files_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."project_has_files_id_seq" OWNED BY "public"."project_has_files"."id"; + + +-- +-- Name: project_has_groups; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_has_groups" ( + "group_id" integer NOT NULL, + "project_id" integer NOT NULL, + "role" character varying(255) NOT NULL +); + + +-- +-- Name: project_has_metadata; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_has_metadata" ( + "project_id" integer NOT NULL, + "name" character varying(50) NOT NULL, + "value" character varying(255) DEFAULT ''::character varying, + "changed_by" integer DEFAULT 0 NOT NULL, + "changed_on" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: project_has_notification_types; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_has_notification_types" ( + "id" integer NOT NULL, + "project_id" integer NOT NULL, + "notification_type" character varying(50) NOT NULL +); + + +-- +-- Name: project_has_notification_types_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."project_has_notification_types_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: project_has_notification_types_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."project_has_notification_types_id_seq" OWNED BY "public"."project_has_notification_types"."id"; + + +-- +-- Name: project_has_roles; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."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 "public"."project_has_roles_role_id_seq" + AS integer + 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 "public"."project_has_roles_role_id_seq" OWNED BY "public"."project_has_roles"."role_id"; + + +-- +-- Name: project_has_users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."project_has_users" ( + "project_id" integer NOT NULL, + "user_id" integer 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 "public"."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 "public"."project_role_has_restrictions_restriction_id_seq" + AS integer + 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 "public"."project_role_has_restrictions_restriction_id_seq" OWNED BY "public"."project_role_has_restrictions"."restriction_id"; + + +-- +-- Name: projects; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."projects" ( + "id" integer NOT NULL, + "name" "text" NOT NULL, + "is_active" boolean DEFAULT true, + "token" character varying(255), + "last_modified" bigint DEFAULT 0, + "is_public" boolean DEFAULT false, + "is_private" boolean DEFAULT false, + "description" "text", + "identifier" character varying(50) DEFAULT ''::character varying, + "start_date" character varying(10) DEFAULT ''::character varying, + "end_date" character varying(10) DEFAULT ''::character varying, + "owner_id" integer DEFAULT 0, + "priority_default" integer DEFAULT 0, + "priority_start" integer DEFAULT 0, + "priority_end" integer DEFAULT 3, + "email" "text", + "predefined_email_subjects" "text" +); + + +-- +-- Name: projects_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."projects_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: projects_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."projects_id_seq" OWNED BY "public"."projects"."id"; + + +-- +-- Name: remember_me; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."remember_me" ( + "id" integer NOT NULL, + "user_id" integer, + "ip" character varying(45), + "user_agent" character varying(255), + "token" character varying(255), + "sequence" character varying(255), + "expiration" integer, + "date_creation" bigint +); + + +-- +-- Name: remember_me_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."remember_me_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: remember_me_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."remember_me_id_seq" OWNED BY "public"."remember_me"."id"; + + +-- +-- Name: schema_version; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."schema_version" ( + "version" integer DEFAULT 0 +); + + +-- +-- Name: sessions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."sessions" ( + "id" "text" NOT NULL, + "expire_at" integer NOT NULL, + "data" "text" DEFAULT ''::"text" +); + + +-- +-- Name: settings; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."settings" ( + "option" character varying(100) NOT NULL, + "value" "text" DEFAULT ''::character varying, + "changed_by" integer DEFAULT 0 NOT NULL, + "changed_on" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: subtask_time_tracking; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."subtask_time_tracking" ( + "id" integer NOT NULL, + "user_id" integer NOT NULL, + "subtask_id" integer NOT NULL, + "start" bigint DEFAULT 0, + "end" bigint DEFAULT 0, + "time_spent" real DEFAULT 0 +); + + +-- +-- Name: subtask_time_tracking_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."subtask_time_tracking_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: subtask_time_tracking_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."subtask_time_tracking_id_seq" OWNED BY "public"."subtask_time_tracking"."id"; + + +-- +-- Name: subtasks; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."subtasks" ( + "id" integer NOT NULL, + "title" "text" NOT NULL, + "status" smallint DEFAULT 0, + "time_estimated" double precision DEFAULT 0, + "time_spent" double precision DEFAULT 0, + "task_id" integer NOT NULL, + "user_id" integer, + "position" integer DEFAULT 1 +); + + +-- +-- Name: swimlanes; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."swimlanes" ( + "id" integer NOT NULL, + "name" "text" NOT NULL, + "position" integer DEFAULT 1, + "is_active" boolean DEFAULT true, + "project_id" integer, + "description" "text" +); + + +-- +-- Name: swimlanes_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."swimlanes_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: swimlanes_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."swimlanes_id_seq" OWNED BY "public"."swimlanes"."id"; + + +-- +-- Name: tags; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."tags" ( + "id" integer NOT NULL, + "name" character varying(255) NOT NULL, + "project_id" integer NOT NULL, + "color_id" character varying(50) DEFAULT NULL::character varying +); + + +-- +-- Name: tags_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."tags_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: tags_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."tags_id_seq" OWNED BY "public"."tags"."id"; + + +-- +-- Name: task_has_external_links; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."task_has_external_links" ( + "id" integer NOT NULL, + "link_type" character varying(100) NOT NULL, + "dependency" character varying(100) NOT NULL, + "title" "text" NOT NULL, + "url" "text" NOT NULL, + "date_creation" integer NOT NULL, + "date_modification" integer NOT NULL, + "task_id" integer NOT NULL, + "creator_id" integer DEFAULT 0 +); + + +-- +-- Name: task_has_external_links_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."task_has_external_links_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_has_external_links_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."task_has_external_links_id_seq" OWNED BY "public"."task_has_external_links"."id"; + + +-- +-- Name: task_has_files; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."task_has_files" ( + "id" integer NOT NULL, + "name" "text" NOT NULL, + "path" "text", + "is_image" boolean DEFAULT false, + "task_id" integer NOT NULL, + "date" bigint DEFAULT 0 NOT NULL, + "user_id" integer DEFAULT 0 NOT NULL, + "size" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: task_has_files_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."task_has_files_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_has_files_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."task_has_files_id_seq" OWNED BY "public"."task_has_files"."id"; + + +-- +-- Name: task_has_links; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."task_has_links" ( + "id" integer NOT NULL, + "link_id" integer NOT NULL, + "task_id" integer NOT NULL, + "opposite_task_id" integer NOT NULL +); + + +-- +-- Name: task_has_links_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."task_has_links_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_has_links_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."task_has_links_id_seq" OWNED BY "public"."task_has_links"."id"; + + +-- +-- Name: task_has_metadata; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."task_has_metadata" ( + "task_id" integer NOT NULL, + "name" character varying(50) NOT NULL, + "value" character varying(255) DEFAULT ''::character varying, + "changed_by" integer DEFAULT 0 NOT NULL, + "changed_on" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: task_has_subtasks_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."task_has_subtasks_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: task_has_subtasks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."task_has_subtasks_id_seq" OWNED BY "public"."subtasks"."id"; + + +-- +-- Name: task_has_tags; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."task_has_tags" ( + "task_id" integer NOT NULL, + "tag_id" integer NOT NULL +); + + +-- +-- Name: tasks; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."tasks" ( + "id" integer NOT NULL, + "title" "text" NOT NULL, + "description" "text", + "date_creation" bigint, + "color_id" character varying(255), + "project_id" integer NOT NULL, + "column_id" integer NOT NULL, + "owner_id" integer DEFAULT 0, + "position" integer, + "is_active" boolean DEFAULT true, + "date_completed" bigint, + "score" integer, + "date_due" bigint, + "category_id" integer DEFAULT 0, + "creator_id" integer DEFAULT 0, + "date_modification" integer DEFAULT 0, + "reference" "text" DEFAULT ''::character varying, + "date_started" bigint, + "time_spent" double precision DEFAULT 0, + "time_estimated" double precision DEFAULT 0, + "swimlane_id" integer NOT NULL, + "date_moved" bigint DEFAULT 0, + "recurrence_status" integer DEFAULT 0 NOT NULL, + "recurrence_trigger" integer DEFAULT 0 NOT NULL, + "recurrence_factor" integer DEFAULT 0 NOT NULL, + "recurrence_timeframe" integer DEFAULT 0 NOT NULL, + "recurrence_basedate" integer DEFAULT 0 NOT NULL, + "recurrence_parent" integer, + "recurrence_child" integer, + "priority" integer DEFAULT 0, + "external_provider" character varying(255), + "external_uri" character varying(255) +); + + +-- +-- Name: tasks_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."tasks_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: tasks_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."tasks_id_seq" OWNED BY "public"."tasks"."id"; + + +-- +-- Name: transitions; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."transitions" ( + "id" integer NOT NULL, + "user_id" integer NOT NULL, + "project_id" integer NOT NULL, + "task_id" integer NOT NULL, + "src_column_id" integer NOT NULL, + "dst_column_id" integer NOT NULL, + "date" bigint NOT NULL, + "time_spent" integer DEFAULT 0 +); + + +-- +-- Name: transitions_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."transitions_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: transitions_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."transitions_id_seq" OWNED BY "public"."transitions"."id"; + + +-- +-- Name: user_has_metadata; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."user_has_metadata" ( + "user_id" integer NOT NULL, + "name" character varying(50) NOT NULL, + "value" character varying(255) DEFAULT ''::character varying, + "changed_by" integer DEFAULT 0 NOT NULL, + "changed_on" integer DEFAULT 0 NOT NULL +); + + +-- +-- Name: user_has_notification_types; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."user_has_notification_types" ( + "id" integer NOT NULL, + "user_id" integer NOT NULL, + "notification_type" character varying(50) +); + + +-- +-- Name: user_has_notification_types_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."user_has_notification_types_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: user_has_notification_types_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."user_has_notification_types_id_seq" OWNED BY "public"."user_has_notification_types"."id"; + + +-- +-- Name: user_has_notifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."user_has_notifications" ( + "user_id" integer NOT NULL, + "project_id" integer +); + + +-- +-- Name: user_has_unread_notifications; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."user_has_unread_notifications" ( + "id" integer NOT NULL, + "user_id" integer NOT NULL, + "date_creation" bigint NOT NULL, + "event_name" "text" NOT NULL, + "event_data" "text" NOT NULL +); + + +-- +-- Name: user_has_unread_notifications_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."user_has_unread_notifications_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: user_has_unread_notifications_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."user_has_unread_notifications_id_seq" OWNED BY "public"."user_has_unread_notifications"."id"; + + +-- +-- Name: users; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE "public"."users" ( + "id" integer NOT NULL, + "username" "text" NOT NULL, + "password" character varying(255), + "is_ldap_user" boolean DEFAULT false, + "name" character varying(255), + "email" character varying(255), + "google_id" character varying(255), + "github_id" character varying(30), + "notifications_enabled" boolean DEFAULT false, + "timezone" character varying(50), + "language" character varying(11), + "disable_login_form" boolean DEFAULT false, + "twofactor_activated" boolean DEFAULT false, + "twofactor_secret" character(16), + "token" character varying(255) DEFAULT ''::character varying, + "notifications_filter" integer DEFAULT 4, + "nb_failed_login" integer DEFAULT 0, + "lock_expiration_date" bigint DEFAULT 0, + "gitlab_id" integer, + "role" character varying(25) DEFAULT 'app-user'::character varying NOT NULL, + "is_active" boolean DEFAULT true, + "avatar_path" character varying(255), + "api_access_token" character varying(255) DEFAULT NULL::character varying, + "filter" "text" DEFAULT NULL::character varying +); + + +-- +-- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE "public"."users_id_seq" + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: users_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE "public"."users_id_seq" OWNED BY "public"."users"."id"; + + +-- +-- Name: action_has_params id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."action_has_params" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."action_has_params_id_seq"'::"regclass"); + + +-- +-- Name: actions id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."actions" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."actions_id_seq"'::"regclass"); + + +-- +-- Name: column_has_move_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."column_has_move_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"public"."column_has_move_restrictions_restriction_id_seq"'::"regclass"); + + +-- +-- Name: column_has_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."column_has_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"public"."column_has_restrictions_restriction_id_seq"'::"regclass"); + + +-- +-- Name: columns id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."columns" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."columns_id_seq"'::"regclass"); + + +-- +-- Name: comments id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."comments" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."comments_id_seq"'::"regclass"); + + +-- +-- Name: custom_filters id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."custom_filters" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."custom_filters_id_seq"'::"regclass"); + + +-- +-- Name: groups id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."groups" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."groups_id_seq"'::"regclass"); + + +-- +-- Name: last_logins id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."last_logins" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."last_logins_id_seq"'::"regclass"); + + +-- +-- Name: links id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."links" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."links_id_seq"'::"regclass"); + + +-- +-- Name: predefined_task_descriptions id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."predefined_task_descriptions" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."predefined_task_descriptions_id_seq"'::"regclass"); + + +-- +-- Name: project_activities id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_activities" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."project_activities_id_seq"'::"regclass"); + + +-- +-- Name: project_daily_column_stats id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_daily_column_stats" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."project_daily_summaries_id_seq"'::"regclass"); + + +-- +-- Name: project_daily_stats id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_daily_stats" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."project_daily_stats_id_seq"'::"regclass"); + + +-- +-- Name: project_has_categories id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_categories" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."project_has_categories_id_seq"'::"regclass"); + + +-- +-- Name: project_has_files id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_files" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."project_has_files_id_seq"'::"regclass"); + + +-- +-- Name: project_has_notification_types id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_notification_types" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."project_has_notification_types_id_seq"'::"regclass"); + + +-- +-- Name: project_has_roles role_id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_roles" ALTER COLUMN "role_id" SET DEFAULT "nextval"('"public"."project_has_roles_role_id_seq"'::"regclass"); + + +-- +-- Name: project_role_has_restrictions restriction_id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_role_has_restrictions" ALTER COLUMN "restriction_id" SET DEFAULT "nextval"('"public"."project_role_has_restrictions_restriction_id_seq"'::"regclass"); + + +-- +-- Name: projects id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."projects" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."projects_id_seq"'::"regclass"); + + +-- +-- Name: remember_me id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."remember_me" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."remember_me_id_seq"'::"regclass"); + + +-- +-- Name: subtask_time_tracking id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."subtask_time_tracking" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."subtask_time_tracking_id_seq"'::"regclass"); + + +-- +-- Name: subtasks id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."subtasks" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."task_has_subtasks_id_seq"'::"regclass"); + + +-- +-- Name: swimlanes id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."swimlanes" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."swimlanes_id_seq"'::"regclass"); + + +-- +-- Name: tags id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tags" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."tags_id_seq"'::"regclass"); + + +-- +-- Name: task_has_external_links id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_external_links" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."task_has_external_links_id_seq"'::"regclass"); + + +-- +-- Name: task_has_files id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_files" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."task_has_files_id_seq"'::"regclass"); + + +-- +-- Name: task_has_links id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_links" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."task_has_links_id_seq"'::"regclass"); + + +-- +-- Name: tasks id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tasks" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."tasks_id_seq"'::"regclass"); + + +-- +-- Name: transitions id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."transitions" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."transitions_id_seq"'::"regclass"); + + +-- +-- Name: user_has_notification_types id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_notification_types" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."user_has_notification_types_id_seq"'::"regclass"); + + +-- +-- Name: user_has_unread_notifications id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_unread_notifications" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."user_has_unread_notifications_id_seq"'::"regclass"); + + +-- +-- Name: users id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."users" ALTER COLUMN "id" SET DEFAULT "nextval"('"public"."users_id_seq"'::"regclass"); + + +-- +-- Name: action_has_params action_has_params_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."action_has_params" + ADD CONSTRAINT "action_has_params_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: actions actions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."actions" + ADD CONSTRAINT "actions_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: column_has_move_restrictions column_has_move_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."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 "public"."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 "public"."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 "public"."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 "public"."columns" + ADD CONSTRAINT "columns_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: columns columns_title_project_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."columns" + ADD CONSTRAINT "columns_title_project_id_key" UNIQUE ("title", "project_id"); + + +-- +-- Name: comments comments_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."comments" + ADD CONSTRAINT "comments_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: currencies currencies_currency_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."currencies" + ADD CONSTRAINT "currencies_currency_key" UNIQUE ("currency"); + + +-- +-- Name: custom_filters custom_filters_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."custom_filters" + ADD CONSTRAINT "custom_filters_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: group_has_users group_has_users_group_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."group_has_users" + ADD CONSTRAINT "group_has_users_group_id_user_id_key" UNIQUE ("group_id", "user_id"); + + +-- +-- Name: groups groups_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."groups" + ADD CONSTRAINT "groups_name_key" UNIQUE ("name"); + + +-- +-- Name: groups groups_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."groups" + ADD CONSTRAINT "groups_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: invites invites_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."invites" + ADD CONSTRAINT "invites_pkey" PRIMARY KEY ("email", "token"); + + +-- +-- Name: last_logins last_logins_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."last_logins" + ADD CONSTRAINT "last_logins_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: links links_label_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."links" + ADD CONSTRAINT "links_label_key" UNIQUE ("label"); + + +-- +-- Name: links links_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."links" + ADD CONSTRAINT "links_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: password_reset password_reset_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."password_reset" + ADD CONSTRAINT "password_reset_pkey" PRIMARY KEY ("token"); + + +-- +-- Name: plugin_schema_versions plugin_schema_versions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."plugin_schema_versions" + ADD CONSTRAINT "plugin_schema_versions_pkey" PRIMARY KEY ("plugin"); + + +-- +-- Name: predefined_task_descriptions predefined_task_descriptions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."predefined_task_descriptions" + ADD CONSTRAINT "predefined_task_descriptions_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: project_activities project_activities_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_activities" + ADD CONSTRAINT "project_activities_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: project_daily_stats project_daily_stats_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_daily_stats" + ADD CONSTRAINT "project_daily_stats_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: project_daily_column_stats project_daily_summaries_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_daily_column_stats" + ADD CONSTRAINT "project_daily_summaries_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: project_has_categories project_has_categories_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_categories" + ADD CONSTRAINT "project_has_categories_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: project_has_categories project_has_categories_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_categories" + ADD CONSTRAINT "project_has_categories_project_id_name_key" UNIQUE ("project_id", "name"); + + +-- +-- Name: project_has_files project_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_files" + ADD CONSTRAINT "project_has_files_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: project_has_groups project_has_groups_group_id_project_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_groups" + ADD CONSTRAINT "project_has_groups_group_id_project_id_key" UNIQUE ("group_id", "project_id"); + + +-- +-- Name: project_has_metadata project_has_metadata_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_metadata" + ADD CONSTRAINT "project_has_metadata_project_id_name_key" UNIQUE ("project_id", "name"); + + +-- +-- Name: project_has_notification_types project_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_notification_types" + ADD CONSTRAINT "project_has_notification_types_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: project_has_notification_types project_has_notification_types_project_id_notification_type_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_notification_types" + ADD CONSTRAINT "project_has_notification_types_project_id_notification_type_key" UNIQUE ("project_id", "notification_type"); + + +-- +-- Name: project_has_roles project_has_roles_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."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 "public"."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 "public"."project_has_users" + ADD CONSTRAINT "project_has_users_project_id_user_id_key" UNIQUE ("project_id", "user_id"); + + +-- +-- Name: project_role_has_restrictions project_role_has_restrictions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."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 "public"."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 "public"."projects" + ADD CONSTRAINT "projects_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: remember_me remember_me_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."remember_me" + ADD CONSTRAINT "remember_me_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: sessions sessions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."sessions" + ADD CONSTRAINT "sessions_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: settings settings_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."settings" + ADD CONSTRAINT "settings_pkey" PRIMARY KEY ("option"); + + +-- +-- Name: subtask_time_tracking subtask_time_tracking_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."subtask_time_tracking" + ADD CONSTRAINT "subtask_time_tracking_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: swimlanes swimlanes_name_project_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."swimlanes" + ADD CONSTRAINT "swimlanes_name_project_id_key" UNIQUE ("name", "project_id"); + + +-- +-- Name: swimlanes swimlanes_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."swimlanes" + ADD CONSTRAINT "swimlanes_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: tags tags_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tags" + ADD CONSTRAINT "tags_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: tags tags_project_id_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tags" + ADD CONSTRAINT "tags_project_id_name_key" UNIQUE ("project_id", "name"); + + +-- +-- Name: task_has_external_links task_has_external_links_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_external_links" + ADD CONSTRAINT "task_has_external_links_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: task_has_files task_has_files_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_files" + ADD CONSTRAINT "task_has_files_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: task_has_links task_has_links_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_links" + ADD CONSTRAINT "task_has_links_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: task_has_metadata task_has_metadata_task_id_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_metadata" + ADD CONSTRAINT "task_has_metadata_task_id_name_key" UNIQUE ("task_id", "name"); + + +-- +-- Name: subtasks task_has_subtasks_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."subtasks" + ADD CONSTRAINT "task_has_subtasks_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: task_has_tags task_has_tags_tag_id_task_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_tags" + ADD CONSTRAINT "task_has_tags_tag_id_task_id_key" UNIQUE ("tag_id", "task_id"); + + +-- +-- Name: tasks tasks_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tasks" + ADD CONSTRAINT "tasks_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: transitions transitions_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."transitions" + ADD CONSTRAINT "transitions_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: user_has_metadata user_has_metadata_user_id_name_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_metadata" + ADD CONSTRAINT "user_has_metadata_user_id_name_key" UNIQUE ("user_id", "name"); + + +-- +-- Name: user_has_notification_types user_has_notification_types_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_notification_types" + ADD CONSTRAINT "user_has_notification_types_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: user_has_notifications user_has_notifications_project_id_user_id_key; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_notifications" + ADD CONSTRAINT "user_has_notifications_project_id_user_id_key" UNIQUE ("project_id", "user_id"); + + +-- +-- Name: user_has_unread_notifications user_has_unread_notifications_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_unread_notifications" + ADD CONSTRAINT "user_has_unread_notifications_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."users" + ADD CONSTRAINT "users_pkey" PRIMARY KEY ("id"); + + +-- +-- Name: categories_project_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "categories_project_idx" ON "public"."project_has_categories" USING "btree" ("project_id"); + + +-- +-- Name: columns_project_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "columns_project_idx" ON "public"."columns" USING "btree" ("project_id"); + + +-- +-- Name: comments_reference_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "comments_reference_idx" ON "public"."comments" USING "btree" ("reference"); + + +-- +-- Name: comments_task_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "comments_task_idx" ON "public"."comments" USING "btree" ("task_id"); + + +-- +-- Name: files_task_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "files_task_idx" ON "public"."task_has_files" USING "btree" ("task_id"); + + +-- +-- Name: project_daily_column_stats_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX "project_daily_column_stats_idx" ON "public"."project_daily_column_stats" USING "btree" ("day", "project_id", "column_id"); + + +-- +-- Name: project_daily_stats_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX "project_daily_stats_idx" ON "public"."project_daily_stats" USING "btree" ("day", "project_id"); + + +-- +-- Name: subtasks_task_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "subtasks_task_idx" ON "public"."subtasks" USING "btree" ("task_id"); + + +-- +-- Name: swimlanes_project_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "swimlanes_project_idx" ON "public"."swimlanes" USING "btree" ("project_id"); + + +-- +-- Name: task_has_links_task_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "task_has_links_task_index" ON "public"."task_has_links" USING "btree" ("task_id"); + + +-- +-- Name: task_has_links_unique; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX "task_has_links_unique" ON "public"."task_has_links" USING "btree" ("link_id", "task_id", "opposite_task_id"); + + +-- +-- Name: tasks_project_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "tasks_project_idx" ON "public"."tasks" USING "btree" ("project_id"); + + +-- +-- Name: tasks_reference_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "tasks_reference_idx" ON "public"."tasks" USING "btree" ("reference"); + + +-- +-- Name: transitions_project_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "transitions_project_index" ON "public"."transitions" USING "btree" ("project_id"); + + +-- +-- Name: transitions_task_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "transitions_task_index" ON "public"."transitions" USING "btree" ("task_id"); + + +-- +-- Name: transitions_user_index; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX "transitions_user_index" ON "public"."transitions" USING "btree" ("user_id"); + + +-- +-- Name: user_has_notification_types_user_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX "user_has_notification_types_user_idx" ON "public"."user_has_notification_types" USING "btree" ("user_id", "notification_type"); + + +-- +-- Name: users_username_idx; Type: INDEX; Schema: public; Owner: - +-- + +CREATE UNIQUE INDEX "users_username_idx" ON "public"."users" USING "btree" ("username"); + + +-- +-- Name: action_has_params action_has_params_action_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."action_has_params" + ADD CONSTRAINT "action_has_params_action_id_fkey" FOREIGN KEY ("action_id") REFERENCES "public"."actions"("id") ON DELETE CASCADE; + + +-- +-- Name: actions actions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."actions" + ADD CONSTRAINT "actions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: column_has_move_restrictions column_has_move_restrictions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."column_has_move_restrictions" + ADD CONSTRAINT "column_has_move_restrictions_dst_column_id_fkey" FOREIGN KEY ("dst_column_id") REFERENCES "public"."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 "public"."column_has_move_restrictions" + ADD CONSTRAINT "column_has_move_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."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 "public"."column_has_move_restrictions" + ADD CONSTRAINT "column_has_move_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "public"."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 "public"."column_has_move_restrictions" + ADD CONSTRAINT "column_has_move_restrictions_src_column_id_fkey" FOREIGN KEY ("src_column_id") REFERENCES "public"."columns"("id") ON DELETE CASCADE; + + +-- +-- Name: column_has_restrictions column_has_restrictions_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."column_has_restrictions" + ADD CONSTRAINT "column_has_restrictions_column_id_fkey" FOREIGN KEY ("column_id") REFERENCES "public"."columns"("id") ON DELETE CASCADE; + + +-- +-- Name: column_has_restrictions column_has_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."column_has_restrictions" + ADD CONSTRAINT "column_has_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: column_has_restrictions column_has_restrictions_role_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."column_has_restrictions" + ADD CONSTRAINT "column_has_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "public"."project_has_roles"("role_id") ON DELETE CASCADE; + + +-- +-- Name: columns columns_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."columns" + ADD CONSTRAINT "columns_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: comments comments_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."comments" + ADD CONSTRAINT "comments_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: group_has_users group_has_users_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."group_has_users" + ADD CONSTRAINT "group_has_users_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "public"."groups"("id") ON DELETE CASCADE; + + +-- +-- Name: group_has_users group_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."group_has_users" + ADD CONSTRAINT "group_has_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: last_logins last_logins_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."last_logins" + ADD CONSTRAINT "last_logins_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: password_reset password_reset_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."password_reset" + ADD CONSTRAINT "password_reset_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: predefined_task_descriptions predefined_task_descriptions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."predefined_task_descriptions" + ADD CONSTRAINT "predefined_task_descriptions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_activities project_activities_creator_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_activities" + ADD CONSTRAINT "project_activities_creator_id_fkey" FOREIGN KEY ("creator_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: project_activities project_activities_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_activities" + ADD CONSTRAINT "project_activities_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_activities project_activities_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_activities" + ADD CONSTRAINT "project_activities_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: project_daily_stats project_daily_stats_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_daily_stats" + ADD CONSTRAINT "project_daily_stats_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_daily_column_stats project_daily_summaries_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_daily_column_stats" + ADD CONSTRAINT "project_daily_summaries_column_id_fkey" FOREIGN KEY ("column_id") REFERENCES "public"."columns"("id") ON DELETE CASCADE; + + +-- +-- Name: project_daily_column_stats project_daily_summaries_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_daily_column_stats" + ADD CONSTRAINT "project_daily_summaries_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_categories project_has_categories_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_categories" + ADD CONSTRAINT "project_has_categories_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_files project_has_files_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_files" + ADD CONSTRAINT "project_has_files_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_groups project_has_groups_group_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_groups" + ADD CONSTRAINT "project_has_groups_group_id_fkey" FOREIGN KEY ("group_id") REFERENCES "public"."groups"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_groups project_has_groups_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_groups" + ADD CONSTRAINT "project_has_groups_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_metadata project_has_metadata_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_metadata" + ADD CONSTRAINT "project_has_metadata_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_notification_types project_has_notification_types_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_notification_types" + ADD CONSTRAINT "project_has_notification_types_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_roles project_has_roles_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_roles" + ADD CONSTRAINT "project_has_roles_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_users project_has_users_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_users" + ADD CONSTRAINT "project_has_users_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: project_has_users project_has_users_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_has_users" + ADD CONSTRAINT "project_has_users_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: project_role_has_restrictions project_role_has_restrictions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."project_role_has_restrictions" + ADD CONSTRAINT "project_role_has_restrictions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."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 "public"."project_role_has_restrictions" + ADD CONSTRAINT "project_role_has_restrictions_role_id_fkey" FOREIGN KEY ("role_id") REFERENCES "public"."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 "public"."remember_me" + ADD CONSTRAINT "remember_me_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: subtask_time_tracking subtask_time_tracking_subtask_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."subtask_time_tracking" + ADD CONSTRAINT "subtask_time_tracking_subtask_id_fkey" FOREIGN KEY ("subtask_id") REFERENCES "public"."subtasks"("id") ON DELETE CASCADE; + + +-- +-- Name: subtask_time_tracking subtask_time_tracking_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."subtask_time_tracking" + ADD CONSTRAINT "subtask_time_tracking_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: swimlanes swimlanes_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."swimlanes" + ADD CONSTRAINT "swimlanes_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_external_links task_has_external_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_external_links" + ADD CONSTRAINT "task_has_external_links_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_files task_has_files_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_files" + ADD CONSTRAINT "task_has_files_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_links task_has_links_link_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_links" + ADD CONSTRAINT "task_has_links_link_id_fkey" FOREIGN KEY ("link_id") REFERENCES "public"."links"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_links task_has_links_opposite_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_links" + ADD CONSTRAINT "task_has_links_opposite_task_id_fkey" FOREIGN KEY ("opposite_task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_links task_has_links_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_links" + ADD CONSTRAINT "task_has_links_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_metadata task_has_metadata_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_metadata" + ADD CONSTRAINT "task_has_metadata_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: subtasks task_has_subtasks_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."subtasks" + ADD CONSTRAINT "task_has_subtasks_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_tags task_has_tags_tag_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_tags" + ADD CONSTRAINT "task_has_tags_tag_id_fkey" FOREIGN KEY ("tag_id") REFERENCES "public"."tags"("id") ON DELETE CASCADE; + + +-- +-- Name: task_has_tags task_has_tags_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."task_has_tags" + ADD CONSTRAINT "task_has_tags_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: tasks tasks_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tasks" + ADD CONSTRAINT "tasks_column_id_fkey" FOREIGN KEY ("column_id") REFERENCES "public"."columns"("id") ON DELETE CASCADE; + + +-- +-- Name: tasks tasks_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tasks" + ADD CONSTRAINT "tasks_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: tasks tasks_swimlane_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."tasks" + ADD CONSTRAINT "tasks_swimlane_id_fkey" FOREIGN KEY ("swimlane_id") REFERENCES "public"."swimlanes"("id") ON DELETE CASCADE; + + +-- +-- Name: transitions transitions_dst_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."transitions" + ADD CONSTRAINT "transitions_dst_column_id_fkey" FOREIGN KEY ("dst_column_id") REFERENCES "public"."columns"("id") ON DELETE CASCADE; + + +-- +-- Name: transitions transitions_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."transitions" + ADD CONSTRAINT "transitions_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: transitions transitions_src_column_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."transitions" + ADD CONSTRAINT "transitions_src_column_id_fkey" FOREIGN KEY ("src_column_id") REFERENCES "public"."columns"("id") ON DELETE CASCADE; + + +-- +-- Name: transitions transitions_task_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."transitions" + ADD CONSTRAINT "transitions_task_id_fkey" FOREIGN KEY ("task_id") REFERENCES "public"."tasks"("id") ON DELETE CASCADE; + + +-- +-- Name: transitions transitions_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."transitions" + ADD CONSTRAINT "transitions_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: user_has_metadata user_has_metadata_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_metadata" + ADD CONSTRAINT "user_has_metadata_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: user_has_notification_types user_has_notification_types_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_notification_types" + ADD CONSTRAINT "user_has_notification_types_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: user_has_notifications user_has_notifications_project_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_notifications" + ADD CONSTRAINT "user_has_notifications_project_id_fkey" FOREIGN KEY ("project_id") REFERENCES "public"."projects"("id") ON DELETE CASCADE; + + +-- +-- Name: user_has_notifications user_has_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_notifications" + ADD CONSTRAINT "user_has_notifications_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- Name: user_has_unread_notifications user_has_unread_notifications_user_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY "public"."user_has_unread_notifications" + ADD CONSTRAINT "user_has_unread_notifications_user_id_fkey" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE CASCADE; + + +-- +-- PostgreSQL database dump complete +-- + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 10.5 +-- Dumped by pg_dump version 10.5 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Data for Name: settings; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('board_highlight_period', '172800', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('board_public_refresh_interval', '60', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('board_private_refresh_interval', '10', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('board_columns', '', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('webhook_token', '1a9fe6b6651d4f17db363279ec08b6b44c8ee4f205d0c9527848a648436c', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('api_token', '8e6d6c81e25529d4d83e8b30385922ce1a99af3908743e888aa87344f8f3', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('application_language', 'en_US', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('application_timezone', 'UTC', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('application_url', '', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('application_date_format', 'm/d/Y', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('project_categories', '', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('subtask_restriction', '0', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('application_stylesheet', '', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('application_currency', 'USD', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('integration_gravatar', '0', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('calendar_user_subtasks_time_tracking', '0', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('calendar_user_tasks', 'date_started', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('calendar_project_tasks', 'date_started', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('webhook_url', '', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('default_color', 'yellow', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('subtask_time_tracking', '1', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('cfd_include_closed_tasks', '1', 0, 0); +INSERT INTO public.settings (option, value, changed_by, changed_on) VALUES ('password_reset', '1', 0, 0); + + +-- +-- PostgreSQL database dump complete +-- + +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 10.5 +-- Dumped by pg_dump version 10.5 + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- Data for Name: links; Type: TABLE DATA; Schema: public; Owner: postgres +-- + +INSERT INTO public.links (id, label, opposite_id) VALUES (1, 'relates to', 0); +INSERT INTO public.links (id, label, opposite_id) VALUES (2, 'blocks', 3); +INSERT INTO public.links (id, label, opposite_id) VALUES (3, 'is blocked by', 2); +INSERT INTO public.links (id, label, opposite_id) VALUES (4, 'duplicates', 5); +INSERT INTO public.links (id, label, opposite_id) VALUES (5, 'is duplicated by', 4); +INSERT INTO public.links (id, label, opposite_id) VALUES (6, 'is a child of', 7); +INSERT INTO public.links (id, label, opposite_id) VALUES (7, 'is a parent of', 6); +INSERT INTO public.links (id, label, opposite_id) VALUES (8, 'targets milestone', 9); +INSERT INTO public.links (id, label, opposite_id) VALUES (9, 'is a milestone of', 8); +INSERT INTO public.links (id, label, opposite_id) VALUES (10, 'fixes', 11); +INSERT INTO public.links (id, label, opposite_id) VALUES (11, 'is fixed by', 10); + + +-- +-- Name: links_id_seq; Type: SEQUENCE SET; Schema: public; Owner: postgres +-- + +SELECT pg_catalog.setval('public.links_id_seq', 11, true); + + +-- +-- PostgreSQL database dump complete +-- + +INSERT INTO public.users (username, password, role) VALUES ('admin', '$2y$10$GzDCeQl/GdH.pCZfz4fWdO3qmayutRCmxEIY9U9t1k9q9F89VNDCm', 'app-admin'); +INSERT INTO public.schema_version VALUES ('111'); diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php new file mode 100644 index 0000000..6a89a5b --- /dev/null +++ b/app/Schema/Sqlite.php @@ -0,0 +1,1572 @@ +<?php + +namespace Schema; + +require_once __DIR__.'/Migration.php'; + +use Kanboard\Core\Security\Token; +use Kanboard\Core\Security\Role; +use PDO; + +const VERSION = 128; + +function version_128(PDO $pdo) +{ + $pdo->exec("ALTER TABLE comments ADD COLUMN visibility VARCHAR(25) NOT NULL DEFAULT '".Role::APP_USER."'"); +} + +function version_127(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN theme TEXT DEFAULT 'light' NOT NULL"); +} + +function version_126(PDO $pdo) +{ + $pdo->exec('ALTER TABLE subtask_time_tracking RENAME TO subtask_time_tracking_old'); + + $pdo->exec(' + CREATE TABLE subtask_time_tracking ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + subtask_id INTEGER NOT NULL, + start INTEGER DEFAULT 0, + end INTEGER DEFAULT 0, + time_spent REAL DEFAULT 0, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(subtask_id) REFERENCES subtasks(id) ON DELETE CASCADE + ) + '); + + $pdo->exec('DROP INDEX subtasks_task_idx'); + $pdo->exec('CREATE INDEX subtasks_task_idx ON subtasks(task_id)'); + $pdo->exec('INSERT INTO subtask_time_tracking SELECT * FROM subtask_time_tracking_old'); + $pdo->exec('DROP TABLE subtask_time_tracking_old'); +} + +function version_125(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE tasks_new + ( + id INTEGER PRIMARY KEY, + title TEXT NOCASE NOT NULL, + description TEXT, + date_creation INTEGER, + color_id TEXT, + project_id INTEGER REFERENCES projects(id) ON DELETE CASCADE, + column_id INTEGER REFERENCES columns(id) ON DELETE CASCADE, + owner_id INTEGER DEFAULT '0', + position INTEGER, + is_active INTEGER DEFAULT 1, + date_completed INTEGER, + score INTEGER, + date_due INTEGER, + category_id INTEGER DEFAULT 0, + creator_id INTEGER DEFAULT '0', + date_modification INTEGER DEFAULT '0', + reference TEXT DEFAULT '', + date_started INTEGER, + time_spent NUMERIC DEFAULT 0, + time_estimated NUMERIC DEFAULT 0, + swimlane_id INTEGER REFERENCES swimlanes(id) ON DELETE CASCADE, + date_moved INTEGER DEFAULT 0, + recurrence_status INTEGER DEFAULT 0 NOT NULL, + recurrence_trigger INTEGER DEFAULT 0 NOT NULL, + recurrence_factor INTEGER DEFAULT 0 NOT NULL, + recurrence_timeframe INTEGER DEFAULT 0 NOT NULL, + recurrence_basedate INTEGER DEFAULT 0 NOT NULL, + recurrence_parent INTEGER, + recurrence_child INTEGER, + priority INTEGER DEFAULT 0, + external_provider TEXT, + external_uri TEXT + ) + "); + + $pdo->exec('INSERT INTO tasks_new SELECT * FROM tasks'); + $pdo->exec('DROP TABLE tasks'); + $pdo->exec('ALTER TABLE tasks_new RENAME TO tasks'); +} + +function version_124(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN enable_global_tags INTEGER DEFAULT 1 NOT NULL'); +} + +function version_123(PDO $pdo) +{ + $pdo->exec('ALTER TABLE swimlanes ADD COLUMN task_limit INTEGER DEFAULT 0'); +} + +function version_122(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN task_limit INTEGER DEFAULT 0'); +} + +function version_121(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN per_swimlane_task_limits INTEGER DEFAULT 0 NOT NULL'); +} + +function version_120(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tags ADD COLUMN color_id TEXT DEFAULT NULL'); +} + +function version_119(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_has_categories ADD COLUMN color_id TEXT DEFAULT NULL'); +} + +function version_118(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN filter TEXT'); +} + +function version_117(PDO $pdo) +{ + $pdo->exec("CREATE TABLE sessions ( + id TEXT PRIMARY KEY, + expire_at INTEGER NOT NULL, + data TEXT DEFAULT '' + )"); +} + +function version_116(PDO $pdo) +{ + $pdo->exec('CREATE TABLE predefined_task_descriptions ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + title TEXT NOT NULL, + description TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )'); +} + +function version_115(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN predefined_email_subjects TEXT'); +} + +function version_114(PDO $pdo) +{ + $pdo->exec('ALTER TABLE column_has_move_restrictions ADD COLUMN only_assigned INTEGER DEFAULT 0'); +} + +function version_113(PDO $pdo) +{ + $pdo->exec( + 'ALTER TABLE project_activities RENAME TO project_activities_bak' + ); + $pdo->exec(" + CREATE TABLE project_activities ( + id INTEGER PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + creator_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + task_id INTEGER NOT NULL, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); + $pdo->exec( + 'INSERT INTO project_activities SELECT * FROM project_activities_bak' + ); + $pdo->exec( + 'DROP TABLE project_activities_bak' + ); +} + +function version_112(PDO $pdo) +{ + migrate_default_swimlane($pdo); +} + +function version_111(PDO $pdo) +{ + $pdo->exec('ALTER TABLE "projects" ADD COLUMN email TEXT'); +} + +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) +{ + $pdo->exec("ALTER TABLE columns ADD COLUMN hide_in_dashboard INTEGER DEFAULT 0 NOT NULL"); +} + +function version_102(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE tags ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + project_id INTEGER NOT NULL, + UNIQUE(project_id, name) + ) + "); + + $pdo->exec(" + CREATE TABLE task_has_tags ( + task_id INTEGER NOT NULL, + tag_id INTEGER NOT NULL, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(tag_id) REFERENCES tags(id) ON DELETE CASCADE, + UNIQUE(tag_id, task_id) + ) + "); +} + +function version_101(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN avatar_path TEXT"); +} + +function version_100(PDO $pdo) +{ + $pdo->exec("ALTER TABLE user_has_metadata ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE user_has_metadata ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE project_has_metadata ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE project_has_metadata ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE task_has_metadata ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE task_has_metadata ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); + + $pdo->exec("ALTER TABLE settings ADD COLUMN changed_by INTEGER DEFAULT 0 NOT NULL"); + $pdo->exec("ALTER TABLE settings ADD COLUMN changed_on INTEGER DEFAULT 0 NOT NULL"); + +} + +function version_99(PDO $pdo) +{ + $pdo->exec("UPDATE project_activities SET event_name='task.file.create' WHERE event_name='file.create'"); +} + +function version_98(PDO $pdo) +{ + $pdo->exec('ALTER TABLE files RENAME TO task_has_files'); + + $pdo->exec( + " + CREATE TABLE project_has_files ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + name TEXT COLLATE NOCASE NOT NULL, + path TEXT NOT NULL, + is_image INTEGER DEFAULT 0, + size INTEGER DEFAULT 0 NOT NULL, + user_id INTEGER DEFAULT 0 NOT NULL, + date INTEGER DEFAULT 0 NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )" + ); +} + +function version_97(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_active INTEGER DEFAULT 1"); +} + +function version_96(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_external_links ( + id INTEGER PRIMARY KEY, + link_type TEXT NOT NULL, + dependency TEXT NOT NULL, + title TEXT NOT NULL, + url TEXT NOT NULL, + date_creation INTEGER NOT NULL, + date_modification INTEGER NOT NULL, + task_id INTEGER NOT NULL, + creator_id INTEGER DEFAULT 0, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); +} + +function version_95(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_default INTEGER DEFAULT 0"); + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_start INTEGER DEFAULT 0"); + $pdo->exec("ALTER TABLE projects ADD COLUMN priority_end INTEGER DEFAULT 3"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN priority INTEGER DEFAULT 0"); +} + +function version_94(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN owner_id INTEGER DEFAULT 0"); +} + +function version_93(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE password_reset ( + token TEXT PRIMARY KEY, + user_id INTEGER NOT NULL, + date_expiration INTEGER NOT NULL, + date_creation INTEGER NOT NULL, + ip TEXT NOT NULL, + user_agent TEXT NOT NULL, + is_active INTEGER NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) + "); + + $pdo->exec("INSERT INTO settings VALUES ('password_reset', '1')"); +} + +function version_92(PDO $pdo) +{ + $rq = $pdo->prepare('SELECT * FROM actions'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE actions SET action_name=? WHERE id=?'); + + foreach ($rows as $row) { + if ($row['action_name'] === 'TaskAssignCurrentUser' && $row['event_name'] === 'task.move.column') { + $row['action_name'] = '\Kanboard\Action\TaskAssignCurrentUserColumn'; + } elseif ($row['action_name'] === 'TaskClose' && $row['event_name'] === 'task.move.column') { + $row['action_name'] = '\Kanboard\Action\TaskCloseColumn'; + } elseif ($row['action_name'] === 'TaskLogMoveAnotherColumn') { + $row['action_name'] = '\Kanboard\Action\CommentCreationMoveTaskColumn'; + } elseif ($row['action_name'][0] !== '\\') { + $row['action_name'] = '\Kanboard\Action\\'.$row['action_name']; + } + + $rq->execute(array($row['action_name'], $row['id'])); + } +} + +function version_91(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN role TEXT NOT NULL DEFAULT '".Role::APP_USER."'"); + + $rq = $pdo->prepare('SELECT * FROM users'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE users SET "role"=? WHERE "id"=?'); + + foreach ($rows as $row) { + $role = Role::APP_USER; + + if ($row['is_admin'] == 1) { + $role = Role::APP_ADMIN; + } elseif ($row['is_project_admin']) { + $role = Role::APP_MANAGER; + } + + $rq->execute(array($role, $row['id'])); + } +} + +function version_90(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_has_groups ( + group_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + role TEXT NOT NULL, + FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(group_id, project_id) + ) + "); + + $pdo->exec("ALTER TABLE project_has_users ADD COLUMN role TEXT NOT NULL DEFAULT '".Role::PROJECT_VIEWER."'"); + + $rq = $pdo->prepare('SELECT * FROM project_has_users'); + $rq->execute(); + $rows = $rq->fetchAll(PDO::FETCH_ASSOC) ?: array(); + + $rq = $pdo->prepare('UPDATE project_has_users SET "role"=? WHERE "user_id"=?'); + + foreach ($rows as $row) { + $rq->execute(array( + $row['is_owner'] == 1 ? Role::PROJECT_MANAGER : Role::PROJECT_MEMBER, + $row['user_id'], + )); + } +} + +function version_89(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE groups ( + id INTEGER PRIMARY KEY, + external_id TEXT DEFAULT '', + name TEXT NOCASE NOT NULL UNIQUE + ) + "); + + $pdo->exec(" + CREATE TABLE group_has_users ( + group_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY(group_id) REFERENCES groups(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(group_id, user_id) + ) + "); +} + +function version_88(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE user_has_metadata ( + user_id INTEGER NOT NULL, + name TEXT NOT NULL, + value TEXT DEFAULT '', + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(user_id, name) + ) + "); + + $pdo->exec(" + CREATE TABLE project_has_metadata ( + project_id INTEGER NOT NULL, + name TEXT NOT NULL, + value TEXT DEFAULT '', + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, name) + ) + "); + + $pdo->exec(" + CREATE TABLE task_has_metadata ( + task_id INTEGER NOT NULL, + name TEXT NOT NULL, + value TEXT DEFAULT '', + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + UNIQUE(task_id, name) + ) + "); + + $pdo->exec("DROP TABLE project_integrations"); + + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_server'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_domain'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_username'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_password'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_nickname'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_jabber_room'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_api_url'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_id'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_hipchat_room_token'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_url'"); + $pdo->exec("DELETE FROM settings WHERE \"option\"='integration_slack_webhook_channel'"); +} + +function version_87(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_has_notification_types ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + notification_type TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, notification_type) + ) + "); +} + +function version_86(PDO $pdo) +{ + $pdo->exec("ALTER TABLE custom_filters ADD COLUMN append INTEGER DEFAULT 0"); +} + +function version_85(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE user_has_unread_notifications ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + event_data TEXT NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) + "); + + $pdo->exec(" + CREATE TABLE user_has_notification_types ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + notification_type TEXT, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX user_has_notification_types_user_idx ON user_has_notification_types(user_id, notification_type)'); + + // Migrate people who have notification enabled before + $rq = $pdo->prepare('SELECT id FROM users WHERE notifications_enabled=1'); + $rq->execute(); + $user_ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0); + + foreach ($user_ids as $user_id) { + $rq = $pdo->prepare('INSERT INTO user_has_notification_types (user_id, notification_type) VALUES (?, ?)'); + $rq->execute(array($user_id, 'email')); + } +} + +function version_84(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE custom_filters ( + id INTEGER PRIMARY KEY, + filter TEXT NOT NULL, + project_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + name TEXT NOT NULL, + is_shared INTEGER DEFAULT 0 + ) + "); +} + +function version_83(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE plugin_schema_versions ( + plugin TEXT NOT NULL PRIMARY KEY, + version INTEGER NOT NULL DEFAULT 0 + ) + "); +} + +function version_82(PDO $pdo) +{ + $pdo->exec("ALTER TABLE swimlanes ADD COLUMN description TEXT"); +} + +function version_81(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN gitlab_id INTEGER"); +} + +function version_80(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN start_date TEXT DEFAULT ''"); + $pdo->exec("ALTER TABLE projects ADD COLUMN end_date TEXT DEFAULT ''"); +} + +function version_79(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_project_admin INTEGER DEFAULT 0"); +} + +function version_78(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN nb_failed_login INTEGER DEFAULT 0"); + $pdo->exec("ALTER TABLE users ADD COLUMN lock_expiration_date INTEGER DEFAULT 0"); +} + +function version_77(PDO $pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('subtask_time_tracking', '1')"); + $pdo->exec("INSERT INTO settings VALUES ('cfd_include_closed_tasks', '1')"); +} + +function version_76(PDO $pdo) +{ + $pdo->exec("INSERT INTO settings VALUES ('default_color', 'yellow')"); +} + +function version_75(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_stats ( + id INTEGER PRIMARY KEY, + day TEXT NOT NULL, + project_id INTEGER NOT NULL, + avg_lead_time INTEGER NOT NULL DEFAULT 0, + avg_cycle_time INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_stats_idx ON project_daily_stats(day, project_id)'); + + $pdo->exec('ALTER TABLE project_daily_summaries RENAME TO project_daily_column_stats'); +} + +function version_74(PDO $pdo) +{ + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN slack_webhook_channel TEXT DEFAULT ''"); + $pdo->exec("INSERT INTO settings VALUES ('integration_slack_webhook_channel', '')"); +} + +function version_73(PDO $pdo) +{ + $pdo->exec("DELETE FROM settings WHERE option='subtask_time_tracking'"); +} + +function version_72(PDO $pdo) +{ + $pdo->exec( + 'ALTER TABLE comments RENAME TO comments_bak' + ); + + $pdo->exec( + 'CREATE TABLE comments ( + id INTEGER PRIMARY KEY, + task_id INTEGER NOT NULL, + user_id INTEGER DEFAULT 0, + date_creation INTEGER NOT NULL, + comment TEXT NOT NULL, + reference VARCHAR(50), + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + )' + ); + + $pdo->exec( + 'INSERT INTO comments SELECT * FROM comments_bak' + ); + + $pdo->exec( + 'DROP TABLE comments_bak' + ); +} + +function version_71(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN notifications_filter INTEGER DEFAULT 4"); +} + +function version_70(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('webhook_url', '')); + + $pdo->exec("DELETE FROM settings WHERE option='webhook_url_task_creation'"); + $pdo->exec("DELETE FROM settings WHERE option='webhook_url_task_modification'"); +} + +function version_69(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN token TEXT DEFAULT ''"); +} + +function version_68(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('calendar_user_subtasks_time_tracking', 0)); + $rq->execute(array('calendar_user_tasks', 'date_started')); + $rq->execute(array('calendar_project_tasks', 'date_started')); + + $pdo->exec("DELETE FROM settings WHERE option='subtask_forecast'"); +} + +function version_67(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_jabber', '0')); + $rq->execute(array('integration_jabber_server', '')); + $rq->execute(array('integration_jabber_domain', '')); + $rq->execute(array('integration_jabber_username', '')); + $rq->execute(array('integration_jabber_password', '')); + $rq->execute(array('integration_jabber_nickname', 'kanboard')); + $rq->execute(array('integration_jabber_room', '')); + + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber INTEGER DEFAULT '0'"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_server TEXT DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_domain TEXT DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_username TEXT DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_password TEXT DEFAULT ''"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_nickname TEXT DEFAULT 'kanboard'"); + $pdo->exec("ALTER TABLE project_integrations ADD COLUMN jabber_room TEXT DEFAULT ''"); +} + +function version_66(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_status INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_trigger INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_factor INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_timeframe INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_basedate INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_parent INTEGER'); + $pdo->exec('ALTER TABLE tasks ADD COLUMN recurrence_child INTEGER'); +} + +function version_65(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN identifier TEXT DEFAULT ''"); +} + +function version_64(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_integrations ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL UNIQUE, + hipchat INTEGER DEFAULT 0, + hipchat_api_url TEXT DEFAULT 'https://api.hipchat.com', + hipchat_room_id TEXT, + hipchat_room_token TEXT, + slack INTEGER DEFAULT 0, + slack_webhook_url TEXT, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); +} + +function version_63(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_daily_summaries ADD COLUMN score INTEGER NOT NULL DEFAULT 0'); +} + +function version_62(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_has_categories ADD COLUMN description TEXT'); +} + +function version_61(PDO $pdo) +{ + $pdo->exec('ALTER TABLE files ADD COLUMN "date" INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE files ADD COLUMN "user_id" INTEGER NOT NULL DEFAULT 0'); + $pdo->exec('ALTER TABLE files ADD COLUMN "size" INTEGER NOT NULL DEFAULT 0'); +} + +function version_60(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN twofactor_activated INTEGER DEFAULT 0'); + $pdo->exec('ALTER TABLE users ADD COLUMN twofactor_secret TEXT'); +} + +function version_59(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_gravatar', '0')); +} + +function version_58(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_hipchat', '0')); + $rq->execute(array('integration_hipchat_api_url', 'https://api.hipchat.com')); + $rq->execute(array('integration_hipchat_room_id', '')); + $rq->execute(array('integration_hipchat_room_token', '')); +} + +function version_57(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('integration_slack_webhook', '0')); + $rq->execute(array('integration_slack_webhook_url', '')); +} + +function version_56(PDO $pdo) +{ + $pdo->exec('CREATE TABLE currencies ("currency" TEXT NOT NULL UNIQUE, "rate" REAL DEFAULT 0)'); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_currency', 'USD')); +} + +function version_55(PDO $pdo) +{ + $pdo->exec('CREATE TABLE transitions ( + "id" INTEGER PRIMARY KEY, + "user_id" INTEGER NOT NULL, + "project_id" INTEGER NOT NULL, + "task_id" INTEGER NOT NULL, + "src_column_id" INTEGER NOT NULL, + "dst_column_id" INTEGER NOT NULL, + "date" INTEGER NOT NULL, + "time_spent" INTEGER DEFAULT 0, + FOREIGN KEY(src_column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(dst_column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + )'); + + $pdo->exec("CREATE INDEX transitions_task_index ON transitions(task_id)"); + $pdo->exec("CREATE INDEX transitions_project_index ON transitions(project_id)"); + $pdo->exec("CREATE INDEX transitions_user_index ON transitions(user_id)"); +} + +function version_54(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('subtask_forecast', '0')); +} + +function version_53(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_stylesheet', '')); +} + +function version_52(PDO $pdo) +{ + $pdo->exec("ALTER TABLE subtask_time_tracking ADD COLUMN time_spent REAL DEFAULT 0"); +} + +function version_48(PDO $pdo) +{ + $pdo->exec('ALTER TABLE subtasks ADD COLUMN position INTEGER DEFAULT 1'); + + // Migrate all subtasks position + + $task_id = 0; + $position = 1; + $urq = $pdo->prepare('UPDATE subtasks SET position=? WHERE id=?'); + + $rq = $pdo->prepare('SELECT * FROM subtasks ORDER BY task_id, id ASC'); + $rq->execute(); + + foreach ($rq->fetchAll(PDO::FETCH_ASSOC) as $subtask) { + if ($task_id != $subtask['task_id']) { + $position = 1; + $task_id = $subtask['task_id']; + } + + $urq->execute(array($position, $subtask['id'])); + $position++; + } +} + +function version_47(PDO $pdo) +{ + $pdo->exec('ALTER TABLE task_has_files RENAME TO files'); + $pdo->exec('ALTER TABLE task_has_subtasks RENAME TO subtasks'); +} + +function version_46(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN description TEXT'); +} + +function version_45(PDO $pdo) +{ + $pdo->exec("CREATE TABLE links ( + id INTEGER PRIMARY KEY, + label TEXT NOT NULL, + opposite_id INTEGER DEFAULT 0, + UNIQUE(label) + )"); + + $pdo->exec("CREATE TABLE task_has_links ( + id INTEGER PRIMARY KEY, + link_id INTEGER NOT NULL, + task_id INTEGER NOT NULL, + opposite_task_id INTEGER NOT NULL, + FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE + )"); + + $pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)"); + $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)"); + + $rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)'); + $rq->execute(array('relates to', 0)); + $rq->execute(array('blocks', 3)); + $rq->execute(array('is blocked by', 2)); + $rq->execute(array('duplicates', 5)); + $rq->execute(array('is duplicated by', 4)); + $rq->execute(array('is a child of', 7)); + $rq->execute(array('is a parent of', 6)); + $rq->execute(array('targets milestone', 9)); + $rq->execute(array('is a milestone of', 8)); + $rq->execute(array('fixes', 11)); + $rq->execute(array('is fixed by', 10)); +} + +function version_44(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INTEGER DEFAULT 0'); + + /* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0. + * We take max project_activities.date_creation where event_name in task.create','task.move.column + * since creation date is always less than task moves + */ + $pdo->exec("UPDATE tasks + SET date_moved = ( + SELECT md + FROM ( + SELECT task_id, max(date_creation) md + FROM project_activities + WHERE event_name IN ('task.create', 'task.move.column') + GROUP BY task_id + ) src + WHERE id = src.task_id + ) + WHERE (date_moved IS NULL OR date_moved = 0) AND id IN ( + SELECT task_id + FROM ( + SELECT task_id, max(date_creation) md + FROM project_activities + WHERE event_name IN ('task.create', 'task.move.column') + GROUP BY task_id + ) src + )"); + + // If there is no activities for some tasks use the date_creation + $pdo->exec("UPDATE tasks SET date_moved = date_creation WHERE date_moved IS NULL OR date_moved = 0"); +} + +function version_43(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN disable_login_form INTEGER DEFAULT 0'); +} + +function version_42(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('subtask_restriction', '0')); + $rq->execute(array('subtask_time_tracking', '0')); + + $pdo->exec(" + CREATE TABLE subtask_time_tracking ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + subtask_id INTEGER NOT NULL, + start INTEGER DEFAULT 0, + end INTEGER DEFAULT 0, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE + ) + "); +} + +function version_41(PDO $pdo) +{ + $pdo->exec('ALTER TABLE columns ADD COLUMN description TEXT'); +} + +function version_40(PDO $pdo) +{ + $pdo->exec('ALTER TABLE users ADD COLUMN timezone TEXT'); + $pdo->exec('ALTER TABLE users ADD COLUMN language TEXT'); +} + +function version_39(PDO $pdo) +{ + // Avoid some full table scans + $pdo->exec('CREATE INDEX users_admin_idx ON users(is_admin)'); + $pdo->exec('CREATE INDEX columns_project_idx ON columns(project_id)'); + $pdo->exec('CREATE INDEX tasks_project_idx ON tasks(project_id)'); + $pdo->exec('CREATE INDEX swimlanes_project_idx ON swimlanes(project_id)'); + $pdo->exec('CREATE INDEX categories_project_idx ON project_has_categories(project_id)'); + $pdo->exec('CREATE INDEX subtasks_task_idx ON task_has_subtasks(task_id)'); + $pdo->exec('CREATE INDEX files_task_idx ON task_has_files(task_id)'); + $pdo->exec('CREATE INDEX comments_task_idx ON comments(task_id)'); + + // Set the ownership for all private projects + $rq = $pdo->prepare('SELECT id FROM projects WHERE is_private=1'); + $rq->execute(); + $project_ids = $rq->fetchAll(PDO::FETCH_COLUMN, 0); + + $rq = $pdo->prepare('UPDATE project_has_users SET is_owner=1 WHERE project_id=?'); + + foreach ($project_ids as $project_id) { + $rq->execute(array($project_id)); + } +} + +function version_38(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('project_categories', '')); +} + +function version_37(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE swimlanes ( + id INTEGER PRIMARY KEY, + name TEXT NOT NULL, + position INTEGER DEFAULT 1, + is_active INTEGER DEFAULT 1, + project_id INTEGER NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE (name, project_id) + ) + "); + + $pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INTEGER DEFAULT 0'); + $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane TEXT DEFAULT 'Default swimlane'"); + $pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane INTEGER DEFAULT 1"); +} + +function version_36(PDO $pdo) +{ + $pdo->exec('ALTER TABLE project_has_users ADD COLUMN is_owner INTEGER DEFAULT "0"'); +} + +function version_35(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_daily_summaries ( + id INTEGER PRIMARY KEY, + day TEXT NOT NULL, + project_id INTEGER NOT NULL, + column_id INTEGER NOT NULL, + total INTEGER NOT NULL DEFAULT 0, + FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('CREATE UNIQUE INDEX project_daily_column_stats_idx ON project_daily_summaries(day, project_id, column_id)'); +} + +function version_34(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN is_everybody_allowed INTEGER DEFAULT "0"'); +} + +function version_33(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_activities ( + id INTEGER PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + creator_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + task_id INTEGER NOT NULL, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ) + "); + + $pdo->exec('DROP TABLE task_has_events'); + $pdo->exec('DROP TABLE comment_has_events'); + $pdo->exec('DROP TABLE subtask_has_events'); +} + +function version_32(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN date_started INTEGER"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN time_spent NUMERIC DEFAULT 0"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN time_estimated NUMERIC DEFAULT 0"); +} + +function version_31(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN is_private INTEGER DEFAULT "0"'); +} + +function version_30(PDO $pdo) +{ + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('application_date_format', 'm/d/Y')); +} + +function version_29(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE settings ( + option TEXT PRIMARY KEY, + value TEXT DEFAULT '' + ) + "); + + // Migrate old config parameters + $rq = $pdo->prepare('SELECT * FROM config'); + $rq->execute(); + $parameters = $rq->fetch(PDO::FETCH_ASSOC); + + $rq = $pdo->prepare('INSERT INTO settings VALUES (?, ?)'); + $rq->execute(array('board_highlight_period', defined('RECENT_TASK_PERIOD') ? RECENT_TASK_PERIOD : 48*60*60)); + $rq->execute(array('board_public_refresh_interval', defined('BOARD_PUBLIC_CHECK_INTERVAL') ? BOARD_PUBLIC_CHECK_INTERVAL : 60)); + $rq->execute(array('board_private_refresh_interval', defined('BOARD_CHECK_INTERVAL') ? BOARD_CHECK_INTERVAL : 10)); + $rq->execute(array('board_columns', $parameters['default_columns'])); + $rq->execute(array('webhook_url_task_creation', $parameters['webhooks_url_task_creation'])); + $rq->execute(array('webhook_url_task_modification', $parameters['webhooks_url_task_modification'])); + $rq->execute(array('webhook_token', $parameters['webhooks_token'])); + $rq->execute(array('api_token', $parameters['api_token'])); + $rq->execute(array('application_language', $parameters['language'])); + $rq->execute(array('application_timezone', $parameters['timezone'])); + $rq->execute(array('application_url', defined('KANBOARD_URL') ? KANBOARD_URL : '')); + + $pdo->exec('DROP TABLE config'); +} + +function version_28(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN reference TEXT DEFAULT ''"); + $pdo->exec("ALTER TABLE comments ADD COLUMN reference TEXT DEFAULT ''"); + + $pdo->exec('CREATE INDEX tasks_reference_idx ON tasks(reference)'); + $pdo->exec('CREATE INDEX comments_reference_idx ON comments(reference)'); +} + +function version_27(PDO $pdo) +{ + $pdo->exec('CREATE UNIQUE INDEX users_username_idx ON users(username)'); +} + +function version_26(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN default_columns TEXT DEFAULT ''"); +} + +function version_25(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE task_has_events ( + id INTEGER PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + creator_id INTEGER, + project_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + "); + + $pdo->exec(" + CREATE TABLE subtask_has_events ( + id INTEGER PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + creator_id INTEGER, + project_id INTEGER, + subtask_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(subtask_id) REFERENCES task_has_subtasks(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + "); + + $pdo->exec(" + CREATE TABLE comment_has_events ( + id INTEGER PRIMARY KEY, + date_creation INTEGER NOT NULL, + event_name TEXT NOT NULL, + creator_id INTEGER, + project_id INTEGER, + comment_id INTEGER, + task_id INTEGER, + data TEXT, + FOREIGN KEY(creator_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(comment_id) REFERENCES comments(id) ON DELETE CASCADE, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + ); + "); +} + +function version_24(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN is_public INTEGER DEFAULT "0"'); +} + +function version_23(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN notifications_enabled INTEGER DEFAULT '0'"); + + $pdo->exec(" + CREATE TABLE user_has_notifications ( + user_id INTEGER NOT NULL, + project_id INTEGER NOT NULL, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE(project_id, user_id) + ); + "); +} + +function version_22(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_modification TEXT"); + $pdo->exec("ALTER TABLE config ADD COLUMN webhooks_url_task_creation TEXT"); +} + +function version_21(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN creator_id INTEGER DEFAULT '0'"); + $pdo->exec("ALTER TABLE tasks ADD COLUMN date_modification INTEGER DEFAULT '0'"); +} + +function version_20(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN github_id TEXT"); +} + +function version_19(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN api_token TEXT DEFAULT ''"); + $pdo->exec("UPDATE config SET api_token='".Token::getToken()."'"); +} + +function version_18(PDO $pdo) +{ + $pdo->exec( + " + CREATE TABLE task_has_subtasks ( + id INTEGER PRIMARY KEY, + title TEXT COLLATE NOCASE NOT NULL, + status INTEGER DEFAULT 0, + time_estimated NUMERIC DEFAULT 0, + time_spent NUMERIC DEFAULT 0, + task_id INTEGER NOT NULL, + user_id INTEGER, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + )" + ); +} + +function version_17(PDO $pdo) +{ + $pdo->exec( + " + CREATE TABLE task_has_files ( + id INTEGER PRIMARY KEY, + name TEXT COLLATE NOCASE NOT NULL, + path TEXT, + is_image INTEGER DEFAULT 0, + task_id INTEGER NOT NULL, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE + )" + ); +} + +function version_16(PDO $pdo) +{ + $pdo->exec( + " + CREATE TABLE project_has_categories ( + id INTEGER PRIMARY KEY, + name TEXT COLLATE NOCASE NOT NULL, + project_id INTEGER NOT NULL, + UNIQUE (project_id, name), + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )" + ); + + $pdo->exec("ALTER TABLE tasks ADD COLUMN category_id INTEGER DEFAULT 0"); +} + +function version_15(PDO $pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN last_modified INTEGER DEFAULT 0"); +} + +function version_14(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN name TEXT"); + $pdo->exec("ALTER TABLE users ADD COLUMN email TEXT"); + $pdo->exec("ALTER TABLE users ADD COLUMN google_id TEXT"); +} + +function version_13(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_ldap_user INTEGER DEFAULT 0"); +} + +function version_12(PDO $pdo) +{ + $pdo->exec( + 'CREATE TABLE remember_me ( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + ip TEXT, + user_agent TEXT, + token TEXT, + sequence TEXT, + expiration INTEGER, + date_creation INTEGER, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + )' + ); + + $pdo->exec( + 'CREATE TABLE last_logins ( + id INTEGER PRIMARY KEY, + auth_type TEXT, + user_id INTEGER NOT NULL, + ip TEXT, + user_agent TEXT, + date_creation INTEGER, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + )' + ); + + $pdo->exec('CREATE INDEX last_logins_user_idx ON last_logins(user_id)'); +} + +function version_11(PDO $pdo) +{ + $pdo->exec( + 'ALTER TABLE comments RENAME TO comments_bak' + ); + + $pdo->exec( + 'CREATE TABLE comments ( + id INTEGER PRIMARY KEY, + task_id INTEGER, + user_id INTEGER, + date INTEGER, + comment TEXT, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE + )' + ); + + $pdo->exec( + 'INSERT INTO comments SELECT * FROM comments_bak' + ); + + $pdo->exec( + 'DROP TABLE comments_bak' + ); +} + +function version_10(PDO $pdo) +{ + $pdo->exec( + 'CREATE TABLE actions ( + id INTEGER PRIMARY KEY, + project_id INTEGER NOT NULL, + event_name TEXT NOT NULL, + action_name TEXT NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE + )' + ); + + $pdo->exec( + 'CREATE TABLE action_has_params ( + id INTEGER PRIMARY KEY, + action_id INTEGER NOT NULL, + name TEXT NOT NULL, + value TEXT NOT NULL, + FOREIGN KEY(action_id) REFERENCES actions(id) ON DELETE CASCADE + )' + ); +} + +function version_9(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN date_due INTEGER"); +} + +function version_8(PDO $pdo) +{ + $pdo->exec( + 'CREATE TABLE comments ( + id INTEGER PRIMARY KEY, + task_id INTEGER, + user_id INTEGER, + date INTEGER, + comment TEXT, + FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES tasks(id) ON DELETE CASCADE + )' + ); +} + +function version_7(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE project_has_users ( + project_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE(project_id, user_id) + ) + "); +} + +function version_6(PDO $pdo) +{ + $pdo->exec("ALTER TABLE columns ADD COLUMN task_limit INTEGER DEFAULT '0'"); +} + +function version_5(PDO $pdo) +{ + $pdo->exec("ALTER TABLE tasks ADD COLUMN score INTEGER"); +} + +function version_4(PDO $pdo) +{ + $pdo->exec("ALTER TABLE config ADD COLUMN timezone TEXT DEFAULT 'UTC'"); +} + +function version_3(PDO $pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN token TEXT'); +} + +function version_2(PDO $pdo) +{ + $pdo->exec('ALTER TABLE tasks ADD COLUMN date_completed INTEGER'); + $pdo->exec('UPDATE tasks SET date_completed=date_creation WHERE is_active=0'); +} + +function version_1(PDO $pdo) +{ + $pdo->exec(" + CREATE TABLE config ( + language TEXT DEFAULT 'en_US', + webhooks_token TEXT DEFAULT '' + ) + "); + + $pdo->exec(" + CREATE TABLE users ( + id INTEGER PRIMARY KEY, + username TEXT NOT NULL, + password TEXT, + is_admin INTEGER DEFAULT 0 + ) + "); + + $pdo->exec(" + CREATE TABLE projects ( + id INTEGER PRIMARY KEY, + name TEXT NOCASE NOT NULL, + is_active INTEGER DEFAULT 1 + ) + "); + + $pdo->exec(" + CREATE TABLE columns ( + id INTEGER PRIMARY KEY, + title TEXT NOT NULL, + position INTEGER, + project_id INTEGER NOT NULL, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + UNIQUE (title, project_id) + ) + "); + + $pdo->exec(" + CREATE TABLE tasks ( + id INTEGER PRIMARY KEY, + title TEXT NOCASE NOT NULL, + description TEXT, + date_creation INTEGER, + color_id TEXT, + project_id INTEGER, + column_id INTEGER, + owner_id INTEGER DEFAULT '0', + position INTEGER, + is_active INTEGER DEFAULT 1, + FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE, + FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE + ) + "); + + $pdo->exec(" + INSERT INTO users + (username, password, is_admin) + VALUES ('admin', '".\password_hash('admin', PASSWORD_BCRYPT)."', '1') + "); + + $pdo->exec(" + INSERT INTO config + (webhooks_token) + VALUES ('".Token::getToken()."') + "); +} diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php new file mode 100644 index 0000000..5fb6abf --- /dev/null +++ b/app/ServiceProvider/ActionProvider.php @@ -0,0 +1,128 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Action\TaskAssignCategorySwimlaneChange; +use Kanboard\Action\TaskAssignColorOnDueDate; +use Kanboard\Action\TaskAssignColorOnStartDate; +use Kanboard\Action\TaskAssignColorPriority; +use Kanboard\Action\TaskAssignDueDateOnCreation; +use Kanboard\Action\TaskMoveColumnClosed; +use Kanboard\Action\TaskMoveColumnNotMovedPeriod; +use Kanboard\Action\TaskMoveColumnOnDueDate; +use Kanboard\Core\Action\ActionManager; +use Kanboard\Action\CommentCreation; +use Kanboard\Action\CommentCreationMoveTaskColumn; +use Kanboard\Action\TaskAssignCategoryColor; +use Kanboard\Action\TaskAssignCategoryLabel; +use Kanboard\Action\TaskAssignCategoryLink; +use Kanboard\Action\TaskAssignColorCategory; +use Kanboard\Action\TaskAssignColorColumn; +use Kanboard\Action\TaskAssignColorLink; +use Kanboard\Action\TaskAssignColorUser; +use Kanboard\Action\TaskAssignCreator; +use Kanboard\Action\TaskAssignCurrentUser; +use Kanboard\Action\TaskAssignCurrentUserColumn; +use Kanboard\Action\TaskAssignSpecificUser; +use Kanboard\Action\TaskAssignUser; +use Kanboard\Action\TaskAssignUserSwimlaneChange; +use Kanboard\Action\TaskClose; +use Kanboard\Action\TaskCloseColumn; +use Kanboard\Action\TaskCreation; +use Kanboard\Action\TaskDuplicateAnotherProject; +use Kanboard\Action\TaskEmail; +use Kanboard\Action\TaskEmailNoActivity; +use Kanboard\Action\TaskMoveAnotherProject; +use Kanboard\Action\TaskMoveColumnAssigned; +use Kanboard\Action\TaskMoveSwimlaneAssigned; +use Kanboard\Action\TaskMoveColumnCategoryChange; +use Kanboard\Action\TaskMoveColumnUnAssigned; +use Kanboard\Action\TaskMoveSwimlaneCategoryChange; +use Kanboard\Action\TaskOpen; +use Kanboard\Action\TaskUpdateStartDate; +use Kanboard\Action\TaskUpdateStartDateOnMoveColumn; +use Kanboard\Action\TaskCloseNoActivity; +use Kanboard\Action\TaskCloseNoActivityColumn; +use Kanboard\Action\TaskCloseNotMovedColumn; +use Kanboard\Action\TaskAssignColorSwimlane; +use Kanboard\Action\TaskAssignPrioritySwimlane; +use Kanboard\Action\SubtaskTimerMoveTaskColumn; +use Kanboard\Action\StopSubtaskTimerMoveTaskColumn; +use Kanboard\Action\TaskMoveColumnOnStartDate; +use Kanboard\Action\TaskAssignDueDateOnMoveColumn; +use Kanboard\Action\TaskAssignToUserOnCreationInColumn; +use Kanboard\Action\TaskAssignCurrentUserColumnIfNoUserAlreadySet; + +/** + * Action Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class ActionProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['actionManager'] = new ActionManager($container); + $container['actionManager']->register(new CommentCreation($container)); + $container['actionManager']->register(new CommentCreationMoveTaskColumn($container)); + $container['actionManager']->register(new TaskAssignCategorySwimlaneChange($container)); + $container['actionManager']->register(new TaskAssignCategoryColor($container)); + $container['actionManager']->register(new TaskAssignCategoryLabel($container)); + $container['actionManager']->register(new TaskAssignCategoryLink($container)); + $container['actionManager']->register(new TaskAssignColorCategory($container)); + $container['actionManager']->register(new TaskAssignColorColumn($container)); + $container['actionManager']->register(new TaskAssignColorLink($container)); + $container['actionManager']->register(new TaskAssignColorUser($container)); + $container['actionManager']->register(new TaskAssignColorPriority($container)); + $container['actionManager']->register(new TaskAssignCreator($container)); + $container['actionManager']->register(new TaskAssignCurrentUser($container)); + $container['actionManager']->register(new TaskAssignCurrentUserColumn($container)); + $container['actionManager']->register(new TaskAssignSpecificUser($container)); + $container['actionManager']->register(new TaskAssignUser($container)); + $container['actionManager']->register(new TaskAssignUserSwimlaneChange($container)); + $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)); + $container['actionManager']->register(new TaskEmailNoActivity($container)); + $container['actionManager']->register(new TaskMoveAnotherProject($container)); + $container['actionManager']->register(new TaskMoveColumnAssigned($container)); + $container['actionManager']->register(new TaskMoveSwimlaneAssigned($container)); + $container['actionManager']->register(new TaskMoveColumnCategoryChange($container)); + $container['actionManager']->register(new TaskMoveColumnClosed($container)); + $container['actionManager']->register(new TaskMoveColumnNotMovedPeriod($container)); + $container['actionManager']->register(new TaskMoveColumnOnDueDate($container)); + $container['actionManager']->register(new TaskMoveColumnUnAssigned($container)); + $container['actionManager']->register(new TaskMoveSwimlaneCategoryChange($container)); + $container['actionManager']->register(new TaskOpen($container)); + $container['actionManager']->register(new TaskUpdateStartDate($container)); + $container['actionManager']->register(new TaskUpdateStartDateOnMoveColumn($container)); + $container['actionManager']->register(new TaskAssignDueDateOnCreation($container)); + $container['actionManager']->register(new TaskAssignColorSwimlane($container)); + $container['actionManager']->register(new TaskAssignPrioritySwimlane($container)); + $container['actionManager']->register(new TaskAssignColorOnDueDate($container)); + $container['actionManager']->register(new SubtaskTimerMoveTaskColumn($container)); + $container['actionManager']->register(new StopSubtaskTimerMoveTaskColumn($container)); + $container['actionManager']->register(new TaskMoveColumnOnStartDate($container)); + $container['actionManager']->register(new TaskAssignColorOnStartDate($container)); + $container['actionManager']->register(new TaskAssignDueDateOnMoveColumn($container)); + $container['actionManager']->register(new TaskAssignToUserOnCreationInColumn($container)); + $container['actionManager']->register(new TaskAssignCurrentUserColumnIfNoUserAlreadySet($container)); + + return $container; + } +} diff --git a/app/ServiceProvider/ApiProvider.php b/app/ServiceProvider/ApiProvider.php new file mode 100644 index 0000000..1bcb580 --- /dev/null +++ b/app/ServiceProvider/ApiProvider.php @@ -0,0 +1,89 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use JsonRPC\Server; +use Kanboard\Api\Procedure\ActionProcedure; +use Kanboard\Api\Procedure\AppProcedure; +use Kanboard\Api\Procedure\BoardProcedure; +use Kanboard\Api\Procedure\CategoryProcedure; +use Kanboard\Api\Procedure\ColumnProcedure; +use Kanboard\Api\Procedure\CommentProcedure; +use Kanboard\Api\Procedure\ProjectFileProcedure; +use Kanboard\Api\Procedure\ProjectMetadataProcedure; +use Kanboard\Api\Procedure\TagProcedure; +use Kanboard\Api\Procedure\TaskExternalLinkProcedure; +use Kanboard\Api\Procedure\TaskFileProcedure; +use Kanboard\Api\Procedure\GroupProcedure; +use Kanboard\Api\Procedure\GroupMemberProcedure; +use Kanboard\Api\Procedure\LinkProcedure; +use Kanboard\Api\Procedure\MeProcedure; +use Kanboard\Api\Middleware\AuthenticationMiddleware; +use Kanboard\Api\Procedure\ProjectProcedure; +use Kanboard\Api\Procedure\ProjectPermissionProcedure; +use Kanboard\Api\Procedure\SubtaskProcedure; +use Kanboard\Api\Procedure\SubtaskTimeTrackingProcedure; +use Kanboard\Api\Procedure\SwimlaneProcedure; +use Kanboard\Api\Procedure\TaskMetadataProcedure; +use Kanboard\Api\Procedure\TaskProcedure; +use Kanboard\Api\Procedure\TaskLinkProcedure; +use Kanboard\Api\Procedure\TaskTagProcedure; +use Kanboard\Api\Procedure\UserProcedure; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * Class ApiProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class ApiProvider implements ServiceProviderInterface +{ + /** + * Registers services on the given container. + * + * @param Container $container + * @return Container + */ + public function register(Container $container) + { + $server = new Server(); + $server->setAuthenticationHeader(API_AUTHENTICATION_HEADER); + $server->getMiddlewareHandler() + ->withMiddleware(new AuthenticationMiddleware($container)) + ; + + $server->getProcedureHandler() + ->withObject(new MeProcedure($container)) + ->withObject(new ActionProcedure($container)) + ->withObject(new AppProcedure($container)) + ->withObject(new BoardProcedure($container)) + ->withObject(new ColumnProcedure($container)) + ->withObject(new CategoryProcedure($container)) + ->withObject(new CommentProcedure($container)) + ->withObject(new TaskFileProcedure($container)) + ->withObject(new ProjectFileProcedure($container)) + ->withObject(new LinkProcedure($container)) + ->withObject(new ProjectProcedure($container)) + ->withObject(new ProjectPermissionProcedure($container)) + ->withObject(new ProjectMetadataProcedure($container)) + ->withObject(new SubtaskProcedure($container)) + ->withObject(new SubtaskTimeTrackingProcedure($container)) + ->withObject(new SwimlaneProcedure($container)) + ->withObject(new TaskProcedure($container)) + ->withObject(new TaskLinkProcedure($container)) + ->withObject(new TaskExternalLinkProcedure($container)) + ->withObject(new TaskMetadataProcedure($container)) + ->withObject(new TaskTagProcedure($container)) + ->withObject(new UserProcedure($container)) + ->withObject(new GroupProcedure($container)) + ->withObject(new GroupMemberProcedure($container)) + ->withObject(new TagProcedure($container)) + ->withBeforeMethod('beforeProcedure') + ; + + $container['api'] = $server; + return $container; + } +} diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php new file mode 100644 index 0000000..9824f79 --- /dev/null +++ b/app/ServiceProvider/AuthenticationProvider.php @@ -0,0 +1,239 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\Security\AuthenticationManager; +use Kanboard\Core\Security\AccessMap; +use Kanboard\Core\Security\Authorization; +use Kanboard\Core\Security\Role; +use Kanboard\Auth\ApiAccessTokenAuth; +use Kanboard\Auth\RememberMeAuth; +use Kanboard\Auth\DatabaseAuth; +use Kanboard\Auth\LdapAuth; +use Kanboard\Auth\TotpAuth; +use Kanboard\Auth\ReverseProxyAuth; + +/** + * Authentication Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class AuthenticationProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['authenticationManager'] = new AuthenticationManager($container); + $container['authenticationManager']->register(new TotpAuth($container)); + + if (REMEMBER_ME_AUTH) { + $container['authenticationManager']->register(new RememberMeAuth($container)); + } + + $container['authenticationManager']->register(new DatabaseAuth($container)); + + if (REVERSE_PROXY_AUTH && ! empty(TRUSTED_PROXY_NETWORKS)) { + $container['authenticationManager']->register(new ReverseProxyAuth($container)); + } + + $container['authenticationManager']->register(new ApiAccessTokenAuth($container)); + + if (LDAP_AUTH) { + $container['authenticationManager']->register(new LdapAuth($container)); + } + + $container['projectAccessMap'] = $this->getProjectAccessMap(); + $container['applicationAccessMap'] = $this->getApplicationAccessMap(); + $container['apiAccessMap'] = $this->getApiAccessMap(); + $container['apiProjectAccessMap'] = $this->getApiProjectAccessMap(); + + $container['projectAuthorization'] = new Authorization($container['projectAccessMap']); + $container['applicationAuthorization'] = new Authorization($container['applicationAccessMap']); + $container['apiAuthorization'] = new Authorization($container['apiAccessMap']); + $container['apiProjectAuthorization'] = new Authorization($container['apiProjectAccessMap']); + + return $container; + } + + /** + * Get ACL for projects + * + * @access public + * @return AccessMap + */ + public function getProjectAccessMap() + { + $acl = new AccessMap; + $acl->setDefaultRole(Role::PROJECT_VIEWER); + $acl->setRoleHierarchy(Role::PROJECT_MANAGER, array(Role::PROJECT_MEMBER, Role::PROJECT_VIEWER)); + $acl->setRoleHierarchy(Role::PROJECT_MEMBER, array(Role::PROJECT_VIEWER)); + + $acl->add('ActionController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectActionDuplicationController', '*', Role::PROJECT_MANAGER); + $acl->add('ActionCreationController', '*', Role::PROJECT_MANAGER); + $acl->add('AnalyticController', '*', Role::PROJECT_MANAGER); + $acl->add('BoardAjaxController', 'save', Role::PROJECT_MEMBER); + $acl->add('BoardPopoverController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskPopoverController', '*', Role::PROJECT_MEMBER); + $acl->add('CalendarController', 'save', Role::PROJECT_MEMBER); + $acl->add('CategoryController', '*', Role::PROJECT_MANAGER); + $acl->add('ColumnController', '*', Role::PROJECT_MANAGER); + $acl->add('ColumnMoveRestrictionController', '*', Role::PROJECT_MANAGER); + $acl->add('ColumnRestrictionController', '*', Role::PROJECT_MANAGER); + $acl->add('CommentController', array('create', 'save', 'edit', 'update', 'confirm', 'remove'), Role::PROJECT_MEMBER); + $acl->add('CommentListController', array('save'), Role::PROJECT_MEMBER); + $acl->add('CommentMailController', '*', Role::PROJECT_MEMBER); + $acl->add('CustomFilterController', '*', Role::PROJECT_MEMBER); + $acl->add('ExportController', '*', Role::PROJECT_MANAGER); + $acl->add('ExternalTaskCreationController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskFileController', array('screenshot', 'create', 'save', 'remove', 'confirm'), Role::PROJECT_MEMBER); + $acl->add('ProjectViewController', array('share', 'updateSharing', 'integrations', 'updateIntegrations', 'notifications', 'updateNotifications', 'duplicate', 'doDuplication', 'importTasks', 'doTasksImport'), Role::PROJECT_MANAGER); + $acl->add('ProjectPermissionController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectEditController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectPredefinedContentController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectRoleController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectRoleRestrictionController', '*', Role::PROJECT_MANAGER); + $acl->add('PredefinedTaskDescriptionController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectFileController', '*', Role::PROJECT_MEMBER); + $acl->add('ProjectUserOverviewController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectStatusController', '*', Role::PROJECT_MANAGER); + $acl->add('ProjectTagController', '*', Role::PROJECT_MANAGER); + $acl->add('SubtaskController', '*', Role::PROJECT_MEMBER); + $acl->add('SubtaskConverterController', '*', Role::PROJECT_MEMBER); + $acl->add('SubtaskRestrictionController', '*', Role::PROJECT_MEMBER); + $acl->add('SubtaskStatusController', '*', Role::PROJECT_MEMBER); + $acl->add('SwimlaneController', '*', Role::PROJECT_MANAGER); + $acl->add('TaskSuppressionController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskCreationController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskBulkController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskBulkMoveColumnController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskBulkChangePropertyController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskDuplicationController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskRecurrenceController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskImportController', '*', Role::PROJECT_MANAGER); + $acl->add('TaskInternalLinkController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskExternalLinkController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskModificationController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskMovePositionController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskReorderController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskStatusController', '*', Role::PROJECT_MEMBER); + $acl->add('TaskMailController', '*', Role::PROJECT_MEMBER); + $acl->add('UserAjaxController', array('mention'), Role::PROJECT_MEMBER); + + return $acl; + } + + /** + * Get ACL for the application + * + * @access public + * @return AccessMap + */ + public function getApplicationAccessMap() + { + $acl = new AccessMap; + $acl->setDefaultRole(Role::APP_USER); + $acl->setRoleHierarchy(Role::APP_ADMIN, array(Role::APP_MANAGER, Role::APP_USER, Role::APP_PUBLIC)); + $acl->setRoleHierarchy(Role::APP_MANAGER, array(Role::APP_USER, Role::APP_PUBLIC)); + $acl->setRoleHierarchy(Role::APP_USER, array(Role::APP_PUBLIC)); + + $acl->add('AuthController', array('login', 'check'), Role::APP_PUBLIC); + $acl->add('CaptchaController', '*', Role::APP_PUBLIC); + $acl->add('PasswordResetController', '*', Role::APP_PUBLIC); + $acl->add('TaskViewController', 'readonly', Role::APP_PUBLIC); + $acl->add('BoardViewController', 'readonly', Role::APP_PUBLIC); + $acl->add('ICalendarController', '*', Role::APP_PUBLIC); + $acl->add('FeedController', '*', Role::APP_PUBLIC); + $acl->add('AvatarFileController', array('show', 'image'), Role::APP_PUBLIC); + $acl->add('UserInviteController', array('signup', 'register'), Role::APP_PUBLIC); + $acl->add('CronjobController', array('run'), Role::APP_PUBLIC); + + $acl->add('ConfigController', '*', Role::APP_ADMIN); + $acl->add('TagController', '*', Role::APP_ADMIN); + $acl->add('PluginController', '*', Role::APP_ADMIN); + $acl->add('CurrencyController', '*', Role::APP_ADMIN); + $acl->add('GroupListController', '*', Role::APP_ADMIN); + $acl->add('GroupCreationController', '*', Role::APP_ADMIN); + $acl->add('GroupModificationController', '*', Role::APP_ADMIN); + $acl->add('LinkController', '*', Role::APP_ADMIN); + $acl->add('ProjectCreationController', 'create', Role::APP_MANAGER); + $acl->add('ProjectUserOverviewController', '*', Role::APP_MANAGER); + $acl->add('TwoFactorController', 'disable', Role::APP_ADMIN); + $acl->add('UserImportController', '*', Role::APP_ADMIN); + $acl->add('UserCreationController', '*', Role::APP_ADMIN); + $acl->add('UserListController', '*', Role::APP_ADMIN); + $acl->add('UserStatusController', '*', Role::APP_ADMIN); + $acl->add('UserCredentialController', array('changeAuthentication', 'saveAuthentication', 'unlock'), Role::APP_ADMIN); + $acl->add('UserInviteController', array('show', 'save'), Role::APP_ADMIN); + + return $acl; + } + + /** + * Get ACL for the API + * + * @access public + * @return AccessMap + */ + public function getApiAccessMap() + { + $acl = new AccessMap; + $acl->setDefaultRole(Role::APP_USER); + $acl->setRoleHierarchy(Role::APP_ADMIN, array(Role::APP_MANAGER, Role::APP_USER, Role::APP_PUBLIC)); + $acl->setRoleHierarchy(Role::APP_MANAGER, array(Role::APP_USER, Role::APP_PUBLIC)); + + $acl->add('UserProcedure', '*', Role::APP_ADMIN); + $acl->add('GroupMemberProcedure', '*', Role::APP_ADMIN); + $acl->add('GroupProcedure', '*', Role::APP_ADMIN); + $acl->add('LinkProcedure', '*', Role::APP_ADMIN); + $acl->add('TaskProcedure', array('getOverdueTasks'), Role::APP_ADMIN); + $acl->add('ProjectProcedure', array('getAllProjects'), Role::APP_ADMIN); + $acl->add('ProjectProcedure', array('createProject'), Role::APP_MANAGER); + + return $acl; + } + + /** + * Get ACL for the API + * + * @access public + * @return AccessMap + */ + public function getApiProjectAccessMap() + { + $acl = new AccessMap; + $acl->setDefaultRole(Role::PROJECT_VIEWER); + $acl->setRoleHierarchy(Role::PROJECT_MANAGER, array(Role::PROJECT_MEMBER, Role::PROJECT_VIEWER)); + $acl->setRoleHierarchy(Role::PROJECT_MEMBER, array(Role::PROJECT_VIEWER)); + + $acl->add('ActionProcedure', array('removeAction', 'getActions', 'createAction'), Role::PROJECT_MANAGER); + $acl->add('CategoryProcedure', 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', 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', 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', 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); + $acl->add('ProjectMetaDataProcedure', array('saveProjectMetadata', 'removeProjectMetadata'), Role::PROJECT_MEMBER); + $acl->add('TaskMetadataProcedure', array('saveTaskMetadata', 'removeTaskMetadata'), Role::PROJECT_MEMBER); + + return $acl; + } +} diff --git a/app/ServiceProvider/AvatarProvider.php b/app/ServiceProvider/AvatarProvider.php new file mode 100644 index 0000000..e03a047 --- /dev/null +++ b/app/ServiceProvider/AvatarProvider.php @@ -0,0 +1,33 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\User\Avatar\AvatarManager; +use Kanboard\User\Avatar\AvatarFileProvider; +use Kanboard\User\Avatar\LetterAvatarProvider; + +/** + * Avatar Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class AvatarProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['avatarManager'] = new AvatarManager; + $container['avatarManager']->register(new LetterAvatarProvider($container)); + $container['avatarManager']->register(new AvatarFileProvider($container)); + return $container; + } +} diff --git a/app/ServiceProvider/CacheProvider.php b/app/ServiceProvider/CacheProvider.php new file mode 100644 index 0000000..9d29638 --- /dev/null +++ b/app/ServiceProvider/CacheProvider.php @@ -0,0 +1,76 @@ +<?php + +namespace Kanboard\ServiceProvider; + +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(); + }; + + $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 new file mode 100644 index 0000000..7bcda1a --- /dev/null +++ b/app/ServiceProvider/ClassProvider.php @@ -0,0 +1,195 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\Paginator; +use Kanboard\Core\Http\OAuth2; +use Kanboard\Core\Tool; +use Kanboard\Core\Http\Client as HttpClient; + +/** + * Class ClassProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class ClassProvider implements ServiceProviderInterface +{ + private $classes = array( + 'Analytic' => array( + 'TaskDistributionAnalytic', + 'UserDistributionAnalytic', + 'EstimatedTimeComparisonAnalytic', + 'AverageLeadCycleTimeAnalytic', + 'AverageTimeSpentColumnAnalytic', + 'EstimatedActualColumnAnalytic', + ), + 'Model' => array( + 'ActionModel', + 'ActionParameterModel', + 'AvatarFileModel', + 'BoardModel', + 'CaptchaModel', + 'CategoryModel', + 'ColorModel', + 'ColumnModel', + 'ColumnRestrictionModel', + 'ColumnMoveRestrictionModel', + 'CommentModel', + 'ConfigModel', + 'CurrencyModel', + 'CustomFilterModel', + 'GroupModel', + 'GroupMemberModel', + 'InviteModel', + 'LanguageModel', + 'LastLoginModel', + 'LinkModel', + 'NotificationModel', + 'PasswordResetModel', + 'PredefinedTaskDescriptionModel', + 'ProjectModel', + 'ProjectFileModel', + 'ProjectActivityModel', + 'ProjectDuplicationModel', + 'ProjectDailyColumnStatsModel', + 'ProjectDailyStatsModel', + 'ProjectPermissionModel', + 'ProjectNotificationModel', + 'ProjectMetadataModel', + 'ProjectGroupRoleModel', + 'ProjectRoleModel', + 'ProjectRoleRestrictionModel', + 'ProjectTaskDuplicationModel', + 'ProjectTaskPriorityModel', + 'ProjectUserRoleModel', + 'RememberMeSessionModel', + 'SubtaskModel', + 'SubtaskPositionModel', + 'SubtaskStatusModel', + 'SubtaskTaskConversionModel', + 'SubtaskTimeTrackingModel', + 'SwimlaneModel', + 'TagDuplicationModel', + 'TagModel', + 'TaskModel', + 'TaskAnalyticModel', + 'TaskCreationModel', + 'TaskDuplicationModel', + 'TaskProjectDuplicationModel', + 'TaskProjectMoveModel', + 'TaskRecurrenceModel', + 'TaskExternalLinkModel', + 'TaskFinderModel', + 'TaskFileModel', + 'TaskLinkModel', + 'TaskModificationModel', + 'TaskPositionModel', + 'TaskReorderModel', + 'TaskStatusModel', + 'TaskTagModel', + 'TaskMetadataModel', + 'ThemeModel', + 'TimezoneModel', + 'TransitionModel', + 'UserModel', + 'UserLockingModel', + 'UserNotificationModel', + 'UserNotificationFilterModel', + 'UserUnreadNotificationModel', + 'UserMetadataModel', + ), + 'Validator' => array( + 'ActionValidator', + 'AuthValidator', + 'CategoryValidator', + 'ColumnMoveRestrictionValidator', + 'ColumnRestrictionValidator', + 'ColumnValidator', + 'CommentValidator', + 'ConfigValidator', + 'CurrencyValidator', + 'CustomFilterValidator', + 'ExternalLinkValidator', + 'GroupValidator', + 'LinkValidator', + 'PasswordResetValidator', + 'ProjectValidator', + 'ProjectRoleValidator', + 'SubtaskValidator', + 'SwimlaneValidator', + 'TagValidator', + 'TaskLinkValidator', + 'TaskValidator', + 'UserValidator', + 'PredefinedTaskDescriptionValidator', + ), + 'Import' => array( + 'UserImport', + ), + 'Export' => array( + 'SubtaskExport', + 'TaskExport', + 'TransitionExport', + ), + 'Pagination' => array( + 'DashboardPagination', + 'ProjectPagination', + 'SubtaskPagination', + 'TaskPagination', + 'UserPagination', + ), + 'Core' => array( + 'DateParser', + 'Lexer', + ), + 'Core\Event' => array( + 'EventManager', + ), + 'Core\Http' => array( + 'Request', + 'Response', + 'RememberMeCookie', + ), + 'Core\Plugin' => array( + 'Hook', + ), + 'Core\Security' => array( + 'Token', + 'Role', + ), + 'Core\User' => array( + 'GroupSync', + 'UserSync', + 'UserSession', + 'UserProfile', + ) + ); + + public function register(Container $container) + { + Tool::buildDIC($container, $this->classes); + + $container['paginator'] = $container->factory(function ($c) { + return new Paginator($c); + }); + + $container['oauth'] = $container->factory(function ($c) { + return new OAuth2($c); + }); + + $container['httpClient'] = function ($c) { + return new HttpClient($c); + }; + + $container['cspRules'] = array( + 'default-src' => "'self'", + 'style-src' => "'self' 'unsafe-inline'", + 'img-src' => '* data:', + ); + + return $container; + } +} diff --git a/app/ServiceProvider/CommandProvider.php b/app/ServiceProvider/CommandProvider.php new file mode 100644 index 0000000..f08f76c --- /dev/null +++ b/app/ServiceProvider/CommandProvider.php @@ -0,0 +1,78 @@ +<?php + +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; +use Kanboard\Console\PluginUninstallCommand; +use Kanboard\Console\PluginUpgradeCommand; +use Kanboard\Console\ProjectActivityArchiveCommand; +use Kanboard\Console\ProjectArchiveCommand; +use Kanboard\Console\ProjectDailyColumnStatsExportCommand; +use Kanboard\Console\ProjectDailyStatsCalculationCommand; +use Kanboard\Console\ResetPasswordCommand; +use Kanboard\Console\ResetTwoFactorCommand; +use Kanboard\Console\SubtaskExportCommand; +use Kanboard\Console\TaskExportCommand; +use Kanboard\Console\TaskOverdueNotificationCommand; +use Kanboard\Console\TaskTriggerCommand; +use Kanboard\Console\TransitionExportCommand; +use Kanboard\Console\VersionCommand; +use Kanboard\Console\WorkerCommand; +use Kanboard\Console\CssCommand; +use Kanboard\Console\JsCommand; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Symfony\Component\Console\Application; + +/** + * Class CommandProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class CommandProvider implements ServiceProviderInterface +{ + /** + * Registers services on the given container. + * + * @param Container $container + * @return Container + */ + public function register(Container $container) + { + $application = new Application('Kanboard', APP_VERSION); + $application->add(new TaskOverdueNotificationCommand($container)); + $application->add(new SubtaskExportCommand($container)); + $application->add(new TaskExportCommand($container)); + $application->add(new ProjectArchiveCommand($container)); + $application->add(new ProjectActivityArchiveCommand($container)); + $application->add(new ProjectDailyStatsCalculationCommand($container)); + $application->add(new ProjectDailyColumnStatsExportCommand($container)); + $application->add(new TransitionExportCommand($container)); + $application->add(new LocaleSyncCommand($container)); + $application->add(new LocaleComparatorCommand($container)); + $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)); + $application->add(new VersionCommand($container)); + $application->add(new CssCommand($container)); + $application->add(new JsCommand($container)); + + $container['cli'] = $application; + return $container; + } +} diff --git a/app/ServiceProvider/DatabaseProvider.php b/app/ServiceProvider/DatabaseProvider.php new file mode 100644 index 0000000..dfa5e06 --- /dev/null +++ b/app/ServiceProvider/DatabaseProvider.php @@ -0,0 +1,196 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use LogicException; +use RuntimeException; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use PicoDb\Database; + +/** + * Class DatabaseProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class DatabaseProvider implements ServiceProviderInterface +{ + /** + * Register provider + * + * @access public + * @param Container $container + * @return Container + */ + public function register(Container $container) + { + $container['db'] = $this->getInstance(); + + if (DB_RUN_MIGRATIONS) { + self::runMigrations($container['db']); + } + + if (DEBUG) { + $container['db']->getStatementHandler() + ->withLogging() + ->withStopWatch() + ; + } + + return $container; + } + + /** + * Setup the database driver + * + * @access public + * @return \PicoDb\Database + */ + public function getInstance() + { + switch (DB_DRIVER) { + case 'sqlite': + $db = $this->getSqliteInstance(); + break; + case 'mysql': + $db = $this->getMysqlInstance(); + break; + case 'postgres': + $db = $this->getPostgresInstance(); + break; + case 'dblib': + $db = $this->getMssqlInstance(); + break; + case 'mssql': + $db = $this->getMssqlInstance(); + break; + case 'odbc': + $db = $this->getMssqlInstance(); + break; + default: + throw new LogicException('Database driver not supported'); + } + + 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; + } + + /** + * Setup the Sqlite database driver + * + * @access private + * @return \PicoDb\Database + */ + private function getSqliteInstance() + { + require_once __DIR__.'/../Schema/Sqlite.php'; + + return new Database([ + 'driver' => 'sqlite', + 'filename' => DB_FILENAME, + 'wal_mode' => DB_WAL_MODE, + ]); + } + + /** + * Setup the Mysql database driver + * + * @access private + * @return \PicoDb\Database + */ + private function getMysqlInstance() + { + require_once __DIR__.'/../Schema/Mysql.php'; + + return new Database(array( + 'driver' => 'mysql', + 'hostname' => DB_HOSTNAME, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + 'charset' => 'utf8mb4', + 'port' => DB_PORT, + 'ssl_key' => DB_SSL_KEY, + 'ssl_ca' => DB_SSL_CA, + 'ssl_cert' => DB_SSL_CERT, + 'verify_server_cert' => DB_VERIFY_SERVER_CERT, + 'timeout' => DB_TIMEOUT, + )); + } + + /** + * Setup the Postgres database driver + * + * @access private + * @return \PicoDb\Database + */ + private function getPostgresInstance() + { + require_once __DIR__.'/../Schema/Postgres.php'; + + return new Database(array( + 'driver' => 'postgres', + 'hostname' => DB_HOSTNAME, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + 'port' => DB_PORT, + 'timeout' => DB_TIMEOUT, + )); + } + + /** + * Setup the MSSQL database driver + * + * @access private + * @return \PicoDb\Database + */ + private function getMssqlInstance() + { + require_once __DIR__.'/../Schema/Mssql.php'; + + return new Database(array( + 'driver' => DB_DRIVER, + 'hostname' => DB_HOSTNAME, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + 'port' => DB_PORT, + 'odbc-dsn' => DB_ODBC_DSN, + 'timeout' => DB_TIMEOUT, + 'appname' => 'Kanboard', + )); + } +} diff --git a/app/ServiceProvider/EventDispatcherProvider.php b/app/ServiceProvider/EventDispatcherProvider.php new file mode 100644 index 0000000..ebf42cb --- /dev/null +++ b/app/ServiceProvider/EventDispatcherProvider.php @@ -0,0 +1,42 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Subscriber\LdapUserPhotoSubscriber; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Symfony\Component\EventDispatcher\EventDispatcher; +use Kanboard\Subscriber\AuthSubscriber; +use Kanboard\Subscriber\BootstrapSubscriber; +use Kanboard\Subscriber\NotificationSubscriber; +use Kanboard\Subscriber\ProjectDailySummarySubscriber; +use Kanboard\Subscriber\ProjectModificationDateSubscriber; +use Kanboard\Subscriber\TransitionSubscriber; +use Kanboard\Subscriber\RecurringTaskSubscriber; + +/** + * Class EventDispatcherProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class EventDispatcherProvider implements ServiceProviderInterface +{ + public function register(Container $container) + { + $container['dispatcher'] = new EventDispatcher; + $container['dispatcher']->addSubscriber(new BootstrapSubscriber($container)); + $container['dispatcher']->addSubscriber(new AuthSubscriber($container)); + $container['dispatcher']->addSubscriber(new ProjectDailySummarySubscriber($container)); + $container['dispatcher']->addSubscriber(new ProjectModificationDateSubscriber($container)); + $container['dispatcher']->addSubscriber(new NotificationSubscriber($container)); + $container['dispatcher']->addSubscriber(new TransitionSubscriber($container)); + $container['dispatcher']->addSubscriber(new RecurringTaskSubscriber($container)); + + if (LDAP_AUTH && LDAP_USER_ATTRIBUTE_PHOTO !== '') { + $container['dispatcher']->addSubscriber(new LdapUserPhotoSubscriber($container)); + } + + return $container; + } +} diff --git a/app/ServiceProvider/ExternalLinkProvider.php b/app/ServiceProvider/ExternalLinkProvider.php new file mode 100644 index 0000000..2cec768 --- /dev/null +++ b/app/ServiceProvider/ExternalLinkProvider.php @@ -0,0 +1,36 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\ExternalLink\ExternalLinkManager; +use Kanboard\ExternalLink\WebLinkProvider; +use Kanboard\ExternalLink\AttachmentLinkProvider; +use Kanboard\ExternalLink\FileLinkProvider; + +/** + * External Link Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class ExternalLinkProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['externalLinkManager'] = new ExternalLinkManager($container); + $container['externalLinkManager']->register(new WebLinkProvider($container)); + $container['externalLinkManager']->register(new AttachmentLinkProvider($container)); + $container['externalLinkManager']->register(new FileLinkProvider($container)); + + return $container; + } +} diff --git a/app/ServiceProvider/ExternalTaskProvider.php b/app/ServiceProvider/ExternalTaskProvider.php new file mode 100644 index 0000000..52484ae --- /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 new file mode 100644 index 0000000..fb94097 --- /dev/null +++ b/app/ServiceProvider/FilterProvider.php @@ -0,0 +1,231 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\Filter\LexerBuilder; +use Kanboard\Core\Filter\QueryBuilder; +use Kanboard\Filter\ProjectActivityCreationDateFilter; +use Kanboard\Filter\ProjectActivityCreatorFilter; +use Kanboard\Filter\ProjectActivityProjectNameFilter; +use Kanboard\Filter\ProjectActivityTaskStatusFilter; +use Kanboard\Filter\ProjectActivityTaskTitleFilter; +use Kanboard\Filter\TaskAssigneeFilter; +use Kanboard\Filter\TaskCategoryFilter; +use Kanboard\Filter\TaskColorFilter; +use Kanboard\Filter\TaskColumnFilter; +use Kanboard\Filter\TaskCommentFilter; +use Kanboard\Filter\TaskCompletionDateFilter; +use Kanboard\Filter\TaskCompletionDateRangeFilter; +use Kanboard\Filter\TaskCreationDateFilter; +use Kanboard\Filter\TaskCreationDateRangeFilter; +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\TaskModificationDateRangeFilter; +use Kanboard\Filter\TaskMovedDateFilter; +use Kanboard\Filter\TaskMovedDateRangeFilter; +use Kanboard\Filter\TaskPriorityFilter; +use Kanboard\Filter\TaskProjectFilter; +use Kanboard\Filter\TaskReferenceFilter; +use Kanboard\Filter\TaskScoreFilter; +use Kanboard\Filter\TaskStatusFilter; +use Kanboard\Filter\TaskSubtaskAssigneeFilter; +use Kanboard\Filter\TaskSwimlaneFilter; +use Kanboard\Filter\TaskTagFilter; +use Kanboard\Filter\TaskTitleFilter; +use Kanboard\Model\ProjectModel; +use Kanboard\Model\ProjectGroupRoleModel; +use Kanboard\Model\ProjectUserRoleModel; +use Kanboard\Model\UserModel; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * Filter Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class FilterProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $this->createUserFilter($container); + $this->createProjectFilter($container); + $this->createTaskFilter($container); + return $container; + } + + public function createUserFilter(Container $container) + { + $container['userQuery'] = $container->factory(function ($c) { + $builder = new QueryBuilder(); + $builder->withQuery($c['db']->table(UserModel::TABLE)); + return $builder; + }); + + return $container; + } + + public function createProjectFilter(Container $container) + { + $container['projectGroupRoleQuery'] = $container->factory(function ($c) { + $builder = new QueryBuilder(); + $builder->withQuery($c['db']->table(ProjectGroupRoleModel::TABLE)); + return $builder; + }); + + $container['projectUserRoleQuery'] = $container->factory(function ($c) { + $builder = new QueryBuilder(); + $builder->withQuery($c['db']->table(ProjectUserRoleModel::TABLE)); + return $builder; + }); + + $container['projectQuery'] = $container->factory(function ($c) { + $builder = new QueryBuilder(); + $builder->withQuery($c['db']->table(ProjectModel::TABLE)); + return $builder; + }); + + $container['projectActivityLexer'] = $container->factory(function ($c) { + $builder = new LexerBuilder(); + $builder + ->withQuery($c['projectActivityModel']->getQuery()) + ->withFilter(new ProjectActivityTaskTitleFilter(), true) + ->withFilter(new ProjectActivityTaskStatusFilter()) + ->withFilter(new ProjectActivityProjectNameFilter()) + ->withFilter( + ProjectActivityCreationDateFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + ProjectActivityCreatorFilter::getInstance() + ->setCurrentUserId($c['userSession']->getId()) + ) + ; + + return $builder; + }); + + $container['projectActivityQuery'] = $container->factory(function ($c) { + $builder = new QueryBuilder(); + $builder->withQuery($c['projectActivityModel']->getQuery()); + + return $builder; + }); + + return $container; + } + + public function createTaskFilter(Container $container) + { + $container['taskQuery'] = $container->factory(function ($c) { + $builder = new QueryBuilder(); + $builder->withQuery($c['taskFinderModel']->getExtendedQuery()); + return $builder; + }); + + $container['taskLexer'] = $container->factory(function ($c) { + $builder = new LexerBuilder(); + + $builder + ->withQuery($c['taskFinderModel']->getExtendedQuery()) + ->withFilter( + TaskAssigneeFilter::getInstance() + ->setCurrentUserId($c['userSession']->getId()) + ) + ->withFilter(new TaskCategoryFilter()) + ->withFilter( + TaskColorFilter::getInstance() + ->setColorModel($c['colorModel']) + ) + ->withFilter(new TaskPriorityFilter()) + ->withFilter(new TaskColumnFilter()) + ->withFilter( + TaskCommentFilter::getInstance() + ->setDatabase($c['db']) + ) + ->withFilter( + TaskCreationDateFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + TaskCreationDateRangeFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + TaskCreatorFilter::getInstance() + ->setCurrentUserId($c['userSession']->getId()) + ) + ->withFilter(new TaskDescriptionFilter()) + ->withFilter( + TaskDueDateFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + TaskStartDateFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + TaskCompletionDateFilter::getInstance() + ->setDateparser($c['dateParser']) + ) + ->withFilter( + TaskCompletionDateRangeFilter::getInstance() + ->setDateparser($c['dateParser']) + ) + ->withFilter(new TaskIdFilter()) + ->withFilter( + TaskLinkFilter::getInstance() + ->setDatabase($c['db']) + ) + ->withFilter( + TaskModificationDateFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + TaskModificationDateRangeFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + TaskMovedDateFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter( + TaskMovedDateRangeFilter::getInstance() + ->setDateParser($c['dateParser']) + ) + ->withFilter(new TaskProjectFilter()) + ->withFilter(new TaskReferenceFilter()) + ->withFilter(new TaskScoreFilter()) + ->withFilter(new TaskStatusFilter()) + ->withFilter( + TaskSubtaskAssigneeFilter::getInstance() + ->setCurrentUserId($c['userSession']->getId()) + ->setDatabase($c['db']) + ) + ->withFilter(new TaskSwimlaneFilter()) + ->withFilter( + TaskTagFilter::getInstance() + ->setDatabase($c['db']) + ) + ->withFilter(new TaskTitleFilter(), true) + ; + + return $builder; + }); + + return $container; + } +} diff --git a/app/ServiceProvider/FormatterProvider.php b/app/ServiceProvider/FormatterProvider.php new file mode 100644 index 0000000..efc85d0 --- /dev/null +++ b/app/ServiceProvider/FormatterProvider.php @@ -0,0 +1,53 @@ +<?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', + 'ProjectApiFormatter', + 'ProjectsApiFormatter', + 'SubtaskListFormatter', + 'SubtaskTimeTrackingCalendarFormatter', + 'TaskApiFormatter', + 'TasksApiFormatter', + 'TaskAutoCompleteFormatter', + 'TaskICalFormatter', + 'TaskListFormatter', + 'TaskListSubtaskFormatter', + 'TaskListSubtaskAssigneeFormatter', + '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/GroupProvider.php b/app/ServiceProvider/GroupProvider.php new file mode 100644 index 0000000..86f5d11 --- /dev/null +++ b/app/ServiceProvider/GroupProvider.php @@ -0,0 +1,40 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\Group\GroupManager; +use Kanboard\Group\DatabaseBackendGroupProvider; +use Kanboard\Group\LdapBackendGroupProvider; + +/** + * Group Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class GroupProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['groupManager'] = new GroupManager(); + + if (DB_GROUP_PROVIDER) { + $container['groupManager']->register(new DatabaseBackendGroupProvider($container)); + } + + if (LDAP_AUTH && LDAP_GROUP_PROVIDER) { + $container['groupManager']->register(new LdapBackendGroupProvider($container)); + } + + return $container; + } +} diff --git a/app/ServiceProvider/HelperProvider.php b/app/ServiceProvider/HelperProvider.php new file mode 100644 index 0000000..f0f7ac0 --- /dev/null +++ b/app/ServiceProvider/HelperProvider.php @@ -0,0 +1,47 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\Helper; +use Kanboard\Core\Template; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * Class HelperProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class HelperProvider implements ServiceProviderInterface +{ + public function register(Container $container) + { + $container['helper'] = new Helper($container); + $container['helper']->register('app', '\Kanboard\Helper\AppHelper'); + $container['helper']->register('asset', '\Kanboard\Helper\AssetHelper'); + $container['helper']->register('board', '\Kanboard\Helper\BoardHelper'); + $container['helper']->register('comment', '\Kanboard\Helper\CommentHelper'); + $container['helper']->register('dt', '\Kanboard\Helper\DateHelper'); + $container['helper']->register('file', '\Kanboard\Helper\FileHelper'); + $container['helper']->register('form', '\Kanboard\Helper\FormHelper'); + $container['helper']->register('hook', '\Kanboard\Helper\HookHelper'); + $container['helper']->register('layout', '\Kanboard\Helper\LayoutHelper'); + $container['helper']->register('model', '\Kanboard\Helper\ModelHelper'); + $container['helper']->register('subtask', '\Kanboard\Helper\SubtaskHelper'); + $container['helper']->register('task', '\Kanboard\Helper\TaskHelper'); + $container['helper']->register('text', '\Kanboard\Helper\TextHelper'); + $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']); + + return $container; + } +} diff --git a/app/ServiceProvider/JobProvider.php b/app/ServiceProvider/JobProvider.php new file mode 100644 index 0000000..4e5e0f1 --- /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/LoggingProvider.php b/app/ServiceProvider/LoggingProvider.php new file mode 100644 index 0000000..2ff6ba4 --- /dev/null +++ b/app/ServiceProvider/LoggingProvider.php @@ -0,0 +1,57 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Psr\Log\LogLevel; +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\Log\Logger; +use Kanboard\Core\Log\Stderr; +use Kanboard\Core\Log\Stdout; +use Kanboard\Core\Log\Syslog; +use Kanboard\Core\Log\File; +use Kanboard\Core\Log\System; + +/** + * Class LoggingProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class LoggingProvider implements ServiceProviderInterface +{ + public function register(Container $container) + { + $logger = new Logger(); + $driver = null; + + switch (LOG_DRIVER) { + case 'syslog': + $driver = new Syslog('kanboard'); + break; + case 'stdout': + $driver = new Stdout(); + break; + case 'stderr': + $driver = new Stderr(); + break; + case 'file': + $driver = new File(LOG_FILE); + break; + case 'system': + $driver = new System(); + break; + } + + if ($driver !== null) { + if (! DEBUG) { + $driver->setLevel(LogLevel::INFO); + } + + $logger->setLogger($driver); + } + + $container['logger'] = $logger; + return $container; + } +} diff --git a/app/ServiceProvider/MailProvider.php b/app/ServiceProvider/MailProvider.php new file mode 100644 index 0000000..685709e --- /dev/null +++ b/app/ServiceProvider/MailProvider.php @@ -0,0 +1,34 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\Mail\Client as EmailClient; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * Mail Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class MailProvider implements ServiceProviderInterface +{ + /** + * Registers services on the given container. + * + * @param Container $container + */ + public function register(Container $container) + { + $container['emailClient'] = function ($container) { + $mailer = new EmailClient($container); + $mailer->setTransport('smtp', '\Kanboard\Core\Mail\Transport\Smtp'); + $mailer->setTransport('sendmail', '\Kanboard\Core\Mail\Transport\Sendmail'); + $mailer->setTransport('mail', '\Kanboard\Core\Mail\Transport\Mail'); + return $mailer; + }; + + return $container; + } +} diff --git a/app/ServiceProvider/NotificationProvider.php b/app/ServiceProvider/NotificationProvider.php new file mode 100644 index 0000000..a057120 --- /dev/null +++ b/app/ServiceProvider/NotificationProvider.php @@ -0,0 +1,45 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Model\UserNotificationTypeModel; +use Kanboard\Model\ProjectNotificationTypeModel; +use Kanboard\Notification\MailNotification as MailNotification; +use Kanboard\Notification\WebNotification as WebNotification; + +/** + * Notification Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class NotificationProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['userNotificationTypeModel'] = function ($container) { + $type = new UserNotificationTypeModel($container); + $type->setType(MailNotification::TYPE, t('Email'), '\Kanboard\Notification\MailNotification'); + $type->setType(WebNotification::TYPE, t('Web'), '\Kanboard\Notification\WebNotification'); + return $type; + }; + + $container['projectNotificationTypeModel'] = function ($container) { + $type = new ProjectNotificationTypeModel($container); + $type->setType('webhook', 'Webhook', '\Kanboard\Notification\WebhookNotification', true); + $type->setType('activity_stream', 'ActivityStream', '\Kanboard\Notification\ActivityStreamNotification', true); + return $type; + }; + + return $container; + } +} diff --git a/app/ServiceProvider/ObjectStorageProvider.php b/app/ServiceProvider/ObjectStorageProvider.php new file mode 100644 index 0000000..d9a79ca --- /dev/null +++ b/app/ServiceProvider/ObjectStorageProvider.php @@ -0,0 +1,51 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\ObjectStorage\FileStorage; +use LogicException; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * Class ObjectStorageProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class ObjectStorageProvider implements ServiceProviderInterface +{ + public function register(Container $container) + { + $container['objectStorage'] = function () { + if (file_exists(FILES_DIR)) { + if (! is_writable(FILES_DIR)) { + $stat = stat(FILES_DIR); + + throw new LogicException(sprintf( + 'The folder to store uploaded files is not writeable by your webserver user (file=%s; mode=%o; uid=%d; gid=%d)', + FILES_DIR, + $stat['mode'], + $stat['uid'], + $stat['gid'] + )); + } + } elseif (! @mkdir(FILES_DIR)) { + $folder = dirname(FILES_DIR); + $stat = stat($folder); + + throw new LogicException(sprintf( + 'Unable to create folder to store uploaded files, check the permissions of the parent directory (file=%s; mode=%o; uid=%d; gid=%d)', + $folder, + $stat['mode'], + $stat['uid'], + $stat['gid'] + )); + } + + return new FileStorage(FILES_DIR); + }; + + return $container; + } +} diff --git a/app/ServiceProvider/PluginProvider.php b/app/ServiceProvider/PluginProvider.php new file mode 100644 index 0000000..4cf5725 --- /dev/null +++ b/app/ServiceProvider/PluginProvider.php @@ -0,0 +1,31 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\Plugin\Loader; + +/** + * Plugin Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class PluginProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['pluginLoader'] = new Loader($container); + $container['pluginLoader']->scan(); + + return $container; + } +} diff --git a/app/ServiceProvider/QueueProvider.php b/app/ServiceProvider/QueueProvider.php new file mode 100644 index 0000000..570f2e7 --- /dev/null +++ b/app/ServiceProvider/QueueProvider.php @@ -0,0 +1,29 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\Queue\QueueManager; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * Class QueueProvider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class QueueProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['queueManager'] = new QueueManager($container); + return $container; + } +} diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php new file mode 100644 index 0000000..a5bc6e0 --- /dev/null +++ b/app/ServiceProvider/RouteProvider.php @@ -0,0 +1,285 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\Http\Route; +use Kanboard\Core\Http\Router; + +/** + * Route Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class RouteProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['router'] = new Router($container); + $container['route'] = new Route($container); + + if (ENABLE_URL_REWRITE) { + $container['route']->enable(); + + // Dashboard + $container['route']->addRoute('dashboard', 'DashboardController', 'show'); + $container['route']->addRoute('dashboard/:user_id', 'DashboardController', 'show'); + $container['route']->addRoute('dashboard/:user_id/projects', 'DashboardController', 'projects'); + $container['route']->addRoute('dashboard/:user_id/tasks', 'DashboardController', 'tasks'); + $container['route']->addRoute('dashboard/:user_id/subtasks', 'DashboardController', 'subtasks'); + $container['route']->addRoute('dashboard/:user_id/activity', 'DashboardController', 'activity'); + $container['route']->addRoute('dashboard/:user_id/notifications', 'DashboardController', 'notifications'); + $container['route']->addRoute('my-activity', 'ActivityController', 'user'); + + // Search routes + $container['route']->addRoute('search', 'SearchController', 'index'); + $container['route']->addRoute('search/activity', 'SearchController', 'activity'); + + // ProjectCreation routes + $container['route']->addRoute('project/create', 'ProjectCreationController', 'create'); + $container['route']->addRoute('project/create/personal', 'ProjectCreationController', 'createPrivate'); + + // Project routes + $container['route']->addRoute('projects', 'ProjectListController', 'show'); + $container['route']->addRoute('project/:project_id', 'ProjectViewController', 'show'); + $container['route']->addRoute('p/:project_id', 'ProjectViewController', 'show'); + $container['route']->addRoute('project/:project_id/customer-filters', 'CustomFilterController', 'index'); + $container['route']->addRoute('project/:project_id/customer-filters/create', 'CustomFilterController', 'create'); + $container['route']->addRoute('project/:project_id/share', 'ProjectViewController', 'share'); + $container['route']->addRoute('project/:project_id/notifications', 'ProjectViewController', 'notifications'); + $container['route']->addRoute('project/:project_id/integrations', 'ProjectViewController', 'integrations'); + $container['route']->addRoute('project/:project_id/duplicate', 'ProjectViewController', 'duplicate'); + $container['route']->addRoute('project/:project_id/permissions', 'ProjectPermissionController', 'index'); + $container['route']->addRoute('project/:project_id/activity', 'ActivityController', 'project'); + $container['route']->addRoute('project/:project_id/tags', 'ProjectTagController', 'index'); + $container['route']->addRoute('project/:project_id/task/create', 'TaskCreationController', 'show'); + $container['route']->addRoute('project/:project_id/predefined-contents', 'ProjectPredefinedContentController', 'show'); + $container['route']->addRoute('project/:project_id/predefined-contents/create', 'PredefinedTaskDescriptionController', 'create'); + $container['route']->addRoute('project/:project_id/predefined-contents/save', 'PredefinedTaskDescriptionController', 'save'); + $container['route']->addRoute('project/:project_id/predefined-contents/edit/:id', 'PredefinedTaskDescriptionController', 'edit'); + $container['route']->addRoute('project/:project_id/predefined-contents/remove/:id', 'PredefinedTaskDescriptionController', 'confirm'); + $container['route']->addRoute('project/:project_id/custom-roles', 'ProjectRoleController', 'show'); + $container['route']->addRoute('project/:project_id/import/tasks', 'ProjectViewController', 'importTasks'); + $container['route']->addRoute('project/:project_id/enable', 'ProjectStatusController', 'confirmEnable'); + $container['route']->addRoute('project/:project_id/disable', 'ProjectStatusController', 'confirmDisable'); + $container['route']->addRoute('project/:project_id/remove', 'ProjectStatusController', 'confirmRemove'); + $container['route']->addRoute('project/:project_id/file/:file_id/thumbnail/:etag', 'FileViewerController', 'thumbnail'); + $container['route']->addRoute('project/:project_id/file/:file_id/image/:etag', 'FileViewerController', 'image'); + $container['route']->addRoute('project/:project_id/file/:file_id/download/:etag', 'FileViewerController', 'download'); + $container['route']->addRoute('project/:project_id/file/:file_id/show/:etag', 'FileViewerController', 'show'); + $container['route']->addRoute('project/:project_id/file/:file_id/remove', 'ProjectFileController', 'confirm'); + $container['route']->addRoute('project/:project_id/file/:file_id/view', 'FileViewerController', 'browser'); + + // Project Overview + $container['route']->addRoute('project/:project_id/overview', 'ProjectOverviewController', 'show'); + $container['route']->addRoute('project/:project_id/overview/:search', 'ProjectOverviewController', 'show'); + + // ProjectEdit routes + $container['route']->addRoute('project/:project_id/edit', 'ProjectEditController', 'show'); + + // ProjectUser routes + $container['route']->addRoute('projects/managers/:user_id', 'ProjectUserOverviewController', 'managers'); + $container['route']->addRoute('projects/members/:user_id', 'ProjectUserOverviewController', 'members'); + $container['route']->addRoute('projects/tasks/:user_id/opens', 'ProjectUserOverviewController', 'opens'); + $container['route']->addRoute('projects/tasks/:user_id/closed', 'ProjectUserOverviewController', 'closed'); + $container['route']->addRoute('projects/managers', 'ProjectUserOverviewController', 'managers'); + + // Action routes + $container['route']->addRoute('project/:project_id/actions', 'ActionController', 'index'); + $container['route']->addRoute('project/:project_id/action/:action_id/confirm', 'ActionController', 'confirm'); + $container['route']->addRoute('project/:project_id/action/:action_id/remove', 'ActionController', 'remove'); + $container['route']->addRoute('project/:project_id/action/create', 'ActionCreationController', 'create'); + $container['route']->addRoute('project/:project_id/action/event', 'ActionCreationController', 'event'); + $container['route']->addRoute('project/:project_id/action/params', 'ActionCreationController', 'params'); + $container['route']->addRoute('project/:project_id/action/save', 'ActionCreationController', 'save'); + + // Column routes + $container['route']->addRoute('project/:project_id/columns', 'ColumnController', 'index'); + + // Swimlane routes + $container['route']->addRoute('project/:project_id/swimlanes', 'SwimlaneController', 'index'); + + // Category routes + $container['route']->addRoute('project/:project_id/categories', 'CategoryController', 'index'); + + // Import routes + $container['route']->addRoute('project/:project_id/import', 'TaskImportController', 'show'); + + // Task routes + $container['route']->addRoute('task/:task_id', 'TaskViewController', 'show'); + $container['route']->addRoute('t/:task_id', 'TaskViewController', 'show'); + $container['route']->addRoute('public/task/:task_id/:token', 'TaskViewController', 'readonly'); + + $container['route']->addRoute('task/:task_id/activity', 'ActivityController', 'task'); + $container['route']->addRoute('task/:task_id/transitions', 'TaskViewController', 'transitions'); + $container['route']->addRoute('task/:task_id/analytics', 'TaskViewController', 'analytics'); + $container['route']->addRoute('task/:task_id/time-tracking', 'TaskViewController', 'timetracking'); + $container['route']->addRoute('task/:task_id/position/show', 'TaskMovePositionController', 'show'); + $container['route']->addRoute('task/:task_id/position/save', 'TaskMovePositionController', 'save'); + $container['route']->addRoute('task/:task_id/edit', 'TaskModificationController', 'edit'); + $container['route']->addRoute('task/:task_id/update', 'TaskModificationController', 'update'); + $container['route']->addRoute('task/:task_id/assign-to-me/:csrf_token', 'TaskModificationController', 'assignToMe'); + $container['route']->addRoute('task/:task_id/start/:csrf_token', 'TaskModificationController', 'start'); + $container['route']->addRoute('task/:task_id/assign-to-me/redirect/:redirect/:csrf_token', 'TaskModificationController', 'assignToMe'); + $container['route']->addRoute('task/:task_id/start/redirect/:redirect/:csrf_token', 'TaskModificationController', 'start'); + $container['route']->addRoute('task/:task_id/close', 'TaskStatusController', 'close'); + $container['route']->addRoute('task/:task_id/open', 'TaskStatusController', 'open'); + $container['route']->addRoute('task/:task_id/email/create', 'TaskMailController', 'create'); + $container['route']->addRoute('task/:task_id/email/send', 'TaskMailController', 'send'); + $container['route']->addRoute('task/:task_id/duplicate', 'TaskDuplicationController', 'duplicate'); + $container['route']->addRoute('task/:task_id/move-to-project/:project_id', 'TaskDuplicationController', 'move'); + $container['route']->addRoute('task/:task_id/copy-to-project/:project_id', 'TaskDuplicationController', 'copy'); + $container['route']->addRoute('task/:task_id/screenshot', 'TaskPopoverController', 'screenshot'); + $container['route']->addRoute('task/:task_id/file/screenshot', 'TaskFileController', 'screenshot'); + $container['route']->addRoute('task/:task_id/file/create', 'TaskFileController', 'create'); + $container['route']->addRoute('task/:task_id/file/save', 'TaskFileController', 'save'); + $container['route']->addRoute('task/:task_id/file/:file_id/remove', 'TaskFileController', 'remove'); + $container['route']->addRoute('task/:task_id/file/:file_id/confirm', 'TaskFileController', 'confirm'); + $container['route']->addRoute('task/:task_id/file/:file_id/view', 'FileViewerController', 'browser'); + $container['route']->addRoute('task/:task_id/file/:file_id/thumbnail/:etag', 'FileViewerController', 'thumbnail'); + $container['route']->addRoute('task/:task_id/file/:file_id/image/:etag', 'FileViewerController', 'image'); + $container['route']->addRoute('task/:task_id/file/:file_id/download/:etag', 'FileViewerController', 'download'); + $container['route']->addRoute('task/:task_id/file/:file_id/show/:etag', 'FileViewerController', 'show'); + $container['route']->addRoute('task/:task_id/external-link/find', 'TaskExternalLinkController', 'find'); + $container['route']->addRoute('task/:task_id/external-link/create', 'TaskExternalLinkController', 'create'); + $container['route']->addRoute('task/:task_id/external-link/save', 'TaskExternalLinkController', 'save'); + $container['route']->addRoute('task/:task_id/internal-link/create', 'TaskInternalLinkController', 'create'); + $container['route']->addRoute('task/:task_id/internal-link/save', 'TaskInternalLinkController', 'save'); + $container['route']->addRoute('task/:task_id/comment/create', 'CommentController', 'create'); + $container['route']->addRoute('task/:task_id/comment/save', 'CommentController', 'save'); + $container['route']->addRoute('task/:task_id/comment/:comment_id/edit', 'CommentController', 'edit'); + $container['route']->addRoute('task/:task_id/comment/:comment_id/update', 'CommentController', 'update'); + $container['route']->addRoute('task/:task_id/comment/:comment_id/confirm', 'CommentController', 'confirm'); + $container['route']->addRoute('task/:task_id/comment/:comment_id/remove/:csrf_token', 'CommentController', 'remove'); + $container['route']->addRoute('task/:task_id/subtask/create', 'SubtaskController', 'create'); + $container['route']->addRoute('task/:task_id/subtask/save', 'SubtaskController', 'save'); + $container['route']->addRoute('task/:task_id/recurrence/edit', 'TaskRecurrenceController', 'edit'); + $container['route']->addRoute('task/:task_id/remove', 'TaskSuppressionController', 'confirm'); + $container['route']->addRoute('task/:task_id/remove/redirect/:redirect', 'TaskSuppressionController', 'confirm'); + + // Exports + $container['route']->addRoute('export/tasks/:project_id', 'ExportController', 'tasks'); + $container['route']->addRoute('export/subtasks/:project_id', 'ExportController', 'subtasks'); + $container['route']->addRoute('export/transitions/:project_id', 'ExportController', 'transitions'); + $container['route']->addRoute('export/summary/:project_id', 'ExportController', 'summary'); + + // Analytics routes + $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'); + $container['route']->addRoute('analytics/lead-cycle-time/:project_id', 'AnalyticController', 'leadAndCycleTime'); + $container['route']->addRoute('analytics/estimated-spent-time/:project_id', 'AnalyticController', 'compareHours'); + + // Board routes + $container['route']->addRoute('board/:project_id', 'BoardViewController', 'show'); + $container['route']->addRoute('board/:project_id/search/:search', 'BoardViewController', 'show'); + $container['route']->addRoute('board/:project_id/task/create/swimlane/:swimlane_id/column/:column_id', 'TaskCreationController', 'show'); + $container['route']->addRoute('board/:project_id/task/bulk/create/swimlane/:swimlane_id/column/:column_id', 'TaskBulkController', 'show'); + $container['route']->addRoute('board/:project_id/close-tasks/swimlane/:swimlane_id/column/:column_id', 'BoardPopoverController', 'confirmCloseColumnTasks'); + $container['route']->addRoute('board/tooltip/:task_id/tasklinks', 'BoardTooltipController', 'tasklinks'); + $container['route']->addRoute('board/tooltip/:task_id/externallinks', 'BoardTooltipController', 'externallinks'); + $container['route']->addRoute('board/tooltip/:task_id/subtasks', 'BoardTooltipController', 'subtasks'); + $container['route']->addRoute('board/tooltip/:task_id/attachments', 'BoardTooltipController', 'attachments'); + $container['route']->addRoute('board/tooltip/:task_id/description', 'BoardTooltipController', 'description'); + $container['route']->addRoute('board/tooltip/:task_id/recurrence', 'BoardTooltipController', 'recurrence'); + $container['route']->addRoute('board/tooltip/:project_id/swimlane/:swimlane_id', 'BoardTooltipController', 'swimlane'); + $container['route']->addRoute('b/:project_id', 'BoardViewController', 'show'); + $container['route']->addRoute('public/board/:token', 'BoardViewController', 'readonly'); + + // Listing routes + $container['route']->addRoute('list/:project_id', 'TaskListController', 'show'); + $container['route']->addRoute('list/:project_id/search/:search', 'TaskListController', 'show'); + $container['route']->addRoute('l/:project_id', 'TaskListController', 'show'); + + // Feed routes + $container['route']->addRoute('feed/project/:token', 'FeedController', 'project'); + $container['route']->addRoute('feed/user/:token', 'FeedController', 'user'); + + // Ical routes + $container['route']->addRoute('ical/project/:token', 'ICalendarController', 'project'); + $container['route']->addRoute('ical/user/:token', 'ICalendarController', 'user'); + + // Users + $container['route']->addRoute('users', 'UserListController', 'show'); + $container['route']->addRoute('user/profile/:user_id', 'UserViewController', 'profile'); + $container['route']->addRoute('user/show/:user_id', 'UserViewController', 'show'); + $container['route']->addRoute('user/show/:user_id/timesheet', 'UserViewController', 'timesheet'); + $container['route']->addRoute('user/show/:user_id/last-logins', 'UserViewController', 'lastLogin'); + $container['route']->addRoute('user/show/:user_id/sessions', 'UserViewController', 'sessions'); + $container['route']->addRoute('user/show/:user_id/password-reset-history', 'UserViewController', 'password'); + $container['route']->addRoute('user/:user_id/edit', 'UserModificationController', 'show'); + $container['route']->addRoute('user/:user_id/password', 'UserCredentialController', 'changePassword'); + $container['route']->addRoute('user/:user_id/share', 'UserViewController', 'share'); + $container['route']->addRoute('user/:user_id/notifications', 'UserViewController', 'notifications'); + $container['route']->addRoute('user/:user_id/accounts', 'UserViewController', 'external'); + $container['route']->addRoute('user/:user_id/integrations', 'UserViewController', 'integrations'); + $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'); + $container['route']->addRoute('user/:user_id/notifications/web', 'WebNotificationController', 'show'); + $container['route']->addRoute('user/:user_id/notifications/web/flush/:csrf_token', 'WebNotificationController', 'flush'); + $container['route']->addRoute('user/:user_id/notifications/web/remove/:notification_id/:csrf_token', 'WebNotificationController', 'remove'); + $container['route']->addRoute('invite/signup/:token', 'UserInviteController', 'signup'); + + // Groups + $container['route']->addRoute('groups', 'GroupListController', 'index'); + $container['route']->addRoute('group/:group_id/members', 'GroupListController', 'users'); + + // Config + $container['route']->addRoute('settings', 'ConfigController', 'index'); + $container['route']->addRoute('settings/application', 'ConfigController', 'application'); + $container['route']->addRoute('settings/email', 'ConfigController', 'email'); + $container['route']->addRoute('settings/project', 'ConfigController', 'project'); + $container['route']->addRoute('settings/project', 'ConfigController', 'project'); + $container['route']->addRoute('settings/board', 'ConfigController', 'board'); + $container['route']->addRoute('settings/integrations', 'ConfigController', 'integrations'); + $container['route']->addRoute('settings/webhook', 'ConfigController', 'webhook'); + $container['route']->addRoute('settings/api', 'ConfigController', 'api'); + $container['route']->addRoute('settings/links', 'LinkController', 'index'); + $container['route']->addRoute('settings/currencies', 'CurrencyController', 'show'); + $container['route']->addRoute('settings/currencies/create', 'CurrencyController', 'create'); + $container['route']->addRoute('settings/currencies/change', 'CurrencyController', 'change'); + $container['route']->addRoute('settings/tags', 'TagController', 'index'); + $container['route']->addRoute('settings/links/labels', 'LinkController', 'show'); + $container['route']->addRoute('settings/links/labels/create', 'LinkController', 'create'); + $container['route']->addRoute('settings/links/labels/edit/:link_id', 'LinkController', 'edit'); + $container['route']->addRoute('settings/links/labels/update/:link_id', 'LinkController', 'update'); + $container['route']->addRoute('settings/links/labels/confirm/:link_id', 'LinkController', 'confirm'); + $container['route']->addRoute('settings/links/labels/remove/:link_id/:csrf_token', 'LinkController', 'remove'); + + // Plugins + $container['route']->addRoute('extensions', 'PluginController', 'show'); + $container['route']->addRoute('extensions/directory', 'PluginController', 'directory'); + + // Doc + $container['route']->addRoute('documentation/:file', 'DocumentationController', 'show'); + $container['route']->addRoute('documentation', 'DocumentationController', 'show'); + + // Auth routes + $container['route']->addRoute('login', 'AuthController', 'login'); + $container['route']->addRoute('login/check', 'AuthController', 'check'); + $container['route']->addRoute('logout', 'AuthController', 'logout'); + + // PasswordReset + $container['route']->addRoute('forgot-password', 'PasswordResetController', 'create'); + $container['route']->addRoute('forgot-password/change/:token', 'PasswordResetController', 'change'); + + // Cronjob + $container['route']->addRoute('cronjob', 'CronjobController', 'run'); + } + + return $container; + } +} diff --git a/app/ServiceProvider/SessionProvider.php b/app/ServiceProvider/SessionProvider.php new file mode 100644 index 0000000..6334e51 --- /dev/null +++ b/app/ServiceProvider/SessionProvider.php @@ -0,0 +1,37 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Kanboard\Core\Session\SessionManager; +use Kanboard\Core\Session\FlashMessage; + +/** + * Session Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class SessionProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['sessionManager'] = function ($c) { + return new SessionManager($c); + }; + + $container['flash'] = function ($c) { + return new FlashMessage($c); + }; + + return $container; + } +} diff --git a/app/ServiceProvider/UserProvider.php b/app/ServiceProvider/UserProvider.php new file mode 100644 index 0000000..c80a2ae --- /dev/null +++ b/app/ServiceProvider/UserProvider.php @@ -0,0 +1,35 @@ +<?php + +namespace Kanboard\ServiceProvider; + +use Kanboard\Core\User\UserManager; +use Kanboard\User\DatabaseBackendUserProvider; +use Pimple\Container; +use Pimple\ServiceProviderInterface; + +/** + * User Provider + * + * @package Kanboard\ServiceProvider + * @author Frederic Guillot + */ +class UserProvider implements ServiceProviderInterface +{ + /** + * Register providers + * + * @access public + * @param \Pimple\Container $container + * @return \Pimple\Container + */ + public function register(Container $container) + { + $container['userManager'] = new UserManager(); + + if (DB_USER_PROVIDER) { + $container['userManager']->register(new DatabaseBackendUserProvider($container)); + } + + return $container; + } +} diff --git a/app/Subscriber/AuthSubscriber.php b/app/Subscriber/AuthSubscriber.php new file mode 100644 index 0000000..b2b4d46 --- /dev/null +++ b/app/Subscriber/AuthSubscriber.php @@ -0,0 +1,117 @@ +<?php + +namespace Kanboard\Subscriber; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Kanboard\Core\Security\AuthenticationManager; +use Kanboard\Core\Session\SessionManager; +use Kanboard\Event\AuthSuccessEvent; +use Kanboard\Event\AuthFailureEvent; + +/** + * Authentication Subscriber + * + * @package subscriber + * @author Frederic Guillot + */ +class AuthSubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + /** + * Get event listeners + * + * @static + * @access public + * @return array + */ + public static function getSubscribedEvents() + { + return array( + AuthenticationManager::EVENT_SUCCESS => 'afterLogin', + AuthenticationManager::EVENT_FAILURE => 'onLoginFailure', + SessionManager::EVENT_DESTROY => 'afterLogout', + ); + } + + /** + * After Login callback + * + * @access public + * @param AuthSuccessEvent $event + */ + public function afterLogin(AuthSuccessEvent $event) + { + $this->logger->debug('Subscriber executed: '.__METHOD__); + + $userAgent = $this->request->getUserAgent(); + $ipAddress = $this->request->getIpAddress(); + + $this->userLockingModel->resetFailedLogin($this->userSession->getUsername()); + $this->captchaModel->resetFailedLogin($ipAddress); + + $this->lastLoginModel->create( + $event->getAuthType(), + $this->userSession->getId(), + $ipAddress, + $userAgent + ); + + if ($event->getAuthType() === 'RememberMe') { + $this->userSession->setPostAuthenticationAsValidated(); + } + + if (REMEMBER_ME_AUTH && session_is_true('hasRememberMe') && ! $this->userSession->hasPostAuthentication()) { + $session = $this->rememberMeSessionModel->create($this->userSession->getId(), $ipAddress, $userAgent); + $this->rememberMeCookie->write($session['token'], $session['sequence'], $session['expiration']); + } + } + + /** + * Destroy RememberMe session on logout + * + * @access public + */ + public function afterLogout() + { + $this->logger->debug('Subscriber executed: '.__METHOD__); + $credentials = $this->rememberMeCookie->read(); + + if ($credentials !== false) { + $session = $this->rememberMeSessionModel->find($credentials['token'], $credentials['sequence']); + + if (! empty($session)) { + $this->rememberMeSessionModel->remove($session['id']); + } + + $this->rememberMeCookie->remove(); + } + } + + /** + * Increment failed login counter + * + * @access public + * @param AuthFailureEvent $event + */ + public function onLoginFailure(AuthFailureEvent $event) + { + $this->logger->debug('Subscriber executed: '.__METHOD__); + $username = $event->getUsername(); + $ipAddress = $this->request->getIpAddress(); + + // IP-based captcha + $this->captchaModel->incrementFailedLogin($ipAddress); + + if (! empty($username)) { + // log login failure in web server log to allow fail2ban usage + error_log('Kanboard: user '.$username.' authentication failure with IP address: '.$ipAddress); + $this->userLockingModel->incrementFailedLogin($username); + + if ($this->userLockingModel->getFailedLogin($username) > BRUTEFORCE_LOCKDOWN) { + $this->userLockingModel->lock($username, BRUTEFORCE_LOCKDOWN_DURATION); + } + } else { + // log login failure in web server log to allow fail2ban usage + error_log('Kanboard: user Unknown authentication failure with IP address: '.$ipAddress); + } + } +} diff --git a/app/Subscriber/BaseSubscriber.php b/app/Subscriber/BaseSubscriber.php new file mode 100644 index 0000000..9244196 --- /dev/null +++ b/app/Subscriber/BaseSubscriber.php @@ -0,0 +1,15 @@ +<?php + +namespace Kanboard\Subscriber; + +use Kanboard\Core\Base; + +/** + * Base class for subscribers + * + * @package subscriber + * @author Frederic Guillot + */ +class BaseSubscriber extends Base +{ +} diff --git a/app/Subscriber/BootstrapSubscriber.php b/app/Subscriber/BootstrapSubscriber.php new file mode 100644 index 0000000..432f837 --- /dev/null +++ b/app/Subscriber/BootstrapSubscriber.php @@ -0,0 +1,42 @@ +<?php + +namespace Kanboard\Subscriber; + +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class BootstrapSubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + 'app.bootstrap' => 'execute', + ); + } + + public function execute() + { + $this->logger->debug('Subscriber executed: '.__METHOD__); + $this->languageModel->loadCurrentLanguage(); + $this->timezoneModel->setCurrentTimezone(); + $this->actionManager->attachEvents(); + + if ($this->userSession->isLogged()) { + session_set('hasSubtaskInProgress', $this->subtaskStatusModel->hasSubtaskInProgress($this->userSession->getId())); + } + } + + public function __destruct() + { + if (DEBUG) { + foreach ($this->db->getLogMessages() as $message) { + $this->logger->debug('SQL: ' . $message); + } + + $this->logger->debug('APP: nb_queries={nb}', array('nb' => $this->db->getStatementHandler()->getNbQueries())); + $this->logger->debug('APP: rendering_time={time}', array('time' => microtime(true) - $this->request->getStartTime())); + $this->logger->debug('APP: memory_usage='.$this->helper->text->bytes(memory_get_usage())); + $this->logger->debug('APP: uri='.$this->request->getUri()); + $this->logger->debug('###############################################'); + } + } +} diff --git a/app/Subscriber/LdapUserPhotoSubscriber.php b/app/Subscriber/LdapUserPhotoSubscriber.php new file mode 100644 index 0000000..93672cd --- /dev/null +++ b/app/Subscriber/LdapUserPhotoSubscriber.php @@ -0,0 +1,49 @@ +<?php + +namespace Kanboard\Subscriber; + +use Kanboard\Core\User\UserProfile; +use Kanboard\Event\UserProfileSyncEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +/** + * Class LdapUserPhotoSubscriber + * + * @package Kanboard\Subscriber + * @author Frederic Guillot + */ +class LdapUserPhotoSubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + /** + * Get event listeners + * + * @static + * @access public + * @return array + */ + public static function getSubscribedEvents() + { + return array( + UserProfile::EVENT_USER_PROFILE_AFTER_SYNC => 'syncUserPhoto', + ); + } + + /** + * Save the user profile photo from LDAP to the object storage + * + * @access public + * @param UserProfileSyncEvent $event + */ + public function syncUserPhoto(UserProfileSyncEvent $event) + { + if (is_a($event->getUser(), 'Kanboard\User\LdapUserProvider')) { + $profile = $event->getProfile(); + $photo = $event->getUser()->getPhoto(); + + if (empty($profile['avatar_path']) && ! empty($photo)) { + $this->logger->info('Saving user photo from LDAP profile'); + $this->avatarFileModel->uploadImageContent($profile['id'], $photo); + } + } + } +} diff --git a/app/Subscriber/NotificationSubscriber.php b/app/Subscriber/NotificationSubscriber.php new file mode 100644 index 0000000..8f9b086 --- /dev/null +++ b/app/Subscriber/NotificationSubscriber.php @@ -0,0 +1,47 @@ +<?php + +namespace Kanboard\Subscriber; + +use Kanboard\Event\GenericEvent; +use Kanboard\Model\TaskLinkModel; +use Kanboard\Model\TaskModel; +use Kanboard\Model\CommentModel; +use Kanboard\Model\SubtaskModel; +use Kanboard\Model\TaskFileModel; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class NotificationSubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + 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_PROJECT => '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', + TaskFileModel::EVENT_DESTROY => 'handleEvent', + TaskLinkModel::EVENT_CREATE_UPDATE => 'handleEvent', + TaskLinkModel::EVENT_DELETE => 'handleEvent', + ); + } + + public function handleEvent(GenericEvent $event, $eventName) + { + $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 new file mode 100644 index 0000000..eaa9d46 --- /dev/null +++ b/app/Subscriber/ProjectDailySummarySubscriber.php @@ -0,0 +1,27 @@ +<?php + +namespace Kanboard\Subscriber; + +use Kanboard\Event\TaskEvent; +use Kanboard\Model\TaskModel; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ProjectDailySummarySubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + TaskModel::EVENT_CREATE_UPDATE => 'execute', + TaskModel::EVENT_CLOSE => 'execute', + TaskModel::EVENT_OPEN => 'execute', + TaskModel::EVENT_MOVE_COLUMN => 'execute', + TaskModel::EVENT_MOVE_SWIMLANE => 'execute', + ); + } + + public function execute(TaskEvent $event) + { + $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 new file mode 100644 index 0000000..6e447c2 --- /dev/null +++ b/app/Subscriber/ProjectModificationDateSubscriber.php @@ -0,0 +1,33 @@ +<?php + +namespace Kanboard\Subscriber; + +use Kanboard\Event\GenericEvent; +use Kanboard\Model\TaskModel; +use Kanboard\Model\SubtaskModel; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class ProjectModificationDateSubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + TaskModel::EVENT_CREATE_UPDATE => 'execute', + TaskModel::EVENT_CLOSE => 'execute', + TaskModel::EVENT_OPEN => 'execute', + TaskModel::EVENT_MOVE_SWIMLANE => 'execute', + TaskModel::EVENT_MOVE_COLUMN => 'execute', + TaskModel::EVENT_MOVE_POSITION => 'execute', + TaskModel::EVENT_MOVE_PROJECT => 'execute', + TaskModel::EVENT_ASSIGNEE_CHANGE => 'execute', + SubtaskModel::EVENT_CREATE_UPDATE => 'execute', + SubtaskModel::EVENT_DELETE => 'execute', + ); + } + + public function execute(GenericEvent $event) + { + $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 new file mode 100644 index 0000000..3e2848f --- /dev/null +++ b/app/Subscriber/RecurringTaskSubscriber.php @@ -0,0 +1,42 @@ +<?php + +namespace Kanboard\Subscriber; + +use Kanboard\Event\TaskEvent; +use Kanboard\Model\TaskModel; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class RecurringTaskSubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + TaskModel::EVENT_MOVE_COLUMN => 'onMove', + TaskModel::EVENT_CLOSE => 'onClose', + ); + } + + public function onMove(TaskEvent $event) + { + $this->logger->debug('Subscriber executed: '.__METHOD__); + $task = $event['task']; + + 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']); + } + } + } + + public function onClose(TaskEvent $event) + { + $this->logger->debug('Subscriber executed: '.__METHOD__); + $task = $event['task']; + + 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/TransitionSubscriber.php b/app/Subscriber/TransitionSubscriber.php new file mode 100644 index 0000000..26d08f8 --- /dev/null +++ b/app/Subscriber/TransitionSubscriber.php @@ -0,0 +1,28 @@ +<?php + +namespace Kanboard\Subscriber; + +use Kanboard\Event\TaskEvent; +use Kanboard\Model\TaskModel; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; + +class TransitionSubscriber extends BaseSubscriber implements EventSubscriberInterface +{ + public static function getSubscribedEvents() + { + return array( + TaskModel::EVENT_MOVE_COLUMN => 'execute', + ); + } + + public function execute(TaskEvent $event) + { + $this->logger->debug('Subscriber executed: '.__METHOD__); + + $user_id = $this->userSession->getId(); + + if (! empty($user_id)) { + $this->transitionModel->save($user_id, $event->getAll()); + } + } +} diff --git a/app/Template/action/index.php b/app/Template/action/index.php new file mode 100644 index 0000000..29b6aca --- /dev/null +++ b/app/Template/action/index.php @@ -0,0 +1,80 @@ +<div class="page-header"> + <h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2> + <ul> + <li> + <?= $this->modal->medium('plus', t('Add a new action'), 'ActionCreationController', 'create', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= $this->modal->medium('copy', t('Import from another project'), 'ProjectActionDuplicationController', 'show', array('project_id' => $project['id'])) ?> + </li> + </ul> +</div> + +<?php if (empty($actions)): ?> + <p class="alert"><?= t('There is no action at the moment.') ?></p> +<?php else: ?> + <table class="table-scrolling"> + <?php foreach ($actions as $action): ?> + <tr> + <th> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ActionController', 'confirm', array('project_id' => $project['id'], 'action_id' => $action['id'])) ?> + </li> + </ul> + </div> + + <?php if (! isset($available_params[$action['action_name']])): ?> + <?= $this->text->e($action['action_name']) ?> + <?php else: ?> + <?= $this->text->in($action['action_name'], $available_actions) ?> + <?php endif ?> + </th> + </tr> + <tr> + <td> + <?php if (! isset($available_params[$action['action_name']])): ?> + <p class="alert alert-error"><?= t('Automatic action not found: "%s"', $action['action_name']) ?></p> + <?php else: ?> + <ul> + <li> + <?= t('Event name') ?> = + <strong><?= $this->text->in($action['event_name'], $available_events) ?></strong> + </li> + <?php foreach ($action['params'] as $param_name => $param_value): ?> + <li> + <?php if (isset($available_params[$action['action_name']][$param_name]) && is_array($available_params[$action['action_name']][$param_name])): ?> + <?= $this->text->e(ucfirst($param_name)) ?> = + <?php else: ?> + <?= $this->text->in($param_name, $available_params[$action['action_name']]) ?> = + <?php endif ?> + <strong> + <?php if ($this->text->contains($param_name, 'column_id')): ?> + <?= $this->text->in($param_value, $columns_list) ?> + <?php elseif ($this->text->contains($param_name, 'user_id')): ?> + <?= $this->text->in($param_value, $users_list) ?> + <?php elseif ($this->text->contains($param_name, 'project_id')): ?> + <?= $this->text->in($param_value, $projects_list) ?> + <?php elseif ($this->text->contains($param_name, 'color_id')): ?> + <?= $this->text->in($param_value, $colors_list) ?> + <?php elseif ($this->text->contains($param_name, 'category_id')): ?> + <?= $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 ?> + </strong> + </li> + <?php endforeach ?> + </ul> + <?php endif ?> + </td> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> diff --git a/app/Template/action/remove.php b/app/Template/action/remove.php new file mode 100644 index 0000000..e3cdb20 --- /dev/null +++ b/app/Template/action/remove.php @@ -0,0 +1,15 @@ +<div class="page-header"> + <h2><?= t('Remove an automatic action') ?></h2> +</div> + +<div class="confirm"> + <p class="alert alert-info"> + <?= 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> + + <?= $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 new file mode 100644 index 0000000..a1169dc --- /dev/null +++ b/app/Template/action_creation/create.php @@ -0,0 +1,13 @@ +<div class="page-header"> + <h2><?= t('Add an action') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ActionCreationController', 'event', array('project_id' => $project['id'])) ?>"> + <?= $this->form->csrf() ?> + + <?= $this->form->label(t('Action'), 'action_name') ?> + <?= $this->form->select('action_name', $available_actions, $values) ?> + + <?= $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 new file mode 100644 index 0000000..2ea7261 --- /dev/null +++ b/app/Template/action_creation/event.php @@ -0,0 +1,23 @@ +<div class="page-header"> + <h2><?= t('Choose an event') ?></h2> +</div> + +<form method="post" action="<?= $this->url->href('ActionCreationController', 'params', array('project_id' => $project['id'])) ?>"> + <?= $this->form->csrf() ?> + + <?= $this->form->hidden('action_name', $values) ?> + + <?= $this->form->label(t('Action'), 'action_name') ?> + <?= $this->form->select('action_name', $available_actions, $values, array(), array('disabled')) ?> + + <?= $this->form->label(t('Event'), 'event_name') ?> + <?= $this->form->select('event_name', $events, $values) ?> + + <div class="form-help"> + <?= t('When the selected event occurs execute the corresponding action.') ?> + </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 new file mode 100644 index 0000000..c92f09f --- /dev/null +++ b/app/Template/action_creation/params.php @@ -0,0 +1,54 @@ +<div class="page-header"> + <h2><?= t('Define action parameters') ?></h2> +</div> + +<form method="post" action="<?= $this->url->href('ActionCreationController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <?= $this->form->hidden('event_name', $values) ?> + <?= $this->form->hidden('action_name', $values) ?> + + <?= $this->form->label(t('Action'), 'action_name') ?> + <?= $this->form->select('action_name', $available_actions, $values, array(), array('disabled')) ?> + + <?= $this->form->label(t('Event'), 'event_name') ?> + <?= $this->form->select('event_name', $events, $values, array(), array('disabled')) ?> + + <?php foreach ($action_params as $param_name => $param_desc): ?> + <?php if ($this->text->contains($param_name, 'column_id')): ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $columns_list, $values) ?> + <?php elseif ($this->text->contains($param_name, 'user_id')): ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $users_list, $values) ?> + <?php elseif ($this->text->contains($param_name, 'project_id')): ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $projects_list, $values) ?> + <?php elseif ($this->text->contains($param_name, 'color_id')): ?> + <?= $this->form->colorSelect('params['.$param_name.']', $values) ?> + <?php elseif ($this->text->contains($param_name, 'category_id')): ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $categories_list, $values) ?> + <?php elseif ($this->text->contains($param_name, 'link_id')): ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $links_list, $values) ?> + <?php elseif ($param_name === 'priority'): ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $priorities_list, $values) ?> + <?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 elseif (is_array($param_desc)): ?> + <?= $this->form->label(ucfirst($param_name), $param_name) ?> + <?= $this->form->select('params['.$param_name.']', $param_desc, $values) ?> + <?php else: ?> + <?= $this->form->label($param_desc, $param_name) ?> + <?= $this->form->text('params['.$param_name.']', $values) ?> + <?php endif ?> + <?php endforeach ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/activity/filter_dropdown.php b/app/Template/activity/filter_dropdown.php new file mode 100644 index 0000000..abd7540 --- /dev/null +++ b/app/Template/activity/filter_dropdown.php @@ -0,0 +1,14 @@ +<div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('Default filters') ?>" aria-label="<?= t('Default filters') ?>"><i class="fa fa-filter fa-fw"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li><a href="#" class="filter-helper filter-reset" data-filter="" title="<?= t('Keyboard shortcut: "%s"', 'r') ?>"><?= t('Reset filters') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="creator:me"><?= t('My activities') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="created:<=<?= date('Y-m-d', strtotime('yesterday')) ?>"><?= t('Activity until yesterday') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="created:<=<?= date('Y-m-d')?>"><?= t('Activity until today') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:closed"><?= t('Closed tasks') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open"><?= t('Open tasks') ?></a></li> + <li> + <?= $this->url->doc(t('View advanced search syntax'), 'search') ?> + </li> + </ul> +</div> \ No newline at end of file diff --git a/app/Template/activity/project.php b/app/Template/activity/project.php new file mode 100644 index 0000000..ce1f8bb --- /dev/null +++ b/app/Template/activity/project.php @@ -0,0 +1,12 @@ +<div class="page-header"> + <h2><?= t('%s\'s activity', $project['name']) ?></h2> + + <?php if ($project['is_public']): ?> + <ul> + <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> + <?php endif ?> +</div> + +<?= $this->render('event/events', array('events' => $events)) ?> diff --git a/app/Template/activity/task.php b/app/Template/activity/task.php new file mode 100644 index 0000000..39953d1 --- /dev/null +++ b/app/Template/activity/task.php @@ -0,0 +1,12 @@ +<?= $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)) ?> diff --git a/app/Template/activity/user.php b/app/Template/activity/user.php new file mode 100644 index 0000000..71a67fb --- /dev/null +++ b/app/Template/activity/user.php @@ -0,0 +1,4 @@ +<div class="page-header"> + <h2><?= t('My activity stream') ?></h2> +</div> +<?= $this->render('event/events', array('events' => $events)) ?> \ No newline at end of file diff --git a/app/Template/analytic/avg_time_columns.php b/app/Template/analytic/avg_time_columns.php new file mode 100644 index 0000000..c17e521 --- /dev/null +++ b/app/Template/analytic/avg_time_columns.php @@ -0,0 +1,31 @@ +<?php if (! $is_ajax): ?> + <div class="page-header"> + <h2><?= t('Average time spent in each column') ?></h2> + </div> +<?php endif ?> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <?= $this->app->component('chart-project-avg-time-column', array( + 'metrics' => $metrics, + 'label' => t('Average time spent'), + )) ?> + + <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> + <?php endforeach ?> + </table> + + <p class="alert alert-info"> + <?= t('This chart shows the average time spent in 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 new file mode 100644 index 0000000..d62c9ba --- /dev/null +++ b/app/Template/analytic/burndown.php @@ -0,0 +1,26 @@ +<?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: ?> + <?= $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() ?> + <?= $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 new file mode 100644 index 0000000..dcd7b58 --- /dev/null +++ b/app/Template/analytic/cfd.php @@ -0,0 +1,23 @@ +<?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: ?> + <?= $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() ?> + <?= $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/estimated_actual_column.php b/app/Template/analytic/estimated_actual_column.php new file mode 100644 index 0000000..c49944e --- /dev/null +++ b/app/Template/analytic/estimated_actual_column.php @@ -0,0 +1,30 @@ +<?php if (! $is_ajax): ?> + <div class="page-header"> + <h2><?= t('Estimated vs actual time per column') ?></h2> + </div> +<?php endif ?> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <?= $this->app->component('chart-project-estimated-actual-column', array( + 'metrics' => $metrics, + 'labelSpent' => t('Hours Spent'), + 'labelEstimated' => t('Hours Estimated'), + )) ?> + + <table class="table-striped"> + <tr> + <th><?= t('Column') ?></th> + <th><?= t('Hours Spent') ?></th> + <th><?= t('Hours Estimated') ?></th> + </tr> + <?php foreach ($metrics as $column): ?> + <tr> + <td><?= $this->text->e($column['title']) ?></td> + <td><?= $this->dt->durationHours($column['hours_spent']) ?></td> + <td><?= $this->dt->durationHours($column['hours_estimated']) ?></td> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> \ No newline at end of file diff --git a/app/Template/analytic/layout.php b/app/Template/analytic/layout.php new file mode 100644 index 0000000..7159094 --- /dev/null +++ b/app/Template/analytic/layout.php @@ -0,0 +1,14 @@ +<?php if ($is_ajax): ?> + <div class="page-header"> + <h2><?= $title ?></h2> + </div> +<?php else: ?> + <?= $this->projectHeader->render($project, 'TaskListController', 'show') ?> +<?php endif ?> +<section class="sidebar-container"> + <?= $this->render($sidebar_template, array('project' => $project)) ?> + + <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 new file mode 100644 index 0000000..0e87671 --- /dev/null +++ b/app/Template/analytic/lead_cycle_time.php @@ -0,0 +1,33 @@ +<?php if (! $is_ajax): ?> + <div class="page-header"> + <h2><?= t('Average Lead and Cycle time') ?></h2> + </div> +<?php endif ?> + +<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> + </ul> +</div> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <?= $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 shows 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 new file mode 100644 index 0000000..ccd5e59 --- /dev/null +++ b/app/Template/analytic/sidebar.php @@ -0,0 +1,30 @@ +<div class="sidebar"> + <ul> + <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', 'userDistribution') ?>> + <?= $this->modal->replaceLink(t('User repartition'), 'AnalyticController', 'userDistribution', array('project_id' => $project['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('AnalyticController', 'cfd') ?>> + <?= $this->modal->replaceLink(t('Cumulative flow diagram'), 'AnalyticController', 'cfd', array('project_id' => $project['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('AnalyticController', 'burndown') ?>> + <?= $this->modal->replaceLink(t('Burndown chart'), 'AnalyticController', 'burndown', array('project_id' => $project['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('AnalyticController', 'averageTimeByColumn') ?>> + <?= $this->modal->replaceLink(t('Average time into each column'), 'AnalyticController', 'averageTimeByColumn', array('project_id' => $project['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('AnalyticController', 'leadAndCycleTime') ?>> + <?= $this->modal->replaceLink(t('Lead and cycle time'), 'AnalyticController', 'leadAndCycleTime', array('project_id' => $project['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('AnalyticController', 'timeComparison') ?>> + <?= $this->modal->replaceLink(t('Estimated vs actual time'), 'AnalyticController', 'timeComparison', array('project_id' => $project['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('AnalyticController', 'estimatedVsActualByColumn') ?>> + <?= $this->modal->replaceLink(t('Estimated vs actual time per column'), 'AnalyticController', 'estimatedVsActualByColumn', array('project_id' => $project['id'])) ?> + </li> + + <?= $this->hook->render('template:analytic:sidebar', array('project' => $project)) ?> + </ul> +</div> diff --git a/app/Template/analytic/task_distribution.php b/app/Template/analytic/task_distribution.php new file mode 100644 index 0000000..671d462 --- /dev/null +++ b/app/Template/analytic/task_distribution.php @@ -0,0 +1,34 @@ +<?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: ?> + <?= $this->app->component('chart-project-task-distribution', array( + 'metrics' => $metrics, + )) ?> + + <table class="table-striped"> + <tr> + <th><?= t('Column') ?></th> + <th><?= t('Number of tasks') ?></th> + <th><?= t('Percentage') ?></th> + </tr> + <?php foreach ($metrics as $metric): ?> + <tr> + <td> + <?= $this->text->e($metric['column_title']) ?> + </td> + <td> + <?= $metric['nb_tasks'] ?> + </td> + <td> + <?= n($metric['percentage']) ?>% + </td> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> diff --git a/app/Template/analytic/time_comparison.php b/app/Template/analytic/time_comparison.php new file mode 100644 index 0000000..bf2cd12 --- /dev/null +++ b/app/Template/analytic/time_comparison.php @@ -0,0 +1,63 @@ +<?php if (! $is_ajax): ?> + <div class="page-header"> + <h2><?= t('Estimated vs actual time') ?></h2> + </div> +<?php endif ?> + +<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> + </ul> +</div> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <?php if ($paginator->isEmpty()): ?> + <p class="alert"><?= t('No tasks found.') ?></p> + <?php elseif (! $paginator->isEmpty()): ?> + <?= $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-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> + <td class="task-table color-<?= $task['color_id'] ?>"> + <?= $this->url->link('#'.$this->text->e($task['id']), 'TaskViewController', 'show', array('task_id' => $task['id']), false, '', t('View this task')) ?> + </td> + <td> + <?= $this->url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id']), false, '', t('View this task')) ?> + </td> + <td> + <?php if ($task['is_active'] == \Kanboard\Model\TaskModel::STATUS_OPEN): ?> + <?= t('Open') ?> + <?php else: ?> + <?= t('Closed') ?> + <?php endif ?> + </td> + <td> + <?= $this->text->e($task['time_estimated']) ?> + </td> + <td> + <?= $this->text->e($task['time_spent']) ?> + </td> + </tr> + <?php endforeach ?> + </table> + + <?= $paginator ?> + <?php endif ?> +<?php endif ?> diff --git a/app/Template/analytic/user_distribution.php b/app/Template/analytic/user_distribution.php new file mode 100644 index 0000000..cae6fa5 --- /dev/null +++ b/app/Template/analytic/user_distribution.php @@ -0,0 +1,34 @@ +<?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: ?> + <?= $this->app->component('chart-project-user-distribution', array( + 'metrics' => $metrics, + )) ?> + + <table class="table-striped"> + <tr> + <th><?= t('User') ?></th> + <th><?= t('Number of tasks') ?></th> + <th><?= t('Percentage') ?></th> + </tr> + <?php foreach ($metrics as $metric): ?> + <tr> + <td> + <?= $this->text->e($metric['user']) ?> + </td> + <td> + <?= $metric['nb_tasks'] ?> + </td> + <td> + <?= n($metric['percentage']) ?>% + </td> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> diff --git a/app/Template/app/filters_helper.php b/app/Template/app/filters_helper.php new file mode 100644 index 0000000..b3844f3 --- /dev/null +++ b/app/Template/app/filters_helper.php @@ -0,0 +1,21 @@ +<?= $this->hook->render('template:app:filters-helper:before', isset($project) ? array('project' => $project) : array()) ?> +<div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon" title="<?= t('Default filters') ?>" aria-label="<?= t('Default filters') ?>"><i class="fa fa-filter fa-fw"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li><a href="#" class="filter-helper filter-reset" data-filter="<?= isset($reset) ? $reset : '' ?>" title="<?= t('Keyboard shortcut: "%s"', 'r') ?>"><?= t('Reset filters') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open assignee:me"><?= t('My tasks') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open assignee:me due:tomorrow"><?= t('My tasks due tomorrow') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open due:today"><?= t('Tasks due today') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open due:tomorrow"><?= t('Tasks due tomorrow') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open due:yesterday"><?= t('Tasks due yesterday') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:closed"><?= t('Closed tasks') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open"><?= t('Open tasks') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open assignee:nobody"><?= t('Not assigned') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open assignee:anybody"><?= t('Assigned') ?></a></li> + <li><a href="#" class="filter-helper" data-filter="status:open category:none"><?= t('No category') ?></a></li> + <li> + <?= $this->url->doc(t('View advanced search syntax'), 'search') ?> + </li> + </ul> +</div> +<?= $this->hook->render('template:app:filters-helper:after', isset($project) ? array('project' => $project) : array()) ?> \ No newline at end of file diff --git a/app/Template/app/forbidden.php b/app/Template/app/forbidden.php new file mode 100644 index 0000000..96e7611 --- /dev/null +++ b/app/Template/app/forbidden.php @@ -0,0 +1,5 @@ +<section id="main"> + <p class="alert alert-error"> + <?= t('Access Forbidden') ?> + </p> +</section> \ No newline at end of file diff --git a/app/Template/app/notfound.php b/app/Template/app/notfound.php new file mode 100644 index 0000000..0419902 --- /dev/null +++ b/app/Template/app/notfound.php @@ -0,0 +1,5 @@ +<section id="main"> + <p class="alert alert-error"> + <?= t('Sorry, I didn\'t find this information in my database!') ?> + </p> +</section> \ No newline at end of file diff --git a/app/Template/auth/index.php b/app/Template/auth/index.php new file mode 100644 index 0000000..ebfd281 --- /dev/null +++ b/app/Template/auth/index.php @@ -0,0 +1,53 @@ +<div class="login-page-wrapper"> +<div class="form-login"> + <h2><?= t('Login') ?></h2> + + <?= $this->hook->render('template:auth:login-form:before') ?> + + <?php if (isset($errors['login'])): ?> + <p class="alert alert-error"><?= $this->text->e($errors['login']) ?></p> + <?php endif ?> + + <?php if (! HIDE_LOGIN_FORM): ?> + <form method="post" action="<?= $this->url->href('AuthController', 'check') ?>"> + + <?= $this->form->csrf() ?> + + <div class="input-icon-wrapper"> + <?= $this->form->label(t('Username'), 'username') ?> + <i class="fa fa-user" aria-hidden="true"></i> + <?= $this->form->text('username', $values, $errors, array('autofocus', 'required', 'autocomplete="username"', 'placeholder="'.t('Username').'"')) ?> + </div> + + <div class="input-icon-wrapper"> + <?= $this->form->label(t('Password'), 'password') ?> + <i class="fa fa-lock" aria-hidden="true"></i> + <?= $this->form->password('password', $values, $errors, array('required', 'autocomplete="current-password"', 'placeholder="'.t('Password').'"')) ?> + </div> + + <?php if (isset($captcha) && $captcha): ?> + <?= $this->form->label(t('Enter the text below'), 'captcha') ?> + <img src="<?= $this->url->href('CaptchaController', 'image') ?>" alt="Captcha"> + <?= $this->form->text('captcha', array(), $errors, array('required')) ?> + <?php endif ?> + + <?php if (REMEMBER_ME_AUTH): ?> + <div class="remember-me-wrapper"> + <?= $this->form->checkbox('remember_me', t('Remember Me'), 1, true) ?> + </div> + <?php endif ?> + + <div class="form-actions"> + <button type="submit" class="btn btn-blue"><?= t('Sign in') ?></button> + </div> + <?php if ($this->app->config('password_reset') == 1): ?> + <div class="reset-password"> + <?= $this->url->link(t('Forgot password?'), 'PasswordResetController', 'create') ?> + </div> + <?php endif ?> + </form> + <?php endif ?> + + <?= $this->hook->render('template:auth:login-form:after') ?> +</div> +</div> diff --git a/app/Template/avatar_file/show.php b/app/Template/avatar_file/show.php new file mode 100644 index 0000000..371d09a --- /dev/null +++ b/app/Template/avatar_file/show.php @@ -0,0 +1,23 @@ +<div class="page-header"> + <h2><?= t('Avatar') ?></h2> +</div> + +<?= $this->avatar->render($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path'], '') ?> + +<div class="form-actions"> +<?php if (! empty($user['avatar_path'])): ?> + <?= $this->url->link(t('Remove my image'), 'AvatarFileController', 'remove', array('user_id' => $user['id']), true, 'btn btn-red js-modal-replace') ?> +<?php endif ?> +</div> + +<hr> + +<h3><?= t('Upload my avatar image') ?></h3> +<form method="post" enctype="multipart/form-data" action="<?= $this->url->href('AvatarFileController', 'upload', array('user_id' => $user['id']), true) ?>"> + <?= $this->form->label(t('Avatar'), 'avatar', ['class="ui-helper-hidden-accessible"']) ?> + <?= $this->form->file('avatar') ?> + + <div class="form-actions"> + <button type="submit" class="btn btn-blue"><?= t('Upload my avatar image') ?></button> + </div> +</form> diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php new file mode 100644 index 0000000..2d15a85 --- /dev/null +++ b/app/Template/board/table_column.php @@ -0,0 +1,127 @@ +<!-- column titles --> + +<?= $this->hook->render('template:board:table:column:before-header-row', array('swimlane' => $swimlane)) ?> + +<tr class="board-swimlane-columns-<?= $swimlane['id'] ?>"> + <?php foreach ($swimlane['columns'] as $column): ?> + <th class="board-column-header board-column-header-<?= $column['id'] ?>" data-column-id="<?= $column['id'] ?>"> + + <!-- column in collapsed mode --> + <div class="board-column-collapsed"> + <small class="board-column-header-task-count" title="<?= t('Task count') ?>"> + <span id="task-number-column-<?= $column['id'] ?>"><span class="ui-helper-hidden-accessible"><?= t('Task count') ?> </span><?= $column['nb_tasks'] ?></span> + </small> + </div> + + <!-- column in expanded mode --> + <div class="board-column-expanded board-column-expanded-header"> + <?php if (! $not_editable && $this->projectRole->canCreateTaskInColumn($column['project_id'], $column['id'])): ?> + <?= $this->task->getNewBoardTaskButton($swimlane, $column) ?> + <?php endif ?> + + <span class="board-column-title"> + <?php if ($not_editable): ?> + <?= $this->text->e($column['title']) ?> + <?php else: ?> + <span class="dropdown"> + <a href="#" class="dropdown-menu"><?= $this->text->e($column['title']) ?> <i class="fa fa-caret-down"></i></a> + <ul> + <li> + <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->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> + <?= $this->modal->confirm('close', t('Close all tasks in this column and this swimlane'), 'BoardPopoverController', 'confirmCloseColumnTasks', array('project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id'])) ?> + </li> + <?php endif ?> + </ul> + <?php if ($column['nb_tasks'] > 0 && $this->user->hasProjectAccess('TaskModificationController', 'update', $column['project_id'])): ?> + <span class="dropdown"> + <a href="#" class="dropdown-menu"><i class="fa fa-sort"></i></i></a> + <ul> + <li> + <?= $this->url->icon('sort-numeric-asc', t('Reorder this column by id (ASC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'id', 'direction' => 'asc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-numeric-desc', t('Reorder this column by id (DESC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'id', 'direction' => 'desc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-numeric-asc', t('Reorder this column by priority (ASC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'priority', 'direction' => 'asc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-numeric-desc', t('Reorder this column by priority (DESC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'priority', 'direction' => 'desc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-amount-asc', t('Reorder this column by assignee and priority (ASC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'assignee-priority', 'direction' => 'asc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-amount-desc', t('Reorder this column by assignee and priority (DESC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'assignee-priority', 'direction' => 'desc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-alpha-asc', t('Reorder this column by assignee (A-Z)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'assignee', 'direction' => 'asc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-alpha-desc', t('Reorder this column by assignee (Z-A)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'assignee', 'direction' => 'desc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-numeric-asc', t('Reorder this column by due date (ASC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'due-date', 'direction' => 'asc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + <li> + <?= $this->url->icon('sort-numeric-desc', t('Reorder this column by due date (DESC)'), 'TaskReorderController', 'reorderColumn', ['sort' => 'due-date', 'direction' => 'desc', 'project_id' => $column['project_id'], 'column_id' => $column['id'], 'swimlane_id' => $swimlane['id']], true) ?> + </li> + </ul> + </span> + <?php endif ?> + + <?= $this->hook->render('template:board:column:dropdown', array('swimlane' => $swimlane, 'column' => $column)) ?> + </span> + <?php endif ?> + </span> + + <span class="pull-right board-column-header-task-count"> + <?php if (! empty($column['score'])): ?> + <span title="<?= t('Score') ?>"> + <span class="ui-helper-hidden-accessible"><?= t('Score') ?> </span><?= $column['score'] ?> + </span> + <?php endif ?> + + <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['cumulative_score_across_swimlane'])): ?> + <span title="<?= t('Total score in this column across all swimlanes') ?>"> + (<span><span class="ui-helper-hidden-accessible"><?= t('Total score in this column across all swimlanes') ?> </span><?= $column['cumulative_score_across_swimlane'] ?></span>)  + </span> + <?php endif ?> + + <?php if (! $not_editable && ! empty($column['description'])): ?> + <?= $this->app->tooltipMarkdown($column['description']) ?>  + <?php endif ?> + + <?php if (! empty($column['nb_tasks'])): ?> + <span title="<?= t('Number of visible tasks in this column and swimlane') ?>"> + <span><span class="ui-helper-hidden-accessible"><?= t('Task count') ?> </span><?= $column['nb_tasks'] ?></span>  + </span> + <?php endif ?> + + <?php if (! empty($column['nb_unfiltered_tasks_across_swimlane'])): ?> + <span title="<?= t('Total number of tasks in this column across all swimlanes') ?>"> + <?php if ($column['task_limit'] > 0): ?> + (<span><span class="ui-helper-hidden-accessible"><?= t('Total number of tasks in this column across all swimlanes') ?> </span><?= $column['nb_unfiltered_tasks_across_swimlane'] ?></span>/<span title="<?= t('Task limit') ?>"><span class="ui-helper-hidden-accessible"><?= t('Task limit') ?> </span><?= $this->text->e($column['task_limit']) ?></span>) + <?php else: ?> + (<span><span class="ui-helper-hidden-accessible"><?= t('Total number of tasks in this column across all swimlanes') ?> </span><?= $column['nb_unfiltered_tasks_across_swimlane'] ?></span>) + <?php endif ?> + </span> + <?php endif ?> + </span> + <?= $this->hook->render('template:board:column:header', array('swimlane' => $swimlane, 'column' => $column)) ?> + </div> + </th> + <?php endforeach ?> +</tr> + +<?= $this->hook->render('template:board:table:column:after-header-row', array('swimlane' => $swimlane)) ?> diff --git a/app/Template/board/table_column_first.php b/app/Template/board/table_column_first.php new file mode 100644 index 0000000..a6e2bf5 --- /dev/null +++ b/app/Template/board/table_column_first.php @@ -0,0 +1,20 @@ +<tr class="board-swimlane-columns-first"> + <?php foreach ($swimlane['columns'] as $column): ?> + <th class="board-column-header board-column-header-first board-column-header-<?= $column['id'] ?>" data-column-id="<?= $column['id'] ?>"> + + <!-- column in collapsed mode --> + <div class="board-column-collapsed"> + </div> + + <!-- column in expanded mode --> + <div class="board-column-expanded board-column-expanded-header"> + <span class="board-column-title"> + </span> + + <span class="pull-right"> + </span> + </div> + + </th> + <?php endforeach ?> +</tr> diff --git a/app/Template/board/table_container.php b/app/Template/board/table_container.php new file mode 100644 index 0000000..cb1533b --- /dev/null +++ b/app/Template/board/table_container.php @@ -0,0 +1,67 @@ +<div id="board-container" + class="<?= ($project['task_limit'] && array_key_exists('nb_active_tasks', $project) && $project['nb_active_tasks'] > $project['task_limit']) ? 'board-task-list-limit' : '' ?>"> + <?php if (empty($swimlanes) || empty($swimlanes[0]['nb_columns'])): ?> + <p class="alert alert-error"><?= t('There is no column or swimlane activated in your project!') ?></p> + <?php else: ?> + + <?php if (isset($not_editable)): ?> + <table id="board" class="board-project-<?= $project['id'] ?>"> + <?php else: ?> + <table id="board" + class="board-project-<?= $project['id'] ?>" + data-project-id="<?= $project['id'] ?>" + data-check-interval="<?= $board_private_refresh_interval ?>" + data-save-url="<?= $this->url->href('BoardAjaxController', 'save', array('project_id' => $project['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?>" + data-reload-url="<?= $this->url->href('BoardAjaxController', 'reload', array('project_id' => $project['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?>" + data-check-url="<?= $this->url->href('BoardAjaxController', 'check', array('project_id' => $project['id'], 'timestamp' => time(), 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?>" + data-task-creation-url="<?= $this->url->href('TaskCreationController', 'show', array('project_id' => $project['id'])) ?>" + > + <?php endif ?> + + <?php foreach ($swimlanes as $index => $swimlane): ?> + <?php if (! ($swimlane['nb_tasks'] === 0 && isset($not_editable))): ?> + + <?php if ($index === 0 && $swimlane['nb_swimlanes'] > 1): ?> + <!-- Render empty columns to setup the "grid" for collapsing columns (Only once and only if more than 1 swimlane in project) --> + <?= $this->render('board/table_column_first', array( + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + )) ?> + <?php endif ?> + + <?php if ($index === 0 && $swimlane['nb_swimlanes'] > 1): ?> + <!-- Only show first swimlane-header if project more than 1 swimlanes --> + <?= $this->render('board/table_swimlane', array( + 'project' => $project, + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + )) ?> + <?php endif ?> + + <?php if ($index > 0 && $swimlane['nb_swimlanes'] > 1): ?> + <?= $this->render('board/table_swimlane', array( + 'project' => $project, + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + )) ?> + <?php endif ?> + + <?= $this->render('board/table_column', array( + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + )) ?> + + <?= $this->render('board/table_tasks', array( + 'project' => $project, + 'swimlane' => $swimlane, + 'not_editable' => isset($not_editable), + 'board_highlight_period' => $board_highlight_period, + )) ?> + + <?php endif ?> + <?php endforeach ?> + + </table> + + <?php endif ?> +</div> diff --git a/app/Template/board/table_swimlane.php b/app/Template/board/table_swimlane.php new file mode 100644 index 0000000..287bde3 --- /dev/null +++ b/app/Template/board/table_swimlane.php @@ -0,0 +1,25 @@ +<!-- swimlane --> +<tr id="swimlane-<?= $swimlane['id'] ?>"> + <th class="board-swimlane-header" colspan="<?= $swimlane['nb_columns'] ?>"> + <?php if (! $not_editable): ?> + <a href="#" class="board-swimlane-toggle" data-swimlane-id="<?= $swimlane['id'] ?>"> + <i class="fa fa-chevron-circle-up hide-icon-swimlane-<?= $swimlane['id'] ?>" title="<?= t('Collapse swimlane') ?>" role="button" aria-label="<?= t('Collapse swimlane') ?>"></i> + <i class="fa fa-chevron-circle-down show-icon-swimlane-<?= $swimlane['id'] ?>" title="<?= t('Expand swimlane') ?>" role="button" aria-label="<?= t('Expand swimlane') ?>" style="display: none"></i> + </a> + <?php endif ?> + + <?= $this->text->e($swimlane['name']) ?> + + <?php if (! $not_editable && ! empty($swimlane['description'])): ?> + <?= $this->app->tooltipLink('<i class="fa fa-info-circle"></i>', $this->url->href('BoardTooltipController', 'swimlane', array('swimlane_id' => $swimlane['id'], 'project_id' => $project['id']))) ?> + <?php endif ?> + + <span title="<?= t('Number of tasks in this swimlane') ?>" class="board-column-header-task-count swimlane-task-count-<?= $swimlane['id'] ?>"> + <?php if ($swimlane['task_limit']): ?> + (<span><span class="ui-helper-hidden-accessible"><?= t('Number of tasks in this swimlane') ?> </span><?= $swimlane['nb_tasks'] ?>/<?= $swimlane['task_limit'] ?>) + <?php else: ?> + (<span><span class="ui-helper-hidden-accessible"><?= t('Number of tasks in this swimlane') ?> </span><?= $swimlane['nb_tasks'] ?>) + <?php endif ?> + </span> + </th> +</tr> diff --git a/app/Template/board/table_tasks.php b/app/Template/board/table_tasks.php new file mode 100644 index 0000000..2526591 --- /dev/null +++ b/app/Template/board/table_tasks.php @@ -0,0 +1,40 @@ +<!-- task row --> +<tr class="board-swimlane board-swimlane-tasks-<?= $swimlane['id'] ?><?= $swimlane['task_limit'] && $swimlane['nb_tasks'] > $swimlane['task_limit'] ? ' board-task-list-limit' : '' ?>"> + <?php foreach ($swimlane['columns'] as $column): ?> + <td class=" + board-column-<?= $column['id'] ?> + <?= $column['task_limit'] > 0 && $column['column_nb_open_tasks'] > $column['task_limit'] ? 'board-task-list-limit' : '' ?> + " + > + + <!-- tasks list --> + <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, + 'task' => $task, + 'board_highlight_period' => $board_highlight_period, + 'not_editable' => $not_editable, + )) ?> + <?php endforeach ?> + </div> + + <!-- column in collapsed mode (rotated text) --> + <div class="board-column-collapsed board-task-list sortable-column" + data-column-id="<?= $column['id'] ?>" + data-swimlane-id="<?= $swimlane['id'] ?>" + data-task-limit="<?= $column['task_limit'] ?>"> + <div class="board-rotation-wrapper"> + <div class="board-column-title board-rotation board-toggle-column-view" data-column-id="<?= $column['id'] ?>" title="<?= $this->text->e($column['title']) ?>"> + <i class="fa fa-plus-square" title="<?= t('Show this column') ?>" role="button" aria-label="<?= t('Show this column') ?>"></i> <?= $this->text->e($column['title']) ?> + </div> + </div> + </div> + </td> + <?php endforeach ?> +</tr> diff --git a/app/Template/board/task_avatar.php b/app/Template/board/task_avatar.php new file mode 100644 index 0000000..14dd239 --- /dev/null +++ b/app/Template/board/task_avatar.php @@ -0,0 +1,20 @@ +<?php if (! empty($task['owner_id'])): ?> +<div class="task-board-avatars"> + <span + <?php if ($this->user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + class="task-board-assignee task-board-change-assignee" + data-url="<?= $this->url->href('TaskModificationController', 'edit', array('task_id' => $task['id'])) ?>"> + <?php else: ?> + class="task-board-assignee"> + <?php endif ?> + <?= $this->avatar->small( + $task['owner_id'], + $task['assignee_username'], + $task['assignee_name'], + $task['assignee_email'], + $task['assignee_avatar_path'], + 'avatar-inline' + ) ?> + </span> +</div> +<?php endif ?> diff --git a/app/Template/board/task_footer.php b/app/Template/board/task_footer.php new file mode 100644 index 0000000..25f3586 --- /dev/null +++ b/app/Template/board/task_footer.php @@ -0,0 +1,141 @@ +<?php if (! empty($task['category_id'])): ?> +<div class="task-board-category-container task-board-category-container-color"> + <span class="task-board-category category-<?= $this->text->e($task['category_name']) ?> <?= $task['category_color_id'] ? "color-{$task['category_color_id']}" : '' ?>"> + <?php if ($not_editable): ?> + <?= $this->text->e($task['category_name']) ?> + <?php else: ?> + <?= $this->url->link( + $this->text->e($task['category_name']), + 'TaskModificationController', + 'edit', + array('task_id' => $task['id']), + false, + 'js-modal-large' . (! empty($task['category_description']) ? ' tooltip' : ''), + t('Change category') + ) ?> + <?php if (! empty($task['category_description'])): ?> + <?= $this->app->tooltipMarkdown($task['category_description']) ?> + <?php endif ?> + <?php endif ?> + </span> +</div> +<?php endif ?> + +<?php if (! empty($task['tags'])): ?> + <div class="task-tags"> + <ul> + <?php foreach ($task['tags'] as $tag): ?> + <li class="task-tag <?= $tag['color_id'] ? "color-{$tag['color_id']}" : '' ?>"><?= $this->text->e($tag['name']) ?></li> + <?php endforeach ?> + </ul> + </div> +<?php endif ?> + +<div class="task-board-icons"> + <div class="task-board-icons-row"> + <?php if ($task['reference']): ?> + <span class="task-board-reference" title="<?= t('Reference') ?>"> + <span class="ui-helper-hidden-accessible"><?= t('Reference') ?> </span><?= $this->task->renderReference($task) ?> + </span> + <?php endif ?> + </div> + <div class="task-board-icons-row"> + <?php if ($task['is_milestone'] == 1): ?> + <span title="<?= t('Milestone') ?>"> + <i class="fa fa-flag flag-milestone" role="img" aria-label="<?= t('Milestone') ?>"></i> + </span> + <?php endif ?> + + <?php if ($task['score']): ?> + <span class="task-score" title="<?= t('Complexity') ?>"> + <i class="fa fa-trophy" role="img" aria-label="<?= t('Complexity') ?>"></i> + <?= $this->text->e($task['score']) ?> + </span> + <?php endif ?> + + <?php if (! empty($task['time_estimated']) || ! empty($task['time_spent'])): ?> + <span class="task-time-estimated" title="<?= t('Time spent and estimated') ?>"> + <span class="ui-helper-hidden-accessible"><?= t('Time spent and estimated') ?> </span><?= $this->text->e($task['time_spent']) ?>/<?= $this->text->e($task['time_estimated']) ?>h + </span> + <?php endif ?> + + <?php if (! empty($task['date_due'])): ?> + <span class="task-date + <?php if (time() > $task['date_due']): ?> + task-date-overdue + <?php elseif (date('Y-m-d') == date('Y-m-d', $task['date_due'])): ?> + task-date-today + <?php endif ?> + "> + <i class="fa fa-calendar"></i> + <?php if (date('Hi', $task['date_due']) === '0000' ): ?> + <?= $this->dt->date($task['date_due']) ?> + <?php else: ?> + <?= $this->dt->datetime($task['date_due']) ?> + <?php endif ?> + </span> + <?php endif ?> + </div> + <div class="task-board-icons-row"> + + <?php if ($task['recurrence_status'] == \Kanboard\Model\TaskModel::RECURRING_STATUS_PENDING): ?> + <?= $this->app->tooltipLink('<i class="fa fa-refresh fa-rotate-90"></i>', $this->url->href('BoardTooltipController', 'recurrence', array('task_id' => $task['id']))) ?> + <?php endif ?> + + <?php if ($task['recurrence_status'] == \Kanboard\Model\TaskModel::RECURRING_STATUS_PROCESSED): ?> + <?= $this->app->tooltipLink('<i class="fa fa-refresh fa-rotate-90 fa-inverse"></i>', $this->url->href('BoardTooltipController', 'recurrence', array('task_id' => $task['id']))) ?> + <?php endif ?> + + <?php if (! empty($task['nb_links'])): ?> + <?= $this->app->tooltipLink('<i class="fa fa-code-fork fa-fw"></i>'.$task['nb_links'], $this->url->href('BoardTooltipController', 'tasklinks', array('task_id' => $task['id']))) ?> + <?php endif ?> + + <?php if (! empty($task['nb_external_links'])): ?> + <?= $this->app->tooltipLink('<i class="fa fa-external-link fa-fw"></i>'.$task['nb_external_links'], $this->url->href('BoardTooltipController', 'externallinks', array('task_id' => $task['id']))) ?> + <?php endif ?> + + <?php if (! empty($task['nb_subtasks'])): ?> + <?php $nb_subtasks = (int) $task['nb_subtasks']; $nb_completed = (int) $task['nb_completed_subtasks']; $percentage = $nb_subtasks > 0 ? round($nb_completed / $nb_subtasks * 100, 0) : 0; ?> + <?= $this->app->tooltipLink('<i class="fa fa-bars fa-fw"></i>'.$percentage.'% ('.$nb_completed.'/'.$nb_subtasks.')', $this->url->href('BoardTooltipController', 'subtasks', array('task_id' => $task['id']))) ?> + <?php endif ?> + + <?php if (! empty($task['nb_files'])): ?> + <?= $this->app->tooltipLink('<i class="fa fa-paperclip fa-fw"></i>'.$task['nb_files'], $this->url->href('BoardTooltipController', 'attachments', array('task_id' => $task['id']))) ?> + <?php endif ?> + + <?php if ($task['nb_comments'] > 0): ?> + <?php if ($not_editable): ?> + <?php $aria_label = $task['nb_comments'] == 1 ? t('%d comment', $task['nb_comments']) : t('%d comments', $task['nb_comments']); ?> + <span title="<?= $aria_label ?>" role="img" aria-label="<?= $aria_label ?>"><i class="fa fa-comments-o"></i> <?= $task['nb_comments'] ?></span> + <?php else: ?> + <?= $this->modal->medium( + 'comments-o', + $task['nb_comments'], + 'CommentListController', + 'show', + array('task_id' => $task['id']), + $task['nb_comments'] == 1 ? t('%d comment', $task['nb_comments']) : t('%d comments', $task['nb_comments']) + ) ?> + <?php endif ?> + <?php endif ?> + + <?php if (! empty($task['description'])): ?> + <?= $this->app->tooltipLink('<i class="fa fa-file-text-o"></i>', $this->url->href('BoardTooltipController', 'description', array('task_id' => $task['id']))) ?> + <?php endif ?> + + <?php if ($task['is_active'] == 1): ?> + <div class="task-icon-age"> + <span title="<?= t('Task age in days')?>" class="task-icon-age-total"><span class="ui-helper-hidden-accessible"><?= t('Task age in days') ?> </span><?= $this->dt->age($task['date_creation']) ?></span> + <span title="<?= t('Days in this column')?>" class="task-icon-age-column"><span class="ui-helper-hidden-accessible"><?= t('Days in this column') ?> </span><?= $this->dt->age($task['date_moved']) ?></span> + </div> + <?php else: ?> + <span class="task-board-closed"><i class="fa fa-ban fa-fw"></i><?= t('Closed') ?></span> + <?php endif ?> + + <?= $this->task->renderPriority($task['priority']) ?> + + <?= $this->hook->render('template:board:task:icons', array('task' => $task)) ?> + </div> +</div> + +<?= $this->hook->render('template:board:task:footer', array('task' => $task)) ?> diff --git a/app/Template/board/task_private.php b/app/Template/board/task_private.php new file mode 100644 index 0000000..276a407 --- /dev/null +++ b/app/Template/board/task_private.php @@ -0,0 +1,71 @@ +<div class=" + task-board + <?= $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'] ?>" + data-swimlane-id="<?= $task['swimlane_id'] ?>" + data-position="<?= $task['position'] ?>" + data-owner-id="<?= $task['owner_id'] ?>" + data-category-id="<?= $task['category_id'] ?>" + data-due-date="<?= $task['date_due'] ?>" + data-task-url="<?= $this->url->href('TaskViewController', 'show', array('task_id' => $task['id'])) ?>"> + + <div class="task-board-sort-handle" style="display: none;"><i class="fa fa-arrows-alt"></i></div> + + <?php if ($this->board->isCollapsed($task['project_id'])): ?> + <div class="task-board-collapsed"> + <div class="task-board-saving-icon" style="display: none;"><i class="fa fa-spinner fa-pulse"></i></div> + <?php if ($this->user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + <?= $this->render('task/dropdown', array('task' => $task, 'redirect' => 'board')) ?> + <?php if ($this->projectRole->canUpdateTask($task)): ?> + <?= $this->modal->large('edit', '', 'TaskModificationController', 'edit', array('task_id' => $task['id'])) ?> + <?php endif ?> + <?php else: ?> + <strong><?= '#'.$task['id'] ?></strong> + <?php endif ?> + + <?php if (! empty($task['assignee_username'])): ?> + <span title="<?= $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?>"> + <?= $this->text->e($this->user->getInitials($task['assignee_name'] ?: $task['assignee_username'])) ?> + </span> - + <?php endif ?> + <?= $this->url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id']), false, '', $this->text->e($task['title'])) ?> + </div> + <?php else: ?> + <div class="task-board-expanded"> + <div class="task-board-saving-icon" style="display: none;"><i class="fa fa-spinner fa-pulse fa-2x"></i></div> + <div class="task-board-header"> + <?php if ($this->user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + <?= $this->render('task/dropdown', array('task' => $task, 'redirect' => 'board')) ?> + <?php if ($this->projectRole->canUpdateTask($task)): ?> + <?= $this->modal->large('edit', '', 'TaskModificationController', 'edit', array('task_id' => $task['id'])) ?> + <?php endif ?> + <?php else: ?> + <strong><?= '#'.$task['id'] ?></strong> + <?php endif ?> + + <?php if (! empty($task['owner_id'])): ?> + <span class="task-board-assignee"> + <?= $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?> + </span> + <?php endif ?> + + <?= $this->render('board/task_avatar', array('task' => $task)) ?> + </div> + + <?= $this->hook->render('template:board:private:task:before-title', array('task' => $task)) ?> + <div class="task-board-title"> + <?= $this->url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'])) ?> + </div> + <?= $this->hook->render('template:board:private:task:after-title', array('task' => $task)) ?> + + <?= $this->render('board/task_footer', array( + 'task' => $task, + 'not_editable' => $not_editable, + 'project' => $project, + )) ?> + </div> + <?php endif ?> +</div> diff --git a/app/Template/board/task_public.php b/app/Template/board/task_public.php new file mode 100644 index 0000000..8e429ea --- /dev/null +++ b/app/Template/board/task_public.php @@ -0,0 +1,25 @@ +<div class="task-board color-<?= $task['color_id'] ?> <?= $task['date_modification'] > time() - $board_highlight_period ? 'task-board-recent' : '' ?>"> + <div class="task-board-header"> + <?= $this->url->link('#'.$task['id'], 'TaskViewController', 'readonly', array('task_id' => $task['id'], 'token' => $project['token'])) ?> + + <?php if (! empty($task['owner_id'])): ?> + <span class="task-board-assignee"> + <?= $this->text->e($task['assignee_name'] ?: $task['assignee_username']) ?> + </span> + <?php endif ?> + + <?= $this->render('board/task_avatar', array('task' => $task)) ?> + </div> + + <?= $this->hook->render('template:board:public:task:before-title', array('task' => $task)) ?> + <div class="task-board-title"> + <?= $this->url->link($this->text->e($task['title']), 'TaskViewController', 'readonly', array('task_id' => $task['id'], 'token' => $project['token'])) ?> + </div> + <?= $this->hook->render('template:board:public:task:after-title', array('task' => $task)) ?> + + <?= $this->render('board/task_footer', array( + 'task' => $task, + 'not_editable' => $not_editable, + 'project' => $project, + )) ?> +</div> diff --git a/app/Template/board/tooltip_description.php b/app/Template/board/tooltip_description.php new file mode 100644 index 0000000..7e0e343 --- /dev/null +++ b/app/Template/board/tooltip_description.php @@ -0,0 +1,5 @@ +<section class="tooltip-large"> +<div class="markdown"> + <?= $this->text->markdown($task['description']) ?> +</div> +</section> \ No newline at end of file diff --git a/app/Template/board/tooltip_external_links.php b/app/Template/board/tooltip_external_links.php new file mode 100644 index 0000000..fd5237a --- /dev/null +++ b/app/Template/board/tooltip_external_links.php @@ -0,0 +1,22 @@ +<div class="tooltip-large"> + <table class="table-small"> + <tr> + <th class="column-20"><?= t('Type') ?></th> + <th class="column-70"><?= t('Title') ?></th> + <th class="column-10"><?= t('Dependency') ?></th> + </tr> + <?php foreach ($links as $link): ?> + <tr> + <td> + <?= $link['type'] ?> + </td> + <td> + <a href="<?= $this->text->e($link['url']) ?>" title="<?= $this->text->e($link['url']) ?>" target="_blank"><?= $this->text->e($link['title']) ?></a> + </td> + <td> + <?= $this->text->e($link['dependency_label']) ?> + </td> + </tr> + <?php endforeach ?> + </table> +</div> diff --git a/app/Template/board/tooltip_files.php b/app/Template/board/tooltip_files.php new file mode 100644 index 0000000..29917f1 --- /dev/null +++ b/app/Template/board/tooltip_files.php @@ -0,0 +1,24 @@ +<div class="tooltip-large"> + <table class="table-small"> + <?php foreach ($files as $file): ?> + <tr> + <th> + <i class="fa <?= $this->file->icon($file['name']) ?> fa-fw"></i> + <?= $this->text->e($file['name']) ?> + </th> + </tr> + <tr> + <td> + <?= $this->url->icon('download', t('Download'), 'FileViewerController', 'download', array('task_id' => $task['id'], 'file_id' => $file['id'], 'etag' => $file['etag'])) ?> + <?php if ($this->file->getPreviewType($file['name']) !== null || $file['is_image'] == 1): ?> +  <?= $this->modal->large('eye', t('View file'), 'FileViewerController', 'show', array('task_id' => $task['id'], 'file_id' => $file['id'], 'etag' => $file['etag'])) ?> +  <?= $this->url->icon('external-link', t('View file'), 'FileViewerController', ($file['is_image'] == 1 ? 'image' : 'show'), array('task_id' => $task['id'], 'file_id' => $file['id'], 'etag' => $file['etag']), false, '', '', true) ?> + <?php elseif ($this->file->getBrowserViewType($file['name']) !== null): ?> + <i class="fa fa-eye fa-fw"></i> + <?= $this->url->link(t('View file'), 'FileViewerController', 'browser', array('task_id' => $task['id'], 'file_id' => $file['id']), false, '', '', true) ?> + <?php endif ?> + </td> + </tr> + <?php endforeach ?> + </table> +</div> diff --git a/app/Template/board/tooltip_subtasks.php b/app/Template/board/tooltip_subtasks.php new file mode 100644 index 0000000..753e491 --- /dev/null +++ b/app/Template/board/tooltip_subtasks.php @@ -0,0 +1,24 @@ +<div class="tooltip-large"> + <table class="table-small"> + <tr> + <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): ?> + <tr> + <td> + <?= $this->subtask->renderToggleStatus($task, $subtask) ?> + </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']) ?> + <?php else: ?> + <?= t('Not assigned') ?> + <?php endif ?> + </td> + </tr> + <?php endforeach ?> + </table> +</div> diff --git a/app/Template/board/tooltip_tasklinks.php b/app/Template/board/tooltip_tasklinks.php new file mode 100644 index 0000000..1d8d8d6 --- /dev/null +++ b/app/Template/board/tooltip_tasklinks.php @@ -0,0 +1,34 @@ +<div class="tooltip-large"> + <table class="table-small"> + <?php foreach ($links as $label => $grouped_links): ?> + <tr> + <th colspan="4"><?= t($label) ?></th> + </tr> + <?php foreach ($grouped_links as $link): ?> + <tr> + <td class="column-10"> + <?= $this->task->getProgress($link).'%' ?> + </td> + <td class="column-60"> + <?= $this->url->link( + $this->text->e('#'.$link['task_id'].' '.$link['title']), + 'TaskViewController', 'show', array('task_id' => $link['task_id']), + false, + $link['is_active'] ? '' : 'task-link-closed' + ) ?> + </td> + <td> + <?php if (! empty($link['task_assignee_username'])): ?> + <?= $this->text->e($link['task_assignee_name'] ?: $link['task_assignee_username']) ?> + <?php else: ?> + <?= t('Not assigned') ?> + <?php endif ?> + </td> + <td> + <?= $link['project_name'] ?> + </td> + </tr> + <?php endforeach ?> + <?php endforeach ?> + </table> +</div> diff --git a/app/Template/board/view_private.php b/app/Template/board/view_private.php new file mode 100644 index 0000000..a89e7d2 --- /dev/null +++ b/app/Template/board/view_private.php @@ -0,0 +1,12 @@ +<section id="main"> + + <?= $this->projectHeader->render($project, 'BoardViewController', 'show', true) ?> + + <?= $this->render('board/table_container', array( + 'project' => $project, + 'swimlanes' => $swimlanes, + 'board_private_refresh_interval' => $board_private_refresh_interval, + 'board_highlight_period' => $board_highlight_period, + )) ?> + +</section> diff --git a/app/Template/board/view_public.php b/app/Template/board/view_public.php new file mode 100644 index 0000000..aea7203 --- /dev/null +++ b/app/Template/board/view_public.php @@ -0,0 +1,11 @@ +<section id="main" class="public-board"> + + <?= $this->render('board/table_container', array( + 'project' => $project, + 'swimlanes' => $swimlanes, + 'board_private_refresh_interval' => $board_private_refresh_interval, + 'board_highlight_period' => $board_highlight_period, + 'not_editable' => true, + )) ?> + +</section> \ No newline at end of file diff --git a/app/Template/board_popover/close_all_tasks_column.php b/app/Template/board_popover/close_all_tasks_column.php new file mode 100644 index 0000000..ab7c2d4 --- /dev/null +++ b/app/Template/board_popover/close_all_tasks_column.php @@ -0,0 +1,15 @@ +<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> + + <?= $this->modal->submitButtons(array( + 'submitLabel' => t('Yes'), + 'color' => 'red', + )) ?> +</form> diff --git a/app/Template/category/create.php b/app/Template/category/create.php new file mode 100644 index 0000000..32501b8 --- /dev/null +++ b/app/Template/category/create.php @@ -0,0 +1,14 @@ +<div class="page-header"> + <h2><?= t('Add a new category') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('CategoryController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <?= $this->form->label(t('Category Name'), 'name') ?> + <?= $this->form->text('name', $values, $errors, array('autofocus', 'required')) ?> + + <?= $this->form->label(t('Color'), 'color_id') ?> + <?= $this->form->select('color_id', array('' => t('No color')) + $colors, $values, $errors, array(), 'color-picker') ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/category/edit.php b/app/Template/category/edit.php new file mode 100644 index 0000000..446ce7b --- /dev/null +++ b/app/Template/category/edit.php @@ -0,0 +1,18 @@ +<div class="page-header"> + <h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2> +</div> + +<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->label(t('Category Name'), 'name') ?> + <?= $this->form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"', 'tabindex="1"')) ?> + + <?= $this->form->label(t('Description'), 'description') ?> + <?= $this->form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> + + <?= $this->form->label(t('Color'), 'color_id') ?> + <?= $this->form->select('color_id', array('' => t('No color')) + $colors, $values, $errors, array(), 'color-picker') ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/category/index.php b/app/Template/category/index.php new file mode 100644 index 0000000..36f666d --- /dev/null +++ b/app/Template/category/index.php @@ -0,0 +1,42 @@ +<div class="page-header"> + <h2><?= t('Categories') ?></h2> + <ul> + <li> + <?= $this->modal->medium('plus', t('Add a new category'), 'CategoryController', 'create', array('project_id' => $project['id'])) ?> + </li> + </ul> +</div> +<?php if (empty($categories)): ?> + <p class="alert"><?= t('There is no category in this project.') ?></p> +<?php else: ?> + <table class="table-striped"> + <tr> + <th><?= t('Category Name') ?></th> + <th><?= t('Color') ?></th> + </tr> + <?php foreach ($categories as $category): ?> + <tr> + <td> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li> + <?= $this->modal->medium('edit', t('Edit'), 'CategoryController', 'edit', array('project_id' => $project['id'], 'category_id' => $category['id'])) ?> + </li> + <li> + <?= $this->modal->confirm('trash-o', t('Remove'), 'CategoryController', 'confirm', array('project_id' => $project['id'], 'category_id' => $category['id'])) ?> + </li> + </ul> + </div> + + <?= $this->text->e($category['name']) ?> + + <?php if (! empty($category['description'])): ?> + <?= $this->app->tooltipMarkdown($category['description']) ?> + <?php endif ?> + </td> + <td><?= $this->text->e($colors[$category['color_id']] ?? '') ?></td> + </tr> + <?php endforeach ?> + </table> +<?php endif ?> diff --git a/app/Template/category/remove.php b/app/Template/category/remove.php new file mode 100644 index 0000000..79e8a56 --- /dev/null +++ b/app/Template/category/remove.php @@ -0,0 +1,15 @@ +<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> + + <?= $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 new file mode 100644 index 0000000..1a91ee0 --- /dev/null +++ b/app/Template/column/create.php @@ -0,0 +1,19 @@ +<div class="page-header"> + <h2><?= t('Add a new column') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ColumnController', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <?= $this->form->label(t('Title'), 'title') ?> + <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="191"', 'tabindex="1"')) ?> + + <?= $this->form->label(t('Task limit'), 'task_limit') ?> + <?= $this->form->number('task_limit', $values, $errors, array('tabindex="2"', 'min="0"')) ?> + + <?= $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->textEditor('description', $values, $errors, array('tabindex' => 4)) ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/column/edit.php b/app/Template/column/edit.php new file mode 100644 index 0000000..c5a3ae1 --- /dev/null +++ b/app/Template/column/edit.php @@ -0,0 +1,20 @@ +<div class="page-header"> + <h2><?= t('Edit column "%s"', $column['title']) ?></h2> +</div> + +<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->label(t('Title'), 'title') ?> + <?= $this->form->text('title', $values, $errors, array('autofocus', 'required', 'maxlength="191"')) ?> + + <?= $this->form->label(t('Task limit'), 'task_limit') ?> + <?= $this->form->number('task_limit', $values, $errors, array('min="0"')) ?> + + <?= $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->textEditor('description', $values, $errors) ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/column/index.php b/app/Template/column/index.php new file mode 100644 index 0000000..5e6f468 --- /dev/null +++ b/app/Template/column/index.php @@ -0,0 +1,64 @@ +<div class="page-header"> + <h2><?= t('Edit the board for "%s"', $project['name']) ?></h2> + <ul> + <li> + <?= $this->modal->medium('plus', t('Add a new column'), 'ColumnController', 'create', array('project_id' => $project['id'])) ?> + </li> + </ul> +</div> + +<?php if (empty($columns)): ?> + <p class="alert alert-error"><?= t('Your board doesn\'t have any columns!') ?></p> +<?php else: ?> + <table + class="columns-table table-striped" + data-save-position-url="<?= $this->url->href('ColumnController', 'move', array('project_id' => $project['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?>"> + <thead> + <tr> + <th><?= t('Column') ?></th> + <th class="column-10"><?= t('Task limit') ?></th> + <th class="column-15"><?= t('Visible on dashboard') ?></th> + <th class="column-12"><?= t('Open tasks') ?></th> + <th class="column-12"><?= t('Closed tasks') ?></th> + </tr> + </thead> + <tbody> + <?php foreach ($columns as $column): ?> + <tr data-column-id="<?= $column['id'] ?>"> + <td> + <i class="fa fa-arrows-alt draggable-row-handle" title="<?= t('Change column position') ?>" role="button" aria-label="<?= t('Change column position') ?>"></i>  + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li> + <?= $this->modal->medium('edit', t('Edit'), 'ColumnController', 'edit', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?> + </li> + <?php if ($column['nb_open_tasks'] == 0 && $column['nb_closed_tasks'] == 0): ?> + <li> + <?= $this->modal->confirm('trash-o', t('Remove'), 'ColumnController', 'confirm', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?> + </li> + <?php endif ?> + </ul> + </div> + <?= $this->text->e($column['title']) ?> + <?php if (! empty($column['description'])): ?> + <?= $this->app->tooltipMarkdown($column['description']) ?> + <?php endif ?> + </td> + <td> + <?= $column['task_limit'] ?: '∞' ?> + </td> + <td> + <?= $column['hide_in_dashboard'] == 0 ? t('Yes') : t('No') ?> + </td> + <td> + <?= $column['nb_open_tasks'] ?> + </td> + <td> + <?= $column['nb_closed_tasks'] ?> + </td> + </tr> + <?php endforeach ?> + </tbody> + </table> +<?php endif ?> diff --git a/app/Template/column/remove.php b/app/Template/column/remove.php new file mode 100644 index 0000000..b4e7942 --- /dev/null +++ b/app/Template/column/remove.php @@ -0,0 +1,15 @@ +<div class="page-header"> + <h2><?= t('Remove a column') ?></h2> +</div> + +<div class="confirm"> + <p class="alert alert-info"> + <?= t('Do you really want to remove this column: "%s"?', $column['title']) ?> + </p> + + <?= $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 0000000..cd9e1bf --- /dev/null +++ b/app/Template/column_move_restriction/create.php @@ -0,0 +1,20 @@ +<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->form->checkbox('only_assigned', t('Only for tasks assigned to the current user'), 1, isset($values['only_assigned']) && $values['only_assigned'] == 1) ?> + + <?= $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 0000000..4902cd2 --- /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 0000000..be158f1 --- /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 0000000..edbd9d6 --- /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 new file mode 100644 index 0000000..4d815c5 --- /dev/null +++ b/app/Template/comment/create.php @@ -0,0 +1,36 @@ +<?php use Kanboard\Core\Security\Role; ?> +<div class="page-header"> + <h2><?= t('Add a comment') ?></h2> + <ul> + <li> + <?= $this->modal->medium('paper-plane', t('Send by email'), 'CommentMailController', 'create', array('task_id' => $task['id'])) ?> + </li> + </ul> +</div> +<form method="post" action="<?= $this->url->href('CommentController', 'save', array('task_id' => $task['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true, 'aria-label' => t('New comment'))) ?> + + <?php + $formName = 'visibility'; + $visibilityOptions['app-user'] = t('Standard users'); + $attribute[] = ('hidden'); + ?> + + <?php if ($this->user->getRole() !== Role::APP_USER) { + echo $this->form->label(t('Visibility:'), $formName); + $attribute = []; + $visibilityOptions['app-user'] = t('Standard users'); + $visibilityOptions['app-manager'] = t('Application managers or more'); + } + ?> + + <?php if ($this->user->getRole() === Role::APP_ADMIN) { + $visibilityOptions['app-admin'] = t('Administrators'); + } + ?> + + <?= $this->form->select($formName, $visibilityOptions, array(), array(), $attribute) ?> + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/comment/edit.php b/app/Template/comment/edit.php new file mode 100644 index 0000000..d2e073e --- /dev/null +++ b/app/Template/comment/edit.php @@ -0,0 +1,11 @@ +<div class="page-header"> + <h2><?= t('Edit a comment') ?></h2> +</div> + +<form method="post" action="<?= $this->url->href('CommentController', 'update', array('task_id' => $task['id'], 'comment_id' => $comment['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <?= $this->form->textEditor('comment', $values, $errors, array('autofocus' => true, 'required' => true, 'aria-label' => t('Comment'))) ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/comment/remove.php b/app/Template/comment/remove.php new file mode 100644 index 0000000..14ab478 --- /dev/null +++ b/app/Template/comment/remove.php @@ -0,0 +1,21 @@ +<div class="page-header"> + <h2><?= t('Remove a comment') ?></h2> +</div> + +<div class="confirm"> + <p class="alert alert-info"> + <?= t('Do you really want to remove this comment?') ?> + </p> + + <?= $this->render('comment/show', array( + 'comment' => $comment, + 'task' => $task, + 'hide_actions' => true + )) ?> + + <?= $this->modal->confirmButtons( + 'CommentController', + 'remove', + array('task_id' => $task['id'], 'comment_id' => $comment['id']) + ) ?> +</div> diff --git a/app/Template/comment/show.php b/app/Template/comment/show.php new file mode 100644 index 0000000..b0fdab8 --- /dev/null +++ b/app/Template/comment/show.php @@ -0,0 +1,75 @@ +<?php + +use Kanboard\Core\Security\Role; + +$userRole = $this->user->getRole(); + +if ($userRole === '' && $comment['visibility'] !== Role::APP_USER) { + return; +} + +if ($userRole === Role::APP_MANAGER && $comment['visibility'] === Role::APP_ADMIN) { + return; +} + +if ($userRole === Role::APP_USER && $comment['visibility'] !== Role::APP_USER) { + return; +} + +?> + +<div class="comment <?= isset($preview) ? 'comment-preview' : '' ?>" id="comment-<?= $comment['id'] ?>"> + + <?= $this->avatar->render($comment['user_id'], $comment['username'], $comment['name'], $comment['email'], $comment['avatar_path']) ?> + + <div class="comment-title"> + <?php if (! empty($comment['username'])): ?> + <strong class="comment-username"><?= $this->text->e($comment['name'] ?: $comment['username']) ?></strong> + <?php endif ?> + + <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> + <small class="comment-visibility"><?= t('Visibility:') ?> + <?php if ($comment['visibility'] === Role::APP_USER): ?> + <?= t('Standard users') ?> + <?php elseif ($comment['visibility'] === Role::APP_MANAGER): ?> + <?= t('Application managers or more') ?> + <?php else: ?> + <?= t('Administrators') ?> + <?php endif ?> + </small> + </div> + + <?php if (! isset($hide_actions)): ?> + <div class="comment-actions"> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li> + <?= $this->url->icon('link', t('Link'), 'TaskViewController', 'show', array('task_id' => $task['id']), false, '', '', $this->app->isAjax(), 'comment-'.$comment['id']) ?> + </li> + <li data-comment-id="<?= $comment['id'] ?>"> + <?= $this->url->icon('reply', t('Reply'), 'TaskViewController', 'show', array('task_id' => $task['id']), false, 'js-reply-to-comment', '', $this->app->isAjax(), 'form-task_id') ?> + </li> + <?php if ($editable && ($this->user->isAdmin() || $this->user->isCurrentUser($comment['user_id']))): ?> + <li> + <?= $this->modal->medium('edit', t('Edit'), 'CommentController', 'edit', array('task_id' => $task['id'], 'comment_id' => $comment['id'])) ?> + </li> + <li> + <?= $this->modal->confirm('trash-o', t('Remove'), 'CommentController', 'confirm', array('task_id' => $task['id'], 'comment_id' => $comment['id'])) ?> + </li> + <?php endif ?> + </ul> + </div> + </div> + <?php endif ?> + + <div class="comment-content"> + <div class="markdown"> + <?= $this->text->markdown($comment['comment'], isset($is_public) && $is_public) ?> + </div> + <template id="comment-reply-content-<?= $comment['id'] ?>"> + <textarea><?= $this->text->reply($comment['name'] ?: $comment['username'], $comment['comment']) ?></textarea> + </template> + </div> +</div> \ No newline at end of file diff --git a/app/Template/comment_list/create.php b/app/Template/comment_list/create.php new file mode 100644 index 0000000..f5de18b --- /dev/null +++ b/app/Template/comment_list/create.php @@ -0,0 +1,30 @@ +<?php use Kanboard\Core\Security\Role;?> +<div class="page-header"> + <h2><?= t('Add a comment') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('CommentListController', 'save', array('task_id' => $task['id'])) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + <?= $this->form->textEditor('comment', array('project_id' => $task['project_id']), array(), array('required' => true, 'aria-label' => t('New comment'))) ?> + + <?php + $formName = 'visibility'; + $visibilityOptions['app-user'] = t('Standard users'); + $attribute[] = ('hidden'); + ?> + + <?php if ($this->user->getRole() !== Role::APP_USER) { + echo $this->form->label(t('Visibility:'), $formName); + $attribute = []; + $visibilityOptions['app-user'] = t('Standard users'); + $visibilityOptions['app-manager'] = t('Application managers or more'); + } + ?> + + <?php if ($this->user->getRole() === Role::APP_ADMIN) { + $visibilityOptions['app-admin'] = t('Administrators'); + } + ?> + + <?= $this->form->select($formName, $visibilityOptions, array(), array(), $attribute) ?> + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/comment_list/show.php b/app/Template/comment_list/show.php new file mode 100644 index 0000000..a39891f --- /dev/null +++ b/app/Template/comment_list/show.php @@ -0,0 +1,31 @@ +<div class="page-header"> + <h2><?= $this->text->e($task['title']) ?></h2> + <?php if (!isset($is_public) || !$is_public): ?> + <ul> + <li> + <?= $this->url->icon('sort', t('Change sorting'), 'CommentListController', 'toggleSorting', array('task_id' => $task['id']), false, 'js-modal-replace') ?> + </li> + <?php if ($editable): ?> + <li> + <?= $this->modal->medium('paper-plane', t('Send by email'), 'CommentMailController', 'create', array('task_id' => $task['id'])) ?> + </li> + <?php endif ?> + </ul> + <?php endif ?> +</div> +<div class="comments"> + <?php foreach ($comments as $comment): ?> + <?= $this->render('comment/show', array( + 'comment' => $comment, + 'task' => $task, + 'editable' => $editable, + 'is_public' => isset($is_public) && $is_public, + )) ?> + <?php endforeach ?> + + <?php if ($editable): ?> + <?= $this->render('comment_list/create', array( + 'task' => $task, + )) ?> + <?php endif ?> +</div> diff --git a/app/Template/comment_mail/create.php b/app/Template/comment_mail/create.php new file mode 100644 index 0000000..d841289 --- /dev/null +++ b/app/Template/comment_mail/create.php @@ -0,0 +1,73 @@ +<?php use Kanboard\Core\Security\Role; ?> +<div class="page-header"> + <h2><?= t('Create and send a comment by email') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('CommentMailController', 'save', array('task_id' => $task['id'])) ?>" autocomplete="off" class="js-mail-form"> + <?= $this->form->csrf() ?> + <?= $this->form->hidden('task_id', $values) ?> + <?= $this->form->hidden('user_id', $values) ?> + + <?= $this->form->label(t('Email'), 'emails') ?> + <?= $this->form->text('emails', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + + <?php if (! empty($members)): ?> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-address-card-o"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php foreach ($members as $member): ?> + <li data-email="<?= $this->text->e($member['email']) ?>" class="js-autocomplete-email"> + <?= $this->text->e($this->user->getFullname($member)) ?> + </li> + <?php endforeach ?> + </ul> + </div> + <?php endif ?> + + <?= $this->form->label(t('Subject'), 'subject') ?> + <?= $this->form->text('subject', $values, $errors, array('required', 'tabindex="2"')) ?> + + <?php if (! empty($project['predefined_email_subjects'])): ?> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-archive"></i><i class="fa fa-caret-down"></i></a> + <ul> + <?php foreach (explode("\r\n", trim($project['predefined_email_subjects'])) as $subject): ?> + <?php $subject = trim($subject); ?> + + <?php if (! empty($subject)): ?> + <li data-subject="<?= $this->text->e($subject) ?>" class="js-autocomplete-subject"> + <?= $this->text->e($subject) ?> + </li> + <?php endif ?> + <?php endforeach ?> + </ul> + </div> + <?php endif ?> + + <?= $this->form->textEditor('comment', $values, $errors, array('required' => true, 'tabindex' => 3, 'aria-label' => t('New comment'))) ?> + + <?php + $formName = 'visibility'; + $visibilityOptions['app-user'] = t('Standard users'); + $attribute[] = ('hidden'); + ?> + + <?php if ($this->user->getRole() !== Role::APP_USER) { + echo $this->form->label(t('Visibility:'), $formName); + $attribute = []; + $visibilityOptions['app-user'] = t('Standard users'); + $visibilityOptions['app-manager'] = t('Application managers or more'); + } + ?> + + <?php if ($this->user->getRole() === Role::APP_ADMIN) { + $visibilityOptions['app-admin'] = t('Administrators'); + } + ?> + + <?= $this->form->select($formName, $visibilityOptions, array(), array(), $attribute) ?> + + <?= $this->modal->submitButtons(array( + 'submitLabel' => t('Send by email'), + 'tabindex' => 4, + )) ?> +</form> diff --git a/app/Template/comment_mail/email.php b/app/Template/comment_mail/email.php new file mode 100644 index 0000000..612fbb2 --- /dev/null +++ b/app/Template/comment_mail/email.php @@ -0,0 +1,3 @@ +<?= $this->text->markdown($email['comment'], true) ?> + +<?= $this->render('notification/footer', array('task' => $task)) ?> diff --git a/app/Template/config/about.php b/app/Template/config/about.php new file mode 100644 index 0000000..4777bd8 --- /dev/null +++ b/app/Template/config/about.php @@ -0,0 +1,94 @@ +<div class="page-header"> + <h2><?= t('About') ?></h2> +</div> +<div class="panel"> + <ul> + <li> + <?= t('Official website:') ?> + <a href="https://kanboard.org/" target="_blank" rel="noopener noreferrer">https://kanboard.org/</a> + </li> + <li> + <?= t('Author:') ?> + <strong>Frédéric Guillot</strong> (<a href="https://github.com/kanboard/kanboard/graphs/contributors" target="_blank" rel="noopener noreferrer"><?= t('contributors') ?></a>) + </li> + <li> + <?= t('License:') ?> + <strong>MIT</strong> + </li> + </ul> +</div> + +<div class="page-header"> + <h2><?= t('Configuration') ?></h2> +</div> +<div class="panel"> + <ul> + <li> + <?= t('Application version:') ?> + <strong><?= APP_VERSION ?></strong> + </li> + <li> + <?= t('PHP version:') ?> + <strong><?= PHP_VERSION ?></strong> + </li> + <li> + <?= t('PHP SAPI:') ?> + <strong><?= PHP_SAPI ?></strong> + </li> + <li> + <?= t('HTTP Client:') ?> + <strong><?= Kanboard\Core\Http\Client::backend() ?></strong> + </li> + <li> + <?= t('OS version:') ?> + <strong><?= @php_uname('s').' '.@php_uname('r') ?></strong> + </li> + <li> + <?= t('Database driver:') ?> + <strong><?= DB_DRIVER ?></strong> + </li> + <li> + <?= t('Database version:') ?> + <strong><?= $this->text->e($db_version) ?></strong> + </li> + <li> + <?= t('Browser:') ?> + <strong><?= $this->text->e($user_agent) ?></strong> + </li> + </ul> +</div> + +<?php if (DB_DRIVER === 'sqlite'): ?> + <div class="page-header"> + <h2><?= t('Database') ?></h2> + </div> + <div class="panel"> + <ul> + <li> + <?= t('Database size:') ?> + <strong><?= $this->text->bytes($db_size) ?></strong> + </li> + <li> + <?= $this->url->link(t('Download the database'), 'ConfigController', 'downloadDb', array(), true) ?>  + <?= t('(Gzip compressed Sqlite file)') ?> + </li> + <li> + <?= $this->url->link(t('Upload the database'), 'ConfigController', 'uploadDb', array(), false, 'js-modal-medium') ?> + </li> + <li> + <?= $this->url->link(t('Optimize the database'), 'ConfigController', 'optimizeDb', array(), true) ?>  + <?= t('(VACUUM command)') ?> + </li> + <?php foreach ($db_options as $option => $value): ?> + <li><strong><?= $this->text->e($option) ?></strong> = <?= $this->text->e($value) ?></li> + <?php endforeach ?> + </ul> + </div> +<?php endif ?> + +<div class="page-header"> + <h2><?= t('License') ?></h2> +</div> +<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 new file mode 100644 index 0000000..3480b57 --- /dev/null +++ b/app/Template/config/api.php @@ -0,0 +1,17 @@ +<div class="page-header"> + <h2><?= t('API') ?></h2> +</div> +<div class="panel"> + <ul> + <li> + <?= t('API token:') ?> + <strong><?= $this->text->e($values['api_token']) ?></strong> + </li> + <li> + <?= t('API endpoint:') ?> + <strong><?= $this->text->e($this->url->base()).'jsonrpc.php' ?></strong> + </li> + </ul> +</div> + +<?= $this->url->link(t('Reset token'), 'ConfigController', 'token', array('type' => 'api'), true, 'btn btn-red') ?> \ No newline at end of file diff --git a/app/Template/config/application.php b/app/Template/config/application.php new file mode 100644 index 0000000..c94a7b7 --- /dev/null +++ b/app/Template/config/application.php @@ -0,0 +1,44 @@ +<div class="page-header"> + <h2><?= t('Application settings') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'application')) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <fieldset> + <?= $this->form->label(t('Application URL'), 'application_url') ?> + <?= $this->form->text('application_url', $values, $errors, array('placeholder="https://example.kanboard.org/"')) ?> + <p class="form-help"><?= t('Example: https://example.kanboard.org/ (used to generate absolute URLs)') ?></p> + + <?= $this->form->label(t('Language'), 'application_language') ?> + <?= $this->form->select('application_language', $languages, $values, $errors) ?> + + <?= $this->form->checkbox('password_reset', t('Enable "Forget Password"'), 1, $values['password_reset'] == 1) ?> + </fieldset> + + <fieldset> + <?= $this->form->label(t('Timezone'), 'application_timezone') ?> + <?= $this->form->select('application_timezone', $timezones, $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->label(t('Time format'), 'application_time_format') ?> + <?= $this->form->select('application_time_format', $time_formats, $values, $errors) ?> + </fieldset> + + <fieldset> + <?= $this->form->checkbox('notifications_enabled', t('Enable notifications by default for all new users'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1) ?> + </fieldset> + + <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)) ?> + + <div class="form-actions"> + <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> + </div> +</form> diff --git a/app/Template/config/board.php b/app/Template/config/board.php new file mode 100644 index 0000000..4d6fe2f --- /dev/null +++ b/app/Template/config/board.php @@ -0,0 +1,26 @@ +<div class="page-header"> + <h2><?= t('Board settings') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'board')) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <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 personal 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> + + <?= $this->hook->render('template:config:board', array('values' => $values, 'errors' => $errors)) ?> + + <div class="form-actions"> + <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> + </div> +</form> diff --git a/app/Template/config/email.php b/app/Template/config/email.php new file mode 100644 index 0000000..be729f5 --- /dev/null +++ b/app/Template/config/email.php @@ -0,0 +1,25 @@ +<div class="page-header"> + <h2><?= t('Email settings') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'email')) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <fieldset> + <legend><?= t('Outgoing Emails') ?></legend> + <?php if (MAIL_CONFIGURATION): ?> + <?= $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) ?> + <?php else: ?> + <p class="alert"><?= t('The email configuration has been disabled by the administrator.') ?></p> + <?php endif ?> + </fieldset> + + <?= $this->hook->render('template:config:email', array('values' => $values, 'errors' => $errors)) ?> + + <div class="form-actions"> + <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> + </div> +</form> diff --git a/app/Template/config/integrations.php b/app/Template/config/integrations.php new file mode 100644 index 0000000..0a3f995 --- /dev/null +++ b/app/Template/config/integrations.php @@ -0,0 +1,14 @@ +<div class="page-header"> + <h2><?= t('Integration with third-party services') ?></h2> +</div> + +<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'integrations')) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + <?php $contents = $this->hook->render('template:config:integrations', array('values' => $values)) ?> + + <?php if (empty($contents)): ?> + <p class="alert"><?= t('There is no external integration installed.') ?></p> + <?php else: ?> + <?= $contents ?> + <?php endif ?> +</form> diff --git a/app/Template/config/keyboard_shortcuts.php b/app/Template/config/keyboard_shortcuts.php new file mode 100644 index 0000000..4e78dbe --- /dev/null +++ b/app/Template/config/keyboard_shortcuts.php @@ -0,0 +1,33 @@ +<div class="page-header"> + <h2><?= t('Keyboard shortcuts') ?></h2> +</div> +<div class="panel"> + <h3><?= t('Board/Calendar/List view') ?></h3> + <ul> + <li><?= t('Switch to the project overview') ?> = <strong>v o</strong></li> + <li><?= t('Switch to the board view') ?> = <strong>v b</strong></li> + <li><?= t('Switch to the list view') ?> = <strong>v l</strong></li> + </ul> + <h3><?= t('Board view') ?></h3> + <ul> + <li><?= t('New task') ?> = <strong>n</strong></li> + <li><?= t('Expand/collapse tasks') ?> = <strong>s</strong></li> + <li><?= t('Compact/wide view') ?> = <strong>c</strong></li> + </ul> + <h3><?= t('Task view') ?></h3> + <ul> + <li><?= t('Edit task') ?> = <strong>e</strong></li> + <li><?= t('New subtask') ?> = <strong>s</strong></li> + <li><?= t('New comment') ?> = <strong>c</strong></li> + <li><?= t('New internal link') ?> = <strong>l</strong></li> + </ul> + <h3><?= t('Application') ?></h3> + <ul> + <li><?= t('Display list of keyboard shortcuts') ?> = <strong>?</strong></li> + <li><?= t('Open board switcher') ?> = <strong>b</strong></li> + <li><?= t('Go to the search/filter box') ?> = <strong>f</strong></li> + <li><?= t('Reset the search/filter box') ?> = <strong>r</strong></li> + <li><?= t('Close dialog box') ?> = <strong>ESC</strong></li> + <li><?= t('Submit a form') ?> = <strong>CTRL+ENTER</strong> <?= t('or') ?> <strong>⌘+ENTER</strong></li> + </ul> +</div> diff --git a/app/Template/config/layout.php b/app/Template/config/layout.php new file mode 100644 index 0000000..6eafa59 --- /dev/null +++ b/app/Template/config/layout.php @@ -0,0 +1,9 @@ +<section id="main"> + <section class="sidebar-container" id="config-section"> + <?= $this->render($sidebar_template) ?> + + <div class="sidebar-content"> + <?= $content_for_sublayout ?> + </div> + </section> +</section> diff --git a/app/Template/config/project.php b/app/Template/config/project.php new file mode 100644 index 0000000..89fe70f --- /dev/null +++ b/app/Template/config/project.php @@ -0,0 +1,30 @@ +<div class="page-header"> + <h2><?= t('Project settings') ?></h2> +</div> +<form method="post" action="<?= $this->url->href('ConfigController', 'save', array('redirect' => 'project')) ?>" autocomplete="off"> + <?= $this->form->csrf() ?> + + <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 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> + + <fieldset> + <?= $this->form->checkbox('disable_private_project', t('Disable personal 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> + </div> +</form> diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php new file mode 100644 index 0000000..2f265ed --- /dev/null +++ b/app/Template/config/sidebar.php @@ -0,0 +1,38 @@ +<div class="sidebar"> + <ul> + <li <?= $this->app->checkMenuSelection('ConfigController', 'index') ?>> + <?= $this->url->link(t('About'), 'ConfigController', 'index') ?> + </li> + <li <?= $this->app->checkMenuSelection('ConfigController', 'application') ?>> + <?= $this->url->link(t('Application settings'), 'ConfigController', 'application') ?> + </li> + <li <?= $this->app->checkMenuSelection('ConfigController', 'email') ?>> + <?= $this->url->link(t('Email settings'), 'ConfigController', 'email') ?> + </li> + <li <?= $this->app->checkMenuSelection('ConfigController', 'project') ?>> + <?= $this->url->link(t('Project settings'), 'ConfigController', 'project') ?> + </li> + <li <?= $this->app->checkMenuSelection('ConfigController', 'board') ?>> + <?= $this->url->link(t('Board settings'), 'ConfigController', 'board') ?> + </li> + <li <?= $this->app->checkMenuSelection('TagController', 'index') ?>> + <?= $this->url->link(t('Tags management'), 'TagController', 'index') ?> + </li> + <li <?= $this->app->checkMenuSelection('LinkController') ?>> + <?= $this->url->link(t('Link labels'), 'LinkController', 'show') ?> + </li> + <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') ?> + </li> + <li <?= $this->app->checkMenuSelection('ConfigController', 'webhook') ?>> + <?= $this->url->link(t('Webhooks'), 'ConfigController', 'webhook') ?> + </li> + <li <?= $this->app->checkMenuSelection('ConfigController', 'api') ?>> + <?= $this->url->link(t('API'), 'ConfigController', 'api') ?> + </li> + <?= $this->hook->render('template:config:sidebar') ?> + </ul> +</div> diff --git a/app/Template/config/upload_db.php b/app/Template/config/upload_db.php new file mode 100644 index 0000000..efc8eb2 --- /dev/null +++ b/app/Template/config/upload_db.php @@ -0,0 +1,16 @@ +<div class="page-header"> + <h2><?= t('Upload the database') ?></h2> +</div> + +<div class="alert"> + <p> + <?= t('You could upload the previously downloaded Sqlite database (Gzip format).') ?> + </p> +</div> + +<form action="<?= $this->url->href('ConfigController', 'saveUploadedDb', [], true) ?>" method="post" enctype="multipart/form-data"> + <?= $this->form->label(t('Database file'), 'file') ?> + <?= $this->form->file('file') ?> + + <?= $this->modal->submitButtons(array('submitLabel' => t('Upload'))) ?> +</form> diff --git a/app/Template/config/webhook.php b/app/Template/config/webhook.php new file mode 100644 index 0000000..bc4bbfd --- /dev/null +++ b/app/Template/config/webhook.php @@ -0,0 +1,23 @@ +<div class="page-header"> + <h2><?= t('Webhook settings') ?></h2> +</div> +<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') ?> + <?= $this->form->text('webhook_url', $values, $errors) ?> + + <div class="form-actions"> + <button type="submit" class="btn btn-blue"><?= t('Save') ?></button> + </div> +</form> + +<div class="page-header margin-top"> + <h2><?= t('Webhook token') ?></h2> +</div> +<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 0000000..59a7ce3 --- /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 0000000..578ece8 --- /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/show.php b/app/Template/currency/show.php new file mode 100644 index 0000000..4b7f34b --- /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/create.php b/app/Template/custom_filter/create.php new file mode 100644 index 0000000..df400ad --- /dev/null +++ b/app/Template/custom_filter/create.php @@ -0,0 +1,20 @@ +<div class="page-header"> + <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->label(t('Name'), 'name') ?> + <?= $this->form->text('name', $values, $errors, array('autofocus', 'required')) ?> + + <?= $this->form->label(t('Filter'), 'filter') ?> + <?= $this->form->text('filter', $values, $errors, array('required')) ?> + + <?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) ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/custom_filter/edit.php b/app/Template/custom_filter/edit.php new file mode 100644 index 0000000..4b0a25a --- /dev/null +++ b/app/Template/custom_filter/edit.php @@ -0,0 +1,25 @@ +<div class="page-header"> + <h2><?= t('Edit custom filter') ?></h2> +</div> + +<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('user_id', $values) ?> + + <?= $this->form->label(t('Name'), 'name') ?> + <?= $this->form->text('name', $values, $errors, array('autofocus', 'required')) ?> + + <?= $this->form->label(t('Filter'), 'filter') ?> + <?= $this->form->text('filter', $values, $errors, array('required')) ?> + + <?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) ?> + <?php endif ?> + + <?= $this->form->checkbox('append', t('Append filter (instead of replacement)'), 1, $values['append'] == 1) ?> + + <?= $this->modal->submitButtons() ?> +</form> diff --git a/app/Template/custom_filter/index.php b/app/Template/custom_filter/index.php new file mode 100644 index 0000000..597a619 --- /dev/null +++ b/app/Template/custom_filter/index.php @@ -0,0 +1,57 @@ +<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> +<?php if (! empty($custom_filters)): ?> + <table class="table-striped table-scrolling"> + <tr> + <th><?= t('Name') ?></th> + <th class="column-30"><?= t('Filter') ?></th> + <th class="column-10"><?= t('Shared') ?></th> + <th class="column-15"><?= t('Append/Replace') ?></th> + <th class="column-20"><?= t('Owner') ?></th> + </tr> + <?php foreach ($custom_filters as $filter): ?> + <tr> + <td> + <?php if (($filter['user_id'] == $this->user->getId() || $this->user->isAdmin() || $this->projectRole->getProjectUserRole($project['id']) == \Kanboard\Core\Security\Role::PROJECT_MANAGER) && $this->user->hasProjectAccess('CustomFilterController', 'edit', $project['id'])): ?> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog"></i><i class="fa fa-caret-down"></i></a> + <ul> + <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 ?> + <?= $this->text->e($filter['name']) ?> + </td> + <td> + <?= $this->text->e($filter['filter']) ?> + </td> + <td> + <?php if ($filter['is_shared'] == 1): ?> + <?= t('Yes') ?> + <?php else: ?> + <?= t('No') ?> + <?php endif ?> + </td> + <td> + <?php if ($filter['append'] == 1): ?> + <?= t('Append') ?> + <?php else: ?> + <?= t('Replace') ?> + <?php endif ?> + </td> + <td> + <?= $this->text->e($filter['owner_name'] ?: $filter['owner_username']) ?> + </td> + </tr> + <?php endforeach ?> + </table> +<?php else: ?> + <p class="alert"><?= t('There is no custom filter.') ?></p> +<?php endif ?> \ No newline at end of file diff --git a/app/Template/custom_filter/remove.php b/app/Template/custom_filter/remove.php new file mode 100644 index 0000000..1c576fa --- /dev/null +++ b/app/Template/custom_filter/remove.php @@ -0,0 +1,15 @@ +<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> + + <?= $this->modal->confirmButtons( + 'CustomFilterController', + 'remove', + array('project_id' => $project['id'], 'filter_id' => $filter['id']) + ) ?> +</div> diff --git a/app/Template/dashboard/layout.php b/app/Template/dashboard/layout.php new file mode 100644 index 0000000..1e7d735 --- /dev/null +++ b/app/Template/dashboard/layout.php @@ -0,0 +1,9 @@ +<section id="main"> + + <section class="sidebar-container" id="dashboard"> + <?= $this->render($sidebar_template, array('user' => $user)) ?> + <div class="sidebar-content"> + <?= $content_for_sublayout ?> + </div> + </section> +</section> diff --git a/app/Template/dashboard/overview.php b/app/Template/dashboard/overview.php new file mode 100644 index 0000000..a2287e5 --- /dev/null +++ b/app/Template/dashboard/overview.php @@ -0,0 +1,105 @@ +<?= $this->hook->render('template:dashboard:show:before-filter-box', array('user' => $user)) ?> + +<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')) ?> + + <div class="input-addon"> + <?= $this->form->text('search', array(), array(), array('placeholder="'.t('Search').'"', 'aria-label="'.t('Search').'"'), 'input-addon-field') ?> + <div class="input-addon-item"> + <?= $this->render('app/filters_helper') ?> + </div> + </div> + </form> +</div> + +<?= $this->hook->render('template:dashboard:show:after-filter-box', array('user' => $user)) ?> + +<?php if (! $project_paginator->isEmpty()): ?> + <div class="table-list"> + <?= $this->render('project_list/header', array('paginator' => $project_paginator)) ?> + <?php foreach ($project_paginator->getCollection() as $project): ?> + <div class="table-list-row table-border-left"> + <div> + <?php if ($this->user->hasProjectAccess('ProjectViewController', 'show', $project['id'])): ?> + <?= $this->render('project/dropdown', array('project' => $project)) ?> + <?php else: ?> + <strong><?= '#'.$project['id'] ?></strong> + <?php endif ?> + + <?= $this->hook->render('template:dashboard:project:before-title', array('project' => $project)) ?> + + <span class="table-list-title <?= $project['is_active'] == 0 ? 'status-closed' : '' ?>"> + <?= $this->url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> + </span> + + <?php if ($project['is_private']): ?> + <i class="fa fa-lock fa-fw" title="<?= t('Personal project') ?>" role="img" aria-label="<?= t('Personal project') ?>"></i> + <?php endif ?> + + <?= $this->hook->render('template:dashboard:project:after-title', array('project' => $project)) ?> + + </div> + <div class="table-list-details"> + <?php foreach ($project['columns'] as $column): ?> + <strong title="<?= t('Task count') ?>"><span class="ui-helper-hidden-accessible"><?= t('Task count') ?> </span><?= $column['nb_open_tasks'] ?></strong> + <small><?= $this->text->e($column['title']) ?></small> + <?php endforeach ?> + </div> + </div> + <?php endforeach ?> + </div> + + <?= $project_paginator ?> +<?php endif ?> + +<?php if (empty($overview_paginator)): ?> + <p class="alert"><?= t('There is nothing assigned to you.') ?></p> +<?php else: ?> + <?php foreach ($overview_paginator as $result): ?> + <?php if (! $result['paginator']->isEmpty()): ?> + <div class="page-header"> + <h2 id="project-tasks-<?= $result['project_id'] ?>"><?= $this->url->link($this->text->e($result['project_name']), 'BoardViewController', 'show', array('project_id' => $result['project_id'])) ?></h2> + </div> + + <div class="table-list"> + <?= $this->render('task_list/header', array( + 'paginator' => $result['paginator'], + )) ?> + + <?php foreach ($result['paginator']->getCollection() as $task): ?> + <div class="table-list-row color-<?= $task['color_id'] ?>"> + <?= $this->render('task_list/task_title', array( + 'task' => $task, + 'redirect' => 'dashboard', + )) ?> + + <?= $this->render('task_list/task_details', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_icons', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $user['id'], + )) ?> + + <?= $this->hook->render('template:dashboard:task:footer', array('task' => $task)) ?> + </div> + <?php endforeach ?> + </div> + + <?= $result['paginator'] ?> + <?php endif ?> + <?php endforeach ?> +<?php endif ?> + +<?= $this->hook->render('template:dashboard:show', array('user' => $user)) ?> diff --git a/app/Template/dashboard/projects.php b/app/Template/dashboard/projects.php new file mode 100644 index 0000000..b7c1fdd --- /dev/null +++ b/app/Template/dashboard/projects.php @@ -0,0 +1,27 @@ +<div class="page-header"> + <h2><?= $this->url->link(t('My projects'), 'DashboardController', 'projects', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2> +</div> +<?php if ($paginator->isEmpty()): ?> + <p class="alert"><?= t('You are not a member of any project.') ?></p> +<?php else: ?> + <div class="table-list"> + <?= $this->render('project_list/header', array('paginator' => $paginator)) ?> + <?php foreach ($paginator->getCollection() as $project): ?> + <div class="table-list-row table-border-left"> + <?= $this->render('project_list/project_title', array( + 'project' => $project, + )) ?> + + <?= $this->render('project_list/project_details', array( + 'project' => $project, + )) ?> + + <?= $this->render('project_list/project_icons', array( + 'project' => $project, + )) ?> + </div> + <?php endforeach ?> + </div> + + <?= $paginator ?> +<?php endif ?> diff --git a/app/Template/dashboard/sidebar.php b/app/Template/dashboard/sidebar.php new file mode 100644 index 0000000..65e5911 --- /dev/null +++ b/app/Template/dashboard/sidebar.php @@ -0,0 +1,24 @@ +<div class="sidebar"> + <ul> + <li <?= $this->app->checkMenuSelection('ProjectListController', 'show') ?>> + <?= $this->url->icon('folder', '<span class="sidebar-text">'.t('Project management').'</span>', 'ProjectListController', 'show') ?> + </li> + <li <?= $this->app->checkMenuSelection('ActivityController', 'user') ?>> + <?= $this->url->icon('dashboard', '<span class="sidebar-text">'.t('My activity stream').'</span>', 'ActivityController', 'user') ?> + </li> + <li><hr></li> + <li <?= $this->app->checkMenuSelection('DashboardController', 'show') ?>> + <?= $this->url->link('<i class="fa fa-fw"></i> <span class="sidebar-text">'.t('Overview').'</span>', 'DashboardController', 'show', array('user_id' => $user['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('DashboardController', 'projects') ?>> + <?= $this->url->link('<i class="fa fa-fw"></i> <span class="sidebar-text">'.t('My projects').'</span>', 'DashboardController', 'projects', array('user_id' => $user['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('DashboardController', 'tasks') ?>> + <?= $this->url->link('<i class="fa fa-fw"></i> <span class="sidebar-text">'.t('My tasks').'</span>', 'DashboardController', 'tasks', array('user_id' => $user['id'])) ?> + </li> + <li <?= $this->app->checkMenuSelection('DashboardController', 'subtasks') ?>> + <?= $this->url->link('<i class="fa fa-fw"></i> <span class="sidebar-text">'.t('My subtasks').'</span>', 'DashboardController', 'subtasks', array('user_id' => $user['id'])) ?> + </li> + <?= $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 new file mode 100644 index 0000000..96bb13c --- /dev/null +++ b/app/Template/dashboard/subtasks.php @@ -0,0 +1,49 @@ +<div class="page-header"> + <h2><?= $this->url->link(t('My subtasks'), 'DashboardController', 'subtasks', array('user_id' => $user['id'])) ?> (<?= $nb_subtasks ?>)</h2> +</div> +<?php if ($nb_subtasks == 0): ?> + <p class="alert"><?= t('There is nothing assigned to you.') ?></p> +<?php else: ?> + <div class="table-list"> + <div class="table-list-header"> + <div class="table-list-header-count"> + <?php if ($nb_subtasks > 1): ?> + <?= t('%d subtasks', $nb_subtasks) ?> + <?php else: ?> + <?= t('%d subtask', $nb_subtasks) ?> + <?php endif ?> + </div> + <div class="table-list-header-menu"> + <div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><strong><?= t('Sort') ?> <i class="fa fa-caret-down"></i></strong></a> + <ul> + <li> + <?= $paginator->order(t('Task ID'), \Kanboard\Model\TaskModel::TABLE.'.id') ?> + </li> + <li> + <?= $paginator->order(t('Title'), \Kanboard\Model\TaskModel::TABLE.'.title') ?> + </li> + <li> + <?= $paginator->order(t('Priority'), \Kanboard\Model\TaskModel::TABLE.'.priority') ?> + </li> + </ul> + </div> + </div> + </div> + + <?php foreach ($paginator->getCollection() as $task): ?> + <div class="table-list-row color-<?= $task['color_id'] ?>"> + <?= $this->render('task_list/task_title', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_subtasks', array( + 'task' => $task, + 'user_id' => $user['id'], + )) ?> + </div> + <?php endforeach ?> + </div> + + <?= $paginator ?> +<?php endif ?> diff --git a/app/Template/dashboard/tasks.php b/app/Template/dashboard/tasks.php new file mode 100644 index 0000000..4392e40 --- /dev/null +++ b/app/Template/dashboard/tasks.php @@ -0,0 +1,41 @@ +<div class="page-header"> + <h2><?= $this->url->link(t('My tasks'), 'DashboardController', 'tasks', array('user_id' => $user['id'])) ?> (<?= $paginator->getTotal() ?>)</h2> +</div> +<?php if ($paginator->isEmpty()): ?> + <p class="alert"><?= t('There is nothing assigned to you.') ?></p> +<?php else: ?> + <div class="table-list"> + <?= $this->render('task_list/header', array( + 'paginator' => $paginator, + )) ?> + + <?php foreach ($paginator->getCollection() as $task): ?> + <div class="table-list-row color-<?= $task['color_id'] ?>"> + <?= $this->render('task_list/task_title', array( + 'task' => $task, + 'redirect' => 'dashboard-tasks', + )) ?> + + <?= $this->render('task_list/task_details', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_icons', array( + 'task' => $task, + )) ?> + + <?= $this->render('task_list/task_subtasks', array( + 'task' => $task, + )) ?> + + <?= $this->hook->render('template:dashboard:task:footer', array('task' => $task)) ?> + </div> + <?php endforeach ?> + </div> + + <?= $paginator ?> +<?php endif ?> diff --git a/app/Template/event/comment_create.php b/app/Template/event/comment_create.php new file mode 100644 index 0000000..de3f226 --- /dev/null +++ b/app/Template/event/comment_create.php @@ -0,0 +1,11 @@ +<p class="activity-title"> + <?= e('%s commented the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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_delete.php b/app/Template/event/comment_delete.php new file mode 100644 index 0000000..13ef37d --- /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'])) + ) ?> + <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 new file mode 100644 index 0000000..0d911fd --- /dev/null +++ b/app/Template/event/comment_update.php @@ -0,0 +1,13 @@ +<p class="activity-title"> + <?= e('%s updated a comment on the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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> + <?php if (! empty($comment['comment'])): ?> + <div class="markdown"><?= $this->text->markdown($comment['comment']) ?></div> + <?php endif ?> +</div> diff --git a/app/Template/event/events.php b/app/Template/event/events.php new file mode 100644 index 0000000..c050f43 --- /dev/null +++ b/app/Template/event/events.php @@ -0,0 +1,19 @@ +<?php if (empty($events)): ?> + <p class="alert"><?= t('There is no activity yet.') ?></p> +<?php else: ?> + <?php foreach ($events as $event): ?> + <div class="activity-event"> + <?= $this->avatar->render( + $event['creator_id'], + $event['author_username'], + $event['author_name'], + $event['email'], + $event['avatar_path'] + ) ?> + + <div class="activity-content"> + <?= $event['event_content'] ?> + </div> + </div> + <?php endforeach ?> +<?php endif ?> \ No newline at end of file diff --git a/app/Template/event/subtask_create.php b/app/Template/event/subtask_create.php new file mode 100644 index 0000000..53f1b3e --- /dev/null +++ b/app/Template/event/subtask_create.php @@ -0,0 +1,23 @@ +<p class="activity-title"> + <?= e('%s created a subtask for the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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><?= t($subtask['status_name']) ?></strong>) + </li> + <li> + <?php if ($subtask['username']): ?> + <?= t('Assigned to %s with an estimate of %s/%sh', $subtask['name'] ?: $subtask['username'], $subtask['time_spent'], $subtask['time_estimated']) ?> + <?php else: ?> + <?= t('Not assigned, estimate of %sh', $subtask['time_estimated']) ?> + <?php endif ?> + </li> + </ul> +</div> diff --git a/app/Template/event/subtask_delete.php b/app/Template/event/subtask_delete.php new file mode 100644 index 0000000..b1e487f --- /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'])) + ) ?> + <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 new file mode 100644 index 0000000..db3e017 --- /dev/null +++ b/app/Template/event/subtask_update.php @@ -0,0 +1,23 @@ +<p class="activity-title"> + <?= e('%s updated a subtask for the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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><?= t($subtask['status_name']) ?></strong>) + </li> + <li> + <?php if ($subtask['username']): ?> + <?= t('Assigned to %s with an estimate of %s/%sh', $subtask['name'] ?: $subtask['username'], $subtask['time_spent'], $subtask['time_estimated']) ?> + <?php else: ?> + <?= t('Not assigned, estimate of %sh', $subtask['time_estimated']) ?> + <?php endif ?> + </li> + </ul> +</div> diff --git a/app/Template/event/task_assignee_change.php b/app/Template/event/task_assignee_change.php new file mode 100644 index 0000000..f57ab63 --- /dev/null +++ b/app/Template/event/task_assignee_change.php @@ -0,0 +1,17 @@ +<p class="activity-title"> + <?php $assignee = $task['assignee_name'] ?: $task['assignee_username'] ?> + + <?php if (! empty($assignee)): ?> + <?= e('%s changed the assignee of the task %s to %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'])), + $this->text->e($assignee) + ) ?> + <?php else: ?> + <?= 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']))) ?> + <?php endif ?> + <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> diff --git a/app/Template/event/task_close.php b/app/Template/event/task_close.php new file mode 100644 index 0000000..aefd3a7 --- /dev/null +++ b/app/Template/event/task_close.php @@ -0,0 +1,10 @@ +<p class="activity-title"> + <?= e('%s closed the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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> diff --git a/app/Template/event/task_create.php b/app/Template/event/task_create.php new file mode 100644 index 0000000..89865cc --- /dev/null +++ b/app/Template/event/task_create.php @@ -0,0 +1,10 @@ +<p class="activity-title"> + <?= e('%s created the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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> diff --git a/app/Template/event/task_file_create.php b/app/Template/event/task_file_create.php new file mode 100644 index 0000000..b197d3c --- /dev/null +++ b/app/Template/event/task_file_create.php @@ -0,0 +1,10 @@ +<p class="activity-title"> + <?= e('%s attached a new file to the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'])) + ) ?> + <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> +</div> diff --git a/app/Template/event/task_file_destroy.php b/app/Template/event/task_file_destroy.php new file mode 100644 index 0000000..882c51b --- /dev/null +++ b/app/Template/event/task_file_destroy.php @@ -0,0 +1,10 @@ +<p class="activity-title"> + <?= e('%s removed a file from the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'])) + ) ?> + <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> +</div> 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 0000000..3031033 --- /dev/null +++ b/app/Template/event/task_internal_link_create_update.php @@ -0,0 +1,14 @@ +<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'])) + ) ?> + <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 0000000..54194ca --- /dev/null +++ b/app/Template/event/task_internal_link_delete.php @@ -0,0 +1,14 @@ +<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'])) + ) ?> + <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 has 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 new file mode 100644 index 0000000..23f0af7 --- /dev/null +++ b/app/Template/event/task_move_column.php @@ -0,0 +1,11 @@ +<p class="activity-title"> + <?= e('%s moved the task %s to the column "%s"', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'])), + $this->text->e($task['column_title']) + ) ?> + <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> diff --git a/app/Template/event/task_move_position.php b/app/Template/event/task_move_position.php new file mode 100644 index 0000000..5dedbf6 --- /dev/null +++ b/app/Template/event/task_move_position.php @@ -0,0 +1,12 @@ +<p class="activity-title"> + <?= e('%s moved the task %s to the position #%d in the column "%s"', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'])), + $task['position'], + $this->text->e($task['column_title']) + ) ?> + <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> diff --git a/app/Template/event/task_move_project.php b/app/Template/event/task_move_project.php new file mode 100644 index 0000000..d0a913b --- /dev/null +++ b/app/Template/event/task_move_project.php @@ -0,0 +1,12 @@ +<p class="activity-title"> + <?= e('%s moved the task #%d "%s" to the project "%s"', + $this->text->e($author), + $task['id'], + $this->text->e($task['title']), + $this->text->e($task['project_name']) + ) ?> + <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> diff --git a/app/Template/event/task_move_swimlane.php b/app/Template/event/task_move_swimlane.php new file mode 100644 index 0000000..2190cf3 --- /dev/null +++ b/app/Template/event/task_move_swimlane.php @@ -0,0 +1,18 @@ +<p class="activity-title"> + <?php if ($task['swimlane_id'] == 0): ?> + <?= e('%s moved the task %s to the first swimlane', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'])) + ) ?> + <?php else: ?> + <?= e('%s moved the task %s to the swimlane "%s"', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['id'])), + $this->text->e($task['swimlane_name']) + ) ?> + <?php endif ?> + <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> diff --git a/app/Template/event/task_open.php b/app/Template/event/task_open.php new file mode 100644 index 0000000..eee795d --- /dev/null +++ b/app/Template/event/task_open.php @@ -0,0 +1,10 @@ +<p class="activity-title"> + <?= e('%s opened the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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> diff --git a/app/Template/event/task_update.php b/app/Template/event/task_update.php new file mode 100644 index 0000000..88c9097 --- /dev/null +++ b/app/Template/event/task_update.php @@ -0,0 +1,15 @@ +<p class="activity-title"> + <?= e('%s updated the task %s', + $this->text->e($author), + $this->url->link(t('#%d', $task['id']), 'TaskViewController', 'show', array('task_id' => $task['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> + <?php if (isset($changes)): ?> + <div class="activity-changes"> + <?= $this->render('task/changes', array('changes' => $changes, 'task' => $task)) ?> + </div> + <?php endif ?> +</div> diff --git a/app/Template/export/header.php b/app/Template/export/header.php new file mode 100644 index 0000000..d9c6284 --- /dev/null +++ b/app/Template/export/header.php @@ -0,0 +1,18 @@ +<div class="page-header"> + <h2><?= $this->text->e($project['name']) ?> > <?= $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> + <?= $this->hook->render('template:export:header', array('project_id' => $project['id'])) ?> + </ul> +</div> diff --git a/app/Template/export/subtasks.php b/app/Template/export/subtasks.php new file mode 100644 index 0000000..ea80af2 --- /dev/null +++ b/app/Template/export/subtasks.php @@ -0,0 +1,17 @@ +<?= $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 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->date(t('Start date'), 'from', $values) ?> + <?= $this->form->date(t('End date'), 'to', $values) ?> + <?= $this->form->checkbox('bom', t('Add a BOM at the beginning of the file (required for Microsoft Excel)'), 1, isset($values['bom']) && $values['bom'] == 1) ?> + + <div class="form-actions"> + <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> diff --git a/app/Template/export/summary.php b/app/Template/export/summary.php new file mode 100644 index 0000000..7273100 --- /dev/null +++ b/app/Template/export/summary.php @@ -0,0 +1,17 @@ +<?= $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 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->date(t('Start date'), 'from', $values) ?> + <?= $this->form->date(t('End date'), 'to', $values) ?> + <?= $this->form->checkbox('bom', t('Add a BOM at the beginning of the file (required for Microsoft Excel)'), 1, isset($values['bom']) && $values['bom'] == 1) ?> + + <div class="form-actions"> + <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> diff --git a/app/Template/export/tasks.php b/app/Template/export/tasks.php new file mode 100644 index 0000000..36926e2 --- /dev/null +++ b/app/Template/export/tasks.php @@ -0,0 +1,17 @@ +<?= $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 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->date(t('Start date'), 'from', $values) ?> + <?= $this->form->date(t('End date'), 'to', $values) ?> + <?= $this->form->checkbox('bom', t('Add a BOM at the beginning of the file (required for Microsoft Excel)'), 1, isset($values['bom']) && $values['bom'] == 1) ?> + + <div class="form-actions"> + <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> diff --git a/app/Template/export/transitions.php b/app/Template/export/transitions.php new file mode 100644 index 0000000..a21f8e3 --- /dev/null +++ b/app/Template/export/transitions.php @@ -0,0 +1,17 @@ +<?= $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 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->date(t('Start date'), 'from', $values) ?> + <?= $this->form->date(t('End date'), 'to', $values) ?> + <?= $this->form->checkbox('bom', t('Add a BOM at the beginning of the file (required for Microsoft Excel)'), 1, isset($values['bom']) && $values['bom'] == 1) ?> + + <div class="form-actions"> + <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> diff --git a/app/Template/external_task_creation/step1.php b/app/Template/external_task_creation/step1.php new file mode 100644 index 0000000..2a3b014 --- /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 0000000..baace3a --- /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 0000000..933ad0d --- /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'])) ?>"> + <?= $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 new file mode 100644 index 0000000..f9e9c13 --- /dev/null +++ b/app/Template/feed/project.php @@ -0,0 +1,23 @@ +<?= '<?xml version="1.0" encoding="UTF-8"?>' ?> +<feed xmlns="http://www.w3.org/2005/Atom"> + <title><?= t("%s's activity", $project['name']) ?> + + + + url->href('FeedController', 'project', ['token' => $project['token']], false, '', true) ?> + + + + url->href('TaskViewController', 'show', ['task_id' => $event['task_id']], false, 'event-'.$event['id'], true) ?> + + + + + + + <?= htmlentities($event['event_title'], ENT_XML1) ?> + ]]> + + + + \ No newline at end of file diff --git a/app/Template/feed/user.php b/app/Template/feed/user.php new file mode 100644 index 0000000..89f3093 --- /dev/null +++ b/app/Template/feed/user.php @@ -0,0 +1,23 @@ +' ?> + + <?= t('Project activities for %s', $this->user->getFullname($user)) ?> + + + + url->href('FeedController', 'user', ['token' => $user['token']], false, '', true) ?> + + + + url->href('TaskViewController', 'show', ['task_id' => $event['task_id']], false, 'event-'.$event['id'], true) ?> + + + + + + + <?= htmlentities($event['event_title'], ENT_XML1) ?> + ]]> + + + + \ No newline at end of file diff --git a/app/Template/file_viewer/show.php b/app/Template/file_viewer/show.php new file mode 100644 index 0000000..0c1c5e2 --- /dev/null +++ b/app/Template/file_viewer/show.php @@ -0,0 +1,14 @@ + +
+ + <?= $this->text->e($file['name']) ?> + +
+ text->markdown($content) ?> +
+ +
text->e($content) ?>
+ +
diff --git a/app/Template/group/associate.php b/app/Template/group/associate.php new file mode 100644 index 0000000..cd7f2b7 --- /dev/null +++ b/app/Template/group/associate.php @@ -0,0 +1,23 @@ + + +

+
+ url->link(t('Close this window'), 'GroupListController', 'index', array(), false, 'btn js-modal-close') ?> +
+ +
+ form->csrf() ?> + form->hidden('group_id', $values) ?> + + form->label(t('User'), 'user_id') ?> + app->component('select-dropdown-autocomplete', array( + 'name' => 'user_id', + 'items' => $users, + 'defaultValue' => isset($values['user_id']) ? $values['user_id'] : key($users), + )) ?> + + modal->submitButtons() ?> +
+ diff --git a/app/Template/group/dissociate.php b/app/Template/group/dissociate.php new file mode 100644 index 0000000..2483639 --- /dev/null +++ b/app/Template/group/dissociate.php @@ -0,0 +1,12 @@ + +
+

+ + modal->confirmButtons( + 'GroupListController', + 'removeUser', + array('group_id' => $group['id'], 'user_id' => $user['id']) + ) ?> +
diff --git a/app/Template/group/dropdown.php b/app/Template/group/dropdown.php new file mode 100644 index 0000000..9d807dc --- /dev/null +++ b/app/Template/group/dropdown.php @@ -0,0 +1,9 @@ + diff --git a/app/Template/group/index.php b/app/Template/group/index.php new file mode 100644 index 0000000..32d1460 --- /dev/null +++ b/app/Template/group/index.php @@ -0,0 +1,71 @@ + + +
+ +
+ +isEmpty()): ?> +

+ +
+
+
+ getTotal() > 1): ?> + getTotal()) ?> + + getTotal()) ?> + +
+
+ +
+
+ + getCollection() as $group): ?> +
+ + render('group/dropdown', array('group' => $group)) ?> + url->link($this->text->e($group['name']), 'GroupListController', 'users', array('group_id' => $group['id'])) ?> + + +
+
    + 1): ?> +
  • + +
  • + + + +
  • text->e($group['external_id']) ?>
  • + +
+
+
+ +
+ + + diff --git a/app/Template/group/remove.php b/app/Template/group/remove.php new file mode 100644 index 0000000..77d602f --- /dev/null +++ b/app/Template/group/remove.php @@ -0,0 +1,12 @@ + +
+

+ + modal->confirmButtons( + 'GroupListController', + 'remove', + array('group_id' => $group['id']) + ) ?> +
diff --git a/app/Template/group/user_dropdown.php b/app/Template/group/user_dropdown.php new file mode 100644 index 0000000..48acb95 --- /dev/null +++ b/app/Template/group/user_dropdown.php @@ -0,0 +1,11 @@ + diff --git a/app/Template/group/users.php b/app/Template/group/users.php new file mode 100644 index 0000000..2469296 --- /dev/null +++ b/app/Template/group/users.php @@ -0,0 +1,44 @@ +
+ + isEmpty()): ?> +

+ +
+ render('user_list/header', array('paginator' => $paginator)) ?> + getCollection() as $user): ?> +
+
+ render('group/user_dropdown', array('user' => $user)) ?> + + avatar->small( + $user['id'], + $user['username'], + $user['name'], + $user['email'], + $user['avatar_path'], + 'avatar-inline' + ) ?> + url->link($this->text->e($user['name'] ?: $user['username']), 'UserViewController', 'show', array('user_id' => $user['id'])) ?> + +
+ + render('user_list/user_details', array( + 'user' => $user, + )) ?> + + render('user_list/user_icons', array( + 'user' => $user, + )) ?> +
+ +
+ + + +
diff --git a/app/Template/group_creation/show.php b/app/Template/group_creation/show.php new file mode 100644 index 0000000..7cec075 --- /dev/null +++ b/app/Template/group_creation/show.php @@ -0,0 +1,11 @@ + +
+ form->csrf() ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"')) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/group_modification/show.php b/app/Template/group_modification/show.php new file mode 100644 index 0000000..aa64777 --- /dev/null +++ b/app/Template/group_modification/show.php @@ -0,0 +1,14 @@ + +
+ form->csrf() ?> + + form->hidden('id', $values) ?> + form->hidden('external_id', $values) ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"')) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/header.php b/app/Template/header.php new file mode 100644 index 0000000..5241870 --- /dev/null +++ b/app/Template/header.php @@ -0,0 +1,43 @@ +render('header/title', array( + 'project' => isset($project) ? $project : null, + 'task' => isset($task) ? $task : null, + 'description' => isset($description) ? $description : null, + 'title' => $title, + 'board_selector' => isset($board_selector) ? $board_selector : null, +)) ?> + +render('header/user_notifications'), + $this->render('header/user_dropdown') + )) ?> + +
+
+ +
+
+ +
+ +
diff --git a/app/Template/header/board_selector.php b/app/Template/header/board_selector.php new file mode 100644 index 0000000..43a3f6d --- /dev/null +++ b/app/Template/header/board_selector.php @@ -0,0 +1,13 @@ +app->component('select-dropdown-autocomplete', array( + 'name' => 'boardId', + 'placeholder' => t('Display another project'), + 'ariaLabel' => 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', + ) +)) ?> \ No newline at end of file diff --git a/app/Template/header/creation_dropdown.php b/app/Template/header/creation_dropdown.php new file mode 100644 index 0000000..8d2a51a --- /dev/null +++ b/app/Template/header/creation_dropdown.php @@ -0,0 +1,21 @@ +user->hasAccess('ProjectCreationController', 'create'); ?> +app->config('disable_private_project', 0) == 0; ?> + + + + diff --git a/app/Template/header/custom_board_selector.php b/app/Template/header/custom_board_selector.php new file mode 100644 index 0000000..49bb185 --- /dev/null +++ b/app/Template/header/custom_board_selector.php @@ -0,0 +1,26 @@ + diff --git a/app/Template/header/title.php b/app/Template/header/title.php new file mode 100644 index 0000000..8db6949 --- /dev/null +++ b/app/Template/header/title.php @@ -0,0 +1,29 @@ +
+
+ + + url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> + + + text->e($title) ?> + + ( / text->e($project['task_limit']) ?>) + + + + + +
+ +
+ + + helper->projectHeader->hookBefore)) echo $this->helper->projectHeader->hookBefore; ?> + helper->projectHeader->hookAfter)) echo $this->helper->projectHeader->hookAfter; ?> +
+ + + render('header/custom_board_selector', array('board_selector' => $board_selector)) ?> + +
+
diff --git a/app/Template/header/user_dropdown.php b/app/Template/header/user_dropdown.php new file mode 100644 index 0000000..996ea89 --- /dev/null +++ b/app/Template/header/user_dropdown.php @@ -0,0 +1,41 @@ + diff --git a/app/Template/header/user_notifications.php b/app/Template/header/user_notifications.php new file mode 100644 index 0000000..036fee2 --- /dev/null +++ b/app/Template/header/user_notifications.php @@ -0,0 +1,7 @@ + +user->hasNotifications()): ?> + modal->mediumIcon('bell web-notification-icon', t('Unread notifications'), 'WebNotificationController', 'show', array('user_id' => $this->user->getId())) ?> + + modal->mediumIcon('bell', t('My notifications'), 'WebNotificationController', 'show', array('user_id' => $this->user->getId())) ?> + + diff --git a/app/Template/layout.php b/app/Template/layout.php new file mode 100644 index 0000000..45a7e75 --- /dev/null +++ b/app/Template/layout.php @@ -0,0 +1,81 @@ + +app->isRtlLanguage()): ?> dir="rtl"> + + + + + + + + + + + + + + asset->colorCss() ?> + asset->css('assets/css/vendor.min.css') ?> + + asset->css('assets/css/'.$this->user->getTheme().'.min.css') ?> + + asset->css('assets/css/light.min.css') ?> + + asset->css('assets/css/print.min.css', true, 'print') ?> + asset->css('assets/css/custom_login.css') ?> + asset->css('assets/css/custom_dashboard.css') ?> + asset->customCss() ?> + + + asset->js('assets/js/vendor.min.js') ?> + asset->js('assets/js/app.min.js') ?> + + + hook->asset('css', 'template:layout:css') ?> + hook->asset('js', 'template:layout:js') ?> + + + + + + + + + + <?php if (isset($page_title)): ?> + <?= $this->text->e($page_title) ?> + <?php elseif (isset($title)): ?> + <?= $this->text->e($title) ?> + <?php else: ?> + Kanboard + <?php endif ?> + + + hook->render('template:layout:head') ?> + + + + + app->flashMessage() ?> + + + hook->render('template:layout:top') ?> + render('header', array( + 'title' => $title, + 'description' => isset($description) ? $description : '', + 'board_selector' => isset($board_selector) ? $board_selector : array(), + 'project' => isset($project) ? $project : array(), + )) ?> +
+ app->flashMessage() ?> + +
+ hook->render('template:layout:bottom') ?> + + + diff --git a/app/Template/link/create.php b/app/Template/link/create.php new file mode 100644 index 0000000..37610a3 --- /dev/null +++ b/app/Template/link/create.php @@ -0,0 +1,12 @@ + + +
+ form->csrf() ?> + form->label(t('Label'), 'label') ?> + form->text('label', $values, $errors, array('required', 'autofocus')) ?> + form->label(t('Opposite label'), 'opposite_label') ?> + form->text('opposite_label', $values, $errors) ?> + modal->submitButtons() ?> +
diff --git a/app/Template/link/edit.php b/app/Template/link/edit.php new file mode 100644 index 0000000..4be5657 --- /dev/null +++ b/app/Template/link/edit.php @@ -0,0 +1,17 @@ + + +
+ + form->csrf() ?> + form->hidden('id', $values) ?> + + form->label(t('Label'), 'label') ?> + form->text('label', $values, $errors, array('required')) ?> + + form->label(t('Opposite label'), 'opposite_id') ?> + form->select('opposite_id', $labels, $values, $errors) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/link/remove.php b/app/Template/link/remove.php new file mode 100644 index 0000000..e5ea246 --- /dev/null +++ b/app/Template/link/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'LinkController', + 'remove', + array('link_id' => $link['id']) + ) ?> +
diff --git a/app/Template/link/show.php b/app/Template/link/show.php new file mode 100644 index 0000000..6aadd66 --- /dev/null +++ b/app/Template/link/show.php @@ -0,0 +1,36 @@ + + + + + + + + + + + + + +
+ + + + | + + +
    + modal->medium('edit', t('Edit'), 'LinkController', 'edit', array('link_id' => $link['id'])) ?> + + modal->confirm('trash-o', t('Remove'), 'LinkController', 'confirm', array('link_id' => $link['id'])) ?> +
+
+ + + diff --git a/app/Template/notification/comment_create.php b/app/Template/notification/comment_create.php new file mode 100644 index 0000000..1f890a5 --- /dev/null +++ b/app/Template/notification/comment_create.php @@ -0,0 +1,15 @@ + + +

text->e($task['title']) ?> (#)

+ + +

+ +

+ + +text->markdown($comment['comment'], true) ?> + +render('notification/footer', array('task' => $task)) ?> + + \ 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 0000000..224fcdb --- /dev/null +++ b/app/Template/notification/comment_delete.php @@ -0,0 +1,11 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +text->markdown($comment['comment'], true) ?> + +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/comment_update.php b/app/Template/notification/comment_update.php new file mode 100644 index 0000000..87c785c --- /dev/null +++ b/app/Template/notification/comment_update.php @@ -0,0 +1,11 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +text->markdown($comment['comment'], true) ?> + +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/comment_user_mention.php b/app/Template/notification/comment_user_mention.php new file mode 100644 index 0000000..afa3aa7 --- /dev/null +++ b/app/Template/notification/comment_user_mention.php @@ -0,0 +1,11 @@ + + +

+ +

text->e($task['title']) ?>

+ +text->markdown($comment['comment'], true) ?> + +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/footer.php b/app/Template/notification/footer.php new file mode 100644 index 0000000..6888d50 --- /dev/null +++ b/app/Template/notification/footer.php @@ -0,0 +1,9 @@ +
+Kanboard + +app->config('application_url') != ''): ?> + + - url->absoluteLink(t('view the task on Kanboard'), 'TaskViewController', 'show', array('task_id' => $task['id'])) ?> + + - url->absoluteLink(t('view the board on Kanboard'), 'BoardViewController', 'show', array('project_id' => $task['project_id'])) ?> + diff --git a/app/Template/notification/subtask_create.php b/app/Template/notification/subtask_create.php new file mode 100644 index 0000000..f5b91d3 --- /dev/null +++ b/app/Template/notification/subtask_create.php @@ -0,0 +1,21 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +
    +
  • text->e($subtask['title']) ?>
  • +
  • +
  • text->e($subtask['name'] ?: $subtask['username'] ?: '?') ?>
  • + +
  • + + +
  • + +
+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/subtask_delete.php b/app/Template/notification/subtask_delete.php new file mode 100644 index 0000000..eaf51e4 --- /dev/null +++ b/app/Template/notification/subtask_delete.php @@ -0,0 +1,15 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +
    +
  • text->e($subtask['title']) ?>
  • +
  • +
  • text->e($subtask['name'] ?: $subtask['username'] ?: '?') ?>
  • +
+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/subtask_update.php b/app/Template/notification/subtask_update.php new file mode 100644 index 0000000..95663e8 --- /dev/null +++ b/app/Template/notification/subtask_update.php @@ -0,0 +1,27 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +
    +
  • text->e($subtask['title']) ?>
  • +
  • +
  • text->e($subtask['name'] ?: $subtask['username'] ?: '?') ?>
  • + +
  • + + + + + / + + + +
  • + +
+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_assignee_change.php b/app/Template/notification/task_assignee_change.php new file mode 100644 index 0000000..de47e56 --- /dev/null +++ b/app/Template/notification/task_assignee_change.php @@ -0,0 +1,24 @@ + + +

text->e($task['title']) ?> (#)

+ +
    +
  • + + + + + + + +
  • +
+ + +

+ text->markdown($task['description'], true) ?: t('There is no description.') ?> + + +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_close.php b/app/Template/notification/task_close.php new file mode 100644 index 0000000..67d04f6 --- /dev/null +++ b/app/Template/notification/task_close.php @@ -0,0 +1,9 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_create.php b/app/Template/notification/task_create.php new file mode 100644 index 0000000..3a34dbe --- /dev/null +++ b/app/Template/notification/task_create.php @@ -0,0 +1,47 @@ + + +

text->e($task['title']) ?> (#)

+ +
    +
  • + dt->datetime($task['date_creation']) ?> +
  • + +
  • + dt->datetime($task['date_due']) ?> +
  • + + +
  • + +
  • + +
  • + + + + + + + +
  • +
  • + + text->e($task['column_title']) ?> +
  • +
  • text->e($task['position']) ?>
  • + +
  • + text->e($task['category_name']) ?> +
  • + +
+ + +

+ text->markdown($task['description'], true) ?> + + +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_file_create.php b/app/Template/notification/task_file_create.php new file mode 100644 index 0000000..c8f6b30 --- /dev/null +++ b/app/Template/notification/task_file_create.php @@ -0,0 +1,9 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_file_destroy.php b/app/Template/notification/task_file_destroy.php new file mode 100644 index 0000000..17d0463 --- /dev/null +++ b/app/Template/notification/task_file_destroy.php @@ -0,0 +1,9 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file 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 0000000..5273dc3 --- /dev/null +++ b/app/Template/notification/task_internal_link_create_update.php @@ -0,0 +1,13 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ url->absoluteLink(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id'])), + e($task_link['label'])) ?> +

+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file 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 0000000..b3413c7 --- /dev/null +++ b/app/Template/notification/task_internal_link_delete.php @@ -0,0 +1,13 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ url->absoluteLink(t('#%d', $task_link['opposite_task_id']), 'TaskViewController', 'show', array('task_id' => $task_link['opposite_task_id']))) ?> +

+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_move_column.php b/app/Template/notification/task_move_column.php new file mode 100644 index 0000000..e4281d5 --- /dev/null +++ b/app/Template/notification/task_move_column.php @@ -0,0 +1,15 @@ + + +

text->e($task['title']) ?> (#)

+ +
    +
  • + + text->e($task['column_title']) ?> +
  • +
  • text->e($task['position']) ?>
  • +
+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_move_position.php b/app/Template/notification/task_move_position.php new file mode 100644 index 0000000..e4281d5 --- /dev/null +++ b/app/Template/notification/task_move_position.php @@ -0,0 +1,15 @@ + + +

text->e($task['title']) ?> (#)

+ +
    +
  • + + text->e($task['column_title']) ?> +
  • +
  • text->e($task['position']) ?>
  • +
+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_move_project.php b/app/Template/notification/task_move_project.php new file mode 100644 index 0000000..96f4e54 --- /dev/null +++ b/app/Template/notification/task_move_project.php @@ -0,0 +1,9 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_move_swimlane.php b/app/Template/notification/task_move_swimlane.php new file mode 100644 index 0000000..95de271 --- /dev/null +++ b/app/Template/notification/task_move_swimlane.php @@ -0,0 +1,23 @@ + + +

text->e($task['title']) ?> (#)

+ +
    +
  • + + + + + text->e($task['swimlane_name']) ?> + +
  • +
  • + + text->e($task['column_title']) ?> +
  • +
  • text->e($task['position']) ?>
  • +
+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_open.php b/app/Template/notification/task_open.php new file mode 100644 index 0000000..2123d5f --- /dev/null +++ b/app/Template/notification/task_open.php @@ -0,0 +1,9 @@ + + +

text->e($task['title']) ?> (#)

+ +

+ +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_overdue.php b/app/Template/notification/task_overdue.php new file mode 100644 index 0000000..8da0d62 --- /dev/null +++ b/app/Template/notification/task_overdue.php @@ -0,0 +1,41 @@ + + +

+ + + + + + + + + + + + + + + + + + + +
# + app->config('application_url') !== ''): ?> + url->absoluteLink($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'])) ?> + + text->e($task['title']) ?> + + dt->datetime($task['date_due']) ?> + app->config('application_url') !== ''): ?> + url->absoluteLink($this->text->e($task['project_name']), 'BoardViewController', 'show', array('project_id' => $task['project_id'])) ?> + + text->e($task['project_name']) ?> + + + + text->e($task['assignee_name'] ?: $task['assignee_username']) ?> + +
+ + \ No newline at end of file diff --git a/app/Template/notification/task_update.php b/app/Template/notification/task_update.php new file mode 100644 index 0000000..86b7466 --- /dev/null +++ b/app/Template/notification/task_update.php @@ -0,0 +1,8 @@ + + +

text->e($task['title']) ?> (#)

+ +render('task/changes', array('changes' => $changes, 'task' => $task, 'public' => true)) ?> +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/notification/task_user_mention.php b/app/Template/notification/task_user_mention.php new file mode 100644 index 0000000..dc72169 --- /dev/null +++ b/app/Template/notification/task_user_mention.php @@ -0,0 +1,11 @@ + + +

+

text->e($task['title']) ?>

+ +

+text->markdown($task['description'], true) ?> + +render('notification/footer', array('task' => $task)) ?> + + \ No newline at end of file diff --git a/app/Template/password_reset/change.php b/app/Template/password_reset/change.php new file mode 100644 index 0000000..ac93309 --- /dev/null +++ b/app/Template/password_reset/change.php @@ -0,0 +1,16 @@ + diff --git a/app/Template/password_reset/create.php b/app/Template/password_reset/create.php new file mode 100644 index 0000000..52bfc9f --- /dev/null +++ b/app/Template/password_reset/create.php @@ -0,0 +1,18 @@ + diff --git a/app/Template/password_reset/email.php b/app/Template/password_reset/email.php new file mode 100644 index 0000000..63b08e4 --- /dev/null +++ b/app/Template/password_reset/email.php @@ -0,0 +1,6 @@ +

+ +

url->to('PasswordResetController', 'change', array('token' => $token), '', true) ?>

+ +
+Kanboard diff --git a/app/Template/plugin/directory.php b/app/Template/plugin/directory.php new file mode 100644 index 0000000..eaaa8db --- /dev/null +++ b/app/Template/plugin/directory.php @@ -0,0 +1,53 @@ + + + +

+ +

+ + + +

+ + $plugin): ?> + + + + + + + + + + + + +
+ text->e($plugin['title']) ?> +
+ text->e($plugin['author']) ?> + + text->e($plugin['version']) ?> + + + + url->icon('cloud-download', t('Install'), 'PluginController', 'install', array('archive_url' => urlencode($plugin['download'])), true) ?> + + url->icon('refresh', t('Update'), 'PluginController', 'update', array('archive_url' => urlencode($plugin['download'])), true) ?> + + + + + + + + +
+
+ text->markdown($plugin['description']) ?> +
+
+ + diff --git a/app/Template/plugin/layout.php b/app/Template/plugin/layout.php new file mode 100644 index 0000000..6eafa59 --- /dev/null +++ b/app/Template/plugin/layout.php @@ -0,0 +1,9 @@ +
+ +
diff --git a/app/Template/plugin/remove.php b/app/Template/plugin/remove.php new file mode 100644 index 0000000..1280f8a --- /dev/null +++ b/app/Template/plugin/remove.php @@ -0,0 +1,13 @@ + + +
+

getPluginName()) ?>

+ + modal->confirmButtons( + 'PluginController', + 'uninstall', + array('pluginId' => $plugin_id) + ) ?> +
diff --git a/app/Template/plugin/show.php b/app/Template/plugin/show.php new file mode 100644 index 0000000..90b62bd --- /dev/null +++ b/app/Template/plugin/show.php @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + $plugin): ?> + + + + + + + + + + + + + +
+ getPluginHomepage()): ?> + text->e($plugin->getPluginName()) ?> + + text->e($plugin->getPluginName()) ?> + + text->e($plugin->getPluginAuthor()) ?>text->e($plugin->getPluginVersion()) ?>text->e($plugin->getCompatibleVersion()) ?> + modal->confirm('trash-o', t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder)) ?> +
text->e($plugin->getPluginDescription()) ?>
+ + + + + +

+ + + + + + + + + + + + $plugin): ?> + + + + + + + + + + + + +
+ getPluginHomepage()): ?> + text->e($plugin->getPluginName()) ?> + + text->e($plugin->getPluginName()) ?> + + text->e($plugin->getPluginAuthor()) ?>text->e($plugin->getPluginVersion()) ?> + modal->confirm('trash-o', t('Uninstall'), 'PluginController', 'confirm', array('pluginId' => $pluginFolder)) ?> +
text->e($plugin->getPluginDescription()) ?>
+ diff --git a/app/Template/plugin/sidebar.php b/app/Template/plugin/sidebar.php new file mode 100644 index 0000000..dd1a2a6 --- /dev/null +++ b/app/Template/plugin/sidebar.php @@ -0,0 +1,10 @@ + diff --git a/app/Template/predefined_task_description/create.php b/app/Template/predefined_task_description/create.php new file mode 100644 index 0000000..5a7a8d9 --- /dev/null +++ b/app/Template/predefined_task_description/create.php @@ -0,0 +1,14 @@ + +
+ form->csrf() ?> + + form->label(t('Title'), 'title') ?> + form->text('title', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + + form->label(t('Description'), 'description') ?> + form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/predefined_task_description/edit.php b/app/Template/predefined_task_description/edit.php new file mode 100644 index 0000000..039d650 --- /dev/null +++ b/app/Template/predefined_task_description/edit.php @@ -0,0 +1,14 @@ + +
+ form->csrf() ?> + + form->label(t('Title'), 'title') ?> + form->text('title', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + + form->label(t('Description'), 'description') ?> + form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/predefined_task_description/remove.php b/app/Template/predefined_task_description/remove.php new file mode 100644 index 0000000..f60a8e7 --- /dev/null +++ b/app/Template/predefined_task_description/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'PredefinedTaskDescriptionController', + 'remove', + array('project_id' => $project['id'], 'id' => $template['id']) + ) ?> +
diff --git a/app/Template/project/dropdown.php b/app/Template/project/dropdown.php new file mode 100644 index 0000000..4b77637 --- /dev/null +++ b/app/Template/project/dropdown.php @@ -0,0 +1,28 @@ + diff --git a/app/Template/project/layout.php b/app/Template/project/layout.php new file mode 100644 index 0000000..ec03920 --- /dev/null +++ b/app/Template/project/layout.php @@ -0,0 +1,11 @@ +
+ projectHeader->render($project, 'TaskListController', 'show') ?> + +
diff --git a/app/Template/project/sidebar.php b/app/Template/project/sidebar.php new file mode 100644 index 0000000..d59ad71 --- /dev/null +++ b/app/Template/project/sidebar.php @@ -0,0 +1,74 @@ + diff --git a/app/Template/project_action_duplication/show.php b/app/Template/project_action_duplication/show.php new file mode 100644 index 0000000..c2f52e3 --- /dev/null +++ b/app/Template/project_action_duplication/show.php @@ -0,0 +1,15 @@ + + +

+ +
+ form->csrf() ?> + + form->label(t('Create from another project'), 'src_project_id') ?> + form->select('src_project_id', $projects_list) ?> + + modal->submitButtons() ?> +
+ diff --git a/app/Template/project_creation/create.php b/app/Template/project_creation/create.php new file mode 100644 index 0000000..877e86a --- /dev/null +++ b/app/Template/project_creation/create.php @@ -0,0 +1,52 @@ +
+ +
+ + form->csrf() ?> + form->hidden('is_private', $values) ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required')) ?> + + form->label(t('Identifier'), 'identifier') ?> + form->text('identifier', $values, $errors, array('autofocus')) ?> +

+ + form->checkbox('per_swimlane_task_limits', t('Column task limits apply to each swimlane individually'), 1, false) ?> + + form->label(t('Task limit'), 'task_limit') ?> + form->number('task_limit', $values, $errors, array('min="0"')) ?> + + 1): ?> + form->label(t('Create from another project'), 'src_project_id') ?> + form->select('src_project_id', $projects_list, $values, array(), array(), 'js-project-creation-select-options') ?> + + +
0 ? '' : 'style="display: none"' ?>> +

+ + + form->checkbox('projectPermissionModel', t('Permissions'), 1, true) ?> + form->checkbox('projectRoleModel', t('Custom roles'), 1, true) ?> + + + form->checkbox('categoryModel', t('Categories'), 1, true) ?> + form->checkbox('tagDuplicationModel', t('Tags'), 1, true) ?> + form->checkbox('actionModel', t('Actions'), 1, true) ?> + form->checkbox('customFilterModel', t('Custom filters'), 1, true) ?> + form->checkbox('projectMetadataModel', t('Metadata'), 1, false) ?> + form->checkbox('projectTaskDuplicationModel', t('Tasks'), 1, false) ?> +
+ + hook->render('template:project:creation:form', array('values' => $values, 'errors' => $errors)) ?> + + modal->submitButtons() ?> +
+ +
+

+
+ +
diff --git a/app/Template/project_edit/show.php b/app/Template/project_edit/show.php new file mode 100644 index 0000000..c1fc6f3 --- /dev/null +++ b/app/Template/project_edit/show.php @@ -0,0 +1,76 @@ +app->isAjax()): ?> + + + + +
+ form->csrf() ?> + +
+ + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('required', 'autofocus', 'tabindex="1"')) ?> + + form->label(t('Email'), 'email') ?> + form->email('email', $values, $errors, array('tabindex="2"', 'autocomplete="email"')) ?> +

+ + form->label(t('Identifier'), 'identifier') ?> + form->text('identifier', $values, $errors, array('maxlength="50"', 'tabindex="3"')) ?> +

+ + form->label(t('Description'), 'description') ?> + form->textEditor('description', $values, $errors, array('tabindex' => 4)) ?> + + hook->render('template:project:edit:form', array('values' => $values, 'errors' => $errors)) ?> + + + form->checkbox('per_swimlane_task_limits', t('Task limits apply to each swimlane individually'), 1, $project['per_swimlane_task_limits'] == 1, '', array('tabindex' => 5)) ?> + + form->label(t('Task limit'), 'task_limit') ?> + form->number('task_limit', $values, $errors, array('tabindex' => 6, 'min="0"')) ?> +
+ +
+ + + app->config('disable_private_project') != 1): ?> + user->hasProjectAccess('ProjectCreationController', 'create', $project['id'])): ?> + form->checkbox('is_private', t('Personal project'), 1, $project['is_private'] == 1) ?> +

+ + + +
+ form->label(t('Project owner'), 'owner_id') ?> + form->select('owner_id', $owners, $values, $errors, array('tabindex="7"')) ?> +
+
+ +
+ + + form->date(t('Start date'), 'start_date', $values, $errors, array('tabindex="8"')) ?> + form->date(t('End date'), 'end_date', $values, $errors, array('tabindex="9"')) ?> +
+ +
+ + + form->label(t('Default priority'), 'priority_default') ?> + form->number('priority_default', $values, $errors, array('tabindex="10"')) ?> + + form->label(t('Lowest priority'), 'priority_start') ?> + form->number('priority_start', $values, $errors, array('tabindex="11"')) ?> + + form->label(t('Highest priority'), 'priority_end') ?> + form->number('priority_end', $values, $errors, array('tabindex="12"')) ?> +
+ + modal->submitButtons(array('tabindex' => 13)) ?> +
diff --git a/app/Template/project_file/create.php b/app/Template/project_file/create.php new file mode 100644 index 0000000..28b80ce --- /dev/null +++ b/app/Template/project_file/create.php @@ -0,0 +1,21 @@ + + +app->component('file-upload', array( + 'csrf' => $this->app->getToken()->getReusableCSRFToken(), + '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' => $max_size > 0 ? t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)) : null, + 'labelSuccess' => t('All files have been uploaded successfully.'), + 'labelCloseSuccess' => t('Close this window'), + 'labelUploadError' => t('Unable to upload this file.'), +)) ?> + +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 new file mode 100644 index 0000000..043b8fc --- /dev/null +++ b/app/Template/project_file/remove.php @@ -0,0 +1,15 @@ + + +
+

+ text->e($file['name'])) ?> +

+ + modal->confirmButtons( + 'ProjectFileController', + 'remove', + array('project_id' => $project['id'], 'file_id' => $file['id']) + ) ?> +
diff --git a/app/Template/project_header/dropdown.php b/app/Template/project_header/dropdown.php new file mode 100644 index 0000000..745ad66 --- /dev/null +++ b/app/Template/project_header/dropdown.php @@ -0,0 +1,83 @@ + diff --git a/app/Template/project_header/header.php b/app/Template/project_header/header.php new file mode 100644 index 0000000..a92d630 --- /dev/null +++ b/app/Template/project_header/header.php @@ -0,0 +1,13 @@ +helper->projectHeader->hookBefore = $this->hook->render('template:project:header:before', array('project' => $project)); +$this->helper->projectHeader->dropdownHtml = $this->render('project_header/dropdown', array('project' => $project, 'board_view' => $board_view)); +$this->helper->projectHeader->viewsHtml = $this->render('project_header/views', array('project' => $project, 'filters' => $filters)); +$this->helper->projectHeader->searchHtml = $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(), +)); +$this->helper->projectHeader->hookAfter = $this->hook->render('template:project:header:after', array('project' => $project)); +?> diff --git a/app/Template/project_header/search.php b/app/Template/project_header/search.php new file mode 100644 index 0000000..36bb73b --- /dev/null +++ b/app/Template/project_header/search.php @@ -0,0 +1,58 @@ +
+ +
diff --git a/app/Template/project_header/views.php b/app/Template/project_header/views.php new file mode 100644 index 0000000..e6e7a99 --- /dev/null +++ b/app/Template/project_header/views.php @@ -0,0 +1,22 @@ +
    + + hook->render('template:project-header:view-switcher-before-project-overview', array('project' => $project, 'filters' => $filters)) ?> + +
  • app->checkMenuSelection('ProjectOverviewController') ?>> + url->icon('eye', t('Overview'), 'ProjectOverviewController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-overview', t('Keyboard shortcut: "%s"', 'v o')) ?> +
  • + + hook->render('template:project-header:view-switcher-before-board-view', array('project' => $project, 'filters' => $filters)) ?> + +
  • app->checkMenuSelection('BoardViewController') ?>> + url->icon('th', t('Board'), 'BoardViewController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-board', t('Keyboard shortcut: "%s"', 'v b')) ?> +
  • + + hook->render('template:project-header:view-switcher-before-task-list', array('project' => $project, 'filters' => $filters)) ?> + +
  • app->checkMenuSelection('TaskListController') ?>> + url->icon('list', t('List'), 'TaskListController', 'show', array('project_id' => $project['id'], 'search' => $filters['search']), false, 'view-listing', t('Keyboard shortcut: "%s"', 'v l')) ?> +
  • + + hook->render('template:project-header:view-switcher', array('project' => $project, 'filters' => $filters)) ?> +
diff --git a/app/Template/project_list/header.php b/app/Template/project_list/header.php new file mode 100644 index 0000000..24ac904 --- /dev/null +++ b/app/Template/project_list/header.php @@ -0,0 +1,12 @@ +
+
+ getTotal() > 1): ?> + getTotal()) ?> + + getTotal()) ?> + +
+
+ render('project_list/sort_menu', array('paginator' => $paginator)) ?> +
+
diff --git a/app/Template/project_list/listing.php b/app/Template/project_list/listing.php new file mode 100644 index 0000000..d1ecd4b --- /dev/null +++ b/app/Template/project_list/listing.php @@ -0,0 +1,56 @@ + + +
+ +
+ +isEmpty()): ?> +

+ +
+ render('project_list/header', array('paginator' => $paginator)) ?> + getCollection() as $project): ?> +
+ render('project_list/project_title', array( + 'project' => $project, + )) ?> + + render('project_list/project_details', array( + 'project' => $project, + )) ?> + + render('project_list/project_icons', array( + 'project' => $project, + )) ?> +
+ +
+ + + diff --git a/app/Template/project_list/project_details.php b/app/Template/project_list/project_details.php new file mode 100644 index 0000000..e83c6c7 --- /dev/null +++ b/app/Template/project_list/project_details.php @@ -0,0 +1,15 @@ +
+
    + 0): ?> +
  • text->e($project['owner_name'] ?: $project['owner_username']) ?>
  • + + + +
  • dt->date($project['start_date']) ?>
  • + + + +
  • dt->date($project['end_date']) ?>
  • + +
+
\ No newline at end of file diff --git a/app/Template/project_list/project_icons.php b/app/Template/project_list/project_icons.php new file mode 100644 index 0000000..45d696c --- /dev/null +++ b/app/Template/project_list/project_icons.php @@ -0,0 +1,23 @@ +
+   + + + + + + + + + + user->hasAccess('ProjectUserOverviewController', 'managers')): ?> + app->tooltipLink('', $this->url->href('ProjectUserOverviewController', 'users', array('project_id' => $project['id']))) ?> + + + + app->tooltipMarkdown($project['description']) ?> + + + + + +
diff --git a/app/Template/project_list/project_title.php b/app/Template/project_list/project_title.php new file mode 100644 index 0000000..04dd42b --- /dev/null +++ b/app/Template/project_list/project_title.php @@ -0,0 +1,16 @@ +
+ user->hasProjectAccess('ProjectViewController', 'show', $project['id'])): ?> + render('project/dropdown', array('project' => $project)) ?> + + + + + hook->render('template:dashboard:project:before-title', array('project' => $project)) ?> + + + url->link($this->text->e($project['name']), 'BoardViewController', 'show', array('project_id' => $project['id'])) ?> + + + hook->render('template:dashboard:project:after-title', array('project' => $project)) ?> + +
diff --git a/app/Template/project_list/sort_menu.php b/app/Template/project_list/sort_menu.php new file mode 100644 index 0000000..39bac60 --- /dev/null +++ b/app/Template/project_list/sort_menu.php @@ -0,0 +1,26 @@ + diff --git a/app/Template/project_overview/activity.php b/app/Template/project_overview/activity.php new file mode 100644 index 0000000..e7d7c1b --- /dev/null +++ b/app/Template/project_overview/activity.php @@ -0,0 +1,5 @@ +
+
+ render('event/events', array('events' => $events)) ?> +
+
\ No newline at end of file diff --git a/app/Template/project_overview/attachments.php b/app/Template/project_overview/attachments.php new file mode 100644 index 0000000..d50211a --- /dev/null +++ b/app/Template/project_overview/attachments.php @@ -0,0 +1,12 @@ +
+
+ user->hasProjectAccess('ProjectFileController', 'create', $project['id'])): ?> +
+ modal->mediumButton('plus', t('Upload a file'), 'ProjectFileController', 'create', array('project_id' => $project['id'])) ?> +
+ + + render('project_overview/images', array('project' => $project, 'images' => $images)) ?> + render('project_overview/files', array('project' => $project, 'files' => $files)) ?> +
+
diff --git a/app/Template/project_overview/columns.php b/app/Template/project_overview/columns.php new file mode 100644 index 0000000..22b0ffd --- /dev/null +++ b/app/Template/project_overview/columns.php @@ -0,0 +1,8 @@ +
+ +
+ + text->e($column['title']) ?> +
+ +
diff --git a/app/Template/project_overview/description.php b/app/Template/project_overview/description.php new file mode 100644 index 0000000..6ad9b67 --- /dev/null +++ b/app/Template/project_overview/description.php @@ -0,0 +1,12 @@ +
+
+ user->hasProjectAccess('ProjectEditController', 'show', $project['id'])): ?> +
+ modal->mediumButton('edit', t('Edit description'), 'ProjectEditController', 'show', array('project_id' => $project['id'])) ?> +
+ +
+ text->markdown($project['description']) ?> +
+
+
diff --git a/app/Template/project_overview/files.php b/app/Template/project_overview/files.php new file mode 100644 index 0000000..09b7915 --- /dev/null +++ b/app/Template/project_overview/files.php @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + +
+ + + + text->e($file['user_name'] ?: $file['username']) ?> + + dt->date($file['date']) ?> + + text->bytes($file['size']) ?> +
+ diff --git a/app/Template/project_overview/images.php b/app/Template/project_overview/images.php new file mode 100644 index 0000000..f875ce7 --- /dev/null +++ b/app/Template/project_overview/images.php @@ -0,0 +1,49 @@ + +
+ +
+ app->component('image-slideshow', array( + 'images' => $images, + 'image' => $file, + 'regex_file_id' => 'FILE_ID', + 'regex_etag' => 'ETAG', + 'url' => array( + 'image' => $this->url->to('FileViewerController', 'image', array('file_id' => 'FILE_ID', 'project_id' => $project['id'], 'etag' => 'ETAG')), + 'thumbnail' => $this->url->to('FileViewerController', 'thumbnail', array('file_id' => 'FILE_ID', 'project_id' => $project['id'], 'etag' => 'ETAG')), + 'download' => $this->url->to('FileViewerController', 'download', array('file_id' => 'FILE_ID', 'project_id' => $project['id'], 'etag' => 'ETAG')), + ) + )) ?> + +
+
+ +
+
+ app->tooltipMarkdown(t('Uploaded: %s', $this->dt->datetime($file['date']))."\n\n".t('Size: %s', $this->text->bytes($file['size']))) ?> + + + + dt->datetime($file['date'])) ?> + +
+
+
+ +
+ \ No newline at end of file diff --git a/app/Template/project_overview/information.php b/app/Template/project_overview/information.php new file mode 100644 index 0000000..bec0543 --- /dev/null +++ b/app/Template/project_overview/information.php @@ -0,0 +1,36 @@ +
+
+
+
    + 0): ?> +
  • text->e($project['owner_name'] ?: $project['owner_username']) ?>
  • + + + + $role_name): ?> + +
  • + text->e($role_name) ?>: + text->implode(', ', $users[$role]) ?> +
  • + + + + + +
  • dt->date($project['start_date']) ?>
  • + + + +
  • dt->date($project['end_date']) ?>
  • + + + +
  • url->icon('share-alt', t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?>
  • +
  • url->icon('rss-square', t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?>
  • +
  • url->icon('calendar', t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?>
  • + +
+
+
+
diff --git a/app/Template/project_overview/show.php b/app/Template/project_overview/show.php new file mode 100644 index 0000000..9f3ab5b --- /dev/null +++ b/app/Template/project_overview/show.php @@ -0,0 +1,40 @@ +
+ projectHeader->render($project, 'ProjectOverviewController', 'show') ?> + +
+ render('project_overview/columns', array('project' => $project, 'columns' => $columns)) ?> +
+ + +
+ + + + + + + +
+ + + + +
+ + +
+
+ render('project_overview/description', array('project' => $project)) ?> +
+
+ render('project_overview/attachments', array('project' => $project, 'images' => $images, 'files' => $files)) ?> +
+
+ render('project_overview/information', array('project' => $project, 'users' => $users, 'roles' => $roles)) ?> +
+
+ render('project_overview/activity', array('project' => $project, 'events' => $events)) ?> +
+
+
+
diff --git a/app/Template/project_permission/groups.php b/app/Template/project_permission/groups.php new file mode 100644 index 0000000..a5fcf66 --- /dev/null +++ b/app/Template/project_permission/groups.php @@ -0,0 +1,59 @@ + + + +
+ + + + + + + + + + + + + + + + +
text->e($group['name']) ?> + app->component('project-select-role', array( + 'roles' => $roles, + 'role' => $group['role'], + 'id' => $group['id'], + 'ariaLabel' => t('Role'), + 'url' => $this->url->to('ProjectPermissionController', 'changeGroupRole', array('project_id' => $project['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())), + )) ?> + + url->icon('trash-o', t('Remove'), 'ProjectPermissionController', 'removeGroup', array('project_id' => $project['id'], 'group_id' => $group['id']), true) ?> +
+ + + +
+
+ form->csrf() ?> + form->hidden('group_id', $values) ?> + form->hidden('external_id', $values) ?> + + form->label(t('Group Name'), 'name') ?> + 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-fields="external_id"', + 'data-search-url="'.$this->url->href('GroupAjaxController', 'autocomplete').'"', + ), + 'autocomplete') ?> + + form->select('role', $roles, $values, $errors, array('aria-label="'.t('Role').'"')) ?> + + +
+
+ \ No newline at end of file diff --git a/app/Template/project_permission/index.php b/app/Template/project_permission/index.php new file mode 100644 index 0000000..52a69fb --- /dev/null +++ b/app/Template/project_permission/index.php @@ -0,0 +1,19 @@ + + +render('project_permission/users', array( + 'project' => $project, + 'roles' => $roles, + 'users' => $users, + 'errors' => $errors, + 'values' => $values, +)) ?> + +render('project_permission/groups', array( + 'project' => $project, + 'roles' => $roles, + 'groups' => $groups, + 'errors' => $errors, + 'values' => $values, +)) ?> diff --git a/app/Template/project_permission/users.php b/app/Template/project_permission/users.php new file mode 100644 index 0000000..b43dda2 --- /dev/null +++ b/app/Template/project_permission/users.php @@ -0,0 +1,60 @@ + +
+ + + + + + + + + + + + + + + + +
text->e($user['name'] ?: $user['username']) ?> + app->component('project-select-role', array( + 'roles' => $roles, + 'role' => $user['role'], + 'id' => $user['id'], + 'ariaLabel' => t('Role'), + 'url' => $this->url->to('ProjectPermissionController', 'changeUserRole', array('project_id' => $project['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())), + )) ?> + + user->isCurrentUser($user['id'])): ?> + url->icon('trash-o', t('Remove'), 'ProjectPermissionController', 'removeUser', array('project_id' => $project['id'], 'user_id' => $user['id']), true) ?> + +
+ + + +
+
+ form->csrf() ?> + form->hidden('user_id', $values) ?> + form->hidden('username', $values) ?> + form->hidden('external_id', $values) ?> + form->hidden('external_id_column', $values) ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array( + 'required', + 'placeholder="'.t('Enter user name...').'"', + 'title="'.t('Enter user name...').'"', + 'data-dst-field="user_id"', + 'data-dst-extra-fields="external_id,external_id_column,username"', + 'data-search-url="'.$this->url->href('UserAjaxController', 'autocomplete').'"', + ), + 'autocomplete') ?> + + form->select('role', $roles, $values, $errors, array('aria-label="'.t('Role').'"')) ?> + + +
+
+ hook->render('template:project-permission:after-adduser', ['project' => $project, 'values' => $values, 'errors' => $errors]) ?> + diff --git a/app/Template/project_predefined_content/show.php b/app/Template/project_predefined_content/show.php new file mode 100644 index 0000000..5bfd072 --- /dev/null +++ b/app/Template/project_predefined_content/show.php @@ -0,0 +1,45 @@ + + + +

+ + + + + + +
+ + text->e($template['title']) ?> + app->tooltipMarkdown($template['description']) ?> +
+ + +
+ form->csrf() ?> + +
+ + form->textarea('predefined_email_subjects', $values, $errors, array('tabindex="1"')) ?> +

+
+ + modal->submitButtons(array('tabindex' => 2)) ?> +
diff --git a/app/Template/project_role/create.php b/app/Template/project_role/create.php new file mode 100644 index 0000000..f554eb1 --- /dev/null +++ b/app/Template/project_role/create.php @@ -0,0 +1,12 @@ + +
+ form->csrf() ?> + form->hidden('project_id', $values) ?> + + form->label(t('Role'), 'role') ?> + form->text('role', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/project_role/edit.php b/app/Template/project_role/edit.php new file mode 100644 index 0000000..740ac0f --- /dev/null +++ b/app/Template/project_role/edit.php @@ -0,0 +1,13 @@ + +
+ form->csrf() ?> + form->hidden('project_id', $values) ?> + form->hidden('role_id', $values) ?> + + form->label(t('Role'), 'role') ?> + form->text('role', $values, $errors, array('autofocus', 'required', 'maxlength="50"')) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/project_role/remove.php b/app/Template/project_role/remove.php new file mode 100644 index 0000000..44d24ed --- /dev/null +++ b/app/Template/project_role/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'ProjectRoleController', + 'remove', + array('project_id' => $project['id'], 'role_id' => $role['role_id']) + ) ?> +
diff --git a/app/Template/project_role/show.php b/app/Template/project_role/show.php new file mode 100644 index 0000000..65c9ef1 --- /dev/null +++ b/app/Template/project_role/show.php @@ -0,0 +1,97 @@ + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + text->e($restriction['title']) ?> + + modal->confirm('trash-o', t('Remove'), 'ProjectRoleRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?> +
+ + + + + + text->e($restriction['column_title']) ?> + + text->e($restriction['title']) ?> + + modal->confirm('trash-o', t('Remove'), 'ColumnRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?> +
+ + text->e($restriction['src_column_title']) ?> / text->e($restriction['dst_column_title']) ?> + + + + + + + + modal->confirm('trash-o', t('Remove'), 'ColumnMoveRestrictionController', 'confirm', array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id'])) ?> +
+ + diff --git a/app/Template/project_role_restriction/create.php b/app/Template/project_role_restriction/create.php new file mode 100644 index 0000000..2b6a61d --- /dev/null +++ b/app/Template/project_role_restriction/create.php @@ -0,0 +1,15 @@ +
+ +
+ form->csrf() ?> + form->hidden('project_id', $values) ?> + form->hidden('role_id', $values) ?> + + form->label(t('Restriction'), 'rule') ?> + form->select('rule', $restrictions, $values, $errors) ?> + + modal->submitButtons() ?> +
+
diff --git a/app/Template/project_role_restriction/remove.php b/app/Template/project_role_restriction/remove.php new file mode 100644 index 0000000..1a99419 --- /dev/null +++ b/app/Template/project_role_restriction/remove.php @@ -0,0 +1,15 @@ + + +
+

+ text->in($restriction['rule'], $restrictions)) ?> +

+ + modal->confirmButtons( + 'ProjectRoleRestrictionController', + 'remove', + array('project_id' => $project['id'], 'restriction_id' => $restriction['restriction_id']) + ) ?> +
diff --git a/app/Template/project_status/disable.php b/app/Template/project_status/disable.php new file mode 100644 index 0000000..f2bd762 --- /dev/null +++ b/app/Template/project_status/disable.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'ProjectStatusController', + 'disable', + array('project_id' => $project['id']) + ) ?> +
diff --git a/app/Template/project_status/enable.php b/app/Template/project_status/enable.php new file mode 100644 index 0000000..c851899 --- /dev/null +++ b/app/Template/project_status/enable.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'ProjectStatusController', + 'enable', + array('project_id' => $project['id']) + ) ?> +
diff --git a/app/Template/project_status/remove.php b/app/Template/project_status/remove.php new file mode 100644 index 0000000..27ae2ae --- /dev/null +++ b/app/Template/project_status/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'ProjectStatusController', + 'remove', + array('project_id' => $project['id']) + ) ?> +
diff --git a/app/Template/project_tag/create.php b/app/Template/project_tag/create.php new file mode 100644 index 0000000..61b6e67 --- /dev/null +++ b/app/Template/project_tag/create.php @@ -0,0 +1,14 @@ + +
+ form->csrf() ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"')) ?> + + form->label(t('Color'), 'color_id') ?> + form->select('color_id', array('' => t('No color')) + $colors, $values, $errors, array(), 'color-picker') ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/project_tag/edit.php b/app/Template/project_tag/edit.php new file mode 100644 index 0000000..0fab036 --- /dev/null +++ b/app/Template/project_tag/edit.php @@ -0,0 +1,14 @@ + +
+ form->csrf() ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"')) ?> + + form->label(t('Color'), 'color_id') ?> + form->select('color_id', array('' => t('No color')) + $colors, $values, $errors, array(), 'color-picker') ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/project_tag/index.php b/app/Template/project_tag/index.php new file mode 100644 index 0000000..f9373c3 --- /dev/null +++ b/app/Template/project_tag/index.php @@ -0,0 +1,62 @@ + + + +

+ + + + + + + + + + + + +
+ + text->e($tag['name']) ?> + + +
+ text->e($colors[$tag['color_id']]) ?> + +
+ + + + +
+
+ form->csrf() ?> + + form->checkbox('enable_global_tags', t('Enable global tags for this project'), 1, $project['enable_global_tags'] == 1) ?> + + modal->submitButtons() ?> +
+
diff --git a/app/Template/project_tag/make_global.php b/app/Template/project_tag/make_global.php new file mode 100644 index 0000000..13a40c5 --- /dev/null +++ b/app/Template/project_tag/make_global.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'ProjectTagController', + 'makeGlobalTag', + array('tag_id' => $tag['id'], 'project_id' => $project['id']) + ) ?> +
diff --git a/app/Template/project_tag/remove.php b/app/Template/project_tag/remove.php new file mode 100644 index 0000000..9f957d1 --- /dev/null +++ b/app/Template/project_tag/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'ProjectTagController', + 'remove', + array('tag_id' => $tag['id'], 'project_id' => $project['id']) + ) ?> +
diff --git a/app/Template/project_user_overview/layout.php b/app/Template/project_user_overview/layout.php new file mode 100644 index 0000000..5a1b312 --- /dev/null +++ b/app/Template/project_user_overview/layout.php @@ -0,0 +1,29 @@ +
+ + +
diff --git a/app/Template/project_user_overview/roles.php b/app/Template/project_user_overview/roles.php new file mode 100644 index 0000000..1d33840 --- /dev/null +++ b/app/Template/project_user_overview/roles.php @@ -0,0 +1,32 @@ +isEmpty()): ?> +

+ + + + + + + + getCollection() as $project): ?> + + + + + + +
order(t('User'), 'users.username') ?>order(t('Project'), 'projects.name') ?>
+ text->e($this->user->getFullname($project)) ?> + + url->link('', 'BoardViewController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Board')) ?> + url->link('', 'ProjectViewController', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Project settings')) ?> + + text->e($project['project_name']) ?> + + + + text->e($column['title']) ?> + +
+ + + diff --git a/app/Template/project_user_overview/sidebar.php b/app/Template/project_user_overview/sidebar.php new file mode 100644 index 0000000..1fee5b7 --- /dev/null +++ b/app/Template/project_user_overview/sidebar.php @@ -0,0 +1,32 @@ + diff --git a/app/Template/project_user_overview/tasks.php b/app/Template/project_user_overview/tasks.php new file mode 100644 index 0000000..6f75134 --- /dev/null +++ b/app/Template/project_user_overview/tasks.php @@ -0,0 +1,46 @@ +isEmpty()): ?> +

+isEmpty()): ?> + + + + + + + + + + + getCollection() as $task): ?> + + + + + + + + + + +
order(t('Id'), 'tasks.id') ?>order(t('Project'), 'projects.name') ?>order(t('Column'), 'tasks.column_id') ?>order(t('Title'), 'tasks.title') ?>order(t('Assignee'), 'users.username') ?>order(t('Start date'), 'tasks.date_started') ?>order(t('Due date'), 'tasks.date_due') ?>
+ url->link('#'.$this->text->e($task['id']), 'TaskViewController', 'show', array('task_id' => $task['id']), false, '', t('View this task')) ?> + + url->link($this->text->e($task['project_name']), 'BoardViewController', 'show', array('project_id' => $task['project_id'])) ?> + + text->e($task['column_name']) ?> + + url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id']), false, '', t('View this task')) ?> + + + text->e($task['assignee_name'] ?: $task['assignee_username']) ?> + + + + + dt->date($task['date_started']) ?> + + dt->datetime($task['date_due']) ?> +
+ + + diff --git a/app/Template/project_user_overview/tooltip_users.php b/app/Template/project_user_overview/tooltip_users.php new file mode 100644 index 0000000..8bc821e --- /dev/null +++ b/app/Template/project_user_overview/tooltip_users.php @@ -0,0 +1,16 @@ + +

+ + + $role_name): ?> + + + $user): ?> + + + + +
text->e($role_name) ?>
+ url->link($this->text->e($user), 'ProjectUserOverviewController', 'opens', array('user_id' => $user_id)) ?> +
+ diff --git a/app/Template/project_view/duplicate.php b/app/Template/project_view/duplicate.php new file mode 100644 index 0000000..f0268f6 --- /dev/null +++ b/app/Template/project_view/duplicate.php @@ -0,0 +1,30 @@ + + +
+

+ +

+
+ + form->csrf() ?> + + + form->checkbox('projectPermissionModel', t('Permissions'), 1, true) ?> + form->checkbox('projectRoleModel', t('Custom roles'), 1, true) ?> + + + form->checkbox('categoryModel', t('Categories'), 1, true) ?> + form->checkbox('tagDuplicationModel', t('Tags'), 1, true) ?> + form->checkbox('actionModel', t('Actions'), 1, true) ?> + form->checkbox('customFilterModel', t('Custom filters'), 1, true) ?> + form->checkbox('projectMetadataModel', t('Metadata'), 1, false) ?> + form->checkbox('projectTaskDuplicationModel', t('Tasks'), 1, false) ?> + +
+ + url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?> +
+
+
diff --git a/app/Template/project_view/importTasks.php b/app/Template/project_view/importTasks.php new file mode 100644 index 0000000..d66011b --- /dev/null +++ b/app/Template/project_view/importTasks.php @@ -0,0 +1,15 @@ + + 0): ?> +
+ form->csrf() ?> + + form->label(t('Select the project to copy tasks from'), 'src_project_id') ?> + form->select('src_project_id', $projects, $values, $errors) ?> + + modal->submitButtons(['submitLabel' => t('Save')]) ?> +
+ +

+ diff --git a/app/Template/project_view/integrations.php b/app/Template/project_view/integrations.php new file mode 100644 index 0000000..f8bff7e --- /dev/null +++ b/app/Template/project_view/integrations.php @@ -0,0 +1,15 @@ + + +
+ form->csrf() ?> + + hook->render('template:project:integrations', array('project' => $project, 'values' => $values, 'webhook_token' => $webhook_token)) ?> + + +

+ + + +
diff --git a/app/Template/project_view/notifications.php b/app/Template/project_view/notifications.php new file mode 100644 index 0000000..29cc088 --- /dev/null +++ b/app/Template/project_view/notifications.php @@ -0,0 +1,20 @@ + + +

+ +
+ + form->csrf() ?> + +

+ form->checkboxes('notification_types', $types, $notifications) ?> + +
+ + + url->link(t('cancel'), 'ProjectViewController', 'show', array('project_id' => $project['id'])) ?> +
+
+ diff --git a/app/Template/project_view/share.php b/app/Template/project_view/share.php new file mode 100644 index 0000000..ab2515c --- /dev/null +++ b/app/Template/project_view/share.php @@ -0,0 +1,20 @@ + + + + + +
+
    +
  • url->icon('share-alt', t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?>
  • +
  • url->icon('rss-square', t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?>
  • +
  • url->icon('calendar', t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token']), false, '', '', true) ?>
  • +
+
+ + url->link(t('Disable public access'), 'ProjectViewController', 'updateSharing', array('project_id' => $project['id'], 'switch' => 'disable'), true, 'btn btn-red') ?> + +

+ url->link(t('Enable public access'), 'ProjectViewController', 'updateSharing', array('project_id' => $project['id'], 'switch' => 'enable'), true, 'btn btn-blue') ?> + diff --git a/app/Template/project_view/show.php b/app/Template/project_view/show.php new file mode 100644 index 0000000..c671879 --- /dev/null +++ b/app/Template/project_view/show.php @@ -0,0 +1,98 @@ + +
    +
  • + + 0): ?> +
  • text->e($project['owner_name'] ?: $project['owner_username']) ?>
  • + + + +
  • + + + +
  • url->icon('share-alt', t('Public link'), 'BoardViewController', 'readonly', array('token' => $project['token']), false, '', '', true) ?>
  • +
  • url->icon('rss-square', t('RSS feed'), 'FeedController', 'project', array('token' => $project['token']), false, '', '', true) ?>
  • +
  • url->icon('calendar', t('iCal feed'), 'ICalendarController', 'project', array('token' => $project['token'])) ?>
  • + +
  • + + + +
  • dt->datetime($project['last_modified']) ?>
  • + + + +
  • dt->date($project['start_date']) ?>
  • + + + +
  • dt->date($project['end_date']) ?>
  • + + + +
  • + +
  • + + +
  • +
+ + + + + hook->render('template:project:view:form', array('values' => $values, 'errors' => $errors)) ?> + + +
+ text->markdown($project['description']) ?> +
+ + + + +

+ + + + + + + + + + + + + + + + + + + + + + +
+ text->e($column['title']) ?> + + app->tooltipMarkdown($column['description']) ?> + + + + + + + + + +
+ diff --git a/app/Template/search/activity.php b/app/Template/search/activity.php new file mode 100644 index 0000000..5d8f142 --- /dev/null +++ b/app/Template/search/activity.php @@ -0,0 +1,40 @@ + + +
+ +
+ + +
+

+

project:"My project" creator:me

+
    +
  • project:"My project"
  • +
  • creator:admin
  • +
  • created:today
  • +
  • status:open
  • +
  • title:"My task"
  • +
+

url->doc(t('View advanced search syntax'), 'search') ?>

+
+ +

+ + render('event/events', array('events' => $events)) ?> + diff --git a/app/Template/search/index.php b/app/Template/search/index.php new file mode 100644 index 0000000..3e8d39b --- /dev/null +++ b/app/Template/search/index.php @@ -0,0 +1,44 @@ + + +
+ +
+ + +
+

+

project:"My project" assignee:me due:tomorrow

+
    +
  • project:"My project"
  • +
  • column:"Work in progress"
  • +
  • assignee:nobody
  • +
  • color:Blue
  • +
  • category:"Feature Request"
  • +
  • description:"Something to find"
  • +
  • due:2015-07-01
  • +
+

url->doc(t('View advanced search syntax'), 'search') ?>

+
+isEmpty()): ?> +

+isEmpty()): ?> + render('search/results', array( + 'paginator' => $paginator, + )) ?> + diff --git a/app/Template/search/results.php b/app/Template/search/results.php new file mode 100644 index 0000000..85b09ea --- /dev/null +++ b/app/Template/search/results.php @@ -0,0 +1,33 @@ +
+ render('task_list/header', array( + 'paginator' => $paginator, + )) ?> + + getCollection() as $task): ?> +
+ render('task_list/task_title', array( + 'task' => $task, + )) ?> + + render('task_list/task_details', array( + 'task' => $task, + )) ?> + + render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + render('task_list/task_icons', array( + 'task' => $task, + )) ?> + + render('task_list/task_subtasks', array( + 'task' => $task, + )) ?> + + hook->render('template:search:task:footer', array('task' => $task)) ?> +
+ +
+ + \ No newline at end of file diff --git a/app/Template/subtask/create.php b/app/Template/subtask/create.php new file mode 100644 index 0000000..6919347 --- /dev/null +++ b/app/Template/subtask/create.php @@ -0,0 +1,27 @@ + + + 0): ?> +

+ + + + + +

+ + +
+ form->csrf() ?> + + subtask->renderBulkTitleField($values, $errors, array('autofocus')) ?> + subtask->renderAssigneeField($users_list, $values, $errors) ?> + subtask->renderTimeEstimatedField($values, $errors) ?> + + hook->render('template:subtask:form:create', array('values' => $values, 'errors' => $errors)) ?> + + form->checkbox('another_subtask', t('Create another sub-task'), 1, isset($values['another_subtask']) && $values['another_subtask'] == 1) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/subtask/edit.php b/app/Template/subtask/edit.php new file mode 100644 index 0000000..90aaca8 --- /dev/null +++ b/app/Template/subtask/edit.php @@ -0,0 +1,16 @@ + + +
+ form->csrf() ?> + + subtask->renderTitleField($values, $errors, array('autofocus')) ?> + subtask->renderAssigneeField($users_list, $values, $errors) ?> + subtask->renderTimeEstimatedField($values, $errors) ?> + subtask->renderTimeSpentField($values, $errors) ?> + + hook->render('template:subtask:form:edit', array('values' => $values, 'errors' => $errors)) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/subtask/menu.php b/app/Template/subtask/menu.php new file mode 100644 index 0000000..30d28c7 --- /dev/null +++ b/app/Template/subtask/menu.php @@ -0,0 +1,16 @@ + diff --git a/app/Template/subtask/remove.php b/app/Template/subtask/remove.php new file mode 100644 index 0000000..3db814e --- /dev/null +++ b/app/Template/subtask/remove.php @@ -0,0 +1,20 @@ + + +
+
+ +
    +
  • + text->e($subtask['title']) ?> +
  • +
+
+ + modal->confirmButtons( + 'SubtaskController', + 'remove', + array('task_id' => $task['id'], 'subtask_id' => $subtask['id']) + ) ?> +
diff --git a/app/Template/subtask/show.php b/app/Template/subtask/show.php new file mode 100644 index 0000000..e5d3860 --- /dev/null +++ b/app/Template/subtask/show.php @@ -0,0 +1,10 @@ +
> + +
+ render('subtask/table', array( + 'subtasks' => $subtasks, + 'task' => $task, + 'editable' => $editable + )) ?> +
+
diff --git a/app/Template/subtask/table.php b/app/Template/subtask/table.php new file mode 100644 index 0000000..8b9e040 --- /dev/null +++ b/app/Template/subtask/table.php @@ -0,0 +1,47 @@ + + + + + + + hook->render('template:subtask:table:header:before-timetracking') ?> + + + + + + + + + hook->render('template:subtask:table:rows', array('subtask' => $subtask)) ?> + + + + +
+
+ +   + render('subtask/menu', array( + 'task' => $task, + 'subtask' => $subtask, + )) ?> + subtask->renderToggleStatus($task, $subtask, 'table') ?> + + subtask->renderTitle($subtask) ?> + +
+
+ + text->e($subtask['name'] ?: $subtask['username']) ?> + + + render('subtask/timer', array( + 'task' => $task, + 'subtask' => $subtask, + )) ?> +
+ diff --git a/app/Template/subtask/timer.php b/app/Template/subtask/timer.php new file mode 100644 index 0000000..0f17fc7 --- /dev/null +++ b/app/Template/subtask/timer.php @@ -0,0 +1,15 @@ + + + + + + / + + + + + + user->hasProjectAccess('SubtaskController', 'edit', $task['project_id']) && $subtask['user_id'] == $this->user->getId()): ?> + subtask->renderTimer($task, $subtask) ?> + + diff --git a/app/Template/subtask_converter/show.php b/app/Template/subtask_converter/show.php new file mode 100644 index 0000000..12e1b1b --- /dev/null +++ b/app/Template/subtask_converter/show.php @@ -0,0 +1,20 @@ + + +
+
+ +
    +
  • + text->e($subtask['title']) ?> +
  • +
+
+ + modal->confirmButtons( + 'SubtaskConverterController', + 'save', + array('task_id' => $task['id'], 'subtask_id' => $subtask['id']) + ) ?> +
diff --git a/app/Template/subtask_restriction/show.php b/app/Template/subtask_restriction/show.php new file mode 100644 index 0000000..e431b86 --- /dev/null +++ b/app/Template/subtask_restriction/show.php @@ -0,0 +1,16 @@ + +
+ form->csrf() ?> + + +

+ +

+ form->radios('status', $status_list) ?> + form->hidden('id', $subtask_inprogress) ?> + + + modal->submitButtons() ?> +
diff --git a/app/Template/swimlane/create.php b/app/Template/swimlane/create.php new file mode 100644 index 0000000..8ecfa52 --- /dev/null +++ b/app/Template/swimlane/create.php @@ -0,0 +1,17 @@ + +
+ form->csrf() ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"', 'tabindex="1"')) ?> + + form->label(t('Description'), 'description') ?> + form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> + + form->label(t('Task limit'), 'task_limit') ?> + form->number('task_limit', $values, $errors, array('tabindex' => 3, 'min="0"')) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/swimlane/edit.php b/app/Template/swimlane/edit.php new file mode 100644 index 0000000..046407a --- /dev/null +++ b/app/Template/swimlane/edit.php @@ -0,0 +1,18 @@ + + +
+ form->csrf() ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"', 'tabindex="1"')) ?> + + form->label(t('Description'), 'description') ?> + form->textEditor('description', $values, $errors, array('tabindex' => 2)) ?> + + form->label(t('Task limit'), 'task_limit') ?> + form->number('task_limit', $values, $errors, array('tabindex' => 3, 'min="0"')) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/swimlane/index.php b/app/Template/swimlane/index.php new file mode 100644 index 0000000..59133db --- /dev/null +++ b/app/Template/swimlane/index.php @@ -0,0 +1,28 @@ + + +

+ + +

+ + render('swimlane/table', array( + 'swimlanes' => $active_swimlanes, + 'project' => $project, + )) ?> + + + +

+ render('swimlane/table', array( + 'swimlanes' => $inactive_swimlanes, + 'project' => $project, + 'disable_handle' => true, + )) ?> + diff --git a/app/Template/swimlane/remove.php b/app/Template/swimlane/remove.php new file mode 100644 index 0000000..02d1e32 --- /dev/null +++ b/app/Template/swimlane/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'SwimlaneController', + 'remove', + array('project_id' => $project['id'], 'swimlane_id' => $swimlane['id']) + ) ?> +
diff --git a/app/Template/swimlane/table.php b/app/Template/swimlane/table.php new file mode 100644 index 0000000..edbf25c --- /dev/null +++ b/app/Template/swimlane/table.php @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + +
+ +   + + + + + text->e($swimlane['name']) ?> + + + app->tooltipMarkdown($swimlane['description']) ?> + + + 0 ? $swimlane['task_limit'] : '∞' ?> + + + + +
diff --git a/app/Template/tag/create.php b/app/Template/tag/create.php new file mode 100644 index 0000000..f080f8d --- /dev/null +++ b/app/Template/tag/create.php @@ -0,0 +1,15 @@ + +
+ form->csrf() ?> + form->hidden('project_id', $values) ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"')) ?> + + form->label(t('Color'), 'color_id') ?> + form->select('color_id', array('' => t('No color')) + $colors, $values, $errors, array(), 'color-picker') ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/tag/edit.php b/app/Template/tag/edit.php new file mode 100644 index 0000000..93ce0c9 --- /dev/null +++ b/app/Template/tag/edit.php @@ -0,0 +1,16 @@ + +
+ form->csrf() ?> + form->hidden('id', $values) ?> + form->hidden('project_id', $values) ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array('autofocus', 'required', 'maxlength="191"')) ?> + + form->label(t('Color'), 'color_id') ?> + form->select('color_id', array('' => t('No color')) + $colors, $values, $errors, array(), 'color-picker') ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/tag/index.php b/app/Template/tag/index.php new file mode 100644 index 0000000..2022438 --- /dev/null +++ b/app/Template/tag/index.php @@ -0,0 +1,35 @@ + + + +

+ + + + + + + + + + + + + + +
text->e($tag['name']) ?> + +
+ text->e($colors[$tag['color_id']]) ?> + +
+ modal->medium('edit', t('Edit'), 'TagController', 'edit', array('tag_id' => $tag['id'])) ?> + modal->confirm('trash-o', t('Remove'), 'TagController', 'confirm', array('tag_id' => $tag['id'])) ?> +
+ diff --git a/app/Template/tag/remove.php b/app/Template/tag/remove.php new file mode 100644 index 0000000..47ba8d3 --- /dev/null +++ b/app/Template/tag/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'TagController', + 'remove', + array('tag_id' => $tag['id']) + ) ?> +
diff --git a/app/Template/task/analytics.php b/app/Template/task/analytics.php new file mode 100644 index 0000000..ec82143 --- /dev/null +++ b/app/Template/task/analytics.php @@ -0,0 +1,45 @@ +render('task/details', array( + 'task' => $task, + 'tags' => $tags, + 'project' => $project, + 'editable' => false, +)) ?> + + + +
+
    +
  • '.$this->dt->duration($lead_time) ?>
  • +
  • '.$this->dt->duration($cycle_time) ?>
  • +
+
+ +

+ +app->component('chart-task-time-column', array( + 'metrics' => $time_spent_columns, + 'label' => t('Time spent'), +)) ?> + + + + + + + + + + + + +
text->e($column['title']) ?>dt->duration($column['time_spent']) ?>
+ +
+
    +
  • +
  • +
  • +
+
diff --git a/app/Template/task/changes.php b/app/Template/task/changes.php new file mode 100644 index 0000000..a228765 --- /dev/null +++ b/app/Template/task/changes.php @@ -0,0 +1,78 @@ + +
    + $value) { + switch ($field) { + case 'title': + echo '
  • '.t('New title: %s', $task['title']).'
  • '; + break; + case 'owner_id': + if (empty($task['owner_id'])) { + echo '
  • '.t('The task is not assigned anymore').'
  • '; + } else { + echo '
  • '.t('New assignee: %s', $task['assignee_name'] ?: $task['assignee_username']).'
  • '; + } + break; + case 'category_id': + if (empty($task['category_id'])) { + echo '
  • '.t('There is no category now').'
  • '; + } else { + echo '
  • '.t('New category: %s', $task['category_name']).'
  • '; + } + break; + case 'color_id': + echo '
  • '.t('New color: %s', $this->text->in($task['color_id'], $this->task->getColors())).'
  • '; + break; + case 'score': + echo '
  • '.t('New complexity: %d', $task['score']).'
  • '; + break; + case 'date_due': + if (empty($task['date_due'])) { + echo '
  • '.t('The due date has been removed').'
  • '; + } else { + echo '
  • '.t('New due date: ').$this->dt->datetime($task['date_due']).'
  • '; + } + break; + case 'description': + if (empty($task['description'])) { + echo '
  • '.t('There is no description anymore').'
  • '; + } + break; + case 'recurrence_status': + case 'recurrence_trigger': + case 'recurrence_factor': + case 'recurrence_timeframe': + case 'recurrence_basedate': + case 'recurrence_parent': + case 'recurrence_child': + echo '
  • '.t('Recurrence settings has been modified').'
  • '; + break; + case 'time_spent': + echo '
  • '.t('Time spent changed: %sh', $task['time_spent']).'
  • '; + break; + case 'time_estimated': + echo '
  • '.t('Time estimated changed: %sh', $task['time_estimated']).'
  • '; + break; + case 'date_started': + if ($value != 0) { + echo '
  • '.t('Start date changed: ').$this->dt->datetime($task['date_started']).'
  • '; + } + break; + default: + echo '
  • '.t('The field "%s" has been updated', $field).'
  • '; + } + } + + ?> +
+ + +

+ +
text->markdown($task['description'], true) ?>
+ +
text->markdown($task['description']) ?>
+ + + \ No newline at end of file diff --git a/app/Template/task/description.php b/app/Template/task/description.php new file mode 100644 index 0000000..241b837 --- /dev/null +++ b/app/Template/task/description.php @@ -0,0 +1,8 @@ +
> + +
+
+ text->markdown($task['description'], isset($is_public) && $is_public) ?> +
+
+
diff --git a/app/Template/task/details.php b/app/Template/task/details.php new file mode 100644 index 0000000..d659ef4 --- /dev/null +++ b/app/Template/task/details.php @@ -0,0 +1,173 @@ +
+

text->e($task['title']) ?>

+ + hook->render('template:task:details:top', array('task' => $task)) ?> + +
+
+
+
    +
  • + + + + + + + + +
  • +
  • + +
  • + +
  • + task->renderReference($task) ?> +
  • + + +
  • + text->e($task['score']) ?> +
  • + + +
  • + + url->icon('external-link', t('Public link'), 'TaskViewController', 'readonly', array('task_id' => $task['id'], 'token' => $project['token']), false, '', '', true) ?> + +
  • + + +
  • + + url->icon('th', t('Back to the board'), 'BoardViewController', 'readonly', array('token' => $project['token'])) ?> + +
  • + + + hook->render('template:task:details:first-column', array('task' => $task)) ?> +
+
+
+
    + +
  • + + text->e($task['category_name']) ?> +
  • + + +
  • + + text->e($task['swimlane_name']) ?> +
  • + +
  • + + text->e($task['column_title']) ?> +
  • +
  • + + +
  • + + hook->render('template:task:details:second-column', array('task' => $task)) ?> +
+
+
+
    +
  • + + + + text->e($task['assignee_name'] ?: $task['assignee_username']) ?> + + + + + user->getId()): ?> + - url->link(t('Assign to me'), 'TaskModificationController', 'assignToMe', ['task_id' => $task['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken()]) ?> + +
  • + +
  • + + text->e($task['creator_name'] ?: $task['creator_username']) ?> +
  • + + +
  • + + +
  • + + +
  • + + +
  • + + + hook->render('template:task:details:third-column', array('task' => $task)) ?> +
+
+
+
    + +
  • + + dt->datetime($task['date_due']) ?> +
  • + +
  • + + + dt->datetime($task['date_started']) ?> + + url->link(t('Start now'), 'TaskModificationController', 'start', ['task_id' => $task['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken()]) ?> + +
  • +
  • + + dt->datetime($task['date_creation']) ?> +
  • +
  • + + dt->datetime($task['date_modification']) ?> +
  • + +
  • + + dt->datetime($task['date_completed']) ?> +
  • + + +
  • + + dt->datetime($task['date_moved']) ?> +
  • + + + hook->render('template:task:details:fourth-column', array('task' => $task)) ?> +
+
+
+ +
+
    + +
  • ">text->e($tag['name']) ?>
  • + +
+
+ +
+ + + app->component('external-task-view', array( + 'url' => $this->url->href('ExternalTaskViewController', 'show', array('task_id' => $task['id'])), + )) ?> + + + hook->render('template:task:details:bottom', array('task' => $task)) ?> +
diff --git a/app/Template/task/dropdown.php b/app/Template/task/dropdown.php new file mode 100644 index 0000000..e73637b --- /dev/null +++ b/app/Template/task/dropdown.php @@ -0,0 +1,80 @@ + diff --git a/app/Template/task/layout.php b/app/Template/task/layout.php new file mode 100644 index 0000000..19f7791 --- /dev/null +++ b/app/Template/task/layout.php @@ -0,0 +1,17 @@ +
+ projectHeader->render($project, 'TaskListController', 'show') ?> + hook->render('template:task:layout:top', array('task' => $task)) ?> + +
diff --git a/app/Template/task/public.php b/app/Template/task/public.php new file mode 100644 index 0000000..eb3b9f1 --- /dev/null +++ b/app/Template/task/public.php @@ -0,0 +1,36 @@ +
+ render('task/details', array( + 'task' => $task, + 'tags' => $tags, + 'project' => $project, + 'editable' => false, + )) ?> + + render('task/description', array( + 'task' => $task, + 'project' => $project, + 'is_public' => true, + )) ?> + + render('subtask/show', array( + 'task' => $task, + 'subtasks' => $subtasks, + 'editable' => false + )) ?> + + render('task_internal_link/show', array( + 'task' => $task, + 'links' => $links, + 'project' => $project, + 'editable' => false, + 'is_public' => true, + )) ?> + + render('task_comments/show', array( + 'task' => $task, + 'comments' => $comments, + 'project' => $project, + 'editable' => false, + 'is_public' => true, + )) ?> +
diff --git a/app/Template/task/show.php b/app/Template/task/show.php new file mode 100644 index 0000000..659d4ca --- /dev/null +++ b/app/Template/task/show.php @@ -0,0 +1,53 @@ +hook->render('template:task:show:top', array('task' => $task, 'project' => $project)) ?> + +render('task/details', array( + 'task' => $task, + 'tags' => $tags, + 'project' => $project, + 'editable' => $this->user->hasProjectAccess('TaskModificationController', 'edit', $project['id']), +)) ?> + +hook->render('template:task:show:before-description', array('task' => $task, 'project' => $project)) ?> +render('task/description', array('task' => $task)) ?> + +hook->render('template:task:show:before-subtasks', array('task' => $task, 'project' => $project)) ?> +render('subtask/show', array( + 'task' => $task, + 'subtasks' => $subtasks, + 'project' => $project, + 'editable' => $this->user->hasProjectAccess('SubtaskController', 'edit', $project['id']), +)) ?> + +hook->render('template:task:show:before-internal-links', array('task' => $task, 'project' => $project)) ?> +render('task_internal_link/show', array( + 'task' => $task, + 'links' => $internal_links, + 'project' => $project, + 'link_label_list' => $link_label_list, + 'editable' => $this->user->hasProjectAccess('TaskInternalLinkController', 'edit', $project['id']), + 'is_public' => false, +)) ?> + +hook->render('template:task:show:before-external-links', array('task' => $task, 'project' => $project)) ?> +render('task_external_link/show', array( + 'task' => $task, + 'links' => $external_links, + 'project' => $project, +)) ?> + +hook->render('template:task:show:before-attachments', array('task' => $task, 'project' => $project)) ?> +render('task_file/show', array( + 'task' => $task, + 'files' => $files, + 'images' => $images +)) ?> + +hook->render('template:task:show:before-comments', array('task' => $task, 'project' => $project)) ?> +render('task_comments/show', array( + 'task' => $task, + 'comments' => $comments, + 'project' => $project, + 'editable' => $this->user->hasProjectAccess('CommentController', 'edit', $project['id']), +)) ?> + +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 new file mode 100644 index 0000000..6bf1db2 --- /dev/null +++ b/app/Template/task/sidebar.php @@ -0,0 +1,109 @@ + diff --git a/app/Template/task/time_tracking_details.php b/app/Template/task/time_tracking_details.php new file mode 100644 index 0000000..7cb419e --- /dev/null +++ b/app/Template/task/time_tracking_details.php @@ -0,0 +1,34 @@ +render('task/details', array( + 'task' => $task, + 'tags' => $tags, + 'project' => $project, + 'editable' => false, +)) ?> + +render('task/time_tracking_summary', array('task' => $task)) ?> + +

+isEmpty()): ?> +

+ + + + + + + + + + getCollection() as $record): ?> + + + + + + + + +
order(t('User'), 'username') ?>order(t('Subtask'), 'subtask_title') ?>order(t('Start'), 'start') ?>order(t('End'), 'end') ?>order(t('Time spent'), \Kanboard\Model\SubtaskTimeTrackingModel::TABLE.'.time_spent') ?>
url->link($this->text->e($record['user_fullname'] ?: $record['username']), 'UserViewController', 'show', array('user_id' => $record['user_id'])) ?>dt->datetime($record['start']) ?>dt->datetime($record['end']) ?>
+ + + diff --git a/app/Template/task/time_tracking_summary.php b/app/Template/task/time_tracking_summary.php new file mode 100644 index 0000000..63de733 --- /dev/null +++ b/app/Template/task/time_tracking_summary.php @@ -0,0 +1,13 @@ + 0 || $task['time_spent'] > 0): ?> + + +
+
    +
  • text->e($task['time_estimated']) ?>
  • +
  • text->e($task['time_spent']) ?>
  • +
  • text->e($task['time_estimated'] - $task['time_spent']) ?>
  • +
+
+ \ No newline at end of file diff --git a/app/Template/task/transitions.php b/app/Template/task/transitions.php new file mode 100644 index 0000000..4a9f22c --- /dev/null +++ b/app/Template/task/transitions.php @@ -0,0 +1,33 @@ +render('task/details', array( + 'task' => $task, + 'tags' => $tags, + 'project' => $project, + 'editable' => false, +)) ?> + + + + +

+ + + + + + + + + + + + + + + + + + +
dt->datetime($transition['date']) ?>text->e($transition['src_column']) ?>text->e($transition['dst_column']) ?>url->link($this->text->e($transition['name'] ?: $transition['username']), 'UserViewController', 'show', array('user_id' => $transition['user_id'])) ?>dt->duration($transition['time_spent']) ?>
+ diff --git a/app/Template/task_bulk/show.php b/app/Template/task_bulk/show.php new file mode 100644 index 0000000..a52208f --- /dev/null +++ b/app/Template/task_bulk/show.php @@ -0,0 +1,30 @@ + + +
+ form->csrf() ?> + form->hidden('column_id', $values) ?> + form->hidden('swimlane_id', $values) ?> + + + form->label(t('Template for the task description'), 'task_description_template_id') ?> + form->select('task_description_template_id', $task_description_templates, $values, $errors) ?> + + + form->label(t('Tasks'), 'tasks') ?> + form->textarea('tasks', $values, $errors, array('placeholder="'.t('My task title').'"')) ?> +

+ + task->renderColorField($values) ?> + task->renderAssigneeField($users_list, $values, $errors) ?> + task->renderPriorityField($project, $values) ?> + task->renderDueDateField($values, $errors) ?> + task->renderTimeEstimatedField($values, $errors) ?> + task->renderScoreField($values, $errors) ?> + task->renderCategoryField($categories_list, $values, $errors) ?> + task->renderTagField($project) ?> + + modal->submitButtons() ?> +
+ diff --git a/app/Template/task_bulk_change_property/show.php b/app/Template/task_bulk_change_property/show.php new file mode 100644 index 0000000..f8c0a8f --- /dev/null +++ b/app/Template/task_bulk_change_property/show.php @@ -0,0 +1,126 @@ + + +
+ form->csrf() ?> + form->hidden('task_ids', $values) ?> + +

+ +
+
+ +
+
+ task->renderColorField($values) ?> +
+
+ +
+
+ +
+
+ task->renderAssigneeField($users_list, $values, $errors) ?> +
+
+ +
+
+ +
+
+ task->renderPriorityField($project, $values) ?> +
+
+ +
+
+ +
+
+ task->renderCategoryField($categories_list, $values, $errors, [], true) ?> +
+
+ +
+
+ +
+
+ task->renderTagField($project) ?> + + + + +
+
+ +
+
+ +
+
+ task->renderDueDateField($values, $errors) ?> +
+
+ +
+
+ +
+
+ task->renderStartDateField($values, $errors) ?> +
+
+ +
+
+ +
+
+ task->renderTimeEstimatedField($values, $errors) ?> +
+
+ +
+
+ +
+
+ task->renderTimeSpentField($values, $errors) ?> +
+
+ +
+
+ +
+
+ task->renderScoreField($values, $errors) ?> +
+
+ +
+
+ +
+
+ task->renderInternalLinkField($internallink_list, $values, $errors) ?> +
+
+ +
+
+ +
+
+ + + +
+
+ + modal->submitButtons() ?> +
diff --git a/app/Template/task_bulk_move_column/show.php b/app/Template/task_bulk_move_column/show.php new file mode 100644 index 0000000..beba207 --- /dev/null +++ b/app/Template/task_bulk_move_column/show.php @@ -0,0 +1,16 @@ + + +
+ form->csrf() ?> + form->hidden('task_ids', $values) ?> + + form->label(t('Swimlane'), 'swimlane_id') ?> + form->select('swimlane_id', $swimlanes, $values) ?> + + form->label(t('Column'), 'column_id') ?> + form->select('column_id', $columns, $values) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/task_comments/create.php b/app/Template/task_comments/create.php new file mode 100644 index 0000000..ebf6075 --- /dev/null +++ b/app/Template/task_comments/create.php @@ -0,0 +1,30 @@ + +
+ form->csrf() ?> + form->hidden('task_id', $values) ?> + form->hidden('user_id', $values) ?> + + form->textEditor('comment', $values, $errors, array('required' => true, 'aria-label' => t('New comment'))) ?> + + + + user->getRole() !== Role::APP_USER) { + echo $this->form->label(t('Visibility:'), $formName); + $attribute = []; + $visibilityOptions['app-user'] = t('Standard users'); + $visibilityOptions['app-manager'] = t('Application managers or more'); + } + ?> + + user->getRole() === Role::APP_ADMIN) { + $visibilityOptions['app-admin'] = t('Administrators'); + } + ?> + + form->select($formName, $visibilityOptions, array(), array(), $attribute) ?> + modal->submitButtons() ?> +
diff --git a/app/Template/task_comments/show.php b/app/Template/task_comments/show.php new file mode 100644 index 0000000..d3704f9 --- /dev/null +++ b/app/Template/task_comments/show.php @@ -0,0 +1,39 @@ +
> + +
+ +
+ + + modal->medium('comment', t('Add a comment'), 'CommentController', 'create', array('task_id' => $task['id'])) ?> + + url->icon('sort', t('Change sorting'), 'CommentController', 'toggleSorting', array('task_id' => $task['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?> + + modal->medium('paper-plane', t('Send by email'), 'CommentMailController', 'create', array('task_id' => $task['id'])) ?> + + +
+ + + render('comment/show', array( + 'comment' => $comment, + 'task' => $task, + 'project' => $project, + 'editable' => $editable, + 'is_public' => isset($is_public) && $is_public, + )) ?> + + + + render('task_comments/create', array( + '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/task_creation/duplicate_projects.php b/app/Template/task_creation/duplicate_projects.php new file mode 100644 index 0000000..3c9c279 --- /dev/null +++ b/app/Template/task_creation/duplicate_projects.php @@ -0,0 +1,25 @@ + + + +

+
+ url->link(t('cancel'), 'BoardViewController', 'show', array('project_id' => $task['project_id']), false, 'js-modal-close btn') ?> +
+ +
+ form->csrf() ?> + form->hidden('task_id', $values) ?> + + form->select( + 'project_ids[]', + $projects_list, + $values, + array(), + array('multiple', 'aria-label="'.t('Duplicate to multiple projects').'"') + ) ?> + + modal->submitButtons() ?> +
+ diff --git a/app/Template/task_creation/show.php b/app/Template/task_creation/show.php new file mode 100644 index 0000000..f9d3f2f --- /dev/null +++ b/app/Template/task_creation/show.php @@ -0,0 +1,132 @@ + + +
+ form->csrf() ?> + +
+
+ task->renderTitleField($values, $errors) ?> + task->renderDescriptionField($values, $errors) ?> + task->renderDescriptionTemplateDropdown($project['id']) ?> + task->renderTagField($project) ?> + + hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?> +
+ +
+ task->renderColorField($values) ?> + task->renderAssigneeField($users_list, $values, $errors) ?> + task->renderCategoryField($categories_list, $values, $errors) ?> + task->renderSwimlaneField($swimlanes_list, $values, $errors) ?> + task->renderColumnField($columns_list, $values, $errors) ?> + task->renderPriorityField($project, $values) ?> + + hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?> + + task->renderDueDateField($values, $errors) ?> + task->renderStartDateField($values, $errors) ?> + task->renderTimeEstimatedField($values, $errors) ?> + task->renderTimeSpentField($values, $errors) ?> + task->renderScoreField($values, $errors) ?> + task->renderReferenceField($values, $errors) ?> + + hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?> +
+ +
+ +
+ +
+ task->renderFileUpload($screenshot, $files) ?> +
+
+ + hook->render('template:task:form:bottom-before-buttons', array('values' => $values, 'errors' => $errors)) ?> + + + form->checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1, '', array("tabindex" => "16")) ?> + form->checkbox('duplicate_multiple_projects', t('Duplicate to multiple projects'), 1, false, '', array("tabindex" => "17")) ?> + + + modal->submitButtons() ?> +
+
+
diff --git a/app/Template/task_duplication/copy.php b/app/Template/task_duplication/copy.php new file mode 100644 index 0000000..03f46ea --- /dev/null +++ b/app/Template/task_duplication/copy.php @@ -0,0 +1,46 @@ + + + +

+
+ url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id']), false, 'js-modal-close btn') ?> +
+ + +
+ form->csrf() ?> + form->hidden('id', $values) ?> + + form->label(t('Project'), 'project_id') ?> + 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'], 'dst_project_id' => 'PROJECT_ID')), + ) + )) ?> + + form->label(t('Swimlane'), 'swimlane_id') ?> + form->select('swimlane_id', $swimlanes_list, $values) ?> +

+ + form->label(t('Column'), 'column_id') ?> + form->select('column_id', $columns_list, $values) ?> +

+ + form->label(t('Category'), 'category_id') ?> + form->select('category_id', $categories_list, $values) ?> +

+ + form->label(t('Assignee'), 'owner_id') ?> + form->select('owner_id', $users_list, $values) ?> +

+ + modal->submitButtons() ?> +
+ diff --git a/app/Template/task_duplication/duplicate.php b/app/Template/task_duplication/duplicate.php new file mode 100644 index 0000000..8df6b93 --- /dev/null +++ b/app/Template/task_duplication/duplicate.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'TaskDuplicationController', + 'duplicate', + array('task_id' => $task['id'], 'confirmation' => 'yes') + ) ?> +
diff --git a/app/Template/task_duplication/move.php b/app/Template/task_duplication/move.php new file mode 100644 index 0000000..a1a2eb6 --- /dev/null +++ b/app/Template/task_duplication/move.php @@ -0,0 +1,48 @@ + + + +

+
+ url->link(t('cancel'), 'TaskViewController', 'show', array('task_id' => $task['id']), false, 'js-modal-close btn') ?> +
+ + +
+ + form->csrf() ?> + form->hidden('id', $values) ?> + + form->label(t('Project'), 'project_id') ?> + 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'], 'dst_project_id' => 'PROJECT_ID')), + ) + )) ?> + + form->label(t('Swimlane'), 'swimlane_id') ?> + form->select('swimlane_id', $swimlanes_list, $values) ?> +

+ + form->label(t('Column'), 'column_id') ?> + form->select('column_id', $columns_list, $values) ?> +

+ + form->label(t('Category'), 'category_id') ?> + form->select('category_id', $categories_list, $values) ?> +

+ + form->label(t('Assignee'), 'owner_id') ?> + form->select('owner_id', $users_list, $values) ?> +

+ + modal->submitButtons() ?> +
+ + diff --git a/app/Template/task_external_link/create.php b/app/Template/task_external_link/create.php new file mode 100644 index 0000000..2ceb1aa --- /dev/null +++ b/app/Template/task_external_link/create.php @@ -0,0 +1,8 @@ + + +
+ render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?> + modal->submitButtons() ?> +
diff --git a/app/Template/task_external_link/edit.php b/app/Template/task_external_link/edit.php new file mode 100644 index 0000000..dd6f8f4 --- /dev/null +++ b/app/Template/task_external_link/edit.php @@ -0,0 +1,8 @@ + + +
+ render('task_external_link/form', array('task' => $task, 'dependencies' => $dependencies, 'values' => $values, 'errors' => $errors)) ?> + modal->submitButtons() ?> +
diff --git a/app/Template/task_external_link/find.php b/app/Template/task_external_link/find.php new file mode 100644 index 0000000..393b97d --- /dev/null +++ b/app/Template/task_external_link/find.php @@ -0,0 +1,23 @@ + + +
+ form->csrf() ?> + + form->label(t('External link'), 'text') ?> + form->text( + 'text', + $values, + $errors, + array( + 'required', + 'autofocus', + 'placeholder="'.t('Copy and paste your link here...').'"', + )) ?> + + form->label(t('Link type'), 'type') ?> + form->select('type', $types, $values) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/task_external_link/form.php b/app/Template/task_external_link/form.php new file mode 100644 index 0000000..4ad2b2e --- /dev/null +++ b/app/Template/task_external_link/form.php @@ -0,0 +1,11 @@ +form->csrf() ?> +form->hidden('link_type', $values) ?> + +form->label(t('URL'), 'url') ?> +form->text('url', $values, $errors, array('required')) ?> + +form->label(t('Title'), 'title') ?> +form->text('title', $values, $errors, array('required')) ?> + +form->label(t('Dependency'), 'dependency') ?> +form->select('dependency', $dependencies, $values, $errors) ?> diff --git a/app/Template/task_external_link/remove.php b/app/Template/task_external_link/remove.php new file mode 100644 index 0000000..c3eead6 --- /dev/null +++ b/app/Template/task_external_link/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'TaskExternalLinkController', + 'remove', + array('link_id' => $link['id'], 'task_id' => $task['id']) + ) ?> +
diff --git a/app/Template/task_external_link/show.php b/app/Template/task_external_link/show.php new file mode 100644 index 0000000..1bb8997 --- /dev/null +++ b/app/Template/task_external_link/show.php @@ -0,0 +1,10 @@ +
> + +
+ render('task_external_link/table', array( + 'links' => $links, + 'task' => $task, + 'project' => $project, + )) ?> +
+
diff --git a/app/Template/task_external_link/table.php b/app/Template/task_external_link/table.php new file mode 100644 index 0000000..816f533 --- /dev/null +++ b/app/Template/task_external_link/table.php @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + +
+ user->hasProjectAccess('TaskExternalLinkController', 'edit', $task['project_id'])): ?> + + + text->e($link['type']) ?> + + text->e($link['title']) ?> (text->e($link['url']) ?>) + + text->e($link['dependency_label']) ?> + + text->e($link['creator_name'] ?: $link['creator_username']) ?> + + dt->date($link['date_creation']) ?> +
+ diff --git a/app/Template/task_file/create.php b/app/Template/task_file/create.php new file mode 100644 index 0000000..22ee1b0 --- /dev/null +++ b/app/Template/task_file/create.php @@ -0,0 +1,21 @@ + + +app->component('file-upload', array( + 'csrf' => $this->app->getToken()->getReusableCSRFToken(), + 'maxSize' => $max_size, + 'url' => $this->url->to('TaskFileController', 'save', array('task_id' => $task['id'])), + 'labelDropzone' => t('Drag and drop your files here'), + 'labelOr' => t('or'), + 'labelChooseFiles' => t('choose files'), + 'labelOversize' => $max_size > 0 ? t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)) : null, + 'labelSuccess' => t('All files have been uploaded successfully.'), + 'labelCloseSuccess' => t('Close this window'), + 'labelUploadError' => t('Unable to upload this file.'), +)) ?> + +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 new file mode 100644 index 0000000..7cdef8a --- /dev/null +++ b/app/Template/task_file/files.php @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + +
+ + + + text->bytes($file['size']) ?> + + text->e($file['user_name'] ?: $file['username']) ?> + + dt->date($file['date']) ?> +
+ diff --git a/app/Template/task_file/images.php b/app/Template/task_file/images.php new file mode 100644 index 0000000..ae1bcab --- /dev/null +++ b/app/Template/task_file/images.php @@ -0,0 +1,50 @@ + +
+ +
+ app->component('image-slideshow', array( + 'images' => $images, + 'image' => $file, + 'regex_file_id' => 'FILE_ID', + 'regex_etag' => 'ETAG', + 'url' => array( + 'image' => $this->url->to('FileViewerController', 'image', array('file_id' => 'FILE_ID', 'task_id' => $task['id'], 'etag' => 'ETAG')), + 'thumbnail' => $this->url->to('FileViewerController', 'thumbnail', array('file_id' => 'FILE_ID', 'task_id' => $task['id'], 'etag' => 'ETAG')), + 'download' => $this->url->to('FileViewerController', 'download', array('file_id' => 'FILE_ID', 'task_id' => $task['id'], 'etag' => 'ETAG')), + ) + )) ?> + +
+
+ +
+
+ hook->render('template:task-file:images:before-thumbnail-description', array('task' => $task, 'file' => $file)) ?> + app->tooltipMarkdown(t('Uploaded: %s', $this->dt->datetime($file['date']))."\n\n".t('Size: %s', $this->text->bytes($file['size']))) ?> + + + + dt->datetime($file['date'])) ?> + +
+
+
+ +
+ diff --git a/app/Template/task_file/remove.php b/app/Template/task_file/remove.php new file mode 100644 index 0000000..351a09e --- /dev/null +++ b/app/Template/task_file/remove.php @@ -0,0 +1,15 @@ + + +
+

+ text->e($file['name'])) ?> +

+ + modal->confirmButtons( + 'TaskFileController', + 'remove', + array('task_id' => $task['id'], 'file_id' => $file['id']) + ) ?> +
diff --git a/app/Template/task_file/screenshot.php b/app/Template/task_file/screenshot.php new file mode 100644 index 0000000..4abe8bf --- /dev/null +++ b/app/Template/task_file/screenshot.php @@ -0,0 +1,15 @@ + + +
+

+
+ +
+ form->csrf() ?> + app->component('screenshot') ?> + modal->submitButtons() ?> +
+ +

diff --git a/app/Template/task_file/show.php b/app/Template/task_file/show.php new file mode 100644 index 0000000..e9723f0 --- /dev/null +++ b/app/Template/task_file/show.php @@ -0,0 +1,7 @@ +
> + +
+ render('task_file/images', array('task' => $task, 'images' => $images)) ?> + render('task_file/files', array('task' => $task, 'files' => $files)) ?> +
+
diff --git a/app/Template/task_import/show.php b/app/Template/task_import/show.php new file mode 100644 index 0000000..c407938 --- /dev/null +++ b/app/Template/task_import/show.php @@ -0,0 +1,37 @@ + +
+ form->csrf() ?> + + form->label(t('Delimiter'), 'delimiter') ?> + form->select('delimiter', $delimiters, $values) ?> + + form->label(t('Enclosure'), 'enclosure') ?> + form->select('enclosure', $enclosures, $values) ?> + + form->label(t('CSV File'), 'file') ?> + form->file('file', $errors) ?> + + 0): ?> +

text->bytes($max_size) : $max_size ?>

+ + + modal->submitButtons(array('submitLabel' => t('Import'))) ?> +
+ +
+

+
    +
  • +
  • +
  • +
  • +
  • +
  • +
  • +
+

+ url->icon('download', t('Download CSV template'), 'TaskImportController', 'template', array('project_id' => $project['id'])) ?> +

+
diff --git a/app/Template/task_internal_link/create.php b/app/Template/task_internal_link/create.php new file mode 100644 index 0000000..1f0e4c3 --- /dev/null +++ b/app/Template/task_internal_link/create.php @@ -0,0 +1,30 @@ + + +
+ + form->csrf() ?> + form->hidden('opposite_task_id', $values) ?> + + form->label(t('Label'), 'link_id') ?> + form->select('link_id', $labels, $values, $errors) ?> + + form->label(t('Task'), 'title') ?> + form->text( + 'title', + $values, + $errors, + array( + 'required', + 'placeholder="'.t('Start to type task title...').'"', + 'title="'.t('Start to type task title...').'"', + 'data-dst-field="opposite_task_id"', + 'data-search-url="'.$this->url->href('TaskAjaxController', 'autocomplete', array('exclude_task_id' => $task['id'])).'"', + ), + 'autocomplete') ?> + + form->checkbox('another_tasklink', t('Create another link'), 1, isset($values['another_tasklink']) && $values['another_tasklink'] == 1) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/task_internal_link/edit.php b/app/Template/task_internal_link/edit.php new file mode 100644 index 0000000..81b0191 --- /dev/null +++ b/app/Template/task_internal_link/edit.php @@ -0,0 +1,28 @@ + + +
+ form->csrf() ?> + + form->hidden('opposite_task_id', $values) ?> + + form->label(t('Label'), 'link_id') ?> + form->select('link_id', $labels, $values, $errors) ?> + + form->label(t('Task'), 'title') ?> + form->text( + 'title', + $values, + $errors, + array( + 'required', + 'placeholder="'.t('Start to type task title...').'"', + 'title="'.t('Start to type task title...').'"', + 'data-dst-field="opposite_task_id"', + 'data-search-url="'.$this->url->href('TaskAjaxController', 'autocomplete', array('exclude_task_id' => $task['id'])).'"', + ), + 'autocomplete') ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/task_internal_link/remove.php b/app/Template/task_internal_link/remove.php new file mode 100644 index 0000000..d331679 --- /dev/null +++ b/app/Template/task_internal_link/remove.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'TaskInternalLinkController', + 'remove', + array('link_id' => $link['id'], 'task_id' => $task['id']) + ) ?> +
diff --git a/app/Template/task_internal_link/show.php b/app/Template/task_internal_link/show.php new file mode 100644 index 0000000..e5c0389 --- /dev/null +++ b/app/Template/task_internal_link/show.php @@ -0,0 +1,12 @@ +
> + +
+ render('task_internal_link/table', array( + 'links' => $links, + 'task' => $task, + 'project' => $project, + 'editable' => $editable, + 'is_public' => $is_public, + )) ?> +
+
diff --git a/app/Template/task_internal_link/table.php b/app/Template/task_internal_link/table.php new file mode 100644 index 0000000..7c24cd8 --- /dev/null +++ b/app/Template/task_internal_link/table.php @@ -0,0 +1,80 @@ + + + $grouped_links): ?> + + + + + + + + + + + + + + + + + + + diff --git a/app/Template/task_list/header.php b/app/Template/task_list/header.php new file mode 100644 index 0000000..2e86a2d --- /dev/null +++ b/app/Template/task_list/header.php @@ -0,0 +1,41 @@ +
+
+ getTotal() > 1): ?> + getTotal()) ?> + + getTotal()) ?> + +
+ + user->hasProjectAccess('TaskModificationController', 'save', $project['id'])): ?> + +
+ -  + +
+ + +
+ + user->hasSubtaskListActivated()): ?> + url->icon('tasks', t('Hide subtasks'), 'TaskListController', 'show', array('project_id' => $project['id'], 'hide_subtasks' => 1, 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?> + + url->icon('tasks', t('Show subtasks'), 'TaskListController', 'show', array('project_id' => $project['id'], 'show_subtasks' => 1, 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?> + + + + render('task_list/sort_menu', array('paginator' => $paginator)) ?> +
+
\ No newline at end of file diff --git a/app/Template/task_list/listing.php b/app/Template/task_list/listing.php new file mode 100644 index 0000000..595a190 --- /dev/null +++ b/app/Template/task_list/listing.php @@ -0,0 +1,41 @@ +projectHeader->render($project, 'TaskListController', 'show') ?> + +isEmpty()): ?> +

+isEmpty()): ?> +
+ render('task_list/header', array( + 'paginator' => $paginator, + 'project' => $project, + 'show_items_selection' => true, + )) ?> + + getCollection() as $task): ?> +
+ render('task_list/task_title', array( + 'task' => $task, + 'show_items_selection' => true, + 'redirect' => 'list', + )) ?> + + render('task_list/task_details', array( + 'task' => $task, + )) ?> + + render('task_list/task_avatars', array( + 'task' => $task, + )) ?> + + render('task_list/task_icons', array( + 'task' => $task, + )) ?> + + render('task_list/task_subtasks', array( + 'task' => $task, + )) ?> +
+ +
+ + + diff --git a/app/Template/task_list/sort_menu.php b/app/Template/task_list/sort_menu.php new file mode 100644 index 0000000..e0322d7 --- /dev/null +++ b/app/Template/task_list/sort_menu.php @@ -0,0 +1,41 @@ + diff --git a/app/Template/task_list/task_avatars.php b/app/Template/task_list/task_avatars.php new file mode 100644 index 0000000..d82460f --- /dev/null +++ b/app/Template/task_list/task_avatars.php @@ -0,0 +1,20 @@ + +
+ user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + class="task-board-change-assignee" + data-url="url->href('TaskModificationController', 'edit', array('task_id' => $task['id'])) ?>"> + + class="task-board-assignee"> + + avatar->small( + $task['owner_id'], + $task['assignee_username'], + $task['assignee_name'], + $task['assignee_email'], + $task['assignee_avatar_path'], + 'avatar-inline' + ) ?>text->e($task['assignee_name'] ?: $task['assignee_username']) ?> + +
+ diff --git a/app/Template/task_list/task_details.php b/app/Template/task_list/task_details.php new file mode 100644 index 0000000..ad2eb23 --- /dev/null +++ b/app/Template/task_list/task_details.php @@ -0,0 +1,32 @@ +
+ text->e($task['project_name']) ?> > + text->e($task['swimlane_name']) ?> > + text->e($task['column_name']) ?> + + + "> + user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + url->link( + $this->text->e($task['category_name']), + 'TaskModificationController', + 'edit', + array('task_id' => $task['id']), + false, + 'js-modal-medium' . (! empty($task['category_description']) ? ' tooltip' : ''), + t('Change category') + ) ?> + + app->tooltipMarkdown($task['category_description']) ?> + + + text->e($task['category_name']) ?> + + + + + + "> + text->e($tag['name']) ?> + + +
diff --git a/app/Template/task_list/task_icons.php b/app/Template/task_list/task_icons.php new file mode 100644 index 0000000..21b90df --- /dev/null +++ b/app/Template/task_list/task_icons.php @@ -0,0 +1,103 @@ +
+ + + task->renderReference($task) ?> + + + + + + + + + + + + text->e($task['score']) ?> + + + + + + text->e($task['time_spent']) ?>/text->e($task['time_estimated']) ?>h + + + + + + + dt->date($task['date_started']) ?> + + + + + + + dt->datetime($task['date_due']) ?> + + + + + app->tooltipLink('', $this->url->href('BoardTooltipController', 'recurrence', array('task_id' => $task['id']))) ?> + + + + app->tooltipLink('', $this->url->href('BoardTooltipController', 'recurrence', array('task_id' => $task['id']))) ?> + + + + app->tooltipLink(''.$task['nb_links'], $this->url->href('BoardTooltipController', 'tasklinks', array('task_id' => $task['id']))) ?> + + + + app->tooltipLink(''.$task['nb_external_links'], $this->url->href('BoardTooltipController', 'externallinks', array('task_id' => $task['id']))) ?> + + + + 0 ? round($nb_completed / $nb_subtasks * 100, 0) : 0; ?> + app->tooltipLink(''.$percentage.'% ('.$nb_completed.'/'.$nb_subtasks.')', $this->url->href('BoardTooltipController', 'subtasks', array('task_id' => $task['id']))) ?> + + + + app->tooltipLink(''.$task['nb_files'], $this->url->href('BoardTooltipController', 'attachments', array('task_id' => $task['id']))) ?> + + + 0): ?> + user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + modal->medium( + 'comments-o', + $task['nb_comments'], + 'CommentListController', + 'show', + array('task_id' => $task['id']), + $task['nb_comments'] == 1 ? t('%d comment', $task['nb_comments']) : t('%d comments', $task['nb_comments']) + ) ?> + + +   + + + + + app->tooltipLink('', $this->url->href('BoardTooltipController', 'description', array('task_id' => $task['id']))) ?> + + + ( ) + + +
+ dt->age($task['date_creation']) ?> + dt->age($task['date_moved']) ?> +
+ + + + + task->renderPriority($task['priority']) ?> +
diff --git a/app/Template/task_list/task_subtasks.php b/app/Template/task_list/task_subtasks.php new file mode 100644 index 0000000..9110b17 --- /dev/null +++ b/app/Template/task_list/task_subtasks.php @@ -0,0 +1,22 @@ + +
+ +
+ + subtask->renderToggleStatus($task, $subtask, 'rows', isset($user_id) ? $user_id : 0) ?> + + + + text->e($subtask['name'] ?: $subtask['username']) ?> + + + + render('subtask/timer', array( + 'task' => $task, + 'subtask' => $subtask, + )) ?> + +
+ +
+ diff --git a/app/Template/task_list/task_title.php b/app/Template/task_list/task_title.php new file mode 100644 index 0000000..43fdb06 --- /dev/null +++ b/app/Template/task_list/task_title.php @@ -0,0 +1,14 @@ +
+ user->hasProjectAccess('TaskModificationController', 'edit', $task['project_id'])): ?> + + + + render('task/dropdown', array('task' => $task, 'redirect' => isset($redirect) ? $redirect : '')) ?> + + + + + + url->link($this->text->e($task['title']), 'TaskViewController', 'show', array('task_id' => $task['id'])) ?> + +
diff --git a/app/Template/task_mail/create.php b/app/Template/task_mail/create.php new file mode 100644 index 0000000..813cfde --- /dev/null +++ b/app/Template/task_mail/create.php @@ -0,0 +1,47 @@ + +
+ form->csrf() ?> + + form->label(t('Email'), 'emails') ?> + form->text('emails', $values, $errors, array('autofocus', 'required', 'tabindex="1"')) ?> + + + + + + form->label(t('Subject'), 'subject') ?> + form->text('subject', $values, $errors, array('required', 'tabindex="2"')) ?> + + + + + + modal->submitButtons(array( + 'submitLabel' => t('Send by email'), + 'tabindex' => 3, + )) ?> +
diff --git a/app/Template/task_mail/email.php b/app/Template/task_mail/email.php new file mode 100644 index 0000000..ee85b1d --- /dev/null +++ b/app/Template/task_mail/email.php @@ -0,0 +1,46 @@ +

text->e($task['title']) ?> (#)

+ +
    +
  • + +
  • +
  • + dt->datetime($task['date_creation']) ?> +
  • + +
  • + dt->datetime($task['date_due']) ?> +
  • + + +
  • + +
  • + +
  • + + + + + + + +
  • +
  • + + text->e($task['column_title']) ?> +
  • +
  • text->e($task['position']) ?>
  • + +
  • + text->e($task['category_name']) ?> +
  • + +
+ + +

+ text->markdown($task['description'], true) ?> + + +render('notification/footer', array('task' => $task)) ?> \ No newline at end of file diff --git a/app/Template/task_modification/show.php b/app/Template/task_modification/show.php new file mode 100644 index 0000000..1b6ffaa --- /dev/null +++ b/app/Template/task_modification/show.php @@ -0,0 +1,44 @@ + +
+ form->csrf() ?> + +
+
+ task->renderTitleField($values, $errors) ?> + task->renderDescriptionField($values, $errors) ?> + task->renderDescriptionTemplateDropdown($project['id']) ?> + task->renderTagField($project, $tags) ?> + + hook->render('template:task:form:first-column', array('values' => $values, 'errors' => $errors)) ?> +
+ +
+ task->renderColorField($values) ?> + task->renderAssigneeField($users_list, $values, $errors) ?> + task->renderCategoryField($categories_list, $values, $errors) ?> + task->renderPriorityField($project, $values) ?> + + hook->render('template:task:form:second-column', array('values' => $values, 'errors' => $errors)) ?> +
+ +
+ task->renderDueDateField($values, $errors) ?> + task->renderStartDateField($values, $errors) ?> + task->renderTimeEstimatedField($values, $errors) ?> + task->renderTimeSpentField($values, $errors) ?> + task->renderScoreField($values, $errors) ?> + task->renderReferenceField($values, $errors) ?> + + hook->render('template:task:form:third-column', array('values' => $values, 'errors' => $errors)) ?> +
+ +
+ + hook->render('template:task:form:bottom-before-buttons', array('values' => $values, 'errors' => $errors)) ?> + + modal->submitButtons() ?> +
+
+
diff --git a/app/Template/task_move_position/show.php b/app/Template/task_move_position/show.php new file mode 100644 index 0000000..35ff293 --- /dev/null +++ b/app/Template/task_move_position/show.php @@ -0,0 +1,20 @@ + + +
+ +app->component('task-move-position', array( + 'saveUrl' => $this->url->href('TaskMovePositionController', 'save', array('task_id' => $task['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())), + 'board' => $board, + 'task' => $task, + 'swimlaneLabel' => t('Swimlane'), + 'columnLabel' => t('Column'), + 'positionLabel' => t('Position'), + 'beforeLabel' => t('Insert before this task'), + 'afterLabel' => t('Insert after this task'), +)) ?> + +modal->submitButtons() ?> + +
diff --git a/app/Template/task_recurrence/edit.php b/app/Template/task_recurrence/edit.php new file mode 100644 index 0000000..0b1bf26 --- /dev/null +++ b/app/Template/task_recurrence/edit.php @@ -0,0 +1,43 @@ + + + +
+ render('task_recurrence/info', array( + 'task' => $task, + 'recurrence_trigger_list' => $recurrence_trigger_list, + 'recurrence_timeframe_list' => $recurrence_timeframe_list, + 'recurrence_basedate_list' => $recurrence_basedate_list, + )) ?> +
+ + + + +
+ + form->csrf() ?> + + form->hidden('id', $values) ?> + form->hidden('project_id', $values) ?> + + form->label(t('Generate recurrent task'), 'recurrence_status') ?> + form->select('recurrence_status', $recurrence_status_list, $values, $errors) ?> + + form->label(t('Trigger to generate recurrent task'), 'recurrence_trigger') ?> + form->select('recurrence_trigger', $recurrence_trigger_list, $values, $errors) ?> + + form->label(t('Factor to calculate new due date'), 'recurrence_factor') ?> + form->number('recurrence_factor', $values, $errors) ?> + + form->label(t('Timeframe to calculate new due date'), 'recurrence_timeframe') ?> + form->select('recurrence_timeframe', $recurrence_timeframe_list, $values, $errors) ?> + + form->label(t('Base date to calculate new due date'), 'recurrence_basedate') ?> + form->select('recurrence_basedate', $recurrence_basedate_list, $values, $errors) ?> + + modal->submitButtons() ?> +
+ + diff --git a/app/Template/task_recurrence/info.php b/app/Template/task_recurrence/info.php new file mode 100644 index 0000000..7c06e3f --- /dev/null +++ b/app/Template/task_recurrence/info.php @@ -0,0 +1,39 @@ +
+
    + +
  • + +
  • +
      +
    • + text->e($recurrence_trigger_list[$task['recurrence_trigger']]) ?> +
    • +
    • + text->e($task['recurrence_factor']) ?> +
    • +
    • + text->e($recurrence_timeframe_list[$task['recurrence_timeframe']]) ?> +
    • +
    • + text->e($recurrence_basedate_list[$task['recurrence_basedate']]) ?> +
    • +
    +
  • + + + + +
  • + + url->link('#'.$task['recurrence_parent'], 'TaskViewController', 'show', array('task_id' => $task['recurrence_parent'])) ?> +
  • + + +
  • + + url->link('#'.$task['recurrence_child'], 'TaskViewController', 'show', array('task_id' => $task['recurrence_child'])) ?> +
  • + + +
+
\ No newline at end of file diff --git a/app/Template/task_status/close.php b/app/Template/task_status/close.php new file mode 100644 index 0000000..dfeba00 --- /dev/null +++ b/app/Template/task_status/close.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'TaskStatusController', + 'close', + array('task_id' => $task['id'], 'confirmation' => 'yes') + ) ?> +
diff --git a/app/Template/task_status/open.php b/app/Template/task_status/open.php new file mode 100644 index 0000000..dce28d7 --- /dev/null +++ b/app/Template/task_status/open.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'TaskStatusController', + 'open', + array('task_id' => $task['id'], 'confirmation' => 'yes') + ) ?> +
diff --git a/app/Template/task_suppression/remove.php b/app/Template/task_suppression/remove.php new file mode 100644 index 0000000..06d0211 --- /dev/null +++ b/app/Template/task_suppression/remove.php @@ -0,0 +1,15 @@ + + +
+

+ text->e($task['title'])) ?> +

+ + modal->confirmButtons( + 'TaskSuppressionController', + 'remove', + array('task_id' => $task['id'], 'redirect' => $redirect) + ) ?> +
diff --git a/app/Template/twofactor/check.php b/app/Template/twofactor/check.php new file mode 100644 index 0000000..dc95eb5 --- /dev/null +++ b/app/Template/twofactor/check.php @@ -0,0 +1,17 @@ + diff --git a/app/Template/twofactor/disable.php b/app/Template/twofactor/disable.php new file mode 100644 index 0000000..c44c450 --- /dev/null +++ b/app/Template/twofactor/disable.php @@ -0,0 +1,15 @@ + + +
+

+ +

+ + modal->confirmButtons( + 'TwoFactorController', + 'disable', + array('user_id' => $user['id'], 'disable' => 'yes') + ) ?> +
diff --git a/app/Template/twofactor/index.php b/app/Template/twofactor/index.php new file mode 100644 index 0000000..1ed414e --- /dev/null +++ b/app/Template/twofactor/index.php @@ -0,0 +1,15 @@ + + +
+ form->csrf() ?> +

text->e($provider) ?>

+
+ + + + + +
+
diff --git a/app/Template/twofactor/show.php b/app/Template/twofactor/show.php new file mode 100644 index 0000000..caa3588 --- /dev/null +++ b/app/Template/twofactor/show.php @@ -0,0 +1,32 @@ + + +app->isAjax()): ?> + app->flashMessage() ?> + + + +
+ +

text->e($secret) ?>

+ + + +

+

text->e($key_url) ?>

+ +
+ + +

+
+
+ + form->csrf() ?> + form->label(t('Code'), 'code') ?> + form->text('code', array(), array(), array('placeholder="123456"', 'autofocus'), 'form-numeric', 'autocomplete="one-time-code"', 'pattern="[0-9]*"', 'inputmode="numeric"') ?> + + modal->submitButtons(array('submitLabel' => t('Check my code'))) ?> +
+
diff --git a/app/Template/user_api_access/show.php b/app/Template/user_api_access/show.php new file mode 100644 index 0000000..39bf354 --- /dev/null +++ b/app/Template/user_api_access/show.php @@ -0,0 +1,17 @@ + + +

+ + + + + +

+ + + url->link(t('Remove your token'), 'UserApiAccessController', 'remove', array('user_id' => $user['id']), true, 'btn btn-red js-modal-replace') ?> + + +url->link(t('Generate a new token'), 'UserApiAccessController', 'generate', array('user_id' => $user['id']), true, 'btn btn-blue js-modal-replace') ?> diff --git a/app/Template/user_creation/show.php b/app/Template/user_creation/show.php new file mode 100644 index 0000000..2c6ad35 --- /dev/null +++ b/app/Template/user_creation/show.php @@ -0,0 +1,74 @@ + +
+ form->csrf() ?> + +
+
+
+ + + form->label(t('Username'), 'username') ?> + form->text('username', $values, $errors, array('autofocus', 'required', 'maxlength="191"', 'autocomplete="username"')) ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, ['autocomplete="name"']) ?> + + form->label(t('Email'), 'email') ?> + form->email('email', $values, $errors, ['autocomplete="email"']) ?> +
+ +
+ + form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?> +

+ + form->label(t('Password'), 'password') ?> + form->password('password', $values, $errors, ['autocomplete="new-password"']) ?> +

+ + form->label(t('Confirmation'), 'confirmation') ?> + form->password('confirmation', $values, $errors) ?> +
+
+ +
+
+ + + form->label(t('Role'), 'role') ?> + form->select('role', $roles, $values, $errors) ?> + + form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?> +
+ +
+ + form->label(t('Timezone'), 'timezone') ?> + form->select('timezone', $timezones, $values, $errors) ?> + + form->label(t('Language'), 'language') ?> + form->select('language', $languages, $values, $errors) ?> + + form->label(t('Filter'), 'filter') ?> + form->text('filter', $values, $errors) ?> + + app->config('notifications_enabled') == 1): ?> + + + form->checkbox('notifications_enabled', t('Enable email notifications'), 1, isset($values['notifications_enabled']) && $values['notifications_enabled'] == 1 ? true : false) ?> + +
+ +
+ + + form->label(t('Add this person to this project'), 'project_id') ?> + form->select('project_id', $projects, $values, $errors) ?> +
+
+
+ + modal->submitButtons() ?> +
diff --git a/app/Template/user_credential/authentication.php b/app/Template/user_credential/authentication.php new file mode 100644 index 0000000..32371bd --- /dev/null +++ b/app/Template/user_credential/authentication.php @@ -0,0 +1,25 @@ + +
+ form->csrf() ?> + +
+ form->hidden('id', $values) ?> + form->hidden('username', $values) ?> + + hook->render('template:user:authentication:form', array('values' => $values, 'errors' => $errors, 'user' => $user)) ?> + + form->checkbox('is_ldap_user', t('Remote user'), 1, isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) ?> + form->checkbox('disable_login_form', t('Disallow login form'), 1, isset($values['disable_login_form']) && $values['disable_login_form'] == 1) ?> +
+ + modal->submitButtons() ?> + +
+
    +
  • +
  • +
+
+
diff --git a/app/Template/user_credential/password.php b/app/Template/user_credential/password.php new file mode 100644 index 0000000..ee752af --- /dev/null +++ b/app/Template/user_credential/password.php @@ -0,0 +1,21 @@ + + +
+ form->hidden('id', $values) ?> + form->csrf() ?> + +
+ form->label(t('Current password for the user "%s"', $this->user->getFullname()), 'current_password') ?> + form->password('current_password', $values, $errors, array('autofocus', 'autocomplete="current-password"')) ?> + + form->label(t('New password for the user "%s"', $this->user->getFullname($user)), 'password') ?> + form->password('password', $values, $errors, ['autocomplete="new-password"']) ?> + + form->label(t('Confirmation'), 'confirmation') ?> + form->password('confirmation', $values, $errors) ?> +
+ + modal->submitButtons() ?> +
diff --git a/app/Template/user_import/show.php b/app/Template/user_import/show.php new file mode 100644 index 0000000..35eddc3 --- /dev/null +++ b/app/Template/user_import/show.php @@ -0,0 +1,38 @@ + + +
+
    +
  • +
  • +
  • +
  • +
  • +
  • +
+
+ +
+ form->csrf() ?> + + form->label(t('Delimiter'), 'delimiter') ?> + form->select('delimiter', $delimiters, $values) ?> + + form->label(t('Enclosure'), 'enclosure') ?> + form->select('enclosure', $enclosures, $values) ?> + + form->label(t('CSV File'), 'file') ?> + form->file('file', $errors) ?> + + 0): ?> +

text->bytes($max_size) : $max_size ?>

+ + + modal->submitButtons(array('submitLabel' => t('Import'))) ?> +
diff --git a/app/Template/user_invite/email.php b/app/Template/user_invite/email.php new file mode 100644 index 0000000..674e4a8 --- /dev/null +++ b/app/Template/user_invite/email.php @@ -0,0 +1,12 @@ +

+ +

+ +

+ url->absoluteLink(t('Click here to join your team'), 'UserInviteController', 'signup', array('token' => $token)) ?> +

+ +app->config('application_url')): ?> +
+ Kanboard + diff --git a/app/Template/user_invite/show.php b/app/Template/user_invite/show.php new file mode 100644 index 0000000..9d82224 --- /dev/null +++ b/app/Template/user_invite/show.php @@ -0,0 +1,15 @@ + +
+ form->csrf() ?> + + form->label(t('Emails'), 'emails') ?> + form->textarea('emails', $values, $errors, array('required', 'autofocus')) ?> +

+ + form->label(t('Add these people to this project'), 'project_id') ?> + form->select('project_id', $projects, $values, $errors) ?> + + modal->submitButtons() ?> +
diff --git a/app/Template/user_invite/signup.php b/app/Template/user_invite/signup.php new file mode 100644 index 0000000..2fb9a09 --- /dev/null +++ b/app/Template/user_invite/signup.php @@ -0,0 +1,50 @@ + \ No newline at end of file diff --git a/app/Template/user_list/dropdown.php b/app/Template/user_list/dropdown.php new file mode 100644 index 0000000..b58c056 --- /dev/null +++ b/app/Template/user_list/dropdown.php @@ -0,0 +1,100 @@ + diff --git a/app/Template/user_list/header.php b/app/Template/user_list/header.php new file mode 100644 index 0000000..9dbcc2a --- /dev/null +++ b/app/Template/user_list/header.php @@ -0,0 +1,13 @@ + +
+
+ getTotal() > 1): ?> + getTotal()) ?> + + getTotal()) ?> + +
+
+ render('user_list/sort_menu', array('paginator' => $paginator)) ?> +
+
diff --git a/app/Template/user_list/listing.php b/app/Template/user_list/listing.php new file mode 100644 index 0000000..2495517 --- /dev/null +++ b/app/Template/user_list/listing.php @@ -0,0 +1,51 @@ + + +
+ +
+ +isEmpty()): ?> +

+isEmpty()): ?> +
+ render('user_list/header', array('paginator' => $paginator)) ?> + getCollection() as $user): ?> +
+ render('user_list/user_title', array( + 'user' => $user, + )) ?> + + render('user_list/user_details', array( + 'user' => $user, + )) ?> + + render('user_list/user_icons', array( + 'user' => $user, + )) ?> +
+ +
+ + + diff --git a/app/Template/user_list/sort_menu.php b/app/Template/user_list/sort_menu.php new file mode 100644 index 0000000..250832f --- /dev/null +++ b/app/Template/user_list/sort_menu.php @@ -0,0 +1,29 @@ + diff --git a/app/Template/user_list/user_details.php b/app/Template/user_list/user_details.php new file mode 100644 index 0000000..3156bc3 --- /dev/null +++ b/app/Template/user_list/user_details.php @@ -0,0 +1,26 @@ +
+ + user->getRoleName($user['role']) ?> + + + + text->e($user['username']) ?> + + + + text->e($user['email']) ?> + + + + user->getUsersGroupNames($user['id']); ?> + + + + text->implode(', ', $users_groups['limited_list']) ?> + + ‑  + + + + +
diff --git a/app/Template/user_list/user_icons.php b/app/Template/user_list/user_icons.php new file mode 100644 index 0000000..2b15b25 --- /dev/null +++ b/app/Template/user_list/user_icons.php @@ -0,0 +1,59 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + dt->datetime($user['lock_expiration_date']); ?> + + + + + + + user->getRoleName($user['role']); ?> + + + + + + + user->getRoleName($user['role']); ?> + + + + + + + user->getRoleName($user['role']); ?> + + + + + + + + + + +
\ No newline at end of file diff --git a/app/Template/user_list/user_title.php b/app/Template/user_list/user_title.php new file mode 100644 index 0000000..02a065c --- /dev/null +++ b/app/Template/user_list/user_title.php @@ -0,0 +1,14 @@ +
+ render('user_list/dropdown', array('user' => $user)) ?> + + avatar->small( + $user['id'], + $user['username'], + $user['name'], + $user['email'], + $user['avatar_path'], + 'avatar-inline' + ) ?> + url->link($this->text->e($user['name'] ?: $user['username']), 'UserViewController', 'show', array('user_id' => $user['id'])) ?> + +
diff --git a/app/Template/user_modification/show.php b/app/Template/user_modification/show.php new file mode 100644 index 0000000..c9d54ff --- /dev/null +++ b/app/Template/user_modification/show.php @@ -0,0 +1,44 @@ + +
+ form->csrf() ?> + form->hidden('id', $values) ?> + +
+ + form->label(t('Username'), 'username') ?> + form->text('username', $values, $errors, array('autofocus', 'required', 'autocomplete="username"', isset($user['is_ldap_user']) && $user['is_ldap_user'] == 1 ? 'readonly' : '', 'maxlength="191"')) ?> + + form->label(t('Name'), 'name') ?> + form->text('name', $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_name') ? 'autocomplete="name"' : 'readonly')) ?> + + form->label(t('Email'), 'email') ?> + form->email('email', $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_email') ? 'autocomplete="email"' : 'readonly')) ?> +
+ +
+ + form->label(t('Theme'), 'theme') ?> + form->select('theme', $themes, $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_theme') ? '' : 'disabled')) ?> + + form->label(t('Timezone'), 'timezone') ?> + form->select('timezone', $timezones, $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_timezone') ? '' : 'disabled')) ?> + + form->label(t('Language'), 'language') ?> + form->select('language', $languages, $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_language') ? '' : 'disabled')) ?> + + form->label(t('Filter'), 'filter') ?> + form->text('filter', $values, $errors, array($this->user->hasAccess('UserModificationController', 'show/edit_filter') ? '' : 'readonly')) ?> +
+ + user->isAdmin()): ?> +
+ + form->label(t('Application role'), 'role') ?> + form->select('role', $roles, $values, $errors) ?> +
+ + + modal->submitButtons() ?> +
diff --git a/app/Template/user_status/disable.php b/app/Template/user_status/disable.php new file mode 100644 index 0000000..1309b08 --- /dev/null +++ b/app/Template/user_status/disable.php @@ -0,0 +1,13 @@ + + +
+

+ + modal->confirmButtons( + 'UserStatusController', + 'disable', + array('user_id' => $user['id']) + ) ?> +
diff --git a/app/Template/user_status/enable.php b/app/Template/user_status/enable.php new file mode 100644 index 0000000..2413739 --- /dev/null +++ b/app/Template/user_status/enable.php @@ -0,0 +1,13 @@ + + +
+

+ + modal->confirmButtons( + 'UserStatusController', + 'enable', + array('user_id' => $user['id']) + ) ?> +
diff --git a/app/Template/user_status/remove.php b/app/Template/user_status/remove.php new file mode 100644 index 0000000..6cd3f63 --- /dev/null +++ b/app/Template/user_status/remove.php @@ -0,0 +1,13 @@ + + +
+

+ + modal->confirmButtons( + 'UserStatusController', + 'remove', + array('user_id' => $user['id']) + ) ?> +
diff --git a/app/Template/user_view/external.php b/app/Template/user_view/external.php new file mode 100644 index 0000000..22c25af --- /dev/null +++ b/app/Template/user_view/external.php @@ -0,0 +1,11 @@ + + +hook->render('template:user:external', array('user' => $user)) ?> + + +

+ + + diff --git a/app/Template/user_view/integrations.php b/app/Template/user_view/integrations.php new file mode 100644 index 0000000..4a23734 --- /dev/null +++ b/app/Template/user_view/integrations.php @@ -0,0 +1,13 @@ + + +
+ form->csrf() ?> + hook->render('template:user:integrations', array('values' => $values)) ?> + + + +

+ +
diff --git a/app/Template/user_view/last.php b/app/Template/user_view/last.php new file mode 100644 index 0000000..72f59bf --- /dev/null +++ b/app/Template/user_view/last.php @@ -0,0 +1,24 @@ + + + +

+ + + + + + + + + + + + + + + + +
dt->datetime($login['date_creation']) ?>text->e($login['auth_type']) ?>text->e($login['ip']) ?>text->e($login['user_agent']) ?>
+ diff --git a/app/Template/user_view/layout.php b/app/Template/user_view/layout.php new file mode 100644 index 0000000..8f24adc --- /dev/null +++ b/app/Template/user_view/layout.php @@ -0,0 +1,26 @@ +
+ + +
diff --git a/app/Template/user_view/notifications.php b/app/Template/user_view/notifications.php new file mode 100644 index 0000000..1a8d9be --- /dev/null +++ b/app/Template/user_view/notifications.php @@ -0,0 +1,22 @@ + + +
+ form->csrf() ?> + +

+ form->checkboxes('notification_types', $types, $notifications) ?> + +
+

+ form->radios('notifications_filter', $filters, $notifications) ?> + +
+ +

+ form->checkboxes('notification_projects', $projects, $notifications) ?> + + + modal->submitButtons() ?> +
diff --git a/app/Template/user_view/password_reset.php b/app/Template/user_view/password_reset.php new file mode 100644 index 0000000..de7047e --- /dev/null +++ b/app/Template/user_view/password_reset.php @@ -0,0 +1,26 @@ + + + +

+ + + + + + + + + + + + + + + + + + +
dt->datetime($token['date_creation']) ?>dt->datetime($token['date_expiration']) ?>text->e($token['ip']) ?>text->e($token['user_agent']) ?>
+ diff --git a/app/Template/user_view/profile.php b/app/Template/user_view/profile.php new file mode 100644 index 0000000..d931e3e --- /dev/null +++ b/app/Template/user_view/profile.php @@ -0,0 +1,12 @@ +
+
+ avatar->render($user['id'], $user['username'], $user['name'], $user['email'], $user['avatar_path']) ?> +
+
    +
  • text->e($user['username']) ?>
  • +
  • text->e($user['name']) ?: t('None') ?>
  • +
  • text->e($user['email']) ?: t('None') ?>
  • + hook->render('template:user:show:profile:info', array('user' => $user)) ?> +
+
+
diff --git a/app/Template/user_view/sessions.php b/app/Template/user_view/sessions.php new file mode 100644 index 0000000..dec1175 --- /dev/null +++ b/app/Template/user_view/sessions.php @@ -0,0 +1,26 @@ + + + +

+ + + + + + + + + + + + + + + + + + +
dt->datetime($session['date_creation']) ?>dt->datetime($session['expiration']) ?>text->e($session['ip']) ?>text->e($session['user_agent']) ?>url->link(t('Remove'), 'UserViewController', 'removeSession', array('user_id' => $user['id'], 'id' => $session['id']), true, 'js-modal-replace') ?>
+ diff --git a/app/Template/user_view/share.php b/app/Template/user_view/share.php new file mode 100644 index 0000000..784e0eb --- /dev/null +++ b/app/Template/user_view/share.php @@ -0,0 +1,15 @@ + + + +
+
    +
  • url->icon('rss-square', t('RSS feed'), 'FeedController', 'user', array('token' => $user['token']), false, '', '', true) ?>
  • +
  • url->icon('calendar', t('iCal feed'), 'ICalendarController', 'user', array('token' => $user['token']), false, '', '', true) ?>
  • +
+
+ url->link(t('Disable public access'), 'UserViewController', 'share', array('user_id' => $user['id'], 'switch' => 'disable'), true, 'btn btn-red js-modal-replace') ?> + + url->link(t('Enable public access'), 'UserViewController', 'share', array('user_id' => $user['id'], 'switch' => 'enable'), true, 'btn btn-blue js-modal-replace') ?> + diff --git a/app/Template/user_view/show.php b/app/Template/user_view/show.php new file mode 100644 index 0000000..af06772 --- /dev/null +++ b/app/Template/user_view/show.php @@ -0,0 +1,53 @@ + +
    +
  • text->e($user['username']) ?>
  • +
  • text->e($user['name']) ?: t('None') ?>
  • +
  • text->e($user['email']) ?: t('None') ?>
  • +
  • + hook->render('template:user:show:profile:info', array('user' => $user)) ?> +
+ + +
    +
  • text->e($this->user->getRoleName($user['role'])) ?>
  • +
  • text->e(implode(', ', $this->user->getUsersGroupNames($user['id'])['full_list'])) ?>
  • +
  • +
  • +
  • + +
  • dt->datetime($user['lock_expiration_date']) ?>
  • + user->isAdmin()): ?> +
  • + url->link(t('Unlock this user'), 'UserCredentialController', 'unlock', array('user_id' => $user['id']), true) ?> +
  • + + +
+ + +
    +
  • text->in($user['theme'], $themes) ?>
  • +
  • text->in($user['timezone'], $timezones) ?>
  • +
  • text->in($user['language'], $languages) ?>
  • +
  • text->e($user['filter']) ?>
  • +
  • +
+ + + + +
+
    +
  • url->icon('rss-square', t('RSS feed'), 'FeedController', 'user', array('token' => $user['token']), false, '', '', true) ?>
  • +
  • url->icon('calendar', t('iCal feed'), 'ICalendarController', 'user', array('token' => $user['token']), false, '', '', true) ?>
  • +
+
+ diff --git a/app/Template/user_view/sidebar.php b/app/Template/user_view/sidebar.php new file mode 100644 index 0000000..a204ec5 --- /dev/null +++ b/app/Template/user_view/sidebar.php @@ -0,0 +1,108 @@ + diff --git a/app/Template/user_view/timesheet.php b/app/Template/user_view/timesheet.php new file mode 100644 index 0000000..c78cb0c --- /dev/null +++ b/app/Template/user_view/timesheet.php @@ -0,0 +1,29 @@ + + +

+isEmpty()): ?> +

+ + + + + + + + + + getCollection() as $record): ?> + + + + + + + + +
order(t('Task'), 'task_title') ?>order(t('Subtask'), 'subtask_title') ?>order(t('Start'), 'start') ?>order(t('End'), 'end') ?>order(t('Time spent'), 'time_spent') ?>
url->link($this->text->e($record['task_title']), 'TaskViewController', 'show', array('task_id' => $record['task_id'])) ?>url->link($this->text->e($record['subtask_title']), 'TaskViewController', 'show', array('task_id' => $record['task_id'])) ?>dt->datetime($record['start']) ?>dt->datetime($record['end']) ?>
+ + + diff --git a/app/Template/web_notification/show.php b/app/Template/web_notification/show.php new file mode 100644 index 0000000..72ebdb2 --- /dev/null +++ b/app/Template/web_notification/show.php @@ -0,0 +1,68 @@ + + + +

+ +
+
+
+ 1): ?> + + + + +
+   +
+ +
+ + text->contains($notification['event_name'], 'subtask')): ?> + + text->contains($notification['event_name'], 'task.move')): ?> + + text->contains($notification['event_name'], 'task.overdue')): ?> + + text->contains($notification['event_name'], 'task')): ?> + + text->contains($notification['event_name'], 'comment')): ?> + + text->contains($notification['event_name'], 'file')): ?> + + + + + url->link( + $this->text->e($notification['event_data']['task']['project_name']), + 'BoardViewController', + 'show', + array('project_id' => $notification['event_data']['task']['project_id']) + ) ?> > + + text->e($notification['event_data']['project_name']) ?> > + + + text->contains($notification['event_name'], 'task.overdue') && count($notification['event_data']['tasks']) > 1): ?> + + + url->link($notification['title'], 'WebNotificationController', 'redirect', array('notification_id' => $notification['id'], 'user_id' => $user['id'])) ?> + + +
+ dt->datetime($notification['date_creation']) ?> + modal->replaceIconLink('check', t('Mark as read'), 'WebNotificationController', 'remove', array('user_id' => $user['id'], 'notification_id' => $notification['id'], 'csrf_token' => $this->app->getToken()->getReusableCSRFToken())) ?> +
+
+ +
+ \ No newline at end of file diff --git a/app/User/Avatar/AvatarFileProvider.php b/app/User/Avatar/AvatarFileProvider.php new file mode 100644 index 0000000..932de07 --- /dev/null +++ b/app/User/Avatar/AvatarFileProvider.php @@ -0,0 +1,42 @@ +helper->url->href('AvatarFileController', 'image', array('user_id' => $user['id'], 'hash' => md5($user['avatar_path'].$size), 'size' => $size)); + $title = $this->helper->text->e($user['name'] ?: $user['username']); + return '' . $title . ''; + } + + /** + * Determine if the provider is active + * + * @access public + * @param array $user + * @return boolean + */ + public function isActive(array $user) + { + return !empty($user['avatar_path']); + } +} diff --git a/app/User/Avatar/LetterAvatarProvider.php b/app/User/Avatar/LetterAvatarProvider.php new file mode 100644 index 0000000..b9f6f2d --- /dev/null +++ b/app/User/Avatar/LetterAvatarProvider.php @@ -0,0 +1,172 @@ +helper->user->getInitials($user['name'] ?: $user['username']); + $rgb = $this->getBackgroundColor($user['name'] ?: $user['username']); + $name = $this->helper->text->e($user['name'] ?: $user['username']); + + return sprintf( + '', + $rgb[0], + $rgb[1], + $rgb[2], + $name, + $name, + $this->helper->text->e($initials) + ); + } + + /** + * Determine if the provider is active + * + * @access public + * @param array $user + * @return boolean + */ + public function isActive(array $user) + { + return true; + } + + /** + * Get background color based on a string + * + * @param string $str + * @return array + */ + public function getBackgroundColor($str) + { + $hsl = $this->getHSL($str); + return $this->getRGB($hsl[0], $hsl[1], $hsl[2]); + } + + /** + * Convert HSL to RGB + * + * @access protected + * @param integer $hue Hue ∈ [0, 360) + * @param integer $saturation Saturation ∈ [0, 1] + * @param integer $lightness Lightness ∈ [0, 1] + * @return array + */ + protected function getRGB($hue, $saturation, $lightness) + { + $hue /= 360; + $q = $lightness < 0.5 ? $lightness * (1 + $saturation) : $lightness + $saturation - $lightness * $saturation; + $p = 2 * $lightness - $q; + + return array_map(function ($color) use ($q, $p) { + if ($color < 0) { + $color++; + } + + if ($color > 1) { + $color--; + } + + if ($color < 1/6) { + $color = $p + ($q - $p) * 6 * $color; + } elseif ($color < 0.5) { + $color = $q; + } elseif ($color < 2/3) { + $color = $p + ($q - $p) * 6 * (2/3 - $color); + } else { + $color = $p; + } + + return round($color * 255); + }, array($hue + 1/3, $hue, $hue - 1/3)); + } + + /** + * Returns the hash in [h, s, l]. + * Note that H ∈ [0, 360); S ∈ [0, 1]; L ∈ [0, 1]; + * + * @access protected + * @param string $str + * @return array + */ + protected function getHSL($str) + { + $hash = $this->hash($str); + $hue = $hash % 359; + + $hash = intval($hash / 360); + $saturation = $this->saturation[$hash % count($this->saturation)]; + + $hash = intval($hash / count($this->saturation)); + $lightness = $this->lightness[$hash % count($this->lightness)]; + + return array($hue, $saturation, $lightness); + } + + /** + * BKDR Hash (modified version) + * + * @access protected + * @param string $str + * @return integer + */ + protected function hash($str) + { + $seed = 131; + $seed2 = 137; + $hash = 0; + + // Make hash more sensitive for short string like 'a', 'b', 'c' + $str .= 'x'; + $max = intval(PHP_INT_MAX / $seed2); + + for ($i = 0, $ilen = mb_strlen($str, 'UTF-8'); $i < $ilen; $i++) { + if ($hash > $max) { + $hash = intval($hash / $seed2); + } + + $hash = $hash * $seed + $this->getCharCode(mb_substr($str, $i, 1, 'UTF-8')); + } + + return $hash; + } + + /** + * Backport of Javascript function charCodeAt() + * + * @access protected + * @param string $c + * @return integer + */ + protected function getCharCode($c) + { + list(, $ord) = unpack('N', mb_convert_encoding($c, 'UCS-4BE', 'UTF-8')); + return $ord; + } +} diff --git a/app/User/DatabaseBackendUserProvider.php b/app/User/DatabaseBackendUserProvider.php new file mode 100644 index 0000000..835d90b --- /dev/null +++ b/app/User/DatabaseBackendUserProvider.php @@ -0,0 +1,43 @@ +userQuery->withFilter(new UserNameFilter($input)) + ->getQuery() + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name') + ->eq(UserModel::TABLE.'.is_active', 1) + ->asc(UserModel::TABLE.'.name') + ->asc(UserModel::TABLE.'.username') + ->findAll(); + + foreach ($users as $user) { + $result[] = new DatabaseUserProvider($user); + } + + return $result; + } +} diff --git a/app/User/DatabaseUserProvider.php b/app/User/DatabaseUserProvider.php new file mode 100644 index 0000000..99b4599 --- /dev/null +++ b/app/User/DatabaseUserProvider.php @@ -0,0 +1,143 @@ +user = $user; + } + + /** + * Return true to allow automatic user creation + * + * @access public + * @return boolean + */ + public function isUserCreationAllowed() + { + return false; + } + + /** + * Get internal id + * + * @access public + * @return integer + */ + public function getInternalId() + { + return $this->user['id']; + } + + /** + * Get external id column name + * + * @access public + * @return string + */ + public function getExternalIdColumn() + { + return ''; + } + + /** + * Get external id + * + * @access public + * @return string + */ + public function getExternalId() + { + return ''; + } + + /** + * Get user role + * + * @access public + * @return string + */ + public function getRole() + { + return empty($this->user['role']) ? '' : $this->user['role']; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + return empty($this->user['username']) ? '' : $this->user['username']; + } + + /** + * Get full name + * + * @access public + * @return string + */ + public function getName() + { + return empty($this->user['name']) ? '' : $this->user['name']; + } + + /** + * Get user email + * + * @access public + * @return string + */ + public function getEmail() + { + return empty($this->user['email']) ? '' : $this->user['email']; + } + + /** + * Get external group ids + * + * @access public + * @return array + */ + public function getExternalGroupIds() + { + return array(); + } + + /** + * Get extra user attributes + * + * @access public + * @return array + */ + public function getExtraAttributes() + { + return array(); + } +} diff --git a/app/User/LdapUserProvider.php b/app/User/LdapUserProvider.php new file mode 100644 index 0000000..4b658eb --- /dev/null +++ b/app/User/LdapUserProvider.php @@ -0,0 +1,242 @@ +dn = $dn; + $this->username = $username; + $this->name = $name; + $this->email = $email; + $this->role = $role; + $this->groupIds = $groupIds; + $this->photo = $photo; + $this->language = $language; + } + + /** + * Return true to allow automatic user creation + * + * @access public + * @return boolean + */ + public function isUserCreationAllowed() + { + return LDAP_USER_CREATION; + } + + /** + * Get internal id + * + * @access public + * @return integer + */ + public function getInternalId() + { + return 0; + } + + /** + * Get external id column name + * + * @access public + * @return string + */ + public function getExternalIdColumn() + { + return 'username'; + } + + /** + * Get external id + * + * @access public + * @return string + */ + public function getExternalId() + { + return $this->getUsername(); + } + + /** + * Get user role + * + * @access public + * @return string + */ + public function getRole() + { + return $this->role; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + return LDAP_USERNAME_CASE_SENSITIVE ? $this->username : strtolower($this->username); + } + + /** + * Get full name + * + * @access public + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get user email + * + * @access public + * @return string + */ + public function getEmail() + { + return $this->email; + } + + /** + * Get groups DN + * + * @access public + * @return string[] + */ + public function getExternalGroupIds() + { + return $this->groupIds; + } + + /** + * Get extra user attributes + * + * @access public + * @return array + */ + public function getExtraAttributes() + { + $attributes = array('is_ldap_user' => 1); + + if (! empty($this->language)) { + $attributes['language'] = LanguageModel::findCode($this->language); + } + + return $attributes; + } + + /** + * Get User DN + * + * @access public + * @return string + */ + public function getDn() + { + return $this->dn; + } + + /** + * Get user photo + * + * @access public + * @return string + */ + public function getPhoto() + { + return $this->photo; + } +} diff --git a/app/User/OAuthUserProvider.php b/app/User/OAuthUserProvider.php new file mode 100644 index 0000000..718cfac --- /dev/null +++ b/app/User/OAuthUserProvider.php @@ -0,0 +1,132 @@ +user = $user; + } + + /** + * Return true to allow automatic user creation + * + * @access public + * @return boolean + */ + public function isUserCreationAllowed() + { + return false; + } + + /** + * Get internal id + * + * @access public + * @return integer + */ + public function getInternalId() + { + return 0; + } + + /** + * Get external id + * + * @access public + * @return string + */ + public function getExternalId() + { + return isset($this->user['id']) ? $this->user['id'] : ''; + } + + /** + * Get user role + * + * @access public + * @return string + */ + public function getRole() + { + return ''; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + return ''; + } + + /** + * Get full name + * + * @access public + * @return string + */ + public function getName() + { + return isset($this->user['name']) ? $this->user['name'] : ''; + } + + /** + * Get user email + * + * @access public + * @return string + */ + public function getEmail() + { + return isset($this->user['email']) ? $this->user['email'] : ''; + } + + /** + * Get external group ids + * + * @access public + * @return array + */ + public function getExternalGroupIds() + { + return array(); + } + + /** + * Get extra user attributes + * + * @access public + * @return array + */ + public function getExtraAttributes() + { + return array(); + } +} diff --git a/app/User/ReverseProxyUserProvider.php b/app/User/ReverseProxyUserProvider.php new file mode 100644 index 0000000..35b4798 --- /dev/null +++ b/app/User/ReverseProxyUserProvider.php @@ -0,0 +1,188 @@ +username = $username; + $this->email = $email; + $this->fullname = $fullname; + $this->userProfile = $userProfile; + } + + /** + * Return true to allow automatic user creation + * + * @access public + * @return boolean + */ + public function isUserCreationAllowed() + { + return true; + } + + /** + * Get internal id + * + * @access public + * @return integer + */ + public function getInternalId() + { + return 0; + } + + /** + * Get external id column name + * + * @access public + * @return string + */ + public function getExternalIdColumn() + { + return 'username'; + } + + /** + * Get external id + * + * @access public + * @return string + */ + public function getExternalId() + { + return $this->username; + } + + /** + * Get user role + * + * @access public + * @return string + */ + public function getRole() + { + if (REVERSE_PROXY_DEFAULT_ADMIN === $this->username) { + return Role::APP_ADMIN; + } + + if (isset($this->userProfile['role'])) { + return $this->userProfile['role']; + } + + return Role::APP_USER; + } + + /** + * Get username + * + * @access public + * @return string + */ + public function getUsername() + { + return $this->username; + } + + /** + * Get full name + * + * @access public + * @return string + */ + public function getName() + { + return $this->fullname; + } + + /** + * Get user email + * + * @access public + * @return string + */ + public function getEmail() + { + if (REVERSE_PROXY_DEFAULT_DOMAIN !== '' && $this->email === '') { + return $this->username.'@'.REVERSE_PROXY_DEFAULT_DOMAIN; + } + + return $this->email; + } + + /** + * Get external group ids + * + * @access public + * @return array + */ + public function getExternalGroupIds() + { + return array(); + } + + /** + * Get extra user attributes + * + * @access public + * @return array + */ + public function getExtraAttributes() + { + return array( + 'is_ldap_user' => 1, + 'disable_login_form' => 1, + ); + } +} diff --git a/app/Validator/ActionValidator.php b/app/Validator/ActionValidator.php new file mode 100644 index 0000000..4ce5db4 --- /dev/null +++ b/app/Validator/ActionValidator.php @@ -0,0 +1,38 @@ +execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/AuthValidator.php b/app/Validator/AuthValidator.php new file mode 100644 index 0000000..dbf0261 --- /dev/null +++ b/app/Validator/AuthValidator.php @@ -0,0 +1,119 @@ +executeValidators(array('validateFields', 'validateLocking', 'validateCaptcha', 'validateCredentials'), $values); + } + + /** + * Validate credentials syntax + * + * @access protected + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + protected function validateFields(array $values) + { + $v = new Validator($values, array( + new Validators\Required('username', t('The username is required')), + new Validators\MaxLength('username', t('The maximum length is %d characters', 191), 191), + new Validators\Required('password', t('The password is required')), + )); + + return array( + $v->execute(), + $v->getErrors(), + ); + } + + /** + * Validate user locking + * + * @access protected + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + protected function validateLocking(array $values) + { + $result = true; + $errors = array(); + + if ($this->userLockingModel->isLocked($values['username'])) { + $result = false; + $errors['login'] = t('Your account is locked for %d minutes', BRUTEFORCE_LOCKDOWN_DURATION); + $this->logger->error('Account locked: '.$values['username']); + } + + return array($result, $errors); + } + + /** + * Validate password syntax + * + * @access protected + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + protected function validateCredentials(array $values) + { + $result = true; + $errors = array(); + + if (! $this->authenticationManager->passwordAuthentication($values['username'], $values['password'])) { + $result = false; + $errors['login'] = t('Bad username or password'); + } + + return array($result, $errors); + } + + /** + * Validate captcha + * + * @access protected + * @param array $values Form values + * @return array + */ + protected function validateCaptcha(array $values) + { + $result = true; + $errors = array(); + + if ($this->userLockingModel->hasCaptcha($values['username']) || $this->captchaModel->isLocked($this->request->getIpAddress())) { + if (! session_exists('captcha')) { + $result = false; + } else { + $builder = new CaptchaBuilder; + $builder->setPhrase(session_get('captcha')); + $result = $builder->testPhrase(isset($values['captcha']) ? $values['captcha'] : ''); + + if (! $result) { + $errors['login'] = t('Bad username or password'); + } + } + } + + return array($result, $errors); + } +} diff --git a/app/Validator/BaseValidator.php b/app/Validator/BaseValidator.php new file mode 100644 index 0000000..6088538 --- /dev/null +++ b/app/Validator/BaseValidator.php @@ -0,0 +1,55 @@ +$method($values); + + if (! $result) { + break; + } + } + + return array($result, $errors); + } + + /** + * Common password validation rules + * + * @access protected + * @return array + */ + protected function commonPasswordValidationRules() + { + return array( + new Validators\Required('password', t('The password is required')), + new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6), + new Validators\Required('confirmation', t('The confirmation is required')), + new Validators\Equals('password', 'confirmation', t('Passwords don\'t match')), + ); + } +} diff --git a/app/Validator/CategoryValidator.php b/app/Validator/CategoryValidator.php new file mode 100644 index 0000000..1d14891 --- /dev/null +++ b/app/Validator/CategoryValidator.php @@ -0,0 +1,74 @@ +commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate category modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + new Validators\Required('name', t('The name 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\Integer('id', t('The id must be an integer')), + new Validators\Integer('project_id', t('The project id must be an integer')), + new Validators\MaxLength('name', t('The maximum length is %d characters', 191), 191) + ); + } +} diff --git a/app/Validator/ColumnMoveRestrictionValidator.php b/app/Validator/ColumnMoveRestrictionValidator.php new file mode 100644 index 0000000..99769c6 --- /dev/null +++ b/app/Validator/ColumnMoveRestrictionValidator.php @@ -0,0 +1,41 @@ +execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/ColumnRestrictionValidator.php b/app/Validator/ColumnRestrictionValidator.php new file mode 100644 index 0000000..b1b2e5a --- /dev/null +++ b/app/Validator/ColumnRestrictionValidator.php @@ -0,0 +1,40 @@ +execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/ColumnValidator.php b/app/Validator/ColumnValidator.php new file mode 100644 index 0000000..59d3ddf --- /dev/null +++ b/app/Validator/ColumnValidator.php @@ -0,0 +1,75 @@ +commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate column creation + * + * @access public + * @param array $values Required parameters to save an action + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + $rules = array( + new Validators\Required('project_id', t('The project id is required')), + new Validators\Integer('project_id', t('This value must be an integer')), + ); + + $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\Integer('task_limit', t('This value must be an integer')), + new Validators\GreaterThan('task_limit', t('This value must be greater than %d', -1), -1), + new Validators\Required('title', t('The title is required')), + new Validators\MaxLength('title', t('The maximum length is %d characters', 191), 191), + ); + } +} diff --git a/app/Validator/CommentValidator.php b/app/Validator/CommentValidator.php new file mode 100644 index 0000000..081d48b --- /dev/null +++ b/app/Validator/CommentValidator.php @@ -0,0 +1,103 @@ +commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate comment creation + * + * @access public + * @param array $values Required parameters to save an action + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateCreation(array $values) + { + $rules = array( + new Validators\Required('task_id', t('This value is required')), + new Validators\Required('visibility', t('Visibility is required')), + new Validators\InArray('visibility', array(Role::APP_USER, Role::APP_MANAGER, Role::APP_ADMIN), t('The visibility should be an app role')) + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate comment modification + * + * @access public + * @param array $values Required parameters to save an action + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('This value 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\Integer('id', t('This value must be an integer')), + new Validators\Integer('task_id', t('This value must be an integer')), + new Validators\Integer('user_id', t('This value must be an integer')), + new Validators\MaxLength('reference', t('The maximum length is %d characters', 191), 191), + new Validators\Required('comment', t('Comment is required')) + ); + } +} diff --git a/app/Validator/ConfigValidator.php b/app/Validator/ConfigValidator.php new file mode 100644 index 0000000..c9c102e --- /dev/null +++ b/app/Validator/ConfigValidator.php @@ -0,0 +1,40 @@ +languageModel->getLanguages()), t('This language is invalid')), + new Validators\Timezone('application_timezone', t('This timezone is invalid')), + new Validators\InArray('application_date_format', $this->dateParser->getDateFormats(true), t('Date format invalid')), + new Validators\InArray('application_time_format', $this->dateParser->getTimeFormats(), t('Time format invalid')), + new Validators\Email('mail_sender_address', t('Email address invalid')), + new Validators\InArray('mail_transport', array_keys($this->emailClient->getAvailableTransports()), t('Invalid Mail transport')), + new Validators\InArray('default_color', array_keys($this->colorModel->getList()), t('Color invalid')), + new Validators\Integer('board_highlight_period', t('This value must be an integer')), + new Validators\Integer('board_public_refresh_interval', t('This value must be an integer')), + new Validators\Integer('board_private_refresh_interval', t('This value must be an integer')), + new Validators\GreaterThanOrEqual('board_highlight_period', t('This value must be greater or equal to %d', 0), 0), + new Validators\GreaterThanOrEqual('board_public_refresh_interval', t('This value must be greater or equal to %d', 0), 0), + new Validators\GreaterThanOrEqual('board_private_refresh_interval', t('This value must be greater or equal to %d', 0), 0), + ]); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/CurrencyValidator.php b/app/Validator/CurrencyValidator.php new file mode 100644 index 0000000..4f375c5 --- /dev/null +++ b/app/Validator/CurrencyValidator.php @@ -0,0 +1,36 @@ +execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/CustomFilterValidator.php b/app/Validator/CustomFilterValidator.php new file mode 100644 index 0000000..eb66197 --- /dev/null +++ b/app/Validator/CustomFilterValidator.php @@ -0,0 +1,74 @@ +commonValidationRules()); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate filter modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('Field required')), + new Validators\Integer('id', t('This value must be an integer')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/ExternalLinkValidator.php b/app/Validator/ExternalLinkValidator.php new file mode 100644 index 0000000..885fb05 --- /dev/null +++ b/app/Validator/ExternalLinkValidator.php @@ -0,0 +1,77 @@ +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('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('url', t('Field required')), + new Validators\MaxLength('url', t('The maximum length is %d characters', 65535), 65535), + new Validators\URL('url', t('This URL is invalid')), + new Validators\Required('title', t('Field required')), + new Validators\MaxLength('title', t('The maximum length is %d characters', 65535), 65535), + new Validators\Required('link_type', t('Field required')), + new Validators\MaxLength('link_type', t('The maximum length is %d characters', 100), 100), + new Validators\Required('dependency', t('Field required')), + new Validators\MaxLength('dependency', t('The maximum length is %d characters', 100), 100), + new Validators\Integer('id', t('This value must be an integer')), + new Validators\Required('task_id', t('Field required')), + new Validators\Integer('task_id', t('This value must be an integer')), + ); + } +} diff --git a/app/Validator/GroupValidator.php b/app/Validator/GroupValidator.php new file mode 100644 index 0000000..d867801 --- /dev/null +++ b/app/Validator/GroupValidator.php @@ -0,0 +1,71 @@ +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('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('name', t('The name is required')), + new Validators\MaxLength('name', t('The maximum length is %d characters', 191), 191), + new Validators\Unique('name', t('The name must be unique'), $this->db->getConnection(), $this->db->escapeIdentifier(GroupModel::TABLE), $this->db->escapeIdentifier('id')), + new Validators\MaxLength('external_id', t('The maximum length is %d characters', 255), 255), + new Validators\Integer('id', t('This value must be an integer')), + ); + } +} diff --git a/app/Validator/LinkValidator.php b/app/Validator/LinkValidator.php new file mode 100644 index 0000000..8e1c878 --- /dev/null +++ b/app/Validator/LinkValidator.php @@ -0,0 +1,59 @@ +db->getConnection(), LinkModel::TABLE), + new Validators\NotEquals('label', 'opposite_label', t('The labels must be different')), + )); + + 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) + { + $v = new Validator($values, array( + new Validators\Required('id', t('Field required')), + new Validators\Required('opposite_id', t('Field required')), + new Validators\Required('label', t('Field required')), + new Validators\Unique('label', t('This label must be unique'), $this->db->getConnection(), LinkModel::TABLE), + )); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/PasswordResetValidator.php b/app/Validator/PasswordResetValidator.php new file mode 100644 index 0000000..bc816f4 --- /dev/null +++ b/app/Validator/PasswordResetValidator.php @@ -0,0 +1,95 @@ +executeValidators(array('validateFields', 'validateCaptcha'), $values); + } + + /** + * 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) + { + $v = new Validator($values, $this->commonPasswordValidationRules()); + + return array( + $v->execute(), + $v->getErrors(), + ); + } + + /** + * Validate fields + * + * @access protected + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + protected function validateFields(array $values) + { + $v = new Validator($values, array( + new Validators\Required('captcha', t('This value is required')), + new Validators\Required('username', t('The username is required')), + new Validators\MaxLength('username', t('The maximum length is %d characters', 191), 191), + )); + + return array( + $v->execute(), + $v->getErrors(), + ); + } + + /** + * Validate captcha + * + * @access protected + * @param array $values Form values + * @return array + */ + protected function validateCaptcha(array $values) + { + $errors = array(); + + if (! session_exists('captcha')) { + $result = false; + } else { + $builder = new CaptchaBuilder; + $builder->setPhrase(session_get('captcha')); + $result = $builder->testPhrase(isset($values['captcha']) ? $values['captcha'] : ''); + + if (! $result) { + $errors['captcha'] = array(t('Invalid captcha')); + } + + // Invalidate captcha to avoid reuse. + session_remove('captcha'); + } + + return array($result, $errors); + } +} diff --git a/app/Validator/PredefinedTaskDescriptionValidator.php b/app/Validator/PredefinedTaskDescriptionValidator.php new file mode 100644 index 0000000..8a65822 --- /dev/null +++ b/app/Validator/PredefinedTaskDescriptionValidator.php @@ -0,0 +1,22 @@ +execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/ProjectRoleValidator.php b/app/Validator/ProjectRoleValidator.php new file mode 100644 index 0000000..4db630d --- /dev/null +++ b/app/Validator/ProjectRoleValidator.php @@ -0,0 +1,70 @@ +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 new file mode 100644 index 0000000..a0e094f --- /dev/null +++ b/app/Validator/ProjectValidator.php @@ -0,0 +1,92 @@ +db->getConnection(), ProjectModel::TABLE), + new Validators\Email('email', t('Email address invalid')), + new Validators\Unique('email', t('The project email must be unique across all projects'), $this->db->getConnection(), ProjectModel::TABLE), + ); + } + + /** + * Validate project 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) + { + if (! empty($values['identifier'])) { + $values['identifier'] = strtoupper($values['identifier']); + } + + $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(), + $v->getErrors() + ); + } + + /** + * Validate project 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) + { + if (! empty($values['identifier'])) { + $values['identifier'] = strtoupper($values['identifier']); + } + + $rules = array( + new Validators\NotEmpty('name', t('This field cannot be empty')), + new Validators\Required('id', t('This value is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/SubtaskValidator.php b/app/Validator/SubtaskValidator.php new file mode 100644 index 0000000..d3e814d --- /dev/null +++ b/app/Validator/SubtaskValidator.php @@ -0,0 +1,101 @@ +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('id', t('The subtask id is required')), + new Validators\Required('task_id', t('The task id is required')), + new Validators\Required('title', t('The title is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate API modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateApiModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The subtask id is required')), + new Validators\Required('task_id', t('The task 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\Integer('id', t('The subtask id must be an integer')), + new Validators\Integer('task_id', t('The task id must be an integer')), + new Validators\MaxLength('title', t('The maximum length is %d characters', 65535), 65535), + new Validators\Integer('user_id', t('The user id must be an integer')), + new Validators\Integer('status', t('The status must be an integer')), + new Validators\Numeric('time_estimated', t('The time must be a numeric value')), + new Validators\Numeric('time_spent', t('The time must be a numeric value')), + ); + } +} diff --git a/app/Validator/SwimlaneValidator.php b/app/Validator/SwimlaneValidator.php new file mode 100644 index 0000000..0df433f --- /dev/null +++ b/app/Validator/SwimlaneValidator.php @@ -0,0 +1,74 @@ +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('id', t('The id is required')), + new Validators\Required('name', t('The name 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\Integer('id', t('The id must be an integer')), + new Validators\Integer('project_id', t('The project id must be an integer')), + new Validators\MaxLength('name', t('The maximum length is %d characters', 191), 191) + ); + } +} diff --git a/app/Validator/TagValidator.php b/app/Validator/TagValidator.php new file mode 100644 index 0000000..dddd076 --- /dev/null +++ b/app/Validator/TagValidator.php @@ -0,0 +1,76 @@ +commonValidationRules()); + $result = $v->execute(); + $errors = $v->getErrors(); + + if ($result && $this->tagModel->exists($values['project_id'], $values['name'])) { + $result = false; + $errors = array('name' => array(t('The name must be unique'))); + } + + return array($result, $errors); + } + + /** + * Validate modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('Field required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + $result = $v->execute(); + $errors = $v->getErrors(); + + if ($result && $this->tagModel->exists($values['project_id'], $values['name'], $values['id'])) { + $result = false; + $errors = array('name' => array(t('The name must be unique'))); + } + + return array($result, $errors); + } + + /** + * Common validation rules + * + * @access protected + * @return array + */ + protected function commonValidationRules() + { + return array( + new Validators\Required('project_id', t('Field required')), + new Validators\Required('name', t('Field required')), + new Validators\MaxLength('name', t('The maximum length is %d characters', 191), 191), + ); + } +} diff --git a/app/Validator/TaskLinkValidator.php b/app/Validator/TaskLinkValidator.php new file mode 100644 index 0000000..6da257b --- /dev/null +++ b/app/Validator/TaskLinkValidator.php @@ -0,0 +1,71 @@ +db->getConnection(), TaskModel::TABLE, 'id') + ); + } + + /** + * 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('id', t('Field required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/TaskValidator.php b/app/Validator/TaskValidator.php new file mode 100644 index 0000000..33d8098 --- /dev/null +++ b/app/Validator/TaskValidator.php @@ -0,0 +1,230 @@ +dateParser->getParserFormats()), + new Validators\Date('date_started', t('Invalid date'), $this->dateParser->getParserFormats()), + new Validators\Numeric('time_spent', t('This value must be numeric')), + new Validators\Numeric('time_estimated', t('This value must be numeric')), + ); + } + + public function validateStartAndDueDate(array $values) + { + if (!empty($values['date_started']) && !empty($values['date_due'])) { + $startDate = $this->dateParser->getTimestamp($values['date_started']); + $endDate = $this->dateParser->getTimestamp($values['date_due']); + + if ($startDate > $endDate) { + return array(false, array('date_started' => array(t('The start date is greater than the end date')))); + } + } + + return array(true, array()); + } + + /** + * Validate task 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) + { + $rules = array( + new Validators\Required('project_id', t('The project is required')), + new Validators\Required('title', t('The title is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + $result = $v->execute(); + $errors = $v->getErrors(); + + if ($result) { + list($result, $errors) = $this->validateStartAndDueDate($values); + } + + return array($result, $errors); + } + + /** + * Validate task creation + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateBulkCreation(array $values) + { + $rules = array( + new Validators\Required('tasks', t('Field required')), + new Validators\Required('column_id', t('Field required')), + new Validators\Required('swimlane_id', t('Field required')), + new Validators\Integer('category_id', t('This value must be an integer')), + new Validators\Integer('swimlane_id', t('This value must be an integer')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate edit recurrence + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateEditRecurrence(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 task modification (form) + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + new Validators\Required('title', t('The title is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + $result = $v->execute(); + $errors = $v->getErrors(); + + if ($result) { + list($result, $errors) = $this->validateStartAndDueDate($values); + } + + return array($result, $errors); + } + + /** + * Validate task modification (Api) + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateApiModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + $result = $v->execute(); + $errors = $v->getErrors(); + + if ($result) { + list($result, $errors) = $this->validateStartAndDueDate($values); + } + + return array($result, $errors); + } + + /** + * Validate project modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateProjectModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The id is required')), + new Validators\Required('project_id', t('The project is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate task email creation + * + * @access public + * @param array $values Required parameters to save an action + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateEmailCreation(array $values) + { + $rules = array( + new Validators\Required('subject', t('This field is required')), + new Validators\Required('emails', t('This field is required')), + ); + + $v = new Validator($values, $rules); + + return array( + $v->execute(), + $v->getErrors() + ); + } +} diff --git a/app/Validator/UserValidator.php b/app/Validator/UserValidator.php new file mode 100644 index 0000000..68772ac --- /dev/null +++ b/app/Validator/UserValidator.php @@ -0,0 +1,135 @@ +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')), + new Validators\InArray('theme', array_keys($this->themeModel->getThemes()), t('This theme is invalid')), + new Validators\InArray('role', array_keys($this->role->getApplicationRoles()), t('This role is invalid')), + new Validators\Timezone('timezone', t('This timezone is invalid')), + new Validators\InArray('language', array_keys($this->languageModel->getLanguages()), t('This language is invalid')), + ); + } + + /** + * Validate user 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) + { + $rules = array( + new Validators\Required('username', t('The username is required')), + ); + + if (isset($values['is_ldap_user']) && $values['is_ldap_user'] == 1) { + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + } else { + $v = new Validator($values, array_merge($rules, $this->commonValidationRules(), $this->commonPasswordValidationRules())); + } + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate user modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The user id is required')), + new Validators\Required('username', t('The username is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate user API modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validateApiModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The user id is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonValidationRules())); + + return array( + $v->execute(), + $v->getErrors() + ); + } + + /** + * Validate password modification + * + * @access public + * @param array $values Form values + * @return array $valid, $errors [0] = Success or not, [1] = List of errors + */ + public function validatePasswordModification(array $values) + { + $rules = array( + new Validators\Required('id', t('The user id is required')), + new Validators\Required('current_password', t('The current password is required')), + ); + + $v = new Validator($values, array_merge($rules, $this->commonPasswordValidationRules())); + + if ($v->execute()) { + if (! $this->userSession->isAdmin() && $values['id'] != $this->userSession->getId()) { + return array(false, array('current_password' => array('Invalid User ID'))); + } + + if ($this->authenticationManager->passwordAuthentication($this->userSession->getUsername(), $values['current_password'], false)) { + return array(true, array()); + } else { + return array(false, array('current_password' => array(t('Wrong password')))); + } + } + + return array(false, $v->getErrors()); + } +} diff --git a/app/check_setup.php b/app/check_setup.php new file mode 100644 index 0000000..e5cdc46 --- /dev/null +++ b/app/check_setup.php @@ -0,0 +1,49 @@ +isEnvironmentVariableDefined()) { + $dbSettings = $dbUrlParser->getSettings(); + + define('DB_DRIVER', $dbSettings['driver']); + define('DB_USERNAME', $dbSettings['username']); + define('DB_PASSWORD', $dbSettings['password']); + define('DB_HOSTNAME', $dbSettings['hostname']); + define('DB_PORT', $dbSettings['port']); + define('DB_NAME', $dbSettings['database']); +} + +$config_file = implode(DIRECTORY_SEPARATOR, array(__DIR__, '..', 'data', 'config.php')); + +if (file_exists($config_file)) { + require $config_file; +} + +$config_file = implode(DIRECTORY_SEPARATOR, array(__DIR__, '..', 'config.php')); + +if (file_exists($config_file)) { + require $config_file; +} + +require __DIR__.'/constants.php'; +require __DIR__.'/check_setup.php'; + +$container = new Pimple\Container; +$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()); +$container->register(new Kanboard\ServiceProvider\ClassProvider()); +$container->register(new Kanboard\ServiceProvider\EventDispatcherProvider()); +$container->register(new Kanboard\ServiceProvider\GroupProvider()); +$container->register(new Kanboard\ServiceProvider\UserProvider()); +$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()); +$container->register(new Kanboard\ServiceProvider\ObjectStorageProvider()); +$container->register(new Kanboard\ServiceProvider\PluginProvider()); diff --git a/app/constants.php b/app/constants.php new file mode 100644 index 0000000..95453fa --- /dev/null +++ b/app/constants.php @@ -0,0 +1,193 @@ + 1, 'k2' => 2], ['k1' => 3, 'k2' => 4], ['k1' => 1, 'k2' => 5] + * ] + * + * array_column_index($input, 'k1') will returns: + * + * [ + * 1 => [['k1' => 1, 'k2' => 2], ['k1' => 1, 'k2' => 5]], + * 3 => [['k1' => 3, 'k2' => 4]], + * ] + * + * @param array $input + * @param string $column + * @return array + */ +function array_column_index(array &$input, $column) +{ + $result = array(); + + foreach ($input as &$row) { + if (isset($row[$column])) { + $result[$row[$column]][] = $row; + } + } + + return $result; +} + +/** + * 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 = [ + * ['column' => 2], ['column' => 3] + * ] + * + * array_column_sum($input, 'column') returns 5 + * + * @param array $input + * @param string $column + * @return double + */ +function array_column_sum(array &$input, $column) +{ + $sum = 0.0; + + foreach ($input as &$row) { + if (isset($row[$column])) { + $sum += (float) $row[$column]; + } + } + + return $sum; +} + +/** + * Build version number from git-archive output + * + * @param string $ref + * @param string $commit_hash + * @return string + */ +function build_app_version($ref, $commit_hash) +{ + if ($ref !== '$Format:%d$') { + $tag = preg_replace('/\s*\(.*tag:\sv([^,]+).*\)/i', '\1', $ref); + + if (!is_null($tag) && $tag !== $ref) { + return $tag; + } + } + + if ($commit_hash !== '$Format:%H$') { + return 'main.'.$commit_hash; + } elseif (file_exists(__DIR__ . '/version.txt')) { + return rtrim(file_get_contents(__DIR__ . '/version.txt')); + } + + return 'main.unknown_revision'; +} + +/** + * Get upload max size. + * + * @return string + */ +function get_upload_max_size() +{ + $upload_max_filesize = convert_php_size_to_bytes(ini_get('upload_max_filesize')); + $post_max_size = convert_php_size_to_bytes(ini_get('post_max_size')); + + if ($post_max_size == 0) { + return $upload_max_filesize; + } + + if ($upload_max_filesize == 0) { + return $post_max_size; + } + + return min($post_max_size, $upload_max_filesize); +} + +/** + * Get the number of bytes from PHP size + * + * @param integer $value PHP size (example: 2M) + * @return integer + */ +function convert_php_size_to_bytes($value) +{ + // Remove the non-unit characters from the size + $unit = preg_replace('/[^bkmgtpezy]/i', '', $value); + + // Remove the non-numeric characters from the size + $size = preg_replace('/[^0-9\.]/', '', $value); + + switch (strtoupper($unit)) { + case 'G': + $size *= 1024; + // no break + case 'M': + $size *= 1024; + // no break + case 'K': + $size *= 1024; + } + + return $size; +} + +/** + * Get file extension + * + * @param $filename + * @return string + */ +function get_file_extension($filename) +{ + return strtolower(pathinfo($filename, PATHINFO_EXTENSION)); +} + +/** + * Translate a string + * + * @return string + */ +function t() +{ + return call_user_func_array(array(Translator::getInstance(), 'translate'), func_get_args()); +} + +/** + * Translate a string with no HTML escaping + * + * @return string + */ +function e() +{ + return call_user_func_array(array(Translator::getInstance(), 'translateNoEscaping'), func_get_args()); +} + +/** + * Translate a number + * + * @param mixed $value + * @return string + */ +function n($value) +{ + return Translator::getInstance()->number($value); +} + +/** + * Sanitize a file path + * + * @param string $path + * @return string|false + */ +function sanitize_path(string $path): string|false +{ + // Handle empty path + if (empty($path)) { + return false; + } + + $dirSeparator = '/'; + + // Get Windows drive letter (C:/ or C:\) + $driveLetter = ''; + if (preg_match('/^([a-zA-Z]:)([\/\\\].*)$/', $path, $matches)) { + $driveLetter = $matches[1]; + $path = $matches[2]; + $dirSeparator = '\\'; + } + + // If path is not absolute, make it relative to current working directory + if ($driveLetter === '' && substr($path, 0, 1) !== '/') { + $path = getcwd() . $dirSeparator . $path; + } + + // Split path into components + $parts = explode($dirSeparator, $path); + $resolved = []; + + foreach ($parts as $part) { + // Skip empty parts (caused by multiple slashes) + if ($part === '' || $part === '.') { + continue; + } + + // Handle parent directory + if ($part === '..') { + if (count($resolved) > 0) { + array_pop($resolved); + } + // If we're at root and encounter .., ignore it + continue; + } + + // Add normal directory/file component + $resolved[] = $part; + } + + // Reconstruct the path + $normalized = ($driveLetter !== '' ? $driveLetter . $dirSeparator : $dirSeparator) . implode($dirSeparator, $resolved); + + // Handle root case + if ($normalized === $dirSeparator) { + return $dirSeparator; + } + + return $normalized; +} diff --git a/assets/css/auto.min.css b/assets/css/auto.min.css new file mode 100644 index 0000000..746bffc --- /dev/null +++ b/assets/css/auto.min.css @@ -0,0 +1 @@ +:root{--body-background-color:#FFF;--header-background-color:#fbfbfb;--color-primary:#333;--color-light:#999;--color-lighter:#dedede;--color-dark:#000;--color-medium:#555;--color-error:#b94a48;--link-color-primary:#36C;--link-color-focus:#DF5353;--link-color-hover:#333;--alert-color-default:#c09853;--alert-color-success:#468847;--alert-color-error:#b94a48;--alert-color-info:#3a87ad;--alert-color-normal:#333;--alert-background-color-default:#fcf8e3;--alert-background-color-success:#dff0d8;--alert-background-color-error:#f2dede;--alert-background-color-info:#d9edf7;--alert-background-color-normal:#f0f0f0;--alert-border-color-default:#fbeed5;--alert-border-color-success:#d6e9c6;--alert-border-color-error:#eed3d7;--alert-border-color-info:#bce8f1;--alert-border-color-normal:#ddd;--button-default-color:#333;--button-default-background-color:#f5f5f5;--button-default-border-color:#ddd;--button-default-color-focus:#000;--button-default-background-color-focus:#fafafa;--button-default-border-color-focus:#bbb;--button-primary-color:#fff;--button-primary-background-color:#4d90fe;--button-primary-border-color:#3079ed;--button-primary-color-focus:#fff;--button-primary-background-color-focus:#357ae8;--button-primary-border-color-focus:#3079ed;--button-danger-color:#fff;--button-danger-background-color:#d14836;--button-danger-border-color:#b0281a;--button-danger-color-focus:#fff;--button-danger-background-color-focus:#c53727;--button-danger-border-color-focus:#b0281a;--button-disabled-color:#ccc;--button-disabled-background-color:#f7f7f7;--button-disabled-border-color:#ccc;--table-header-background-color:#fbfbfb;--table-nth-background-color:#fefefe;--table-border-color:#eee;--avatar-color-letter:#fff;--activity-title-color:#000;--activity-title-border-color:#efefef;--activity-event-background-color:#fafafa;--activity-event-hover-color:#fff8dc;--user-mention-color:#000;--board-task-limit-color:#DF5353;--table-list-header-border-color:#e5e5e5;--table-list-header-background-color:#fbfbfb;--table-list-nth-background-color:#fefefe;--table-list-border-color:#e5e5e5;--table-list-row-hover-border-color:#ffeb8e;--table-list-row-background-color:#fff8dc;--sidebar-border-color:#efefef;--dropdown-background-color:#fff;--dropdown-border-color:#b2b2b2;--dropdown-li-border-color:#f8f8f8;--input-addon-background-color:rgba(147,128,108,.1);--input-addon-color:#666;--views-background-color:#fafafa;--views-border-color:#ddd;--views-active-color:#000;--input-focus-color:#000;--input-focus-border-color:rgba(82,168,236,.8);--input-focus-shadow-color:rgba(82,168,236,.6);--input-background-color:#fff;--input-border-color:#ccc;--input-placeholder-color:#dedede;--tooltip-background-color:#fff;--tooltip-border-color:#ddd;--tooltip-shadow-color:#aaa;--panel-background-color:#fcfcfc;--panel-border-color:#ddd;--draggable-item-selected-background-color:#fff;--draggable-item-selected-border-color:#666;--draggable-item-hover-background-color:#FEFFF2;--draggable-row-handle-color:#dedede;--draggable-placeholder-background-color:#fafafa;--draggable-placeholder-border-color:#000;--task-list-icons-color:#999;--form-help-color:brown;--form-error-color:#b94a48;--comment-title-border-color:#eee;--comment-nth-background-color:#fbfbfb;--comment-highlighted-background-color:#fff8dc;--comment-highlighted-hover-background-color:#fff8dc;--comment-highlighted-border-color:#ffeb8e}html{color-scheme:light}@media (prefers-color-scheme:dark){:root{--body-background-color:#222;--header-background-color:#222;--color-primary:#a0a0a0;--color-light:#a0a0a0;--color-lighter:#efefef;--color-dark:#000;--color-medium:#4f4c4c;--color-error:#b94a48;--link-color-primary:#aaa;--link-color-focus:#ddd;--link-color-hover:#ddd;--alert-color-default:#efefef;--alert-color-success:#def6de;--alert-color-error:#de9393;--alert-color-info:#3a87ad;--alert-color-normal:#333;--alert-background-color-default:#333;--alert-background-color-success:#304b27;--alert-background-color-error:#500606;--alert-background-color-info:#d9edf7;--alert-background-color-normal:#f0f0f0;--alert-border-color-default:#444;--alert-border-color-success:#3c621b;--alert-border-color-error:#7e0315;--alert-border-color-info:#bce8f1;--alert-border-color-normal:#ddd;--button-default-color:#333;--button-default-background-color:#f5f5f5;--button-default-border-color:#ddd;--button-default-color-focus:#000;--button-default-background-color-focus:#fafafa;--button-default-border-color-focus:#bbb;--button-primary-color:#efefef;--button-primary-background-color:#333;--button-primary-border-color:#444;--button-primary-color-focus:#fff;--button-primary-background-color-focus:#555;--button-primary-border-color-focus:#888;--button-danger-color:#fff;--button-danger-background-color:#d14836;--button-danger-border-color:#b0281a;--button-danger-color-focus:#fff;--button-danger-background-color-focus:#c53727;--button-danger-border-color-focus:#b0281a;--button-disabled-color:#ccc;--button-disabled-background-color:#f7f7f7;--button-disabled-border-color:#ccc;--table-header-background-color:#1a1a1a;--table-nth-background-color:#2d2c2c;--table-border-color:rgba(147,128,108,.25);--avatar-color-letter:#fff;--activity-title-color:#e3e2e2;--activity-title-border-color:#efefef;--activity-event-background-color:#313131;--activity-event-hover-color:#000;--user-mention-color:#fff;--board-task-limit-color:#DF5353;--table-list-header-border-color:rgba(147,128,108,.25);--table-list-header-background-color:rgb(59,59,59);--table-list-nth-background-color:#2d2c2c;--table-list-border-color:rgba(147,128,108,.25);--table-list-row-hover-border-color:rgba(147,128,108,.25);--table-list-row-background-color:#434343;--sidebar-border-color:rgba(147,128,108,.25);--dropdown-background-color:#222;--dropdown-border-color:#000;--dropdown-li-border-color:#555;--input-addon-background-color:#1a1a1a;--input-addon-color:rgba(147,128,108,.25);--views-background-color:#1a1a1a;--views-border-color:rgba(147,128,108,.25);--views-active-color:#949494;--input-focus-color:#e6edf3;--input-focus-border-color:rgba(82,168,236,.8);--input-focus-shadow-color:rgba(82,168,236,.6);--input-background-color:rgb(59,59,59);--input-border-color:#777575;--input-placeholder-color:#666;--tooltip-background-color:#333;--tooltip-border-color:#555;--tooltip-shadow-color:#111;--panel-background-color:#2c2c2c;--panel-border-color:#000;--draggable-item-selected-background-color:#222;--draggable-item-selected-border-color:#111;--draggable-item-hover-background-color:#555;--draggable-row-handle-color:#444;--draggable-placeholder-background-color:#444;--draggable-placeholder-border-color:#666;--task-list-icons-color:#ccc;--form-help-color:#a8a12f;--form-error-color:#f2332f;--comment-title-border-color:#eee;--comment-nth-background-color:#2b2a2a;--comment-highlighted-background-color:#2b2901;--comment-highlighted-hover-background-color:#000;--comment-highlighted-border-color:#c09e05}html{color-scheme:dark}.select2-dropdown,.select2-close-mask{background-color:var(--input-background-color)}.select2-container--default .select2-selection--multiple,.select2-container--default .select2-selection--single{background-color:var(--input-background-color);border-color:var(--input-border-color)}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:var(--input-focus-border-color)}.select2-container--default .select2-selection--single .select2-selection__rendered,.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#fff}.task-board-title{color:#000}.task-summary-column a,.task-summary-column a:hover{color:#000}}h1,li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0}body{background-color:var(--body-background-color);font-size:100%;padding-bottom:10px;color:var(--color-primary);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility;overflow-x:hidden}small{font-size:.8em}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,.1);border-bottom:1px solid rgba(255,255,255,.3)}.page{margin-left:10px;margin-right:10px}.margin-top{margin-top:20px}.margin-bottom{margin-bottom:20px}.pull-right{text-align:right;margin-left:auto}ul.no-bullet li{list-style-type:none;margin-left:0}#app-loading-icon{position:fixed;right:3px;bottom:3px}.assign-me{vertical-align:bottom}a{color:var(--link-color-primary);border:none}a:focus{color:var(--link-color-focus);outline:0;text-decoration:none}a:hover{color:var(--link-color-hover);text-decoration:none}a .fa{color:var(--color-primary);padding-right:3px;text-decoration:none}h1,h2,h3{font-weight:400;color:var(--color-primary)}h1{font-size:1.5em}h2{font-size:1.4em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px}table.table-fixed{table-layout:fixed;white-space:nowrap}table.table-fixed th{overflow:hidden}table.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.table-small{font-size:.8em}table.table-striped tr:nth-child(odd){background:var(--table-nth-background-color)}@media (max-width:768px){table.table-scrolling{overflow-x:auto;display:inline-block;vertical-align:top;max-width:100%;white-space:nowrap}}table th{text-align:left;padding:.5em 3px;border:1px solid var(--table-border-color);background-color:var(--table-header-background-color)}table th a{text-decoration:none;color:var(--color-primary)}table th a:focus,table th a:hover{text-decoration:underline}table td{border:1px solid var(--table-border-color);padding:.5em 3px;vertical-align:top}table td li{margin-left:20px}table td .color-picker-square{display:inline-block;width:12px;height:12px;border:1px solid #000}.task-table a{color:#000}.column-1{width:1%}.column-2{width:2%}.column-3{width:3%}.column-4{width:4%}.column-5{width:5%}.column-6{width:6%}.column-7{width:7%}.column-8{width:8%}.column-9{width:9%}.column-10{width:10%}.column-11{width:11%}.column-12{width:12%}.column-13{width:13%}.column-14{width:14%}.column-15{width:15%}.column-16{width:16%}.column-17{width:17%}.column-18{width:18%}.column-19{width:19%}.column-20{width:20%}.column-21{width:21%}.column-22{width:22%}.column-23{width:23%}.column-24{width:24%}.column-25{width:25%}.column-26{width:26%}.column-27{width:27%}.column-28{width:28%}.column-29{width:29%}.column-30{width:30%}.column-31{width:31%}.column-32{width:32%}.column-33{width:33%}.column-34{width:34%}.column-35{width:35%}.column-36{width:36%}.column-37{width:37%}.column-38{width:38%}.column-39{width:39%}.column-40{width:40%}.column-41{width:41%}.column-42{width:42%}.column-43{width:43%}.column-44{width:44%}.column-45{width:45%}.column-46{width:46%}.column-47{width:47%}.column-48{width:48%}.column-49{width:49%}.column-50{width:50%}.column-51{width:51%}.column-52{width:52%}.column-53{width:53%}.column-54{width:54%}.column-55{width:55%}.column-56{width:56%}.column-57{width:57%}.column-58{width:58%}.column-59{width:59%}.column-60{width:60%}.column-61{width:61%}.column-62{width:62%}.column-63{width:63%}.column-64{width:64%}.column-65{width:65%}.column-66{width:66%}.column-67{width:67%}.column-68{width:68%}.column-69{width:69%}.column-70{width:70%}.column-71{width:71%}.column-72{width:72%}.column-73{width:73%}.column-74{width:74%}.column-75{width:75%}.column-76{width:76%}.column-77{width:77%}.column-78{width:78%}.column-79{width:79%}.column-80{width:80%}.column-81{width:81%}.column-82{width:82%}.column-83{width:83%}.column-84{width:84%}.column-85{width:85%}.column-86{width:86%}.column-87{width:87%}.column-88{width:88%}.column-89{width:89%}.column-90{width:90%}.column-91{width:91%}.column-92{width:92%}.column-93{width:93%}.column-94{width:94%}.column-95{width:95%}.column-96{width:96%}.column-97{width:97%}.column-98{width:98%}.column-99{width:99%}.column-100{width:100%}.draggable-row-handle{cursor:move;color:var(--draggable-row-handle-color)}.draggable-row-handle:hover{color:var(--color-primary)}tr.draggable-item-selected{background:var(--draggable-item-selected-background-color);border:2px solid var(--draggable-item-selected-border-color);box-shadow:4px 2px 10px -4px rgba(0,0,0,.55)}tr.draggable-item-selected td{border-top:none;border-bottom:none}tr.draggable-item-selected td:first-child{border-left:none}tr.draggable-item-selected td:last-child{border-right:none}.table-stripped tr.draggable-item-hover,.table-stripped tr.draggable-item-hover{background:var(--draggable-item-hover-background-color)}.table-list{font-size:.85em;margin-bottom:20px}.table-list-header{background:var(--table-list-header-background-color);border:1px solid var(--table-list-header-border-color);border-radius:5px 5px 0 0;line-height:28px;padding-left:3px;padding-right:3px}.table-list-header a{color:var(--color-primary);font-weight:500;text-decoration:none;margin-right:10px}.table-list-header a:hover,.table-list-header a:focus{color:#767676}.table-list-header .table-list-header-count{color:#767676;display:inline-block;float:left}.table-list-header .table-list-header-menu{text-align:right}.table-list-row{padding-left:3px;padding-right:3px;border-bottom:1px solid var(--table-list-border-color);border-right:1px solid var(--table-list-border-color)}.table-list-row.table-border-left{border-left:1px solid var(--table-list-border-color)}.table-list-row:nth-child(odd){background:var(--table-list-nth-background-color)}.table-list-row:last-child{border-radius:0 0 5px 5px}.table-list-row:hover{background:var(--table-list-row-background-color);border-bottom:1px solid var(--table-list-row-hover-border-color);border-right:1px solid var(--table-list-row-hover-border-color)}.table-list-row .table-list-title{font-weight:500;line-height:23px}.table-list-row .table-list-title.status-closed{text-decoration:line-through;margin-right:10px}.table-list-row .table-list-title.status-closed a{font-style:italic}.table-list-row .table-list-title a{color:var(--color-primary);text-decoration:none}.table-list-row .table-list-title a:hover,.table-list-row .table-list-title a:focus{text-decoration:underline}.table-list-row .table-list-details{color:#999;font-weight:300;line-height:20px}.table-list-row .table-list-details span{margin-left:5px}.table-list-row .table-list-details span:first-child{margin-left:0}.table-list-row .table-list-details li{display:inline;list-style-type:none}.table-list-row .table-list-details li:after{content:', '}.table-list-row .table-list-details li:last-child:after{content:''}.table-list-row .table-list-details strong{font-weight:400;color:#555}.table-list-row .table-list-details-with-icons{float:left}@media (max-width:768px){.table-list-row .table-list-details-with-icons{float:none}}.table-list-row .table-list-icons{font-size:.8em;text-align:right;line-height:30px}@media (max-width:768px){.table-list-row .table-list-icons{text-align:left;line-height:20px}}.table-list-row .table-list-icons span{margin-left:5px}.table-list-row .table-list-icons a{text-decoration:none}.table-list-row .table-list-icons a:hover{color:var(--color-primary)}.table-list-row .table-list-icons a:hover i{color:var(--color-primary)}.table-list-category{font-size:.9em;font-weight:500;color:#000;padding:1px 2px 1px 2px;border-radius:3px;background:#fcfcfc;border:1px solid #ccc}.table-list-category a{text-decoration:none;color:#000}.table-list-category a:hover{color:#36c}fieldset{border:1px solid #ddd;margin-top:10px}legend{font-weight:500;font-size:1.2em}label{cursor:pointer;display:block;margin-top:10px;font-weight:400}input,textarea{font-family:sans-serif;background-color:var(--input-background-color)}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]:not(.input-addon-field){color:var(--color-light);border:1px solid var(--input-border-color);width:300px;max-width:95%;font-size:1em;height:25px;padding-bottom:0;padding-left:4px;-webkit-appearance:none;-moz-appearance:none}input[type="number"]::placeholder,input[type="date"]::placeholder,input[type="email"]::placeholder,input[type="password"]::placeholder,input[type="text"]:not(.input-addon-field)::placeholder{color:var(--input-placeholder-color)}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus{color:var(--input-focus-color);border-color:var(--input-focus-border-color);outline:0;box-shadow:0 0 8px var(--input-focus-shadow-color)}input[type="number"]{width:70px}input[type="text"]:not(.input-addon-field).form-numeric{width:70px}input[type="text"]:not(.input-addon-field).form-datetime,input[type="text"]:not(.input-addon-field).form-date{width:150px}input[type="text"]:not(.input-addon-field).form-input-large{width:400px}input[type="text"]:not(.input-addon-field).form-input-small{width:150px}textarea:focus{color:var(--input-focus-color);border-color:var(--input-focus-border-color);outline:0;box-shadow:0 0 8px var(--input-focus-shadow-color)}textarea{padding:4px;border:1px solid var(--input-border-color);width:400px;max-width:99%;height:200px;font-size:1em}textarea::placeholder{color:var(--input-placeholder-color)}select{font-size:1em;max-width:95%}select:focus{outline:0}select[multiple]{width:300px}.tag-autocomplete{width:400px}span.select2-container{margin-top:2px}.form-actions{padding-top:20px;clear:both}.form-required{color:red;padding-left:5px;font-weight:700}@media (max-width:480px){.form-required{display:none}}input[type="text"].form-max-width{width:100%}input.form-error,textarea.form-error{border:2px solid var(--form-error-color)}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid var(--form-error-color)}.form-errors{color:var(--form-error-color);list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:.8em;color:var(--form-help-color);margin-bottom:15px}.form-inline{padding:0;margin:0;border:none}.form-inline label{display:inline;padding-right:3px}.form-inline input,.form-inline select{margin:0 15px 0 0}.form-inline .form-required{display:none}.form-inline .form-actions{display:inline-block}.form-inline .js-submit-buttons-rendered{display:inline-block}.form-inline-group{display:inline}.form-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.form-columns .form-column{margin-right:25px;flex-grow:1}.form-columns fieldset{margin-top:0}.form-login{max-width:350px;margin:5% auto 0}@media (max-width:480px){.form-login{margin-left:5px}}.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-weight:700}.reset-password{margin-top:20px;margin-bottom:20px}.reset-password a{color:var(--color-light)}.input-addon{display:flex}.input-addon-field{flex:1;font-size:1em;color:var(--color-light);margin:0;-webkit-appearance:none;-moz-appearance:none}.input-addon-field:first-child{border-radius:5px 0 0 5px}.input-addon-field:last-child{border-radius:0 5px 5px 0}.input-addon-item{background-color:var(--input-addon-background-color);color:var(--input-addon-color);font:inherit;font-weight:400}.input-addon-item:first-child{border-radius:5px 0 0 5px}.input-addon-item:last-child{border-radius:0 5px 5px 0}@media (max-width:480px){.input-addon-item .dropdown .fa-caret-down{display:none}}.input-addon-field,.input-addon-item{border:1px solid rgba(147,128,108,.25);padding:4px .75em}.input-addon-field:not(:first-child),.input-addon-item:not(:first-child){border-left:0}.input-addon .input-addon-field{flex:1 1 auto;width:1%!important}@media (max-width:400px){.input-addon-item{padding:3px}}.icon-success{color:#468847}.icon-error{color:#b94a48}.icon-fade-out{opacity:1;animation:icon-fadeout 5s linear forwards}@keyframes icon-fadeout{0%{opacity:1}100%{opacity:0}}.alert{padding:8px 35px 8px 14px;margin-top:5px;margin-bottom:5px;color:var(--alert-color-default);background-color:var(--alert-background-color-default);border:1px solid var(--alert-border-color-default);border-radius:4px}.alert-success{color:var(--alert-color-success);background-color:var(--alert-background-color-success);border-color:var(--alert-border-color-success)}.alert-error{color:var(--alert-color-error);background-color:var(--alert-background-color-error);border-color:var(--alert-border-color-error)}.alert-info{color:var(--alert-color-info);background-color:var(--alert-background-color-info);border-color:var(--alert-border-color-info)}.alert-normal{color:var(--alert-color-normal);background-color:var(--alert-background-color-normal);border-color:var(--alert-border-color-normal)}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.alert-fade-out{text-align:center;position:fixed;bottom:0;left:20%;width:60%;padding-top:5px;padding-bottom:5px;margin-bottom:0;border-width:1px 0 0;border-radius:4px 4px 0 0;z-index:9999;opacity:1;animation:fadeout 5s linear forwards}@keyframes fadeout{0%{opacity:1}100%{opacity:0;visibility:hidden}}a.btn{text-decoration:none}.btn{-webkit-appearance:none;-moz-appearance:none;font-size:1.2em;font-weight:400;cursor:pointer;display:inline-block;border-radius:2px;padding:3px 10px;margin:0;border:1px solid var(--button-default-border-color);background:var(--button-default-background-color);color:var(--button-default-color)}.btn:hover,.btn:focus{border-color:var(--button-default-border-color-focus);background:var(--button-default-background-color-focus);color:var(--button-default-color-focus)}.btn-red{border-color:var(--button-danger-border-color);background:var(--button-danger-background-color);color:var(--button-danger-color)}.btn-red:hover,.btn-red:focus{border-color:var(--button-danger-border-color-focus);background:var(--button-danger-background-color-focus);color:var(--button-danger-color-focus)}.btn-blue{border-color:var(--button-primary-border-color);background:var(--button-primary-background-color);color:var(--button-primary-color)}.btn-blue:hover,.btn-blue:focus{border-color:var(--button-primary-border-color-focus);background:var(--button-primary-background-color-focus);color:var(--button-primary-color-focus)}.btn:disabled{color:var(--button-disabled-color);border-color:var(--button-disabled-border-color);background:var(--button-disabled-background-color)}.buttons-header{font-size:.8em;margin-top:5px;margin-bottom:15px}.tooltip i.fa{cursor:pointer}.tooltip .fa-info-circle{color:var(--color-light)}#tooltip-container{padding:5px;background:var(--tooltip-background-color);border:1px solid var(--tooltip-border-color);border-radius:4px;box-shadow:0 6px 12px var(--tooltip-shadow-color);position:absolute;min-width:350px}#tooltip-container .markdown p:last-child{margin-bottom:0}#tooltip-container .tooltip-large{width:600px}h2 .dropdown ul{display:none}.dropdown{display:inline;position:relative}.dropdown ul{display:none}.dropdown-smaller{font-size:.85em}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:var(--dropdown-background-color);border:1px solid var(--dropdown-border-color);border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.dropdown-submenu-open li{display:block;margin:0;padding:8px 10px;font-size:.9em;border-bottom:1px solid var(--dropdown-li-border-color);cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.dropdown-submenu-open li:last-child{border:none}.dropdown-submenu-open li:not(.no-hover):hover{background:#4078C0;color:#fff}.dropdown-submenu-open li:hover a{color:#fff}.dropdown-submenu-open li:hover i{color:#fff}.dropdown-submenu-open a{text-decoration:none;color:var(--color-primary)}.dropdown-submenu-open a:focus{text-decoration:underline}.dropdown-menu-link-text,.dropdown-menu-link-icon{color:var(--color-primary);text-decoration:none}.dropdown-menu-link-icon{display:inline-flex}.dropdown-menu-link-text:hover{text-decoration:underline}td a.dropdown-menu strong{color:var(--color-primary)}td a.dropdown-menu strong i{color:var(--color-primary)}td a.dropdown-menu i{color:#dedede}td a.dropdown-menu:hover strong{color:#555}td a.dropdown-menu:hover strong i{color:#555}td a.dropdown-menu:hover i{color:#333}.accordion-title{font-size:1.2em;cursor:pointer;margin-top:10px}.accordion-content{margin-top:15px;margin-bottom:25px}#select-dropdown-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:var(--dropdown-background-color);list-style:none;border:1px solid var(--dropdown-border-color);border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,.175);overflow:scroll}.select-dropdown-menu-item{white-space:nowrap;overflow:hidden;padding:3px 10px;color:var(--color-medium);cursor:pointer;border-bottom:1px solid var(--dropdown-li-border-color);line-height:1.5em;font-weight:400}.select-dropdown-menu-item.active{color:#fff;background:#428bca}.select-dropdown-menu-item:last-child{border:none}.select-dropdown-input-container{position:relative;border:1px solid var(--input-border-color);border-radius:5px;background-color:var(--input-background-color);max-width:300px}.select-dropdown-input-container input.select-dropdown-input{margin:0 0 0 5px;border:none;height:23px;width:270px}.select-dropdown-input-container input.select-dropdown-input:focus{border:none;box-shadow:none}.select-dropdown-input-container .select-dropdown-chevron{color:var(--color-medium);position:absolute;top:4px;right:5px;cursor:pointer}.select-dropdown-input-container .select-loading-icon{color:var(--color-medium);position:absolute;top:4px;right:5px}#suggest-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,.175)}.suggest-menu-item{white-space:nowrap;padding:3px 10px;color:var(--color-primary);font-weight:700;cursor:pointer}.suggest-menu-item.active{color:#fff;background:#428bca}.suggest-menu-item.active small{color:#fff}.suggest-menu-item small{color:var(--color-light);font-weight:400}#modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.9);overflow:auto;z-index:100}#modal-box{position:fixed;max-height:calc(100% - 30px);top:2%;left:50%;transform:translateX(-50%);background:var(--body-background-color);overflow:auto;border-radius:5px}#modal-content{padding:0 5px 5px}#modal-header{text-align:right;padding-right:5px}#modal-close-button{color:var(--color-primary)}#modal-close-button:hover{color:var(--color-error)}.pagination{text-align:center;font-size:.9em}.pagination-showing{margin-right:5px;padding-right:5px;border-right:1px solid #999}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}header{display:flex;flex-wrap:wrap;padding:5px 10px;margin-bottom:5px;border-bottom:1px solid #dedede;background-color:var(--header-background-color)}header .title-container{flex:1;min-width:300px}@media (max-width:480px){header .title-container{order:3}}header .board-selector-container{min-width:320px;display:flex;align-items:center}@media (max-width:480px){header .board-selector-container{order:2;min-width:300px}header .board-selector-container input[type=text]{max-width:280px}}header .menus-container{min-width:120px;display:flex;align-items:center;justify-content:flex-end}@media (max-width:480px){header .menus-container{order:1;margin-bottom:5px;margin-left:auto}}header h1{font-size:1.5em}header h1 .tooltip{opacity:.3;font-size:.7em}a i.web-notification-icon{color:var(--link-color-primary)}a i.web-notification-icon:focus,a i.web-notification-icon:hover{color:#000}.logo a{opacity:.5;color:#d40000;text-decoration:none}.logo span{color:var(--color-primary)}.logo a:hover{opacity:.8;color:var(--color-primary)}.logo a:focus span,.logo a:hover span{color:#d40000}.page-header{margin-bottom:20px}.page-header .dropdown{padding-right:10px}.page-header h2{margin:0;padding:0;font-weight:700;border-bottom:1px dotted #ccc}.page-header h2 a{color:var(--color-primary);text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:var(--color-light)}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.page-header li{display:inline;padding-right:15px}@media (max-width:480px){.page-header li{display:block;line-height:1.5em}}.page-header li.active a{color:var(--color-primary);text-decoration:none;font-weight:700}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}.menu-inline li{display:inline;padding-right:15px}.menu-inline li .active a{font-weight:700;color:#000;text-decoration:none}.sidebar-container{height:100%;display:flex;flex-flow:row}@media (max-width:768px){.sidebar-container{flex-flow:wrap}}.sidebar-content{padding-left:10px;flex:1 100%;max-width:85%;overflow-wrap:break-word}@media (max-width:768px){.sidebar-content{padding-left:0;order:1;max-width:100%}}@media only screen and (min-device-width:768px) and (max-device-width:1024px) and (orientation:landscape) and (-webkit-min-device-pixel-ratio:1){.sidebar-content{max-width:75%}}.sidebar{max-width:25%;min-width:230px}@media (max-width:768px){.sidebar{flex:1 auto;order:2}}.sidebar h2{margin-top:0}.sidebar>ul a{text-decoration:none;color:var(--color-light);font-weight:300}.sidebar>ul a:hover{color:var(--color-primary)}.sidebar>ul li{list-style-type:none;line-height:35px;border-bottom:1px dotted var(--sidebar-border-color);padding-left:13px}.sidebar>ul li:hover{border-left:5px solid #555;padding-left:8px}.sidebar>ul li.active{border-left:5px solid #333;padding-left:8px}.sidebar>ul li.active a{color:var(--color-primary);font-weight:400}.sidebar-icons>ul li{padding-left:0}.sidebar-icons>ul li:hover,.sidebar-icons>ul li.active{padding-left:0;border-left:none}.sidebar>ul li.active a:focus,.sidebar>ul li.active a:hover{color:var(--color-medium)}.sidebar>ul li:last-child{margin-bottom:15px}.avatar img{vertical-align:bottom}.avatar-left{float:left;margin-right:10px}.avatar-inline{display:inline-block;margin-right:3px}.avatar-48 img,.avatar-48 div{border-radius:30px}.avatar-48 .avatar-letter{line-height:48px;width:48px;font-size:25px}.avatar-20 img,.avatar-20 div{border-radius:10px}.avatar-20 .avatar-letter{line-height:20px;width:20px;font-size:11px}.avatar-letter{color:var(--avatar-color-letter);text-align:center}#file-dropzone,#screenshot-zone{position:relative;border:2px dashed #ccc;width:99%;height:250px;overflow:auto}#file-dropzone-inner,#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center;color:#aaa}#screenshot-zone.screenshot-pasted{border:2px solid #333}#file-list{margin:20px}#file-list li{list-style-type:none;padding-top:8px;padding-bottom:8px;border-bottom:1px dotted #ddd;width:95%}#file-list li .file-error{font-weight:700;color:#b94a48}.file-thumbnails{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.file-thumbnail{width:250px;border:1px solid #efefef;border-radius:5px;margin-bottom:20px;box-shadow:4px 2px 10px -6px rgba(0,0,0,.55);margin-right:15px}.file-thumbnail img{cursor:pointer;border-top-left-radius:5px;border-top-right-radius:5px}.file-thumbnail img:hover{opacity:.5}.file-thumbnail-content{padding-left:8px;padding-right:8px}.file-thumbnail-title{font-weight:700;font-size:.9em;color:var(--color-medium);overflow:hidden;text-overflow:ellipsis}.file-thumbnail-description{font-size:.8em;color:var(--color-light);margin-top:8px;margin-bottom:5px}.file-viewer{position:relative}.file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.color-picker{width:220px}.color-picker-option{height:25px}.color-picker-square{display:inline-block;width:18px;height:18px;margin-right:5px;border:1px solid #000}.color-picker-label{display:inline-block;vertical-align:bottom;padding-bottom:3px}.filter-box{max-width:100%}.action-menu{color:var(--color-primary);text-decoration:none}.action-menu:hover,.action-menu:focus{text-decoration:underline}.js-project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}.project-overview-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin-bottom:20px;font-size:1.4em}@media (max-width:480px){.project-overview-columns{display:block}}.project-overview-column{text-align:center;margin-right:3%;margin-top:5px;padding:3px 15px 3px 15px;border:1px dashed #ddd}@media (max-width:480px){.project-overview-column{text-align:left}}.project-overview-column small{color:var(--color-light)}.project-overview-column strong{color:var(--color-medium);display:block}@media (max-width:480px){.project-overview-column strong{display:inline}}.project-header{margin-bottom:8px}.project-header .dropdown-component{margin-top:4px;margin-right:5px;float:left}@media (max-width:768px){.project-header .dropdown-component{float:none}}.project-header .views-switcher-component{margin-top:4px;margin-bottom:10px;float:left}@media (max-width:768px){.project-header .views-switcher-component{float:none;margin-bottom:10px}}.project-header .filter-box-component form{margin:0}.views{margin-right:10px;margin-top:1px;font-size:.9em}@media (max-width:560px){.views{width:100%}}@media (max-width:768px){.views{margin-top:10px;font-size:1em}}@media (max-width:480px){.views{margin-top:5px}}.views li{white-space:nowrap;background:var(--views-background-color);border:1px solid var(--views-border-color);border-right:none;padding:4px 8px;display:inline}@media (max-width:560px){.views li{display:block;margin-top:5px;border-radius:5px;border:1px solid var(--views-border-color)}}.views li.active a{font-weight:700;color:var(--views-active-color);text-decoration:none}.views li:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-right:1px solid var(--views-border-color);border-top-right-radius:5px;border-bottom-right-radius:5px}.views a{color:var(--color-ligth);text-decoration:none}.views a:hover{color:var(--color-primary);text-decoration:underline}.dashboard-project-stats small{margin-right:10px;color:var(--color-light)}.dashboard-table-link{font-weight:700;color:#000;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:var(--color-light)}.public-board{margin-top:5px}.public-task{max-width:800px;margin:5px auto 0}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board tr.board-swimlane-columns-first{visibility:hidden;padding:0}#board th.board-column-header{width:240px}#board th.board-column-header-first{visibility:hidden;padding:0}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast:active),(-ms-high-contrast:none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}.board-column-expanded-header{display:flex;align-items:center}td.board-column-task-collapsed{font-weight:700;background-color:var(--table-header-background-color)}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;transform:rotate(90deg);transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon i{text-decoration:none;color:var(--link-color-primary);font-size:1.4em}.board-add-icon i:focus,.board-add-icon i:hover{text-decoration:none;color:red}.board-column-header-task-count{color:var(--color-light);font-weight:400;font-size:.85em}a.board-swimlane-toggle{text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:none}.board-task-list{min-height:60px}.board-task-list-compact{max-height:90vh;overflow-y:auto}.board-task-list .task-board:last-child{margin-bottom:0}.board-task-list-limit{background-color:var(--board-task-limit-color)}.draggable-item{cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.draggable-placeholder{border:2px dashed var(--draggable-placeholder-border-color);background:var(--draggable-placeholder-background-color);height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;word-wrap:break-word;font-size:.9em;border-radius:6px}div.task-board-recent{border-width:2px}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-board a{color:#000;text-decoration:none}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.task-board-title{margin-top:5px;margin-bottom:8px}.task-board-title a:hover{text-decoration:underline}.task-board-saving-state{opacity:.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board-avatars{text-align:right;float:right}.task-board-change-assignee{cursor:pointer}.task-board-change-assignee:hover{opacity:.6}.task-list-avatars{display:inline-block;float:left}@media (max-width:768px){.task-list-avatars{float:none;display:block}}.task-list-avatars .task-avatar-assignee{font-weight:300;color:#999}.task-list-avatars:hover .task-avatar-assignee{font-weight:400;color:#000}.task-board-icons,.task-list-icons{font-size:.8em;text-align:right}.task-board-icons a,.task-board-icons span.tooltip,.task-list-icons a,.task-list-icons span.tooltip{text-decoration:none}.task-board-icons a:hover,.task-board-icons span.tooltip:hover,.task-list-icons a:hover,.task-list-icons span.tooltip:hover{color:var(--color-primary)}.task-board-icons a:hover i,.task-board-icons span.tooltip:hover i,.task-list-icons a:hover i,.task-list-icons span.tooltip:hover i{color:var(--color-primary)}.task-board-icons .task-score,.task-list-icons .task-score{font-weight:700}.task-board-icons .flag-milestone,.task-list-icons .flag-milestone{color:green}.task-board-icons{margin-top:7px}.task-board-icons a{opacity:.5}.task-board-icons span{opacity:.5;margin-left:4px}.task-board-icons a:hover,.task-board-icons span.tooltip:hover{opacity:1;font-weight:700}.task-board-icons .task-board-icons-row{line-height:22px}.task-list-icons{line-height:22px}.task-list-icons a,.task-list-icons span,.task-list-icons i{color:var(--task-list-icons-color);opacity:1}.task-list-icons span{margin-left:5px}@media (max-width:768px){.task-list-icons{text-align:left}}.task-icon-age{display:inline-block}span.task-icon-age-total{border:1px solid #e5e5e5;padding:1px 3px 1px 3px;border-top-left-radius:3px;border-bottom-left-radius:3px}span.task-icon-age-column{border:1px solid #e5e5e5;border-left:none;margin-left:-5px;padding:1px 3px 1px 3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.task-board span.task-icon-age-total,.task-board span.task-icon-age-column{border-color:#666}.task-board-category-container{text-align:right;margin-top:8px;margin-bottom:8px}.task-board-category{border:1px solid #555;font-size:.9em;font-weight:500;color:#000;padding:1px 3px 1px 2px;border-radius:3px}.task-board-category a:hover{text-decoration:underline}.task-date{font-weight:500;color:#000}span.task-date-today{opacity:1;color:var(--link-color-primary)}span.task-date-overdue{opacity:1;color:#b94a48}.task-tags li{display:inline-block;margin:3px 3px 0 0;padding:1px 3px 1px 3px;color:var(--color-primary);border:1px solid #333;border-radius:4px}.task-summary-container .task-tags{margin-top:10px}#task-summary{margin-bottom:15px}#task-summary h2{color:var(--color-medium);font-size:1.6em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:10px}.task-summary-columns{display:flex;flex-flow:row;justify-content:space-between}@media (max-width:768px){.task-summary-columns{flex-flow:column}}.task-summary-column{color:var(--color-dark)}.task-summary-column span{color:var(--color-medium)}.task-summary-column li{line-height:23px}#external-task-view{padding:10px;margin-top:10px;margin-bottom:10px;border:1px dotted #ccc}.task-form-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.task-form-container>*{box-sizing:border-box}.task-form-container>*{width:1%}.task-form-main-column{width:60%}@media (max-width:1000px){.task-form-main-column{width:100%}}.task-form-main-column input[type="text"]{width:700px;max-width:99%}.task-form-secondary-column{max-width:250px;min-width:200px;max-height:600px;padding-left:10px;overflow:auto;width:20%}@media (max-width:1000px){.task-form-secondary-column{width:100%;max-width:99%;max-height:none}}@media (max-width:768px){.task-form-secondary-column{padding-left:0}}.task-form-secondary-column label:first-child{margin-top:0}@media (max-width:1000px){.task-form-secondary-column label:first-child{margin-top:10px}}.task-form-bottom{width:100%}.task-form-bottom label{display:inline-block}.task-form-bottom-column{display:inline-block;width:49%;margin-left:5px;margin-right:5px}.comment-sorting{text-align:right}.comment-sorting a{color:var(--color-medium);font-weight:400;text-decoration:none}.comment-sorting a:hover{color:var(--color-light)}.comment{padding:5px;margin-bottom:15px}.comment-title{border-bottom:1px dotted var(--comment-title-border-color);margin-left:55px}.comment-date{color:var(--color-light);font-weight:200}.comment-visibility{color:var(--color-light);font-weight:200}.comment-actions{text-align:right}.comment-content{margin-left:55px}.comments .text-editor textarea{height:90px}.comments .text-editor .text-editor-preview-area{height:90px}.comments .comment-highlighted{background-color:var(--comment-highlighted-background-color);border:2px solid var(--comment-highlighted-border-color)}.comments .comment-highlighted:hover{background-color:var(--comment-highlighted-hover-background-color)}.comments .comment:hover{background:var(--comment-highlighted-hover-background-color)}.comments .comment:nth-child(even):not(.comment-highlighted){background:var(--comment-nth-background-color)}.comments .comment:nth-child(even):not(.comment-highlighted):hover{background:var(--comment-highlighted-hover-background-color)}.subtask-cell{padding:4px 10px;border-top:1px dotted #dedede;border-left:1px dotted #dedede;display:table-cell;vertical-align:middle}.subtask-cell a{color:var(--color-primary);text-decoration:none}.subtask-cell a:hover,.subtask-cell a:focus{color:var(--link-color-primary)}.subtask-cell:first-child{border-left:none}@media (max-width:768px){.subtask-cell{width:90%;display:block;border-left:none}}.subtasks-table .subtask-table-td{display:flex;white-space:normal;min-width:400px}.subtasks-table .subtask-submenu{display:flex}.js-subtask-toggle-status{display:flex;text-decoration:none}.task-list-subtasks{display:table;width:100%}@media (max-width:768px){.task-list-subtasks{display:block}}.task-list-subtask{display:table-row}@media (max-width:768px){.task-list-subtask{display:block}}@media (max-width:768px){.subtask-assignee,.subtask-time-tracking-cell{display:none}}.subtask-time-tracking{white-space:normal}.task-links-table td{vertical-align:middle}.task-links-task-count{color:var(--color-light);font-weight:400}.task-link-closed{text-decoration:line-through}.task-links-table-td{display:flex}.text-editor{margin-top:10px}.text-editor a{font-size:1em;color:var(--color-light);text-decoration:none;margin-right:10px}.text-editor a:hover{color:var(--link-color-primary)}.text-editor .text-editor-preview-area{border:1px solid #dedede;width:700px;max-width:99%;height:250px;overflow:auto;padding:2px}.text-editor textarea{width:700px;max-width:98%;height:250px}.markdown{line-height:1.4em}.markdown h1{margin-top:5px;margin-bottom:10px;font-weight:700}.markdown h2{font-weight:700}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;overflow-wrap:initial;color:var(--color-medium)}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.panel{border-radius:4px;padding:8px 35px 8px 10px;margin-top:10px;margin-bottom:15px;border:1px solid var(--panel-border-color);color:var(--color-primary);background-color:var(--panel-background-color);overflow:auto}.panel li{list-style-type:square;margin-left:20px;line-height:1.35em}.activity-event{margin-bottom:15px;padding:10px}.activity-event:nth-child(even){background:var(--activity-event-background-color)}.activity-event:hover{background:var(--activity-event-hover-color)}.activity-date{margin-left:10px;font-weight:400;color:var(--color-light)}.activity-content{margin-left:55px}.activity-title{font-weight:700;color:var(--activity-title-color);border-bottom:1px dotted var(--activity-title-border-color)}.activity-description{color:var(--color-light);margin-top:10px}@media (max-width:480px){.activity-description{overflow:auto}}.activity-description li{list-style-type:circle}.activity-description ul{margin-top:10px;margin-left:20px}.user-mention-link{font-weight:700;color:var(--user-mention-color);text-decoration:none}.user-mention-link:hover{color:var(--color-medium)}.image-slideshow-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.95);overflow:auto;z-index:100}.image-slideshow-overlay img{display:block;margin:auto}.image-slideshow-overlay figcaption{color:#fff;opacity:.7;position:absolute;bottom:5px;right:15px}.slideshow-icon{color:#fff;position:absolute;font-size:2.5em;opacity:.6}.slideshow-icon:hover{opacity:.9;cursor:pointer}.slideshow-previous-icon{left:10px;top:45%}.slideshow-next-icon{right:10px;top:45%}.slideshow-close-icon{right:10px;top:10px;font-size:1.4em}.slideshow-download-icon{left:10px;bottom:10px;font-size:1.3em}.list-item-links,.list-item-actions{display:inline-block;float:left;margin-left:10px}.list-item-links a{margin:0}.list-item-action-hidden{display:none}.bulk-change-checkbox{float:left}.bulk-change-inputs{float:left;padding-left:10px}.bulk-change-inputs label{margin-top:0;margin-bottom:3px} \ No newline at end of file diff --git a/assets/css/custom_dashboard.css b/assets/css/custom_dashboard.css new file mode 100644 index 0000000..734088b --- /dev/null +++ b/assets/css/custom_dashboard.css @@ -0,0 +1,834 @@ +/* custom_dashboard.css */ + +/* Global Dashboard Background */ +body { + background: var(--login-bg) !important; + color: var(--login-text-color) !important; +} + +/* Header & Title Stack Restructuring */ +.header-left { + display: flex; + align-items: center; +} + +.header-left .logo { + font-size: 32px; + margin-right: 15px; + line-height: 1; +} + +.header-titles-stack { + display: flex; + flex-direction: column; + justify-content: center; +} + +.header-titles-stack .title { + font-size: 15px; + font-weight: 600; + color: var(--login-text-color); + margin-bottom: 2px; + line-height: 1; +} + +/* Custom Board Selector */ +.custom-board-selector { + margin: 0; + line-height: 1; +} + +.custom-board-selector .board-selector-btn { + display: inline-block; + padding: 4px 12px; + border-radius: 6px; /* Match standard dropdown shapes better */ + background: rgba(255, 255, 255, 0.5) !important; + color: #333 !important; + font-size: 13px; + border: 1px solid rgba(0,0,0,0.15); + text-decoration: none; + transition: all 0.2s; + width: 140px; /* Fixed width to align with menu */ + box-sizing: border-box; + text-align: left; +} + +.custom-board-selector .board-selector-btn i.fa-caret-down { + float: right; + margin-top: 2px; +} + +.custom-board-selector .board-selector-btn:hover { + background: rgba(0, 0, 0, 0.05) !important; +} + +ul.custom-board-selector-menu { + overflow: hidden !important; /* Strictly no scrollbars */ + border-radius: 6px !important; + box-shadow: 0 4px 15px rgba(0,0,0,0.15) !important; + border: 1px solid rgba(0,0,0,0.15) !important; + background: #fff !important; + padding: 4px 0 !important; + width: 140px !important; /* Fixed width to match button */ + min-width: 140px !important; + margin-top: 8px !important; /* Increased spacing to prevent overlap */ + white-space: nowrap !important; /* Prevent text wrapping so it shrinks to fit */ +} + +/* Fix Kanboard native li hover nesting */ +ul.custom-board-selector-menu li { + padding: 0 !important; + margin: 0 !important; + background: transparent !important; + border: none !important; +} + +ul.custom-board-selector-menu li:hover { + background: transparent !important; +} + +/* Style the links inside the dropdown beautifully */ +ul.custom-board-selector-menu li a { + padding: 8px 12px !important; /* More compact vertically */ + color: #333 !important; + text-decoration: none !important; + display: block !important; + font-size: 13px !important; /* Slightly smaller font */ + transition: background 0.2s, color 0.2s !important; + border-radius: 0 !important; /* Override default Kanboard link styling if needed */ + box-sizing: border-box !important; + width: 100% !important; +} + +ul.custom-board-selector-menu li a:hover { + background: var(--login-btn-bg) !important; + color: var(--login-btn-text) !important; +} + +/* Custom divider */ +ul.custom-board-selector-menu li hr { + margin: 4px 0 !important; + border-top: 1px solid rgba(0,0,0,0.08) !important; + border-bottom: none !important; +} + +/* Sidebar restructuring */ +.sidebar-container { + display: flex; + margin-top: 20px; +} + +.sidebar { + border-right: none !important; + width: 160px; /* Slimmer fixed width, no longer 250px */ + padding-right: 20px; +} + +.sidebar ul { + border: none !important; +} + +.sidebar ul li { + margin-bottom: 5px; + border-radius: 12px; + transition: all 0.2s ease; + border-left: none !important; +} + +.sidebar ul li:hover, .sidebar ul li.active { + background: rgba(255, 224, 102, 0.15) !important; + border-left: none !important; +} + +.sidebar ul li a { + position: relative; + display: block; + padding: 10px 0; /* Remove horizontal padding so text centers relative to the full width */ + color: var(--login-text-color) !important; + font-weight: 500; + text-align: center; + border-radius: 8px; +} + +.sidebar ul li a i { + position: absolute; + left: 15px; /* Fixed position on the left */ + top: 50%; + transform: translateY(-50%); + width: 20px; + text-align: center; + margin: 0; /* Remove previous margins */ +} + +.sidebar-text { + display: inline-block; + width: 5em; /* Exactly 5 characters wide */ + text-align: center; /* Center the text inside the 5-char block */ + margin: 0 auto; /* Center the block inside the a tag */ +} + +.sidebar ul li.active a { + color: #f39c12 !important; + font-weight: 600; +} + +/* Page Header links */ +.page-header { + border-bottom: 1px solid var(--login-icon-color); + padding-bottom: 15px; + margin-bottom: 20px; +} +.page-header ul { + border: none !important; +} +.page-header ul li { + margin-right: 10px !important; +} +.page-header ul li a { + border-radius: 50px; + padding: 6px 15px; + transition: all 0.2s; + color: var(--login-text-color) !important; +} +.page-header ul li a:hover { + background: rgba(255, 224, 102, 0.2); +} + +/* Cards and Tables (Overview) */ +.sidebar-content .table-list { + background: var(--login-card-bg); + border-radius: 16px; + box-shadow: var(--login-shadow); + border: none !important; + overflow: hidden; + margin-top: 15px; +} + +.table-list-header { + background: var(--login-input-bg) !important; + border: none !important; + padding: 15px 20px !important; + color: var(--login-text-color); +} + +.table-list-row { + border: none !important; + border-bottom: 1px solid var(--login-icon-color) !important; + padding: 15px 20px !important; + transition: background 0.2s; + background: var(--login-card-bg); +} + +.table-list-row:hover { + background: var(--login-input-bg) !important; +} + +.table-list-row:last-child { + border-bottom: none !important; +} + +/* Empty state */ +.alert { + border-radius: 12px; + padding: 20px !important; + border: none !important; + box-shadow: 0 4px 15px rgba(0,0,0,0.02); +} + +.alert-info { + background: rgba(255, 224, 102, 0.15) !important; + color: #d35400 !important; + text-align: center; + font-weight: 500; +} + +/* Filter bar / Search */ +.filter-box { + background: var(--login-card-bg); + border-radius: 50px; + box-shadow: var(--login-shadow); + padding: 5px 20px; + display: flex; + align-items: center; + border: 1px solid var(--login-icon-color); +} + +.filter-box .form-input-group { + flex-grow: 1; +} + +.filter-box input[type="text"] { + border: none !important; + background: transparent !important; + box-shadow: none !important; + width: 100%; + color: var(--login-input-text); +} + +/* Header general styling */ +header { + background: var(--login-card-bg) !important; + box-shadow: var(--login-shadow); + padding: 12px 15px !important; + border-bottom: none !important; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo-container { + flex: 0 0 auto; + display: flex; + justify-content: flex-start; + align-items: center; + margin-right: 20px; + font-size: 1.8em; /* Enlarge logo */ +} + +.title-container { + flex: 1; + display: flex; + justify-content: flex-start; + align-items: center; + font-size: 1.5em; /* Enlarge title */ + font-weight: 500; +} + +.menus-container { + flex: 0 0 auto; + display: flex; + justify-content: flex-end; + align-items: center; + font-size: 1.2em; /* Enlarge buttons */ + gap: 10px; /* Add spacing between buttons if they don't have it */ +} + +.header-center-description { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + font-size: 15px; /* Enlarge info text */ + color: #666; + margin: 0 0 0 15px; /* Stick close to title container */ + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-height: 40px; /* Prevent header stretching */ + font-weight: normal; /* Override title font weight */ +} + +.header-center-description hr { + display: inline-block; + width: 1px; + height: 12px; + margin: 0 10px; + background: #ccc; + border: none; + vertical-align: middle; +} + +.header-center-description p, .header-center-description strong, .header-center-description em { + display: inline; + margin: 0 5px; +} + +.header-center-description ul, .header-center-description ol { + display: inline; + padding: 0; + margin: 0 5px; + list-style: none; +} + +.header-center-description li { + display: inline; + margin-right: 8px; +} + + + +/* Stylings for injected Project Header components */ +.menus-container .dropdown-component { + margin-right: 10px; + font-size: 14px; +} + +.menus-container .views-switcher-component { + margin-right: 15px; +} + +.menus-container .views-switcher-component ul.views { + display: flex; + margin: 0; + padding: 0; + list-style: none; + gap: 8px; +} + +.menus-container .views-switcher-component ul.views li { + background: transparent; + border: none; + margin: 0; +} + +.menus-container .views-switcher-component ul.views li a { + padding: 4px 10px; + border-radius: 6px; + font-size: 14px; + font-weight: normal; + color: #555; + background: rgba(0,0,0,0.04); + text-decoration: none; + display: flex; + align-items: center; + gap: 5px; + transition: all 0.2s ease; +} + +.menus-container .views-switcher-component ul.views li.active a, +.menus-container .views-switcher-component ul.views li:hover a { + background: var(--color-primary); + color: white; +} + +.menus-container .views-switcher-component ul.views li.active a i, +.menus-container .views-switcher-component ul.views li:hover a i { + color: white; +} + +.filter-box-component form { + display: flex; + align-items: center; + gap: 5px; + margin: 0; +} + +.filter-box-component .input-addon-item { + background: transparent; + border: none; +} + +.filter-box-component .input-addon-field { + border-radius: 20px; + padding: 4px 12px; + border: 1px solid #ccc; + font-size: 13px; + width: 200px; + transition: width 0.3s; +} + +.filter-box-component .input-addon-field:focus { + width: 250px; + outline: none; + border-color: var(--color-primary); +} +/* ========================================================= + Project Overview Dashboard Redesign + ========================================================= */ + +/* Main Container padding */ +.project-overview-dashboard { + padding: 20px 30px; +} + +/* --- KPI Cards Row --- */ +.dashboard-kpi-row { + margin-bottom: 20px; +} + +.dashboard-kpi-row .project-overview-columns { + display: flex; + gap: 15px; + border: none; + background: transparent; + padding: 0; + margin: 0; +} + +.dashboard-kpi-row .project-overview-column { + flex: 1; + background: white; + border-radius: 8px; + padding: 15px 20px; + box-shadow: 0 2px 10px rgba(0,0,0,0.04); + border: 1px solid rgba(0,0,0,0.05); + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + transition: transform 0.2s, box-shadow 0.2s; +} + +.dashboard-kpi-row .project-overview-column:hover { + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(0,0,0,0.08); +} + +.dashboard-kpi-row .project-overview-column strong { + font-size: 28px; + font-weight: 700; + color: var(--color-primary); + line-height: 1; + margin-bottom: 6px; +} + +.dashboard-kpi-row .project-overview-column small { + font-size: 13px; + color: #666; + font-weight: 500; +} + +/* --- Pure CSS Tabs Layout --- */ +.tab-radio { + display: none; +} + +.dashboard-tabs-header { + display: flex; + gap: 8px; + margin-bottom: 15px; + border-bottom: 2px solid #f0f0f0; + padding-bottom: 8px; +} + +.dashboard-tabs-header .tab-btn { + flex: 1; + text-align: center; + background: transparent; + border: none; + padding: 8px 16px; + font-size: 14px; + font-weight: 500; + color: #666; + cursor: pointer; + border-radius: 6px; + transition: all 0.2s ease; + display: block; +} + +.dashboard-tabs-header .tab-btn:hover { + background: #f5f5f5; + color: #333; +} + +/* Radio button active states mapping to labels */ +#tab-desc:checked ~ .dashboard-tabs-header [for="tab-desc"], +#tab-attach:checked ~ .dashboard-tabs-header [for="tab-attach"], +#tab-info:checked ~ .dashboard-tabs-header [for="tab-info"], +#tab-act:checked ~ .dashboard-tabs-header [for="tab-act"] { + background: var(--color-primary); + color: white; + box-shadow: 0 4px 10px rgba(0,0,0,0.1); +} + +.dashboard-tabs-content .tab-pane { + display: none; +} + +/* Radio button active states mapping to panes */ +#tab-desc:checked ~ .dashboard-tabs-content .pane-desc, +#tab-attach:checked ~ .dashboard-tabs-content .pane-attach, +#tab-info:checked ~ .dashboard-tabs-content .pane-info, +#tab-act:checked ~ .dashboard-tabs-content .pane-act { + display: block; + animation: fadeIn 0.3s ease; +} + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(5px); } + to { opacity: 1; transform: translateY(0); } +} + +/* --- Card Styles (Replaced details with div) --- */ +.dashboard-tabs-content .accordion-section { + background: white; + border-radius: 10px; + box-shadow: 0 2px 10px rgba(0,0,0,0.04); + border: 1px solid rgba(0,0,0,0.05); + overflow: hidden; + margin-bottom: 0; +} + +.dashboard-tabs-content .accordion-section .accordion-content { + padding: 25px; +} + +.project-overview-dashboard .accordion-content > .panel { + border: none; + background: transparent; + padding: 0; + margin: 0; + box-shadow: none; +} + +/* Styling specific content inside cards */ +.project-overview-dashboard .accordion-content ul { + list-style-type: none; + padding: 0; + margin: 0; +} + +.project-overview-dashboard .accordion-content ul li { + padding: 8px 0; + border-bottom: 1px solid #f5f5f5; + color: #555; +} + +.project-overview-dashboard .accordion-content ul li:last-child { + border-bottom: none; +} + +.project-overview-dashboard .accordion-content ul li strong { + color: #222; +} + +/* Activity feed timeline style */ +.project-overview-dashboard .activity-event { + margin-bottom: 15px; + padding-bottom: 15px; + border-bottom: 1px dashed #eee; +} + +.project-overview-dashboard .activity-event:last-child { + margin-bottom: 0; + padding-bottom: 0; + border-bottom: none; +} + +.project-overview-dashboard .activity-date { + font-size: 12px; + color: #999; +} + +@media (max-width: 900px) { + .dashboard-kpi-row .project-overview-columns { + flex-direction: column; + } + + .dashboard-tabs-header { + flex-wrap: wrap; + } +} + +/* ========================================================= + Kanban Board Redesign + ========================================================= */ + +/* Main Board Container */ +#board { + border-collapse: separate !important; + border-spacing: 15px 0 !important; + margin: 10px 0 !important; /* Removed side margin so 100% width fits perfectly */ + background: transparent !important; + width: 100% !important; + table-layout: fixed !important; +} + +/* Column Headers */ +.board-column-header { + background-color: #f4f5f7 !important; + border: none !important; + border-radius: 8px 8px 0 0 !important; + padding: 15px !important; +} + +/* Column Body */ +.board-column { + background-color: #f4f5f7 !important; + border: none !important; + border-radius: 0 0 8px 8px !important; + padding: 0 10px 15px 10px !important; + vertical-align: top; +} + +/* Remove default column borders */ +.board-column-header th, .board-column td { + border: none !important; +} + +/* Header Flexbox Layout */ +.board-column-expanded-header { + display: flex !important; + align-items: center; + justify-content: space-between; +} + +.board-column-title { + font-weight: 700; + font-size: 15px; + color: #333; + flex: 1; + text-align: left; + margin-left: 8px; +} + +/* Task Count Badge */ +.board-column-header-task-count { + background: #e0e4e8; + color: #555; + padding: 3px 10px; + border-radius: 12px; + font-size: 12px; + font-weight: 600; + margin-left: 10px; +} + +/* Task Cards */ +.task-board { + background-color: #ffffff !important; + border-top: none !important; + border-right: none !important; + border-bottom: none !important; + border-left-width: 4px !important; + border-left-style: solid !important; + border-radius: 6px !important; + box-shadow: 0 1px 3px rgba(0,0,0,0.1) !important; + padding: 12px 15px !important; + margin-bottom: 12px !important; + margin-top: 0 !important; + transition: transform 0.2s, box-shadow 0.2s; +} + +.task-board:hover { + box-shadow: 0 3px 8px rgba(0,0,0,0.15) !important; + transform: translateY(-2px); +} + +/* Typography inside Tasks */ +.task-board-header { + font-size: 12px; + color: #888; + margin-bottom: 8px; +} + +.task-board-header a { + color: #888 !important; + font-weight: 500; +} + +.task-board-title { + font-size: 14px; + font-weight: 600; + line-height: 1.4; + margin-bottom: 4px; +} + +.task-board-title a { + color: #222 !important; +} + +.task-board-title a:hover { + color: var(--color-primary) !important; + text-decoration: none; +} + +/* Clean up add button */ +.board-add-icon { + color: var(--color-primary) !important; + font-size: 16px; + opacity: 0.8; +} + +.board-add-icon:hover { + opacity: 1; +} + +/* ========================================================= + New Task / Form Modal Redesign + ========================================================= */ + +/* Use CSS Grid for the form container */ +.task-form-container { + display: grid !important; + grid-template-columns: 2fr 1fr 1fr; + gap: 20px; +} + +/* Make columns fill their grid cells */ +.task-form-main-column, +.task-form-secondary-column { + width: 100% !important; + float: none !important; +} + +/* Bottom area spans across all columns */ +.task-form-bottom { + grid-column: 1 / -1; + margin-top: 15px; + padding-top: 20px; + border-top: 1px solid #f0f0f0; +} + +/* Labels */ +.task-form-container label { + display: block; + font-size: 13px; + font-weight: 600; + color: #444; + margin-bottom: 6px; + margin-top: 12px; +} + +/* Inputs, Selects, Textareas */ +.task-form-container input[type="text"], +.task-form-container input[type="number"], +.task-form-container input[type="password"], +.task-form-container select, +.task-form-container textarea { + width: 100% !important; + padding: 10px 12px !important; + border: 1px solid #d1d5db !important; + border-radius: 6px !important; + box-sizing: border-box !important; + font-size: 14px; + color: #333; + transition: all 0.2s ease !important; + background-color: #fff !important; +} + +/* Focus states for inputs */ +.task-form-container input[type="text"]:focus, +.task-form-container input[type="number"]:focus, +.task-form-container input[type="password"]:focus, +.task-form-container select:focus, +.task-form-container textarea:focus { + border-color: var(--color-primary) !important; + box-shadow: 0 0 0 3px rgba(52, 104, 192, 0.1) !important; + outline: none !important; +} + +/* Checkboxes */ +.task-form-bottom label { + display: inline-block; + margin-top: 0; + margin-right: 15px; + font-weight: 500; +} + +/* Primary Button in Form */ +.task-form-bottom .btn-blue { + padding: 10px 24px !important; + font-size: 15px !important; + font-weight: 600 !important; + border-radius: 6px !important; + margin-right: 10px; +} + +/* Markdown Editor Toolbar */ +.text-editor-toolbar { + background-color: #f9f9f9; + border: 1px solid #d1d5db; + border-bottom: none; + border-radius: 6px 6px 0 0; + padding: 6px 10px; +} + +.text-editor-write-mode { + margin-top: 0 !important; +} + +.text-editor-write-mode textarea { + border-radius: 0 0 6px 6px !important; + border-top: none !important; +} diff --git a/assets/css/custom_login.css b/assets/css/custom_login.css new file mode 100644 index 0000000..f0ec5ec --- /dev/null +++ b/assets/css/custom_login.css @@ -0,0 +1,223 @@ +/* custom_login.css */ + +/* Define variables for Light Theme */ +:root { + --login-bg: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%); + --login-card-bg: #ffffff; + --login-text-color: #333333; + --login-input-bg: #f1f3f5; + --login-input-text: #495057; + --login-input-placeholder: #adb5bd; + --login-icon-color: #ced4da; + --login-icon-focus: #f39c12; /* Rich yellow/orange for light mode focus */ + --login-btn-bg: #ffe066; /* Bright yellow */ + --login-btn-hover: #ffd43b; + --login-btn-text: #212529; + --login-shadow: 0 15px 35px rgba(0,0,0,0.05); + --login-btn-shadow: 0 8px 24px rgba(255, 224, 102, 0.4); +} + +/* Define variables for Dark Theme using prefers-color-scheme */ +@media (prefers-color-scheme: dark) { + :root { + --login-bg: #1f2029; /* Dark background */ + --login-card-bg: #2a2b38; /* Slightly lighter card */ + --login-text-color: #f8f9fa; + --login-input-bg: #1f2029; + --login-input-text: #f8f9fa; + --login-input-placeholder: #6c757d; + --login-icon-color: #495057; + --login-icon-focus: #ffeba7; + --login-btn-bg: #ffeba7; + --login-btn-hover: #ffe066; + --login-btn-text: #1f2029; + --login-shadow: 0 15px 35px rgba(0,0,0,0.2); + --login-btn-shadow: 0 8px 24px rgba(255, 235, 167, 0.15); + } +} + +/* Ensure body takes full height and uses Flexbox for centering on login page */ +body:has(.login-page-wrapper) { + background: var(--login-bg); + min-height: 100vh; + display: flex; + align-items: center; + justify-content: center; + margin: 0; + font-family: 'Inter', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; + color: var(--login-text-color); +} + +/* Hide header if it appears on login page */ +body:has(.login-page-wrapper) header { + display: none; +} +body:has(.login-page-wrapper) .page { + margin: 0; + padding: 0; + width: 100%; + display: flex; + align-items: center; + justify-content: center; +} + +/* Card Container */ +.login-page-wrapper { + width: 100%; + max-width: 400px; + padding: 20px; + box-sizing: border-box; +} + +.login-page-wrapper .form-login { + background: var(--login-card-bg); + border-radius: 20px; + padding: 25px 30px 25px 30px; + box-shadow: var(--login-shadow); + text-align: center; + border: none; + margin: 0; +} + +.login-page-wrapper .form-login h2 { + margin-top: 0; + margin-bottom: 20px; + font-size: 26px; + font-weight: 600; + color: var(--login-text-color); + border: none; +} + +.login-page-wrapper .form-login h2::after { + display: none; +} + +/* Input Icon Wrapper */ +.input-icon-wrapper { + position: relative; + margin-bottom: 25px; + text-align: left; +} + +.input-icon-wrapper label { + display: none; /* Hide original labels, rely on placeholders and icons */ +} + +.input-icon-wrapper i { + position: absolute; + left: 20px; + top: 50%; + transform: translateY(-50%); + color: var(--login-icon-color); + transition: color 0.3s ease; + font-size: 18px; + pointer-events: none; /* Let clicks pass through to input */ +} + +.input-icon-wrapper input[type="text"], +.input-icon-wrapper input[type="password"] { + width: 100%; + background: var(--login-input-bg); + border: 2px solid transparent; + border-radius: 50px; + padding: 16px 20px 16px 50px; /* Space for icon */ + font-size: 15px; + color: var(--login-input-text); + transition: all 0.3s ease; + box-sizing: border-box; + box-shadow: none; +} + +/* Add placeholder styling */ +.input-icon-wrapper input::placeholder { + color: var(--login-input-placeholder); + opacity: 1; +} + +.input-icon-wrapper input:focus { + outline: none; + background: var(--login-card-bg); + border-color: var(--login-icon-focus); + box-shadow: 0 0 0 4px rgba(243, 156, 18, 0.1); +} + +.input-icon-wrapper:focus-within i { + color: var(--login-icon-focus); +} + +/* Button */ +.login-page-wrapper .form-actions { + margin-top: 25px; + text-align: center; +} + +.login-page-wrapper .btn { + background: var(--login-btn-bg); + color: var(--login-btn-text); + border: none; + border-radius: 50px; + padding: 15px 40px; + font-size: 16px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 1px; + cursor: pointer; + transition: all 0.3s ease; + box-shadow: var(--login-btn-shadow); + width: 100%; /* Full width */ +} + +.login-page-wrapper .btn:hover { + background: var(--login-btn-hover); + transform: translateY(-2px); + box-shadow: 0 10px 25px rgba(255, 235, 167, 0.3); +} + +/* Links */ +.login-page-wrapper .reset-password { + margin-top: 15px; +} + +.login-page-wrapper .reset-password a { + color: var(--login-input-placeholder); + text-decoration: none; + font-size: 14px; + transition: color 0.3s ease; +} + +.login-page-wrapper .reset-password a:hover { + color: var(--login-icon-focus); +} + +/* Remember me */ +.login-page-wrapper .remember-me-wrapper { + display: flex; + align-items: center; + justify-content: flex-start; + padding-left: 10px; +} + +.login-page-wrapper input[type="checkbox"] { + margin: 0; + margin-right: 10px; + accent-color: var(--login-icon-focus); + width: 16px; + height: 16px; +} +.login-page-wrapper label.remember-me-label { + color: var(--login-input-placeholder); + font-size: 14px; + display: inline-block; + cursor: pointer; +} + +/* Error messages */ +.login-page-wrapper .alert-error { + background: rgba(231, 76, 60, 0.1); + color: #e74c3c; + border: none; + border-radius: 10px; + padding: 15px; + margin-bottom: 20px; + font-size: 14px; +} diff --git a/assets/css/dark.min.css b/assets/css/dark.min.css new file mode 100644 index 0000000..a5d03c6 --- /dev/null +++ b/assets/css/dark.min.css @@ -0,0 +1 @@ +:root{--body-background-color:#222;--header-background-color:#222;--color-primary:#a0a0a0;--color-light:#a0a0a0;--color-lighter:#efefef;--color-dark:#000;--color-medium:#4f4c4c;--color-error:#b94a48;--link-color-primary:#aaa;--link-color-focus:#ddd;--link-color-hover:#ddd;--alert-color-default:#efefef;--alert-color-success:#def6de;--alert-color-error:#de9393;--alert-color-info:#3a87ad;--alert-color-normal:#333;--alert-background-color-default:#333;--alert-background-color-success:#304b27;--alert-background-color-error:#500606;--alert-background-color-info:#d9edf7;--alert-background-color-normal:#f0f0f0;--alert-border-color-default:#444;--alert-border-color-success:#3c621b;--alert-border-color-error:#7e0315;--alert-border-color-info:#bce8f1;--alert-border-color-normal:#ddd;--button-default-color:#333;--button-default-background-color:#f5f5f5;--button-default-border-color:#ddd;--button-default-color-focus:#000;--button-default-background-color-focus:#fafafa;--button-default-border-color-focus:#bbb;--button-primary-color:#efefef;--button-primary-background-color:#333;--button-primary-border-color:#444;--button-primary-color-focus:#fff;--button-primary-background-color-focus:#555;--button-primary-border-color-focus:#888;--button-danger-color:#fff;--button-danger-background-color:#d14836;--button-danger-border-color:#b0281a;--button-danger-color-focus:#fff;--button-danger-background-color-focus:#c53727;--button-danger-border-color-focus:#b0281a;--button-disabled-color:#ccc;--button-disabled-background-color:#f7f7f7;--button-disabled-border-color:#ccc;--table-header-background-color:#1a1a1a;--table-nth-background-color:#2d2c2c;--table-border-color:rgba(147,128,108,.25);--avatar-color-letter:#fff;--activity-title-color:#e3e2e2;--activity-title-border-color:#efefef;--activity-event-background-color:#313131;--activity-event-hover-color:#000;--user-mention-color:#fff;--board-task-limit-color:#DF5353;--table-list-header-border-color:rgba(147,128,108,.25);--table-list-header-background-color:rgb(59,59,59);--table-list-nth-background-color:#2d2c2c;--table-list-border-color:rgba(147,128,108,.25);--table-list-row-hover-border-color:rgba(147,128,108,.25);--table-list-row-background-color:#434343;--sidebar-border-color:rgba(147,128,108,.25);--dropdown-background-color:#222;--dropdown-border-color:#000;--dropdown-li-border-color:#555;--input-addon-background-color:#1a1a1a;--input-addon-color:rgba(147,128,108,.25);--views-background-color:#1a1a1a;--views-border-color:rgba(147,128,108,.25);--views-active-color:#949494;--input-focus-color:#e6edf3;--input-focus-border-color:rgba(82,168,236,.8);--input-focus-shadow-color:rgba(82,168,236,.6);--input-background-color:rgb(59,59,59);--input-border-color:#777575;--input-placeholder-color:#666;--tooltip-background-color:#333;--tooltip-border-color:#555;--tooltip-shadow-color:#111;--panel-background-color:#2c2c2c;--panel-border-color:#000;--draggable-item-selected-background-color:#222;--draggable-item-selected-border-color:#111;--draggable-item-hover-background-color:#555;--draggable-row-handle-color:#444;--draggable-placeholder-background-color:#444;--draggable-placeholder-border-color:#666;--task-list-icons-color:#ccc;--form-help-color:#a8a12f;--form-error-color:#f2332f;--comment-title-border-color:#eee;--comment-nth-background-color:#2b2a2a;--comment-highlighted-background-color:#2b2901;--comment-highlighted-hover-background-color:#000;--comment-highlighted-border-color:#c09e05}html{color-scheme:dark}.select2-dropdown,.select2-close-mask{background-color:var(--input-background-color)}.select2-container--default .select2-selection--multiple,.select2-container--default .select2-selection--single{background-color:var(--input-background-color);border-color:var(--input-border-color)}.select2-container--default.select2-container--focus .select2-selection--multiple{border-color:var(--input-focus-border-color)}.select2-container--default .select2-selection--single .select2-selection__rendered,.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#fff}.task-board-title{color:#000}.task-summary-column a,.task-summary-column a:hover{color:#000}h1,li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0}body{background-color:var(--body-background-color);font-size:100%;padding-bottom:10px;color:var(--color-primary);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility;overflow-x:hidden}small{font-size:.8em}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,.1);border-bottom:1px solid rgba(255,255,255,.3)}.page{margin-left:10px;margin-right:10px}.margin-top{margin-top:20px}.margin-bottom{margin-bottom:20px}.pull-right{text-align:right;margin-left:auto}ul.no-bullet li{list-style-type:none;margin-left:0}#app-loading-icon{position:fixed;right:3px;bottom:3px}.assign-me{vertical-align:bottom}a{color:var(--link-color-primary);border:none}a:focus{color:var(--link-color-focus);outline:0;text-decoration:none}a:hover{color:var(--link-color-hover);text-decoration:none}a .fa{color:var(--color-primary);padding-right:3px;text-decoration:none}h1,h2,h3{font-weight:400;color:var(--color-primary)}h1{font-size:1.5em}h2{font-size:1.4em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px}table.table-fixed{table-layout:fixed;white-space:nowrap}table.table-fixed th{overflow:hidden}table.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.table-small{font-size:.8em}table.table-striped tr:nth-child(odd){background:var(--table-nth-background-color)}@media (max-width:768px){table.table-scrolling{overflow-x:auto;display:inline-block;vertical-align:top;max-width:100%;white-space:nowrap}}table th{text-align:left;padding:.5em 3px;border:1px solid var(--table-border-color);background-color:var(--table-header-background-color)}table th a{text-decoration:none;color:var(--color-primary)}table th a:focus,table th a:hover{text-decoration:underline}table td{border:1px solid var(--table-border-color);padding:.5em 3px;vertical-align:top}table td li{margin-left:20px}table td .color-picker-square{display:inline-block;width:12px;height:12px;border:1px solid #000}.task-table a{color:#000}.column-1{width:1%}.column-2{width:2%}.column-3{width:3%}.column-4{width:4%}.column-5{width:5%}.column-6{width:6%}.column-7{width:7%}.column-8{width:8%}.column-9{width:9%}.column-10{width:10%}.column-11{width:11%}.column-12{width:12%}.column-13{width:13%}.column-14{width:14%}.column-15{width:15%}.column-16{width:16%}.column-17{width:17%}.column-18{width:18%}.column-19{width:19%}.column-20{width:20%}.column-21{width:21%}.column-22{width:22%}.column-23{width:23%}.column-24{width:24%}.column-25{width:25%}.column-26{width:26%}.column-27{width:27%}.column-28{width:28%}.column-29{width:29%}.column-30{width:30%}.column-31{width:31%}.column-32{width:32%}.column-33{width:33%}.column-34{width:34%}.column-35{width:35%}.column-36{width:36%}.column-37{width:37%}.column-38{width:38%}.column-39{width:39%}.column-40{width:40%}.column-41{width:41%}.column-42{width:42%}.column-43{width:43%}.column-44{width:44%}.column-45{width:45%}.column-46{width:46%}.column-47{width:47%}.column-48{width:48%}.column-49{width:49%}.column-50{width:50%}.column-51{width:51%}.column-52{width:52%}.column-53{width:53%}.column-54{width:54%}.column-55{width:55%}.column-56{width:56%}.column-57{width:57%}.column-58{width:58%}.column-59{width:59%}.column-60{width:60%}.column-61{width:61%}.column-62{width:62%}.column-63{width:63%}.column-64{width:64%}.column-65{width:65%}.column-66{width:66%}.column-67{width:67%}.column-68{width:68%}.column-69{width:69%}.column-70{width:70%}.column-71{width:71%}.column-72{width:72%}.column-73{width:73%}.column-74{width:74%}.column-75{width:75%}.column-76{width:76%}.column-77{width:77%}.column-78{width:78%}.column-79{width:79%}.column-80{width:80%}.column-81{width:81%}.column-82{width:82%}.column-83{width:83%}.column-84{width:84%}.column-85{width:85%}.column-86{width:86%}.column-87{width:87%}.column-88{width:88%}.column-89{width:89%}.column-90{width:90%}.column-91{width:91%}.column-92{width:92%}.column-93{width:93%}.column-94{width:94%}.column-95{width:95%}.column-96{width:96%}.column-97{width:97%}.column-98{width:98%}.column-99{width:99%}.column-100{width:100%}.draggable-row-handle{cursor:move;color:var(--draggable-row-handle-color)}.draggable-row-handle:hover{color:var(--color-primary)}tr.draggable-item-selected{background:var(--draggable-item-selected-background-color);border:2px solid var(--draggable-item-selected-border-color);box-shadow:4px 2px 10px -4px rgba(0,0,0,.55)}tr.draggable-item-selected td{border-top:none;border-bottom:none}tr.draggable-item-selected td:first-child{border-left:none}tr.draggable-item-selected td:last-child{border-right:none}.table-stripped tr.draggable-item-hover,.table-stripped tr.draggable-item-hover{background:var(--draggable-item-hover-background-color)}.table-list{font-size:.85em;margin-bottom:20px}.table-list-header{background:var(--table-list-header-background-color);border:1px solid var(--table-list-header-border-color);border-radius:5px 5px 0 0;line-height:28px;padding-left:3px;padding-right:3px}.table-list-header a{color:var(--color-primary);font-weight:500;text-decoration:none;margin-right:10px}.table-list-header a:hover,.table-list-header a:focus{color:#767676}.table-list-header .table-list-header-count{color:#767676;display:inline-block;float:left}.table-list-header .table-list-header-menu{text-align:right}.table-list-row{padding-left:3px;padding-right:3px;border-bottom:1px solid var(--table-list-border-color);border-right:1px solid var(--table-list-border-color)}.table-list-row.table-border-left{border-left:1px solid var(--table-list-border-color)}.table-list-row:nth-child(odd){background:var(--table-list-nth-background-color)}.table-list-row:last-child{border-radius:0 0 5px 5px}.table-list-row:hover{background:var(--table-list-row-background-color);border-bottom:1px solid var(--table-list-row-hover-border-color);border-right:1px solid var(--table-list-row-hover-border-color)}.table-list-row .table-list-title{font-weight:500;line-height:23px}.table-list-row .table-list-title.status-closed{text-decoration:line-through;margin-right:10px}.table-list-row .table-list-title.status-closed a{font-style:italic}.table-list-row .table-list-title a{color:var(--color-primary);text-decoration:none}.table-list-row .table-list-title a:hover,.table-list-row .table-list-title a:focus{text-decoration:underline}.table-list-row .table-list-details{color:#999;font-weight:300;line-height:20px}.table-list-row .table-list-details span{margin-left:5px}.table-list-row .table-list-details span:first-child{margin-left:0}.table-list-row .table-list-details li{display:inline;list-style-type:none}.table-list-row .table-list-details li:after{content:', '}.table-list-row .table-list-details li:last-child:after{content:''}.table-list-row .table-list-details strong{font-weight:400;color:#555}.table-list-row .table-list-details-with-icons{float:left}@media (max-width:768px){.table-list-row .table-list-details-with-icons{float:none}}.table-list-row .table-list-icons{font-size:.8em;text-align:right;line-height:30px}@media (max-width:768px){.table-list-row .table-list-icons{text-align:left;line-height:20px}}.table-list-row .table-list-icons span{margin-left:5px}.table-list-row .table-list-icons a{text-decoration:none}.table-list-row .table-list-icons a:hover{color:var(--color-primary)}.table-list-row .table-list-icons a:hover i{color:var(--color-primary)}.table-list-category{font-size:.9em;font-weight:500;color:#000;padding:1px 2px 1px 2px;border-radius:3px;background:#fcfcfc;border:1px solid #ccc}.table-list-category a{text-decoration:none;color:#000}.table-list-category a:hover{color:#36c}fieldset{border:1px solid #ddd;margin-top:10px}legend{font-weight:500;font-size:1.2em}label{cursor:pointer;display:block;margin-top:10px;font-weight:400}input,textarea{font-family:sans-serif;background-color:var(--input-background-color)}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]:not(.input-addon-field){color:var(--color-light);border:1px solid var(--input-border-color);width:300px;max-width:95%;font-size:1em;height:25px;padding-bottom:0;padding-left:4px;-webkit-appearance:none;-moz-appearance:none}input[type="number"]::placeholder,input[type="date"]::placeholder,input[type="email"]::placeholder,input[type="password"]::placeholder,input[type="text"]:not(.input-addon-field)::placeholder{color:var(--input-placeholder-color)}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus{color:var(--input-focus-color);border-color:var(--input-focus-border-color);outline:0;box-shadow:0 0 8px var(--input-focus-shadow-color)}input[type="number"]{width:70px}input[type="text"]:not(.input-addon-field).form-numeric{width:70px}input[type="text"]:not(.input-addon-field).form-datetime,input[type="text"]:not(.input-addon-field).form-date{width:150px}input[type="text"]:not(.input-addon-field).form-input-large{width:400px}input[type="text"]:not(.input-addon-field).form-input-small{width:150px}textarea:focus{color:var(--input-focus-color);border-color:var(--input-focus-border-color);outline:0;box-shadow:0 0 8px var(--input-focus-shadow-color)}textarea{padding:4px;border:1px solid var(--input-border-color);width:400px;max-width:99%;height:200px;font-size:1em}textarea::placeholder{color:var(--input-placeholder-color)}select{font-size:1em;max-width:95%}select:focus{outline:0}select[multiple]{width:300px}.tag-autocomplete{width:400px}span.select2-container{margin-top:2px}.form-actions{padding-top:20px;clear:both}.form-required{color:red;padding-left:5px;font-weight:700}@media (max-width:480px){.form-required{display:none}}input[type="text"].form-max-width{width:100%}input.form-error,textarea.form-error{border:2px solid var(--form-error-color)}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid var(--form-error-color)}.form-errors{color:var(--form-error-color);list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:.8em;color:var(--form-help-color);margin-bottom:15px}.form-inline{padding:0;margin:0;border:none}.form-inline label{display:inline;padding-right:3px}.form-inline input,.form-inline select{margin:0 15px 0 0}.form-inline .form-required{display:none}.form-inline .form-actions{display:inline-block}.form-inline .js-submit-buttons-rendered{display:inline-block}.form-inline-group{display:inline}.form-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.form-columns .form-column{margin-right:25px;flex-grow:1}.form-columns fieldset{margin-top:0}.form-login{max-width:350px;margin:5% auto 0}@media (max-width:480px){.form-login{margin-left:5px}}.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-weight:700}.reset-password{margin-top:20px;margin-bottom:20px}.reset-password a{color:var(--color-light)}.input-addon{display:flex}.input-addon-field{flex:1;font-size:1em;color:var(--color-light);margin:0;-webkit-appearance:none;-moz-appearance:none}.input-addon-field:first-child{border-radius:5px 0 0 5px}.input-addon-field:last-child{border-radius:0 5px 5px 0}.input-addon-item{background-color:var(--input-addon-background-color);color:var(--input-addon-color);font:inherit;font-weight:400}.input-addon-item:first-child{border-radius:5px 0 0 5px}.input-addon-item:last-child{border-radius:0 5px 5px 0}@media (max-width:480px){.input-addon-item .dropdown .fa-caret-down{display:none}}.input-addon-field,.input-addon-item{border:1px solid rgba(147,128,108,.25);padding:4px .75em}.input-addon-field:not(:first-child),.input-addon-item:not(:first-child){border-left:0}.input-addon .input-addon-field{flex:1 1 auto;width:1%!important}@media (max-width:400px){.input-addon-item{padding:3px}}.icon-success{color:#468847}.icon-error{color:#b94a48}.icon-fade-out{opacity:1;animation:icon-fadeout 5s linear forwards}@keyframes icon-fadeout{0%{opacity:1}100%{opacity:0}}.alert{padding:8px 35px 8px 14px;margin-top:5px;margin-bottom:5px;color:var(--alert-color-default);background-color:var(--alert-background-color-default);border:1px solid var(--alert-border-color-default);border-radius:4px}.alert-success{color:var(--alert-color-success);background-color:var(--alert-background-color-success);border-color:var(--alert-border-color-success)}.alert-error{color:var(--alert-color-error);background-color:var(--alert-background-color-error);border-color:var(--alert-border-color-error)}.alert-info{color:var(--alert-color-info);background-color:var(--alert-background-color-info);border-color:var(--alert-border-color-info)}.alert-normal{color:var(--alert-color-normal);background-color:var(--alert-background-color-normal);border-color:var(--alert-border-color-normal)}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.alert-fade-out{text-align:center;position:fixed;bottom:0;left:20%;width:60%;padding-top:5px;padding-bottom:5px;margin-bottom:0;border-width:1px 0 0;border-radius:4px 4px 0 0;z-index:9999;opacity:1;animation:fadeout 5s linear forwards}@keyframes fadeout{0%{opacity:1}100%{opacity:0;visibility:hidden}}a.btn{text-decoration:none}.btn{-webkit-appearance:none;-moz-appearance:none;font-size:1.2em;font-weight:400;cursor:pointer;display:inline-block;border-radius:2px;padding:3px 10px;margin:0;border:1px solid var(--button-default-border-color);background:var(--button-default-background-color);color:var(--button-default-color)}.btn:hover,.btn:focus{border-color:var(--button-default-border-color-focus);background:var(--button-default-background-color-focus);color:var(--button-default-color-focus)}.btn-red{border-color:var(--button-danger-border-color);background:var(--button-danger-background-color);color:var(--button-danger-color)}.btn-red:hover,.btn-red:focus{border-color:var(--button-danger-border-color-focus);background:var(--button-danger-background-color-focus);color:var(--button-danger-color-focus)}.btn-blue{border-color:var(--button-primary-border-color);background:var(--button-primary-background-color);color:var(--button-primary-color)}.btn-blue:hover,.btn-blue:focus{border-color:var(--button-primary-border-color-focus);background:var(--button-primary-background-color-focus);color:var(--button-primary-color-focus)}.btn:disabled{color:var(--button-disabled-color);border-color:var(--button-disabled-border-color);background:var(--button-disabled-background-color)}.buttons-header{font-size:.8em;margin-top:5px;margin-bottom:15px}.tooltip i.fa{cursor:pointer}.tooltip .fa-info-circle{color:var(--color-light)}#tooltip-container{padding:5px;background:var(--tooltip-background-color);border:1px solid var(--tooltip-border-color);border-radius:4px;box-shadow:0 6px 12px var(--tooltip-shadow-color);position:absolute;min-width:350px}#tooltip-container .markdown p:last-child{margin-bottom:0}#tooltip-container .tooltip-large{width:600px}h2 .dropdown ul{display:none}.dropdown{display:inline;position:relative}.dropdown ul{display:none}.dropdown-smaller{font-size:.85em}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:var(--dropdown-background-color);border:1px solid var(--dropdown-border-color);border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.dropdown-submenu-open li{display:block;margin:0;padding:8px 10px;font-size:.9em;border-bottom:1px solid var(--dropdown-li-border-color);cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.dropdown-submenu-open li:last-child{border:none}.dropdown-submenu-open li:not(.no-hover):hover{background:#4078C0;color:#fff}.dropdown-submenu-open li:hover a{color:#fff}.dropdown-submenu-open li:hover i{color:#fff}.dropdown-submenu-open a{text-decoration:none;color:var(--color-primary)}.dropdown-submenu-open a:focus{text-decoration:underline}.dropdown-menu-link-text,.dropdown-menu-link-icon{color:var(--color-primary);text-decoration:none}.dropdown-menu-link-icon{display:inline-flex}.dropdown-menu-link-text:hover{text-decoration:underline}td a.dropdown-menu strong{color:var(--color-primary)}td a.dropdown-menu strong i{color:var(--color-primary)}td a.dropdown-menu i{color:#dedede}td a.dropdown-menu:hover strong{color:#555}td a.dropdown-menu:hover strong i{color:#555}td a.dropdown-menu:hover i{color:#333}.accordion-title{font-size:1.2em;cursor:pointer;margin-top:10px}.accordion-content{margin-top:15px;margin-bottom:25px}#select-dropdown-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:var(--dropdown-background-color);list-style:none;border:1px solid var(--dropdown-border-color);border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,.175);overflow:scroll}.select-dropdown-menu-item{white-space:nowrap;overflow:hidden;padding:3px 10px;color:var(--color-medium);cursor:pointer;border-bottom:1px solid var(--dropdown-li-border-color);line-height:1.5em;font-weight:400}.select-dropdown-menu-item.active{color:#fff;background:#428bca}.select-dropdown-menu-item:last-child{border:none}.select-dropdown-input-container{position:relative;border:1px solid var(--input-border-color);border-radius:5px;background-color:var(--input-background-color);max-width:300px}.select-dropdown-input-container input.select-dropdown-input{margin:0 0 0 5px;border:none;height:23px;width:270px}.select-dropdown-input-container input.select-dropdown-input:focus{border:none;box-shadow:none}.select-dropdown-input-container .select-dropdown-chevron{color:var(--color-medium);position:absolute;top:4px;right:5px;cursor:pointer}.select-dropdown-input-container .select-loading-icon{color:var(--color-medium);position:absolute;top:4px;right:5px}#suggest-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,.175)}.suggest-menu-item{white-space:nowrap;padding:3px 10px;color:var(--color-primary);font-weight:700;cursor:pointer}.suggest-menu-item.active{color:#fff;background:#428bca}.suggest-menu-item.active small{color:#fff}.suggest-menu-item small{color:var(--color-light);font-weight:400}#modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.9);overflow:auto;z-index:100}#modal-box{position:fixed;max-height:calc(100% - 30px);top:2%;left:50%;transform:translateX(-50%);background:var(--body-background-color);overflow:auto;border-radius:5px}#modal-content{padding:0 5px 5px}#modal-header{text-align:right;padding-right:5px}#modal-close-button{color:var(--color-primary)}#modal-close-button:hover{color:var(--color-error)}.pagination{text-align:center;font-size:.9em}.pagination-showing{margin-right:5px;padding-right:5px;border-right:1px solid #999}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}header{display:flex;flex-wrap:wrap;padding:5px 10px;margin-bottom:5px;border-bottom:1px solid #dedede;background-color:var(--header-background-color)}header .title-container{flex:1;min-width:300px}@media (max-width:480px){header .title-container{order:3}}header .board-selector-container{min-width:320px;display:flex;align-items:center}@media (max-width:480px){header .board-selector-container{order:2;min-width:300px}header .board-selector-container input[type=text]{max-width:280px}}header .menus-container{min-width:120px;display:flex;align-items:center;justify-content:flex-end}@media (max-width:480px){header .menus-container{order:1;margin-bottom:5px;margin-left:auto}}header h1{font-size:1.5em}header h1 .tooltip{opacity:.3;font-size:.7em}a i.web-notification-icon{color:var(--link-color-primary)}a i.web-notification-icon:focus,a i.web-notification-icon:hover{color:#000}.logo a{opacity:.5;color:#d40000;text-decoration:none}.logo span{color:var(--color-primary)}.logo a:hover{opacity:.8;color:var(--color-primary)}.logo a:focus span,.logo a:hover span{color:#d40000}.page-header{margin-bottom:20px}.page-header .dropdown{padding-right:10px}.page-header h2{margin:0;padding:0;font-weight:700;border-bottom:1px dotted #ccc}.page-header h2 a{color:var(--color-primary);text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:var(--color-light)}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.page-header li{display:inline;padding-right:15px}@media (max-width:480px){.page-header li{display:block;line-height:1.5em}}.page-header li.active a{color:var(--color-primary);text-decoration:none;font-weight:700}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}.menu-inline li{display:inline;padding-right:15px}.menu-inline li .active a{font-weight:700;color:#000;text-decoration:none}.sidebar-container{height:100%;display:flex;flex-flow:row}@media (max-width:768px){.sidebar-container{flex-flow:wrap}}.sidebar-content{padding-left:10px;flex:1 100%;max-width:85%;overflow-wrap:break-word}@media (max-width:768px){.sidebar-content{padding-left:0;order:1;max-width:100%}}@media only screen and (min-device-width:768px) and (max-device-width:1024px) and (orientation:landscape) and (-webkit-min-device-pixel-ratio:1){.sidebar-content{max-width:75%}}.sidebar{max-width:25%;min-width:230px}@media (max-width:768px){.sidebar{flex:1 auto;order:2}}.sidebar h2{margin-top:0}.sidebar>ul a{text-decoration:none;color:var(--color-light);font-weight:300}.sidebar>ul a:hover{color:var(--color-primary)}.sidebar>ul li{list-style-type:none;line-height:35px;border-bottom:1px dotted var(--sidebar-border-color);padding-left:13px}.sidebar>ul li:hover{border-left:5px solid #555;padding-left:8px}.sidebar>ul li.active{border-left:5px solid #333;padding-left:8px}.sidebar>ul li.active a{color:var(--color-primary);font-weight:400}.sidebar-icons>ul li{padding-left:0}.sidebar-icons>ul li:hover,.sidebar-icons>ul li.active{padding-left:0;border-left:none}.sidebar>ul li.active a:focus,.sidebar>ul li.active a:hover{color:var(--color-medium)}.sidebar>ul li:last-child{margin-bottom:15px}.avatar img{vertical-align:bottom}.avatar-left{float:left;margin-right:10px}.avatar-inline{display:inline-block;margin-right:3px}.avatar-48 img,.avatar-48 div{border-radius:30px}.avatar-48 .avatar-letter{line-height:48px;width:48px;font-size:25px}.avatar-20 img,.avatar-20 div{border-radius:10px}.avatar-20 .avatar-letter{line-height:20px;width:20px;font-size:11px}.avatar-letter{color:var(--avatar-color-letter);text-align:center}#file-dropzone,#screenshot-zone{position:relative;border:2px dashed #ccc;width:99%;height:250px;overflow:auto}#file-dropzone-inner,#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center;color:#aaa}#screenshot-zone.screenshot-pasted{border:2px solid #333}#file-list{margin:20px}#file-list li{list-style-type:none;padding-top:8px;padding-bottom:8px;border-bottom:1px dotted #ddd;width:95%}#file-list li .file-error{font-weight:700;color:#b94a48}.file-thumbnails{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.file-thumbnail{width:250px;border:1px solid #efefef;border-radius:5px;margin-bottom:20px;box-shadow:4px 2px 10px -6px rgba(0,0,0,.55);margin-right:15px}.file-thumbnail img{cursor:pointer;border-top-left-radius:5px;border-top-right-radius:5px}.file-thumbnail img:hover{opacity:.5}.file-thumbnail-content{padding-left:8px;padding-right:8px}.file-thumbnail-title{font-weight:700;font-size:.9em;color:var(--color-medium);overflow:hidden;text-overflow:ellipsis}.file-thumbnail-description{font-size:.8em;color:var(--color-light);margin-top:8px;margin-bottom:5px}.file-viewer{position:relative}.file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.color-picker{width:220px}.color-picker-option{height:25px}.color-picker-square{display:inline-block;width:18px;height:18px;margin-right:5px;border:1px solid #000}.color-picker-label{display:inline-block;vertical-align:bottom;padding-bottom:3px}.filter-box{max-width:100%}.action-menu{color:var(--color-primary);text-decoration:none}.action-menu:hover,.action-menu:focus{text-decoration:underline}.js-project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}.project-overview-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin-bottom:20px;font-size:1.4em}@media (max-width:480px){.project-overview-columns{display:block}}.project-overview-column{text-align:center;margin-right:3%;margin-top:5px;padding:3px 15px 3px 15px;border:1px dashed #ddd}@media (max-width:480px){.project-overview-column{text-align:left}}.project-overview-column small{color:var(--color-light)}.project-overview-column strong{color:var(--color-medium);display:block}@media (max-width:480px){.project-overview-column strong{display:inline}}.project-header{margin-bottom:8px}.project-header .dropdown-component{margin-top:4px;margin-right:5px;float:left}@media (max-width:768px){.project-header .dropdown-component{float:none}}.project-header .views-switcher-component{margin-top:4px;margin-bottom:10px;float:left}@media (max-width:768px){.project-header .views-switcher-component{float:none;margin-bottom:10px}}.project-header .filter-box-component form{margin:0}.views{margin-right:10px;margin-top:1px;font-size:.9em}@media (max-width:560px){.views{width:100%}}@media (max-width:768px){.views{margin-top:10px;font-size:1em}}@media (max-width:480px){.views{margin-top:5px}}.views li{white-space:nowrap;background:var(--views-background-color);border:1px solid var(--views-border-color);border-right:none;padding:4px 8px;display:inline}@media (max-width:560px){.views li{display:block;margin-top:5px;border-radius:5px;border:1px solid var(--views-border-color)}}.views li.active a{font-weight:700;color:var(--views-active-color);text-decoration:none}.views li:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-right:1px solid var(--views-border-color);border-top-right-radius:5px;border-bottom-right-radius:5px}.views a{color:var(--color-ligth);text-decoration:none}.views a:hover{color:var(--color-primary);text-decoration:underline}.dashboard-project-stats small{margin-right:10px;color:var(--color-light)}.dashboard-table-link{font-weight:700;color:#000;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:var(--color-light)}.public-board{margin-top:5px}.public-task{max-width:800px;margin:5px auto 0}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board tr.board-swimlane-columns-first{visibility:hidden;padding:0}#board th.board-column-header{width:240px}#board th.board-column-header-first{visibility:hidden;padding:0}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast:active),(-ms-high-contrast:none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}.board-column-expanded-header{display:flex;align-items:center}td.board-column-task-collapsed{font-weight:700;background-color:var(--table-header-background-color)}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;transform:rotate(90deg);transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon i{text-decoration:none;color:var(--link-color-primary);font-size:1.4em}.board-add-icon i:focus,.board-add-icon i:hover{text-decoration:none;color:red}.board-column-header-task-count{color:var(--color-light);font-weight:400;font-size:.85em}a.board-swimlane-toggle{text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:none}.board-task-list{min-height:60px}.board-task-list-compact{max-height:90vh;overflow-y:auto}.board-task-list .task-board:last-child{margin-bottom:0}.board-task-list-limit{background-color:var(--board-task-limit-color)}.draggable-item{cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.draggable-placeholder{border:2px dashed var(--draggable-placeholder-border-color);background:var(--draggable-placeholder-background-color);height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;word-wrap:break-word;font-size:.9em;border-radius:6px}div.task-board-recent{border-width:2px}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-board a{color:#000;text-decoration:none}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.task-board-title{margin-top:5px;margin-bottom:8px}.task-board-title a:hover{text-decoration:underline}.task-board-saving-state{opacity:.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board-avatars{text-align:right;float:right}.task-board-change-assignee{cursor:pointer}.task-board-change-assignee:hover{opacity:.6}.task-list-avatars{display:inline-block;float:left}@media (max-width:768px){.task-list-avatars{float:none;display:block}}.task-list-avatars .task-avatar-assignee{font-weight:300;color:#999}.task-list-avatars:hover .task-avatar-assignee{font-weight:400;color:#000}.task-board-icons,.task-list-icons{font-size:.8em;text-align:right}.task-board-icons a,.task-board-icons span.tooltip,.task-list-icons a,.task-list-icons span.tooltip{text-decoration:none}.task-board-icons a:hover,.task-board-icons span.tooltip:hover,.task-list-icons a:hover,.task-list-icons span.tooltip:hover{color:var(--color-primary)}.task-board-icons a:hover i,.task-board-icons span.tooltip:hover i,.task-list-icons a:hover i,.task-list-icons span.tooltip:hover i{color:var(--color-primary)}.task-board-icons .task-score,.task-list-icons .task-score{font-weight:700}.task-board-icons .flag-milestone,.task-list-icons .flag-milestone{color:green}.task-board-icons{margin-top:7px}.task-board-icons a{opacity:.5}.task-board-icons span{opacity:.5;margin-left:4px}.task-board-icons a:hover,.task-board-icons span.tooltip:hover{opacity:1;font-weight:700}.task-board-icons .task-board-icons-row{line-height:22px}.task-list-icons{line-height:22px}.task-list-icons a,.task-list-icons span,.task-list-icons i{color:var(--task-list-icons-color);opacity:1}.task-list-icons span{margin-left:5px}@media (max-width:768px){.task-list-icons{text-align:left}}.task-icon-age{display:inline-block}span.task-icon-age-total{border:1px solid #e5e5e5;padding:1px 3px 1px 3px;border-top-left-radius:3px;border-bottom-left-radius:3px}span.task-icon-age-column{border:1px solid #e5e5e5;border-left:none;margin-left:-5px;padding:1px 3px 1px 3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.task-board span.task-icon-age-total,.task-board span.task-icon-age-column{border-color:#666}.task-board-category-container{text-align:right;margin-top:8px;margin-bottom:8px}.task-board-category{border:1px solid #555;font-size:.9em;font-weight:500;color:#000;padding:1px 3px 1px 2px;border-radius:3px}.task-board-category a:hover{text-decoration:underline}.task-date{font-weight:500;color:#000}span.task-date-today{opacity:1;color:var(--link-color-primary)}span.task-date-overdue{opacity:1;color:#b94a48}.task-tags li{display:inline-block;margin:3px 3px 0 0;padding:1px 3px 1px 3px;color:var(--color-primary);border:1px solid #333;border-radius:4px}.task-summary-container .task-tags{margin-top:10px}#task-summary{margin-bottom:15px}#task-summary h2{color:var(--color-medium);font-size:1.6em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:10px}.task-summary-columns{display:flex;flex-flow:row;justify-content:space-between}@media (max-width:768px){.task-summary-columns{flex-flow:column}}.task-summary-column{color:var(--color-dark)}.task-summary-column span{color:var(--color-medium)}.task-summary-column li{line-height:23px}#external-task-view{padding:10px;margin-top:10px;margin-bottom:10px;border:1px dotted #ccc}.task-form-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.task-form-container>*{box-sizing:border-box}.task-form-container>*{width:1%}.task-form-main-column{width:60%}@media (max-width:1000px){.task-form-main-column{width:100%}}.task-form-main-column input[type="text"]{width:700px;max-width:99%}.task-form-secondary-column{max-width:250px;min-width:200px;max-height:600px;padding-left:10px;overflow:auto;width:20%}@media (max-width:1000px){.task-form-secondary-column{width:100%;max-width:99%;max-height:none}}@media (max-width:768px){.task-form-secondary-column{padding-left:0}}.task-form-secondary-column label:first-child{margin-top:0}@media (max-width:1000px){.task-form-secondary-column label:first-child{margin-top:10px}}.task-form-bottom{width:100%}.task-form-bottom label{display:inline-block}.task-form-bottom-column{display:inline-block;width:49%;margin-left:5px;margin-right:5px}.comment-sorting{text-align:right}.comment-sorting a{color:var(--color-medium);font-weight:400;text-decoration:none}.comment-sorting a:hover{color:var(--color-light)}.comment{padding:5px;margin-bottom:15px}.comment-title{border-bottom:1px dotted var(--comment-title-border-color);margin-left:55px}.comment-date{color:var(--color-light);font-weight:200}.comment-visibility{color:var(--color-light);font-weight:200}.comment-actions{text-align:right}.comment-content{margin-left:55px}.comments .text-editor textarea{height:90px}.comments .text-editor .text-editor-preview-area{height:90px}.comments .comment-highlighted{background-color:var(--comment-highlighted-background-color);border:2px solid var(--comment-highlighted-border-color)}.comments .comment-highlighted:hover{background-color:var(--comment-highlighted-hover-background-color)}.comments .comment:hover{background:var(--comment-highlighted-hover-background-color)}.comments .comment:nth-child(even):not(.comment-highlighted){background:var(--comment-nth-background-color)}.comments .comment:nth-child(even):not(.comment-highlighted):hover{background:var(--comment-highlighted-hover-background-color)}.subtask-cell{padding:4px 10px;border-top:1px dotted #dedede;border-left:1px dotted #dedede;display:table-cell;vertical-align:middle}.subtask-cell a{color:var(--color-primary);text-decoration:none}.subtask-cell a:hover,.subtask-cell a:focus{color:var(--link-color-primary)}.subtask-cell:first-child{border-left:none}@media (max-width:768px){.subtask-cell{width:90%;display:block;border-left:none}}.subtasks-table .subtask-table-td{display:flex;white-space:normal;min-width:400px}.subtasks-table .subtask-submenu{display:flex}.js-subtask-toggle-status{display:flex;text-decoration:none}.task-list-subtasks{display:table;width:100%}@media (max-width:768px){.task-list-subtasks{display:block}}.task-list-subtask{display:table-row}@media (max-width:768px){.task-list-subtask{display:block}}@media (max-width:768px){.subtask-assignee,.subtask-time-tracking-cell{display:none}}.subtask-time-tracking{white-space:normal}.task-links-table td{vertical-align:middle}.task-links-task-count{color:var(--color-light);font-weight:400}.task-link-closed{text-decoration:line-through}.task-links-table-td{display:flex}.text-editor{margin-top:10px}.text-editor a{font-size:1em;color:var(--color-light);text-decoration:none;margin-right:10px}.text-editor a:hover{color:var(--link-color-primary)}.text-editor .text-editor-preview-area{border:1px solid #dedede;width:700px;max-width:99%;height:250px;overflow:auto;padding:2px}.text-editor textarea{width:700px;max-width:98%;height:250px}.markdown{line-height:1.4em}.markdown h1{margin-top:5px;margin-bottom:10px;font-weight:700}.markdown h2{font-weight:700}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;overflow-wrap:initial;color:var(--color-medium)}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.panel{border-radius:4px;padding:8px 35px 8px 10px;margin-top:10px;margin-bottom:15px;border:1px solid var(--panel-border-color);color:var(--color-primary);background-color:var(--panel-background-color);overflow:auto}.panel li{list-style-type:square;margin-left:20px;line-height:1.35em}.activity-event{margin-bottom:15px;padding:10px}.activity-event:nth-child(even){background:var(--activity-event-background-color)}.activity-event:hover{background:var(--activity-event-hover-color)}.activity-date{margin-left:10px;font-weight:400;color:var(--color-light)}.activity-content{margin-left:55px}.activity-title{font-weight:700;color:var(--activity-title-color);border-bottom:1px dotted var(--activity-title-border-color)}.activity-description{color:var(--color-light);margin-top:10px}@media (max-width:480px){.activity-description{overflow:auto}}.activity-description li{list-style-type:circle}.activity-description ul{margin-top:10px;margin-left:20px}.user-mention-link{font-weight:700;color:var(--user-mention-color);text-decoration:none}.user-mention-link:hover{color:var(--color-medium)}.image-slideshow-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.95);overflow:auto;z-index:100}.image-slideshow-overlay img{display:block;margin:auto}.image-slideshow-overlay figcaption{color:#fff;opacity:.7;position:absolute;bottom:5px;right:15px}.slideshow-icon{color:#fff;position:absolute;font-size:2.5em;opacity:.6}.slideshow-icon:hover{opacity:.9;cursor:pointer}.slideshow-previous-icon{left:10px;top:45%}.slideshow-next-icon{right:10px;top:45%}.slideshow-close-icon{right:10px;top:10px;font-size:1.4em}.slideshow-download-icon{left:10px;bottom:10px;font-size:1.3em}.list-item-links,.list-item-actions{display:inline-block;float:left;margin-left:10px}.list-item-links a{margin:0}.list-item-action-hidden{display:none}.bulk-change-checkbox{float:left}.bulk-change-inputs{float:left;padding-left:10px}.bulk-change-inputs label{margin-top:0;margin-bottom:3px} \ No newline at end of file diff --git a/assets/css/images/ui-bg_flat_0_aaaaaa_40x100.png b/assets/css/images/ui-bg_flat_0_aaaaaa_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..a2e6bfc085f51b392569e58b72d454586b900f61 GIT binary patch literal 86 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F$P6UUt$JVyq?iMILR?p^S|wQb`zMgg=jq}Y hQo)$KfKh^VF#`ir1OsEt!yO=<44$rjF6*2UngAlC6uAHZ literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-bg_flat_75_ffffff_40x100.png b/assets/css/images/ui-bg_flat_75_ffffff_40x100.png new file mode 100644 index 0000000000000000000000000000000000000000..e36540bbf980dbc5369cef72757e55fdebc9177d GIT binary patch literal 74 zcmeAS@N?(olHy`uVBq!ia0vp^8bF-F2qYNp$opRhQi7f?jv*C{$v^m+uBf`73>0EG WHSv>-*Xh&dAVr?8elF{r5}E)sHxhyX literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-bg_glass_55_fbf9ee_1x400.png b/assets/css/images/ui-bg_glass_55_fbf9ee_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..579a9ca8f3723fa6494004726725ac1bfd26a6bf GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjg9*rbU^TY~NSS%MIEGX(zCE>(mqCHUCGeYk zSg2OxA?{1N8g@QUUY^gU%l)Iq ozyJU3jb;n9t(4}h5a~~1P+GZC=cQ8M51?WOPgg&ebxsLQ00h|@+W-In literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-bg_glass_75_dadada_1x400.png b/assets/css/images/ui-bg_glass_75_dadada_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..b286bf07c1ee1bc2115755110f40ac5b5886d3b2 GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjg9*rbU^TY~Na=dIIEGX(CRcrW!YRzm%q(Wp z<^ECQ-?z8YGtyF0Tn;)gEKbZRvAJ_NO{71GfhULK{+eKh=Rl1Np00i_>zopr033`S A0RR91 literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-bg_glass_75_e6e6e6_1x400.png b/assets/css/images/ui-bg_glass_75_e6e6e6_1x400.png new file mode 100644 index 0000000000000000000000000000000000000000..607e1f9eebf48097814ab1e4b67e66c2bfa5fa2f GIT binary patch literal 102 zcmeAS@N?(olHy`uVBq!ia0vp^j6gJjg9*rbU^TY~Na=dIIEGX(CRhFbrYp?M%q(Wp z<^ECQ-_OtLGtyF0S}xu&h>_!%aFstDC~nBGgp1>gsQS`NK#dHZu6{1-oD!Mxj94?_dDOn|k{@Dp8x(k4Bzrp6ofn=1 PG=;&_)z4*}Q$iB}bhRe* literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/assets/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png new file mode 100644 index 0000000000000000000000000000000000000000..d3e97a6e279e7f25b5202a09bcde094ff41d6f6f GIT binary patch literal 86 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 Z4M5tcb1h`_*53XEEKi>^dwCKQ_g~4IP literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-icons_444444_256x240.png b/assets/css/images/ui-icons_444444_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..19f664d970194372c3228494e34ac01d611a4d45 GIT binary patch literal 6992 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
`A6pHt6IR6lNF6^&(ovlbP|R$VC3 z#Kj`X&Lyxos$n5C#2uMfZZsvbj)i z+$1(n=6q63)^IOf+^1x0R%Jn?&*qGX`{G5;8i!QR3oXJfw~DcSI#S?P@6TZI#^)nO zwBfhXH&dU8oVeLDqb6r-R>&#mCM;g!tPBL&yUn8|aGk2Z;+<@plmQtZfs}+*Cs>)w zJK)hUcu#Axe^!n8SWqhu2KsCjRL7fdv|8+ohg@hb&_b}-xyXe1kWcrO41<^<<7&ZQ zz`{1CCxUin(^K(w@eEMT(^}Rul1#tbP)zHUXQs;T&4gaG6=ckgoPc(7n z*nP2UyM#N~U!%DWT-%~=m$RLWZ{BbaHA2Pw2Jkcvd9BQ+`pcKWLIsiFyNfBO1oInH zC%Gdn^(k@+F)a~?>K~};Kc#WB z^{nk2D~t~niv&TrKOd+v`FXMR=DN+SgIjGD%g1Ye+s=6>ipoV8mP1RD?mg8&DMU=q zO^zcP7!_*&@^Np)=&5r*;QialnPQcsta!aRkF70#VabT)k?5u-;YzptvmB<#WuY3R zCHt}suyr7}=u<>SA!(YcV$8U;juwU8^~d)`Oo#4yDH(yMUn7ajXIk654oRQH1d5R5 zWQl6N4<$AfH86-?|I)&>?_Yvz75C_;;V8U9u;+dh-hiJfqm(Gh|2>&!^|uw8Sf!N& z9WrD?bX<1ttD2$v^8DeVAp2*9cB(6Un`IlxfzXmt;l3tj(F!-4ZXJajoUOha-mse| zA!`x7DG7-qhC}*iY;B^j1mbc$3RLMO^k4L=lXULiXz#zsf{Fz|)4^5x2{aQ>Z*5#E z?vdX&8)WO?ucP>9E?pIzt1RwC{5rPs{h+hGPYV7P+>A3xJe?O)$@6T{rX>PzP+?q> z;;#q;(=T~?W`h>R_;Ks-_~}fi2}AdrFMFIdE1o0}W}xGQU9x@`PQY7H{;8Qo?e<{~ zpKwEX7LjJY8@J`OP0iy!ifw(LLS^?8cYRZLaU*tLUX+^zUp#OYztQ*IJJVtGpa=vt z5kUQuI#p2*!|A_c$k4oQv63yk2dbsn%h^HtD>s48SH&Fx^p!EU%dtP`gZW_eQ~lD% z=1H@F2j8g~?=|<`-+VUSqGzqu`pWji0Wn_*0&TTAnpzFDp}&mJKU@JgOf_ zb;2;7x~$&YR>MwuN4Y+=+#p^M4MJWZpFdmo<}A=FxP1T!{_NcOYnv;lm?Pr55-@Rp z_Zw(#Yp@9w*S~`|Bbq3EEti?agR{4d!p#l31aBX|JVE(dSOq@?uUcKRg4>v`Ea^WP zvScvPt5m&LU<@#dVu1HHWDd&M<}MA}MO{nFS4ALwSEZ&SkQdw*mL1lF(*{jkf#MMB zDV>%pS4?g14ZS1Mk<_u3q$@5+yn5KrVLW`$+I=o&gd}Nxq`=Yx|=X%|V^yMSA#&uGDocw=YDx0(zS#)KM1H zcoDN9KqHH7E90e0OzF(xVRN5$N2dQJSx(Pi?9tyoYdK*!kX=m5UqNr4#V%tnAXyf7 zh@#7GW^T>=a7yJ|->AUNYkTaF&Z3y^_JZa&v|Chg2WGnNI7g+Br+G{N_$LYQcblb3(QrR$@sbar5n*kBIjSafWl~O) z!}!-exeo$ZFhbe1GXl>SNQJ|K(@!Nl$wETs7nUKenZX|bynIKP@Sc+xXhW`-WP{Q# znGJRZ{n8~pVjUN|nJx6cPp1T|E}cOX`x_uF310qRZ&nkGYt=14@CQ^2_-^Syl*w#c zhTX`qiW%(KoCFMjV2^Gg)Q!t@HNpPl-)M0MK-J{85Q@g-{rMHerkBXXsBTNiu<+oQ zN_*2Wi0&X+{H=SoHscgP)2!BpnBWcAJ{dRSwjilve-qFi$_rqJz%pyu&v}Z&W{I9g zD~K=^RFMj1UI#(9fbZ0Bd`?LKmDIU1dz2C>Hn(By*(zLjIogqGttE5VO@Opqm8e|j zn|*e5+1qX1hqVBw{@|Vnx#PR}5cHP1IXLDTH7(IIU^kG^z0C}zV3r|Qg==OHDzt3N(HOX{{aG= BEO!6^ literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-icons_454545_256x240.png b/assets/css/images/ui-icons_454545_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..9df8cc54da7825c579658b77c94c821dcece6b29 GIT binary patch literal 3686 zcmd^?`8O2q7ssDj7`rj{vWH}+lC6yLk*z3XuZhZ{{+*X(~7bqpxB1p}P3w zjKxk`Sjo65YoAxYdn6&Y&F^E+2YoZC=7uP8&5M+h@@kb`qT&({C-hN)=ac6Z0Ta3A zCjw*Ad}dxvAp-JqtS|6C`doJ}Z>_SHnw~atF_O#&eC-F>9yjFlB#dqj zl(Y%!OS!UFw&ri^G8xE%;`z&ExJqcNHpw4-rbd!p=)$kgzHGyG=ZN!d{V8}Eem{9p zZm=zETd|t06Hhv37?JQYXs+-3>~p8kDky~^l`M)hM5<1%#cR@>g0+1D`>h4Nm`hC@ zrF{d=#FXA|!TM4H32nq-?u}|aPYi7iiIkfrbN2+TAJOt4Du-i~F#A7~FITP({thve zk-5+MvNiwJEAH!DYDH!bKhG}@aHESZpZ)_G_SxI%I0=ekWZ(qn8V@-$n>bkTgtou^ z>CzOpp3Q2A$ZCo@ZK`HN-WVM?NrM#yJhQ>NS@XvW@ExroPE}2l$lO?N2ft*3gqn{* z=67ob%uV)K9N#q8;`p7U5Pln2kwK!U;!BmM+Su)Jq0Yqy*rrC4NS9 zu6O&$uos05yDoyyg7?>ZJvsVxAYIqlPc1(IofzxKyB|Wt4*a9)gdCoo3X>{YG{Tf_ zhuP@&s9lB=9t?gHkt?G0>OiW@M>c9r2}0K(HRGEiTxT4zp=VljIqWjV-&Q7$K-4!< zHGDPVd>^bT=${*06)C&h?e%zOG%>VZp#dsJ(Hc+ud%+taV0 zsA8Vg3O{0`athG^rJ|{WwY3Tl+O48Rnu_ghLbU zp%h20#SLbOs9ihTt3Z=2pt}}@E$PI)YS{e7%^6j z0&Gu6j!C9uiU}hJfi3tVW^TfWgnc6EoFp85->uShcFAKtZ+(M}>#lxp zWc{@I$Ln>e<0-sj;R~LVQ47Jb@UAXJCpP5|Xn5jiwsKQ!$(k?u+qfvZ{g$u!9ltS!=l>F6^D!Yy-``6;tX9Ak%sl0uW-gg6o z(wj9^ljBM|SS=?{-E(o`%RJ$Ke*+rsksv1CQ7?>62k({Y1u6l$nipQ~C5=`F$ztjt zxvkA&A0_I9_$Me-Ya1x%0&eljcBtfC!WBegtgJ`+0ZjG@on#? zO%LJ|1Z8=KVsD9SWC3ML!4W_brl?FRxZdHDxtPwtyCj(z6oeOYozp!CAbMo~%b{Gx zF*!2OP|<&$H}EN>MGWrU+MPPR4a^rgboQlOD-hCkRu|hgz>k#0-ym?xy?~g>DMNBV zP7aCCb`xbBHiNbWtB?+k<#fGI|27Cx8^8`{R+%ZMZJu$g!Wbmv0-?H|oAfIR*t+_J zJ5&{yDh(D3qiwFWJ{^+P#lJ(?)q}Y(3YFdipM>JBruaAHcZ8#J%9F0-pj76iG92&b za;Ul%zm~>80&rYtY@}T$0{t_v#{;qK$quIwUYd>(5Bb2-lA@XejOLHd|Jdc#1lFW4 zi-}b(!r@mdnRPJWk;`4f`G-ZminpZ~q3{_9H`P{gw4wsCob79AT|zjdVULZ}8+n&9 zWO^vPT;}1Vg-3Rg<-(&hjGiWbpwuStW|4OD=_UJTLKbwVbYPb$d1qw$HUKmms*yuNdIC>{Q{Xg5M5@= z6MDQ#z{0(xQNU-;ePIX>r}ofjFUzA_Q>L4$eMH7{viC1I7?+by6ylRzd+1$$;+ogi$fu1J|Vvv84Unsvc2_f>znwc zembA4vRnP2R+}{R$Jw54Jz6ikJsQ#xD-sv43v_-B`P|arM}Lw=QPgy6LmHSI8o6e1 z53nuSRs$8Z8ldmPbsYCd%6>=D%3H^&rA1(wJu+tW;7Y*Mne#0s{QF{4`6iwg{;qLH z%oK;gO8kFKRmA@FH1B{CRo4%HkPcaA7eb4vFp`?Ev!$!hl{pbTZsUCQ7FM=a#<+pq zyVXoA?OR0FEn0p&p}!%r)gfQ01m{ke2t(mV+YNS}bi4^Q>-t$Vo~;0S)6#~ByCA0tfJoUoq21*uw{JJzcr zj-E{u5=n|X0}SP&_wyoO+FL*)f^M3=X%!{w?g!?t+)Xa_9WT1jTVi6oFdFGuDvQ@S zvIt351g-%R7GiR6m6~BVDI8dm9XhW%I$8p?3#u!kGtdqdi>yw@6S7HqH-xCufS0{O z4WC^ypbJhS_2W;i1@k;shxX9e9g$02G`Ab7HOu^JzD3yg5M$MQrqKx1LxF?<4a~e=F z9DW9VjAR3_7OjWoa^~Hs<({m&&SIz)6m^gDj_|s8%P4h?GdD*7>I+nHx&jW{Vm@EQ z1s`6SxIfWV{U~lRoRbUU4*@$TY_gcl54wJ26Z?lGZvKu*tYr&UEYsuz@N+F*gE-^% zFJG`|FJlFO=L5PT={%tn4rEr(tQjNeOzSotV?ZAA@H)S-cUF{ z-ygn8`ZX;Iw}-Qh6mNDnN4|~OtAd5_a9?A250XqmXHeMMv+Tpy#Xn8ZzSxl$I@D@5 zIGo{{_#-j_34YE`XT>aFl`HPRgJbR0>j{9#<_GPlUe$hyVBSK=aJ}iFV?KO>0r=_u~4ke?Qc{Pdnc8HssAk zJ4_5CF^8URpWp0ow318e3N+J0H#?<_B2>^f!#k{hSkqYA`(DbW9)Z*{nBLmErhbpfET<)gpO_~~(<=sJ5o!s(jvVarcO+ac9WZT(Ta8vR)sxZ$lJa2iMc1}x1_{hNLM#w>`C?Uj9oEKG!BRq3eW}7bJsY4$n-cO zUXdxqFRlFzoW9{Sz&6PRzLaf1Rk zEjp8t3(M~-$4xJIhaEGd&=1beAX+ z(lK~>?>YDPJLlZ{$9~p+KA*Mrex9}0`tH5HJ6cCenV0}d00027s)~Xx0D$hgz#$&? z-BU69h0L7*TdON800z}@5vq4@_#P@IUUyREznfCOvlIY89aR-%^?m1my$^P=Rrc)}nev#FJE^KgXEw7+V@d`y^D~bgP-i_Wna_cf}GegPJ!E=3$vK1!@mU6ht2U zuzbkL;UpS*N{L?pd})D%hu?mBszca0;{P^PZhSjZ-^Ji#klR({E0N8!glp&R}8On1Zu%Vlfmq>R|!%`>K&;<;^%`;tgP#y z3VSP2iXADxc_$$Gz=<5_^^6L0bk<4N@8bJZV6X^^7z~yl@c!`_fj8leDu_{)9%BxZ zJC2H)z~}1L_RruAHeI$Fu4yTBDcc~CD+CP@j6Eona$uCoU)1ClD$=cT&x~N=-}3jj z>my6Cz!y4a(PJEf);q0K7s2+!Hq7!~gET%0 z?zknmD^VA{58Nl{y9Vj6*t$G+1bI^j{6Wl-!hZz2*-pl4J1n{CuouyXXaL~d9EO*W zG2!Z1|K+Gg+l=bUc=LJ}i0187QG4uA=ay(#I)tRvv&XoqJiC{uXVshl&6sQC>BDg& zr_=S1v!^oP3)Z=+5__#d=*=*Ryu#7fg)Ti;It&z2s%sMKbZ_HbZI;|jy|s#3oMV`<)U zrzxj1(AUp|W8#jcsCPaQibx_xqrZ==txmeG_hT+NK|H`kn_X0v;mu4{Xujk-ghs?gpz)|O1n_bKTP%PxX@D0t-XuS7`#^4C`Th>#}x1zyGK9B zYfZyI=p(b?aQiRyZg2~b>GUbdi>dmlIF4xgY)g_wz=omP06r>Uw9%5ritF949jySn9 z)CFQK;NRCE7uZ~xS*euaCy~WKf=oN=h%i8(=9kcA?q)jpX=pU0zb>oUU7O?lva8Z1P(U zgUClISn&?tu=XLtHovoEHu)X;9ibq~Yx`Yze><1aEM-IA6JtO$1P>%Hgt= zSVxu8I$eS5g*}#ZWT~E)aA_cfThs%ye280;g`0IsHmtt3mW?7a;;v)A& zn=GhJ&2@|S^S>x!|44P~TQvgP?#WPod-Xq9_b+iCp$$Cis!^FI+QjJ?PIVQS3)9j! zkD^?1^?N{D#~79;E9P_LR+v5x-P%pYQyrwPEFHWeYWa?PD4vMZ(W>TZ@k`6!JGN36 z`wJpZKjutn(YWkGDXdyONIu^=UJ!G{l#m0 zF3Bz8!G6lJQV&GWA@TCogngMqdq?wj{c5w8L3Aq!ftli2zEmE|TN^=Br4>b^ju}}N zKy@kC*vveIH92ocwvhMSV?};NqbUXSQRrG zf#X81tT%#L2d{@ye*A{gJeE>l-IvO@@<7ABh$x)kZi~s#3mxvE2_uGO!|W$So?p0t z7#0ZrWlyQj!iKY$JEnhEzY3UR{6pJN;zg_ghS7^Il25+X^B=O_ZDigouQSc%zW&= zuzi)yz6VQo4$3!`-^6%OSWx}A?b=KbM0Huc!%ym_SvuzHO*fpeR{CRj^wL+M;oM8c-VS0)JTtK;Tqg3Z}aoFXBbDRfRdK8YVkyg6C&f=)S z>CeM*j&(=1XrR11XI+G-x-Tt+vBuV{aEUQEdpcs)K1#AT_MOO>J$4>JMiSydM=2Ay zbU8;l$JC}z%0eBzQo!9M>&CK*%1b$?cCaIM5b3=aqd$P}`{j0Jo$+h$obk8%hllbF z1Xi0YcTROu(VTJbCSN2<`C33&AR#Q*taUGPx+j)gxE-@{JVXz@=Lasz5@QlS zW51~`%;Y9A#wgv5_FRDyG;>)VgXzY_uFa3R8W~i})Wmwof{MU=ZT|3nlYTg8yE?~c z!@ZHK_+GPImT>OygJr{XtWBO014&@^K^xCk#>4P0wNaR3I_!3WM55LGsu0DqokzYX zEIN1?)-yIg$r&%n+#gnilxJPkqk>&Tqs|RpTAw>N@u{I>U}Xx&ba36HOrr>r!?Q*E_UQb}ZVN+#D%9LVlfdMh!Z~)o zL)*2gC^5>ggK4?%N4i|=;OofUFH!ibGhr)yPVBlEMLdGDbAeO5SNs8tYo$ZCR^&iJ z2wZdyEWy?B_aW>50ne=Y1$GVxl`JJHwX4%b(#&lc(G@n4FU2`q6YG9C88(65rNbaz zS3X0KS8OR?$=tgjRKG@i*LQ2Ay`*N`P*N{8vCGgzMpcFIeAWOzifOx4U!<>+d2f}} z#m&Y2O}lo$1fGqrrGE0l=a#)8NbKg}N^Fi&$x@Q(RrKB({s`rI%lyWIOx@vRfIm4q zVD}M20??;YeMZUsz?YcS&@2NmM%K;u~hzRl_ImCm>5-cr>?Tp0s8S((UA2c*iHa z5;4NVe{5*HJNO+Ha!Wol+Y4cnbi5UV=h+alPhCze@=j<|#T_0Gyq$KW%-8@g*>uG_ zkZ7gq8KswR7Ar13%59)q?iZI{U-5lQB`ng3ZDz`D?8*c-!Zay*BXe<(CK&m05J3f@Nw*a2B6ea+PN?ABU*hB5wMKGW!V%^|S6I zXm7?PFyk$Cj^*IN*F9}5?U(AqI1FoVCJwZJt1u5SlZ=BH>_fyxl+DO@0Oqe=z*}%5 zDF`%&N5!>!VE6uhV4Ba7`Le;MXMgP(og+Qz&4r7i=!OvC#GHOtX&1ogp=|crtH5wQ zC>6P@jBteg$4ZyHh9rH!cZDMig{l5JIMfto+D)d=tJpu7-FDS0WKqasVLKb9|P+69?5R?+1n zt=~T6-l;eYg@bZZZiS$)JkbW!kNFJX7BoFC*SHM04ZHfgb&7*k`Wuub=hwJrJ0FhtjtV#Mdi@xgeO~;rYIug6Pg?7FlG_~`1K-kZiQmc!n>cIFRtYpS z#7IvUexJGIG<7-BTh^J#*#g5d=TFkqXI43uPSoP^8C1?>cM;#&y{an=T-}K`^x0*~ z!riUjdG=gZZORscVWj7%JhB2qv#jw8 z%gC>W?9T_YhZ#?bjZV)wjaJP)VVE2I!{Zn-Mi4xShElA248%NFJ!Ku43Ea~9zLC5E z^LeoQb336lpLtI5jF)`cB7Bb5NUU8*=K@{zy0N;FMDuCcp<_aNeN`?_sjH}87~vaF zDF@?r2v^{a`9TQ8xFt#bKIVcHFp#;kfnku_z1#aJM*E7ak~R?@5x-Q&)bAV(3buk4 z^`q`bC0q)L*Y<^_FN|X|(t@axK1okN{_@qzXWz@QPLY`lt~zwwUJq70>w*$W5WQp0 zbfl>6XaCb{+N9+d8a%NM-@tWSwRKzVxd;~2w}0j8g)*J%8eSY4?)aq|<$@P=W2wNW z`YW#vGu-VCN;A3Whv;Ln#{S~QAr1-FlzLo}-@;>YluPRb?1tM#sIeWh1h4VKt zk!eqOn7I1SEZxL`Kl{md!!S~mT5znCR?P4O@ApHGB*#^`HF}EMbnUHdiN2zH=DrD6 zWWpW&P3PKP0#rY;Q!sM`OGPQq;NfGd>_=P1uMp*1HIcK(a=dRRzs^nSJ7)|9th(5f-@HB6ahY`Tq5e(8^X3AwrK&Lh*`ic@Vig$o8u0 z7ye%ZmE%Bvg%#B4=8~bkB==TppU6Koxt(u!xc_X~-KPdlX!RkoT!3*DY$zm%DHRPN z;$#8otkgDg33Xxu*_DG(qg?98jW7VMpmlDX5 zyT9Q~B_N(+wu$BxA>M7-GBa1X|NKp6Kl9yI=I8Xu6cOT2GG^89pVr8`Og`>}o>i7# zE6MOvB@oxQM~-*NdhbnvbU^h0x1eq7fJvlnVLFRbE~nM71X*_Gu;iWE?7BPY%Ov4k z4q)&BtyuJob#%#>hD2L%p>e*g$_md;>DHSdvf>iGo+cLIa*yXK6{mnFW-e=>Wld_ZAsBSQ;W22f{hRMPmO ztY-#kV;`U|e>$6_f}N`_P(WZ^+aS+Qb>g|bcDaR z$TuocSxmkd5lNTV%|c%$IG10xe&as()Ed3Z0962whW7JLP9eRQkmw|B0?^nx;oq6v>I zAPKFK^&y;LYWp$akY_s0=byeGT}E5QS78oewH_MMp?c^2BUMoT&tgtF--tj+>=%!QbLmIS+>LR6d*zF7g6@a zf9)Bh?_4*plr5;;DP>?Qom-WACIvWtXy<2IAco-()E3WP`^?eIFA*NiHz{vWbrMR5 zr%);Ro`8_jcc*w)(Eq1R@35*5yW=IJjQN$hMAAgg9ct`}wym7f3bnB$Hv$VmfcLJ5 zs28U2yaoBH&`9YaaDLcGFn@jV?52WuY1^S@gtfPug_DcC#@;=4I$7|Og$HY6!`(3tv2Qdk&=PWG zN-%ke*A{HyG8g6K(ugQ$!8bvDGA-{1$*%)KEl5GN9e$;xpi_g^_P}n6(h?87D>vxZ z!aT&~%7|?=Msf@oe>nD%5k-qZ!b8F@vQ(%n`{cSOM+x+4=~?O7%z1;2eXmj!s>jP3 zv~|&+1TZ+!m>*{9Eaa$IT@bmMe&GG2qF{E{j?vBUmXHbMqmxIao5ClbhGlRUD>$Ix)$+j=ai#?gQeLHNts>;#*8hJX zO5u6p_112e>Ul9R2YsB3T+ofER6nN{r-(%PmoL;5>QwFE*6ST6CQ<>kSdFs+6Zg-F z#Am4;T6!eQ9e&~W`-BYalLTyA~?OO z(nLw9$z@~D_x$;cNXUD|0fy_+aF~$0c2yaP-VEqq}MnkTeH%I_&ZMtDxvTv@Cjb7vuiOK(lKY^u`2VH z=!wx_|EVx;fc9wRka;Z7f9mTpJa9s6$vuI@IDc7FeA0231z5D^y^`iH^s za4y-M0nOht=(*b=e5_vB0+9(YZ004O5 z0=B6sF6Jq1o#TrQ1|vN)WKbaN|Hpq7Fc;N|zYxeez{J25z{z+AOl{QuBuDJJxi7*M z&BY=yNPkU{O%0_ByK2LCOWagAS~3>f<@R~FM;6J`+Vl}qQD2<+rtB?mtsn`QZ+v5m z1_Gi;`S)&yPC>Qj+)FJd74Pu0`^SiXeqRIu-qqF80K`#VL0v4MJ}7Jd3;V}M0W|yE zl>vZD&e|HPW{sa41Q4KQVqy{p-AG+;hAK?(YJNd%{kln_=*gX0{4aH&>7M?J= zDQt>v8|{dwEv5LFHIn)OLR zh7}i%8qIk>@Q$6H6=ep%FrZwASAyg2Mq}etx<`(&wqj`ljun;xrjx#c6S11|n24K@ z&c1{i{EAU>n`A>H=O6fzj{&plf%1?j-Qj0Xg!GU$aJm75dgYx+QV$2%1^B1g%Tg&Dkz>&CwIpfTU?a` z+QoB^4R^UvGvRJwan-6v0fua*9r7|c?LZGhujd|vZf3-0DC-jDj4{}E@}Y;P{>JAMcy+%h_Y{C+k< zG%+tY@uHt91BELNPgsc1BUV6S2A;(6^1)3axN_jMHLVgAM9MnKx=j?~u4KRbn{RvP zhWA^S_aXB)VKp*#Rzrim*yrut5;H8>Vermx{5)O^HTC{?RfTyPG=8q9(Z5rrSTDDT z(O%iZB+xe*-UHXLZU4$ysY{54*2v%!Vuml} zIJ#zsfQl)fa8cG+w9p<9^cjXS`BuZ4+flf0Ta~8rZI*^A;stmMNK4G=RpPB#TXMSo z7n(@}Z3+Hb(Tca-<(yiQkxjD8S#7V+aVf;*;b4fX=j(mHf5nIB4AS1NAF+RIrkL{b z#RpYR@GBX{QrU&-LeMm{d?$O%paY3hW@6aK7QG#e3f5D(%dXmWW1o_0=mSwcbPX)| zqa*>_&CIY{5;8BC?`4|@c}8qMg#{AjFCkk3yNY@-GVk>vTPoTJe#& zxq9f)oMTM;%{=M`uN0``8JHT%G|T%dks-mLkA*K{S*i{yAW0^^QfZE9+B`x2`8Yf8 zBFi&PS6>BOqaCvChr_?*>?}~_fN;rSCd88B+Rf--!Crk@F_Zxne9u9ZE?(1GQq3`n3 z)zl&R5R}ioZeI0F_fsPOy)b33cpeygBMP{j7%I3)8>`3iX@hGaO7o_yz*9lLgZ^R2 zwPIVzrIuIRzQ*oN@I6mkJ(q^sdWTY(xA_b39E7qs7t9K-didQYdKEWq;ly2Z8JT7Q ziyEW@F;W!h1^nwieTO;~$*WUWB`L{9wAre9il;O{`vvSNqd{2%#bhX51e6g7I-m!s zmM1fy)R^^9g>a<;gy2*cufpB#zs}HDy z{YqBX!#*&9?Nb>TKP@2ezWOg;{Iq2w@s zVi^}WB~E@BYyb$+P-g@wckZ(6e)2s2r-{1Ya%UuSStlNEI>O&C3{!Y%u=rvVRJ_hl ztWyhx_5FxcbWlxx#iVABx_b2Rxbwt70LY>S;9!GKo`g`}PdIG?i+|A81=M5LYc%}h zv+3Lt^PY^}+HGy4UC<|ez=LFgg`!w|g?P>VJB?sx*HBO2OkThilq!xg$}7@50K-L{Fe_aCqq3=x)P-YMJ_co{p_KMC|Q;;;6P+2MANlx$#ZXIYv*l>InV zoXTAbU;No8{>=Q8oz{|~Fz~?rgSf1-Z|%DTvzFxXL?pMpF9;<|H{VAp9p0SL!fWQ# zZP7LM>f(5bDLebODhvEb@rjX_IJph|R!)%POeH{=c01;sY&HA>-^`A)BC0C z1y?CkDS2?OeiVA8j`8Y$ixwe@&9nTrR4NY85hi9>;RN4dqnScJUp1mP@x^0d;Ywk|5Oi*{SgiI+WeWHZiI~w`|kk>F&Ujoa37e4!5J%~cE zZ;j4KN2XumgZyRuB$ZDLhY{tKLdWWy)(3m1M?X(uIpX~3vRy02X)LmV z!Q9ga_QZF&+2b39g@j<`Nfrw~aE3~`DVRXh{?icAi*ptWBA2bxQg~*^+Tq`GGpCzJ^dAzp_ru2Fx;l^-s(6LGdtDO zioM4J9G-B39Op%D8KpICRXEu{bc;Ja->>49g3SV2A4K!!8Xv~m%ecd*%~xmNMo*4k z(Q0$&w(4YlwauGwhp(Y=*TZ_JecfTCvsVVw@l`l*UlHaJeP5LZ7dJajGEL_Hto^HBbI1> zvbOlCb8N2RVNjZAP1x1~L4mK!FBZ%?v@bdhW-S_^cv&h-zvj&clhP}hzOO9a`5+QY zmo>2HlV4>qr4LY{S`m9F1Nk(|249C+aUu3c^vb)0^l?HN<=gC7d`JqgOVd4IB;?Jf zQdt(ccsP0+e<}*=#;OxdgzV~x|1ycyBnQypOx}@ZmK5kG!p-ot+dIAX4UT&6FaIrI znVu(tF!L@HuH9+hmZ9;=Q-s$iky@=75Y`CIXjD=xt1hlP{vu5^dN4_`TH-?&;mOe# zO5GU0Xb%KPk46vlrmkUVaWQRmlnw%v&0|4CB9%GOUOiW$0Ii(a%ti*`V@bl~L7x|E zblKP(WNA%LOw$mVw}uHwVT02I(|G{9l7XxWycCd+wA;tYSLP@+u`?&YzgLstK41D= zrSO$ZbF|`L7i1bNcu!_I#BbccXxlU*|K8_>=UG$&Tys%jWL3+@0ajh)8$i&8fkovY z2gP2WNt;9a3q|Q7AJJ~ZXxUyv+iF8+4R>XSipx1Xh6{!*q1?$sq;(yLX@1$HbJ3=! z#nuZIyrqBx_Pb{|5ms>KR<}@d9HxYVX^%x{Dw~OsNFlfFuWB=B=^!2enU{{9R`@!4 zt{Z5AF~)^bzms>C;X+#xv~^2J$ZJ#C9C_-dz(_QMPgr1bEXhq?Ew-p;k?bQUz2LpD z|JeWLtK5{h3oO&0U=XO17iG=;_G@cGv4~ffsQUuA?JYmJhlVZ^_hZ?rWztqO`>XsW zPfq$(N0umNS3z9IYR56`^>&`cB6x>-2_tJ+{M_nZb4X$w8%!$Xv-fa6v?ffhXiw@D)T}!phJSXs>BR)ocQNwO{vqwMKw-xTb2V5m+T8ustDit7$D z)BQCFcw{X!b8jw&(kQe#v`xFh=EkS!NG`~Ag@j#MnKJom6wtCh9WTmH30ws#L7x?+5muYe+lg4VcnnQ z^W3HG1;|oWQ4a93-u_&DKf-raGW58YO8+_(h8&**05wusPFmM{b|=@*$wo2Yv!rbU z;g(afsjRAXEaw$iouT5CRaiyaIc=*sRXwR^Ax0ZkVa@in9ZyKAC?-aC_?^s6EDN^$ zjzZl6b8u?N{!2NFLQ)#+Ch&HZzP*liqjFMAbw%Tu@J*kbPtL8IslfW^R*pyQUemaL z^_Iup#n@_iEs9YqflJtv8bLT_gwx2T7HZ=j-Ij*kg9L{=}O3x zT^8dsZP(~6Z32&B&tXybL9CuC0B+$Bdb<12w>>kwM0c}ET?fiiVxl=7_aJ0aAeiZa zkycb*uwn5Vm*5%>xlw$ggOhQM2bD7q4Tnqkcg0eJ2_vK$>B$Kk@z+LDcKDKHJ>21-0*nG7DPkB0}w|-Q4xKF zQhiI<1xx#&30H3nJJD-jl>eN?dQjvGgaqnm43N!-=xwF%xyE|tw^@Z-m)-ZSbB}|G zqlLa_41}nWkqBiKT7*wE*Dt=gMC?{*28>tXu-D-7LS04-vA*#Nu&d5nJMqD~9$!tD z|5Q`Y&f4psKJ*3r4(8;f=mr*^!)GuvQ*nf4M=@O!6Yph(?l7hxRRM~gbAGTrnAW^P^_*K%6_ z@aNy(p88+znnCguMf=-Omwu%x>p?*u{$Q4eS*$TOt}$9_r-{#6kre)Rhk`@C&g?BS zFjh%%#8!;gtxCp~`l^?s!{0eLwfxl@#!2RtPtl|2pB@l!c^)i_-A2JA=yZ!5GBYn2 z`tClCH&t!bOwF4<@kFZdkuaLNJ~gPG(3Pv}WmCe33@Vqc(sIZg8ROR(-hp>n{$@bMbA#vYV_pZAHdSIBMz>kDUGJ zDE`=(VhT{M|D~cXP$|`cl|5BgZXiPG>?qw_+Cjuz*~`8Bsy#K7pSnlgbB1OWC&37z zFA069dl$l^! zaHpeuJ<5DY^JC@xWZY_lm9-?SjyA-1qApvQIqt=Q`*@Uj za~Wf-so{KabDpTKbUPt7H`@Ul#7lEtBGfdIYJ4KiV!V!wI>Ux|2Ng2U(~EI~FA0*a zcytabW27P92LL$bH!|6ajaA$E27`7n*XfU9YMfn$FEoHnUDSumoxe;1B=U0hHkk{8 zF0Iwnk0Z12OV+c6lDJl#uG#|#J9)pb->+_6j6+~R*WY7JOcabo#XXLd3VvTS-P)}(XBk+ysp&&1prikDdO@&5#5CvZ&D zCuplL6)1rsX7x)`{mSMr+kqQ@*+%#zW&WYwtF#6?3AIzs^Z=isgUS>V8nu(Mj<(@W z3Gup@K}`Y%(8bitq$D#^Fg4d?4a7s|8ki4@{n-Uik@#xbRQUSi58an#1&A52KxlpH z%OW``2^?NJMjq26`JB3b5vB_BtM?oqNJGK;#%5Z}HJfb?81*|&^vc#CMjC<<CUl`W4u8ppJ`z&TZ_&1g0fSS7O{0! zuQwSqWsyP$DK67F71>_ABH&D5aCaegaC=ECeFH;!^fvF%5HU?(eoMa+@u6X8YI;0f zpW?TUn+L;JQI=*@)$k~Nt@E3zu$$?tgS?bBg-?NeE*IBqu|N1P>BT@@B!6KjRp?#U z$|%8u7Md4~OC^7ot{*WnglUZ~A*eZrR{y2M?^s}9(=O!vqnAD13aaaRfRNNktm92k zBKqi1d+i>0xvJNN!F~iWH=ePROdgVB&@6{Dvi}K|@@}tq$Y0BrJg?|o^!Ia9T^kQ~ z#L@g$)pG9I*TE|vuy%JJQsbOqK6HZ11)0-hev`&LyX`RlMl0vdy2Gn54*6C8?tk`^ zzv1>%21+I~Bg%t|y7NO17R!ip9U1>Z{b*g*%c#zo?1QLw6EoQsO@nMQML!u`2B=f{ zRlM=uUiW-yo$dVcw`Vb)nxRLKUDed*0CVHp!K3*_(*s@;sX}^OChJ1VjJ_#^MTmOz0kQq z9IGB-XVYJ$v(#FW@6b=waOH5nQ1Hp=9X0cp=&VPxn1Re;0)#D9;3ODg!s10raQ*Sm zSHaEwR^w&IH!qo!^?=lmQL5q(9@bbgAqF% zRk1JIt3(4%{rYZH5gDthJ%mm)rS_x)-vwsDxJ30(?}RCw%N#~sCXeJAkO5RqYWni% z$|IKOY0u-0v#nGg(SA=X6X(IzT%0UJ%&f74VCFP;P+r}g$OZ1ftC@69F8t1(S4?p^ z;Yz>&arwq9Fh^zh3r_i@8AR0WUh4M6=<_6Dc;8wWjmckMLz_km_c@E&1+<1Xs1=1BR%fLi|ZX6 z>=SUheFH{g+vrb1D^%yBrV3o3<84d$c|ezLE3`i7wQSFKAem(DMyPJ^)*qvG&7lSn zSJ|X;SlyY{0;+&CTU$t~?Gt&79>gJ@u2UF~@{j0!a=U%d{opR1@=)aCGkl+LVdqn_ zLyeW>NqgcWQbGng63bAoekkP`bD?CP@i`5EFo?&SUbKsqr)?Ox2xcl$$5gAscKf-g zt$UX$ryt!jzFu>|8DpfJZ70XF7421r(d+jS$GI=&tr$UI2LkUTD*?K4D8JH>dc%qV zR(vmCtYdwbe~@!riPxYan21qAS?go-2iO~gAHgW%c}^Sjys<}dhL&@$dB=nYLu>K! znIQ&$b?FlpevU1VBHY$o!%)D`>8T~?&YvPG!ifb_Z134_l0{gZJ|Cvcym`k0(93=_ zeUI&}i~2{|RoAl5@f^k-@&w?DjQ`==n$x)v!BH$9U{q%VT|BhKFYf+9dxK1<$wK~B zy{d>Sg?sLydV~C}Q(dWB9+GUAGs$T3;C|!q)RKq(DX8ycViv2U%?Y zftdv~xAoddE-8BFB<_oz9YmFclo;5)Sq)LwoVzUd4T=BAgt54)$L&Ub*I#SzPutk0 zrqpxLt7fKvOLPqNiN~acO{Q#`0dG`h%w^zeV>`?@$d7p4WjThqHksI8rqm&GQER`f z#XRFTR1%E(n?MdDOU_P*vbXUJ2RQ?*qMxWea=vFFw|?!lg~v9w4LV=H6V??>Ul5*L zeX2@T%P~XnNXck1Ia!Q*I_u?XxNp-ZViGUlIYHd#d8=4cVI0wiV zhJ71TOaDObZt@1n9}+*fgdnZ?nyTHBV>jWvkx$idy~7?sM4@iVw3uPwL=0Vmxdm)2 zYYCIGqQzlWwsZQHeTkPG2G!)JmB!toi8a(ZawLfM-jG!Bn-TvcwhOT4Ayd~dM?7J) zaKUDuz$L8HqNsAkHozsU)WZ?{tg5IuW|%DzWc0M!h!!&nuJj^I;6NaUKA~{cGjl~e z(rUWejI^u|S(uoD-7^~4R?z&2Ok#Cjat(auE^+2A6g12cEj*OC;zDz9VqmsGbv15( z*h%e$p*(a*w5tr4dws(NbYbQ&>g~0yD+b(E&~nvc^y}r+A_d-pt6MKeY9pfwYHWiJ zAxTmv5Z=7@GLAPN#g){2PlNc@YDgp)1?$YxWYZp2D`Dxn0DDW!3~Ttm1cQc@Z#CY2 zY3L6?8V-LR6GXML)DvJri$PB~`_Kv2d`-7P3tUk>9fYTMRwjk2!q?&@1tZ-@bx-5V zWzFi|Ga%e+gXNF0$i`QM0uy;K_p#unI$_>e27{kHg>V@X&4R>n;z@o_Bh7e8wJ4wxgPWrCMp4Ne4c+9|AD6E(6)j%?38j>-o$kKVH<;h#W0M*V&KhQ6 z#H0hlQE4G~*Eo-JmMQj@;nyfL&i*)u^_2sjgQ68Yk%Q+lFIR+R7Cx(opCisFFQ$nu zPKe~&x%!=!sQ4u~lrE{gbBOgW2+gDQ98RCGE-$ON^Hwgb8@?U0NoXj{(2dUP?1$&X zum3zbx(}o1WwoWkUWcV0;ArnGPi^;*MKBkG)G#Js`zM^RF*ap z5BUJS4U{%!^fUUFG-|24>!f<`PtzIv;YyfDCgGtECZZ;6UjdySs$zk?B;hNRNHEed z+le?19h5$)qm^-`H->&!83;AeI2+(%e^%DP=zQaF8tX`Z=&_@Dh;U$N?X?O&XIKXf zp`|4%gQT8f?#0M%oUrwbbISM=p={O+)}mx^!>oU5#}YdboV)x|KRxgeACdj)`~vgX z?h~vi!HE$&`-cYTESUzx zcx0Fqi>{b>pn|9}N#>uXZG1grJwc%Tm8w?)P2Wr86H7B}!V@f&b%mEn9 z*B6%EXXV8u?uAeIZ*9n!nNMq}C%gOqTEq^N{uqejsT+P+b@%`dkt)Py)NbIcA{;SD zfSfXnf6Cr!xbtWOSL-UWbRU7!hsx4N*~K5GHsO#Q2~EU7=5-Gw$D(tl#f^3mW{zRP zcVEE`pUHd=T;0vSJR+JKJGzeIa^?!h+6WB3<~?HU?ltOUamflAKpxz#JQ7uQ{eN+kMxWE_fCwg4B>G>Q{8F*~mwTXpj3J5}98IF=8%b@YjA*FFhBP`|Ca*Y0XV-;=-bP0O`VY528i&O1t1Y}-UsOC$-|nTm zQZrYrchgaLudbcfnw#Efou|{^5T0Qho#LPUv#m_|<5*!wbyx>MCQg)5z~ekC({Eud zxt$$$Q8dMi@*j?hQ%qso=}_;ov+s%{f&*4(gq)YkT|=4|tZYIYz&k$477rz?0`jxF zc{D=ExE^|YcK%Kliq<0HCoitG>VEuXYKqN z%QZKnSy``QZZofD!6(~)nl`%~X=oG?TKjVR-XMoFFk%obhD|wV8^_L!@u#TUI3$y= z3ZyI-JTk z;&?vK1g=DGm6A2YR2Y2Y<4~uM4E80Ou=wQrriFWf;Hy(NT0wTHCeufmKup&*@N1v6 z=k^SV7Df|L#cw4(Y#0ecWH40}P+5Et2k&N$N!+WUm-mQHlY|P`flA89PouwEhG+J^ zPlACE28A|Ci#tQGBep9>Egjo9H+ctxrT(zjVg9B)oTOBhGsw3+fBO92Duijw8m5)* zs@%gvPp3!pL|Dg*>F?MHWUc#OKJV|bDL|dj31plu_-;#EgC=6&#S1;e)Mi(&b9bbP zr>*pv$xx7aq~<(Xy9#Sd<`N%$!Io=*+=7X_n3u`N4Z0FPyCgNNT$|wW{TDLyznNr; zpZ&(;db44WI|PAKt_O`I{DrH2HgVt5LEyrz_}(a&hT%uESyScd-WlnJh3}sB2ojVY z!XzLaI+8t(wCv|oBy9=$ts1VaH6`6|Pf8iSHm@WOTXjc*UN`))>cK{#W3RDiX&*Nh z2#eJBgA_B~jerv0_)h+ublIHhu8o%5>|0-&JwFouc6#oJI>>mwo4FU$i4#>Y11Jx| z5TYX?Gj#8bFt6VqUtNR{Fg%%;4OPNCHYo1%@3i;L6Ryl6=K$Sr3cQ+Buh5lsgGX(P zThCPP=TC*Dux{bx8caH&5MU|QOl^)sfp}4WvR5zB=m#ANCQG+JVr*y(J`yjZUBR6c z(wh8~_?a7JMtV=28vm0-y{CV3akbI|pCy(YD^k`_NlZFV=0Ok>U5`URQ^lKJTIrs#I-xnnf!ZFtJJ_`~l}JH{;PY&&zDF;&WDJ&2F|_ z=5{v4J5dVzd*t1GptG*id}btE6up(a&s0p_H2{%8tD zMMl@ZKMD5uy6!s{`!-{NWWBOjkkztDRDSYP?_ zVG8?B?jdgK+e17ltu-aJO4fz}ze@y0nVF=tZ#VS~B99Wi2*p5~N#bL;;N6WpY;?d9 z=->GR(*`@!!ImRQ+?)~bNonjvJd`bi%qI4@&FMuIPq+)ZV!p4}X2=LpzEoLM4@C>6 z?q2*$g+WkC5U(*8_m@I9je#Y{E$ieb=zA@2s zRcb+cmfe@-kM6O<=D@_~X77p~oRUn}`?c_*D$5n*0M8*qr=((AWNp#n)@0D&ovUI(AdlG`-(|swh?6g|90*7^ zDE?f1F#q|S{7tc|=f?PcfN3e+-KFC$6x~36ES>IxJ|5mDl%XbZA$Ah|??C%cUY&_L zbdFS$LA_lOdvK=bo;zsfN5b1b4;G@azgf*U3x)e~eRp}bC2$BbxB4agQ^9vRPa2m7 z)A@62B&G8WJ@s#FbD}sRTB}i|L~-OFGD}ll3$cCdZ9-0QmA7c@IGB=rg0n`U=~WYc zlA`ipsFd-YS^6%YI~cpC#7HrB_XS2EbJ_n*1b@%(^DZR{Uy|t?=^#ieD38lJCaFAl z-7iF)756q3M+$-(Iu~$48+rBLR$JJ5@Z8@RI~OoP8@1R5F)USJ$gmRRyWw6>aH#mLTTXxh32<-HkyaBfY{PSw%}0;;+9TsG&AzGoR$=>=5yWGka2TT#ef6O}bfXq07!^tF_-ln`b@!VvPcg^cD& zBKru*Pza;3XPKCJ)Or4k=k>#9`Qh`v_kBL++;eV{-MKS--21oz0DP7fW)}c}?3G}d z6SfyeEUmWp62s2M;dJ8v6U2L+k$c8%!!De&2NG;ST{x#*>)mWk1&3htb<93gH?N$r z*hvd38CPZP3+nfdB*aquKK6dlH%uax7epl#YjfArZ}NqUhBpE~=B4ci?N=iByE;0pYH@}k@j zC2U)55cfc-cHAfP!uBrCotD^$eG!UV8Ih8fA#6o zWVhbU8i>ehiaKqoc0=A69XLsY6$d=C!Ma)V#q;wXttCuVPZLR8SS|;@zFmOuVU*! zs{(>8yCeK)0~!9;*taU*H?%mdML?DFK_gy&Y5pARDp-CJUJYBTNtd}`{u1_YS*CD< z+ee1IC~U-a5quK3zuxDG=+}XC-(Wwr>;yV7){l2TgoqtDt?DE=GCLI}RlI0~DccUS z(eG8e0?&Lf^i4#rnAWEQsWuyQII+p3GO%NT!Kl{gAf-$>Q) z)rj+bu&SVcerQ#s{9cdOL!M~qZXAseAoG9Q`xKQYJ6LGl!$&RLVf{y+52Lc# zFnI~>s}0r`1Fam};u_2+gCs#*mX`b}Z^D}M->WrHmkto>Ui3PWBUcvgfNw^^p^5fT zvZL1G2D4PuuAS{wsLAHnU5mn&cHv$%?tX+!8uX7DNHAwqDKS!$q*p8OYnL8IjMbok zoTAU;O)IjD=OFH@|At6J1R@|(TX-EO3La+%zgVa*hxkX-*O1)(`9?OpSSBou)Xbgj zIJ541Q3PEDw7JXcX?$lr8Dp@NoEFx;YFP1bq@lF9?AboD5r5u zt_(C(^z`#aUWK%Xp}ncysWaQad=a6uFXeinkgl`3*tP+Fv^@T1CWqV$h?$%+Bp2l7 z5;G~cP{v_1Xj`xf=|C)}>vjIyC_rrlI~>{NrX05U#<2=xkdTX+H4R+EUs1r;)hFDc zx};2Ls6-fTbG_~9u&gfr9n!7=%!QGu^j`QR6n8DfzpHGB1_kcrOp3 z>RR$z8UqQyaiX!2c3nvH&%j;}Mde{}xGE{`U#CVpN_ ztacF&zgESphXIc~u3C;iEb>;qExQDT&p^1Swu)aWD@wHGO) z$-11{Ln3hx4(o^hf^~b^n*^Z9CejbFD;z<=XxCON&)3azZrXS3gXV9@-?b{CH@t~) zkWzZZk)vFH7YwG@wqbLva?oDp@Wg58ay?}(F9SPabmCA3^N?um4Y0HZk<~uO16Q4` zEV@)uvQw*7{CsHnC!@tL=M&6}e_u{tXG=#;gqAoSe&M^+vV3LwXItuL$lQVGa$D}u z5Ti>?6iC-R| z^SUa#H4JG{q@h2~_4eq|`rz%+kd9c9xOiQl^K01Wwgw-%QyN89)2$0>WFj>3%;Fwk zTXU=iD`~Yr--q)!?va%Jj-r*fj#FzZ|1x`Y%;>?@fT^<=T21)&#isI3JT3ZN>yDf$ z34@jT|C*|d{kzlr14>j~Km0*DWSw0EEuq4QYQoNzu0mJmMD)0f3)EX#Ia(Rx26pdO zv#_*p5!ttC1@V~!jgf5*1xlqj_soef6dosxKJ_BGUBuaUrm|f;YmK2ak)7;l`J#RN zYeSR$)yWuniIIi>NK}XP^E>?TRIk|jCl3LR;ODuWapC(h!sN{f>j_+ts@3^peJbMU z*)$=Mq`0%dP%e5ukKjvt3ur{pEz>t`q9on@!2FfF$)*0|#TWZZO^g@5M0%FV;&qNJ zLXs7MYk-7>m>gWCb_7lg2bLsWb(Lv_r)ryNmIJWRl(kA?P&XWv^1h zXO|4<0+WLJ@u${;xgV=Tdui;B$fa(Y+fCKlWj;0E<|Yt@+~Fj<)U(XZYh7zn8~r6t z>Khu;;e7@_#Km*nqXxRYpX9+ZySiXNQgq_+OqSnlDEb21F9QhCiSbJ0lN==2xeRz} zSe8v>S|F%w9*B7ZI(i`2+%=!7e|xt6R!oN@b? zFIu#hvjV{LL0yq_?ocv9FuQlwjFEJOEU=A6PCH?y zjIv=in|BH8wD*(aKNUH9bJ(|n#kBiqSzdjMZH@DdcYc76j}}GNrM9P#cv)6oD4dV? z4__tyniqxJ!`ViPw|ZJ4-^T29!9sYrud%!bQ6`})C~WOn&fy#4ofEXrcBF+4wb~7Y zGh7pYL?$4?&-v-Bm<6nA#T|Gc)?U4y0GJ#;(2nX;9gqm-DT0hNn7;j9t`K+wiJ?_~ zr5TVjvSEP?7yQxTDRpgkFM?bEe=PkIHj&avpjTjQp(9fh5a);Bl?FmjitT+Nx9t1pIQ#nTYHxrxLq7I zdU8+tO#6}aeeJqvrTYWOV7G&9+%&^z9#u)X^TT0XXX>BOy(ed_V1K|0v03=s-w7qJ z=yh+SZ_Dg;jISo<^)0`DG1}@UCGJk$Fpf@LRn31DBujgQ!{H5zVl?{XJqD6sl*}wus8!o+VHrzFcD* znAX&go)@9@lmhSD+i|O1zd_v>SxEHTfQ1PUkV3r*?b!Jw9)>_BF^uppzA#6}<@=6o zOMhu^yL}n2_hmxw3I9RWn9$C2@h zOesET?N1P1@7Vug)Wtb2B1peyu`{tCJ726lFn1ZLtzgggm;NSD0I`J zGZ}fXg8mBJ^n!QTF+(!_pgmi94j-_Kol#FMW%FM`I`1Bf;2(iJ(7QaG8ym|8Aa%^S U7BY5g@BaiWPoFb;brKWzU-;_YD*ylh literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-icons_cc0000_256x240.png b/assets/css/images/ui-icons_cc0000_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..45ac7787cd2bb4d6c3eea7e6e4a893034ddf75d8 GIT binary patch literal 4549 zcmeHK2U8PFw@yL`C4e;PN)$zq7LZ;;35H&!^Ae<}^rk2T2vU_UMLL~@l9q%K1Kilz^sSTF$VyE zXD(oqp5|8(@0Vw#PYAmv8wa z+|hg-G6T$)RJpWJ2C$2ELf51%#A9URu$>;C2fG!K0xgXnF%@;iiEql@3f72Hfca)u zHW(ownv{QUXXqDHd(XVQ>7wBqk#_eG@z3wGAi&!OH+2Blk3kE-wZQz;xARPAQ_&K8 zu;T`mng+!D@QaJH>ErbNnJ%$H&x#LI#RdaJplobx(x5A;t4$0lV}iP05F5X)lE*dL znaTz_*|o%3ISoN~Llc8CbIC%0O37X1*4OYg{C6vE~)59p{Cb8rZR zGfiMq3|gT>lCmd`=!M3mWK*5L=!*AW$|omN3Zjvoh`jyie9ia#!0?Xin*=%>!zJ6J zf(#ozJUyEC?t>c+hBmYr0F$6{eL)$X+iMLC6X{+#W_qfn33yIe2AE0y0$##q!fPyk zOg{S@YTzqI&10MkjhwybPdx-oYd=tiMEg%|fX<}&i)L+&9*xCZNdLx#hdbhcgMjNn zpQoC`Fe(5~(*oH|!5_H+nYwH7veb^B~BC%c5ObA?Ll!dm^ay#5VAu(PvU*G(n60XBLqn%vV*I z5Yn#h^GzFkx`TK!7l7D&489IDWPK+RP~y$QQ^@Zp zLuB(?1xG&26J?+X)xj|<31-9+NXpooJX$`mP6k)@AGf4cqJk+|2U*w2V*Hid=YI=r zZe8(x>-Ii$_9_gg;9x@-=)pd3>ynw`$PR~hd=nP%VZqe}-qsWssMq{J;14`7TI#m(>l7%FA* zQi-Q?ngCQy`Gk+Q#iGS_fS}JXl=(LtXKs7pu6`O{jCXsH)B+mCP@$t8cF>;8F*`Z@W3%BFMg!}u-sw>7zP3wCNBBTi4qE}s z{wPVncCoW;mxRuW=KI*^L7p+54&acC>;_QVM(Bi2J1I`0c$k50BmcY@u-3p!t%Vqs zn`?+3&N;-iUCpDf_eo)JJ^|A+uuTj8N@Phe?&T1QT$HP25Rs*lUuiVOHm)Bb|9qT- zZ;|zhuDibqzQF<6`orm8YIYW=vR}MpKNDh2bLnbK2=bC{9ZyHSB$TsS4DQ+; zENaPBunk>dR}}ISpMpHx(P|hspd>90zyR0T6CY+hwV45TBe8CWc^nz+IqX;}qrqxy z@>Oqcegp0aJjfYc$P>(Dquj!$cRbJ4n26-Azk2JH0> zLM|2C%Pus(;`cZ6Y=rN4+Z(#o*VH+cD!k2~gXbXBrTJhsaLxVib}`HNNh=rrqVvc! zD_HaZ6Nr_jz%b}v&&eC~sYpTn+A3LDF0$Q5)l(v^F*+bMy1;Fhw%30zrGs zAkFe*7L*pdA!-znzUe~yoEFbHBzVhT4Fd2e7@qyPwc}!hAErBQlc8@6Twozx+{o$$ z>SMoBwGFZN%wfB9#%51TDCFeFL=a2nGF)wB8(@2nDgk_hk^N+Kz7(3_NM3iGq|MU$ z2Xr`{pPE_iggPVdwVs;-Uzx+IKD!df!01e#o9j}8;0s+5%P9;Z5q6+HO;$EPr@Sy~ zcp$lq51bOOya+Z1gz9Lsg48>F*{SH-e=<=otX&u&X$o zz$e*E9*KF6hp%n7v_j{MC?AL*MPQ*MmRKQ;yL+Pn?CKuo?Vl+K*kGWGr;YZBx)u8C zmw;i3?-c$#3sVTxK}`d}Ai^uswU_FazzL2iHRiCsw zC_}&-K1#cD$VsbG)4Sa|miFE}ZRuxZ@Dn0&FsZ$fntugZCeD=qQX0s+afRHX8Oi~KBmIefw5cso>8*(tZf_%v-^r4 z3B{@WHSqbLz0%Ka9dkpiX$l|gd47;qboH-!mtfhPJer8)ck~CL6q#mwDW!w!Q#Xma zIkg*14Lt^UL2}C0?zPH-0LqiZsB^shCIQRG^#N-GrF%ygIdpD&0)=oQ|NLwmb1^cRQHr0BkUA$Q$;vY{hz)K4&LJD z6zkc@D6DXkK@x=Rn2qO`$hq`#Md?|j7DAUt_^3hUvLPt-O`h zGzXk3Qu*PcaczgsP3%FgR1>v@8^{GOR=I1o->zuELOnTdvRTeBkL05hABe>3_6tZ06=Rh7WY8r8ww9)oE+8%gBDjqssTTG7h+ zW4@W(LBNMx!47MuFp3-|GB>bbN9vMB<$8PiH9z0cqI3!uAxQhi%uwGBg1^PAO*HOX z&`}2(Po2MgN!-;|>8ZS?46bDQ;K^RgY5Atq+S97i8Z0f;=X+3%rGffLd&L(a#~wq7 zN{ByBf2ch(AaQ)NqsD%Zo4vq6A}IE2>xmpQf@71=F!(CsILT+w67dc%rjh}0>8H;~ zH@n-Zef>@OQ_JJ;Ik_pCU{wpx`;8rSx2KQ*W~mQNS6c0+h+K7&Zf_$ze#Lzq+b0p7 z`igi1tev&wU9%USuM~*Tl;JJ913~t9O!9ukLPaK{lpN4JBC-T8@>A9Zm(bNk+Eb9n z?o?NyZ$L1RWA@D&f+=VBq2KB?GAdlvT*IhF1}FLw289i;yuTASXyrG?b)Z$}Q?CXp zqs3VF5h@+twpqcQo-0b5j7RqxgkUgUtfo%imIt47Eo{YkJ+x8j!H(+R2dL72%r?Gb zF97c4*>qHcj4+8%ph5zU<$L*y#Bn;mSRPZ_3x&?rIW6~gjt_nw#qq=kGG)70j51nf z14H;H_Z-RZaf;<%0pJXca(6(Y2BC-Y3((4(#bwqZ4Z&LiInd3qVe~Lm zzbCcvH;s5Gn80Rb*PmdSlri{XBtk=Pp!z=Be3_X+x}njM8zDAnJsiL8zq35b#?DRm zv|{H`KaV$@q{MrcTZW+)Ehu6>EQ^&|m{r%uHNnH5G97#o}GawGkN!XQ~1alVk)4VKIWM1(Vg2@?{Ox{(O?zk6) zWyh|{(i1p(lXxr%>%wZ2&BYw*$p5lQ;gW-x@aFF*Qwu80V-c3bn$4{q$9iYO_vil> zux!s0L6})L8uzZWZ;K31)F()5~J8-Vqb0Is^h8dRB=8>z+}#(IgCGvFK2p?UhvP%u|vOI`U)G7cK0XFL%u2U3>AaJHTa$n{^eRZ7wqfC4}{W*`GA7AH&qfV%J9EO|d$ zB9pqjJTnfeYB>$t0I^W2*LYa5W7Opd#1Nw3@q(f=mE zm6T=VmE~mrL)a!1u$~ck|3t9xbq@-54sZi#y87OBgXwuY-*Gc{b9Q~?-|MD&b_k${ LG}fuoLO=c=v^XOP literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-icons_cd0a0a_256x240.png b/assets/css/images/ui-icons_cd0a0a_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..6eda2d9865aaf997e9f4e304e649b980c1a63df4 GIT binary patch literal 3686 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 bcn$#RW3F|Ou^W5)6R`gKjKzx+n1ufV;!UOf literal 0 HcmV?d00001 diff --git a/assets/css/images/ui-icons_ffffff_256x240.png b/assets/css/images/ui-icons_ffffff_256x240.png new file mode 100644 index 0000000000000000000000000000000000000000..fe41d2d0fdd40f87538d2312fa537a799994e55b GIT binary patch literal 6299 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 literal 0 HcmV?d00001 diff --git a/assets/css/light.min.css b/assets/css/light.min.css new file mode 100644 index 0000000..c0e8e3a --- /dev/null +++ b/assets/css/light.min.css @@ -0,0 +1 @@ +:root{--body-background-color:#FFF;--header-background-color:#fbfbfb;--color-primary:#333;--color-light:#777;--color-lighter:#dedede;--color-dark:#000;--color-medium:#555;--color-error:#b94a48;--link-color-primary:#36C;--link-color-focus:#DF5353;--link-color-hover:#333;--alert-color-default:#c09853;--alert-color-success:#468847;--alert-color-error:#b94a48;--alert-color-info:#3a87ad;--alert-color-normal:#333;--alert-background-color-default:#fcf8e3;--alert-background-color-success:#dff0d8;--alert-background-color-error:#f2dede;--alert-background-color-info:#d9edf7;--alert-background-color-normal:#f0f0f0;--alert-border-color-default:#fbeed5;--alert-border-color-success:#d6e9c6;--alert-border-color-error:#eed3d7;--alert-border-color-info:#bce8f1;--alert-border-color-normal:#ddd;--button-default-color:#333;--button-default-background-color:#f5f5f5;--button-default-border-color:#ddd;--button-default-color-focus:#000;--button-default-background-color-focus:#fafafa;--button-default-border-color-focus:#bbb;--button-primary-color:#fff;--button-primary-background-color:#4d90fe;--button-primary-border-color:#3079ed;--button-primary-color-focus:#fff;--button-primary-background-color-focus:#357ae8;--button-primary-border-color-focus:#3079ed;--button-danger-color:#fff;--button-danger-background-color:#d14836;--button-danger-border-color:#b0281a;--button-danger-color-focus:#fff;--button-danger-background-color-focus:#c53727;--button-danger-border-color-focus:#b0281a;--button-disabled-color:#ccc;--button-disabled-background-color:#f7f7f7;--button-disabled-border-color:#ccc;--table-header-background-color:#fbfbfb;--table-nth-background-color:#fefefe;--table-border-color:#eee;--avatar-color-letter:#fff;--activity-title-color:#000;--activity-title-border-color:#efefef;--activity-event-background-color:#fafafa;--activity-event-hover-color:#fff8dc;--user-mention-color:#000;--board-task-limit-color:#DF5353;--table-list-header-border-color:#e5e5e5;--table-list-header-background-color:#fbfbfb;--table-list-nth-background-color:#fefefe;--table-list-border-color:#e5e5e5;--table-list-row-hover-border-color:#ffeb8e;--table-list-row-background-color:#fff8dc;--sidebar-border-color:#efefef;--dropdown-background-color:#fff;--dropdown-border-color:#b2b2b2;--dropdown-li-border-color:#f8f8f8;--input-addon-background-color:rgba(147,128,108,.1);--input-addon-color:#666;--views-background-color:#fafafa;--views-border-color:#ddd;--views-active-color:#000;--input-focus-color:#000;--input-focus-border-color:rgba(82,168,236,.8);--input-focus-shadow-color:rgba(82,168,236,.6);--input-background-color:#fff;--input-border-color:#ccc;--input-placeholder-color:#dedede;--tooltip-background-color:#fff;--tooltip-border-color:#ddd;--tooltip-shadow-color:#aaa;--panel-background-color:#fcfcfc;--panel-border-color:#ddd;--draggable-item-selected-background-color:#fff;--draggable-item-selected-border-color:#666;--draggable-item-hover-background-color:#FEFFF2;--draggable-row-handle-color:#dedede;--draggable-placeholder-background-color:#fafafa;--draggable-placeholder-border-color:#000;--task-list-icons-color:#999;--form-help-color:brown;--form-error-color:#b94a48;--comment-title-border-color:#eee;--comment-nth-background-color:#fbfbfb;--comment-highlighted-background-color:#fff8dc;--comment-highlighted-hover-background-color:#fff8dc;--comment-highlighted-border-color:#ffeb8e}html{color-scheme:light}h1,li,ul,ol,table,tr,td,th,p,blockquote,body{margin:0;padding:0}body{background-color:var(--body-background-color);font-size:100%;padding-bottom:10px;color:var(--color-primary);font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;text-rendering:optimizeLegibility;overflow-x:hidden}small{font-size:.8em}hr{border:0;height:0;border-top:1px solid rgba(0,0,0,.1);border-bottom:1px solid rgba(255,255,255,.3)}.page{margin-left:10px;margin-right:10px}.margin-top{margin-top:20px}.margin-bottom{margin-bottom:20px}.pull-right{text-align:right;margin-left:auto}ul.no-bullet li{list-style-type:none;margin-left:0}#app-loading-icon{position:fixed;right:3px;bottom:3px}.assign-me{vertical-align:bottom}a{color:var(--link-color-primary);border:none}a:focus{color:var(--link-color-focus);outline:0;text-decoration:none}a:hover{color:var(--link-color-hover);text-decoration:none}a .fa{color:var(--color-primary);padding-right:3px;text-decoration:none}h1,h2,h3{font-weight:400;color:var(--color-primary)}h1{font-size:1.5em}h2{font-size:1.4em;margin-bottom:10px}h3{margin-top:10px;font-size:1.2em}table{width:100%;border-collapse:collapse;border-spacing:0;margin-bottom:20px}table.table-fixed{table-layout:fixed;white-space:nowrap}table.table-fixed th{overflow:hidden}table.table-fixed td{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}table.table-small{font-size:.8em}table.table-striped tr:nth-child(odd){background:var(--table-nth-background-color)}@media (max-width:768px){table.table-scrolling{overflow-x:auto;display:inline-block;vertical-align:top;max-width:100%;white-space:nowrap}}table th{text-align:left;padding:.5em 3px;border:1px solid var(--table-border-color);background-color:var(--table-header-background-color)}table th a{text-decoration:none;color:var(--color-primary)}table th a:focus,table th a:hover{text-decoration:underline}table td{border:1px solid var(--table-border-color);padding:.5em 3px;vertical-align:top}table td li{margin-left:20px}table td .color-picker-square{display:inline-block;width:12px;height:12px;border:1px solid #000}.task-table a{color:#000}.column-1{width:1%}.column-2{width:2%}.column-3{width:3%}.column-4{width:4%}.column-5{width:5%}.column-6{width:6%}.column-7{width:7%}.column-8{width:8%}.column-9{width:9%}.column-10{width:10%}.column-11{width:11%}.column-12{width:12%}.column-13{width:13%}.column-14{width:14%}.column-15{width:15%}.column-16{width:16%}.column-17{width:17%}.column-18{width:18%}.column-19{width:19%}.column-20{width:20%}.column-21{width:21%}.column-22{width:22%}.column-23{width:23%}.column-24{width:24%}.column-25{width:25%}.column-26{width:26%}.column-27{width:27%}.column-28{width:28%}.column-29{width:29%}.column-30{width:30%}.column-31{width:31%}.column-32{width:32%}.column-33{width:33%}.column-34{width:34%}.column-35{width:35%}.column-36{width:36%}.column-37{width:37%}.column-38{width:38%}.column-39{width:39%}.column-40{width:40%}.column-41{width:41%}.column-42{width:42%}.column-43{width:43%}.column-44{width:44%}.column-45{width:45%}.column-46{width:46%}.column-47{width:47%}.column-48{width:48%}.column-49{width:49%}.column-50{width:50%}.column-51{width:51%}.column-52{width:52%}.column-53{width:53%}.column-54{width:54%}.column-55{width:55%}.column-56{width:56%}.column-57{width:57%}.column-58{width:58%}.column-59{width:59%}.column-60{width:60%}.column-61{width:61%}.column-62{width:62%}.column-63{width:63%}.column-64{width:64%}.column-65{width:65%}.column-66{width:66%}.column-67{width:67%}.column-68{width:68%}.column-69{width:69%}.column-70{width:70%}.column-71{width:71%}.column-72{width:72%}.column-73{width:73%}.column-74{width:74%}.column-75{width:75%}.column-76{width:76%}.column-77{width:77%}.column-78{width:78%}.column-79{width:79%}.column-80{width:80%}.column-81{width:81%}.column-82{width:82%}.column-83{width:83%}.column-84{width:84%}.column-85{width:85%}.column-86{width:86%}.column-87{width:87%}.column-88{width:88%}.column-89{width:89%}.column-90{width:90%}.column-91{width:91%}.column-92{width:92%}.column-93{width:93%}.column-94{width:94%}.column-95{width:95%}.column-96{width:96%}.column-97{width:97%}.column-98{width:98%}.column-99{width:99%}.column-100{width:100%}.draggable-row-handle{cursor:move;color:var(--draggable-row-handle-color)}.draggable-row-handle:hover{color:var(--color-primary)}tr.draggable-item-selected{background:var(--draggable-item-selected-background-color);border:2px solid var(--draggable-item-selected-border-color);box-shadow:4px 2px 10px -4px rgba(0,0,0,.55)}tr.draggable-item-selected td{border-top:none;border-bottom:none}tr.draggable-item-selected td:first-child{border-left:none}tr.draggable-item-selected td:last-child{border-right:none}.table-stripped tr.draggable-item-hover,.table-stripped tr.draggable-item-hover{background:var(--draggable-item-hover-background-color)}.table-list{font-size:.85em;margin-bottom:20px}.table-list-header{background:var(--table-list-header-background-color);border:1px solid var(--table-list-header-border-color);border-radius:5px 5px 0 0;line-height:28px;padding-left:3px;padding-right:3px}.table-list-header a{color:var(--color-primary);font-weight:500;text-decoration:none;margin-right:10px}.table-list-header a:hover,.table-list-header a:focus{color:#767676}.table-list-header .table-list-header-count{color:#767676;display:inline-block;float:left}.table-list-header .table-list-header-menu{text-align:right}.table-list-row{padding-left:3px;padding-right:3px;border-bottom:1px solid var(--table-list-border-color);border-right:1px solid var(--table-list-border-color)}.table-list-row.table-border-left{border-left:1px solid var(--table-list-border-color)}.table-list-row:nth-child(odd){background:var(--table-list-nth-background-color)}.table-list-row:last-child{border-radius:0 0 5px 5px}.table-list-row:hover{background:var(--table-list-row-background-color);border-bottom:1px solid var(--table-list-row-hover-border-color);border-right:1px solid var(--table-list-row-hover-border-color)}.table-list-row .table-list-title{font-weight:500;line-height:23px}.table-list-row .table-list-title.status-closed{text-decoration:line-through;margin-right:10px}.table-list-row .table-list-title.status-closed a{font-style:italic}.table-list-row .table-list-title a{color:var(--color-primary);text-decoration:none}.table-list-row .table-list-title a:hover,.table-list-row .table-list-title a:focus{text-decoration:underline}.table-list-row .table-list-details{color:#999;font-weight:300;line-height:20px}.table-list-row .table-list-details span{margin-left:5px}.table-list-row .table-list-details span:first-child{margin-left:0}.table-list-row .table-list-details li{display:inline;list-style-type:none}.table-list-row .table-list-details li:after{content:', '}.table-list-row .table-list-details li:last-child:after{content:''}.table-list-row .table-list-details strong{font-weight:400;color:#555}.table-list-row .table-list-details-with-icons{float:left}@media (max-width:768px){.table-list-row .table-list-details-with-icons{float:none}}.table-list-row .table-list-icons{font-size:.8em;text-align:right;line-height:30px}@media (max-width:768px){.table-list-row .table-list-icons{text-align:left;line-height:20px}}.table-list-row .table-list-icons span{margin-left:5px}.table-list-row .table-list-icons a{text-decoration:none}.table-list-row .table-list-icons a:hover{color:var(--color-primary)}.table-list-row .table-list-icons a:hover i{color:var(--color-primary)}.table-list-category{font-size:.9em;font-weight:500;color:#000;padding:1px 2px 1px 2px;border-radius:3px;background:#fcfcfc;border:1px solid #ccc}.table-list-category a{text-decoration:none;color:#000}.table-list-category a:hover{color:#36c}fieldset{border:1px solid #ddd;margin-top:10px}legend{font-weight:500;font-size:1.2em}label{cursor:pointer;display:block;margin-top:10px;font-weight:400}input,textarea{font-family:sans-serif;background-color:var(--input-background-color)}input[type="number"],input[type="date"],input[type="email"],input[type="password"],input[type="text"]:not(.input-addon-field){color:var(--color-light);border:1px solid var(--input-border-color);width:300px;max-width:95%;font-size:1em;height:25px;padding-bottom:0;padding-left:4px;-webkit-appearance:none;-moz-appearance:none}input[type="number"]::placeholder,input[type="date"]::placeholder,input[type="email"]::placeholder,input[type="password"]::placeholder,input[type="text"]:not(.input-addon-field)::placeholder{color:var(--input-placeholder-color)}input[type="number"]:focus,input[type="date"]:focus,input[type="email"]:focus,input[type="password"]:focus,input[type="text"]:focus{color:var(--input-focus-color);border-color:var(--input-focus-border-color);outline:0;box-shadow:0 0 8px var(--input-focus-shadow-color)}input[type="number"]{width:70px}input[type="text"]:not(.input-addon-field).form-numeric{width:70px}input[type="text"]:not(.input-addon-field).form-datetime,input[type="text"]:not(.input-addon-field).form-date{width:150px}input[type="text"]:not(.input-addon-field).form-input-large{width:400px}input[type="text"]:not(.input-addon-field).form-input-small{width:150px}textarea:focus{color:var(--input-focus-color);border-color:var(--input-focus-border-color);outline:0;box-shadow:0 0 8px var(--input-focus-shadow-color)}textarea{padding:4px;border:1px solid var(--input-border-color);width:400px;max-width:99%;height:200px;font-size:1em}textarea::placeholder{color:var(--input-placeholder-color)}select{font-size:1em;max-width:95%}select:focus{outline:0}select[multiple]{width:300px}.tag-autocomplete{width:400px}span.select2-container{margin-top:2px}.form-actions{padding-top:20px;clear:both}.form-required{color:red;padding-left:5px;font-weight:700}@media (max-width:480px){.form-required{display:none}}input[type="text"].form-max-width{width:100%}input.form-error,textarea.form-error{border:2px solid var(--form-error-color)}input.form-error:focus,textarea.form-error:focus{box-shadow:none;border:2px solid var(--form-error-color)}.form-errors{color:var(--form-error-color);list-style-type:none}ul.form-errors li{margin-left:0}.form-help{font-size:.8em;color:var(--form-help-color);margin-bottom:15px}.form-inline{padding:0;margin:0;border:none}.form-inline label{display:inline;padding-right:3px}.form-inline input,.form-inline select{margin:0 15px 0 0}.form-inline .form-required{display:none}.form-inline .form-actions{display:inline-block}.form-inline .js-submit-buttons-rendered{display:inline-block}.form-inline-group{display:inline}.form-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.form-columns .form-column{margin-right:25px;flex-grow:1}.form-columns fieldset{margin-top:0}.form-login{max-width:350px;margin:5% auto 0}@media (max-width:480px){.form-login{margin-left:5px}}.form-login li{margin-left:25px;line-height:25px}.form-login h2{margin-bottom:30px;font-weight:700}.reset-password{margin-top:20px;margin-bottom:20px}.reset-password a{color:var(--color-light)}.input-addon{display:flex}.input-addon-field{flex:1;font-size:1em;color:var(--color-light);margin:0;-webkit-appearance:none;-moz-appearance:none}.input-addon-field:first-child{border-radius:5px 0 0 5px}.input-addon-field:last-child{border-radius:0 5px 5px 0}.input-addon-item{background-color:var(--input-addon-background-color);color:var(--input-addon-color);font:inherit;font-weight:400}.input-addon-item:first-child{border-radius:5px 0 0 5px}.input-addon-item:last-child{border-radius:0 5px 5px 0}@media (max-width:480px){.input-addon-item .dropdown .fa-caret-down{display:none}}.input-addon-field,.input-addon-item{border:1px solid rgba(147,128,108,.25);padding:4px .75em}.input-addon-field:not(:first-child),.input-addon-item:not(:first-child){border-left:0}.input-addon .input-addon-field{flex:1 1 auto;width:1%!important}@media (max-width:400px){.input-addon-item{padding:3px}}.icon-success{color:#468847}.icon-error{color:#b94a48}.icon-fade-out{opacity:1;animation:icon-fadeout 5s linear forwards}@keyframes icon-fadeout{0%{opacity:1}100%{opacity:0}}.alert{padding:8px 35px 8px 14px;margin-top:5px;margin-bottom:5px;color:var(--alert-color-default);background-color:var(--alert-background-color-default);border:1px solid var(--alert-border-color-default);border-radius:4px}.alert-success{color:var(--alert-color-success);background-color:var(--alert-background-color-success);border-color:var(--alert-border-color-success)}.alert-error{color:var(--alert-color-error);background-color:var(--alert-background-color-error);border-color:var(--alert-border-color-error)}.alert-info{color:var(--alert-color-info);background-color:var(--alert-background-color-info);border-color:var(--alert-border-color-info)}.alert-normal{color:var(--alert-color-normal);background-color:var(--alert-background-color-normal);border-color:var(--alert-border-color-normal)}.alert ul{margin-top:10px;margin-bottom:10px}.alert li{margin-left:25px}.alert-fade-out{text-align:center;position:fixed;bottom:0;left:20%;width:60%;padding-top:5px;padding-bottom:5px;margin-bottom:0;border-width:1px 0 0;border-radius:4px 4px 0 0;z-index:9999;opacity:1;animation:fadeout 5s linear forwards}@keyframes fadeout{0%{opacity:1}100%{opacity:0;visibility:hidden}}a.btn{text-decoration:none}.btn{-webkit-appearance:none;-moz-appearance:none;font-size:1.2em;font-weight:400;cursor:pointer;display:inline-block;border-radius:2px;padding:3px 10px;margin:0;border:1px solid var(--button-default-border-color);background:var(--button-default-background-color);color:var(--button-default-color)}.btn:hover,.btn:focus{border-color:var(--button-default-border-color-focus);background:var(--button-default-background-color-focus);color:var(--button-default-color-focus)}.btn-red{border-color:var(--button-danger-border-color);background:var(--button-danger-background-color);color:var(--button-danger-color)}.btn-red:hover,.btn-red:focus{border-color:var(--button-danger-border-color-focus);background:var(--button-danger-background-color-focus);color:var(--button-danger-color-focus)}.btn-blue{border-color:var(--button-primary-border-color);background:var(--button-primary-background-color);color:var(--button-primary-color)}.btn-blue:hover,.btn-blue:focus{border-color:var(--button-primary-border-color-focus);background:var(--button-primary-background-color-focus);color:var(--button-primary-color-focus)}.btn:disabled{color:var(--button-disabled-color);border-color:var(--button-disabled-border-color);background:var(--button-disabled-background-color)}.buttons-header{font-size:.8em;margin-top:5px;margin-bottom:15px}.tooltip i.fa{cursor:pointer}.tooltip .fa-info-circle{color:var(--color-light)}#tooltip-container{padding:5px;background:var(--tooltip-background-color);border:1px solid var(--tooltip-border-color);border-radius:4px;box-shadow:0 6px 12px var(--tooltip-shadow-color);position:absolute;min-width:350px}#tooltip-container .markdown p:last-child{margin-bottom:0}#tooltip-container .tooltip-large{width:600px}h2 .dropdown ul{display:none}.dropdown{display:inline;position:relative}.dropdown ul{display:none}.dropdown-smaller{font-size:.85em}ul.dropdown-submenu-open{display:block;position:absolute;z-index:1000;min-width:285px;list-style:none;margin:3px 0 0 1px;padding:6px 0;background-color:var(--dropdown-background-color);border:1px solid var(--dropdown-border-color);border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.dropdown-submenu-open li{display:block;margin:0;padding:8px 10px;font-size:.9em;border-bottom:1px solid var(--dropdown-li-border-color);cursor:pointer}.dropdown-submenu-open li.no-hover{cursor:default}.dropdown-submenu-open li:last-child{border:none}.dropdown-submenu-open li:not(.no-hover):hover{background:#4078C0;color:#fff}.dropdown-submenu-open li:hover a{color:#fff}.dropdown-submenu-open li:hover i{color:#fff}.dropdown-submenu-open a{text-decoration:none;color:var(--color-primary)}.dropdown-submenu-open a:focus{text-decoration:underline}.dropdown-menu-link-text,.dropdown-menu-link-icon{color:var(--color-primary);text-decoration:none}.dropdown-menu-link-icon{display:inline-flex}.dropdown-menu-link-text:hover{text-decoration:underline}td a.dropdown-menu strong{color:var(--color-primary)}td a.dropdown-menu strong i{color:var(--color-primary)}td a.dropdown-menu i{color:#dedede}td a.dropdown-menu:hover strong{color:#555}td a.dropdown-menu:hover strong i{color:#555}td a.dropdown-menu:hover i{color:#333}.accordion-title{font-size:1.2em;cursor:pointer;margin-top:10px}.accordion-content{margin-top:15px;margin-bottom:25px}#select-dropdown-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:var(--dropdown-background-color);list-style:none;border:1px solid var(--dropdown-border-color);border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,.175);overflow:scroll}.select-dropdown-menu-item{white-space:nowrap;overflow:hidden;padding:3px 10px;color:var(--color-medium);cursor:pointer;border-bottom:1px solid var(--dropdown-li-border-color);line-height:1.5em;font-weight:400}.select-dropdown-menu-item.active{color:#fff;background:#428bca}.select-dropdown-menu-item:last-child{border:none}.select-dropdown-input-container{position:relative;border:1px solid var(--input-border-color);border-radius:5px;background-color:var(--input-background-color);max-width:300px}.select-dropdown-input-container input.select-dropdown-input{margin:0 0 0 5px;border:none;height:23px;width:270px}.select-dropdown-input-container input.select-dropdown-input:focus{border:none;box-shadow:none}.select-dropdown-input-container .select-dropdown-chevron{color:var(--color-medium);position:absolute;top:4px;right:5px;cursor:pointer}.select-dropdown-input-container .select-loading-icon{color:var(--color-medium);position:absolute;top:4px;right:5px}#suggest-menu{position:absolute;display:block;z-index:1000;min-width:160px;padding:5px 0;background:#fff;list-style:none;border:1px solid #ccc;border-radius:3px;box-shadow:0 6px 12px rgba(0,0,0,.175)}.suggest-menu-item{white-space:nowrap;padding:3px 10px;color:var(--color-primary);font-weight:700;cursor:pointer}.suggest-menu-item.active{color:#fff;background:#428bca}.suggest-menu-item.active small{color:#fff}.suggest-menu-item small{color:var(--color-light);font-weight:400}#modal-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.9);overflow:auto;z-index:100}#modal-box{position:fixed;max-height:calc(100% - 30px);top:2%;left:50%;transform:translateX(-50%);background:var(--body-background-color);overflow:auto;border-radius:5px}#modal-content{padding:0 5px 5px}#modal-header{text-align:right;padding-right:5px}#modal-close-button{color:var(--color-primary)}#modal-close-button:hover{color:var(--color-error)}.pagination{text-align:center;font-size:.9em}.pagination-showing{margin-right:5px;padding-right:5px;border-right:1px solid #999}.pagination-next{margin-left:5px}.pagination-previous{margin-right:5px}header{display:flex;flex-wrap:wrap;padding:5px 10px;margin-bottom:5px;border-bottom:1px solid #dedede;background-color:var(--header-background-color)}header .title-container{flex:1;min-width:300px}@media (max-width:480px){header .title-container{order:3}}header .board-selector-container{min-width:320px;display:flex;align-items:center}@media (max-width:480px){header .board-selector-container{order:2;min-width:300px}header .board-selector-container input[type=text]{max-width:280px}}header .menus-container{min-width:120px;display:flex;align-items:center;justify-content:flex-end}@media (max-width:480px){header .menus-container{order:1;margin-bottom:5px;margin-left:auto}}header h1{font-size:1.5em}header h1 .tooltip{opacity:.3;font-size:.7em}a i.web-notification-icon{color:var(--link-color-primary)}a i.web-notification-icon:focus,a i.web-notification-icon:hover{color:#000}.logo a{opacity:.5;color:#d40000;text-decoration:none}.logo span{color:var(--color-primary)}.logo a:hover{opacity:.8;color:var(--color-primary)}.logo a:focus span,.logo a:hover span{color:#d40000}.page-header{margin-bottom:20px}.page-header .dropdown{padding-right:10px}.page-header h2{margin:0;padding:0;font-weight:700;border-bottom:1px dotted #ccc}.page-header h2 a{color:var(--color-primary);text-decoration:none}.page-header h2 a:focus,.page-header h2 a:hover{color:var(--color-light)}.page-header ul{text-align:left;margin-top:5px;display:inline-block}.page-header li{display:inline;padding-right:15px}@media (max-width:480px){.page-header li{display:block;line-height:1.5em}}.page-header li.active a{color:var(--color-primary);text-decoration:none;font-weight:700}.page-header li.active a:hover,.page-header li.active a:focus{text-decoration:underline}.menu-inline{margin-bottom:5px}.menu-inline li{display:inline;padding-right:15px}.menu-inline li .active a{font-weight:700;color:#000;text-decoration:none}.sidebar-container{height:100%;display:flex;flex-flow:row}@media (max-width:768px){.sidebar-container{flex-flow:wrap}}.sidebar-content{padding-left:10px;flex:1 100%;max-width:85%;overflow-wrap:break-word}@media (max-width:768px){.sidebar-content{padding-left:0;order:1;max-width:100%}}@media only screen and (min-device-width:768px) and (max-device-width:1024px) and (orientation:landscape) and (-webkit-min-device-pixel-ratio:1){.sidebar-content{max-width:75%}}.sidebar{max-width:25%;min-width:230px}@media (max-width:768px){.sidebar{flex:1 auto;order:2}}.sidebar h2{margin-top:0}.sidebar>ul a{text-decoration:none;color:var(--color-light);font-weight:300}.sidebar>ul a:hover{color:var(--color-primary)}.sidebar>ul li{list-style-type:none;line-height:35px;border-bottom:1px dotted var(--sidebar-border-color);padding-left:13px}.sidebar>ul li:hover{border-left:5px solid #555;padding-left:8px}.sidebar>ul li.active{border-left:5px solid #333;padding-left:8px}.sidebar>ul li.active a{color:var(--color-primary);font-weight:400}.sidebar-icons>ul li{padding-left:0}.sidebar-icons>ul li:hover,.sidebar-icons>ul li.active{padding-left:0;border-left:none}.sidebar>ul li.active a:focus,.sidebar>ul li.active a:hover{color:var(--color-medium)}.sidebar>ul li:last-child{margin-bottom:15px}.avatar img{vertical-align:bottom}.avatar-left{float:left;margin-right:10px}.avatar-inline{display:inline-block;margin-right:3px}.avatar-48 img,.avatar-48 div{border-radius:30px}.avatar-48 .avatar-letter{line-height:48px;width:48px;font-size:25px}.avatar-20 img,.avatar-20 div{border-radius:10px}.avatar-20 .avatar-letter{line-height:20px;width:20px;font-size:11px}.avatar-letter{color:var(--avatar-color-letter);text-align:center}#file-dropzone,#screenshot-zone{position:relative;border:2px dashed #ccc;width:99%;height:250px;overflow:auto}#file-dropzone-inner,#screenshot-inner{position:absolute;left:0;bottom:48%;width:100%;text-align:center;color:#aaa}#screenshot-zone.screenshot-pasted{border:2px solid #333}#file-list{margin:20px}#file-list li{list-style-type:none;padding-top:8px;padding-bottom:8px;border-bottom:1px dotted #ddd;width:95%}#file-list li .file-error{font-weight:700;color:#b94a48}.file-thumbnails{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-justify-content:flex-start;justify-content:flex-start}.file-thumbnail{width:250px;border:1px solid #efefef;border-radius:5px;margin-bottom:20px;box-shadow:4px 2px 10px -6px rgba(0,0,0,.55);margin-right:15px}.file-thumbnail img{cursor:pointer;border-top-left-radius:5px;border-top-right-radius:5px}.file-thumbnail img:hover{opacity:.5}.file-thumbnail-content{padding-left:8px;padding-right:8px}.file-thumbnail-title{font-weight:700;font-size:.9em;color:var(--color-medium);overflow:hidden;text-overflow:ellipsis}.file-thumbnail-description{font-size:.8em;color:var(--color-light);margin-top:8px;margin-bottom:5px}.file-viewer{position:relative}.file-viewer img{max-width:95%;max-height:85%;margin-top:10px}.color-picker{width:220px}.color-picker-option{height:25px}.color-picker-square{display:inline-block;width:18px;height:18px;margin-right:5px;border:1px solid #000}.color-picker-label{display:inline-block;vertical-align:bottom;padding-bottom:3px}.filter-box{max-width:100%}.action-menu{color:var(--color-primary);text-decoration:none}.action-menu:hover,.action-menu:focus{text-decoration:underline}.js-project-creation-options{max-width:500px;border-left:3px dotted #efefef;margin-top:20px;padding-left:15px;padding-bottom:5px;padding-top:5px}.project-overview-columns{display:-webkit-flex;display:flex;-webkit-flex-direction:row;flex-direction:row;-webkit-flex-wrap:wrap;flex-wrap:wrap;-webkit-align-items:center;align-items:center;-webkit-justify-content:center;justify-content:center;margin-bottom:20px;font-size:1.4em}@media (max-width:480px){.project-overview-columns{display:block}}.project-overview-column{text-align:center;margin-right:3%;margin-top:5px;padding:3px 15px 3px 15px;border:1px dashed #ddd}@media (max-width:480px){.project-overview-column{text-align:left}}.project-overview-column small{color:var(--color-light)}.project-overview-column strong{color:var(--color-medium);display:block}@media (max-width:480px){.project-overview-column strong{display:inline}}.project-header{margin-bottom:8px}.project-header .dropdown-component{margin-top:4px;margin-right:5px;float:left}@media (max-width:768px){.project-header .dropdown-component{float:none}}.project-header .views-switcher-component{margin-top:4px;margin-bottom:10px;float:left}@media (max-width:768px){.project-header .views-switcher-component{float:none;margin-bottom:10px}}.project-header .filter-box-component form{margin:0}.views{margin-right:10px;margin-top:1px;font-size:.9em}@media (max-width:560px){.views{width:100%}}@media (max-width:768px){.views{margin-top:10px;font-size:1em}}@media (max-width:480px){.views{margin-top:5px}}.views li{white-space:nowrap;background:var(--views-background-color);border:1px solid var(--views-border-color);border-right:none;padding:4px 8px;display:inline}@media (max-width:560px){.views li{display:block;margin-top:5px;border-radius:5px;border:1px solid var(--views-border-color)}}.views li.active a{font-weight:700;color:var(--views-active-color);text-decoration:none}.views li:first-child{border-top-left-radius:5px;border-bottom-left-radius:5px}.views li:last-child{border-right:1px solid var(--views-border-color);border-top-right-radius:5px;border-bottom-right-radius:5px}.views a{color:var(--color-ligth);text-decoration:none}.views a:hover{color:var(--color-primary);text-decoration:underline}.dashboard-project-stats small{margin-right:10px;color:var(--color-light)}.dashboard-table-link{font-weight:700;color:#000;text-decoration:none}.dashboard-table-link:focus,.dashboard-table-link:hover{color:var(--color-light)}.public-board{margin-top:5px}.public-task{max-width:800px;margin:5px auto 0}#board-container{overflow-x:auto}#board{table-layout:fixed;margin-bottom:0}#board tr.board-swimlane-columns-first{visibility:hidden;padding:0}#board th.board-column-header{width:240px}#board th.board-column-header-first{visibility:hidden;padding:0}#board td{vertical-align:top}.board-container-compact{overflow-x:initial}@media all and (-ms-high-contrast:active),(-ms-high-contrast:none){.board-container-compact #board{table-layout:auto}}#board th.board-column-header.board-column-compact{width:initial}.board-column-collapsed{display:none}.board-column-expanded-header{display:flex;align-items:center}td.board-column-task-collapsed{font-weight:700;background-color:var(--table-header-background-color)}#board th.board-column-header-collapsed{width:28px;min-width:28px;text-align:center;overflow:hidden}.board-rotation-wrapper{position:relative;padding:8px 4px;min-height:150px;overflow:hidden}.board-rotation{white-space:nowrap;transform:rotate(90deg);transform-origin:0 100%}.board-column-title .dropdown-menu{text-decoration:none}.board-add-icon{float:left;padding:0 5px}.board-add-icon i{text-decoration:none;color:var(--link-color-primary);font-size:1.4em}.board-add-icon i:focus,.board-add-icon i:hover{text-decoration:none;color:red}.board-column-header-task-count{color:var(--color-light);font-weight:400;font-size:.85em}a.board-swimlane-toggle{text-decoration:none}a.board-swimlane-toggle:hover,a.board-swimlane-toggle:focus{color:#000;text-decoration:none;border:none}.board-task-list{min-height:60px}.board-task-list-compact{max-height:90vh;overflow-y:auto}.board-task-list .task-board:last-child{margin-bottom:0}.board-task-list-limit{background-color:var(--board-task-limit-color)}.draggable-item{cursor:pointer;user-select:none;-webkit-user-select:none;-moz-user-select:none}.draggable-placeholder{border:2px dashed var(--draggable-placeholder-border-color);background:var(--draggable-placeholder-background-color);height:70px;margin-bottom:10px}div.draggable-item-selected{border:1px solid #000}.task-board-sort-handle{float:left;padding-right:5px}.task-board{position:relative;margin-bottom:4px;border:1px solid #000;padding:2px;word-wrap:break-word;font-size:.9em;border-radius:6px}div.task-board-recent{border-width:2px}div.task-board-status-closed{user-select:none;border:1px dotted #555}.task-board a{color:#000;text-decoration:none}.task-board-collapsed{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.task-board-title{margin-top:5px;margin-bottom:8px}.task-board-title a:hover{text-decoration:underline}.task-board-saving-state{opacity:.3}.task-board-saving-icon{position:absolute;margin:auto;width:100%;text-align:center;color:#000}.task-board-avatars{text-align:right;float:right}.task-board-change-assignee{cursor:pointer}.task-board-change-assignee:hover{opacity:.6}.task-list-avatars{display:inline-block;float:left}@media (max-width:768px){.task-list-avatars{float:none;display:block}}.task-list-avatars .task-avatar-assignee{font-weight:300;color:#999}.task-list-avatars:hover .task-avatar-assignee{font-weight:400;color:#000}.task-board-icons,.task-list-icons{font-size:.8em;text-align:right}.task-board-icons a,.task-board-icons span.tooltip,.task-list-icons a,.task-list-icons span.tooltip{text-decoration:none}.task-board-icons a:hover,.task-board-icons span.tooltip:hover,.task-list-icons a:hover,.task-list-icons span.tooltip:hover{color:var(--color-primary)}.task-board-icons a:hover i,.task-board-icons span.tooltip:hover i,.task-list-icons a:hover i,.task-list-icons span.tooltip:hover i{color:var(--color-primary)}.task-board-icons .task-score,.task-list-icons .task-score{font-weight:700}.task-board-icons .flag-milestone,.task-list-icons .flag-milestone{color:green}.task-board-icons{margin-top:7px}.task-board-icons a{opacity:.5}.task-board-icons span{opacity:.5;margin-left:4px}.task-board-icons a:hover,.task-board-icons span.tooltip:hover{opacity:1;font-weight:700}.task-board-icons .task-board-icons-row{line-height:22px}.task-list-icons{line-height:22px}.task-list-icons a,.task-list-icons span,.task-list-icons i{color:var(--task-list-icons-color);opacity:1}.task-list-icons span{margin-left:5px}@media (max-width:768px){.task-list-icons{text-align:left}}.task-icon-age{display:inline-block}span.task-icon-age-total{border:1px solid #e5e5e5;padding:1px 3px 1px 3px;border-top-left-radius:3px;border-bottom-left-radius:3px}span.task-icon-age-column{border:1px solid #e5e5e5;border-left:none;margin-left:-5px;padding:1px 3px 1px 3px;border-top-right-radius:3px;border-bottom-right-radius:3px}.task-board span.task-icon-age-total,.task-board span.task-icon-age-column{border-color:#666}.task-board-category-container{text-align:right;margin-top:8px;margin-bottom:8px}.task-board-category{border:1px solid #555;font-size:.9em;font-weight:500;color:#000;padding:1px 3px 1px 2px;border-radius:3px}.task-board-category a:hover{text-decoration:underline}.task-date{font-weight:500;color:#000}span.task-date-today{opacity:1;color:var(--link-color-primary)}span.task-date-overdue{opacity:1;color:#b94a48}.task-tags li{display:inline-block;margin:3px 3px 0 0;padding:1px 3px 1px 3px;color:var(--color-primary);border:1px solid #333;border-radius:4px}.task-summary-container .task-tags{margin-top:10px}#task-summary{margin-bottom:15px}#task-summary h2{color:var(--color-medium);font-size:1.6em;margin-top:0;padding-top:0}.task-summary-container{border:2px solid #000;border-radius:8px;padding:10px}.task-summary-columns{display:flex;flex-flow:row;justify-content:space-between}@media (max-width:768px){.task-summary-columns{flex-flow:column}}.task-summary-column{color:var(--color-dark)}.task-summary-column span{color:var(--color-medium)}.task-summary-column li{line-height:23px}#external-task-view{padding:10px;margin-top:10px;margin-bottom:10px;border:1px dotted #ccc}.task-form-container{box-sizing:border-box;display:flex;flex-wrap:wrap}.task-form-container>*{box-sizing:border-box}.task-form-container>*{width:1%}.task-form-main-column{width:60%}@media (max-width:1000px){.task-form-main-column{width:100%}}.task-form-main-column input[type="text"]{width:700px;max-width:99%}.task-form-secondary-column{max-width:250px;min-width:200px;max-height:600px;padding-left:10px;overflow:auto;width:20%}@media (max-width:1000px){.task-form-secondary-column{width:100%;max-width:99%;max-height:none}}@media (max-width:768px){.task-form-secondary-column{padding-left:0}}.task-form-secondary-column label:first-child{margin-top:0}@media (max-width:1000px){.task-form-secondary-column label:first-child{margin-top:10px}}.task-form-bottom{width:100%}.task-form-bottom label{display:inline-block}.task-form-bottom-column{display:inline-block;width:49%;margin-left:5px;margin-right:5px}.comment-sorting{text-align:right}.comment-sorting a{color:var(--color-medium);font-weight:400;text-decoration:none}.comment-sorting a:hover{color:var(--color-light)}.comment{padding:5px;margin-bottom:15px}.comment-title{border-bottom:1px dotted var(--comment-title-border-color);margin-left:55px}.comment-date{color:var(--color-light);font-weight:200}.comment-visibility{color:var(--color-light);font-weight:200}.comment-actions{text-align:right}.comment-content{margin-left:55px}.comments .text-editor textarea{height:90px}.comments .text-editor .text-editor-preview-area{height:90px}.comments .comment-highlighted{background-color:var(--comment-highlighted-background-color);border:2px solid var(--comment-highlighted-border-color)}.comments .comment-highlighted:hover{background-color:var(--comment-highlighted-hover-background-color)}.comments .comment:hover{background:var(--comment-highlighted-hover-background-color)}.comments .comment:nth-child(even):not(.comment-highlighted){background:var(--comment-nth-background-color)}.comments .comment:nth-child(even):not(.comment-highlighted):hover{background:var(--comment-highlighted-hover-background-color)}.subtask-cell{padding:4px 10px;border-top:1px dotted #dedede;border-left:1px dotted #dedede;display:table-cell;vertical-align:middle}.subtask-cell a{color:var(--color-primary);text-decoration:none}.subtask-cell a:hover,.subtask-cell a:focus{color:var(--link-color-primary)}.subtask-cell:first-child{border-left:none}@media (max-width:768px){.subtask-cell{width:90%;display:block;border-left:none}}.subtasks-table .subtask-table-td{display:flex;white-space:normal;min-width:400px}.subtasks-table .subtask-submenu{display:flex}.js-subtask-toggle-status{display:flex;text-decoration:none}.task-list-subtasks{display:table;width:100%}@media (max-width:768px){.task-list-subtasks{display:block}}.task-list-subtask{display:table-row}@media (max-width:768px){.task-list-subtask{display:block}}@media (max-width:768px){.subtask-assignee,.subtask-time-tracking-cell{display:none}}.subtask-time-tracking{white-space:normal}.task-links-table td{vertical-align:middle}.task-links-task-count{color:var(--color-light);font-weight:400}.task-link-closed{text-decoration:line-through}.task-links-table-td{display:flex}.text-editor{margin-top:10px}.text-editor a{font-size:1em;color:var(--color-light);text-decoration:none;margin-right:10px}.text-editor a:hover{color:var(--link-color-primary)}.text-editor .text-editor-preview-area{border:1px solid #dedede;width:700px;max-width:99%;height:250px;overflow:auto;padding:2px}.text-editor textarea{width:700px;max-width:98%;height:250px}.markdown{line-height:1.4em}.markdown h1{margin-top:5px;margin-bottom:10px;font-weight:700}.markdown h2{font-weight:700}.markdown p{margin-bottom:10px}.markdown ol,.markdown ul{margin-left:25px;margin-top:10px;margin-bottom:10px}.markdown pre{background:#fbfbfb;padding:10px;border-radius:5px;border:1px solid #ddd;overflow:auto;overflow-wrap:initial;color:var(--color-medium)}.markdown blockquote{font-style:italic;border-left:3px solid #ddd;padding-left:10px;margin-bottom:10px;margin-left:20px}.markdown img{display:block;max-width:80%;margin-top:10px}.panel{border-radius:4px;padding:8px 35px 8px 10px;margin-top:10px;margin-bottom:15px;border:1px solid var(--panel-border-color);color:var(--color-primary);background-color:var(--panel-background-color);overflow:auto}.panel li{list-style-type:square;margin-left:20px;line-height:1.35em}.activity-event{margin-bottom:15px;padding:10px}.activity-event:nth-child(even){background:var(--activity-event-background-color)}.activity-event:hover{background:var(--activity-event-hover-color)}.activity-date{margin-left:10px;font-weight:400;color:var(--color-light)}.activity-content{margin-left:55px}.activity-title{font-weight:700;color:var(--activity-title-color);border-bottom:1px dotted var(--activity-title-border-color)}.activity-description{color:var(--color-light);margin-top:10px}@media (max-width:480px){.activity-description{overflow:auto}}.activity-description li{list-style-type:circle}.activity-description ul{margin-top:10px;margin-left:20px}.user-mention-link{font-weight:700;color:var(--user-mention-color);text-decoration:none}.user-mention-link:hover{color:var(--color-medium)}.image-slideshow-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,.95);overflow:auto;z-index:100}.image-slideshow-overlay img{display:block;margin:auto}.image-slideshow-overlay figcaption{color:#fff;opacity:.7;position:absolute;bottom:5px;right:15px}.slideshow-icon{color:#fff;position:absolute;font-size:2.5em;opacity:.6}.slideshow-icon:hover{opacity:.9;cursor:pointer}.slideshow-previous-icon{left:10px;top:45%}.slideshow-next-icon{right:10px;top:45%}.slideshow-close-icon{right:10px;top:10px;font-size:1.4em}.slideshow-download-icon{left:10px;bottom:10px;font-size:1.3em}.list-item-links,.list-item-actions{display:inline-block;float:left;margin-left:10px}.list-item-links a{margin:0}.list-item-action-hidden{display:none}.bulk-change-checkbox{float:left}.bulk-change-inputs{float:left;padding-left:10px}.bulk-change-inputs label{margin-top:0;margin-bottom:3px} \ No newline at end of file diff --git a/assets/css/print.min.css b/assets/css/print.min.css new file mode 100644 index 0000000..c5e3ca2 --- /dev/null +++ b/assets/css/print.min.css @@ -0,0 +1 @@ +@page{orientation:landscape;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3)}#board-container{overflow-x:initial!important}.board-task-list{min-height:0!important}.task-board{page-break-inside:avoid}.menu-inline,.project-header,.page-header,.menus-container,.sidebar,.alert,.alert-info,.dropdown>ul{display:none} \ No newline at end of file diff --git a/assets/css/vendor.min.css b/assets/css/vendor.min.css new file mode 100644 index 0000000..db558ff --- /dev/null +++ b/assets/css/vendor.min.css @@ -0,0 +1,16 @@ +/*! jQuery UI - v1.13.2 - 2022-07-14 +* http://jqueryui.com +* Includes: core.css, accordion.css, autocomplete.css, menu.css, button.css, controlgroup.css, checkboxradio.css, datepicker.css, dialog.css, draggable.css, resizable.css, progressbar.css, selectable.css, selectmenu.css, slider.css, sortable.css, spinner.css, tabs.css, tooltip.css, theme.css +* To view and modify this theme, visit http://jqueryui.com/themeroller/?bgShadowXPos=&bgOverlayXPos=&bgErrorXPos=&bgHighlightXPos=&bgContentXPos=&bgHeaderXPos=&bgActiveXPos=&bgHoverXPos=&bgDefaultXPos=&bgShadowYPos=&bgOverlayYPos=&bgErrorYPos=&bgHighlightYPos=&bgContentYPos=&bgHeaderYPos=&bgActiveYPos=&bgHoverYPos=&bgDefaultYPos=&bgShadowRepeat=&bgOverlayRepeat=&bgErrorRepeat=&bgHighlightRepeat=&bgContentRepeat=&bgHeaderRepeat=&bgActiveRepeat=&bgHoverRepeat=&bgDefaultRepeat=&iconsHover=url(%22images%2Fui-icons_555555_256x240.png%22)&iconsHighlight=url(%22images%2Fui-icons_777620_256x240.png%22)&iconsHeader=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsError=url(%22images%2Fui-icons_cc0000_256x240.png%22)&iconsDefault=url(%22images%2Fui-icons_777777_256x240.png%22)&iconsContent=url(%22images%2Fui-icons_444444_256x240.png%22)&iconsActive=url(%22images%2Fui-icons_ffffff_256x240.png%22)&bgImgUrlShadow=&bgImgUrlOverlay=&bgImgUrlHover=&bgImgUrlHighlight=&bgImgUrlHeader=&bgImgUrlError=&bgImgUrlDefault=&bgImgUrlContent=&bgImgUrlActive=&opacityFilterShadow=Alpha(Opacity%3D30)&opacityFilterOverlay=Alpha(Opacity%3D30)&opacityShadowPerc=30&opacityOverlayPerc=30&iconColorHover=%23555555&iconColorHighlight=%23777620&iconColorHeader=%23444444&iconColorError=%23cc0000&iconColorDefault=%23777777&iconColorContent=%23444444&iconColorActive=%23ffffff&bgImgOpacityShadow=0&bgImgOpacityOverlay=0&bgImgOpacityError=95&bgImgOpacityHighlight=55&bgImgOpacityContent=75&bgImgOpacityHeader=75&bgImgOpacityActive=65&bgImgOpacityHover=75&bgImgOpacityDefault=75&bgTextureShadow=flat&bgTextureOverlay=flat&bgTextureError=flat&bgTextureHighlight=flat&bgTextureContent=flat&bgTextureHeader=flat&bgTextureActive=flat&bgTextureHover=flat&bgTextureDefault=flat&cornerRadius=3px&fwDefault=normal&ffDefault=Arial%2CHelvetica%2Csans-serif&fsDefault=1em&cornerRadiusShadow=8px&thicknessShadow=5px&offsetLeftShadow=0px&offsetTopShadow=0px&opacityShadow=.3&bgColorShadow=%23666666&opacityOverlay=.3&bgColorOverlay=%23aaaaaa&fcError=%235f3f3f&borderColorError=%23f1a899&bgColorError=%23fddfdf&fcHighlight=%23777620&borderColorHighlight=%23dad55e&bgColorHighlight=%23fffa90&fcContent=%23333333&borderColorContent=%23dddddd&bgColorContent=%23ffffff&fcHeader=%23333333&borderColorHeader=%23dddddd&bgColorHeader=%23e9e9e9&fcActive=%23ffffff&borderColorActive=%23003eff&bgColorActive=%23007fff&fcHover=%232b2b2b&borderColorHover=%23cccccc&bgColorHover=%23ededed&fcDefault=%23454545&borderColorDefault=%23c5c5c5&bgColorDefault=%23f6f6f6 +* Copyright jQuery Foundation and other contributors; Licensed MIT */ + +.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;-ms-filter:"alpha(opacity=0)"}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;font-size:100%}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup > .ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup > .ui-controlgroup-item:focus,.ui-controlgroup > .ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical > .ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label + .ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label + .ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}.ui-draggable-handle{-ms-touch-action:none;touch-action:none}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,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");height:100%;-ms-filter:"alpha(opacity=25)";opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-text{display:block;margin-right:20px;overflow:hidden;text-overflow:ellipsis}.ui-selectmenu-button.ui-button{text-align:left;white-space:nowrap;width:14em}.ui-selectmenu-icon.ui-icon{float:right;margin-top:0}.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:pointer;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}.ui-sortable-handle{-ms-touch-action:none;touch-action:none}.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:.222em 0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:2em}.ui-spinner-button{width:1.6em;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top-style:none;border-bottom-style:none;border-right-style:none}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px}.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px rgb(94,158,214)}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;-ms-filter:"alpha(opacity=70)";font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;-ms-filter:"alpha(opacity=35)";background-image:none}.ui-state-disabled .ui-icon{-ms-filter:"alpha(opacity=35)"}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-widget-header .ui-icon{background-image:url("images/ui-icons_444444_256x240.png")}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url("images/ui-icons_555555_256x240.png")}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url("images/ui-icons_ffffff_256x240.png")}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url("images/ui-icons_777620_256x240.png")}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url("images/ui-icons_cc0000_256x240.png")}.ui-button .ui-icon{background-image:url("images/ui-icons_777777_256x240.png")}.ui-icon-blank.ui-icon-blank.ui-icon-blank{background-image:none}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:.003;-ms-filter:Alpha(Opacity=.3)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666}/*! jQuery Timepicker Addon - v1.6.3 - 2016-04-20 +* http://trentrichardson.com/examples/timepicker +* Copyright (c) 2016 Trent Richardson; Licensed MIT */ + +.ui-timepicker-div .ui-widget-header{margin-bottom:8px}.ui-timepicker-div dl{text-align:left}.ui-timepicker-div dl dt{float:left;clear:left;padding:0 0 0 5px}.ui-timepicker-div dl dd{margin:0 10px 10px 40%}.ui-timepicker-div td{font-size:90%}.ui-tpicker-grid-label{background:0 0;border:0;margin:0;padding:0}.ui-timepicker-div .ui_tpicker_unit_hide{display:none}.ui-timepicker-div .ui_tpicker_time .ui_tpicker_time_input{background:0 0;color:inherit;border:0;outline:0;border-bottom:solid 1px #555;width:95%}.ui-timepicker-div .ui_tpicker_time .ui_tpicker_time_input:focus{border-bottom-color:#aaa}.ui-timepicker-rtl{direction:rtl}.ui-timepicker-rtl dl{text-align:right;padding:0 5px 0 0}.ui-timepicker-rtl dl dt{float:right;clear:right}.ui-timepicker-rtl dl dd{margin:0 40% 10px 10px}.ui-timepicker-div.ui-timepicker-oneLine{padding-right:2px}.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_time,.ui-timepicker-div.ui-timepicker-oneLine dt{display:none}.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_time_label{display:block;padding-top:2px}.ui-timepicker-div.ui-timepicker-oneLine dl{text-align:right}.ui-timepicker-div.ui-timepicker-oneLine dl dd,.ui-timepicker-div.ui-timepicker-oneLine dl dd>div{display:inline-block;margin:0}.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_minute:before,.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_second:before{content:':';display:inline-block}.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_millisec:before,.ui-timepicker-div.ui-timepicker-oneLine dl dd.ui_tpicker_microsec:before{content:'.';display:inline-block}.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_unit_hide,.ui-timepicker-div.ui-timepicker-oneLine .ui_tpicker_unit_hide:before{display:none}.select2-container{box-sizing:border-box;display:inline-block;margin:0;position:relative;vertical-align:middle}.select2-container .select2-selection--single{box-sizing:border-box;cursor:pointer;display:block;height:28px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--single .select2-selection__rendered{display:block;padding-left:8px;padding-right:20px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-selection--single .select2-selection__clear{position:relative}.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered{padding-right:8px;padding-left:20px}.select2-container .select2-selection--multiple{box-sizing:border-box;cursor:pointer;display:block;min-height:32px;user-select:none;-webkit-user-select:none}.select2-container .select2-selection--multiple .select2-selection__rendered{display:inline-block;overflow:hidden;padding-left:8px;text-overflow:ellipsis;white-space:nowrap}.select2-container .select2-search--inline{float:left}.select2-container .select2-search--inline .select2-search__field{box-sizing:border-box;border:none;font-size:100%;margin-top:5px;padding:0}.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-dropdown{background-color:white;border:1px solid #aaa;border-radius:4px;box-sizing:border-box;display:block;position:absolute;left:-100000px;width:100%;z-index:1051}.select2-results{display:block}.select2-results__options{list-style:none;margin:0;padding:0}.select2-results__option{padding:6px;user-select:none;-webkit-user-select:none}.select2-results__option[aria-selected]{cursor:pointer}.select2-container--open .select2-dropdown{left:0}.select2-container--open .select2-dropdown--above{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--open .select2-dropdown--below{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-search--dropdown{display:block;padding:4px}.select2-search--dropdown .select2-search__field{padding:4px;width:100%;box-sizing:border-box}.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button{-webkit-appearance:none}.select2-search--dropdown.select2-search--hide{display:none}.select2-close-mask{border:0;margin:0;padding:0;display:block;position:fixed;left:0;top:0;min-height:100%;min-width:100%;height:auto;width:auto;opacity:0;z-index:99;background-color:#fff;filter:alpha(opacity=0)}.select2-hidden-accessible{border:0 !important;clip:rect(0 0 0 0) !important;height:1px !important;margin:-1px !important;overflow:hidden !important;padding:0 !important;position:absolute !important;width:1px !important}.select2-container--default .select2-selection--single{background-color:#fff;border:1px solid #aaa;border-radius:4px}.select2-container--default .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--default .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold}.select2-container--default .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--default .select2-selection--single .select2-selection__arrow{height:26px;position:absolute;top:1px;right:1px;width:20px}.select2-container--default .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow{left:1px;right:auto}.select2-container--default.select2-container--disabled .select2-selection--single{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear{display:none}.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--default .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text}.select2-container--default .select2-selection--multiple .select2-selection__rendered{box-sizing:border-box;list-style:none;margin:0;padding:0 5px;width:100%}.select2-container--default .select2-selection--multiple .select2-selection__placeholder{color:#999;margin-top:5px;float:left}.select2-container--default .select2-selection--multiple .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-top:5px;margin-right:10px}.select2-container--default .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove{color:#999;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover{color:#333}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder,.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline{float:right}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--default.select2-container--focus .select2-selection--multiple{border:solid black 1px;outline:0}.select2-container--default.select2-container--disabled .select2-selection--multiple{background-color:#eee;cursor:default}.select2-container--default.select2-container--disabled .select2-selection__choice__remove{display:none}.select2-container--default.select2-container--open.select2-container--above .select2-selection--single,.select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple{border-top-left-radius:0;border-top-right-radius:0}.select2-container--default.select2-container--open.select2-container--below .select2-selection--single,.select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--default .select2-search--dropdown .select2-search__field{border:1px solid #aaa}.select2-container--default .select2-search--inline .select2-search__field{background:transparent;border:none;outline:0;box-shadow:none;-webkit-appearance:textfield}.select2-container--default .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--default .select2-results__option[role=group]{padding:0}.select2-container--default .select2-results__option[aria-disabled=true]{color:#999}.select2-container--default .select2-results__option[aria-selected=true]{background-color:#ddd}.select2-container--default .select2-results__option .select2-results__option{padding-left:1em}.select2-container--default .select2-results__option .select2-results__option .select2-results__group{padding-left:0}.select2-container--default .select2-results__option .select2-results__option .select2-results__option{margin-left:-1em;padding-left:2em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-2em;padding-left:3em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-3em;padding-left:4em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-4em;padding-left:5em}.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option{margin-left:-5em;padding-left:6em}.select2-container--default .select2-results__option--highlighted[aria-selected]{background-color:#5897fb;color:white}.select2-container--default .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic .select2-selection--single{background-color:#f7f7f7;border:1px solid #aaa;border-radius:4px;outline:0;background-image:-webkit-linear-gradient(top, #fff 50%, #eee 100%);background-image:-o-linear-gradient(top, #fff 50%, #eee 100%);background-image:linear-gradient(to bottom, #fff 50%, #eee 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic .select2-selection--single:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--single .select2-selection__rendered{color:#444;line-height:28px}.select2-container--classic .select2-selection--single .select2-selection__clear{cursor:pointer;float:right;font-weight:bold;margin-right:10px}.select2-container--classic .select2-selection--single .select2-selection__placeholder{color:#999}.select2-container--classic .select2-selection--single .select2-selection__arrow{background-color:#ddd;border:none;border-left:1px solid #aaa;border-top-right-radius:4px;border-bottom-right-radius:4px;height:26px;position:absolute;top:1px;right:1px;width:20px;background-image:-webkit-linear-gradient(top, #eee 50%, #ccc 100%);background-image:-o-linear-gradient(top, #eee 50%, #ccc 100%);background-image:linear-gradient(to bottom, #eee 50%, #ccc 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0)}.select2-container--classic .select2-selection--single .select2-selection__arrow b{border-color:#888 transparent transparent transparent;border-style:solid;border-width:5px 4px 0 4px;height:0;left:50%;margin-left:-4px;margin-top:-2px;position:absolute;top:50%;width:0}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear{float:left}.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow{border:none;border-right:1px solid #aaa;border-radius:0;border-top-left-radius:4px;border-bottom-left-radius:4px;left:1px;right:auto}.select2-container--classic.select2-container--open .select2-selection--single{border:1px solid #5897fb}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow{background:transparent;border:none}.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b{border-color:transparent transparent #888 transparent;border-width:0 4px 5px 4px}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single{border-top:none;border-top-left-radius:0;border-top-right-radius:0;background-image:-webkit-linear-gradient(top, #fff 0%, #eee 50%);background-image:-o-linear-gradient(top, #fff 0%, #eee 50%);background-image:linear-gradient(to bottom, #fff 0%, #eee 50%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0)}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0;background-image:-webkit-linear-gradient(top, #eee 50%, #fff 100%);background-image:-o-linear-gradient(top, #eee 50%, #fff 100%);background-image:linear-gradient(to bottom, #eee 50%, #fff 100%);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0)}.select2-container--classic .select2-selection--multiple{background-color:white;border:1px solid #aaa;border-radius:4px;cursor:text;outline:0}.select2-container--classic .select2-selection--multiple:focus{border:1px solid #5897fb}.select2-container--classic .select2-selection--multiple .select2-selection__rendered{list-style:none;margin:0;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__clear{display:none}.select2-container--classic .select2-selection--multiple .select2-selection__choice{background-color:#e4e4e4;border:1px solid #aaa;border-radius:4px;cursor:default;float:left;margin-right:5px;margin-top:5px;padding:0 5px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove{color:#888;cursor:pointer;display:inline-block;font-weight:bold;margin-right:2px}.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover{color:#555}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{float:right}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice{margin-left:5px;margin-right:auto}.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove{margin-left:2px;margin-right:auto}.select2-container--classic.select2-container--open .select2-selection--multiple{border:1px solid #5897fb}.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple{border-top:none;border-top-left-radius:0;border-top-right-radius:0}.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple{border-bottom:none;border-bottom-left-radius:0;border-bottom-right-radius:0}.select2-container--classic .select2-search--dropdown .select2-search__field{border:1px solid #aaa;outline:0}.select2-container--classic .select2-search--inline .select2-search__field{outline:0;box-shadow:none}.select2-container--classic .select2-dropdown{background-color:#fff;border:1px solid transparent}.select2-container--classic .select2-dropdown--above{border-bottom:none}.select2-container--classic .select2-dropdown--below{border-top:none}.select2-container--classic .select2-results>.select2-results__options{max-height:200px;overflow-y:auto}.select2-container--classic .select2-results__option[role=group]{padding:0}.select2-container--classic .select2-results__option[aria-disabled=true]{color:grey}.select2-container--classic .select2-results__option--highlighted[aria-selected]{background-color:#3875d7;color:#fff}.select2-container--classic .select2-results__group{cursor:default;display:block;padding:6px}.select2-container--classic.select2-container--open .select2-dropdown{border-color:#5897fb} +/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.7.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.7.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff2?v=4.7.0') format('woff2'),url('../fonts/fontawesome-webfont.woff?v=4.7.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.7.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.7.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} +.c3 svg{font:10px sans-serif;-webkit-tap-highlight-color:transparent}.c3 line,.c3 path{fill:none;stroke:#000}.c3 text{-webkit-user-select:none;-moz-user-select:none;user-select:none}.c3-bars path,.c3-event-rect,.c3-legend-item-tile,.c3-xgrid-focus,.c3-ygrid{shape-rendering:crispEdges}.c3-chart-arc path{stroke:#fff}.c3-chart-arc rect{stroke:#fff;stroke-width:1}.c3-chart-arc text{fill:#fff;font-size:13px}.c3-grid line{stroke:#aaa}.c3-grid text{fill:#aaa}.c3-xgrid,.c3-ygrid{stroke-dasharray:3 3}.c3-text.c3-empty{fill:grey;font-size:2em}.c3-line{stroke-width:1px}.c3-circle._expanded_{stroke-width:1px;stroke:#fff}.c3-selected-circle{fill:#fff;stroke-width:2px}.c3-bar{stroke-width:0}.c3-bar._expanded_{fill-opacity:1;fill-opacity:.75}.c3-target.c3-focused{opacity:1}.c3-target.c3-focused path.c3-line,.c3-target.c3-focused path.c3-step{stroke-width:2px}.c3-target.c3-defocused{opacity:.3!important}.c3-region{fill:#4682b4;fill-opacity:.1}.c3-brush .extent{fill-opacity:.1}.c3-legend-item{font-size:12px}.c3-legend-item-hidden{opacity:.15}.c3-legend-background{opacity:.75;fill:#fff;stroke:#d3d3d3;stroke-width:1}.c3-title{font:14px sans-serif}.c3-tooltip-container{z-index:10}.c3-tooltip{border-collapse:collapse;border-spacing:0;background-color:#fff;empty-cells:show;-webkit-box-shadow:7px 7px 12px -9px #777;-moz-box-shadow:7px 7px 12px -9px #777;box-shadow:7px 7px 12px -9px #777;opacity:.9}.c3-tooltip tr{border:1px solid #ccc}.c3-tooltip th{background-color:#aaa;font-size:14px;padding:2px 5px;text-align:left;color:#fff}.c3-tooltip td{font-size:13px;padding:3px 6px;background-color:#fff;border-left:1px dotted #999}.c3-tooltip td>span{display:inline-block;width:10px;height:10px;margin-right:6px}.c3-tooltip td.value{text-align:right}.c3-area{stroke-width:0;opacity:.2}.c3-chart-arcs-title{dominant-baseline:middle;font-size:1.3em}.c3-chart-arcs .c3-chart-arcs-background{fill:#e0e0e0;stroke:#fff}.c3-chart-arcs .c3-chart-arcs-gauge-unit{fill:#000;font-size:16px}.c3-chart-arcs .c3-chart-arcs-gauge-max{fill:#777}.c3-chart-arcs .c3-chart-arcs-gauge-min{fill:#777}.c3-chart-arc .c3-gauge-value{fill:#000}.c3-chart-arc.c3-target g path{opacity:1}.c3-chart-arc.c3-target.c3-focused g path{opacity:1} \ No newline at end of file diff --git a/assets/fonts/FontAwesome.otf b/assets/fonts/FontAwesome.otf new file mode 100644 index 0000000000000000000000000000000000000000..401ec0f36e4f73b8efa40bd6f604fe80d286db70 GIT binary patch literal 134808 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 literal 0 HcmV?d00001 diff --git a/assets/fonts/fontawesome-webfont.eot b/assets/fonts/fontawesome-webfont.eot new file mode 100644 index 0000000000000000000000000000000000000000..e9f60ca953f93e35eab4108bd414bc02ddcf3928 GIT binary patch literal 165742 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 G*yFsbY~cLNXLP73aA74Mq6M9f^&YV`isWW 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

5q!RY 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 + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/fonts/fontawesome-webfont.ttf b/assets/fonts/fontawesome-webfont.ttf new file mode 100644 index 0000000000000000000000000000000000000000..35acda2fa1196aad98c2adf4378a7611dd713aa3 GIT binary patch literal 165548 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 z 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 d0mRO}xo^G_%I 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 SdSQM28?+M|D(2r_;{O0|uQ74} literal 0 HcmV?d00001 diff --git a/assets/fonts/fontawesome-webfont.woff2 b/assets/fonts/fontawesome-webfont.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4d13fc60404b91e398a37200c4a77b645cfd9586 GIT binary patch literal 77160 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 literal 0 HcmV?d00001 diff --git a/assets/img/adaptive-favicon.svg b/assets/img/adaptive-favicon.svg new file mode 100644 index 0000000..288e825 --- /dev/null +++ b/assets/img/adaptive-favicon.svg @@ -0,0 +1,13 @@ + + + + + + + diff --git a/assets/img/favicon.png b/assets/img/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..99f5ccbc9a0d08e9136559b8fde1ecb8dafc752c GIT binary patch literal 1336 zcmV-81;_e{P)000F4Nkl%Tk{ zfC5ke3P1rUfZxJwwrI6UrqzZ(nP3`?C_vo1bZOI^ImzbcdGijeU3+fdz9%WKp+KR3H|Lk&B?_Qi|2y;FMj6bT^D%{+NbadAum z)BE({_O6~XMI0cRx%uFM6~PkFy=qn73xx=P=H*M$+?+WR?MwkL@89RXS>W$}-@l6h zc$O$3^d;{qRWMkY0`xjviy}q1jRPAt6!$7Ynwwr;=w%0Z`}lF!%9XiYNGuHK0Aly< zMWbZElpa0aK6&zEKD~A8^sZeSXU`tdsujw3xpeGUD?&aXuuB(o$pJ`6d0D3J#!=jvJ|O`0ULw3Cw%12a^kQc(bWa&yyt{D?+IV-#CjFYeu2J8fF^^yztq zaUDC-8KYih%2;$dMgXp4e5X!4!^s^xC;*V5c$P$07O(+815Nv`-$z_Z3RTFT1B`BU$bX7MMtv+An@eouW4)q;9WnUT|0sy zphJhh0B~l{9^MecNVRo6V2XRA9z?cB)&ynpsg3IGpA zzebG+hM-=(SO6w=?aJGuj*W_v0zeo;LcV0kKrqA|JxUjV{q(XfF5LEZrAx~?AHbT1 z0QizNtsx+Q;=9n$X3e<0$YtZk$pRpP2Miz>eCyW5*)yZJQ5<>re)}dDU{cR;wu< zvzB*r!)_PNlX@J<_|&LDFoX;pN(<01ckYibUeN!n1P*3!tW7W^T(}?;P~P2r;fN72 zk&%MiD%v-1eCyOnGI6t7lM4)fp>`zB7A$(b1OV<}Y%uOwxiTgq;_v^w5cb5M-??K; uzI*x<#c|QmcmgP&*kTw8KmjNK1@IG5Yi??L?td5n0000 + + + + + diff --git a/assets/img/inkscape-path-icon.svg b/assets/img/inkscape-path-icon.svg new file mode 100644 index 0000000..625faea --- /dev/null +++ b/assets/img/inkscape-path-icon.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/assets/img/inkscape-text-icon.svg b/assets/img/inkscape-text-icon.svg new file mode 100644 index 0000000..7109242 --- /dev/null +++ b/assets/img/inkscape-text-icon.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + K + B + + diff --git a/assets/img/touch-icon-ipad-retina.png b/assets/img/touch-icon-ipad-retina.png new file mode 100644 index 0000000000000000000000000000000000000000..51702e7a7df6a3e55aa31f2a2f564fe41290f7ee GIT binary patch literal 2673 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 literal 0 HcmV?d00001 diff --git a/assets/img/touch-icon-iphone-retina.png b/assets/img/touch-icon-iphone-retina.png new file mode 100644 index 0000000000000000000000000000000000000000..8c3549076cdd7c0815e715bf3ee49e36261e397c GIT binary patch literal 2267 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 literal 0 HcmV?d00001 diff --git a/assets/img/touch-icon-iphone.png b/assets/img/touch-icon-iphone.png new file mode 100644 index 0000000000000000000000000000000000000000..5d8d555bdeb617eadb5e109e3c8f2784cc6705e8 GIT binary patch literal 1214 zcmV;v1VQ_WP)3M*Ro}nM9zHZl<(Dtk>)`+%CR6eH_3sC+9X~#2;6UrSXvB#1GiDrEv*y+P`wE%N zwl%y*k8f^nCK=MYwKeF658qRyz{6xF?b-ADnH3Wzly!5nKP!5B&+OaR=hG*iR=r&A zU#b)p8rHcp9AL0wmiP4BvwXS1VBlko95I3l_05}CmzcS+i2DF*9c1F{)LgRvXPCK05mS)2HY5?(I;fjES{D&YViQ zoYkt=2RCU#g$7owYS3sHtQdNo?%3wdCfp1A_cL1KSFff*f22*T_VvYM3|0*2Vr0p* zUcFeXig)k6dwbKH)ZV>tfQvObA)#{0lvvWDP$7q{dYR0xcyW4@>e2-c@UlX5EL#>! zs-#Zsur+eX5Zg_PlXXJp&SaHJsdT_9K6{qlq&z&ZNpZB!A3BurdDVo4{mh)%c9Y^~ z9o)Dvmb5Nb%x)_*sfG=0Hz}^x#(DE%$)HA!?6t-%Uv9feakavYFbgG$}Ug!FB77 zY}{y)8)nV~6Dl!#RVZ9(zojZ3YV-6~hcw*lj43nmztW7w=Aetz)!nq;t# z&*SUYk8avDchH~;US8z+zIE#0&6creMGhOrw`XDae*WC2RxPq*R{#EtR!qsoi+rsZ zT8*Yn@#0tl&vZ<9xNWOXo;=W`sMep^vT38D_*yX@Ub#Y6!KTBmc_3!iEUFbUq(uv( z!NAvw(W!iStg4eG3+xJPTVaxddCC$uVbdlTS*LXGP8Pyl*RJ_h<>T`sO&WR&OO;CX z^{Xqa@Vb+QpI*GM_g;{+dpFez8Bo5wL8anuJ+W;YSqM8k2ku*;9Xnd$Mo*s1-MV|( zGO`dx90#nr_;{b(xh<`Rq<8M{wJsPoj4Zr-_^@O90qO1Az@l=YyXg`VI9i7`ZAuox zXzTFaC3?~%iWRp6oUDnlu{F}CCkx?r;II`wMgbKnSmGw_+sDPac;rYd!PN`Po)DT8 zv?}_AFjFFle=G*2s{MDLs0aZ{^_)z;1=F z){s`MEOBG!&o{T?&}9DG25bW1YE>&!Ch8QsY4&VqcktSn7@zFfsn+kFp7LkUxa_ru zHE#|}S#~RiparseInt(computed.height)) +style.overflowY='scroll'}else{style.overflow='hidden'} +div.textContent=element.value.substring(0,position);if(isInput) +div.textContent=div.textContent.replace(/\s/g,'\u00a0');var span=document.createElement('span');span.textContent=element.value.substring(position)||'.';div.appendChild(span);var coordinates={top:span.offsetTop+parseInt(computed.borderTopWidth),left:span.offsetLeft+parseInt(computed.borderLeftWidth),height:parseInt(computed.lineHeight)};if(debug){span.style.backgroundColor='#aaa'}else{document.body.removeChild(div)} +return coordinates} +if(typeof module!='undefined'&&typeof module.exports!='undefined'){module.exports=getCaretCoordinates}else if(isBrowser){window.getCaretCoordinates=getCaretCoordinates}}());(function(){var keyboardeventKeyPolyfill={polyfill:polyfill,keys:{3:'Cancel',6:'Help',8:'Backspace',9:'Tab',12:'Clear',13:'Enter',16:'Shift',17:'Control',18:'Alt',19:'Pause',20:'CapsLock',27:'Escape',28:'Convert',29:'NonConvert',30:'Accept',31:'ModeChange',32:' ',33:'PageUp',34:'PageDown',35:'End',36:'Home',37:'ArrowLeft',38:'ArrowUp',39:'ArrowRight',40:'ArrowDown',41:'Select',42:'Print',43:'Execute',44:'PrintScreen',45:'Insert',46:'Delete',48:['0',')'],49:['1','!'],50:['2','@'],51:['3','#'],52:['4','$'],53:['5','%'],54:['6','^'],55:['7','&'],56:['8','*'],57:['9','('],91:'OS',93:'ContextMenu',144:'NumLock',145:'ScrollLock',181:'VolumeMute',182:'VolumeDown',183:'VolumeUp',186:[';',':'],187:['=','+'],188:[',','<'],189:['-','_'],190:['.','>'],191:['/','?'],192:['`','~'],219:['[','{'],220:['\\','|'],221:[']','}'],222:["'",'"'],224:'Meta',225:'AltGraph',246:'Attn',247:'CrSel',248:'ExSel',249:'EraseEof',250:'Play',251:'ZoomOut'}};var i;for(i=1;i<25;i++){keyboardeventKeyPolyfill.keys[111+i]='F'+i} +var letter='';for(i=65;i<91;i++){letter=String.fromCharCode(i);keyboardeventKeyPolyfill.keys[i]=[letter.toLowerCase(),letter.toUpperCase()]} +function polyfill(){if(!('KeyboardEvent' in window)||'key' in KeyboardEvent.prototype){return!1} +var proto={get:function(x){var key=keyboardeventKeyPolyfill.keys[this.which||this.keyCode];if(Array.isArray(key)){key=key[+this.shiftKey]} +return key}};Object.defineProperty(KeyboardEvent.prototype,'key',proto);return proto} +keyboardeventKeyPolyfill.polyfill()})();if(!Element.prototype.matches){Element.prototype.matches=Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector||function(s){var matches=(this.document||this.ownerDocument).querySelectorAll(s),i=matches.length;while(--i>=0&&matches.item(i)!==this){} +return i>-1}} +var KB={components:{},utils:{},html:{},http:{},listeners:{clicks:{},changes:{},keys:[],internals:{}}};KB.on=function(eventType,callback){if(!this.listeners.internals.hasOwnProperty(eventType)){this.listeners.internals[eventType]=[]} +this.listeners.internals[eventType].push(callback)};KB.trigger=function(eventType,eventData){if(this.listeners.internals.hasOwnProperty(eventType)){for(var i=0;i0){var reset=!0;for(var i=0;i-1){window.location=redirect.split('#')[0]}else if(redirect){window.location=redirect}else if(location){window.location=location}else if(request.getResponseHeader('Content-Type')==='application/json'){try{return JSON.parse(request.responseText)}catch(e){}} +return request.responseText} +this.execute=function(){var request=new XMLHttpRequest();request.open(method,url,!0);request.setRequestHeader('X-Requested-With','XMLHttpRequest');for(var header in headers){if(headers.hasOwnProperty(header)){request.setRequestHeader(header,headers[header])}} +request.onerror=function(){errorCallback()};request.onreadystatechange=function(){if(request.readyState===XMLHttpRequest.DONE){var response=parseResponse(request);switch(request.status){case 200:successCallback(response);return;case 401:authErrorCallback(response);errorCallback(response);break;case 0:netErrorCallback(response);errorCallback(response);break;default:errorCallback(response);break}}};request.send(body);return this};this.success=function(callback){successCallback=callback;return this};this.authError=function(callback){authErrorCallback=callback;return this};this.netError=function(callback){netErrorCallback=callback;return this};this.error=function(callback){errorCallback=callback;return this}};KB.http.get=function(url){return(new KB.http.request('GET',url)).execute()};KB.http.postJson=function(url,body){var headers={'Content-Type':'application/json','Accept':'application/json'};return(new KB.http.request('POST',url,headers,JSON.stringify(body))).execute()};KB.http.postForm=function(url,formElement){var formData=new FormData(formElement);return(new KB.http.request('POST',url,{},formData)).execute()};KB.http.uploadFile=function(url,file,csrf,onProgress,onComplete,onError,onServerError,onRequestTooLarge){var fd=new FormData();fd.append('files[]',file);fd.append('csrf_token',csrf);var xhr=new XMLHttpRequest();xhr.upload.addEventListener('progress',onProgress);xhr.upload.addEventListener('error',onError);xhr.open('POST',url,!0);xhr.setRequestHeader('X-Requested-With','XMLHttpRequest');xhr.onreadystatechange=function(){if(xhr.readyState===XMLHttpRequest.DONE){if(xhr.status===200){onComplete()}else if(xhr.status===413){if(typeof onRequestTooLarge!=='undefined'){onRequestTooLarge()}else{onError(xhr.responseText)}}else if(typeof onServerError!=='undefined'){onServerError(JSON.parse(xhr.responseText))}}};xhr.send(fd)};(function(){var isOpen=!1;var isFormDirty=!1;function onOverlayClick(e){if(e.target.matches('#modal-overlay')&&isFormDirty===!1){e.stopPropagation();e.preventDefault();destroy()}} +function onCloseButtonClick(){KB.trigger('modal.close')} +function onFormChange(){isFormDirty=!0} +function onFormSubmit(){KB.trigger('modal.loading');submitForm()} +function getForm(){return document.querySelector('#modal-content form:not(.js-modal-ignore-form)')} +function submitForm(){var form=getForm();if(form){var url=form.getAttribute('action');if(url){KB.http.postForm(url,form).success(function(response){KB.trigger('modal.stop');if(response){replace(response)}else{destroy()}}).error(function(response){KB.trigger('modal.stop');if(response.hasOwnProperty('message')){window.alert(response.message)}})}}} +function afterRendering(){var formElement=KB.find('#modal-content form');if(formElement){formElement.on('change',onFormChange,!1);formElement.on('submit',onFormSubmit,!1)} +var autoFocusElement=document.querySelector('#modal-content input[autofocus]');if(autoFocusElement){autoFocusElement.focus()} +KB.render();_KB.datePicker();_KB.autoComplete();_KB.tagAutoComplete();_KB.get('Task').onPopoverOpened();KB.trigger('modal.afterRender')} +function replace(html){var contentElement=KB.find('#modal-content');if(contentElement){contentElement.replace(KB.dom('div').attr('id','modal-content').html(html).build());afterRendering()}} +function create(html,width,overlayClickDestroy){var closeButtonElement=KB.dom('a').attr('href','#').attr('id','modal-close-button').html('').click(onCloseButtonClick).build();var headerElement=KB.dom('div').attr('id','modal-header').add(closeButtonElement).build();var contentElement=KB.dom('div').attr('id','modal-content').html(html).build();var boxElement=KB.dom('div').attr('id','modal-box').style('width',width).add(headerElement).add(contentElement).build();var overlayElement=KB.dom('div').attr('id','modal-overlay').add(boxElement).build();if(overlayClickDestroy){overlayElement.addEventListener('click',onOverlayClick,!1)} +document.body.appendChild(overlayElement);afterRendering()} +function destroy(){isOpen=!1;isFormDirty=!1;var overlayElement=KB.find('#modal-overlay');if(overlayElement){KB.trigger('modal.beforeDestroy');overlayElement.remove()}} +function getWidth(size){var viewport=KB.utils.getViewportSize();if(viewport.width<700){return'99%'} +switch(size){case 'large':return viewport.width<1350?'98%':'1350px';case 'medium':return viewport.width<1024?'70%':'1024px'} +return viewport.width<800?'75%':'800px'} +KB.on('modal.close',function(){destroy()});KB.on('modal.submit',function(){submitForm()});KB.modal={open:function(url,size,overlayClickDestroy){KB.trigger('modal.open');_KB.get('Dropdown').close();destroy();if(typeof overlayClickDestroy==='undefined'){overlayClickDestroy=!0} +KB.http.get(url).success(function(response){isOpen=!0;create(response,getWidth(size),overlayClickDestroy)})},close:function(){destroy()},isOpen:function(){return isOpen},replace:function(url){KB.http.get(url).success(function(response){replace(response)})},replaceHtml:replace,getForm:getForm,submitForm:submitForm}}());KB.on('dom.ready',function(){function onMouseOver(mytarget){if(!exists()){create(mytarget)}} +function onMouseLeaveContainer(){setTimeout(destroy,500)} +function mouseLeftParent(){setTimeout(destroyIfNotOnTooltip,500)} +function mouseOnTooltip(){document.getElementById("tooltip-container").mouseOnTooltip=!0} +function destroyIfNotOnTooltip(){var div=document.getElementById("tooltip-container");if(div!=null&&!div.mouseOnTooltip)destroy()} +function create(element){var contentElement=element.querySelector("script");if(contentElement){render(element,contentElement.innerHTML);return} +var link=element.dataset.href;if(link){fetch(link,function(html){if(html){render(element,html)}})}} +function fetch(url,callback){var request=new XMLHttpRequest();request.open("GET",url,!0);request.setRequestHeader("X-Requested-With","XMLHttpRequest");request.onreadystatechange=function(){if(request.readyState===XMLHttpRequest.DONE){if(request.status===200){callback(request.responseText)}}};request.send(null)} +function render(element,html){var containerElement=document.createElement("div");containerElement.id="tooltip-container";containerElement.innerHTML=html;containerElement.addEventListener("mouseleave",onMouseLeaveContainer,!1);containerElement.addEventListener("mouseenter",mouseOnTooltip,!1);containerElement.mouseOnTooltip=!1;var elementRect=element.getBoundingClientRect();var top=elementRect.top+window.scrollY+elementRect.height;containerElement.style.top=top-20+"px";if(elementRect.left>(window.innerWidth-600)){var right=window.innerWidth-elementRect.right-window.scrollX;containerElement.style.right=right-25+"px"}else{var left=elementRect.left+window.scrollX;containerElement.style.left=left-25+"px"} +document.body.appendChild(containerElement);document.body.onclick=function(event){if(!containerElement.contains(event.target)){destroy()}}} +function destroy(){var element=document.getElementById("tooltip-container");if(element){element.parentNode.removeChild(element)}} +function exists(){return!!document.getElementById("tooltip-container")} +document.body.addEventListener('mouseover',function(e){if(e.target.classList.contains('tooltip')){onMouseOver(e.target)} +if(e.target.classList.contains('fa')&&e.target.parentNode.classList.contains('tooltip')){onMouseOver(e.target.parentNode)}});document.body.addEventListener('mouseout',function(e){if(e.target.classList.contains('tooltip')){mouseLeftParent()} +if(e.target.classList.contains('fa')&&e.target.parentNode.classList.contains('tooltip')){mouseLeftParent()}})});KB.utils.formatDuration=function(d){if(d>=86400){return Math.round(d/86400)+"d"}else if(d>=3600){return Math.round(d/3600)+"h"}else if(d>=60){return Math.round(d/60)+"m"} +return d+"s"};KB.utils.getSelectionPosition=function(element){var selectionStart,selectionEnd;if(element.value.length0){columns.push([currentValue])}}else{if(j>0){columns[j].push(currentValue);if(typeof columns[0][i]==='undefined'){columns[0].push(0)} +columns[0][i]+=currentValue}else{categories.push(outputFormat(inputFormat.parse(currentValue)))}}}} +KB.dom(containerElement).add(KB.dom('div').attr('id','chart').build());c3.generate({data:{columns:columns},axis:{x:{type:'category',categories:categories}}})}});KB.component('chart-project-cumulative-flow',function(containerElement,options){this.render=function(){var metrics=options.metrics;var columns=[];var groups=[];var categories=[];var inputFormat=d3.time.format("%Y-%m-%d");var outputFormat=d3.time.format(options.dateFormat);for(var i=0;i0){groups.push(currentValue);columns.push([currentValue])}}else{if(j>0){columns[j-1].push(currentValue)}else{categories.push(outputFormat(inputFormat.parse(currentValue)))}}}} +KB.dom(containerElement).add(KB.dom('div').attr('id','chart').build());c3.generate({data:{columns:columns.reverse(),type:'area-spline',groups:[groups],order:null},axis:{x:{type:'category',categories:categories}}})}});KB.component('chart-project-estimated-actual-column',function(containerElement,options){this.render=function(){var spent=[options.labelSpent];var estimated=[options.labelEstimated];var columns=[];for(var column in options.metrics){spent.push(options.metrics[column].hours_spent);estimated.push(options.metrics[column].hours_estimated);columns.push(options.metrics[column].title)} +KB.dom(containerElement).add(KB.dom('div').attr('id','chart').build());c3.generate({data:{columns:[spent,estimated],type:'bar'},bar:{width:{ratio:0.2}},axis:{x:{type:'category',categories:columns}},legend:{show:!0}})}});KB.component('chart-project-lead-cycle-time',function(containerElement,options){this.render=function(){var metrics=options.metrics;var cycle=[options.labelCycle];var lead=[options.labelLead];var categories=[];var types={};types[options.labelCycle]='area';types[options.labelLead]='area-spline';var colors={};colors[options.labelLead]='#afb42b';colors[options.labelCycle]='#4e342e';for(var i=0;i i.fa-pencil-square-o)');if($editButton.length===0){console.error('Could not find the edit button');return!1} +$editButton[0].click();var $previewButton=$editorContainer.find('.text-editor-toolbar a:has(> i.fa-eye)');if($previewButton.length===0){console.error('Could not find the preview button');return!1} +$previewButton[0].click();return!1});KB.component('confirm-buttons',function(containerElement,options){var isLoading=!1;function onSubmit(){isLoading=!0;KB.find('#modal-confirm-button').replace(buildButton());KB.http.get(options.url)} +function onCancel(){KB.trigger('modal.close')} +function onStop(){isLoading=!1;KB.find('#modal-confirm-button').replace(buildButton())} +function buildButton(){var button=KB.dom('button').click(onSubmit).attr('id','modal-confirm-button').attr('type','button').attr('class','btn btn-red');if(isLoading){button.disable().add(KB.dom('i').attr('class','fa fa-spinner fa-pulse').build()).text(' ')} +if(options.tabindex){button.attr('tabindex',options.tabindex)} +return button.text(options.submitLabel).build()} +this.render=function(){KB.on('modal.stop',onStop);KB.on('modal.close',function(){KB.removeListener('modal.stop',onStop)});var element=KB.dom('div').attr('class','form-actions').add(buildButton()).text(' '+options.orLabel+' ').add(KB.dom('a').attr('href','#').click(onCancel).text(options.cancelLabel).build()).build();containerElement.appendChild(element)}});KB.component('external-task-view',function(containerElement,options){this.render=function(){KB.dom(containerElement).html('

');$.ajax({cache:!1,url:options.url,success:function(data){KB.dom(containerElement).html('
'+data+'
')}})}});var files=[];KB.component('file-upload-task-create',function(containerElement,options){var inputFileElement=null;var dropzoneElement=null;var totalSize=0;var currentScreenshotSize=0;var inputElement=null;function onFileLoaded(e){createImage(e.target.result);sizeOfImage(e.target.result)} +function sizeOfImage(e){totalSize-=currentScreenshotSize;currentScreenshotSize=e.length;totalSize+=currentScreenshotSize;checkMaxSize()} +function onPaste(e){if(e.clipboardData&&e.clipboardData.items){var items=e.clipboardData.items;if(items){for(var i=0;ioptions.maxSize){isOversize=!0} +if(isOversize){KB.trigger('modal.disable');if(!message){messageElement.insertBefore(KB.dom('div').attr('id','message-container').attr('class','alert alert-error').text(options.labelOversize).build(),messageElement.firstChild)}}else{KB.trigger('modal.enable');if(message){message.remove()}}} +function onFileChange(){for(var i=0;i0){KB.dom(dropzoneElement).empty().add(buildFileListElement())}else{KB.dom(dropzoneElement).empty().add(buildInnerDropzoneElement())} +checkMaxSize()} +function buildFileInputElement(){return KB.dom('input').attr('id','file-input-element').attr('type','file').attr('name','files[]').attr('multiple',!0).on('change',onFileChange).hide().build()} +function buildInnerDropzoneElement(){var dropzoneLinkElement=KB.dom('a').attr('href','#').text(options.labelChooseFiles).click(onClickFileBrowser).build();return KB.dom('div').attr('id','file-dropzone-inner').text(options.labelDropzone+' '+options.labelOr+' ').add(dropzoneLinkElement).build()} +function buildDropzoneElement(){var dropzoneElement=KB.dom('div').attr('id','file-dropzone').add(buildInnerDropzoneElement()).build();dropzoneElement.ondragover=onDragOver;dropzoneElement.ondrop=onDrop;return dropzoneElement} +function buildFileListItem(index){var deleteElement=KB.dom('span').attr('id','file-delete-'+index).html('
').on('click',function(){totalSize-=files[index].size;files.splice(index,1);KB.find('#file-item-'+index).remove();showFiles()}).build();var itemElement=KB.dom('li').attr('id','file-item-'+index).add(deleteElement).text(' '+files[index].name+' ');return itemElement.build()} +function buildFileListElement(){var fileListElement=KB.dom('ul').attr('id','file-list').build();for(var i=0;i0){KB.http.uploadFile(options.url,files[currentFileIndex],options.csrf,onProgress,onComplete,onError,onServerError,onRequestTooLarge)}} +function showFiles(){if(files.length>0){KB.trigger('modal.enable');KB.dom(dropzoneElement).empty().add(buildFileListElement())}else{KB.trigger('modal.disable');KB.dom(dropzoneElement).empty().add(buildInnerDropzoneElement())}} +function buildFileInputElement(){return KB.dom('input').attr('id','file-input-element').attr('type','file').attr('name','files[]').attr('multiple',!0).on('change',onFileChange).hide().build()} +function buildInnerDropzoneElement(){var dropzoneLinkElement=KB.dom('a').attr('href','#').text(options.labelChooseFiles).click(onClickFileBrowser).build();return KB.dom('div').attr('id','file-dropzone-inner').text(options.labelDropzone+' '+options.labelOr+' ').add(dropzoneLinkElement).build()} +function buildDropzoneElement(){var dropzoneElement=KB.dom('div').attr('id','file-dropzone').add(buildInnerDropzoneElement()).build();dropzoneElement.ondragover=onDragOver;dropzoneElement.ondrop=onDrop;dropzoneElement.ondragover=onDragOver;return dropzoneElement} +function buildFileListItem(index){var isOversize=!1;var progressElement=KB.dom('progress').attr('id','file-progress-'+index).attr('value',0).build();var percentageElement=KB.dom('span').attr('id','file-percentage-'+index).text('(0%)').build();var deleteElement=KB.dom('span').attr('id','file-delete-'+index).html('').on('click',function(){files.splice(index,1);KB.find('#file-item-'+index).remove();showFiles()}).build();var itemElement=KB.dom('li').attr('id','file-item-'+index).add(deleteElement).add(progressElement).text(' '+files[index].name+' ').add(percentageElement);if(options.maxSize>0&&files[index].size>options.maxSize){itemElement.add(KB.dom('div').addClass('file-error').text(options.labelOversize).build());isOversize=!0} +if(isOversize){KB.trigger('modal.disable')} +return itemElement.build()} +function buildFileListElement(){var fileListElement=KB.dom('ul').attr('id','file-list').build();for(var i=0;i=options.images.length){index=0} +currentImage=options.images[index];break}} +renderSlide()} +function renderPreviousSlide(){destroySlide();for(var i=0;i1){if(document.activeElement.tagName==='INPUT'||document.activeElement.tagName==='TEXTAREA'){$(document.activeElement).parents("form").submit()}}}} +KB.onKey('?',function(){if(!KB.modal.isOpen()){KB.modal.open(KB.find('body').data('keyboardShortcutUrl'),'',!0)}});KB.onKey('Escape',function(){if(!KB.exists('#suggest-menu')){KB.trigger('modal.close');_KB.get("Dropdown").close()}},!0);KB.onKey('Enter',submitForm,!0,!0);KB.onKey('Enter',submitForm,!0,!1,!0);KB.onKey('b',function(){if(!KB.modal.isOpen()){KB.trigger('board.selector.open')}});if(KB.exists('#board')){KB.onKey('c',function(){if(!KB.modal.isOpen()){_KB.get('BoardHorizontalScrolling').toggle()}});KB.onKey('s',function(){if(!KB.modal.isOpen()){_KB.get('BoardCollapsedMode').toggle()}});KB.onKey('n',function(){if(!KB.modal.isOpen()){KB.modal.open(KB.find('#board').data('taskCreationUrl'),'large',!1)}})} +if(KB.exists('#task-view')){KB.onKey('e',function(){if(!KB.modal.isOpen()){KB.modal.open(KB.find('#task-view').data('editUrl'),'large',!1)}});KB.onKey('c',function(){if(!KB.modal.isOpen()){KB.modal.open(KB.find('#task-view').data('commentUrl'),'medium',!1)}});KB.onKey('s',function(){if(!KB.modal.isOpen()){KB.modal.open(KB.find('#task-view').data('subtaskUrl'),'medium',!1)}});KB.onKey('l',function(){if(!KB.modal.isOpen()){KB.modal.open(KB.find('#task-view').data('internalLinkUrl'),'medium',!1)}})} +KB.onKey('f',function(){if(!KB.modal.isOpen()){KB.focus('#form-search')}});KB.onKey('r',function(){if(!KB.modal.isOpen()){var reset=$(".filter-reset").data("filter");var input=$("#form-search");input.val(reset);$("form.search").submit()}});KB.onKey('v+o',function(){goToLink('a.view-overview')});KB.onKey('v+b',function(){goToLink('a.view-board')});KB.onKey('v+l',function(){goToLink('a.view-listing')})};document.addEventListener("DOMContentLoaded",function(){function selectAllItems(event){event.preventDefault();var items=document.querySelectorAll("input[data-list-item=selectable]");for(var i=0;i0){showActionMenu()}else if(selectedItems.length==0){hideActionMenu()}} +function showActionMenu(){var element=document.querySelector(".list-item-actions");if(element){element.classList.remove("list-item-action-hidden")}} +function hideActionMenu(){var element=document.querySelector(".list-item-actions");if(element&&!element.classList.contains("list-item-action-hidden")){element.classList.add("list-item-action-hidden")}} +function onActionClick(event){event.preventDefault();var selectedItems=document.querySelectorAll("input[data-list-item=selectable]:checked");var taskIDs=[];for(var i=0;i0){result.index=result.index-1} +KB.dom(result.items[result.index]).addClass('active')} +function moveDown(){var result=resetSelection();if(result.indexvalue2?1:0)})}else{elements.sort(function(a,b){var value1=a['data-label'].toLowerCase();var value2=b['data-label'].toLowerCase();return value1value2?1:0)})} +return elements} +function filterItems(text,items){var filteredItems=[];var hasActiveItem=!1;for(var i=0;i-1){var item=items[i];if(typeof options.defaultValue!=='undefined'&&String(options.defaultValue)===item['data-value']){item.class+=' active';hasActiveItem=!0} +filteredItems.push(item)}} +if(!hasActiveItem&&filteredItems.length>0){filteredItems[0].class+=' active'} +return filteredItems} +function buildDropdownMenu(){var itemElements=filterItems(inputElement.value,buildItems(options.items));var componentPosition=componentElement.getBoundingClientRect();var windowPosition=document.body.scrollTop||document.documentElement.scrollTop;if(itemElements.length===0){return null} +return KB.dom('ul').attr('id','select-dropdown-menu').style('top',(windowPosition+componentPosition.bottom)+'px').style('left',componentPosition.left+'px').style('width',componentPosition.width+'px').style('maxHeight',(window.innerHeight-componentPosition.bottom-20)+'px').mouseover(onItemMouseOver).click(onItemClick).for('li',itemElements).build()} +function destroyDropdownMenu(){var menuElement=KB.find('#select-dropdown-menu');if(menuElement!==null){menuElement.remove()} +document.removeEventListener('keydown',onKeyDown,!1);document.removeEventListener('click',onDocumentClick,!1)} +function renderDropdownMenu(){var element=buildDropdownMenu();if(element!==null){document.body.appendChild(element)} +document.addEventListener('keydown',onKeyDown,!1);document.addEventListener('click',onDocumentClick,!1)} +function getPlaceholderValue(){if(options.defaultValue&&options.defaultValue in options.items){return options.items[options.defaultValue]} +if(options.placeholder){return options.placeholder} +return''} +function getAriaLabelValue(){if(options.ariaLabel){return options.ariaLabel} +return''} +this.render=function(){KB.on('select.dropdown.loading.start',onLoadingStart);KB.on('select.dropdown.loading.stop',onLoadingStop);KB.on('modal.close',function(){KB.removeListener('select.dropdown.loading.start',onLoadingStart);KB.removeListener('select.dropdown.loading.stop',onLoadingStop)});chevronIconElement=KB.dom('i').attr('class','fa fa-chevron-down select-dropdown-chevron').click(toggleDropdownMenu).build();loadingIconElement=KB.dom('span').hide().addClass('select-loading-icon').add(KB.dom('i').attr('class','fa fa-spinner fa-pulse').build()).build();inputHiddenElement=KB.dom('input').attr('type','hidden').attr('name',options.name).attr('value',options.defaultValue||'').build();inputElement=KB.dom('input').attr('type','text').attr('placeholder',getPlaceholderValue()).attr('aria-label',getAriaLabelValue()).addClass('select-dropdown-input').on('focus',toggleDropdownMenu).on('input',onInputChanged,!0).build();componentElement=KB.dom('div').addClass('select-dropdown-input-container').add(inputHiddenElement).add(inputElement).add(chevronIconElement).add(loadingIconElement).build();containerElement.appendChild(componentElement);if(options.onFocus){options.onFocus.forEach(function(eventName){KB.on(eventName,function(){inputElement.focus()})})} +window.addEventListener('scroll',onScroll,!1)}});KB.interval(60,function(){var statusUrl=KB.find('body').data('statusUrl');var loginUrl=KB.find('body').data('loginUrl');if(KB.find('.form-login')===null){KB.http.get(statusUrl).authError(function(){window.location=loginUrl})}});KB.component('submit-buttons',function(containerElement,options){var isLoading=!1;var isDisabled=options.disabled||!1;var submitLabel=options.submitLabel;var formActionElement=null;var submitButtonElement=null;function onSubmit(){isLoading=!0;replaceButton();KB.trigger('modal.submit')} +function onCancel(){KB.trigger('modal.close')} +function onStop(){isLoading=!1;replaceButton()} +function onDisable(){isLoading=!1;isDisabled=!0;replaceButton()} +function onEnable(){isLoading=!1;isDisabled=!1;replaceButton()} +function onHide(){KB.dom(formActionElement).hide()} +function onUpdateSubmitLabel(eventData){submitLabel=eventData.submitLabel;replaceButton()} +function buildButton(){var button=KB.dom('button').attr('type','submit').attr('class','btn btn-'+(options.color||'blue'));if(KB.modal.isOpen()){button.click(onSubmit)} +if(options.tabindex){button.attr('tabindex',options.tabindex)} +if(isLoading){button.disable().add(KB.dom('i').attr('class','fa fa-spinner fa-pulse').build()).text(' ')} +if(isDisabled){button.disable()} +return button.text(submitLabel).build()} +function replaceButton(){var buttonElement=buildButton();KB.dom(submitButtonElement).replace(buttonElement);submitButtonElement=buttonElement} +this.render=function(){KB.on('modal.stop',onStop);KB.on('modal.disable',onDisable);KB.on('modal.enable',onEnable);KB.on('modal.hide',onHide);KB.on('modal.submit.label',onUpdateSubmitLabel);KB.on('modal.close',function(){KB.removeListener('modal.stop',onStop);KB.removeListener('modal.disable',onDisable);KB.removeListener('modal.enable',onEnable);KB.removeListener('modal.hide',onHide);KB.removeListener('modal.submit.label',onUpdateSubmitLabel)});submitButtonElement=buildButton();var formActionElementBuilder=KB.dom('div').attr('class','form-actions').add(submitButtonElement);if(KB.modal.isOpen()){formActionElementBuilder.text(' '+options.orLabel+' ').add(KB.dom('a').attr('href','#').click(onCancel).text(options.cancelLabel).build())} +formActionElement=formActionElementBuilder.build();containerElement.appendChild(formActionElement)}});(function(){function savePosition(subtaskId,position){var url=$(".subtasks-table").data("save-position-url");$.ajax({cache:!1,url:url,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({"subtask_id":subtaskId,"position":position})})} +function bootstrap(){$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")});$(".subtasks-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(e,ui){ui.children().each(function(){$(this).width($(this).width())});return ui},stop:function(event,ui){var subtask=ui.item;subtask.removeClass("draggable-item-selected");savePosition(subtask.data("subtask-id"),subtask.index()+1)},start:function(event,ui){ui.item.addClass("draggable-item-selected")}}).disableSelection()} +KB.on('dom.ready',bootstrap);KB.on('subtasks.reloaded',bootstrap)}());KB.on('dom.ready',function(){$(document).on('click','.js-subtask-toggle-status',function(e){var el=$(this);var url=el.attr('href');e.preventDefault();$.ajax({cache:!1,url:url,success:function(data){if(url.indexOf('fragment=table')!=-1){$('.subtasks-table').replaceWith(data)}else if(url.indexOf('fragment=rows')!=-1){$(el).closest('.task-list-subtasks').replaceWith(data)}else{$(el).closest('.subtask-title').replaceWith(data)} +KB.trigger('subtasks.reloaded')}})});$(document).on('click','.js-subtask-toggle-timer',function(e){var el=$(this);e.preventDefault();$.ajax({cache:!1,url:el.attr('href'),success:function(data){$(el).closest('.subtask-time-tracking').replaceWith(data)}})})});KB.component('suggest-menu',function(containerElement,options){function onKeyDown(e){switch(KB.utils.getKey(e)){case 'Escape':destroy();break;case 'ArrowUp':e.preventDefault();e.stopImmediatePropagation();moveUp();break;case 'ArrowDown':e.preventDefault();e.stopImmediatePropagation();moveDown();break;case 'Enter':e.preventDefault();e.stopImmediatePropagation();insertSelectedItem();break}} +function onClick(){insertSelectedItem()} +function onMouseOver(element){if(KB.dom(element).hasClass('suggest-menu-item')){KB.find('.suggest-menu-item.active').removeClass('active');KB.dom(element).addClass('active')}} +function insertSelectedItem(){containerElement.focus();var element=KB.find('.suggest-menu-item.active');var value=element.data('value');var trigger=element.data('trigger');var content=containerElement.value;var text=getLastWord(containerElement);var substitute=trigger+value+' ';var selectionPosition=KB.utils.getSelectionPosition(containerElement);var before=content.substring(0,selectionPosition.selectionStart-text.length);var after=content.substring(selectionPosition.selectionEnd);var position=before.length+substitute.length;containerElement.value=before+substitute+after;containerElement.setSelectionRange(position,position);destroy()} +function getLastWord(element){var lines=element.value.substring(0,element.selectionEnd).split("\n");var lastLine=lines[lines.length-1];var words=lastLine.split(' ');return words[words.length-1]} +function getParentElement(){var selectors=['#modal-content form','#modal-content','body'];for(var i=0;i0){result.index=result.index-1} +KB.dom(result.items[result.index]).addClass('active')} +function moveDown(){var result=resetSelection();if(result.index0){renderMenu(buildItems(trigger,items))}} +function filterItems(text,items){var filteredItems=[];if(text.length===0){return items} +for(var i=0;i0){container.add(KB.html.label(options.positionLabel,'form-position')).add(KB.dom('select').attr('id','form-position').for('option',tasks).build()).add(KB.html.radio(options.beforeLabel,'positionChoice','before')).add(KB.html.radio(options.afterLabel,'positionChoice','after'))} +return container.build()} +this.render=function(){KB.on('modal.submit',onSubmit);KB.on('modal.close',function(){KB.removeListener('modal.submit',onSubmit)});var form=KB.dom('div').add(KB.dom('div').attr('id','message-container').build()).add(KB.html.label(options.swimlaneLabel,'form-swimlanes')).add(buildSwimlaneSelect()).add(KB.html.label(options.columnLabel,'form-columns')).add(buildColumnSelect()).add(buildTasks()).build();containerElement.appendChild(form)}});KB.onClick('.js-template',function(e){var template=KB.dom(e.target).data('template');var target=KB.dom(e.target).data('templateTarget');var targetField=KB.find(target);if(targetField){targetField.build().value=template} +_KB.controllers.Dropdown.close()});KB.component('text-editor',function(containerElement,options){var textarea,viewModeElement,writeModeElement,previewElement,selectionStart,selectionEnd;this.render=function(){writeModeElement=buildWriteMode();viewModeElement=buildViewMode();containerElement.appendChild(KB.dom('div').attr('class','text-editor').add(viewModeElement).add(writeModeElement).build());if(options.autofocus){textarea.focus()}};function buildViewMode(){var toolbarElement=KB.dom('div').attr('class','text-editor-toolbar').for('a',[{href:'#',html:' '+options.labelWrite,click:function(){toggleViewMode()}}]).build();previewElement=KB.dom('div').attr('class','text-editor-preview-area markdown').build();return KB.dom('div').attr('class','text-editor-view-mode').add(toolbarElement).add(previewElement).hide().build()} +function buildWriteMode(){var toolbarElement=KB.dom('div').attr('class','text-editor-toolbar').for('a',[{href:'#',html:' '+options.labelPreview,click:function(){toggleViewMode()}},{href:'#',html:'',click:function(){insertEnclosedTag('**')}},{href:'#',html:'',click:function(){insertEnclosedTag('_')}},{href:'#',html:'',click:function(){insertEnclosedTag('~~')}},{href:'#',html:'',click:function(){insertLinkTag()}},{href:'#',html:'',click:function(){insertPrependTag('> ')}},{href:'#',html:'',click:function(){insertPrependTag('* ')}},{href:'#',html:'',click:function(){insertBlockTag('```')}}]).build();var textareaElement=KB.dom('textarea');textareaElement.attr('name',options.name);if(options.tabindex){textareaElement.attr('tabindex',options.tabindex)} +if(options.required){textareaElement.attr('required','required')} +if(options.ariaLabel){textareaElement.attr('aria-label',options.ariaLabel)} +var textWrapper=KB.dom(containerElement).find('script');textareaElement.html(textWrapper.innerHTML);if(options.placeholder){textareaElement.attr('placeholder',options.placeholder)} +textarea=textareaElement.build();if(options.suggestOptions){KB.getComponent('suggest-menu',textarea,options.suggestOptions).render()} +return KB.dom('div').attr('class','text-editor-write-mode').add(toolbarElement).add(textarea).build()} +function toggleViewMode(){$.ajax({cache:!1,type:'POST',url:options.previewUrl,data:{'text':textarea.value},success:function(data){KB.dom(previewElement).html(data)}});KB.dom(viewModeElement).toggle();KB.dom(writeModeElement).toggle()} +function getSelectedText(){return textarea.value.substring(textarea.selectionStart,textarea.selectionEnd)} +function replaceTextRange(s,start,end,substitute){return s.substring(0,start)+substitute+s.substring(end)} +function insertEnclosedTag(tag){var selectedText=getSelectedText();insertText(tag+selectedText+tag);setCursorBeforeClosingTag(tag)} +function insertBlockTag(tag){var selectedText=getSelectedText();insertText('\n'+tag+'\n'+selectedText+'\n'+tag);setCursorBeforeClosingTag(tag,2)} +function insertPrependTag(tag){var selectedText=getSelectedText();if(selectedText.indexOf('\n')===-1){insertText('\n'+tag+selectedText)}else{var lines=selectedText.split('\n');for(var i=0;i0){linkLabel=selectedText;selectionStartOffset=-1*(linkUrl.length+1);selectionEndOffset=selectionStartOffset+linkUrl.length} +insertText('['+linkLabel+']('+linkUrl+')');var selectionPosition=KB.utils.getSelectionPosition(textarea);var currentSelectionStart=selectionPosition.selectionStart;textarea.setSelectionRange(currentSelectionStart+selectionStartOffset,currentSelectionStart+selectionEndOffset)} +function insertText(replacedText){textarea.focus();var result=!1;var selectionPosition=KB.utils.getSelectionPosition(textarea);selectionStart=selectionPosition.selectionStart;selectionEnd=selectionPosition.selectionEnd;if(document.queryCommandSupported('insertText')){result=document.execCommand('insertText',!1,replacedText)} +if(!result){try{document.execCommand('ms-beginUndoUnit')}catch(error){} +textarea.value=replaceTextRange(textarea.value,selectionStart,selectionEnd,replacedText);try{document.execCommand('ms-endUndoUnit')}catch(error){}}} +function setCursorBeforeClosingTag(tag,offset){offset=offset||0;var position=selectionEnd+tag.length+offset;textarea.setSelectionRange(position,position)}});document.addEventListener('DOMContentLoaded',function(){KB.render();KB.listen();KB.keyboardShortcuts();KB.trigger('dom.ready')});var Kanboard={};Kanboard.App=function(){this.controllers={}};Kanboard.App.prototype.get=function(controller){return this.controllers[controller]};Kanboard.App.prototype.execute=function(){for(var className in Kanboard){if(className!=="App"){var controller=new Kanboard[className](this);this.controllers[className]=controller;if(typeof controller.execute==="function"){controller.execute()} +if(typeof controller.listen==="function"){controller.listen()} +if(typeof controller.focus==="function"){controller.focus()}}} +this.focus();this.datePicker();this.autoComplete();this.tagAutoComplete()};Kanboard.App.prototype.focus=function(){$(document).on('focus','.auto-select',function(){$(this).select()});$(document).on('mouseup','.auto-select',function(e){e.preventDefault()})};Kanboard.App.prototype.datePicker=function(){var bodyElement=$("body");var dateFormat=bodyElement.data("js-date-format");var timeFormat=bodyElement.data("js-time-format");var lang=$("html").attr("lang");$.datepicker.setDefaults($.datepicker.regional[lang]);$.timepicker.setDefaults($.timepicker.regional[lang]);$(".form-date").datepicker({showOtherMonths:!0,selectOtherMonths:!0,dateFormat:dateFormat,constrainInput:!1});$(".form-datetime").datetimepicker({controlType:'select',dateFormat:dateFormat,timeFormat:timeFormat,constrainInput:!1,amNames:['am','AM'],pmNames:['pm','PM']})};Kanboard.App.prototype.tagAutoComplete=function(){$(".tag-autocomplete").select2({tags:!0})};Kanboard.App.prototype.autoComplete=function(){$(".autocomplete").each(function(){var input=$(this);var field=input.data("dst-field");var extraFields=input.data("dst-extra-fields");if($('#form-'+field).val()===''){input.parent().find("button[type=submit]").attr('disabled','disabled')} +input.autocomplete({source:input.data("search-url"),minLength:1,select:function(event,ui){$("input[name="+field+"]").val(ui.item.id);if(extraFields){var fields=extraFields.split(',');for(var i=0;i ')};Kanboard.App.prototype.hideLoadingIcon=function(){$("#app-loading-icon").remove()};Kanboard.BoardCollapsedMode=function(app){this.app=app};Kanboard.BoardCollapsedMode.prototype.toggle=function(){var self=this;this.app.showLoadingIcon();$.ajax({cache:!1,url:$('.filter-display-mode:not([style="display: none;"]) a').attr('href'),success:function(data){$('.filter-display-mode').toggle();self.app.get("BoardDragAndDrop").refresh(data)}})};Kanboard.BoardColumnView=function(app){this.app=app};Kanboard.BoardColumnView.prototype.execute=function(){if(this.app.hasId("board")){this.render()}};Kanboard.BoardColumnView.prototype.listen=function(){var self=this;$(document).on("click",".board-toggle-column-view",function(){self.toggle($(this).data("column-id"))})};Kanboard.BoardColumnView.prototype.onBoardRendered=function(){this.render()};Kanboard.BoardColumnView.prototype.render=function(){var self=this;$(".board-column-header").each(function(){var columnId=$(this).data('column-id');if(localStorage.getItem("hidden_column_"+columnId)){self.hideColumn(columnId)}})};Kanboard.BoardColumnView.prototype.toggle=function(columnId){if(localStorage.getItem("hidden_column_"+columnId)){this.showColumn(columnId)}else{this.hideColumn(columnId)} +this.app.get("BoardDragAndDrop").dragAndDrop()};Kanboard.BoardColumnView.prototype.hideColumn=function(columnId){$(".board-column-"+columnId+" .board-column-expanded").hide();$(".board-column-"+columnId+" .board-column-collapsed").show();$(".board-column-header-"+columnId+" .board-column-expanded").hide();$(".board-column-header-"+columnId+" .board-column-collapsed").show();$(".board-column-header-"+columnId).each(function(){$(this).removeClass("board-column-compact");$(this).addClass("board-column-header-collapsed")});$(".board-column-"+columnId).each(function(){$(this).addClass("board-column-task-collapsed")});$(".board-column-"+columnId+" .board-rotation").each(function(){$(this).css("width",$(".board-column-"+columnId+"").height())});localStorage.setItem("hidden_column_"+columnId,1)};Kanboard.BoardColumnView.prototype.showColumn=function(columnId){$(".board-column-"+columnId+" .board-column-expanded").show();$(".board-column-"+columnId+" .board-column-collapsed").hide();$(".board-column-header-"+columnId+" .board-column-expanded").show();$(".board-column-header-"+columnId+" .board-column-collapsed").hide();$(".board-column-header-"+columnId).removeClass("board-column-header-collapsed");$(".board-column-"+columnId).removeClass("board-column-task-collapsed");if(localStorage.getItem("horizontal_scroll")==0){$(".board-column-header-"+columnId).addClass("board-column-compact")} +localStorage.removeItem("hidden_column_"+columnId)};Kanboard.BoardHorizontalScrolling=function(app){this.app=app};Kanboard.BoardHorizontalScrolling.prototype.execute=function(){if(this.app.hasId("board")){this.render()}};Kanboard.BoardHorizontalScrolling.prototype.listen=function(){var self=this;$(document).on('click',".filter-toggle-scrolling",function(e){e.preventDefault();self.toggle()})};Kanboard.BoardHorizontalScrolling.prototype.onBoardRendered=function(){this.render()};Kanboard.BoardHorizontalScrolling.prototype.toggle=function(){var scrolling=localStorage.getItem("horizontal_scroll")||1;localStorage.setItem("horizontal_scroll",scrolling==0?1:0);this.render()};Kanboard.BoardHorizontalScrolling.prototype.render=function(){if(localStorage.getItem("horizontal_scroll")==0){$(".filter-wide").show();$(".filter-compact").hide();$("#board-container").addClass("board-container-compact");$("#board th:not(.board-column-header-collapsed)").addClass("board-column-compact")}else{$(".filter-wide").hide();$(".filter-compact").show();$("#board-container").removeClass("board-container-compact");$("#board th").removeClass("board-column-compact")}};Kanboard.BoardPolling=function(app){this.app=app};Kanboard.BoardPolling.prototype.execute=function(){if(this.app.hasId("board")){var interval=parseInt($("#board").attr("data-check-interval"));if(interval>0){window.setInterval(this.check.bind(this),interval*1000)}}};Kanboard.BoardPolling.prototype.check=function(){if(KB.utils.isVisible()&&!this.app.get("BoardDragAndDrop").savingInProgress){var self=this;this.app.showLoadingIcon();$.ajax({cache:!1,url:$("#board").data("check-url"),statusCode:{200:function(data){self.app.get("BoardDragAndDrop").refresh(data)},304:function(){self.app.hideLoadingIcon()}}})}};Kanboard.BoardVerticalScrolling=function(app){this.app=app};Kanboard.BoardVerticalScrolling.prototype.execute=function(){if(this.app.hasId("board")){this.render()}};Kanboard.BoardVerticalScrolling.prototype.listen=function(){var self=this;$(document).on('click',".filter-vert-toggle-collapse",function(e){e.preventDefault();self.toggle()})};Kanboard.BoardVerticalScrolling.prototype.onBoardRendered=function(){this.render()};Kanboard.BoardVerticalScrolling.prototype.toggle=function(){var self=this;var scrolling=localStorage.getItem("vertical_scroll")||1;localStorage.setItem("vertical_scroll",scrolling==0?1:0);var task_lists=$(".board-task-list");task_lists.each(function(){$(this).css("min-height","")});this.render();self.app.get("BoardDragAndDrop").dragAndDrop()};Kanboard.BoardVerticalScrolling.prototype.render=function(){if(localStorage.getItem("vertical_scroll")==0){$(".filter-vert-expand").show();$(".filter-vert-collapse").hide();$("#board td .board-task-list").addClass("board-task-list-compact")}else{$(".filter-vert-expand").hide();$(".filter-vert-collapse").show();$("#board td .board-task-list").removeClass("board-task-list-compact")}};Kanboard.Column=function(app){this.app=app};Kanboard.Column.prototype.listen=function(){this.dragAndDrop()};Kanboard.Column.prototype.dragAndDrop=function(){var self=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")});$(".columns-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(e,ui){ui.children().each(function(){$(this).width($(this).width())});return ui},stop:function(event,ui){var column=ui.item;column.removeClass("draggable-item-selected");self.savePosition(column.data("column-id"),column.index()+1)},start:function(event,ui){ui.item.addClass("draggable-item-selected")}}).disableSelection()};Kanboard.Column.prototype.savePosition=function(columnId,position){var url=$(".columns-table").data("save-position-url");var self=this;this.app.showLoadingIcon();$.ajax({cache:!1,url:url,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({"column_id":columnId,"position":position}),complete:function(){self.app.hideLoadingIcon()}})};Kanboard.Dropdown=function(app){this.app=app};Kanboard.Dropdown.prototype.listen=function(){var self=this;$(document).on('click',function(){self.close()});$(document).on('click','.active-dropdown-menu',function(){self.close()});$(document).on('click','.dropdown-menu',function(e){e.preventDefault();e.stopImmediatePropagation();self.close();$(this).removeClass('dropdown-menu');$(this).addClass('active-dropdown-menu');var submenu=$(this).next('ul');var offset=$(this).offset();$("body").append(jQuery("
",{"id":"dropdown"}));submenu.clone().appendTo("#dropdown");var clone=$("#dropdown ul");clone.addClass('dropdown-submenu-open');var submenuHeight=clone.outerHeight();var submenuWidth=clone.outerWidth();if(offset.top+submenuHeight-$(window).scrollTop()<$(window).height()||$(window).scrollTop()+offset.top$(window).width()){var newOffset=offset.left-submenuWidth+$(this).outerWidth();if(newOffset<0){newOffset=15} +clone.css('left',newOffset)}else{clone.css('left',offset.left)} +if(document.getElementById('dropdown')!==null){KB.trigger('dropdown.afterRender')}});$(document).on('click','.dropdown-submenu-open li',function(e){if($(e.target).is('li')){KB.trigger('dropdown.clicked');var element=$(this).find('a:visible');if(element.length>0){element[0].click()}}})};Kanboard.Dropdown.prototype.close=function(){if(document.getElementById('dropdown')!==null){KB.trigger('dropdown.beforeDestroy')} +$('.active-dropdown-menu').addClass('dropdown-menu');$('.active-dropdown-menu').removeClass('active-dropdown-menu');$("#dropdown").remove()};Kanboard.Search=function(app){this.app=app};Kanboard.Search.prototype.focus=function(){$(document).on("focus","#form-search",function(){var input=$("#form-search");if(input[0].setSelectionRange){var len=input.val().length*2;input[0].setSelectionRange(len,len)}})};Kanboard.Search.prototype.listen=function(){$(document).on("click",".filter-helper",function(e){e.preventDefault();var filter=$(this).data("filter");var appendFilter=$(this).data("append-filter");var uniqueFilter=$(this).data("unique-filter");var input=$("#form-search");if(uniqueFilter){var attribute=uniqueFilter.substr(0,uniqueFilter.indexOf(':'));filter=input.val().replace(new RegExp('('+attribute+':[#a-z0-9]+)','g'),'');filter=filter.replace(new RegExp('('+attribute+':"(.+)")','g'),'');filter=filter.trim();filter+=' '+uniqueFilter}else if(appendFilter){filter=input.val()+" "+appendFilter} +input.val(filter);$("form.search").submit()})};Kanboard.Swimlane=function(app){this.app=app};Kanboard.Swimlane.prototype.execute=function(){if($(".swimlanes-table").length){this.dragAndDrop()} +if($("#board").length){this.expandSingleSwimlane()}};Kanboard.Swimlane.prototype.listen=function(){var self=this;$(document).on('click',".board-swimlane-toggle",function(e){e.preventDefault();var swimlaneId=$(this).data('swimlane-id');if(self.isCollapsed(swimlaneId)){self.expand(swimlaneId)}else{self.collapse(swimlaneId)}})};Kanboard.Swimlane.prototype.onBoardRendered=function(){var swimlaneIds=this.getAllCollapsed();for(var i=0;i-1){swimlaneIds.splice(index,1)} +localStorage.setItem(this.getStorageKey(),JSON.stringify(swimlaneIds));$('.board-swimlane-columns-'+swimlaneId).css('display','table-row');$('.board-swimlane-tasks-'+swimlaneId).css('display','table-row');$('.hide-icon-swimlane-'+swimlaneId).css('display','inline');$('.show-icon-swimlane-'+swimlaneId).css('display','none')};Kanboard.Swimlane.prototype.collapse=function(swimlaneId){var swimlaneIds=this.getAllCollapsed();if(swimlaneIds.indexOf(swimlaneId)<0){swimlaneIds.push(swimlaneId);localStorage.setItem(this.getStorageKey(),JSON.stringify(swimlaneIds))} +$('.board-swimlane-columns-'+swimlaneId+':not(:first-child)').css('display','none');$('.board-swimlane-tasks-'+swimlaneId).css('display','none');$('.hide-icon-swimlane-'+swimlaneId).css('display','none');$('.show-icon-swimlane-'+swimlaneId).css('display','inline')};Kanboard.Swimlane.prototype.isCollapsed=function(swimlaneId){return this.getAllCollapsed().indexOf(swimlaneId)>-1};Kanboard.Swimlane.prototype.getAllCollapsed=function(){return JSON.parse(localStorage.getItem(this.getStorageKey()))||[]};Kanboard.Swimlane.prototype.dragAndDrop=function(){var self=this;$(".draggable-row-handle").mouseenter(function(){$(this).parent().parent().addClass("draggable-item-hover")}).mouseleave(function(){$(this).parent().parent().removeClass("draggable-item-hover")});$(".swimlanes-table tbody").sortable({forcePlaceholderSize:!0,handle:"td:first i",helper:function(e,ui){ui.children().each(function(){$(this).width($(this).width())});return ui},stop:function(event,ui){var swimlane=ui.item;swimlane.removeClass("draggable-item-selected");self.savePosition(swimlane.data("swimlane-id"),swimlane.index()+1)},start:function(event,ui){ui.item.addClass("draggable-item-selected")}}).disableSelection()};Kanboard.Swimlane.prototype.savePosition=function(swimlaneId,position){var url=$(".swimlanes-table").data("save-position-url");var self=this;this.app.showLoadingIcon();$.ajax({cache:!1,url:url,contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({"swimlane_id":swimlaneId,"position":position}),complete:function(){self.app.hideLoadingIcon()}})};Kanboard.Swimlane.prototype.expandSingleSwimlane=function(){if($("tr.board-swimlane").length==1){localStorage.removeItem(this.getStorageKey())}} +Kanboard.Task=function(app){this.app=app};Kanboard.Task.prototype.onPopoverOpened=function(){var self=this;self.renderColorPicker();$(document).on("click",".assign-me",function(e){var currentId=$(this).data("current-id");var dropdownId="#modal-box #"+$(this).data("target-id");e.preventDefault();if($(dropdownId+' option[value='+currentId+']').length){$(dropdownId).val(currentId)}})};Kanboard.Task.prototype.renderColorPicker=function(){function renderColorOption(color){return $('
'+'
'+'
'+color.text+'
'+'
')} +$(".color-picker").select2({minimumResultsForSearch:Infinity,templateResult:renderColorOption,templateSelection:renderColorOption})};Kanboard.BoardDragAndDrop=function(app){this.app=app;this.savingInProgress=!1};Kanboard.BoardDragAndDrop.prototype.execute=function(){if(this.app.hasId("board")){this.executeListeners();this.dragAndDrop()}};Kanboard.BoardDragAndDrop.prototype.dragAndDrop=function(){var self=this;var dropzone=$(".board-task-list");var params={forcePlaceholderSize:!0,tolerance:"pointer",connectWith:".sortable-column:visible",placeholder:"draggable-placeholder",items:".draggable-item",stop:function(event,ui){var task=ui.item;var taskId=task.attr('data-task-id');var taskPosition=task.attr('data-position');var taskColumnId=task.attr('data-column-id');var taskSwimlaneId=task.attr('data-swimlane-id');var newColumnId=task.parent().attr("data-column-id");var newSwimlaneId=task.parent().attr('data-swimlane-id');var newPosition=task.index()+1;task.removeClass("draggable-item-selected");if(newColumnId!=taskColumnId||newSwimlaneId!=taskSwimlaneId||newPosition!=taskPosition){self.changeTaskState(taskId);self.save(taskId,taskColumnId,newColumnId,newPosition,newSwimlaneId)}},start:function(event,ui){ui.item.addClass("draggable-item-selected");ui.placeholder.height(ui.item.height())}};if(isMobile.any){$(".task-board-sort-handle").css("display","inline");params.handle=".task-board-sort-handle"} +dropzone.each(function(){$(this).css("min-height",$(this).parent().height())});dropzone.sortable(params)};Kanboard.BoardDragAndDrop.prototype.changeTaskState=function(taskId){var task=$("div[data-task-id="+taskId+"]");task.addClass('task-board-saving-state');task.find('.task-board-saving-icon').show()};Kanboard.BoardDragAndDrop.prototype.save=function(taskId,srcColumnId,dstColumnId,position,swimlaneId){var self=this;self.app.showLoadingIcon();self.savingInProgress=!0;$.ajax({cache:!1,url:$("#board").data("save-url"),contentType:"application/json",type:"POST",processData:!1,data:JSON.stringify({"task_id":taskId,"src_column_id":srcColumnId,"dst_column_id":dstColumnId,"swimlane_id":swimlaneId,"position":position}),success:function(data){self.refresh(data);self.savingInProgress=!1},error:function(){self.app.hideLoadingIcon();self.savingInProgress=!1},statusCode:{403:function(data){window.alert(data.responseJSON.message);document.location.reload(!0)}}})};Kanboard.BoardDragAndDrop.prototype.refresh=function(data){$("#board-container").replaceWith(data);this.app.hideLoadingIcon();this.executeListeners();this.dragAndDrop()};Kanboard.BoardDragAndDrop.prototype.executeListeners=function(){for(var className in this.app.controllers){var controller=this.app.get(className);if(typeof controller.onBoardRendered==="function"){controller.onBoardRendered()}}};var _KB=null;jQuery(document).ready(function(){_KB=new Kanboard.App();_KB.execute()}) \ No newline at end of file diff --git a/assets/js/vendor.min.js b/assets/js/vendor.min.js new file mode 100644 index 0000000..c4a4057 --- /dev/null +++ b/assets/js/vendor.min.js @@ -0,0 +1,3329 @@ +/*! jQuery v3.6.1 | (c) OpenJS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(C,e){"use strict";var t=[],r=Object.getPrototypeOf,s=t.slice,g=t.flat?function(e){return t.flat.call(e)}:function(e){return t.concat.apply([],e)},u=t.push,i=t.indexOf,n={},o=n.toString,y=n.hasOwnProperty,a=y.toString,l=a.call(Object),v={},m=function(e){return"function"==typeof e&&"number"!=typeof e.nodeType&&"function"!=typeof e.item},x=function(e){return null!=e&&e===e.window},E=C.document,c={type:!0,src:!0,nonce:!0,noModule:!0};function b(e,t,n){var r,i,o=(n=n||E).createElement("script");if(o.text=e,t)for(r in c)(i=t[r]||t.getAttribute&&t.getAttribute(r))&&o.setAttribute(r,i);n.head.appendChild(o).parentNode.removeChild(o)}function w(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?n[o.call(e)]||"object":typeof e}var f="3.6.1",S=function(e,t){return new S.fn.init(e,t)};function p(e){var t=!!e&&"length"in e&&e.length,n=w(e);return!m(e)&&!x(e)&&("array"===n||0===t||"number"==typeof t&&0+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&v(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!y||!y.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ve(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace(B,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ye(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ve(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],y=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||y.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||y.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||y.push(".#.+[+~]"),e.querySelectorAll("\\\f"),y.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),y=y.length&&new RegExp(y.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),v=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&v(p,e)?-1:t==C||t.ownerDocument==p&&v(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!y||!y.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),v.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",v.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",v.option=!!ce.lastChild;var ge={thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var Ut,Xt=[],Vt=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Xt.pop()||S.expando+"_"+Ct.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Vt.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Vt.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Vt,"$1"+r):!1!==e.jsonp&&(e.url+=(Et.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,Xt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),v.createHTMLDocument=((Ut=E.implementation.createHTMLDocument("").body).innerHTML="
",2===Ut.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(v.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return B(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=_e(v.pixelPosition,function(e,t){if(t)return t=Be(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return B(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0",options:{classes:{},disabled:!1,create:null},_createWidget:function(t,e){e=V(e||this.defaultElement||this)[0],this.element=V(e),this.uuid=i++,this.eventNamespace="."+this.widgetName+this.uuid,this.bindings=V(),this.hoverable=V(),this.focusable=V(),this.classesElementLookup={},e!==this&&(V.data(e,this.widgetFullName,this),this._on(!0,this.element,{remove:function(t){t.target===e&&this.destroy()}}),this.document=V(e.style?e.ownerDocument:e.document||e),this.window=V(this.document[0].defaultView||this.document[0].parentWindow)),this.options=V.widget.extend({},this.options,this._getCreateOptions(),t),this._create(),this.options.disabled&&this._setOptionDisabled(this.options.disabled),this._trigger("create",null,this._getCreateEventData()),this._init()},_getCreateOptions:function(){return{}},_getCreateEventData:V.noop,_create:V.noop,_init:V.noop,destroy:function(){var i=this;this._destroy(),V.each(this.classesElementLookup,function(t,e){i._removeClass(e,t)}),this.element.off(this.eventNamespace).removeData(this.widgetFullName),this.widget().off(this.eventNamespace).removeAttr("aria-disabled"),this.bindings.off(this.eventNamespace)},_destroy:V.noop,widget:function(){return this.element},option:function(t,e){var i,s,n,o=t;if(0===arguments.length)return V.widget.extend({},this.options);if("string"==typeof t)if(o={},t=(i=t.split(".")).shift(),i.length){for(s=o[t]=V.widget.extend({},this.options[t]),n=0;n
"),i=e.children()[0];return V("body").append(e),t=i.offsetWidth,e.css("overflow","scroll"),t===(i=i.offsetWidth)&&(i=e[0].clientWidth),e.remove(),s=t-i},getScrollInfo:function(t){var e=t.isWindow||t.isDocument?"":t.element.css("overflow-x"),i=t.isWindow||t.isDocument?"":t.element.css("overflow-y"),e="scroll"===e||"auto"===e&&t.widthx(k(s),k(n))?o.important="horizontal":o.important="vertical",u.using.call(this,t,o)}),a.offset(V.extend(h,{using:t}))})},V.ui.position={fit:{left:function(t,e){var i=e.within,s=i.isWindow?i.scrollLeft:i.offset.left,n=i.width,o=t.left-e.collisionPosition.marginLeft,a=s-o,r=o+e.collisionWidth-n-s;e.collisionWidth>n?0n?0")[0],w=d.each;function P(t){return null==t?t+"":"object"==typeof t?p[e.call(t)]||"object":typeof t}function M(t,e,i){var s=v[e.type]||{};return null==t?i||!e.def?null:e.def:(t=s.floor?~~t:parseFloat(t),isNaN(t)?e.def:s.mod?(t+s.mod)%s.mod:Math.min(s.max,Math.max(0,t)))}function S(s){var n=m(),o=n._rgba=[];return s=s.toLowerCase(),w(g,function(t,e){var i=e.re.exec(s),i=i&&e.parse(i),e=e.space||"rgba";if(i)return i=n[e](i),n[_[e].cache]=i[_[e].cache],o=n._rgba=i._rgba,!1}),o.length?("0,0,0,0"===o.join()&&d.extend(o,B.transparent),n):B[s]}function H(t,e,i){return 6*(i=(i+1)%1)<1?t+(e-t)*i*6:2*i<1?e:3*i<2?t+(e-t)*(2/3-i)*6:t}y.style.cssText="background-color:rgba(1,1,1,.5)",b.rgba=-1o.mod/2?s+=o.mod:s-n>o.mod/2&&(s-=o.mod)),l[i]=M((n-s)*a+s,e)))}),this[e](l)},blend:function(t){if(1===this._rgba[3])return this;var e=this._rgba.slice(),i=e.pop(),s=m(t)._rgba;return m(d.map(e,function(t,e){return(1-i)*s[e]+i*t}))},toRgbaString:function(){var t="rgba(",e=d.map(this._rgba,function(t,e){return null!=t?t:2").addClass("ui-effects-wrapper").css({fontSize:"100%",background:"transparent",border:"none",margin:0,padding:0}),e={width:i.width(),height:i.height()},n=document.activeElement;try{n.id}catch(t){n=document.body}return i.wrap(t),i[0]!==n&&!V.contains(i[0],n)||V(n).trigger("focus"),t=i.parent(),"static"===i.css("position")?(t.css({position:"relative"}),i.css({position:"relative"})):(V.extend(s,{position:i.css("position"),zIndex:i.css("z-index")}),V.each(["top","left","bottom","right"],function(t,e){s[e]=i.css(e),isNaN(parseInt(s[e],10))&&(s[e]="auto")}),i.css({position:"relative",top:0,left:0,right:"auto",bottom:"auto"})),i.css(e),t.css(s).show()},removeWrapper:function(t){var e=document.activeElement;return t.parent().is(".ui-effects-wrapper")&&(t.parent().replaceWith(t),t[0]!==e&&!V.contains(t[0],e)||V(e).trigger("focus")),t}}),V.extend(V.effects,{version:"1.13.2",define:function(t,e,i){return i||(i=e,e="effect"),V.effects.effect[t]=i,V.effects.effect[t].mode=e,i},scaledDimensions:function(t,e,i){if(0===e)return{height:0,width:0,outerHeight:0,outerWidth:0};var s="horizontal"!==i?(e||100)/100:1,e="vertical"!==i?(e||100)/100:1;return{height:t.height()*e,width:t.width()*s,outerHeight:t.outerHeight()*e,outerWidth:t.outerWidth()*s}},clipToBox:function(t){return{width:t.clip.right-t.clip.left,height:t.clip.bottom-t.clip.top,left:t.clip.left,top:t.clip.top}},unshift:function(t,e,i){var s=t.queue();1").insertAfter(t).css({display:/^(inline|ruby)/.test(t.css("display"))?"inline-block":"block",visibility:"hidden",marginTop:t.css("marginTop"),marginBottom:t.css("marginBottom"),marginLeft:t.css("marginLeft"),marginRight:t.css("marginRight"),float:t.css("float")}).outerWidth(t.outerWidth()).outerHeight(t.outerHeight()).addClass("ui-effects-placeholder"),t.data(j+"placeholder",e)),t.css({position:i,left:s.left,top:s.top}),e},removePlaceholder:function(t){var e=j+"placeholder",i=t.data(e);i&&(i.remove(),t.removeData(e))},cleanUp:function(t){V.effects.restoreStyle(t),V.effects.removePlaceholder(t)},setTransition:function(s,t,n,o){return o=o||{},V.each(t,function(t,e){var i=s.cssUnit(e);0");l.appendTo("body").addClass(t.className).css({top:s.top-a,left:s.left-r,height:i.innerHeight(),width:i.innerWidth(),position:n?"fixed":"absolute"}).animate(o,t.duration,t.easing,function(){l.remove(),"function"==typeof e&&e()})}}),V.fx.step.clip=function(t){t.clipInit||(t.start=V(t.elem).cssClip(),"string"==typeof t.end&&(t.end=G(t.end,t.elem)),t.clipInit=!0),V(t.elem).cssClip({top:t.pos*(t.end.top-t.start.top)+t.start.top,right:t.pos*(t.end.right-t.start.right)+t.start.right,bottom:t.pos*(t.end.bottom-t.start.bottom)+t.start.bottom,left:t.pos*(t.end.left-t.start.left)+t.start.left})},Y={},V.each(["Quad","Cubic","Quart","Quint","Expo"],function(e,t){Y[t]=function(t){return Math.pow(t,e+2)}}),V.extend(Y,{Sine:function(t){return 1-Math.cos(t*Math.PI/2)},Circ:function(t){return 1-Math.sqrt(1-t*t)},Elastic:function(t){return 0===t||1===t?t:-Math.pow(2,8*(t-1))*Math.sin((80*(t-1)-7.5)*Math.PI/15)},Back:function(t){return t*t*(3*t-2)},Bounce:function(t){for(var e,i=4;t<((e=Math.pow(2,--i))-1)/11;);return 1/Math.pow(4,3-i)-7.5625*Math.pow((3*e-2)/22-t,2)}}),V.each(Y,function(t,e){V.easing["easeIn"+t]=e,V.easing["easeOut"+t]=function(t){return 1-e(1-t)},V.easing["easeInOut"+t]=function(t){return t<.5?e(2*t)/2:1-e(-2*t+2)/2}});y=V.effects,V.effects.define("blind","hide",function(t,e){var i={up:["bottom","top"],vertical:["bottom","top"],down:["top","bottom"],left:["right","left"],horizontal:["right","left"],right:["left","right"]},s=V(this),n=t.direction||"up",o=s.cssClip(),a={clip:V.extend({},o)},r=V.effects.createPlaceholder(s);a.clip[i[n][0]]=a.clip[i[n][1]],"show"===t.mode&&(s.cssClip(a.clip),r&&r.css(V.effects.clipToBox(a)),a.clip=o),r&&r.animate(V.effects.clipToBox(a),t.duration,t.easing),s.animate(a,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("bounce",function(t,e){var i,s,n=V(this),o=t.mode,a="hide"===o,r="show"===o,l=t.direction||"up",h=t.distance,c=t.times||5,o=2*c+(r||a?1:0),u=t.duration/o,d=t.easing,p="up"===l||"down"===l?"top":"left",f="up"===l||"left"===l,g=0,t=n.queue().length;for(V.effects.createPlaceholder(n),l=n.css(p),h=h||n["top"==p?"outerHeight":"outerWidth"]()/3,r&&((s={opacity:1})[p]=l,n.css("opacity",0).css(p,f?2*-h:2*h).animate(s,u,d)),a&&(h/=Math.pow(2,c-1)),(s={})[p]=l;g").css({position:"absolute",visibility:"visible",left:-s*p,top:-i*f}).parent().addClass("ui-effects-explode").css({position:"absolute",overflow:"hidden",width:p,height:f,left:n+(u?a*p:0),top:o+(u?r*f:0),opacity:u?0:1}).animate({left:n+(u?0:a*p),top:o+(u?0:r*f),opacity:u?1:0},t.duration||500,t.easing,m)}),V.effects.define("fade","toggle",function(t,e){var i="show"===t.mode;V(this).css("opacity",i?0:1).animate({opacity:i?1:0},{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("fold","hide",function(e,t){var i=V(this),s=e.mode,n="show"===s,o="hide"===s,a=e.size||15,r=/([0-9]+)%/.exec(a),l=!!e.horizFirst?["right","bottom"]:["bottom","right"],h=e.duration/2,c=V.effects.createPlaceholder(i),u=i.cssClip(),d={clip:V.extend({},u)},p={clip:V.extend({},u)},f=[u[l[0]],u[l[1]]],s=i.queue().length;r&&(a=parseInt(r[1],10)/100*f[o?0:1]),d.clip[l[0]]=a,p.clip[l[0]]=a,p.clip[l[1]]=0,n&&(i.cssClip(p.clip),c&&c.css(V.effects.clipToBox(p)),p.clip=u),i.queue(function(t){c&&c.animate(V.effects.clipToBox(d),h,e.easing).animate(V.effects.clipToBox(p),h,e.easing),t()}).animate(d,h,e.easing).animate(p,h,e.easing).queue(t),V.effects.unshift(i,s,4)}),V.effects.define("highlight","show",function(t,e){var i=V(this),s={backgroundColor:i.css("backgroundColor")};"hide"===t.mode&&(s.opacity=0),V.effects.saveStyle(i),i.css({backgroundImage:"none",backgroundColor:t.color||"#ffff99"}).animate(s,{queue:!1,duration:t.duration,easing:t.easing,complete:e})}),V.effects.define("size",function(s,e){var n,i=V(this),t=["fontSize"],o=["borderTopWidth","borderBottomWidth","paddingTop","paddingBottom"],a=["borderLeftWidth","borderRightWidth","paddingLeft","paddingRight"],r=s.mode,l="effect"!==r,h=s.scale||"both",c=s.origin||["middle","center"],u=i.css("position"),d=i.position(),p=V.effects.scaledDimensions(i),f=s.from||p,g=s.to||V.effects.scaledDimensions(i,0);V.effects.createPlaceholder(i),"show"===r&&(r=f,f=g,g=r),n={from:{y:f.height/p.height,x:f.width/p.width},to:{y:g.height/p.height,x:g.width/p.width}},"box"!==h&&"both"!==h||(n.from.y!==n.to.y&&(f=V.effects.setTransition(i,o,n.from.y,f),g=V.effects.setTransition(i,o,n.to.y,g)),n.from.x!==n.to.x&&(f=V.effects.setTransition(i,a,n.from.x,f),g=V.effects.setTransition(i,a,n.to.x,g))),"content"!==h&&"both"!==h||n.from.y!==n.to.y&&(f=V.effects.setTransition(i,t,n.from.y,f),g=V.effects.setTransition(i,t,n.to.y,g)),c&&(c=V.effects.getBaseline(c,p),f.top=(p.outerHeight-f.outerHeight)*c.y+d.top,f.left=(p.outerWidth-f.outerWidth)*c.x+d.left,g.top=(p.outerHeight-g.outerHeight)*c.y+d.top,g.left=(p.outerWidth-g.outerWidth)*c.x+d.left),delete f.outerHeight,delete f.outerWidth,i.css(f),"content"!==h&&"both"!==h||(o=o.concat(["marginTop","marginBottom"]).concat(t),a=a.concat(["marginLeft","marginRight"]),i.find("*[width]").each(function(){var t=V(this),e=V.effects.scaledDimensions(t),i={height:e.height*n.from.y,width:e.width*n.from.x,outerHeight:e.outerHeight*n.from.y,outerWidth:e.outerWidth*n.from.x},e={height:e.height*n.to.y,width:e.width*n.to.x,outerHeight:e.height*n.to.y,outerWidth:e.width*n.to.x};n.from.y!==n.to.y&&(i=V.effects.setTransition(t,o,n.from.y,i),e=V.effects.setTransition(t,o,n.to.y,e)),n.from.x!==n.to.x&&(i=V.effects.setTransition(t,a,n.from.x,i),e=V.effects.setTransition(t,a,n.to.x,e)),l&&V.effects.saveStyle(t),t.css(i),t.animate(e,s.duration,s.easing,function(){l&&V.effects.restoreStyle(t)})})),i.animate(g,{queue:!1,duration:s.duration,easing:s.easing,complete:function(){var t=i.offset();0===g.opacity&&i.css("opacity",f.opacity),l||(i.css("position","static"===u?"relative":u).offset(t),V.effects.saveStyle(i)),e()}})}),V.effects.define("scale",function(t,e){var i=V(this),s=t.mode,s=parseInt(t.percent,10)||(0===parseInt(t.percent,10)||"effect"!==s?0:100),s=V.extend(!0,{from:V.effects.scaledDimensions(i),to:V.effects.scaledDimensions(i,s,t.direction||"both"),origin:t.origin||["middle","center"]},t);t.fade&&(s.from.opacity=1,s.to.opacity=0),V.effects.effect.size.call(this,s,e)}),V.effects.define("puff","hide",function(t,e){t=V.extend(!0,{},t,{fade:!0,percent:parseInt(t.percent,10)||150});V.effects.effect.scale.call(this,t,e)}),V.effects.define("pulsate","show",function(t,e){var i=V(this),s=t.mode,n="show"===s,o=2*(t.times||5)+(n||"hide"===s?1:0),a=t.duration/o,r=0,l=1,s=i.queue().length;for(!n&&i.is(":visible")||(i.css("opacity",0).show(),r=1);l li > :first-child").add(t.find("> :not(li)").even())},heightStyle:"auto",icons:{activeHeader:"ui-icon-triangle-1-s",header:"ui-icon-triangle-1-e"},activate:null,beforeActivate:null},hideProps:{borderTopWidth:"hide",borderBottomWidth:"hide",paddingTop:"hide",paddingBottom:"hide",height:"hide"},showProps:{borderTopWidth:"show",borderBottomWidth:"show",paddingTop:"show",paddingBottom:"show",height:"show"},_create:function(){var t=this.options;this.prevShow=this.prevHide=V(),this._addClass("ui-accordion","ui-widget ui-helper-reset"),this.element.attr("role","tablist"),t.collapsible||!1!==t.active&&null!=t.active||(t.active=0),this._processPanels(),t.active<0&&(t.active+=this.headers.length),this._refresh()},_getCreateEventData:function(){return{header:this.active,panel:this.active.length?this.active.next():V()}},_createIcons:function(){var t,e=this.options.icons;e&&(t=V(""),this._addClass(t,"ui-accordion-header-icon","ui-icon "+e.header),t.prependTo(this.headers),t=this.active.children(".ui-accordion-header-icon"),this._removeClass(t,e.header)._addClass(t,null,e.activeHeader)._addClass(this.headers,"ui-accordion-icons"))},_destroyIcons:function(){this._removeClass(this.headers,"ui-accordion-icons"),this.headers.children(".ui-accordion-header-icon").remove()},_destroy:function(){var t;this.element.removeAttr("role"),this.headers.removeAttr("role aria-expanded aria-selected aria-controls tabIndex").removeUniqueId(),this._destroyIcons(),t=this.headers.next().css("display","").removeAttr("role aria-hidden aria-labelledby").removeUniqueId(),"content"!==this.options.heightStyle&&t.css("height","")},_setOption:function(t,e){"active"!==t?("event"===t&&(this.options.event&&this._off(this.headers,this.options.event),this._setupEvents(e)),this._super(t,e),"collapsible"!==t||e||!1!==this.options.active||this._activate(0),"icons"===t&&(this._destroyIcons(),e&&this._createIcons())):this._activate(e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",t),this._toggleClass(null,"ui-state-disabled",!!t),this._toggleClass(this.headers.add(this.headers.next()),null,"ui-state-disabled",!!t)},_keydown:function(t){if(!t.altKey&&!t.ctrlKey){var e=V.ui.keyCode,i=this.headers.length,s=this.headers.index(t.target),n=!1;switch(t.keyCode){case e.RIGHT:case e.DOWN:n=this.headers[(s+1)%i];break;case e.LEFT:case e.UP:n=this.headers[(s-1+i)%i];break;case e.SPACE:case e.ENTER:this._eventHandler(t);break;case e.HOME:n=this.headers[0];break;case e.END:n=this.headers[i-1]}n&&(V(t.target).attr("tabIndex",-1),V(n).attr("tabIndex",0),V(n).trigger("focus"),t.preventDefault())}},_panelKeyDown:function(t){t.keyCode===V.ui.keyCode.UP&&t.ctrlKey&&V(t.currentTarget).prev().trigger("focus")},refresh:function(){var t=this.options;this._processPanels(),!1===t.active&&!0===t.collapsible||!this.headers.length?(t.active=!1,this.active=V()):!1===t.active?this._activate(0):this.active.length&&!V.contains(this.element[0],this.active[0])?this.headers.length===this.headers.find(".ui-state-disabled").length?(t.active=!1,this.active=V()):this._activate(Math.max(0,t.active-1)):t.active=this.headers.index(this.active),this._destroyIcons(),this._refresh()},_processPanels:function(){var t=this.headers,e=this.panels;"function"==typeof this.options.header?this.headers=this.options.header(this.element):this.headers=this.element.find(this.options.header),this._addClass(this.headers,"ui-accordion-header ui-accordion-header-collapsed","ui-state-default"),this.panels=this.headers.next().filter(":not(.ui-accordion-content-active)").hide(),this._addClass(this.panels,"ui-accordion-content","ui-helper-reset ui-widget-content"),e&&(this._off(t.not(this.headers)),this._off(e.not(this.panels)))},_refresh:function(){var i,t=this.options,e=t.heightStyle,s=this.element.parent();this.active=this._findActive(t.active),this._addClass(this.active,"ui-accordion-header-active","ui-state-active")._removeClass(this.active,"ui-accordion-header-collapsed"),this._addClass(this.active.next(),"ui-accordion-content-active"),this.active.next().show(),this.headers.attr("role","tab").each(function(){var t=V(this),e=t.uniqueId().attr("id"),i=t.next(),s=i.uniqueId().attr("id");t.attr("aria-controls",s),i.attr("aria-labelledby",e)}).next().attr("role","tabpanel"),this.headers.not(this.active).attr({"aria-selected":"false","aria-expanded":"false",tabIndex:-1}).next().attr({"aria-hidden":"true"}).hide(),this.active.length?this.active.attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0}).next().attr({"aria-hidden":"false"}):this.headers.eq(0).attr("tabIndex",0),this._createIcons(),this._setupEvents(t.event),"fill"===e?(i=s.height(),this.element.siblings(":visible").each(function(){var t=V(this),e=t.css("position");"absolute"!==e&&"fixed"!==e&&(i-=t.outerHeight(!0))}),this.headers.each(function(){i-=V(this).outerHeight(!0)}),this.headers.next().each(function(){V(this).height(Math.max(0,i-V(this).innerHeight()+V(this).height()))}).css("overflow","auto")):"auto"===e&&(i=0,this.headers.next().each(function(){var t=V(this).is(":visible");t||V(this).show(),i=Math.max(i,V(this).css("height","").height()),t||V(this).hide()}).height(i))},_activate:function(t){t=this._findActive(t)[0];t!==this.active[0]&&(t=t||this.active[0],this._eventHandler({target:t,currentTarget:t,preventDefault:V.noop}))},_findActive:function(t){return"number"==typeof t?this.headers.eq(t):V()},_setupEvents:function(t){var i={keydown:"_keydown"};t&&V.each(t.split(" "),function(t,e){i[e]="_eventHandler"}),this._off(this.headers.add(this.headers.next())),this._on(this.headers,i),this._on(this.headers.next(),{keydown:"_panelKeyDown"}),this._hoverable(this.headers),this._focusable(this.headers)},_eventHandler:function(t){var e=this.options,i=this.active,s=V(t.currentTarget),n=s[0]===i[0],o=n&&e.collapsible,a=o?V():s.next(),r=i.next(),a={oldHeader:i,oldPanel:r,newHeader:o?V():s,newPanel:a};t.preventDefault(),n&&!e.collapsible||!1===this._trigger("beforeActivate",t,a)||(e.active=!o&&this.headers.index(s),this.active=n?V():s,this._toggle(a),this._removeClass(i,"ui-accordion-header-active","ui-state-active"),e.icons&&(i=i.children(".ui-accordion-header-icon"),this._removeClass(i,null,e.icons.activeHeader)._addClass(i,null,e.icons.header)),n||(this._removeClass(s,"ui-accordion-header-collapsed")._addClass(s,"ui-accordion-header-active","ui-state-active"),e.icons&&(n=s.children(".ui-accordion-header-icon"),this._removeClass(n,null,e.icons.header)._addClass(n,null,e.icons.activeHeader)),this._addClass(s.next(),"ui-accordion-content-active")))},_toggle:function(t){var e=t.newPanel,i=this.prevShow.length?this.prevShow:t.oldPanel;this.prevShow.add(this.prevHide).stop(!0,!0),this.prevShow=e,this.prevHide=i,this.options.animate?this._animate(e,i,t):(i.hide(),e.show(),this._toggleComplete(t)),i.attr({"aria-hidden":"true"}),i.prev().attr({"aria-selected":"false","aria-expanded":"false"}),e.length&&i.length?i.prev().attr({tabIndex:-1,"aria-expanded":"false"}):e.length&&this.headers.filter(function(){return 0===parseInt(V(this).attr("tabIndex"),10)}).attr("tabIndex",-1),e.attr("aria-hidden","false").prev().attr({"aria-selected":"true","aria-expanded":"true",tabIndex:0})},_animate:function(t,i,e){var s,n,o,a=this,r=0,l=t.css("box-sizing"),h=t.length&&(!i.length||t.index()",delay:300,options:{icons:{submenu:"ui-icon-caret-1-e"},items:"> *",menus:"ul",position:{my:"left top",at:"right top"},role:"menu",blur:null,focus:null,select:null},_create:function(){this.activeMenu=this.element,this.mouseHandled=!1,this.lastMousePosition={x:null,y:null},this.element.uniqueId().attr({role:this.options.role,tabIndex:0}),this._addClass("ui-menu","ui-widget ui-widget-content"),this._on({"mousedown .ui-menu-item":function(t){t.preventDefault(),this._activateItem(t)},"click .ui-menu-item":function(t){var e=V(t.target),i=V(V.ui.safeActiveElement(this.document[0]));!this.mouseHandled&&e.not(".ui-state-disabled").length&&(this.select(t),t.isPropagationStopped()||(this.mouseHandled=!0),e.has(".ui-menu").length?this.expand(t):!this.element.is(":focus")&&i.closest(".ui-menu").length&&(this.element.trigger("focus",[!0]),this.active&&1===this.active.parents(".ui-menu").length&&clearTimeout(this.timer)))},"mouseenter .ui-menu-item":"_activateItem","mousemove .ui-menu-item":"_activateItem",mouseleave:"collapseAll","mouseleave .ui-menu":"collapseAll",focus:function(t,e){var i=this.active||this._menuItems().first();e||this.focus(t,i)},blur:function(t){this._delay(function(){V.contains(this.element[0],V.ui.safeActiveElement(this.document[0]))||this.collapseAll(t)})},keydown:"_keydown"}),this.refresh(),this._on(this.document,{click:function(t){this._closeOnDocumentClick(t)&&this.collapseAll(t,!0),this.mouseHandled=!1}})},_activateItem:function(t){var e,i;this.previousFilter||t.clientX===this.lastMousePosition.x&&t.clientY===this.lastMousePosition.y||(this.lastMousePosition={x:t.clientX,y:t.clientY},e=V(t.target).closest(".ui-menu-item"),i=V(t.currentTarget),e[0]===i[0]&&(i.is(".ui-state-active")||(this._removeClass(i.siblings().children(".ui-state-active"),null,"ui-state-active"),this.focus(t,i))))},_destroy:function(){var t=this.element.find(".ui-menu-item").removeAttr("role aria-disabled").children(".ui-menu-item-wrapper").removeUniqueId().removeAttr("tabIndex role aria-haspopup");this.element.removeAttr("aria-activedescendant").find(".ui-menu").addBack().removeAttr("role aria-labelledby aria-expanded aria-hidden aria-disabled tabIndex").removeUniqueId().show(),t.children().each(function(){var t=V(this);t.data("ui-menu-submenu-caret")&&t.remove()})},_keydown:function(t){var e,i,s,n=!0;switch(t.keyCode){case V.ui.keyCode.PAGE_UP:this.previousPage(t);break;case V.ui.keyCode.PAGE_DOWN:this.nextPage(t);break;case V.ui.keyCode.HOME:this._move("first","first",t);break;case V.ui.keyCode.END:this._move("last","last",t);break;case V.ui.keyCode.UP:this.previous(t);break;case V.ui.keyCode.DOWN:this.next(t);break;case V.ui.keyCode.LEFT:this.collapse(t);break;case V.ui.keyCode.RIGHT:this.active&&!this.active.is(".ui-state-disabled")&&this.expand(t);break;case V.ui.keyCode.ENTER:case V.ui.keyCode.SPACE:this._activate(t);break;case V.ui.keyCode.ESCAPE:this.collapse(t);break;default:e=this.previousFilter||"",s=n=!1,i=96<=t.keyCode&&t.keyCode<=105?(t.keyCode-96).toString():String.fromCharCode(t.keyCode),clearTimeout(this.filterTimer),i===e?s=!0:i=e+i,e=this._filterMenuItems(i),(e=s&&-1!==e.index(this.active.next())?this.active.nextAll(".ui-menu-item"):e).length||(i=String.fromCharCode(t.keyCode),e=this._filterMenuItems(i)),e.length?(this.focus(t,e),this.previousFilter=i,this.filterTimer=this._delay(function(){delete this.previousFilter},1e3)):delete this.previousFilter}n&&t.preventDefault()},_activate:function(t){this.active&&!this.active.is(".ui-state-disabled")&&(this.active.children("[aria-haspopup='true']").length?this.expand(t):this.select(t))},refresh:function(){var t,e,s=this,n=this.options.icons.submenu,i=this.element.find(this.options.menus);this._toggleClass("ui-menu-icons",null,!!this.element.find(".ui-icon").length),e=i.filter(":not(.ui-menu)").hide().attr({role:this.options.role,"aria-hidden":"true","aria-expanded":"false"}).each(function(){var t=V(this),e=t.prev(),i=V("").data("ui-menu-submenu-caret",!0);s._addClass(i,"ui-menu-icon","ui-icon "+n),e.attr("aria-haspopup","true").prepend(i),t.attr("aria-labelledby",e.attr("id"))}),this._addClass(e,"ui-menu","ui-widget ui-widget-content ui-front"),(t=i.add(this.element).find(this.options.items)).not(".ui-menu-item").each(function(){var t=V(this);s._isDivider(t)&&s._addClass(t,"ui-menu-divider","ui-widget-content")}),i=(e=t.not(".ui-menu-item, .ui-menu-divider")).children().not(".ui-menu").uniqueId().attr({tabIndex:-1,role:this._itemRole()}),this._addClass(e,"ui-menu-item")._addClass(i,"ui-menu-item-wrapper"),t.filter(".ui-state-disabled").attr("aria-disabled","true"),this.active&&!V.contains(this.element[0],this.active[0])&&this.blur()},_itemRole:function(){return{menu:"menuitem",listbox:"option"}[this.options.role]},_setOption:function(t,e){var i;"icons"===t&&(i=this.element.find(".ui-menu-icon"),this._removeClass(i,null,this.options.icons.submenu)._addClass(i,null,e.submenu)),this._super(t,e)},_setOptionDisabled:function(t){this._super(t),this.element.attr("aria-disabled",String(t)),this._toggleClass(null,"ui-state-disabled",!!t)},focus:function(t,e){var i;this.blur(t,t&&"focus"===t.type),this._scrollIntoView(e),this.active=e.first(),i=this.active.children(".ui-menu-item-wrapper"),this._addClass(i,null,"ui-state-active"),this.options.role&&this.element.attr("aria-activedescendant",i.attr("id")),i=this.active.parent().closest(".ui-menu-item").children(".ui-menu-item-wrapper"),this._addClass(i,null,"ui-state-active"),t&&"keydown"===t.type?this._close():this.timer=this._delay(function(){this._close()},this.delay),(i=e.children(".ui-menu")).length&&t&&/^mouse/.test(t.type)&&this._startOpening(i),this.activeMenu=e.parent(),this._trigger("focus",t,{item:e})},_scrollIntoView:function(t){var e,i,s;this._hasScroll()&&(i=parseFloat(V.css(this.activeMenu[0],"borderTopWidth"))||0,s=parseFloat(V.css(this.activeMenu[0],"paddingTop"))||0,e=t.offset().top-this.activeMenu.offset().top-i-s,i=this.activeMenu.scrollTop(),s=this.activeMenu.height(),t=t.outerHeight(),e<0?this.activeMenu.scrollTop(i+e):s",options:{appendTo:null,autoFocus:!1,delay:300,minLength:1,position:{my:"left top",at:"left bottom",collision:"none"},source:null,change:null,close:null,focus:null,open:null,response:null,search:null,select:null},requestIndex:0,pending:0,liveRegionTimer:null,_create:function(){var i,s,n,t=this.element[0].nodeName.toLowerCase(),e="textarea"===t,t="input"===t;this.isMultiLine=e||!t&&this._isContentEditable(this.element),this.valueMethod=this.element[e||t?"val":"text"],this.isNewMenu=!0,this._addClass("ui-autocomplete-input"),this.element.attr("autocomplete","off"),this._on(this.element,{keydown:function(t){if(this.element.prop("readOnly"))s=n=i=!0;else{s=n=i=!1;var e=V.ui.keyCode;switch(t.keyCode){case e.PAGE_UP:i=!0,this._move("previousPage",t);break;case e.PAGE_DOWN:i=!0,this._move("nextPage",t);break;case e.UP:i=!0,this._keyEvent("previous",t);break;case e.DOWN:i=!0,this._keyEvent("next",t);break;case e.ENTER:this.menu.active&&(i=!0,t.preventDefault(),this.menu.select(t));break;case e.TAB:this.menu.active&&this.menu.select(t);break;case e.ESCAPE:this.menu.element.is(":visible")&&(this.isMultiLine||this._value(this.term),this.close(t),t.preventDefault());break;default:s=!0,this._searchTimeout(t)}}},keypress:function(t){if(i)return i=!1,void(this.isMultiLine&&!this.menu.element.is(":visible")||t.preventDefault());if(!s){var e=V.ui.keyCode;switch(t.keyCode){case e.PAGE_UP:this._move("previousPage",t);break;case e.PAGE_DOWN:this._move("nextPage",t);break;case e.UP:this._keyEvent("previous",t);break;case e.DOWN:this._keyEvent("next",t)}}},input:function(t){if(n)return n=!1,void t.preventDefault();this._searchTimeout(t)},focus:function(){this.selectedItem=null,this.previous=this._value()},blur:function(t){clearTimeout(this.searching),this.close(t),this._change(t)}}),this._initSource(),this.menu=V("