Compare commits

..

1 Commits

Author SHA1 Message Date
dependabot[bot]
91391ea760 Chore(deps): Bump shivammathur/setup-php from 2.34.1 to 2.35.5
Bumps [shivammathur/setup-php](https://github.com/shivammathur/setup-php) from 2.34.1 to 2.35.5.
- [Release notes](https://github.com/shivammathur/setup-php/releases)
- [Commits](https://github.com/shivammathur/setup-php/compare/2.34.1...2.35.5)

---
updated-dependencies:
- dependency-name: shivammathur/setup-php
  dependency-version: 2.35.5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-20 02:38:23 +00:00
112 changed files with 1837 additions and 5131 deletions

View File

@@ -24,7 +24,7 @@ jobs:
- name: Set up npm7
run: npm i -g npm@7
- name: Setup PHP
uses: shivammathur/setup-php@2.34.1
uses: shivammathur/setup-php@2.35.5
with:
php-version: '7.4'
tools: composer

View File

@@ -87,7 +87,7 @@ jobs:
filename: ${{ env.APP_NAME }}/appinfo/info.xml
- name: Set up php ${{ steps.php-versions.outputs.php-min }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: ${{ steps.php-versions.outputs.php-min }}
coverage: none

View File

@@ -91,7 +91,7 @@ jobs:
restore-keys: ${{ steps.extcache.outputs.key }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@2.34.1
uses: shivammathur/setup-php@2.35.5
with:
php-version: ${{ matrix.php-versions }}
extensions: ${{ env.extensions }}

View File

@@ -78,7 +78,7 @@ jobs:
path: apps/activity
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@2.34.1
uses: shivammathur/setup-php@2.35.5
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, mysql, pdo_mysql, pgsql, pdo_pgsql, apcu, gd

View File

@@ -34,7 +34,7 @@ jobs:
uses: icewind1991/nextcloud-version-matrix@58becf3b4bb6dc6cef677b15e2fd8e7d48c0908f # v1.3.1
- name: Set up php${{ steps.versions.outputs.php-min }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: ${{ steps.versions.outputs.php-min }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite

View File

@@ -48,7 +48,7 @@ jobs:
persist-credentials: false
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: ${{ matrix.php-versions }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite

View File

@@ -103,7 +103,7 @@ jobs:
path: apps/${{ env.APP_NAME }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation

View File

@@ -106,7 +106,7 @@ jobs:
path: apps/${{ env.APP_NAME }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation

View File

@@ -95,7 +95,7 @@ jobs:
path: apps/${{ env.APP_NAME }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: ${{ matrix.php-versions }}
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation

View File

@@ -36,7 +36,7 @@ jobs:
run: grep 'phpVersion="${{ steps.versions.outputs.php-min }}' psalm.xml
- name: Set up php${{ steps.versions.outputs.php-min }}
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: ${{ steps.versions.outputs.php-min }}
extensions: bz2, ctype, curl, dom, fileinfo, gd, iconv, intl, json, libxml, mbstring, openssl, pcntl, posix, session, simplexml, xmlreader, xmlwriter, zip, zlib, sqlite, pdo_sqlite

View File

@@ -38,7 +38,7 @@ jobs:
- name: Set up php8.2
if: steps.checkout.outcome == 'success'
uses: shivammathur/setup-php@0f7f1d08e3e32076e51cae65eb0b0c871405b16e # v2.34.1
uses: shivammathur/setup-php@bf6b4fbd49ca58e4608c9c89fba0b8d90bd2a39f # v2.35.5
with:
php-version: 8.2
# https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation

View File

@@ -24,7 +24,7 @@ Deck is a kanban style organization tool aimed at personal planning and project
### Mobile apps
- [Nextcloud Deck app for Android](https://github.com/stefan-niedermann/nextcloud-deck) - It is available in [F-Droid](https://f-droid.org/de/packages/it.niedermann.nextcloud.deck/) and the [Google Play Store](https://play.google.com/store/apps/details?id=it.niedermann.nextcloud.deck.play)
- Nextcloud Deck app for iOS - It is available in [Apple App store](https://apps.apple.com/de/app/next-deck/id6752478755)
- [Nextcloud Deck app for iOS](https://github.com/StCyr/deck-react-native) - It is available in [Apple App store](https://apps.apple.com/ml/app/nextcloud-deck/id1570892788)
### 3rd-Party Integrations

View File

@@ -9,35 +9,31 @@
}
],
"require": {
"justinrainbow/json-schema": "^6.0",
"bamarni/composer-bin-plugin": "^1.8"
"justinrainbow/json-schema": "^6.0"
},
"require-dev": {
"roave/security-advisories": "dev-master",
"phpunit/phpunit": "^9",
"nextcloud/coding-standard": "^1.1",
"nextcloud/ocp": "dev-master"
"nextcloud/ocp": "dev-master",
"psalm/phar": "^5.13"
},
"config": {
"optimize-autoloader": true,
"allow-plugins": {
"composer/package-versions-deprecated": true,
"bamarni/composer-bin-plugin": true
"composer/package-versions-deprecated": true
},
"platform": {
"php": "8.1"
}
},
"scripts": {
"post-install-cmd": [
"@composer bin all install --ansi"
],
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"psalm": "psalm",
"psalm:update-baseline": "psalm --threads=$(nproc) --no-cache --update-baseline",
"psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType",
"psalm": "psalm.phar",
"psalm:update-baseline": "psalm.phar --update-baseline",
"psalm:fix": "psalm.phar --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType",
"test": [
"@test:unit",
"@test:integration"

102
composer.lock generated
View File

@@ -4,65 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "263f9ff9e6a13d50ab09bc9f4e06b749",
"content-hash": "6950663d9d213151028e780637480220",
"packages": [
{
"name": "bamarni/composer-bin-plugin",
"version": "1.8.2",
"source": {
"type": "git",
"url": "https://github.com/bamarni/composer-bin-plugin.git",
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/bamarni/composer-bin-plugin/zipball/92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"reference": "92fd7b1e6e9cdae19b0d57369d8ad31a37b6a880",
"shasum": ""
},
"require": {
"composer-plugin-api": "^2.0",
"php": "^7.2.5 || ^8.0"
},
"require-dev": {
"composer/composer": "^2.0",
"ext-json": "*",
"phpstan/extension-installer": "^1.1",
"phpstan/phpstan": "^1.8",
"phpstan/phpstan-phpunit": "^1.1",
"phpunit/phpunit": "^8.5 || ^9.5",
"symfony/console": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/finder": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0",
"symfony/process": "^2.8.52 || ^3.4.35 || ^4.4 || ^5.0 || ^6.0"
},
"type": "composer-plugin",
"extra": {
"class": "Bamarni\\Composer\\Bin\\BamarniBinPlugin"
},
"autoload": {
"psr-4": {
"Bamarni\\Composer\\Bin\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "No conflicts for your bin dependencies",
"keywords": [
"composer",
"conflict",
"dependency",
"executable",
"isolation",
"tool"
],
"support": {
"issues": "https://github.com/bamarni/composer-bin-plugin/issues",
"source": "https://github.com/bamarni/composer-bin-plugin/tree/1.8.2"
},
"time": "2022-10-31T08:38:03+00:00"
},
{
"name": "justinrainbow/json-schema",
"version": "6.4.2",
@@ -437,12 +380,12 @@
"source": {
"type": "git",
"url": "https://github.com/nextcloud-deps/ocp.git",
"reference": "9a2e6c0bf6f2d87e1db8d18063a5bedf85040bb2"
"reference": "f087138947c284c5db161308255b80696ff6b5c8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/9a2e6c0bf6f2d87e1db8d18063a5bedf85040bb2",
"reference": "9a2e6c0bf6f2d87e1db8d18063a5bedf85040bb2",
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/f087138947c284c5db161308255b80696ff6b5c8",
"reference": "f087138947c284c5db161308255b80696ff6b5c8",
"shasum": ""
},
"require": {
@@ -478,7 +421,7 @@
"issues": "https://github.com/nextcloud-deps/ocp/issues",
"source": "https://github.com/nextcloud-deps/ocp/tree/master"
},
"time": "2025-09-27T00:45:05+00:00"
"time": "2025-09-10T00:46:52+00:00"
},
{
"name": "nikic/php-parser",
@@ -1130,6 +1073,41 @@
],
"time": "2024-12-05T13:48:26+00:00"
},
{
"name": "psalm/phar",
"version": "5.26.1",
"source": {
"type": "git",
"url": "https://github.com/psalm/phar.git",
"reference": "8a38e7ad04499a0ccd2c506fd1da6fc01fff4547"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/psalm/phar/zipball/8a38e7ad04499a0ccd2c506fd1da6fc01fff4547",
"reference": "8a38e7ad04499a0ccd2c506fd1da6fc01fff4547",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0"
},
"conflict": {
"vimeo/psalm": "*"
},
"bin": [
"psalm.phar"
],
"type": "library",
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Composer-based Psalm Phar",
"support": {
"issues": "https://github.com/psalm/phar/issues",
"source": "https://github.com/psalm/phar/tree/5.26.1"
},
"time": "2024-09-09T16:22:43+00:00"
},
{
"name": "psr/clock",
"version": "1.0.0",

View File

@@ -9,11 +9,9 @@ OC.L10N.register(
"Missing a temporary folder" : "Адсутнічае часовая папка",
"Could not write file to disk" : "Не ўдалося запісаць файл на дыск",
"A PHP extension stopped the file upload" : "Пашырэнне PHP спыніла запампоўванне файла",
"No file uploaded or file size exceeds maximum of %s" : "Файл не запампаваны, або памер файла перавышае максімальны %s",
"copy" : "копія",
"Done" : "Гатова",
"File" : "Файл",
"Invalid date, date format must be YYYY-MM-DD" : "Памылковая дата, дата павінна быць у фармаце ГГГГ-ММ-ДД",
"Cancel" : "Скасаваць",
"Drop your files to upload" : "Перацягніце файлы для запампоўвання",
"File already exists" : "Файл ужо існуе",
@@ -39,7 +37,6 @@ OC.L10N.register(
"Delete" : "Выдаліць",
"Edit" : "Рэдагаваць",
"Members" : "Удзельнікі",
"File to share" : "Файл для абагульвання",
"Invalid path selected" : "Выбраны памылковы шлях",
"Share from Files" : "Абагуліць з Файлаў",
"Show in Files" : "Паказаць у Файлах",
@@ -52,15 +49,7 @@ OC.L10N.register(
"Reply" : "Адказаць",
"Update" : "Абнавіць",
"Description" : "Апісанне",
"Formatting help" : "Даведка па фармаціраванні",
"Later today {timeLocale}" : "Пазней сёння {timeLocale}",
"Set due date for later today" : "Задаць дату выканання на пазней сёння",
"Tomorrow {timeLocale}" : "Заўтра {timeLocale}",
"This weekend {timeLocale}" : "У гэты ўік-энд {timeLocale}",
"Set due date for this weekend" : "Задаць дату выканання на гэты ўік-энд",
"Next week {timeLocale}" : "На наступным тыдні {timeLocale}",
"Set due date for next week" : "Задаць дату выканання на наступны тыдзень",
"Create a new tag:" : "Стварыць новы тэг:",
"(group)" : "(група)",
"Open link" : "Адкрыць спасылку",
"Edit title" : "Рэдагаваць загаловак",
@@ -69,7 +58,6 @@ OC.L10N.register(
"Keyboard shortcut" : "Спалучэнне клавіш",
"Action" : "Дзеянне",
"Shift" : "Shift",
"Ctrl" : "Ctrl",
"Search" : "Пошук",
"Enter" : "Enter",
"Shared with you" : "Абагулена з вамі",
@@ -83,10 +71,6 @@ OC.L10N.register(
"Message from {author} in {conversationName}" : "Паведамленне ад {author} у {conversationName}",
"Failed to upload {name}" : "Не ўдалося запампаваць {name}",
"Share" : "Абагуліць",
"Personal" : "Асабістыя",
"Example Task 3" : "Прыклад задання 3",
"Example Task 2" : "Прыклад задання 2",
"Example Task 1" : "Прыклад задання 1",
"Today" : "Сёння",
"Tomorrow" : "Заўтра"
},

View File

@@ -7,11 +7,9 @@
"Missing a temporary folder" : "Адсутнічае часовая папка",
"Could not write file to disk" : "Не ўдалося запісаць файл на дыск",
"A PHP extension stopped the file upload" : "Пашырэнне PHP спыніла запампоўванне файла",
"No file uploaded or file size exceeds maximum of %s" : "Файл не запампаваны, або памер файла перавышае максімальны %s",
"copy" : "копія",
"Done" : "Гатова",
"File" : "Файл",
"Invalid date, date format must be YYYY-MM-DD" : "Памылковая дата, дата павінна быць у фармаце ГГГГ-ММ-ДД",
"Cancel" : "Скасаваць",
"Drop your files to upload" : "Перацягніце файлы для запампоўвання",
"File already exists" : "Файл ужо існуе",
@@ -37,7 +35,6 @@
"Delete" : "Выдаліць",
"Edit" : "Рэдагаваць",
"Members" : "Удзельнікі",
"File to share" : "Файл для абагульвання",
"Invalid path selected" : "Выбраны памылковы шлях",
"Share from Files" : "Абагуліць з Файлаў",
"Show in Files" : "Паказаць у Файлах",
@@ -50,15 +47,7 @@
"Reply" : "Адказаць",
"Update" : "Абнавіць",
"Description" : "Апісанне",
"Formatting help" : "Даведка па фармаціраванні",
"Later today {timeLocale}" : "Пазней сёння {timeLocale}",
"Set due date for later today" : "Задаць дату выканання на пазней сёння",
"Tomorrow {timeLocale}" : "Заўтра {timeLocale}",
"This weekend {timeLocale}" : "У гэты ўік-энд {timeLocale}",
"Set due date for this weekend" : "Задаць дату выканання на гэты ўік-энд",
"Next week {timeLocale}" : "На наступным тыдні {timeLocale}",
"Set due date for next week" : "Задаць дату выканання на наступны тыдзень",
"Create a new tag:" : "Стварыць новы тэг:",
"(group)" : "(група)",
"Open link" : "Адкрыць спасылку",
"Edit title" : "Рэдагаваць загаловак",
@@ -67,7 +56,6 @@
"Keyboard shortcut" : "Спалучэнне клавіш",
"Action" : "Дзеянне",
"Shift" : "Shift",
"Ctrl" : "Ctrl",
"Search" : "Пошук",
"Enter" : "Enter",
"Shared with you" : "Абагулена з вамі",
@@ -81,10 +69,6 @@
"Message from {author} in {conversationName}" : "Паведамленне ад {author} у {conversationName}",
"Failed to upload {name}" : "Не ўдалося запампаваць {name}",
"Share" : "Абагуліць",
"Personal" : "Асабістыя",
"Example Task 3" : "Прыклад задання 3",
"Example Task 2" : "Прыклад задання 2",
"Example Task 1" : "Прыклад задання 1",
"Today" : "Сёння",
"Tomorrow" : "Заўтра"
},"pluralForm" :"nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);"

View File

@@ -51,8 +51,8 @@ OC.L10N.register(
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} har tilknyttet kortet {deck-card} på {deck-board} til dig.",
"The card \"%s\" on \"%s\" has reached its due date." : "Kortet \"%s\" på \"%s\" har nået sin forfaldsdato.",
"The card {deck-card} on {deck-board} has reached its due date." : "Kortet {deck-card} på {deck-board} har nået sin forfaldsdato.",
"%s has mentioned you in a comment on \"%s\"." : " %s har omtalt dig i en kommentar på \"%s\".",
"{user} has mentioned you in a comment on {deck-card}." : "{user} har omtalt dig i en kommentar på {deck-card}.",
"%s has mentioned you in a comment on \"%s\"." : " %s har nævnt dig i en kommentar på \"%s\".",
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nævnt dig i en kommentar på {deck-card}.",
"The board \"%s\" has been shared with you by %s." : "Tavlen \"%s\" er blevet delt med dig af %s.",
"{user} has shared {deck-board} with you." : "{user} har delt {deck-board} med dig.",
"Deck board" : "Opslagstavle",
@@ -188,10 +188,6 @@ OC.L10N.register(
"Add Attachment" : "Tilføj vedhæftning",
"Choose attachment" : "Vælg en vedhæftning",
"Select Date" : "Vælg dato",
"Later today {timeLocale}" : "Senere i dag {timeLocale}",
"Tomorrow {timeLocale}" : "I morgen {timeLocale}",
"This weekend {timeLocale}" : "Denne weekend {timeLocale}",
"Next week {timeLocale}" : "Næste uge {timeLocale}",
"Set a due date" : "Angiv en forfaldsdato",
"Remove due date" : "Fjern forfaldsdato",
"Mark as done" : "Marker som færdig",

View File

@@ -49,8 +49,8 @@
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} har tilknyttet kortet {deck-card} på {deck-board} til dig.",
"The card \"%s\" on \"%s\" has reached its due date." : "Kortet \"%s\" på \"%s\" har nået sin forfaldsdato.",
"The card {deck-card} on {deck-board} has reached its due date." : "Kortet {deck-card} på {deck-board} har nået sin forfaldsdato.",
"%s has mentioned you in a comment on \"%s\"." : " %s har omtalt dig i en kommentar på \"%s\".",
"{user} has mentioned you in a comment on {deck-card}." : "{user} har omtalt dig i en kommentar på {deck-card}.",
"%s has mentioned you in a comment on \"%s\"." : " %s har nævnt dig i en kommentar på \"%s\".",
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nævnt dig i en kommentar på {deck-card}.",
"The board \"%s\" has been shared with you by %s." : "Tavlen \"%s\" er blevet delt med dig af %s.",
"{user} has shared {deck-board} with you." : "{user} har delt {deck-board} med dig.",
"Deck board" : "Opslagstavle",
@@ -186,10 +186,6 @@
"Add Attachment" : "Tilføj vedhæftning",
"Choose attachment" : "Vælg en vedhæftning",
"Select Date" : "Vælg dato",
"Later today {timeLocale}" : "Senere i dag {timeLocale}",
"Tomorrow {timeLocale}" : "I morgen {timeLocale}",
"This weekend {timeLocale}" : "Denne weekend {timeLocale}",
"Next week {timeLocale}" : "Næste uge {timeLocale}",
"Set a due date" : "Angiv en forfaldsdato",
"Remove due date" : "Fjern forfaldsdato",
"Mark as done" : "Marker som færdig",

View File

@@ -81,14 +81,10 @@ OC.L10N.register(
"Could not write file to disk" : "Αδυναμία εγγραφής αρχείου στον δίσκο",
"A PHP extension stopped the file upload" : "Ένα πρόσθετο PHP διέκοψε την μεταφόρτωση του αρχείου",
"No file uploaded or file size exceeds maximum of %s" : "Δεν μεταφορτώθηκε αρχείο ή το μέγεθος αρχείου υπερβαίνει το μέγιστο %s",
"Invalid file type. Only JSON files are allowed." : "Μη έγκυρος τύπος αρχείου. Επιτρέπονται μόνο αρχεία JSON.",
"Invalid JSON data" : "Μη έγκυρα δεδομένα JSON",
"Failed to import board" : "Αποτυχία εισαγωγής πίνακα",
"Cards due today" : "Κάρτες που λήγουν σήμερα",
"Cards due tomorrow" : "Κάρτες που λήγουν αύριο",
"Upcoming cards" : "Επερχόμενες καρτέλες",
"Load more" : "Φόρτωση περισσότερων",
"Welcome to Nextcloud Deck!" : "Καλώς ήρθατε στο Nextcloud Deck!",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Η καρτέλα \"%s\" του \"%s\" ανατέθηκε σε εσάς από τον %s.",
"{user} has assigned the card {deck-card} on {deck-board} to you." : "Ο/Η {user} έχει αναθέσει την καρτέλα {deck-card} του πίνακα {deck-board} σε εσάς.",
"The card \"%s\" on \"%s\" has reached its due date." : "Η καρτέλα \"%s\" στο \"%s\" έχει λήξει.",
@@ -100,7 +96,6 @@ OC.L10N.register(
"Deck board" : "Πίνακας του Deck",
"Owned by %1$s" : "Ανήκει στον/στην %1$s",
"Deck boards, cards and comments" : "Πίνακες, κάρτες και σχόλια Deck",
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Από %1$s, στον %2$s/%3$s, που ανήκει στον %4$s",
"Create a new deck card" : "Δημιουργήστε μια νέα κάρτα",
"Card comments" : "Σχόλια καρτέλας",
"%s on %s" : "%s στο %s",
@@ -111,20 +106,11 @@ OC.L10N.register(
"Action needed" : "Απαιτείται ενέργεια",
"Later" : "Αργότερα",
"copy" : "Αντιγραφή",
"Read more inside" : "Διαβάστε περισσότερα εντός",
"Custom lists - click to rename!" : "Προσαρμοσμένες λίστες - κάντε κλικ για μετονομασία!",
"To Do" : "Προς Ενέργεια",
"In Progress" : "Σε Εξέλιξη",
"Done" : "Ολοκληρώθηκε",
"1. Open to learn more about boards and cards" : "1. Ανοίξτε για να μάθετε περισσότερα για τους πίνακες και τις κάρτες",
"2. Drag cards left and right, up and down" : "2. Σύρετε κάρτες αριστερά και δεξιά, πάνω και κάτω",
"3. Apply rich formatting and link content" : "3. Εφαρμόστε πλούσια μορφοποίηση και συνδέστε περιεχόμενο",
"4. Share, comment and collaborate!" : "4. Μοιραστείτε, σχολιάστε και συνεργαστείτε!",
"Create your first card!" : "Δημιουργήστε την πρώτη σας κάρτα!",
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Αυτό το σχόλιο έχει περισσότερους από %s χαρακτήρες.\nΠροστέθηκε ως συνημμένο στην καρτέλα με όνομα %s .\nΠροσβάσιμο στη διεύθυνση URL: %s.",
"Attachments" : "Συνημμένα",
"File" : "Αρχείο",
"date" : "ημερομηνία",
"Card not found" : "Η καρτέλα δεν βρέθηκε",
"Path is already shared with this card" : "Η διαδρομή κοινοποιείται ήδη σε αυτήν την καρτέλα",
"Invalid date, date format must be YYYY-MM-DD" : "Μη έγκυρη ημερομηνία, η μορφή ημερομηνίας πρέπει να είναι ΕΕΕΕ-ΜΜ-ΗΗ",
@@ -135,12 +121,10 @@ OC.L10N.register(
"Select the board to link to a project" : "Επιλέξτε πίνακα και συνδέστε τον σε ένα έργο",
"Search by board title" : "Αναζήτηση με το όνομα πίνακα",
"Select board" : "Επιλογή πίνακα",
"Move/copy card" : "Μετακίνηση/αντιγραφή κάρτας",
"Select a board" : "Επιλογή ενός πίνακα",
"No lists available" : "Δεν υπάρχουν διαθέσιμες λίστες",
"Select a list" : "Επιλέξτε μια λίστα",
"Move card" : "Μετακίνηση καρτέλας",
"Copy card" : "Αντίγραφο κάρτας",
"Select the card to link to a project" : "Επιλογή καρτέλας για σύνδεση στο έργο",
"Link to card" : "Σύνδεσμος σε καρτέλα",
"Select a card" : "Επιλογή μιας καρτέλας",
@@ -228,7 +212,7 @@ OC.L10N.register(
"Select a user to assign to this card…" : "Επιλέξτε έναν χρήστη για να του αναθέσετε αυτή την κάρτα...",
"File to share" : "Αρχείο για κοινή χρήση",
"Invalid path selected" : "Επιλέχθηκε μη έγκυρη διαδρομή",
"Upload new files" : "Μεταφορτώστε νέα αρχεία",
"Upload new files" : "Ανεβάστε νέα αρχεία",
"Share from Files" : "Κοινή χρήση από Αρχεία",
"Pending share" : "Κοινή χρήση σε εκκρεμότητα",
"Add this attachment" : "Προσθήκη αυτού του συνημμένου",
@@ -240,7 +224,6 @@ OC.L10N.register(
"Modified" : "Τροποποιήθηκε",
"Created" : "Δημιουργήθηκε",
"The title cannot be empty." : "Ο τίτλος δεν μπορεί να είναι κενός.",
"Cannot close unsaved card!" : "Αδυναμία κλεισίματος της κάρτας που δεν έχει αποθηκευτεί!",
"Open in sidebar view" : "Άνοιγμα σε προβολή πλευρικής στήλης",
"Open in bigger view" : "Άνοιγμα σε μεγαλύτερη προβολή",
"Comments" : "Σχόλια",
@@ -255,7 +238,6 @@ OC.L10N.register(
"Reply" : "Απάντηση",
"Update" : "Ενημέρωση",
"Write a description …" : "Γράψτε μια περιγραφή…",
"Could not save description" : "Αδυναμία αποθήκευσης της περιγραφής",
"Description" : "Περιγραφή",
"(Unsaved)" : "(Δεν αποθηκεύτηκε)",
"(Saving…)" : "(Αποθήκευση...)",
@@ -290,7 +272,6 @@ OC.L10N.register(
"{count} comments, {unread} unread" : "{count} σχόλια, {unread} μη αναγνωσμένα",
"Todo items" : "Στοιχεία todo",
"Edit card title" : "Επεξεργασία τίτλου κάρτας",
"Open link" : "Άνοιγμα συνδέσμου",
"Card deleted" : "Η καρτέλα διαγράφηκε",
"Edit title" : "Επεξεργασία τίτλου",
"Assign to me" : "Ανάθεση σε εμένα",
@@ -334,7 +315,6 @@ OC.L10N.register(
"Limit board creation to some groups" : "Περιορισμός της δημιουργίας πινάκων σε ορισμένες ομάδες",
"Users outside of those groups will not be able to create their own boards, but will still be able to work on boards that have been shared with them." : "Οι χρήστες εκτός αυτών των ομάδων δεν θα μπορούν να δημιουργούν τους δικούς τους πίνακες, αλλά θα μπορούν να εργάζονται σε πίνακες που τους έχουν διαμοιραστεί.",
"Cancel edit" : "Ακύρωση επεξεργασίας",
"Save board" : "Αποθήκευση πίνακα",
"Board {0} deleted" : "Διαγράφηκε {0} πίνακας ",
"All cards" : "Όλες οι καρτέλες",
"Only assigned cards" : "Μόνο καρτέλες που έχουν ανατεθεί",
@@ -342,7 +322,6 @@ OC.L10N.register(
"An error occurred" : "Παρουσιάστηκε σφάλμα",
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον πίνακα {title}; Αυτό θα διαγράψει όλα τα δεδομένα του πίνακα συμπεριλαμβανομένων και των αρχειοθετημένων καρτών.",
"Delete the board?" : "Διαγραφή του πίνακα;",
"Exporting board..." : "Εξαγωγή πίνακα...",
"Board details" : "Λεπτομέριες πίνακα",
"Edit board" : "Επεξεργασία πίνακα",
"Clone board" : "Κλώνος πίνακα",
@@ -355,25 +334,12 @@ OC.L10N.register(
"Assigned cards" : "Ανατεθειμένες καρτέλες",
"No notifications" : "Δεν υπάρχουν ειδοποιήσεις",
"Delete board" : "Διαγραφή πίνακα",
"Importing board..." : "Εισαγωγή πίνακα...",
"Board imported successfully" : "Ο πίνακας εισήχθη επιτυχώς",
"Import board" : "Εισαγωγή πίνακα",
"Clone {boardTitle}" : "Κλωνοποίηση {boardTitle}",
"Clone cards" : "Κλωνοποίηση καρτών",
"Clone assignments" : "Κλωνοποίηση αναθέσεων",
"Clone labels" : "Κλωνοποίηση ετικετών",
"Clone due dates" : "Κλωνοποίηση προθεσμιών",
"Advanced options" : "Προχωρημένες επιλογές",
"Move all cards to the first list" : "Μετακίνηση όλων των καρτών στην πρώτη λίστα",
"Restore archived cards" : "Επαναφορά αρχειοθετημένων καρτών",
"Clone" : "Κλωνοποίηση",
"Export {boardTitle}" : "Εξαγωγή {boardTitle}",
"Export as JSON" : "Εξαγωγή ως JSON",
"Export as CSV" : "Εξαγωγή ως CSV",
"Note: Only the JSON format is supported for importing back into the Deck app." : "Σημείωση: Μόνο η μορφή JSON υποστηρίζεται για εισαγωγή πίσω στην εφαρμογή Deck.",
"Clone cards" : "Κάρτες κλώνου",
"Advanced options" : "Επιλογές για προχωρημένους",
"Clone" : "Κλώνος",
"Export as CSV" : "Εξαγωγή σε CSV",
"Export" : "Εξαγωγή",
"Loading filtered view" : "Φόρτωση εμφάνισης με βάση το φίλτρο",
"Search for {searchQuery} in other boards" : "Αναζήτηση για {searchQuery} σε άλλους πίνακες",
"Search for {searchQuery} in all boards" : "Αναζήτηση για {searchQuery} σε όλους τους πίνακες",
"No results found" : "Δεν βρέθηκαν αποτελέσματα",
"Deck board {name}\n* Last modified on {lastMod}" : "Πίνακας Deck {name}\n* Τελευταία τροποποίηση στις {lastMod}",
@@ -402,7 +368,6 @@ OC.L10N.register(
"Something went wrong" : "Κάτι πήγε στραβά",
"Failed to upload {name}" : "Αποτυχία μεταφόρτωσης {name}",
"Maximum file size of {size} exceeded" : "Υπέρβαση επιτρεπόμενου μεγέθους αρχείου {size}",
"Assigned users" : "Ανατεθειμένοι χρήστες",
"Due date" : "Προθεσμία",
"Error creating the share" : "Σφάλμα κατά τη δημιουργία της κοινοποίησης",
"Share with a Deck card" : "Μοιραστείτε με μια καρτέλα Deck",

View File

@@ -79,14 +79,10 @@
"Could not write file to disk" : "Αδυναμία εγγραφής αρχείου στον δίσκο",
"A PHP extension stopped the file upload" : "Ένα πρόσθετο PHP διέκοψε την μεταφόρτωση του αρχείου",
"No file uploaded or file size exceeds maximum of %s" : "Δεν μεταφορτώθηκε αρχείο ή το μέγεθος αρχείου υπερβαίνει το μέγιστο %s",
"Invalid file type. Only JSON files are allowed." : "Μη έγκυρος τύπος αρχείου. Επιτρέπονται μόνο αρχεία JSON.",
"Invalid JSON data" : "Μη έγκυρα δεδομένα JSON",
"Failed to import board" : "Αποτυχία εισαγωγής πίνακα",
"Cards due today" : "Κάρτες που λήγουν σήμερα",
"Cards due tomorrow" : "Κάρτες που λήγουν αύριο",
"Upcoming cards" : "Επερχόμενες καρτέλες",
"Load more" : "Φόρτωση περισσότερων",
"Welcome to Nextcloud Deck!" : "Καλώς ήρθατε στο Nextcloud Deck!",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Η καρτέλα \"%s\" του \"%s\" ανατέθηκε σε εσάς από τον %s.",
"{user} has assigned the card {deck-card} on {deck-board} to you." : "Ο/Η {user} έχει αναθέσει την καρτέλα {deck-card} του πίνακα {deck-board} σε εσάς.",
"The card \"%s\" on \"%s\" has reached its due date." : "Η καρτέλα \"%s\" στο \"%s\" έχει λήξει.",
@@ -98,7 +94,6 @@
"Deck board" : "Πίνακας του Deck",
"Owned by %1$s" : "Ανήκει στον/στην %1$s",
"Deck boards, cards and comments" : "Πίνακες, κάρτες και σχόλια Deck",
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Από %1$s, στον %2$s/%3$s, που ανήκει στον %4$s",
"Create a new deck card" : "Δημιουργήστε μια νέα κάρτα",
"Card comments" : "Σχόλια καρτέλας",
"%s on %s" : "%s στο %s",
@@ -109,20 +104,11 @@
"Action needed" : "Απαιτείται ενέργεια",
"Later" : "Αργότερα",
"copy" : "Αντιγραφή",
"Read more inside" : "Διαβάστε περισσότερα εντός",
"Custom lists - click to rename!" : "Προσαρμοσμένες λίστες - κάντε κλικ για μετονομασία!",
"To Do" : "Προς Ενέργεια",
"In Progress" : "Σε Εξέλιξη",
"Done" : "Ολοκληρώθηκε",
"1. Open to learn more about boards and cards" : "1. Ανοίξτε για να μάθετε περισσότερα για τους πίνακες και τις κάρτες",
"2. Drag cards left and right, up and down" : "2. Σύρετε κάρτες αριστερά και δεξιά, πάνω και κάτω",
"3. Apply rich formatting and link content" : "3. Εφαρμόστε πλούσια μορφοποίηση και συνδέστε περιεχόμενο",
"4. Share, comment and collaborate!" : "4. Μοιραστείτε, σχολιάστε και συνεργαστείτε!",
"Create your first card!" : "Δημιουργήστε την πρώτη σας κάρτα!",
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Αυτό το σχόλιο έχει περισσότερους από %s χαρακτήρες.\nΠροστέθηκε ως συνημμένο στην καρτέλα με όνομα %s .\nΠροσβάσιμο στη διεύθυνση URL: %s.",
"Attachments" : "Συνημμένα",
"File" : "Αρχείο",
"date" : "ημερομηνία",
"Card not found" : "Η καρτέλα δεν βρέθηκε",
"Path is already shared with this card" : "Η διαδρομή κοινοποιείται ήδη σε αυτήν την καρτέλα",
"Invalid date, date format must be YYYY-MM-DD" : "Μη έγκυρη ημερομηνία, η μορφή ημερομηνίας πρέπει να είναι ΕΕΕΕ-ΜΜ-ΗΗ",
@@ -133,12 +119,10 @@
"Select the board to link to a project" : "Επιλέξτε πίνακα και συνδέστε τον σε ένα έργο",
"Search by board title" : "Αναζήτηση με το όνομα πίνακα",
"Select board" : "Επιλογή πίνακα",
"Move/copy card" : "Μετακίνηση/αντιγραφή κάρτας",
"Select a board" : "Επιλογή ενός πίνακα",
"No lists available" : "Δεν υπάρχουν διαθέσιμες λίστες",
"Select a list" : "Επιλέξτε μια λίστα",
"Move card" : "Μετακίνηση καρτέλας",
"Copy card" : "Αντίγραφο κάρτας",
"Select the card to link to a project" : "Επιλογή καρτέλας για σύνδεση στο έργο",
"Link to card" : "Σύνδεσμος σε καρτέλα",
"Select a card" : "Επιλογή μιας καρτέλας",
@@ -226,7 +210,7 @@
"Select a user to assign to this card…" : "Επιλέξτε έναν χρήστη για να του αναθέσετε αυτή την κάρτα...",
"File to share" : "Αρχείο για κοινή χρήση",
"Invalid path selected" : "Επιλέχθηκε μη έγκυρη διαδρομή",
"Upload new files" : "Μεταφορτώστε νέα αρχεία",
"Upload new files" : "Ανεβάστε νέα αρχεία",
"Share from Files" : "Κοινή χρήση από Αρχεία",
"Pending share" : "Κοινή χρήση σε εκκρεμότητα",
"Add this attachment" : "Προσθήκη αυτού του συνημμένου",
@@ -238,7 +222,6 @@
"Modified" : "Τροποποιήθηκε",
"Created" : "Δημιουργήθηκε",
"The title cannot be empty." : "Ο τίτλος δεν μπορεί να είναι κενός.",
"Cannot close unsaved card!" : "Αδυναμία κλεισίματος της κάρτας που δεν έχει αποθηκευτεί!",
"Open in sidebar view" : "Άνοιγμα σε προβολή πλευρικής στήλης",
"Open in bigger view" : "Άνοιγμα σε μεγαλύτερη προβολή",
"Comments" : "Σχόλια",
@@ -253,7 +236,6 @@
"Reply" : "Απάντηση",
"Update" : "Ενημέρωση",
"Write a description …" : "Γράψτε μια περιγραφή…",
"Could not save description" : "Αδυναμία αποθήκευσης της περιγραφής",
"Description" : "Περιγραφή",
"(Unsaved)" : "(Δεν αποθηκεύτηκε)",
"(Saving…)" : "(Αποθήκευση...)",
@@ -288,7 +270,6 @@
"{count} comments, {unread} unread" : "{count} σχόλια, {unread} μη αναγνωσμένα",
"Todo items" : "Στοιχεία todo",
"Edit card title" : "Επεξεργασία τίτλου κάρτας",
"Open link" : "Άνοιγμα συνδέσμου",
"Card deleted" : "Η καρτέλα διαγράφηκε",
"Edit title" : "Επεξεργασία τίτλου",
"Assign to me" : "Ανάθεση σε εμένα",
@@ -332,7 +313,6 @@
"Limit board creation to some groups" : "Περιορισμός της δημιουργίας πινάκων σε ορισμένες ομάδες",
"Users outside of those groups will not be able to create their own boards, but will still be able to work on boards that have been shared with them." : "Οι χρήστες εκτός αυτών των ομάδων δεν θα μπορούν να δημιουργούν τους δικούς τους πίνακες, αλλά θα μπορούν να εργάζονται σε πίνακες που τους έχουν διαμοιραστεί.",
"Cancel edit" : "Ακύρωση επεξεργασίας",
"Save board" : "Αποθήκευση πίνακα",
"Board {0} deleted" : "Διαγράφηκε {0} πίνακας ",
"All cards" : "Όλες οι καρτέλες",
"Only assigned cards" : "Μόνο καρτέλες που έχουν ανατεθεί",
@@ -340,7 +320,6 @@
"An error occurred" : "Παρουσιάστηκε σφάλμα",
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον πίνακα {title}; Αυτό θα διαγράψει όλα τα δεδομένα του πίνακα συμπεριλαμβανομένων και των αρχειοθετημένων καρτών.",
"Delete the board?" : "Διαγραφή του πίνακα;",
"Exporting board..." : "Εξαγωγή πίνακα...",
"Board details" : "Λεπτομέριες πίνακα",
"Edit board" : "Επεξεργασία πίνακα",
"Clone board" : "Κλώνος πίνακα",
@@ -353,25 +332,12 @@
"Assigned cards" : "Ανατεθειμένες καρτέλες",
"No notifications" : "Δεν υπάρχουν ειδοποιήσεις",
"Delete board" : "Διαγραφή πίνακα",
"Importing board..." : "Εισαγωγή πίνακα...",
"Board imported successfully" : "Ο πίνακας εισήχθη επιτυχώς",
"Import board" : "Εισαγωγή πίνακα",
"Clone {boardTitle}" : "Κλωνοποίηση {boardTitle}",
"Clone cards" : "Κλωνοποίηση καρτών",
"Clone assignments" : "Κλωνοποίηση αναθέσεων",
"Clone labels" : "Κλωνοποίηση ετικετών",
"Clone due dates" : "Κλωνοποίηση προθεσμιών",
"Advanced options" : "Προχωρημένες επιλογές",
"Move all cards to the first list" : "Μετακίνηση όλων των καρτών στην πρώτη λίστα",
"Restore archived cards" : "Επαναφορά αρχειοθετημένων καρτών",
"Clone" : "Κλωνοποίηση",
"Export {boardTitle}" : "Εξαγωγή {boardTitle}",
"Export as JSON" : "Εξαγωγή ως JSON",
"Export as CSV" : "Εξαγωγή ως CSV",
"Note: Only the JSON format is supported for importing back into the Deck app." : "Σημείωση: Μόνο η μορφή JSON υποστηρίζεται για εισαγωγή πίσω στην εφαρμογή Deck.",
"Clone cards" : "Κάρτες κλώνου",
"Advanced options" : "Επιλογές για προχωρημένους",
"Clone" : "Κλώνος",
"Export as CSV" : "Εξαγωγή σε CSV",
"Export" : "Εξαγωγή",
"Loading filtered view" : "Φόρτωση εμφάνισης με βάση το φίλτρο",
"Search for {searchQuery} in other boards" : "Αναζήτηση για {searchQuery} σε άλλους πίνακες",
"Search for {searchQuery} in all boards" : "Αναζήτηση για {searchQuery} σε όλους τους πίνακες",
"No results found" : "Δεν βρέθηκαν αποτελέσματα",
"Deck board {name}\n* Last modified on {lastMod}" : "Πίνακας Deck {name}\n* Τελευταία τροποποίηση στις {lastMod}",
@@ -400,7 +366,6 @@
"Something went wrong" : "Κάτι πήγε στραβά",
"Failed to upload {name}" : "Αποτυχία μεταφόρτωσης {name}",
"Maximum file size of {size} exceeded" : "Υπέρβαση επιτρεπόμενου μεγέθους αρχείου {size}",
"Assigned users" : "Ανατεθειμένοι χρήστες",
"Due date" : "Προθεσμία",
"Error creating the share" : "Σφάλμα κατά τη δημιουργία της κοινοποίησης",
"Share with a Deck card" : "Μοιραστείτε με μια καρτέλα Deck",

View File

@@ -357,7 +357,7 @@ OC.L10N.register(
"Delete board" : "Supprimer le tableau",
"Importing board..." : "Importation du tableau...",
"Board imported successfully" : "Carte importée avec succès",
"Import board" : "Importer un tableau",
"Import board" : "Tableau d'importation",
"Clone {boardTitle}" : "Cloner {boardTitle}",
"Clone cards" : "Dupliquer les cartes",
"Clone assignments" : "Cloner les affectations",
@@ -373,7 +373,6 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Remarque : seul le format JSON est pris en charge pour la réimportation dans l'application Deck.",
"Export" : "Exporter",
"Loading filtered view" : "Chargement de la vue filtrée",
"Search for {searchQuery} in other boards" : "Rechercher {searchQuery} dans les autres tableaux",
"Search for {searchQuery} in all boards" : "Recherche de {searchQuery} dans tous les tableaux",
"No results found" : "Aucun résultat",
"Deck board {name}\n* Last modified on {lastMod}" : "Tableau Deck {name}\n* Dernière modification le {lastMod}",

View File

@@ -355,7 +355,7 @@
"Delete board" : "Supprimer le tableau",
"Importing board..." : "Importation du tableau...",
"Board imported successfully" : "Carte importée avec succès",
"Import board" : "Importer un tableau",
"Import board" : "Tableau d'importation",
"Clone {boardTitle}" : "Cloner {boardTitle}",
"Clone cards" : "Dupliquer les cartes",
"Clone assignments" : "Cloner les affectations",
@@ -371,7 +371,6 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Remarque : seul le format JSON est pris en charge pour la réimportation dans l'application Deck.",
"Export" : "Exporter",
"Loading filtered view" : "Chargement de la vue filtrée",
"Search for {searchQuery} in other boards" : "Rechercher {searchQuery} dans les autres tableaux",
"Search for {searchQuery} in all boards" : "Recherche de {searchQuery} dans tous les tableaux",
"No results found" : "Aucun résultat",
"Deck board {name}\n* Last modified on {lastMod}" : "Tableau Deck {name}\n* Dernière modification le {lastMod}",

View File

@@ -373,7 +373,6 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Megjegyzés: Csak a JSON formátum támogatott a Kártyák alkalmazásba való importáláskor.",
"Export" : "Exportálás",
"Loading filtered view" : "Szűrt nézet betöltése",
"Search for {searchQuery} in other boards" : "Keresés a(z) {searchQuery} kifejezésre a többi táblában",
"Search for {searchQuery} in all boards" : "Keresés a(z) {searchQuery} kifejezésre az összes táblában",
"No results found" : "Nincs találat",
"Deck board {name}\n* Last modified on {lastMod}" : "{name} kártyatábla\n* Legutóbb módosítva: {lastMod}",

View File

@@ -371,7 +371,6 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Megjegyzés: Csak a JSON formátum támogatott a Kártyák alkalmazásba való importáláskor.",
"Export" : "Exportálás",
"Loading filtered view" : "Szűrt nézet betöltése",
"Search for {searchQuery} in other boards" : "Keresés a(z) {searchQuery} kifejezésre a többi táblában",
"Search for {searchQuery} in all boards" : "Keresés a(z) {searchQuery} kifejezésre az összes táblában",
"No results found" : "Nincs találat",
"Deck board {name}\n* Last modified on {lastMod}" : "{name} kártyatábla\n* Legutóbb módosítva: {lastMod}",

View File

@@ -6,19 +6,16 @@ OC.L10N.register(
"No file was uploaded" : "Ulac afaylu i d-yettwasulin",
"Missing a temporary folder" : "Ixuṣ ukaram akudan",
"Finished" : "Immed",
"copy" : "nɣel",
"Done" : "Immed",
"Attachments" : "Ticeqqufin",
"File" : "Afaylu",
"Cancel" : "Sefsex",
"Open" : "Ldi",
"Completed" : "Yemmed",
"Open details" : "Ldi talqayt",
"Details" : "Talqayt",
"Sharing" : "Beṭṭu",
"Tags" : "Tibzimin",
"Activity" : "Armud",
"Transfer" : "Seḍfeṛ",
"Owner" : "Bab",
"Delete" : "Kkes",
"Edit" : "Ẓreg",
@@ -29,9 +26,6 @@ OC.L10N.register(
"Save" : "Sekles",
"Cancel reply" : "Semmet tiririt.",
"Reply" : "Err",
"Description" : "Aglam",
"Open link" : "Nɣel aseɣwen",
"Keyboard shortcuts" : "Inegzumen n unasiw",
"Ctrl" : "Ctrl",
"Search" : "Nadi",
"Cancel edit" : "Sefsex aseẓreg",

View File

@@ -4,19 +4,16 @@
"No file was uploaded" : "Ulac afaylu i d-yettwasulin",
"Missing a temporary folder" : "Ixuṣ ukaram akudan",
"Finished" : "Immed",
"copy" : "nɣel",
"Done" : "Immed",
"Attachments" : "Ticeqqufin",
"File" : "Afaylu",
"Cancel" : "Sefsex",
"Open" : "Ldi",
"Completed" : "Yemmed",
"Open details" : "Ldi talqayt",
"Details" : "Talqayt",
"Sharing" : "Beṭṭu",
"Tags" : "Tibzimin",
"Activity" : "Armud",
"Transfer" : "Seḍfeṛ",
"Owner" : "Bab",
"Delete" : "Kkes",
"Edit" : "Ẓreg",
@@ -27,9 +24,6 @@
"Save" : "Sekles",
"Cancel reply" : "Semmet tiririt.",
"Reply" : "Err",
"Description" : "Aglam",
"Open link" : "Nɣel aseɣwen",
"Keyboard shortcuts" : "Inegzumen n unasiw",
"Ctrl" : "Ctrl",
"Search" : "Nadi",
"Cancel edit" : "Sefsex aseẓreg",

View File

@@ -12,7 +12,7 @@ OC.L10N.register(
"You have removed {acl} from the board {board}" : "Го избришавте {acl} од таблата {board}",
"{user} has removed {acl} from the board {board}" : "{user} го избриша {acl} од таблата {board}",
"You have renamed the board {before} to {board}" : "Ја преименувавте таблата {before} во {board}",
"{user} has renamed the board {before} to {board}" : "{user} ја преименуваше таблата {before} во {board}",
"{user} has renamed the board {before} to {board}" : "{user} ја преименување таблата {before} во {board}",
"You have archived the board {board}" : "Ја архивиравте таблата {board}",
"{user} has archived the board {before}" : "{user} ја архивирање таблата {before}",
"You have unarchived the board {board}" : "Ја вративте од архива таблата {board}",
@@ -20,7 +20,7 @@ OC.L10N.register(
"You have created a new list {stack} on board {board}" : "Креиравте нова листа {stack} на таблата {board}",
"{user} has created a new list {stack} on board {board}" : "{user} креирање нова листа {stack} на таблата {board}",
"You have renamed list {before} to {stack} on board {board}" : "Ја преименувавте листа {before} во {stack} на таблата {board}",
"{user} has renamed list {before} to {stack} on board {board}" : "{user} ја преименуваше листата {before} во {stack} на таблата {board}",
"{user} has renamed list {before} to {stack} on board {board}" : "{user} ја преименување листата {before} во {stack} на таблата {board}",
"You have deleted list {stack} on board {board}" : "Ја избришавте листата {stack} од таблата {board}",
"{user} has deleted list {stack} on board {board}" : "{user} ја избриша листата {stack} од таблата {board}",
"You have created card {card} in list {stack} on board {board}" : "Креиравте картица {card} во листата {stack} на таблата {board}",
@@ -28,7 +28,7 @@ OC.L10N.register(
"You have deleted card {card} in list {stack} on board {board}" : "Избришавте картица {card} во листата {stack} на таблата {board}",
"{user} has deleted card {card} in list {stack} on board {board}" : "{user} избриша картица {card} во листата {stack} на таблата {board}",
"You have renamed the card {before} to {card}" : "Ја преименувавте картицата {before} во {card}",
"{user} has renamed the card {before} to {card}" : "{user} ја преименуваше картицата {before} во {card}",
"{user} has renamed the card {before} to {card}" : "{user} ја преименување картицата {before} во {card}",
"You have added a description to card {card} in list {stack} on board {board}" : "Додадовте опис на картицата {card} во листата {stack} на таблата {board}",
"{user} has added a description to card {card} in list {stack} on board {board}" : "{user} додаде опис на картицата {card} во листата {stack} на таблата {board}",
"You have updated the description of card {card} in list {stack} on board {board}" : "Го ажуриравте описот на картицата {card} во листата {stack} на таблата {board}",
@@ -37,10 +37,6 @@ OC.L10N.register(
"{user} has archived card {card} in list {stack} on board {board}" : "{user} ја архивираше картицата {card} во листата {stack} на таблата {board}",
"You have unarchived card {card} in list {stack} on board {board}" : "Ја вративте од архива картицата {card} во листата {stack} на таблата {board}",
"{user} has unarchived card {card} in list {stack} on board {board}" : "{user} ја врати од архива картицата {card} во листата {stack} на таблата {board}",
"You have marked the card {card} as done in list {stack} on board {board}" : "Ја означивте картицата {card} како завршена во листата {stack} на таблата {board}",
"{user} has marked card {card} as done in list {stack} on board {board}" : "{user} ја означи картичката {card} како завршена во листата {stack} на таблата {board}",
"You have marked the card {card} as undone in list {stack} on board {board}" : "Ја означивте картицата {card} како не-завршена во листата {stack} на таблата {board}",
"{user} has marked the card {card} as undone in list {stack} on board {board}" : "{user} ја означи картичката {card} како не-завршена во листата {stack} на таблата {board}",
"You have removed the due date of card {card}" : "Го избришавте датумот на истекување на картицата {card}",
"{user} has removed the due date of card {card}" : "{user} го избриша датумот на истекување на картицата {card}",
"You have set the due date of card {card} to {after}" : "Поставивте датум на истекување на картицата {card}",
@@ -69,9 +65,7 @@ OC.L10N.register(
"{user} has commented on card {card}" : "{user} коментирање на картицата {card}",
"Deck" : "Deck",
"Changes in the <strong>Deck app</strong>" : "Промени во <strong>апликацијата Deck</strong>",
"A <strong>board, list or card</strong> was changed" : "Променета е <strong>табла, листа или картица</strong>",
"A <strong>comment</strong> was created on a card" : "<strong>Коментар</strong> е креиран на картица",
"A <strong>card description</strong> has been changed" : "Променет е <strong>опис на картица</strong> ",
"The file was uploaded" : "Датотеката е прикачена",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Подигнатата датотека ја надминува upload_max_filesize директивата во php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Големината на датотеката ја надминува MAX_FILE_SIZE директивата која беше специфицирана во HTML формата",
@@ -81,66 +75,35 @@ OC.L10N.register(
"Could not write file to disk" : "Неможе да се запишува на дискот",
"A PHP extension stopped the file upload" : "PHP додаток го стопираше прикачувањето на датотеката",
"No file uploaded or file size exceeds maximum of %s" : "Нема прикачена дадотека или големината го надмминува максимумот од %s",
"Invalid file type. Only JSON files are allowed." : "Невалиден тип на датотека. Дозволени се само JSON датотеки.",
"Invalid JSON data" : "Невалидни JSON податоци",
"Failed to import board" : "Неуспешен увоз на табла",
"Cards due today" : "Картици со рок до денес",
"Cards due tomorrow" : "Картици со рок до утре",
"Upcoming cards" : "Престојни картици",
"Load more" : "Вчитај повеќе",
"Welcome to Nextcloud Deck!" : "Добредојдовте во Nextcloud Deck!",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Картицата \"%s\" на \"%s\" ти е доделена од %s.",
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} ти ја додели картицата {deck-card} на {deck-board}.",
"The card \"%s\" on \"%s\" has reached its due date." : "Картицата \"%s\" на \"%s\" го достигна датумот на истекување.",
"The card {deck-card} on {deck-board} has reached its due date." : "Картицата {deck-card} на {deck-board} го достигна рокот.",
"%s has mentioned you in a comment on \"%s\"." : "%s те спомна во коментар на \"%s\".",
"{user} has mentioned you in a comment on {deck-card}." : "{user} те спомна во коментар на {deck-card}.",
"The board \"%s\" has been shared with you by %s." : "Таблата \"%s\" ја сподли со тебе %s.",
"{user} has shared {deck-board} with you." : "{user} сподели {deck-board} со вас.",
"Deck board" : "Табла",
"Owned by %1$s" : "Сопственик %1$s",
"Deck boards, cards and comments" : "Табли, картици и коментари",
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Од %1$s, во %2$s/%3$s, сопственик %4$s",
"Create a new deck card" : "Креирај нова картица",
"Card comments" : "Коментари на картица",
"%s on %s" : "%s на %s",
"Deck boards and cards" : "Табли и картици",
"No data was provided to create an attachment." : "Нема податоци за креирање на прилог.",
"Finished" : "Завршено",
"To review" : "На ревизија",
"Action needed" : "Потребна е акција",
"Later" : "Покасно",
"copy" : "копирај",
"Read more inside" : "Прочитај повеќе",
"Custom lists - click to rename!" : "Прилагодени листи кликнете за преименување!",
"To Do" : "За правење",
"In Progress" : "Во тек",
"Done" : "Готово",
"1. Open to learn more about boards and cards" : "1. Отворете за да дознаете повеќе за таблите и картичките",
"2. Drag cards left and right, up and down" : "2. Влечете ги картичките лево и десно, горе и долу",
"3. Apply rich formatting and link content" : "3. Применете богато форматирање и поврзете содржина",
"4. Share, comment and collaborate!" : "4. Споделувајте, коментирајте и соработувајте!",
"Create your first card!" : "Креирајте ја вашата прва картичка!",
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Коментарот има повеќе од %s карактери.\nДодаден е како пролог на картицата со име %s.\nДостапен е на линк: %s.",
"Attachments" : "Прилози",
"File" : "Датотека",
"date" : "датум",
"Card not found" : "Картицата не е пронајдена",
"Path is already shared with this card" : "Патеката веќе е споделена со оваа картица",
"Invalid date, date format must be YYYY-MM-DD" : "Невалиден датум, форматот мора да биде ГГГГ-ММ-ДД",
"Personal planning and team project organization" : "Персонален планер и тимски проект организер",
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in Markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 📎 Attach files and embed them in your Markdown description\n- 💬 Discuss with your team using comments\n- ⚡ Keep track of changes in the activity stream\n- 🚀 Get your project organized" : "Deck е алатка за организација во стил на kanban, наменета за лични планови и организација на проекти за тимови интегрирани со Nextcloud.\n\n\n📥 Додај ги задачите на картички и подреди ги\n📄 Запиши дополнителни белешки во Markdown\n🔖 Додели етикети за уште подобра организација\n👥 Сподели со твојот тим, пријатели или семејство\n📎 Прикачи датотеки и вметни ги во Markdown описот\n💬 Дискутирај со твојот тим преку коментари\n⚡ Следи ги промените во активностите\n🚀 Организирај го твојот проект",
"Add board" : "Додади табла",
"Card details" : "Детали за картица",
"Select the board to link to a project" : "Избери табла за поврзување со проект",
"Search by board title" : "Барај по име на табла",
"Select board" : "Избери табла",
"Move/copy card" : "Премести/копирај картица",
"Select a board" : "Избери табла",
"No lists available" : "Нема достапни листи",
"Select a list" : "Избери листа",
"Move card" : "Премести картица",
"Copy card" : "Копирај картица",
"Select the card to link to a project" : "Избери картица за поврзување со проект",
"Link to card" : "Линк до картица",
"Select a card" : "Избери картица",
@@ -161,8 +124,6 @@ OC.L10N.register(
"Filter by tag" : "Филтрирај по ознака",
"Filter by assigned user" : "Филтрирај по назначени корисници",
"Unassigned" : "Неназначени",
"Filter by status" : "Филтрирај по статус",
"Open and completed" : "Отворени и завршени",
"Open" : "Отвори",
"Completed" : "Завршено",
"Filter by due date" : "Филтрирај по краен рок",
@@ -172,17 +133,12 @@ OC.L10N.register(
"Next 30 days" : "Следни 30 дена",
"No due date" : "Нема краен рок",
"Clear filter" : "Исчисти филтри",
"View Modes" : "Режими на приказ",
"Toggle View Modes" : "Промени режими на приказ",
"Hide archived cards" : "Сокриј ги архивираните картици",
"Show archived cards" : "Прикажи ги архивираните картици",
"Toggle compact mode" : "Вклучи компактен мод",
"Hide card cover images" : "Сокриј насловни слики на картици",
"Show card cover images" : "Прикажи насловни слики на картици",
"Open details" : "Отвори детали",
"Details" : "Детали",
"Currently present people" : "Моментално приситни корисници",
"Loading board" : "Вчитување на табла",
"Loading board" : "Вчирување на табла",
"Board not found" : "Таблата не е пронајдена",
"Create a new list to add cards to this board" : "Додадете нова листа за да додадете картици на таблата",
"Sharing" : "Споделување",
@@ -193,69 +149,51 @@ OC.L10N.register(
"Undo" : "Врати",
"Deleted cards" : "Избришани картици",
"Failed to create share with {displayName}" : "Неможе да се сподели со {displayName}",
"Are you sure you want to transfer the board {title} to {user}?" : "Дали сте сигурни дека сакате да ја префрлите таблата {title} на {user}?",
"Transfer the board." : "Префрли табла.",
"Transfer" : "Трансфер",
"The board has been transferred to {user}" : "Таблата е префрлена на {user}",
"Failed to transfer the board to {user}" : "Неуспешно префрлање на таблата на {user}",
"Share board with a user, group or team …" : "Сподели табла со корисник, група или тим...",
"Searching for users, groups and teams …" : "Пребарување на корисници, групи или тимови...",
"No participants found" : "Не се пронајдени учесници",
"Board owner" : "Сопственик на таблата",
"(Group)" : "(Група)",
"(Team)" : "(Тим)",
"Can edit" : "Може да се уредува",
"Can share" : "Can share",
"Can manage" : "Може да ја менаџира",
"Owner" : "Сопственик",
"Delete" : "Избриши",
"List deleted" : "Листата е избришана",
"Edit list title" : "Удери наслов на листата",
"Archive all cards" : "Архивирај ги сите картици",
"Unarchive all cards" : "Врати ги од архива сите картици",
"Delete list" : "Избриши листа",
"Archive all cards in this list" : "Архивирај ги сите картици во листата",
"Unarchive all cards in this list" : "Врати ги од архива сите картици во оваа листа",
"Add a new card" : "Додади нова картица",
"Card name" : "Име на картицата",
"title and color value must be provided" : "Мора да се внесе наслов и боја",
"title and color value must be provided" : "наслов и боја мора да се приложи",
"Edit" : "Уреди",
"Add a new tag" : "Додади нова ознака",
"Board name" : "Име на табла",
"Members" : "Членови",
"Assign to users/groups/team" : "Додели на корисници/групи/тимови",
"Assign a user to this card…" : "Додели корисник на оваа картица...",
"Select a user to assign to this card…" : "Избери на кого да се додели оваа картица…",
"File to share" : "Датотека за споделување",
"Invalid path selected" : "Избрана невалидна патека",
"Upload new files" : "Прикачи нови датотеки",
"Share from Files" : "Сподели од датотеките",
"Pending share" : "Споделување на чекање",
"Add this attachment" : "Додади го овој прилог",
"Show in Files" : "Прикажи во датотеките",
"Download" : "Преземи",
"Remove attachment" : "Отстрани прилог",
"Delete Attachment" : "Избриши прилог",
"Restore Attachment" : "Врати прилог",
"Modified" : "Изменето",
"Created" : "Креирано",
"The title cannot be empty." : "Насловот неможе да биде празен.",
"Cannot close unsaved card!" : "Неможе да се затвори незачувана картица!",
"Open in sidebar view" : "Отвори страничен поглед",
"Open in bigger view" : "Отвори на голем екран",
"Comments" : "Коментари",
"Failed to load comments" : "Неуспешно вчитување на коментари",
"No comments yet. Begin the discussion!" : "Сеуште нема коментари. Започни дискусија!",
"The comment cannot be empty." : "Коментарот неможе да биде празен.",
"The comment cannot be longer than 1000 characters." : "Коментарот неможе да биде поголем од 1000 карактери.",
"Save" : "Зачувај",
"Created:" : "Создадено:",
"In reply to" : "Како одговор на",
"Cancel reply" : "Откажи одговор",
"Reply" : "Одговор",
"Update" : "Ажурирај",
"Write a description …" : "Напиши опис ...",
"Could not save description" : "Неможе да се зачува описот",
"Description" : "Опис",
"(Unsaved)" : "(Незачувано)",
"(Saving…)" : "(Снимање…)",
@@ -266,135 +204,63 @@ OC.L10N.register(
"Choose attachment" : "Избери прилог",
"Select Date" : "Избери датум",
"Later today {timeLocale}" : "Денес покасно {timeLocale}",
"Set due date for later today" : "Постави краен рок за денес подоцна",
"Tomorrow {timeLocale}" : "Утре {timeLocale}",
"Set due date for tomorrow" : "Постави краен рок за утре",
"This weekend {timeLocale}" : "Овој викенд {timeLocale}",
"Set due date for this weekend" : "Постави краен рок за овој викенд",
"Next week {timeLocale}" : "Следна недела {timeLocale}",
"Set due date for next week" : "Постави краен рок за следната недела",
"Assign a due date to this card…" : "Додели рок за оваа картица…",
"Set a due date" : "Постави краен рок",
"Add due date" : "Додади краен рок",
"Choose a date" : "Избери датум",
"Remove due date" : "Отстрани краен рок",
"Mark as done" : "Означи како готово",
"Due at:" : "Краен рок:",
"Not done" : "Не е завршено",
"Unarchive card" : "Врати картица од архива",
"Archive card" : "Архивирај картица",
"Assign a tag to this card…" : "Додади ознака на оваа картица...",
"Select or create a tag…" : "Избери или креирај ознака...",
"Create a new tag:" : "Направи нова ознака:",
"(group)" : "(group)",
"{count} comments, {unread} unread" : "{count} коментари, {unread} непрочитани",
"Todo items" : "Задачи",
"Edit card title" : "Измени наслов на картица",
"Open link" : "Отвори линк",
"Card deleted" : "Картицата е избришана",
"Edit title" : "Удери наслов",
"Assign to me" : "Доделени мене",
"Unassign myself" : "Откажи се",
"Mark as not done" : "Означи како не-готово",
"Delete card" : "Избриши картица",
"seconds ago" : "пред неколку секунди",
"Keyboard shortcuts" : "Кратенки преку тастатура",
"Boost your productivity using Deck with keyboard shortcuts." : "Зголеми ја продуктивноста со користење на кратенки преку тастатура.",
"Board actions" : "Акции за табла",
"Keyboard shortcut" : "Кратенка преку тастатура",
"Action" : "Акција",
"Shift" : "Shift",
"Scroll" : "Scroll",
"Scroll sideways" : "Лизгај странично",
"Navigate between cards" : "Навигација помеѓу картиците",
"Esc" : "Esc",
"Close card details" : "Затвори детали на картица",
"Ctrl" : "Ctrl",
"Search" : "Барај",
"Show card filters" : "Прикажи филтри за картици",
"Clear card filters" : "Исчисти филтри за картици",
"Show help dialog" : "Прикажи дијалог за помош",
"Card actions" : "Акции за картици",
"The following actions can be triggered on the currently highlighted card" : "Следните акции можат да се активираат на моментално обележаната картичка",
"Enter" : "Ентер",
"Space" : "Празно место",
"Open card details" : "Отвори детали на картица",
"Edit the card title" : "Измени наслов на картица",
"Assign yourself to the current card" : "Доделете се себеси на тековната картица",
"Archive/unarchive the current card" : "Архивирај/одархивирај тековната картица",
"Mark card as completed/not completed" : "Означи ја картицата како завршена/незавршена",
"Open card menu" : "Отвори мени на картица",
"All boards" : "Сите табли",
"Archived boards" : "Архивирани табли",
"Shared with you" : "Споделено со тебе",
"Deck settings" : "Deck параметри",
"Use bigger card view" : "Користи поголем преглед на картици",
"Show card ID badge" : "Прикажи ID на картиците",
"Show boards in calendar/tasks" : "Прикажи ги таблите во календарнот",
"Limit board creation to some groups" : "Ограничи го креирањето на нови табли само на овие групи",
"Users outside of those groups will not be able to create their own boards, but will still be able to work on boards that have been shared with them." : "Корисниците кој што не се во овие групи нема да можат да прават нови таби, но ќе можат да работат на таблите кој ќе бидат споделени со нив.",
"Cancel edit" : "Откажи ажурирање",
"Save board" : "Зачувај табла",
"Board {0} deleted" : "Таблата {0} е избришана",
"All cards" : "Сите картици",
"Only assigned cards" : "Само доделени картици",
"No reminder" : "Нема потсетник",
"An error occurred" : "Настана грешка",
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Дали сте сигурени дека сакате да ја избришете оваа табла {title}? Ова ќе ги избрише и сите податоци во таблата и архивираните картици.",
"Delete the board?" : "Бришење на таблата?",
"Exporting board..." : "Извоз на табла...",
"Board details" : "Детали за таблата",
"Edit board" : "Измени табла",
"Clone board" : "Клонирај табла",
"Unarchive board" : "Врати табла од архива",
"Archive board" : "Архивирај табла",
"Export board" : "Извези табла",
"Turn on due date reminders" : "Вклучи потсетници за крајните рокови",
"Turn off due date reminders" : "Исклучи потсетници за крајните рокови",
"Due date reminders" : "Потсетници за крајните рокови",
"Assigned cards" : "Доделени картици",
"No notifications" : "Нема известувања",
"Delete board" : "Избриши табла",
"Importing board..." : "Увезување табла...",
"Board imported successfully" : "Таблата е успешно увезена",
"Import board" : "Увези табла",
"Clone {boardTitle}" : "Клонирај {boardTitle}",
"Clone cards" : "Клинирај картици",
"Clone assignments" : "Клонирај задачи",
"Clone labels" : "Клонирај ознаки",
"Clone due dates" : "Клонирај крајни рокови",
"Advanced options" : "Напредни опции",
"Move all cards to the first list" : "Помести ги сите картици во првата листа",
"Restore archived cards" : "Врати архивирани картици",
"Clone" : "Клонирај",
"Export {boardTitle}" : "Извези {boardTitle}",
"Export as JSON" : "Извези како JSON",
"Export as CSV" : "Извези како CSV",
"Note: Only the JSON format is supported for importing back into the Deck app." : "Забелешка: Поддржан е само JSON формат за увоз назад во апликацијата Deck.",
"Export" : "Извези",
"Loading filtered view" : "Вчитување на филтриран поглед",
"Search for {searchQuery} in other boards" : "Барај {searchQuery} во други табли",
"Search for {searchQuery} in all boards" : "Барај {searchQuery} во сите табли",
"No results found" : "Нема пронајдено резултати",
"Deck board {name}\n* Last modified on {lastMod}" : "Табла {name}\n* Последна промена на {lastMod}",
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Креирана на {created}\n* Последна промена на {lastMod}\n* {nbAttachments} прилози\n* {nbComments} коментари",
"{nbCards} cards" : "{nbCards} картици",
"Due on {date}" : "Истекува на {date}",
"{stack} in {board}" : "{stack} во {board}",
"Click to expand description" : "Кликнни за проширување на опис",
"Click to expand comment" : "Кликнни за проширување на коментарот",
"Create card" : "Креирајте картица",
"Create a new card" : "Креирајте нова картица",
"Card title" : "Наслов на картицата",
"Creating the new card …" : "Креирање нова картица ...",
"Card \"{card}\" was added to \"{board}\"" : "Картицата \"{card}\" е додадена во \"{board}\"",
"Open card" : "Отвори картица",
"Close" : "Затвори",
"No upcoming cards" : "Нема престојни картици",
"upcoming cards today" : "престојни картици за денес",
"upcoming cards tomorrow" : "престојни картици за утре",
"upcoming cards" : "престојни картици",
"New card" : "Нова картица",
"Link to a board" : "Линк до табла",
"Link to a card" : "Линк до картица",
"Create a card" : "Креирајте картица",
@@ -402,7 +268,6 @@ OC.L10N.register(
"Something went wrong" : "Нешто не е во ред",
"Failed to upload {name}" : "Неуспешно прикачување {name}",
"Maximum file size of {size} exceeded" : "Максималната големина на датотека од {size} е достигната",
"Assigned users" : "Доделени корисници",
"Due date" : "До датум",
"Error creating the share" : "Грешка при креирање на споделување",
"Share with a Deck card" : "Споделено со Deck картица",

View File

@@ -10,7 +10,7 @@
"You have removed {acl} from the board {board}" : "Го избришавте {acl} од таблата {board}",
"{user} has removed {acl} from the board {board}" : "{user} го избриша {acl} од таблата {board}",
"You have renamed the board {before} to {board}" : "Ја преименувавте таблата {before} во {board}",
"{user} has renamed the board {before} to {board}" : "{user} ја преименуваше таблата {before} во {board}",
"{user} has renamed the board {before} to {board}" : "{user} ја преименување таблата {before} во {board}",
"You have archived the board {board}" : "Ја архивиравте таблата {board}",
"{user} has archived the board {before}" : "{user} ја архивирање таблата {before}",
"You have unarchived the board {board}" : "Ја вративте од архива таблата {board}",
@@ -18,7 +18,7 @@
"You have created a new list {stack} on board {board}" : "Креиравте нова листа {stack} на таблата {board}",
"{user} has created a new list {stack} on board {board}" : "{user} креирање нова листа {stack} на таблата {board}",
"You have renamed list {before} to {stack} on board {board}" : "Ја преименувавте листа {before} во {stack} на таблата {board}",
"{user} has renamed list {before} to {stack} on board {board}" : "{user} ја преименуваше листата {before} во {stack} на таблата {board}",
"{user} has renamed list {before} to {stack} on board {board}" : "{user} ја преименување листата {before} во {stack} на таблата {board}",
"You have deleted list {stack} on board {board}" : "Ја избришавте листата {stack} од таблата {board}",
"{user} has deleted list {stack} on board {board}" : "{user} ја избриша листата {stack} од таблата {board}",
"You have created card {card} in list {stack} on board {board}" : "Креиравте картица {card} во листата {stack} на таблата {board}",
@@ -26,7 +26,7 @@
"You have deleted card {card} in list {stack} on board {board}" : "Избришавте картица {card} во листата {stack} на таблата {board}",
"{user} has deleted card {card} in list {stack} on board {board}" : "{user} избриша картица {card} во листата {stack} на таблата {board}",
"You have renamed the card {before} to {card}" : "Ја преименувавте картицата {before} во {card}",
"{user} has renamed the card {before} to {card}" : "{user} ја преименуваше картицата {before} во {card}",
"{user} has renamed the card {before} to {card}" : "{user} ја преименување картицата {before} во {card}",
"You have added a description to card {card} in list {stack} on board {board}" : "Додадовте опис на картицата {card} во листата {stack} на таблата {board}",
"{user} has added a description to card {card} in list {stack} on board {board}" : "{user} додаде опис на картицата {card} во листата {stack} на таблата {board}",
"You have updated the description of card {card} in list {stack} on board {board}" : "Го ажуриравте описот на картицата {card} во листата {stack} на таблата {board}",
@@ -35,10 +35,6 @@
"{user} has archived card {card} in list {stack} on board {board}" : "{user} ја архивираше картицата {card} во листата {stack} на таблата {board}",
"You have unarchived card {card} in list {stack} on board {board}" : "Ја вративте од архива картицата {card} во листата {stack} на таблата {board}",
"{user} has unarchived card {card} in list {stack} on board {board}" : "{user} ја врати од архива картицата {card} во листата {stack} на таблата {board}",
"You have marked the card {card} as done in list {stack} on board {board}" : "Ја означивте картицата {card} како завршена во листата {stack} на таблата {board}",
"{user} has marked card {card} as done in list {stack} on board {board}" : "{user} ја означи картичката {card} како завршена во листата {stack} на таблата {board}",
"You have marked the card {card} as undone in list {stack} on board {board}" : "Ја означивте картицата {card} како не-завршена во листата {stack} на таблата {board}",
"{user} has marked the card {card} as undone in list {stack} on board {board}" : "{user} ја означи картичката {card} како не-завршена во листата {stack} на таблата {board}",
"You have removed the due date of card {card}" : "Го избришавте датумот на истекување на картицата {card}",
"{user} has removed the due date of card {card}" : "{user} го избриша датумот на истекување на картицата {card}",
"You have set the due date of card {card} to {after}" : "Поставивте датум на истекување на картицата {card}",
@@ -67,9 +63,7 @@
"{user} has commented on card {card}" : "{user} коментирање на картицата {card}",
"Deck" : "Deck",
"Changes in the <strong>Deck app</strong>" : "Промени во <strong>апликацијата Deck</strong>",
"A <strong>board, list or card</strong> was changed" : "Променета е <strong>табла, листа или картица</strong>",
"A <strong>comment</strong> was created on a card" : "<strong>Коментар</strong> е креиран на картица",
"A <strong>card description</strong> has been changed" : "Променет е <strong>опис на картица</strong> ",
"The file was uploaded" : "Датотеката е прикачена",
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "Подигнатата датотека ја надминува upload_max_filesize директивата во php.ini",
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "Големината на датотеката ја надминува MAX_FILE_SIZE директивата која беше специфицирана во HTML формата",
@@ -79,66 +73,35 @@
"Could not write file to disk" : "Неможе да се запишува на дискот",
"A PHP extension stopped the file upload" : "PHP додаток го стопираше прикачувањето на датотеката",
"No file uploaded or file size exceeds maximum of %s" : "Нема прикачена дадотека или големината го надмминува максимумот од %s",
"Invalid file type. Only JSON files are allowed." : "Невалиден тип на датотека. Дозволени се само JSON датотеки.",
"Invalid JSON data" : "Невалидни JSON податоци",
"Failed to import board" : "Неуспешен увоз на табла",
"Cards due today" : "Картици со рок до денес",
"Cards due tomorrow" : "Картици со рок до утре",
"Upcoming cards" : "Престојни картици",
"Load more" : "Вчитај повеќе",
"Welcome to Nextcloud Deck!" : "Добредојдовте во Nextcloud Deck!",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Картицата \"%s\" на \"%s\" ти е доделена од %s.",
"{user} has assigned the card {deck-card} on {deck-board} to you." : "{user} ти ја додели картицата {deck-card} на {deck-board}.",
"The card \"%s\" on \"%s\" has reached its due date." : "Картицата \"%s\" на \"%s\" го достигна датумот на истекување.",
"The card {deck-card} on {deck-board} has reached its due date." : "Картицата {deck-card} на {deck-board} го достигна рокот.",
"%s has mentioned you in a comment on \"%s\"." : "%s те спомна во коментар на \"%s\".",
"{user} has mentioned you in a comment on {deck-card}." : "{user} те спомна во коментар на {deck-card}.",
"The board \"%s\" has been shared with you by %s." : "Таблата \"%s\" ја сподли со тебе %s.",
"{user} has shared {deck-board} with you." : "{user} сподели {deck-board} со вас.",
"Deck board" : "Табла",
"Owned by %1$s" : "Сопственик %1$s",
"Deck boards, cards and comments" : "Табли, картици и коментари",
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Од %1$s, во %2$s/%3$s, сопственик %4$s",
"Create a new deck card" : "Креирај нова картица",
"Card comments" : "Коментари на картица",
"%s on %s" : "%s на %s",
"Deck boards and cards" : "Табли и картици",
"No data was provided to create an attachment." : "Нема податоци за креирање на прилог.",
"Finished" : "Завршено",
"To review" : "На ревизија",
"Action needed" : "Потребна е акција",
"Later" : "Покасно",
"copy" : "копирај",
"Read more inside" : "Прочитај повеќе",
"Custom lists - click to rename!" : "Прилагодени листи кликнете за преименување!",
"To Do" : "За правење",
"In Progress" : "Во тек",
"Done" : "Готово",
"1. Open to learn more about boards and cards" : "1. Отворете за да дознаете повеќе за таблите и картичките",
"2. Drag cards left and right, up and down" : "2. Влечете ги картичките лево и десно, горе и долу",
"3. Apply rich formatting and link content" : "3. Применете богато форматирање и поврзете содржина",
"4. Share, comment and collaborate!" : "4. Споделувајте, коментирајте и соработувајте!",
"Create your first card!" : "Креирајте ја вашата прва картичка!",
"This comment has more than %s characters.\nAdded as an attachment to the card with name %s.\nAccessible on URL: %s." : "Коментарот има повеќе од %s карактери.\nДодаден е како пролог на картицата со име %s.\nДостапен е на линк: %s.",
"Attachments" : "Прилози",
"File" : "Датотека",
"date" : "датум",
"Card not found" : "Картицата не е пронајдена",
"Path is already shared with this card" : "Патеката веќе е споделена со оваа картица",
"Invalid date, date format must be YYYY-MM-DD" : "Невалиден датум, форматот мора да биде ГГГГ-ММ-ДД",
"Personal planning and team project organization" : "Персонален планер и тимски проект организер",
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in Markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 📎 Attach files and embed them in your Markdown description\n- 💬 Discuss with your team using comments\n- ⚡ Keep track of changes in the activity stream\n- 🚀 Get your project organized" : "Deck е алатка за организација во стил на kanban, наменета за лични планови и организација на проекти за тимови интегрирани со Nextcloud.\n\n\n📥 Додај ги задачите на картички и подреди ги\n📄 Запиши дополнителни белешки во Markdown\n🔖 Додели етикети за уште подобра организација\n👥 Сподели со твојот тим, пријатели или семејство\n📎 Прикачи датотеки и вметни ги во Markdown описот\n💬 Дискутирај со твојот тим преку коментари\n⚡ Следи ги промените во активностите\n🚀 Организирај го твојот проект",
"Add board" : "Додади табла",
"Card details" : "Детали за картица",
"Select the board to link to a project" : "Избери табла за поврзување со проект",
"Search by board title" : "Барај по име на табла",
"Select board" : "Избери табла",
"Move/copy card" : "Премести/копирај картица",
"Select a board" : "Избери табла",
"No lists available" : "Нема достапни листи",
"Select a list" : "Избери листа",
"Move card" : "Премести картица",
"Copy card" : "Копирај картица",
"Select the card to link to a project" : "Избери картица за поврзување со проект",
"Link to card" : "Линк до картица",
"Select a card" : "Избери картица",
@@ -159,8 +122,6 @@
"Filter by tag" : "Филтрирај по ознака",
"Filter by assigned user" : "Филтрирај по назначени корисници",
"Unassigned" : "Неназначени",
"Filter by status" : "Филтрирај по статус",
"Open and completed" : "Отворени и завршени",
"Open" : "Отвори",
"Completed" : "Завршено",
"Filter by due date" : "Филтрирај по краен рок",
@@ -170,17 +131,12 @@
"Next 30 days" : "Следни 30 дена",
"No due date" : "Нема краен рок",
"Clear filter" : "Исчисти филтри",
"View Modes" : "Режими на приказ",
"Toggle View Modes" : "Промени режими на приказ",
"Hide archived cards" : "Сокриј ги архивираните картици",
"Show archived cards" : "Прикажи ги архивираните картици",
"Toggle compact mode" : "Вклучи компактен мод",
"Hide card cover images" : "Сокриј насловни слики на картици",
"Show card cover images" : "Прикажи насловни слики на картици",
"Open details" : "Отвори детали",
"Details" : "Детали",
"Currently present people" : "Моментално приситни корисници",
"Loading board" : "Вчитување на табла",
"Loading board" : "Вчирување на табла",
"Board not found" : "Таблата не е пронајдена",
"Create a new list to add cards to this board" : "Додадете нова листа за да додадете картици на таблата",
"Sharing" : "Споделување",
@@ -191,69 +147,51 @@
"Undo" : "Врати",
"Deleted cards" : "Избришани картици",
"Failed to create share with {displayName}" : "Неможе да се сподели со {displayName}",
"Are you sure you want to transfer the board {title} to {user}?" : "Дали сте сигурни дека сакате да ја префрлите таблата {title} на {user}?",
"Transfer the board." : "Префрли табла.",
"Transfer" : "Трансфер",
"The board has been transferred to {user}" : "Таблата е префрлена на {user}",
"Failed to transfer the board to {user}" : "Неуспешно префрлање на таблата на {user}",
"Share board with a user, group or team …" : "Сподели табла со корисник, група или тим...",
"Searching for users, groups and teams …" : "Пребарување на корисници, групи или тимови...",
"No participants found" : "Не се пронајдени учесници",
"Board owner" : "Сопственик на таблата",
"(Group)" : "(Група)",
"(Team)" : "(Тим)",
"Can edit" : "Може да се уредува",
"Can share" : "Can share",
"Can manage" : "Може да ја менаџира",
"Owner" : "Сопственик",
"Delete" : "Избриши",
"List deleted" : "Листата е избришана",
"Edit list title" : "Удери наслов на листата",
"Archive all cards" : "Архивирај ги сите картици",
"Unarchive all cards" : "Врати ги од архива сите картици",
"Delete list" : "Избриши листа",
"Archive all cards in this list" : "Архивирај ги сите картици во листата",
"Unarchive all cards in this list" : "Врати ги од архива сите картици во оваа листа",
"Add a new card" : "Додади нова картица",
"Card name" : "Име на картицата",
"title and color value must be provided" : "Мора да се внесе наслов и боја",
"title and color value must be provided" : "наслов и боја мора да се приложи",
"Edit" : "Уреди",
"Add a new tag" : "Додади нова ознака",
"Board name" : "Име на табла",
"Members" : "Членови",
"Assign to users/groups/team" : "Додели на корисници/групи/тимови",
"Assign a user to this card…" : "Додели корисник на оваа картица...",
"Select a user to assign to this card…" : "Избери на кого да се додели оваа картица…",
"File to share" : "Датотека за споделување",
"Invalid path selected" : "Избрана невалидна патека",
"Upload new files" : "Прикачи нови датотеки",
"Share from Files" : "Сподели од датотеките",
"Pending share" : "Споделување на чекање",
"Add this attachment" : "Додади го овој прилог",
"Show in Files" : "Прикажи во датотеките",
"Download" : "Преземи",
"Remove attachment" : "Отстрани прилог",
"Delete Attachment" : "Избриши прилог",
"Restore Attachment" : "Врати прилог",
"Modified" : "Изменето",
"Created" : "Креирано",
"The title cannot be empty." : "Насловот неможе да биде празен.",
"Cannot close unsaved card!" : "Неможе да се затвори незачувана картица!",
"Open in sidebar view" : "Отвори страничен поглед",
"Open in bigger view" : "Отвори на голем екран",
"Comments" : "Коментари",
"Failed to load comments" : "Неуспешно вчитување на коментари",
"No comments yet. Begin the discussion!" : "Сеуште нема коментари. Започни дискусија!",
"The comment cannot be empty." : "Коментарот неможе да биде празен.",
"The comment cannot be longer than 1000 characters." : "Коментарот неможе да биде поголем од 1000 карактери.",
"Save" : "Зачувај",
"Created:" : "Создадено:",
"In reply to" : "Како одговор на",
"Cancel reply" : "Откажи одговор",
"Reply" : "Одговор",
"Update" : "Ажурирај",
"Write a description …" : "Напиши опис ...",
"Could not save description" : "Неможе да се зачува описот",
"Description" : "Опис",
"(Unsaved)" : "(Незачувано)",
"(Saving…)" : "(Снимање…)",
@@ -264,135 +202,63 @@
"Choose attachment" : "Избери прилог",
"Select Date" : "Избери датум",
"Later today {timeLocale}" : "Денес покасно {timeLocale}",
"Set due date for later today" : "Постави краен рок за денес подоцна",
"Tomorrow {timeLocale}" : "Утре {timeLocale}",
"Set due date for tomorrow" : "Постави краен рок за утре",
"This weekend {timeLocale}" : "Овој викенд {timeLocale}",
"Set due date for this weekend" : "Постави краен рок за овој викенд",
"Next week {timeLocale}" : "Следна недела {timeLocale}",
"Set due date for next week" : "Постави краен рок за следната недела",
"Assign a due date to this card…" : "Додели рок за оваа картица…",
"Set a due date" : "Постави краен рок",
"Add due date" : "Додади краен рок",
"Choose a date" : "Избери датум",
"Remove due date" : "Отстрани краен рок",
"Mark as done" : "Означи како готово",
"Due at:" : "Краен рок:",
"Not done" : "Не е завршено",
"Unarchive card" : "Врати картица од архива",
"Archive card" : "Архивирај картица",
"Assign a tag to this card…" : "Додади ознака на оваа картица...",
"Select or create a tag…" : "Избери или креирај ознака...",
"Create a new tag:" : "Направи нова ознака:",
"(group)" : "(group)",
"{count} comments, {unread} unread" : "{count} коментари, {unread} непрочитани",
"Todo items" : "Задачи",
"Edit card title" : "Измени наслов на картица",
"Open link" : "Отвори линк",
"Card deleted" : "Картицата е избришана",
"Edit title" : "Удери наслов",
"Assign to me" : "Доделени мене",
"Unassign myself" : "Откажи се",
"Mark as not done" : "Означи како не-готово",
"Delete card" : "Избриши картица",
"seconds ago" : "пред неколку секунди",
"Keyboard shortcuts" : "Кратенки преку тастатура",
"Boost your productivity using Deck with keyboard shortcuts." : "Зголеми ја продуктивноста со користење на кратенки преку тастатура.",
"Board actions" : "Акции за табла",
"Keyboard shortcut" : "Кратенка преку тастатура",
"Action" : "Акција",
"Shift" : "Shift",
"Scroll" : "Scroll",
"Scroll sideways" : "Лизгај странично",
"Navigate between cards" : "Навигација помеѓу картиците",
"Esc" : "Esc",
"Close card details" : "Затвори детали на картица",
"Ctrl" : "Ctrl",
"Search" : "Барај",
"Show card filters" : "Прикажи филтри за картици",
"Clear card filters" : "Исчисти филтри за картици",
"Show help dialog" : "Прикажи дијалог за помош",
"Card actions" : "Акции за картици",
"The following actions can be triggered on the currently highlighted card" : "Следните акции можат да се активираат на моментално обележаната картичка",
"Enter" : "Ентер",
"Space" : "Празно место",
"Open card details" : "Отвори детали на картица",
"Edit the card title" : "Измени наслов на картица",
"Assign yourself to the current card" : "Доделете се себеси на тековната картица",
"Archive/unarchive the current card" : "Архивирај/одархивирај тековната картица",
"Mark card as completed/not completed" : "Означи ја картицата како завршена/незавршена",
"Open card menu" : "Отвори мени на картица",
"All boards" : "Сите табли",
"Archived boards" : "Архивирани табли",
"Shared with you" : "Споделено со тебе",
"Deck settings" : "Deck параметри",
"Use bigger card view" : "Користи поголем преглед на картици",
"Show card ID badge" : "Прикажи ID на картиците",
"Show boards in calendar/tasks" : "Прикажи ги таблите во календарнот",
"Limit board creation to some groups" : "Ограничи го креирањето на нови табли само на овие групи",
"Users outside of those groups will not be able to create their own boards, but will still be able to work on boards that have been shared with them." : "Корисниците кој што не се во овие групи нема да можат да прават нови таби, но ќе можат да работат на таблите кој ќе бидат споделени со нив.",
"Cancel edit" : "Откажи ажурирање",
"Save board" : "Зачувај табла",
"Board {0} deleted" : "Таблата {0} е избришана",
"All cards" : "Сите картици",
"Only assigned cards" : "Само доделени картици",
"No reminder" : "Нема потсетник",
"An error occurred" : "Настана грешка",
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Дали сте сигурени дека сакате да ја избришете оваа табла {title}? Ова ќе ги избрише и сите податоци во таблата и архивираните картици.",
"Delete the board?" : "Бришење на таблата?",
"Exporting board..." : "Извоз на табла...",
"Board details" : "Детали за таблата",
"Edit board" : "Измени табла",
"Clone board" : "Клонирај табла",
"Unarchive board" : "Врати табла од архива",
"Archive board" : "Архивирај табла",
"Export board" : "Извези табла",
"Turn on due date reminders" : "Вклучи потсетници за крајните рокови",
"Turn off due date reminders" : "Исклучи потсетници за крајните рокови",
"Due date reminders" : "Потсетници за крајните рокови",
"Assigned cards" : "Доделени картици",
"No notifications" : "Нема известувања",
"Delete board" : "Избриши табла",
"Importing board..." : "Увезување табла...",
"Board imported successfully" : "Таблата е успешно увезена",
"Import board" : "Увези табла",
"Clone {boardTitle}" : "Клонирај {boardTitle}",
"Clone cards" : "Клинирај картици",
"Clone assignments" : "Клонирај задачи",
"Clone labels" : "Клонирај ознаки",
"Clone due dates" : "Клонирај крајни рокови",
"Advanced options" : "Напредни опции",
"Move all cards to the first list" : "Помести ги сите картици во првата листа",
"Restore archived cards" : "Врати архивирани картици",
"Clone" : "Клонирај",
"Export {boardTitle}" : "Извези {boardTitle}",
"Export as JSON" : "Извези како JSON",
"Export as CSV" : "Извези како CSV",
"Note: Only the JSON format is supported for importing back into the Deck app." : "Забелешка: Поддржан е само JSON формат за увоз назад во апликацијата Deck.",
"Export" : "Извези",
"Loading filtered view" : "Вчитување на филтриран поглед",
"Search for {searchQuery} in other boards" : "Барај {searchQuery} во други табли",
"Search for {searchQuery} in all boards" : "Барај {searchQuery} во сите табли",
"No results found" : "Нема пронајдено резултати",
"Deck board {name}\n* Last modified on {lastMod}" : "Табла {name}\n* Последна промена на {lastMod}",
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Креирана на {created}\n* Последна промена на {lastMod}\n* {nbAttachments} прилози\n* {nbComments} коментари",
"{nbCards} cards" : "{nbCards} картици",
"Due on {date}" : "Истекува на {date}",
"{stack} in {board}" : "{stack} во {board}",
"Click to expand description" : "Кликнни за проширување на опис",
"Click to expand comment" : "Кликнни за проширување на коментарот",
"Create card" : "Креирајте картица",
"Create a new card" : "Креирајте нова картица",
"Card title" : "Наслов на картицата",
"Creating the new card …" : "Креирање нова картица ...",
"Card \"{card}\" was added to \"{board}\"" : "Картицата \"{card}\" е додадена во \"{board}\"",
"Open card" : "Отвори картица",
"Close" : "Затвори",
"No upcoming cards" : "Нема престојни картици",
"upcoming cards today" : "престојни картици за денес",
"upcoming cards tomorrow" : "престојни картици за утре",
"upcoming cards" : "престојни картици",
"New card" : "Нова картица",
"Link to a board" : "Линк до табла",
"Link to a card" : "Линк до картица",
"Create a card" : "Креирајте картица",
@@ -400,7 +266,6 @@
"Something went wrong" : "Нешто не е во ред",
"Failed to upload {name}" : "Неуспешно прикачување {name}",
"Maximum file size of {size} exceeded" : "Максималната големина на датотека од {size} е достигната",
"Assigned users" : "Доделени корисници",
"Due date" : "До датум",
"Error creating the share" : "Грешка при креирање на споделување",
"Share with a Deck card" : "Споделено со Deck картица",

View File

@@ -266,7 +266,6 @@ OC.L10N.register(
"{count} comments, {unread} unread" : "{count} reacties, {unread} ongelezen",
"Todo items" : "Te doen onderwerpen",
"Edit card title" : "Wijzig de titel van de kaart",
"Open link" : "Open link",
"Card deleted" : "Kaart verwijderd",
"Edit title" : "Titel bewerken",
"Assign to me" : "Aan mij toewijzen",
@@ -280,7 +279,6 @@ OC.L10N.register(
"Shift" : "Shift",
"Ctrl" : "Ctrl",
"Search" : "Zoeken",
"Enter" : "Enter",
"All boards" : "Alle borden",
"Archived boards" : "Gearchiveerde borden",
"Shared with you" : "Deelde met jou",

View File

@@ -264,7 +264,6 @@
"{count} comments, {unread} unread" : "{count} reacties, {unread} ongelezen",
"Todo items" : "Te doen onderwerpen",
"Edit card title" : "Wijzig de titel van de kaart",
"Open link" : "Open link",
"Card deleted" : "Kaart verwijderd",
"Edit title" : "Titel bewerken",
"Assign to me" : "Aan mij toewijzen",
@@ -278,7 +277,6 @@
"Shift" : "Shift",
"Ctrl" : "Ctrl",
"Search" : "Zoeken",
"Enter" : "Enter",
"All boards" : "Alle borden",
"Archived boards" : "Gearchiveerde borden",
"Shared with you" : "Deelde met jou",

View File

@@ -373,7 +373,6 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Observera: Endast JSON-formatet stöds för import tillbaka till Deck-appen.",
"Export" : "Exportera",
"Loading filtered view" : "Laddar filtrerad vy",
"Search for {searchQuery} in other boards" : "Sök efter {searchQuery} i andra tavlor",
"Search for {searchQuery} in all boards" : "Sök efter {searchQuery} i alla tavlor",
"No results found" : "Inga resultat funna",
"Deck board {name}\n* Last modified on {lastMod}" : "Deck tavla {name}\n* Senast ändrad den {lastMod}",

View File

@@ -371,7 +371,6 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Observera: Endast JSON-formatet stöds för import tillbaka till Deck-appen.",
"Export" : "Exportera",
"Loading filtered view" : "Laddar filtrerad vy",
"Search for {searchQuery} in other boards" : "Sök efter {searchQuery} i andra tavlor",
"Search for {searchQuery} in all boards" : "Sök efter {searchQuery} i alla tavlor",
"No results found" : "Inga resultat funna",
"Deck board {name}\n* Last modified on {lastMod}" : "Deck tavla {name}\n* Senast ändrad den {lastMod}",

View File

@@ -516,7 +516,7 @@ class ActivityManager {
];
}
private function findDetailsForCard(int $cardId, ?string $subject = null): array {
private function findDetailsForCard($cardId, $subject = null) {
$card = $this->cardMapper->find($cardId);
$stack = $this->stackMapper->find($card->getStackId());
$board = $this->boardMapper->find($stack->getBoardId());

View File

@@ -7,9 +7,6 @@
namespace OCA\Deck\Activity;
/**
* @psalm-api SettingComment
*/
class SettingComment extends SettingBase {
/**

View File

@@ -6,13 +6,9 @@
*/
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Service\AttachmentService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@@ -25,52 +21,72 @@ class AttachmentApiController extends ApiController {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function getAll(string $apiVersion): DataResponse {
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function getAll($apiVersion) {
$attachment = $this->attachmentService->findAll($this->request->getParam('cardId'), true);
if ($apiVersion === '1.0') {
$attachment = array_filter($attachment, fn (Attachment $attachment): bool => $attachment->getType() === 'deck_file');
$attachment = array_filter($attachment, function ($attachment) {
return $attachment->getType() === 'deck_file';
});
}
return new DataResponse($attachment, HTTP::STATUS_OK);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function display(int $cardId, int $attachmentId, string $type = 'deck_file') {
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function display($cardId, $attachmentId, $type = 'deck_file') {
return $this->attachmentService->display($cardId, $attachmentId, $type);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function create(int $cardId, string $type, string $data): DataResponse {
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function create($cardId, $type, $data) {
$attachment = $this->attachmentService->create($cardId, $type, $data);
return new DataResponse($attachment, HTTP::STATUS_OK);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function update(int $cardId, int $attachmentId, string $data, string $type = 'deck_file'): DataResponse {
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function update($cardId, $attachmentId, $data, $type = 'deck_file') {
$attachment = $this->attachmentService->update($cardId, $attachmentId, $data, $type);
return new DataResponse($attachment, HTTP::STATUS_OK);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function delete(int $cardId, int $attachmentId, string $type = 'deck_file'): DataResponse {
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function delete($cardId, $attachmentId, $type = 'deck_file') {
$attachment = $this->attachmentService->delete($cardId, $attachmentId, $type);
return new DataResponse($attachment, HTTP::STATUS_OK);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function restore(int $cardId, int $attachmentId, string $type = 'deck_file'): DataResponse {
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*/
public function restore($cardId, $attachmentId, $type = 'deck_file') {
$attachment = $this->attachmentService->restore($cardId, $attachmentId, $type);
return new DataResponse($attachment, HTTP::STATUS_OK);
}

View File

@@ -7,13 +7,8 @@
namespace OCA\Deck\Controller;
use OCA\Deck\BadRequestException;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Service\AttachmentService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Response;
use OCP\IRequest;
class AttachmentController extends Controller {
@@ -25,66 +20,74 @@ class AttachmentController extends Controller {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
public function getAll(int $cardId): array {
/**
* @NoAdminRequired
*/
public function getAll($cardId) {
return $this->attachmentService->findAll($cardId, true);
}
/**
* @param $cardId
* @param $attachmentId
* @NoCSRFRequired
* @NoAdminRequired
* @return \OCP\AppFramework\Http\Response
* @throws \OCA\Deck\NotFoundException
*/
#[NoAdminRequired]
#[NoCSRFRequired]
public function display(int $cardId, string $attachmentId): Response {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->display($cardId, $attachmentId, $type);
}
#[NoAdminRequired]
public function create(int $cardId): Attachment {
return $this->attachmentService->create(
$cardId,
$this->request->getParam('type'),
$this->request->getParam('data') ?? '',
);
}
#[NoAdminRequired]
public function update(int $cardId, string $attachmentId): Attachment {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->update($cardId, $attachmentId, $this->request->getParam('data') ?? '', $type);
}
#[NoAdminRequired]
public function delete(int $cardId, string $attachmentId): Attachment {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->delete($cardId, $attachmentId, $type);
}
#[NoAdminRequired]
public function restore(int $cardId, string $attachmentId): Attachment {
['type' => $type, 'attachmentId' => $attachmentId] = $this->extractTypeAndAttachmentId($attachmentId);
return $this->attachmentService->restore($cardId, $attachmentId, $type);
}
/**
* @return array{type: string, attachmentId: int}
* @throws BadRequestException
*/
private function extractTypeAndAttachmentId(string $attachmentId): array {
public function display($cardId, $attachmentId) {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = [...explode(':', $attachmentId), '', ''];
[$type, $attachmentId] = explode(':', $attachmentId);
}
return $this->attachmentService->display($cardId, $attachmentId, $type);
}
if ($type === '' || !is_numeric($attachmentId)) {
throw new BadRequestException('Invalid attachment id');
/**
* @NoAdminRequired
*/
public function create($cardId) {
return $this->attachmentService->create(
$cardId,
$this->request->getParam('type'),
$this->request->getParam('data')
);
}
/**
* @NoAdminRequired
*/
public function update($cardId, $attachmentId) {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = explode(':', $attachmentId);
}
return $this->attachmentService->update($cardId, $attachmentId, $this->request->getParam('data'), $type);
}
return [
'type' => $type,
'attachmentId' => (int)$attachmentId,
];
/**
* @NoAdminRequired
*/
public function delete($cardId, $attachmentId) {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = explode(':', $attachmentId);
}
return $this->attachmentService->delete($cardId, $attachmentId, $type);
}
/**
* @NoAdminRequired
*/
public function restore($cardId, $attachmentId) {
if (!str_contains($attachmentId, ':')) {
$type = 'deck_file';
} else {
[$type, $attachmentId] = explode(':', $attachmentId);
}
return $this->attachmentService->restore($cardId, $attachmentId, $type);
}
}

View File

@@ -12,13 +12,10 @@ use OCA\Deck\Service\BoardService;
use OCA\Deck\StatusException;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use function Sabre\HTTP\parseDate;
use OCP\IRequest;
use Sabre\HTTP\Util;
/**
* Class BoardApiController
@@ -39,18 +36,21 @@ class BoardApiController extends ApiController {
}
/**
* Return all the boards that the current user has access to.
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Return all of the boards that the current user has access to.
*
* @param bool $details
* @throws StatusException
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function index(bool $details = false): DataResponse {
public function index(bool $details = false) {
$modified = $this->request->getHeader('If-Modified-Since');
if ($modified === '') {
if ($modified === null || $modified === '') {
$boards = $this->boardService->findAll(0, $details === true);
} else {
$date = parseDate($modified);
$date = Util::parseHTTPDate($modified);
if (!$date) {
throw new StatusException('Invalid If-Modified-Since header provided.');
}
@@ -64,12 +64,14 @@ class BoardApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Return the board specified by $this->request->getParam('boardId').
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function get(): DataResponse {
public function get() {
$board = $this->boardService->find($this->request->getParam('boardId'));
$response = new DataResponse($board, HTTP::STATUS_OK);
$response->setETag($board->getEtag());
@@ -77,53 +79,68 @@ class BoardApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
*
* Create a board with the specified title and color.
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function create(string $title, string $color): DataResponse {
public function create($title, $color) {
$board = $this->boardService->create($title, $this->userId, $color);
return new DataResponse($board, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
* @params $archived
*
* Update a board with the specified boardId, title and color, and archived state.
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function update(string $title, string $color, bool $archived = false): DataResponse {
public function update($title, $color, $archived = false) {
$board = $this->boardService->update($this->request->getParam('boardId'), $title, $color, $archived);
return new DataResponse($board, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Delete the board specified by $boardId. Return the board that was deleted.
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function delete(): DataResponse {
public function delete() {
$board = $this->boardService->delete($this->request->getParam('boardId'));
return new DataResponse($board, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Undo the deletion of the board specified by $boardId.
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function undoDelete(): DataResponse {
public function undoDelete() {
$board = $this->boardService->deleteUndo($this->request->getParam('boardId'));
return new DataResponse($board, HTTP::STATUS_OK);
}
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function addAcl(int $boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) {
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) {
$acl = $this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage);
return new DataResponse($acl, HTTP::STATUS_OK);
}

View File

@@ -14,7 +14,6 @@ use OCA\Deck\Service\Importer\BoardImportService;
use OCA\Deck\Service\PermissionService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IL10N;
use OCP\IRequest;
@@ -32,38 +31,68 @@ class BoardController extends ApiController {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function index() {
return $this->boardService->findAll();
}
#[NoAdminRequired]
public function read(int $boardId): Board {
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function read(int $boardId) {
return $this->boardService->find($boardId);
}
#[NoAdminRequired]
public function create(string $title, string $color): Board {
/**
* @NoAdminRequired
* @param $title
* @param $color
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $color) {
return $this->boardService->create($title, $this->userId, $color);
}
#[NoAdminRequired]
public function update(int $id, string $title, string $color, bool $archived): Board {
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $color
* @param $archived
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $color, $archived) {
return $this->boardService->update($id, $title, $color, $archived);
}
#[NoAdminRequired]
public function delete(int $boardId): Board {
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($boardId) {
return $this->boardService->delete($boardId);
}
#[NoAdminRequired]
public function deleteUndo(int $boardId): Board {
/**
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function deleteUndo($boardId) {
return $this->boardService->deleteUndo($boardId);
}
#[NoAdminRequired]
public function getUserPermissions(int $boardId): array {
/**
* @NoAdminRequired
* @param $boardId
* @return array|bool
* @internal param $userId
*/
public function getUserPermissions($boardId) {
$permissions = $this->permissionService->getPermissions($boardId);
return [
'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ],
@@ -74,10 +103,16 @@ class BoardController extends ApiController {
}
/**
* @NoAdminRequired
* @param $boardId
* @param $type
* @param $participant
* @param $permissionEdit
* @param $permissionShare
* @param $permissionManage
* @return \OCP\AppFramework\Db\Entity
*/
#[NoAdminRequired]
public function addAcl(int $boardId, int $type, $participant, bool $permissionEdit, bool $permissionShare, bool $permissionManage): Acl {
public function addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage) {
return $this->boardService->addAcl($boardId, $type, $participant, $permissionEdit, $permissionShare, $permissionManage);
}

View File

@@ -9,9 +9,6 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Service\Importer\BoardImportService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@@ -26,9 +23,11 @@ class BoardImportApiController extends OCSController {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function import(string $system, array $config, array $data): DataResponse {
$this->boardImportService->setSystem($system);
$config = json_decode(json_encode($config));
@@ -39,17 +38,21 @@ class BoardImportApiController extends OCSController {
return new DataResponse($this->boardImportService->getBoard(), Http::STATUS_OK);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function getAllowedSystems(): DataResponse {
$allowedSystems = $this->boardImportService->getAllowedImportSystems();
return new DataResponse($allowedSystems, Http::STATUS_OK);
}
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function getConfigSchema(string $name): DataResponse {
$this->boardImportService->setSystem($name);
$this->boardImportService->validateSystem();

View File

@@ -12,9 +12,6 @@ use OCA\Deck\Service\AssignmentService;
use OCA\Deck\Service\CardService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@@ -30,7 +27,7 @@ class CardApiController extends ApiController {
* @param IRequest $request
* @param CardService $cardService
* @param AssignmentService $assignmentService
* @param string $userId
* @param $userId
*/
public function __construct(
string $appName,
@@ -83,102 +80,112 @@ class CardApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
*
* Update a card
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function update(string $title, $type, string $owner, string $description = '', int $order = 0, $duedate = null, $archived = null): DataResponse {
public function update($title, $type, $owner, $description = '', $order = 0, $duedate = null, $archived = null) {
$done = array_key_exists('done', $this->request->getParams()) ? new OptionalNullableValue($this->request->getParam('done', null)) : null;
$card = $this->cardService->update($this->request->getParam('cardId'), $title, $this->request->getParam('stackId'), $type, $owner, $description, $order, $duedate, 0, $archived, $done);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Delete a specific card.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function delete(): DataResponse {
public function delete() {
$card = $this->cardService->delete($this->request->getParam('cardId'));
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Assign a label to a card.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function assignLabel(int $labelId): DataResponse {
public function assignLabel($labelId) {
$card = $this->cardService->assignLabel($this->request->getParam('cardId'), $labelId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Assign a label to a card.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function removeLabel(int $labelId): DataResponse {
public function removeLabel($labelId) {
$card = $this->cardService->removeLabel($this->request->getParam('cardId'), $labelId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Assign a user to a card
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function assignUser(int $cardId, string $userId, int $type = 0): DataResponse {
public function assignUser($cardId, $userId, $type = 0) {
$card = $this->assignmentService->assignUser($cardId, $userId, $type);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Unassign a user from a card
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function unassignUser(int $cardId, string $userId, int $type = 0): DataResponse {
public function unassignUser($cardId, $userId, $type = 0) {
$card = $this->assignmentService->unassignUser($cardId, $userId, $type);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Archive card
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function archive(int $cardId): DataResponse {
public function archive($cardId) {
$card = $this->cardService->archive($cardId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Unarchive card
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function unarchive(int $cardId): DataResponse {
public function unarchive($cardId) {
$card = $this->cardService->unarchive($cardId);
return new DataResponse($card, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Reorder cards
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function reorder(int $stackId, int $order): DataResponse {
$card = $this->cardService->reorder((int)$this->request->getParam('cardId'), $stackId, $order);
public function reorder($stackId, $order) {
$card = $this->cardService->reorder($this->request->getParam('cardId'), $stackId, $order);
return new DataResponse($card, HTTP::STATUS_OK);
}
}

View File

@@ -7,12 +7,9 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\Card;
use OCA\Deck\Service\AssignmentService;
use OCA\Deck\Service\CardService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IRequest;
class CardController extends Controller {
@@ -26,26 +23,45 @@ class CardController extends Controller {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
public function read(int $cardId): Card {
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function read($cardId) {
return $this->cardService->find($cardId);
}
/**
* @return Card[]
* @NoAdminRequired
* @param $cardId
* @param $stackId
* @param $order
* @return array
*/
#[NoAdminRequired]
public function reorder(int $cardId, int $stackId, int $order): array {
return $this->cardService->reorder($cardId, $stackId, $order);
public function reorder($cardId, $stackId, $order) {
return $this->cardService->reorder((int)$cardId, (int)$stackId, (int)$order);
}
#[NoAdminRequired]
public function rename(int $cardId, string $title): Card {
/**
* @NoAdminRequired
* @param $cardId
* @param $title
* @return \OCP\AppFramework\Db\Entity
*/
public function rename($cardId, $title) {
return $this->cardService->rename($cardId, $title);
}
#[NoAdminRequired]
public function create(string $title, int $stackId, string $type = 'plain', int $order = 999, string $description = '', $duedate = null, array $labels = [], array $users = []): Card {
/**
* @NoAdminRequired
* @param $title
* @param $stackId
* @param $type
* @param int $order
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $stackId, $type = 'plain', $order = 999, string $description = '', $duedate = null, $labels = [], $users = []) {
$card = $this->cardService->create($title, $stackId, $type, $order, $this->userId, $description, $duedate);
foreach ($labels as $label) {
@@ -60,68 +76,113 @@ class CardController extends Controller {
}
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $stackId
* @param $type
* @param $order
* @param $description
* @param $duedate
* @param $deletedAt
* @return \OCP\AppFramework\Db\Entity
*/
#[NoAdminRequired]
public function update(int $id, string $title, int $stackId, string $type, int $order, string $description, $duedate, $deletedAt): Card {
public function update($id, $title, $stackId, $type, $order, $description, $duedate, $deletedAt) {
return $this->cardService->update($id, $title, $stackId, $type, $this->userId, $description, $order, $duedate, $deletedAt);
}
#[NoAdminRequired]
public function clone(int $cardId, ?int $targetStackId = null): Card {
/**
* @NoAdminRequired
* @param $cardId
* @param $targetStackId
* @return \OCP\AppFramework\Db\Entity
*/
public function clone(int $cardId, ?int $targetStackId = null) {
return $this->cardService->cloneCard($cardId, $targetStackId);
}
#[NoAdminRequired]
public function delete(int $cardId): Card {
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($cardId) {
return $this->cardService->delete($cardId);
}
/**
* @return Card[]
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
#[NoAdminRequired]
public function deleted(int $boardId): array {
public function deleted($boardId) {
return $this->cardService->fetchDeleted($boardId);
}
#[NoAdminRequired]
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function archive($cardId) {
return $this->cardService->archive($cardId);
}
#[NoAdminRequired]
public function unarchive(int $cardId): Card {
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function unarchive($cardId) {
return $this->cardService->unarchive($cardId);
}
#[NoAdminRequired]
public function done(int $cardId): Card {
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function done(int $cardId) {
return $this->cardService->done($cardId);
}
#[NoAdminRequired]
public function undone(int $cardId): Card {
/**
* @NoAdminRequired
* @param $cardId
* @return \OCP\AppFramework\Db\Entity
*/
public function undone(int $cardId) {
return $this->cardService->undone($cardId);
}
#[NoAdminRequired]
public function assignLabel(int $cardId, int $labelId): void {
/**
* @NoAdminRequired
* @param $cardId
* @param $labelId
*/
public function assignLabel($cardId, $labelId) {
$this->cardService->assignLabel($cardId, $labelId);
}
#[NoAdminRequired]
public function removeLabel(int $cardId, int $labelId): void {
/**
* @NoAdminRequired
* @param $cardId
* @param $labelId
*/
public function removeLabel($cardId, $labelId) {
$this->cardService->removeLabel($cardId, $labelId);
}
#[NoAdminRequired]
public function assignUser(int $cardId, string $userId, int $type = 0): Assignment {
/**
* @NoAdminRequired
*/
public function assignUser($cardId, $userId, $type = 0) {
return $this->assignmentService->assignUser($cardId, $userId, $type);
}
#[NoAdminRequired]
public function unassignUser(int $cardId, string $userId, int $type = 0): Assignment {
/**
* @NoAdminRequired
*/
public function unassignUser($cardId, $userId, $type = 0) {
return $this->assignmentService->unassignUser($cardId, $userId, $type);
}
}

View File

@@ -9,15 +9,11 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Service\CommentService;
use OCA\Deck\StatusException;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
/**
* @psalm-api
*/
class CommentsApiController extends OCSController {
public function __construct(
string $appName,
@@ -31,33 +27,33 @@ class CommentsApiController extends OCSController {
}
/**
* @NoAdminRequired
* @throws StatusException
*/
#[NoAdminRequired]
public function list(int $cardId, int $limit = 20, int $offset = 0): DataResponse {
public function list(string $cardId, int $limit = 20, int $offset = 0): DataResponse {
return $this->commentService->list($cardId, $limit, $offset);
}
/**
* @NoAdminRequired
* @throws StatusException
*/
#[NoAdminRequired]
public function create(int $cardId, string $message, int $parentId = 0): DataResponse {
return $this->commentService->create($cardId, $message, $parentId);
}
/**
* @NoAdminRequired
* @throws StatusException
*/
#[NoAdminRequired]
public function update(int $cardId, int $commentId, string $message): DataResponse {
return $this->commentService->update($cardId, $commentId, $message);
}
/**
* @NoAdminRequired
* @throws StatusException
*/
#[NoAdminRequired]
public function delete(int $cardId, int $commentId): DataResponse {
return $this->commentService->delete($cardId, $commentId);
}

View File

@@ -8,8 +8,6 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Service\ConfigService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\AppFramework\OCSController;
@@ -24,15 +22,19 @@ class ConfigController extends OCSController {
parent::__construct($AppName, $request);
}
#[NoAdminRequired]
#[NoCSRFRequired]
/**
* @NoCSRFRequired
* @NoAdminRequired
*/
public function get(): DataResponse {
return new DataResponse($this->configService->getAll());
}
#[NoAdminRequired]
#[NoCSRFRequired]
public function setValue(string $key, mixed $value): DataResponse|NotFoundResponse {
/**
* @NoCSRFRequired
* @NoAdminRequired
*/
public function setValue(string $key, $value) {
$result = $this->configService->set($key, $value);
if ($result === null) {
return new NotFoundResponse();

View File

@@ -10,9 +10,6 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Service\LabelService;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
@@ -29,50 +26,59 @@ class LabelApiController extends ApiController {
$appName,
IRequest $request,
private LabelService $labelService,
private $userId,
) {
parent::__construct($appName, $request);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Get a specific label.
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function get(): DataResponse {
public function get() {
$label = $this->labelService->find($this->request->getParam('labelId'));
return new DataResponse($label, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
* Create a new label
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function create(string $title, string $color): DataResponse {
public function create($title, $color) {
$label = $this->labelService->create($title, $color, $this->request->getParam('boardId'));
return new DataResponse($label, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $color
* Update a specific label
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function update(string $title, string $color): DataResponse {
public function update($title, $color) {
$label = $this->labelService->update($this->request->getParam('labelId'), $title, $color);
return new DataResponse($label, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Delete a specific label
*/
#[NoAdminRequired]
#[NoCSRFRequired]
#[CORS]
public function delete(): DataResponse {
public function delete() {
$label = $this->labelService->delete($this->request->getParam('labelId'));
return new DataResponse($label, HTTP::STATUS_OK);
}

View File

@@ -7,10 +7,8 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Label;
use OCA\Deck\Service\LabelService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IRequest;
class LabelController extends Controller {
@@ -22,18 +20,34 @@ class LabelController extends Controller {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
public function create(string $title, string $color, int $boardId): Label {
/**
* @NoAdminRequired
* @param $title
* @param $color
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $color, $boardId) {
return $this->labelService->create($title, $color, $boardId);
}
#[NoAdminRequired]
public function update(int $id, string $title, string $color): Label {
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $color
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $color) {
return $this->labelService->update($id, $title, $color);
}
#[NoAdminRequired]
public function delete(int $labelId): Label {
/**
* @NoAdminRequired
* @param $labelId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($labelId) {
return $this->labelService->delete($labelId);
}
}

View File

@@ -10,7 +10,6 @@ declare(strict_types=1);
namespace OCA\Deck\Controller;
use OCA\Deck\Service\OverviewService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@@ -25,7 +24,9 @@ class OverviewApiController extends OCSController {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function upcomingCards(): DataResponse {
return new DataResponse($this->dashboardService->findUpcomingCards($this->userId));
}

View File

@@ -13,7 +13,6 @@ namespace OCA\Deck\Controller;
use OCA\Deck\Db\Card;
use OCA\Deck\Model\CardDetails;
use OCA\Deck\Service\SearchService;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
@@ -27,7 +26,9 @@ class SearchController extends OCSController {
parent::__construct($appName, $request);
}
#[NoAdminRequired]
/**
* @NoAdminRequired
*/
public function search(string $term, ?int $limit = null, ?int $cursor = null): DataResponse {
$cards = $this->searchService->searchCards($term, $limit, $cursor);
return new DataResponse(array_map(function (Card $card) {

View File

@@ -7,16 +7,14 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Service\BoardService;
use OCA\Deck\Service\StackService;
use OCA\Deck\StatusException;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\CORS;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\DataResponse;
use OCP\IRequest;
use function Sabre\HTTP\parseDate;
use Sabre\HTTP\Util;
/**
* Class StackApiController
@@ -31,21 +29,23 @@ class StackApiController extends ApiController {
$appName,
IRequest $request,
private StackService $stackService,
private BoardService $boardService,
) {
parent::__construct($appName, $request);
}
/**
* Return all the stacks in the specified board.
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Return all of the stacks in the specified board.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function index(): DataResponse {
public function index() {
$since = 0;
$modified = $this->request->getHeader('If-Modified-Since');
if ($modified !== '') {
$date = parseDate($modified);
if ($modified !== null && $modified !== '') {
$date = Util::parseHTTPDate($modified);
if (!$date) {
throw new StatusException('Invalid If-Modified-Since header provided.');
}
@@ -56,12 +56,13 @@ class StackApiController extends ApiController {
}
/**
* Return all the stacks in the specified board.
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Return all of the stacks in the specified board.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function get(): DataResponse {
public function get() {
$stack = $this->stackService->find($this->request->getParam('stackId'));
$response = new DataResponse($stack, HTTP::STATUS_OK);
$response->setETag($stack->getETag());
@@ -69,45 +70,55 @@ class StackApiController extends ApiController {
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $order
*
* Create a stack with the specified title and order.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function create(string $title, int $order): DataResponse {
public function create($title, $order) {
$stack = $this->stackService->create($title, $this->request->getParam('boardId'), $order);
return new DataResponse($stack, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* @params $title
* @params $order
*
* Update a stack by the specified stackId and boardId with the values that were put.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function update(string $title, int $order) {
public function update($title, $order) {
$stack = $this->stackService->update($this->request->getParam('stackId'), $title, $this->request->getParam('boardId'), $order, 0);
return new DataResponse($stack, HTTP::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* Delete the stack specified by $this->request->getParam('stackId').
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function delete(): DataResponse {
public function delete() {
$stack = $this->stackService->delete($this->request->getParam('stackId'));
return new DataResponse($stack, HTTP::STATUS_OK);
}
/**
* Get the stacks that have been archived.
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*
* get the stacks that have been archived.
*/
#[NoAdminRequired]
#[CORS]
#[NoCSRFRequired]
public function getArchived(): DataResponse {
public function getArchived() {
$stacks = $this->stackService->findAllArchived($this->request->getParam('boardId'));
return new DataResponse($stacks, HTTP::STATUS_OK);
}

View File

@@ -7,12 +7,10 @@
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Stack;
use OCA\Deck\Service\StackService;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http\Attribute\NoAdminRequired;
use OCP\IRequest;
class StackController extends Controller {
@@ -20,54 +18,78 @@ class StackController extends Controller {
string $appName,
IRequest $request,
private StackService $stackService,
private $userId,
) {
parent::__construct($appName, $request);
}
/**
* @return Stack[]
* @NoAdminRequired
* @param $boardId
* @return array
*/
#[NoAdminRequired]
public function index(int $boardId): array {
public function index($boardId) {
return $this->stackService->findAll($boardId);
}
/**
* @return Stack[]
* @NoAdminRequired
* @param $boardId
* @return array
*/
#[NoAdminRequired]
public function archived(int $boardId): array {
public function archived($boardId) {
return $this->stackService->findAllArchived($boardId);
}
#[NoAdminRequired]
public function create(string $title, int $boardId, int $order = 999): Stack {
/**
* @NoAdminRequired
* @param $title
* @param $boardId
* @param int $order
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $boardId, $order = 999) {
return $this->stackService->create($title, $boardId, $order);
}
#[NoAdminRequired]
public function update(int $id, string $title, int $boardId, int $order, ?int $deletedAt = null): Stack {
/**
* @NoAdminRequired
* @param $id
* @param $title
* @param $boardId
* @param $order
* @param $deletedAt
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $boardId, $order, $deletedAt) {
return $this->stackService->update($id, $title, $boardId, $order, $deletedAt);
}
/**
* @return array<int, Stack>
* @NoAdminRequired
* @param $stackId
* @param $order
* @return array
*/
#[NoAdminRequired]
public function reorder(int $stackId, int $order): array {
return $this->stackService->reorder($stackId, $order);
public function reorder($stackId, $order) {
return $this->stackService->reorder((int)$stackId, (int)$order);
}
#[NoAdminRequired]
public function delete(int $stackId): Stack {
/**
* @NoAdminRequired
* @param $stackId
* @return \OCP\AppFramework\Db\Entity
*/
public function delete($stackId) {
return $this->stackService->delete($stackId);
}
/**
* @return Stack[]
* @NoAdminRequired
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
*/
#[NoAdminRequired]
public function deleted(int $boardId): array {
public function deleted($boardId) {
return $this->stackService->fetchDeleted($boardId);
}
}

View File

@@ -7,20 +7,6 @@
namespace OCA\Deck\Db;
/**
* @method int getBoardId()
* @method bool isPermissionEdit()
* @method void setPermissionEdit(bool $permissionEdit)
* @method bool isPermissionShare()
* @method void setPermissionShare(bool $permissionShare)
* @method bool isPermissionManage()
* @method void setPermissionManage(bool $permissionManage)
* @method int getType()
* @method void setType(int $type)
* @method bool isOwner()
* @method void setOwner(int $owner)
*
*/
class Acl extends RelationalEntity {
public const PERMISSION_READ = 0;
public const PERMISSION_EDIT = 1;
@@ -51,13 +37,17 @@ class Acl extends RelationalEntity {
$this->addResolvable('participant');
}
public function getPermission(int $permission): bool {
return match ($permission) {
self::PERMISSION_READ => true,
self::PERMISSION_EDIT => $this->getPermissionEdit(),
self::PERMISSION_SHARE => $this->getPermissionShare(),
self::PERMISSION_MANAGE => $this->getPermissionManage(),
default => false,
};
public function getPermission($permission) {
switch ($permission) {
case self::PERMISSION_READ:
return true;
case self::PERMISSION_EDIT:
return $this->getPermissionEdit();
case self::PERMISSION_SHARE:
return $this->getPermissionShare();
case self::PERMISSION_MANAGE:
return $this->getPermissionManage();
}
return false;
}
}

View File

@@ -19,10 +19,13 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findAll(int $boardId, ?int $limit = null, ?int $offset = null) {
public function findAll($boardId, $limit = null, $offset = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage')
->from('deck_board_acl')
@@ -48,9 +51,12 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $userId
* @param numeric $id
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner(string $userId, int $id): bool {
public function isOwner($userId, $id): bool {
$aclId = $id;
$qb = $this->db->getQueryBuilder();
$qb->select('acl.id')
@@ -62,7 +68,11 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
return count($qb->executeQuery()->fetchAll()) > 0;
}
public function findBoardId(int $id): ?int {
/**
* @param numeric $id
* @return int|null
*/
public function findBoardId($id): ?int {
try {
$entity = $this->find($id);
return $entity->getBoardId();
@@ -77,7 +87,7 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findByParticipant(int $type, string $participant): array {
public function findByParticipant($type, $participant): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')

View File

@@ -107,11 +107,11 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper {
}
public function isOwner(string $userId, int $id): bool {
public function isOwner($userId, $id): bool {
return $this->cardMapper->isOwner($userId, $id);
}
public function findBoardId(int $id): ?int {
public function findBoardId($id): ?int {
return $this->cardMapper->findBoardId($id);
}
@@ -123,9 +123,6 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper {
* @throws NotFoundException
*/
public function insert(Entity $entity): Entity {
if (!($entity instanceof Assignment)) {
throw new \LogicException('Trying to insert a ' . get_class($entity) . ' in the assignment mapper');
}
$origin = $this->getOrigin($entity);
if ($origin === null) {
throw new NotFoundException('No origin found for assignment');
@@ -144,7 +141,7 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper {
});
}
public function isUserAssigned(int $cardId, string $userId): bool {
public function isUserAssigned($cardId, $userId): bool {
$assignments = $this->findAll($cardId);
foreach ($assignments as $assignment) {
$origin = $this->getOrigin($assignment);

View File

@@ -36,11 +36,13 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param int $id
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function find(int $id): Attachment {
public function find($id) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -50,11 +52,14 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param int $cardId
* @param string $data
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function findByData(int $cardId, string $data): Attachment {
public function findByData($cardId, $data) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -65,10 +70,11 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param $cardId
* @return Entity[]
* @throws \OCP\DB\Exception
*/
public function findAll(int $cardId): array {
public function findAll($cardId) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -80,9 +86,11 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @return Attachment[]
* @param null $cardId
* @param bool $withOffset
* @return array
*/
public function findToDelete(?int $cardId = null, bool $withOffset = true): array {
public function findToDelete($cardId = null, $withOffset = true) {
// add buffer of 5 min
$timeLimit = time() - (60 * 5);
$qb = $this->db->getQueryBuilder();
@@ -104,8 +112,12 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
/**
* Check if $userId is owner of Entity with $id
*
* @param $userId string userId
* @param $id int|string unique entity identifier
* @return boolean
*/
public function isOwner(string $userId, int $id): bool {
public function isOwner($userId, $id): bool {
try {
$attachment = $this->find($id);
return $this->cardMapper->isOwner($userId, $attachment->getCardId());
@@ -118,10 +130,10 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
/**
* Query boardId for Entity of given $id
*
* @param $id int unique entity identifier
* @param $id int|string unique entity identifier
* @return int|null id of Board
*/
public function findBoardId(int $id): ?int {
public function findBoardId($id): ?int {
try {
$attachment = $this->find($id);
} catch (\Exception $e) {

View File

@@ -10,20 +10,10 @@ namespace OCA\Deck\Db;
/**
* @method int getId()
* @method string getTitle()
* @method void setTitle(string $title)
* @method int getShared()
* @method void setShared(int $shared)
* @method bool isArchived()
* @method bool getArchived()
* @method void setArchived(bool $archived)
* @method int getDeletedAt()
* @method void setDeletedAt(int $deletedAt)
* @method int getLastModified()
* @method void setLastModified(int $lastModified)
* @method string getOwner()
* @method void setOwner(string $owner)
* @method string getColor()
* @method void setColor(string $color)
*/
class Board extends RelationalEntity {
protected $title;

View File

@@ -469,16 +469,16 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
return parent::delete($entity);
}
public function isOwner(string $userId, int $id): bool {
public function isOwner($userId, $id): bool {
$board = $this->find($id);
return ($board->getOwner() === $userId);
}
public function findBoardId(int $id): ?int {
public function findBoardId($id): ?int {
return $id;
}
public function mapAcl(Acl &$acl): void {
public function mapAcl(Acl &$acl) {
$acl->resolveRelation('participant', function ($participant) use (&$acl) {
if ($acl->getType() === Acl::PERMISSION_TYPE_USER) {
if ($this->userManager->userExists($acl->getParticipant())) {

View File

@@ -15,18 +15,13 @@ use Sabre\VObject\Component\VCalendar;
/**
* @method string getTitle()
* @method void setTitle(string $title)
* @method string getDescription()
* @method string getDescriptionPrev()
* @method int getStackId()
* @method void setStackId(int $stackId)
* @method int getOrder()
* @method void setOrder(int $order)
* @method int getLastModified()
* @method int getCreatedAt()
* @method bool getArchived()
* @method string getType()
* @method void setType(string $type)
* @method int getDeletedAt()
* @method void setDeletedAt(int $deletedAt)
* @method bool getNotified()
@@ -73,8 +68,8 @@ class Card extends RelationalEntity {
protected $createdAt;
protected $labels;
protected $assignedUsers;
protected array $attachments = [];
protected int $attachmentCount = 0;
protected $attachments;
protected $attachmentCount;
protected $owner;
protected $order;
protected $archived = false;

View File

@@ -86,15 +86,16 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$updatedFields = $entity->getUpdatedFields();
if (isset($updatedFields['duedate']) && $updatedFields['duedate']) {
try {
/** @var Card $existing */
$existing = $this->find($entity->getId());
if ($entity->getDueDate() !== $existing->getDueDate()) {
if ($existing && $entity->getDuedate() !== $existing->getDuedate()) {
$entity->setNotified(false);
}
// remove pending notifications
$notification = $this->notificationManager->createNotification();
$notification
->setApp('deck')
->setObject('card', (string)$entity->getId());
->setObject('card', $entity->getId());
$this->notificationManager->markProcessed($notification);
} catch (Exception $e) {
}
@@ -134,7 +135,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
* @return Card[]
* @throws \OCP\DB\Exception
*/
public function findAll($stackId, ?int $limit = null, int $offset = 0, int $since = -1) {
public function findAll($stackId, ?int $limit = null, ?int $offset = null, int $since = -1) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_cards')
@@ -154,7 +155,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
* @return array<int, null|Card[]>
* @throws \OCP\DB\Exception
*/
public function findAllForStacks(array $stackIds, ?int $limit = null, int $offset = 0, int $since = -1): array {
public function findAllForStacks(array $stackIds, ?int $limit = null, ?int $offset = null, int $since = -1): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_cards')
@@ -193,10 +194,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $qb;
}
/**
* @return Card[]
*/
public function findToDelete(int $timeLimit, ?int $limit = null): array {
public function findToDelete($timeLimit, $limit = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'owner', 'archived', 'deleted_at', 'last_modified')
->from('deck_cards')
@@ -207,10 +205,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @return Card[]
*/
public function findDeleted(int $boardId, ?int $limit = null, int $offset = 0): array {
public function findDeleted($boardId, $limit = null, $offset = null) {
$qb = $this->queryCardsByBoard($boardId);
$qb->andWhere($qb->expr()->neq('c.deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->setMaxResults($limit)
@@ -220,10 +215,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @return Card[]
*/
public function findCalendarEntries(int $boardId, ?int $limit = null, $offset = 0): array {
public function findCalendarEntries($boardId, $limit = null, $offset = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
->from('deck_cards', 'c')
@@ -278,11 +270,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @param int[] $boardIds
* @return Card[]
*/
public function findAllWithDue(array $boardIds): array {
public function findAllWithDue(array $boardIds) {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
->from('deck_cards', 'c')
@@ -299,11 +287,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @param int[] $boardIds
* @return Card[]
*/
public function findToMeOrNotAssignedCards(array $boardIds, string $username): array {
public function findToMeOrNotAssignedCards(array $boardIds, string $username) {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
->from('deck_cards', 'c')
@@ -325,10 +309,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @return Card[]
*/
public function findOverdue(): array {
public function findOverdue() {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'duedate', 'notified')
->from('deck_cards')
@@ -340,9 +321,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @return Card[]
*/
public function findUnexposedDescriptionChances() {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'duedate', 'notified', 'description_prev', 'last_editor', 'description')
@@ -351,9 +329,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @return Card[]
*/
public function search(array $boardIds, SearchQuery $query, ?int $limit = null, ?int $offset = null): array {
$qb = $this->queryCardsByBoards($boardIds);
$this->extendQueryByFilter($qb, $query);
@@ -388,7 +363,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$qb->andWhere($qb->expr()->lt('c.last_modified', $qb->createNamedParameter($offset, IQueryBuilder::PARAM_INT)));
}
$result = $qb->executeQuery();
$result = $qb->execute();
$entities = [];
while ($row = $result->fetch()) {
$entities[] = Card::fromRow($row);
@@ -431,7 +406,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$qb->andWhere($qb->expr()->lt('comments.id', $qb->createNamedParameter($offset, IQueryBuilder::PARAM_INT)));
}
$result = $qb->executeQuery();
$result = $qb->execute();
$entities = $result->fetchAll();
$result->closeCursor();
return $entities;
@@ -527,7 +502,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
});
$groups = $this->groupManager->search($assignment->getValue());
foreach ($searchUsers as $user) {
$groups = array_merge($groups, $this->groupManager->getUserGroups($user));
$groups = array_merge($groups, $this->groupManager->getUserIdGroups($user->getUID()));
}
$assignmentSearches = [];
@@ -580,7 +555,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$result = $qb->executeQuery();
$result = $qb->execute();
$all = $result->fetchAll();
$result->closeCursor();
return $all;
@@ -592,32 +567,32 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return parent::delete($entity);
}
public function deleteByStack($stackId): void {
public function deleteByStack($stackId) {
$cards = $this->findAllByStack($stackId);
foreach ($cards as $card) {
$this->delete($card);
}
}
public function assignLabel(int $card, int $label): void {
public function assignLabel($card, $label) {
$qb = $this->db->getQueryBuilder();
$qb->insert('deck_assigned_labels')
->values([
'label_id' => $qb->createNamedParameter($label, IQueryBuilder::PARAM_INT),
'card_id' => $qb->createNamedParameter($card, IQueryBuilder::PARAM_INT),
]);
$qb->executeStatement();
$qb->execute();
}
public function removeLabel(int $card, int $label): void {
public function removeLabel($card, $label) {
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($card, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('label_id', $qb->createNamedParameter($label, IQueryBuilder::PARAM_INT)));
$qb->executeStatement();
$qb->execute();
}
public function isOwner(string $userId, int $id): bool {
public function isOwner($userId, $id): bool {
$qb = $this->db->getQueryBuilder();
$qb->select('c.id')
->from($this->getTableName(), 'c')
@@ -629,7 +604,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return count($qb->executeQuery()->fetchAll()) > 0;
}
public function findBoardId(int $id): ?int {
public function findBoardId($id): ?int {
$result = $this->cache->get('findBoardId:' . $id);
if ($result === null) {
try {
@@ -659,11 +634,13 @@ class CardMapper extends QBMapper implements IPermissionMapper {
}
public function transferOwnership(string $ownerId, string $newOwnerId, ?int $boardId = null): void {
$qb = $this->db->getQueryBuilder();
$qb->update($this->getTableName())
->set('owner', $qb->createNamedParameter($newOwnerId, IQueryBuilder::PARAM_STR))
->where('owner', $qb->createNamedParameter($ownerId, IQueryBuilder::PARAM_STR))
->executeStatement();
$params = [
'owner' => $ownerId,
'newOwner' => $newOwnerId
];
$sql = "UPDATE `*PREFIX*{$this->tableName}` SET `owner` = :newOwner WHERE `owner` = :owner";
$stmt = $this->db->executeQuery($sql, $params);
$stmt->closeCursor();
}
public function remapCardOwner(int $boardId, string $userId, string $newUserId): void {

View File

@@ -19,11 +19,12 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
abstract class DeckMapper extends QBMapper {
/**
* @param $id
* @return T
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws \OCP\AppFramework\Db\DoesNotExistException
*/
public function find(int $id): Entity {
public function find($id) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -36,7 +37,7 @@ abstract class DeckMapper extends QBMapper {
* Helper function to split passed array into chunks of 1000 elements and
* call a given callback for fetching query results
*
* Can be useful to limit to 1000 results per query for oracle compatibility
* Can be useful to limit to 1000 results per query for oracle compatiblity
* but still iterate over all results
*/
public function chunkQuery(array $ids, callable $callback): Generator {

View File

@@ -8,25 +8,22 @@
namespace OCA\Deck\Db;
/**
*
*/
interface IPermissionMapper {
/**
* Check if $userId is owner of Entity with $id
*
* @param $userId string userId
* @param $id int unique entity identifier
* @param $id int|string unique entity identifier
* @return boolean
*/
public function isOwner(string $userId, int $id): bool;
public function isOwner($userId, $id): bool;
/**
* Query boardId for Entity of given $id
*
* @param $id int unique entity identifier
* @return ?int id of Board
* @param $id int|string unique entity identifier
* @return int|null id of Board
*/
public function findBoardId(int $id): ?int;
public function findBoardId($id): ?int;
}

View File

@@ -8,16 +8,7 @@
namespace OCA\Deck\Db;
/**
* @method string getTitle()
* @method void setTitle(string $title)
* @method string getColor()
* @method void setColor(string $color)
* @method int getBoardId()
* @method void setBoardId(int $boardId)
* @method int getCardId()
* @method void setCardId(int $cardId)
* @method int getLastModified()
* @method void setLastModified(int $lastModified)
* @method getTitle(): string
*/
class Label extends RelationalEntity {
protected $title;
@@ -33,7 +24,7 @@ class Label extends RelationalEntity {
$this->addType('lastModified', 'integer');
}
public function getETag(): string {
public function getETag() {
return md5((string)$this->getLastModified());
}
}

View File

@@ -20,10 +20,13 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAll(int $boardId, ?int $limit = null, int $offset = 0): array {
public function findAll($boardId, $limit = null, $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -41,10 +44,13 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $cardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAssignedLabelsForCard(int $cardId, ?int $limit = null, int $offset = 0): array {
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.*', 'card_id')
->from($this->getTableName(), 'l')
@@ -57,7 +63,7 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findAssignedLabelsForCards(array $cardIds, ?int $limit = null, int $offset = 0): array {
public function findAssignedLabelsForCards($cardIds, $limit = null, $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.*', 'card_id')
->from($this->getTableName(), 'l')
@@ -71,10 +77,13 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Label[]
* @throws \OCP\DB\Exception
*/
public function findAssignedLabelsForBoard(int $boardId, ?int $limit = null, int $offset = 0): array {
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('l.id as id', 'l.title as title', 'l.color as color')
->selectAlias('c.id', 'card_id')
@@ -104,10 +113,11 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @return array<int, list<Label>>
* @param numeric $boardId
* @return array
* @throws \OCP\DB\Exception
*/
public function getAssignedLabelsForBoard(int $boardId): array {
public function getAssignedLabelsForBoard($boardId) {
$labels = $this->findAssignedLabelsForBoard($boardId);
$result = [];
foreach ($labels as $label) {
@@ -120,9 +130,11 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $labelId
* @return void
* @throws \OCP\DB\Exception
*/
public function deleteLabelAssignments(int $labelId): void {
public function deleteLabelAssignments($labelId) {
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('label_id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)));
@@ -130,9 +142,11 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $cardId
* @return void
* @throws \OCP\DB\Exception
*/
public function deleteLabelAssignmentsForCard(int $cardId): void {
public function deleteLabelAssignmentsForCard($cardId) {
$qb = $this->db->getQueryBuilder();
$qb->delete('deck_assigned_labels')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
@@ -140,25 +154,33 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param string $userId
* @param numeric $labelId
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner(string $userId, int $id): bool {
public function isOwner($userId, $labelId): bool {
$qb = $this->db->getQueryBuilder();
$qb->select('l.id')
->from($this->getTableName(), 'l')
->innerJoin('l', 'deck_boards', 'b', 'l.board_id = b.id')
->where($qb->expr()->eq('l.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->where($qb->expr()->eq('l.id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
return count($qb->executeQuery()->fetchAll()) > 0;
}
public function findBoardId(int $id): ?int {
/**
* @param numeric $id
* @return int|null
*/
public function findBoardId($id): ?int {
try {
$entity = $this->find($id);
return $entity->getBoardId();
} catch (DoesNotExistException|MultipleObjectsReturnedException) {
return null;
} catch (DoesNotExistException $e) {
} catch (MultipleObjectsReturnedException $e) {
}
return null;
}
}

View File

@@ -11,16 +11,10 @@ use Sabre\VObject\Component\VCalendar;
/**
* @method int getId()
* @method string getTitle()
* @method void setTitle(string $title)
* @method int getBoardId()
* @method void setBoardId(int $boardId)
* @method int getDeletedAt()
* @method void setDeletedAt(int $deletedAt)
* @method int getLastModified()
* @method void setLastModified(int $lastModified)
* @method \int getOrder()
* @method void setOrder(int $order)
* @method int getOrder()
* @method Card[] getCards()
*/
class Stack extends RelationalEntity {

View File

@@ -35,11 +35,13 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
/**
* @param numeric $id
* @return Stack
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function find(int $id): Stack {
public function find($id): Stack {
if (isset($this->stackCache[(string)$id])) {
return $this->stackCache[(string)$id];
}
@@ -54,9 +56,11 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param $cardId
* @return Stack|null
* @throws \OCP\DB\Exception
*/
public function findStackFromCardId(int $cardId): ?Stack {
public function findStackFromCardId($cardId): ?Stack {
$qb = $this->db->getQueryBuilder();
$qb->select('s.*')
->from($this->getTableName(), 's')
@@ -72,10 +76,11 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function findAll(int $boardId, ?int $limit = null, int $offset = 0): array {
public function findAll($boardId, ?int $limit = null, ?int $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -88,9 +93,13 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function findDeleted(int $boardId, ?int $limit = null, int $offset = 0): array {
public function findDeleted($boardId, $limit = null, $offset = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
@@ -116,9 +125,12 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $userId
* @param numeric $stackId
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner(string $userId, int $id): bool {
public function isOwner($userId, $id): bool {
$qb = $this->db->getQueryBuilder();
$qb->select('s.id')
->from($this->getTableName(), 's')
@@ -130,9 +142,11 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param numeric $id
* @return int|null
* @throws \OCP\DB\Exception
*/
public function findBoardId(int $id): ?int {
public function findBoardId($id): ?int {
$result = $this->cache->get('findBoardId:' . $id);
if ($result !== null) {
return $result !== false ? $result : null;
@@ -149,10 +163,6 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
return $result !== false ? $result : null;
}
/**
* @return array<Stack>
* @throws \OCP\DB\Exception
*/
public function findToDelete(): array {
// add buffer of 5 min
$timeLimit = time() - (60 * 5);

View File

@@ -35,10 +35,6 @@ class BeforeTemplateRenderedListener implements IEventListener {
Util::addStyle('deck', 'deck');
$pathInfo = $this->request->getPathInfo();
if (!$pathInfo) {
return;
}
if (str_starts_with($pathInfo, '/apps/calendar')) {
Util::addScript('deck', 'deck-calendar');
}

View File

@@ -18,10 +18,14 @@ use OCP\EventDispatcher\IEventListener;
/** @template-implements IEventListener<Event|AclDeletedEvent|AclCreatedEvent> */
class ResourceListener implements IEventListener {
public function __construct(
private readonly IManager $resourceManager,
private readonly ResourceProviderCard $resourceProviderCard,
) {
/** @var IManager */
private $resourceManager;
/** @var ResourceProviderCard */
private $resourceProviderCard;
public function __construct(IManager $resourceManager, ResourceProviderCard $resourceProviderCard) {
$this->resourceManager = $resourceManager;
$this->resourceProviderCard = $resourceProviderCard;
}
public function handle(Event $event): void {
@@ -34,10 +38,10 @@ class ResourceListener implements IEventListener {
$this->resourceManager->invalidateAccessCacheForProvider($this->resourceProviderCard);
try {
$resource = $this->resourceManager->getResourceForUser(ResourceProvider::RESOURCE_TYPE, (string)$boardId, null);
$resource = $this->resourceManager->getResourceForUser(ResourceProvider::RESOURCE_TYPE, $boardId, null);
$this->resourceManager->invalidateAccessCacheForResource($resource);
} catch (ResourceException $e) {
// If there is no resource we don't need to invalidate anything, but this should not happen anyway
// If there is no resource we don't need to invalidate anything, but this should not happen anyways
}
}
}

View File

@@ -11,7 +11,6 @@ use OCA\Deck\Db\Board;
class BoardSummary extends Board {
private Board $board;
/** @psalm-suppress ConstructorSignatureMismatch */
public function __construct(Board $board) {
parent::__construct();
$this->board = $board;
@@ -28,7 +27,7 @@ class BoardSummary extends Board {
return $this->board->getter($name);
}
public function __call($methodName, $args) {
return $this->board->__call($methodName, $args);
public function __call($name, $arguments) {
return $this->board->__call($name, $arguments);
}
}

View File

@@ -15,7 +15,6 @@ class CardDetails extends Card {
private ?Board $board;
private ?Reference $referenceData = null;
/** @psalm-suppress ConstructorSignatureMismatch */
public function __construct(Card $card, ?Board $board = null) {
parent::__construct();
$this->card = $card;

View File

@@ -95,7 +95,7 @@ class NotificationHelper {
$shouldNotify = $notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ALL;
if ($user->getUID() === $board->getOwner() && count($board->getAcl() ?? []) === 0) {
if ($user->getUID() === $board->getOwner() && count($board->getAcl()) === 0) {
// Notify if all or assigned is configured for unshared boards
$shouldNotify = true;
} elseif ($notificationSetting === ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED && $this->assignmentMapper->isUserAssigned($card->getId(), $user->getUID())) {

View File

@@ -77,7 +77,7 @@ class Notifier implements INotifier {
*/
public function prepare(INotification $notification, string $languageCode): INotification {
$l = $this->l10nFactory->get('deck', $languageCode);
if ($notification->getApp() !== 'deck' || $notification->getObjectType() === 'activity_notification') {
if ($notification->getApp() !== 'deck') {
throw new UnknownNotificationException();
}
$notification->setIcon($this->url->getAbsoluteURL($this->url->imagePath('deck', 'deck-dark.svg')));

View File

@@ -21,6 +21,7 @@ use OCA\Deck\NotFoundException;
use OCA\Deck\Notification\NotificationHelper;
use OCA\Deck\Validators\AssignmentServiceValidator;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\EventDispatcher\IEventDispatcher;
@@ -91,12 +92,15 @@ class AssignmentService {
}
/**
* @param $cardId
* @param $userId
* @return bool|null|Entity
* @throws BadRequestException
* @throws NoPermissionException
* @throws MultipleObjectsReturnedException
* @throws DoesNotExistException
*/
public function assignUser(int $cardId, string $userId, int $type = Assignment::TYPE_USER): Assignment {
public function assignUser($cardId, $userId, int $type = Assignment::TYPE_USER) {
$this->assignmentServiceValidator->check(compact('cardId', 'userId'));
if ($type !== Assignment::TYPE_USER && $type !== Assignment::TYPE_GROUP) {
@@ -140,13 +144,16 @@ class AssignmentService {
}
/**
* @param $cardId
* @param $userId
* @return Entity
* @throws BadRequestException
* @throws NotFoundException
* @throws NoPermissionException
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function unassignUser(int $cardId, string $userId, int $type = 0): Assignment {
public function unassignUser($cardId, $userId, $type = 0) {
$this->assignmentServiceValidator->check(compact('cardId', 'userId'));
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);

View File

@@ -25,7 +25,6 @@ use OCP\AppFramework\Db\IMapperException;
use OCP\AppFramework\Http\Response;
use OCP\IL10N;
use OCP\IUserManager;
use Psr\Container\ContainerExceptionInterface;
class AttachmentService {
private $attachmentMapper;
@@ -81,16 +80,20 @@ class AttachmentService {
}
/**
* @throws ContainerExceptionInterface
* @param string $type
* @param string $class
* @throws \OCP\AppFramework\QueryException
*/
public function registerAttachmentService(string $type, string $class): void {
$this->services[$type] = $this->application->getContainer()->get($class);
public function registerAttachmentService($type, $class) {
$this->services[$type] = $this->application->getContainer()->query($class);
}
/**
* @param string $type
* @return IAttachmentService
* @throws InvalidAttachmentType
*/
public function getService(string $type): IAttachmentService {
public function getService($type) {
if (isset($this->services[$type])) {
return $this->services[$type];
}
@@ -98,11 +101,16 @@ class AttachmentService {
}
/**
* @return Attachment[]
* @param $cardId
* @return array
* @throws \OCA\Deck\NoPermissionException
* @throws BadRequestException
*/
public function findAll(int $cardId, bool $withDeleted = false): array {
public function findAll($cardId, $withDeleted = false) {
if (is_numeric($cardId) === false) {
throw new BadRequestException('card id must be a number');
}
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
$attachments = $this->attachmentMapper->findAll($cardId);
@@ -114,7 +122,7 @@ class AttachmentService {
/** @var IAttachmentService $service */
$service = $this->getService($attachmentType);
if ($service instanceof ICustomAttachmentService) {
$attachments = array_merge($attachments, $service->listAttachments($cardId));
$attachments = array_merge($attachments, $service->listAttachments((int)$cardId));
}
}
@@ -132,40 +140,49 @@ class AttachmentService {
}
/**
* @param $cardId
* @return int|mixed
* @throws BadRequestException
* @throws InvalidAttachmentType
* @throws \OCP\DB\Exception
*/
public function count(int $cardId): int {
$count = $this->attachmentCacheHelper->getAttachmentCount($cardId);
public function count($cardId) {
if (is_numeric($cardId) === false) {
throw new BadRequestException('card id must be a number');
}
$count = $this->attachmentCacheHelper->getAttachmentCount((int)$cardId);
if ($count === null) {
$count = count($this->attachmentMapper->findAll($cardId));
foreach (array_keys($this->services) as $attachmentType) {
$service = $this->getService($attachmentType);
if ($service instanceof ICustomAttachmentService) {
$count += $service->getAttachmentCount($cardId);
$count += $service->getAttachmentCount((int)$cardId);
}
}
$this->attachmentCacheHelper->setAttachmentCount($cardId, $count);
$this->attachmentCacheHelper->setAttachmentCount((int)$cardId, $count);
}
return $count;
}
/**
* @param $cardId
* @param $type
* @param $data
* @return Attachment|\OCP\AppFramework\Db\Entity
* @throws NoPermissionException
* @throws StatusException
* @throws BadRequestException
*/
public function create(int $cardId, string $type, string $data) {
public function create($cardId, $type, $data) {
$this->attachmentServiceValidator->check(compact('cardId', 'type'));
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
$this->attachmentCacheHelper->clearAttachmentCount($cardId);
$this->attachmentCacheHelper->clearAttachmentCount((int)$cardId);
$attachment = new Attachment();
$attachment->setCardId($cardId);
$attachment->setType($type);
@@ -201,10 +218,12 @@ class AttachmentService {
/**
* Display the attachment
*
* @param $attachmentId
* @return Response
* @throws NoPermissionException
* @throws NotFoundException
*/
public function display(int $cardId, int $attachmentId, string $type = 'deck_file'): Response {
public function display($cardId, $attachmentId, $type = 'deck_file') {
try {
$service = $this->getService($type);
} catch (InvalidAttachmentType $e) {
@@ -238,10 +257,13 @@ class AttachmentService {
/**
* Update an attachment with custom data
*
* @param $attachmentId
* @param $data
* @return mixed
* @throws BadRequestException
* @throws NoPermissionException
*/
public function update(int $cardId, int $attachmentId, string $data, string $type = 'deck_file'): Attachment {
public function update($cardId, $attachmentId, $data, $type = 'deck_file') {
$this->attachmentServiceValidator->check(compact('cardId', 'type', 'data'));
try {
@@ -362,6 +384,8 @@ class AttachmentService {
}
/**
* @param Attachment $attachment
* @return Attachment
* @throws \ReflectionException
*/
private function addCreator(Attachment $attachment): Attachment {

View File

@@ -75,6 +75,8 @@ class BoardService {
/**
* Set a different user than the current one, e.g. when no user is available in occ
*
* @param string $userId
*/
public function setUserId(string $userId): void {
$this->userId = $userId;
@@ -115,18 +117,22 @@ class BoardService {
}
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
/** @var Board $board */
$board = $this->boardMapper->find($boardId, true, true, $allowDeleted);
[$board] = $this->enrichBoards([$board], $fullDetails);
return $board;
}
/**
* @param $mapper
* @param $id
* @return bool
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function isArchived($mapper, int $id): bool {
public function isArchived($mapper, $id) {
$this->boardServiceValidator->check(compact('id'));
try {
@@ -145,12 +151,15 @@ class BoardService {
}
/**
* @param $mapper
* @param $id
* @return bool
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function isDeleted($mapper, int $id): bool {
public function isDeleted($mapper, $id) {
$this->boardServiceValidator->check(compact('mapper', 'id'));
try {
@@ -170,9 +179,13 @@ class BoardService {
/**
* @param $title
* @param $userId
* @param $color
* @return \OCP\AppFramework\Db\Entity
* @throws BadRequestException
*/
public function create(string $title, string $userId, string $color): Board {
public function create($title, $userId, $color) {
$this->boardServiceValidator->check(compact('title', 'userId', 'color'));
if (!$this->permissionService->canCreate()) {
@@ -183,8 +196,7 @@ class BoardService {
$board->setTitle($title);
$board->setOwner($userId);
$board->setColor($color);
/** @var Board $board */
$board = $this->boardMapper->insert($board);
$new_board = $this->boardMapper->insert($board);
// create new labels
$default_labels = [
@@ -198,31 +210,33 @@ class BoardService {
$label = new Label();
$label->setColor($labelColor);
$label->setTitle($labelTitle);
$label->setBoardId($board->getId());
$label->setBoardId($new_board->getId());
$labels[] = $this->labelMapper->insert($label);
}
$board->setLabels($labels);
$this->boardMapper->mapOwner($board);
$permissions = $this->permissionService->matchPermissions($board);
$board->setPermissions([
$new_board->setLabels($labels);
$this->boardMapper->mapOwner($new_board);
$permissions = $this->permissionService->matchPermissions($new_board);
$new_board->setPermissions([
'PERMISSION_READ' => $permissions[Acl::PERMISSION_READ] ?? false,
'PERMISSION_EDIT' => $permissions[Acl::PERMISSION_EDIT] ?? false,
'PERMISSION_MANAGE' => $permissions[Acl::PERMISSION_MANAGE] ?? false,
'PERMISSION_SHARE' => $permissions[Acl::PERMISSION_SHARE] ?? false
]);
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $board, ActivityManager::SUBJECT_BOARD_CREATE, [], $userId);
$this->changeHelper->boardChanged($board->getId());
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_BOARD, $new_board, ActivityManager::SUBJECT_BOARD_CREATE, [], $userId);
$this->changeHelper->boardChanged($new_board->getId());
return $board;
return $new_board;
}
/**
* @param $id
* @return Board
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete(int $id): Board {
public function delete($id) {
$this->boardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -239,11 +253,13 @@ class BoardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
*/
public function deleteUndo(int $id): Board {
public function deleteUndo($id) {
$this->boardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -257,12 +273,14 @@ class BoardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function deleteForce(int $id): Board {
public function deleteForce($id) {
$this->boardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -273,12 +291,17 @@ class BoardService {
}
/**
* @param $id
* @param $title
* @param $color
* @param $archived
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update(int $id, string $title, string $color, bool $archived): Board {
public function update($id, $title, $color, $archived) {
$this->boardServiceValidator->check(compact('id', 'title', 'color', 'archived'));
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_MANAGE);
@@ -297,7 +320,7 @@ class BoardService {
return $board;
}
private function applyPermissions(int $boardId, bool $edit, bool $share, bool $manage, ?Acl $oldAcl = null): array {
private function applyPermissions($boardId, $edit, $share, $manage, $oldAcl = null) {
try {
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_MANAGE);
} catch (NoPermissionException $e) {
@@ -309,7 +332,7 @@ class BoardService {
return [$edit, $share, $manage];
}
public function enrichWithBoardSettings(Board $board): void {
public function enrichWithBoardSettings(Board $board) {
$globalCalendarConfig = (bool)$this->config->getUserValue($this->userId, Application::APP_ID, 'calendar', true);
$settings = [
'notify-due' => $this->config->getUserValue($this->userId, Application::APP_ID, 'board:' . $board->getId() . ':notify-due', ConfigService::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED),
@@ -318,7 +341,7 @@ class BoardService {
$board->setSettings($settings);
}
public function enrichWithActiveSessions(Board $board): void {
public function enrichWithActiveSessions(Board $board) {
$sessions = $this->sessionMapper->findAllActive($board->getId());
$board->setActiveSessions(array_values(
@@ -331,11 +354,17 @@ class BoardService {
}
/**
* @param Acl::PERMISSION_TYPE_* $type
* @param $boardId
* @param $type
* @param $participant
* @param $edit
* @param $share
* @param $manage
* @return \OCP\AppFramework\Db\Entity
* @throws BadRequestException
* @throws NoPermissionException
*/
public function addAcl(int $boardId, int $type, $participant, bool $edit, bool $share, bool $manage): Acl {
public function addAcl($boardId, $type, $participant, $edit, $share, $manage) {
$this->boardServiceValidator->check(compact('boardId', 'type', 'participant', 'edit', 'share', 'manage'));
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_SHARE);
@@ -371,12 +400,17 @@ class BoardService {
}
/**
* @param $id
* @param $edit
* @param $share
* @param $manage
* @return \OCP\AppFramework\Db\Entity
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function updateAcl(int $id, bool $edit, bool $share, bool $manage): Acl {
public function updateAcl($id, $edit, $share, $manage) {
$this->boardServiceValidator->check(compact('id', 'edit', 'share', 'manage'));
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
@@ -388,12 +422,12 @@ class BoardService {
$acl->setPermissionShare($share);
$acl->setPermissionManage($manage);
$this->boardMapper->mapAcl($acl);
$acl = $this->aclMapper->update($acl);
$board = $this->aclMapper->update($acl);
$this->changeHelper->boardChanged($acl->getBoardId());
$this->eventDispatcher->dispatchTyped(new AclUpdatedEvent($acl));
return $acl;
return $board;
}
/**
@@ -545,23 +579,27 @@ class BoardService {
}
/**
* @param $id
* @return Board
* @throws DoesNotExistException
* @throws NoPermissionException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function export(int $id): Board {
public function export($id) : Board {
if (is_numeric($id) === false) {
throw new BadRequestException('board id must be a number');
}
$this->permissionService->checkPermission($this->boardMapper, $id, Acl::PERMISSION_READ);
$board = $this->boardMapper->find($id);
$board = $this->boardMapper->find((int)$id);
$this->enrichWithCards($board);
$this->enrichWithLabels($board);
return $board;
}
/**
* @param Board[] $boards
* @return Board[]
*/
/** @param Board[] $boards */
private function enrichBoards(array $boards, bool $fullDetails = true): array {
$result = [];
foreach ($boards as $board) {
@@ -677,8 +715,8 @@ class BoardService {
}
}
private function enrichWithStacks(Board $board): void {
$stacks = $this->stackMapper->findAll($board->getId());
private function enrichWithStacks($board, $since = -1) {
$stacks = $this->stackMapper->findAll($board->getId(), null, null, $since);
if (\count($stacks) === 0) {
return;
@@ -687,8 +725,8 @@ class BoardService {
$board->setStacks($stacks);
}
private function enrichWithLabels(Board $board): void {
$labels = $this->labelMapper->findAll($board->getId());
private function enrichWithLabels($board, $since = -1) {
$labels = $this->labelMapper->findAll($board->getId(), null, null, $since);
if (\count($labels) === 0) {
return;
@@ -697,7 +735,7 @@ class BoardService {
$board->setLabels($labels);
}
private function enrichWithUsers(Board $board): void {
private function enrichWithUsers($board, $since = -1) {
$boardUsers = $this->permissionService->findUsers($board->getId());
if ($boardUsers === null || \count($boardUsers) === 0) {
return;
@@ -708,7 +746,7 @@ class BoardService {
/**
* Clean a given board data from the Cache
*/
private function clearBoardFromCache(Board $board): void {
private function clearBoardFromCache(Board $board) {
$boardId = $board->getId();
$boardOwnerId = $board->getOwner();
@@ -717,7 +755,7 @@ class BoardService {
unset($this->boardsCachePartial[$boardId]);
}
private function enrichWithCards(Board $board): void {
private function enrichWithCards($board) {
$stacks = $this->stackMapper->findAll($board->getId());
foreach ($stacks as $stack) {
$cards = $this->cardMapper->findAllByStack($stack->getId());

View File

@@ -64,14 +64,10 @@ class CardService {
) {
}
/**
* @param Card[] $cards
* @return CardDetails[]
*/
public function enrichCards(array $cards): array {
public function enrichCards($cards) {
$user = $this->userManager->get($this->userId);
$cardIds = array_map(function (Card $card) use ($user): int {
$cardIds = array_map(function (Card $card) use ($user) {
// Everything done in here might be heavy as it is executed for every card
$cardId = $card->getId();
$this->cardMapper->mapOwner($card);
@@ -124,8 +120,7 @@ class CardService {
);
}
/** @return Card[] */
public function fetchDeleted($boardId): array {
public function fetchDeleted($boardId) {
$this->cardServiceValidator->check(compact('boardId'));
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
$cards = $this->cardMapper->findDeleted($boardId);
@@ -134,12 +129,13 @@ class CardService {
}
/**
* @return \OCA\Deck\Db\RelationalEntity
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function find(int $cardId): Card {
public function find(int $cardId) {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
$card = $this->cardMapper->find($cardId);
[$card] = $this->enrichCards([$card]);
@@ -156,10 +152,7 @@ class CardService {
return $card;
}
/**
* @return Card[]
*/
public function findCalendarEntries(int $boardId): array {
public function findCalendarEntries($boardId) {
try {
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
} catch (NoPermissionException $e) {
@@ -170,13 +163,20 @@ class CardService {
}
/**
* @param $title
* @param $stackId
* @param $type
* @param integer $order
* @param $description
* @param $owner
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadrequestException
*/
public function create(string $title, int $stackId, string $type, int $order, string $owner, string $description = '', $duedate = null): Card {
public function create($title, $stackId, $type, $order, $owner, $description = '', $duedate = null) {
$this->cardServiceValidator->check(compact('title', 'stackId', 'type', 'order', 'owner'));
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_EDIT);
@@ -203,13 +203,19 @@ class CardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete(int $id): Card {
public function delete($id) {
if (is_numeric($id) === false) {
throw new BadRequestException('card id must be a number');
}
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
if ($this->boardService->isArchived($this->cardMapper, $id)) {
throw new StatusException('Operation not allowed. This board is archived.');
@@ -227,13 +233,25 @@ class CardService {
}
/**
* @param $id
* @param $title
* @param $stackId
* @param $type
* @param $owner
* @param $description
* @param $order
* @param $duedate
* @param $deletedAt
* @param $archived
* @param $done
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update(int $id, string $title, int $stackId, string $type, string $owner, string $description = '', int $order = 0, ?string $duedate = null, ?int $deletedAt = null, ?bool $archived = null, ?OptionalNullableValue $done = null): Card {
public function update($id, $title, $stackId, $type, $owner, $description = '', $order = 0, $duedate = null, $deletedAt = null, $archived = null, ?OptionalNullableValue $done = null) {
$this->cardServiceValidator->check(compact('id', 'title', 'stackId', 'type', 'owner', 'order'));
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT, allowDeletedCard: true);
@@ -380,13 +398,16 @@ class CardService {
}
/**
* @param $id
* @param $title
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function rename(int $id, string $title): Card {
public function rename($id, $title) {
$this->cardServiceValidator->check(compact('id', 'title'));
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
@@ -408,14 +429,17 @@ class CardService {
}
/**
* @return list<Card>
* @param $id
* @param $stackId
* @param $order
* @return array
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function reorder(int $id, int $stackId, int $order): array {
public function reorder($id, $stackId, $order) {
$this->cardServiceValidator->check(compact('id', 'stackId', 'order'));
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
@@ -464,13 +488,15 @@ class CardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function archive(int $id): Card {
public function archive($id) {
$this->cardServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->cardMapper, $id, Acl::PERMISSION_EDIT);
@@ -491,13 +517,15 @@ class CardService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function unarchive(int $id): Card {
public function unarchive($id) {
$this->cardServiceValidator->check(compact('id'));
@@ -518,6 +546,8 @@ class CardService {
}
/**
* @param $id
* @return \OCA\Deck\Db\Card
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
@@ -569,13 +599,15 @@ class CardService {
}
/**
* @param $cardId
* @param $labelId
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function assignLabel(int $cardId, int $labelId): Card {
public function assignLabel($cardId, $labelId) {
$this->cardServiceValidator->check(compact('cardId', 'labelId'));
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_EDIT);
@@ -597,16 +629,18 @@ class CardService {
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_ASSIGN, ['label' => $label]);
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card));
return $card;
}
/**
* @param $cardId
* @param $labelId
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function removeLabel(int $cardId, int $labelId): Card {
public function removeLabel($cardId, $labelId) {
$this->cardServiceValidator->check(compact('cardId', 'labelId'));
@@ -626,7 +660,6 @@ class CardService {
$this->activityManager->triggerEvent(ActivityManager::DECK_OBJECT_CARD, $card, ActivityManager::SUBJECT_LABEL_UNASSING, ['label' => $label]);
$this->eventDispatcher->dispatchTyped(new CardUpdatedEvent($card));
return $card;
}
public function getCardUrl(int $cardId): string {

View File

@@ -66,7 +66,7 @@ class CirclesService {
$circlesManager->startSession($federatedUser);
$circle = $circlesManager->getCircle($circleId);
$member = $circle->getInitiator();
$isUserInCircle = $member->getLevel() >= Member::LEVEL_MEMBER;
$isUserInCircle = $member !== null && $member->getLevel() >= Member::LEVEL_MEMBER;
if (!isset($this->userCircleCache[$circleId])) {
$this->userCircleCache[$circleId] = [];

View File

@@ -22,6 +22,8 @@ use OCP\IUserManager;
use OutOfBoundsException;
use Psr\Log\LoggerInterface;
use function is_numeric;
class CommentService {
public function __construct(
@@ -34,9 +36,12 @@ class CommentService {
) {
}
public function list(int $cardId, int $limit = 20, int $offset = 0): DataResponse {
public function list(string $cardId, int $limit = 20, int $offset = 0): DataResponse {
if (!is_numeric($cardId)) {
throw new BadRequestException('A valid card id must be provided');
}
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
$comments = $this->commentsManager->getForObject(Application::COMMENT_ENTITY_TYPE, (string)$cardId, $limit, $offset);
$comments = $this->commentsManager->getForObject(Application::COMMENT_ENTITY_TYPE, $cardId, $limit, $offset);
$result = [];
foreach ($comments as $comment) {
$formattedComment = $this->formatComment($comment);
@@ -91,13 +96,13 @@ class CommentService {
* @throws BadRequestException
* @throws NotFoundException|NoPermissionException
*/
public function create(int $cardId, string $message, int $replyTo = 0): DataResponse {
public function create(int $cardId, string $message, string $replyTo = '0'): DataResponse {
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
// Check if parent is a comment on the same card
if ($replyTo !== 0) {
if ($replyTo !== '0') {
try {
$comment = $this->commentsManager->get((string)$replyTo);
$comment = $this->commentsManager->get($replyTo);
if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$comment->getObjectId() !== $cardId) {
throw new CommentNotFoundException();
}
@@ -110,7 +115,7 @@ class CommentService {
$comment = $this->commentsManager->create('users', $this->userId, Application::COMMENT_ENTITY_TYPE, (string)$cardId);
$comment->setMessage($message);
$comment->setVerb('comment');
$comment->setParentId((string)$replyTo);
$comment->setParentId($replyTo);
$this->commentsManager->save($comment);
return new DataResponse($this->formatComment($comment, true));
} catch (\InvalidArgumentException $e) {
@@ -123,8 +128,14 @@ class CommentService {
}
}
public function update(int $cardId, int $commentId, string $message): DataResponse {
$comment = $this->get($cardId, $commentId);
public function update(string $cardId, string $commentId, string $message): DataResponse {
if (!is_numeric($cardId)) {
throw new BadRequestException('A valid card id must be provided');
}
if (!is_numeric($commentId)) {
throw new BadRequestException('A valid comment id must be provided');
}
$comment = $this->get((int)$cardId, (int)$commentId);
if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) {
throw new NoPermissionException('Only authors are allowed to edit their comment.');
}
@@ -134,12 +145,18 @@ class CommentService {
return new DataResponse($this->formatComment($comment));
}
public function delete(int $cardId, int $commentId): DataResponse {
public function delete(string $cardId, string $commentId): DataResponse {
if (!is_numeric($cardId)) {
throw new BadRequestException('A valid card id must be provided');
}
if (!is_numeric($commentId)) {
throw new BadRequestException('A valid comment id must be provided');
}
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
try {
$comment = $this->commentsManager->get((string)$commentId);
if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$comment->getObjectId() !== $cardId) {
$comment = $this->commentsManager->get($commentId);
if ($comment->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $comment->getObjectId() !== $cardId) {
throw new CommentNotFoundException();
}
} catch (CommentNotFoundException $e) {
@@ -148,11 +165,11 @@ class CommentService {
if ($comment->getActorType() !== 'users' || $comment->getActorId() !== $this->userId) {
throw new NoPermissionException('Only authors are allowed to edit their comment.');
}
$this->commentsManager->delete((string)$commentId);
$this->commentsManager->delete($commentId);
return new DataResponse([]);
}
private function formatComment(IComment $comment, bool $addReplyTo = false): array {
private function formatComment(IComment $comment, $addReplyTo = false): array {
$actorDisplayName = $this->userManager->getDisplayName($comment->getActorId()) ?? $comment->getActorId();
$formattedComment = [

View File

@@ -48,8 +48,7 @@ class ConfigService {
}
public function getAll(): array {
$userId = $this->getUserId();
if ($userId === null) {
if ($this->getUserId() === null) {
return [];
}
@@ -58,7 +57,7 @@ class ConfigService {
'cardDetailsInModal' => $this->isCardDetailsInModal(),
'cardIdBadge' => $this->isCardIdBadgeEnabled()
];
if ($this->groupManager->isAdmin($userId)) {
if ($this->groupManager->isAdmin($this->getUserId())) {
$data['groupLimit'] = $this->get('groupLimit');
}
return $data;
@@ -96,48 +95,44 @@ class ConfigService {
}
public function isCalendarEnabled(?int $boardId = null): bool {
$userId = $this->getUserId();
if ($userId === null) {
if ($this->getUserId() === null) {
return false;
}
$appConfigState = $this->config->getAppValue(Application::APP_ID, 'calendar', 'yes') === 'yes';
$defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'calendar', $appConfigState);
$defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'calendar', $appConfigState);
if ($boardId === null) {
return $defaultState;
}
return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'board:' . $boardId . ':calendar', $defaultState);
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'board:' . $boardId . ':calendar', $defaultState);
}
public function isCardDetailsInModal(?int $boardId = null): bool {
$userId = $this->getUserId();
if ($userId === null) {
if ($this->getUserId() === null) {
return false;
}
$defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardDetailsInModal', true);
$defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', true);
if ($boardId === null) {
return $defaultState;
}
return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'board:' . $boardId . ':cardDetailsInModal', $defaultState);
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'board:' . $boardId . ':cardDetailsInModal', $defaultState);
}
public function isCardIdBadgeEnabled(): bool {
$userId = $this->getUserId();
if ($userId === null) {
if ($this->getUserId() === null) {
return false;
}
$appConfigState = $this->config->getAppValue(Application::APP_ID, 'cardIdBadge', 'yes') === 'no';
$defaultState = (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardIdBadge', $appConfigState);
$defaultState = (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', $appConfigState);
return (bool)$this->config->getUserValue($userId, Application::APP_ID, 'cardIdBadge', $defaultState);
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', $defaultState);
}
public function set($key, $value) {
$userId = $this->getUserId();
if ($userId === null) {
if ($this->getUserId() === null) {
throw new NoPermissionException('Must be logged in to set user config');
}
@@ -145,21 +140,21 @@ class ConfigService {
[$scope] = explode(':', $key, 2);
switch ($scope) {
case 'groupLimit':
if (!$this->groupManager->isAdmin($userId)) {
if (!$this->groupManager->isAdmin($this->getUserId())) {
throw new NoPermissionException('You must be admin to set the group limit');
}
$result = $this->setGroupLimit($value);
break;
case 'calendar':
$this->config->setUserValue($userId, Application::APP_ID, 'calendar', (string)$value);
$this->config->setUserValue($this->getUserId(), Application::APP_ID, 'calendar', (string)$value);
$result = $value;
break;
case 'cardDetailsInModal':
$this->config->setUserValue($userId, Application::APP_ID, 'cardDetailsInModal', (string)$value);
$this->config->setUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', (string)$value);
$result = $value;
break;
case 'cardIdBadge':
$this->config->setUserValue($userId, Application::APP_ID, 'cardIdBadge', (string)$value);
$this->config->setUserValue($this->getUserId(), Application::APP_ID, 'cardIdBadge', (string)$value);
$result = $value;
break;
case 'board':
@@ -167,7 +162,7 @@ class ConfigService {
if ($boardConfigKey === 'notify-due' && !in_array($value, [self::SETTING_BOARD_NOTIFICATION_DUE_ALL, self::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED, self::SETTING_BOARD_NOTIFICATION_DUE_OFF], true)) {
throw new BadRequestException('Board notification option must be one of: off, assigned, all');
}
$this->config->setUserValue($userId, Application::APP_ID, $key, (string)$value);
$this->config->setUserValue($this->getUserId(), Application::APP_ID, $key, (string)$value);
$result = $value;
}
return $result;

View File

@@ -124,15 +124,18 @@ class FullTextSearchService {
/**
* @param int $cardId
*
* @return Board
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
*/
public function getBoardFromCardId(int $cardId): Board {
$boardId = $this->cardMapper->findBoardId($cardId);
if ($boardId === null) {
throw new DoesNotExistException("Board '$cardId' does not exist");
}
return $this->boardMapper->find($boardId);
$boardId = (int)$this->cardMapper->findBoardId($cardId);
/** @var Board $board */
$board = $this->boardMapper->find($boardId);
return $board;
}
@@ -142,7 +145,7 @@ class FullTextSearchService {
* @return Card[]
*/
private function getCardsFromStack(int $stackId): array {
return $this->cardMapper->findAll($stackId);
return $this->cardMapper->findAll($stackId, null, null);
}
@@ -152,7 +155,7 @@ class FullTextSearchService {
* @return Stack[]
*/
private function getStacksFromBoard(int $boardId): array {
return $this->stackMapper->findAll($boardId);
return $this->stackMapper->findAll($boardId, null, null);
}
@@ -162,6 +165,6 @@ class FullTextSearchService {
* @return Board[]
*/
private function getBoardsFromUser(string $userId): array {
return $this->boardMapper->findAllByUser($userId);
return $this->boardMapper->findAllByUser($userId, null, null, null);
}
}

View File

@@ -19,25 +19,29 @@ use OCP\Comments\IComment;
abstract class ABoardImportService {
/** @var string */
public static $name = '';
private BoardImportService $boardImportService;
protected bool $needValidateData = true;
/** @var BoardImportService */
private $boardImportService;
/** @var bool */
protected $needValidateData = true;
/** @var Stack[] */
protected array $stacks = [];
protected $stacks = [];
/** @var Label[] */
protected array $labels = [];
protected $labels = [];
/** @var Card[] */
protected array $cards = [];
protected $cards = [];
/** @var Acl[] */
protected array $acls = [];
protected $acls = [];
/** @var IComment[][] */
protected array $comments = [];
protected $comments = [];
/** @var Assignment[] */
protected array $assignments = [];
/** @var int[][] */
protected array $labelCardAssignments = [];
protected $assignments = [];
/** @var string[][] */
protected $labelCardAssignments = [];
/**
* Configure import service
*
* @return void
*/
abstract public function bootstrap(): void;
@@ -64,13 +68,10 @@ abstract class ABoardImportService {
abstract public function getCardAssignments(): array;
/**
* @return array<int, array<int, int>>
*/
abstract public function getCardLabelAssignment(): array;
/**
* @return array<int, array<string, IComment>>
* @return IComment[][]|array
*/
abstract public function getComments(): array;
@@ -97,16 +98,16 @@ abstract class ABoardImportService {
$this->acls[$code] = $acl;
}
public function updateComment(int $cardId, string $commentId, IComment $comment): void {
public function updateComment(string $cardId, string $commentId, IComment $comment): void {
$this->comments[$cardId][$commentId] = $comment;
}
public function updateCardAssignment(int $cardId, int $assignmentId, Entity $assignment): void {
public function updateCardAssignment(string $cardId, string $assignmentId, Entity $assignment): void {
$this->assignments[$cardId][$assignmentId] = $assignment;
}
public function updateCardLabelsAssignment(int $cardId, int $assignmentId, int $labelId): void {
$this->labelCardAssignments[$cardId][$assignmentId] = $labelId;
public function updateCardLabelsAssignment(string $cardId, string $assignmentId, string $assignment): void {
$this->labelCardAssignments[$cardId][$assignmentId] = $assignment;
}
public function setImportService(BoardImportService $service): void {

View File

@@ -209,15 +209,14 @@ class BoardImportService {
public function importBoard(): void {
$board = $this->getImportSystem()->getBoard();
if ($board === null) {
throw new \LogicException('Import board not found');
}
if (!$this->userManager->userExists($board->getOwner())) {
throw new \Exception('Target owner ' . $board->getOwner() . ' not found. Please provide a mapping through the import config.');
}
$this->boardMapper->insert($board);
$this->board = $board;
if ($board) {
$this->boardMapper->insert($board);
$this->board = $board;
}
}
public function getBoard(bool $reset = false): Board {
@@ -293,7 +292,12 @@ class BoardImportService {
}
}
public function assignCardToLabel(int $cardId, int $labelId): self {
/**
* @param mixed $cardId
* @param mixed $labelId
* @return self
*/
public function assignCardToLabel($cardId, $labelId): self {
$this->cardMapper->assignLabel(
$cardId,
$labelId
@@ -303,14 +307,14 @@ class BoardImportService {
public function assignCardsToLabels(): void {
$data = $this->getImportSystem()->getCardLabelAssignment();
foreach ($data as $cardId => $assignment) {
foreach ($assignment as $assignmentId => $labelId) {
foreach ($data as $cardId => $assignemnt) {
foreach ($assignemnt as $assignmentId => $labelId) {
try {
$this->assignCardToLabel(
(int)$cardId,
$cardId,
$labelId
);
$this->getImportSystem()->updateCardLabelsAssignment((int)$cardId, (int)$assignmentId, $labelId);
$this->getImportSystem()->updateCardLabelsAssignment($cardId, $assignmentId, $labelId);
} catch (\Exception $e) {
$this->addError('Failed to assign label ' . $labelId . ' to ' . $cardId, $e);
}
@@ -322,20 +326,20 @@ class BoardImportService {
$allComments = $this->getImportSystem()->getComments();
foreach ($allComments as $cardId => $comments) {
foreach ($comments as $commentId => $comment) {
$this->insertComment((int)$cardId, $comment);
$this->getImportSystem()->updateComment((int)$cardId, $commentId, $comment);
$this->insertComment($cardId, $comment);
$this->getImportSystem()->updateComment($cardId, $commentId, $comment);
}
}
}
private function insertComment(int $cardId, IComment $comment): void {
$comment->setObject('deckCard', (string)$cardId);
private function insertComment(string $cardId, IComment $comment): void {
$comment->setObject('deckCard', $cardId);
$comment->setVerb('comment');
// Check if parent is a comment on the same card
if ($comment->getParentId() !== '0') {
try {
$parent = $this->commentsManager->get($comment->getParentId());
if ($parent->getObjectType() !== Application::COMMENT_ENTITY_TYPE || (int)$parent->getObjectId() !== $cardId) {
if ($parent->getObjectType() !== Application::COMMENT_ENTITY_TYPE || $parent->getObjectId() !== $cardId) {
throw new CommentNotFoundException();
}
} catch (CommentNotFoundException $e) {
@@ -358,7 +362,7 @@ class BoardImportService {
foreach ($assignments as $assignment) {
try {
$assignment = $this->assignmentMapper->insert($assignment);
$this->getImportSystem()->updateCardAssignment((int)$cardId, $assignment->getId(), $assignment);
$this->getImportSystem()->updateCardAssignment($cardId, (string)$assignment->getId(), $assignment);
$this->addOutput('Assignment ' . $assignment->getParticipant() . ' added');
} catch (NotFoundException $e) {
$this->addError('No origin or mapping found for card "' . $cardId . '" and ' . $assignment->getTypeString() . ' assignment "' . $assignment->getParticipant(), $e);

View File

@@ -16,7 +16,6 @@ use OCA\Deck\Db\Card;
use OCA\Deck\Db\Label;
use OCA\Deck\Db\Stack;
use OCA\Deck\Service\Importer\ABoardImportService;
use OCP\Comments\IComment;
use OCP\IUser;
use OCP\IUserManager;
@@ -119,7 +118,6 @@ class DeckJsonService extends ABoardImportService {
$comments[$this->cards[$sourceCard->id]->getId()][$commentOriginal->id] = $comment;
}
}
/** @var array<int, array<string, IComment>> */
return $comments;
}

View File

@@ -17,7 +17,7 @@ use Psr\Log\LoggerInterface;
class TrelloApiService extends TrelloJsonService {
/** @var string */
public static $name = 'Trello API';
protected bool $needValidateData = false;
protected $needValidateData = false;
/** @var IClient */
private $httpClient;
/** @var LoggerInterface */
@@ -176,7 +176,7 @@ class TrelloApiService extends TrelloJsonService {
if (empty($queryString['limit'])) {
return [];
}
if ((count($data) < $queryString['limit']) || (count($data) === 0)) {
if (count($data) < $queryString['limit']) {
return [];
}
$queryString['before'] = end($data)->id;

View File

@@ -159,7 +159,6 @@ class TrelloJsonService extends ABoardImportService {
$comments[$cardId][$commentId] = $comment;
}
}
/** @var array<int, array<string, IComment>> */
return $comments;
}

View File

@@ -43,24 +43,33 @@ class LabelService {
}
/**
* @param $labelId
* @return \OCP\AppFramework\Db\Entity
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function find(int $labelId): Label {
public function find($labelId) {
if (is_numeric($labelId) === false) {
throw new BadRequestException('label id must be a number');
}
$this->permissionService->checkPermission($this->labelMapper, $labelId, Acl::PERMISSION_READ);
return $this->labelMapper->find($labelId);
}
/**
* @param $title
* @param $color
* @param $boardId
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function create(string $title, string $color, int $boardId): Label {
public function create($title, $color, $boardId) {
$this->labelServiceValidator->check(compact('title', 'color', 'boardId'));
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
@@ -97,13 +106,15 @@ class LabelService {
}
/**
* @param $id
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete(int $id): Label {
public function delete($id) {
$this->labelServiceValidator->check(compact('id'));
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);
@@ -116,13 +127,17 @@ class LabelService {
}
/**
* @param $id
* @param $title
* @param $color
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update(int $id, string $title, string $color): Label {
public function update($id, $title, $color) {
$this->labelServiceValidator->check(compact('title', 'color', 'id'));
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);

View File

@@ -9,28 +9,53 @@ declare(strict_types=1);
namespace OCA\Deck\Service;
use OCA\Deck\Db\AssignmentMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Model\CardDetails;
use OCP\Comments\ICommentsManager;
use OCP\IUserManager;
class OverviewService {
private CardService $cardService;
private BoardMapper $boardMapper;
private LabelMapper $labelMapper;
private CardMapper $cardMapper;
private AssignmentMapper $assignedUsersMapper;
private IUserManager $userManager;
private ICommentsManager $commentsManager;
private AttachmentService $attachmentService;
public function __construct(
private readonly CardService $cardService,
private readonly BoardMapper $boardMapper,
private readonly CardMapper $cardMapper,
CardService $cardService,
BoardMapper $boardMapper,
LabelMapper $labelMapper,
CardMapper $cardMapper,
AssignmentMapper $assignedUsersMapper,
IUserManager $userManager,
ICommentsManager $commentsManager,
AttachmentService $attachmentService,
) {
$this->cardService = $cardService;
$this->boardMapper = $boardMapper;
$this->labelMapper = $labelMapper;
$this->cardMapper = $cardMapper;
$this->assignedUsersMapper = $assignedUsersMapper;
$this->userManager = $userManager;
$this->commentsManager = $commentsManager;
$this->attachmentService = $attachmentService;
}
public function findUpcomingCards(string $userId): array {
$userBoards = $this->boardMapper->findAllForUser($userId);
$boardOwnerIds = array_filter(array_map(function (Board $board) {
return count($board->getAcl() ?? []) === 0 ? $board->getId() : null;
return count($board->getAcl()) === 0 ? $board->getId() : null;
}, $userBoards));
$boardSharedIds = array_filter(array_map(function (Board $board) {
return count($board->getAcl() ?? []) > 0 ? $board->getId() : null;
return count($board->getAcl()) > 0 ? $board->getId() : null;
}, $userBoards));
$foundCards = array_merge(

View File

@@ -29,7 +29,6 @@ class PermissionService {
private array $users = [];
private CappedMemoryCache $boardCache;
/** @var CappedMemoryCache<array<Acl::PERMISSION_*, bool>> */
private CappedMemoryCache $permissionCache;
public function __construct(
@@ -50,16 +49,15 @@ class PermissionService {
/**
* Get current user permissions for a board by id
*
* @return array<Acl::PERMISSION_*, bool>
* @return bool|array
*/
public function getPermissions(int $boardId, ?string $userId = null): array {
public function getPermissions(int $boardId, ?string $userId = null) {
if ($userId === null) {
$userId = $this->userId;
}
$cacheKey = $boardId . '-' . $userId;
if ($cached = $this->permissionCache->get($cacheKey)) {
/** @var array<Acl::PERMISSION_*, bool> $cached */
return $cached;
}

View File

@@ -77,7 +77,7 @@ class StackService {
/** @param Stack[] $stacks */
private function enrichStacksWithCards(array $stacks, $since = -1): void {
$cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, 0, $since);
$cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, null, $since);
foreach ($cardsByStackId as $stackId => $cards) {
if (!$cards) {
@@ -95,11 +95,18 @@ class StackService {
}
/**
* @param $stackId
*
* @return \OCP\AppFramework\Db\Entity
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function find(int $stackId): Stack {
public function find($stackId) {
if (is_numeric($stackId) === false) {
throw new BadRequestException('stack id must be a number');
}
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_READ);
$stack = $this->stackMapper->find($stackId);
@@ -120,11 +127,17 @@ class StackService {
}
/**
* @param mixed $boardId
*
* @return Stack[]
* @throws \OCA\Deck\NoPermissionException
* @throws BadRequestException
*/
public function findAll(int $boardId, int $since = -1): array {
public function findAll($boardId, $since = -1) {
if (is_numeric($boardId) === false) {
throw new BadRequestException('boardId must be a number');
}
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
$stacks = $this->stackMapper->findAll($boardId);
$this->enrichStacksWithCards($stacks, $since);
@@ -132,11 +145,7 @@ class StackService {
return $stacks;
}
/**
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function findCalendarEntries(int $boardId): array {
public function findCalendarEntries($boardId) {
try {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
} catch (NoPermissionException $e) {
@@ -146,11 +155,7 @@ class StackService {
return $this->stackMapper->findAll($boardId);
}
/**
* @return Stack[]
* @throws \OCP\DB\Exception
*/
public function fetchDeleted(int $boardId): array {
public function fetchDeleted($boardId) {
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
$stacks = $this->stackMapper->findDeleted($boardId);
$this->enrichStacksWithCards($stacks);
@@ -159,11 +164,17 @@ class StackService {
}
/**
* @return Stack[]
* @param $boardId
*
* @return array
* @throws \OCA\Deck\NoPermissionException
* @throws BadRequestException
*/
public function findAllArchived(int $boardId): array {
public function findAllArchived($boardId) {
if (is_numeric($boardId) === false) {
throw new BadRequestException('board id must be a number');
}
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
$stacks = $this->stackMapper->findAll($boardId);
$labels = $this->labelMapper->getAssignedLabelsForBoard($boardId);
@@ -178,18 +189,22 @@ class StackService {
$stacks[$stackIndex]->setCards($cards);
}
/** @var Stack[] $stacks */
return $stacks;
}
/**
* @param $title
* @param $boardId
* @param integer $order
*
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function create(string $title, int $boardId, int $order): Stack {
public function create($title, $boardId, $order) {
$this->stackServiceValidator->check(compact('title', 'boardId', 'order'));
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
@@ -211,13 +226,19 @@ class StackService {
}
/**
* @return Stack The deleted stack.
* @param $id
*
* @return \OCP\AppFramework\Db\Entity
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function delete(int $id): Stack {
public function delete($id) {
if (is_numeric($id) === false) {
throw new BadRequestException('stack id must be a number');
}
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
$stack = $this->stackMapper->find($id);
@@ -235,13 +256,20 @@ class StackService {
}
/**
* @param $id
* @param $title
* @param $boardId
* @param $order
* @param $deletedAt
*
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update(int $id, string $title, int $boardId, int $order, ?int $deletedAt): Stack {
public function update($id, $title, $boardId, $order, $deletedAt) {
$this->stackServiceValidator->check(compact('id', 'title', 'boardId', 'order'));
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
@@ -269,13 +297,16 @@ class StackService {
}
/**
* @return array<int, Stack> The stacks in the correct order.
* @param $id
* @param $order
*
* @return array
* @throws \OCA\Deck\NoPermissionException
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function reorder(int $id, int $order): array {
public function reorder($id, $order) {
$this->stackServiceValidator->check(compact('id', 'order'));
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
@@ -296,7 +327,7 @@ class StackService {
if ($stack->id !== $id) {
$stack->setOrder($i++);
}
$stack = $this->stackMapper->update($stack);
$this->stackMapper->update($stack);
$result[$stack->getOrder()] = $stack;
}
$this->changeHelper->boardChanged($stackToSort->getBoardId());

View File

@@ -13,6 +13,7 @@ namespace OCA\Deck\Sharing;
use OC\Files\Cache\Cache;
use OCA\Deck\Cache\AttachmentCacheHelper;
use OCA\Deck\Db\Acl;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\User;
@@ -98,13 +99,9 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
* @inheritDoc
*/
public function create(IShare $share) {
$cardId = (int)$share->getSharedWith();
$cardId = $share->getSharedWith();
$boardId = $this->cardMapper->findBoardId($cardId);
$valid = $boardId !== null;
if (!$valid) {
throw new GenericShareException('Card not found', $this->l->t('Card not found'), 404);
}
try {
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_EDIT);
} catch (NoPermissionException $e) {
@@ -150,7 +147,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
);
$data = $this->getRawShare($shareId);
$this->attachmentCacheHelper->clearAttachmentCount($cardId);
$this->attachmentCacheHelper->clearAttachmentCount((int)$cardId);
return $this->createShareObject($data);
}
@@ -216,7 +213,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->from('share')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
@@ -300,7 +297,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
->executeStatement();
->execute();
/*
* Update all user defined group shares
@@ -313,7 +310,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->set('item_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('file_source', $qb->createNamedParameter($share->getNode()->getId()))
->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE))
->executeStatement();
->execute();
/*
* Now update the permissions for all children that have not set it to 0
@@ -323,7 +320,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
->andWhere($qb->expr()->neq('permissions', $qb->createNamedParameter(0)))
->set('permissions', $qb->createNamedParameter($share->getPermissions()))
->executeStatement();
->execute();
return $share;
}
@@ -338,7 +335,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->orWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())));
$qb->executeStatement();
$qb->execute();
$this->attachmentCacheHelper->clearAttachmentCount((int)$share->getSharedWith());
}
@@ -358,7 +355,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
))
->executeQuery();
->execute();
$data = $stmt->fetch();
$stmt->closeCursor();
@@ -379,14 +376,14 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
'file_target' => $qb->createNamedParameter($share->getTarget()),
'permissions' => $qb->createNamedParameter(0),
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
])->executeStatement();
])->execute();
} elseif ($data['permissions'] !== 0) {
// Already a userroom share. Update it.
$qb = $this->dbConnection->getQueryBuilder();
$qb->update('share')
->set('permissions', $qb->createNamedParameter(0))
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
->executeStatement();
->execute();
}
}
@@ -400,7 +397,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where(
$qb->expr()->eq('id', $qb->createNamedParameter($share->getId()))
);
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
@@ -417,7 +414,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))
);
$qb->executeStatement();
$qb->execute();
return $this->getShareById((int)$share->getId(), $recipient);
}
@@ -438,7 +435,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
))
->setMaxResults(1)
->executeQuery();
->execute();
$data = $stmt->fetch();
$stmt->closeCursor();
@@ -512,7 +509,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->orderBy('s.id');
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
$shares = [];
while ($data = $cursor->fetch()) {
$shares[$data['fileid']][] = $this->createShareObject($data);
@@ -557,7 +554,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->setFirstResult($offset);
$qb->orderBy('id');
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
$shares = [];
while ($data = $cursor->fetch()) {
$shares[] = $this->createShareObject($data);
@@ -585,7 +582,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where($qb->expr()->eq('s.id', $qb->createNamedParameter($id)))
->andWhere($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK)));
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
$data = $cursor->fetch();
$cursor->closeCursor();
@@ -650,7 +647,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
));
$stmt = $query->executeQuery();
$stmt = $query->execute();
while ($data = $stmt->fetch()) {
$this->applyBoardPermission($shareMap[$data['parent']], (int)$data['permissions'], $userId);
@@ -680,7 +677,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->from('share')
->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($path->getId())))
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
->executeQuery();
->execute();
$shares = [];
while ($data = $cursor->fetch()) {
@@ -752,7 +749,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
$qb->andWhere($qb->expr()->eq('dc.deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
if (!$this->isAccessibleResult($data)) {
continue;
@@ -845,7 +842,6 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
/**
* @inheritDoc
* @psalm-suppress InvalidReturnType Not returning anything
*/
public function getShareByToken($token) {
throw new ShareNotFound();
@@ -855,7 +851,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->from('share')
->where($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_ROOM)))
->andWhere($qb->expr()->eq('token', $qb->createNamedParameter($token)))
->executeQuery();
->execute();
$data = $cursor->fetch();
@@ -1019,7 +1015,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
->orderBy('id');
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$children[] = $this->createShareObject($data);
}
@@ -1042,7 +1038,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
)
);
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
while ($data = $cursor->fetch()) {
$share = $this->createShareObject($data);
@@ -1059,7 +1055,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
->where($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
->andWhere($qb->expr()->notIn('s.share_with', $qb->createNamedParameter($allCardIds, IQueryBuilder::PARAM_STR_ARRAY)));
$cursor = $qb->executeQuery();
$cursor = $qb->execute();
$shares = [];
while ($data = $cursor->fetch()) {
$shares[] = $this->createShareObject($data);

780
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -39,11 +39,11 @@
"@nextcloud/event-bus": "^3.3.2",
"@nextcloud/files": "^3.10.1",
"@nextcloud/initial-state": "^2.2.0",
"@nextcloud/l10n": "^3.4.0",
"@nextcloud/moment": "^1.3.5",
"@nextcloud/l10n": "^3.1.0",
"@nextcloud/moment": "^1.3.4",
"@nextcloud/notify_push": "^1.3.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/vue": "^8.31.0",
"@nextcloud/vue": "^8.27.0",
"blueimp-md5": "^2.19.0",
"chroma-js": "^3.1.2",
"dompurify": "^3.2.5",
@@ -59,7 +59,6 @@
"vue-click-outside": "^1.1.0",
"vue-easymde": "^2.0.0",
"vue-infinite-loading": "^2.4.5",
"vue-loader": "^15.11.1",
"vue-material-design-icons": "^5.3.1",
"vue-router": "^3.6.5",
"vue-smooth-dnd": "^0.8.1",
@@ -70,8 +69,8 @@
"extends @nextcloud/browserslist-config"
],
"engines": {
"node": "^22.0.0",
"npm": "^10.5.0"
"node": "^20.0.0",
"npm": "^10.0.0"
},
"devDependencies": {
"@nextcloud/babel-config": "^1.2.0",

View File

@@ -6,9 +6,7 @@
xmlns="https://getpsalm.org/schema/config"
xsi:schemaLocation="https://getpsalm.org/schema/config"
errorBaseline="tests/psalm-baseline.xml"
findUnusedBaselineEntry="true"
findUnusedCode="false"
phpVersion="8.2"
phpVersion="8.1"
>
<stubs>
<file name="tests/stub.phpstub" preloadClasses="true"/>
@@ -17,7 +15,6 @@
<directory name="lib" />
<ignoreFiles>
<directory name="vendor" />
<directory name="vendor-bin" />
</ignoreFiles>
</projectFiles>
<extraFiles>
@@ -29,6 +26,13 @@
<referencedMethod name="/Db\\.*::.*/" />
</errorLevel>
</UndefinedMagicMethod>
<UndefinedInterfaceMethod>
<errorLevel type="suppress">
<!-- FIXME Deprecated event handling -->
<referencedMethod name="OCP\IUserManager::listen" />
<referencedMethod name="OCP\IGroupManager::listen" />
</errorLevel>
</UndefinedInterfaceMethod>
<UndefinedClass>
<errorLevel type="suppress">
<referencedClass name="OC\*" />

View File

@@ -107,10 +107,6 @@ export default {
this.$store.dispatch('loadSharees')
},
mounted() {
// Redirect to cleaner URL (without /index.php) if RewriteBase is enabled
if (this.$route.path.startsWith('/index.php')) {
window.location.href = this.$route.fullPath.replace('/index.php', '')
}
// Set navigation to initial state and update in case it gets toggled
emit('toggle-navigation', { open: !this.isMobile && this.navShown, _initial: true })
this.$nextTick(() => {

View File

@@ -338,11 +338,10 @@ export default {
flex-direction: column;
gap: $card-gap;
overflow: hidden;
cursor: pointer;
&:deep(*:not(.dragDisabled)) {
cursor: pointer;
}
&:deep(*) {
cursor: pointer;
}
&.current-card {
border: 2px solid var(--color-primary-element);

View File

@@ -90,8 +90,8 @@
}
],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -123,8 +123,8 @@
"createdAt": 1689667572,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -168,8 +168,8 @@
"type": 0
}
],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -240,8 +240,8 @@
}
],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -283,8 +283,8 @@
}
],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -328,8 +328,8 @@
"type": 0
}
],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -493,8 +493,8 @@
"createdAt": 1689667483,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -526,8 +526,8 @@
"createdAt": 1689667518,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -559,8 +559,8 @@
"createdAt": 1689667527,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -592,8 +592,8 @@
"createdAt": 1689667537,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -636,8 +636,8 @@
"createdAt": 1689667488,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -680,8 +680,8 @@
"createdAt": 1689667493,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",
@@ -713,8 +713,8 @@
"createdAt": 1689667502,
"labels": [],
"assignedUsers": [],
"attachments": [],
"attachmentCount": 0,
"attachments": null,
"attachmentCount": null,
"owner": {
"primaryKey": "admin",
"uid": "admin",

View File

@@ -1 +1 @@
82774
81373

View File

@@ -1,78 +1,152 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="6.5.0@38fc8444edf0cebc9205296ee6e30e906ade783b">
<files psalm-version="5.6.0@e784128902dfe01d489c4123d69918a9f3c1eac5">
<file src="lib/Activity/Filter.php">
<MethodSignatureMismatch>
<code><![CDATA[$types]]></code>
<code>$types</code>
</MethodSignatureMismatch>
</file>
<file src="lib/Command/UserExport.php">
<ImplementedReturnTypeMismatch>
<code>void</code>
</ImplementedReturnTypeMismatch>
<UndefinedThisPropertyAssignment>
<code>$this-&gt;boardMapper</code>
<code>$this-&gt;stackMapper</code>
</UndefinedThisPropertyAssignment>
<UndefinedThisPropertyFetch>
<code>$this-&gt;boardMapper</code>
<code>$this-&gt;stackMapper</code>
</UndefinedThisPropertyFetch>
</file>
<file src="lib/Controller/BoardApiController.php">
<UndefinedFunction>
<code><![CDATA[parseDate($modified)]]></code>
</UndefinedFunction>
<TypeDoesNotContainNull>
<code>$modified === null</code>
<code>$modified === null</code>
</TypeDoesNotContainNull>
<UndefinedClass>
<code>Util</code>
</UndefinedClass>
<UndefinedThisPropertyAssignment>
<code>$this-&gt;userId</code>
</UndefinedThisPropertyAssignment>
<UndefinedThisPropertyFetch>
<code>$this-&gt;userId</code>
</UndefinedThisPropertyFetch>
</file>
<file src="lib/Controller/CommentsApiController.php">
<InvalidScalarArgument>
<code>$cardId</code>
<code>$cardId</code>
<code>$cardId</code>
<code>$commentId</code>
<code>$commentId</code>
<code>$parentId</code>
</InvalidScalarArgument>
</file>
<file src="lib/Controller/PageController.php">
<UndefinedClass>
<code><![CDATA[LoadSidebar]]></code>
<code>LoadSidebar</code>
</UndefinedClass>
</file>
<file src="lib/Controller/StackApiController.php">
<UndefinedFunction>
<code><![CDATA[parseDate($modified)]]></code>
</UndefinedFunction>
<RedundantCondition>
<code>$modified !== null</code>
</RedundantCondition>
<UndefinedClass>
<code>Util</code>
</UndefinedClass>
</file>
<file src="lib/DAV/Calendar.php">
<UndefinedClass>
<code><![CDATA[ExternalCalendar]]></code>
<code>ExternalCalendar</code>
</UndefinedClass>
</file>
<file src="lib/DAV/CalendarObject.php">
<UndefinedClass>
<code><![CDATA[ICalendarObject]]></code>
<code>ICalendarObject</code>
</UndefinedClass>
</file>
<file src="lib/DAV/CalendarPlugin.php">
<UndefinedClass>
<code><![CDATA[ICalendarProvider]]></code>
<code>ICalendarProvider</code>
</UndefinedClass>
</file>
<file src="lib/DAV/DeckCalendarBackend.php">
<UndefinedClass>
<code><![CDATA[NotFound]]></code>
<code>NotFound</code>
</UndefinedClass>
</file>
<file src="lib/Db/Card.php">
<UndefinedClass>
<code><![CDATA[VCalendar]]></code>
<code><![CDATA[VCalendar]]></code>
<code>VCalendar</code>
<code>VCalendar</code>
</UndefinedClass>
</file>
<file src="lib/Db/CardMapper.php">
<InvalidScalarArgument>
<code>$entity-&gt;getId()</code>
</InvalidScalarArgument>
<UndefinedInterfaceMethod>
<code>getUserIdGroups</code>
</UndefinedInterfaceMethod>
</file>
<file src="lib/Db/LabelMapper.php">
<ParamNameMismatch>
<code>$labelId</code>
</ParamNameMismatch>
</file>
<file src="lib/Db/Stack.php">
<UndefinedClass>
<code><![CDATA[VCalendar]]></code>
<code><![CDATA[VCalendar]]></code>
<code>VCalendar</code>
<code>VCalendar</code>
</UndefinedClass>
</file>
<file src="lib/Model/BoardSummary.php">
<ConstructorSignatureMismatch>
<code>public function __construct(Board $board) {</code>
<code>public function __construct(Board $board) {</code>
</ConstructorSignatureMismatch>
</file>
<file src="lib/Model/CardDetails.php">
<ConstructorSignatureMismatch>
<code>public function __construct(Card $card, ?Board $board = null) {</code>
<code>public function __construct(Card $card, ?Board $board = null) {</code>
</ConstructorSignatureMismatch>
</file>
<file src="lib/Service/AttachmentService.php">
<InvalidCatch>
<code>try {
$attachment = $this-&gt;attachmentMapper-&gt;find($attachmentId);
} catch (IMapperException $e) {
throw new NoPermissionException('Permission denied');
}</code>
</InvalidCatch>
</file>
<file src="lib/Service/BoardService.php">
<TooManyArguments>
<code>findAll</code>
<code>findAll</code>
</TooManyArguments>
</file>
<file src="lib/Service/CirclesService.php">
<RedundantCondition>
<code>$member !== null</code>
</RedundantCondition>
</file>
<file src="lib/Service/FileService.php">
<RedundantCondition>
<code><![CDATA[is_resource($content)]]></code>
<code><![CDATA[is_resource($content)]]></code>
<code>is_resource($content)</code>
<code>is_resource($content)</code>
</RedundantCondition>
</file>
<file src="lib/Service/Importer/BoardImportCommandService.php">
<UndefinedInterfaceMethod>
<code><![CDATA[ask]]></code>
<code><![CDATA[ask]]></code>
<code><![CDATA[ask]]></code>
</UndefinedInterfaceMethod>
<file src="lib/Sharing/DeckShareProvider.php">
<InvalidReturnType>
<code>getShareByToken</code>
</InvalidReturnType>
</file>
<file src="lib/Service/Importer/Systems/TrelloJsonService.php">
<InvalidPropertyFetch>
<code><![CDATA[$createCardDate->date]]></code>
</InvalidPropertyFetch>
</file>
<file src="lib/Teams/DeckTeamResourceProvider.php">
<FalsableReturnStatement>
<code><![CDATA[file_get_contents(__DIR__ . '/../../img/deck-current.svg')]]></code>
</FalsableReturnStatement>
<file src="lib/Sharing/Listener.php">
<InvalidArgument>
<code>[self::class, 'listenPreShare']</code>
</InvalidArgument>
</file>
</files>

View File

@@ -160,7 +160,6 @@ class ActivityManagerTest extends TestCase {
'id' => 123,
'title' => 'My card',
'description' => str_repeat('A', 1000),
'stackId' => 42,
]);
$this->cardMapper->expects(self::any())
->method('find')
@@ -171,7 +170,6 @@ class ActivityManagerTest extends TestCase {
]);
$this->stackMapper->expects(self::any())
->method('find')
->with(42)
->willReturn($stack);
$expectedCard = $card->jsonSerialize();
@@ -209,7 +207,6 @@ class ActivityManagerTest extends TestCase {
$card->setDescription(str_repeat('A', 5000));
$card->setTitle('My card');
$card->setId(123);
$card->setStackId(42);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);
@@ -257,7 +254,6 @@ class ActivityManagerTest extends TestCase {
$card->setDescription(str_repeat('A', 5000));
$card->setTitle('My card');
$card->setId(123);
$card->setStackId(42);
$this->cardMapper->expects(self::any())
->method('find')
->willReturn($card);

View File

@@ -119,7 +119,7 @@ class AttachmentMapperTest extends TestCase {
->method('isOwner')
->with('admin', 1)
->willReturn(true);
$this->assertTrue($this->attachmentMapper->isOwner('admin', $this->attachments[0]->getId()));
$this->assertTrue($this->attachmentMapper->isOwner('admin', (string)$this->attachments[0]->getId()));
}
public function testIsOwnerInvalid() {
@@ -127,7 +127,7 @@ class AttachmentMapperTest extends TestCase {
->method('isOwner')
->with('admin', 1)
->will($this->throwException(new DoesNotExistException('does not exist')));
$this->assertFalse($this->attachmentMapper->isOwner('admin', $this->attachments[0]->getId()));
$this->assertFalse($this->attachmentMapper->isOwner('admin', (string)$this->attachments[0]->getId()));
}
public function testFindBoardId() {

View File

@@ -81,8 +81,8 @@ class CardTest extends TestCase {
'duedate' => null,
'overdue' => 0,
'archived' => false,
'attachments' => [],
'attachmentCount' => 0,
'attachments' => null,
'attachmentCount' => null,
'assignedUsers' => null,
'deletedAt' => 0,
'commentsUnread' => 0,
@@ -110,8 +110,8 @@ class CardTest extends TestCase {
'duedate' => null,
'overdue' => 0,
'archived' => false,
'attachments' => [],
'attachmentCount' => 0,
'attachments' => null,
'attachmentCount' => null,
'assignedUsers' => null,
'deletedAt' => 0,
'commentsUnread' => 0,
@@ -141,8 +141,8 @@ class CardTest extends TestCase {
'duedate' => null,
'overdue' => 0,
'archived' => false,
'attachments' => [],
'attachmentCount' => 0,
'attachments' => null,
'attachmentCount' => null,
'assignedUsers' => ['user1'],
'deletedAt' => 0,
'commentsUnread' => 0,

View File

@@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
@@ -118,7 +116,7 @@ class AttachmentServiceTest extends TestCase {
$this->activityManager = $this->createMock(ActivityManager::class);
$this->appContainer->expects($this->exactly(2))
->method('get')
->method('query')
->withConsecutive(
[FileService::class],
[FilesAppService::class]
@@ -158,7 +156,7 @@ class AttachmentServiceTest extends TestCase {
$fileAppServiceMock = $this->createMock(FilesAppService::class);
$appContainer->expects($this->exactly(3))
->method('get')
->method('query')
->withConsecutive(
[FileService::class],
[FilesAppService::class],
@@ -187,7 +185,7 @@ class AttachmentServiceTest extends TestCase {
$fileAppServiceMock = $this->createMock(FilesAppService::class);
$appContainer->expects($this->exactly(3))
->method('get')
->method('query')
->withConsecutive(
[FileService::class],
[FilesAppService::class],

View File

@@ -1,7 +1,5 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
@@ -242,7 +240,6 @@ class BoardServiceTest extends TestCase {
public function testDelete() {
$board = new Board();
$board->setId(42);
$board->setOwner('admin');
$board->setDeletedAt(0);
$this->boardMapper->expects($this->once())
@@ -255,7 +252,7 @@ class BoardServiceTest extends TestCase {
]);
$this->sessionMapper->expects($this->once())
->method('findAllActive')
->with(42)
->with(null)
->willReturn([]);
$boardDeleted = clone $board;
$boardDeleted->setDeletedAt(1);
@@ -270,7 +267,7 @@ class BoardServiceTest extends TestCase {
$user->method('getUID')->willReturn('admin');
$acl = new Acl();
$acl->setBoardId(123);
$acl->setType(Acl::PERMISSION_TYPE_USER);
$acl->setType('user');
$acl->setParticipant('admin');
$acl->setPermissionEdit(true);
$acl->setPermissionShare(true);
@@ -290,7 +287,7 @@ class BoardServiceTest extends TestCase {
'admin' => 'admin',
]);
$this->assertEquals($acl, $this->service->addAcl(
123, Acl::PERMISSION_TYPE_USER, 'admin', true, true, true
123, 'user', 'admin', true, true, true
));
}
@@ -326,7 +323,7 @@ class BoardServiceTest extends TestCase {
public function testAddAclExtendPermission($currentUserAcl, $providedAcl, $resultingAcl) {
$existingAcl = new Acl();
$existingAcl->setBoardId(123);
$existingAcl->setType(Acl::PERMISSION_TYPE_USER);
$existingAcl->setType('user');
$existingAcl->setParticipant('admin');
$existingAcl->setPermissionEdit($currentUserAcl[0]);
$existingAcl->setPermissionShare($currentUserAcl[1]);
@@ -394,14 +391,14 @@ class BoardServiceTest extends TestCase {
->method('dispatchTyped')
->with(new AclCreatedEvent($acl));
$this->assertEquals($expected, $this->service->addAcl(
123, Acl::PERMISSION_TYPE_USER, 'admin', $providedAcl[0], $providedAcl[1], $providedAcl[2]
123, 'user', 'admin', $providedAcl[0], $providedAcl[1], $providedAcl[2]
));
}
public function testUpdateAcl() {
$acl = new Acl();
$acl->setBoardId(123);
$acl->setType(Acl::PERMISSION_TYPE_USER);
$acl->setType('user');
$acl->setParticipant('admin');
$acl->setPermissionEdit(true);
$acl->setPermissionShare(true);

Some files were not shown because too many files have changed in this diff Show More