├── .codecov.yml ├── .devcontainer ├── devcontainer.json └── setup.sh ├── .editorconfig ├── .eslintignore ├── .eslintrc.js ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── Bug_report.md │ └── Feature_request.md ├── dependabot.yml ├── pull_request_template.md └── workflows │ ├── appbuild.yml │ ├── appstore-build-publish.yml │ ├── cypress-e2e.yml │ ├── dependabot-approve-merge.yml │ ├── fixup.yml │ ├── integration.yml │ ├── lint-eslint.yml │ ├── lint-php-cs.yml │ ├── lint-php.yml │ ├── lint-stylelint.yml │ ├── nodejs.yml │ ├── npm-audit-fix.yml │ ├── phpunit-mysql.yml │ ├── phpunit-pgsql.yml │ ├── phpunit-sqlite.yml │ ├── pr-feedback.yml │ ├── psalm.yml │ ├── reuse.yml │ ├── update-nextcloud-ocp-approve-merge.yml │ └── update-nextcloud-ocp.yml ├── .gitignore ├── .nextcloudignore ├── .php-cs-fixer.dist.php ├── .tx └── config ├── AUTHORS.md ├── CHANGELOG.md ├── LICENSE ├── LICENSES ├── AGPL-3.0-or-later.txt ├── Apache-2.0.txt ├── CC0-1.0.txt └── MIT.txt ├── Makefile ├── README.md ├── REUSE.toml ├── SECURITY.md ├── appinfo ├── autoload.php ├── info.xml └── routes.php ├── babel.config.js ├── composer.json ├── composer.lock ├── css ├── activity.css ├── collections.css ├── deck.css └── print.scss ├── cypress.config.js ├── cypress ├── .eslintrc.js ├── e2e │ ├── boardFeatures.js │ ├── cardFeatures.js │ ├── deckDashboard.js │ ├── sharingFeatures.js │ └── stackFeatures.js ├── fixtures │ ├── example.json │ └── import-board.json ├── plugins │ └── index.js ├── support │ ├── commands.js │ ├── component-index.html │ ├── component.js │ └── e2e.js └── utils │ ├── index.js │ └── sampleBoard.js ├── docs ├── API-Nextcloud.md ├── API.md ├── Markdown.md ├── Release.md ├── User_documentation_en.md ├── export-import.md ├── extra.css ├── implement-import.md ├── import-class-diagram.md ├── index.md ├── index.md.license ├── resources │ ├── BoardImport.svg │ ├── BoardImport.svg.license │ ├── BoardImport.yuml │ ├── er-diagram.dia │ ├── er-diagram.dia.license │ ├── er-diagram.jpg │ ├── er-diagram.jpg.license │ └── gifs │ │ ├── EN_archive.gif │ │ ├── EN_archive.gif.license │ │ ├── EN_create_board.gif │ │ ├── EN_create_board.gif.license │ │ ├── EN_create_columns.gif │ │ ├── EN_create_columns.gif.license │ │ ├── EN_create_task.gif │ │ ├── EN_create_task.gif.license │ │ ├── EN_done.gif │ │ ├── EN_done.gif.license │ │ ├── EN_put_infos.gif │ │ ├── EN_put_infos.gif.license │ │ ├── EN_put_infos_2.gif │ │ └── EN_put_infos_2.gif.license └── structure.md ├── img ├── activity-dark.svg ├── activity.svg ├── add-white.svg ├── archive.svg ├── attach.svg ├── calendar-dark.svg ├── calendar-white.svg ├── card.svg ├── circles-dark.svg ├── circles.svg ├── color_picker-dark.svg ├── color_picker.svg ├── deck-current.svg ├── deck-dark.svg ├── deck.svg ├── description.svg ├── details-white.svg ├── details.svg ├── favicon.ico ├── favicon.png ├── favicon.svg ├── filter.svg ├── filter_set.svg ├── notifications-dark.svg ├── sample-image.jpg ├── toggle-view-collapse.svg └── toggle-view-expand.svg ├── krankerl.toml ├── l10n ├── .gitkeep ├── af.js ├── af.json ├── ar.js ├── ar.json ├── ast.js ├── ast.json ├── az.js ├── az.json ├── bg.js ├── bg.json ├── bn_BD.js ├── bn_BD.json ├── br.js ├── br.json ├── bs.js ├── bs.json ├── ca.js ├── ca.json ├── cs.js ├── cs.json ├── cy_GB.js ├── cy_GB.json ├── da.js ├── da.json ├── de.js ├── de.json ├── de_DE.js ├── de_DE.json ├── el.js ├── el.json ├── en_GB.js ├── en_GB.json ├── eo.js ├── eo.json ├── es.js ├── es.json ├── es_419.js ├── es_419.json ├── es_AR.js ├── es_AR.json ├── es_CL.js ├── es_CL.json ├── es_CO.js ├── es_CO.json ├── es_CR.js ├── es_CR.json ├── es_DO.js ├── es_DO.json ├── es_EC.js ├── es_EC.json ├── es_GT.js ├── es_GT.json ├── es_HN.js ├── es_HN.json ├── es_MX.js ├── es_MX.json ├── es_NI.js ├── es_NI.json ├── es_PA.js ├── es_PA.json ├── es_PE.js ├── es_PE.json ├── es_PR.js ├── es_PR.json ├── es_PY.js ├── es_PY.json ├── es_SV.js ├── es_SV.json ├── es_UY.js ├── es_UY.json ├── et_EE.js ├── et_EE.json ├── eu.js ├── eu.json ├── fa.js ├── fa.json ├── fi.js ├── fi.json ├── fr.js ├── fr.json ├── ga.js ├── ga.json ├── gd.js ├── gd.json ├── gl.js ├── gl.json ├── he.js ├── he.json ├── hr.js ├── hr.json ├── hu.js ├── hu.json ├── hy.js ├── hy.json ├── ia.js ├── ia.json ├── id.js ├── id.json ├── is.js ├── is.json ├── it.js ├── it.json ├── ja.js ├── ja.json ├── ka.js ├── ka.json ├── ka_GE.js ├── ka_GE.json ├── kab.js ├── kab.json ├── km.js ├── km.json ├── kn.js ├── kn.json ├── ko.js ├── ko.json ├── lb.js ├── lb.json ├── lo.js ├── lo.json ├── lt_LT.js ├── lt_LT.json ├── lv.js ├── lv.json ├── mk.js ├── mk.json ├── mn.js ├── mn.json ├── ms_MY.js ├── ms_MY.json ├── nb.js ├── nb.json ├── nl.js ├── nl.json ├── nn_NO.js ├── nn_NO.json ├── oc.js ├── oc.json ├── pl.js ├── pl.json ├── pt_BR.js ├── pt_BR.json ├── pt_PT.js ├── pt_PT.json ├── ro.js ├── ro.json ├── ru.js ├── ru.json ├── sc.js ├── sc.json ├── si.js ├── si.json ├── sk.js ├── sk.json ├── sl.js ├── sl.json ├── sq.js ├── sq.json ├── sr.js ├── sr.json ├── sr@latin.js ├── sr@latin.json ├── sv.js ├── sv.json ├── ta.js ├── ta.json ├── th.js ├── th.json ├── tr.js ├── tr.json ├── ug.js ├── ug.json ├── uk.js ├── uk.json ├── uz.js ├── uz.json ├── vi.js ├── vi.json ├── zh_CN.js ├── zh_CN.json ├── zh_HK.js ├── zh_HK.json ├── zh_TW.js └── zh_TW.json ├── lib ├── Activity │ ├── ActivityManager.php │ ├── ChangeSet.php │ ├── DeckProvider.php │ ├── Filter.php │ ├── SettingBase.php │ ├── SettingChanges.php │ ├── SettingComment.php │ └── SettingDescription.php ├── AppInfo │ └── Application.php ├── ArchivedItemException.php ├── BadRequestException.php ├── Cache │ └── AttachmentCacheHelper.php ├── Capabilities.php ├── Collaboration │ └── Resources │ │ ├── ResourceProvider.php │ │ └── ResourceProviderCard.php ├── Command │ ├── BoardImport.php │ ├── TransferOwnership.php │ └── UserExport.php ├── Controller │ ├── AttachmentApiController.php │ ├── AttachmentApiV11Controller.php │ ├── AttachmentController.php │ ├── BoardApiController.php │ ├── BoardController.php │ ├── BoardImportApiController.php │ ├── CardApiController.php │ ├── CardController.php │ ├── CommentsApiController.php │ ├── ConfigController.php │ ├── LabelApiController.php │ ├── LabelController.php │ ├── OverviewApiController.php │ ├── PageController.php │ ├── SearchController.php │ ├── SessionController.php │ ├── StackApiController.php │ └── StackController.php ├── Cron │ ├── CardDescriptionActivity.php │ ├── DeleteCron.php │ ├── ScheduledNotifications.php │ └── SessionsCleanup.php ├── DAV │ ├── Calendar.php │ ├── CalendarObject.php │ ├── CalendarPlugin.php │ └── DeckCalendarBackend.php ├── Dashboard │ ├── DeckWidgetToday.php │ ├── DeckWidgetTomorrow.php │ └── DeckWidgetUpcoming.php ├── Db │ ├── Acl.php │ ├── AclMapper.php │ ├── Assignment.php │ ├── AssignmentMapper.php │ ├── Attachment.php │ ├── AttachmentMapper.php │ ├── Board.php │ ├── BoardMapper.php │ ├── Card.php │ ├── CardMapper.php │ ├── ChangeHelper.php │ ├── Circle.php │ ├── DeckMapper.php │ ├── Group.php │ ├── IPermissionMapper.php │ ├── Label.php │ ├── LabelMapper.php │ ├── RelationalEntity.php │ ├── RelationalObject.php │ ├── Session.php │ ├── SessionMapper.php │ ├── Stack.php │ ├── StackMapper.php │ └── User.php ├── Event │ ├── AAclEvent.php │ ├── ABoardImportGetAllowedEvent.php │ ├── ACardEvent.php │ ├── AclCreatedEvent.php │ ├── AclDeletedEvent.php │ ├── AclUpdatedEvent.php │ ├── BoardImportGetAllowedEvent.php │ ├── BoardUpdatedEvent.php │ ├── CardCreatedEvent.php │ ├── CardDeletedEvent.php │ ├── CardUpdatedEvent.php │ ├── SessionClosedEvent.php │ └── SessionCreatedEvent.php ├── Exceptions │ └── ConflictException.php ├── InvalidAttachmentType.php ├── Listeners │ ├── BeforeTemplateRenderedListener.php │ ├── CommentEventListener.php │ ├── FullTextSearchEventListener.php │ ├── LiveUpdateListener.php │ ├── ParticipantCleanupListener.php │ ├── ResourceAdditionalScriptsListener.php │ └── ResourceListener.php ├── Middleware │ ├── DefaultBoardMiddleware.php │ └── ExceptionMiddleware.php ├── Migration │ ├── DeletedCircleCleanup.php │ ├── LabelMismatchCleanup.php │ ├── Version1000Date20200306161713.php │ ├── Version1000Date20200308073933.php │ ├── Version1011Date20230901010840.php │ ├── Version1011Date20231106160059.php │ ├── Version10200Date20201111150114.php │ ├── Version10800Date20220422061816.php │ ├── Version10900Date202206151724222.php │ └── Version11000Date20240222115515.php ├── Model │ ├── BoardSummary.php │ ├── CardDetails.php │ └── OptionalNullableValue.php ├── NoPermissionException.php ├── NotFoundException.php ├── Notification │ ├── NotificationHelper.php │ └── Notifier.php ├── NotifyPushEvents.php ├── Provider │ └── DeckProvider.php ├── Reference │ ├── BoardReferenceProvider.php │ ├── CardReferenceProvider.php │ ├── CommentReferenceProvider.php │ └── CreateCardReferenceProvider.php ├── Search │ ├── BoardSearchResultEntry.php │ ├── CardCommentProvider.php │ ├── CardSearchResultEntry.php │ ├── CommentSearchResultEntry.php │ ├── DeckProvider.php │ ├── FilterStringParser.php │ └── Query │ │ ├── AQueryParameter.php │ │ ├── DateQueryParameter.php │ │ ├── SearchQuery.php │ │ └── StringQueryParameter.php ├── Service │ ├── AssignmentService.php │ ├── AttachmentService.php │ ├── BoardService.php │ ├── CardService.php │ ├── CirclesService.php │ ├── CommentService.php │ ├── ConfigService.php │ ├── DefaultBoardService.php │ ├── FileService.php │ ├── FilesAppService.php │ ├── FullTextSearchService.php │ ├── IAttachmentService.php │ ├── ICustomAttachmentService.php │ ├── Importer │ │ ├── ABoardImportService.php │ │ ├── BoardImportCommandService.php │ │ ├── BoardImportService.php │ │ ├── Systems │ │ │ ├── DeckJsonService.php │ │ │ ├── TrelloApiService.php │ │ │ └── TrelloJsonService.php │ │ └── fixtures │ │ │ ├── config-deckJson-schema.json │ │ │ ├── config-trelloApi-schema.json │ │ │ └── config-trelloJson-schema.json │ ├── LabelService.php │ ├── OverviewService.php │ ├── PermissionService.php │ ├── SearchService.php │ ├── SessionService.php │ ├── StackService.php │ └── fixtures │ │ └── default-board.json ├── Sharing │ ├── DeckShareProvider.php │ ├── Listener.php │ └── ShareAPIHelper.php ├── StatusException.php ├── Teams │ └── DeckTeamResourceProvider.php └── Validators │ ├── AssignmentServiceValidator.php │ ├── AttachmentServiceValidator.php │ ├── BaseValidator.php │ ├── BoardServiceValidator.php │ ├── CardServiceValidator.php │ ├── LabelServiceValidator.php │ └── StackServiceValidator.php ├── mkdocs.yml ├── package-lock.json ├── package.json ├── psalm.xml ├── relativeci.config.js ├── src ├── App.vue ├── BoardSelector.vue ├── CardCreateDialog.vue ├── CardMoveDialog.vue ├── CardSelector.vue ├── components │ ├── ActivityEntry.vue │ ├── ActivityList.vue │ ├── AttachmentDragAndDrop.vue │ ├── CollaborationView.vue │ ├── Controls.vue │ ├── KeyboardShortcuts.vue │ ├── SessionList.vue │ ├── Sidebar.vue │ ├── board │ │ ├── Board.vue │ │ ├── BoardSidebar.vue │ │ ├── DeletedTabSidebar.vue │ │ ├── SharingTabSidebar.vue │ │ ├── Stack.vue │ │ ├── TagsTabSidebar.vue │ │ └── TimelineTabSidebar.vue │ ├── boards │ │ ├── BoardItem.vue │ │ └── Boards.vue │ ├── card │ │ ├── AssignmentSelector.vue │ │ ├── AttachmentList.vue │ │ ├── CardDetailEntry.vue │ │ ├── CardSidebar.vue │ │ ├── CardSidebarTabActivity.vue │ │ ├── CardSidebarTabAttachments.vue │ │ ├── CardSidebarTabComments.vue │ │ ├── CardSidebarTabDetails.vue │ │ ├── CommentForm.vue │ │ ├── CommentItem.vue │ │ ├── Description.vue │ │ ├── DueDateSelector.vue │ │ └── TagSelector.vue │ ├── cards │ │ ├── AvatarList.vue │ │ ├── CardBadges.vue │ │ ├── CardCover.vue │ │ ├── CardItem.vue │ │ ├── CardMenu.vue │ │ ├── CardMenuEntries.vue │ │ └── badges │ │ │ ├── CardId.vue │ │ │ └── DueDate.vue │ ├── dashboard │ │ └── Card.vue │ ├── icons │ │ └── DeckIcon.vue │ ├── modals │ │ └── HelpModal.vue │ ├── navigation │ │ ├── AppNavigation.vue │ │ ├── AppNavigationAddBoard.vue │ │ ├── AppNavigationBoard.vue │ │ ├── AppNavigationBoardCategory.vue │ │ ├── AppNavigationImportBoard.vue │ │ ├── BoardCloneModal.vue │ │ └── BoardExportModal.vue │ ├── overview │ │ └── Overview.vue │ └── search │ │ ├── GlobalSearchResults.vue │ │ └── Placeholder.vue ├── css │ ├── animations.scss │ ├── comments.scss │ ├── dashboard.scss │ ├── labels.scss │ ├── markdown.scss │ ├── selector.scss │ └── variables.scss ├── directives │ └── focus.js ├── helpers │ ├── applyOrderToArray.js │ ├── errors.js │ ├── mentions.js │ ├── selector.js │ └── xml.js ├── init-calendar.js ├── init-collections.js ├── init-dashboard.js ├── init-reference.js ├── init-talk.js ├── main.js ├── mixins │ ├── attachmentUpload.js │ ├── color.js │ ├── isTouchDevice.js │ ├── labelStyle.js │ ├── readableDate.js │ └── relativeDate.js ├── models │ └── index.js ├── router.js ├── services │ ├── AttachmentApi.js │ ├── BoardApi.js │ ├── CardApi.js │ ├── CommentApi.js │ ├── OverviewApi.js │ ├── SessionApi.js │ ├── SharingApi.js │ └── StackApi.js ├── sessions.js ├── shared-init.js ├── store │ ├── actions.js │ ├── attachment.js │ ├── card.js │ ├── comment.js │ ├── dashboard.js │ ├── main.js │ ├── overview.js │ ├── stack.js │ └── trashbin.js └── views │ ├── BoardReferenceWidget.vue │ ├── CardReferenceWidget.vue │ ├── CommentReferenceWidget.vue │ ├── CreateNewCardCustomPicker.vue │ ├── DashboardToday.vue │ ├── DashboardTomorrow.vue │ ├── DashboardUpcoming.vue │ └── FileSharingPicker.js ├── stylelint.config.js ├── templates └── main.php ├── tests ├── bootstrap.php ├── data │ ├── config-deckJson.json │ ├── config-trelloJson.json │ ├── data-trelloJson.json │ ├── deck.json │ └── test.txt ├── integration │ ├── app │ │ └── AppTest.php │ ├── base-query-count.txt │ ├── composer.json │ ├── config │ │ └── behat.yml │ ├── database │ │ ├── AssignmentMapperTest.php │ │ ├── BoardDatabaseTest.php │ │ └── TransferOwnershipTest.php │ ├── features │ │ ├── acl.feature │ │ ├── bootstrap │ │ │ ├── AttachmentContext.php │ │ │ ├── BoardContext.php │ │ │ ├── CommentContext.php │ │ │ ├── RequestContext.php │ │ │ ├── RequestTrait.php │ │ │ ├── SearchContext.php │ │ │ ├── ServerContext.php │ │ │ └── SessionContext.php │ │ ├── decks.feature │ │ ├── search.feature │ │ ├── sessions.feature │ │ └── sharing.feature │ ├── import │ │ └── ImportExportTest.php │ └── run.sh ├── phpunit.integration.xml ├── phpunit.xml ├── psalm-baseline.xml ├── stub.phpstub └── unit │ ├── Activity │ ├── ActivityManagerTest.php │ ├── ChangeSetTest.php │ ├── DeckProviderTest.php │ ├── FilterTest.php │ └── SettingTest.php │ ├── Command │ ├── BoardImportTest.php │ └── UserExportTest.php │ ├── Cron │ ├── DeleteCronTest.php │ └── ScheduledNoificationsTest.php │ ├── Db │ ├── AclMapperTest.php │ ├── AclTest.php │ ├── AttachmentMapperTest.php │ ├── AttachmentTest.php │ ├── BoardMapperTest.php │ ├── BoardTest.php │ ├── CardTest.php │ ├── GroupTest.php │ ├── LabelTest.php │ ├── RelationalEntityTest.php │ ├── StackTest.php │ └── UserTest.php │ ├── ExceptionsTest.php │ ├── Listeners │ └── CommentEventListenerTest.php │ ├── Middleware │ └── ExceptionMiddlewareTest.php │ ├── Notification │ ├── NotificationHelperTest.php │ └── NotifierTest.php │ ├── Reference │ └── CardReferenceProviderTest.php │ ├── Search │ ├── FilterStringParserTest.php │ └── Query │ │ └── AQueryParameterTest.php │ ├── Service │ ├── AssignmentServiceTest.php │ ├── AttachmentServiceTest.php │ ├── BoardServiceTest.php │ ├── CardServiceTest.php │ ├── DefaultBoardServiceTest.php │ ├── FileServiceTest.php │ ├── Importer │ │ ├── BoardImportServiceTest.php │ │ └── Systems │ │ │ ├── DeckJsonServiceTest.php │ │ │ └── TrelloJsonServiceTest.php │ ├── LabelServiceTest.php │ ├── PermissionServiceTest.php │ └── StackServiceTest.php │ ├── Validators │ ├── StackServiceValidatorTest.php │ └── ValidatorTestBase.php │ └── controller │ ├── BoardApiControllerTest.php │ ├── BoardControllerTest.php │ ├── BoardImportApiControllerTest.php │ ├── CardApiControllerTest.php │ ├── CardControllerTest.php │ ├── LabelApiControllerTest.php │ ├── LabelControllerTest.php │ ├── PageControllerTest.php │ ├── StackApiControllerTest.php │ └── StackControllerTest.php └── webpack.js /.codecov.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: CC0-1.0 3 | codecov: 4 | notify: 5 | require_ci_to_pass: yes 6 | 7 | coverage: 8 | precision: 2 9 | round: down 10 | range: "70...100" 11 | 12 | status: 13 | project: yes 14 | patch: yes 15 | changes: no 16 | 17 | comment: 18 | layout: "header, diff, changes, sunburst, uncovered" 19 | behavior: default 20 | require_changes: no 21 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "ghcr.io/juliusknorr/nextcloud-dev-php81:latest", 3 | "forwardPorts": [80], 4 | "containerEnv": { 5 | "NEXTCLOUD_AUTOINSTALL_APPS": "deck", 6 | "XDEBUG_MODE": "debug" 7 | }, 8 | "customizations": { 9 | "vscode": { 10 | "extensions": [ 11 | "felixfbecker.php-intellisense", 12 | "octref.vetur" 13 | ], 14 | "settings": { 15 | "php.suggest.basic": false, 16 | "git.alwaysSignOff": true 17 | } 18 | } 19 | }, 20 | "workspaceMount": "source=${localWorkspaceFolder},target=/var/www/html/apps-extra/deck,type=bind", 21 | "workspaceFolder": "/var/www/html/apps-extra/deck", 22 | "overrideCommand": true, 23 | "postAttachCommand": "bash ./.devcontainer/setup.sh", 24 | "portsAttributes": { 25 | "80": { 26 | "label": "Webserver" 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /.devcontainer/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors 4 | # SPDX-License-Identifier: CC0-1.0 5 | 6 | ( 7 | cd /tmp && /usr/local/bin/bootstrap.sh apache2ctl start 8 | ) 9 | 10 | composer install --no-dev 11 | npm ci 12 | npm run dev -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = tab 7 | indent_style = tab 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.{js,vue}] 12 | indent_style = tab 13 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: CC0-1.0 3 | /js/tests/* 4 | /js/vendor/* 5 | /js/legacy/* 6 | /js/node_modules/* 7 | /js/public/* 8 | /karma.conf.js 9 | /tests/* 10 | /l10n/* 11 | 12 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | '@nextcloud', 5 | ], 6 | rules: { 7 | 'jsdoc/require-param-description': ['off'], 8 | 'jsdoc/require-param-type': ['off'], 9 | 'jsdoc/check-param-names': ['off'], 10 | 'jsdoc/no-undefined-types': ['off'], 11 | 'jsdoc/require-property-description': ['off'], 12 | 'import/no-named-as-default-member': ['off'], 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # App maintainers 2 | * @luka-nextcloud @grnd-alt @elzody 3 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | 2 | * Resolves: # 3 | * Target version: main 4 | 5 | ### Summary 6 | 7 | 8 | ### TODO 9 | 10 | - [ ] ... 11 | 12 | ### Checklist 13 | 14 | - [ ] Code is properly formatted 15 | - [ ] Sign-off message is added to all commits 16 | - [ ] Tests (unit, integration, api and/or acceptance) are included 17 | - [ ] Documentation (manuals or wiki) has been updated or is not required 18 | -------------------------------------------------------------------------------- /.github/workflows/appbuild.yml: -------------------------------------------------------------------------------- 1 | name: Package build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - master 8 | - stable* 9 | 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | 14 | strategy: 15 | matrix: 16 | node-version: [16.x] 17 | 18 | steps: 19 | - uses: actions/checkout@v4.2.2 20 | - name: Use Node.js ${{ matrix.node-version }} 21 | uses: actions/setup-node@v4.4.0 22 | with: 23 | node-version: ${{ matrix.node-version }} 24 | - name: Set up npm7 25 | run: npm i -g npm@7 26 | - name: Setup PHP 27 | uses: shivammathur/setup-php@2.33.0 28 | with: 29 | php-version: '7.4' 30 | tools: composer 31 | - name: install dependencies 32 | run: | 33 | wget https://github.com/ChristophWurst/krankerl/releases/download/v0.14.0/krankerl_0.14.0_amd64.deb 34 | sudo dpkg -i krankerl_0.14.0_amd64.deb 35 | - name: package 36 | run: | 37 | uname -a 38 | RUST_BACKTRACE=1 krankerl --version 39 | RUST_BACKTRACE=1 krankerl package 40 | - uses: actions/upload-artifact@v4 41 | with: 42 | name: Deck app tarball 43 | path: build/artifacts/deck.tar.gz 44 | -------------------------------------------------------------------------------- /.github/workflows/fixup.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | # 6 | # SPDX-FileCopyrightText: 2021-2024 Nextcloud GmbH and Nextcloud contributors 7 | # SPDX-License-Identifier: MIT 8 | 9 | name: Block fixup and squash commits 10 | 11 | on: 12 | pull_request: 13 | types: [opened, ready_for_review, reopened, synchronize] 14 | 15 | permissions: 16 | contents: read 17 | 18 | concurrency: 19 | group: fixup-${{ github.head_ref || github.run_id }} 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | commit-message-check: 24 | if: github.event.pull_request.draft == false 25 | 26 | permissions: 27 | pull-requests: write 28 | name: Block fixup and squash commits 29 | 30 | runs-on: ubuntu-latest-low 31 | 32 | steps: 33 | - name: Run check 34 | uses: skjnldsv/block-fixup-merge-action@c138ea99e45e186567b64cf065ce90f7158c236a # v2 35 | with: 36 | repo-token: ${{ secrets.GITHUB_TOKEN }} 37 | -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [14.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v4.2.2 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v4.4.0 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: Set up npm7 21 | run: npm i -g npm@7 22 | - name: install dependencies 23 | run: | 24 | npm ci 25 | - name: build 26 | env: 27 | RELATIVE_CI_KEY: ${{ secrets.RELATIVE_CI_KEY }} 28 | RELATIVE_CI_SLUG: nextcloud/deck 29 | run: | 30 | mkdir -p js 31 | npm run build --if-present -- --profile --json | tail -n +6 > js/webpack-stats.json 32 | npx relative-ci-agent 33 | 34 | 35 | -------------------------------------------------------------------------------- /.github/workflows/reuse.yml: -------------------------------------------------------------------------------- 1 | # This workflow is provided via the organization template repository 2 | # 3 | # https://github.com/nextcloud/.github 4 | # https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization 5 | 6 | # SPDX-FileCopyrightText: 2022 Free Software Foundation Europe e.V. 7 | # 8 | # SPDX-License-Identifier: CC0-1.0 9 | 10 | name: REUSE Compliance Check 11 | 12 | on: [pull_request] 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | reuse-compliance-check: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 23 | with: 24 | persist-credentials: false 25 | 26 | - name: REUSE Compliance Check 27 | uses: fsfe/reuse-action@bb774aa972c2a89ff34781233d275075cbddf542 # v5.0.0 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | js/ 3 | build/ 4 | css/style.css 5 | css/vendor.css 6 | cypress/videos/ 7 | tests/integration/vendor/ 8 | tests/integration/composer.lock 9 | tests/.phpunit.result.cache 10 | vendor/ 11 | .php_cs.cache 12 | \.idea/ 13 | settings.json 14 | -------------------------------------------------------------------------------- /.nextcloudignore: -------------------------------------------------------------------------------- 1 | /build/ 2 | /.git 3 | /.github 4 | /docs/ 5 | /tests 6 | /babel.config.js 7 | /.editorconfig 8 | /.eslintrc.js 9 | /.nextcloudignore 10 | /webpack.js 11 | /.codecov.yml 12 | /composer.json 13 | /composer.lock 14 | /_config.yml 15 | /.drone.yml 16 | /.travis.yml 17 | /.eslintignore 18 | /.eslintrc.yml 19 | /.gitignore 20 | /issue_template.md 21 | /krankerl.toml 22 | /Makefile 23 | /mkdocs.yml 24 | /run-eslint.sh 25 | /package.json 26 | /package-lock.json 27 | /node_modules/ 28 | /src/ 29 | /cypress/ 30 | /cypress.config.js 31 | /.devcontainer/ 32 | /.php-cs-fixer.dist.php 33 | /psalm.xml 34 | /relativeci.config.js 35 | /stylelint.config.js 36 | -------------------------------------------------------------------------------- /.php-cs-fixer.dist.php: -------------------------------------------------------------------------------- 1 | getFinder() 12 | // ->ignoreVCSIgnored(true) 13 | ->notPath('build') 14 | ->notPath('l10n') 15 | ->notPath('src') 16 | ->notPath('node_modules') 17 | ->notPath('vendor') 18 | ->in(__DIR__); 19 | return $config; 20 | -------------------------------------------------------------------------------- /.tx/config: -------------------------------------------------------------------------------- 1 | [main] 2 | host = https://www.transifex.com 3 | lang_map = hu_HU: hu, nb_NO: nb, sk_SK: sk, th_TH: th, ja_JP: ja, bg_BG: bg, cs_CZ: cs, fi_FI: fi 4 | 5 | [o:nextcloud:p:nextcloud:r:deck] 6 | file_filter = translationfiles//deck.po 7 | source_file = translationfiles/templates/deck.pot 8 | source_lang = en 9 | type = PO 10 | 11 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | 5 | # Authors 6 | 7 | - Adrian Missy 8 | - Alexandru Puiu 9 | - Chandi Langecker 10 | - Christoph Wurst 11 | - Gary Kim 12 | - Georg Ehrke 13 | - Jakob Röhrl 14 | - Johannes Szeibert 15 | - John Molakvoæ 16 | - Julien Veyssier 17 | - Julius Härtl 18 | - Luka Trovic 19 | - Maxence Lange 20 | - Michael Weimann 21 | - Raul Ferreira Fuentes 22 | - Ryan Fletcher 23 | - Steven R. Baker 24 | - Thanos kamber 25 | - Vitor Mattos 26 | -------------------------------------------------------------------------------- /LICENSES/MIT.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 4 | 5 | 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: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | 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. 10 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 5 | # Security Policy 6 | 7 | ## Supported Versions 8 | 9 | | Version | Nextcloud version | Supported | 10 | | ------- | ----------------- | ------------------ | 11 | | 1.0.x | 18, 19 | :white_check_mark: | 12 | | 0.8.x | 17 | :white_check_mark: | 13 | | <0.7.x | - | :x: | 14 | 15 | 16 | ## Reporting a Vulnerability 17 | 18 | Security is very important to us. If you have discovered a security issue with Nextcloud, 19 | please read our responsible disclosure guidelines and contact us at [hackerone.com/nextcloud](https://hackerone.com/nextcloud). 20 | Your report should include: 21 | 22 | - Product version 23 | - A vulnerability description 24 | - Reproduction steps 25 | 26 | A member of the security team will confirm the vulnerability, determine its impact, and develop a fix. 27 | The fix will be applied to the main branch, tested, and packaged in the next security release. 28 | The vulnerability will be publicly announced after the release. Finally, your name will be added 29 | to the [hall of fame](https://hackerone.com/nextcloud/thanks) as a thank you from the entire Nextcloud community. Note our 30 | [threat model](https://nextcloud.com/security/threat-model) to know what is expected behavior. 31 | 32 | 33 | Please visit https://nextcloud.com/security/ for further information about security. 34 | -------------------------------------------------------------------------------- /appinfo/autoload.php: -------------------------------------------------------------------------------- 1 | 2 | // *********************************************************** 3 | // This example plugins/index.js can be used to load plugins 4 | // 5 | // You can change the location of this file or turn off loading 6 | // the plugins file with the 'pluginsFile' configuration option. 7 | // 8 | // You can read more here: 9 | // https://on.cypress.io/plugins-guide 10 | // *********************************************************** 11 | 12 | // This function is called when a project is opened or re-opened (e.g. due to 13 | // the project's config changing) 14 | 15 | /** 16 | * @type {Cypress.PluginConfig} 17 | */ 18 | // eslint-disable-next-line no-unused-vars 19 | module.exports = (on, config) => { 20 | // `on` is used to hook into various events Cypress emits 21 | // `config` is the resolved Cypress config 22 | } 23 | -------------------------------------------------------------------------------- /cypress/support/component-index.html: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | Components App 12 | 13 | 14 |
15 | 16 | -------------------------------------------------------------------------------- /cypress/support/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | // *********************************************************** 6 | // This example support/component.js is processed and 7 | // loaded automatically before your test files. 8 | // 9 | // This is a great place to put global configuration and 10 | // behavior that modifies Cypress. 11 | // 12 | // You can change the location of this file or turn off 13 | // automatically serving support files with the 14 | // 'supportFile' configuration option. 15 | // 16 | // You can read more here: 17 | // https://on.cypress.io/configuration 18 | // *********************************************************** 19 | 20 | // Import commands.js using ES2015 syntax: 21 | import './commands' 22 | 23 | // Alternatively you can use CommonJS syntax: 24 | // require('./commands') 25 | 26 | import { mount } from 'cypress/vue2' 27 | 28 | Cypress.Commands.add('mount', mount) 29 | 30 | // Example use: 31 | // cy.mount(MyComponent) 32 | -------------------------------------------------------------------------------- /cypress/support/e2e.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | // *********************************************************** 6 | // This example support/index.js is processed and 7 | // loaded automatically before your test files. 8 | // 9 | // This is a great place to put global configuration and 10 | // behavior that modifies Cypress. 11 | // 12 | // You can change the location of this file or turn off 13 | // automatically serving support files with the 14 | // 'supportFile' configuration option. 15 | // 16 | // You can read more here: 17 | // https://on.cypress.io/configuration 18 | // *********************************************************** 19 | 20 | // Import commands.js using ES2015 syntax: 21 | import './commands.js' 22 | 23 | Cypress.on('uncaught:exception', (err) => { 24 | return !err.message.includes('ResizeObserver loop limit exceeded') && !err.message.includes('ResizeObserver loop completed with undelivered notifications') 25 | }) 26 | 27 | // Alternatively you can use CommonJS syntax: 28 | // require('./commands') 29 | -------------------------------------------------------------------------------- /cypress/utils/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { User } from '@nextcloud/cypress' 6 | 7 | export const randHash = () => Math.random().toString(36).replace(/[^a-z]+/g, '').slice(0, 10) 8 | export const randUser = () => new User(randHash(), randHash()) 9 | -------------------------------------------------------------------------------- /cypress/utils/sampleBoard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | export const sampleBoard = (title = 'MyTestBoard') => { 7 | return { 8 | title: title, 9 | color: '00ff00', 10 | stacks: [ 11 | { 12 | title: 'TestList', 13 | cards: [ 14 | { 15 | title: 'Hello world', 16 | description: '# Hello world', 17 | }, 18 | ], 19 | }, 20 | ], 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/Release.md: -------------------------------------------------------------------------------- 1 | 5 | Releasing a new version works quite easy with [krankerl](https://github.com/ChristophWurst/krankerl) and [github-release](https://github.com/aktau/github-release) installed: 6 | 7 | 1. Run krankerl to build the package 8 | 9 | ``` 10 | krankerl package 11 | ``` 12 | 13 | 2. Tag the release on GitHub 14 | 15 | ``` 16 | # For a prerelease 17 | github-release release -u nextcloud -r deck -t v0.3.1 -p 18 | 19 | # For a regular release 20 | github-release release -u nextcloud -r deck -t v0.3.1 21 | ``` 22 | 23 | 3. Upload the release package to GitHub 24 | 25 | ``` 26 | github-release upload -u nextcloud -r deck -t v0.3.1 -n deck.tar.gz -f build/artifacts/deck.tar.gz 27 | ``` 28 | 29 | 4. Run krankerl to release the package to the app store (add `--nightly` for prerelease packages) 30 | 31 | ``` 32 | krankerl publish https://github.com/nextcloud/deck/releases/download/v0.3.1/deck.tar.gz 33 | ``` 34 | 35 | ## Release PR template 36 | 37 | ``` 38 | ## Backports 39 | 40 | - [ ] ... 41 | 42 | ## Translations 43 | 44 | - [ ] ... 45 | 46 | ## Release 47 | 48 | - [ ] Set proper Nextcloud versions in info.xml 49 | - [ ] Update changelog 50 | - [ ] Build test release 51 | - [ ] Tested on 52 | - [ ] Nextcloud 13 53 | - [ ] Nextcloud 14 54 | - [ ] Nextcloud 15 55 | - [ ] Merge 56 | - [ ] Build final release 57 | - [ ] Publish release 58 | - [ ] Upload to the app store 59 | ``` 60 | -------------------------------------------------------------------------------- /docs/extra.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | .subnav ul { 6 | padding-left: 20px; 7 | } 8 | -------------------------------------------------------------------------------- /docs/implement-import.md: -------------------------------------------------------------------------------- 1 | 5 | ## Implement import 6 | 7 | * Create a new importer class extending `ABoardImportService` 8 | * Create a listener for event `BoardImportGetAllowedEvent` to enable your importer. 9 | > You can read more about listeners on [Nextcloud](https://docs.nextcloud.com/server/latest/developer_manual/basics/events.html?highlight=event#writing-a-listener) doc. 10 | 11 | Example: 12 | 13 | ```php 14 | class YourCustomImporterListener { 15 | public function handle(Event $event): void { 16 | if (!($event instanceof BoardImportGetAllowedEvent)) { 17 | return; 18 | } 19 | 20 | $event->getService()->addAllowedImportSystem([ 21 | 'name' => YourCustomImporterService::$name, 22 | 'class' => YourCustomImporterService::class, 23 | 'internalName' => 'YourCustomImporter' 24 | ]); 25 | } 26 | } 27 | ``` 28 | * Register your listener on your `Application` class like this: 29 | ```php 30 | $dispatcher = $this->getContainer()->query(IEventDispatcher::class); 31 | $dispatcher->registerEventListener( 32 | BoardImportGetAllowedEvent::class, 33 | YourCustomImporterListener::class 34 | ); 35 | ``` 36 | * Use the `lib/Service/Importer/Systems/TrelloJsonService.php` class as inspiration 37 | -------------------------------------------------------------------------------- /docs/import-class-diagram.md: -------------------------------------------------------------------------------- 1 | 5 | ## Import class diagram 6 | 7 | Importing boards to the Deck implements the class diagram below. 8 | 9 | > **NOTE**: When making any changes to the structure of the classes or implementing import from other sources, edit the `BoardImport.yuml` file 10 | 11 | ![Screenshot](resources/BoardImport.svg) -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /docs/index.md.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later 3 | -------------------------------------------------------------------------------- /docs/resources/BoardImport.svg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/BoardImport.yuml: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors 2 | // SPDX-License-Identifier: AGPL-3.0-or-later 3 | // Created using [yUML](https://github.com/jaime-olivares/vscode-yuml) 4 | 5 | // {type:class} 6 | // {direction:topDown} 7 | // {generate:true} 8 | 9 | [note: Classes used on board import. Methods just to illustrate. {bg:cornsilk}] 10 | 11 | [ApiController]<-[BoardImportApiController|+import();+getAllowedSystems();+getConfigSchema()] 12 | [BoardImportApiController]uses-.->[BoardImportService|+import();+bootstrap();+validateSystem();#validateConfig();#validateData();] 13 | 14 | [Command]<-[BoardImport|+boardImportCommandService|#configure();#execute(input,output)] 15 | [BoardImport]uses-.->[BoardImportCommandService|+bootstrap();+import();+validateSystem();#validateConfig();#validateData()] 16 | [BoardImportCommandService]->[BoardImportService] 17 | 18 | [BoardImportService]uses-.->[TrelloApiService|+name:string] 19 | [TrelloApiService]uses-.->[BoardImportService] 20 | [TrelloApiService]implements-.-^[<> ABoardImportService|#needValidateData:false|+needValidateData():bool] 21 | 22 | [BoardImportService]uses-.->[TrelloJsonService|+name:string;#needValidateData:true] 23 | [TrelloJsonService]uses-.->[BoardImportService] 24 | [BoardImportService]-[note: validateSystem is public because is used on Api. {bg:cornsilk}] 25 | [TrelloJsonService]-[note: To create an import to another system, create another class similar to this. {bg:cornsilk}] 26 | [TrelloJsonService]implements-.-^[<> ABoardImportService] 27 | -------------------------------------------------------------------------------- /docs/resources/er-diagram.dia: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/er-diagram.dia -------------------------------------------------------------------------------- /docs/resources/er-diagram.dia.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/er-diagram.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/er-diagram.jpg -------------------------------------------------------------------------------- /docs/resources/er-diagram.jpg.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/gifs/EN_archive.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/gifs/EN_archive.gif -------------------------------------------------------------------------------- /docs/resources/gifs/EN_archive.gif.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/gifs/EN_create_board.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/gifs/EN_create_board.gif -------------------------------------------------------------------------------- /docs/resources/gifs/EN_create_board.gif.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/gifs/EN_create_columns.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/gifs/EN_create_columns.gif -------------------------------------------------------------------------------- /docs/resources/gifs/EN_create_columns.gif.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/gifs/EN_create_task.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/gifs/EN_create_task.gif -------------------------------------------------------------------------------- /docs/resources/gifs/EN_create_task.gif.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/gifs/EN_done.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/gifs/EN_done.gif -------------------------------------------------------------------------------- /docs/resources/gifs/EN_done.gif.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/gifs/EN_put_infos.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/gifs/EN_put_infos.gif -------------------------------------------------------------------------------- /docs/resources/gifs/EN_put_infos.gif.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/resources/gifs/EN_put_infos_2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/docs/resources/gifs/EN_put_infos_2.gif -------------------------------------------------------------------------------- /docs/resources/gifs/EN_put_infos_2.gif.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors 2 | SPDX-License-Identifier: AGPL-3.0-or-later -------------------------------------------------------------------------------- /docs/structure.md: -------------------------------------------------------------------------------- 1 | 5 | ## Database structure 6 | 7 | Deck stores most of its data inside of the database. The structure and relationships between entities is documented in the following ER diagram: 8 | 9 | ![Screenshot](resources/er-diagram.jpg) 10 | 11 | -------------------------------------------------------------------------------- /img/activity-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /img/activity.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /img/add-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/archive.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/attach.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/calendar-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/calendar-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/card.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /img/circles-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/circles.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/color_picker-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/color_picker.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/deck-current.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /img/deck-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /img/deck.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /img/description.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /img/details-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/details.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/img/favicon.ico -------------------------------------------------------------------------------- /img/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/img/favicon.png -------------------------------------------------------------------------------- /img/filter.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/filter_set.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/notifications-dark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/sample-image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/img/sample-image.jpg -------------------------------------------------------------------------------- /img/toggle-view-collapse.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /img/toggle-view-expand.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /krankerl.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | before_cmds = [ 3 | 'make release' 4 | ] 5 | -------------------------------------------------------------------------------- /l10n/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nextcloud/deck/ff74bfbf13650bc8771beb9a6438a0c1763229d0/l10n/.gitkeep -------------------------------------------------------------------------------- /l10n/bn_BD.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "আপলোড করা ফাইলটি HTML ফর্মে উল্লিখিত MAX_FILE_SIZE নির্ধারিত ফাইলের সর্বোচ্চ আকার অতিক্রম করতে চলেছে ", 5 | "No file was uploaded" : "কোন ফাইল আপলোড করা হয় নি", 6 | "Missing a temporary folder" : "অস্থায়ী ফোল্ডারটি হারানো গিয়েছে", 7 | "Done" : "Done", 8 | "Cancel" : "বাতির", 9 | "Completed" : "সুসম্পন্ন", 10 | "Details" : "বিসতারিত", 11 | "Sharing" : "ভাগাভাগিরত", 12 | "Tags" : "ট্যাগ", 13 | "Activity" : "সক্রিয়তা", 14 | "Can edit" : "Can edit", 15 | "Can share" : "Can share", 16 | "Owner" : "Owner", 17 | "Delete" : "মুছে", 18 | "Edit" : "সম্পাদনা", 19 | "Download" : "ডাউনলোড", 20 | "Modified" : "পরিবর্তিত", 21 | "Save" : "সংরক্ষণ", 22 | "Created:" : "তৈরীর নির্ঘন্টঃ", 23 | "Reply" : "জবাব", 24 | "Update" : "পরিবর্ধন", 25 | "Description" : "বিবরণ", 26 | "(group)" : "(গোষ্ঠি)", 27 | "seconds ago" : "সেকেন্ড পূর্বে", 28 | "Keyboard shortcuts" : "কী-বোর্ড শর্টকাট", 29 | "Search" : "Search", 30 | "Shared with you" : "Shared with you", 31 | "Export" : "রপ্তানি", 32 | "Today" : "আজ", 33 | "Tomorrow" : "আগামীকাল", 34 | "Close" : "বন্ধ", 35 | "Share" : "ভাগাভাগি কর", 36 | "Personal" : "ব্যক্তিগত" 37 | }, 38 | "nplurals=2; plural=(n != 1);"); 39 | -------------------------------------------------------------------------------- /l10n/bn_BD.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "আপলোড করা ফাইলটি HTML ফর্মে উল্লিখিত MAX_FILE_SIZE নির্ধারিত ফাইলের সর্বোচ্চ আকার অতিক্রম করতে চলেছে ", 3 | "No file was uploaded" : "কোন ফাইল আপলোড করা হয় নি", 4 | "Missing a temporary folder" : "অস্থায়ী ফোল্ডারটি হারানো গিয়েছে", 5 | "Done" : "Done", 6 | "Cancel" : "বাতির", 7 | "Completed" : "সুসম্পন্ন", 8 | "Details" : "বিসতারিত", 9 | "Sharing" : "ভাগাভাগিরত", 10 | "Tags" : "ট্যাগ", 11 | "Activity" : "সক্রিয়তা", 12 | "Can edit" : "Can edit", 13 | "Can share" : "Can share", 14 | "Owner" : "Owner", 15 | "Delete" : "মুছে", 16 | "Edit" : "সম্পাদনা", 17 | "Download" : "ডাউনলোড", 18 | "Modified" : "পরিবর্তিত", 19 | "Save" : "সংরক্ষণ", 20 | "Created:" : "তৈরীর নির্ঘন্টঃ", 21 | "Reply" : "জবাব", 22 | "Update" : "পরিবর্ধন", 23 | "Description" : "বিবরণ", 24 | "(group)" : "(গোষ্ঠি)", 25 | "seconds ago" : "সেকেন্ড পূর্বে", 26 | "Keyboard shortcuts" : "কী-বোর্ড শর্টকাট", 27 | "Search" : "Search", 28 | "Shared with you" : "Shared with you", 29 | "Export" : "রপ্তানি", 30 | "Today" : "আজ", 31 | "Tomorrow" : "আগামীকাল", 32 | "Close" : "বন্ধ", 33 | "Share" : "ভাগাভাগি কর", 34 | "Personal" : "ব্যক্তিগত" 35 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 36 | } -------------------------------------------------------------------------------- /l10n/bs.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Učitana datoteka premašuje maksimalnu dopuštenu veličinu datoteke MAX_FILE_SIZE navedenu u HTML formi", 5 | "No file was uploaded" : "Nijedna datoteka nije učitana.", 6 | "Missing a temporary folder" : "Nedostaje privremeni direktorij.", 7 | "Done" : "Done", 8 | "Cancel" : "Otkaži", 9 | "Completed" : "Zavrženo", 10 | "Sharing" : "Dijeljenje", 11 | "Activity" : "Aktivnost", 12 | "Can edit" : "Can edit", 13 | "Can share" : "Can share", 14 | "Owner" : "Vlasnik", 15 | "Delete" : "Obriši", 16 | "Edit" : "Izmjeni", 17 | "Members" : "Članovi", 18 | "Download" : "Preuzmi", 19 | "Modified" : "Izmijenjeno", 20 | "Comments" : "Komentari", 21 | "Save" : "Spremi", 22 | "Update" : "Ažuriraj", 23 | "Description" : "Opis", 24 | "Keyboard shortcuts" : "Tipkovni prečaci", 25 | "Search" : "Search", 26 | "Shared with you" : "Shared with you", 27 | "Export" : "Izvezi", 28 | "Today" : "Danas", 29 | "Tomorrow" : "Sutra", 30 | "Close" : "Zatvori", 31 | "Maximum file size of {size} exceeded" : "Maksimalna veličina datoteke prekoračena", 32 | "Share" : "Podjeli", 33 | "Personal" : "Osobno" 34 | }, 35 | "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);"); 36 | -------------------------------------------------------------------------------- /l10n/bs.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Učitana datoteka premašuje maksimalnu dopuštenu veličinu datoteke MAX_FILE_SIZE navedenu u HTML formi", 3 | "No file was uploaded" : "Nijedna datoteka nije učitana.", 4 | "Missing a temporary folder" : "Nedostaje privremeni direktorij.", 5 | "Done" : "Done", 6 | "Cancel" : "Otkaži", 7 | "Completed" : "Zavrženo", 8 | "Sharing" : "Dijeljenje", 9 | "Activity" : "Aktivnost", 10 | "Can edit" : "Can edit", 11 | "Can share" : "Can share", 12 | "Owner" : "Vlasnik", 13 | "Delete" : "Obriši", 14 | "Edit" : "Izmjeni", 15 | "Members" : "Članovi", 16 | "Download" : "Preuzmi", 17 | "Modified" : "Izmijenjeno", 18 | "Comments" : "Komentari", 19 | "Save" : "Spremi", 20 | "Update" : "Ažuriraj", 21 | "Description" : "Opis", 22 | "Keyboard shortcuts" : "Tipkovni prečaci", 23 | "Search" : "Search", 24 | "Shared with you" : "Shared with you", 25 | "Export" : "Izvezi", 26 | "Today" : "Danas", 27 | "Tomorrow" : "Sutra", 28 | "Close" : "Zatvori", 29 | "Maximum file size of {size} exceeded" : "Maksimalna veličina datoteke prekoračena", 30 | "Share" : "Podjeli", 31 | "Personal" : "Osobno" 32 | },"pluralForm" :"nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" 33 | } -------------------------------------------------------------------------------- /l10n/cy_GB.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Mae'r ffeil lwythwyd i fyny'n fwy na chyfarwyddeb MAX_FILE_SIZE bennwyd yn y ffurflen HTML", 5 | "No file was uploaded" : "Ni lwythwyd ffeil i fyny", 6 | "Missing a temporary folder" : "Plygell dros dro yn eisiau", 7 | "%s on %s" : "%s ar %s", 8 | "Done" : "Done", 9 | "Cancel" : "Diddymu", 10 | "Open" : "Ar Agor", 11 | "Details" : "Manylion", 12 | "Tags" : "Tagiau", 13 | "Activity" : "Gweithred", 14 | "Undo" : "Dadwneud", 15 | "Can edit" : "Can edit", 16 | "Can share" : "Can share", 17 | "Owner" : "Owner", 18 | "Delete" : "Dileu", 19 | "Edit" : "Golygu", 20 | "Download" : "Llwytho i lawr", 21 | "Modified" : "Addaswyd", 22 | "Save" : "Cadw", 23 | "Created:" : "Crewyd:", 24 | "Update" : "Diweddaru", 25 | "Description" : "Disgrifiad", 26 | "Select Date" : "Dewis Dyddiad", 27 | "seconds ago" : "eiliad yn ôl", 28 | "Keyboard shortcuts" : "Llwybrau byr bysellfwrdd", 29 | "Search" : "Chwilio", 30 | "Shared with you" : "Shared with you", 31 | "No reminder" : "Dim nodyn atgoffa", 32 | "An error occurred" : "Digwyddodd gwall", 33 | "Export" : "Allforio", 34 | "Today" : "Heddiw", 35 | "Close" : "Cau", 36 | "Share" : "Rhannu", 37 | "Personal" : "Personol" 38 | }, 39 | "nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;"); 40 | -------------------------------------------------------------------------------- /l10n/cy_GB.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Mae'r ffeil lwythwyd i fyny'n fwy na chyfarwyddeb MAX_FILE_SIZE bennwyd yn y ffurflen HTML", 3 | "No file was uploaded" : "Ni lwythwyd ffeil i fyny", 4 | "Missing a temporary folder" : "Plygell dros dro yn eisiau", 5 | "%s on %s" : "%s ar %s", 6 | "Done" : "Done", 7 | "Cancel" : "Diddymu", 8 | "Open" : "Ar Agor", 9 | "Details" : "Manylion", 10 | "Tags" : "Tagiau", 11 | "Activity" : "Gweithred", 12 | "Undo" : "Dadwneud", 13 | "Can edit" : "Can edit", 14 | "Can share" : "Can share", 15 | "Owner" : "Owner", 16 | "Delete" : "Dileu", 17 | "Edit" : "Golygu", 18 | "Download" : "Llwytho i lawr", 19 | "Modified" : "Addaswyd", 20 | "Save" : "Cadw", 21 | "Created:" : "Crewyd:", 22 | "Update" : "Diweddaru", 23 | "Description" : "Disgrifiad", 24 | "Select Date" : "Dewis Dyddiad", 25 | "seconds ago" : "eiliad yn ôl", 26 | "Keyboard shortcuts" : "Llwybrau byr bysellfwrdd", 27 | "Search" : "Chwilio", 28 | "Shared with you" : "Shared with you", 29 | "No reminder" : "Dim nodyn atgoffa", 30 | "An error occurred" : "Digwyddodd gwall", 31 | "Export" : "Allforio", 32 | "Today" : "Heddiw", 33 | "Close" : "Cau", 34 | "Share" : "Rhannu", 35 | "Personal" : "Personol" 36 | },"pluralForm" :"nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n != 8 && n != 11) ? 2 : 3;" 37 | } -------------------------------------------------------------------------------- /l10n/gd.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The file was uploaded" : "Chaidh am faidhle a luchdadh suas", 5 | "The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Tha am faidhle a luchdaich thu suas a’ dol thairis air an riaghailt upload_max_filesize ann am php.ini", 6 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Tha am faidhle a luchdaich thu suas a’ dol thairis air an riaghailt MAX_FILE_SIZE a chaidh a shònrachadh san fhoirm HTML", 7 | "The file was only partially uploaded" : "Cha deach ach pàirt dhen fhaidhle a luchdadh suas", 8 | "No file was uploaded" : "Cha deach faidhle sam bith a luchdadh suas", 9 | "Missing a temporary folder" : "Tha pasgan sealach a dhìth", 10 | "Could not write file to disk" : "Cha b’ urrainn dhuinn am faidhle a sgrìobhadh dhan diosg", 11 | "A PHP extension stopped the file upload" : "Chur leudachan PHP stad air luchdadh suas an fhaidhle", 12 | "Finished" : "Deiseil", 13 | "Cancel" : "Sguir dheth", 14 | "Completed" : "Coileanta", 15 | "Details" : "Mion-fhiosrachadh", 16 | "Sharing" : "Co-roinneadh", 17 | "Tags" : "Tagaichean", 18 | "Activity" : "Gnìomhachd", 19 | "Undo" : "Neo-dhèan", 20 | "Transfer" : "Tar-chuir", 21 | "Delete" : "Sguab às", 22 | "Edit" : "Deasaich", 23 | "Download" : "Luchdaich a-nuas", 24 | "Save" : "Sàbhail", 25 | "seconds ago" : "diog air ais", 26 | "Search" : "Lorg", 27 | "No notifications" : "Gun bhrath", 28 | "Today" : "An-diugh", 29 | "Close" : "Dùin", 30 | "Share" : "Co-roinn" 31 | }, 32 | "nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;"); 33 | -------------------------------------------------------------------------------- /l10n/gd.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The file was uploaded" : "Chaidh am faidhle a luchdadh suas", 3 | "The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Tha am faidhle a luchdaich thu suas a’ dol thairis air an riaghailt upload_max_filesize ann am php.ini", 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Tha am faidhle a luchdaich thu suas a’ dol thairis air an riaghailt MAX_FILE_SIZE a chaidh a shònrachadh san fhoirm HTML", 5 | "The file was only partially uploaded" : "Cha deach ach pàirt dhen fhaidhle a luchdadh suas", 6 | "No file was uploaded" : "Cha deach faidhle sam bith a luchdadh suas", 7 | "Missing a temporary folder" : "Tha pasgan sealach a dhìth", 8 | "Could not write file to disk" : "Cha b’ urrainn dhuinn am faidhle a sgrìobhadh dhan diosg", 9 | "A PHP extension stopped the file upload" : "Chur leudachan PHP stad air luchdadh suas an fhaidhle", 10 | "Finished" : "Deiseil", 11 | "Cancel" : "Sguir dheth", 12 | "Completed" : "Coileanta", 13 | "Details" : "Mion-fhiosrachadh", 14 | "Sharing" : "Co-roinneadh", 15 | "Tags" : "Tagaichean", 16 | "Activity" : "Gnìomhachd", 17 | "Undo" : "Neo-dhèan", 18 | "Transfer" : "Tar-chuir", 19 | "Delete" : "Sguab às", 20 | "Edit" : "Deasaich", 21 | "Download" : "Luchdaich a-nuas", 22 | "Save" : "Sàbhail", 23 | "seconds ago" : "diog air ais", 24 | "Search" : "Lorg", 25 | "No notifications" : "Gun bhrath", 26 | "Today" : "An-diugh", 27 | "Close" : "Dùin", 28 | "Share" : "Co-roinn" 29 | },"pluralForm" :"nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;" 30 | } -------------------------------------------------------------------------------- /l10n/hy.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The file was uploaded" : "Նիշքը վերբերռնված է", 5 | "The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Վերբեռնած նիշքը գերազանցում է upload_max_filesize սահմանված php.ini֊ում", 6 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Վերբեռնած նիշքը գերազանցում է MAX_FILE_SIZE, որը սահմանված է HTML ձևաթղթում", 7 | "The file was only partially uploaded" : "Նիշքի մի մասն է վերբեռնված", 8 | "No file was uploaded" : "Ոչ մի նիշք չի վերնբեռնվել", 9 | "Missing a temporary folder" : "Բացակայում է ժամանակավոր պանակը", 10 | "Could not write file to disk" : "Չհաջողվեց գրառել նիշքը սկավառակի վրա", 11 | "A PHP extension stopped the file upload" : "PHP֊ի ընդլայնումն կանգնեցրեց նիշքի վերբեռնումը", 12 | "Done" : "Done", 13 | "Cancel" : "ընդհատել", 14 | "Completed" : "Ավարտվեց", 15 | "Details" : "Մանրամասներ", 16 | "Activity" : "Գործունեություն", 17 | "Can edit" : "Can edit", 18 | "Can share" : "Can share", 19 | "Owner" : "Owner", 20 | "Delete" : "հեռացնել", 21 | "Edit" : "մշակել", 22 | "Download" : "Ներբեռնել", 23 | "Modified" : "Փոփոխված", 24 | "Comments" : "Կարծիքներ", 25 | "Save" : "Պահպանել", 26 | "Created:" : "Ստեղծված.", 27 | "Update" : "Թարմացնել", 28 | "Description" : "Նկարագրություն", 29 | "seconds ago" : "վրկ. առաջ", 30 | "Search" : "Search", 31 | "Shared with you" : "Shared with you", 32 | "Export" : "Արտահանում", 33 | "Today" : "այսօր", 34 | "Close" : "Փակել", 35 | "Share" : "Կիսվել", 36 | "Personal" : "Անձնական" 37 | }, 38 | "nplurals=2; plural=(n != 1);"); 39 | -------------------------------------------------------------------------------- /l10n/hy.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The file was uploaded" : "Նիշքը վերբերռնված է", 3 | "The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Վերբեռնած նիշքը գերազանցում է upload_max_filesize սահմանված php.ini֊ում", 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Վերբեռնած նիշքը գերազանցում է MAX_FILE_SIZE, որը սահմանված է HTML ձևաթղթում", 5 | "The file was only partially uploaded" : "Նիշքի մի մասն է վերբեռնված", 6 | "No file was uploaded" : "Ոչ մի նիշք չի վերնբեռնվել", 7 | "Missing a temporary folder" : "Բացակայում է ժամանակավոր պանակը", 8 | "Could not write file to disk" : "Չհաջողվեց գրառել նիշքը սկավառակի վրա", 9 | "A PHP extension stopped the file upload" : "PHP֊ի ընդլայնումն կանգնեցրեց նիշքի վերբեռնումը", 10 | "Done" : "Done", 11 | "Cancel" : "ընդհատել", 12 | "Completed" : "Ավարտվեց", 13 | "Details" : "Մանրամասներ", 14 | "Activity" : "Գործունեություն", 15 | "Can edit" : "Can edit", 16 | "Can share" : "Can share", 17 | "Owner" : "Owner", 18 | "Delete" : "հեռացնել", 19 | "Edit" : "մշակել", 20 | "Download" : "Ներբեռնել", 21 | "Modified" : "Փոփոխված", 22 | "Comments" : "Կարծիքներ", 23 | "Save" : "Պահպանել", 24 | "Created:" : "Ստեղծված.", 25 | "Update" : "Թարմացնել", 26 | "Description" : "Նկարագրություն", 27 | "seconds ago" : "վրկ. առաջ", 28 | "Search" : "Search", 29 | "Shared with you" : "Shared with you", 30 | "Export" : "Արտահանում", 31 | "Today" : "այսօր", 32 | "Close" : "Փակել", 33 | "Share" : "Կիսվել", 34 | "Personal" : "Անձնական" 35 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 36 | } -------------------------------------------------------------------------------- /l10n/kab.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The file was uploaded" : "Ulac afaylu yettwaznen", 5 | "The file was only partially uploaded" : "Afaylu, cwiṭ kan i yettwaznen segs", 6 | "No file was uploaded" : "Ulac afaylu i d-yettwasulin", 7 | "Missing a temporary folder" : "Ixuṣ ukaram akudan", 8 | "Finished" : "Immed", 9 | "Done" : "Immed", 10 | "Cancel" : "Sefsex", 11 | "Open" : "Ldi", 12 | "Completed" : "Yemmed", 13 | "Details" : "Talqayt", 14 | "Sharing" : "Beṭṭu", 15 | "Tags" : "Tibzimin", 16 | "Activity" : "Armud", 17 | "Owner" : "Bab", 18 | "Delete" : "Kkes", 19 | "Edit" : "Ẓreg", 20 | "Download" : "Sider", 21 | "Modified" : "Yettwabeddel", 22 | "Created" : "Yettwarna.", 23 | "Attachments" : "Ticeqqufin", 24 | "Comments" : "Commentaires", 25 | "Save" : "Sekles", 26 | "Cancel reply" : "Semmet tiririt.", 27 | "Reply" : "Err", 28 | "Search" : "Nadi", 29 | "Cancel edit" : "Sefsex aseẓreg", 30 | "No notifications" : "Ulac tisezmal", 31 | "Today" : "Ass-a", 32 | "Close" : "Mdel", 33 | "Share" : "Bḍu", 34 | "Personal" : "Udmawan" 35 | }, 36 | "nplurals=2; plural=(n != 1);"); 37 | -------------------------------------------------------------------------------- /l10n/kab.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The file was uploaded" : "Ulac afaylu yettwaznen", 3 | "The file was only partially uploaded" : "Afaylu, cwiṭ kan i yettwaznen segs", 4 | "No file was uploaded" : "Ulac afaylu i d-yettwasulin", 5 | "Missing a temporary folder" : "Ixuṣ ukaram akudan", 6 | "Finished" : "Immed", 7 | "Done" : "Immed", 8 | "Cancel" : "Sefsex", 9 | "Open" : "Ldi", 10 | "Completed" : "Yemmed", 11 | "Details" : "Talqayt", 12 | "Sharing" : "Beṭṭu", 13 | "Tags" : "Tibzimin", 14 | "Activity" : "Armud", 15 | "Owner" : "Bab", 16 | "Delete" : "Kkes", 17 | "Edit" : "Ẓreg", 18 | "Download" : "Sider", 19 | "Modified" : "Yettwabeddel", 20 | "Created" : "Yettwarna.", 21 | "Attachments" : "Ticeqqufin", 22 | "Comments" : "Commentaires", 23 | "Save" : "Sekles", 24 | "Cancel reply" : "Semmet tiririt.", 25 | "Reply" : "Err", 26 | "Search" : "Nadi", 27 | "Cancel edit" : "Sefsex aseẓreg", 28 | "No notifications" : "Ulac tisezmal", 29 | "Today" : "Ass-a", 30 | "Close" : "Mdel", 31 | "Share" : "Bḍu", 32 | "Personal" : "Udmawan" 33 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 34 | } -------------------------------------------------------------------------------- /l10n/km.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "copy" : "ចម្លង", 5 | "Done" : "Done", 6 | "Cancel" : "បោះបង់", 7 | "Details" : "ព័ត៌មាន​លម្អិត", 8 | "Sharing" : "ការ​ចែក​រំលែក", 9 | "Tags" : "ស្លាក", 10 | "Activity" : "សកម្មភាព", 11 | "Can edit" : "Can edit", 12 | "Can share" : "Can share", 13 | "Owner" : "Owner", 14 | "Delete" : "លុប", 15 | "Edit" : "កែប្រែ", 16 | "Download" : "ទាញយក", 17 | "Modified" : "បាន​កែ​ប្រែ", 18 | "Save" : "រក្សាទុក", 19 | "Created:" : "បាន​បង្កើត៖", 20 | "Reply" : "ឆ្លើយតប", 21 | "Update" : "ធ្វើ​បច្ចុប្បន្នភាព", 22 | "Description" : "ការ​អធិប្បាយ", 23 | "seconds ago" : "វិនាទី​មុន", 24 | "Search" : "ស្វែងរក", 25 | "Shared with you" : "Shared with you", 26 | "Export" : "នាំចេញ", 27 | "Today" : "ថ្ងៃ​នេះ", 28 | "Close" : "បិទ", 29 | "Share" : "ចែក​រំលែក", 30 | "Personal" : "ផ្ទាល់​ខ្លួន" 31 | }, 32 | "nplurals=1; plural=0;"); 33 | -------------------------------------------------------------------------------- /l10n/km.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "copy" : "ចម្លង", 3 | "Done" : "Done", 4 | "Cancel" : "បោះបង់", 5 | "Details" : "ព័ត៌មាន​លម្អិត", 6 | "Sharing" : "ការ​ចែក​រំលែក", 7 | "Tags" : "ស្លាក", 8 | "Activity" : "សកម្មភាព", 9 | "Can edit" : "Can edit", 10 | "Can share" : "Can share", 11 | "Owner" : "Owner", 12 | "Delete" : "លុប", 13 | "Edit" : "កែប្រែ", 14 | "Download" : "ទាញយក", 15 | "Modified" : "បាន​កែ​ប្រែ", 16 | "Save" : "រក្សាទុក", 17 | "Created:" : "បាន​បង្កើត៖", 18 | "Reply" : "ឆ្លើយតប", 19 | "Update" : "ធ្វើ​បច្ចុប្បន្នភាព", 20 | "Description" : "ការ​អធិប្បាយ", 21 | "seconds ago" : "វិនាទី​មុន", 22 | "Search" : "ស្វែងរក", 23 | "Shared with you" : "Shared with you", 24 | "Export" : "នាំចេញ", 25 | "Today" : "ថ្ងៃ​នេះ", 26 | "Close" : "បិទ", 27 | "Share" : "ចែក​រំលែក", 28 | "Personal" : "ផ្ទាល់​ខ្លួន" 29 | },"pluralForm" :"nplurals=1; plural=0;" 30 | } -------------------------------------------------------------------------------- /l10n/kn.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "No file was uploaded" : "ವರ್ಗಾವಣೆಗೆ ಯಾವುದೇ ಕಡತಗಳು ಕಂಡುಬಂದಿಲ್ಲ", 5 | "Missing a temporary folder" : "ತಾತ್ಕಾಲಿಕ ಕಡತಕೋಶ ದೊರೆಕುತ್ತಿಲ್ಲ", 6 | "Done" : "Done", 7 | "Cancel" : "ರದ್ದು", 8 | "Open" : "ತೆರೆಯಿರಿ", 9 | "Sharing" : "ಹಂಚಿಕೆ", 10 | "Can edit" : "Can edit", 11 | "Can share" : "Can share", 12 | "Owner" : "Owner", 13 | "Delete" : "ಅಳಿಸಿ", 14 | "Edit" : "ಸಂಪಾದಿಸು", 15 | "Download" : "ಪ್ರತಿಯನ್ನು ಸ್ಥಳೀಯವಾಗಿ ಉಳಿಸಿಕೊಳ್ಳಿ", 16 | "Modified" : "ಬದಲಾಯಿಸಿದ", 17 | "Save" : "ಉಳಿಸಿ", 18 | "Search" : "Search", 19 | "Shared with you" : "Shared with you", 20 | "Export" : "ರಫ್ತು", 21 | "Today" : "Today", 22 | "Close" : "ಮುಚ್ಚು", 23 | "Share" : "ಹಂಚಿಕೊಳ್ಳಿ", 24 | "Personal" : "ವೈಯಕ್ತಿಕ" 25 | }, 26 | "nplurals=2; plural=(n > 1);"); 27 | -------------------------------------------------------------------------------- /l10n/kn.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "No file was uploaded" : "ವರ್ಗಾವಣೆಗೆ ಯಾವುದೇ ಕಡತಗಳು ಕಂಡುಬಂದಿಲ್ಲ", 3 | "Missing a temporary folder" : "ತಾತ್ಕಾಲಿಕ ಕಡತಕೋಶ ದೊರೆಕುತ್ತಿಲ್ಲ", 4 | "Done" : "Done", 5 | "Cancel" : "ರದ್ದು", 6 | "Open" : "ತೆರೆಯಿರಿ", 7 | "Sharing" : "ಹಂಚಿಕೆ", 8 | "Can edit" : "Can edit", 9 | "Can share" : "Can share", 10 | "Owner" : "Owner", 11 | "Delete" : "ಅಳಿಸಿ", 12 | "Edit" : "ಸಂಪಾದಿಸು", 13 | "Download" : "ಪ್ರತಿಯನ್ನು ಸ್ಥಳೀಯವಾಗಿ ಉಳಿಸಿಕೊಳ್ಳಿ", 14 | "Modified" : "ಬದಲಾಯಿಸಿದ", 15 | "Save" : "ಉಳಿಸಿ", 16 | "Search" : "Search", 17 | "Shared with you" : "Shared with you", 18 | "Export" : "ರಫ್ತು", 19 | "Today" : "Today", 20 | "Close" : "ಮುಚ್ಚು", 21 | "Share" : "ಹಂಚಿಕೊಳ್ಳಿ", 22 | "Personal" : "ವೈಯಕ್ತಿಕ" 23 | },"pluralForm" :"nplurals=2; plural=(n > 1);" 24 | } -------------------------------------------------------------------------------- /l10n/lb.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Déi ropgelueden Datei ass méi grouss wei d'MAX_FILE_SIZE Eegenschaft déi an der HTML form uginn ass", 5 | "No file was uploaded" : "Et ass kee Fichier ropgeluede ginn", 6 | "Missing a temporary folder" : "Et feelt en temporären Dossier", 7 | "In Progress" : "A Beaarbechtung", 8 | "Done" : "Done", 9 | "Cancel" : "Ofbriechen", 10 | "Completed" : "Erfëllt", 11 | "Details" : "Detailer", 12 | "Sharing" : "Gedeelt", 13 | "Tags" : "Tags", 14 | "Activity" : "Aktivitéit", 15 | "Can edit" : "Can edit", 16 | "Can share" : "Can share", 17 | "Owner" : "Owner", 18 | "Delete" : "Läschen", 19 | "Edit" : "Änneren", 20 | "Download" : "Eroflueden", 21 | "Modified" : "Geännert", 22 | "Comments" : "Kommentarer", 23 | "Save" : "Späicheren", 24 | "Created:" : "Erstallt:", 25 | "Reply" : "Äntwerten", 26 | "Update" : "Update", 27 | "Description" : "Beschreiwung", 28 | "seconds ago" : "Sekonnen hier", 29 | "Search" : "Search", 30 | "Shared with you" : "Mat dir gedeelt", 31 | "Advanced options" : "Erweidert Astellungen", 32 | "Export" : "Exportéieren", 33 | "Today" : "Haut", 34 | "Tomorrow" : "Muer", 35 | "Close" : "Zoumaachen", 36 | "Share" : "Deelen", 37 | "Personal" : "Perséinlech" 38 | }, 39 | "nplurals=2; plural=(n != 1);"); 40 | -------------------------------------------------------------------------------- /l10n/lb.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Déi ropgelueden Datei ass méi grouss wei d'MAX_FILE_SIZE Eegenschaft déi an der HTML form uginn ass", 3 | "No file was uploaded" : "Et ass kee Fichier ropgeluede ginn", 4 | "Missing a temporary folder" : "Et feelt en temporären Dossier", 5 | "In Progress" : "A Beaarbechtung", 6 | "Done" : "Done", 7 | "Cancel" : "Ofbriechen", 8 | "Completed" : "Erfëllt", 9 | "Details" : "Detailer", 10 | "Sharing" : "Gedeelt", 11 | "Tags" : "Tags", 12 | "Activity" : "Aktivitéit", 13 | "Can edit" : "Can edit", 14 | "Can share" : "Can share", 15 | "Owner" : "Owner", 16 | "Delete" : "Läschen", 17 | "Edit" : "Änneren", 18 | "Download" : "Eroflueden", 19 | "Modified" : "Geännert", 20 | "Comments" : "Kommentarer", 21 | "Save" : "Späicheren", 22 | "Created:" : "Erstallt:", 23 | "Reply" : "Äntwerten", 24 | "Update" : "Update", 25 | "Description" : "Beschreiwung", 26 | "seconds ago" : "Sekonnen hier", 27 | "Search" : "Search", 28 | "Shared with you" : "Mat dir gedeelt", 29 | "Advanced options" : "Erweidert Astellungen", 30 | "Export" : "Exportéieren", 31 | "Today" : "Haut", 32 | "Tomorrow" : "Muer", 33 | "Close" : "Zoumaachen", 34 | "Share" : "Deelen", 35 | "Personal" : "Perséinlech" 36 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 37 | } -------------------------------------------------------------------------------- /l10n/lo.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The file was uploaded" : "ຟາຍຖຶກອັບໂຫລດສຳເລັດ", 5 | "No file was uploaded" : "ບໍ່ມີການອັບໂຫລດຟາຍ", 6 | "Finished" : "ສຳເລັດ", 7 | "Done" : "ສໍາເລັດ", 8 | "Cancel" : "ຍົກເລີກ", 9 | "Completed" : "ສຳເລັດ", 10 | "Details" : "ລາຍລະອຽດ", 11 | "Sharing" : "ການແບ່ງປັນ", 12 | "Tags" : "ປ້າຍກຳກັບ", 13 | "Activity" : "ກິດຈະກໍາ", 14 | "Can edit" : "ແກ້ໄຂໄດ້", 15 | "Can share" : "ແບ່ງປັນໄດ້", 16 | "Owner" : "ເຈົ້າຂອງ", 17 | "Delete" : "ລຶບ", 18 | "Edit" : "ແກ້ໄຂ", 19 | "Download" : "ດາວໂຫລດ", 20 | "Modified" : "\"{name}\" ແມ່ນຊື່ໄຟລ໌ທີ່ບໍ່ຖືກຕ້ອງ.", 21 | "Comments" : "ຄໍາເຫັນ", 22 | "Save" : "ບັນທຶກ", 23 | "seconds ago" : "ວິນາທີຜ່ານມາ", 24 | "Search" : "ຄົ້ນຫາ", 25 | "Shared with you" : "ແບ່ງປັບກັບທ່ານ", 26 | "No notifications" : "ບໍ່ມີການແຈ້ງເຕືອນ", 27 | "Today" : "ມື້ນີ້", 28 | "Close" : "ປິດ", 29 | "Share" : "ແບ່ງປັນ", 30 | "Personal" : "ສ່ວນບຸກຄົນ" 31 | }, 32 | "nplurals=1; plural=0;"); 33 | -------------------------------------------------------------------------------- /l10n/lo.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The file was uploaded" : "ຟາຍຖຶກອັບໂຫລດສຳເລັດ", 3 | "No file was uploaded" : "ບໍ່ມີການອັບໂຫລດຟາຍ", 4 | "Finished" : "ສຳເລັດ", 5 | "Done" : "ສໍາເລັດ", 6 | "Cancel" : "ຍົກເລີກ", 7 | "Completed" : "ສຳເລັດ", 8 | "Details" : "ລາຍລະອຽດ", 9 | "Sharing" : "ການແບ່ງປັນ", 10 | "Tags" : "ປ້າຍກຳກັບ", 11 | "Activity" : "ກິດຈະກໍາ", 12 | "Can edit" : "ແກ້ໄຂໄດ້", 13 | "Can share" : "ແບ່ງປັນໄດ້", 14 | "Owner" : "ເຈົ້າຂອງ", 15 | "Delete" : "ລຶບ", 16 | "Edit" : "ແກ້ໄຂ", 17 | "Download" : "ດາວໂຫລດ", 18 | "Modified" : "\"{name}\" ແມ່ນຊື່ໄຟລ໌ທີ່ບໍ່ຖືກຕ້ອງ.", 19 | "Comments" : "ຄໍາເຫັນ", 20 | "Save" : "ບັນທຶກ", 21 | "seconds ago" : "ວິນາທີຜ່ານມາ", 22 | "Search" : "ຄົ້ນຫາ", 23 | "Shared with you" : "ແບ່ງປັບກັບທ່ານ", 24 | "No notifications" : "ບໍ່ມີການແຈ້ງເຕືອນ", 25 | "Today" : "ມື້ນີ້", 26 | "Close" : "ປິດ", 27 | "Share" : "ແບ່ງປັນ", 28 | "Personal" : "ສ່ວນບຸກຄົນ" 29 | },"pluralForm" :"nplurals=1; plural=0;" 30 | } -------------------------------------------------------------------------------- /l10n/mn.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "Deck" : "Ажлын талбар", 5 | "Finished" : "Дуусгасан", 6 | "To review" : "Дахин хянах", 7 | "Action needed" : "Үйлдэл шаардлагатай", 8 | "Later" : "Хойшлуулах", 9 | "copy" : "Хуулах ", 10 | "Done" : "Хийсэн", 11 | "Cancel" : "болиулах", 12 | "Open" : "Нээх", 13 | "Completed" : "Гүйцэтгэгдсэн", 14 | "Hide archived cards" : "Архивлагдсан картуудыг нуух", 15 | "Show archived cards" : "Архивлагдсан картуудыг харах", 16 | "Details" : "Дэлгэрэнгүй", 17 | "Sharing" : "Түгээх", 18 | "Tags" : "Tag-үүд", 19 | "Activity" : "Үйлдлүүд", 20 | "Undo" : "буцах", 21 | "Can edit" : "Can edit", 22 | "Can share" : "Can share", 23 | "Owner" : "Эзэмшигч", 24 | "Delete" : "Устгах", 25 | "Edit" : "Засварлах", 26 | "Members" : "Гишүүд", 27 | "Download" : "Татах", 28 | "Modified" : "Өөрчлөгдсөн", 29 | "Created" : "Үүсгэсэн", 30 | "Attachments" : "Хавсралт", 31 | "Comments" : "Сэтгэгдлүүд", 32 | "Save" : "Хадгалах", 33 | "Created:" : "Үүсгэсэн:", 34 | "Reply" : "хариулт", 35 | "Update" : "Шинэчлэх", 36 | "Description" : "Тайлбар", 37 | "(group)" : "(бүлэг)", 38 | "seconds ago" : "хоёрдахь өмнө", 39 | "Keyboard shortcut" : "Keyboard товчлуур", 40 | "Search" : "Хайх", 41 | "Shared with you" : "тантай хуваалцсан", 42 | "No notifications" : "Мэдэгдэл байхгүй", 43 | "Advanced options" : "Бусад сонголтууд", 44 | "Export" : "Экспорт", 45 | "Today" : "өнөөдөр", 46 | "Tomorrow" : "маргааш", 47 | "Close" : "Хаах", 48 | "Share" : "Түгээх", 49 | "Personal" : "Хувийн" 50 | }, 51 | "nplurals=2; plural=(n != 1);"); 52 | -------------------------------------------------------------------------------- /l10n/mn.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "Deck" : "Ажлын талбар", 3 | "Finished" : "Дуусгасан", 4 | "To review" : "Дахин хянах", 5 | "Action needed" : "Үйлдэл шаардлагатай", 6 | "Later" : "Хойшлуулах", 7 | "copy" : "Хуулах ", 8 | "Done" : "Хийсэн", 9 | "Cancel" : "болиулах", 10 | "Open" : "Нээх", 11 | "Completed" : "Гүйцэтгэгдсэн", 12 | "Hide archived cards" : "Архивлагдсан картуудыг нуух", 13 | "Show archived cards" : "Архивлагдсан картуудыг харах", 14 | "Details" : "Дэлгэрэнгүй", 15 | "Sharing" : "Түгээх", 16 | "Tags" : "Tag-үүд", 17 | "Activity" : "Үйлдлүүд", 18 | "Undo" : "буцах", 19 | "Can edit" : "Can edit", 20 | "Can share" : "Can share", 21 | "Owner" : "Эзэмшигч", 22 | "Delete" : "Устгах", 23 | "Edit" : "Засварлах", 24 | "Members" : "Гишүүд", 25 | "Download" : "Татах", 26 | "Modified" : "Өөрчлөгдсөн", 27 | "Created" : "Үүсгэсэн", 28 | "Attachments" : "Хавсралт", 29 | "Comments" : "Сэтгэгдлүүд", 30 | "Save" : "Хадгалах", 31 | "Created:" : "Үүсгэсэн:", 32 | "Reply" : "хариулт", 33 | "Update" : "Шинэчлэх", 34 | "Description" : "Тайлбар", 35 | "(group)" : "(бүлэг)", 36 | "seconds ago" : "хоёрдахь өмнө", 37 | "Keyboard shortcut" : "Keyboard товчлуур", 38 | "Search" : "Хайх", 39 | "Shared with you" : "тантай хуваалцсан", 40 | "No notifications" : "Мэдэгдэл байхгүй", 41 | "Advanced options" : "Бусад сонголтууд", 42 | "Export" : "Экспорт", 43 | "Today" : "өнөөдөр", 44 | "Tomorrow" : "маргааш", 45 | "Close" : "Хаах", 46 | "Share" : "Түгээх", 47 | "Personal" : "Хувийн" 48 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 49 | } -------------------------------------------------------------------------------- /l10n/ms_MY.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Saiz fail yang dimuatnaik melebihi MAX_FILE_SIZE yang ditetapkan dalam borang HTML", 5 | "No file was uploaded" : "Tiada fail dimuatnaik", 6 | "Missing a temporary folder" : "Direktori sementara hilang", 7 | "Done" : "Done", 8 | "Cancel" : "Batal", 9 | "Activity" : "Aktiviti", 10 | "Can edit" : "Can edit", 11 | "Can share" : "Can share", 12 | "Owner" : "Owner", 13 | "Delete" : "Padam", 14 | "Edit" : "Sunting", 15 | "Download" : "Muat turun", 16 | "Modified" : "Dimodifikasi", 17 | "Save" : "Simpan", 18 | "Created:" : "Telah dibina:", 19 | "Update" : "Kemaskini", 20 | "Description" : "Keterangan", 21 | "Search" : "Search", 22 | "Shared with you" : "Shared with you", 23 | "Export" : "Eksport", 24 | "Today" : "Hari ini", 25 | "Close" : "Tutup", 26 | "Share" : "Kongsi", 27 | "Personal" : "Peribadi" 28 | }, 29 | "nplurals=1; plural=0;"); 30 | -------------------------------------------------------------------------------- /l10n/ms_MY.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Saiz fail yang dimuatnaik melebihi MAX_FILE_SIZE yang ditetapkan dalam borang HTML", 3 | "No file was uploaded" : "Tiada fail dimuatnaik", 4 | "Missing a temporary folder" : "Direktori sementara hilang", 5 | "Done" : "Done", 6 | "Cancel" : "Batal", 7 | "Activity" : "Aktiviti", 8 | "Can edit" : "Can edit", 9 | "Can share" : "Can share", 10 | "Owner" : "Owner", 11 | "Delete" : "Padam", 12 | "Edit" : "Sunting", 13 | "Download" : "Muat turun", 14 | "Modified" : "Dimodifikasi", 15 | "Save" : "Simpan", 16 | "Created:" : "Telah dibina:", 17 | "Update" : "Kemaskini", 18 | "Description" : "Keterangan", 19 | "Search" : "Search", 20 | "Shared with you" : "Shared with you", 21 | "Export" : "Eksport", 22 | "Today" : "Hari ini", 23 | "Close" : "Tutup", 24 | "Share" : "Kongsi", 25 | "Personal" : "Peribadi" 26 | },"pluralForm" :"nplurals=1; plural=0;" 27 | } -------------------------------------------------------------------------------- /l10n/nn_NO.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Den opplasta fila er større enn variabelen MAX_FILE_SIZE i HTML-skjemaet", 5 | "No file was uploaded" : "Ingen filer vart lasta opp", 6 | "Missing a temporary folder" : "Manglar ei mellombels mappe", 7 | "Done" : "Ferdig", 8 | "Cancel" : "Avbryt", 9 | "Completed" : "Fullført", 10 | "Details" : "Detaljar", 11 | "Sharing" : "Deling", 12 | "Tags" : "Emneord", 13 | "Activity" : "Aktivitet", 14 | "Can edit" : "Can edit", 15 | "Can share" : "Can share", 16 | "Owner" : "Owner", 17 | "Delete" : "Ta bort", 18 | "Edit" : "Endra", 19 | "Download" : "Last ned", 20 | "Remove attachment" : "Fjern vedlegg", 21 | "Modified" : "Endra", 22 | "Created" : "Lagd", 23 | "Comments" : "Kommentarar", 24 | "Save" : "Lagre", 25 | "Created:" : "Oppretta:", 26 | "Cancel reply" : "Avbryt svar", 27 | "Reply" : "Svare", 28 | "Update" : "Oppdater", 29 | "Description" : "Skildring", 30 | "seconds ago" : "sekund sidan", 31 | "Keyboard shortcuts" : "Tastatursnarvegar", 32 | "Search" : "Søk", 33 | "Shared with you" : "Shared with you", 34 | "An error occurred" : "Det oppstod ein feil.", 35 | "Export" : "Eksporter", 36 | "Today" : "I dag", 37 | "Close" : "Lukk", 38 | "Share" : "Del", 39 | "Personal" : "Personleg" 40 | }, 41 | "nplurals=2; plural=(n != 1);"); 42 | -------------------------------------------------------------------------------- /l10n/nn_NO.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Den opplasta fila er større enn variabelen MAX_FILE_SIZE i HTML-skjemaet", 3 | "No file was uploaded" : "Ingen filer vart lasta opp", 4 | "Missing a temporary folder" : "Manglar ei mellombels mappe", 5 | "Done" : "Ferdig", 6 | "Cancel" : "Avbryt", 7 | "Completed" : "Fullført", 8 | "Details" : "Detaljar", 9 | "Sharing" : "Deling", 10 | "Tags" : "Emneord", 11 | "Activity" : "Aktivitet", 12 | "Can edit" : "Can edit", 13 | "Can share" : "Can share", 14 | "Owner" : "Owner", 15 | "Delete" : "Ta bort", 16 | "Edit" : "Endra", 17 | "Download" : "Last ned", 18 | "Remove attachment" : "Fjern vedlegg", 19 | "Modified" : "Endra", 20 | "Created" : "Lagd", 21 | "Comments" : "Kommentarar", 22 | "Save" : "Lagre", 23 | "Created:" : "Oppretta:", 24 | "Cancel reply" : "Avbryt svar", 25 | "Reply" : "Svare", 26 | "Update" : "Oppdater", 27 | "Description" : "Skildring", 28 | "seconds ago" : "sekund sidan", 29 | "Keyboard shortcuts" : "Tastatursnarvegar", 30 | "Search" : "Søk", 31 | "Shared with you" : "Shared with you", 32 | "An error occurred" : "Det oppstod ein feil.", 33 | "Export" : "Eksporter", 34 | "Today" : "I dag", 35 | "Close" : "Lukk", 36 | "Share" : "Del", 37 | "Personal" : "Personleg" 38 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 39 | } -------------------------------------------------------------------------------- /l10n/si.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "No file was uploaded" : "කිසිදු ගොනුවක් උඩුගත කර නොමැත", 5 | "Missing a temporary folder" : "තාවකාලික බහාලුමක් අස්ථානගත වී ඇත", 6 | "Later" : "පසුව", 7 | "copy" : "පිටපත්", 8 | "Cancel" : "අවලංගු කරන්න", 9 | "File already exists" : "ගොනුව දැනටමත් පවතී", 10 | "Add list" : "ලැයිස්තුව එකතු කරන්න", 11 | "Open" : "විවෘත", 12 | "Completed" : "සම්පුර්ණයි", 13 | "Details" : "විස්තර", 14 | "Activity" : "ක්‍රියාකාරකම", 15 | "Undo" : "පෙරසේ", 16 | "(Group)" : "(සමූහය)", 17 | "Owner" : "හිමිකරු", 18 | "Edit" : "සංස්කරණය", 19 | "Download" : "බාගන්න", 20 | "Attachments" : "ඇමිණුම්", 21 | "Comments" : "අදහස්", 22 | "Save" : "සුරකින්න", 23 | "Reply" : "පිළිතුර", 24 | "Update" : "යාවත්කාල", 25 | "Description" : "විස්තරය", 26 | "Select Date" : "දිනය තෝරන්න", 27 | "seconds ago" : "තත්පර කිහිපයකට පෙර", 28 | "Search" : "සොයන්න", 29 | "Today" : "අද", 30 | "Tomorrow" : "හෙට", 31 | "Close" : "වසන්න", 32 | "Share" : "බෙදාගන්න", 33 | "Personal" : "පුද්ගලික", 34 | "Example Task 3" : "උදාහරණ කාර්යය 3", 35 | "Example Task 2" : "උදාහරණ කාර්යය 2", 36 | "Example Task 1" : "උදාහරණ කාර්යය 1" 37 | }, 38 | "nplurals=2; plural=(n != 1);"); 39 | -------------------------------------------------------------------------------- /l10n/si.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "No file was uploaded" : "කිසිදු ගොනුවක් උඩුගත කර නොමැත", 3 | "Missing a temporary folder" : "තාවකාලික බහාලුමක් අස්ථානගත වී ඇත", 4 | "Later" : "පසුව", 5 | "copy" : "පිටපත්", 6 | "Cancel" : "අවලංගු කරන්න", 7 | "File already exists" : "ගොනුව දැනටමත් පවතී", 8 | "Add list" : "ලැයිස්තුව එකතු කරන්න", 9 | "Open" : "විවෘත", 10 | "Completed" : "සම්පුර්ණයි", 11 | "Details" : "විස්තර", 12 | "Activity" : "ක්‍රියාකාරකම", 13 | "Undo" : "පෙරසේ", 14 | "(Group)" : "(සමූහය)", 15 | "Owner" : "හිමිකරු", 16 | "Edit" : "සංස්කරණය", 17 | "Download" : "බාගන්න", 18 | "Attachments" : "ඇමිණුම්", 19 | "Comments" : "අදහස්", 20 | "Save" : "සුරකින්න", 21 | "Reply" : "පිළිතුර", 22 | "Update" : "යාවත්කාල", 23 | "Description" : "විස්තරය", 24 | "Select Date" : "දිනය තෝරන්න", 25 | "seconds ago" : "තත්පර කිහිපයකට පෙර", 26 | "Search" : "සොයන්න", 27 | "Today" : "අද", 28 | "Tomorrow" : "හෙට", 29 | "Close" : "වසන්න", 30 | "Share" : "බෙදාගන්න", 31 | "Personal" : "පුද්ගලික", 32 | "Example Task 3" : "උදාහරණ කාර්යය 3", 33 | "Example Task 2" : "උදාහරණ කාර්යය 2", 34 | "Example Task 1" : "උදාහරණ කාර්යය 1" 35 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 36 | } -------------------------------------------------------------------------------- /l10n/ta.js: -------------------------------------------------------------------------------- 1 | OC.L10N.register( 2 | "deck", 3 | { 4 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "பதிவேற்றப்பட்ட கோப்பானது HTML படிவத்தில் குறிப்பிடப்பட்டுள்ள MAX_FILE_SIZE directive ஐ விட கூடியது", 5 | "No file was uploaded" : "எந்த கோப்பும் பதிவேற்றப்படவில்லை", 6 | "Missing a temporary folder" : "ஒரு தற்காலிகமான கோப்புறையை காணவில்லை", 7 | "Done" : "Done", 8 | "Cancel" : "இரத்து செய்க", 9 | "Details" : "விவரங்கள்", 10 | "Tags" : "சீட்டுகள்", 11 | "Can edit" : "Can edit", 12 | "Can share" : "Can share", 13 | "Owner" : "Owner", 14 | "Delete" : "நீக்குக", 15 | "Edit" : "தொகுக்க", 16 | "Show in Files" : "கோப்புகளில் காட்டவும்", 17 | "Download" : "பதிவிறக்குக", 18 | "Modified" : "மாற்றப்பட்டது", 19 | "Save" : "சேமிக்க ", 20 | "Created:" : "உருவாக்கப்பட்டது:", 21 | "Update" : "இற்றைப்படுத்தல்", 22 | "Description" : "விவரிப்பு", 23 | "seconds ago" : "செக்கன்களுக்கு முன்", 24 | "Keyboard shortcuts" : "விசைப்பலகை குறுக்குவழிகள்", 25 | "Search" : "தேடுதல்", 26 | "Shared with you" : "Shared with you", 27 | "Export" : "ஏற்றுமதி", 28 | "Today" : "இன்று", 29 | "Close" : "மூடுக", 30 | "Share" : "பகிர்வு", 31 | "Personal" : "தனிப்பட்ட" 32 | }, 33 | "nplurals=2; plural=(n != 1);"); 34 | -------------------------------------------------------------------------------- /l10n/ta.json: -------------------------------------------------------------------------------- 1 | { "translations": { 2 | "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "பதிவேற்றப்பட்ட கோப்பானது HTML படிவத்தில் குறிப்பிடப்பட்டுள்ள MAX_FILE_SIZE directive ஐ விட கூடியது", 3 | "No file was uploaded" : "எந்த கோப்பும் பதிவேற்றப்படவில்லை", 4 | "Missing a temporary folder" : "ஒரு தற்காலிகமான கோப்புறையை காணவில்லை", 5 | "Done" : "Done", 6 | "Cancel" : "இரத்து செய்க", 7 | "Details" : "விவரங்கள்", 8 | "Tags" : "சீட்டுகள்", 9 | "Can edit" : "Can edit", 10 | "Can share" : "Can share", 11 | "Owner" : "Owner", 12 | "Delete" : "நீக்குக", 13 | "Edit" : "தொகுக்க", 14 | "Show in Files" : "கோப்புகளில் காட்டவும்", 15 | "Download" : "பதிவிறக்குக", 16 | "Modified" : "மாற்றப்பட்டது", 17 | "Save" : "சேமிக்க ", 18 | "Created:" : "உருவாக்கப்பட்டது:", 19 | "Update" : "இற்றைப்படுத்தல்", 20 | "Description" : "விவரிப்பு", 21 | "seconds ago" : "செக்கன்களுக்கு முன்", 22 | "Keyboard shortcuts" : "விசைப்பலகை குறுக்குவழிகள்", 23 | "Search" : "தேடுதல்", 24 | "Shared with you" : "Shared with you", 25 | "Export" : "ஏற்றுமதி", 26 | "Today" : "இன்று", 27 | "Close" : "மூடுக", 28 | "Share" : "பகிர்வு", 29 | "Personal" : "தனிப்பட்ட" 30 | },"pluralForm" :"nplurals=2; plural=(n != 1);" 31 | } -------------------------------------------------------------------------------- /lib/Activity/ChangeSet.php: -------------------------------------------------------------------------------- 1 | setBefore($before); 18 | } 19 | if ($after !== null) { 20 | $this->setAfter($after); 21 | } 22 | } 23 | 24 | public function enableDiff() { 25 | $this->diff = true; 26 | } 27 | 28 | public function getDiff() { 29 | return $this->diff; 30 | } 31 | 32 | public function setBefore($before) { 33 | if (is_object($before)) { 34 | $this->before = clone $before; 35 | } else { 36 | $this->before = $before; 37 | } 38 | } 39 | 40 | public function setAfter($after) { 41 | if (is_object($after)) { 42 | $this->after = clone $after; 43 | } else { 44 | $this->after = $after; 45 | } 46 | } 47 | 48 | public function getBefore() { 49 | return $this->before; 50 | } 51 | 52 | public function getAfter() { 53 | return $this->after; 54 | } 55 | 56 | public function jsonSerialize(): array { 57 | return [ 58 | 'before' => $this->getBefore(), 59 | 'after' => $this->getAfter(), 60 | 'diff' => $this->getDiff(), 61 | 'type' => get_class($this->before) 62 | ]; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/Activity/SettingComment.php: -------------------------------------------------------------------------------- 1 | l->t('A comment was created on a card'); 26 | } 27 | 28 | /** 29 | * @return bool True when the option can be changed for the stream 30 | * @since 11.0.0 31 | */ 32 | public function canChangeStream(): bool { 33 | return false; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Activity/SettingDescription.php: -------------------------------------------------------------------------------- 1 | l->t('A card description has been changed'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/ArchivedItemException.php: -------------------------------------------------------------------------------- 1 | cache = $cacheFactory->createDistributed('deck-attachments'); 22 | } 23 | 24 | public function getAttachmentCount(int $cardId): ?int { 25 | return $this->cache->get('count-' . $cardId); 26 | } 27 | 28 | public function setAttachmentCount(int $cardId, int $count): void { 29 | $this->cache->set('count-' . $cardId, $count); 30 | } 31 | 32 | public function clearAttachmentCount(int $cardId): void { 33 | $this->cache->remove('count-' . $cardId); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Capabilities.php: -------------------------------------------------------------------------------- 1 | appManager = $appManager; 24 | $this->permissionService = $permissionService; 25 | } 26 | 27 | /** 28 | * Function an app uses to return the capabilities 29 | * 30 | * @return array{deck: array{version: string, canCreateBoards: bool, apiVersions: array}} 31 | * @since 8.2.0 32 | */ 33 | public function getCapabilities() { 34 | return [ 35 | 'deck' => [ 36 | 'version' => $this->appManager->getAppVersion('deck'), 37 | 'canCreateBoards' => $this->permissionService->canCreate(), 38 | 'apiVersions' => [ 39 | '1.0', 40 | '1.1' 41 | ] 42 | ] 43 | ]; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/Controller/AttachmentApiV11Controller.php: -------------------------------------------------------------------------------- 1 | configService->getAll()); 31 | } 32 | 33 | /** 34 | * @NoCSRFRequired 35 | * @NoAdminRequired 36 | */ 37 | public function setValue(string $key, $value) { 38 | $result = $this->configService->set($key, $value); 39 | if ($result === null) { 40 | return new NotFoundResponse(); 41 | } 42 | return new DataResponse($result); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Controller/LabelController.php: -------------------------------------------------------------------------------- 1 | labelService->create($title, $color, $boardId); 32 | } 33 | 34 | /** 35 | * @NoAdminRequired 36 | * @param $id 37 | * @param $title 38 | * @param $color 39 | * @return \OCP\AppFramework\Db\Entity 40 | */ 41 | public function update($id, $title, $color) { 42 | return $this->labelService->update($id, $title, $color); 43 | } 44 | 45 | /** 46 | * @NoAdminRequired 47 | * @param $labelId 48 | * @return \OCP\AppFramework\Db\Entity 49 | */ 50 | public function delete($labelId) { 51 | return $this->labelService->delete($labelId); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Controller/OverviewApiController.php: -------------------------------------------------------------------------------- 1 | dashboardService->findUpcomingCards($this->userId)); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/Controller/SearchController.php: -------------------------------------------------------------------------------- 1 | searchService->searchCards($term, $limit, $cursor); 34 | return new DataResponse(array_map(function (Card $card) { 35 | $board = $card->getRelatedBoard(); 36 | $json = (new CardDetails($card, $board))->jsonSerialize(); 37 | 38 | $json['relatedBoard'] = $board; 39 | $json['relatedStack'] = $card->getRelatedStack(); 40 | 41 | return $json; 42 | }, $cards)); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Cron/CardDescriptionActivity.php: -------------------------------------------------------------------------------- 1 | activityManager = $activityManager; 26 | $this->cardMapper = $cardMapper; 27 | } 28 | 29 | /** 30 | * @param $argument 31 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) 32 | */ 33 | public function run($argument) { 34 | $cards = $this->cardMapper->findUnexposedDescriptionChances(); 35 | foreach ($cards as $card) { 36 | $this->activityManager->triggerEvent( 37 | ActivityManager::DECK_OBJECT_CARD, 38 | $card, 39 | ActivityManager::SUBJECT_CARD_UPDATE_DESCRIPTION, 40 | [ 41 | 'before' => $card->getDescriptionPrev(), 42 | 'after' => $card->getDescription() 43 | ], 44 | $card->getLastEditor() 45 | ); 46 | 47 | $card->setDescriptionPrev(null); 48 | $card->setLastEditor(null); 49 | $this->cardMapper->update($card, false); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/Cron/ScheduledNotifications.php: -------------------------------------------------------------------------------- 1 | cardMapper->findOverdue(); 36 | /** @var Card $card */ 37 | foreach ($cards as $card) { 38 | try { 39 | $this->notificationHelper->sendCardDuedate($card); 40 | } catch (DoesNotExistException $e) { 41 | // Skip if any error occurs 42 | $this->logger->debug('Could not create overdue notification for card with id ' . $card->getId()); 43 | } 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/Cron/SessionsCleanup.php: -------------------------------------------------------------------------------- 1 | setInterval(SessionService::SESSION_VALID_TIME); 31 | } 32 | 33 | protected function run($argument) { 34 | $this->logger->debug('Run cleanup job for deck sessions'); 35 | 36 | $removedSessions = $this->sessionService->removeInactiveSessions(); 37 | $this->logger->debug('Removed ' . $removedSessions . ' inactive sessions'); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/Dashboard/DeckWidgetToday.php: -------------------------------------------------------------------------------- 1 | l10n = $l10n; 25 | } 26 | 27 | /** 28 | * @inheritDoc 29 | */ 30 | public function getId(): string { 31 | return 'deckToday'; 32 | } 33 | 34 | /** 35 | * @inheritDoc 36 | */ 37 | public function getTitle(): string { 38 | return $this->l10n->t('Cards due today'); 39 | } 40 | 41 | /** 42 | * @inheritDoc 43 | */ 44 | public function getOrder(): int { 45 | return 20; 46 | } 47 | 48 | /** 49 | * @inheritDoc 50 | */ 51 | public function getIconClass(): string { 52 | return 'icon-deck'; 53 | } 54 | 55 | /** 56 | * @inheritDoc 57 | */ 58 | public function getUrl(): ?string { 59 | return null; 60 | } 61 | 62 | /** 63 | * @inheritDoc 64 | */ 65 | public function load(): void { 66 | \OCP\Util::addScript('deck', 'deck-dashboard'); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/Dashboard/DeckWidgetTomorrow.php: -------------------------------------------------------------------------------- 1 | l10n = $l10n; 25 | } 26 | 27 | /** 28 | * @inheritDoc 29 | */ 30 | public function getId(): string { 31 | return 'deckTomorrow'; 32 | } 33 | 34 | /** 35 | * @inheritDoc 36 | */ 37 | public function getTitle(): string { 38 | return $this->l10n->t('Cards due tomorrow'); 39 | } 40 | 41 | /** 42 | * @inheritDoc 43 | */ 44 | public function getOrder(): int { 45 | return 20; 46 | } 47 | 48 | /** 49 | * @inheritDoc 50 | */ 51 | public function getIconClass(): string { 52 | return 'icon-deck'; 53 | } 54 | 55 | /** 56 | * @inheritDoc 57 | */ 58 | public function getUrl(): ?string { 59 | return null; 60 | } 61 | 62 | /** 63 | * @inheritDoc 64 | */ 65 | public function load(): void { 66 | \OCP\Util::addScript('deck', 'deck-dashboard'); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /lib/Db/Acl.php: -------------------------------------------------------------------------------- 1 | addType('id', 'integer'); 30 | $this->addType('boardId', 'integer'); 31 | $this->addType('permissionEdit', 'boolean'); 32 | $this->addType('permissionShare', 'boolean'); 33 | $this->addType('permissionManage', 'boolean'); 34 | $this->addType('type', 'integer'); 35 | $this->addType('owner', 'boolean'); 36 | $this->addRelation('owner'); 37 | $this->addResolvable('participant'); 38 | } 39 | 40 | public function getPermission($permission) { 41 | switch ($permission) { 42 | case self::PERMISSION_READ: 43 | return true; 44 | case self::PERMISSION_EDIT: 45 | return $this->getPermissionEdit(); 46 | case self::PERMISSION_SHARE: 47 | return $this->getPermissionShare(); 48 | case self::PERMISSION_MANAGE: 49 | return $this->getPermissionManage(); 50 | } 51 | return false; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Db/Assignment.php: -------------------------------------------------------------------------------- 1 | addType('id', 'integer'); 24 | $this->addType('cardId', 'integer'); 25 | $this->addType('type', 'integer'); 26 | $this->addResolvable('participant'); 27 | } 28 | 29 | public function getTypeString(): string { 30 | switch ($this->getType()) { 31 | case self::TYPE_USER: 32 | return 'user'; 33 | case self::TYPE_GROUP: 34 | return 'group'; 35 | case self::TYPE_CIRCLE: 36 | return 'circle'; 37 | } 38 | 39 | return 'unknown'; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/Db/Attachment.php: -------------------------------------------------------------------------------- 1 | addType('id', 'integer'); 22 | $this->addType('cardId', 'integer'); 23 | $this->addType('lastModified', 'integer'); 24 | $this->addType('createdAt', 'integer'); 25 | $this->addType('deletedAt', 'integer'); 26 | $this->addResolvable('createdBy'); 27 | $this->addRelation('extendedData'); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Db/Circle.php: -------------------------------------------------------------------------------- 1 | getUniqueId(); 17 | parent::__construct($primaryKey, $circle); 18 | } 19 | 20 | public function getObjectSerialization() { 21 | return [ 22 | 'uid' => $this->object->getUniqueId(), 23 | 'displayname' => $this->object->getDisplayName(), 24 | 'typeString' => '', 25 | 'circleOwner' => $this->object->getOwner(), 26 | 'type' => 7 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Db/DeckMapper.php: -------------------------------------------------------------------------------- 1 | 18 | */ 19 | abstract class DeckMapper extends QBMapper { 20 | 21 | /** 22 | * @param $id 23 | * @return T 24 | * @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException 25 | * @throws \OCP\AppFramework\Db\DoesNotExistException 26 | */ 27 | public function find($id) { 28 | $qb = $this->db->getQueryBuilder(); 29 | $qb->select('*') 30 | ->from($this->getTableName()) 31 | ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))); 32 | 33 | return $this->findEntity($qb); 34 | } 35 | 36 | /** 37 | * Helper function to split passed array into chunks of 1000 elements and 38 | * call a given callback for fetching query results 39 | * 40 | * Can be useful to limit to 1000 results per query for oracle compatiblity 41 | * but still iterate over all results 42 | */ 43 | public function chunkQuery(array $ids, callable $callback): Generator { 44 | $limit = 1000; 45 | while (!empty($ids)) { 46 | $slice = array_splice($ids, 0, $limit); 47 | foreach ($callback($slice) as $item) { 48 | yield $item; 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/Db/Group.php: -------------------------------------------------------------------------------- 1 | getGID(); 15 | parent::__construct($primaryKey, $group); 16 | } 17 | 18 | public function getObjectSerialization() { 19 | return [ 20 | 'uid' => $this->object->getGID(), 21 | 'displayname' => $this->object->getDisplayName(), 22 | 'type' => 1 23 | ]; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/Db/IPermissionMapper.php: -------------------------------------------------------------------------------- 1 | addType('id', 'integer'); 22 | $this->addType('boardId', 'integer'); 23 | $this->addType('cardId', 'integer'); 24 | $this->addType('lastModified', 'integer'); 25 | } 26 | 27 | public function getETag() { 28 | return md5((string)$this->getLastModified()); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/Db/RelationalObject.php: -------------------------------------------------------------------------------- 1 | primaryKey = $primaryKey; 24 | $this->object = $object; 25 | } 26 | 27 | public function jsonSerialize(): array { 28 | return array_merge( 29 | ['primaryKey' => $this->primaryKey], 30 | $this->getObjectSerialization() 31 | ); 32 | } 33 | 34 | public function getObject() { 35 | if (is_callable($this->object)) { 36 | $this->object = call_user_func($this->object, $this); 37 | } 38 | 39 | return $this->object; 40 | } 41 | 42 | /** 43 | * This method should be overwritten if object doesn't implement \JsonSerializable 44 | * 45 | * @throws \Exception 46 | */ 47 | public function getObjectSerialization() { 48 | if ($this->getObject() instanceof JsonSerializable) { 49 | return $this->getObject()->jsonSerialize(); 50 | } else { 51 | throw new \Exception('jsonSerialize is not implemented on ' . get_class($this->getObject())); 52 | } 53 | } 54 | 55 | public function getPrimaryKey(): string { 56 | return $this->primaryKey; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/Db/Session.php: -------------------------------------------------------------------------------- 1 | addType('id', 'integer'); 23 | $this->addType('boardId', 'integer'); 24 | $this->addType('lastContact', 'integer'); 25 | } 26 | 27 | public function jsonSerialize(): array { 28 | return [ 29 | 'id' => $this->id, 30 | 'userId' => $this->userId, 31 | 'token' => $this->token, 32 | 'lastContact' => $this->lastContact, 33 | 'boardId' => $this->boardId, 34 | ]; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Db/Stack.php: -------------------------------------------------------------------------------- 1 | addType('id', 'integer'); 29 | $this->addType('boardId', 'integer'); 30 | $this->addType('deletedAt', 'integer'); 31 | $this->addType('lastModified', 'integer'); 32 | $this->addType('order', 'integer'); 33 | } 34 | 35 | public function setCards($cards) { 36 | $this->cards = $cards; 37 | } 38 | 39 | public function jsonSerialize(): array { 40 | $json = parent::jsonSerialize(); 41 | if (empty($this->cards)) { 42 | unset($json['cards']); 43 | } 44 | return $json; 45 | } 46 | 47 | public function getCalendarObject(): VCalendar { 48 | $calendar = new VCalendar(); 49 | $event = $calendar->createComponent('VTODO'); 50 | $event->UID = 'deck-stack-' . $this->getId(); 51 | $event->SUMMARY = 'List : ' . $this->getTitle(); 52 | $calendar->add($event); 53 | return $calendar; 54 | } 55 | 56 | public function getCalendarPrefix(): string { 57 | return 'stack'; 58 | } 59 | 60 | public function getETag() { 61 | return md5((string)$this->getLastModified()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/Db/User.php: -------------------------------------------------------------------------------- 1 | userManager = $userManager; 16 | parent::__construct($uid, function ($object) { 17 | return $this->userManager->get($object->getPrimaryKey()); 18 | }); 19 | } 20 | 21 | public function getObjectSerialization() { 22 | return [ 23 | 'uid' => $this->getObject()->getUID(), 24 | 'displayname' => $this->getDisplayName(), 25 | 'type' => Acl::PERMISSION_TYPE_USER 26 | ]; 27 | } 28 | 29 | public function getUID() { 30 | return $this->getPrimaryKey(); 31 | } 32 | 33 | public function getDisplayName() { 34 | return $this->userManager->getDisplayName($this->getPrimaryKey()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Event/AAclEvent.php: -------------------------------------------------------------------------------- 1 | acl = $acl; 23 | } 24 | 25 | public function getAcl(): Acl { 26 | return $this->acl; 27 | } 28 | 29 | public function getBoardId(): int { 30 | return $this->acl->getBoardId(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/Event/ABoardImportGetAllowedEvent.php: -------------------------------------------------------------------------------- 1 | service = $service; 23 | } 24 | 25 | public function getService(): BoardImportService { 26 | return $this->service; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/Event/ACardEvent.php: -------------------------------------------------------------------------------- 1 | card = $card; 23 | } 24 | 25 | public function getCard(): Card { 26 | return $this->card; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/Event/AclCreatedEvent.php: -------------------------------------------------------------------------------- 1 | boardId = $boardId; 22 | } 23 | 24 | public function getBoardId(): int { 25 | return $this->boardId; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/Event/CardCreatedEvent.php: -------------------------------------------------------------------------------- 1 | cardBefore = $before; 21 | } 22 | 23 | public function getCardBefore() { 24 | return $this->cardBefore; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Event/SessionClosedEvent.php: -------------------------------------------------------------------------------- 1 | boardId = $boardId; 24 | $this->userId = $userId; 25 | } 26 | 27 | public function getBoardId(): int { 28 | return $this->boardId; 29 | } 30 | public function getUserId(): string { 31 | return $this->userId; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/Event/SessionCreatedEvent.php: -------------------------------------------------------------------------------- 1 | boardId = $boardId; 25 | $this->userId = $userId; 26 | } 27 | 28 | public function getBoardId(): int { 29 | return $this->boardId; 30 | } 31 | public function getUserId(): string { 32 | return $this->userId; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/Exceptions/ConflictException.php: -------------------------------------------------------------------------------- 1 | data = $data; 18 | } 19 | 20 | public function getStatus() { 21 | return 409; 22 | } 23 | 24 | public function getData() { 25 | return $this->data; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/InvalidAttachmentType.php: -------------------------------------------------------------------------------- 1 | */ 20 | class BeforeTemplateRenderedListener implements IEventListener { 21 | private $request; 22 | 23 | public function __construct(IRequest $request) { 24 | $this->request = $request; 25 | } 26 | 27 | public function handle(Event $event): void { 28 | if (!($event instanceof BeforeTemplateRenderedEvent)) { 29 | return; 30 | } 31 | 32 | if (!$event->isLoggedIn()) { 33 | return; 34 | } 35 | Util::addStyle('deck', 'deck'); 36 | 37 | $pathInfo = $this->request->getPathInfo(); 38 | if (str_starts_with($pathInfo, '/apps/calendar')) { 39 | Util::addScript('deck', 'deck-calendar'); 40 | } 41 | 42 | if (str_starts_with($pathInfo, '/call/') || str_starts_with($pathInfo, '/apps/spreed')) { 43 | Util::addScript('deck', 'deck-talk'); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/Listeners/ResourceAdditionalScriptsListener.php: -------------------------------------------------------------------------------- 1 | */ 16 | class ResourceAdditionalScriptsListener implements IEventListener { 17 | private IRequest $request; 18 | 19 | public function __construct(IRequest $request) { 20 | $this->request = $request; 21 | } 22 | 23 | public function handle(Event $event): void { 24 | if (!$event instanceof LoadAdditionalScriptsEvent) { 25 | return; 26 | } 27 | 28 | if (str_starts_with($this->request->getPathInfo(), '/call/')) { 29 | // Talk integration has its own entrypoint which already includes collections handling 30 | return; 31 | } 32 | 33 | Util::addScript('deck', 'deck-collections'); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Middleware/DefaultBoardMiddleware.php: -------------------------------------------------------------------------------- 1 | userId !== null && $this->defaultBoardService->checkFirstRun($this->userId) && $this->permissionService->canCreate()) { 30 | $this->defaultBoardService->createDefaultBoard($this->l10n->t('Welcome to Nextcloud Deck!'), $this->userId, 'bf678b'); 31 | } 32 | } catch (\Throwable $e) { 33 | $this->logger->error('Could not create default board', ['exception' => $e]); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Migration/DeletedCircleCleanup.php: -------------------------------------------------------------------------------- 1 | aclMapper = $aclMapper; 21 | $this->circleService = $circlesService; 22 | } 23 | 24 | public function getName() { 25 | return 'Cleanup Deck ACL entries for circles which have been already deleted'; 26 | } 27 | 28 | public function run(IOutput $output) { 29 | if (!$this->circleService->isCirclesEnabled()) { 30 | return; 31 | } 32 | 33 | foreach ($this->aclMapper->findByType(Acl::PERMISSION_TYPE_CIRCLE) as $acl) { 34 | if ($this->circleService->getCircle($acl->getParticipant()) === null) { 35 | $this->aclMapper->delete($acl); 36 | $output->info('Removed circle with id ' . $acl->getParticipant()); 37 | } 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /lib/Migration/Version1000Date20200308073933.php: -------------------------------------------------------------------------------- 1 | getTable('deck_assigned_users'); 24 | 25 | // Defaults to TYPE_USER = 0 26 | $table->addColumn('type', 'integer', [ 27 | 'notnull' => true, 28 | 'default' => 0 29 | ]); 30 | //$table->addIndex(['participant'], 'deck_assigned_users_idx_t'); 31 | $table->addIndex(['type'], 'deck_assigned_users_idx_ty'); 32 | 33 | return $schema; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/Migration/Version1011Date20230901010840.php: -------------------------------------------------------------------------------- 1 | getTable('deck_cards'); 33 | if (!$table->hasColumn('done')) { 34 | $table->addColumn('done', Types::DATETIME, [ 35 | 'default' => null, 36 | 'notnull' => false, 37 | ]); 38 | 39 | return $schema; 40 | } 41 | 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Migration/Version1011Date20231106160059.php: -------------------------------------------------------------------------------- 1 | getTable('deck_cards'); 24 | 25 | if (!$table->hasIndex('idx_last_editor')) { 26 | $createIndex = true; 27 | } 28 | 29 | if (!$createIndex) { 30 | $index = $table->getIndex('idx_last_editor'); 31 | if (in_array('description_prev', $index->getColumns(), true)) { 32 | $table->dropIndex('idx_last_editor'); 33 | $createIndex = true; 34 | } 35 | } 36 | 37 | if ($createIndex) { 38 | $table->addIndex(['last_editor'], 'idx_last_editor'); 39 | return $schema; 40 | } 41 | 42 | return null; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/Migration/Version10900Date202206151724222.php: -------------------------------------------------------------------------------- 1 | hasTable('deck_sessions')) { 24 | $table = $schema->createTable('deck_sessions'); 25 | $table->addColumn('id', Types::INTEGER, [ 26 | 'autoincrement' => true, 27 | 'notnull' => true, 28 | 'unsigned' => true, 29 | ]); 30 | $table->addColumn('user_id', Types::STRING, [ 31 | 'notnull' => false, 32 | 'length' => 64, 33 | ]); 34 | $table->addColumn('board_id', Types::INTEGER, [ 35 | 'notnull' => false, 36 | ]); 37 | $table->addColumn('token', Types::STRING, [ 38 | 'notnull' => true, 39 | 'length' => 64, 40 | ]); 41 | $table->addColumn('last_contact', Types::INTEGER, [ 42 | 'notnull' => true, 43 | 'length' => 20, 44 | 'unsigned' => true, 45 | ]); 46 | $table->setPrimaryKey(['id']); 47 | $table->addIndex(['board_id'], 'deck_session_board_id_idx'); 48 | $table->addIndex(['token'], 'deck_session_token_idx'); 49 | $table->addIndex(['last_contact'], 'deck_session_last_contact_idx'); 50 | } 51 | return $schema; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/Migration/Version11000Date20240222115515.php: -------------------------------------------------------------------------------- 1 | getTable('deck_assigned_users'); 24 | if ($assignedUsersTable->hasIndex('deck_assigned_users_idx_c')) { 25 | $assignedUsersTable->dropIndex('deck_assigned_users_idx_c'); 26 | $returnValue = $schema; 27 | } 28 | 29 | $boardAclTable = $schema->getTable('deck_board_acl'); 30 | if ($boardAclTable->hasIndex('deck_board_acl_idx_i')) { 31 | $boardAclTable->dropIndex('deck_board_acl_idx_i'); 32 | $returnValue = $schema; 33 | } 34 | return $returnValue; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/Model/BoardSummary.php: -------------------------------------------------------------------------------- 1 | board = $board; 17 | } 18 | 19 | public function jsonSerialize(): array { 20 | return [ 21 | 'id' => $this->getId(), 22 | 'title' => $this->getTitle() 23 | ]; 24 | } 25 | 26 | protected function getter(string $name): mixed { 27 | return $this->board->getter($name); 28 | } 29 | 30 | public function __call($name, $arguments) { 31 | return $this->board->__call($name, $arguments); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/Model/OptionalNullableValue.php: -------------------------------------------------------------------------------- 1 | value = $value; 29 | } 30 | 31 | /** @return ?T */ 32 | public function getValue(): mixed { 33 | return $this->value; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /lib/NoPermissionException.php: -------------------------------------------------------------------------------- 1 | message = get_class($controller) . '#' . $method . ': ' . $message; 15 | } 16 | } 17 | 18 | public function getStatus() { 19 | return 403; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/NotFoundException.php: -------------------------------------------------------------------------------- 1 | getAbsoluteURL( 21 | $urlGenerator->imagePath(Application::APP_ID, 'deck-dark.svg') 22 | ), 23 | $board->getTitle(), 24 | '', 25 | $urlGenerator->linkToRouteAbsolute('deck.page.indexBoard', ['boardId' => $board->getId()]), 26 | 'icon-deck'); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/Search/CardSearchResultEntry.php: -------------------------------------------------------------------------------- 1 | getAbsoluteURL( 23 | $urlGenerator->imagePath(Application::APP_ID, 'card.svg') 24 | ), 25 | $card->getTitle(), 26 | $board->getTitle() . ' » ' . $stack->getTitle(), 27 | $urlGenerator->linkToRouteAbsolute('deck.page.redirectToCard', ['cardId' => $card->getId()]), 28 | 'icon-deck' 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/Search/CommentSearchResultEntry.php: -------------------------------------------------------------------------------- 1 | getAbsoluteURL( 24 | $urlGenerator->imagePath('core', 'actions/comment.svg') 25 | ), 26 | // TRANSLATORS This is describing the author and card title related to a comment e.g. "Jane on MyTask" 27 | $l10n->t('%s on %s', [$commentAuthor, $card->getTitle()]), 28 | $commentMessage, 29 | $urlGenerator->linkToRouteAbsolute('deck.page.index') . '#/board/' . $card->getRelatedBoard()->getId() . '/card/' . $card->getId() . '/comments/' . $commentId, // $commentId 30 | 'icon-comment'); 31 | $this->commentId = $commentId; 32 | } 33 | 34 | public function getCommentId(): string { 35 | return $this->commentId; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/Search/Query/AQueryParameter.php: -------------------------------------------------------------------------------- 1 | value) && mb_strlen($this->value) > 1) { 24 | $param = (mb_substr($this->value, 0, 1) === '"' && mb_substr($this->value, -1, 1) === '"') ? mb_substr($this->value, 1, -1): $this->value; 25 | return $param; 26 | } 27 | return $this->value; 28 | } 29 | 30 | public function getComparator(): int { 31 | return $this->comparator; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/Search/Query/DateQueryParameter.php: -------------------------------------------------------------------------------- 1 | field = $field; 19 | $this->comparator = $comparator; 20 | $this->value = $value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /lib/Search/Query/StringQueryParameter.php: -------------------------------------------------------------------------------- 1 | field = $field; 20 | $this->comparator = $comparator; 21 | $this->value = $value; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/Service/ICustomAttachmentService.php: -------------------------------------------------------------------------------- 1 | ['numeric'], 16 | 'userId' => ['not_empty', 'not_null', 'not_false', 'max:64'], 17 | ]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Validators/AttachmentServiceValidator.php: -------------------------------------------------------------------------------- 1 | ['numeric'], 15 | 'type' => ['not_empty', 'not_null', 'not_false'], 16 | 'data' => ['not_empty', 'not_null', 'not_false', 'max:255'], 17 | ]; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/Validators/BoardServiceValidator.php: -------------------------------------------------------------------------------- 1 | ['numeric'], 16 | 'boardId' => ['numeric'], 17 | 'type' => ['numeric'], 18 | 'mapper' => ['not_empty', 'not_null', 'not_false'], 19 | 'title' => ['not_empty', 'not_null', 'not_false', 'max:100'], 20 | 'userId' => ['not_empty', 'not_null', 'not_false', 'max:64'], 21 | 'color' => ['not_empty', 'not_null', 'not_false', 'max:6'], 22 | 'participant' => ['not_empty', 'not_null', 'not_false', 'max:64'], 23 | 'edit' => ['not_null'], 24 | 'share' => ['not_null'], 25 | 'manage' => ['not_null'], 26 | 'archived' => ['bool'] 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/Validators/CardServiceValidator.php: -------------------------------------------------------------------------------- 1 | ['numeric'], 16 | 'title' => ['not_empty', 'not_null', 'not_false', 'max:255'], 17 | 'cardId' => ['numeric'], 18 | 'stackId' => ['numeric'], 19 | 'boardId' => ['numeric'], 20 | 'labelId' => ['numeric'], 21 | 'type' => ['not_empty', 'not_null', 'not_false', 'max:64'], 22 | 'order' => ['numeric'], 23 | 'owner' => ['not_empty', 'not_null', 'not_false', 'max:64'], 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lib/Validators/LabelServiceValidator.php: -------------------------------------------------------------------------------- 1 | ['numeric'], 16 | 'title' => ['not_empty', 'not_null', 'not_false', 'max:100'], 17 | 'boardId' => ['numeric', 'not_null'], 18 | 'color' => ['not_empty', 'not_null', 'not_false', 'max:6'] 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/Validators/StackServiceValidator.php: -------------------------------------------------------------------------------- 1 | ['numeric'], 16 | 'title' => ['not_empty', 'not_null', 'not_false', 'max:100'], 17 | 'boardId' => ['numeric', 'not_null'], 18 | 'order' => ['numeric', 'not_null'] 19 | ]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /mkdocs.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 2 | # SPDX-License-Identifier: AGPL-3.0-or-later 3 | site_name: Nextcloud Deck 4 | theme: 5 | name: readthedocs 6 | extra_css: [extra.css] 7 | pages: 8 | - Home: index.md 9 | - User documentation: User_documentation_en.md 10 | - Markdown composing: Markdown.md 11 | - API documentation: 12 | - REST API: API.md 13 | - Nextcloud API: API-Nextcloud.md 14 | - Developer documentation: 15 | - Data structure: structure.md 16 | - Import documentation: 17 | - Implement import: implement-import.md 18 | - Class diagram: import-class-diagram.md 19 | -------------------------------------------------------------------------------- /relativeci.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // Allow the agent to pick up the current commit message 3 | includeCommitMessage: true, 4 | webpack: { 5 | // Path to Webpack stats JSON file 6 | stats: './js/webpack-stats.json' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /src/CardCreateDialog.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 15 | 16 | 38 | -------------------------------------------------------------------------------- /src/components/CollaborationView.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 39 | -------------------------------------------------------------------------------- /src/components/Sidebar.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 9 | 10 | 31 | 32 | 41 | -------------------------------------------------------------------------------- /src/components/board/TimelineTabSidebar.vue: -------------------------------------------------------------------------------- 1 | 5 | 12 | 13 | 29 | -------------------------------------------------------------------------------- /src/components/card/CardDetailEntry.vue: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | 29 | 32 | -------------------------------------------------------------------------------- /src/components/card/CardSidebarTabActivity.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 14 | 15 | 36 | -------------------------------------------------------------------------------- /src/components/card/CardSidebarTabAttachments.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 12 | 13 | 36 | -------------------------------------------------------------------------------- /src/components/cards/CardMenu.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 21 | 47 | -------------------------------------------------------------------------------- /src/components/cards/badges/CardId.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | 24 | 25 | 30 | -------------------------------------------------------------------------------- /src/components/icons/DeckIcon.vue: -------------------------------------------------------------------------------- 1 | 5 | 41 | 42 | 61 | -------------------------------------------------------------------------------- /src/components/navigation/AppNavigationImportBoard.vue: -------------------------------------------------------------------------------- 1 | 5 | 15 | 16 | 56 | -------------------------------------------------------------------------------- /src/css/animations.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | .fade-enter-active { 6 | transition: opacity 250ms; 7 | } 8 | 9 | .fade-enter, .fade-leave-to { 10 | opacity: 0; 11 | } 12 | 13 | .zoom-appear-active-class, 14 | .zoom-enter-active { 15 | animation: zoom-in var(--animation-quick); 16 | } 17 | 18 | .zoom-leave-active { 19 | animation: zoom-in var(--animation-quick) reverse; 20 | will-change: transform; 21 | } 22 | @keyframes zoom-in { 23 | 0% { 24 | transform: scale(0); 25 | opacity: 0; 26 | } 27 | 100% { 28 | transform: scale(1); 29 | opacity: 1; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/css/comments.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | .comment-form form { 6 | display: flex; 7 | flex-grow: 1; 8 | position: relative; 9 | 10 | .editor__content:deep { 11 | flex-grow: 1; 12 | margin-left: var(--default-clickable-area); 13 | 14 | .ProseMirror { 15 | width: 100%; 16 | } 17 | } 18 | 19 | input[type='submit'] { 20 | width: var(--default-clickable-area); 21 | height: var(--default-clickable-area); 22 | margin: 0; 23 | padding: 13px; 24 | background-color: transparent; 25 | border: none; 26 | position: absolute; 27 | bottom: var(--default-grid-baseline); 28 | right: var(--default-grid-baseline); 29 | 30 | &:disabled { 31 | opacity: .7; 32 | } 33 | } 34 | } 35 | 36 | .comment { 37 | margin-bottom: 10px; 38 | } 39 | 40 | .comment--header { 41 | display: flex; 42 | align-items: center; 43 | color: var(--color-text-light); 44 | 45 | .username { 46 | padding: 10px; 47 | } 48 | .spacer { 49 | flex-grow: 1; 50 | } 51 | .timestamp { 52 | color: var(--color-text-maxcontrast); 53 | } 54 | } 55 | 56 | .comment--content { 57 | margin-left: var(--default-clickable-area); 58 | word-break: break-word; 59 | } 60 | -------------------------------------------------------------------------------- /src/css/dashboard.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | .icon-deck { 6 | background-image: url(./../../img/deck-dark.svg); 7 | } 8 | 9 | body.dark .icon-deck { 10 | background-image: url(./../../img/deck.svg); 11 | } 12 | -------------------------------------------------------------------------------- /src/css/labels.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | $card-spacing: 10px; 6 | $card-padding: 10px; 7 | 8 | .labels { 9 | flex-grow: 1; 10 | flex-shrink: 1; 11 | min-width: 0; 12 | display: flex; 13 | flex-direction: row; 14 | gap: 3px; 15 | 16 | li { 17 | flex-grow: 0; 18 | flex-shrink: 1; 19 | display: flex; 20 | flex-direction: row; 21 | overflow: hidden; 22 | padding: 1px 8px; 23 | border-radius: 15px; 24 | font-size: 13px; 25 | 26 | &:hover { 27 | overflow: unset; 28 | } 29 | 30 | span { 31 | flex-grow: 0; 32 | flex-shrink: 1; 33 | min-width: 0; 34 | overflow: hidden; 35 | text-overflow: ellipsis; 36 | white-space: nowrap; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/css/selector.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | .selector-wrapper { 7 | display: flex; 8 | max-width: 100%; 9 | margin-top: 10px; 10 | 11 | &--icon { 12 | width: var(--default-clickable-area); 13 | height: var(--default-clickable-area); 14 | flex-shrink: 0; 15 | display: flex; 16 | align-items: center; 17 | align-content: center; 18 | justify-content: center; 19 | } 20 | 21 | &--selector { 22 | width: 100%; 23 | } 24 | 25 | &--content { 26 | display: flex; 27 | flex-grow: 1; 28 | align-items: center; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/css/variables.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | $card-spacing: 8px; 6 | $card-padding: 4px; 7 | $stack-spacing: 12px; 8 | $stack-width: 280px; 9 | $board-spacing: 16px; 10 | -------------------------------------------------------------------------------- /src/directives/focus.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | /** 6 | * 7 | * @param el 8 | * @param binding 9 | */ 10 | function focusElement(el, binding) { 11 | // If directive has bound value 12 | if (binding.value !== undefined && !binding.value) return 13 | 14 | // Focus the element 15 | el.focus() 16 | } 17 | 18 | // Register a global custom directive called `v-focus` 19 | export default { 20 | bind(el, binding, vnode) { 21 | // When the component of the element gets activated 22 | vnode.context.$on('hook:activated', () => focusElement(el, binding)) 23 | }, 24 | // When the bound element is inserted into the DOM... 25 | inserted: focusElement, 26 | } 27 | -------------------------------------------------------------------------------- /src/helpers/applyOrderToArray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | const arrayMove = function(arrayToSort, removedIndex, addedIndex) { 7 | if (removedIndex === null && addedIndex === null) return arrayToSort 8 | 9 | const result = [...arrayToSort] 10 | let itemToAdd = arrayToSort[removedIndex] 11 | 12 | if (removedIndex !== null) { 13 | itemToAdd = result.splice(removedIndex, 1)[0] 14 | } 15 | 16 | if (addedIndex !== null) { 17 | result.splice(addedIndex, 0, itemToAdd) 18 | } 19 | return result 20 | } 21 | 22 | export default arrayMove 23 | -------------------------------------------------------------------------------- /src/helpers/errors.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { showError as errorDialog } from '@nextcloud/dialogs' 6 | 7 | const showAxiosError = err => { 8 | const response = err?.response || {} 9 | const message = response?.data.message 10 | 11 | if (message) { 12 | errorDialog(message) 13 | return 14 | } 15 | 16 | errorDialog(err.message) 17 | } 18 | 19 | const showError = err => { 20 | // axios error 21 | if (err.response) { 22 | showAxiosError(err) 23 | return 24 | } 25 | 26 | errorDialog(err.message) 27 | } 28 | 29 | export { 30 | showError, 31 | } 32 | -------------------------------------------------------------------------------- /src/helpers/mentions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | const rawToParsed = (text) => { 7 | text = text.replace(/
/g, '\n') 8 | text = text.replace(/ /g, ' ') 9 | 10 | // Since we used innerHTML to get the content of the div.contenteditable 11 | // it is escaped. With this little trick from https://stackoverflow.com/a/7394787 12 | // We unescape the code again, so if you write `` we can display 13 | // it again instead of `<strong>` 14 | const temp = document.createElement('textarea') 15 | temp.innerHTML = text 16 | text = temp.value 17 | 18 | // Although the text is fully trimmed, at the very least the last 19 | // "\n" occurrence should be always removed, as browsers add a 20 | // "
" element as soon as some rich text is written in a content 21 | // editable div (for example, if a new line is added the div content 22 | // will be "

"). 23 | return text.trim() 24 | } 25 | 26 | export { 27 | rawToParsed, 28 | } 29 | -------------------------------------------------------------------------------- /src/helpers/selector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import Vue from 'vue' 6 | 7 | const buildSelector = (selector, propsData = {}) => { 8 | return new Promise((resolve, reject) => { 9 | const container = document.createElement('div') 10 | document.getElementById('body-user').append(container) 11 | const ComponentVM = new Vue({ 12 | render: (h) => h(selector, propsData), 13 | }).$mount(container) 14 | ComponentVM.$root.$on('close', () => { 15 | ComponentVM.$el.remove() 16 | ComponentVM.$destroy() 17 | reject(new Error('Selection canceled')) 18 | }) 19 | ComponentVM.$root.$on('select', (id) => { 20 | ComponentVM.$el.remove() 21 | ComponentVM.$destroy() 22 | resolve(id) 23 | }) 24 | }) 25 | } 26 | 27 | export { 28 | buildSelector, 29 | } 30 | -------------------------------------------------------------------------------- /src/init-calendar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import { subscribe } from '@nextcloud/event-bus' 7 | import { generateUrl } from '@nextcloud/router' 8 | 9 | import './shared-init.js' 10 | 11 | subscribe('calendar:handle-todo-click', ({ calendarId, taskId }) => { 12 | const deckAppPrefix = 'app-generated--deck--board-' 13 | if (calendarId.startsWith(deckAppPrefix)) { 14 | const board = calendarId.slice(deckAppPrefix.length) 15 | const card = taskId.slice('card-'.length).replace('.ics', '') 16 | console.debug('[deck] Clicked task matches deck calendar pattern') 17 | window.location = generateUrl(`apps/deck/#/board/${board}/card/${card}`) 18 | } 19 | }) 20 | -------------------------------------------------------------------------------- /src/init-collections.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import Vue from 'vue' 7 | 8 | import './../css/collections.css' 9 | import FileSharingPicker from './views/FileSharingPicker.js' 10 | import { buildSelector } from './helpers/selector.js' 11 | 12 | import './shared-init.js' 13 | 14 | Vue.prototype.t = t 15 | Vue.prototype.n = n 16 | Vue.prototype.OC = OC 17 | 18 | window.addEventListener('DOMContentLoaded', () => { 19 | if (OCA.Sharing && OCA.Sharing.ShareSearch) { 20 | OCA.Sharing.ShareSearch.addNewResult(FileSharingPicker) 21 | } 22 | 23 | window.OCP.Collaboration.registerType('deck', { 24 | action: () => { 25 | const BoardSelector = () => import('./BoardSelector.vue') 26 | return buildSelector(BoardSelector) 27 | }, 28 | typeString: t('deck', 'Link to a board'), 29 | typeIconClass: 'icon-deck', 30 | }) 31 | 32 | window.OCP.Collaboration.registerType('deck-card', { 33 | action: () => { 34 | const CardSelector = () => import('./CardSelector.vue') 35 | return buildSelector(CardSelector) 36 | }, 37 | typeString: t('deck', 'Link to a card'), 38 | typeIconClass: 'icon-deck', 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /src/mixins/color.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import chroma from 'chroma-js' 7 | 8 | export default { 9 | methods: { 10 | textColor(hex) { 11 | return chroma(hex ?? 'ffffff').get('lab.l') > 50 ? '#000000' : '#ffffff' 12 | }, 13 | colorIsValid(hex) { 14 | const re = /[A-Fa-f0-9]{6}/ 15 | if (re.test(hex)) { 16 | return true 17 | } 18 | return false 19 | }, 20 | randomColor() { 21 | return Math.floor(Math.random() * (0xffffff + 1)).toString(16).padStart(6, '0') 22 | }, 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /src/mixins/isTouchDevice.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | export default { 7 | computed: { 8 | isTouchDevice() { 9 | return ('ontouchstart' in window) || (navigator.maxTouchPoints > 0) 10 | }, 11 | }, 12 | } 13 | -------------------------------------------------------------------------------- /src/mixins/labelStyle.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import Color from './color.js' 7 | 8 | export default { 9 | mixins: [Color], 10 | computed: { 11 | labelStyle() { 12 | return (label) => { 13 | return { 14 | backgroundColor: '#' + label.color, 15 | color: this.textColor(label.color), 16 | } 17 | } 18 | }, 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/mixins/readableDate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import moment from '@nextcloud/moment' 7 | 8 | export default { 9 | computed: { 10 | formatReadableDate() { 11 | return (timestamp) => { 12 | return moment(timestamp).format('lll') 13 | } 14 | }, 15 | }, 16 | } 17 | -------------------------------------------------------------------------------- /src/mixins/relativeDate.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import moment from '@nextcloud/moment' 7 | 8 | export default { 9 | computed: { 10 | relativeDate() { 11 | return (timestamp) => { 12 | const diff = moment(this.$root.time).diff(moment(timestamp)) 13 | if (diff >= 0 && diff < 45000) { 14 | return t('core', 'seconds ago') 15 | } 16 | return moment(timestamp).fromNow() 17 | } 18 | }, 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /src/models/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | /** 7 | * Board model 8 | * 9 | * @typedef {object} Board 10 | * @property {string} title 11 | * @property {boolean} archived 12 | * @property {number} shared 1 (shared) or 0 (not shared) 13 | */ 14 | 15 | /** 16 | * Stack model 17 | * 18 | * @typedef {object} Stack 19 | * @property {string} title 20 | * @property {number} boardId 21 | * @property {number} order 22 | */ 23 | 24 | /** 25 | * Card model 26 | * 27 | * @typedef {object} Card 28 | * @property {string} title 29 | * @property {boolean} archived 30 | * @property {number} order 31 | */ 32 | /** 33 | * Label model 34 | * 35 | * @typedef {object} Label 36 | * @property {string} title 37 | * @property {string} color 38 | */ 39 | -------------------------------------------------------------------------------- /src/services/OverviewApi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import axios from '@nextcloud/axios' 7 | import { generateOcsUrl } from '@nextcloud/router' 8 | 9 | export class OverviewApi { 10 | 11 | url(url) { 12 | return generateOcsUrl(`apps/deck/api/v1.0/${url}`) 13 | } 14 | 15 | get(filter) { 16 | return axios.get(this.url(`overview/${filter}`), { 17 | headers: { 'OCS-APIRequest': 'true' }, 18 | }) 19 | .then( 20 | (response) => Promise.resolve(response.data.ocs.data), 21 | (err) => Promise.reject(err), 22 | ) 23 | .catch((err) => Promise.reject(err), 24 | ) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/services/SessionApi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import axios from '@nextcloud/axios' 7 | import { generateOcsUrl } from '@nextcloud/router' 8 | 9 | export class SessionApi { 10 | 11 | url(url) { 12 | return generateOcsUrl(`apps/deck/api/v1.0${url}`) 13 | } 14 | 15 | async createSession(boardId) { 16 | return (await axios.put(this.url('/session/create'), { boardId })).data.ocs.data 17 | } 18 | 19 | async syncSession(boardId, token) { 20 | return await axios.post(this.url('/session/sync'), { boardId, token }) 21 | } 22 | 23 | async closeSession(boardId, token) { 24 | return await axios.post(this.url('/session/close'), { boardId, token }) 25 | } 26 | 27 | async closeSessionViaBeacon(boardId, token) { 28 | const body = { 29 | boardId, 30 | token, 31 | } 32 | const headers = { 33 | type: 'application/json', 34 | } 35 | const blob = new Blob([JSON.stringify(body)], headers) 36 | navigator.sendBeacon(this.url('/session/close'), blob) 37 | } 38 | 39 | } 40 | 41 | export const sessionApi = new SessionApi() 42 | -------------------------------------------------------------------------------- /src/services/SharingApi.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import axios from '@nextcloud/axios' 6 | import { generateOcsUrl } from '@nextcloud/router' 7 | 8 | const shareUrl = generateOcsUrl('apps/files_sharing/api/v1/shares') 9 | 10 | const createShare = async function({ path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label }) { 11 | try { 12 | const request = await axios.post(shareUrl, { path, permissions, shareType, shareWith, publicUpload, password, sendPasswordByTalk, expireDate, label }) 13 | if (!request?.data?.ocs) { 14 | throw request 15 | } 16 | return request 17 | } catch (error) { 18 | console.error('Error while creating share', error) 19 | OC.Notification.showTemporary(t('files_sharing', 'Error creating the share'), { type: 'error' }) 20 | throw error 21 | } 22 | } 23 | 24 | export { 25 | createShare, 26 | } 27 | -------------------------------------------------------------------------------- /src/shared-init.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | import { generateFilePath } from '@nextcloud/router' 6 | 7 | // eslint-disable-next-line 8 | __webpack_nonce__ = btoa(OC.requestToken) 9 | 10 | if (!process.env.WEBPACK_SERVE) { 11 | // eslint-disable-next-line 12 | __webpack_public_path__ = generateFilePath('deck', '', 'js/') 13 | } else { 14 | // eslint-disable-next-line 15 | __webpack_public_path__ = 'http://127.0.0.1:3000/' 16 | } 17 | -------------------------------------------------------------------------------- /src/store/actions.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | export default { 7 | state: { 8 | actions: { 9 | card: [], 10 | }, 11 | }, 12 | getters: { 13 | cardActions: (state) => state.actions.card, 14 | }, 15 | mutations: { 16 | ADD_CARD_ACTION(state, action) { 17 | state.actions.card.push(action) 18 | }, 19 | }, 20 | actions: { 21 | async addCardAction({ commit }, action) { 22 | commit('ADD_CARD_ACTION', action) 23 | }, 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /src/store/dashboard.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import Vue from 'vue' 7 | import Vuex from 'vuex' 8 | import { OverviewApi } from '../services/OverviewApi.js' 9 | 10 | Vue.use(Vuex) 11 | 12 | const apiClient = new OverviewApi() 13 | 14 | export default { 15 | state: { 16 | assignedCards: [], 17 | }, 18 | getters: { 19 | assignedCardsDashboard: state => { 20 | return Object.values(state.assignedCards).flat() 21 | }, 22 | }, 23 | mutations: { 24 | setAssignedCards(state, assignedCards) { 25 | state.assignedCards = assignedCards 26 | }, 27 | }, 28 | actions: { 29 | async loadUpcoming({ commit }) { 30 | const upcommingCards = await apiClient.get('upcoming') 31 | commit('setAssignedCards', upcommingCards) 32 | }, 33 | }, 34 | } 35 | -------------------------------------------------------------------------------- /src/store/overview.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import Vue from 'vue' 7 | import Vuex from 'vuex' 8 | import { OverviewApi } from '../services/OverviewApi.js' 9 | Vue.use(Vuex) 10 | 11 | const apiClient = new OverviewApi() 12 | export default { 13 | state: { 14 | assignedCards: [], 15 | loading: false, 16 | }, 17 | getters: { 18 | assignedCardsDashboard: state => { 19 | return state.assignedCards 20 | }, 21 | }, 22 | mutations: { 23 | setAssignedCards(state, assignedCards) { 24 | state.assignedCards = assignedCards 25 | }, 26 | setLoading(state, promise) { 27 | state.loading = promise 28 | }, 29 | }, 30 | actions: { 31 | async loadUpcoming({ state, commit }) { 32 | if (state.loading) { 33 | return state.loading 34 | } 35 | const promise = (async () => { 36 | commit('setCurrentBoard', null) 37 | const assignedCards = await apiClient.get('upcoming') 38 | const assignedCardsFlat = Object.values(assignedCards).flat() 39 | for (const i in assignedCardsFlat) { 40 | commit('addCard', assignedCardsFlat[i]) 41 | } 42 | commit('setAssignedCards', assignedCards) 43 | commit('setLoading', false) 44 | })() 45 | commit('setLoading', promise) 46 | return promise 47 | }, 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /src/views/FileSharingPicker.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors 3 | * SPDX-License-Identifier: AGPL-3.0-or-later 4 | */ 5 | 6 | import Vue from 'vue' 7 | import { createShare } from '../services/SharingApi.js' 8 | 9 | export default { 10 | icon: 'icon-deck', 11 | displayName: t('deck', 'Share with a Deck card'), 12 | handler: async self => { 13 | 14 | return new Promise((resolve, reject) => { 15 | const container = document.createElement('div') 16 | container.id = 'deck-board-select' 17 | const body = document.getElementById('body-user') 18 | body.append(container) 19 | const CardSelector = () => import('./../CardSelector.vue') 20 | const ComponentVM = new Vue({ 21 | render: (h) => h(CardSelector, { 22 | title: t('deck', 'Share {file} with a Deck card', { file: decodeURIComponent(self.fileInfo.name) }), 23 | action: t('deck', 'Share'), 24 | }), 25 | }) 26 | ComponentVM.$mount(container) 27 | ComponentVM.$root.$on('close', () => { 28 | ComponentVM.$el.remove() 29 | ComponentVM.$destroy() 30 | reject(new Error('Canceled')) 31 | }) 32 | ComponentVM.$root.$on('select', async (id) => { 33 | const result = await createShare({ 34 | path: self.fileInfo.path + '/' + self.fileInfo.name, 35 | shareType: 12, 36 | shareWith: '' + id, 37 | }) 38 | ComponentVM.$el.remove() 39 | ComponentVM.$destroy() 40 | resolve(result.data.ocs.data) 41 | }) 42 | 43 | }) 44 | }, 45 | condition: self => true, 46 | } 47 | -------------------------------------------------------------------------------- /stylelint.config.js: -------------------------------------------------------------------------------- 1 | const stylelintConfig = require('@nextcloud/stylelint-config') 2 | 3 | module.exports = stylelintConfig 4 | -------------------------------------------------------------------------------- /templates/main.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @author Julius Härtl 7 | * 8 | * @license GNU AGPL version 3 or any later version 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as 12 | * published by the Free Software Foundation, either version 3 of the 13 | * License, or (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | * 23 | */ 24 | 25 | require_once __DIR__ . '/../../../tests/bootstrap.php'; 26 | require_once __DIR__ . '/../appinfo/autoload.php'; 27 | -------------------------------------------------------------------------------- /tests/data/config-deckJson.json: -------------------------------------------------------------------------------- 1 | { 2 | "owner": "admin", 3 | "color": "0800fd", 4 | "uidRelation": { 5 | "johndoe": "test-user-1" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/data/config-trelloJson.json: -------------------------------------------------------------------------------- 1 | { 2 | "owner": "admin", 3 | "color": "0800fd", 4 | "uidRelation": { 5 | "johndoe": "johndoe" 6 | } 7 | } -------------------------------------------------------------------------------- /tests/data/test.txt: -------------------------------------------------------------------------------- 1 | Hello world 2 | -------------------------------------------------------------------------------- /tests/integration/app/AppTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @author Julius Härtl 7 | * 8 | * @license GNU AGPL version 3 or any later version 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as 12 | * published by the Free Software Foundation, either version 3 of the 13 | * License, or (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | * 23 | */ 24 | 25 | use Test\TestCase; 26 | 27 | /** 28 | * This test shows how to make a small Integration Test. Query your class 29 | * directly from the container, only pass in mocks if needed and run your tests 30 | * against the database 31 | */ 32 | class AppTest extends TestCase { 33 | private $container; 34 | private $app; 35 | 36 | public function setUp(): void { 37 | parent::setUp(); 38 | $this->app = \OCP\Server::get(\OCA\Deck\AppInfo\Application::class); 39 | $this->container = $this->app->getContainer(); 40 | } 41 | 42 | public function testAppInstalled() { 43 | $appManager = $this->container->query('OCP\App\IAppManager'); 44 | $this->assertTrue($appManager->isInstalled('deck')); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /tests/integration/base-query-count.txt: -------------------------------------------------------------------------------- 1 | 81091 2 | -------------------------------------------------------------------------------- /tests/integration/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "phpunit/phpunit": "~9", 4 | "behat/behat": "~3.21.1", 5 | "guzzlehttp/guzzle": "7.9.2", 6 | "jarnaiz/behat-junit-formatter": "^1.3", 7 | "sabre/dav": "4.7.0", 8 | "symfony/event-dispatcher": "~5.4" 9 | }, 10 | "autoload": { 11 | "psr-0": { 12 | "": [ 13 | "features/bootstrap/", 14 | "../../../../build/integration/features/bootstrap/" 15 | ] 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/integration/config/behat.yml: -------------------------------------------------------------------------------- 1 | default: 2 | suites: 3 | test: 4 | paths: 5 | - '%paths.base%/../features/' 6 | contexts: 7 | - ServerContext: 8 | baseUrl: http://localhost:8080/ 9 | - RequestContext 10 | - BoardContext 11 | - CommentContext 12 | - AttachmentContext 13 | - SearchContext 14 | - SessionContext 15 | -------------------------------------------------------------------------------- /tests/integration/features/bootstrap/RequestTrait.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @author Julius Härtl 7 | * 8 | * @license GNU AGPL version 3 or any later version 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as 12 | * published by the Free Software Foundation, either version 3 of the 13 | * License, or (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | * 23 | */ 24 | 25 | declare(strict_types=1); 26 | 27 | 28 | use Behat\Behat\Hook\Scope\BeforeScenarioScope; 29 | 30 | require_once __DIR__ . '/../../vendor/autoload.php'; 31 | 32 | 33 | trait RequestTrait { 34 | 35 | /** @var RequestContext */ 36 | protected $requestContext; 37 | 38 | /** @BeforeScenario */ 39 | public function gatherRequestTraitContext(BeforeScenarioScope $scope) { 40 | $environment = $scope->getEnvironment(); 41 | $this->requestContext = $environment->getContext('RequestContext'); 42 | } 43 | 44 | public function getResponse() { 45 | return $this->requestContext->getResponse(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/integration/features/bootstrap/ServerContext.php: -------------------------------------------------------------------------------- 1 | rawBaseUrl = $baseUrl; 19 | 20 | $testServerUrl = getenv('BEHAT_SERVER_URL'); 21 | if ($testServerUrl !== false) { 22 | $this->rawBaseUrl = rtrim($testServerUrl, '/'); 23 | } 24 | 25 | $this->__tConstruct($this->rawBaseUrl . '/ocs/', ['admin', 'admin'], '123456'); 26 | } 27 | 28 | 29 | /** 30 | * @BeforeSuite 31 | */ 32 | public static function addFilesToSkeleton() { 33 | } 34 | 35 | /** 36 | * @Given /^acting as user "([^"]*)"$/ 37 | */ 38 | public function actingAsUser($user) { 39 | $this->cookieJar = new CookieJar(); 40 | $this->loggingInUsingWebAs($user); 41 | $this->asAn($user); 42 | } 43 | 44 | public function getBaseUrl(): string { 45 | return $this->rawBaseUrl; 46 | } 47 | 48 | public function getCookieJar(): CookieJar { 49 | return $this->cookieJar; 50 | } 51 | 52 | public function getReqestToken(): string { 53 | return $this->requestToken; 54 | } 55 | 56 | public function getCurrentUser(): string { 57 | return $this->currentUser; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tests/integration/features/sessions.feature: -------------------------------------------------------------------------------- 1 | Feature: Sessions 2 | 3 | Background: 4 | Given user "admin" exists 5 | And user "user0" exists 6 | And user "user1" exists 7 | Given acting as user "user0" 8 | And creates a board named "Shared board" with color "fafafa" 9 | And shares the board with user "user1" 10 | 11 | 12 | Scenario: Open a board with multiple users 13 | Given acting as user "user0" 14 | And user opens the board named "Shared board" 15 | When fetches the board named "Shared board" 16 | Then the response should have a status code "200" 17 | And the response should have a list of active sessions with the length 1 18 | And the user "user0" should be in the list of active sessions 19 | 20 | Given acting as user "user1" 21 | And user opens the board named "Shared board" 22 | When fetches the board named "Shared board" 23 | Then the response should have a status code "200" 24 | And the response should have a list of active sessions with the length 2 25 | And the user "user0" should be in the list of active sessions 26 | And the user "user1" should be in the list of active sessions 27 | 28 | When user closes the board named "Shared board" 29 | And fetches the board named "Shared board" 30 | Then the response should have a status code "200" 31 | And the response should have a list of active sessions with the length 1 32 | And the user "user0" should be in the list of active sessions 33 | 34 | -------------------------------------------------------------------------------- /tests/integration/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | OC_PATH=../../../../ 4 | OCC=${OC_PATH}occ 5 | SCENARIO_TO_RUN=$1 6 | HIDE_OC_LOGS=$2 7 | 8 | # Nextcloud integration tests composer 9 | ( 10 | cd ${OC_PATH}build/integration 11 | composer install 12 | ) 13 | INSTALLED=$($OCC status | grep installed: | cut -d " " -f 5) 14 | 15 | if [ "$INSTALLED" == "true" ]; then 16 | $OCC app:enable deck 17 | else 18 | echo "Nextcloud instance needs to be installed" >&2 19 | exit 1 20 | fi 21 | 22 | composer install 23 | 24 | # avoid port collision on jenkins - use $EXECUTOR_NUMBER 25 | if [ -z "$EXECUTOR_NUMBER" ]; then 26 | EXECUTOR_NUMBER=0 27 | fi 28 | PORT=$((9090 + $EXECUTOR_NUMBER)) 29 | echo $PORT 30 | php -q -S localhost:$PORT -t $OC_PATH & 31 | PHPPID=$! 32 | echo $PHPPID 33 | 34 | export TEST_SERVER_URL="http://localhost:$PORT/ocs/" 35 | 36 | vendor/bin/behat --colors $SCENARIO_TO_RUN 37 | RESULT=$? 38 | 39 | kill -9 $PHPPID 40 | 41 | echo "runsh: Exit code: $RESULT" 42 | exit $RESULT 43 | -------------------------------------------------------------------------------- /tests/phpunit.integration.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./../lib 6 | 7 | 8 | 9 | 10 | ./integration/database 11 | 12 | 13 | ./integration/app 14 | 15 | 16 | ./integration/import 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ./../lib 6 | 7 | 8 | 9 | 10 | ./unit 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /tests/unit/Search/Query/AQueryParameterTest.php: -------------------------------------------------------------------------------- 1 | 5 | * 6 | * @author Julius Härtl 7 | * 8 | * @license GNU AGPL version 3 or any later version 9 | * 10 | * This program is free software: you can redistribute it and/or modify 11 | * it under the terms of the GNU Affero General Public License as 12 | * published by the Free Software Foundation, either version 3 of the 13 | * License, or (at your option) any later version. 14 | * 15 | * This program is distributed in the hope that it will be useful, 16 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | * GNU Affero General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU Affero General Public License 21 | * along with this program. If not, see . 22 | * 23 | */ 24 | 25 | declare(strict_types=1); 26 | 27 | namespace OCA\Deck\Search\Query; 28 | 29 | use PHPUnit\Framework\TestCase; 30 | 31 | class AQueryParameterTest extends TestCase { 32 | public static function dataValue() { 33 | return [ 34 | ['foo', 'foo'], 35 | ['spätial character', 'spätial character'], 36 | ['"spätial character"', 'spätial character'], 37 | ['"spätial "character"', 'spätial "character'], 38 | ['"spätial 🐘"', 'spätial 🐘'], 39 | ['\'spätial character\'', '\'spätial character\''], 40 | ]; 41 | } 42 | 43 | /** @dataProvider dataValue */ 44 | public function testValue($input, $expectedValue) { 45 | $parameter = new StringQueryParameter('test', 0, $input); 46 | $this->assertEquals($expectedValue, $parameter->getValue()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /webpack.js: -------------------------------------------------------------------------------- 1 | const webpackConfig = require('@nextcloud/webpack-vue-config') 2 | const webpack = require('webpack') 3 | const path = require('path') 4 | 5 | const buildMode = process.env.NODE_ENV 6 | const isDevServer = process.env.WEBPACK_SERVE 7 | 8 | webpackConfig.entry = { 9 | ...webpackConfig.entry, 10 | collections: path.join(__dirname, 'src', 'init-collections.js'), 11 | dashboard: path.join(__dirname, 'src', 'init-dashboard.js'), 12 | calendar: path.join(__dirname, 'src', 'init-calendar.js'), 13 | talk: path.join(__dirname, 'src', 'init-talk.js'), 14 | reference: path.join(__dirname, 'src', 'init-reference.js'), 15 | } 16 | 17 | if (isDevServer) { 18 | webpackConfig.output.publicPath = 'http://127.0.0.1:3000/' 19 | webpackConfig.plugins.push( 20 | new webpack.DefinePlugin({ 21 | 'process.env.WEBPACK_SERVE': true, 22 | }) 23 | ) 24 | } else { 25 | webpackConfig.stats = { 26 | context: path.resolve(__dirname, 'src'), 27 | assets: true, 28 | entrypoints: true, 29 | chunks: true, 30 | modules: true, 31 | } 32 | } 33 | // Workaround for https://github.com/nextcloud/webpack-vue-config/pull/432 causing problems with nextcloud-vue-collections 34 | webpackConfig.resolve.alias = {} 35 | 36 | module.exports = webpackConfig 37 | --------------------------------------------------------------------------------