Compare commits

..

28 Commits

Author SHA1 Message Date
grnd-alt
c37f6f92c4 fix: do not show dragged over state when no data in drag event
Signed-off-by: grnd-alt <github@belakkaf.net>
2025-09-24 15:30:13 +02:00
Nextcloud bot
4abf895d22 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-24 00:31:53 +00:00
Nextcloud bot
d216e56dac fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-23 00:31:50 +00:00
Luka Trovic
15514162f3 Merge pull request #7256 from nextcloud/bump-nextcloud-vue-8.31.0
Chore(deps): Bump @nextcloud/vue from 8.27.0 to 8.31.0
2025-09-22 21:34:25 +02:00
Luka Trovic
b3bb29157c Chore(deps): Bump @nextcloud/vue from 8.27.0 to 8.31.0
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2025-09-22 20:45:52 +02:00
Nextcloud bot
12b656dd8c fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-22 00:31:30 +00:00
github-actions[bot]
661eea3018 Merge pull request #7250 from nextcloud/automated/noid/main-update-nextcloud-ocp
[main] Update nextcloud/ocp dependency
2025-09-21 03:31:25 +00:00
nextcloud-command
ecd3cb42de chore(dev-deps): Bump nextcloud/ocp package
Signed-off-by: GitHub <noreply@github.com>
2025-09-21 02:40:50 +00:00
github-actions[bot]
f49c8f6ee4 Merge pull request #7231 from nextcloud/automated/noid/main-update-nextcloud-ocp
[main] Update nextcloud/ocp dependency
2025-09-19 17:09:19 +00:00
nextcloud-command
96b56a2447 chore(dev-deps): Bump nextcloud/ocp package
Signed-off-by: GitHub <noreply@github.com>
2025-09-19 19:03:21 +02:00
Nextcloud bot
26badb58dd fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-19 00:32:01 +00:00
Nextcloud bot
04d9433bdd fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-18 00:31:43 +00:00
Nextcloud bot
a69eb654fd fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-17 00:31:43 +00:00
Elizabeth Danzberger
ab5ccb7bc1 Merge pull request #7238 from nextcloud/bugfix/noid/activity-icons
fix(darkmode): Fix activity icon colors
2025-09-16 14:57:18 -04:00
Luka Trovic
f054cc2fbd Merge pull request #7225 from ABartelt/fix/mysql-error-1093-user-deletion
fix: resolve MySQL Error 1093 when deleting users from boards
2025-09-16 20:43:16 +02:00
Luka Trovic
1afc5cdbcc Merge pull request #7237 from nextcloud/bugfix/7207/fix-colors
fix: Fix colors from due dates and done
2025-09-16 20:16:18 +02:00
Joas Schilling
1a6e5929b2 fix(darkmode): Fix activity icon colors
Signed-off-by: Joas Schilling <coding@schilljs.com>
2025-09-16 15:17:54 +02:00
Joas Schilling
74a3ab5008 fix: Fix colors from due dates and done
Signed-off-by: Joas Schilling <coding@schilljs.com>
2025-09-16 14:42:54 +02:00
Luka Trovic
0ebd05e649 Merge pull request #7210 from nextcloud/style/noid/deleteMaterialSymbolOutline
Migrate delete icon to Material Symbol outline variant
2025-09-16 13:43:08 +02:00
Luka Trovic
3611c8f8ac Merge pull request #7159 from Somebodyisnobody/patch-1
Docs: Updating docs for API endpoints for uploading attachments
2025-09-16 10:31:31 +02:00
Luka Trovic
da3b857ab0 Merge pull request #7165 from nextcloud/get-cards-at-once
perf(cards): fetch all cards at once
2025-09-16 10:31:00 +02:00
Nextcloud bot
199b698cb7 fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-16 00:31:49 +00:00
Nextcloud bot
fb1132652a fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-15 00:31:00 +00:00
Nextcloud bot
f78c3d42df fix(l10n): Update translations from Transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2025-09-14 00:30:54 +00:00
Arne Bartelt
bffa4d0925 fix: resolve MySQL Error 1093 when deleting users from boards
Fixes #7125 and #7069 by implementing a two-step deletion process
that avoids MySQL's restriction on deleting from a table while
selecting from it in a subquery.

The fix separates the SELECT and DELETE operations:
1. First query: Get card IDs for assignments to delete
2. Second query: Delete assignments using the collected IDs

This approach works on all supported database systems (MySQL 5.7+,
MySQL 8.0+, MariaDB 10.x+) and follows MySQL's official best practices
for handling Error 1093: 'You can't specify target table for update in FROM clause'.

The issue occurred because the original deleteByParticipantOnBoard method
used a subquery that referenced the same table being deleted from,
which MySQL prohibits but MariaDB allows (explaining why it worked
in development but failed in production).

Signed-off-by: Arne Bartelt <arne.bartelt@gmail.com>
Signed-off-by: Arne Bartelt <Arne.Bartelt@gmail.com>
2025-09-10 16:39:51 +02:00
Andy Scherzinger
44d244f9aa style(icon): Migrate delete icon to Material Symbol outline variant
Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
2025-09-05 18:26:28 +02:00
Carl Schwan
a3fa72341d perf(cards): fetch all cards at once
Instead of one by one

Signed-off-by: Carl Schwan <carl.schwan@nextclound.com>
2025-08-13 16:01:52 +02:00
Somebodyisnobody
152181ff67 docs: Update API docs
Clarifying documentation for the attachment upload endpoints.

Signed-off-by: Somebodyisnobody <35230554+Somebodyisnobody@users.noreply.github.com>
2025-08-07 11:43:42 +02:00
50 changed files with 543 additions and 568 deletions

View File

@@ -6,6 +6,7 @@
- Adrian Missy <adrian.missy@onewavestudios.com>
- Alexandru Puiu <alexpuiu20@yahoo.com>
- Arne Bartelt <arne.bartelt@gmail.com>
- Chandi Langecker <git@chandi.it>
- Christoph Wurst <christoph@winzerhof-wurst.at>
- Gary Kim <gary@garykim.dev>

8
composer.lock generated
View File

@@ -380,12 +380,12 @@
"source": {
"type": "git",
"url": "https://github.com/nextcloud-deps/ocp.git",
"reference": "6a5219dda0583a45fb5541719de83c9b673b3efa"
"reference": "d927392a2a368c372ef80096171139d4287b2339"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/6a5219dda0583a45fb5541719de83c9b673b3efa",
"reference": "6a5219dda0583a45fb5541719de83c9b673b3efa",
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/d927392a2a368c372ef80096171139d4287b2339",
"reference": "d927392a2a368c372ef80096171139d4287b2339",
"shasum": ""
},
"require": {
@@ -421,7 +421,7 @@
"issues": "https://github.com/nextcloud-deps/ocp/issues",
"source": "https://github.com/nextcloud-deps/ocp/tree/master"
},
"time": "2025-09-06T00:45:32+00:00"
"time": "2025-09-16T00:45:42+00:00"
},
{
"name": "nikic/php-parser",

View File

@@ -6,7 +6,7 @@ The REST API provides access for authenticated users to their data inside the De
# Prerequisites
- All requests require a `OCS-APIRequest` HTTP header to be set to `true` and a `Content-Type` of `application/json`.
- All requests require a `OCS-APIRequest` HTTP header to be set to `true` and a `Content-Type` of `application/json`. This does not apply to the endpoint for uploading attachments, which consumes `multipart/form-data`.
- The API is located at https://nextcloud.local/index.php/apps/deck/api/v1.0
- All request parameters are required, unless otherwise specified
@@ -212,32 +212,28 @@ Returns an array of board items
"color": "31CC7C",
"boardId": 10,
"cardId": null,
"id": 37,
"customSettings": {}
"id": 37
},
{
"title": "To review",
"color": "317CCC",
"boardId": 10,
"cardId": null,
"id": 38,
"customSettings": {}
"id": 38
},
{
"title": "Action needed",
"color": "FF7A66",
"boardId": 10,
"cardId": null,
"id": 39,
"customSettings": { "isImportant": true }
"id": 39
},
{
"title": "Later",
"color": "F1DB50",
"boardId": 10,
"cardId": null,
"id": 40,
"customSettings": {}
"id": 40
}
],
"acl": [],
@@ -286,32 +282,28 @@ A 403 response might be returned if the users ability to create new boards has b
"color": "31CC7C",
"boardId": "10",
"cardId": null,
"id": 37,
"customSettings": {}
"id": 37
},
{
"title": "To review",
"color": "317CCC",
"boardId": "10",
"cardId": null,
"id": 38,
"customSettings": {}
"id": 38
},
{
"title": "Action needed",
"color": "FF7A66",
"boardId": "10",
"cardId": null,
"id": 39,
"customSettings": {}
"id": 39
},
{
"title": "Later",
"color": "F1DB50",
"boardId": "10",
"cardId": null,
"id": 40,
"customSettings": {}
"id": 40
}
],
"acl": [],
@@ -741,6 +733,7 @@ The board list endpoint supports setting an `If-Modified-Since` header to limit
| Parameter | Type | Description |
| --------- | ------- | --------------------------------------- |
| labelId | Integer | The label id to assign to the card |
#### Response
##### 200 Success
@@ -875,8 +868,7 @@ The request can fail with a bad request response for the following reasons:
"color": "31CC7C",
"boardId": "2",
"cardId": null,
"id": 5,
"customSettings": { "isImportant": false }
"id": 5
}
```
@@ -884,18 +876,16 @@ The request can fail with a bad request response for the following reasons:
#### Request parameters
| Parameter | Type | Description |
|----------------|---------|------------------------------------------------------------------------------|
| boardId | Integer | The id of the board the label belongs to |
| customSettings | Object | An key-value structure, currently supported only bool property `isImportant` |
| Parameter | Type | Description |
| --------- | ------- | ---------------------------------------- |
| boardId | Integer | The id of the board the label belongs to |
#### Request data
```json
{
"title": "Finished",
"color": "31CC7C",
"customSettings": { "isImportant": false }
"color": "31CC7C"
}
```
@@ -907,11 +897,10 @@ The request can fail with a bad request response for the following reasons:
#### Request parameters
| Parameter | Type | Description |
| --------- | ------- |-----------------------------------------------------------------------------------|
| boardId | Integer | The id of the board the label belongs to |
| labelId | Integer | The id of the label |
| customSettings | Object | An key-value structure, currently supported only bool property `isImportant` |
| Parameter | Type | Description |
| --------- | ------- | ---------------------------------------- |
| boardId | Integer | The id of the board the label belongs to |
| labelId | Integer | The id of the label |
#### Request data
@@ -919,8 +908,7 @@ The request can fail with a bad request response for the following reasons:
```json
{
"title": "Finished",
"color": "31CC7C",
"customSettings": { }
"color": "31CC7C"
}
```
@@ -1010,10 +998,12 @@ The request can fail with a bad request response for the following reasons:
#### Request data
| Parameter | Type | Description |
| --------- | ------- | --------------------------------------------- |
| type | String | The type of the attachement |
| file | Binary | File data to add as an attachment |
The request is performed as `multipart/form-data`.
| Parameter | Type | Description |
| --------- | ------- | ----------------------------------------------------------------------------------------------- |
| type | String | The type of the attachement. Use `file` or `deck_file`. |
| file | Binary | File data to add as an attachment together with the `filename` parameter according to RFC 7578. |
- Prior to Deck version v1.3.0 (API v1.0), attachments were stored within deck. For this type of attachments `deck_file` was used as the default type of attachments
- Starting with Deck version 1.3.0 (API v1.1) files are stored within the users regular Nextcloud files and the type `file` has been introduced for that
@@ -1035,12 +1025,13 @@ The request can fail with a bad request response for the following reasons:
#### Request data
| Parameter | Type | Description |
| --------- | ------- | --------------------------------------------- |
| type | String | The type of the attachement |
| file | Binary | File data to add as an attachment |
The request is performed as `multipart/form-data`.
| Parameter | Type | Description |
| --------- | ------- | ----------------------------------------------------------------------------------------------- |
| type | String | The type of the attachement. For now only `deck_file` is supported as an attachment type. |
| file | Binary | File data to add as an attachment together with the `filename` parameter according to RFC 7578. |
For now only `deck_file` is supported as an attachment type.
#### Response

View File

@@ -13,6 +13,7 @@ OC.L10N.register(
"Done" : "Гатова",
"File" : "Файл",
"Cancel" : "Скасаваць",
"Drop your files to upload" : "Перацягніце файлы для запампоўвання",
"File already exists" : "Файл ужо існуе",
"A file with the name {filename} already exists." : "Файл з назвай {filename} ужо існуе.",
"Do you want to overwrite it?" : "Хочаце перазапісаць яго?",
@@ -36,6 +37,7 @@ OC.L10N.register(
"Delete" : "Выдаліць",
"Edit" : "Рэдагаваць",
"Members" : "Удзельнікі",
"File to share" : "Файл для абагульвання",
"Invalid path selected" : "Выбраны памылковы шлях",
"Share from Files" : "Абагуліць з Файлаў",
"Show in Files" : "Паказаць у Файлах",
@@ -48,7 +50,13 @@ OC.L10N.register(
"Reply" : "Адказаць",
"Update" : "Абнавіць",
"Description" : "Апісанне",
"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" : "Задаць дату выканання на наступны тыдзень",
"(group)" : "(група)",
"Open link" : "Адкрыць спасылку",
"Edit title" : "Рэдагаваць загаловак",
@@ -56,15 +64,21 @@ OC.L10N.register(
"Keyboard shortcuts" : "Спалучэнні клавіш",
"Keyboard shortcut" : "Спалучэнне клавіш",
"Action" : "Дзеянне",
"Shift" : "Shift",
"Search" : "Пошук",
"Enter" : "Enter",
"Shared with you" : "Абагулена з вамі",
"Cancel edit" : "Скасаваць рэдагаванне",
"An error occurred" : "Узнікла памылка",
"No notifications" : "Няма апавяшчэнняў",
"Export" : "Экспарт",
"No results found" : "Вынікаў не знойдзена",
"{stack} in {board}" : "{stack} у {board}",
"Close" : "Закрыць",
"Message from {author} in {conversationName}" : "Паведамленне ад {author} у {conversationName}",
"Failed to upload {name}" : "Не ўдалося запампаваць {name}",
"Share" : "Абагуліць",
"Personal" : "Асабістыя",
"Today" : "Сёння",
"Tomorrow" : "Заўтра"
},

View File

@@ -11,6 +11,7 @@
"Done" : "Гатова",
"File" : "Файл",
"Cancel" : "Скасаваць",
"Drop your files to upload" : "Перацягніце файлы для запампоўвання",
"File already exists" : "Файл ужо існуе",
"A file with the name {filename} already exists." : "Файл з назвай {filename} ужо існуе.",
"Do you want to overwrite it?" : "Хочаце перазапісаць яго?",
@@ -34,6 +35,7 @@
"Delete" : "Выдаліць",
"Edit" : "Рэдагаваць",
"Members" : "Удзельнікі",
"File to share" : "Файл для абагульвання",
"Invalid path selected" : "Выбраны памылковы шлях",
"Share from Files" : "Абагуліць з Файлаў",
"Show in Files" : "Паказаць у Файлах",
@@ -46,7 +48,13 @@
"Reply" : "Адказаць",
"Update" : "Абнавіць",
"Description" : "Апісанне",
"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" : "Задаць дату выканання на наступны тыдзень",
"(group)" : "(група)",
"Open link" : "Адкрыць спасылку",
"Edit title" : "Рэдагаваць загаловак",
@@ -54,15 +62,21 @@
"Keyboard shortcuts" : "Спалучэнні клавіш",
"Keyboard shortcut" : "Спалучэнне клавіш",
"Action" : "Дзеянне",
"Shift" : "Shift",
"Search" : "Пошук",
"Enter" : "Enter",
"Shared with you" : "Абагулена з вамі",
"Cancel edit" : "Скасаваць рэдагаванне",
"An error occurred" : "Узнікла памылка",
"No notifications" : "Няма апавяшчэнняў",
"Export" : "Экспарт",
"No results found" : "Вынікаў не знойдзена",
"{stack} in {board}" : "{stack} у {board}",
"Close" : "Закрыць",
"Message from {author} in {conversationName}" : "Паведамленне ад {author} у {conversationName}",
"Failed to upload {name}" : "Не ўдалося запампаваць {name}",
"Share" : "Абагуліць",
"Personal" : "Асабістыя",
"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

@@ -373,6 +373,7 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Pozn.: Pro import zpět do aplikace Deck je podporován pouze formát JSON.",
"Export" : "Exportovat",
"Loading filtered view" : "Načítání filtrovaného pohledu",
"Search for {searchQuery} in other boards" : "Hledat {searchQuery} v ostatních tabulích",
"Search for {searchQuery} in all boards" : "Hledat {searchQuery} na všech tabulích",
"No results found" : "Nenalezeny žádné výsledky",
"Deck board {name}\n* Last modified on {lastMod}" : "Deck karta {name}\n* Naposledy změněno {lastMod}",

View File

@@ -371,6 +371,7 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Pozn.: Pro import zpět do aplikace Deck je podporován pouze formát JSON.",
"Export" : "Exportovat",
"Loading filtered view" : "Načítání filtrovaného pohledu",
"Search for {searchQuery} in other boards" : "Hledat {searchQuery} v ostatních tabulích",
"Search for {searchQuery} in all boards" : "Hledat {searchQuery} na všech tabulích",
"No results found" : "Nenalezeny žádné výsledky",
"Deck board {name}\n* Last modified on {lastMod}" : "Deck karta {name}\n* Naposledy změněno {lastMod}",

View File

@@ -81,10 +81,14 @@ 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\" έχει λήξει.",
@@ -96,6 +100,7 @@ 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",
@@ -106,11 +111,20 @@ 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" : "Μη έγκυρη ημερομηνία, η μορφή ημερομηνίας πρέπει να είναι ΕΕΕΕ-ΜΜ-ΗΗ",
@@ -121,10 +135,12 @@ 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" : "Επιλογή μιας καρτέλας",
@@ -212,7 +228,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" : "Προσθήκη αυτού του συνημμένου",
@@ -224,6 +240,7 @@ OC.L10N.register(
"Modified" : "Τροποποιήθηκε",
"Created" : "Δημιουργήθηκε",
"The title cannot be empty." : "Ο τίτλος δεν μπορεί να είναι κενός.",
"Cannot close unsaved card!" : "Αδυναμία κλεισίματος της κάρτας που δεν έχει αποθηκευτεί!",
"Open in sidebar view" : "Άνοιγμα σε προβολή πλευρικής στήλης",
"Open in bigger view" : "Άνοιγμα σε μεγαλύτερη προβολή",
"Comments" : "Σχόλια",
@@ -238,6 +255,7 @@ OC.L10N.register(
"Reply" : "Απάντηση",
"Update" : "Ενημέρωση",
"Write a description …" : "Γράψτε μια περιγραφή…",
"Could not save description" : "Αδυναμία αποθήκευσης της περιγραφής",
"Description" : "Περιγραφή",
"(Unsaved)" : "(Δεν αποθηκεύτηκε)",
"(Saving…)" : "(Αποθήκευση...)",
@@ -272,6 +290,7 @@ OC.L10N.register(
"{count} comments, {unread} unread" : "{count} σχόλια, {unread} μη αναγνωσμένα",
"Todo items" : "Στοιχεία todo",
"Edit card title" : "Επεξεργασία τίτλου κάρτας",
"Open link" : "Άνοιγμα συνδέσμου",
"Card deleted" : "Η καρτέλα διαγράφηκε",
"Edit title" : "Επεξεργασία τίτλου",
"Assign to me" : "Ανάθεση σε εμένα",
@@ -315,6 +334,7 @@ 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" : "Μόνο καρτέλες που έχουν ανατεθεί",
@@ -322,6 +342,7 @@ 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" : "Κλώνος πίνακα",
@@ -334,12 +355,25 @@ OC.L10N.register(
"Assigned cards" : "Ανατεθειμένες καρτέλες",
"No notifications" : "Δεν υπάρχουν ειδοποιήσεις",
"Delete board" : "Διαγραφή πίνακα",
"Clone cards" : "Κάρτες κλώνου",
"Advanced options" : "Επιλογές για προχωρημένους",
"Clone" : "Κλώνος",
"Export as CSV" : "Εξαγωγή σε CSV",
"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}" : "Πίνακας Deck {name}\n* Τελευταία τροποποίηση στις {lastMod}",
@@ -368,6 +402,7 @@ 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,10 +79,14 @@
"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\" έχει λήξει.",
@@ -94,6 +98,7 @@
"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",
@@ -104,11 +109,20 @@
"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" : "Μη έγκυρη ημερομηνία, η μορφή ημερομηνίας πρέπει να είναι ΕΕΕΕ-ΜΜ-ΗΗ",
@@ -119,10 +133,12 @@
"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" : "Επιλογή μιας καρτέλας",
@@ -210,7 +226,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" : "Προσθήκη αυτού του συνημμένου",
@@ -222,6 +238,7 @@
"Modified" : "Τροποποιήθηκε",
"Created" : "Δημιουργήθηκε",
"The title cannot be empty." : "Ο τίτλος δεν μπορεί να είναι κενός.",
"Cannot close unsaved card!" : "Αδυναμία κλεισίματος της κάρτας που δεν έχει αποθηκευτεί!",
"Open in sidebar view" : "Άνοιγμα σε προβολή πλευρικής στήλης",
"Open in bigger view" : "Άνοιγμα σε μεγαλύτερη προβολή",
"Comments" : "Σχόλια",
@@ -236,6 +253,7 @@
"Reply" : "Απάντηση",
"Update" : "Ενημέρωση",
"Write a description …" : "Γράψτε μια περιγραφή…",
"Could not save description" : "Αδυναμία αποθήκευσης της περιγραφής",
"Description" : "Περιγραφή",
"(Unsaved)" : "(Δεν αποθηκεύτηκε)",
"(Saving…)" : "(Αποθήκευση...)",
@@ -270,6 +288,7 @@
"{count} comments, {unread} unread" : "{count} σχόλια, {unread} μη αναγνωσμένα",
"Todo items" : "Στοιχεία todo",
"Edit card title" : "Επεξεργασία τίτλου κάρτας",
"Open link" : "Άνοιγμα συνδέσμου",
"Card deleted" : "Η καρτέλα διαγράφηκε",
"Edit title" : "Επεξεργασία τίτλου",
"Assign to me" : "Ανάθεση σε εμένα",
@@ -313,6 +332,7 @@
"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" : "Μόνο καρτέλες που έχουν ανατεθεί",
@@ -320,6 +340,7 @@
"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" : "Κλώνος πίνακα",
@@ -332,12 +353,25 @@
"Assigned cards" : "Ανατεθειμένες καρτέλες",
"No notifications" : "Δεν υπάρχουν ειδοποιήσεις",
"Delete board" : "Διαγραφή πίνακα",
"Clone cards" : "Κάρτες κλώνου",
"Advanced options" : "Επιλογές για προχωρημένους",
"Clone" : "Κλώνος",
"Export as CSV" : "Εξαγωγή σε CSV",
"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}" : "Πίνακας Deck {name}\n* Τελευταία τροποποίηση στις {lastMod}",
@@ -366,6 +400,7 @@
"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" : "Tableau d'importation",
"Import board" : "Importer un tableau",
"Clone {boardTitle}" : "Cloner {boardTitle}",
"Clone cards" : "Dupliquer les cartes",
"Clone assignments" : "Cloner les affectations",
@@ -373,6 +373,7 @@ 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" : "Tableau d'importation",
"Import board" : "Importer un tableau",
"Clone {boardTitle}" : "Cloner {boardTitle}",
"Clone cards" : "Dupliquer les cartes",
"Clone assignments" : "Cloner les affectations",
@@ -371,6 +371,7 @@
"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

@@ -129,7 +129,7 @@ OC.L10N.register(
"Path is already shared with this card" : "A ruta xa está compartida con esta tarxeta",
"Invalid date, date format must be YYYY-MM-DD" : "Data incorrecta, o formato da data debe ser AAAA-MM-DD",
"Personal planning and team project organization" : "Planificación persoal e organización de proxectos de equipo",
"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" : "Gabeta é unha ferramenta de organización de estilo kanban dirixida a planificación persoal e organización de proxectos para equipos integrados con Nextcloud. \n\n\n 📥 Engada as súas tarefas ás tarxetas e fagas ordenadas\n 📄 Escriba notas adicionais en Markdown\n 🔖 Asigne etiquetas para unha mellor organización\n 👥 Comparta co seu equipo, amigos ou a súa familia\n 📎 Anexe ficheiros e integreos na súa descrición de Markdown\n 💬 Debata co seu equipo usando os comentarios\n ⚡ Faga un seguimento dos cambios no fluxo de actividade\n 🚀 Teña o seu proxecto organizado",
"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" : "Gabeta é unha ferramenta de organización de estilo kanban dirixida a planificación persoal e organización de proxectos para equipos integrados con Nextcloud. \n\n\n-- 📥 Engada as súas tarefas ás tarxetas e fagas ordenadas\n- 📄 Escriba notas adicionais en Markdown\n- 🔖 Asigne etiquetas para unha mellor organización\n- 👥 Comparta co seu equipo, amigos ou a súa familia\n- 📎 Anexe ficheiros e integreos na súa descrición de Markdown\n- 💬 Debata co seu equipo usando os comentarios\n- ⚡ Faga un seguimento dos cambios no fluxo de actividade\n- 🚀 Teña o seu proxecto organizado",
"Add board" : "Engadir taboleiro",
"Card details" : "Detalles da tarxeta",
"Select the board to link to a project" : "Seleccione o taboleiro para ligar a un proxecto",

View File

@@ -127,7 +127,7 @@
"Path is already shared with this card" : "A ruta xa está compartida con esta tarxeta",
"Invalid date, date format must be YYYY-MM-DD" : "Data incorrecta, o formato da data debe ser AAAA-MM-DD",
"Personal planning and team project organization" : "Planificación persoal e organización de proxectos de equipo",
"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" : "Gabeta é unha ferramenta de organización de estilo kanban dirixida a planificación persoal e organización de proxectos para equipos integrados con Nextcloud. \n\n\n 📥 Engada as súas tarefas ás tarxetas e fagas ordenadas\n 📄 Escriba notas adicionais en Markdown\n 🔖 Asigne etiquetas para unha mellor organización\n 👥 Comparta co seu equipo, amigos ou a súa familia\n 📎 Anexe ficheiros e integreos na súa descrición de Markdown\n 💬 Debata co seu equipo usando os comentarios\n ⚡ Faga un seguimento dos cambios no fluxo de actividade\n 🚀 Teña o seu proxecto organizado",
"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" : "Gabeta é unha ferramenta de organización de estilo kanban dirixida a planificación persoal e organización de proxectos para equipos integrados con Nextcloud. \n\n\n-- 📥 Engada as súas tarefas ás tarxetas e fagas ordenadas\n- 📄 Escriba notas adicionais en Markdown\n- 🔖 Asigne etiquetas para unha mellor organización\n- 👥 Comparta co seu equipo, amigos ou a súa familia\n- 📎 Anexe ficheiros e integreos na súa descrición de Markdown\n- 💬 Debata co seu equipo usando os comentarios\n- ⚡ Faga un seguimento dos cambios no fluxo de actividade\n- 🚀 Teña o seu proxecto organizado",
"Add board" : "Engadir taboleiro",
"Card details" : "Detalles da tarxeta",
"Select the board to link to a project" : "Seleccione o taboleiro para ligar a un proxecto",

View File

@@ -164,12 +164,13 @@ OC.L10N.register(
"Archive 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 a user to this card…" : "Додели корисник на оваа картица...",
"Select a user to assign to this card…" : "Избери на кого да се додели оваа картица…",
"File to share" : "Датотека за споделување",
"Invalid path selected" : "Избрана невалидна патека",
"Upload new files" : "Прикачи нови датотеки",
@@ -205,12 +206,16 @@ OC.L10N.register(
"Select Date" : "Избери датум",
"Later today {timeLocale}" : "Денес покасно {timeLocale}",
"Tomorrow {timeLocale}" : "Утре {timeLocale}",
"This weekend {timeLocale}" : "Овој викенд {timeLocale}",
"Set due date for this weekend" : "Постави рок за овој викенд",
"Assign a due date to this card…" : "Додели рок за оваа картица…",
"Set a due date" : "Постави краен рок",
"Remove due date" : "Отстрани краен рок",
"Mark as done" : "Означи како готово",
"Unarchive card" : "Врати картица од архива",
"Archive card" : "Архивирај картица",
"Assign a tag to this card…" : "Додади ознака на оваа картица...",
"Select or create a tag…" : "Избери или креирај ознака...",
"(group)" : "(group)",
"Card deleted" : "Картицата е избришана",
"Edit title" : "Удери наслов",

View File

@@ -162,12 +162,13 @@
"Archive 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 a user to this card…" : "Додели корисник на оваа картица...",
"Select a user to assign to this card…" : "Избери на кого да се додели оваа картица…",
"File to share" : "Датотека за споделување",
"Invalid path selected" : "Избрана невалидна патека",
"Upload new files" : "Прикачи нови датотеки",
@@ -203,12 +204,16 @@
"Select Date" : "Избери датум",
"Later today {timeLocale}" : "Денес покасно {timeLocale}",
"Tomorrow {timeLocale}" : "Утре {timeLocale}",
"This weekend {timeLocale}" : "Овој викенд {timeLocale}",
"Set due date for this weekend" : "Постави рок за овој викенд",
"Assign a due date to this card…" : "Додели рок за оваа картица…",
"Set a due date" : "Постави краен рок",
"Remove due date" : "Отстрани краен рок",
"Mark as done" : "Означи како готово",
"Unarchive card" : "Врати картица од архива",
"Archive card" : "Архивирај картица",
"Assign a tag to this card…" : "Додади ознака на оваа картица...",
"Select or create a tag…" : "Избери или креирај ознака...",
"(group)" : "(group)",
"Card deleted" : "Картицата е избришана",
"Edit title" : "Удери наслов",

View File

@@ -373,6 +373,7 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Uwaga: tylko format JSON jest obsługiwany przy imporcie z powrotem do aplikacji Deck.",
"Export" : "Eksportuj",
"Loading filtered view" : "Wczytywanie przefiltrowanego widoku",
"Search for {searchQuery} in other boards" : "Szukaj {searchQuery} na innych tablicach",
"Search for {searchQuery} in all boards" : "Wyszukaj dla {searchQuery} na wszystkich tablicach",
"No results found" : "Nie znaleziono wyników",
"Deck board {name}\n* Last modified on {lastMod}" : "Tablica {name}\n* Ostatnia modyfikacja w dniu {lastMod}",

View File

@@ -371,6 +371,7 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Uwaga: tylko format JSON jest obsługiwany przy imporcie z powrotem do aplikacji Deck.",
"Export" : "Eksportuj",
"Loading filtered view" : "Wczytywanie przefiltrowanego widoku",
"Search for {searchQuery} in other boards" : "Szukaj {searchQuery} na innych tablicach",
"Search for {searchQuery} in all boards" : "Wyszukaj dla {searchQuery} na wszystkich tablicach",
"No results found" : "Nie znaleziono wyników",
"Deck board {name}\n* Last modified on {lastMod}" : "Tablica {name}\n* Ostatnia modyfikacja w dniu {lastMod}",

View File

@@ -373,6 +373,7 @@ 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,6 +371,7 @@
"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

@@ -373,7 +373,8 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Not: Yeniden Tahta uygulaması içine aktarmak için yalnızca JSON biçimi desteklenir.",
"Export" : "Dışa aktar",
"Loading filtered view" : "Süzülmüş görünüm yükleniyor",
"Search for {searchQuery} in all boards" : "Tüm panolarda {searchQuery} araması için sonuçlar",
"Search for {searchQuery} in other boards" : "Diğer panolarda {searchQuery} ara",
"Search for {searchQuery} in all boards" : "Tüm panolarda {searchQuery} ara",
"No results found" : "Herhangi bir sonuç bulunamadı",
"Deck board {name}\n* Last modified on {lastMod}" : "{name} tahta panosu\n* Son değişiklik: {lastMod}",
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Oluşturulma: {created}\n* Son değiştirilme: {lastMod}\n* {nbAttachments} ek dosya\n* {nbComments} yorum",

View File

@@ -371,7 +371,8 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Not: Yeniden Tahta uygulaması içine aktarmak için yalnızca JSON biçimi desteklenir.",
"Export" : "Dışa aktar",
"Loading filtered view" : "Süzülmüş görünüm yükleniyor",
"Search for {searchQuery} in all boards" : "Tüm panolarda {searchQuery} araması için sonuçlar",
"Search for {searchQuery} in other boards" : "Diğer panolarda {searchQuery} ara",
"Search for {searchQuery} in all boards" : "Tüm panolarda {searchQuery} ara",
"No results found" : "Herhangi bir sonuç bulunamadı",
"Deck board {name}\n* Last modified on {lastMod}" : "{name} tahta panosu\n* Son değişiklik: {lastMod}",
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Oluşturulma: {created}\n* Son değiştirilme: {lastMod}\n* {nbAttachments} ek dosya\n* {nbComments} yorum",

View File

@@ -373,6 +373,7 @@ OC.L10N.register(
"Note: Only the JSON format is supported for importing back into the Deck app." : "Примітка: Для імпорту в додаток Deck підтримується лише формат JSON.",
"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}",

View File

@@ -371,6 +371,7 @@
"Note: Only the JSON format is supported for importing back into the Deck app." : "Примітка: Для імпорту в додаток Deck підтримується лише формат JSON.",
"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}",

View File

@@ -55,6 +55,7 @@ OC.L10N.register(
"Edit title" : "Sarlavhani tahrirlash",
"Delete card" : "Kartani o'chirish",
"seconds ago" : "seconds ago",
"Keyboard shortcuts" : "Klaviatura yorliqlari",
"Search" : "Qidirish",
"Archived boards" : "Arxivlangan taxtalar",
"Shared with you" : "Shared with you",

View File

@@ -53,6 +53,7 @@
"Edit title" : "Sarlavhani tahrirlash",
"Delete card" : "Kartani o'chirish",
"seconds ago" : "seconds ago",
"Keyboard shortcuts" : "Klaviatura yorliqlari",
"Search" : "Qidirish",
"Archived boards" : "Arxivlangan taxtalar",
"Shared with you" : "Shared with you",

View File

@@ -50,12 +50,10 @@ class LabelApiController extends ApiController {
*
* @params $title
* @params $color
* @param array<string, scalar> $customSettings
*
* Create a new label
*/
public function create($title, $color, array $customSettings = []) {
$label = $this->labelService->create($title, $color, $this->request->getParam('boardId'), $customSettings);
public function create($title, $color) {
$label = $this->labelService->create($title, $color, $this->request->getParam('boardId'));
return new DataResponse($label, HTTP::STATUS_OK);
}
@@ -66,12 +64,10 @@ class LabelApiController extends ApiController {
*
* @params $title
* @params $color
* @param array<string, scalar> $customSettings
*
* Update a specific label
*/
public function update($title, $color, array $customSettings = []) {
$label = $this->labelService->update($this->request->getParam('labelId'), $title, $color, $customSettings);
public function update($title, $color) {
$label = $this->labelService->update($this->request->getParam('labelId'), $title, $color);
return new DataResponse($label, HTTP::STATUS_OK);
}

View File

@@ -25,11 +25,10 @@ class LabelController extends Controller {
* @param $title
* @param $color
* @param $boardId
* @param array<string, scalar> $customSettings
* @return \OCP\AppFramework\Db\Entity
*/
public function create($title, $color, $boardId, array $customSettings = []) {
return $this->labelService->create($title, $color, $boardId, $customSettings);
public function create($title, $color, $boardId) {
return $this->labelService->create($title, $color, $boardId);
}
/**
@@ -37,11 +36,10 @@ class LabelController extends Controller {
* @param $id
* @param $title
* @param $color
* @param array<string, scalar> $customSettings
* @return \OCP\AppFramework\Db\Entity
*/
public function update($id, $title, $color, array $customSettings = []) {
return $this->labelService->update($id, $title, $color, $customSettings);
public function update($id, $title, $color) {
return $this->labelService->update($id, $title, $color);
}
/**

View File

@@ -77,18 +77,33 @@ class AssignmentMapper extends DeckMapper implements IPermissionMapper {
}
public function deleteByParticipantOnBoard(string $participant, int $boardId, $type = Assignment::TYPE_USER) {
$qb = $this->db->getQueryBuilder();
// Step 1: Get all card IDs for the board that have assignments for this participant
// This avoids MySQL Error 1093 by separating the SELECT from the DELETE operation
$cardIdQuery = $this->db->getQueryBuilder();
$cardIdQuery->select('a.card_id')
->from('deck_assigned_users', 'a')
->innerJoin('a', 'deck_cards', 'c', 'c.id = a.card_id')
->innerJoin('c', 'deck_stacks', 's', 's.id = c.stack_id')
->where($cardIdQuery->expr()->eq('a.participant', $qb->createNamedParameter($participant, IQueryBuilder::PARAM_STR)))
->andWhere($cardIdQuery->expr()->eq('s.board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->andWhere($cardIdQuery->expr()->eq('a.type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
$qb->delete('deck_assigned_users')
->where($qb->expr()->in('card_id', $qb->createFunction($cardIdQuery->getSQL()), IQueryBuilder::PARAM_INT_ARRAY));
$qb->executeStatement();
->where($cardIdQuery->expr()->eq('a.participant', $cardIdQuery->createNamedParameter($participant, IQueryBuilder::PARAM_STR)))
->andWhere($cardIdQuery->expr()->eq('s.board_id', $cardIdQuery->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->andWhere($cardIdQuery->expr()->eq('a.type', $cardIdQuery->createNamedParameter($type, IQueryBuilder::PARAM_INT)));
$result = $cardIdQuery->executeQuery();
$cardIds = [];
while ($row = $result->fetch()) {
$cardIds[] = $row['card_id'];
}
$result->closeCursor();
// Step 2: If we have card IDs, delete the assignments
if (!empty($cardIds)) {
$deleteQuery = $this->db->getQueryBuilder();
$deleteQuery->delete('deck_assigned_users')
->where($deleteQuery->expr()->eq('participant', $deleteQuery->createNamedParameter($participant, IQueryBuilder::PARAM_STR)))
->andWhere($deleteQuery->expr()->eq('type', $deleteQuery->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
->andWhere($deleteQuery->expr()->in('card_id', $deleteQuery->createNamedParameter($cardIds, IQueryBuilder::PARAM_INT_ARRAY)));
$deleteQuery->executeStatement();
}
}

View File

@@ -131,7 +131,11 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $card;
}
public function findAll($stackId, $limit = null, $offset = null, $since = -1) {
/**
* @return Card[]
* @throws \OCP\DB\Exception
*/
public function findAll($stackId, ?int $limit = null, ?int $offset = null, int $since = -1) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_cards')
@@ -146,6 +150,32 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
/**
* @param int[] $stackIds
* @return array<int, null|Card[]>
* @throws \OCP\DB\Exception
*/
public function findAllForStacks(array $stackIds, ?int $limit = null, ?int $offset = null, int $since = -1): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_cards')
->where($qb->expr()->in('stack_id', $qb->createNamedParameter($stackIds, IQueryBuilder::PARAM_INT_ARRAY)))
->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)))
->setMaxResults($limit)
->setFirstResult($offset)
->orderBy('order')
->addOrderBy('id');
$rawCards = $this->findEntities($qb);
$cards = array_fill_keys($stackIds, null);
foreach ($rawCards as $card) {
$cards[$card->getStackId()][] = $card;
}
return $cards;
}
public function queryCardsByBoard(int $boardId): IQueryBuilder {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')

View File

@@ -9,8 +9,6 @@ namespace OCA\Deck\Db;
/**
* @method getTitle(): string
* @method getCustomSettings(): string
* @method setCustomSettings(string $customSettings)
*/
class Label extends RelationalEntity {
protected $title;
@@ -18,32 +16,15 @@ class Label extends RelationalEntity {
protected $boardId;
protected $cardId;
protected $lastModified;
protected $customSettings;
public function __construct() {
$this->addType('id', 'integer');
$this->addType('boardId', 'integer');
$this->addType('cardId', 'integer');
$this->addType('lastModified', 'integer');
$this->addType('customSettings', 'string');
}
public function getETag() {
return md5((string)$this->getLastModified());
}
public function getCustomSettingsArray(): array {
return $this->customSettings ? json_decode($this->customSettings, true) : [];
}
public function setCustomSettingsArray(array $customSettings): void {
$this->setCustomSettings(json_encode($customSettings ?: new \stdClass()));
}
public function jsonSerialize(): array {
$data = parent::jsonSerialize();
$data['customSettings'] = $this->getCustomSettingsArray() ?: new \stdClass();
return $data;
}
}

View File

@@ -15,6 +15,7 @@ use Sabre\VObject\Component\VCalendar;
* @method int getDeletedAt()
* @method int getLastModified()
* @method int getOrder()
* @method Card[] getCards()
*/
class Stack extends RelationalEntity {
protected $title;

View File

@@ -77,12 +77,10 @@ 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 findAll($boardId, $limit = null, $offset = null): array {
public function findAll($boardId, ?int $limit = null, ?int $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())

View File

@@ -1,32 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
declare(strict_types=1);
namespace OCA\Deck\Migration;
use Closure;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;
class Version20000Date20250907000000 extends SimpleMigrationStep {
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$table = $schema->getTable('deck_labels');
if (!$table->hasColumn('custom_settings')) {
$table->addColumn('custom_settings', Types::JSON, [
'notnull' => false,
]);
}
return $schema;
}
}

View File

@@ -340,7 +340,7 @@ class CardService {
// clone labels that are assigned to card but don't exist in new board
if (empty($filteredLabels)) {
if ($this->permissionService->getPermissions($boardId)[Acl::PERMISSION_MANAGE] === true) {
$newLabel = $this->labelService->create($label->getTitle(), $label->getColor(), $board->getId(), $label->getCustomSettingsArray());
$newLabel = $this->labelService->create($label->getTitle(), $label->getColor(), $board->getId());
$boardLabels[] = $label;
$this->assignLabel($card->getId(), $newLabel->getId());
}

View File

@@ -62,7 +62,6 @@ class LabelService {
* @param $title
* @param $color
* @param $boardId
* @param array<string, scalar> $customSettings
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
@@ -70,7 +69,7 @@ class LabelService {
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function create($title, $color, $boardId, array $customSettings = []) {
public function create($title, $color, $boardId) {
$this->labelServiceValidator->check(compact('title', 'color', 'boardId'));
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
@@ -90,7 +89,6 @@ class LabelService {
$label->setTitle($title);
$label->setColor($color);
$label->setBoardId($boardId);
$label->setCustomSettingsArray($customSettings);
$this->changeHelper->boardChanged($boardId);
return $this->labelMapper->insert($label);
}
@@ -101,7 +99,7 @@ class LabelService {
$originLabel = $this->find($labelId);
$filteredValues = array_values(array_filter($boardLabels, fn ($item) => $item->getTitle() === $originLabel->getTitle()));
if (empty($filteredValues)) {
$label = $this->create($originLabel->getTitle(), $originLabel->getColor(), $targetBoardId, $originLabel->getCustomSettingsArray());
$label = $this->create($originLabel->getTitle(), $originLabel->getColor(), $targetBoardId);
return $label;
}
return $originLabel;
@@ -132,7 +130,6 @@ class LabelService {
* @param $id
* @param $title
* @param $color
* @param array<string, scalar> $customSettings
* @return \OCP\AppFramework\Db\Entity
* @throws StatusException
* @throws \OCA\Deck\NoPermissionException
@@ -140,7 +137,7 @@ class LabelService {
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws BadRequestException
*/
public function update($id, $title, $color, array $customSettings = []) {
public function update($id, $title, $color) {
$this->labelServiceValidator->check(compact('title', 'color', 'id'));
$this->permissionService->checkPermission($this->labelMapper, $id, Acl::PERMISSION_MANAGE);
@@ -164,7 +161,6 @@ class LabelService {
$label->setTitle($title);
$label->setColor($color);
$label->setCustomSettingsArray($customSettings);
$this->changeHelper->boardChanged($label->getBoardId());
return $this->labelMapper->update($label);
}

View File

@@ -75,19 +75,22 @@ class StackService {
$this->stackServiceValidator = $stackServiceValidator;
}
private function enrichStackWithCards($stack, $since = -1) {
$cards = $this->cardMapper->findAll($stack->getId(), null, null, $since);
/** @param Stack[] $stacks */
private function enrichStacksWithCards(array $stacks, $since = -1): void {
$cardsByStackId = $this->cardMapper->findAllForStacks(array_map(fn (Stack $stack) => $stack->getId(), $stacks), null, null, $since);
if (\count($cards) === 0) {
return;
}
foreach ($cardsByStackId as $stackId => $cards) {
if (!$cards) {
continue;
}
$stack->setCards($this->cardService->enrichCards($cards));
}
private function enrichStacksWithCards($stacks, $since = -1) {
foreach ($stacks as $stack) {
$this->enrichStackWithCards($stack, $since);
foreach ($stacks as $stack) {
if ($stack->getId() === $stackId) {
$stack->setCards($this->cardService->enrichCards($cards));
break;
}
}
}
}
@@ -124,9 +127,9 @@ class StackService {
}
/**
* @param $boardId
* @param mixed $boardId
*
* @return array
* @return Stack[]
* @throws \OCA\Deck\NoPermissionException
* @throws BadRequestException
*/
@@ -247,7 +250,7 @@ class StackService {
);
$this->changeHelper->boardChanged($stack->getBoardId());
$this->eventDispatcher->dispatchTyped(new BoardUpdatedEvent($stack->getBoardId()));
$this->enrichStackWithCards($stack);
$this->enrichStacksWithCards([$stack]);
return $stack;
}

502
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.1.0",
"@nextcloud/moment": "^1.3.4",
"@nextcloud/l10n": "^3.4.0",
"@nextcloud/moment": "^1.3.5",
"@nextcloud/notify_push": "^1.3.0",
"@nextcloud/router": "^3.0.1",
"@nextcloud/vue": "^8.27.0",
"@nextcloud/vue": "^8.31.0",
"blueimp-md5": "^2.19.0",
"chroma-js": "^3.1.2",
"dompurify": "^3.2.5",
@@ -59,6 +59,7 @@
"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",

View File

@@ -6,7 +6,7 @@
<template>
<div v-if="activity" class="activity">
<div class="activity--header">
<img :src="activity.icon" class="activity--icon">
<img :src="activity.icon" class="activity--icon" :class="applyMonochromeIconColor">
<NcRichText class="activity--subject" :text="message.subject" :arguments="message.parameters" />
<div class="activity--timestamp" :name="formatReadableDate(activity.datetime)">
{{ relativeDate(activity.datetime) }}
@@ -94,6 +94,15 @@ export default {
}
},
applyMonochromeIconColor() {
// copied from https://github.com/nextcloud/activity/blob/db919d45c45356082b17104614018e2c7e691996/js/script.js#L225
const monochromeIcon = this.activity.type !== 'file_created' && this.activity.type !== 'file_deleted' && this.activity.type !== 'favorite' && !this.activity.icon.endsWith('-color.svg')
if (monochromeIcon) {
return 'monochrome'
}
return ''
},
sanitizedMessage() {
return DOMPurify.sanitize(this.activity.message, { ALLOWED_TAGS: ['ins', 'del'], ALLOWED_ATTR: ['class'] })
},
@@ -115,6 +124,12 @@ export default {
height: 16px;
flex-shrink: 0;
flex-grow: 0;
/* colored icons, in addition to core ones */
&.monochrome {
opacity: 0.8;
filter: var(--background-invert-if-dark);
}
}
.activity--subject {
margin-left: 10px;

View File

@@ -5,7 +5,7 @@
<template>
<div class="attachments-drag-zone"
@dragover.prevent="!isDraggingOver && (isDraggingOver = true)"
@dragover.prevent="handleDragOver"
@dragleave.prevent="isDraggingOver && (isDraggingOver = false)"
@drop.prevent="handleDropFiles">
<slot />
@@ -83,6 +83,13 @@ export default {
},
},
methods: {
handleDragOver(event) {
if (!event.dataTransfer || event.dataTransfer.items?.length <= 0) {
return
}
!this.isDraggingOver && (this.isDraggingOver = true)
},
handleDropFiles(event) {
event.dataTransfer.dropEffect = 'copy'
this.isDraggingOver = false

View File

@@ -281,7 +281,6 @@ export default {
}
.board {
padding-left: $board-gap;
position: relative;
overflow-x: auto;
flex-grow: 1;

View File

@@ -7,7 +7,6 @@
<NcAppSidebar v-if="board != null"
:actions="[]"
:name="board.title"
style="width: 400px"
@close="closeSidebar">
<NcAppSidebarTab id="sharing"
:order="0"
@@ -59,7 +58,7 @@ import { NcAppSidebar, NcAppSidebarTab } from '@nextcloud/vue'
import ActivityIcon from 'vue-material-design-icons/LightningBolt.vue'
import SharingIcon from 'vue-material-design-icons/ShareVariantOutline.vue'
import TagsIcon from 'vue-material-design-icons/TagMultipleOutline.vue'
import TrashIcon from 'vue-material-design-icons/DeleteOutline.vue'
import TrashIcon from 'vue-material-design-icons/TrashCanOutline.vue'
const capabilities = window.OC.getCapabilities()
export default {

View File

@@ -15,11 +15,7 @@
@input="updateColor">
<div :style="{ backgroundColor: '#' + editingLabel.color }" class="color0 icon-colorpicker" />
</NcColorPicker>
<NcCheckboxRadioSwitch v-model="editingLabelIsImportant"
type="switch">
{{ t('deck', 'Important') }}
</NcCheckboxRadioSwitch>
<input v-model="editingLabel.title" type="text" style="margin-right: 20px;">
<input v-model="editingLabel.title" type="text">
<input :disabled="!editLabelObjValidated"
type="submit"
value=""
@@ -38,18 +34,10 @@
</template>
<template v-else>
<div v-if="canManage && !isArchived" class="label-title" @click="clickEdit(label)">
<span :style="{
backgroundColor: `#${label.color}`,
color: textColor(label.color),
fontWeight: label.customSettings.isImportant ? 'bold' : 'normal'
}">{{ label.title }}</span>
<span :style="{ backgroundColor: `#${label.color}`, color: textColor(label.color) }">{{ label.title }}</span>
</div>
<div v-else class="label-title">
<span :style="{
backgroundColor: `#${label.color}`,
color: textColor(label.color),
fontWeight: label.customSettings.isImportant ? 'bold' : 'normal'
}">{{ label.title }}</span>
<span :style="{ backgroundColor: `#${label.color}`, color: textColor(label.color) }">{{ label.title }}</span>
</div>
<NcActions v-if="canManage && !isArchived">
@@ -74,11 +62,7 @@
@input="updateColor">
<div :style="{ backgroundColor: '#' + addLabelObj.color }" class="color0 icon-colorpicker" />
</NcColorPicker>
<NcCheckboxRadioSwitch v-model="addLabelIsImportant"
type="switch">
{{ t('deck', 'Important') }}
</NcCheckboxRadioSwitch>
<input v-model="addLabelObj.title" type="text" style="margin-right: 20px;">
<input v-model="addLabelObj.title" type="text">
<input :disabled="!addLabelObjValidated"
type="submit"
value=""
@@ -104,7 +88,7 @@
import { mapGetters } from 'vuex'
import Color from '../../mixins/color.js'
import { NcColorPicker, NcActions, NcActionButton, NcCheckboxRadioSwitch } from '@nextcloud/vue'
import { NcColorPicker, NcActions, NcActionButton } from '@nextcloud/vue'
export default {
name: 'TagsTabSidebar',
@@ -112,7 +96,6 @@ export default {
NcColorPicker,
NcActions,
NcActionButton,
NcCheckboxRadioSwitch,
},
mixins: [Color],
data() {
@@ -156,22 +139,7 @@ export default {
labelsSorted() {
return [...this.labels].sort((a, b) => a.title.localeCompare(b.title))
},
addLabelIsImportant: {
get() {
return this.addLabelObj?.customSettings?.isImportant || false
},
set(isImportant) {
this.addLabelObj.customSettings = { ...this.addLabelObj.customSettings, isImportant }
},
},
editingLabelIsImportant: {
get() {
return this.editingLabel?.customSettings?.isImportant
},
set(isImportant) {
this.editingLabel.customSettings = { ...this.editingLabel.customSettings, isImportant }
},
},
},
methods: {
updateColor(c) {
@@ -189,23 +157,15 @@ export default {
this.$store.dispatch('removeLabelFromCurrentBoard', id)
},
updateLabel(label) {
const payload = {
...this.editingLabel,
customSettings: { ...this.editingLabel.customSettings },
}
this.$store.dispatch('updateLabelFromCurrentBoard', payload)
this.$store.dispatch('updateLabelFromCurrentBoard', this.editingLabel)
this.editingLabelId = null
},
clickShowAddLabel() {
this.addLabelObj = { cardId: null, color: this.defaultColors[Math.floor(Math.random() * this.defaultColors.length)], title: '', customSettings: {} }
this.addLabelObj = { cardId: null, color: this.defaultColors[Math.floor(Math.random() * this.defaultColors.length)], title: '' }
this.addLabel = true
},
clickAddLabel() {
const payload = {
...this.addLabelObj,
customSettings: { ...this.addLabelObj.customSettings },
}
this.$store.dispatch('addLabelToCurrentBoard', payload)
this.$store.dispatch('addLabelToCurrentBoard', this.addLabelObj)
this.addLabel = false
this.addLabelObj = null
},

View File

@@ -131,42 +131,6 @@ export default {
height: var(--default-clickable-area);
}
.badges .icon.due {
background-position: 4px center;
border-radius: var(--border-radius);
padding: 4px;
font-size: 13px;
display: flex;
align-items: center;
opacity: .5;
flex-shrink: 1;
.icon {
background-size: contain;
}
&.overdue {
background-color: var(--color-error);
color: var(--color-primary-element-text);
opacity: .7;
}
&.now {
background-color: var(--color-warning);
opacity: .7;
}
&.next {
background-color: var(--color-background-dark);
opacity: .7;
}
span {
margin-left: 20px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
}
.badge-left, .badge-right {
display: flex;
}

View File

@@ -6,19 +6,10 @@
<template>
<AttachmentDragAndDrop v-if="card" :card-id="card.id" class="drop-upload--card">
<div :ref="`card${card.id}`"
:class="{
'compact': compactMode,
'current-card': currentCard,
'no-labels': !hasLabels,
'card__editable': canEdit,
'card__archived': card.archived,
'card__highlight': highlight,
'card__important': !!importantColor,
}"
:class="{'compact': compactMode, 'current-card': currentCard, 'no-labels': !hasLabels, 'card__editable': canEdit, 'card__archived': card.archived, 'card__highlight': highlight}"
tag="div"
:tabindex="0"
class="card"
:style="{'box-shadow': importantColor ? `-5px 0px 0px 0px ${importantColor}` : null}"
@click="openCard"
@keyup.self="handleCardKeyboardShortcut"
@mouseenter="focus(card.id)">
@@ -142,14 +133,6 @@ export default {
currentBoard: state => state.currentBoard,
showCardCover: state => state.showCardCover,
shortcutLock: state => state.shortcutLock,
importantColor() {
for (const label of this.card.labels) {
if (label.customSettings.isImportant) {
return '#' + label.color
}
}
return null
},
}),
...mapGetters([
'isArchived',
@@ -438,10 +421,6 @@ export default {
&.card__highlight {
animation: highlight 2s;
}
&:not(.card__important) {
box-shadow: -5px 0px 0px 0px var(--color-main-background);
}
.card-labels {
display: flex;
align-items: end;

View File

@@ -95,16 +95,16 @@ export default {
z-index: 2;
[data-due-state='Overdue'] & {
color: var(--color-error-text);
background-color: rgba(var(--color-error-rgb), .1);
color: var(--color-element-error, var(--color-error-text));
background-color: rgba(var(--color-error-rgb), .5);
}
[data-due-state='Now'] & {
color: var(--color-warning-text);
background-color: rgba(var(--color-warning-rgb), .1);
color: var(--color-element-warning, var(--color-warning-text));
background-color: rgba(var(--color-warning-rgb), .5);
}
[data-due-state='Done'] & {
color: var(--color-success-text);
background-color: rgba(var(--color-success-rgb), .1);
color: var(--color-element-success, var(--color-success-text));
background-color: rgba(var(--color-success-rgb), .5);
}
.due--label {

View File

@@ -272,7 +272,6 @@ export default new Vuex.Store({
labelToUpdate.title = newLabel.title
labelToUpdate.color = newLabel.color
labelToUpdate.customSettings = newLabel.customSettings
},
addLabelToCurrentBoard(state, newLabel) {
state.currentBoard.labels.push(newLabel)

View File

@@ -45,13 +45,11 @@ class LabelTest extends TestCase {
'lastModified' => null,
'color' => '000000',
'ETag' => $label->getETag(),
'customSettings' => new \stdClass(),
], $label->jsonSerialize());
}
public function testJsonSerializeCard() {
$label = $this->createLabel();
$label->setCardId(123);
$label->setCustomSettingsArray(['isImportant' => true]);
$this->assertEquals([
'id' => 1,
'title' => 'My Label',
@@ -60,7 +58,6 @@ class LabelTest extends TestCase {
'lastModified' => null,
'color' => '000000',
'ETag' => $label->getETag(),
'customSettings' => ['isImportant' => true]
], $label->jsonSerialize());
}
}

View File

@@ -75,16 +75,14 @@ class LabelServiceTest extends TestCase {
$label->setTitle('Label title');
$label->setBoardId(123);
$label->setColor('00ff00');
$label->setCustomSettingsArray(['isImportant' => true]);
$this->labelMapper->expects($this->once())
->method('insert')
->willReturn($label);
$b = $this->labelService->create('Label title', '00ff00', 123, ['isImportant' => true]);
$b = $this->labelService->create('Label title', '00ff00', 123);
$this->assertEquals($b->getTitle(), 'Label title');
$this->assertEquals($b->getBoardId(), 123);
$this->assertEquals($b->getColor(), '00ff00');
$this->assertEquals($b->getCustomSettingsArray(), ['isImportant' => true]);
}
@@ -113,7 +111,6 @@ class LabelServiceTest extends TestCase {
$label->setId(1);
$label->setTitle('title');
$label->setColor('00ff00');
$label->setCustomSettingsArray(['isImportant' => true]);
$this->labelMapper->expects($this->once())
->method('find')
->willReturn($label);
@@ -122,7 +119,6 @@ class LabelServiceTest extends TestCase {
$expectedLabel->setTitle('title');
$expectedLabel->setColor('00ff00');
$expectedLabel->setBoardId(1);
$expectedLabel->setCustomSettingsArray(['isImportant' => true]);
$this->labelMapper->expects($this->once())
->method('insert')
->with($expectedLabel)

View File

@@ -129,12 +129,17 @@ class StackServiceTest extends TestCase {
}
)
);
$this->cardMapper->expects($this->any())->method('findAll')->willReturn($this->getCards(222));
$this->cardMapper->expects($this->any())->method('findAllForStacks')->willReturnCallback(function (array $stackIds) {
$r = [];
foreach ($stackIds as $stackId) {
$r[$stackId] = $this->getCards(222);
}
return $r;
});
$actual = $this->stackService->findAll(123);
for ($stackId = 0; $stackId < 3; $stackId++) {
for ($cardId = 0;$cardId < 10;$cardId++) {
for ($cardId = 0; $cardId < 10; $cardId++) {
$this->assertEquals($actual[0]->getCards()[$cardId]->getId(), $cardId);
$this->assertEquals($actual[0]->getCards()[$cardId]->getStackId(), 222);
$this->assertEquals($actual[0]->getCards()[$cardId]->getLabels(), $this->getLabels()[$cardId]);
@@ -211,7 +216,7 @@ class StackServiceTest extends TestCase {
$stackToBeDeleted->setBoardId(1);
$this->stackMapper->expects($this->once())->method('find')->willReturn($stackToBeDeleted);
$this->stackMapper->expects($this->once())->method('update')->willReturn($stackToBeDeleted);
$this->cardMapper->expects($this->once())->method('findAll')->willReturn([]);
$this->cardMapper->expects($this->once())->method('findAllForStacks')->willReturn([]);
$this->stackService->delete(123);
$this->assertTrue($stackToBeDeleted->getDeletedAt() <= time(), 'deletedAt is in the past');
$this->assertTrue($stackToBeDeleted->getDeletedAt() > 0, 'deletedAt is set');