Compare commits

..

319 Commits

Author SHA1 Message Date
Jonas
3486e0f24c Merge pull request #4571 from nextcloud/backport/4566/stable22
[stable22] Gracefully handle not found card for a share
2023-03-29 11:28:18 +02:00
Jonas
9519033881 Gracefully handle not found card for a share
Fixes: #3464
Fixes: #4565

Signed-off-by: Jonas <jonas@freesources.org>
2023-03-29 09:08:50 +00:00
Nextcloud bot
fc1f3fd10a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-11-04 02:21:24 +00:00
Nextcloud bot
c483b042fd [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-11-03 02:21:21 +00:00
Nextcloud bot
3d26acd0ba [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-11-02 02:21:39 +00:00
Nextcloud bot
dd23f7b0d3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-11-01 02:21:46 +00:00
Nextcloud bot
a12043357a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-31 02:21:11 +00:00
Nextcloud bot
2bd5d5532d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-30 02:24:16 +00:00
Nextcloud bot
ef72c08c11 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-29 02:23:29 +00:00
Nextcloud bot
3c3ff391a6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-27 02:21:21 +00:00
Nextcloud bot
29361b3ee7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-25 02:26:22 +00:00
Nextcloud bot
a421a62fb5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-24 02:22:00 +00:00
Nextcloud bot
2392308f58 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-18 02:21:45 +00:00
Nextcloud bot
26cc02bea1 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-13 02:22:27 +00:00
Nextcloud bot
9ecdc84148 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-08 02:21:56 +00:00
Nextcloud bot
af6a099f59 [tx-robot] Update transifex configuration
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-07 02:21:48 +00:00
Nextcloud bot
b885ec4b80 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-06 02:26:21 +00:00
Nextcloud bot
e9e8e26fb5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-05 02:21:27 +00:00
Nextcloud bot
1e2eb5ae2f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-10-01 02:24:42 +00:00
Nextcloud bot
d445ae7527 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-30 03:23:48 +00:00
Nextcloud bot
cef8fe8b0d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-28 02:46:22 +00:00
Nextcloud bot
98b66d0b5d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-26 02:47:22 +00:00
Nextcloud bot
81733002e5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-24 02:47:27 +00:00
Nextcloud bot
80b729c944 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-23 02:42:14 +00:00
Nextcloud bot
c286ce3859 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-22 02:45:53 +00:00
Nextcloud bot
f31033de72 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-20 02:46:01 +00:00
Nextcloud bot
79e83e56ba [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-17 02:45:19 +00:00
Nextcloud bot
09176b3ae6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-16 02:39:42 +00:00
Nextcloud bot
ed85991683 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-15 02:45:13 +00:00
Nextcloud bot
cbf1dc790d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-14 02:39:58 +00:00
Nextcloud bot
8a1c1c4690 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-13 02:40:45 +00:00
Nextcloud bot
0c80b60c6d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-12 02:39:46 +00:00
Nextcloud bot
0f904e65f4 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-09 02:40:45 +00:00
Nextcloud bot
e1b5d6d557 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-08 02:40:36 +00:00
Nextcloud bot
eca6b1f4f0 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-07 02:40:05 +00:00
Nextcloud bot
7dfdae86bf [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-06 02:39:43 +00:00
Nextcloud bot
cf491c5ad0 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-05 02:39:04 +00:00
Nextcloud bot
63cddda8de [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-04 02:38:06 +00:00
Nextcloud bot
ad17f558ce [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-03 02:44:40 +00:00
Nextcloud bot
4134818a85 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-09-01 02:50:11 +00:00
Nextcloud bot
85a7e888e7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-28 02:36:36 +00:00
Nextcloud bot
062025873b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-27 02:36:48 +00:00
Nextcloud bot
2262cf00c7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-24 02:39:45 +00:00
Nextcloud bot
ec022909de [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-22 02:37:19 +00:00
Nextcloud bot
8de5ef1255 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-21 02:36:39 +00:00
Nextcloud bot
c6a0cfa172 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-20 02:37:03 +00:00
Nextcloud bot
5002a3df29 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-13 02:37:54 +00:00
Nextcloud bot
cac42bac12 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-12 02:37:00 +00:00
Nextcloud bot
1bef8004ac [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-09 02:36:41 +00:00
Julius Härtl
3b9efba8ab Merge pull request #3961 from nextcloud/backport/3952/stable22 2022-08-08 09:32:55 +02:00
Julius Härtl
f10e1a21fe Fetch attachment folder for the correct user during cron job
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-08-08 07:00:59 +00:00
Nextcloud bot
af36323e53 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-07 02:41:03 +00:00
Nextcloud bot
9c1ae577fd [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-06 02:37:39 +00:00
Nextcloud bot
532516b4c4 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-05 02:13:46 +00:00
Nextcloud bot
02f3f821b5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-08-03 02:13:46 +00:00
Nextcloud bot
52cd5f9881 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-29 02:13:37 +00:00
Nextcloud bot
d718aafc74 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-28 02:13:43 +00:00
Nextcloud bot
6f475b7acc [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-27 02:13:42 +00:00
Nextcloud bot
ee2fc04fa1 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-25 02:39:14 +00:00
Nextcloud bot
a5217a49d3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-24 02:40:52 +00:00
Nextcloud bot
c241ee54fe [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-23 02:38:39 +00:00
Nextcloud bot
ff4ee6ced6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-21 02:38:57 +00:00
Nextcloud bot
e72a3d34fd [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-19 02:38:04 +00:00
Nextcloud bot
120b4ad981 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-18 02:37:20 +00:00
Nextcloud bot
0d35859ab1 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-14 02:37:48 +00:00
Julius Härtl
db9e367633 Merge pull request #3919 from nextcloud/backport/3916/stable22
[stable22] Prevent opening card and applyLabelFilter on card drag end
2022-07-12 16:36:54 +02:00
Julien Veyssier
2537aaa6ea refs #2594 prevent opening card and applyLabelFilter on card drag end
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2022-07-12 11:57:28 +00:00
Nextcloud bot
e7c15b6154 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-08 02:37:20 +00:00
Nextcloud bot
9016e5020d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-06 02:35:59 +00:00
Nextcloud bot
f8d811463e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-04 02:35:53 +00:00
Nextcloud bot
b60b5d4225 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-07-02 02:37:26 +00:00
Raul Ferreira Fuentes
e19befa2df Merge pull request #3887 from nextcloud/backport/3884/stable22
[stable22] Fix z-index for deck sidebar
2022-06-28 15:26:16 +02:00
Raul
5ca11c7515 Fix z-index for deck sidebar
The sidebar would previously render above the user menu (logout, settings, etc)

Signed-off-by: Raul <r.ferreira.fuentes@gmail.com>
2022-06-28 12:40:42 +00:00
Nextcloud bot
4ec9aa3cb6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-06-22 02:40:30 +00:00
Nextcloud bot
571d764209 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-06-14 02:41:55 +00:00
Nextcloud bot
5230a76442 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-06-12 02:41:08 +00:00
Nextcloud bot
284d8642f6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-06-09 02:45:36 +00:00
Nextcloud bot
6b6fbab98d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-06-07 02:44:10 +00:00
Nextcloud bot
cc3e946880 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-06-03 02:47:55 +00:00
Nextcloud bot
e131aa70cf [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-06-02 02:47:13 +00:00
Julius Härtl
d643ea75a2 Merge pull request #3838 from nextcloud/release/1.5.8
release/1.5.8
2022-06-01 11:28:01 +02:00
Julius Härtl
8f04c37275 Pin engines
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-06-01 11:27:13 +02:00
Julius Härtl
5251e69830 Merge pull request #3846 from nextcloud/backport/3528/stable22
[stable22] Add app config to toggle the default calendar setting as an admin
2022-05-30 18:20:54 +02:00
Julius Härtl
33f24e5add Add app config to toggle the default calendar setting as an admin
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-05-30 12:42:40 +00:00
Nextcloud bot
1d16341d0f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-29 02:46:57 +00:00
Nextcloud bot
8220872eba [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-28 02:43:28 +00:00
Luka Trovic
73df3588e5 changes for 1.5.8
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-05-27 17:41:08 +02:00
Nextcloud bot
dbca67dec0 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-26 02:44:20 +00:00
Nextcloud bot
9fd46c2e43 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-24 02:47:16 +00:00
Nextcloud bot
9bfc39b316 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-23 02:43:49 +00:00
Nextcloud bot
e9fb6616d9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-22 02:43:44 +00:00
Nextcloud bot
e4a4ab5912 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-21 02:47:16 +00:00
Nextcloud bot
9e7261e5d5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-20 02:46:31 +00:00
Julius Härtl
2af2e7c9cc Merge pull request #3817 from nextcloud/backport/3811/stable22
[stable22] Align Duedate-delete icon properly - fixes nextcloud/deck#3791
2022-05-13 09:35:05 +02:00
Nextcloud bot
2dc9eaf74e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-13 02:41:51 +00:00
ben
62318fedef Align Duedate-delete icon properly - fixes nextcloud/deck#3791
Signed-off-by: ben <ben@ro.tt>
2022-05-12 14:54:23 +00:00
Julius Härtl
d8c640d0a4 Merge pull request #3806 from nextcloud/backport/3682/stable22
[stable22] Increase file count after sharing
2022-05-12 09:57:56 +02:00
Julius Härtl
47f546fb12 Fix php-cs-fixer failure
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-05-12 09:09:40 +02:00
Julius Härtl
fc0571aeda Move all caching to helper
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-05-11 14:44:53 +00:00
Luka Trovic
8695641c2a fix: move shares count cache logic to the DeckShareProvider
Signed-off-by: Luka Trovic <luka@nextcloud.com>

fix: conflicts

Signed-off-by: Luka Trovic <luka@nextcloud.com>

fix: conflicts and test issues

Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-05-11 14:44:52 +00:00
Luka Trovic
894e17aaef fix: update attachments count when sharing
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-05-11 14:44:51 +00:00
Luka Trovic
cd3b7e7fd9 fix: increase file count after sharing
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-05-11 14:44:51 +00:00
Julius Härtl
ab6d12cc5f Merge pull request #3794 from nextcloud/backport/3681/stable22-bis
[stable22] Show cards after moving into another list
2022-05-10 12:32:14 +02:00
Luka Trovic
a8db7a90f1 fix: undefined variable in AttachmentMapper.php
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-05-09 19:25:41 +01:00
Luka Trovic
19b4d0ac21 fix: feedback
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-05-09 16:48:50 +01:00
Luka Trovic
1dec7be713 fix: show card after moving into another list
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-05-09 16:46:38 +01:00
Nextcloud bot
92a0ef904f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-06 02:41:19 +00:00
Nextcloud bot
f389f834f0 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-05 02:41:09 +00:00
Julius Härtl
c035a6be73 Merge pull request #3781 from nextcloud/backport/3777/stable22
[stable22] Fetch full board data after cloning
2022-05-04 17:17:14 +02:00
Julius Härtl
d1498486eb Fetch full board data after cloning
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-05-04 10:29:41 +00:00
Nextcloud bot
408a19ba9a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-04 02:43:00 +00:00
Nextcloud bot
9523aa9eeb [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-05-03 02:41:27 +00:00
Nextcloud bot
3d74323be4 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-30 02:39:58 +00:00
Julius Härtl
3d14e3f916 Bump version to 1.5.7
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-04-29 16:31:04 +02:00
Julius Härtl
cce5bd8a7d Merge pull request #3767 from nextcloud/backport/3761/stable22
[stable22] Fix text selection in dark mode and modal view
2022-04-29 16:29:22 +02:00
Julius Härtl
62ccab6e96 Fix text selection in dark mode and modal view
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-04-29 15:58:44 +02:00
Nextcloud bot
9f8e7258b5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-29 02:41:10 +00:00
Julius Härtl
2ca00fed08 Merge pull request #3756 from nextcloud/backport/3745/stable22
[stable22] Add missing indices
2022-04-28 10:12:04 +02:00
Nextcloud bot
22cb32e197 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-28 02:41:24 +00:00
Julius Härtl
e1391efbef Add missing indices
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-04-26 16:26:44 +00:00
Nextcloud bot
4633ddc153 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-24 02:41:18 +00:00
Nextcloud bot
a96ccc3bfd [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-23 02:39:31 +00:00
Nextcloud bot
14bcab8c8a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-22 02:40:29 +00:00
Joas Schilling
40f0f3d194 Merge pull request #3742 from nextcloud/backport/stable22/3683
[stable22] Fix paramter replacements when creating deck cards from talk messages
2022-04-21 09:35:27 +02:00
Julius Härtl
32e5f6dd6b Merge pull request #3743 from nextcloud/backport/stable22/3326
[stable22] add autofocus on board edit #3326
2022-04-21 08:22:45 +02:00
Julius Härtl
5f2235a91f Merge pull request #3739 from nextcloud/backport/3663/stable22
[stable22] [stable23] Sort boards non case sensitive
2022-04-20 18:55:26 +02:00
Michael Weimann
deca12ddd3 add autofocus on board edit
Signed-off-by: Michael Weimann <mail@michael-weimann.eu>
2022-04-20 18:53:42 +02:00
Joas Schilling
31d6794490 Fix paramter replacements when creating deck cards from talk messages
Signed-off-by: Joas Schilling <coding@schilljs.com>
2022-04-20 18:51:05 +02:00
ben
90938d5f58 fixes nextcloud/deck#3410
Signed-off-by: ben <git@rott.io>
2022-04-20 16:17:47 +00:00
Julius Härtl
0c7768b36d Merge pull request #3735 from nextcloud/backport/3692/stable22
[stable22] Fix hidden attachment icon on archived cards
2022-04-20 18:14:20 +02:00
Luka Trovic
d14c7810ee fix: hidden attachment icon on archived cards
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-04-20 14:22:33 +00:00
Julius Härtl
de5153337d Merge pull request #3731 from nextcloud/backport/3497/stable22
[stable22] [stable23] Use explicit cast to make use of index
2022-04-20 16:20:43 +02:00
Julius Härtl
bd405122e2 Use explicit cast to make use of index
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-04-19 19:43:29 +00:00
Nextcloud bot
e7d6233796 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-17 02:38:12 +00:00
Nextcloud bot
25bb72c3dc [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-14 02:42:25 +00:00
Nextcloud bot
487c626a73 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-13 02:40:16 +00:00
Julius Härtl
6b0182ea70 Merge pull request #3721 from nextcloud/backport/3717/stable22
[stable22] Fix: Check all circle shares for permissions
2022-04-12 22:25:43 +02:00
Bink
ca6fe65580 Fix: Check all circle shares for permissions instead of returning after the first 2022-04-12 15:19:09 +00:00
Julius Härtl
498ed9dfff Merge pull request #3665 from nextcloud/backport/stable22/2496
[stable22] Transfer ownership
2022-04-11 18:29:28 +02:00
Julius Härtl
32a2a4ebf5 Merge pull request #3714 from nextcloud/backport/3670/stable22
[stable22] Properly check for the stack AND setting board permissions
2022-04-11 18:28:06 +02:00
Julius Härtl
4bd1b70a5e Properly check for the stack AND setting board permissions
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-04-11 14:35:25 +00:00
Julius Härtl
71b78e8ec3 Remove unused argument
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-04-11 16:25:37 +02:00
Julius Härtl
2d6fc18218 Merge pull request #3707 from nextcloud/backport/3501/stable22
[stable22] Add a missing translation - not found in transifex
2022-04-11 09:53:33 +02:00
Nextcloud bot
b7554a06d2 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-09 02:39:17 +00:00
Luka Trovic
35a8fee3b3 add a missing translation - not found in transifex
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-04-08 19:35:39 +00:00
Nextcloud bot
4fe94e65ad [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-06 02:39:54 +00:00
Nextcloud bot
80fa93716b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-04-05 02:39:56 +00:00
Nextcloud bot
0145593cba [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-31 02:41:42 +00:00
Nextcloud bot
af712ddb56 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-30 02:39:33 +00:00
Nextcloud bot
f0deb93cb7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-29 02:39:55 +00:00
Nextcloud bot
781cfc11c7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-28 02:39:02 +00:00
Nextcloud bot
97e45ebc69 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-25 03:07:29 +00:00
Nextcloud bot
c4bc945b1e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-24 02:38:36 +00:00
Julius Härtl
2bd12ed7b5 lint: fix eslint
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 15:58:54 +01:00
Julius Härtl
658d358e70 Skip unavailable cache
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 15:12:41 +01:00
Julius Härtl
3e6a80eb37 Fix tests and move to 7.3 as a min php version
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 15:07:42 +01:00
Julius Härtl
5408d4f9c5 Adjust documentaion wording
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:29 +01:00
Julius Härtl
6e5fd9e25a Handle board exceptions more gracefully
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:29 +01:00
Julius Härtl
9fbdafbe73 Cover case where the owner is preserved
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:29 +01:00
Luka Trovic
9403fb1759 fix: feedback
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-03-22 14:20:29 +01:00
Luka Trovic
691abb02d8 feat: add api endpoint and UI to transfer a board to a different user
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-03-22 14:20:29 +01:00
Julius Härtl
3bb99a9001 fix: test cases using generator
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:28 +01:00
Julius Härtl
74e0149a6d Reuse single board transfer for all user boards
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:28 +01:00
Julius Härtl
c5d83e662c fix: Properly handle limited scope for remapping users
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:28 +01:00
Julius Härtl
376c7c7d07 cleanup test cases
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:28 +01:00
Julius Härtl
a52664e9e4 Allow transfer of single boards
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:28 +01:00
Julius Härtl
f573abe279 fix: Psalm
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:20:26 +01:00
Julius Härtl
975af7c056 fix: unit tests
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:18:40 +01:00
Luka Trovic
f2456d796c feat: add integration test for transferring board ownership with data
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-03-22 14:18:13 +01:00
Luka Trovic
c7c9302109 fix: integration tests
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-03-22 14:18:11 +01:00
Luka Trovic
989db4702c fix: unit test & psalm static code analysis issues
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-03-22 14:17:04 +01:00
Max
9de5f12f4b fix: queries with the new base mapper in BoardMapper
Signed-off-by: Max <max@nextcloud.com>
2022-03-22 14:17:04 +01:00
Max
9338ebb561 fix: Assignment is the new AssignedUsers
Signed-off-by: Max <max@nextcloud.com>
2022-03-22 14:17:04 +01:00
Julius Härtl
0d9cdc5f1e Make queries work with the new base mapper
Signed-off-by: Julius Härtl <jus@bitgrid.net>

fix: conflicts
2022-03-22 14:17:04 +01:00
Julius Härtl
f66c71ee55 Just cleanup old ACL rules, there are none for the board owner so nothing to cleanup or persist there
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:17:03 +01:00
Julius Härtl
d742bc097b Use proper description of what gets transferred
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:17:03 +01:00
Julius Härtl
53386a7f1a Fix card mapper query for transfer
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-22 14:17:03 +01:00
Sergey Shliakhov
cfbc18deb9 Fix coding styles
Signed-off-by: Julius Härtl <jus@bitgrid.net>
Signed-off-by: Max <max@nextcloud.com>
2022-03-22 14:17:03 +01:00
Sergey Shliakhov
bdf4631504 Transfer deck ownership even if target user already participant of a board
https://github.com/nextcloud/deck/pull/1955#issuecomment-640392715
Signed-off-by: Sergey Shliakhov <husband.sergey@gmail.com>
2022-03-22 14:17:03 +01:00
Sergey Shliakhov
a0f93a81d2 Check type before transfer card participants ownership
Signed-off-by: Sergey Shliakhov <husband.sergey@gmail.com>

temp
2022-03-22 14:17:03 +01:00
Sergey Shliakhov
d9b086f146 Fix wrong class name
Signed-off-by: Sergey Shliakhov <husband.sergey@gmail.com>
2022-03-22 14:17:03 +01:00
Sergey Shliakhov
b8b3ac3516 Fix code style
Signed-off-by: Sergey Shliakhov <husband.sergey@gmail.com>
2022-03-22 14:17:03 +01:00
Sergey Shliakhov
118959795f Add tests
Signed-off-by: Sergey Shliakhov <husband.sergey@gmail.com>
2022-03-22 14:17:03 +01:00
Sergey Shliakhov
c93aaeb9bf Update docs
Signed-off-by: Sergey Shliakhov <husband.sergey@gmail.com>

fix: conflicts
2022-03-22 14:17:02 +01:00
Sergey Shliakhov
f42c82274f Add deck:transfer-ownership command
Signed-off-by: Sergey Shliakhov <husband.sergey@gmail.com>
2022-03-22 14:17:02 +01:00
Nextcloud bot
fff9c4f7a2 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-18 02:38:33 +00:00
Julius Härtl
3eb5ccbbe2 Merge pull request #3642 from nextcloud/backport/3635/stable22
[stable22] 🐛 Fix missing files sidebar
2022-03-17 08:19:47 +01:00
Vinicius Reis
cc61f3d4e0 fix style-lint
Signed-off-by: Vinicius Reis <vinicius.reis@nextcloud.com>
2022-03-17 00:05:24 -03:00
Nextcloud bot
209adc1e7a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-03-17 02:41:21 +00:00
Vinicius Reis
e26f06be8f 🐛 Fix missing files sidebar
Signed-off-by: Vinicius Reis <vinicius.reis@nextcloud.com>
2022-03-14 14:11:02 +00:00
Julius Härtl
c99b4bf4ab Merge pull request #3629 from nextcloud/release/1.5.6
Release 1.5.6
2022-03-10 09:54:21 +01:00
Julius Härtl
296e7abf14 Bump version to 1.5.6
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-10 09:30:38 +01:00
Julius Härtl
c6ef07ceb0 Merge pull request #3627 from nextcloud/backport/3611/stable22
[stable22] Generate fixed link for activity emails
2022-03-09 20:20:26 +01:00
Luka Trovic
4e937bd03a fix: generate fixed link for activity emails
Signed-off-by: Luka Trovic <luka@nextcloud.com>

fix: generate fixed link for activity emails

Signed-off-by: Luka Trovic <luka@nextcloud.com>

Fix tests

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-03-09 17:24:32 +00:00
Julius Härtl
84943c5889 Create appstore-build-publish.yml 2022-03-04 13:58:23 +01:00
Julius Härtl
4ed71b7a63 Merge pull request #3615 from nextcloud/backport/3612/stable22
[stable22] Make insert attachment buttom easy to click
2022-03-02 21:05:00 +01:00
Luka Trovic
4ab8078103 fix: make insert attachment buttom easy to click
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-03-02 16:24:22 +00:00
Nextcloud bot
6b75a1ab1b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-19 02:38:02 +00:00
Nextcloud bot
76e3a3a25e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-17 02:38:53 +00:00
Nextcloud bot
9503de4716 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-08 02:38:07 +00:00
Nextcloud bot
3340ccec6f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-06 02:37:01 +00:00
Nextcloud bot
a90e099113 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-05 02:37:29 +00:00
Nextcloud bot
aa69495075 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-02-01 02:37:27 +00:00
Nextcloud bot
11eb779beb [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-31 02:37:27 +00:00
Nextcloud bot
836e2b33df [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-24 02:37:12 +00:00
Nextcloud bot
c63b72bf2e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-19 02:38:15 +00:00
Julien Veyssier
c20008dacc Merge pull request #3544 from nextcloud/backport/3541/stable22
[stable22] Fix confusion between stackId and boardId in StackService
2022-01-18 16:45:52 +01:00
Julien Veyssier
edacda1377 fix confusion between stackId and boardId in StackService::update()
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2022-01-18 15:23:26 +00:00
Nextcloud bot
2f0ad2d639 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-18 02:56:05 +00:00
Nextcloud bot
745d76c74b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-13 02:37:11 +00:00
Julius Härtl
3b7f49de55 Merge pull request #3527 from nextcloud/backport/3500/stable22 2022-01-12 13:45:45 +01:00
Julius Härtl
e336bd98b2 Merge pull request #3524 from nextcloud/backport/3502/stable22 2022-01-12 08:39:40 +01:00
Julius Härtl
f62e87cffa Update psalm baseline
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-01-12 08:38:51 +01:00
Nextcloud bot
d4d716b1f5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-12 02:37:26 +00:00
Julius Härtl
6e89dd48bc Move any circles API usage to internal service
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-01-11 09:31:34 +01:00
Julius Härtl
2bbd572cb5 Avoid blocking calendar access if something goes wrong while fetching deck entries
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2022-01-11 09:29:29 +01:00
Luka Trovic
17543eaf94 exclude deleted boards in the selection for target
Signed-off-by: Luka Trovic <luka@nextcloud.com>
2022-01-11 08:00:48 +00:00
Nextcloud bot
4f4e9fa5c7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-11 02:37:41 +00:00
Julius Härtl
d0060604a6 Merge pull request #3521 from nextcloud/backport/3512/stable22 2022-01-10 21:08:16 +01:00
Simon Spannagel
9f8df2bbd4 CardApiController: Fix order of optional parameters
Signed-off-by: Simon Spannagel <simonspa@kth.se>
2022-01-10 11:02:10 +00:00
Nextcloud bot
139cca1ae8 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-10 02:37:43 +00:00
Nextcloud bot
32054adcb5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-08 02:36:42 +00:00
Nextcloud bot
764dd1e995 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-06 02:38:51 +00:00
Nextcloud bot
b65fde069f [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-05 02:38:16 +00:00
Nextcloud bot
bec68eb5c3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-04 02:38:10 +00:00
Nextcloud bot
8dca76f9f0 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-03 02:38:06 +00:00
Nextcloud bot
13ed30e2bc [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-02 02:37:46 +00:00
Nextcloud bot
a7ff79605d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2022-01-01 02:37:48 +00:00
Nextcloud bot
b5bebac3c2 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-31 02:38:05 +00:00
Nextcloud bot
756d78f78a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-24 02:39:53 +00:00
Nextcloud bot
7e55cb18b3 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-22 02:38:53 +00:00
Nextcloud bot
b834ef664a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-20 02:40:45 +00:00
Nextcloud bot
1ee0e16f9e [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-18 02:37:55 +00:00
Nextcloud bot
63434d7b63 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-17 02:40:02 +00:00
Nextcloud bot
d9f993dc5b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-16 02:38:16 +00:00
Nextcloud bot
9090c13417 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-12 02:38:56 +00:00
Nextcloud bot
0630dafef9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-09 02:40:05 +00:00
Nextcloud bot
150ab7b83a [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-05 02:43:14 +00:00
Nextcloud bot
11cc569914 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-03 02:40:13 +00:00
Nextcloud bot
eaf58e0fc9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-12-02 02:40:11 +00:00
Julius Härtl
da994c5ecd Merge pull request #3460 from nextcloud/backport/3459/stable22 2021-11-30 16:32:06 +01:00
Julius Härtl
cef4cc6a4d Fix cursor generation if no results are found
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-30 12:22:08 +00:00
Nextcloud bot
d761323887 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-27 02:37:10 +00:00
Nextcloud bot
0857270a48 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-26 02:57:41 +00:00
Nextcloud bot
df178369c0 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-25 02:36:05 +00:00
Nextcloud bot
8c3edf077d [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-24 02:37:00 +00:00
Jonas
ac477a58c3 Merge pull request #3441 from nextcloud/backport/3428/stable22
[stable22] Allow to download an attachment without navigating to the files app
2021-11-22 19:11:19 +01:00
Julius Härtl
504f977739 Allow to download an attachment without navigating to the files app
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-22 17:40:38 +00:00
Nextcloud bot
00e813295b [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-20 02:53:16 +00:00
Nextcloud bot
300d300c45 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-19 02:53:35 +00:00
Nextcloud bot
1514f9a737 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-15 02:35:08 +00:00
Nextcloud bot
f4fe271b39 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-14 02:36:21 +00:00
Nextcloud bot
1c01881eb7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-13 02:36:43 +00:00
Julius Härtl
af1564d8e5 Bump version to 1.5.5
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-10 20:12:55 +01:00
Julius Härtl
35515ce157 Bump version to 1.5.4
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-10 16:16:20 +01:00
Nextcloud bot
fd62ab7a59 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-10 02:36:04 +00:00
Nextcloud bot
1557797926 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-09 04:07:36 +00:00
Nextcloud bot
90a8b479f6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-08 02:49:15 +00:00
Nextcloud bot
c2fae6b2d7 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-07 02:32:58 +00:00
Nextcloud bot
89a3f4fc26 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-11-06 02:33:23 +00:00
Julius Härtl
7aa94c74d7 Merge pull request #3407 from nextcloud/backport/3384/stable22
[stable22] Keep exceptions http response generic
2021-11-05 19:53:06 +01:00
Julius Härtl
33993418ac Keep exceptions http response generic and return the request ID for further tracing
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-11-05 17:02:27 +00:00
Nextcloud bot
75a2d9d54c [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-30 02:33:52 +00:00
Nextcloud bot
b1a3c6b237 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-29 02:33:14 +00:00
Julius Härtl
437704abb3 Merge pull request #3392 from nextcloud/backport/3391/stable22 2021-10-27 14:16:01 +02:00
Paweł Kuffel
0e09548e69 use displayname instead of uid for mentions
Signed-off-by: Paweł Kuffel <pawel@kuffel.io>
2021-10-27 11:59:19 +00:00
Julius Härtl
37834cb926 Merge pull request #3378 from nextcloud/backport/3324/stable22 2021-10-22 20:24:40 +02:00
Nextcloud bot
7dd3bb49f4 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-22 02:32:43 +00:00
Julius Härtl
fdf1eaeaed Merge pull request #3381 from Artem4590/backport/3323/stable22 2021-10-21 15:49:43 +02:00
Nextcloud bot
4cc88c8b64 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-21 02:34:56 +00:00
Nextcloud bot
db331ecb72 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-20 02:53:53 +00:00
Artem Lavrukhin
c2757bec7d [stable22] Extend drag-and-drop zone in card sidebar
Signed-off-by: Artem Lavrukhin <lavryha4590@gmail.com>
2021-10-15 16:09:10 +03:00
Lera Dmitrieva
e074eac092 Fix menu button position in card modal
Signed-off-by: Lera Dmitrieva <dmit.valerya@yandex.ru>
2021-10-12 10:25:11 +00:00
Nextcloud bot
d5166c74e5 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-07 02:36:02 +00:00
Julius Härtl
226e3c8212 Merge pull request #3366 from nextcloud/backport/3364/stable22 2021-10-06 11:40:20 +02:00
Julius Härtl
a680915a89 Fix optional parameter order
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-10-06 09:24:12 +00:00
Julius Härtl
b8ff06e5d2 Merge pull request #3361 from nextcloud/enh/stable22-paginated-search-for-boards-and-cards 2021-10-05 08:24:06 +02:00
Julien Veyssier
5a108b64b0 use distinct pagination cursor for cards and boards, use cursor and limit in SearchService::searchBoards()
Signed-off-by: Julien Veyssier <eneiluj@posteo.net>
2021-10-04 17:38:07 +02:00
Nextcloud bot
00630587af [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-04 02:37:16 +00:00
Nextcloud bot
b02cf925a1 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-10-02 02:37:09 +00:00
Julius Härtl
aaa26575dd Bump version to 1.5.3
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-09-14 21:08:10 +02:00
Julius Härtl
aebb3cef03 Merge pull request #3317 from nextcloud/backport/3316/stable22
[stable22] Additional check for stacks
2021-09-14 21:06:56 +02:00
Julius Härtl
18a7ae3a1a Additional check for stacks
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-09-14 19:01:19 +00:00
Nextcloud bot
6c84c67970 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-14 02:42:00 +00:00
Nextcloud bot
d449acde30 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-13 02:40:06 +00:00
Nextcloud bot
c1e700cefa [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-12 02:40:07 +00:00
Julius Härtl
30df03948c Bump version to 1.5.2
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-09-09 14:18:07 +02:00
Nextcloud bot
15ea081daa [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-09 02:38:56 +00:00
Julius Härtl
0f71741c46 Merge pull request #3306 from nextcloud/backport/3299/stable22
[stable22] Return false instead of throwing when getting calendar setting
2021-09-08 18:28:57 +02:00
Julius Härtl
585f0999a3 Merge pull request #3303 from nextcloud/backport/3298/stable22 2021-09-08 18:14:35 +02:00
Julius Härtl
d51f645299 Return false instead of throwing when getting calendar integration setting
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-09-08 16:00:28 +00:00
Julius Härtl
ebbe4eb802 Delete file shares through attachments API
Previously the file was deleted in the file structure of the user is not
expected as the file might not only be related to the card.

Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-09-08 17:57:01 +02:00
Nextcloud bot
d6c94f44d9 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-08 02:38:21 +00:00
Julius Härtl
86345a6338 Merge pull request #3300 from nextcloud/backport/3294/stable22
[stable22] Fix print style issues
2021-09-07 13:14:33 +02:00
Michael Weimann
2c77d8a589 fix print style issues
Signed-off-by: Michael Weimann <mail@michael-weimann.eu>
2021-09-06 13:58:34 +00:00
Nextcloud bot
16cbbd4805 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-06 02:38:53 +00:00
Nextcloud bot
8aa77679c8 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-09-05 02:38:20 +00:00
Julius Härtl
e6cff5bbb6 Bump version to 1.5.1
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-09-03 15:03:27 +02:00
Nextcloud bot
86ed24744c [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-08-31 02:37:47 +00:00
Julius Härtl
01ed3625dc Merge pull request #3264 from nextcloud/backport/3263/stable22
[stable22] Defer obtaining the user session in the config service
2021-08-20 19:07:13 +02:00
Julius Härtl
cc9dea1f2b Defer obtaining the user session in the config service which might be injected before login has hapened
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-08-20 16:02:48 +00:00
Nextcloud bot
b16ade905c [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-08-18 02:37:01 +00:00
Nextcloud bot
ee1bba7d99 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-08-10 02:44:35 +00:00
Nextcloud bot
3407097e95 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-08-06 02:38:51 +00:00
Julius Härtl
47ac3e6082 Merge pull request #3231 from nextcloud/backport/3225/stable22
[stable22] Check for null value to avoid TypeError in the group manager
2021-08-05 09:04:16 +02:00
Julius Härtl
75110bed47 Check for null value to avoid TypeError in the group manager
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-08-04 16:41:31 +00:00
Julius Härtl
74f0106718 Merge pull request #3224 from nextcloud/backport/3217/stable22
[stable22] Move circle checks to a unified service and improve member checks
2021-08-03 11:41:03 +02:00
Julius Härtl
958d50d9b7 Move circle checks to a unified service and improve member checks
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-08-03 07:43:30 +00:00
Julius Härtl
5f4aa017b6 Pin mariadb to 10.5 for tests
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-08-03 09:42:43 +02:00
Nextcloud bot
92c2a58f50 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-08-01 02:12:52 +00:00
Nextcloud bot
75bf0dffe6 [tx-robot] updated from transifex
Signed-off-by: Nextcloud bot <bot@nextcloud.com>
2021-07-15 10:52:50 +00:00
Julius Härtl
45a10f0840 Bump version to 1.5.0
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-07-09 11:55:34 +02:00
Julius Härtl
fbf3b1cd19 Merge pull request #3168 from nextcloud/backport/3161/stable22
[stable22] Reduce duplicate queries when fetching user boards an permissions
2021-07-06 07:54:35 +02:00
Julius Härtl
0cc4151929 Reduce duplicate queries when fetching user boards an permissions
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-07-05 18:27:49 +00:00
Julius Härtl
e261ade1bb Merge pull request #3165 from nextcloud/backport/3151/stable22
[stable22] Always log generic exceptions
2021-07-05 16:21:15 +02:00
Julius Härtl
5d7e54d419 Always log generic exceptions
Signed-off-by: Julius Härtl <jus@bitgrid.net>
2021-07-02 15:55:44 +00:00
Julius Härtl
3499858295 Merge remote-tracking branch 'origin/master' into stable22 2021-06-25 15:39:38 +02:00
Julius Härtl
1615e218bd Merge pull request #3148 from nextcloud/update-stable22-target-versions
Update stable22 target versions
2021-06-25 09:07:38 +02:00
Joas Schilling
9a3b859780 Update stable22 target versions
Signed-off-by: Joas Schilling <coding@schilljs.com>
2021-06-24 14:07:30 +02:00
245 changed files with 37124 additions and 43195 deletions

View File

@@ -3,10 +3,7 @@ root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = tab
indent_style = tab
insert_final_newline = true
trim_trailing_whitespace = true
[*.{js,vue}]
indent_style = tab

View File

@@ -1,14 +1,8 @@
module.exports = {
root: true,
extends: [
'@nextcloud',
],
rules: {
'jsdoc/require-param-description': ['off'],
'jsdoc/require-param-type': ['off'],
'jsdoc/check-param-names': ['off'],
'jsdoc/no-undefined-types': ['off'],
'jsdoc/require-property-description': ['off'],
'import/no-named-as-default-member': ['off']
'valid-jsdoc': ['off'],
},
}

View File

@@ -11,6 +11,19 @@ updates:
open-pull-requests-limit: 10
reviewers:
- juliushaertl
- jakobroehrl
#- package-ecosystem: npm
# directory: "/"
# target-branch: "stable1.1"
# schedule:
# interval: weekly
# day: saturday
# time: "03:00"
# timezone: Europe/Paris
# open-pull-requests-limit: 10
# reviewers:
# - juliushaertl
# - jakobroehrl
- package-ecosystem: composer
directory: "/"
schedule:
@@ -21,23 +34,8 @@ updates:
open-pull-requests-limit: 10
reviewers:
- juliushaertl
- package-ecosystem: composer
directory: "/tests/integration"
schedule:
interval: weekly
day: saturday
time: "03:00"
timezone: Europe/Paris
open-pull-requests-limit: 10
reviewers:
- juliushaertl
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: weekly
day: saturday
time: "03:00"
timezone: Europe/Paris
open-pull-requests-limit: 10
reviewers:
- juliushaertl
ignore:
- dependency-name: christophwurst/nextcloud
versions:
- "< 16"
- ">= 15.a"

25
.github/stale.yml vendored Normal file
View File

@@ -0,0 +1,25 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 60
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "1. to develop"
- "2. developing"
- "3. to review"
- "discussion"
- "bounty"
- "bug"
- "enhancement"
# Limit the number of actions per hour, from 1-30. Default is 30
limitPerRun: 30
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.

View File

@@ -1,11 +1,7 @@
name: Package build
on:
push:
branches:
- main
- master
- stable*
pull_request:
jobs:
build:
@@ -16,15 +12,15 @@ jobs:
node-version: [14.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Set up npm7
run: npm i -g npm@7
- name: Setup PHP
uses: shivammathur/setup-php@2.21.2
uses: shivammathur/setup-php@v1
with:
php-version: '7.4'
tools: composer
@@ -37,7 +33,7 @@ jobs:
uname -a
RUST_BACKTRACE=1 krankerl --version
RUST_BACKTRACE=1 krankerl package
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v2
with:
name: Deck app tarball
path: build/artifacts/deck.tar.gz

View File

@@ -66,7 +66,7 @@ jobs:
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
- name: Set up php ${{ env.PHP_VERSION }}
uses: shivammathur/setup-php@2.21.2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
coverage: none

View File

@@ -1,51 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Rebase command
on:
issue_comment:
types: created
permissions:
contents: read
jobs:
rebase:
runs-on: ubuntu-latest
permissions:
contents: none
# On pull requests and if the comment starts with `/rebase`
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase')
steps:
- name: Add reaction on start
uses: peter-evans/create-or-update-comment@v2
with:
token: ${{ secrets.COMMAND_BOT_PAT }}
repository: ${{ github.event.repository.full_name }}
comment-id: ${{ github.event.comment.id }}
reaction-type: "+1"
- name: Checkout the latest code
uses: actions/checkout@v3
with:
fetch-depth: 0
token: ${{ secrets.COMMAND_BOT_PAT }}
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.7
env:
GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
- name: Add reaction on failure
uses: peter-evans/create-or-update-comment@v2
if: failure()
with:
token: ${{ secrets.COMMAND_BOT_PAT }}
repository: ${{ github.event.repository.full_name }}
comment-id: ${{ github.event.comment.id }}
reaction-type: "-1"

View File

@@ -1,112 +0,0 @@
name: Cypress
on:
pull_request:
push:
branches:
- master
- stable*
env:
APP_NAME: deck
CYPRESS_baseUrl: http://localhost:8081/index.php
jobs:
cypress:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [14.x]
# containers: [1, 2, 3]
php-versions: [ '7.4' ]
databases: [ 'sqlite' ]
server-versions: [ 'master' ]
steps:
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
- name: Set up npm7
run: npm i -g npm@7
- name: Checkout server
uses: actions/checkout@v3
with:
repository: nextcloud/server
ref: ${{ matrix.server-versions }}
- name: Checkout submodules
shell: bash
run: |
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
- name: Checkout ${{ env.APP_NAME }}
uses: actions/checkout@v3
with:
path: apps/${{ env.APP_NAME }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@2.21.2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, iconv, fileinfo, intl, sqlite, pdo_sqlite, zip, gd, apcu
ini-values:
apc.enable_cli=on
coverage: none
- name: Set up Nextcloud
env:
DB_PORT: 4444
PHP_CLI_SERVER_WORKERS: 10
run: |
mkdir data
php occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass admin
php occ config:system:set memcache.local --value="\\OC\\Memcache\\APCu"
php occ config:system:set debug --value=true --type=boolean
php -f index.php
php -S 0.0.0.0:8081 &
export OC_PASS=1234561
php occ user:add --password-from-env user1
php occ user:add --password-from-env user2
php occ app:enable deck
php occ app:list
cd apps/deck
composer install --no-dev
npm ci
npm run build
cd ../../
curl -v http://localhost:8081/index.php/login
- name: Cypress run
uses: cypress-io/github-action@v4
with:
record: true
parallel: false
wait-on: '${{ env.CYPRESS_baseUrl }}'
working-directory: 'apps/${{ env.APP_NAME }}'
config: defaultCommandTimeout=10000,video=false
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
npm_package_name: ${{ env.APP_NAME }}
- name: Upload test failure screenshots
uses: actions/upload-artifact@v3
if: failure()
with:
name: Upload screenshots
path: apps/${{ env.APP_NAME }}/cypress/screenshots/
retention-days: 5
- name: Upload nextcloud logs
uses: actions/upload-artifact@v3
if: failure()
with:
name: Upload nextcloud log
path: data/nextcloud.log
retention-days: 5

View File

@@ -1,36 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Dependabot
on:
pull_request_target:
branches:
- main
- master
- stable*
permissions:
contents: read
jobs:
auto-approve-merge:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
# for hmarr/auto-approve-action to approve PRs
pull-requests: write
steps:
# Github actions bot approve
- uses: hmarr/auto-approve-action@v2
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
# Nextcloud bot approve and merge request
- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
target: minor
github-token: ${{ secrets.DEPENDABOT_AUTOMERGE_TOKEN }}

View File

@@ -1,20 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Pull request checks
on: pull_request
jobs:
commit-message-check:
name: Block fixup and squash commits
runs-on: ubuntu-latest
steps:
- name: Run check
uses: xt0rted/block-autosquash-commits-action@v2
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -2,14 +2,6 @@ name: Integration tests
on:
pull_request:
paths:
- '.github/workflows/integration.yml'
- 'appinfo/**'
- 'lib/**'
- 'templates/**'
- 'tests/**'
- 'composer.json'
- 'composer.lock'
push:
branches:
- master
@@ -27,13 +19,13 @@ jobs:
matrix:
php-versions: ['7.4']
databases: ['sqlite', 'mysql', 'pgsql']
server-versions: ['master']
server-versions: ['stable22']
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
services:
postgres:
image: postgres:14
image: postgres
ports:
- 4445:5432/tcp
env:
@@ -51,7 +43,7 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
repository: nextcloud/server
ref: ${{ matrix.server-versions }}
@@ -62,15 +54,14 @@ jobs:
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
git submodule sync --recursive
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
cd build/integration && composer require --dev phpunit/phpunit:~9
- name: Checkout app
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
path: apps/${{ env.APP_NAME }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@2.21.2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: phpunit
@@ -79,7 +70,7 @@ jobs:
- name: Set up PHPUnit
working-directory: apps/${{ env.APP_NAME }}
run: composer i --no-dev
run: composer i
- name: Set up Nextcloud
run: |

View File

@@ -13,13 +13,13 @@ jobs:
strategy:
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['7.3', '7.4']
name: php${{ matrix.php-versions }} lint
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Set up php${{ matrix.php-versions }}
uses: shivammathur/setup-php@2.21.2
uses: shivammathur/setup-php@v1
with:
php-version: ${{ matrix.php-versions }}
coverage: none
@@ -31,9 +31,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@master
- name: Set up php
uses: shivammathur/setup-php@2.21.2
uses: shivammathur/setup-php@master
with:
php-version: 7.4
coverage: none
@@ -50,9 +50,9 @@ jobs:
node-version: [14.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Use node ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Set up npm7
@@ -67,16 +67,16 @@ jobs:
strategy:
matrix:
node-version: [14.x]
node-versions: [14.x]
name: stylelint node${{ matrix.node-version }}
name: stylelint node${{ matrix.node-versions }}
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v2
- name: Set up node ${{ matrix.node-version }}
uses: actions/setup-node@v3
- name: Set up node ${{ matrix.node-versions }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
node-versions: ${{ matrix.node-versions }}
- name: Set up npm7
run: npm i -g npm@7

View File

@@ -17,15 +17,15 @@ jobs:
node-version: [14.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Set up npm7
run: npm i -g npm@7
- name: Setup PHP
uses: shivammathur/setup-php@2.21.2
uses: shivammathur/setup-php@v1
with:
php-version: '7.4'
tools: composer

View File

@@ -12,9 +12,9 @@ jobs:
node-version: [14.x]
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v1
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Set up npm7

View File

@@ -2,14 +2,6 @@ name: PHPUnit
on:
pull_request:
paths:
- '.github/workflows/phpunit.yml'
- 'appinfo/**'
- 'lib/**'
- 'templates/**'
- 'tests/**'
- 'composer.json'
- 'composer.lock'
push:
branches:
- master
@@ -26,15 +18,15 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['7.3', '7.4']
databases: ['sqlite', 'mysql', 'pgsql']
server-versions: ['master']
server-versions: ['stable22']
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
services:
postgres:
image: postgres:14
image: postgres
ports:
- 4445:5432/tcp
env:
@@ -52,7 +44,7 @@ jobs:
steps:
- name: Checkout server
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
repository: nextcloud/server
ref: ${{ matrix.server-versions }}
@@ -65,12 +57,12 @@ jobs:
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
- name: Checkout app
uses: actions/checkout@v3
uses: actions/checkout@v2
with:
path: apps/${{ env.APP_NAME }}
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@2.21.2
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: phpunit

View File

@@ -1,35 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Static analysis
on:
pull_request:
push:
branches:
- master
- main
- stable*
jobs:
static-analysis:
runs-on: ubuntu-latest
name: Nextcloud
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up php
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
coverage: none
- name: Install dependencies
run: composer i
- name: Run coding standards check
run: composer run psalm

31
.github/workflows/static-analysis.yml vendored Normal file
View File

@@ -0,0 +1,31 @@
name: Static analysis
on:
pull_request:
push:
branches:
- master
- stable*
jobs:
static-psalm-analysis:
runs-on: ubuntu-latest
strategy:
matrix:
ocp-version: [ 'dev-stable22' ]
name: Nextcloud ${{ matrix.ocp-version }}
steps:
- name: Checkout
uses: actions/checkout@master
- name: Set up php
uses: shivammathur/setup-php@master
with:
php-version: 7.4
tools: composer:v1
coverage: none
- name: Install dependencies
run: composer i
- name: Install dependencies
run: composer require --dev christophwurst/nextcloud:${{ matrix.ocp-version }}
- name: Run coding standards check
run: composer run psalm

View File

@@ -1,65 +0,0 @@
# This workflow is provided via the organization template repository
#
# https://github.com/nextcloud/.github
# https://docs.github.com/en/actions/learn-github-actions/sharing-workflows-with-your-organization
name: Update nextcloud/ocp
on:
workflow_dispatch:
schedule:
- cron: "5 2 * * 0"
jobs:
update-nextcloud-ocp:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
branches: ["master", "stable25", "stable24", "stable23"]
name: update-nextcloud-ocp-${{ matrix.branches }}
steps:
- uses: actions/checkout@v3
with:
ref: ${{ matrix.branches }}
submodules: true
- name: Set up php7.4
uses: shivammathur/setup-php@v2
with:
php-version: 7.4
extensions: ctype,curl,dom,fileinfo,gd,intl,json,mbstring,openssl,pdo_sqlite,posix,sqlite,xml,zip
coverage: none
- name: Composer install
run: composer install
- name: Composer update nextcloud/ocp
run: composer require --dev nextcloud/ocp:dev-${{ matrix.branches }}
continue-on-error: true
- name: Reset checkout dirs
run: |
git clean -f 3rdparty
git clean -f vendor
git checkout 3rdparty vendor
continue-on-error: true
- name: Create Pull Request
uses: peter-evans/create-pull-request@v4
with:
token: ${{ secrets.COMMAND_BOT_PAT }}
commit-message: Update psalm baseline
committer: GitHub <noreply@github.com>
author: nextcloud-command <nextcloud-command@users.noreply.github.com>
signoff: true
branch: automated/noid/${{ matrix.branches }}-update-nextcloud-ocp
title: "[${{ matrix.branches }}] Update nextcloud/ocp dependency"
body: |
Auto-generated update of [nextcloud/ocp](https://github.com/nextcloud-deps/ocp/) dependency
labels: |
dependencies
3. to review

View File

@@ -1,6 +1,6 @@
[main]
host = https://www.transifex.com
lang_map = hu_HU: hu, nb_NO: nb, sk_SK: sk, th_TH: th, ja_JP: ja, bg_BG: bg, cs_CZ: cs, fi_FI: fi
lang_map = bg_BG: bg, cs_CZ: cs, fi_FI: fi, hu_HU: hu, nb_NO: nb, sk_SK: sk, th_TH: th, ja_JP: ja
[o:nextcloud:p:nextcloud:r:deck]
file_filter = translationfiles/<lang>/deck.po

View File

@@ -1,130 +1,124 @@
# Changelog
All notable changes to this project will be documented in this file.
## 1.8.0-beta.1
### Enhancements
## 1.5.8
- Nextcloud 25 compatibility
- Performance improvements
- Use capped memory cache for board permissions @juliushaertl [#3980](https://github.com/nextcloud/deck/pull/3980)
- Improve CalDAV integration performance @juliushaertl [#3982](https://github.com/nextcloud/deck/pull/3982)
- Simpify query for getting shared files @juliushaertl [#3983](https://github.com/nextcloud/deck/pull/3983)
- Accessibility improvements
- Add a11y label for sidebar button @marcelklehr [#3986](https://github.com/nextcloud/deck/pull/3986)
- Improve filter popover accessibility @juliushaertl [#3820](https://github.com/nextcloud/deck/pull/3820)
- Set ids to skip to content/navigation @juliushaertl [#3924](https://github.com/nextcloud/deck/pull/3924)
- Invert icons properly in dark mode @juliushaertl [#3939](https://github.com/nextcloud/deck/pull/3939)
- Bump dependencies
### Fixed
- Align Duedate-delete icon properly - fixes nextcloud/deck#3791 [#3817](https://github.com/nextcloud/deck/pull/3817)
- Increase file count after sharing [#3806](https://github.com/nextcloud/deck/pull/3806)
- Show cards after moving into another list [#3794](https://github.com/nextcloud/deck/pull/3794)
- Fetch full board data after cloning [#3781](https://github.com/nextcloud/deck/pull/3781)
## 1.5.7
### Added
- Transfer ownership @juliushaertl [#3665](https://github.com/nextcloud/deck/pull/3665)
### Fixed
- set last modified when the card was found. Fixes #3763 @ylebre [#3796](https://github.com/nextcloud/deck/pull/3796)
- Increase file count after sharing @luka-nextcloud [#3682](https://github.com/nextcloud/deck/pull/3682)
- Align Duedate-delete icon properly - fixes nextcloud/deck#3791 @Ben-Ro [#3811](https://github.com/nextcloud/deck/pull/3811)
- Fix for issue #3637 @flummer [#3833](https://github.com/nextcloud/deck/pull/3833)
- Switch to 'markdown-it-task-checkbox' for rendering of task lists @q-wertz [#3898](https://github.com/nextcloud/deck/pull/3898)
- Make rename functions accessibly by keyboard navigation @juliushaertl [#3813](https://github.com/nextcloud/deck/pull/3813)
- Prevent opening card and applyLabelFilter on card drag end @eneiluj [#3916](https://github.com/nextcloud/deck/pull/3916)
- Inserted required property in the rename list field, to prevent the l… @mstolf [#3862](https://github.com/nextcloud/deck/pull/3862)
- Fix share provider for master changes @nickvergessen [#3942](https://github.com/nextcloud/deck/pull/3942)
- Fetch attachment folder for the correct user during cron job @juliushaertl [#3952](https://github.com/nextcloud/deck/pull/3952)
- Fix z-index for deck sidebar @Raudius [#3884](https://github.com/nextcloud/deck/pull/3884)
- Fix: Check all circle shares for permissions [#3721](https://github.com/nextcloud/deck/pull/3721)
- Add a missing translation - not found in transifex [#3707](https://github.com/nextcloud/deck/pull/3707)
- 🐛 Fix missing files sidebar [#3642](https://github.com/nextcloud/deck/pull/3642)
- [stable23] Use explicit cast to make use of index [#3731](https://github.com/nextcloud/deck/pull/3731)
- Fix hidden attachment icon on archived cards [#3735](https://github.com/nextcloud/deck/pull/3735)
- [stable23] Sort boards non case sensitive [#3739](https://github.com/nextcloud/deck/pull/3739)
- add autofocus on board edit #3326 @juliushaertl [#3743](https://github.com/nextcloud/deck/pull/3743)
- Fix paramter replacements when creating deck cards from talk messages @juliushaertl [#3742](https://github.com/nextcloud/deck/pull/3742)
- Fix text selection in dark mode and modal view [#3767](https://github.com/nextcloud/deck/pull/3767)
### Other
- Switch from OC::$server->get to OCP\Server::get @CarlSchwan [#3801](https://github.com/nextcloud/deck/pull/3801)
- Add performance section in README @eneiluj [#3830](https://github.com/nextcloud/deck/pull/3830)
- Fix static analysis by stubbing more circle methods @juliushaertl [#3900](https://github.com/nextcloud/deck/pull/3900)
- fix(docs): fix links to JSON schemas for Trello @wiktor2200 [#3872](https://github.com/nextcloud/deck/pull/3872)
- Move to OCP\Collaboration\Resources\LoadAdditionalScriptsEvent @juliushaertl [#3818](https://github.com/nextcloud/deck/pull/3818)
- Rename settings to deck settings @PVince81 [#3928](https://github.com/nextcloud/deck/pull/3928)
- SCSS cleanup @juliushaertl [#3803](https://github.com/nextcloud/deck/pull/3803)
- Hide deprecated projects in sidebar and card details by default @Pytal [#3984](https://github.com/nextcloud/deck/pull/3984)
- Properly check for the stack AND setting board permissions [#3714](https://github.com/nextcloud/deck/pull/3714)
- Add missing indices [#3756](https://github.com/nextcloud/deck/pull/3756)
## 1.7.0
### Added
- Transfer ownership @matchish @luka-nextcloud @juliushaertl [#2496](https://github.com/nextcloud/deck/pull/2496)
- Import from trello via CLI @vitormattos [#3182](https://github.com/nextcloud/deck/pull/3182)
- Add app config to toggle the default calendar setting as an admin @juliushaertl [#3528](https://github.com/nextcloud/deck/pull/3528)
- Show board name in browser title @luka-nextcloud [#3499](https://github.com/nextcloud/deck/pull/3499)
- Move DeleteCron to be time insensitive @juliushaertl [#3599](https://github.com/nextcloud/deck/pull/3599)
- 🚸 Shows error on board fetchData @vinicius73 [#3653](https://github.com/nextcloud/deck/pull/3653)
- Add support for PHP 8.1 @juliushaertl [#3601](https://github.com/nextcloud/deck/pull/3601)
- Nextcloud 24 compatibility
## 1.5.6
### Fixed
- CardApiController: Fix order of optional parameters @simonspa [#3512](https://github.com/nextcloud/deck/pull/3512)
- Exclude deleted boards in the selection for target @luka-nextcloud [#3502](https://github.com/nextcloud/deck/pull/3502)
- Fix CalDAV blocking and modernize circles API usage @juliushaertl [#3500](https://github.com/nextcloud/deck/pull/3500)
- Timestamps on created and modified at values @luka-nextcloud [#3532](https://github.com/nextcloud/deck/pull/3532)
- return the selector for collections @dartcafe [#3552](https://github.com/nextcloud/deck/pull/3552)
- Generate fixed link for activity emails @luka-nextcloud [#3611](https://github.com/nextcloud/deck/pull/3611)
- 🐛 Fix missing files sidebar @vinicius73 [#3635](https://github.com/nextcloud/deck/pull/3635)
- Handle description shortening more gracefully @juliushaertl [#3650](https://github.com/nextcloud/deck/pull/3650)
- Sort boards non case sensitive @Ben-Ro [#3560](https://github.com/nextcloud/deck/pull/3560)
- Remove unused argument from transfer ownership @juliushaertl [#3712](https://github.com/nextcloud/deck/pull/3712)
- Fix: Check all circle shares for permissions @bink [#3625](https://github.com/nextcloud/deck/pull/3625)
- Extend API changelog @juliushaertl [#3522](https://github.com/nextcloud/deck/pull/3522)
- Fix talk integration @nickvergessen [#3529](https://github.com/nextcloud/deck/pull/3529)
- Fix confusion between stackId and boardId in StackService @eneiluj [#3541](https://github.com/nextcloud/deck/pull/3541)
- Add horizontal scrollbar into the large table inside description @luka-nextcloud [#3531](https://github.com/nextcloud/deck/pull/3531)
- Make links in markdown note bolder @luka-nextcloud [#3530](https://github.com/nextcloud/deck/pull/3530)
- Update master php testing versions @nickvergessen [#3561](https://github.com/nextcloud/deck/pull/3561)
- Update master php enviroment @nickvergessen [#3582](https://github.com/nextcloud/deck/pull/3582)
- Make insert attachment buttom easy to click @luka-nextcloud [#3612](https://github.com/nextcloud/deck/pull/3612)
- Remove extra bullet @elitejake [#3613](https://github.com/nextcloud/deck/pull/3613)
- l10n: Delete space @Valdnet [#3666](https://github.com/nextcloud/deck/pull/3666)
- Update master php testing versions @nickvergessen [#3688](https://github.com/nextcloud/deck/pull/3688)
- Fix wording to represent the code behavior @q-wertz [#3685](https://github.com/nextcloud/deck/pull/3685)
- Fix cron jobs @nickvergessen [#3689](https://github.com/nextcloud/deck/pull/3689)
- Update master php testing versions @nickvergessen [#3695](https://github.com/nextcloud/deck/pull/3695)
- Optimise queries when preparing card related notifications @Raudius [#3690](https://github.com/nextcloud/deck/pull/3690)
- Properly check for the stack AND setting board permissions @juliushaertl [#3670](https://github.com/nextcloud/deck/pull/3670)
- Replace deprecated String.prototype.substr() @CommanderRoot [#3669](https://github.com/nextcloud/deck/pull/3669)
- Dependency updates
- Show cards after moving into another list [#3736](https://github.com/nextcloud/deck/pull/3736)
- Fix paramter replacements when creating deck cards from talk messages @nickvergessen [#3683](https://github.com/nextcloud/deck/pull/3683)
- Fix hidden attachment icon on archived cards [#3733](https://github.com/nextcloud/deck/pull/3733)
- Adapt the card modal to upstream changes [#3764](https://github.com/nextcloud/deck/pull/3764)
- Fix text selection in dark mode and modal view [#3765](https://github.com/nextcloud/deck/pull/3765)
- Add missing indices [#3754](https://github.com/nextcloud/deck/pull/3754)
- Allow to download an attachment without navigating to the files app [#3441](https://api.github.com/repos/nextcloud/deck/pulls/3441)
- Fix CalDAV blocking and modernize circles API usage [#3527](https://api.github.com/repos/nextcloud/deck/pulls/3527)
- CardApiController: Fix order of optional parameters [#3521](https://api.github.com/repos/nextcloud/deck/pulls/3521)
- Fix cursor generation if no results are found [#3460](https://api.github.com/repos/nextcloud/deck/pulls/3460)
- Exclude deleted boards in the selection for target [#3524](https://api.github.com/repos/nextcloud/deck/pulls/3524)
- Generate fixed link for activity emails [#3627](https://api.github.com/repos/nextcloud/deck/pulls/3627)
- Make insert attachment buttom easy to click [#3615](https://api.github.com/repos/nextcloud/deck/pulls/3615)
- Fix confusion between stackId and boardId in StackService [#3544](https://api.github.com/repos/nextcloud/deck/pulls/3544)
## 1.5.5
## 1.6.0-beta1
- Fix release asset build
### Added
- #3177 Use async import for vue component on collections entrypoint @juliushaertl
- #2791 Open description links in new tab @fm-sys
- #3344 Improve combined search @eneiluj
- #3362 Improve search performance @eneiluj
- #2710 Due date shortcuts in the datepicker @jakobroehrl
## 1.5.4
### Fixed
- #3161 Reduce duplicate queries when fetching user boards an permissions @juliushaertl
- #3151 Always log generic exceptions @juliushaertl
- #3217 Move circle checks to a unified service and improve member checks @juliushaertl
- #3225 Check for null value to avoid TypeError in the group manager @juliushaertl
- #3263 Defer obtaining the user session in the config service @juliushaertl
- #3294 Fix print style issues @weeman1337
- #3299 Return false instead of throwing when getting calendar setting @juliushaertl
- #3298 Delete file shares through attachments API @juliushaertl
- #3343 Fix search pagination cursor @eneiluj
- #3326 add autofocus on board edit @weeman1337
- #3323 Extend drag-and-drop zone in card sidebar @old-green-frog
- #3364 Fix optional parameter order @juliushaertl
- #3324 Fix menu button position in card modal @valerydmitrieva
- #3391 Use displayname instead of uid for mentions (reopened against master) @kffl
- #3316 Additional check for stacks @juliushaertl
- #3357 Revert "Fix search pagination cursor" @juliushaertl
- #3327 Do not show both bullets and checkboxes for checklists @Themanwhosmellslikesugar
- #3375 Show absolute dates when printing @weeman1337
- #3376 Print assignee names @weeman1337
- #3384 Keep exceptions http response generic @juliushaertl
- #3378 Fix menu button position in card modal
- #3392 Use displayname instead of uid for mentions (reopened against master)
- #3361 Improve combined search @eneiluj
- #3381 Extend drag-and-drop zone in card sidebar @Artem4590
- #3366 Fix optional parameter order
- #3407 Keep exceptions http response generic
## 1.5.3
### Fied
- #3317 Additional check for stacks
## 1.5.2
### Fixed
- #3300 Fix print style issues
- #3303 Delete file shares through attachments API
- #3306 Return false instead of throwing when getting calendar setting
## 1.5.1 - 2021-09-03
### Fixed
- #3224 Move circle checks to a unified service and improve member checks
- #3231 Check for null value to avoid TypeError in the group manager
- #3264 Defer obtaining the user session in the config service
## 1.5.0 - 2021-07-09
### Added
* Nextcloud 22 compatibility
* [#3105](https://github.com/nextcloud/deck/pull/3105) Compatibility with Cirlces changes in 22
* [#3147](https://github.com/nextcloud/deck/pull/3147) Add card button to the dashboard widget @jakobroehrl
* [#2854](https://github.com/nextcloud/deck/pull/2854) Add card button in card overview @jakobroehrl
* [#3078](https://github.com/nextcloud/deck/pull/3078) Show on shared boards unassigned cards to all users @jakobroehrl
### Fixed
* [#2935](https://github.com/nextcloud/deck/pull/2935) Rich object string parameters for notifications @nickvergessen
* [#2950](https://github.com/nextcloud/deck/pull/2950) Remove notification on unshare and add type hints
* [#2983](https://github.com/nextcloud/deck/pull/2983) Fix codemirror description width
* [#2989](https://github.com/nextcloud/deck/pull/2989) Fix unified comments search with postgres
* [#3005](https://github.com/nextcloud/deck/pull/3005) Do not query the lookupserver when looking for sharees
* [#3011](https://github.com/nextcloud/deck/pull/3011) L10n: Spelling unification @Valdnet
* [#3014](https://github.com/nextcloud/deck/pull/3014) Proper error handling when fetching comments fails
* [#3016](https://github.com/nextcloud/deck/pull/3016) Allow searching for filters without a query to match all that have a given filter set
* [#3021](https://github.com/nextcloud/deck/pull/3021) L10n: Add word "Card" @Valdnet
* [#3025](https://github.com/nextcloud/deck/pull/3025) Show comment counter and highlight if unread comments are available
* [#3036](https://github.com/nextcloud/deck/pull/3036) Add link to migration tool for Trello @maxammann
* [#3037](https://github.com/nextcloud/deck/pull/3037) Catch any error during circle detail fetching
* [#3038](https://github.com/nextcloud/deck/pull/3038) Get attachment from the user node instead of the share source
* [#3092](https://github.com/nextcloud/deck/pull/3092) Refactor update to have proper order of optional parameters
* [#3113](https://github.com/nextcloud/deck/pull/3113) Use new viewer syntax with destructuring object @azul
* [#3142](https://github.com/nextcloud/deck/pull/3142) Always pass user id in share provider
* [#3152](https://github.com/nextcloud/deck/pull/3152) Only offer stack creation in emptycontent with proper permissions
* [#3165](https://github.com/nextcloud/deck/pull/3165) Always log generic exceptions
* [#3168](https://github.com/nextcloud/deck/pull/3168) Reduce duplicate queries when fetching user boards an permissions
## 1.4.0 - 2021-04-13
@@ -161,15 +155,15 @@ All notable changes to this project will be documented in this file.
## 1.3.0-beta2
### Fixed
* [#2700](https://github.com/nextcloud/deck/pull/2700) Attempt to copy file on dropping it to deck @juliushaertl
* [#2701](https://github.com/nextcloud/deck/pull/2701) Fix uploading files by drag and drop @juliushaertl
* [#2700](https://github.com/nextcloud/deck/pull/2700) Attempt to copy file on dropping it to deck
* [#2701](https://github.com/nextcloud/deck/pull/2701) Fix uploading files by drag and drop
* [#2707](https://github.com/nextcloud/deck/pull/2707) L10n: Change to a capital letter @Valdnet
* [#2712](https://github.com/nextcloud/deck/pull/2712) Docs: Fix table in section "GET /api/v1.0/config" @das-g
* [#2716](https://github.com/nextcloud/deck/pull/2716) Remove repair step which is no longer needed as we cleanup properly @juliushaertl
* [#2716](https://github.com/nextcloud/deck/pull/2716) Remove repair step which is no longer needed as we cleanup properly
* [#2723](https://github.com/nextcloud/deck/pull/2723) Pad random color with leading zeroes @PVince81
* [#2729](https://github.com/nextcloud/deck/pull/2729) Remove invalid activity parameters @nickvergessen
* [#2750](https://github.com/nextcloud/deck/pull/2750) Fix deck activity emails not being translated @nickvergessen
* [#2751](https://github.com/nextcloud/deck/pull/2751) Properly set author for activity events that are triggered by cron @juliushaertl
* [#2751](https://github.com/nextcloud/deck/pull/2751) Properly set author for activity events that are triggered by cron
## 1.2.2 - 2020-11-24
@@ -278,31 +272,31 @@ All notable changes to this project will be documented in this file.
### Fixed
* [#2116](https://github.com/nextcloud/deck/pull/2116) Fix navigation layout issues @juliushaertl
* [#2118](https://github.com/nextcloud/deck/pull/2118) Use proper parameter when handling attachments @juliushaertl
* [#2116](https://github.com/nextcloud/deck/pull/2116) Fix navigation layout issues
* [#2118](https://github.com/nextcloud/deck/pull/2118) Use proper parameter when handling attachments
## 1.0.4 - 2020-06-26
### Fixed
* [#2062](https://github.com/nextcloud/deck/pull/2062) Fix saving card description after toggling checkboxes @juliushaertl
* [#2062](https://github.com/nextcloud/deck/pull/2062) Fix saving card description after toggling checkboxes
* [#2065](https://github.com/nextcloud/deck/pull/2065) Adding CSS rule for Markdown Blockquotes @reox
* [#2059](https://github.com/nextcloud/deck/pull/2059) Fix fetching attachments on card change @juliushaertl
* [#2060](https://github.com/nextcloud/deck/pull/2060) Use mixing for relative date in card sidebar @juliushaertl
* [#2059](https://github.com/nextcloud/deck/pull/2059) Fix fetching attachments on card change
* [#2060](https://github.com/nextcloud/deck/pull/2060) Use mixing for relative date in card sidebar
## 1.0.3 - 2020-06-19
### Fixed
* [#2019](https://github.com/nextcloud/deck/pull/2019) Remove old global css rule @juliushaertl
* [#2020](https://github.com/nextcloud/deck/pull/2020) Fix navigation issue with leftover nodes @juliushaertl
* [#2021](https://github.com/nextcloud/deck/pull/2021) Fix description issues @juliushaertl
* [#2022](https://github.com/nextcloud/deck/pull/2022) Fix replyto issues with the comments API @juliushaertl
* [#2027](https://github.com/nextcloud/deck/pull/2027) Allow to unassign current user from card @juliushaertl
* [#2019](https://github.com/nextcloud/deck/pull/2019) Remove old global css rule
* [#2020](https://github.com/nextcloud/deck/pull/2020) Fix navigation issue with leftover nodes
* [#2021](https://github.com/nextcloud/deck/pull/2021) Fix description issues
* [#2022](https://github.com/nextcloud/deck/pull/2022) Fix replyto issues with the comments API
* [#2027](https://github.com/nextcloud/deck/pull/2027) Allow to unassign current user from card
* [#2029](https://github.com/nextcloud/deck/pull/2029) Fix wording : stack -> list @cloud2018
* [#2032](https://github.com/nextcloud/deck/pull/2032) Force order by id as second sorting key @juliushaertl
* [#2045](https://github.com/nextcloud/deck/pull/2045) Improve label styling @juliushaertl
* [#2032](https://github.com/nextcloud/deck/pull/2032) Force order by id as second sorting key
* [#2045](https://github.com/nextcloud/deck/pull/2045) Improve label styling
* [#2010](https://github.com/nextcloud/deck/pull/2010) User documentation fixes @Nyco
* [#1998](https://github.com/nextcloud/deck/pull/1998) Add Checklist explaination to the doc @4rnoP

View File

@@ -30,16 +30,6 @@ build: clean-dist install-deps build-js
release: clean-dist install-deps-nodev build-js
lint: lint-js lint-php
lint-js:
npm run lint
npm run stylelint
lint-php:
composer run lint 1>/dev/null
composer run cs:check
build-js: install-deps-js
npm run build

View File

@@ -24,8 +24,7 @@ Deck is a kanban style organization tool aimed at personal planning and project
### 3rd-Party Integrations
- [trello-to-deck](https://github.com/maxammann/trello-to-deck) - Migrates cards from Trello
- [mail2deck](https://github.com/newroco/mail2deck) - Provides an "email in" solution
- [A-deck](https://github.com/leoossa/A-deck) - Chrome Extension that allows to create new card in selected stack based on current tab
- [mail2deck](https://github.com/newroco/mail2deck) - Provides an "email in" solution
## Installation/Update
@@ -52,32 +51,14 @@ Please make sure you have installed the following dependencies: `make, which, ta
Instead of setting everything up manually, you can just [download the nightly build](https://github.com/nextcloud/deck/releases/tag/nightly) instead. These builds are updated every 24 hours, and are pre-configured with all the needed dependencies.
## Performance limitations
Deck is not yet ready for intensive usage.
A lot of database queries are generated when the number of boards, cards and attachments is high.
For example, a user having access to 13 boards, with each board having on average 100 cards,
and each card having on average 5 attachments,
would generate 6500 database queries when doing the file related queries
which would increase the page loading time significantly.
Improvements on Nextcloud server and Deck itself will improve the situation.
## Developing
### Nextcloud environment
You need to setup a [development environment](https://docs.nextcloud.com/server/latest/developer_manual//getting_started/devenv.html) of the current nextcloud version. You can also alternatively install & run the [nextcloud docker container](https://github.com/juliushaertl/nextcloud-docker-dev).
After the finished installation, you can clone the deck project directly in the `/[nextcloud-docker-dev-dir]/workspace/server/apps/` folder.
### PHP
Nothing to prepare, just dig into the code.
### JavaScript
This requires at least Node 16 and npm 7 to be installed.
Deck requires running a `make build-js` to install npm dependencies and build the JavaScript code using webpack. While developing you can also use `make watch` to rebuild everytime the code changes.
#### Hot reloading

View File

@@ -7,16 +7,16 @@
- 📥 Add your tasks to cards and put them in order
- 📄 Write down additional notes in Markdown
- 📄 Write down additional notes in markdown
- 🔖 Assign labels for even better organization
- 👥 Share with your team, friends or family
- 📎 Attach files and embed them in your Markdown description
- 📎 Attach files and embed them in your markdown description
- 💬 Discuss with your team using comments
- ⚡ Keep track of changes in the activity stream
- 🚀 Get your project organized
</description>
<version>1.9.0-beta.1</version>
<version>1.5.8</version>
<licence>agpl</licence>
<author>Julius Härtl</author>
<namespace>Deck</namespace>
@@ -31,10 +31,11 @@
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/1.0/Deck-1.png</screenshot>
<screenshot>https://download.bitgrid.net/nextcloud/deck/screenshots/1.0/Deck-2.png</screenshot>
<dependencies>
<php min-version="7.3"/>
<database min-version="9.4">pgsql</database>
<database>sqlite</database>
<database min-version="8.0">mysql</database>
<nextcloud min-version="26" max-version="26"/>
<database min-version="5.5">mysql</database>
<nextcloud min-version="22" max-version="22"/>
</dependencies>
<background-jobs>
<job>OCA\Deck\Cron\DeleteCron</job>
@@ -43,7 +44,6 @@
</background-jobs>
<commands>
<command>OCA\Deck\Command\UserExport</command>
<command>OCA\Deck\Command\BoardImport</command>
<command>OCA\Deck\Command\TransferOwnership</command>
</commands>
<activity>

View File

@@ -92,10 +92,6 @@ return [
['name' => 'board_api#deleteAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl/{aclId}', 'verb' => 'DELETE'],
['name' => 'board_api#updateAcl', 'url' => '/api/v{apiVersion}/boards/{boardId}/acl/{aclId}', 'verb' => 'PUT'],
['name' => 'board_import_api#getAllowedSystems', 'url' => '/api/v{apiVersion}/boards/import/getSystems','verb' => 'GET'],
['name' => 'board_import_api#getConfigSchema', 'url' => '/api/v{apiVersion}/boards/import/config/schema/{name}','verb' => 'GET'],
['name' => 'board_import_api#import', 'url' => '/api/v{apiVersion}/boards/import','verb' => 'POST'],
['name' => 'stack_api#index', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks', 'verb' => 'GET'],
['name' => 'stack_api#getArchived', 'url' => '/api/v{apiVersion}/boards/{boardId}/stacks/archived', 'verb' => 'GET'],

View File

@@ -8,35 +8,32 @@
"email": "jus@bitgrid.net"
}
],
"config": {
"platform": {
"php": "7.3"
}
},
"require": {
"cogpowered/finediff": "0.3.*",
"justinrainbow/json-schema": "^5.2"
"cogpowered/finediff": "0.3.*"
},
"require-dev": {
"roave/security-advisories": "dev-master",
"phpunit/phpunit": "^9",
"nextcloud/coding-standard": "^1.0.0",
"christophwurst/nextcloud": "^22@dev",
"phpunit/phpunit": "^8",
"nextcloud/coding-standard": "^0.5.0",
"symfony/event-dispatcher": "^4.0",
"vimeo/psalm": "^4.3",
"php-parallel-lint/php-parallel-lint": "^1.2",
"nextcloud/ocp": "dev-master"
"php-parallel-lint/php-parallel-lint": "^1.2"
},
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true,
"allow-plugins": {
"composer/package-versions-deprecated": true
},
"platform": {
"php": "7.4"
}
"classmap-authoritative": true
},
"scripts": {
"lint": "find . -name \\*.php -not -path './vendor/*' -print0 | xargs -0 -n1 php -l",
"cs:check": "php-cs-fixer fix --dry-run --diff",
"cs:fix": "php-cs-fixer fix",
"psalm": "psalm",
"psalm:update-baseline": "psalm --update-baseline",
"psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType",
"test": [
"@test:unit",
@@ -44,10 +41,5 @@
],
"test:unit": "phpunit -c tests/phpunit.xml",
"test:integration": "phpunit -c tests/phpunit.integration.xml && cd tests/integration && ./run.sh"
},
"autoload-dev": {
"psr-4": {
"OCP\\": "vendor/nextcloud/ocp/OCP"
}
}
}

1818
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
.icon-deck {
background-image: url(../img/deck-dark.svg);
filter: var(--background-invert-if-dark);
}
.icon-deck-white, .icon-deck.icon-white {
background-image: url(../img/deck.svg);
filter: var(--background-invert-if-dark);
}

1
css/deck.scss Normal file
View File

@@ -0,0 +1 @@
@include icon-black-white('deck', 'deck', 1);

View File

@@ -1,8 +1,11 @@
<?php
/**
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
/*
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
*
* @author Raul Ferreira Fuentes <raul@nextcloud.com>
* @author Julius Härtl <jus@bitgrid.net>
* @author Artem Anufrij <artem.anufrij@live.de>
* @author Marin Treselj <marin@pixelipo.com>
* @author Oskar Kurz <oskar.kurz@gmail.com>
* @author Ryan Fletcher <ryan.fletcher@codepassion.ca>
*
* @license GNU AGPL version 3 or any later version
*
@@ -20,26 +23,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Model;
use OCA\Deck\Db\Board;
class BoardSummary extends Board {
private Board $board;
public function __construct(Board $board) {
parent::__construct();
$this->board = $board;
}
public function jsonSerialize(): array {
return [
'id' => $this->getId(),
'title' => $this->getTitle()
];
}
public function __call($name, $arguments) {
return $this->board->__call($name, $arguments);
}
}
@import 'icons';
@import 'print';

41
css/icons.scss Normal file
View File

@@ -0,0 +1,41 @@
/**
* Custom icons
*/
@include icon-black-white('deck', 'deck', 1);
@include icon-black-white('archive', 'deck', 1);
@include icon-black-white('circles', 'deck', 1);
@include icon-black-white('clone', 'deck', 1);
@include icon-black-white('filter', 'deck', 1);
@include icon-black-white('filter_set', 'deck', 1);
@include icon-black-white('attach', 'deck', 1);
@include icon-black-white('reply', 'deck', 1);
@include icon-black-white('notifications-dark', 'deck', 1);
@include icon-black-white('description', 'deck', 1);
.icon-toggle-compact-collapsed {
@include icon-color('toggle-view-expand', 'deck', $color-black);
}
.icon-toggle-compact-expanded {
@include icon-color('toggle-view-collapse', 'deck', $color-black);
}
.icon-activity {
@include icon-color('activity-dark', 'activity', $color-black);
}
.icon-comment--unread {
@include icon-color('comment', 'actions', $color-primary, 1, true);
}
.avatardiv.circles {
background: var(--color-primary);
}
.icon-circles {
opacity: 1;
background-size: 20px;
background-position: center center;
}
.icon-colorpicker {
background-image: url('../img/color_picker.svg');
}

View File

@@ -1,17 +0,0 @@
const { defineConfig } = require('cypress')
module.exports = defineConfig({
projectId: '1s7wkc',
viewportWidth: 1280,
viewportHeight: 720,
e2e: {
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
baseUrl: 'http://nextcloud.local/index.php',
experimentalSessionAndOrigin: true,
specPattern: 'cypress/e2e/**/*.{js,jsx,ts,tsx}',
},
})

View File

@@ -1,41 +0,0 @@
import { randHash } from '../utils'
const randUser = randHash()
describe('Board', function() {
const password = 'pass123'
before(function() {
cy.nextcloudCreateUser(randUser, password)
})
beforeEach(function() {
cy.login(randUser, password)
})
it('Can create a board', function() {
const board = 'Test'
cy.intercept({
method: 'POST',
url: '/index.php/apps/deck/boards',
}).as('createBoardRequest')
// Click "Add board"
cy.openLeftSidebar()
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
.eq(3).find('a').first().click({ force: true })
// Type the board title
cy.get('.board-create form input[type=text]')
.type(board, { force: true })
// Submit
cy.get('.board-create form input[type=submit]')
.first().click({ force: true })
cy.wait('@createBoardRequest').its('response.statusCode').should('equal', 200)
cy.get('.app-navigation__list .app-navigation-entry__children .app-navigation-entry')
.contains(board).should('be.visible')
})
})

View File

@@ -1,67 +0,0 @@
import { randHash } from '../utils'
const randUser = randHash()
const testBoardData = {
title: 'MyBoardTest',
color: '00ff00',
stacks: [
{
title: 'TestList',
cards: [
{
title: 'Hello world',
},
],
},
],
}
describe('Card', function() {
before(function() {
cy.nextcloudCreateUser(randUser, randUser)
cy.createExampleBoard({
user: randUser,
password: randUser,
board: testBoardData,
})
})
beforeEach(function() {
cy.login(randUser, randUser)
})
it('Can show card details modal', function() {
cy.openLeftSidebar()
cy.getNavigationEntry(testBoardData.title)
.first().click({ force: true })
cy.get('.board .stack').eq(0).within(() => {
cy.get('.card:contains("Hello world")').should('be.visible').click()
})
cy.get('.modal__card').should('be.visible')
cy.get('.app-sidebar-header__maintitle').contains('Hello world')
})
it('Can add a card', function() {
const newCardTitle = 'Write some cypress tests'
cy.openLeftSidebar()
cy.getNavigationEntry(testBoardData.title)
.first().click({ force: true })
cy.get('.board .stack').eq(0).within(() => {
cy.get('.card:contains("Hello world")').should('be.visible')
cy.get('.button-vue[aria-label*="Add card"]')
.first().click()
cy.get('.stack__card-add form input#new-stack-input-main')
.type(newCardTitle)
cy.get('.stack__card-add form input[type=submit]')
.first().click()
cy.get(`.card:contains("${newCardTitle}")`).should('be.visible')
})
})
})

View File

@@ -1,31 +0,0 @@
import { randHash } from '../utils'
const randUser = randHash()
describe('Deck dashboard', function() {
const password = 'pass123'
before(function() {
cy.nextcloudCreateUser(randUser, password)
})
beforeEach(function() {
cy.login(randUser, password)
})
it('Can show the right title on the dashboard', function() {
cy.get('.board-title h2')
.should('have.length', 1).first()
.should('have.text', 'Upcoming cards')
})
it('Can see the default "Personal Board" created for user by default', function() {
const defaultBoard = 'Personal'
cy.openLeftSidebar()
cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + defaultBoard + ')')
.first()
.contains(defaultBoard)
.should('be.visible')
})
})

View File

@@ -1,30 +0,0 @@
import { randHash } from '../utils'
const randUser = randHash()
describe('Stack', function() {
const board = 'TestBoard'
const password = 'pass123'
const stack = 'List 1'
before(function() {
cy.nextcloudCreateUser(randUser, password)
cy.deckCreateBoard({ user: randUser, password }, board)
})
beforeEach(function() {
cy.logout()
cy.login(randUser, password)
})
it('Can create a stack', function() {
cy.openLeftSidebar()
cy.getNavigationEntry(board)
.click({ force: true })
cy.get('#stack-add button').first().click()
cy.get('#stack-add form input#new-stack-input-main').type(stack)
cy.get('#stack-add form input[type=submit]').first().click()
cy.get('.board .stack').eq(0).contains(stack).should('be.visible')
})
})

View File

@@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@@ -1,22 +0,0 @@
/// <reference types="cypress" />
// ***********************************************************
// This example plugins/index.js can be used to load plugins
//
// You can change the location of this file or turn off loading
// the plugins file with the 'pluginsFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/plugins-guide
// ***********************************************************
// This function is called when a project is opened or re-opened (e.g. due to
// the project's config changing)
/**
* @type {Cypress.PluginConfig}
*/
// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
// `config` is the resolved Cypress config
}

View File

@@ -1,159 +0,0 @@
/**
* @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author John Molakvoæ <skjnldsv@protonmail.com>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
const url = Cypress.config('baseUrl').replace(/\/index.php\/?$/g, '')
Cypress.env('baseUrl', url)
Cypress.Commands.add('login', (user, password, route = '/apps/deck/') => {
const session = `${user}-${Date.now()}`
cy.session(session, function() {
cy.visit(route)
cy.get('input[name=user]').type(user)
cy.get('input[name=password]').type(password)
cy.get('form[name=login] [type=submit]').click()
cy.url().should('include', route)
})
cy.visit(route)
})
Cypress.Commands.add('logout', (route = '/') => {
cy.session('_guest', function() {})
})
Cypress.Commands.add('nextcloudCreateUser', (user, password) => {
cy.clearCookies()
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/ocs/v1.php/cloud/users?format=json`,
form: true,
body: {
userid: user,
password,
},
auth: { user: 'admin', pass: 'admin' },
headers: {
'OCS-ApiRequest': 'true',
'Content-Type': 'application/x-www-form-urlencoded',
},
}).then((response) => {
cy.log(`Created user ${user}`, response.status)
})
})
Cypress.Commands.add('nextcloudUpdateUser', (user, password, key, value) => {
cy.request({
method: 'PUT',
url: `${Cypress.env('baseUrl')}/ocs/v2.php/cloud/users/${user}`,
form: true,
body: { key, value },
auth: { user, pass: password },
headers: {
'OCS-ApiRequest': 'true',
'Content-Type': 'application/x-www-form-urlencoded',
},
}).then((response) => {
cy.log(`Updated user ${user} ${key} to ${value}`, response.status)
})
})
Cypress.Commands.add('openLeftSidebar', () => {
cy.get('.app-navigation button.app-navigation-toggle').click()
})
Cypress.Commands.add('deckCreateBoard', ({ user, password }, title) => {
cy.login(user, password)
cy.get('.app-navigation button.app-navigation-toggle').click()
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
.eq(3)
.find('a')
.first()
.click({ force: true })
cy.get('.board-create form input[type=text]').type(title, { force: true })
cy.get('.board-create form input[type=submit]')
.first()
.click({ force: true })
})
Cypress.Commands.add('deckCreateList', ({ user, password }, title) => {
cy.login(user, password)
cy.get('.app-navigation button.app-navigation-toggle').click()
cy.get('#app-navigation-vue .app-navigation__list .app-navigation-entry')
.eq(3)
.find('a.app-navigation-entry-link')
.first()
.click({ force: true })
cy.get('#stack-add button').first().click()
cy.get('#stack-add form input#new-stack-input-main').type(title)
cy.get('#stack-add form input[type=submit]').first().click()
})
Cypress.Commands.add('createExampleBoard', ({ user, password, board }) => {
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards`,
auth: {
user,
password,
},
body: { title: board.title, color: board.color ?? 'ff0000' },
}).then((boardResponse) => {
expect(boardResponse.status).to.eq(200)
const boardData = boardResponse.body
for (const stackIndex in board.stacks) {
const stack = board.stacks[stackIndex]
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks`,
auth: {
user,
password,
},
body: { title: stack.title, order: 0 },
}).then((stackResponse) => {
const stackData = stackResponse.body
for (const cardIndex in stack.cards) {
const card = stack.cards[cardIndex]
cy.request({
method: 'POST',
url: `${Cypress.env('baseUrl')}/index.php/apps/deck/api/v1.0/boards/${boardData.id}/stacks/${stackData.id}/cards`,
auth: {
user,
password,
},
body: { title: card.title },
})
}
})
}
})
})
Cypress.Commands.add('getNavigationEntry', (boardTitle) => {
return cy.get('.app-navigation-entry-wrapper[icon=icon-deck]')
.find('ul.app-navigation-entry__children .app-navigation-entry:contains(' + boardTitle + ')')
.find('a.app-navigation-entry-link')
})

View File

@@ -1,20 +0,0 @@
// ***********************************************************
// This example support/index.js is processed and
// loaded automatically before your test files.
//
// This is a great place to put global configuration and
// behavior that modifies Cypress.
//
// You can change the location of this file or turn off
// automatically serving support files with the
// 'supportFile' configuration option.
//
// You can read more here:
// https://on.cypress.io/configuration
// ***********************************************************
// Import commands.js using ES2015 syntax:
import './commands'
// Alternatively you can use CommonJS syntax:
// require('./commands')

View File

@@ -1 +0,0 @@
export const randHash = () => Math.random().toString(36).replace(/[^a-z]+/g, '').slice(0, 10)

View File

@@ -96,27 +96,10 @@ If available the ETag will also be part of JSON response objects as shown below
# Changelog
## API version 1.0
- Deck >=1.0.0: The maximum length of the card title has been extended from 100 to 255 characters
- Deck >=1.0.0: The API will now return a 400 Bad request response if the length limitation of a board, stack or card title is exceeded
## API version 1.1
This API version has become available with **Deck 1.3.0**.
## 1.0.0 (unreleased)
- The maximum length of the card title has been extended from 100 to 255 characters
- The API will now return a 400 Bad request response if the length limitation of a board, stack or card title is exceeded
- The attachments API endpoints will return other attachment types than deck_file
- 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
## API version 1.2 (unreleased)
- Endpoints for the new import functionality have been added:
- [GET /boards/import/getSystems - Import a board](#get-boardsimportgetsystems-import-a-board)
- [GET /boards/import/config/system/{schema} - Import a board](#get-boardsimportconfigsystemschema-import-a-board)
- [POST /boards/import - Import a board](#post-boardsimport-import-a-board)
# Endpoints
@@ -944,8 +927,7 @@ The request can fail with a bad request response for the following reasons:
| type | String | The type of the attachement |
| file | Binary | File data to add as an attachment |
- 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
For now only `deck_file` is supported as an attachment type.
#### Response
@@ -1006,49 +988,6 @@ For now only `deck_file` is supported as an attachment type.
##### 200 Success
### GET /boards/import/getSystems - Import a board
#### Request parameters
| Parameter | Type | Description |
| ------------ | ------- | --------------------------------------------- |
| system | Integer | The system name. Example: trello |
#### Response
Make a request to see the json schema of system
```json
{
}
```
### GET /boards/import/config/system/{schema} - Import a board
#### Request parameters
#### Response
```json
[
"trello"
]
```
### POST /boards/import - Import a board
#### Request parameters
| Parameter | Type | Description |
| ------------ | ------- | --------------------------------------------- |
| system | string | The allowed name of system to import from |
| config | Object | The config object (JSON) |
| data | Object | The data object to import (JSON) |
#### Response
##### 200 Success
# OCS API
The following endpoints are available through the Nextcloud OCS endpoint, which is available at `/ocs/v2.php/apps/deck/api/v1.0/`.
@@ -1065,7 +1004,6 @@ Deck stores user and app configuration values globally and per board. The GET en
| Config key | Description |
| --- | --- |
| calendar | Determines if the calendar/tasks integration through the CalDAV backend is enabled for the user (boolean) |
| cardDetailsInModal | Determines if the bigger view is used (boolean) |
| groupLimit | Determines if creating new boards is limited to certain groups of the instance. The resulting output is an array of group objects with the id and the displayname (Admin only)|
```
@@ -1078,7 +1016,6 @@ Deck stores user and app configuration values globally and per board. The GET en
},
"data": {
"calendar": true,
"cardDetailsInModal": true,
"groupLimit": [
{
"id": "admin",
@@ -1108,7 +1045,6 @@ Deck stores user and app configuration values globally and per board. The GET en
| --- | ----- |
| notify-due | `off`, `assigned` or `all` |
| calendar | Boolean |
| cardDetailsInModal | Boolean |
#### Example request

View File

@@ -14,9 +14,7 @@ Overall, Deck is easy to use. You can create boards, add users, share the Deck,
3. [Handle cards options](#3-handle-cards-options)
4. [Archive old tasks](#4-archive-old-tasks)
5. [Manage your board](#5-manage-your-board)
6. [Import boards](#6-import-boards)
7. [Search](#7-search)
8. [New owner for the deck entities](#8-new-owner-for-the-deck-entities)
6. [New owner for the deck entities](#8-new-owner-for-the-deck-entities)
### 1. Create my first board
In this example, we're going to create a board and share it with an other nextcloud user.
@@ -72,80 +70,14 @@ The **sharing tab** allows you to add users or even groups to your boards.
**Deleted objects** allows you to return previously deleted stacks or cards.
The **Timeline** allows you to see everything that happened in your boards. Everything!
### 6. Import boards
Importing can be done using the API or the `occ` `deck:import` command.
Comments with more than 1000 characters are placed as attached files to the card.
It is possible to import from the following sources:
#### Trello JSON
Steps:
* Create the data file
* Access Trello
* go to the board you want to export
* Follow the steps in [Trello documentation](https://help.trello.com/article/747-exporting-data-from-trello-1) and export as JSON
* Create the configuration file
* Execute the import informing the import file path, data file and source as `Trello JSON`
Create the configuration file respecting the [JSON Schema](https://github.com/nextcloud/deck/blob/master/lib/Service/Importer/fixtures/config-trelloJson-schema.json) for import `Trello JSON`
Example configuration file:
```json
{
"owner": "admin",
"color": "0800fd",
"uidRelation": {
"johndoe": "johndoe"
}
}
```
**Limitations**:
Importing from a JSON file imports up to 1000 actions. To find out how many actions the board to be imported has, identify how many actions the JSON has.
#### Trello API
Import using API is recommended for boards with more than 1000 actions.
Trello makes it possible to attach links to a card. Deck does not have this feature. Attachments and attachment links are added in a markdown table at the end of the description for every imported card that has attachments in Trello.
* Get the API Key and API Token [here](https://developer.atlassian.com/cloud/trello/guides/rest-api/api-introduction/#authentication-and-authorization)
* Get the ID of the board you want to import by making a request to:
https://api.trello.com/1/members/me/boards?key={yourKey}&token={yourToken}&fields=id,name
This ID you will use in the configuration file in the `board` property
* Create the configuration file
Create the configuration file respecting the [JSON Schema](https://github.com/nextcloud/deck/blob/master/lib/Service/Importer/fixtures/config-trelloApi-schema.json) for import `Trello JSON`
Example configuration file:
```json
{
"owner": "admin",
"color": "0800fd",
"api": {
"key": "0cc175b9c0f1b6a831c399e269772661",
"token": "92eb5ffee6ae2fec3ad71c777531578f4a8a08f09d37b73795649038408b5f33"
},
"board": "8277e0910d750195b4487976",
"uidRelation": {
"johndoe": "johndoe"
}
}
```
### 7. Search
## Search
Deck provides a global search either through the unified search in the Nextcloud header or with the inline search next to the board controls.
This search allows advanced filtering of cards across all board of the logged in user.
For example the search `project tag:ToDo assigned:alice assigned:bob` will return all cards where the card title or description contains project **and** the tag ToDo is set **and** the user alice is assigned **and** the user bob is assigned.
#### Supported search filters
### Supported search filters
| Filter | Operators | Query |
| ----------- | ----------------- | ------------------------------------------------------------ |

View File

@@ -1,32 +0,0 @@
## Implement import
* Create a new importer class extending `ABoardImportService`
* Create a listener for event `BoardImportGetAllowedEvent` to enable your importer.
> You can read more about listeners on [Nextcloud](https://docs.nextcloud.com/server/latest/developer_manual/basics/events.html?highlight=event#writing-a-listener) doc.
Example:
```php
class YourCustomImporterListener {
public function handle(Event $event): void {
if (!($event instanceof BoardImportGetAllowedEvent)) {
return;
}
$event->getService()->addAllowedImportSystem([
'name' => YourCustomImporterService::$name,
'class' => YourCustomImporterService::class,
'internalName' => 'YourCustomImporter'
]);
}
}
```
* Register your listener on your `Application` class like this:
```php
$dispatcher = $this->getContainer()->query(IEventDispatcher::class);
$dispatcher->registerEventListener(
BoardImportGetAllowedEvent::class,
YourCustomImporterListener::class
);
```
* Use the `lib/Service/Importer/Systems/TrelloJsonService.php` class as inspiration

View File

@@ -1,7 +0,0 @@
## Import class diagram
Importing boards to the Deck implements the class diagram below.
> **NOTE**: When making any changes to the structure of the classes or implementing import from other sources, edit the `BoardImport.yuml` file
![Screenshot](resources/BoardImport.svg)

View File

@@ -1,214 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Generated by graphviz version 2.40.1 (20161225.0304)
-->
<!-- Title: G Pages: 1 -->
<svg width="417pt" height="830pt"
viewBox="0.00 0.00 417.01 830.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 826)">
<title>G</title>
<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-826 413.012,-826 413.012,4 -4,4"/>
<!-- A0 -->
<g id="node1" class="node">
<title>A0</title>
<polygon fill="#fff8dc" stroke="#000000" points="165.909,-822 70.091,-822 70.091,-766 171.909,-766 171.909,-816 165.909,-822"/>
<polyline fill="none" stroke="#000000" points="165.909,-822 165.909,-816 "/>
<polyline fill="none" stroke="#000000" points="171.909,-816 165.909,-816 "/>
<text text-anchor="middle" x="121" y="-809" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Classes used on</text>
<text text-anchor="middle" x="121" y="-797" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">board import.</text>
<text text-anchor="middle" x="121" y="-785" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Methods just to</text>
<text text-anchor="middle" x="121" y="-773" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">illustrate.</text>
</g>
<!-- A1 -->
<g id="node2" class="node">
<title>A1</title>
<polygon fill="none" stroke="#000000" points="108.7773,-680 23.2227,-680 23.2227,-644 108.7773,-644 108.7773,-680"/>
<text text-anchor="middle" x="66" y="-659" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ApiController</text>
</g>
<!-- A2 -->
<g id="node3" class="node">
<title>A2</title>
<polygon fill="none" stroke="#000000" points="0,-514 0,-546 132,-546 132,-514 0,-514"/>
<text text-anchor="start" x="9.607" y="-527" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">BoardImportApiController</text>
<polygon fill="none" stroke="#000000" points="0,-458 0,-514 132,-514 132,-458 0,-458"/>
<text text-anchor="start" x="45.8645" y="-495" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+import()</text>
<text text-anchor="start" x="16.1335" y="-483" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+getAllowedSystems()</text>
<text text-anchor="start" x="20.0185" y="-471" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+getConfigSchema()</text>
</g>
<!-- A1&#45;&gt;A2 -->
<g id="edge1" class="edge">
<title>A1&#45;&gt;A2</title>
<path fill="none" stroke="#000000" d="M66,-633.6693C66,-609.4424 66,-574.1663 66,-546.2238"/>
<polygon fill="#000000" stroke="#000000" points="66,-643.957 61.5001,-633.9569 66,-638.957 66.0001,-633.957 66.0001,-633.957 66.0001,-633.957 66,-638.957 70.5001,-633.957 66,-643.957 66,-643.957"/>
</g>
<!-- A3 -->
<g id="node4" class="node">
<title>A3</title>
<polygon fill="none" stroke="#000000" points="92,-364 92,-396 200,-396 200,-364 92,-364"/>
<text text-anchor="start" x="101.828" y="-377" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">BoardImportService</text>
<polygon fill="none" stroke="#000000" points="92,-284 92,-364 200,-364 200,-284 92,-284"/>
<text text-anchor="start" x="125.8645" y="-345" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+import()</text>
<text text-anchor="start" x="118.9105" y="-333" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+bootstrap()</text>
<text text-anchor="start" x="105.857" y="-321" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+validateSystem()</text>
<text text-anchor="start" x="108.218" y="-309" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#validateConfig()</text>
<text text-anchor="start" x="112.107" y="-297" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#validateData()</text>
</g>
<!-- A2&#45;&gt;A3 -->
<g id="edge2" class="edge">
<title>A2&#45;&gt;A3</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M87.8604,-457.7328C95.8577,-441.5382 105.0823,-422.8583 113.7939,-405.2174"/>
<polygon fill="#000000" stroke="#000000" points="118.2935,-396.1057 117.9004,-407.0646 116.0795,-400.5889 113.8656,-405.072 113.8656,-405.072 113.8656,-405.072 116.0795,-400.5889 109.8308,-403.0795 118.2935,-396.1057 118.2935,-396.1057"/>
<text text-anchor="middle" x="88.3076" y="-434.7378" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">uses</text>
</g>
<!-- A7 -->
<g id="node8" class="node">
<title>A7</title>
<polygon fill="none" stroke="#000000" points="37,-196 37,-228 129,-228 129,-196 37,-196"/>
<text text-anchor="start" x="46.612" y="-209" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">TrelloApiService</text>
<polygon fill="none" stroke="#000000" points="37,-164 37,-196 129,-196 129,-164 37,-164"/>
<text text-anchor="start" x="53.9655" y="-177" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+name:string</text>
</g>
<!-- A3&#45;&gt;A7 -->
<g id="edge6" class="edge">
<title>A3&#45;&gt;A7</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M114.8609,-283.9135C107.8316,-268.5143 100.7854,-252.0928 95.0404,-237.6613"/>
<polygon fill="#000000" stroke="#000000" points="91.2872,-228.0253 99.1098,-235.7102 93.1019,-232.6844 94.9167,-237.3434 94.9167,-237.3434 94.9167,-237.3434 93.1019,-232.6844 90.7235,-238.9767 91.2872,-228.0253 91.2872,-228.0253"/>
<text text-anchor="middle" x="99.6759" y="-267.8975" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">uses</text>
</g>
<!-- A9 -->
<g id="node10" class="node">
<title>A9</title>
<polygon fill="none" stroke="#000000" points="148,-202 148,-234 273,-234 273,-202 148,-202"/>
<text text-anchor="start" x="170.7765" y="-215" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">TrelloJsonService</text>
<polygon fill="none" stroke="#000000" points="148,-158 148,-202 273,-202 273,-158 148,-158"/>
<text text-anchor="start" x="181.4655" y="-183" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+name:string</text>
<text text-anchor="start" x="157.981" y="-171" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#needValidateData:true</text>
</g>
<!-- A3&#45;&gt;A9 -->
<g id="edge9" class="edge">
<title>A3&#45;&gt;A9</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M164.3261,-283.9135C170.0039,-270.5688 176.3462,-256.4563 182.4816,-243.5365"/>
<polygon fill="#000000" stroke="#000000" points="186.9002,-234.3677 186.6126,-245.3298 184.7295,-238.872 182.5588,-243.3762 182.5588,-243.3762 182.5588,-243.3762 184.7295,-238.872 178.505,-241.4226 186.9002,-234.3677 186.9002,-234.3677"/>
<text text-anchor="middle" x="163.6874" y="-260.9237" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">uses</text>
</g>
<!-- A10 -->
<g id="node11" class="node">
<title>A10</title>
<polygon fill="#fff8dc" stroke="#000000" points="317.7872,-362 218.2128,-362 218.2128,-318 323.7872,-318 323.7872,-356 317.7872,-362"/>
<polyline fill="none" stroke="#000000" points="317.7872,-362 317.7872,-356 "/>
<polyline fill="none" stroke="#000000" points="323.7872,-356 317.7872,-356 "/>
<text text-anchor="middle" x="271" y="-349" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">validateSystem is</text>
<text text-anchor="middle" x="271" y="-337" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">public because is</text>
<text text-anchor="middle" x="271" y="-325" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">used on Api.</text>
</g>
<!-- A3&#45;&gt;A10 -->
<g id="edge11" class="edge">
<title>A3&#45;&gt;A10</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M200.1992,-340C206.1915,-340 212.1837,-340 218.176,-340"/>
</g>
<!-- A4 -->
<g id="node5" class="node">
<title>A4</title>
<polygon fill="none" stroke="#000000" points="264.1131,-812 189.8869,-812 189.8869,-776 264.1131,-776 264.1131,-812"/>
<text text-anchor="middle" x="227" y="-791" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">Command</text>
</g>
<!-- A5 -->
<g id="node6" class="node">
<title>A5</title>
<polygon fill="none" stroke="#000000" points="148,-684 148,-716 307,-716 307,-684 148,-684"/>
<text text-anchor="start" x="199.9955" y="-697" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">BoardImport</text>
<polygon fill="none" stroke="#000000" points="148,-652 148,-684 307,-684 307,-652 148,-652"/>
<text text-anchor="start" x="157.907" y="-665" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+boardImportCommandService</text>
<polygon fill="none" stroke="#000000" points="148,-608 148,-652 307,-652 307,-608 148,-608"/>
<text text-anchor="start" x="200.8305" y="-633" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#configure()</text>
<text text-anchor="start" x="177.76" y="-621" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#execute(input,output)</text>
</g>
<!-- A4&#45;&gt;A5 -->
<g id="edge3" class="edge">
<title>A4&#45;&gt;A5</title>
<path fill="none" stroke="#000000" d="M227,-765.6356C227,-751.1554 227,-733.0451 227,-716.0324"/>
<polygon fill="#000000" stroke="#000000" points="227,-775.9227 222.5001,-765.9227 227,-770.9227 227.0001,-765.9227 227.0001,-765.9227 227.0001,-765.9227 227,-770.9227 231.5001,-765.9228 227,-775.9227 227,-775.9227"/>
</g>
<!-- A6 -->
<g id="node7" class="node">
<title>A6</title>
<polygon fill="none" stroke="#000000" points="150,-526 150,-558 304,-558 304,-526 150,-526"/>
<text text-anchor="start" x="159.7715" y="-539" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">BoardImportCommandService</text>
<polygon fill="none" stroke="#000000" points="150,-446 150,-526 304,-526 304,-446 150,-446"/>
<text text-anchor="start" x="199.9105" y="-507" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+bootstrap()</text>
<text text-anchor="start" x="206.8645" y="-495" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+import()</text>
<text text-anchor="start" x="186.857" y="-483" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+validateSystem()</text>
<text text-anchor="start" x="189.218" y="-471" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#validateConfig()</text>
<text text-anchor="start" x="193.107" y="-459" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#validateData()</text>
</g>
<!-- A5&#45;&gt;A6 -->
<g id="edge4" class="edge">
<title>A5&#45;&gt;A6</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M227,-607.8313C227,-595.0442 227,-581.2707 227,-568.0248"/>
<polygon fill="#000000" stroke="#000000" points="227,-558.0234 231.5001,-568.0234 227,-563.0234 227.0001,-568.0234 227.0001,-568.0234 227.0001,-568.0234 227,-563.0234 222.5001,-568.0235 227,-558.0234 227,-558.0234"/>
<text text-anchor="middle" x="218.5476" y="-586.7051" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">uses</text>
</g>
<!-- A6&#45;&gt;A3 -->
<g id="edge5" class="edge">
<title>A6&#45;&gt;A3</title>
<path fill="none" stroke="#000000" d="M198.8975,-445.7949C192.3634,-432.7268 185.3528,-418.7057 178.6417,-405.2834"/>
<polygon fill="#000000" stroke="#000000" points="174.0529,-396.1057 182.55,-403.0375 176.289,-400.5779 178.5251,-405.05 178.5251,-405.05 178.5251,-405.05 176.289,-400.5779 174.5001,-407.0625 174.0529,-396.1057 174.0529,-396.1057"/>
</g>
<!-- A7&#45;&gt;A3 -->
<g id="edge7" class="edge">
<title>A7&#45;&gt;A3</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M102.735,-228.0253C109.5347,-241.763 117.1224,-258.3431 124.0627,-274.4849"/>
<polygon fill="#000000" stroke="#000000" points="128.0634,-283.9135 120.0148,-276.4657 126.1104,-279.3107 124.1573,-274.7079 124.1573,-274.7079 124.1573,-274.7079 126.1104,-279.3107 128.2998,-272.9502 128.0634,-283.9135 128.0634,-283.9135"/>
<text text-anchor="middle" x="118.307" y="-237.5757" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">uses</text>
</g>
<!-- A8 -->
<g id="node9" class="node">
<title>A8</title>
<polygon fill="none" stroke="#000000" points="80,-64 80,-108 213,-108 213,-64 80,-64"/>
<text text-anchor="start" x="117.04" y="-89" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">&lt;&lt;abstract&gt;&gt;</text>
<text text-anchor="start" x="98.9935" y="-77" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">ABoardImportService</text>
<polygon fill="none" stroke="#000000" points="80,-32 80,-64 213,-64 213,-32 80,-32"/>
<text text-anchor="start" x="92.036" y="-45" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">#needValidateData:false</text>
<polygon fill="none" stroke="#000000" points="80,0 80,-32 213,-32 213,0 80,0"/>
<text text-anchor="start" x="89.677" y="-13" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">+needValidateData():bool</text>
</g>
<!-- A7&#45;&gt;A8 -->
<g id="edge8" class="edge">
<title>A7&#45;&gt;A8</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M97.2957,-163.778C103.3956,-150.029 110.7371,-133.4813 117.8485,-117.4527"/>
<polygon fill="none" stroke="#000000" points="121.1416,-118.6605 121.9978,-108.1003 114.743,-115.8216 121.1416,-118.6605"/>
<text text-anchor="middle" x="96.9205" y="-140.7815" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">implements</text>
</g>
<!-- A9&#45;&gt;A3 -->
<g id="edge10" class="edge">
<title>A9&#45;&gt;A3</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M198.9952,-234.3677C194.0646,-246.7117 188.0483,-260.7568 181.8434,-274.4849"/>
<polygon fill="#000000" stroke="#000000" points="177.5286,-283.9135 177.598,-272.9478 179.6093,-279.367 181.6899,-274.8204 181.6899,-274.8204 181.6899,-274.8204 179.6093,-279.367 185.7818,-276.693 177.5286,-283.9135 177.5286,-283.9135"/>
<text text-anchor="middle" x="200.0654" y="-251.3391" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">uses</text>
</g>
<!-- A9&#45;&gt;A8 -->
<g id="edge13" class="edge">
<title>A9&#45;&gt;A8</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M192.8492,-157.9466C187.2535,-145.5313 180.8796,-131.389 174.6742,-117.6209"/>
<polygon fill="none" stroke="#000000" points="177.7167,-115.8534 170.4168,-108.1747 171.3349,-118.7297 177.7167,-115.8534"/>
<text text-anchor="middle" x="177.6953" y="-141.8944" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">implements</text>
</g>
<!-- A11 -->
<g id="node12" class="node">
<title>A11</title>
<polygon fill="#fff8dc" stroke="#000000" points="403.024,-224 290.976,-224 290.976,-168 409.024,-168 409.024,-218 403.024,-224"/>
<polyline fill="none" stroke="#000000" points="403.024,-224 403.024,-218 "/>
<polyline fill="none" stroke="#000000" points="409.024,-218 403.024,-218 "/>
<text text-anchor="middle" x="350" y="-211" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">To create an import</text>
<text text-anchor="middle" x="350" y="-199" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">to another system,</text>
<text text-anchor="middle" x="350" y="-187" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">create another class</text>
<text text-anchor="middle" x="350" y="-175" font-family="Helvetica,sans-Serif" font-size="10.00" fill="#000000">similar to this.</text>
</g>
<!-- A9&#45;&gt;A11 -->
<g id="edge12" class="edge">
<title>A9&#45;&gt;A11</title>
<path fill="none" stroke="#000000" stroke-dasharray="5,2" d="M272.6172,-196C278.6627,-196 284.7083,-196 290.7538,-196"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,24 +0,0 @@
// Created using [yUML](https://github.com/jaime-olivares/vscode-yuml)
// {type:class}
// {direction:topDown}
// {generate:true}
[note: Classes used on board import. Methods just to illustrate. {bg:cornsilk}]
[ApiController]<-[BoardImportApiController|+import();+getAllowedSystems();+getConfigSchema()]
[BoardImportApiController]uses-.->[BoardImportService|+import();+bootstrap();+validateSystem();#validateConfig();#validateData();]
[Command]<-[BoardImport|+boardImportCommandService|#configure();#execute(input,output)]
[BoardImport]uses-.->[BoardImportCommandService|+bootstrap();+import();+validateSystem();#validateConfig();#validateData()]
[BoardImportCommandService]->[BoardImportService]
[BoardImportService]uses-.->[TrelloApiService|+name:string]
[TrelloApiService]uses-.->[BoardImportService]
[TrelloApiService]implements-.-^[<<abstract>> ABoardImportService|#needValidateData:false|+needValidateData():bool]
[BoardImportService]uses-.->[TrelloJsonService|+name:string;#needValidateData:true]
[TrelloJsonService]uses-.->[BoardImportService]
[BoardImportService]-[note: validateSystem is public because is used on Api. {bg:cornsilk}]
[TrelloJsonService]-[note: To create an import to another system, create another class similar to this. {bg:cornsilk}]
[TrelloJsonService]implements-.-^[<<abstract>> ABoardImportService]

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" version="1.0" viewbox="0 0 32 32">
<path d="m16 1-10 18h11l-1 12 10-18h-11z"/>
</svg>

Before

Width:  |  Height:  |  Size: 205 B

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" version="1.0" viewBox="0 0 32 32">
<path d="m16 1-10 18h11l-1 12 10-18h-11z" fill="#FFF"/>
</svg>

Before

Width:  |  Height:  |  Size: 217 B

1
img/archive-white.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16"><g transform="translate(0 -1036.362)" fill="#fff"><path d="M1.93 1041.296c-.185 0-.336.138-.336.31v9.842c0 .172.15.313.336.313h12.517c.185 0 .333-.14.333-.313v-9.842c0-.172-.148-.31-.333-.31H1.93zm4.124 1.507h4.223c.39 0 .705.314.705.704v.43c0 .39-.315.705-.705.705H6.054a.703.703 0 0 1-.705-.705v-.43c0-.39.314-.704.705-.704z"/><rect width="15.742" height="2.296" x=".136" y="1037.543" ry="0"/></g></svg>

After

Width:  |  Height:  |  Size: 488 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58" width="512" height="512"><g fill="#000"><path d="M54.319 37.839C54.762 35.918 55 33.96 55 32c0-9.095-4.631-17.377-12.389-22.153a1 1 0 1 0-1.049 1.703C48.724 15.96 53 23.604 53 32c0 1.726-.2 3.451-.573 5.147A6.992 6.992 0 0 0 51 37c-3.86 0-7 3.141-7 7s3.14 7 7 7 7-3.141 7-7a7.006 7.006 0 0 0-3.681-6.161zM38.171 54.182A23.867 23.867 0 0 1 29 56a24.047 24.047 0 0 1-17.017-7.092A6.974 6.974 0 0 0 14 44c0-3.859-3.14-7-7-7s-7 3.141-7 7 3.14 7 7 7a6.952 6.952 0 0 0 3.381-.875C15.26 55.136 21.994 58 29 58c3.435 0 6.778-.663 9.936-1.971.51-.211.753-.796.542-1.307a1.001 1.001 0 0 0-1.307-.54zM4 31.213a1 1 0 0 0 1.068-.927c.712-10.089 7.586-18.52 17.22-21.314C23.142 11.874 25.825 14 29 14c3.86 0 7-3.141 7-7s-3.14-7-7-7c-3.851 0-6.985 3.127-6.999 6.975C11.42 9.922 3.851 19.12 3.073 30.146A.999.999 0 0 0 4 31.213z"/></g></svg>

Before

Width:  |  Height:  |  Size: 885 B

View File

@@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58" width="512" height="512"><g fill="#fff"><path d="M54.319 37.839C54.762 35.918 55 33.96 55 32c0-9.095-4.631-17.377-12.389-22.153a1 1 0 1 0-1.049 1.703C48.724 15.96 53 23.604 53 32c0 1.726-.2 3.451-.573 5.147A6.992 6.992 0 0 0 51 37c-3.86 0-7 3.141-7 7s3.14 7 7 7 7-3.141 7-7a7.006 7.006 0 0 0-3.681-6.161zM38.171 54.182A23.867 23.867 0 0 1 29 56a24.047 24.047 0 0 1-17.017-7.092A6.974 6.974 0 0 0 14 44c0-3.859-3.14-7-7-7s-7 3.141-7 7 3.14 7 7 7a6.952 6.952 0 0 0 3.381-.875C15.26 55.136 21.994 58 29 58c3.435 0 6.778-.663 9.936-1.971.51-.211.753-.796.542-1.307a1.001 1.001 0 0 0-1.307-.54zM4 31.213a1 1 0 0 0 1.068-.927c.712-10.089 7.586-18.52 17.22-21.314C23.142 11.874 25.825 14 29 14c3.86 0 7-3.141 7-7s-3.14-7-7-7c-3.851 0-6.985 3.127-6.999 6.975C11.42 9.922 3.851 19.12 3.073 30.146A.999.999 0 0 0 4 31.213z"/></g></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 58 58" width="512" height="512"><g fill="#000"><path d="M54.319 37.839C54.762 35.918 55 33.96 55 32c0-9.095-4.631-17.377-12.389-22.153a1 1 0 1 0-1.049 1.703C48.724 15.96 53 23.604 53 32c0 1.726-.2 3.451-.573 5.147A6.992 6.992 0 0 0 51 37c-3.86 0-7 3.141-7 7s3.14 7 7 7 7-3.141 7-7a7.006 7.006 0 0 0-3.681-6.161zM38.171 54.182A23.867 23.867 0 0 1 29 56a24.047 24.047 0 0 1-17.017-7.092A6.974 6.974 0 0 0 14 44c0-3.859-3.14-7-7-7s-7 3.141-7 7 3.14 7 7 7a6.952 6.952 0 0 0 3.381-.875C15.26 55.136 21.994 58 29 58c3.435 0 6.778-.663 9.936-1.971.51-.211.753-.796.542-1.307a1.001 1.001 0 0 0-1.307-.54zM4 31.213a1 1 0 0 0 1.068-.927c.712-10.089 7.586-18.52 17.22-21.314C23.142 11.874 25.825 14 29 14c3.86 0 7-3.141 7-7s-3.14-7-7-7c-3.851 0-6.985 3.127-6.999 6.975C11.42 9.922 3.851 19.12 3.073 30.146A.999.999 0 0 0 4 31.213z"/></g></svg>

Before

Width:  |  Height:  |  Size: 885 B

After

Width:  |  Height:  |  Size: 885 B

1
img/clone.svg Normal file
View File

@@ -0,0 +1 @@
<svg width="16" height="16" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M11.8 13.8H2.2V4.2h9.6m1.2 0c0-.67-.53-1.2-1.2-1.2H2.2C1.53 3 1 3.53 1 4.2v9.6c0 .67.53 1.2 1.2 1.2h9.6c.67 0 1.2-.53 1.2-1.2"/><path d="m4.2 1c-0.67 0-1.2 0.54-1.2 1.2h10.8v10.8c0.67 0 1.2-0.53 1.2-1.2v-9.6c0-0.67-0.53-1.2-1.2-1.2z"/></svg>

After

Width:  |  Height:  |  Size: 327 B

1
img/reply.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16"><path d="M15 15s-.4-7.8-7-10V1L1 8l7 7v-4c5.1 0 7 4 7 4z"/></svg>

After

Width:  |  Height:  |  Size: 128 B

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "Nepodařilo se předat vlastnictví tabule uživateli {user}",
"Add a new list" : "Přidat nový sloupec",
"Archive all cards" : "Archivovat všechny karty",
"Unarchive all cards" : "Zrušit archivaci všech karet",
"Delete list" : "Smazat seznam",
"Archive all cards in this list" : "Archivovat všechny karty v tomto seznamu",
"Unarchive all cards in this list" : "Zrušit archivaci všech karet v tomto seznamu",
"Add a new card" : "Přidat novou kartu",
"Card name" : "Název karty",
"List deleted" : "Seznam smazán",
@@ -260,6 +262,7 @@ OC.L10N.register(
"Shared with you" : "Sdíleno s vámi",
"Deck settings" : "Nastavení pro Deck",
"Use bigger card view" : "Použít větší zobrazení karet",
"Show card ID badge" : "Zobrazit odznáček s identifikátorem karty",
"Show boards in calendar/tasks" : "Zobrazit tabule v kalendáři/úkolech",
"Limit deck usage of groups" : "Omezit využití deck na skupiny",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Omezení nastavené pro Deck brání uživatelům, kteří nejsou součástí těchto skupin, ve vytváření vlastních tabulí. Nicméně i tak ale pořád budou moci pracovat na tabulích, které jsou jim nasdíleny.",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "Nepodařilo se předat vlastnictví tabule uživateli {user}",
"Add a new list" : "Přidat nový sloupec",
"Archive all cards" : "Archivovat všechny karty",
"Unarchive all cards" : "Zrušit archivaci všech karet",
"Delete list" : "Smazat seznam",
"Archive all cards in this list" : "Archivovat všechny karty v tomto seznamu",
"Unarchive all cards in this list" : "Zrušit archivaci všech karet v tomto seznamu",
"Add a new card" : "Přidat novou kartu",
"Card name" : "Název karty",
"List deleted" : "Seznam smazán",
@@ -258,6 +260,7 @@
"Shared with you" : "Sdíleno s vámi",
"Deck settings" : "Nastavení pro Deck",
"Use bigger card view" : "Použít větší zobrazení karet",
"Show card ID badge" : "Zobrazit odznáček s identifikátorem karty",
"Show boards in calendar/tasks" : "Zobrazit tabule v kalendáři/úkolech",
"Limit deck usage of groups" : "Omezit využití deck na skupiny",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Omezení nastavené pro Deck brání uživatelům, kteří nejsou součástí těchto skupin, ve vytváření vlastních tabulí. Nicméně i tak ale pořád budou moci pracovat na tabulích, které jsou jim nasdíleny.",

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "Das Board konnte nicht an {user} übertragen werden",
"Add a new list" : "Eine neue Liste hinzufügen",
"Archive all cards" : "Alle Karten archivieren",
"Unarchive all cards" : "Alle Karten dearchivieren",
"Delete list" : "Liste löschen",
"Archive all cards in this list" : "Alle Karten in dieser Liste archivieren",
"Unarchive all cards in this list" : "Alle Karten dieser Liste dearchivieren",
"Add a new card" : "Neue Karte hinzufügen",
"Card name" : "Kartenname",
"List deleted" : "Liste gelöscht",
@@ -213,10 +215,10 @@ OC.L10N.register(
"The title cannot be empty." : "Der Titel darf nicht leer sein.",
"No comments yet. Begin the discussion!" : "Bislang keine Kommentare. Beginne die Diskussion!",
"Failed to load comments" : "Das Laden der Kommentare ist fehlgeschlagen",
"Assign a tag to this card…" : "Dieser Karte ein Schlagwort zuweisen…",
"Assign a tag to this card…" : "Dieser Karte ein Schlagwort zuweisen …",
"Assign to users" : "Benutzern zuweisen",
"Assign to users/groups/circles" : "An Benutzer, Gruppen oder Kreise zuweisen",
"Assign a user to this card…" : "Diese Karte einem Benutzer zuweisen…",
"Assign a user to this card…" : "Diese Karte einem Benutzer zuweisen …",
"Due date" : "Fälligkeitsdatum",
"Set a due date" : "Ein Ablaufdatum setzen",
"Remove due date" : "Fälligkeitsdatum löschen",
@@ -235,7 +237,7 @@ OC.L10N.register(
"Description" : "Beschreibung",
"(Unsaved)" : "(nicht gespeichert)",
"(Saving…)" : "(Speichere…)",
"Formatting help" : "Formatierungshilfe",
"Formatting help" : "Hilfe zur Formatierung",
"Edit description" : "Beschreibung bearbeiten",
"View description" : "Beschreibung anzeigen",
"Add Attachment" : "Anhang anhängen",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "Das Board konnte nicht an {user} übertragen werden",
"Add a new list" : "Eine neue Liste hinzufügen",
"Archive all cards" : "Alle Karten archivieren",
"Unarchive all cards" : "Alle Karten dearchivieren",
"Delete list" : "Liste löschen",
"Archive all cards in this list" : "Alle Karten in dieser Liste archivieren",
"Unarchive all cards in this list" : "Alle Karten dieser Liste dearchivieren",
"Add a new card" : "Neue Karte hinzufügen",
"Card name" : "Kartenname",
"List deleted" : "Liste gelöscht",
@@ -211,10 +213,10 @@
"The title cannot be empty." : "Der Titel darf nicht leer sein.",
"No comments yet. Begin the discussion!" : "Bislang keine Kommentare. Beginne die Diskussion!",
"Failed to load comments" : "Das Laden der Kommentare ist fehlgeschlagen",
"Assign a tag to this card…" : "Dieser Karte ein Schlagwort zuweisen…",
"Assign a tag to this card…" : "Dieser Karte ein Schlagwort zuweisen …",
"Assign to users" : "Benutzern zuweisen",
"Assign to users/groups/circles" : "An Benutzer, Gruppen oder Kreise zuweisen",
"Assign a user to this card…" : "Diese Karte einem Benutzer zuweisen…",
"Assign a user to this card…" : "Diese Karte einem Benutzer zuweisen …",
"Due date" : "Fälligkeitsdatum",
"Set a due date" : "Ein Ablaufdatum setzen",
"Remove due date" : "Fälligkeitsdatum löschen",
@@ -233,7 +235,7 @@
"Description" : "Beschreibung",
"(Unsaved)" : "(nicht gespeichert)",
"(Saving…)" : "(Speichere…)",
"Formatting help" : "Formatierungshilfe",
"Formatting help" : "Hilfe zur Formatierung",
"Edit description" : "Beschreibung bearbeiten",
"View description" : "Beschreibung anzeigen",
"Add Attachment" : "Anhang anhängen",

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "Das Board konnte nicht auf {user} übertragen werden",
"Add a new list" : "Eine neue Liste hinzufügen",
"Archive all cards" : "Alle Karten archivieren",
"Unarchive all cards" : "Alle Karten dearchivieren",
"Delete list" : "Liste löschen",
"Archive all cards in this list" : "Alle Karten in dieser Liste archivieren",
"Unarchive all cards in this list" : "Alle Karten in dieser Liste dearchivieren",
"Add a new card" : "Neue Karte hinzufügen",
"Card name" : "Kartenname",
"List deleted" : "Liste gelöscht",
@@ -260,6 +262,7 @@ OC.L10N.register(
"Shared with you" : "Mit Ihnen geteilt",
"Deck settings" : "Deck-Einstellungen",
"Use bigger card view" : "Größere Kartenansicht verwenden",
"Show card ID badge" : "Abzeichen mit Karten-ID zeigen",
"Show boards in calendar/tasks" : "Board in Kalender/Aufgaben anzeigen",
"Limit deck usage of groups" : "Nutzung auf Gruppen einschränken",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Wenn Sie Deck einschränken, können Benutzer, die nicht zu diesen Gruppen gehören, keine eigenen Boards erstellen. Die Benutzer können weiterhin an Boards arbeiten, die für sie freigegeben wurden.",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "Das Board konnte nicht auf {user} übertragen werden",
"Add a new list" : "Eine neue Liste hinzufügen",
"Archive all cards" : "Alle Karten archivieren",
"Unarchive all cards" : "Alle Karten dearchivieren",
"Delete list" : "Liste löschen",
"Archive all cards in this list" : "Alle Karten in dieser Liste archivieren",
"Unarchive all cards in this list" : "Alle Karten in dieser Liste dearchivieren",
"Add a new card" : "Neue Karte hinzufügen",
"Card name" : "Kartenname",
"List deleted" : "Liste gelöscht",
@@ -258,6 +260,7 @@
"Shared with you" : "Mit Ihnen geteilt",
"Deck settings" : "Deck-Einstellungen",
"Use bigger card view" : "Größere Kartenansicht verwenden",
"Show card ID badge" : "Abzeichen mit Karten-ID zeigen",
"Show boards in calendar/tasks" : "Board in Kalender/Aufgaben anzeigen",
"Limit deck usage of groups" : "Nutzung auf Gruppen einschränken",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Wenn Sie Deck einschränken, können Benutzer, die nicht zu diesen Gruppen gehören, keine eigenen Boards erstellen. Die Benutzer können weiterhin an Boards arbeiten, die für sie freigegeben wurden.",

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "Failed to transfer the board to {user}",
"Add a new list" : "Añadir una lista nueva",
"Archive all cards" : "Archivar todas las tarjetas",
"Unarchive all cards" : "Desarchivar todas las tarjetas",
"Delete list" : "Eliminar lista",
"Archive all cards in this list" : "Archivar todas las tarjetas en esta lista",
"Unarchive all cards in this list" : "Desarchivar todas las tarjetas en esta lista",
"Add a new card" : "Añadir una nueva tarjeta",
"Card name" : "Nombre de la tarjeta",
"List deleted" : "Lista borrada",
@@ -260,6 +262,7 @@ OC.L10N.register(
"Shared with you" : "Compartido contigo",
"Deck settings" : "Configuración del tablero",
"Use bigger card view" : "Usar vista de tarjeta mayor",
"Show card ID badge" : "Mostrar insignia de la ID de tarjeta",
"Show boards in calendar/tasks" : "Mostrar tableros en calendario/tareas",
"Limit deck usage of groups" : "Limitar el uso de Deck a grupos",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Limitar Deck impedirá que usuarios que no formen parte de esos grupos creen sus propios tableros. Los usuarios todavía podrán trabajar en tableros que hayan sido compartidos con ellos.",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "Failed to transfer the board to {user}",
"Add a new list" : "Añadir una lista nueva",
"Archive all cards" : "Archivar todas las tarjetas",
"Unarchive all cards" : "Desarchivar todas las tarjetas",
"Delete list" : "Eliminar lista",
"Archive all cards in this list" : "Archivar todas las tarjetas en esta lista",
"Unarchive all cards in this list" : "Desarchivar todas las tarjetas en esta lista",
"Add a new card" : "Añadir una nueva tarjeta",
"Card name" : "Nombre de la tarjeta",
"List deleted" : "Lista borrada",
@@ -258,6 +260,7 @@
"Shared with you" : "Compartido contigo",
"Deck settings" : "Configuración del tablero",
"Use bigger card view" : "Usar vista de tarjeta mayor",
"Show card ID badge" : "Mostrar insignia de la ID de tarjeta",
"Show boards in calendar/tasks" : "Mostrar tableros en calendario/tareas",
"Limit deck usage of groups" : "Limitar el uso de Deck a grupos",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Limitar Deck impedirá que usuarios que no formen parte de esos grupos creen sus propios tableros. Los usuarios todavía podrán trabajar en tableros que hayan sido compartidos con ellos.",

View File

@@ -260,6 +260,7 @@ OC.L10N.register(
"Shared with you" : "Partagés avec vous",
"Deck settings" : "Paramètres de Deck",
"Use bigger card view" : "Utiliser la vue large des cartes",
"Show card ID badge" : "Afficher la carte d'identité du badge",
"Show boards in calendar/tasks" : "Afficher les tableaux dans les agendas/tâches",
"Limit deck usage of groups" : "Limiter l'utilisation du tableau aux groupes",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Limiter Deck empêchera les utilisateurs ne faisant pas partie de ces groupes de créer leurs propres tableaux. Ces utilisateurs pourront toujours travailler sur les tableaux qui ont été partagés avec eux.",

View File

@@ -258,6 +258,7 @@
"Shared with you" : "Partagés avec vous",
"Deck settings" : "Paramètres de Deck",
"Use bigger card view" : "Utiliser la vue large des cartes",
"Show card ID badge" : "Afficher la carte d'identité du badge",
"Show boards in calendar/tasks" : "Afficher les tableaux dans les agendas/tâches",
"Limit deck usage of groups" : "Limiter l'utilisation du tableau aux groupes",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Limiter Deck empêchera les utilisateurs ne faisant pas partie de ces groupes de créer leurs propres tableaux. Ces utilisateurs pourront toujours travailler sur les tableaux qui ont été partagés avec eux.",

View File

@@ -70,6 +70,7 @@ OC.L10N.register(
"Personal" : "Asmeniniai",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Kortelę \"%s\" ties \"%s\" priskyrė jums %s.",
"The card \"%s\" on \"%s\" has reached its due date." : "Kortelė „%s“, esanti lentoje „%s“, pasiekė savo galutinį terminą.",
"The card {deck-card} on {deck-board} has reached its due date." : "Kortelė {deck-card}, esanti lentoje {deck-board} pasiekė savo galutinio termino datą.",
"%s has mentioned you in a comment on \"%s\"." : "%s paminėjo jus komentare ties \"%s\".",
"The board \"%s\" has been shared with you by %s." : "Lentą \"%s\" su jumis bendrina %s.",
"%s on %s" : "%s ant %s",
@@ -200,9 +201,9 @@ OC.L10N.register(
"Assign to users" : "Priskirti naudotojams",
"Assign to users/groups/circles" : "Priskirti naudotojams/grupėms/ratams",
"Assign a user to this card…" : "Priskirti šiai kortelei naudotoją…",
"Due date" : "Terminas",
"Due date" : "Galutinio termino data",
"Set a due date" : "Nustatyti galutinį terminą",
"Remove due date" : "Pašalinti terminą",
"Remove due date" : "Šalinti galutinio termino datą",
"Select Date" : "Pasirinkti datą",
"Today" : "Šiandien",
"Tomorrow" : "Rytoj",
@@ -243,9 +244,9 @@ OC.L10N.register(
"Clone board" : "Dubliuoti lentą",
"Unarchive board" : "Išarchyvuoti lentą",
"Archive board" : "Archyvuoti lentą",
"Turn on due date reminders" : "Įjungti priminimus apie galutinį terminą",
"Turn off due date reminders" : "Išjungti priminimus apie galutinį terminą",
"Due date reminders" : "Priminimai apie galutinį terminą",
"Turn on due date reminders" : "Įjungti priminimus apie galutinio termino datą",
"Turn off due date reminders" : "Išjungti priminimus apie galutinio termino datą",
"Due date reminders" : "Priminimai apie galutinio termino datą",
"All cards" : "Visos kortelės",
"Assigned cards" : "Priskirtos kortelės",
"No notifications" : "Pranešimų nėra",
@@ -257,6 +258,7 @@ OC.L10N.register(
"Delete the board?" : "Ištrinti lentą?",
"No due" : "Be galutinio termino",
"No results found" : "Nerasta jokių rezultatų",
"Due on {date}" : "Galutinis terminas {date}",
"Link to a board" : "Susieti su lenta",
"Link to a card" : "Susieti su kortele",
"Create a card" : "Sukurti kortelę",

View File

@@ -68,6 +68,7 @@
"Personal" : "Asmeniniai",
"The card \"%s\" on \"%s\" has been assigned to you by %s." : "Kortelę \"%s\" ties \"%s\" priskyrė jums %s.",
"The card \"%s\" on \"%s\" has reached its due date." : "Kortelė „%s“, esanti lentoje „%s“, pasiekė savo galutinį terminą.",
"The card {deck-card} on {deck-board} has reached its due date." : "Kortelė {deck-card}, esanti lentoje {deck-board} pasiekė savo galutinio termino datą.",
"%s has mentioned you in a comment on \"%s\"." : "%s paminėjo jus komentare ties \"%s\".",
"The board \"%s\" has been shared with you by %s." : "Lentą \"%s\" su jumis bendrina %s.",
"%s on %s" : "%s ant %s",
@@ -198,9 +199,9 @@
"Assign to users" : "Priskirti naudotojams",
"Assign to users/groups/circles" : "Priskirti naudotojams/grupėms/ratams",
"Assign a user to this card…" : "Priskirti šiai kortelei naudotoją…",
"Due date" : "Terminas",
"Due date" : "Galutinio termino data",
"Set a due date" : "Nustatyti galutinį terminą",
"Remove due date" : "Pašalinti terminą",
"Remove due date" : "Šalinti galutinio termino datą",
"Select Date" : "Pasirinkti datą",
"Today" : "Šiandien",
"Tomorrow" : "Rytoj",
@@ -241,9 +242,9 @@
"Clone board" : "Dubliuoti lentą",
"Unarchive board" : "Išarchyvuoti lentą",
"Archive board" : "Archyvuoti lentą",
"Turn on due date reminders" : "Įjungti priminimus apie galutinį terminą",
"Turn off due date reminders" : "Išjungti priminimus apie galutinį terminą",
"Due date reminders" : "Priminimai apie galutinį terminą",
"Turn on due date reminders" : "Įjungti priminimus apie galutinio termino datą",
"Turn off due date reminders" : "Išjungti priminimus apie galutinio termino datą",
"Due date reminders" : "Priminimai apie galutinio termino datą",
"All cards" : "Visos kortelės",
"Assigned cards" : "Priskirtos kortelės",
"No notifications" : "Pranešimų nėra",
@@ -255,6 +256,7 @@
"Delete the board?" : "Ištrinti lentą?",
"No due" : "Be galutinio termino",
"No results found" : "Nerasta jokių rezultatų",
"Due on {date}" : "Galutinis terminas {date}",
"Link to a board" : "Susieti su lenta",
"Link to a card" : "Susieti su kortele",
"Create a card" : "Sukurti kortelę",

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "Nie udało się przenieść tablicy do {user}",
"Add a new list" : "Dodaj nową listę",
"Archive all cards" : "Zarchiwizuj wszystkie karty",
"Unarchive all cards" : "Przywróć wszystkie karty z archiwum",
"Delete list" : "Usuń listę",
"Archive all cards in this list" : "Zarchiwizuj wszystkie karty na tej liście",
"Unarchive all cards in this list" : "Cofnij archiwizację wszystkich kart z tej listy",
"Add a new card" : "Dodaj nową kartę",
"Card name" : "Nazwa karty",
"List deleted" : "Lista usunięta",
@@ -260,6 +262,7 @@ OC.L10N.register(
"Shared with you" : "Udostępnione Tobie",
"Deck settings" : "Ustawienia Tablicy",
"Use bigger card view" : "Użyj większego widoku karty",
"Show card ID badge" : "Pokaż ID karty",
"Show boards in calendar/tasks" : "Pokaż tablice w kalendarzu/zadaniach",
"Limit deck usage of groups" : "Ogranicz użycie tablic dla grup",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Ograniczenie Tablicy zablokuje użytkownikom z tych grup możliwość tworzenia własnych tablic. Użytkownicy nadal będą mogli pracować na tablicach, które zostały im udostępnione.",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "Nie udało się przenieść tablicy do {user}",
"Add a new list" : "Dodaj nową listę",
"Archive all cards" : "Zarchiwizuj wszystkie karty",
"Unarchive all cards" : "Przywróć wszystkie karty z archiwum",
"Delete list" : "Usuń listę",
"Archive all cards in this list" : "Zarchiwizuj wszystkie karty na tej liście",
"Unarchive all cards in this list" : "Cofnij archiwizację wszystkich kart z tej listy",
"Add a new card" : "Dodaj nową kartę",
"Card name" : "Nazwa karty",
"List deleted" : "Lista usunięta",
@@ -258,6 +260,7 @@
"Shared with you" : "Udostępnione Tobie",
"Deck settings" : "Ustawienia Tablicy",
"Use bigger card view" : "Użyj większego widoku karty",
"Show card ID badge" : "Pokaż ID karty",
"Show boards in calendar/tasks" : "Pokaż tablice w kalendarzu/zadaniach",
"Limit deck usage of groups" : "Ogranicz użycie tablic dla grup",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Ograniczenie Tablicy zablokuje użytkownikom z tych grup możliwość tworzenia własnych tablic. Użytkownicy nadal będą mogli pracować na tablicach, które zostały im udostępnione.",

View File

@@ -136,6 +136,7 @@ OC.L10N.register(
"Archived cards" : "Arkiverade kort",
"Add list" : "Lägg till lista...",
"List name" : "Namn på lista",
"Active filters" : "Aktiva filter",
"Apply filter" : "Tillämpa filter",
"Filter by tag" : "Filtrera efter tagg",
"Filter by assigned user" : "Filtrera efter tilldelad användare",
@@ -175,11 +176,17 @@ OC.L10N.register(
"Owner" : "Ägare",
"Delete" : "Ta bort",
"Failed to create share with {displayName}" : "Kunde inte skapa delning med {displayName}",
"Are you sure you want to transfer the board {title} to {user}?" : "Är du säker att du vill överföra tavlan {title} till {user}?",
"Transfer the board." : "Överför tavlan.",
"Transfer" : "Överför",
"The board has been transferred to {user}" : "Tavlan har överförts till {user}",
"Failed to transfer the board to {user}" : "Kunde inte överföra tavlan till {user}",
"Add a new list" : "Lägg till en ny lista",
"Archive all cards" : "Arkivera alla kort",
"Unarchive all cards" : "Ta ut alla kort ur arkivet",
"Delete list" : "Ta bort lista",
"Archive all cards in this list" : "Arkivera alla kort i denna lista",
"Unarchive all cards in this list" : "Ta ut alla kort i denna lista ur arkivet",
"Add a new card" : "Lägg till ett nytt kort",
"Card name" : "Kortets namn",
"List deleted" : "Listan har raderats",
@@ -237,7 +244,9 @@ OC.L10N.register(
"Write a description …" : "Ange en beskrivning ...",
"Choose attachment" : "Välj bilaga",
"(group)" : " (grupp)",
"Todo items" : "Todo saker",
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
"Edit card title" : "Ändra korttitel",
"Assign to me" : "Tilldela till mig",
"Unassign myself" : "Ta bort från mig själv",
"Move card" : "Flytta kort",
@@ -251,7 +260,9 @@ OC.L10N.register(
"All boards" : "Alla tavlor",
"Archived boards" : "Arkiverade tavlor",
"Shared with you" : "Delad med dig",
"Deck settings" : "Deckinställningar",
"Use bigger card view" : "Visa större kort",
"Show card ID badge" : "Visa kortets ID-märke",
"Show boards in calendar/tasks" : "Visa tavlor i kalender / uppgifter",
"Limit deck usage of groups" : "Begränsa användningen av grupper",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Begränsning av Deck blockerar användare som inte ingår i dessa grupper från att skapa egna tavlor. Användare kan dock fortfarande integrera med tavlor som har delats med dem.",
@@ -271,13 +282,19 @@ OC.L10N.register(
"Only assigned cards" : "Bara tilldelade kort",
"No reminder" : "Ingen påminnelse",
"An error occurred" : "Ett fel uppstod",
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Är du säker på att du vill radera brädet {title}? Detta kommer radera all data som tillhör brädet inklusive arkiverade kort.",
"Delete the board?" : "Ta bort tavlan?",
"Loading filtered view" : "Laddar filtrerad vy",
"No due" : "Inget slut",
"Search for {searchQuery} in all boards" : "Sök efter {searchQuery} i alla tavlor",
"No results found" : "Inga resultat funna",
"{stack} in {board}" : "{stack} i {board}",
"Click to expand description" : "Klicka för att utöka beskrivningen",
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Skapades {created}\n* Ändrades senast {lastMod}\n* {nbAttachments} bilagor\n* {nbComments} kommentarer",
"{nbCards} cards" : "{nbCards} kort",
"No upcoming cards" : "Inga kommande kort",
"upcoming cards" : "kommande kort",
"Due on {date}" : "Går ut {date}",
"Link to a board" : "Länka till en tavla",
"Link to a card" : "Länka till ett kort",
"Create a card" : "Skapa ett kort",
@@ -290,7 +307,11 @@ OC.L10N.register(
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
"Share" : "Dela",
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 📎 Attach files and embed them in your markdown description\n- 💬 Discuss with your team using comments\n- ⚡ Keep track of changes in the activity stream\n- 🚀 Get your project organized" : "Deck är en kanban-liknande projekt- och organiseringsapp för arbetsgrupper i Nextcloud.\n\n\n- 📥 Lägg till uppgifter på kort och ordna dem\n- 📄 Skriv anteckningar i markdown\n- 🔖 Tilldela etiketter för organisering\n- 👥 Dela med arbetsgruppen, vänner eller familj\n- 📎 Bifoga filer och bädda in dem i markdown-anteckningarna\n- 💬 Diskutera i arbetsgruppen genom kommentarer\n- ⚡ Håll koll på ändringar i aktivitetsflödet\n- 🚀 Börja organisera din arbetsgrupp nu!",
"Are you sure you want to transfer the board {title} for {user} ?" : "Är du säker på att du vill överföra brädet {title} för {user}?",
"Transfer the board for {user} successfully" : "Överförde brädet för {user}",
"Failed to transfer the board for {user}" : "Misslyckades med att överföra brädet för {user}",
"Are you sure you want to delete the board {title}? This will delete all the data of this board." : "Är du säker på att du vill radera tavla {title}? Detta kommer att radera all information från denna tavla.",
"This week" : "Denna vecka"
"This week" : "Denna vecka",
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra brädet {title} för {user}?"
},
"nplurals=2; plural=(n != 1);");

View File

@@ -134,6 +134,7 @@
"Archived cards" : "Arkiverade kort",
"Add list" : "Lägg till lista...",
"List name" : "Namn på lista",
"Active filters" : "Aktiva filter",
"Apply filter" : "Tillämpa filter",
"Filter by tag" : "Filtrera efter tagg",
"Filter by assigned user" : "Filtrera efter tilldelad användare",
@@ -173,11 +174,17 @@
"Owner" : "Ägare",
"Delete" : "Ta bort",
"Failed to create share with {displayName}" : "Kunde inte skapa delning med {displayName}",
"Are you sure you want to transfer the board {title} to {user}?" : "Är du säker att du vill överföra tavlan {title} till {user}?",
"Transfer the board." : "Överför tavlan.",
"Transfer" : "Överför",
"The board has been transferred to {user}" : "Tavlan har överförts till {user}",
"Failed to transfer the board to {user}" : "Kunde inte överföra tavlan till {user}",
"Add a new list" : "Lägg till en ny lista",
"Archive all cards" : "Arkivera alla kort",
"Unarchive all cards" : "Ta ut alla kort ur arkivet",
"Delete list" : "Ta bort lista",
"Archive all cards in this list" : "Arkivera alla kort i denna lista",
"Unarchive all cards in this list" : "Ta ut alla kort i denna lista ur arkivet",
"Add a new card" : "Lägg till ett nytt kort",
"Card name" : "Kortets namn",
"List deleted" : "Listan har raderats",
@@ -235,7 +242,9 @@
"Write a description …" : "Ange en beskrivning ...",
"Choose attachment" : "Välj bilaga",
"(group)" : " (grupp)",
"Todo items" : "Todo saker",
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
"Edit card title" : "Ändra korttitel",
"Assign to me" : "Tilldela till mig",
"Unassign myself" : "Ta bort från mig själv",
"Move card" : "Flytta kort",
@@ -249,7 +258,9 @@
"All boards" : "Alla tavlor",
"Archived boards" : "Arkiverade tavlor",
"Shared with you" : "Delad med dig",
"Deck settings" : "Deckinställningar",
"Use bigger card view" : "Visa större kort",
"Show card ID badge" : "Visa kortets ID-märke",
"Show boards in calendar/tasks" : "Visa tavlor i kalender / uppgifter",
"Limit deck usage of groups" : "Begränsa användningen av grupper",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Begränsning av Deck blockerar användare som inte ingår i dessa grupper från att skapa egna tavlor. Användare kan dock fortfarande integrera med tavlor som har delats med dem.",
@@ -269,13 +280,19 @@
"Only assigned cards" : "Bara tilldelade kort",
"No reminder" : "Ingen påminnelse",
"An error occurred" : "Ett fel uppstod",
"Are you sure you want to delete the board {title}? This will delete all the data of this board including archived cards." : "Är du säker på att du vill radera brädet {title}? Detta kommer radera all data som tillhör brädet inklusive arkiverade kort.",
"Delete the board?" : "Ta bort tavlan?",
"Loading filtered view" : "Laddar filtrerad vy",
"No due" : "Inget slut",
"Search for {searchQuery} in all boards" : "Sök efter {searchQuery} i alla tavlor",
"No results found" : "Inga resultat funna",
"{stack} in {board}" : "{stack} i {board}",
"Click to expand description" : "Klicka för att utöka beskrivningen",
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Skapades {created}\n* Ändrades senast {lastMod}\n* {nbAttachments} bilagor\n* {nbComments} kommentarer",
"{nbCards} cards" : "{nbCards} kort",
"No upcoming cards" : "Inga kommande kort",
"upcoming cards" : "kommande kort",
"Due on {date}" : "Går ut {date}",
"Link to a board" : "Länka till en tavla",
"Link to a card" : "Länka till ett kort",
"Create a card" : "Skapa ett kort",
@@ -288,7 +305,11 @@
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
"Share" : "Dela",
"Deck is a kanban style organization tool aimed at personal planning and project organization for teams integrated with Nextcloud.\n\n\n- 📥 Add your tasks to cards and put them in order\n- 📄 Write down additional notes in markdown\n- 🔖 Assign labels for even better organization\n- 👥 Share with your team, friends or family\n- 📎 Attach files and embed them in your markdown description\n- 💬 Discuss with your team using comments\n- ⚡ Keep track of changes in the activity stream\n- 🚀 Get your project organized" : "Deck är en kanban-liknande projekt- och organiseringsapp för arbetsgrupper i Nextcloud.\n\n\n- 📥 Lägg till uppgifter på kort och ordna dem\n- 📄 Skriv anteckningar i markdown\n- 🔖 Tilldela etiketter för organisering\n- 👥 Dela med arbetsgruppen, vänner eller familj\n- 📎 Bifoga filer och bädda in dem i markdown-anteckningarna\n- 💬 Diskutera i arbetsgruppen genom kommentarer\n- ⚡ Håll koll på ändringar i aktivitetsflödet\n- 🚀 Börja organisera din arbetsgrupp nu!",
"Are you sure you want to transfer the board {title} for {user} ?" : "Är du säker på att du vill överföra brädet {title} för {user}?",
"Transfer the board for {user} successfully" : "Överförde brädet för {user}",
"Failed to transfer the board for {user}" : "Misslyckades med att överföra brädet för {user}",
"Are you sure you want to delete the board {title}? This will delete all the data of this board." : "Är du säker på att du vill radera tavla {title}? Detta kommer att radera all information från denna tavla.",
"This week" : "Denna vecka"
"This week" : "Denna vecka",
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra brädet {title} för {user}?"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
}

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "Pano {user} kullanıcısına aktarılamadı",
"Add a new list" : "Yeni liste ekle",
"Archive all cards" : "Tüm kartları arşivle",
"Unarchive all cards" : "Tüm kartları arşivden çıkar",
"Delete list" : "Listeyi sil",
"Archive all cards in this list" : "Bu listedeki tüm kartları arşivle",
"Unarchive all cards in this list" : "Bu listedeki tüm kartları arşivden çıkar",
"Add a new card" : "Yeni kart ekle",
"Card name" : "Kart adı",
"List deleted" : "Liste silindi",
@@ -260,6 +262,7 @@ OC.L10N.register(
"Shared with you" : "Sizinle paylaşıldı",
"Deck settings" : "Tahta ayarları",
"Use bigger card view" : "Daha büyük kart görünümü kullanılsın",
"Show card ID badge" : "Kart kodu etiketi görüntülensin",
"Show boards in calendar/tasks" : "Takvimler ve görevlerde panolar görüntülensin",
"Limit deck usage of groups" : "Tahtayı şu gruplar kullanabilsin",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Tahta kullanımı gruplar ile sınırlandığında belirtilen grupların üyesi olmayan kişiler kendi tahtalarını oluşturamaz. Bu kullanıcılar ancak kendileri ile paylaşılan tahtalar üzerinde çalışabilir.",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "Pano {user} kullanıcısına aktarılamadı",
"Add a new list" : "Yeni liste ekle",
"Archive all cards" : "Tüm kartları arşivle",
"Unarchive all cards" : "Tüm kartları arşivden çıkar",
"Delete list" : "Listeyi sil",
"Archive all cards in this list" : "Bu listedeki tüm kartları arşivle",
"Unarchive all cards in this list" : "Bu listedeki tüm kartları arşivden çıkar",
"Add a new card" : "Yeni kart ekle",
"Card name" : "Kart adı",
"List deleted" : "Liste silindi",
@@ -258,6 +260,7 @@
"Shared with you" : "Sizinle paylaşıldı",
"Deck settings" : "Tahta ayarları",
"Use bigger card view" : "Daha büyük kart görünümü kullanılsın",
"Show card ID badge" : "Kart kodu etiketi görüntülensin",
"Show boards in calendar/tasks" : "Takvimler ve görevlerde panolar görüntülensin",
"Limit deck usage of groups" : "Tahtayı şu gruplar kullanabilsin",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "Tahta kullanımı gruplar ile sınırlandığında belirtilen grupların üyesi olmayan kişiler kendi tahtalarını oluşturamaz. Bu kullanıcılar ancak kendileri ile paylaşılan tahtalar üzerinde çalışabilir.",

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "未能將面板轉移給 {user}",
"Add a new list" : "添加一張新清單",
"Archive all cards" : "封存所有卡片",
"Unarchive all cards" : "解除封存所有卡片",
"Delete list" : "刪除清單",
"Archive all cards in this list" : "封存此清單內的所有卡片",
"Unarchive all cards in this list" : "解除封存所有此列表中的卡片",
"Add a new card" : "添加一張新卡片",
"Card name" : "卡片名稱",
"List deleted" : "清單已被刪除",
@@ -260,6 +262,7 @@ OC.L10N.register(
"Shared with you" : "收到的分享",
"Deck settings" : "看板設定",
"Use bigger card view" : "使用較大的卡片視圖",
"Show card ID badge" : "顯示卡片 ID 徽章",
"Show boards in calendar/tasks" : "在日曆/任務中顯示面板",
"Limit deck usage of groups" : "限制群組使用 Deck",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "限制 Deck 將阻止不屬於這些群組的用戶創建自己的面板。用戶仍然可以在與他們的面板上工作。",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "未能將面板轉移給 {user}",
"Add a new list" : "添加一張新清單",
"Archive all cards" : "封存所有卡片",
"Unarchive all cards" : "解除封存所有卡片",
"Delete list" : "刪除清單",
"Archive all cards in this list" : "封存此清單內的所有卡片",
"Unarchive all cards in this list" : "解除封存所有此列表中的卡片",
"Add a new card" : "添加一張新卡片",
"Card name" : "卡片名稱",
"List deleted" : "清單已被刪除",
@@ -258,6 +260,7 @@
"Shared with you" : "收到的分享",
"Deck settings" : "看板設定",
"Use bigger card view" : "使用較大的卡片視圖",
"Show card ID badge" : "顯示卡片 ID 徽章",
"Show boards in calendar/tasks" : "在日曆/任務中顯示面板",
"Limit deck usage of groups" : "限制群組使用 Deck",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "限制 Deck 將阻止不屬於這些群組的用戶創建自己的面板。用戶仍然可以在與他們的面板上工作。",

View File

@@ -183,8 +183,10 @@ OC.L10N.register(
"Failed to transfer the board to {user}" : "轉移看板給 {user} 失敗",
"Add a new list" : "新增列表",
"Archive all cards" : "封存所有卡片",
"Unarchive all cards" : "解除封存所有卡片",
"Delete list" : "刪除列表",
"Archive all cards in this list" : "封存此列表中的所有卡片",
"Unarchive all cards in this list" : "解除封存所有此列表中的卡片",
"Add a new card" : "新增卡片",
"Card name" : "卡片名稱",
"List deleted" : "列表已刪除",
@@ -260,6 +262,7 @@ OC.L10N.register(
"Shared with you" : "與您分享",
"Deck settings" : "Deck 設定",
"Use bigger card view" : "使用較大的卡片檢視",
"Show card ID badge" : "顯示卡片 ID 徽章",
"Show boards in calendar/tasks" : "在日曆/工作項目中顯示佈告欄",
"Limit deck usage of groups" : "限制群組的 Deck 使用",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "限制 Deck 將會阻止不屬於這些群組的使用者建立自己的佈告欄。使用者仍然可以在與他們分享的佈告欄上工作。",

View File

@@ -181,8 +181,10 @@
"Failed to transfer the board to {user}" : "轉移看板給 {user} 失敗",
"Add a new list" : "新增列表",
"Archive all cards" : "封存所有卡片",
"Unarchive all cards" : "解除封存所有卡片",
"Delete list" : "刪除列表",
"Archive all cards in this list" : "封存此列表中的所有卡片",
"Unarchive all cards in this list" : "解除封存所有此列表中的卡片",
"Add a new card" : "新增卡片",
"Card name" : "卡片名稱",
"List deleted" : "列表已刪除",
@@ -258,6 +260,7 @@
"Shared with you" : "與您分享",
"Deck settings" : "Deck 設定",
"Use bigger card view" : "使用較大的卡片檢視",
"Show card ID badge" : "顯示卡片 ID 徽章",
"Show boards in calendar/tasks" : "在日曆/工作項目中顯示佈告欄",
"Limit deck usage of groups" : "限制群組的 Deck 使用",
"Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them." : "限制 Deck 將會阻止不屬於這些群組的使用者建立自己的佈告欄。使用者仍然可以在與他們分享的佈告欄上工作。",

View File

@@ -31,6 +31,7 @@ use OCA\Deck\Db\Acl;
use OCA\Deck\Db\AclMapper;
use OCA\Deck\Db\Assignment;
use OCA\Deck\Db\Attachment;
use OCA\Deck\Db\AttachmentMapper;
use OCA\Deck\Db\Board;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\Card;
@@ -45,24 +46,19 @@ use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\Comments\IComment;
use OCP\IUser;
use OCP\Server;
use OCP\L10N\IFactory;
use Psr\Log\LoggerInterface;
class ActivityManager {
public const DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED = 'DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED';
public const SUBJECT_PARAMS_MAX_LENGTH = 4000;
public const SHORTENED_DESCRIPTION_MAX_LENGTH = 2000;
private IManager $manager;
private ?string $userId;
private PermissionService $permissionService;
private BoardMapper $boardMapper;
private CardMapper $cardMapper;
private AclMapper $aclMapper;
private StackMapper $stackMapper;
private IFactory $l10nFactory;
private $manager;
private $userId;
private $permissionService;
private $boardMapper;
private $cardMapper;
private $attachmentMapper;
private $aclMapper;
private $stackMapper;
private $l10nFactory;
public const DECK_OBJECT_BOARD = 'deck_board';
public const DECK_OBJECT_CARD = 'deck_card';
@@ -114,15 +110,17 @@ class ActivityManager {
BoardMapper $boardMapper,
CardMapper $cardMapper,
StackMapper $stackMapper,
AttachmentMapper $attachmentMapper,
AclMapper $aclMapper,
IFactory $l10nFactory,
?string $userId
$userId
) {
$this->manager = $manager;
$this->permissionService = $permissionsService;
$this->boardMapper = $boardMapper;
$this->cardMapper = $cardMapper;
$this->stackMapper = $stackMapper;
$this->attachmentMapper = $attachmentMapper;
$this->aclMapper = $aclMapper;
$this->l10nFactory = $l10nFactory;
$this->userId = $userId;
@@ -251,6 +249,19 @@ class ActivityManager {
try {
$event = $this->createEvent($objectType, $entity, $subject, $additionalParams, $author);
if ($event !== null) {
$json = json_encode($event->getSubjectParameters());
if (mb_strlen($json) > 4000) {
$params = json_decode(json_encode($event->getSubjectParameters()), true);
$newContent = $params['after'];
unset($params['before'], $params['after'], $params['card']['description']);
$params['after'] = mb_substr($newContent, 0, 2000);
if (mb_strlen($newContent) > 2000) {
$params['after'] .= '...';
}
$event->setSubject($event->getSubject(), $params);
}
$this->sendToUsers($event);
}
} catch (\Exception $e) {
@@ -312,10 +323,10 @@ class ActivityManager {
try {
$object = $this->findObjectForEntity($objectType, $entity);
} catch (DoesNotExistException $e) {
Server::get(LoggerInterface::class)->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
\OC::$server->getLogger()->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
return null;
} catch (MultipleObjectsReturnedException $e) {
Server::get(LoggerInterface::class)->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
\OC::$server->getLogger()->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
return null;
}
@@ -367,15 +378,7 @@ class ActivityManager {
case self::SUBJECT_CARD_USER_ASSIGN:
case self::SUBJECT_CARD_USER_UNASSIGN:
$subjectParams = $this->findDetailsForCard($entity->getId(), $subject);
if (isset($additionalParams['after']) && $additionalParams['after'] instanceof \DateTimeInterface) {
$additionalParams['after'] = $additionalParams['after']->format('c');
}
if (isset($additionalParams['before']) && $additionalParams['before'] instanceof \DateTimeInterface) {
$additionalParams['before'] = $additionalParams['before']->format('c');
}
break;
break;
case self::SUBJECT_ATTACHMENT_CREATE:
case self::SUBJECT_ATTACHMENT_UPDATE:
case self::SUBJECT_ATTACHMENT_DELETE:
@@ -407,31 +410,12 @@ class ActivityManager {
$subjectParams['author'] = $author === null ? $this->userId : $author;
$subjectParams = array_merge($subjectParams, $additionalParams);
$json = json_encode($subjectParams);
if (mb_strlen($json) > self::SUBJECT_PARAMS_MAX_LENGTH) {
$params = json_decode(json_encode($subjectParams), true);
if ($subject === self::SUBJECT_CARD_UPDATE_DESCRIPTION && isset($params['after'])) {
$newContent = $params['after'];
unset($params['before'], $params['after'], $params['card']['description']);
$params['after'] = mb_substr($newContent, 0, self::SHORTENED_DESCRIPTION_MAX_LENGTH);
if (mb_strlen($newContent) > self::SHORTENED_DESCRIPTION_MAX_LENGTH) {
$params['after'] .= '...';
}
$subjectParams = $params;
} else {
throw new \Exception('Subject parameters too long');
}
}
$event = $this->manager->generateEvent();
$event->setApp('deck')
->setType($eventType)
->setAuthor($subjectParams['author'])
->setObject($objectType, (int)$object->getId(), $object->getTitle())
->setSubject($subject, $subjectParams)
->setSubject($subject, array_merge($subjectParams, $additionalParams))
->setTimestamp(time());
if ($message !== null) {

View File

@@ -69,7 +69,15 @@ class ChangeSet implements \JsonSerializable {
return $this->after;
}
public function jsonSerialize(): array {
/**
* Specify data which should be serialized to JSON
*
* @link http://php.net/manual/en/jsonserializable.jsonserialize.php
* @return mixed data which can be serialized by <b>json_encode</b>,
* which is a value of any type other than a resource.
* @since 5.4.0
*/
public function jsonSerialize() {
return [
'before' => $this->getBefore(),
'after' => $this->getAfter(),

View File

@@ -121,7 +121,6 @@ class DeckProvider implements IProvider {
'link' => $this->deckUrl('/board/' . $event->getObjectId()),
];
$params['board'] = $board;
$event->setLink($this->deckUrl('/board/' . $event->getObjectId()));
}
if (isset($subjectParams['card']) && $event->getObjectType() === ActivityManager::DECK_OBJECT_CARD) {
@@ -135,8 +134,8 @@ class DeckProvider implements IProvider {
];
if (array_key_exists('board', $subjectParams)) {
$archivedParam = $subjectParams['card']['archived'] ? 'archived/' : '';
$card['link'] = $this->cardService->getRedirectUrlForCard($event->getObjectId());
$event->setLink($card['link']);
}
$params['card'] = $card;
}
@@ -312,19 +311,12 @@ class DeckProvider implements IProvider {
$userLanguage = $this->config->getUserValue($event->getAuthor(), 'core', 'lang', $this->l10nFactory->findLanguage());
$userLocale = $this->config->getUserValue($event->getAuthor(), 'core', 'locale', $this->l10nFactory->findLocale());
$l10n = $this->l10nFactory->get('deck', $userLanguage, $userLocale);
if (is_array($subjectParams['after'])) {
// Unluckily there was a time when we stored jsonSerialized date objects in the database
// Broken in 1.8.0 and fixed again in 1.8.1
$date = new \DateTime($subjectParams['after']['date']);
$date->setTimezone(new \DateTimeZone(\date_default_timezone_get()));
} else {
$date = new \DateTime($subjectParams['after']);
$date->setTimezone(new \DateTimeZone(\date_default_timezone_get()));
}
$date = new \DateTime($subjectParams['after']);
$date->setTimezone(new \DateTimeZone(\date_default_timezone_get()));
$params['after'] = [
'type' => 'highlight',
'id' => 'dt:' . $subjectParams['after'],
'name' => $l10n->l('datetime', $date),
'name' => $l10n->l('datetime', $date)
];
}
return $params;

View File

@@ -47,7 +47,6 @@ use OCA\Deck\Listeners\FullTextSearchEventListener;
use OCA\Deck\Middleware\DefaultBoardMiddleware;
use OCA\Deck\Middleware\ExceptionMiddleware;
use OCA\Deck\Notification\Notifier;
use OCA\Deck\Reference\CardReferenceProvider;
use OCA\Deck\Search\CardCommentProvider;
use OCA\Deck\Search\DeckProvider;
use OCA\Deck\Service\PermissionService;
@@ -58,22 +57,20 @@ use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
use OCP\Collaboration\Reference\RenderReferenceEvent;
use OCP\Collaboration\Resources\IProviderManager;
use OCP\Comments\CommentsEntityEvent;
use OCP\Comments\ICommentsManager;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\Group\Events\GroupDeletedEvent;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\Server;
use OCP\IServerContainer;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Notification\IManager as NotificationManager;
use OCP\Share\IManager;
use OCP\User\Events\UserDeletedEvent;
use OCP\Util;
use Psr\Container\ContainerInterface;
@@ -82,16 +79,13 @@ class Application extends App implements IBootstrap {
public const COMMENT_ENTITY_TYPE = 'deckCard';
/** @var IServerContainer */
private $server;
public function __construct(array $urlParams = []) {
parent::__construct(self::APP_ID, $urlParams);
// TODO move this back to ::register after fixing the autoload issue
// (and use a listener class)
$container = $this->getContainer();
$eventDispatcher = $container->get(IEventDispatcher::class);
$eventDispatcher->addListener(RenderReferenceEvent::class, function () {
Util::addScript(self::APP_ID, self::APP_ID . '-card-reference');
});
$this->server = \OC::$server;
}
public function boot(IBootContext $context): void {
@@ -130,12 +124,8 @@ class Application extends App implements IBootstrap {
$context->registerSearchProvider(CardCommentProvider::class);
$context->registerDashboardWidget(DeckWidget::class);
// reference widget
$context->registerReferenceProvider(CardReferenceProvider::class);
// $context->registerEventListener(RenderReferenceEvent::class, CardReferenceListener::class);
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
// Event listening for full text search indexing
$context->registerEventListener(CardCreatedEvent::class, FullTextSearchEventListener::class);
$context->registerEventListener(CardUpdatedEvent::class, FullTextSearchEventListener::class);
@@ -151,43 +141,33 @@ class Application extends App implements IBootstrap {
private function registerUserGroupHooks(IUserManager $userManager, IGroupManager $groupManager): void {
$container = $this->getContainer();
/** @var IEventDispatcher $eventDispatcher */
$eventDispatcher = $container->get(IEventDispatcher::class);
// Delete user/group acl entries when they get deleted
$eventDispatcher->addListener(UserDeletedEvent::class, static function (Event $event) use ($container): void {
if (!($event instanceof UserDeletedEvent)) {
return;
}
$user = $event->getUser();
$userManager->listen('\OC\User', 'postDelete', static function (IUser $user) use ($container) {
// delete existing acl entries for deleted user
/** @var AclMapper $aclMapper */
$aclMapper = $container->get(AclMapper::class);
$aclMapper = $container->query(AclMapper::class);
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_USER, $user->getUID());
foreach ($acls as $acl) {
$aclMapper->delete($acl);
}
// delete existing user assignments
$assignmentMapper = $container->get(AssignmentMapper::class);
$assignmentMapper = $container->query(AssignmentMapper::class);
$assignments = $assignmentMapper->findByParticipant($user->getUID());
foreach ($assignments as $assignment) {
$assignmentMapper->delete($assignment);
}
/** @var BoardMapper $boardMapper */
$boardMapper = $container->get(BoardMapper::class);
$boardMapper = $container->query(BoardMapper::class);
$boards = $boardMapper->findAllByOwner($user->getUID());
foreach ($boards as $board) {
$boardMapper->delete($board);
}
});
$eventDispatcher->addListener(GroupDeletedEvent::class, static function (Event $event) use ($container): void {
if (!($event instanceof GroupDeletedEvent)) {
return;
}
$group = $event->getGroup();
$groupManager->listen('\OC\Group', 'postDelete', static function (IGroup $group) use ($container) {
/** @var AclMapper $aclMapper */
$aclMapper = $container->get(AclMapper::class);
$aclMapper = $container->query(AclMapper::class);
$aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
foreach ($acls as $acl) {
@@ -201,7 +181,6 @@ class Application extends App implements IBootstrap {
$event->addEntityCollection(self::COMMENT_ENTITY_TYPE, function ($name) {
/** @var CardMapper */
$cardMapper = $this->getContainer()->get(CardMapper::class);
/** @var PermissionService $permissionService */
$permissionService = $this->getContainer()->get(PermissionService::class);
try {
@@ -224,7 +203,7 @@ class Application extends App implements IBootstrap {
$resourceManager->registerResourceProvider(ResourceProviderCard::class);
$symfonyAdapter->addListener('\OCP\Collaboration\Resources::loadAdditionalScripts', static function () {
if (strpos(Server::get(IRequest::class)->getPathInfo(), '/call/') === 0) {
if (strpos(\OC::$server->getRequest()->getPathInfo(), '/call/') === 0) {
// Talk integration has its own entrypoint which already includes collections handling
return;
}

View File

@@ -32,23 +32,20 @@ use OCP\AppFramework\QueryException;
use OCP\Collaboration\Resources\IManager;
use OCP\Collaboration\Resources\IProvider;
use OCP\Collaboration\Resources\IResource;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Server;
class ResourceProvider implements IProvider {
public const RESOURCE_TYPE = 'deck';
private BoardMapper $boardMapper;
private PermissionService $permissionService;
private IURLGenerator $urlGenerator;
private $boardMapper;
private $permissionService;
protected array $nodes = [];
/** @var array */
protected $nodes = [];
public function __construct(BoardMapper $boardMapper, PermissionService $permissionService, IURLGenerator $urlGenerator) {
public function __construct(BoardMapper $boardMapper, PermissionService $permissionService) {
$this->boardMapper = $boardMapper;
$this->permissionService = $permissionService;
$this->urlGenerator = $urlGenerator;
}
/**
@@ -73,14 +70,14 @@ class ResourceProvider implements IProvider {
*/
public function getResourceRichObject(IResource $resource): array {
$board = $this->getBoard($resource);
$link = $this->urlGenerator->linkToRoute('deck.page.index') . '#/board/' . $resource->getId();
$link = \OC::$server->getURLGenerator()->linkToRoute('deck.page.index') . '#/board/' . $resource->getId();
return [
'type' => self::RESOURCE_TYPE,
'id' => $resource->getId(),
'name' => $board->getTitle(),
'link' => $link,
'iconUrl' => $this->urlGenerator->imagePath('deck', 'deck-dark.svg')
'iconUrl' => \OC::$server->getURLGenerator()->imagePath('deck', 'deck-dark.svg')
];
}
@@ -103,9 +100,6 @@ class ResourceProvider implements IProvider {
if ($board->getOwner() === $user->getUID()) {
return true;
}
if ($board->getAcl() === null) {
return false;
}
return $this->permissionService->userCan($board->getAcl(), Acl::PERMISSION_READ, $user->getUID());
}
@@ -121,7 +115,7 @@ class ResourceProvider implements IProvider {
public function invalidateAccessCache($boardId = null) {
try {
/** @var IManager $resourceManager */
$resourceManager = Server::get(IManager::class);
$resourceManager = \OC::$server->query(IManager::class);
} catch (QueryException $e) {
}
if ($boardId !== null) {

View File

@@ -37,16 +37,24 @@ use OCP\Collaboration\Resources\IResource;
use OCP\Collaboration\Resources\ResourceException;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\Server;
class ResourceProviderCard implements IProvider {
public const RESOURCE_TYPE = 'deck-card';
private CardMapper $cardMapper;
private BoardMapper $boardMapper;
private PermissionService $permissionService;
private IURLGenerator $urlGenerator;
protected array $nodes = [];
/** @var CardMapper */
private $cardMapper;
/** @var BoardMapper */
private $boardMapper;
/** @var PermissionService */
private $permissionService;
/** @var IURLGenerator */
private $urlGenerator;
/** @var array */
protected $nodes = [];
public function __construct(CardMapper $cardMapper, BoardMapper $boardMapper, PermissionService $permissionService, IURLGenerator $urlGenerator) {
$this->cardMapper = $cardMapper;
@@ -119,9 +127,6 @@ class ResourceProviderCard implements IProvider {
if ($board->getOwner() === $user->getUID()) {
return true;
}
if ($board->getAcl() === null) {
return false;
}
return $this->permissionService->userCan($board->getAcl(), Acl::PERMISSION_READ, $user->getUID());
}
@@ -139,7 +144,7 @@ class ResourceProviderCard implements IProvider {
public function invalidateAccessCache($cardId = null) {
try {
/** @var IManager $resourceManager */
$resourceManager = Server::get(IManager::class);
$resourceManager = \OC::$server->query(IManager::class);
} catch (QueryException $e) {
}
if ($cardId !== null) {

View File

@@ -1,91 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
*
* @author Vitor Mattos <vitor@php.rio>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Command;
use OCA\Deck\Service\Importer\BoardImportCommandService;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class BoardImport extends Command {
private BoardImportCommandService $boardImportCommandService;
public function __construct(
BoardImportCommandService $boardImportCommandService
) {
$this->boardImportCommandService = $boardImportCommandService;
parent::__construct();
}
/**
* @return void
*/
protected function configure() {
$allowedSystems = $this->boardImportCommandService->getAllowedImportSystems();
$names = array_column($allowedSystems, 'name');
$this
->setName('deck:import')
->setDescription('Import data')
->addOption(
'system',
null,
InputOption::VALUE_REQUIRED,
'Source system for import. Available options: ' . implode(', ', $names) . '.',
null
)
->addOption(
'config',
null,
InputOption::VALUE_REQUIRED,
'Configuration json file.',
'config.json'
)
->addOption(
'data',
null,
InputOption::VALUE_OPTIONAL,
'Data file to import.',
'data.json'
)
;
}
/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output): int {
$this
->boardImportCommandService
->setInput($input)
->setOutput($output)
->setCommand($this)
->import();
$output->writeln('Done!');
return 0;
}
}

View File

@@ -27,7 +27,6 @@ use OCA\Deck\Db\AssignmentMapper;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Db\StackMapper;
use OCA\Deck\Model\CardDetails;
use OCA\Deck\Service\BoardService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
@@ -102,9 +101,7 @@ class UserExport extends Command {
$fullCard = $this->cardMapper->find($card->getId());
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
$fullCard->setAssignedUsers($assignedUsers);
$cardDetails = new CardDetails($fullCard, $fullBoard);
$data[$board->getId()]['stacks'][$stack->getId()]['cards'][] = $cardDetails->jsonSerialize();
$data[$board->getId()]['stacks'][$stack->getId()]['cards'][] = (array)$fullCard->jsonSerialize();
}
}
}

View File

@@ -1,85 +0,0 @@
<?php
/**
* @copyright Copyright (c) 2021 Vitor Mattos <vitor@php.rio>
*
* @author Vitor Mattos <vitor@php.rio>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Controller;
use OCA\Deck\Service\Importer\BoardImportService;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
use OCP\IRequest;
class BoardImportApiController extends OCSController {
/** @var BoardImportService */
private $boardImportService;
/** @var string */
private $userId;
public function __construct(
string $appName,
IRequest $request,
BoardImportService $boardImportService,
string $userId
) {
parent::__construct($appName, $request);
$this->boardImportService = $boardImportService;
$this->userId = $userId;
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function import(string $system, array $config, array $data): DataResponse {
$this->boardImportService->setSystem($system);
$config = json_decode(json_encode($config));
$config->owner = $this->userId;
$this->boardImportService->setConfigInstance($config);
$this->boardImportService->setData(json_decode(json_encode($data)));
$this->boardImportService->import();
return new DataResponse($this->boardImportService->getBoard(), Http::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function getAllowedSystems(): DataResponse {
$allowedSystems = $this->boardImportService->getAllowedImportSystems();
return new DataResponse($allowedSystems, Http::STATUS_OK);
}
/**
* @NoAdminRequired
* @CORS
* @NoCSRFRequired
*/
public function getConfigSchema(string $name): DataResponse {
$this->boardImportService->setSystem($name);
$this->boardImportService->validateSystem();
$jsonSchemaPath = json_decode(file_get_contents($this->boardImportService->getJsonSchemaPath()));
return new DataResponse($jsonSchemaPath, Http::STATUS_OK);
}
}

View File

@@ -29,9 +29,7 @@ use OCA\Deck\Service\PermissionService;
use OCA\Files\Event\LoadSidebar;
use OCA\Viewer\Event\LoadViewer;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent as CollaborationResourcesEvent;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IConfig;
use OCP\IInitialStateService;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;
@@ -43,17 +41,16 @@ use OCA\Deck\Db\Acl;
use OCA\Deck\Service\CardService;
class PageController extends Controller {
private PermissionService $permissionService;
private IInitialStateService $initialState;
private ConfigService $configService;
private IEventDispatcher $eventDispatcher;
private CardMapper $cardMapper;
private IURLGenerator $urlGenerator;
private CardService $cardService;
private IConfig $config;
private $permissionService;
private $initialState;
private $configService;
private $eventDispatcher;
private $cardMapper;
private $urlGenerator;
private $cardService;
public function __construct(
string $AppName,
$AppName,
IRequest $request,
PermissionService $permissionService,
IInitialStateService $initialStateService,
@@ -61,8 +58,7 @@ class PageController extends Controller {
IEventDispatcher $eventDispatcher,
CardMapper $cardMapper,
IURLGenerator $urlGenerator,
CardService $cardService,
IConfig $config
CardService $cardService
) {
parent::__construct($AppName, $request);
@@ -73,7 +69,6 @@ class PageController extends Controller {
$this->cardMapper = $cardMapper;
$this->urlGenerator = $urlGenerator;
$this->cardService = $cardService;
$this->config = $config;
}
/**
@@ -89,17 +84,13 @@ class PageController extends Controller {
$this->initialState->provideInitialState(Application::APP_ID, 'config', $this->configService->getAll());
$this->eventDispatcher->dispatchTyped(new LoadSidebar());
$this->eventDispatcher->dispatchTyped(new CollaborationResourcesEvent());
if (class_exists(LoadViewer::class)) {
$this->eventDispatcher->dispatchTyped(new LoadViewer());
}
$response = new TemplateResponse('deck', 'main', [
'id-app-content' => '#app-content-vue',
'id-app-navigation' => '#app-navigation-vue',
]);
$response = new TemplateResponse('deck', 'main');
if ($this->config->getSystemValueBool('debug', false)) {
if (\OC::$server->getConfig()->getSystemValueBool('debug', false)) {
$csp = new ContentSecurityPolicy();
$csp->addAllowedConnectDomain('*');
$csp->addAllowedScriptDomain('*');

View File

@@ -27,7 +27,6 @@ declare(strict_types=1);
namespace OCA\Deck\Controller;
use OCA\Deck\Db\Card;
use OCA\Deck\Model\CardDetails;
use OCA\Deck\Service\SearchService;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\OCSController;
@@ -51,12 +50,9 @@ class SearchController extends OCSController {
public function search(string $term, ?int $limit = null, ?int $cursor = null): DataResponse {
$cards = $this->searchService->searchCards($term, $limit, $cursor);
return new DataResponse(array_map(function (Card $card) {
$board = $card->getRelatedBoard();
$json = (new CardDetails($card, $board))->jsonSerialize();
$json['relatedBoard'] = $board;
$json = $card->jsonSerialize();
$json['relatedStack'] = $card->getRelatedStack();
$json['relatedBoard'] = $card->getRelatedBoard();
return $json;
}, $cards));
}

View File

@@ -24,8 +24,7 @@
namespace OCA\Deck\Cron;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\Job;
use OC\BackgroundJob\Job;
use OCA\Deck\Activity\ActivityManager;
use OCA\Deck\Db\CardMapper;
@@ -36,8 +35,7 @@ class CardDescriptionActivity extends Job {
/** @var CardMapper */
private $cardMapper;
public function __construct(ITimeFactory $time, ActivityManager $activityManager, CardMapper $cardMapper) {
parent::__construct($time);
public function __construct(ActivityManager $activityManager, CardMapper $cardMapper) {
$this->activityManager = $activityManager;
$this->cardMapper = $cardMapper;
}

View File

@@ -24,15 +24,13 @@
namespace OCA\Deck\Cron;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use OC\BackgroundJob\Job;
use OCA\Deck\Db\AttachmentMapper;
use OCA\Deck\Db\BoardMapper;
use OCA\Deck\InvalidAttachmentType;
use OCA\Deck\Service\AttachmentService;
use OCP\BackgroundJob\IJob;
class DeleteCron extends TimedJob {
class DeleteCron extends Job {
/** @var BoardMapper */
private $boardMapper;
@@ -41,14 +39,10 @@ class DeleteCron extends TimedJob {
/** @var AttachmentMapper */
private $attachmentMapper;
public function __construct(ITimeFactory $time, BoardMapper $boardMapper, AttachmentService $attachmentService, AttachmentMapper $attachmentMapper) {
parent::__construct($time);
public function __construct(BoardMapper $boardMapper, AttachmentService $attachmentService, AttachmentMapper $attachmentMapper) {
$this->boardMapper = $boardMapper;
$this->attachmentService = $attachmentService;
$this->attachmentMapper = $attachmentMapper;
$this->setInterval(60 * 60 * 24);
$this->setTimeSensitivity(IJob::TIME_INSENSITIVE);
}
/**

View File

@@ -23,8 +23,7 @@
namespace OCA\Deck\Cron;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\Job;
use OC\BackgroundJob\Job;
use OCA\Deck\Db\Card;
use OCA\Deck\Db\CardMapper;
use OCA\Deck\Notification\NotificationHelper;
@@ -41,12 +40,10 @@ class ScheduledNotifications extends Job {
protected $logger;
public function __construct(
ITimeFactory $time,
CardMapper $cardMapper,
NotificationHelper $notificationHelper,
ILogger $logger
) {
parent::__construct($time);
$this->cardMapper = $cardMapper;
$this->notificationHelper = $notificationHelper;
$this->logger = $logger;

View File

@@ -52,6 +52,12 @@ class Calendar extends ExternalCalendar {
$this->board = $board;
$this->principalUri = $principalUri;
if ($board) {
$this->children = $this->backend->getChildren($board->getId());
} else {
$this->children = [];
}
}
public function getOwner() {
@@ -116,7 +122,7 @@ class Calendar extends ExternalCalendar {
public function getChild($name) {
if ($this->childExists($name)) {
$card = array_values(array_filter(
$this->getBackendChildren(),
$this->children,
function ($card) use (&$name) {
return $card->getCalendarPrefix() . '-' . $card->getId() . '.ics' === $name;
}
@@ -131,7 +137,7 @@ class Calendar extends ExternalCalendar {
public function getChildren() {
$childNames = array_map(function ($card) {
return $card->getCalendarPrefix() . '-' . $card->getId() . '.ics';
}, $this->getBackendChildren());
}, $this->children);
$children = [];
@@ -142,23 +148,9 @@ class Calendar extends ExternalCalendar {
return $children;
}
private function getBackendChildren() {
if ($this->children) {
return $this->children;
}
if ($this->board) {
$this->children = $this->backend->getChildren($this->board->getId());
} else {
$this->children = [];
}
return $this->children;
}
public function childExists($name) {
return count(array_filter(
$this->getBackendChildren(),
$this->children,
function ($card) use (&$name) {
return $card->getCalendarPrefix() . '-' . $card->getId() . '.ics' === $name;
}

View File

@@ -26,34 +26,18 @@ declare(strict_types=1);
namespace OCA\Deck\Dashboard;
use DateTime;
use OCA\Deck\AppInfo\Application;
use OCA\Deck\Db\Label;
use OCA\Deck\Service\OverviewService;
use OCP\Dashboard\IAPIWidget;
use OCP\Dashboard\IButtonWidget;
use OCP\Dashboard\IIconWidget;
use OCP\Dashboard\Model\WidgetButton;
use OCP\Dashboard\Model\WidgetItem;
use OCP\IDateTimeFormatter;
use OCP\Dashboard\IWidget;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Util;
class DeckWidget implements IAPIWidget, IButtonWidget, IIconWidget {
private IL10N $l10n;
private OverviewService $dashboardService;
private IURLGenerator $urlGenerator;
private IDateTimeFormatter $dateTimeFormatter;
class DeckWidget implements IWidget {
public function __construct(IL10N $l10n,
OverviewService $dashboardService,
IDateTimeFormatter $dateTimeFormatter,
IURLGenerator $urlGenerator) {
/**
* @var IL10N
*/
private $l10n;
public function __construct(IL10N $l10n) {
$this->l10n = $l10n;
$this->dashboardService = $dashboardService;
$this->urlGenerator = $urlGenerator;
$this->dateTimeFormatter = $dateTimeFormatter;
}
/**
@@ -84,88 +68,17 @@ class DeckWidget implements IAPIWidget, IButtonWidget, IIconWidget {
return 'icon-deck';
}
/**
* @inheritDoc
*/
public function getIconUrl(): string {
return $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->imagePath(Application::APP_ID, 'deck-dark.svg')
);
}
/**
* @inheritDoc
*/
public function getUrl(): ?string {
return $this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->linkToRoute(Application::APP_ID . '.page.index')
);
return null;
}
/**
* @inheritDoc
*/
public function load(): void {
Util::addScript('deck', 'deck-dashboard');
}
/**
* @inheritDoc
*/
public function getItems(string $userId, ?string $since = null, int $limit = 7): array {
$upcomingCards = $this->dashboardService->findUpcomingCards($userId);
$nowTimestamp = (new Datetime())->getTimestamp();
$sinceTimestamp = $since !== null ? (new Datetime($since))->getTimestamp() : null;
$upcomingCards = array_filter($upcomingCards, static function (array $card) use ($nowTimestamp, $sinceTimestamp) {
if ($card['duedate']) {
$ts = (new Datetime($card['duedate']))->getTimestamp();
return $ts > $nowTimestamp && ($sinceTimestamp === null || $ts > $sinceTimestamp);
}
return false;
});
usort($upcomingCards, static function ($a, $b) {
$a = new Datetime($a['duedate']);
$ta = $a->getTimestamp();
$b = new Datetime($b['duedate']);
$tb = $b->getTimestamp();
return ($ta > $tb) ? 1 : -1;
});
$upcomingCards = array_slice($upcomingCards, 0, $limit);
$urlGenerator = $this->urlGenerator;
$dateTimeFormatter = $this->dateTimeFormatter;
return array_map(static function (array $card) use ($urlGenerator, $dateTimeFormatter) {
$formattedDueDate = $dateTimeFormatter->formatDateTime(new DateTime($card['duedate']));
return new WidgetItem(
$card['title'] . ' (' . $formattedDueDate . ')',
implode(
', ',
array_map(static function (Label $label) {
return $label->jsonSerialize()['title'];
}, $card['labels'])
),
$urlGenerator->getAbsoluteURL(
$urlGenerator->linkToRoute(Application::APP_ID . '.page.redirectToCard', ['cardId' => $card['id']])
),
$urlGenerator->getAbsoluteURL(
$urlGenerator->imagePath(Application::APP_ID, 'deck-dark.svg')
),
$card['duedate']
);
}, $upcomingCards);
}
/**
* @inheritDoc
*/
public function getWidgetButtons(string $userId): array {
return [
new WidgetButton(
WidgetButton::TYPE_MORE,
$this->urlGenerator->getAbsoluteURL(
$this->urlGenerator->linkToRoute(Application::APP_ID . '.page.index')
),
$this->l10n->t('Load more')
),
];
\OCP\Util::addScript('deck', 'deck-dashboard');
}
}

View File

@@ -33,46 +33,18 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
parent::__construct($db, 'deck_board_acl', Acl::class);
}
/**
* @param numeric $boardId
* @param int|null $limit
* @param int|null $offset
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findAll($boardId, $limit = null, $offset = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'board_id', 'type', 'participant', 'permission_edit', 'permission_share', 'permission_manage')
->from('deck_board_acl')
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->setMaxResults($limit)
->setFirstResult($offset);
return $this->findEntities($qb);
$sql = 'SELECT id, board_id, type, participant, permission_edit, permission_share, permission_manage FROM `*PREFIX*deck_board_acl` WHERE `board_id` = ? ';
return $this->findEntities($sql, [$boardId], $limit, $offset);
}
/**
* @param numeric $userId
* @param numeric $id
* @return bool
* @throws \OCP\DB\Exception
*/
public function isOwner($userId, $id): bool {
$aclId = $id;
$qb = $this->db->getQueryBuilder();
$qb->select('acl.id')
->from($this->getTableName(), 'acl')
->innerJoin('acl', 'deck_boards', 'b', 'acl.board_id = b.id')
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('acl.id', $qb->createNamedParameter($aclId, IQueryBuilder::PARAM_INT)));
return count($qb->executeQuery()->fetchAll()) > 0;
public function isOwner($userId, $aclId): bool {
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_board_acl` WHERE id = ?)';
$stmt = $this->execute($sql, [$aclId]);
$row = $stmt->fetch();
return ($row['owner'] === $userId);
}
/**
* @param numeric $id
* @return int|null
*/
public function findBoardId($id): ?int {
try {
$entity = $this->find($id);
@@ -82,21 +54,9 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
return null;
}
/**
* @param int $type
* @param string $participant
* @return Acl[]
* @throws \OCP\DB\Exception
*/
public function findByParticipant($type, $participant): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->where($qb->expr()->eq('type', $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('participant', $qb->createNamedParameter($participant, IQueryBuilder::PARAM_STR)));
return $this->findEntities($qb);
$sql = 'SELECT * from *PREFIX*deck_board_acl WHERE type = ? AND participant = ?';
return $this->findEntities($sql, [$type, $participant]);
}
/**

View File

@@ -55,6 +55,9 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
$this->circleService = $circleService;
}
/**
* @return Assignment[]
*/
public function findAll(int $cardId): array {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
@@ -77,8 +80,8 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
}
public function isOwner($userId, $id): bool {
return $this->cardMapper->isOwner($userId, $id);
public function isOwner($userId, $cardId): bool {
return $this->cardMapper->isOwner($userId, $cardId);
}
public function findBoardId($id): ?int {

View File

@@ -30,6 +30,7 @@ use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUserManager;
use PDO;
class AttachmentMapper extends DeckMapper implements IPermissionMapper {
private $cardMapper;
@@ -51,53 +52,70 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
}
/**
* @param int $id
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
* @param $id
* @return Entity|Attachment
* @throws \OCP\AppFramework\Db\DoesNotExistException
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
*/
public function find($id) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->from('deck_attachment')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
return $this->findEntity($qb);
$cursor = $qb->execute();
$row = $cursor->fetch(PDO::FETCH_ASSOC);
if ($row === false) {
$cursor->closeCursor();
throw new DoesNotExistException('Did expect one result but found none when executing' . $qb);
}
$row2 = $cursor->fetch();
$cursor->closeCursor();
if ($row2 !== false) {
throw new MultipleObjectsReturnedException('Did not expect more than one result when executing' . $qb);
}
return $this->mapRowToEntity($row);
}
/**
* @param int $cardId
* @param string $data
* @return Attachment
* @throws DoesNotExistException
* @throws MultipleObjectsReturnedException
* @throws \OCP\DB\Exception
*/
public function findByData($cardId, $data) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->from('deck_attachment')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('data', $qb->createNamedParameter($data, IQueryBuilder::PARAM_STR)));
return $this->findEntity($qb);
$cursor = $qb->execute();
$row = $cursor->fetch(PDO::FETCH_ASSOC);
if ($row === false) {
$cursor->closeCursor();
throw new DoesNotExistException('Did expect one result but found none when executing' . $qb);
}
$cursor->closeCursor();
return $this->mapRowToEntity($row);
}
/**
* Find all attachments for a card
*
* @param $cardId
* @return Entity[]
* @throws \OCP\DB\Exception
* @return array
*/
public function findAll($cardId) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->from('deck_attachment')
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
$entities = [];
$cursor = $qb->execute();
while ($row = $cursor->fetch()) {
$entities[] = $this->mapRowToEntity($row);
}
$cursor->closeCursor();
return $entities;
}
/**
@@ -110,7 +128,7 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
$timeLimit = time() - (60 * 5);
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from($this->getTableName())
->from('deck_attachment')
->where($qb->expr()->gt('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
if ($withOffset) {
$qb
@@ -121,7 +139,13 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
->andWhere($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
}
return $this->findEntities($qb);
$entities = [];
$cursor = $qb->execute();
while ($row = $cursor->fetch()) {
$entities[] = $this->mapRowToEntity($row);
}
$cursor->closeCursor();
return $entities;
}

View File

@@ -23,23 +23,13 @@
namespace OCA\Deck\Db;
/**
* @method int getId()
* @method string getTitle()
* @method int getShared()
* @method bool getArchived()
* @method int getDeletedAt()
* @method int getLastModified()
*/
class Board extends RelationalEntity {
protected $title;
protected $owner;
protected $color;
protected $archived = false;
/** @var Label[]|null */
protected $labels = null;
/** @var Acl[]|null */
protected $acl = null;
protected $labels = [];
protected $acl = [];
protected $permissions = [];
protected $users = [];
protected $shared;
@@ -66,15 +56,11 @@ class Board extends RelationalEntity {
$this->shared = -1;
}
public function jsonSerialize(): array {
public function jsonSerialize() {
$json = parent::jsonSerialize();
if ($this->shared === -1) {
unset($json['shared']);
}
// FIXME: Ideally the API responses should follow the internal data structure and return null if the labels/acls have not been fetched from the db
// however this would be a breaking change for consumers of the API
$json['acl'] = $this->acl ?? [];
$json['labels'] = $this->labels ?? [];
return $json;
}
@@ -82,27 +68,21 @@ class Board extends RelationalEntity {
* @param Label[] $labels
*/
public function setLabels($labels) {
$this->labels = $labels;
foreach ($labels as $l) {
$this->labels[] = $l;
}
}
/**
* @param Acl[] $acl
*/
public function setAcl($acl) {
$this->acl = $acl;
foreach ($acl as $a) {
$this->acl[] = $a;
}
}
public function getETag() {
return md5((string)$this->getLastModified());
}
/** @returns Acl[]|null */
public function getAcl(): ?array {
return $this->acl;
}
/** @returns Label[]|null */
public function getLabels(): ?array {
return $this->labels;
}
}

View File

@@ -26,14 +26,13 @@ namespace OCA\Deck\Db;
use OC\Cache\CappedMemoryCache;
use OCA\Deck\Service\CirclesService;
use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUserManager;
use OCP\IGroupManager;
use Psr\Log\LoggerInterface;
class BoardMapper extends QBMapper implements IPermissionMapper {
class BoardMapper extends DeckMapper implements IPermissionMapper {
private $labelMapper;
private $aclMapper;
private $stackMapper;
@@ -42,10 +41,8 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
private $circlesService;
private $logger;
/** @var CappedMemoryCache<Board[]> */
/** @var CappedMemoryCache */
private $userBoardCache;
/** @var CappedMemoryCache<Board> */
private $boardCache;
public function __construct(
IDBConnection $db,
@@ -67,7 +64,6 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
$this->logger = $logger;
$this->userBoardCache = new CappedMemoryCache();
$this->boardCache = new CappedMemoryCache();
}
@@ -75,93 +71,40 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
* @param $id
* @param bool $withLabels
* @param bool $withAcl
* @return Board
* @return \OCP\AppFramework\Db\Entity
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
* @throws DoesNotExistException
*/
public function find($id, $withLabels = false, $withAcl = false): Board {
if (!isset($this->boardCache[$id])) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_boards')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
->orderBy('id');
$this->boardCache[$id] = $this->findEntity($qb);
}
// FIXME is this necessary? it was NOT done with the old mapper
// $this->mapOwner($board);
public function find($id, $withLabels = false, $withAcl = false) {
$sql = 'SELECT id, title, owner, color, archived, deleted_at, last_modified FROM `*PREFIX*deck_boards` ' .
'WHERE `id` = ?';
$board = $this->findEntity($sql, [$id]);
// Add labels
if ($withLabels && $this->boardCache[$id]->getLabels() === null) {
if ($withLabels) {
$labels = $this->labelMapper->findAll($id);
$this->boardCache[$id]->setLabels($labels);
$board->setLabels($labels);
}
// Add acl
if ($withAcl && $this->boardCache[$id]->getAcl() === null) {
if ($withAcl) {
$acl = $this->aclMapper->findAll($id);
$this->boardCache[$id]->setAcl($acl);
$board->setAcl($acl);
}
return $this->boardCache[$id];
return $board;
}
public function findBoardIds(string $userId): array {
$qb = $this->db->getQueryBuilder();
$qb->selectDistinct('b.id')
->from($this->getTableName(), 'b')
->leftJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'));
// Owned by the user
$qb->where($qb->expr()->andX(
$qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)),
));
// Shared to the user
$qb->orWhere($qb->expr()->andX(
$qb->expr()->eq('acl.participant', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)),
$qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_USER, IQueryBuilder::PARAM_INT)),
));
// Shared to user groups of the user
$groupIds = $this->groupManager->getUserGroupIds($this->userManager->get($userId));
if (count($groupIds) !== 0) {
$qb->orWhere($qb->expr()->andX(
$qb->expr()->in('acl.participant', $qb->createNamedParameter($groupIds, IQueryBuilder::PARAM_STR_ARRAY)),
$qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_GROUP, IQueryBuilder::PARAM_INT)),
));
}
// Shared to circles of the user
$circles = $this->circlesService->getUserCircles($userId);
if (count($circles) !== 0) {
$qb->orWhere($qb->expr()->andX(
$qb->expr()->in('acl.participant', $qb->createNamedParameter($circles, IQueryBuilder::PARAM_STR_ARRAY)),
$qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_CIRCLE, IQueryBuilder::PARAM_INT)),
));
}
$result = $qb->executeQuery();
return array_map(function (string $id) {
return (int)$id;
}, $result->fetchAll(\PDO::FETCH_COLUMN));
}
public function findAllForUser(string $userId, ?int $since = null, bool $includeArchived = true, ?int $before = null,
?string $term = null): array {
$useCache = ($since === -1 && $includeArchived === true && $before === null && $term === null);
public function findAllForUser(string $userId, int $since = -1, $includeArchived = true): array {
$useCache = ($since === -1 && $includeArchived === true);
if (!isset($this->userBoardCache[$userId]) || !$useCache) {
$groups = $this->groupManager->getUserGroupIds(
$this->userManager->get($userId)
);
$userBoards = $this->findAllByUser($userId, null, null, $since, $includeArchived, $before, $term);
$groupBoards = $this->findAllByGroups($userId, $groups, null, null, $since, $includeArchived, $before, $term);
$circleBoards = $this->findAllByCircles($userId, null, null, $since, $includeArchived, $before, $term);
$userBoards = $this->findAllByUser($userId, null, null, $since, $includeArchived);
$groupBoards = $this->findAllByGroups($userId, $groups, null, null, $since, $includeArchived);
$circleBoards = $this->findAllByCircles($userId, null, null, $since, $includeArchived);
$allBoards = array_unique(array_merge($userBoards, $groupBoards, $circleBoards));
foreach ($allBoards as $board) {
$this->boardCache[$board->getId()] = $board;
}
if ($useCache) {
$this->userBoardCache[$userId] = $allBoards;
}
@@ -172,92 +115,25 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
/**
* Find all boards for a given user
*
* @param $userId
* @param null $limit
* @param null $offset
* @return array
*/
public function findAllByUser(string $userId, ?int $limit = null, ?int $offset = null, ?int $since = null,
bool $includeArchived = true, ?int $before = null, ?string $term = null): array {
// FIXME this used to be a UNION to get boards owned by $userId and the user shares in one single query
// Is it possible with the query builder?
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
// this does not work in MySQL/PostgreSQL
//->selectAlias('0', 'shared')
->from('deck_boards', 'b')
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
public function findAllByUser($userId, $limit = null, $offset = null, $since = -1, $includeArchived = true) {
// FIXME: One moving to QBMapper we should allow filtering the boards probably by method chaining for additional where clauses
$sql = 'SELECT id, title, owner, color, archived, deleted_at, 0 as shared, last_modified FROM `*PREFIX*deck_boards` WHERE owner = ? AND last_modified > ?';
if (!$includeArchived) {
$qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
$sql .= ' AND NOT archived AND deleted_at = 0';
}
if ($since !== null) {
$qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
}
if ($before !== null) {
$qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
}
if ($term !== null) {
$qb->andWhere(
$qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
}
$qb->orderBy('b.id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$entries = $this->findEntities($qb);
foreach ($entries as $entry) {
$entry->setShared(0);
}
// shared with user
$qb->resetQueryParts();
$qb->select('b.id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
//->selectAlias('1', 'shared')
->from('deck_boards', 'b')
->innerJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'))
->where($qb->expr()->eq('acl.participant', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->andWhere($qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_USER, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->neq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$sql .= ' UNION ' .
'SELECT boards.id, title, owner, color, archived, deleted_at, 1 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' .
'JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE acl.participant=? AND acl.type=? AND boards.owner != ? AND last_modified > ?';
if (!$includeArchived) {
$qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
$sql .= ' AND NOT archived AND deleted_at = 0';
}
if ($since !== null) {
$qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
}
if ($before !== null) {
$qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
}
if ($term !== null) {
$qb->andWhere(
$qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
}
$qb->orderBy('b.id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$sharedEntries = $this->findEntities($qb);
foreach ($sharedEntries as $entry) {
$entry->setShared(1);
}
$entries = array_merge($entries, $sharedEntries);
$entries = $this->findEntities($sql, [$userId, $since, $userId, Acl::PERMISSION_TYPE_USER, $userId, $since], $limit, $offset);
/* @var Board $entry */
foreach ($entries as $entry) {
$acl = $this->aclMapper->findAll($entry->id);
@@ -266,75 +142,37 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
return $entries;
}
public function findAllByOwner(string $userId, ?int $limit = null, ?int $offset = null) {
$qb = $this->db->getQueryBuilder();
$qb->select('*')
->from('deck_boards')
->where($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)))
->orderBy('id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
return $this->findEntities($qb);
public function findAllByOwner(string $userId, int $limit = null, int $offset = null) {
$sql = 'SELECT * FROM `*PREFIX*deck_boards` WHERE owner = ?';
return $this->findEntities($sql, [$userId], $limit, $offset);
}
/**
* Find all boards for a given user
*
* @param $userId
* @param $groups
* @param null $limit
* @param null $offset
* @return array
*/
public function findAllByGroups(string $userId, array $groups, ?int $limit = null, ?int $offset = null, ?int $since = null,
bool $includeArchived = true, ?int $before = null, ?string $term = null): array {
public function findAllByGroups($userId, $groups, $limit = null, $offset = null, $since = -1,$includeArchived = true) {
if (count($groups) <= 0) {
return [];
}
$qb = $this->db->getQueryBuilder();
$qb->select('b.id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
//->selectAlias('2', 'shared')
->from('deck_boards', 'b')
->innerJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'))
->where($qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_GROUP, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->neq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$or = $qb->expr()->orx();
$sql = 'SELECT boards.id, title, owner, color, archived, deleted_at, 2 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' .
'INNER JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE owner != ? AND type=? AND (';
for ($i = 0, $iMax = count($groups); $i < $iMax; $i++) {
$or->add(
$qb->expr()->eq('acl.participant', $qb->createNamedParameter($groups[$i], IQueryBuilder::PARAM_STR))
);
$sql .= 'acl.participant = ? ';
if (count($groups) > 1 && $i < count($groups) - 1) {
$sql .= ' OR ';
}
}
$qb->andWhere($or);
$sql .= ')';
if (!$includeArchived) {
$qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
}
if ($since !== null) {
$qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
}
if ($before !== null) {
$qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
}
if ($term !== null) {
$qb->andWhere(
$qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
}
$qb->orderBy('b.id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$entries = $this->findEntities($qb);
foreach ($entries as $entry) {
$entry->setShared(2);
$sql .= ' AND NOT archived AND deleted_at = 0';
}
$entries = $this->findEntities($sql, array_merge([$userId, Acl::PERMISSION_TYPE_GROUP], $groups), $limit, $offset);
/* @var Board $entry */
foreach ($entries as $entry) {
$acl = $this->aclMapper->findAll($entry->id);
@@ -343,59 +181,25 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
return $entries;
}
public function findAllByCircles(string $userId, ?int $limit = null, ?int $offset = null, ?int $since = null,
bool $includeArchived = true, ?int $before = null, ?string $term = null) {
public function findAllByCircles($userId, $limit = null, $offset = null, $since = -1,$includeArchived = true) {
$circles = $this->circlesService->getUserCircles($userId);
if (count($circles) === 0) {
return [];
}
$qb = $this->db->getQueryBuilder();
$qb->select('b.id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
//->selectAlias('2', 'shared')
->from('deck_boards', 'b')
->innerJoin('b', 'deck_board_acl', 'acl', $qb->expr()->eq('b.id', 'acl.board_id'))
->where($qb->expr()->eq('acl.type', $qb->createNamedParameter(Acl::PERMISSION_TYPE_CIRCLE, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->neq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
$or = $qb->expr()->orx();
$sql = 'SELECT boards.id, title, owner, color, archived, deleted_at, 2 as shared, last_modified FROM `*PREFIX*deck_boards` as boards ' .
'INNER JOIN `*PREFIX*deck_board_acl` as acl ON boards.id=acl.board_id WHERE owner != ? AND type=? AND (';
for ($i = 0, $iMax = count($circles); $i < $iMax; $i++) {
$or->add(
$qb->expr()->eq('acl.participant', $qb->createNamedParameter($circles[$i], IQueryBuilder::PARAM_STR))
);
$sql .= 'acl.participant = ? ';
if (count($circles) > 1 && $i < count($circles) - 1) {
$sql .= ' OR ';
}
}
$qb->andWhere($or);
$sql .= ')';
if (!$includeArchived) {
$qb->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
}
if ($since !== null) {
$qb->andWhere($qb->expr()->gt('last_modified', $qb->createNamedParameter($since, IQueryBuilder::PARAM_INT)));
}
if ($before !== null) {
$qb->andWhere($qb->expr()->lt('last_modified', $qb->createNamedParameter($before, IQueryBuilder::PARAM_INT)));
}
if ($term !== null) {
$qb->andWhere(
$qb->expr()->iLike(
'title',
$qb->createNamedParameter(
'%' . $this->db->escapeLikeParameter($term) . '%',
IQueryBuilder::PARAM_STR
)
)
);
}
$qb->orderBy('b.id');
if ($limit !== null) {
$qb->setMaxResults($limit);
}
if ($offset !== null) {
$qb->setFirstResult($offset);
}
$entries = $this->findEntities($qb);
foreach ($entries as $entry) {
$entry->setShared(2);
$sql .= ' AND NOT archived AND deleted_at = 0';
}
$entries = $this->findEntities($sql, array_merge([$userId, Acl::PERMISSION_TYPE_CIRCLE], $circles), $limit, $offset);
/* @var Board $entry */
foreach ($entries as $entry) {
$acl = $this->aclMapper->findAll($entry->id);
@@ -404,26 +208,21 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
return $entries;
}
public function findAll(): array {
$qb = $this->db->getQueryBuilder();
$qb->select('id')
->from('deck_boards');
return $this->findEntities($qb);
public function findAll() {
$sql = 'SELECT id from *PREFIX*deck_boards;';
return $this->findEntities($sql);
}
public function findToDelete() {
// add buffer of 5 min
$timeLimit = time() - (60 * 5);
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'owner', 'color', 'archived', 'deleted_at', 'last_modified')
->from('deck_boards')
->where($qb->expr()->gt('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->lt('deleted_at', $qb->createNamedParameter($timeLimit, IQueryBuilder::PARAM_INT)));
return $this->findEntities($qb);
$sql = 'SELECT id, title, owner, color, archived, deleted_at, last_modified FROM `*PREFIX*deck_boards` ' .
'WHERE `deleted_at` > 0 AND `deleted_at` < ?';
return $this->findEntities($sql, [$timeLimit]);
}
public function delete(/** @noinspection PhpUnnecessaryFullyQualifiedNameInspection */
\OCP\AppFramework\Db\Entity $entity): \OCP\AppFramework\Db\Entity {
\OCP\AppFramework\Db\Entity $entity) {
// delete acl
$acl = $this->aclMapper->findAll($entity->getId());
foreach ($acl as $item) {
@@ -444,8 +243,8 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
return parent::delete($entity);
}
public function isOwner($userId, $id): bool {
$board = $this->find($id);
public function isOwner($userId, $boardId): bool {
$board = $this->find($boardId);
return ($board->getOwner() === $userId);
}
@@ -524,11 +323,6 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
* Reset cache for a given board or a given user
*/
public function flushCache(?int $boardId = null, ?string $userId = null) {
if ($boardId) {
unset($this->boardCache[$boardId]);
} else {
$this->boardCache = null;
}
if ($userId) {
unset($this->userBoardCache[$userId]);
} else {

View File

@@ -27,44 +27,6 @@ use DateTime;
use DateTimeZone;
use Sabre\VObject\Component\VCalendar;
/**
* @method string getTitle()
* @method string getDescription()
* @method string getDescriptionPrev()
* @method int getStackId()
* @method int getOrder()
* @method int getLastModified()
* @method int getCreatedAt()
* @method bool getArchived()
* @method bool getNotified()
*
* @method void setLabels(Label[] $labels)
* @method null|Label[] getLabels()
*
* @method void setAssignedUsers(Assignment[] $users)
* @method null|User[] getAssignedUsers()
*
* @method void setAttachments(Attachment[] $attachments)
* @method null|Attachment[] getAttachments()
*
* @method void setAttachmentCount(int $count)
* @method null|int getAttachmentCount()
*
* @method void setCommentsUnread(int $count)
* @method null|int getCommentsUnread()
*
* @method void setCommentsCount(int $count)
* @method null|int getCommentsCount()
*
* @method void setOwner(string $user)
* @method null|string getOwner()
*
* @method void setRelatedStack(Stack $stack)
* @method null|Stack getRelatedStack()
*
* @method void setRelatedBoard(Board $board)
* @method null|Board getRelatedBoard()
*/
class Card extends RelationalEntity {
public const TITLE_MAX_LENGTH = 255;
@@ -88,7 +50,7 @@ class Card extends RelationalEntity {
protected $deletedAt = 0;
protected $commentsUnread = 0;
protected $commentsCount = 0;
protected $relatedStack = null;
protected $relatedBoard = null;
@@ -108,7 +70,6 @@ class Card extends RelationalEntity {
$this->addType('archived', 'boolean');
$this->addType('notified', 'boolean');
$this->addType('deletedAt', 'integer');
$this->addType('duedate', 'datetime');
$this->addRelation('labels');
$this->addRelation('assignedUsers');
$this->addRelation('attachments');
@@ -117,7 +78,7 @@ class Card extends RelationalEntity {
$this->addRelation('commentsUnread');
$this->addRelation('commentsCount');
$this->addResolvable('owner');
$this->addRelation('relatedStack');
$this->addRelation('relatedBoard');
}
@@ -126,6 +87,51 @@ class Card extends RelationalEntity {
$this->databaseType = $type;
}
public function getDuedate($isoFormat = false) {
if ($this->duedate === null) {
return null;
}
$dt = new DateTime($this->duedate);
if (!$isoFormat && $this->databaseType === 'mysql') {
return $dt->format('Y-m-d H:i:s');
}
return $dt->format('c');
}
public function jsonSerialize() {
$json = parent::jsonSerialize();
$json['overdue'] = self::DUEDATE_FUTURE;
$due = strtotime($this->duedate);
$today = new DateTime();
$today->setTime(0, 0);
$match_date = new DateTime($this->duedate);
$match_date->setTime(0, 0);
$diff = $today->diff($match_date);
$diffDays = (integer) $diff->format('%R%a'); // Extract days count in interval
if ($due !== false) {
if ($diffDays === 1) {
$json['overdue'] = self::DUEDATE_NEXT;
}
if ($diffDays === 0) {
$json['overdue'] = self::DUEDATE_NOW;
}
if ($diffDays < 0) {
$json['overdue'] = self::DUEDATE_OVERDUE;
}
}
$json['duedate'] = $this->getDuedate(true);
unset($json['notified']);
unset($json['descriptionPrev']);
unset($json['relatedStack']);
unset($json['relatedBoard']);
return $json;
}
public function getCalendarObject(): VCalendar {
$calendar = new VCalendar();
$event = $calendar->createComponent('VTODO');
@@ -134,7 +140,7 @@ class Card extends RelationalEntity {
$creationDate = new DateTime();
$creationDate->setTimestamp($this->createdAt);
$event->DTSTAMP = $creationDate;
$event->DUE = new DateTime($this->getDuedate()->format('c'), new DateTimeZone('UTC'));
$event->DUE = new DateTime($this->getDuedate(true), new DateTimeZone('UTC'));
}
$event->add('RELATED-TO', 'deck-stack-' . $this->getStackId());

View File

@@ -30,8 +30,6 @@ use OCA\Deck\Search\Query\SearchQuery;
use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\QBMapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUser;
@@ -48,8 +46,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
private $groupManager;
/** @var IManager */
private $notificationManager;
/** @var ICache */
private $cache;
private $databaseType;
private $database4ByteSupport;
@@ -59,7 +55,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
IUserManager $userManager,
IGroupManager $groupManager,
IManager $notificationManager,
ICacheFactory $cacheFactory,
$databaseType = 'sqlite3',
$database4ByteSupport = true
) {
@@ -68,7 +63,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->notificationManager = $notificationManager;
$this->cache = $cacheFactory->createDistributed('deck-cardMapper');
$this->databaseType = $databaseType;
$this->database4ByteSupport = $database4ByteSupport;
}
@@ -81,9 +75,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$description = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $entity->getDescription());
$entity->setDescription($description);
}
$entity = parent::insert($entity);
$this->cache->remove('findBoardId:' . $entity->getId());
return $entity;
return parent::insert($entity);
}
public function update(Entity $entity, $updateModified = true): Entity {
@@ -115,10 +107,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
} catch (Exception $e) {
}
}
// Invalidate cache when the card may be moved to a different board
if (isset($updatedFields['stackId'])) {
$this->cache->remove('findBoardId:' . $entity->getId());
}
return parent::update($entity);
}
@@ -193,7 +181,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
->from('deck_cards', 'c')
->join('c', 'deck_stacks', 's', 's.id = c.stack_id')
->where($qb->expr()->eq('s.board_id', $qb->createNamedParameter($boardId)))
->andWhere($qb->expr()->eq('c.archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->andWhere($qb->expr()->eq('c.deleted_at', $qb->createNamedParameter('0')))
->orderBy('c.duedate')
->setMaxResults($limit)
@@ -227,21 +214,6 @@ class CardMapper extends QBMapper implements IPermissionMapper {
return $this->findEntities($qb);
}
public function findAllByBoardId(int $boardId, ?int $limit = null, ?int $offset = null): array {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
->from('deck_cards', 'c')
->innerJoin('c', 'deck_stacks', 's', 's.id = c.stack_id')
->innerJoin('s', 'deck_boards', 'b', 'b.id = s.board_id')
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
->andWhere($qb->expr()->eq('archived', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
->setMaxResults($limit)
->setFirstResult($offset)
->orderBy('c.lastmodified')
->addOrderBy('c.id');
return $this->findEntities($qb);
}
public function findAllWithDue($boardId) {
$qb = $this->db->getQueryBuilder();
$qb->select('c.*')
@@ -281,7 +253,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
public function findOverdue() {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'duedate', 'notified')
$qb->select('id','title','duedate','notified')
->from('deck_cards')
->where($qb->expr()->lt('duedate', $qb->createFunction('NOW()')))
->andWhere($qb->expr()->eq('notified', $qb->createNamedParameter(false, IQueryBuilder::PARAM_BOOL)))
@@ -292,7 +264,7 @@ class CardMapper extends QBMapper implements IPermissionMapper {
public function findUnexposedDescriptionChances() {
$qb = $this->db->getQueryBuilder();
$qb->select('id', 'title', 'duedate', 'notified', 'description_prev', 'last_editor', 'description')
$qb->select('id','title','duedate','notified','description_prev','last_editor','description')
->from('deck_cards')
->where($qb->expr()->isNotNull('last_editor'))
->andWhere($qb->expr()->isNotNull('description_prev'));
@@ -507,8 +479,8 @@ class CardMapper extends QBMapper implements IPermissionMapper {
}
return $qb->createNamedParameter($dateTime, IQueryBuilder::PARAM_DATE);
}
public function searchRaw($boardIds, $term, $limit = null, $offset = null) {
$qb = $this->queryCardsByBoards($boardIds)
@@ -534,8 +506,9 @@ class CardMapper extends QBMapper implements IPermissionMapper {
}
public function delete(Entity $entity): Entity {
// delete assigned labels
$this->labelMapper->deleteLabelAssignmentsForCard($entity->getId());
$this->cache->remove('findBoardId:' . $entity->getId());
// delete card
return parent::delete($entity);
}
@@ -564,32 +537,21 @@ class CardMapper extends QBMapper implements IPermissionMapper {
$qb->execute();
}
public function isOwner($userId, $id): bool {
public function isOwner($userId, $cardId): bool {
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id IN (SELECT stack_id FROM `*PREFIX*deck_cards` WHERE id = ?))';
$stmt = $this->db->prepare($sql);
$stmt->bindParam(1, $id, \PDO::PARAM_INT, 0);
$stmt->bindParam(1, $cardId, \PDO::PARAM_INT);
$stmt->execute();
$row = $stmt->fetch();
return ($row['owner'] === $userId);
}
public function findBoardId($id): ?int {
$result = $this->cache->get('findBoardId:' . $id);
if ($result === null) {
try {
$qb = $this->db->getQueryBuilder();
$qb->select('board_id')
->from('deck_stacks', 's')
->innerJoin('s', 'deck_cards', 'c', 'c.stack_id = s.id')
->where($qb->expr()->eq('c.id', $qb->createNamedParameter($id)));
$queryResult = $qb->executeQuery();
$result = $queryResult->fetchOne();
} catch (\Exception $e) {
$result = false;
}
$this->cache->set('findBoardId:' . $id, $result);
}
return $result !== false ? $result : null;
$sql = 'SELECT id FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id IN (SELECT stack_id FROM `*PREFIX*deck_cards` WHERE id = ?))';
$stmt = $this->db->prepare($sql);
$stmt->bindParam(1, $id, \PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchColumn() ?? null;
}
public function mapOwner(Card &$card) {

View File

@@ -24,7 +24,6 @@
namespace OCA\Deck\Db;
use OCP\ICacheFactory;
use OCP\ICache;
use OCP\IDBConnection;
use OCP\IRequest;
@@ -32,16 +31,13 @@ class ChangeHelper {
public const TYPE_BOARD = 'boardChanged';
public const TYPE_CARD = 'cardChanged';
private IDBConnection $db;
private ICache $cache;
private IRequest $request;
private ?string $userId;
private $db;
public function __construct(
IDBConnection $db,
ICacheFactory $cacheFactory,
IRequest $request,
?string $userId
$userId
) {
$this->db = $db;
$this->cache = $cacheFactory->createDistributed('deck_changes');

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