Compare commits

...

23 Commits

Author SHA1 Message Date
Julius Härtl
99c3beafb7 1.0.0 is 18+ only
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-09 16:34:53 +02:00
Julius Härtl
9dc78b790c Merge pull request #1654 from nextcloud/release/1.0.0-beta1
Prepare 1.0.0 Beta 1
2020-04-09 16:34:20 +02:00
Julius Härtl
854798f7e2 Merge pull request #1670 from nextcloud/bugfix/noid/fix-card-order
Fix filtering and sorting of stacks that caused card reordering
2020-04-09 16:34:05 +02:00
Julius Härtl
59211e3e9b Merge pull request #1669 from nextcloud/enh/design-card
Adjust card design
2020-04-09 15:26:35 +02:00
Julius Härtl
929ef69d31 Fix filtering and sorting of stacks that caused card reordering to only apply after reload
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-09 12:24:29 +02:00
Julius Härtl
4e091d0428 Fix checkmark text spacing
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-09 10:50:47 +02:00
Julius Härtl
266818cd2b Adjust card design
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-09 10:47:15 +02:00
Nextcloud bot
fd82f4c792 [tx-robot] updated from transifex 2020-04-09 02:30:23 +00:00
Nextcloud bot
e7d2c74986 [tx-robot] updated from transifex 2020-04-08 02:30:32 +00:00
Julius Härtl
23e06b58f5 Merge pull request #1653 from nextcloud/bugfix/noid/use-parent-permission
Harden permission check on reshares
2020-04-07 12:08:23 +02:00
Julius Härtl
bea8370c03 Merge pull request #1656 from nextcloud/bugfix/noid/snap.js-disable
Disable snap.js
2020-04-07 12:07:47 +02:00
Julius Härtl
08284caedd Merge pull request #1652 from nextcloud/enh/attachmentProgressIndicator
Progress indicator for attachment uploads
2020-04-07 12:07:16 +02:00
Nextcloud bot
da3cc5b589 [tx-robot] updated from transifex 2020-04-07 02:29:29 +00:00
Julius Härtl
d0d2da490c Merge pull request #1662 from nextcloud/bugfix/noid/save-desc
Properly update description and add autosave delay
2020-04-06 14:28:23 +02:00
Julius Härtl
f6b4807ba0 Properly update description and add autosave delay
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-06 08:58:29 +02:00
Nextcloud bot
50391204c9 [tx-robot] updated from transifex 2020-04-06 02:29:42 +00:00
Julius Härtl
cf4b59ddcd Disable snap.js
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-04 22:49:07 +02:00
Julius Härtl
e22a72d836 Add CHANGELOG draft for 1.0.0
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-04 21:51:38 +02:00
Julius Härtl
a590e15e75 Bump version to 1.0.0-beta1
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-04 21:46:18 +02:00
Julius Härtl
396a5c395f Fix styling and run only two uploads in parallel
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-04 21:36:41 +02:00
Julius Härtl
22fb70f957 Add progress indicator and basic uplaod queue
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-04 21:24:15 +02:00
Jakob Röhrl
6cab67cb11 axios config
Signed-off-by: Jakob Röhrl <jakob.roehrl@web.de>
2020-04-04 21:01:36 +02:00
Julius Härtl
bb6790882a Let new shares only use the current users permissions
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2020-04-04 12:58:14 +02:00
43 changed files with 542 additions and 207 deletions

View File

@@ -1,6 +1,36 @@
# Changelog
All notable changes to this project will be documented in this file.
## 1.0.0 - unreleased
## Added
- Completly rewritten frontend
- Better maintainability
- Various small fixes
- Unified user interface with Nextcloud
- Separate comment and activity timelines
- Add ability to reply to comments #1537
- Filter cards on board #1507 @jakobroehrl
- Add cards to projects #1294 @jakobroehrl
- Move cards to other boards #1242 @jakobroehrl
- Clone boards with existing stacks and labels #1221 @jakobroehrl
- Upload multiple files at once and in parallel
A huge thangs goes to our awesome community that put enourmous effort into the frontend migration:
Special thanks for contributing huge parts of the Vue.js migration:
@jakobroehrl @weeman1337 @nicolad
Testers/reporters:
@cloud2018 @putt1ck @bpcurse
Calendar/Tasks integration help:
@raimund-schluessler @georgehrke
Android app team for helping to improve our REST API:
@desperateCoder @stefan-niedermann
## 0.8.0 - 2020-01-16
## Added

View File

@@ -17,7 +17,7 @@
- 🚀 Get your project organized
</description>
<version>1.0.0-alpha1</version>
<version>1.0.0-beta1</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>
@@ -36,7 +36,7 @@
<database min-version="9.4">pgsql</database>
<database>sqlite</database>
<database min-version="5.5">mysql</database>
<nextcloud min-version="17" max-version="19" />
<nextcloud min-version="18" max-version="19" />
</dependencies>
<background-jobs>
<job>OCA\Deck\Cron\DeleteCron</job>

View File

@@ -161,6 +161,8 @@ OC.L10N.register(
"Set a due date" : "Vybrat termín",
"Remove due date" : "Odstranit termín",
"Description" : "Popis",
"(Unsaved)" : "(Neuloženo)",
"(Saving…)" : "(Ukládání…)",
"Formatting help" : "Nápověda k formátování",
"Attachments" : "Přílohy",
"Comments" : "Komentáře",

View File

@@ -159,6 +159,8 @@
"Set a due date" : "Vybrat termín",
"Remove due date" : "Odstranit termín",
"Description" : "Popis",
"(Unsaved)" : "(Neuloženo)",
"(Saving…)" : "(Ukládání…)",
"Formatting help" : "Nápověda k formátování",
"Attachments" : "Přílohy",
"Comments" : "Komentáře",

View File

@@ -161,6 +161,8 @@ OC.L10N.register(
"Set a due date" : "Ein Ablaufdatum setzen",
"Remove due date" : "Fälligkeitsdatum löschen",
"Description" : "Beschreibung",
"(Unsaved)" : "(nicht gespeichert)",
"(Saving…)" : "(speichere...)",
"Formatting help" : "Formatierungshilfe",
"Attachments" : "Anhänge",
"Comments" : "Kommentare",

View File

@@ -159,6 +159,8 @@
"Set a due date" : "Ein Ablaufdatum setzen",
"Remove due date" : "Fälligkeitsdatum löschen",
"Description" : "Beschreibung",
"(Unsaved)" : "(nicht gespeichert)",
"(Saving…)" : "(speichere...)",
"Formatting help" : "Formatierungshilfe",
"Attachments" : "Anhänge",
"Comments" : "Kommentare",

View File

@@ -161,6 +161,8 @@ OC.L10N.register(
"Set a due date" : "Ein Ablaufdatum setzen",
"Remove due date" : "Fälligkeitsdatum entfernen",
"Description" : "Beschreibung",
"(Unsaved)" : "(nicht gespeichert)",
"(Saving…)" : "(speichere...)",
"Formatting help" : "Formatierungshilfe",
"Attachments" : "Anhänge",
"Comments" : "Kommentare",

View File

@@ -159,6 +159,8 @@
"Set a due date" : "Ein Ablaufdatum setzen",
"Remove due date" : "Fälligkeitsdatum entfernen",
"Description" : "Beschreibung",
"(Unsaved)" : "(nicht gespeichert)",
"(Saving…)" : "(speichere...)",
"Formatting help" : "Formatierungshilfe",
"Attachments" : "Anhänge",
"Comments" : "Kommentare",

View File

@@ -161,6 +161,8 @@ OC.L10N.register(
"Set a due date" : "Καθορίστε ημερομηνίας λήξης",
"Remove due date" : "Αφαίρεση ημερομηνίας λήξης",
"Description" : "Περιγραφή",
"(Unsaved)" : "(Δεν αποθηκεύτηκε)",
"(Saving…)" : "(Αποθήκευση...)",
"Formatting help" : "Βοήθεια μορφοποίησης",
"Attachments" : "Συνημμένα",
"Comments" : "Σχόλια",

View File

@@ -159,6 +159,8 @@
"Set a due date" : "Καθορίστε ημερομηνίας λήξης",
"Remove due date" : "Αφαίρεση ημερομηνίας λήξης",
"Description" : "Περιγραφή",
"(Unsaved)" : "(Δεν αποθηκεύτηκε)",
"(Saving…)" : "(Αποθήκευση...)",
"Formatting help" : "Βοήθεια μορφοποίησης",
"Attachments" : "Συνημμένα",
"Comments" : "Σχόλια",

View File

@@ -161,6 +161,8 @@ OC.L10N.register(
"Set a due date" : "Définir une date d'échéance",
"Remove due date" : "Supprimer la date d'échéance",
"Description" : "Description",
"(Unsaved)" : "(Non enregistré)",
"(Saving…)" : "(Enregistrement ...)",
"Formatting help" : "Aide sur la mise en forme",
"Attachments" : "Pièces jointes",
"Comments" : "Commentaires",

View File

@@ -159,6 +159,8 @@
"Set a due date" : "Définir une date d'échéance",
"Remove due date" : "Supprimer la date d'échéance",
"Description" : "Description",
"(Unsaved)" : "(Non enregistré)",
"(Saving…)" : "(Enregistrement ...)",
"Formatting help" : "Aide sur la mise en forme",
"Attachments" : "Pièces jointes",
"Comments" : "Commentaires",

View File

@@ -161,6 +161,8 @@ OC.L10N.register(
"Set a due date" : "Estabelecer a data de caducidade",
"Remove due date" : "Retirar a data de caducidade",
"Description" : "Descrición",
"(Unsaved)" : "(Sen gardar)",
"(Saving…)" : "(Gardando…)",
"Formatting help" : "Axuda de formatado",
"Attachments" : "Anexos",
"Comments" : "Comentarios",

View File

@@ -159,6 +159,8 @@
"Set a due date" : "Estabelecer a data de caducidade",
"Remove due date" : "Retirar a data de caducidade",
"Description" : "Descrición",
"(Unsaved)" : "(Sen gardar)",
"(Saving…)" : "(Gardando…)",
"Formatting help" : "Axuda de formatado",
"Attachments" : "Anexos",
"Comments" : "Comentarios",

View File

@@ -155,11 +155,14 @@ OC.L10N.register(
"Details" : "Dettagli",
"Assign a tag to this card…" : "Assegna un'etichetta a questa scheda…",
"Assign to users" : "Assegna a utenti",
"Assign to users/groups/circles" : "Assegna a utenti/gruppi/cerchie",
"Assign a user to this card…" : "Assegna un utente a questa scheda…",
"Due date" : "Data di scadenza",
"Set a due date" : "Imposta una data di scadenza",
"Remove due date" : "Rimuovi data di scadenza",
"Description" : "Descrizione",
"(Unsaved)" : "(Non salvata)",
"(Saving…)" : "(Salvataggio...)",
"Formatting help" : "Guida di formattazione",
"Attachments" : "Allegati",
"Comments" : "Commenti",
@@ -176,6 +179,7 @@ OC.L10N.register(
"Reply" : "Rispondi",
"Update" : "Aggiorna",
"(group)" : "(gruppo)",
"(circle)" : "(cerchia)",
"seconds ago" : "secondi fa",
"Assign to me" : "Assegna a me",
"Delete card" : "Elimina scheda",

View File

@@ -153,11 +153,14 @@
"Details" : "Dettagli",
"Assign a tag to this card…" : "Assegna un'etichetta a questa scheda…",
"Assign to users" : "Assegna a utenti",
"Assign to users/groups/circles" : "Assegna a utenti/gruppi/cerchie",
"Assign a user to this card…" : "Assegna un utente a questa scheda…",
"Due date" : "Data di scadenza",
"Set a due date" : "Imposta una data di scadenza",
"Remove due date" : "Rimuovi data di scadenza",
"Description" : "Descrizione",
"(Unsaved)" : "(Non salvata)",
"(Saving…)" : "(Salvataggio...)",
"Formatting help" : "Guida di formattazione",
"Attachments" : "Allegati",
"Comments" : "Commenti",
@@ -174,6 +177,7 @@
"Reply" : "Rispondi",
"Update" : "Aggiorna",
"(group)" : "(gruppo)",
"(circle)" : "(cerchia)",
"seconds ago" : "secondi fa",
"Assign to me" : "Assegna a me",
"Delete card" : "Elimina scheda",

View File

@@ -155,11 +155,14 @@ OC.L10N.register(
"Details" : "Szczegóły",
"Assign a tag to this card…" : "Przypisz etykietę do tej karty…",
"Assign to users" : "Przypisz do użytkowników",
"Assign to users/groups/circles" : "Przypisz do użytkowników/grup/kręgów",
"Assign a user to this card…" : "Przypisz użytkownika do tej karty…",
"Due date" : "Data realizacji",
"Set a due date" : "Ustaw termin",
"Remove due date" : "Usuń datę realizacji",
"Description" : "Opis",
"(Unsaved)" : "(Niezapisane)",
"(Saving…)" : "(Zapisywanie...)",
"Formatting help" : "Pomoc przy formatowaniu",
"Attachments" : "Załączniki",
"Comments" : "Komentarze",
@@ -176,6 +179,7 @@ OC.L10N.register(
"Reply" : "Odpowiedz",
"Update" : "Aktualizuj",
"(group)" : "(grupa)",
"(circle)" : "(krąg)",
"seconds ago" : "przed chwilą",
"Assign to me" : "Przydziel do mnie",
"Delete card" : "Usuń kartę",

View File

@@ -153,11 +153,14 @@
"Details" : "Szczegóły",
"Assign a tag to this card…" : "Przypisz etykietę do tej karty…",
"Assign to users" : "Przypisz do użytkowników",
"Assign to users/groups/circles" : "Przypisz do użytkowników/grup/kręgów",
"Assign a user to this card…" : "Przypisz użytkownika do tej karty…",
"Due date" : "Data realizacji",
"Set a due date" : "Ustaw termin",
"Remove due date" : "Usuń datę realizacji",
"Description" : "Opis",
"(Unsaved)" : "(Niezapisane)",
"(Saving…)" : "(Zapisywanie...)",
"Formatting help" : "Pomoc przy formatowaniu",
"Attachments" : "Załączniki",
"Comments" : "Komentarze",
@@ -174,6 +177,7 @@
"Reply" : "Odpowiedz",
"Update" : "Aktualizuj",
"(group)" : "(grupa)",
"(circle)" : "(krąg)",
"seconds ago" : "przed chwilą",
"Assign to me" : "Przydziel do mnie",
"Delete card" : "Usuń kartę",

View File

@@ -161,6 +161,8 @@ OC.L10N.register(
"Set a due date" : "Definir uma data de finalização",
"Remove due date" : "Remover data de vencimento",
"Description" : "Descrição",
"(Unsaved)" : "(Não salvo)",
"(Saving…)" : "(Salvando...)",
"Formatting help" : "Formatando ajuda",
"Attachments" : "Anexos",
"Comments" : "Comentários",

View File

@@ -159,6 +159,8 @@
"Set a due date" : "Definir uma data de finalização",
"Remove due date" : "Remover data de vencimento",
"Description" : "Descrição",
"(Unsaved)" : "(Não salvo)",
"(Saving…)" : "(Salvando...)",
"Formatting help" : "Formatando ajuda",
"Attachments" : "Anexos",
"Comments" : "Comentários",

View File

@@ -155,11 +155,14 @@ OC.L10N.register(
"Details" : "Подробнее",
"Assign a tag to this card…" : "Назначить метку этой карточке…",
"Assign to users" : "Назначить пользователям",
"Assign to users/groups/circles" : "Назначить пользователям / группам / кругам",
"Assign a user to this card…" : "Назначить пользователя этой карточке…",
"Due date" : "Срок исполнения",
"Set a due date" : "Задать дату исполнения",
"Remove due date" : "Удалить срок исполнения",
"Description" : "Описание",
"(Unsaved)" : "(Не сохранен)",
"(Saving…)" : "(Сохранение...)",
"Formatting help" : "Справка по форматированию",
"Attachments" : "Вложения",
"Comments" : "Комментарии",
@@ -176,6 +179,7 @@ OC.L10N.register(
"Reply" : "Ответить",
"Update" : "Обновить",
"(group)" : "(группа)",
"(circle)" : "(круг)",
"seconds ago" : "несколько секунд назад",
"Assign to me" : "Назначить себе",
"Delete card" : "Удалить",

View File

@@ -153,11 +153,14 @@
"Details" : "Подробнее",
"Assign a tag to this card…" : "Назначить метку этой карточке…",
"Assign to users" : "Назначить пользователям",
"Assign to users/groups/circles" : "Назначить пользователям / группам / кругам",
"Assign a user to this card…" : "Назначить пользователя этой карточке…",
"Due date" : "Срок исполнения",
"Set a due date" : "Задать дату исполнения",
"Remove due date" : "Удалить срок исполнения",
"Description" : "Описание",
"(Unsaved)" : "(Не сохранен)",
"(Saving…)" : "(Сохранение...)",
"Formatting help" : "Справка по форматированию",
"Attachments" : "Вложения",
"Comments" : "Комментарии",
@@ -174,6 +177,7 @@
"Reply" : "Ответить",
"Update" : "Обновить",
"(group)" : "(группа)",
"(circle)" : "(круг)",
"seconds ago" : "несколько секунд назад",
"Assign to me" : "Назначить себе",
"Delete card" : "Удалить",

View File

@@ -68,6 +68,8 @@ OC.L10N.register(
"Changes in the <strong>Deck app</strong>" : "Spremembe v programu <strong>Deck</strong>",
"A <strong>comment</strong> was created on a card" : "Nalogi je dodana nova <strong>opomba</strong>",
"Personal" : "Osebno",
"{user} has assigned the card \"%s\" on \"%s\" to you." : "{user} vam dodeli nalogo »%s« na »%s«.",
"The card \"%s\" on \"%s\" has reached its due date." : "Naloga »%s« na »%s« je dosegla datum preteka.",
"%s has mentioned you in a comment on \"%s\"." : "%s vas omeni v opombi na »%s«.",
"{user} has mentioned you in a comment on \"%s\"." : "{user} vas omeni v opombi na »%s«.",
"{user} has shared the board %s with you." : "{user} vam omogoči souporabo zbirke %s.",
@@ -103,9 +105,12 @@ OC.L10N.register(
"Link to card" : "Poveži nalogo",
"Cancel" : "Prekliči",
"File already exists" : "Datoteka s tem imenom že obstaja.",
"A file with the name {filename} already exists." : "Datoteka z imenom {filename} že obstaja.",
"Do you want to overwrite it?" : "Ali želite predmet prepisati?",
"Overwrite file" : "Prepiši datoteko",
"Keep existing file" : "Ohrani obstoječe datoteke",
"This board is read only" : "Zbirka je označena le za branje",
"Drop your files to upload" : "Spustite datoteke za pošiljanje v oblak",
"Add new list" : "Dodaj nov seznam",
"List name" : "Ime seznama",
"Apply filter" : "Uveljavi filter",
@@ -146,11 +151,14 @@ OC.L10N.register(
"Details" : "Podrobnosti",
"Assign a tag to this card…" : "Dodeli oznako nalogi ...",
"Assign to users" : "Dodeli uporabnikom",
"Assign to users/groups/circles" : "Dodeli uporabnikom/skupinam/krogom",
"Assign a user to this card…" : "Dodeli uporabnika k nalogi ...",
"Due date" : "Datum preteka",
"Set a due date" : "Nastavi datum preteka",
"Remove due date" : "Odstrani datum preteka",
"Description" : "Opis",
"(Unsaved)" : "(neshranjeno)",
"(Saving…)" : "(shranjevanje ...)",
"Formatting help" : "Pomoč pri oblikovanju",
"Attachments" : "Priloge",
"Comments" : "Opombe",
@@ -163,9 +171,11 @@ OC.L10N.register(
"Save" : "Shrani",
"The comment cannot be empty." : "Polje opombe ne sme biti prazno.",
"The comment cannot be longer than 1000 characters." : "Opomba ne sme biti daljša od 1000 znakov.",
"In reply to" : "V odgovor",
"Reply" : "Odgovori",
"Update" : "Posodobi",
"(group)" : "(skupina)",
"(circle)" : "(krog)",
"seconds ago" : "pred nekaj sekundami",
"Assign to me" : "Nalogo dodeli meni",
"Delete card" : "Izbriši nalogo",

View File

@@ -66,6 +66,8 @@
"Changes in the <strong>Deck app</strong>" : "Spremembe v programu <strong>Deck</strong>",
"A <strong>comment</strong> was created on a card" : "Nalogi je dodana nova <strong>opomba</strong>",
"Personal" : "Osebno",
"{user} has assigned the card \"%s\" on \"%s\" to you." : "{user} vam dodeli nalogo »%s« na »%s«.",
"The card \"%s\" on \"%s\" has reached its due date." : "Naloga »%s« na »%s« je dosegla datum preteka.",
"%s has mentioned you in a comment on \"%s\"." : "%s vas omeni v opombi na »%s«.",
"{user} has mentioned you in a comment on \"%s\"." : "{user} vas omeni v opombi na »%s«.",
"{user} has shared the board %s with you." : "{user} vam omogoči souporabo zbirke %s.",
@@ -101,9 +103,12 @@
"Link to card" : "Poveži nalogo",
"Cancel" : "Prekliči",
"File already exists" : "Datoteka s tem imenom že obstaja.",
"A file with the name {filename} already exists." : "Datoteka z imenom {filename} že obstaja.",
"Do you want to overwrite it?" : "Ali želite predmet prepisati?",
"Overwrite file" : "Prepiši datoteko",
"Keep existing file" : "Ohrani obstoječe datoteke",
"This board is read only" : "Zbirka je označena le za branje",
"Drop your files to upload" : "Spustite datoteke za pošiljanje v oblak",
"Add new list" : "Dodaj nov seznam",
"List name" : "Ime seznama",
"Apply filter" : "Uveljavi filter",
@@ -144,11 +149,14 @@
"Details" : "Podrobnosti",
"Assign a tag to this card…" : "Dodeli oznako nalogi ...",
"Assign to users" : "Dodeli uporabnikom",
"Assign to users/groups/circles" : "Dodeli uporabnikom/skupinam/krogom",
"Assign a user to this card…" : "Dodeli uporabnika k nalogi ...",
"Due date" : "Datum preteka",
"Set a due date" : "Nastavi datum preteka",
"Remove due date" : "Odstrani datum preteka",
"Description" : "Opis",
"(Unsaved)" : "(neshranjeno)",
"(Saving…)" : "(shranjevanje ...)",
"Formatting help" : "Pomoč pri oblikovanju",
"Attachments" : "Priloge",
"Comments" : "Opombe",
@@ -161,9 +169,11 @@
"Save" : "Shrani",
"The comment cannot be empty." : "Polje opombe ne sme biti prazno.",
"The comment cannot be longer than 1000 characters." : "Opomba ne sme biti daljša od 1000 znakov.",
"In reply to" : "V odgovor",
"Reply" : "Odgovori",
"Update" : "Posodobi",
"(group)" : "(skupina)",
"(circle)" : "(krog)",
"seconds ago" : "pred nekaj sekundami",
"Assign to me" : "Nalogo dodeli meni",
"Delete card" : "Izbriši nalogo",

View File

@@ -154,6 +154,7 @@ OC.L10N.register(
"Details" : "Detaljer",
"Assign a tag to this card…" : "Tilldela en tagg till det här kortet ...",
"Assign to users" : "Tilldela till användare",
"Assign to users/groups/circles" : "Tilldela till användare/grupper/cirklar",
"Assign a user to this card…" : "Tilldela en användare till det här kortet ...",
"Due date" : "Slutdatum",
"Set a due date" : "Sätt ett slutdatum",
@@ -175,6 +176,7 @@ OC.L10N.register(
"Reply" : "Svara",
"Update" : "Uppdatera",
"(group)" : " (grupp)",
"(circle)" : "(cirkel)",
"seconds ago" : "sekunder sedan",
"Assign to me" : "Tilldela till mig",
"Delete card" : "Ta bort kort",

View File

@@ -152,6 +152,7 @@
"Details" : "Detaljer",
"Assign a tag to this card…" : "Tilldela en tagg till det här kortet ...",
"Assign to users" : "Tilldela till användare",
"Assign to users/groups/circles" : "Tilldela till användare/grupper/cirklar",
"Assign a user to this card…" : "Tilldela en användare till det här kortet ...",
"Due date" : "Slutdatum",
"Set a due date" : "Sätt ett slutdatum",
@@ -173,6 +174,7 @@
"Reply" : "Svara",
"Update" : "Uppdatera",
"(group)" : " (grupp)",
"(circle)" : "(cirkel)",
"seconds ago" : "sekunder sedan",
"Assign to me" : "Tilldela till mig",
"Delete card" : "Ta bort kort",

View File

@@ -155,11 +155,14 @@ OC.L10N.register(
"Details" : "Ayrıntılar",
"Assign a tag to this card…" : "Bu karta bir etiket ata…",
"Assign to users" : "Kullanıcılara ata",
"Assign to users/groups/circles" : "Kullanıcılara/gruplara/çevrelere ata",
"Assign a user to this card…" : "Bu karta bir kullanıcı ata…",
"Due date" : "Bitiş tarihi",
"Set a due date" : "Bitiş tarihi ayarla",
"Remove due date" : "Bitiş tarihini kaldır",
"Description" : "Açıklama",
"(Unsaved)" : "(Kaydedilmemiş)",
"(Saving…)" : "(Kaydediliyor…)",
"Formatting help" : "Biçimlendirme yardımı",
"Attachments" : "Ek dosyalar",
"Comments" : "Açıklamalar",
@@ -176,6 +179,7 @@ OC.L10N.register(
"Reply" : "Yanıtla",
"Update" : "Güncelle",
"(group)" : "(grup)",
"(circle)" : "(çevre)",
"seconds ago" : "saniye önce",
"Assign to me" : "Bana ata",
"Delete card" : "Kartı sil",

View File

@@ -153,11 +153,14 @@
"Details" : "Ayrıntılar",
"Assign a tag to this card…" : "Bu karta bir etiket ata…",
"Assign to users" : "Kullanıcılara ata",
"Assign to users/groups/circles" : "Kullanıcılara/gruplara/çevrelere ata",
"Assign a user to this card…" : "Bu karta bir kullanıcı ata…",
"Due date" : "Bitiş tarihi",
"Set a due date" : "Bitiş tarihi ayarla",
"Remove due date" : "Bitiş tarihini kaldır",
"Description" : "Açıklama",
"(Unsaved)" : "(Kaydedilmemiş)",
"(Saving…)" : "(Kaydediliyor…)",
"Formatting help" : "Biçimlendirme yardımı",
"Attachments" : "Ek dosyalar",
"Comments" : "Açıklamalar",
@@ -174,6 +177,7 @@
"Reply" : "Yanıtla",
"Update" : "Güncelle",
"(group)" : "(grup)",
"(circle)" : "(çevre)",
"seconds ago" : "saniye önce",
"Assign to me" : "Bana ata",
"Delete card" : "Kartı sil",

View File

@@ -455,6 +455,17 @@ class BoardService {
return $board;
}
private function applyPermissions($boardId, $edit, $share, $manage) {
try {
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_MANAGE);
} catch (NoPermissionException $e) {
$acls = $this->aclMapper->findAll($boardId);
$edit = $this->permissionService->userCan($acls, Acl::PERMISSION_EDIT, $this->userId) && $edit;
$share = $this->permissionService->userCan($acls, Acl::PERMISSION_SHARE, $this->userId) && $share;
$manage = $this->permissionService->userCan($acls, Acl::PERMISSION_MANAGE, $this->userId) && $manage;
}
return [$edit, $share, $manage];
}
/**
* @param $boardId
@@ -494,6 +505,8 @@ class BoardService {
}
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_SHARE);
[$edit, $share, $manage] = $this->applyPermissions($boardId, $edit, $share, $manage);
$acl = new Acl();
$acl->setBoardId($boardId);
$acl->setType($type);
@@ -556,8 +569,10 @@ class BoardService {
}
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
/** @var Acl $acl */
$acl = $this->aclMapper->find($id);
[$edit, $share, $manage] = $this->applyPermissions($acl->getBoardId(), $edit, $share, $manage);
$acl->setPermissionEdit($edit);
$acl->setPermissionShare($share);
$acl->setPermissionManage($manage);

25
package-lock.json generated
View File

@@ -7049,6 +7049,11 @@
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs="
},
"eventemitter3": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.0.tgz",
"integrity": "sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg=="
},
"events": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
@@ -12778,8 +12783,7 @@
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=",
"dev": true
"integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4="
},
"p-is-promise": {
"version": "2.1.0",
@@ -12805,6 +12809,23 @@
"p-limit": "^1.1.0"
}
},
"p-queue": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.3.0.tgz",
"integrity": "sha512-fg5dJlFpd5+3CgG3/0ogpVZUeJbjiyXFg0nu53hrOYsybqSiDyxyOpad0Rm6tAiGjgztAwkyvhlYHC53OiAJOA==",
"requires": {
"eventemitter3": "^4.0.0",
"p-timeout": "^3.1.0"
}
},
"p-timeout": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
"integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
"requires": {
"p-finally": "^1.0.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",

View File

@@ -40,7 +40,9 @@
"@nextcloud/vue": "^1.4.1",
"blueimp-md5": "^2.13.0",
"dompurify": "^2.0.8",
"moment": "^2.24.0",
"nextcloud-vue-collections": "^0.7.2",
"p-queue": "^6.3.0",
"url-search-params-polyfill": "^8.0.0",
"vue": "^2.6.11",
"vue-at": "^2.5.0-beta.2",

View File

@@ -102,18 +102,15 @@ export default {
},
},
methods: {
dragEnter() {
},
dragLeave() {
},
handleDropFiles(event) {
this.isDraggingOver = false
if (this.isReadOnly) {
return
}
this.onLocalAttachmentSelected(event.dataTransfer.files[0])
const files = event.dataTransfer.files
for (const file of files) {
this.onLocalAttachmentSelected(file)
}
event.dataTransfer.value = ''
},
},

View File

@@ -77,7 +77,7 @@
:drag-handle-selector="dragHandleSelector"
@should-accept-drop="canEdit"
@drop="($event) => onDropCard(stack.id, $event)">
<Draggable v-for="card in cardsByStack(stack.id)" :key="card.id">
<Draggable v-for="card in cardsByStack" :key="card.id">
<CardItem v-if="card" :id="card.id" />
</Draggable>
</Container>
@@ -122,7 +122,7 @@ export default {
'canEdit',
]),
cardsByStack() {
return (id) => this.$store.getters.cardsByStack(id)
return this.$store.getters.cardsByStack(this.stack.id)
},
dragHandleSelector() {
return this.canEdit ? null : '.no-drag'
@@ -150,7 +150,7 @@ export default {
},
payloadForCard(stackId) {
return index => {
return this.cardsByStack(stackId)[index]
return this.cardsByStack[index]
}
},
deleteStack(stack) {

View File

@@ -121,13 +121,18 @@
<h5>
{{ t('deck', 'Description') }}
<span v-if="copiedCard.descriptionLastEdit && !descriptionSaving">{{ t('deck', '(Unsaved)') }}</span>
<span v-if="descriptionSaving">{{ t('deck', '(Saving)') }}</span>
<a v-tooltip="t('deck', 'Formatting help')"
href="https://deck.readthedocs.io/en/latest/Markdown/"
target="_blank"
class="icon icon-info" />
</h5>
<!-- FIXME: make sure the editor is disabled when canEdit is false -->
<VueEasymde ref="markdownEditor" v-model="copiedCard.description" :configs="mdeConfig" />
<VueEasymde ref="markdownEditor"
:value="copiedCard.description"
:configs="mdeConfig"
@input="updateDescription" />
</AppSidebarTab>
<AppSidebarTab id="attachments"
@@ -158,7 +163,6 @@
<script>
import { Avatar, Actions, ActionButton, Multiselect, AppSidebar, AppSidebarTab, DatetimePicker } from '@nextcloud/vue'
import { mapState, mapGetters } from 'vuex'
import VueEasymde from 'vue-easymde/dist/VueEasyMDE.common'
import Color from '../../mixins/color'
import { CollectionList } from 'nextcloud-vue-collections'
import CardSidebarTabAttachments from './CardSidebarTabAttachments'
@@ -174,7 +178,7 @@ export default {
AppSidebarTab,
Multiselect,
DatetimePicker,
VueEasymde,
VueEasymde: () => import('vue-easymde/dist/VueEasyMDE.common'),
Actions,
ActionButton,
Avatar,
@@ -209,6 +213,8 @@ export default {
},
lastModifiedRelative: null,
lastCreatedRemative: null,
descriptionSaveTimeout: null,
descriptionSaving: false,
hasActivity: capabilities && capabilities.activity,
hasComments: window.OCP && window.OCP.Comments,
}
@@ -270,7 +276,7 @@ export default {
this.copiedCard = JSON.parse(JSON.stringify(this.currentCard))
this.allLabels = this.currentCard.labels
if (this.currentCard.assignedUsers.length > 0) {
if (this.currentCard.assignedUsers && this.currentCard.assignedUsers.length > 0) {
this.assignedUsers = this.currentCard.assignedUsers.map((item) => item.participant)
} else {
this.assignedUsers = []
@@ -299,8 +305,20 @@ export default {
this.copiedCard.duedate = null
this.$store.dispatch('updateCardDue', this.copiedCard)
},
saveDesc() {
this.$store.dispatch('updateCardDesc', this.copiedCard)
updateDescription(text) {
this.copiedCard.description = text
this.copiedCard.descriptionLastEdit = Date.now()
clearTimeout(this.descriptionSaveTimeout)
this.descriptionSaveTimeout = setTimeout(async() => {
if (!Object.prototype.hasOwnProperty.call(this.copiedCard, 'descriptionLastEdit') || this.descriptionSaving) {
return
}
this.descriptionSaving = true
await this.$store.dispatch('updateCardDesc', this.copiedCard)
delete this.copiedCard.descriptionLastEdit
this.descriptionSaving = false
}, 2500)
},
closeSidebar() {

View File

@@ -28,13 +28,24 @@
<input ref="localAttachments"
type="file"
style="display: none;"
multiple
@change="handleUploadFile">
<div class="attachment-list">
<ul>
<li v-for="attachment in uploadQueue" :key="attachment.name" class="attachment">
<a class="fileicon" :style="mimetypeForAttachment('none')" />
<div class="details">
<a>
<div class="filename">
<span class="basename">{{ attachment.name }}</span>
</div>
<progress :value="attachment.progress" max="100" />
</a>
</div>
</li>
<li v-for="attachment in attachments"
:key="attachment.id"
class="attachment"
style="display: flex;">
class="attachment">
<a class="fileicon" :style="mimetypeForAttachment(attachment.extendedData.mimetype)" :href="attachmentUrl(attachment)" />
<div class="details">
<a :href="attachmentUrl(attachment)" target="_blank">
@@ -109,7 +120,7 @@ export default {
}
},
attachments() {
return this.$store.getters.attachmentsByCard(this.card.id)
return [...this.$store.getters.attachmentsByCard(this.card.id)].sort((a, b) => b.id - a.id)
},
formattedFileSize() {
return (filesize) => formatFileSize(filesize)
@@ -134,22 +145,11 @@ export default {
this.$store.dispatch('fetchAttachments', this.card.id)
},
methods: {
dragEnter() {
},
dragLeave() {
},
handleDropFiles(event) {
this.isDraggingOver = false
if (this.isReadOnly) {
return
}
this.onLocalAttachmentSelected(event.dataTransfer.files[0])
event.dataTransfer.value = ''
},
handleUploadFile(event) {
this.onLocalAttachmentSelected(event.target.files[0])
const files = event.target.files ?? []
for (const file of files) {
this.onLocalAttachmentSelected(file)
}
event.target.value = ''
},
clickAddNewAttachmment() {
@@ -214,6 +214,7 @@ export default {
li.attachment {
display: flex;
padding: 3px;
min-height: 44px;
&.deleted {
opacity: .5;

View File

@@ -22,16 +22,10 @@
<template>
<div class="badges">
<div v-if="card.description" class="icon icon-edit" />
<div v-if="card.commentsUnread > 0" class="icon icon-comment" />
<div v-if="card.duedate" :class="dueIcon">
<span>{{ relativeDate }}</span>
</div>
<div v-if="card.description && checkListCount > 0" class="card-tasks icon icon-checkmark">
<span>{{ checkListCheckedCount }}/{{ checkListCount }}</span>
{{ checkListCheckedCount }}/{{ checkListCount }}
</div>
<div v-if="card.attachmentCount > 0" class="icon-attach icon icon-attach-dark">
@@ -39,22 +33,79 @@
</div>
<AvatarList :users="card.assignedUsers" />
<div @click.stop.prevent>
<Actions v-if="canEdit">
<ActionButton v-if="showArchived === false" icon="icon-user" @click="assignCardToMe()">
{{ t('deck', 'Assign to me') }}
</ActionButton>
<ActionButton icon="icon-archive" @click="archiveUnarchiveCard()">
{{ t('deck', (showArchived ? 'Unarchive card' : 'Archive card')) }}
</ActionButton>
<ActionButton v-if="showArchived === false" icon="icon-delete" @click="deleteCard()">
{{ t('deck', 'Delete card') }}
</ActionButton>
<ActionButton icon="icon-external" @click.stop="modalShow=true">
{{ t('deck', 'Move card') }}
</ActionButton>
<ActionButton icon="icon-settings-dark" @click="openCard">
{{ t('deck', 'Card details') }}
</ActionButton>
</Actions>
</div>
<Modal v-if="modalShow" title="Move card to another board" @close="modalShow=false">
<div class="modal__content">
<Multiselect v-model="selectedBoard"
:placeholder="t('deck', 'Select a board')"
:options="boards"
label="title"
@select="loadStacksFromBoard" />
<Multiselect v-model="selectedStack"
:placeholder="t('deck', 'Select a stack')"
:options="stacksFromBoard"
label="title" />
<button :disabled="!isBoardAndStackChoosen" class="primary" @click="moveCard">
{{ t('deck', 'Move card') }}
</button>
<button @click="modalShow=false">
{{ t('deck', 'Cancel') }}
</button>
</div>
</Modal>
</div>
</template>
<script>
import AvatarList from './AvatarList'
import moment from '@nextcloud/moment'
import { Modal, Actions, ActionButton, Multiselect } from '@nextcloud/vue'
import { mapGetters, mapState } from 'vuex'
import axios from '@nextcloud/axios'
export default {
name: 'CardBadges',
components: { AvatarList },
components: { AvatarList, Actions, ActionButton, Modal, Multiselect },
props: {
id: {
type: Number,
default: null,
},
},
data() {
return {
modalShow: false,
selectedBoard: '',
selectedStack: '',
stacksFromBoard: [],
}
},
computed: {
...mapGetters([
'canEdit',
]),
...mapState({
showArchived: state => state.showArchived,
currentBoard: state => state.currentBoard,
}),
checkListCount() {
return (this.card.description.match(/\[\s*\x*\]/g) || []).length
},
@@ -67,28 +118,53 @@ export default {
card() {
return this.$store.getters.cardById(this.id)
},
dueDateTooltip() {
return moment(this.card.duedate).format('LLLL')
isBoardAndStackChoosen() {
if (this.selectedBoard === '' || this.selectedStack === '') {
return false
}
return true
},
relativeDate() {
const diff = moment(this.$root.time).diff(this.card.duedate, 'seconds')
if (diff >= 0 && diff < 45) {
return t('core', 'seconds ago')
}
return moment(this.card.duedate).fromNow()
boards() {
return this.$store.getters.boards.filter(board => {
return board.id !== this.currentBoard.id
})
},
dueIcon() {
const days = Math.floor(moment(this.card.duedate).diff(this.$root.time, 'seconds') / 60 / 60 / 24)
if (days < 0) {
return 'icon-calendar due icon overdue'
},
methods: {
openCard() {
this.$router.push({ name: 'card', params: { cardId: this.id } })
},
deleteCard() {
this.$store.dispatch('deleteCard', this.card)
},
archiveUnarchiveCard() {
this.$store.dispatch('archiveUnarchiveCard', { ...this.card, archived: !this.card.archived })
},
assignCardToMe() {
this.copiedCard = Object.assign({}, this.card)
this.$store.dispatch('assignCardToUser', {
card: this.copiedCard,
assignee: {
userId: OC.getCurrentUser().uid,
type: 0,
},
})
},
moveCard() {
this.copiedCard = Object.assign({}, this.card)
this.copiedCard.stackId = this.selectedStack.id
this.$store.dispatch('moveCard', this.copiedCard)
this.modalShow = false
},
async loadStacksFromBoard(board) {
try {
console.debug(board)
const url = OC.generateUrl('/apps/deck/stacks/' + board.id)
const response = await axios.get(url)
this.stacksFromBoard = response.data
} catch (err) {
return err
}
if (days === 0) {
return 'icon-calendar-dark due icon now'
}
if (days === 1) {
return 'icon-calendar-dark due icon next'
}
return 'icon-calendar-dark due icon'
},
},
}
@@ -102,7 +178,7 @@ export default {
.icon {
opacity: 0.5;
padding: 12px 14px;
padding: 12px 18px;
padding-right: 4px;
margin-right: 5px;
background-position: left;
@@ -153,4 +229,27 @@ export default {
overflow: hidden;
}
}
.fade-enter-active, .fade-leave-active {
transition: opacity .125s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
.modal__content {
width: 25vw;
min-width: 250px;
height: 120px;
text-align: center;
margin: 20px 20px 60px 20px;
.multiselect {
margin-bottom: 10px;
}
}
.modal__content button {
float: right;
}
</style>

View File

@@ -47,44 +47,11 @@
<input type="button" class="icon-confirm" @click="finishedEdit(card)">
</form>
<Actions v-if="canEdit && !editing" @click.stop.prevent>
<ActionButton v-if="showArchived === false" icon="icon-user" @click="assignCardToMe()">
{{ t('deck', 'Assign to me') }}
</ActionButton>
<ActionButton icon="icon-archive" @click="archiveUnarchiveCard()">
{{ t('deck', (showArchived ? 'Unarchive card' : 'Archive card')) }}
</ActionButton>
<ActionButton v-if="showArchived === false" icon="icon-delete" @click="deleteCard()">
{{ t('deck', 'Delete card') }}
</ActionButton>
<ActionButton icon="icon-external" @click.stop="modalShow=true">
{{ t('deck', 'Move card') }}
</ActionButton>
<ActionButton icon="icon-settings-dark" @click="openCard">
{{ t('deck', 'Card details') }}
</ActionButton>
</Actions>
<Modal v-if="modalShow" title="Move card to another board" @close="modalShow=false">
<div class="modal__content">
<Multiselect v-model="selectedBoard"
:placeholder="t('deck', 'Select a board')"
:options="boards"
label="title"
@select="loadStacksFromBoard" />
<Multiselect v-model="selectedStack"
:placeholder="t('deck', 'Select a stack')"
:options="stacksFromBoard"
label="title" />
<button :disabled="!isBoardAndStackChoosen" class="primary" @click="moveCard">
{{ t('deck', 'Move card') }}
</button>
<button @click="modalShow=false">
{{ t('deck', 'Cancel') }}
</button>
<div v-if="!editing" class="right">
<div v-if="card.duedate" :class="dueIcon">
<span>{{ relativeDate }}</span>
</div>
</Modal>
</div>
</div>
<ul v-if="card.labels && card.labels.length > 0" class="labels" @click="openCard">
<li v-for="label in card.labels" :key="label.id" :style="labelStyle(label)">
@@ -99,11 +66,9 @@
</template>
<script>
import { Modal, Actions, ActionButton, Multiselect } from '@nextcloud/vue'
import ClickOutside from 'vue-click-outside'
import { mapState, mapGetters } from 'vuex'
import axios from '@nextcloud/axios'
import moment from 'moment'
import CardBadges from './CardBadges'
import Color from '../../mixins/color'
import labelStyle from '../../mixins/labelStyle'
@@ -111,7 +76,7 @@ import AttachmentDragAndDrop from '../AttachmentDragAndDrop'
export default {
name: 'CardItem',
components: { Modal, CardBadges, Actions, ActionButton, Multiselect, AttachmentDragAndDrop },
components: { CardBadges, AttachmentDragAndDrop },
directives: {
ClickOutside,
},
@@ -124,13 +89,8 @@ export default {
},
data() {
return {
menuOpened: false,
editing: false,
copiedCard: '',
modalShow: false,
selectedBoard: '',
selectedStack: '',
stacksFromBoard: [],
copiedCard: null,
}
},
computed: {
@@ -145,34 +105,37 @@ export default {
card() {
return this.$store.getters.cardById(this.id)
},
boards() {
return this.$store.getters.boards.filter(board => {
return board.id !== this.currentBoard.id
})
},
menu() {
return []
},
currentCard() {
return this.$route.params.cardId === this.id
},
isBoardAndStackChoosen() {
if (this.selectedBoard === '' || this.selectedStack === '') {
return false
relativeDate() {
const diff = moment(this.$root.time).diff(this.card.duedate, 'seconds')
if (diff >= 0 && diff < 45) {
return t('core', 'seconds ago')
}
return true
return moment(this.card.duedate).fromNow()
},
dueIcon() {
const days = Math.floor(moment(this.card.duedate).diff(this.$root.time, 'seconds') / 60 / 60 / 24)
if (days < 0) {
return 'icon-calendar due icon overdue'
}
if (days === 0) {
return 'icon-calendar-dark due icon now'
}
if (days === 1) {
return 'icon-calendar-dark due icon next'
}
return 'icon-calendar-dark due icon'
},
dueDateTooltip() {
return moment(this.card.duedate).format('LLLL')
},
},
methods: {
openCard() {
this.$router.push({ name: 'card', params: { cardId: this.id } })
},
togglePopoverMenu() {
this.menuOpened = !this.menuOpened
},
hidePopoverMenu() {
this.menuOpened = false
},
startEditing(card) {
this.copiedCard = Object.assign({}, card)
this.editing = true
@@ -186,39 +149,6 @@ export default {
cancelEdit() {
this.editing = false
},
deleteCard() {
this.$store.dispatch('deleteCard', this.card)
},
archiveUnarchiveCard() {
this.copiedCard = Object.assign({}, this.card)
this.copiedCard.archived = !this.copiedCard.archived
this.$store.dispatch('archiveUnarchiveCard', this.copiedCard)
},
assignCardToMe() {
this.copiedCard = Object.assign({}, this.card)
this.$store.dispatch('assignCardToUser', {
card: this.copiedCard,
assignee: {
userId: OC.getCurrentUser().uid,
type: 0,
},
})
},
async loadStacksFromBoard(board) {
try {
const url = OC.generateUrl('/apps/deck/stacks/' + board.id)
const response = await axios.get(url)
this.stacksFromBoard = response.data
} catch (err) {
return err
}
},
moveCard() {
this.copiedCard = Object.assign({}, this.card)
this.copiedCard.stackId = this.selectedStack.id
this.$store.dispatch('moveCard', this.copiedCard)
this.modalShow = false
},
},
}
</script>
@@ -227,13 +157,6 @@ export default {
$card-spacing: 10px;
$card-padding: 10px;
.fade-enter-active, .fade-leave-active {
transition: opacity .125s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
body.dark .card {
border: 1px solid var(--color-border);
}
@@ -290,8 +213,8 @@ export default {
display: flex;
flex-direction: row;
overflow: hidden;
padding: 1px 3px;
border-radius: 3px;
padding: 3px 7px;
border-radius: 15px;
font-size: 85%;
margin-right: 3px;
margin-bottom: 3px;
@@ -314,13 +237,56 @@ export default {
.card-controls {
display: flex;
margin-left: $card-padding;
margin-right: $card-padding;
& > div {
display: flex;
max-height: 44px;
}
}
}
.right {
display: flex;
align-items: flex-start;
margin-right: 9px;
}
.icon.due {
background-position: 4px center;
border-radius: 3px;
margin-top: 9px;
margin-bottom: 9px;
padding: 3px 4px;
font-size: 90%;
display: flex;
align-items: center;
opacity: .5;
flex-shrink: 1;
z-index: 2;
.icon {
background-size: contain;
}
&.overdue {
background-color: var(--color-error);
color: var(--color-primary-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;
}
}
.compact {
min-height: 50px;
@@ -340,20 +306,4 @@ export default {
color: transparent;
}
}
.modal__content {
width: 25vw;
min-width: 250px;
height: 120px;
text-align: center;
margin: 20px 20px 60px 20px;
.multiselect {
margin-bottom: 10px;
}
}
.modal__content button {
float: right;
}
</style>

View File

@@ -30,6 +30,9 @@ import { Tooltip } from '@nextcloud/vue'
import ClickOutside from 'vue-click-outside'
import './models'
// the server snap.js conflicts with vertical scrolling so we disable it
document.body.setAttribute('data-snap-ignore', 'true')
// eslint-disable-next-line
__webpack_nonce__ = btoa(OC.requestToken)
// eslint-disable-next-line

View File

@@ -21,8 +21,16 @@
*/
import { showError } from '@nextcloud/dialogs'
import { formatFileSize } from '@nextcloud/files'
import PQueue from 'p-queue'
const queue = new PQueue({ concurrency: 2 })
export default {
data() {
return {
uploadQueue: {},
}
},
methods: {
async onLocalAttachmentSelected(file) {
if (this.maxUploadSize > 0 && file.size > this.maxUploadSize) {
@@ -34,20 +42,32 @@ export default {
return
}
this.$set(this.uploadQueue, file.name, { name: file.name, progress: 0 })
const bodyFormData = new FormData()
bodyFormData.append('cardId', this.cardId)
bodyFormData.append('type', 'deck_file')
bodyFormData.append('file', file)
try {
await this.$store.dispatch('createAttachment', { cardId: this.cardId, formData: bodyFormData })
} catch (err) {
if (err.response.data.status === 409) {
this.overwriteAttachment = err.response.data.data
this.modalShow = true
} else {
showError(err.response.data.message)
await queue.add(async() => {
try {
await this.$store.dispatch('createAttachment', { cardId: this.cardId,
formData: bodyFormData,
onUploadProgress: (e) => {
const percentCompleted = Math.round((e.loaded * 100) / e.total)
console.debug(percentCompleted)
this.$set(this.uploadQueue[file.name], 'progress', percentCompleted)
},
})
} catch (err) {
if (err.response.data.status === 409) {
this.overwriteAttachment = err.response.data.data
this.modalShow = true
} else {
showError(err.response.data.message)
}
}
}
this.$delete(this.uploadQueue, file.name)
})
},
overrideAttachment() {

View File

@@ -37,11 +37,12 @@ export class AttachmentApi {
return response.data
}
async createAttachment({ cardId, formData }) {
async createAttachment({ cardId, formData, onUploadProgress }) {
const response = await axios({
method: 'POST',
url: this.url(`/cards/${cardId}/attachment`),
data: formData,
onUploadProgress,
})
return response.data
}

View File

@@ -83,8 +83,8 @@ export default {
commit('createAttachments', { cardId, attachments })
},
async createAttachment({ commit }, { cardId, formData }) {
const attachment = await apiClient.createAttachment({ cardId, formData })
async createAttachment({ commit }, { cardId, formData, onUploadProgress }) {
const attachment = await apiClient.createAttachment({ cardId, formData, onUploadProgress })
commit('createAttachment', { cardId, attachment })
commit('cardIncreaseAttachmentCount', cardId)
},

View File

@@ -76,10 +76,16 @@ export default {
return true
})
.filter((card) => card.stackId === id && (getters.getSearchQuery === ''
|| (card.title.toLowerCase().includes(getters.getSearchQuery.toLowerCase())
|| card.description.toLowerCase().includes(getters.getSearchQuery.toLowerCase()))
.sort((a, b) => a.order - b.order)))
.filter((card) => card.stackId === id)
.filter((card) => {
if (getters.getSearchQuery === '') {
return true
}
return card.title.toLowerCase().includes(getters.getSearchQuery.toLowerCase())
|| card.description.toLowerCase().includes(getters.getSearchQuery.toLowerCase())
})
.sort((a, b) => a.order - b.order)
},
cardById: state => (id) => {
return state.cards.find((card) => card.id === id)

View File

@@ -34,6 +34,7 @@ use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\ChangeHelper;
use OCA\Deck\Db\LabelMapper;
use OCA\Deck\Db\StackMapper;
use OCA\Deck\NoPermissionException;
use OCA\Deck\Notification\NotificationHelper;
use OCP\IUser;
use OCP\IUserManager;
@@ -260,6 +261,93 @@ class BoardServiceTest extends TestCase {
));
}
public function dataAddAclExtendPermission() {
return [
[[false, false, false], [false, false, false], [false, false, false]],
[[false, false, false], [true, true, true], [false, false, false]],
// user has share permissions -> can only reshare with those
[[false, true, false], [false, false, false], [false, false, false]],
[[false, true, false], [false, true, false], [false, true, false]],
[[false, true, false], [true, true, true], [false, true, false]],
// user has write permissions -> can only reshare with those
[[true, true, false], [false, false, false], [false, false, false]],
[[true, true, false], [false, true, false], [false, true, false]],
[[true, true, false], [true, true, true], [true, true, false]],
// user has manage permissions -> can upgrade acl permissions
[[false, false, true], [true, true, true], [true, true, true]],
[[true, true, true], [false, false, true], [false, false, true]],
];
}
/**
* @dataProvider dataAddAclExtendPermission
* @param $currentUserAcl
* @param $providedAcl
* @param $resultingAcl
* @throws NoPermissionException
* @throws \OCA\Deck\BadRequestException
*/
public function testAddAclExtendPermission($currentUserAcl, $providedAcl, $resultingAcl) {
$existingAcl = new Acl();
$existingAcl->setBoardId(123);
$existingAcl->setType('user');
$existingAcl->setParticipant('admin');
$existingAcl->setPermissionEdit($currentUserAcl[0]);
$existingAcl->setPermissionShare($currentUserAcl[1]);
$existingAcl->setPermissionManage($currentUserAcl[2]);
$this->permissionService->expects($this->at(0))
->method('checkPermission')
->with($this->boardMapper, 123, Acl::PERMISSION_SHARE, null);
if ($currentUserAcl[2]) {
$this->permissionService->expects($this->at(1))
->method('checkPermission')
->with($this->boardMapper, 123, Acl::PERMISSION_MANAGE, null);
} else {
$this->aclMapper->expects($this->once())
->method('findAll')
->willReturn([$existingAcl]);
$this->permissionService->expects($this->at(1))
->method('checkPermission')
->with($this->boardMapper, 123, Acl::PERMISSION_MANAGE, null)
->willThrowException(new NoPermissionException('No permission'));
$this->permissionService->expects($this->at(2))
->method('userCan')
->willReturn($currentUserAcl[0]);
$this->permissionService->expects($this->at(3))
->method('userCan')
->willReturn($currentUserAcl[1]);
$this->permissionService->expects($this->at(4))
->method('userCan')
->willReturn($currentUserAcl[2]);
}
$user = $this->createMock(IUser::class);
$user->method('getUID')->willReturn('admin');
$acl = new Acl();
$acl->setBoardId(123);
$acl->setType('user');
$acl->setParticipant('admin');
$acl->setPermissionEdit($resultingAcl[0]);
$acl->setPermissionShare($resultingAcl[1]);
$acl->setPermissionManage($resultingAcl[2]);
$acl->resolveRelation('participant', function($participant) use (&$user) {
return null;
});
$this->notificationHelper->expects($this->once())
->method('sendBoardShared');
$expected = clone $acl;
$this->aclMapper->expects($this->once())
->method('insert')
->with($acl)
->willReturn($acl);
$this->assertEquals($expected, $this->service->addAcl(
123, 'user', 'admin', $providedAcl[0], $providedAcl[1], $providedAcl[2]
));
}
public function testUpdateAcl() {
$acl = new Acl();
$acl->setBoardId(123);