Compare commits
706 Commits
v1.7.4
...
backport/4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0cf40ae9af | ||
|
|
083db2f3b6 | ||
|
|
cfd0fc828f | ||
|
|
b0ebb34fb8 | ||
|
|
e72b654bc6 | ||
|
|
d5ae69afce | ||
|
|
5dfe12a302 | ||
|
|
103a2c8508 | ||
|
|
4120f2e7ee | ||
|
|
9f9ef99a31 | ||
|
|
7ce6e1779a | ||
|
|
5cc075cbf9 | ||
|
|
150e663283 | ||
|
|
2bdaba5c85 | ||
|
|
3c3837c8d3 | ||
|
|
c30e253f83 | ||
|
|
c3acf91184 | ||
|
|
4e513264c5 | ||
|
|
f470ed4848 | ||
|
|
396b725442 | ||
|
|
5c8b48faa0 | ||
|
|
5141d463fc | ||
|
|
5360b8552e | ||
|
|
c93b837754 | ||
|
|
9ca7a2fc2c | ||
|
|
f5e733362e | ||
|
|
c3697295e6 | ||
|
|
cffe0f708e | ||
|
|
ca96096617 | ||
|
|
baea5d83d3 | ||
|
|
14e37c4392 | ||
|
|
be39fb3b7c | ||
|
|
7a262c5188 | ||
|
|
d97fe80e48 | ||
|
|
88b4fd0756 | ||
|
|
c8878d4497 | ||
|
|
68c1faabaa | ||
|
|
60d8370dbe | ||
|
|
685db5a4fe | ||
|
|
c190832287 | ||
|
|
2740d2145e | ||
|
|
58ed490c7d | ||
|
|
372878b318 | ||
|
|
d055b44fa2 | ||
|
|
deb63d4a9a | ||
|
|
66fda2265d | ||
|
|
44037cfd18 | ||
|
|
b450d96d4b | ||
|
|
9f1597a2f3 | ||
|
|
a0a929bdec | ||
|
|
deffd3adaa | ||
|
|
cd7ade1eaf | ||
|
|
e568d79bbe | ||
|
|
1ddb3cdac9 | ||
|
|
a982a0dbed | ||
|
|
bebbb046ea | ||
|
|
37f305aba2 | ||
|
|
dcc42f7613 | ||
|
|
bea851009c | ||
|
|
6a5e72bf6d | ||
|
|
b5c6b54fab | ||
|
|
61a801502b | ||
|
|
54c5cbbef1 | ||
|
|
6956551e3c | ||
|
|
62a54481e5 | ||
|
|
9d9b333152 | ||
|
|
0179b99f24 | ||
|
|
342003ea2e | ||
|
|
20d4476843 | ||
|
|
cc1b30be54 | ||
|
|
c1e52fd168 | ||
|
|
81d8748e96 | ||
|
|
06e3272b79 | ||
|
|
b71d8e2090 | ||
|
|
302ec1c44e | ||
|
|
28c031a68e | ||
|
|
b938c3b2bf | ||
|
|
78fea30e39 | ||
|
|
fcbc0cf092 | ||
|
|
dbdbb00fd5 | ||
|
|
dcccd63864 | ||
|
|
cf52359e7a | ||
|
|
cf7f231047 | ||
|
|
8449d91656 | ||
|
|
4126e4dd43 | ||
|
|
566778be58 | ||
|
|
063c4cbfef | ||
|
|
8d03a03b27 | ||
|
|
46f76f2dbc | ||
|
|
9f68a58407 | ||
|
|
58a5275e78 | ||
|
|
c644fc9680 | ||
|
|
7eb078d0a8 | ||
|
|
6126cc53a2 | ||
|
|
38344cee55 | ||
|
|
3e0baf9330 | ||
|
|
2899927efe | ||
|
|
4ce2265f5e | ||
|
|
2872cd39d9 | ||
|
|
73752472ac | ||
|
|
508ded19ba | ||
|
|
57e8698f3b | ||
|
|
abf00984f4 | ||
|
|
b53c10c47d | ||
|
|
5e2d8ac24a | ||
|
|
5db87329ac | ||
|
|
d12ccc0208 | ||
|
|
d67b1e4ba9 | ||
|
|
81c0e37ba0 | ||
|
|
f44eb87d77 | ||
|
|
55b75e16a0 | ||
|
|
020b16a33d | ||
|
|
fef75fa169 | ||
|
|
4547e1ae3d | ||
|
|
0e0ff7e16c | ||
|
|
6329da3d4d | ||
|
|
c37cae0a5c | ||
|
|
9d852b93ce | ||
|
|
5035be90f0 | ||
|
|
6a07581508 | ||
|
|
84b1de5308 | ||
|
|
3533388c37 | ||
|
|
43fa69aa65 | ||
|
|
ab051171ed | ||
|
|
72de3f257a | ||
|
|
186fc572b3 | ||
|
|
ea95fcaaf3 | ||
|
|
e4e474235e | ||
|
|
64f406355e | ||
|
|
d523e4f64e | ||
|
|
81f8423218 | ||
|
|
2d1f39eb82 | ||
|
|
d601b0300b | ||
|
|
eb8b94328f | ||
|
|
e29fc0f320 | ||
|
|
165c8ebc1c | ||
|
|
527be16719 | ||
|
|
cc86209179 | ||
|
|
c7de8c4590 | ||
|
|
68acd38421 | ||
|
|
54e298d00f | ||
|
|
ba20c1a9eb | ||
|
|
49cb27d576 | ||
|
|
7320a48dce | ||
|
|
3c834b79f5 | ||
|
|
4637024240 | ||
|
|
440f2710f0 | ||
|
|
0ebd55890c | ||
|
|
3409a9f13f | ||
|
|
7fae8e0928 | ||
|
|
609c975de4 | ||
|
|
64615a3e14 | ||
|
|
fb72a0f24d | ||
|
|
54c5a7f963 | ||
|
|
8d46fbbd04 | ||
|
|
0d787f9989 | ||
|
|
f19f22737a | ||
|
|
687f5c933c | ||
|
|
fd95e77d4e | ||
|
|
fba52536d0 | ||
|
|
cfcbc94006 | ||
|
|
9643f0581e | ||
|
|
c02a24f85b | ||
|
|
728c7916be | ||
|
|
1af1c7f581 | ||
|
|
87439dbaad | ||
|
|
c79ec72b79 | ||
|
|
beff26500a | ||
|
|
6df2e2b755 | ||
|
|
4b793d3738 | ||
|
|
399a85ce9f | ||
|
|
673222d37e | ||
|
|
eced4dd2d3 | ||
|
|
293f241aa6 | ||
|
|
5a79c61732 | ||
|
|
c28a877d0b | ||
|
|
484b9df8c9 | ||
|
|
db7403acc0 | ||
|
|
8c8788eab3 | ||
|
|
0f4ad52ca9 | ||
|
|
8b156958f8 | ||
|
|
88ba778e55 | ||
|
|
192314fb02 | ||
|
|
0bfb983a1c | ||
|
|
c3a8f797d1 | ||
|
|
b67ed2057c | ||
|
|
234ba20eba | ||
|
|
88097f98e8 | ||
|
|
4c3c483011 | ||
|
|
5a9cba00ed | ||
|
|
8f822cfd84 | ||
|
|
45c19c1c2d | ||
|
|
369749bb46 | ||
|
|
5ec04ac670 | ||
|
|
69b8ed2486 | ||
|
|
74d8fba022 | ||
|
|
2ca8863b5d | ||
|
|
63e57f2667 | ||
|
|
165ef3adf6 | ||
|
|
8ccdc6a2ed | ||
|
|
a04f2004bd | ||
|
|
ad00818bd1 | ||
|
|
138087b296 | ||
|
|
cd5500ee50 | ||
|
|
cda8a40607 | ||
|
|
84e6679704 | ||
|
|
7d8bc27d73 | ||
|
|
566de0f9e7 | ||
|
|
146fbbcb6f | ||
|
|
d2dd0d2914 | ||
|
|
6010e839d0 | ||
|
|
2895f563cd | ||
|
|
7663a4a346 | ||
|
|
864272c1a2 | ||
|
|
4f75064ba4 | ||
|
|
ffd6d4dedc | ||
|
|
891139f682 | ||
|
|
496304c896 | ||
|
|
9cfde4d80c | ||
|
|
eba4453728 | ||
|
|
f36a2a11a6 | ||
|
|
6c9af9cc12 | ||
|
|
de6604ad5f | ||
|
|
d4ebfc7ab1 | ||
|
|
2a2f578c13 | ||
|
|
f04cf33c78 | ||
|
|
6d89bbacf7 | ||
|
|
0010255391 | ||
|
|
50de5610b9 | ||
|
|
8def56fd8b | ||
|
|
bdbcb236a1 | ||
|
|
70fa18f186 | ||
|
|
43a7255c2a | ||
|
|
8c24def8d8 | ||
|
|
937f285689 | ||
|
|
a66c960b21 | ||
|
|
81d922c8bd | ||
|
|
347364543d | ||
|
|
6bc703a9b0 | ||
|
|
ee99a768f4 | ||
|
|
beee212fcb | ||
|
|
33145e377b | ||
|
|
5552a9d538 | ||
|
|
4fcdb6f304 | ||
|
|
0e4d345fc1 | ||
|
|
ee0092e7ca | ||
|
|
c3f0a1edc4 | ||
|
|
a8b46ad10f | ||
|
|
daa65b40fb | ||
|
|
fa6027f7ba | ||
|
|
f26a085b13 | ||
|
|
9d09916c17 | ||
|
|
f250d9956b | ||
|
|
93994bb39c | ||
|
|
17f7d19624 | ||
|
|
7e0c1a8024 | ||
|
|
f6dc22c545 | ||
|
|
d196956519 | ||
|
|
4769c69ba7 | ||
|
|
f322b6191e | ||
|
|
e076c76b3f | ||
|
|
df8257a18f | ||
|
|
e7f22ca0d3 | ||
|
|
a9921ecdf0 | ||
|
|
a5da643513 | ||
|
|
8c68b7ce83 | ||
|
|
a4137a4edd | ||
|
|
cb5a181993 | ||
|
|
46972646d8 | ||
|
|
8549d4a13f | ||
|
|
47077af838 | ||
|
|
6cc589539b | ||
|
|
2237745c09 | ||
|
|
b74569abef | ||
|
|
de67847ef1 | ||
|
|
a430eaf41f | ||
|
|
31b68ae5e5 | ||
|
|
fa1877ef7b | ||
|
|
5851c4a5f1 | ||
|
|
4fadb9a633 | ||
|
|
d021559d7c | ||
|
|
7f22925063 | ||
|
|
e65fa778cb | ||
|
|
003ee7a926 | ||
|
|
8b344a653f | ||
|
|
7403aafe16 | ||
|
|
f7b6e8a3bc | ||
|
|
ad051c5e0e | ||
|
|
7ceb23f7a2 | ||
|
|
9760c838aa | ||
|
|
8002cecda4 | ||
|
|
27b1c6a2f2 | ||
|
|
af8e61ece6 | ||
|
|
f9836d4dfb | ||
|
|
982b867a04 | ||
|
|
a6c9bd5c09 | ||
|
|
8008b9d0cb | ||
|
|
7f01db17d0 | ||
|
|
9ec44bdadd | ||
|
|
660290121c | ||
|
|
7dab7fad81 | ||
|
|
7b0630143d | ||
|
|
17258783c2 | ||
|
|
9398d86fba | ||
|
|
88ef90fce7 | ||
|
|
4213ec0986 | ||
|
|
f4b2563629 | ||
|
|
baf0e0c1f5 | ||
|
|
b888a65b8b | ||
|
|
a6eda5a0b8 | ||
|
|
6449082349 | ||
|
|
44127d4bf6 | ||
|
|
0c95e7ca1e | ||
|
|
e29ac75f3d | ||
|
|
7dba89a03a | ||
|
|
61d23ddc6f | ||
|
|
fc921143d3 | ||
|
|
395c79b32a | ||
|
|
7c683efce6 | ||
|
|
f07eb17dff | ||
|
|
242906162f | ||
|
|
c423f6ecd8 | ||
|
|
3c83320c20 | ||
|
|
89068641ee | ||
|
|
3e46fe777d | ||
|
|
a73d790a95 | ||
|
|
40b8596275 | ||
|
|
6487ed966f | ||
|
|
c5cb8ed5a7 | ||
|
|
d7ee3b72a6 | ||
|
|
36e2443267 | ||
|
|
2c1b95a3cb | ||
|
|
0628624edf | ||
|
|
2bd854ff18 | ||
|
|
18b6ed080c | ||
|
|
5010f0e0e1 | ||
|
|
82c7145163 | ||
|
|
cb60e70ae9 | ||
|
|
f8a255a9f0 | ||
|
|
38040cc246 | ||
|
|
0961bf088f | ||
|
|
76afb87624 | ||
|
|
8dc8513263 | ||
|
|
4d3f2bf1e4 | ||
|
|
4c82154eb0 | ||
|
|
858334cc64 | ||
|
|
7bb02a9b63 | ||
|
|
1551cdf517 | ||
|
|
8a8a1ac060 | ||
|
|
123e43e626 | ||
|
|
03e54ffdff | ||
|
|
c897074cb3 | ||
|
|
03fa4dc816 | ||
|
|
3ea4c635d0 | ||
|
|
78ed0852ea | ||
|
|
89d46dcab4 | ||
|
|
411626c038 | ||
|
|
56e3215785 | ||
|
|
b81f55057a | ||
|
|
e2dc1c2684 | ||
|
|
e2c5367050 | ||
|
|
15c48b919d | ||
|
|
7ad36b07b1 | ||
|
|
85dbb18663 | ||
|
|
a712be416e | ||
|
|
2246e12a6a | ||
|
|
0975eb7d78 | ||
|
|
f4610dc6eb | ||
|
|
df5b2abf21 | ||
|
|
e865627158 | ||
|
|
fdd6b78fa8 | ||
|
|
58db7712ea | ||
|
|
325ec9ae55 | ||
|
|
935a2a240d | ||
|
|
a0ca4f0a33 | ||
|
|
a0c47f8115 | ||
|
|
957776871d | ||
|
|
fe7d318f3d | ||
|
|
b8155835b6 | ||
|
|
74d9e63888 | ||
|
|
7c5601eed6 | ||
|
|
49acc1a88f | ||
|
|
f072b06b81 | ||
|
|
4be99a93c8 | ||
|
|
e761c9aec9 | ||
|
|
1032e8fb06 | ||
|
|
36d9cd1c76 | ||
|
|
281dcf464e | ||
|
|
90bed2da26 | ||
|
|
ba1f1a99ed | ||
|
|
cdd838cffe | ||
|
|
d1997c0f65 | ||
|
|
1cfc20365e | ||
|
|
ed8877ca6b | ||
|
|
de0dc2782f | ||
|
|
e33dd1527f | ||
|
|
9171ffc88a | ||
|
|
eb65468382 | ||
|
|
7c40172c40 | ||
|
|
ab48cccefc | ||
|
|
1b38ebe89e | ||
|
|
c235f05340 | ||
|
|
3a7219a94f | ||
|
|
accff8c8b6 | ||
|
|
9a9ac07ab2 | ||
|
|
2d6433ab4d | ||
|
|
937d93894a | ||
|
|
fc122027cb | ||
|
|
1e790b7a20 | ||
|
|
33dcef981a | ||
|
|
1c59fd7ed3 | ||
|
|
4d559d4094 | ||
|
|
96b852a0e7 | ||
|
|
aacf6b5d52 | ||
|
|
2573b5728c | ||
|
|
9eb2c04a26 | ||
|
|
71b19be030 | ||
|
|
73c5127088 | ||
|
|
a8831b2c9e | ||
|
|
d8a40611f8 | ||
|
|
762afcfc21 | ||
|
|
6b0e5ae392 | ||
|
|
660621bffb | ||
|
|
ba64441619 | ||
|
|
d63234f385 | ||
|
|
bd4223c721 | ||
|
|
5cbdbc7520 | ||
|
|
5c95b5ac98 | ||
|
|
ef1800c50a | ||
|
|
5c2a6d6f7c | ||
|
|
acdff604e4 | ||
|
|
0aff6c1561 | ||
|
|
6c556263c6 | ||
|
|
e639456a82 | ||
|
|
5dedf7bec3 | ||
|
|
cb8ef37c79 | ||
|
|
ae499d513a | ||
|
|
507c80afc7 | ||
|
|
3d02cacc4d | ||
|
|
f9e96922eb | ||
|
|
791239eabb | ||
|
|
4c72f6d1fb | ||
|
|
34e3310669 | ||
|
|
4bd45d1f5b | ||
|
|
55d02d4955 | ||
|
|
f5fd8c9fe5 | ||
|
|
ff39027869 | ||
|
|
3b1bae3775 | ||
|
|
ef0dde23d0 | ||
|
|
512537afe5 | ||
|
|
e85782da4d | ||
|
|
d86a10af32 | ||
|
|
ceab78e2d8 | ||
|
|
11afab61d7 | ||
|
|
801e691011 | ||
|
|
b3f7c648db | ||
|
|
3842950309 | ||
|
|
57ef6f02bc | ||
|
|
ffb916a48a | ||
|
|
3f78ee72b4 | ||
|
|
2d41ce0ae7 | ||
|
|
484a47e0fd | ||
|
|
09d67f1c05 | ||
|
|
167ca9a595 | ||
|
|
6d9df29a4e | ||
|
|
5ef7ca7723 | ||
|
|
bac0313d7c | ||
|
|
af64824733 | ||
|
|
ad490ce006 | ||
|
|
ff4de2c77b | ||
|
|
bc542aad12 | ||
|
|
1211077dc8 | ||
|
|
9de6a61ea5 | ||
|
|
cc1f059950 | ||
|
|
ef3a754033 | ||
|
|
e24bd88971 | ||
|
|
caf2193a0f | ||
|
|
cb27f36f66 | ||
|
|
6ec8e7ac3b | ||
|
|
dd566ee112 | ||
|
|
7f230f2999 | ||
|
|
48d8e2f453 | ||
|
|
865842e5b8 | ||
|
|
fda26f521f | ||
|
|
70e35f77bf | ||
|
|
0af348fb02 | ||
|
|
b67ae13f3c | ||
|
|
d9252e37b3 | ||
|
|
3b6e3d636d | ||
|
|
0443ca3185 | ||
|
|
01e2708233 | ||
|
|
9d3f09fafa | ||
|
|
22a208e505 | ||
|
|
cbc26cd135 | ||
|
|
8c5c7d13b8 | ||
|
|
7e042c9dab | ||
|
|
4a744bac5c | ||
|
|
89c22acb95 | ||
|
|
007ed35a66 | ||
|
|
2bb0e268c8 | ||
|
|
d6c7601a14 | ||
|
|
a79ec6c5ed | ||
|
|
4eea383f74 | ||
|
|
45746fbf34 | ||
|
|
7ac40d2d99 | ||
|
|
bebd157586 | ||
|
|
02a6fa7418 | ||
|
|
bc16421b64 | ||
|
|
bcc4cf8b57 | ||
|
|
d7418c7ad1 | ||
|
|
c7fb25e3f8 | ||
|
|
9e26af66bf | ||
|
|
b741ea79e5 | ||
|
|
7d2f5065be | ||
|
|
a9e7f94409 | ||
|
|
d3be13182d | ||
|
|
a4658be5b6 | ||
|
|
b3f252ee46 | ||
|
|
f98b5764ea | ||
|
|
9f8fcec1ad | ||
|
|
deb4b71e7b | ||
|
|
90a2b07e5f | ||
|
|
e69f909b61 | ||
|
|
0686575484 | ||
|
|
c7cadedd21 | ||
|
|
78d41878e8 | ||
|
|
a527ccd8ed | ||
|
|
5f5f745892 | ||
|
|
6160d67032 | ||
|
|
641341d75d | ||
|
|
146af217a0 | ||
|
|
4a0410b609 | ||
|
|
e858a42455 | ||
|
|
b037cb0e82 | ||
|
|
5455b436f6 | ||
|
|
786e13b0b7 | ||
|
|
759fb1bcbc | ||
|
|
227c962ec4 | ||
|
|
f03ffd13a0 | ||
|
|
00120a6b37 | ||
|
|
884121bdae | ||
|
|
48b92ce0b1 | ||
|
|
f8e1326837 | ||
|
|
00cf15935b | ||
|
|
172ee5a228 | ||
|
|
2f2a43e9c6 | ||
|
|
a9e32dfc99 | ||
|
|
5450b0fd0b | ||
|
|
e09581aa78 | ||
|
|
e08a8ff132 | ||
|
|
8ee0a0fad0 | ||
|
|
4b3ed4f43d | ||
|
|
6ed2e11730 | ||
|
|
e784dafefe | ||
|
|
6cdc6f7bdb | ||
|
|
60a7a7cf94 | ||
|
|
765c0ea17c | ||
|
|
de7de4cd8a | ||
|
|
19473ddba9 | ||
|
|
14421b533d | ||
|
|
21fc5d590f | ||
|
|
24651db512 | ||
|
|
27644bd032 | ||
|
|
0c2e7fae9f | ||
|
|
9d4824bb23 | ||
|
|
3b19b4212b | ||
|
|
69c2b3ffbb | ||
|
|
92ca6261b8 | ||
|
|
9e5e160845 | ||
|
|
6a66e920fe | ||
|
|
cab9f6ab4e | ||
|
|
0395f01c33 | ||
|
|
de88f17a1a | ||
|
|
9db8f9c924 | ||
|
|
7328a27524 | ||
|
|
8ee41e9608 | ||
|
|
0ed6403141 | ||
|
|
6980661a8e | ||
|
|
4797313f9e | ||
|
|
b17774da6e | ||
|
|
a4bba21610 | ||
|
|
7e1d522601 | ||
|
|
cd2225bbc8 | ||
|
|
62af22c928 | ||
|
|
2b531c5302 | ||
|
|
41ed0cee1d | ||
|
|
ce7da62a88 | ||
|
|
f93772ebc9 | ||
|
|
f63b052864 | ||
|
|
43dbdd049f | ||
|
|
6a489eaaf3 | ||
|
|
3a783a722a | ||
|
|
44481e1c2a | ||
|
|
e3d1970369 | ||
|
|
2c7708dab1 | ||
|
|
e156ff77f8 | ||
|
|
5f5f22ee8c | ||
|
|
011b60b3bf | ||
|
|
9bd8669cf4 | ||
|
|
4b5a47f127 | ||
|
|
db45e7907c | ||
|
|
03f400620e | ||
|
|
d938a528c3 | ||
|
|
2309749d15 | ||
|
|
b37b8eaeaf | ||
|
|
9c57243d5a | ||
|
|
6518cd464c | ||
|
|
7e32a16969 | ||
|
|
349c94ee23 | ||
|
|
167774e3b0 | ||
|
|
f2ba1bd4ab | ||
|
|
3ce8041a12 | ||
|
|
a4bbf8f233 | ||
|
|
1b813c4ea2 | ||
|
|
e6c2d85197 | ||
|
|
23ae20efe7 | ||
|
|
37b3f03809 | ||
|
|
43540b008c | ||
|
|
169ccbb47f | ||
|
|
0af35817a6 | ||
|
|
c942542079 | ||
|
|
8a6d8e0549 | ||
|
|
0c4dbebaeb | ||
|
|
5995ec299c | ||
|
|
e2c82fe3dc | ||
|
|
39f6c12d33 | ||
|
|
ae24d9ef03 | ||
|
|
5a1f5757c1 | ||
|
|
b194b88c49 | ||
|
|
b86fbb3cd8 | ||
|
|
9fca104059 | ||
|
|
9369a697e3 | ||
|
|
ebbafbe55d | ||
|
|
6bf9ba397e | ||
|
|
7b7af75802 | ||
|
|
723ce6c893 | ||
|
|
ed3be361b5 | ||
|
|
0b6990f828 | ||
|
|
2af94410f5 | ||
|
|
b348565449 | ||
|
|
aa06255e26 | ||
|
|
a0d967cdc7 | ||
|
|
494a72709d | ||
|
|
8947cebf04 | ||
|
|
b51993db0c | ||
|
|
5c8f80a907 | ||
|
|
9fab40d12a | ||
|
|
420a5fa782 | ||
|
|
fe335ea865 | ||
|
|
ad04a483e3 | ||
|
|
b3228f1e27 | ||
|
|
249935fc66 | ||
|
|
53c0ccfba8 | ||
|
|
4706adb0d5 | ||
|
|
53b6bf0986 | ||
|
|
65d9a75421 | ||
|
|
c089192904 | ||
|
|
a8af3310b4 | ||
|
|
58e6989307 | ||
|
|
18f7ea2a7e | ||
|
|
4e8219c6fa | ||
|
|
24f7ef69c7 | ||
|
|
1578c13bf5 | ||
|
|
ca2aa5d7f7 | ||
|
|
4d43f83443 | ||
|
|
319869bc6d | ||
|
|
405fb52cb2 | ||
|
|
76a6fad4e2 | ||
|
|
47be49253b | ||
|
|
5177c793a7 | ||
|
|
bb06ac0c42 | ||
|
|
20668bef9e | ||
|
|
747f8d4567 | ||
|
|
501927c844 | ||
|
|
d03ae91d11 | ||
|
|
cc2f45f764 | ||
|
|
713dcd5d08 | ||
|
|
8b69c90bf1 | ||
|
|
5513122ca9 | ||
|
|
7507ac31f7 | ||
|
|
49e51675d3 | ||
|
|
056d54d313 | ||
|
|
00eac849fe | ||
|
|
75a17dae2c | ||
|
|
62c81ac785 | ||
|
|
15c5170195 | ||
|
|
4b302c0330 | ||
|
|
7977fa40e7 | ||
|
|
45c4c507bc | ||
|
|
33f2e03e13 | ||
|
|
56391021a4 | ||
|
|
ab831c2604 | ||
|
|
11fc4d88aa | ||
|
|
57dd1982a0 | ||
|
|
3f754dc662 | ||
|
|
0c5b1a88a6 | ||
|
|
1d5fdef4b4 | ||
|
|
815b8597d1 | ||
|
|
53e3a7ae7f | ||
|
|
04974d37d6 | ||
|
|
8c1e53a8df | ||
|
|
8399b00a10 | ||
|
|
9244adee30 | ||
|
|
3265a9de7b | ||
|
|
ec602f3e15 | ||
|
|
c9bb49e0f7 |
@@ -3,7 +3,10 @@ root = true
|
|||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
charset = utf-8
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
indent_size = tab
|
||||||
|
indent_style = tab
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
[*.{js,vue}]
|
[*.{js,vue}]
|
||||||
indent_style = tab
|
indent_style = tab
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
root: true,
|
||||||
extends: [
|
extends: [
|
||||||
'@nextcloud',
|
'@nextcloud',
|
||||||
],
|
],
|
||||||
@@ -7,6 +8,7 @@ module.exports = {
|
|||||||
'jsdoc/require-param-type': ['off'],
|
'jsdoc/require-param-type': ['off'],
|
||||||
'jsdoc/check-param-names': ['off'],
|
'jsdoc/check-param-names': ['off'],
|
||||||
'jsdoc/no-undefined-types': ['off'],
|
'jsdoc/no-undefined-types': ['off'],
|
||||||
'jsdoc/require-property-description' : ['off']
|
'jsdoc/require-property-description': ['off'],
|
||||||
|
'import/no-named-as-default-member': ['off']
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
8
.github/workflows/appbuild.yml
vendored
@@ -1,7 +1,11 @@
|
|||||||
name: Package build
|
name: Package build
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- master
|
||||||
|
- stable*
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
@@ -20,7 +24,7 @@ jobs:
|
|||||||
- name: Set up npm7
|
- name: Set up npm7
|
||||||
run: npm i -g npm@7
|
run: npm i -g npm@7
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@2.18.0
|
uses: shivammathur/setup-php@2.21.2
|
||||||
with:
|
with:
|
||||||
php-version: '7.4'
|
php-version: '7.4'
|
||||||
tools: composer
|
tools: composer
|
||||||
|
|||||||
2
.github/workflows/appstore-build-publish.yml
vendored
@@ -66,7 +66,7 @@ jobs:
|
|||||||
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
|
run: npm i -g npm@"${{ steps.versions.outputs.npmVersion }}"
|
||||||
|
|
||||||
- name: Set up php ${{ env.PHP_VERSION }}
|
- name: Set up php ${{ env.PHP_VERSION }}
|
||||||
uses: shivammathur/setup-php@2.18.0
|
uses: shivammathur/setup-php@2.21.2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ env.PHP_VERSION }}
|
php-version: ${{ env.PHP_VERSION }}
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|||||||
7
.github/workflows/command-rebase.yml
vendored
@@ -9,9 +9,14 @@ on:
|
|||||||
issue_comment:
|
issue_comment:
|
||||||
types: created
|
types: created
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
rebase:
|
rebase:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: none
|
||||||
|
|
||||||
# On pull requests and if the comment starts with `/rebase`
|
# On pull requests and if the comment starts with `/rebase`
|
||||||
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase')
|
if: github.event.issue.pull_request != '' && startsWith(github.event.comment.body, '/rebase')
|
||||||
@@ -32,7 +37,7 @@ jobs:
|
|||||||
token: ${{ secrets.COMMAND_BOT_PAT }}
|
token: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
|
||||||
- name: Automatic Rebase
|
- name: Automatic Rebase
|
||||||
uses: cirrus-actions/rebase@1.5
|
uses: cirrus-actions/rebase@1.7
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
|
GITHUB_TOKEN: ${{ secrets.COMMAND_BOT_PAT }}
|
||||||
|
|
||||||
|
|||||||
112
.github/workflows/cypress.yml
vendored
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
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: [ 'stable25' ]
|
||||||
|
|
||||||
|
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@v2
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: Upload screenshots
|
||||||
|
path: apps/${{ env.APP_NAME }}/cypress/screenshots/
|
||||||
|
retention-days: 5
|
||||||
|
|
||||||
|
- name: Upload nextcloud logs
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
if: failure()
|
||||||
|
with:
|
||||||
|
name: Upload nextcloud log
|
||||||
|
path: data/nextcloud.log
|
||||||
|
retention-days: 5
|
||||||
@@ -8,13 +8,20 @@ name: Dependabot
|
|||||||
on:
|
on:
|
||||||
pull_request_target:
|
pull_request_target:
|
||||||
branches:
|
branches:
|
||||||
|
- main
|
||||||
- master
|
- master
|
||||||
- stable*
|
- stable*
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
auto-approve-merge:
|
auto-approve-merge:
|
||||||
if: github.actor == 'dependabot[bot]'
|
if: github.actor == 'dependabot[bot]'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
# for hmarr/auto-approve-action to approve PRs
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
# Github actions bot approve
|
# Github actions bot approve
|
||||||
|
|||||||
14
.github/workflows/integration.yml
vendored
@@ -2,6 +2,14 @@ name: Integration tests
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/integration.yml'
|
||||||
|
- 'appinfo/**'
|
||||||
|
- 'lib/**'
|
||||||
|
- 'templates/**'
|
||||||
|
- 'tests/**'
|
||||||
|
- 'composer.json'
|
||||||
|
- 'composer.lock'
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@@ -19,7 +27,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
php-versions: ['7.4']
|
php-versions: ['7.4']
|
||||||
databases: ['sqlite', 'mysql', 'pgsql']
|
databases: ['sqlite', 'mysql', 'pgsql']
|
||||||
server-versions: ['stable24']
|
server-versions: ['stable25']
|
||||||
|
|
||||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||||
|
|
||||||
@@ -54,7 +62,7 @@ jobs:
|
|||||||
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
auth_header="$(git config --local --get http.https://github.com/.extraheader)"
|
||||||
git submodule sync --recursive
|
git submodule sync --recursive
|
||||||
git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
|
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:~8
|
cd build/integration && composer require --dev phpunit/phpunit:~9
|
||||||
|
|
||||||
- name: Checkout app
|
- name: Checkout app
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
@@ -62,7 +70,7 @@ jobs:
|
|||||||
path: apps/${{ env.APP_NAME }}
|
path: apps/${{ env.APP_NAME }}
|
||||||
|
|
||||||
- name: Set up php ${{ matrix.php-versions }}
|
- name: Set up php ${{ matrix.php-versions }}
|
||||||
uses: shivammathur/setup-php@2.18.0
|
uses: shivammathur/setup-php@2.21.2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
tools: phpunit
|
tools: phpunit
|
||||||
|
|||||||
4
.github/workflows/lint.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
- name: Set up php${{ matrix.php-versions }}
|
- name: Set up php${{ matrix.php-versions }}
|
||||||
uses: shivammathur/setup-php@2.18.0
|
uses: shivammathur/setup-php@2.21.2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
coverage: none
|
coverage: none
|
||||||
@@ -33,7 +33,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
- name: Set up php
|
- name: Set up php
|
||||||
uses: shivammathur/setup-php@2.18.0
|
uses: shivammathur/setup-php@2.21.2
|
||||||
with:
|
with:
|
||||||
php-version: 7.4
|
php-version: 7.4
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|||||||
2
.github/workflows/nightly.yml
vendored
@@ -25,7 +25,7 @@ jobs:
|
|||||||
- name: Set up npm7
|
- name: Set up npm7
|
||||||
run: npm i -g npm@7
|
run: npm i -g npm@7
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
uses: shivammathur/setup-php@2.18.0
|
uses: shivammathur/setup-php@2.21.2
|
||||||
with:
|
with:
|
||||||
php-version: '7.4'
|
php-version: '7.4'
|
||||||
tools: composer
|
tools: composer
|
||||||
|
|||||||
12
.github/workflows/phpunit.yml
vendored
@@ -2,6 +2,14 @@ name: PHPUnit
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- '.github/workflows/phpunit.yml'
|
||||||
|
- 'appinfo/**'
|
||||||
|
- 'lib/**'
|
||||||
|
- 'templates/**'
|
||||||
|
- 'tests/**'
|
||||||
|
- 'composer.json'
|
||||||
|
- 'composer.lock'
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
@@ -20,7 +28,7 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
php-versions: ['7.4', '8.0', '8.1']
|
php-versions: ['7.4', '8.0', '8.1']
|
||||||
databases: ['sqlite', 'mysql', 'pgsql']
|
databases: ['sqlite', 'mysql', 'pgsql']
|
||||||
server-versions: ['stable24']
|
server-versions: ['stable25']
|
||||||
|
|
||||||
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
name: php${{ matrix.php-versions }}-${{ matrix.databases }}-${{ matrix.server-versions }}
|
||||||
|
|
||||||
@@ -62,7 +70,7 @@ jobs:
|
|||||||
path: apps/${{ env.APP_NAME }}
|
path: apps/${{ env.APP_NAME }}
|
||||||
|
|
||||||
- name: Set up php ${{ matrix.php-versions }}
|
- name: Set up php ${{ matrix.php-versions }}
|
||||||
uses: shivammathur/setup-php@2.18.0
|
uses: shivammathur/setup-php@2.21.2
|
||||||
with:
|
with:
|
||||||
php-version: ${{ matrix.php-versions }}
|
php-version: ${{ matrix.php-versions }}
|
||||||
tools: phpunit
|
tools: phpunit
|
||||||
|
|||||||
129
CHANGELOG.md
@@ -1,51 +1,106 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## 1.7.4
|
## 1.8.5
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Gracefully handle not found card for a share [#4569](https://github.com/nextcloud/deck/pull/4569)
|
- fix: Properly overwrite z-index of datepicker above modal [#4667](https://github.com/nextcloud/deck/pull/4667)
|
||||||
- fix: Use passed userid when getting attachment folder [#4541](https://github.com/nextcloud/deck/pull/4541)
|
|
||||||
- fix: Append datetime picker to body to avoid cut off [#4646](https://github.com/nextcloud/deck/pull/4646)
|
|
||||||
- Permanently delete deck cards marked as deleted after 5 min in a cron job [#4302](https://github.com/nextcloud/deck/pull/4302)
|
|
||||||
- Fix : Overlapping expiry dates on tags [#4538](https://github.com/nextcloud/deck/pull/4538)
|
|
||||||
- Update dependencies
|
|
||||||
|
|
||||||
## 1.7.3
|
|
||||||
|
## 1.8.4
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- feat: add validators to check values in services @juliushaertl [#4176](https://github.com/nextcloud/deck/pull/4176)
|
- fix: Use passed userid when getting attachment folder [#4540](https://github.com/nextcloud/deck/pull/4540)
|
||||||
- Add integration test for attachment handling on cards [#4178](https://github.com/nextcloud/deck/pull/4178)
|
- fix: Adapt NcEmptyContent usages to new slots [#4563](https://github.com/nextcloud/deck/pull/4563)
|
||||||
- disables autocomplete on card creation @juliushaertl [#4182](https://github.com/nextcloud/deck/pull/4182)
|
- Gracefully handle not found card for a share [#4568](https://github.com/nextcloud/deck/pull/4568)
|
||||||
- minor style fixes [#4202](https://github.com/nextcloud/deck/pull/4202)
|
- allow user to toggle visibility of the calendar for a deck board [#4626](https://github.com/nextcloud/deck/pull/4626)
|
||||||
|
- fix: Append datetime picker to body to avoid cut off [#4645](https://github.com/nextcloud/deck/pull/4645)
|
||||||
|
- Fix : Overlapping expiry dates on tags [#4536](https://github.com/nextcloud/deck/pull/4536)
|
||||||
|
- Better display of card dates (creation and change dates) [#4620](https://github.com/nextcloud/deck/pull/4620)
|
||||||
|
- Dependency updates
|
||||||
|
|
||||||
|
## 1.8.3
|
||||||
## 1.7.2
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Cache user membership for circles [#4132](https://github.com/nextcloud/deck/pull/4132)
|
- Fix component renaming so that acl works on shares again [#4328](https://github.com/nextcloud/deck/pull/4328)
|
||||||
- Set event link also for notifications that get emitted from activities [#4118](https://github.com/nextcloud/deck/pull/4118)
|
- Permanently delete deck cards marked as deleted after 5 min in a cron job [#4301](https://github.com/nextcloud/deck/pull/4301)
|
||||||
- Fix Card menu not displaying when description is not set [#4103](https://github.com/nextcloud/deck/pull/4103)
|
- Dependency updates
|
||||||
- disable Create card button while no stack is chosen [#4019](https://github.com/nextcloud/deck/pull/4019)
|
|
||||||
- to nextcloud/OCP package in stable24 [#4093](https://github.com/nextcloud/deck/pull/4093)
|
|
||||||
- Fix attachment creator name: show display name [#4037](https://github.com/nextcloud/deck/pull/4037)
|
|
||||||
- Use capped memory cache for board permissions [#3997](https://github.com/nextcloud/deck/pull/3997)
|
|
||||||
- Improve CalDAV integration performance [#3995](https://github.com/nextcloud/deck/pull/3995)
|
|
||||||
- Fetch attachment folder for the correct user during cron job [#3959](https://github.com/nextcloud/deck/pull/3959)
|
|
||||||
- Switch to 'markdown-it-task-checkbox' for rendering of task lists [#3925](https://github.com/nextcloud/deck/pull/3925)
|
|
||||||
- Prevent opening card and applyLabelFilter on card drag end [#3917](https://github.com/nextcloud/deck/pull/3917)
|
|
||||||
- Fix for issue #3637 [#3901](https://github.com/nextcloud/deck/pull/3901)
|
|
||||||
- Fix z-index for deck sidebar [#3885](https://github.com/nextcloud/deck/pull/3885)
|
|
||||||
|
|
||||||
## 1.7.1
|
|
||||||
|
## 1.8.2
|
||||||
|
|
||||||
### Fixed
|
### 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)
|
- minor style fixes [#4201](https://github.com/nextcloud/deck/pull/4201)
|
||||||
- Fetch full board data after cloning [#3781](https://github.com/nextcloud/deck/pull/3781)
|
- feat: add validators to check values in services [#4174](https://github.com/nextcloud/deck/pull/4174)
|
||||||
|
- Add integration test for attachment handling on cards [#4179](https://github.com/nextcloud/deck/pull/4179)
|
||||||
|
- Add missing userId property [#4198](https://github.com/nextcloud/deck/pull/4198)
|
||||||
|
|
||||||
|
## 1.8.1
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix Duedate activity @nickvergessen [#4155](https://github.com/nextcloud/deck/pull/4155)
|
||||||
|
|
||||||
|
## 1.8.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- 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)
|
||||||
|
- Implement card reference widget @eneiluj [#4031](https://github.com/nextcloud/deck/pull/4031)
|
||||||
|
- Implement new dashboard widget interfaces @eneiluj [#4033](https://github.com/nextcloud/deck/pull/4033)
|
||||||
|
- Add related resources panel to board sharing tab sidebar @Pytal [#4000](https://github.com/nextcloud/deck/pull/4000)
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix sorting stacks [#4116](https://github.com/nextcloud/deck/pull/4116)
|
||||||
|
- Fix issue with duedate format [#4140](https://github.com/nextcloud/deck/pull/4140)
|
||||||
|
- Fix missing icon for activity rendering [#4090](https://github.com/nextcloud/deck/pull/4090)
|
||||||
|
- disables autocomplete on card creation [#4142](https://github.com/nextcloud/deck/pull/4142)
|
||||||
|
- Set event link also for notifications that get emitted from activities [#4117](https://github.com/nextcloud/deck/pull/4117)
|
||||||
|
- Fix attachment creator name: show display name @eneiluj [#4036](https://github.com/nextcloud/deck/pull/4036)
|
||||||
|
- Fix reference provider when caching @eneiluj [#4056](https://github.com/nextcloud/deck/pull/4056)
|
||||||
|
- Use global import for nextcloud-vue [#4072](https://github.com/nextcloud/deck/pull/4072)
|
||||||
|
- Disable Create card button while no stack is chosen @icewind1991 [#4014](https://github.com/nextcloud/deck/pull/4014)
|
||||||
|
- Adjust testing matrix for Nextcloud 25 on stable25 @nickvergessen [#4068](https://github.com/nextcloud/deck/pull/4068)
|
||||||
|
- Fix Card menu not displaying when description is not set @marcelklehr [#4105](https://github.com/nextcloud/deck/pull/4105)
|
||||||
|
- Reference widget adjustments for Text [#4075](https://github.com/nextcloud/deck/pull/4075)
|
||||||
|
- use OCP\Collaboration\Reference\Reference [#4078](https://github.com/nextcloud/deck/pull/4078)
|
||||||
|
- Cache user membership for circles [#4141](https://github.com/nextcloud/deck/pull/4141)
|
||||||
|
- 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)
|
||||||
|
|
||||||
|
### 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)
|
||||||
|
|
||||||
## 1.7.0
|
## 1.7.0
|
||||||
|
|
||||||
@@ -97,7 +152,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Adapt the card modal to upstream changes [#3764](https://github.com/nextcloud/deck/pull/3764)
|
- 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)
|
- 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)
|
- Add missing indices [#3754](https://github.com/nextcloud/deck/pull/3754)
|
||||||
- Handle qb mapper exception messages properly @juliushaertl [#3769](https://github.com/nextcloud/deck/pull/3769)
|
|
||||||
|
|
||||||
## 1.6.0-beta1
|
## 1.6.0-beta1
|
||||||
|
|
||||||
@@ -468,7 +523,7 @@ Android app team for helping to improve our REST API:
|
|||||||
- Fix comment activities on Nextcloud 15
|
- Fix comment activities on Nextcloud 15
|
||||||
- Fix issues with Edge
|
- Fix issues with Edge
|
||||||
- API: Fix numeric types that were returned as strings
|
- API: Fix numeric types that were returned as strings
|
||||||
- API: Fix If-Modified-Since header parsing
|
- API: Fix If-Modified-Since header parsing
|
||||||
|
|
||||||
|
|
||||||
## 0.5.1 - 2018-12-05
|
## 0.5.1 - 2018-12-05
|
||||||
@@ -595,7 +650,7 @@ Android app team for helping to improve our REST API:
|
|||||||
### Fixed
|
### Fixed
|
||||||
- Various frontend fixes
|
- Various frontend fixes
|
||||||
- Fix sidebar drag issues
|
- Fix sidebar drag issues
|
||||||
- Improvements for IE11
|
- Improvements for IE11
|
||||||
- Fix bug when draging a card to an empty stack
|
- Fix bug when draging a card to an empty stack
|
||||||
|
|
||||||
## 0.2.1 - 2017-07-04
|
## 0.2.1 - 2017-07-04
|
||||||
@@ -669,7 +724,7 @@ Android app team for helping to improve our REST API:
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Various styling improvements
|
- Various styling improvements
|
||||||
- Fix problems with MySQL and PostgreSQL
|
- Fix problems with MySQL and PostgreSQL
|
||||||
- Select first color by default when creating boards
|
- Select first color by default when creating boards
|
||||||
- Fix error when changing board permissions
|
- Fix error when changing board permissions
|
||||||
|
|
||||||
@@ -677,9 +732,9 @@ Android app team for helping to improve our REST API:
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
- Sharing boards with other users
|
- Sharing boards with other users
|
||||||
- Create and manage boards
|
- Create and manage boards
|
||||||
- Sort cards on stacks by drag-and-drop
|
- Sort cards on stacks by drag-and-drop
|
||||||
- Assign labels
|
- Assign labels
|
||||||
- Markdown notes for each card
|
- Markdown notes for each card
|
||||||
- Archive cards
|
- Archive cards
|
||||||
|
|
||||||
|
|||||||
17
README.md
@@ -24,9 +24,9 @@ Deck is a kanban style organization tool aimed at personal planning and project
|
|||||||
### 3rd-Party Integrations
|
### 3rd-Party Integrations
|
||||||
|
|
||||||
- [trello-to-deck](https://github.com/maxammann/trello-to-deck) - Migrates cards from Trello
|
- [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
|
- [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
|
- [A-deck](https://github.com/leoossa/A-deck) - Chrome Extension that allows to create new card in selected stack based on current tab
|
||||||
|
|
||||||
## Installation/Update
|
## Installation/Update
|
||||||
|
|
||||||
This app is supposed to work on the two latest Nextcloud versions.
|
This app is supposed to work on the two latest Nextcloud versions.
|
||||||
@@ -52,6 +52,17 @@ 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.
|
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
|
## Developing
|
||||||
|
|
||||||
### PHP
|
### PHP
|
||||||
@@ -60,6 +71,8 @@ Nothing to prepare, just dig into the code.
|
|||||||
|
|
||||||
### JavaScript
|
### JavaScript
|
||||||
|
|
||||||
|
This requires at least Node 14 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.
|
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
|
#### Hot reloading
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
- 🚀 Get your project organized
|
- 🚀 Get your project organized
|
||||||
|
|
||||||
</description>
|
</description>
|
||||||
<version>1.7.4</version>
|
<version>1.8.5</version>
|
||||||
<licence>agpl</licence>
|
<licence>agpl</licence>
|
||||||
<author>Julius Härtl</author>
|
<author>Julius Härtl</author>
|
||||||
<namespace>Deck</namespace>
|
<namespace>Deck</namespace>
|
||||||
@@ -34,7 +34,7 @@
|
|||||||
<database min-version="9.4">pgsql</database>
|
<database min-version="9.4">pgsql</database>
|
||||||
<database>sqlite</database>
|
<database>sqlite</database>
|
||||||
<database min-version="8.0">mysql</database>
|
<database min-version="8.0">mysql</database>
|
||||||
<nextcloud min-version="24" max-version="24"/>
|
<nextcloud min-version="25" max-version="25"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<background-jobs>
|
<background-jobs>
|
||||||
<job>OCA\Deck\Cron\DeleteCron</job>
|
<job>OCA\Deck\Cron\DeleteCron</job>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"symfony/event-dispatcher": "^4.0",
|
"symfony/event-dispatcher": "^4.0",
|
||||||
"vimeo/psalm": "^4.3",
|
"vimeo/psalm": "^4.3",
|
||||||
"php-parallel-lint/php-parallel-lint": "^1.2",
|
"php-parallel-lint/php-parallel-lint": "^1.2",
|
||||||
"nextcloud/ocp": "dev-stable24"
|
"nextcloud/ocp": "dev-stable25"
|
||||||
},
|
},
|
||||||
"config": {
|
"config": {
|
||||||
"optimize-autoloader": true,
|
"optimize-autoloader": true,
|
||||||
@@ -36,6 +36,7 @@
|
|||||||
"cs:check": "php-cs-fixer fix --dry-run --diff",
|
"cs:check": "php-cs-fixer fix --dry-run --diff",
|
||||||
"cs:fix": "php-cs-fixer fix",
|
"cs:fix": "php-cs-fixer fix",
|
||||||
"psalm": "psalm",
|
"psalm": "psalm",
|
||||||
|
"psalm:update-baseline": "psalm --update-baseline",
|
||||||
"psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType",
|
"psalm:fix": "psalm --alter --issues=InvalidReturnType,InvalidNullableReturnType,MismatchingDocblockParamType,MismatchingDocblockReturnType,MissingParamType,InvalidFalsableReturnType",
|
||||||
"test": [
|
"test": [
|
||||||
"@test:unit",
|
"@test:unit",
|
||||||
|
|||||||
365
composer.lock
generated
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "fdd039ec52f9c829403494423b527e10",
|
"content-hash": "445858d371d9a1c7057d0603c566966a",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "cogpowered/finediff",
|
"name": "cogpowered/finediff",
|
||||||
@@ -63,16 +63,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "justinrainbow/json-schema",
|
"name": "justinrainbow/json-schema",
|
||||||
"version": "5.2.11",
|
"version": "5.2.12",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/justinrainbow/json-schema.git",
|
"url": "https://github.com/justinrainbow/json-schema.git",
|
||||||
"reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa"
|
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/2ab6744b7296ded80f8cc4f9509abbff393399aa",
|
"url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||||
"reference": "2ab6744b7296ded80f8cc4f9509abbff393399aa",
|
"reference": "ad87d5a5ca981228e0e205c2bc7dfb8e24559b60",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -127,9 +127,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/justinrainbow/json-schema/issues",
|
"issues": "https://github.com/justinrainbow/json-schema/issues",
|
||||||
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.11"
|
"source": "https://github.com/justinrainbow/json-schema/tree/5.2.12"
|
||||||
},
|
},
|
||||||
"time": "2021-07-22T09:24:00+00:00"
|
"time": "2022-04-13T08:02:27+00:00"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"packages-dev": [
|
"packages-dev": [
|
||||||
@@ -445,16 +445,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/semver",
|
"name": "composer/semver",
|
||||||
"version": "3.2.9",
|
"version": "3.3.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/composer/semver.git",
|
"url": "https://github.com/composer/semver.git",
|
||||||
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649"
|
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/composer/semver/zipball/a951f614bd64dcd26137bc9b7b2637ddcfc57649",
|
"url": "https://api.github.com/repos/composer/semver/zipball/3953f23262f2bff1919fc82183ad9acb13ff62c9",
|
||||||
"reference": "a951f614bd64dcd26137bc9b7b2637ddcfc57649",
|
"reference": "3953f23262f2bff1919fc82183ad9acb13ff62c9",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -506,7 +506,7 @@
|
|||||||
"support": {
|
"support": {
|
||||||
"irc": "irc://irc.freenode.org/composer",
|
"irc": "irc://irc.freenode.org/composer",
|
||||||
"issues": "https://github.com/composer/semver/issues",
|
"issues": "https://github.com/composer/semver/issues",
|
||||||
"source": "https://github.com/composer/semver/tree/3.2.9"
|
"source": "https://github.com/composer/semver/tree/3.3.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -522,7 +522,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-02-04T13:58:43+00:00"
|
"time": "2022-04-01T19:23:25+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "composer/xdebug-handler",
|
"name": "composer/xdebug-handler",
|
||||||
@@ -896,16 +896,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "felixfbecker/language-server-protocol",
|
"name": "felixfbecker/language-server-protocol",
|
||||||
"version": "1.5.1",
|
"version": "v1.5.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/felixfbecker/php-language-server-protocol.git",
|
"url": "https://github.com/felixfbecker/php-language-server-protocol.git",
|
||||||
"reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730"
|
"reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
|
"url": "https://api.github.com/repos/felixfbecker/php-language-server-protocol/zipball/6e82196ffd7c62f7794d778ca52b69feec9f2842",
|
||||||
"reference": "9d846d1f5cf101deee7a61c8ba7caa0a975cd730",
|
"reference": "6e82196ffd7c62f7794d778ca52b69feec9f2842",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -946,9 +946,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
|
"issues": "https://github.com/felixfbecker/php-language-server-protocol/issues",
|
||||||
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/1.5.1"
|
"source": "https://github.com/felixfbecker/php-language-server-protocol/tree/v1.5.2"
|
||||||
},
|
},
|
||||||
"time": "2021-02-22T14:02:09+00:00"
|
"time": "2022-03-02T22:36:06+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "friendsofphp/php-cs-fixer",
|
"name": "friendsofphp/php-cs-fixer",
|
||||||
@@ -1192,16 +1192,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nextcloud/ocp",
|
"name": "nextcloud/ocp",
|
||||||
"version": "dev-stable24",
|
"version": "dev-stable25",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nextcloud-deps/ocp.git",
|
"url": "https://github.com/nextcloud-deps/ocp.git",
|
||||||
"reference": "0b89697ba1146d48506132c452cf548adb2a9cb8"
|
"reference": "1e34a80be034fe9a58057d2e756913363675bddb"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/0b89697ba1146d48506132c452cf548adb2a9cb8",
|
"url": "https://api.github.com/repos/nextcloud-deps/ocp/zipball/1e34a80be034fe9a58057d2e756913363675bddb",
|
||||||
"reference": "0b89697ba1146d48506132c452cf548adb2a9cb8",
|
"reference": "1e34a80be034fe9a58057d2e756913363675bddb",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1213,7 +1213,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "24.0.0-dev"
|
"dev-master": "26.0.0-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
@@ -1229,22 +1229,22 @@
|
|||||||
"description": "Composer package containing Nextcloud's public API (classes, interfaces)",
|
"description": "Composer package containing Nextcloud's public API (classes, interfaces)",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nextcloud-deps/ocp/issues",
|
"issues": "https://github.com/nextcloud-deps/ocp/issues",
|
||||||
"source": "https://github.com/nextcloud-deps/ocp/tree/stable24"
|
"source": "https://github.com/nextcloud-deps/ocp/tree/stable25"
|
||||||
},
|
},
|
||||||
"time": "2023-03-16T00:40:04+00:00"
|
"time": "2023-05-13T00:33:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nikic/php-parser",
|
"name": "nikic/php-parser",
|
||||||
"version": "v4.13.2",
|
"version": "v4.14.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nikic/PHP-Parser.git",
|
"url": "https://github.com/nikic/PHP-Parser.git",
|
||||||
"reference": "210577fe3cf7badcc5814d99455df46564f3c077"
|
"reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/210577fe3cf7badcc5814d99455df46564f3c077",
|
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/34bea19b6e03d8153165d8f30bba4c3be86184c1",
|
||||||
"reference": "210577fe3cf7badcc5814d99455df46564f3c077",
|
"reference": "34bea19b6e03d8153165d8f30bba4c3be86184c1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1285,9 +1285,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
"issues": "https://github.com/nikic/PHP-Parser/issues",
|
||||||
"source": "https://github.com/nikic/PHP-Parser/tree/v4.13.2"
|
"source": "https://github.com/nikic/PHP-Parser/tree/v4.14.0"
|
||||||
},
|
},
|
||||||
"time": "2021-11-30T19:35:32+00:00"
|
"time": "2022-05-31T20:59:12+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "openlss/lib-array2xml",
|
"name": "openlss/lib-array2xml",
|
||||||
@@ -1722,92 +1722,25 @@
|
|||||||
},
|
},
|
||||||
"time": "2022-03-15T21:29:03+00:00"
|
"time": "2022-03-15T21:29:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "phpspec/prophecy",
|
|
||||||
"version": "v1.15.0",
|
|
||||||
"source": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/phpspec/prophecy.git",
|
|
||||||
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13"
|
|
||||||
},
|
|
||||||
"dist": {
|
|
||||||
"type": "zip",
|
|
||||||
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
|
|
||||||
"reference": "bbcd7380b0ebf3961ee21409db7b38bc31d69a13",
|
|
||||||
"shasum": ""
|
|
||||||
},
|
|
||||||
"require": {
|
|
||||||
"doctrine/instantiator": "^1.2",
|
|
||||||
"php": "^7.2 || ~8.0, <8.2",
|
|
||||||
"phpdocumentor/reflection-docblock": "^5.2",
|
|
||||||
"sebastian/comparator": "^3.0 || ^4.0",
|
|
||||||
"sebastian/recursion-context": "^3.0 || ^4.0"
|
|
||||||
},
|
|
||||||
"require-dev": {
|
|
||||||
"phpspec/phpspec": "^6.0 || ^7.0",
|
|
||||||
"phpunit/phpunit": "^8.0 || ^9.0"
|
|
||||||
},
|
|
||||||
"type": "library",
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-master": "1.x-dev"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"autoload": {
|
|
||||||
"psr-4": {
|
|
||||||
"Prophecy\\": "src/Prophecy"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"notification-url": "https://packagist.org/downloads/",
|
|
||||||
"license": [
|
|
||||||
"MIT"
|
|
||||||
],
|
|
||||||
"authors": [
|
|
||||||
{
|
|
||||||
"name": "Konstantin Kudryashov",
|
|
||||||
"email": "ever.zet@gmail.com",
|
|
||||||
"homepage": "http://everzet.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Marcello Duarte",
|
|
||||||
"email": "marcello.duarte@gmail.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"description": "Highly opinionated mocking framework for PHP 5.3+",
|
|
||||||
"homepage": "https://github.com/phpspec/prophecy",
|
|
||||||
"keywords": [
|
|
||||||
"Double",
|
|
||||||
"Dummy",
|
|
||||||
"fake",
|
|
||||||
"mock",
|
|
||||||
"spy",
|
|
||||||
"stub"
|
|
||||||
],
|
|
||||||
"support": {
|
|
||||||
"issues": "https://github.com/phpspec/prophecy/issues",
|
|
||||||
"source": "https://github.com/phpspec/prophecy/tree/v1.15.0"
|
|
||||||
},
|
|
||||||
"time": "2021-12-08T12:19:24+00:00"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-code-coverage",
|
"name": "phpunit/php-code-coverage",
|
||||||
"version": "9.2.15",
|
"version": "9.2.17",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
|
||||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f"
|
"reference": "aa94dc41e8661fe90c7316849907cba3007b10d8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/aa94dc41e8661fe90c7316849907cba3007b10d8",
|
||||||
"reference": "2e9da11878c4202f97915c1cb4bb1ca318a63f5f",
|
"reference": "aa94dc41e8661fe90c7316849907cba3007b10d8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"ext-dom": "*",
|
"ext-dom": "*",
|
||||||
"ext-libxml": "*",
|
"ext-libxml": "*",
|
||||||
"ext-xmlwriter": "*",
|
"ext-xmlwriter": "*",
|
||||||
"nikic/php-parser": "^4.13.0",
|
"nikic/php-parser": "^4.14",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
"phpunit/php-file-iterator": "^3.0.3",
|
"phpunit/php-file-iterator": "^3.0.3",
|
||||||
"phpunit/php-text-template": "^2.0.2",
|
"phpunit/php-text-template": "^2.0.2",
|
||||||
@@ -1856,7 +1789,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.15"
|
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.17"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -1864,7 +1797,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-03-07T09:28:20+00:00"
|
"time": "2022-08-30T12:24:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/php-file-iterator",
|
"name": "phpunit/php-file-iterator",
|
||||||
@@ -2109,16 +2042,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phpunit/phpunit",
|
"name": "phpunit/phpunit",
|
||||||
"version": "9.5.20",
|
"version": "9.5.24",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
"url": "https://github.com/sebastianbergmann/phpunit.git",
|
||||||
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba"
|
"reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/12bc8879fb65aef2138b26fc633cb1e3620cffba",
|
"url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/d0aa6097bef9fd42458a9b3c49da32c6ce6129c5",
|
||||||
"reference": "12bc8879fb65aef2138b26fc633cb1e3620cffba",
|
"reference": "d0aa6097bef9fd42458a9b3c49da32c6ce6129c5",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -2133,7 +2066,6 @@
|
|||||||
"phar-io/manifest": "^2.0.3",
|
"phar-io/manifest": "^2.0.3",
|
||||||
"phar-io/version": "^3.0.2",
|
"phar-io/version": "^3.0.2",
|
||||||
"php": ">=7.3",
|
"php": ">=7.3",
|
||||||
"phpspec/prophecy": "^1.12.1",
|
|
||||||
"phpunit/php-code-coverage": "^9.2.13",
|
"phpunit/php-code-coverage": "^9.2.13",
|
||||||
"phpunit/php-file-iterator": "^3.0.5",
|
"phpunit/php-file-iterator": "^3.0.5",
|
||||||
"phpunit/php-invoker": "^3.1.1",
|
"phpunit/php-invoker": "^3.1.1",
|
||||||
@@ -2148,13 +2080,9 @@
|
|||||||
"sebastian/global-state": "^5.0.1",
|
"sebastian/global-state": "^5.0.1",
|
||||||
"sebastian/object-enumerator": "^4.0.3",
|
"sebastian/object-enumerator": "^4.0.3",
|
||||||
"sebastian/resource-operations": "^3.0.3",
|
"sebastian/resource-operations": "^3.0.3",
|
||||||
"sebastian/type": "^3.0",
|
"sebastian/type": "^3.1",
|
||||||
"sebastian/version": "^3.0.2"
|
"sebastian/version": "^3.0.2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
|
||||||
"ext-pdo": "*",
|
|
||||||
"phpspec/prophecy-phpunit": "^2.0.1"
|
|
||||||
},
|
|
||||||
"suggest": {
|
"suggest": {
|
||||||
"ext-soap": "*",
|
"ext-soap": "*",
|
||||||
"ext-xdebug": "*"
|
"ext-xdebug": "*"
|
||||||
@@ -2196,7 +2124,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
"issues": "https://github.com/sebastianbergmann/phpunit/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.20"
|
"source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.24"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -2208,7 +2136,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-04-01T12:37:26+00:00"
|
"time": "2022-08-30T07:42:16+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "psr/cache",
|
"name": "psr/cache",
|
||||||
@@ -3205,16 +3133,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/environment",
|
"name": "sebastian/environment",
|
||||||
"version": "5.1.3",
|
"version": "5.1.4",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/environment.git",
|
"url": "https://github.com/sebastianbergmann/environment.git",
|
||||||
"reference": "388b6ced16caa751030f6a69e588299fa09200ac"
|
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/388b6ced16caa751030f6a69e588299fa09200ac",
|
"url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
||||||
"reference": "388b6ced16caa751030f6a69e588299fa09200ac",
|
"reference": "1b5dff7bb151a4db11d49d90e5408e4e938270f7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -3256,7 +3184,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
"issues": "https://github.com/sebastianbergmann/environment/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.3"
|
"source": "https://github.com/sebastianbergmann/environment/tree/5.1.4"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3264,7 +3192,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2020-09-28T05:52:38+00:00"
|
"time": "2022-04-03T09:37:03+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/exporter",
|
"name": "sebastian/exporter",
|
||||||
@@ -3696,16 +3624,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/type",
|
"name": "sebastian/type",
|
||||||
"version": "3.0.0",
|
"version": "3.1.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/sebastianbergmann/type.git",
|
"url": "https://github.com/sebastianbergmann/type.git",
|
||||||
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad"
|
"reference": "fb44e1cc6e557418387ad815780360057e40753e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
|
"url": "https://api.github.com/repos/sebastianbergmann/type/zipball/fb44e1cc6e557418387ad815780360057e40753e",
|
||||||
"reference": "b233b84bc4465aff7b57cf1c4bc75c86d00d6dad",
|
"reference": "fb44e1cc6e557418387ad815780360057e40753e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -3717,7 +3645,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-master": "3.0-dev"
|
"dev-master": "3.1-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
@@ -3740,7 +3668,7 @@
|
|||||||
"homepage": "https://github.com/sebastianbergmann/type",
|
"homepage": "https://github.com/sebastianbergmann/type",
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/sebastianbergmann/type/issues",
|
"issues": "https://github.com/sebastianbergmann/type/issues",
|
||||||
"source": "https://github.com/sebastianbergmann/type/tree/3.0.0"
|
"source": "https://github.com/sebastianbergmann/type/tree/3.1.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3748,7 +3676,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-03-15T09:54:48+00:00"
|
"time": "2022-08-29T06:55:37+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "sebastian/version",
|
"name": "sebastian/version",
|
||||||
@@ -3805,16 +3733,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v5.4.5",
|
"version": "v5.4.12",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/console.git",
|
"url": "https://github.com/symfony/console.git",
|
||||||
"reference": "d8111acc99876953f52fe16d4c50eb60940d49ad"
|
"reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/console/zipball/d8111acc99876953f52fe16d4c50eb60940d49ad",
|
"url": "https://api.github.com/repos/symfony/console/zipball/c072aa8f724c3af64e2c7a96b796a4863d24dba1",
|
||||||
"reference": "d8111acc99876953f52fe16d4c50eb60940d49ad",
|
"reference": "c072aa8f724c3af64e2c7a96b796a4863d24dba1",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -3884,7 +3812,7 @@
|
|||||||
"terminal"
|
"terminal"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/console/tree/v5.4.5"
|
"source": "https://github.com/symfony/console/tree/v5.4.12"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3900,20 +3828,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-02-24T12:45:35+00:00"
|
"time": "2022-08-17T13:18:05+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/deprecation-contracts",
|
"name": "symfony/deprecation-contracts",
|
||||||
"version": "v2.5.0",
|
"version": "v2.5.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8"
|
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||||
"reference": "6f981ee24cf69ee7ce9736146d1c57c2780598a8",
|
"reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -3951,7 +3879,7 @@
|
|||||||
"description": "A generic function and convention to trigger deprecation notices",
|
"description": "A generic function and convention to trigger deprecation notices",
|
||||||
"homepage": "https://symfony.com",
|
"homepage": "https://symfony.com",
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.0"
|
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -3967,7 +3895,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-07-12T14:48:14+00:00"
|
"time": "2022-01-02T09:53:40+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher",
|
"name": "symfony/event-dispatcher",
|
||||||
@@ -4055,16 +3983,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/event-dispatcher-contracts",
|
"name": "symfony/event-dispatcher-contracts",
|
||||||
"version": "v1.1.11",
|
"version": "v1.1.13",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
|
"url": "https://github.com/symfony/event-dispatcher-contracts.git",
|
||||||
"reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c"
|
"reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/01e9a4efac0ee33a05dfdf93b346f62e7d0e998c",
|
"url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/1d5cd762abaa6b2a4169d3e77610193a7157129e",
|
||||||
"reference": "01e9a4efac0ee33a05dfdf93b346f62e7d0e998c",
|
"reference": "1d5cd762abaa6b2a4169d3e77610193a7157129e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4114,7 +4042,7 @@
|
|||||||
"standards"
|
"standards"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.11"
|
"source": "https://github.com/symfony/event-dispatcher-contracts/tree/v1.1.13"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4130,7 +4058,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-03-23T15:25:38+00:00"
|
"time": "2022-01-02T09:41:36+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/filesystem",
|
"name": "symfony/filesystem",
|
||||||
@@ -4330,16 +4258,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.25.0",
|
"version": "v1.26.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab"
|
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/30885182c981ab175d4d034db0f6f469898070ab",
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||||
"reference": "30885182c981ab175d4d034db0f6f469898070ab",
|
"reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4354,7 +4282,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.23-dev"
|
"dev-main": "1.26-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
@@ -4392,7 +4320,7 @@
|
|||||||
"portable"
|
"portable"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.25.0"
|
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4408,20 +4336,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-10-20T20:35:02+00:00"
|
"time": "2022-05-24T11:49:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-grapheme",
|
"name": "symfony/polyfill-intl-grapheme",
|
||||||
"version": "v1.25.0",
|
"version": "v1.26.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
"url": "https://github.com/symfony/polyfill-intl-grapheme.git",
|
||||||
"reference": "81b86b50cf841a64252b439e738e97f4a34e2783"
|
"reference": "433d05519ce6990bf3530fba6957499d327395c2"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/81b86b50cf841a64252b439e738e97f4a34e2783",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2",
|
||||||
"reference": "81b86b50cf841a64252b439e738e97f4a34e2783",
|
"reference": "433d05519ce6990bf3530fba6957499d327395c2",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4433,7 +4361,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.23-dev"
|
"dev-main": "1.26-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
@@ -4473,7 +4401,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.25.0"
|
"source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4489,20 +4417,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-11-23T21:10:46+00:00"
|
"time": "2022-05-24T11:49:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-intl-normalizer",
|
"name": "symfony/polyfill-intl-normalizer",
|
||||||
"version": "v1.25.0",
|
"version": "v1.26.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
"url": "https://github.com/symfony/polyfill-intl-normalizer.git",
|
||||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8"
|
"reference": "219aa369ceff116e673852dce47c3a41794c14bd"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
"url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd",
|
||||||
"reference": "8590a5f561694770bdcd3f9b5c69dde6945028e8",
|
"reference": "219aa369ceff116e673852dce47c3a41794c14bd",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4514,7 +4442,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.23-dev"
|
"dev-main": "1.26-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
@@ -4557,7 +4485,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.25.0"
|
"source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4573,20 +4501,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-02-19T12:13:01+00:00"
|
"time": "2022-05-24T11:49:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-mbstring",
|
"name": "symfony/polyfill-mbstring",
|
||||||
"version": "v1.25.0",
|
"version": "v1.26.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
"url": "https://github.com/symfony/polyfill-mbstring.git",
|
||||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825"
|
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||||
"reference": "0abb51d2f102e00a4eefcf46ba7fec406d245825",
|
"reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4601,7 +4529,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.23-dev"
|
"dev-main": "1.26-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
@@ -4640,7 +4568,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.25.0"
|
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4656,20 +4584,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-11-30T18:21:41+00:00"
|
"time": "2022-05-24T11:49:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php73",
|
"name": "symfony/polyfill-php73",
|
||||||
"version": "v1.25.0",
|
"version": "v1.26.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php73.git",
|
"url": "https://github.com/symfony/polyfill-php73.git",
|
||||||
"reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5"
|
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/cc5db0e22b3cb4111010e48785a97f670b350ca5",
|
"url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
||||||
"reference": "cc5db0e22b3cb4111010e48785a97f670b350ca5",
|
"reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4678,7 +4606,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.23-dev"
|
"dev-main": "1.26-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
@@ -4719,7 +4647,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php73/tree/v1.25.0"
|
"source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4735,20 +4663,20 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-06-05T21:20:04+00:00"
|
"time": "2022-05-24T11:49:31+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php80",
|
"name": "symfony/polyfill-php80",
|
||||||
"version": "v1.25.0",
|
"version": "v1.26.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-php80.git",
|
"url": "https://github.com/symfony/polyfill-php80.git",
|
||||||
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c"
|
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/4407588e0d3f1f52efb65fbe92babe41f37fe50c",
|
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||||
"reference": "4407588e0d3f1f52efb65fbe92babe41f37fe50c",
|
"reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -4757,7 +4685,7 @@
|
|||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.23-dev"
|
"dev-main": "1.26-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
@@ -4802,7 +4730,7 @@
|
|||||||
"shim"
|
"shim"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/polyfill-php80/tree/v1.25.0"
|
"source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -4818,7 +4746,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-03-04T08:16:47+00:00"
|
"time": "2022-05-10T07:21:04+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-php81",
|
"name": "symfony/polyfill-php81",
|
||||||
@@ -4963,22 +4891,22 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/service-contracts",
|
"name": "symfony/service-contracts",
|
||||||
"version": "v2.5.0",
|
"version": "v2.5.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/service-contracts.git",
|
"url": "https://github.com/symfony/service-contracts.git",
|
||||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc"
|
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
"url": "https://api.github.com/repos/symfony/service-contracts/zipball/4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
|
||||||
"reference": "1ab11b933cd6bc5464b08e81e2c5b07dec58b0fc",
|
"reference": "4b426aac47d6427cc1a1d0f7e2ac724627f5966c",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=7.2.5",
|
"php": ">=7.2.5",
|
||||||
"psr/container": "^1.1",
|
"psr/container": "^1.1",
|
||||||
"symfony/deprecation-contracts": "^2.1"
|
"symfony/deprecation-contracts": "^2.1|^3"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"ext-psr": "<1.1|>=2"
|
"ext-psr": "<1.1|>=2"
|
||||||
@@ -5026,7 +4954,7 @@
|
|||||||
"standards"
|
"standards"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/service-contracts/tree/v2.5.0"
|
"source": "https://github.com/symfony/service-contracts/tree/v2.5.2"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5042,7 +4970,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-11-04T16:48:04+00:00"
|
"time": "2022-05-30T19:17:29+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/stopwatch",
|
"name": "symfony/stopwatch",
|
||||||
@@ -5108,16 +5036,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/string",
|
"name": "symfony/string",
|
||||||
"version": "v5.4.3",
|
"version": "v5.4.12",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/string.git",
|
"url": "https://github.com/symfony/string.git",
|
||||||
"reference": "92043b7d8383e48104e411bc9434b260dbeb5a10"
|
"reference": "2fc515e512d721bf31ea76bd02fe23ada4640058"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/string/zipball/92043b7d8383e48104e411bc9434b260dbeb5a10",
|
"url": "https://api.github.com/repos/symfony/string/zipball/2fc515e512d721bf31ea76bd02fe23ada4640058",
|
||||||
"reference": "92043b7d8383e48104e411bc9434b260dbeb5a10",
|
"reference": "2fc515e512d721bf31ea76bd02fe23ada4640058",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5174,7 +5102,7 @@
|
|||||||
"utf8"
|
"utf8"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/symfony/string/tree/v5.4.3"
|
"source": "https://github.com/symfony/string/tree/v5.4.12"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -5190,7 +5118,7 @@
|
|||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2022-01-02T09:53:40+00:00"
|
"time": "2022-08-12T17:03:11+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "theseer/tokenizer",
|
"name": "theseer/tokenizer",
|
||||||
@@ -5244,16 +5172,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "vimeo/psalm",
|
"name": "vimeo/psalm",
|
||||||
"version": "4.22.0",
|
"version": "4.27.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/vimeo/psalm.git",
|
"url": "https://github.com/vimeo/psalm.git",
|
||||||
"reference": "fc2c6ab4d5fa5d644d8617089f012f3bb84b8703"
|
"reference": "faf106e717c37b8c81721845dba9de3d8deed8ff"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/vimeo/psalm/zipball/fc2c6ab4d5fa5d644d8617089f012f3bb84b8703",
|
"url": "https://api.github.com/repos/vimeo/psalm/zipball/faf106e717c37b8c81721845dba9de3d8deed8ff",
|
||||||
"reference": "fc2c6ab4d5fa5d644d8617089f012f3bb84b8703",
|
"reference": "faf106e717c37b8c81721845dba9de3d8deed8ff",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -5278,6 +5206,7 @@
|
|||||||
"php": "^7.1|^8",
|
"php": "^7.1|^8",
|
||||||
"sebastian/diff": "^3.0 || ^4.0",
|
"sebastian/diff": "^3.0 || ^4.0",
|
||||||
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
|
"symfony/console": "^3.4.17 || ^4.1.6 || ^5.0 || ^6.0",
|
||||||
|
"symfony/polyfill-php80": "^1.25",
|
||||||
"webmozart/path-util": "^2.3"
|
"webmozart/path-util": "^2.3"
|
||||||
},
|
},
|
||||||
"provide": {
|
"provide": {
|
||||||
@@ -5344,27 +5273,27 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/vimeo/psalm/issues",
|
"issues": "https://github.com/vimeo/psalm/issues",
|
||||||
"source": "https://github.com/vimeo/psalm/tree/4.22.0"
|
"source": "https://github.com/vimeo/psalm/tree/4.27.0"
|
||||||
},
|
},
|
||||||
"time": "2022-02-24T20:34:05+00:00"
|
"time": "2022-08-31T13:47:09+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webmozart/assert",
|
"name": "webmozart/assert",
|
||||||
"version": "1.10.0",
|
"version": "1.11.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/webmozarts/assert.git",
|
"url": "https://github.com/webmozarts/assert.git",
|
||||||
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25"
|
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/webmozarts/assert/zipball/6964c76c7804814a842473e0c8fd15bab0f18e25",
|
"url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991",
|
||||||
"reference": "6964c76c7804814a842473e0c8fd15bab0f18e25",
|
"reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.2 || ^8.0",
|
"ext-ctype": "*",
|
||||||
"symfony/polyfill-ctype": "^1.8"
|
"php": "^7.2 || ^8.0"
|
||||||
},
|
},
|
||||||
"conflict": {
|
"conflict": {
|
||||||
"phpstan/phpstan": "<0.12.20",
|
"phpstan/phpstan": "<0.12.20",
|
||||||
@@ -5402,9 +5331,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/webmozarts/assert/issues",
|
"issues": "https://github.com/webmozarts/assert/issues",
|
||||||
"source": "https://github.com/webmozarts/assert/tree/1.10.0"
|
"source": "https://github.com/webmozarts/assert/tree/1.11.0"
|
||||||
},
|
},
|
||||||
"time": "2021-03-09T10:59:23+00:00"
|
"time": "2022-06-03T18:03:27+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "webmozart/path-util",
|
"name": "webmozart/path-util",
|
||||||
|
|||||||
9
css/deck.css
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
.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 +0,0 @@
|
|||||||
@include icon-black-white('deck', 'deck', 1);
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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');
|
|
||||||
}
|
|
||||||
17
cypress.config.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
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}',
|
||||||
|
},
|
||||||
|
})
|
||||||
41
cypress/e2e/boardFeatures.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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 = 'TestBoard'
|
||||||
|
|
||||||
|
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')
|
||||||
|
})
|
||||||
|
})
|
||||||
67
cypress/e2e/cardFeatures.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
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')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
||||||
31
cypress/e2e/deckDashboard.js
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
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')
|
||||||
|
})
|
||||||
|
})
|
||||||
30
cypress/e2e/stackFeatures.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
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')
|
||||||
|
})
|
||||||
|
})
|
||||||
5
cypress/fixtures/example.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"name": "Using fixtures to represent data",
|
||||||
|
"email": "hello@cypress.io",
|
||||||
|
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||||
|
}
|
||||||
22
cypress/plugins/index.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
/// <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
|
||||||
|
}
|
||||||
159
cypress/support/commands.js
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
/**
|
||||||
|
* @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')
|
||||||
|
})
|
||||||
20
cypress/support/e2e.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// ***********************************************************
|
||||||
|
// 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')
|
||||||
1
cypress/utils/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const randHash = () => Math.random().toString(36).replace(/[^a-z]+/g, '').slice(0, 10)
|
||||||
@@ -90,7 +90,7 @@ Steps:
|
|||||||
* Create the configuration file
|
* Create the configuration file
|
||||||
* Execute the import informing the import file path, data file and source as `Trello JSON`
|
* 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/fixtures/config-trelloJson-schema.json) for import `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:
|
Example configuration file:
|
||||||
```json
|
```json
|
||||||
@@ -120,7 +120,7 @@ https://api.trello.com/1/members/me/boards?key={yourKey}&token={yourToken}&field
|
|||||||
This ID you will use in the configuration file in the `board` property
|
This ID you will use in the configuration file in the `board` property
|
||||||
* Create the configuration file
|
* Create the configuration file
|
||||||
|
|
||||||
Create the configuration file respecting the [JSON Schema](https://github.com/nextcloud/deck/blob/master/lib/Service/fixtures/config-trelloApi-schema.json) for import `Trello JSON`
|
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:
|
Example configuration file:
|
||||||
```json
|
```json
|
||||||
|
|||||||
4
img/activity-dark.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?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>
|
||||||
|
After Width: | Height: | Size: 205 B |
4
img/activity.svg
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?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>
|
||||||
|
After Width: | Height: | Size: 217 B |
@@ -1 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 488 B |
1
img/circles-dark.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<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>
|
||||||
|
After Width: | Height: | Size: 885 B |
@@ -1 +1 @@
|
|||||||
<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>
|
<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>
|
||||||
|
Before Width: | Height: | Size: 885 B After Width: | Height: | Size: 885 B |
@@ -1 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 327 B |
@@ -1 +0,0 @@
|
|||||||
<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>
|
|
||||||
|
Before Width: | Height: | Size: 128 B |
50
l10n/ast.js
@@ -1,50 +0,0 @@
|
|||||||
OC.L10N.register(
|
|
||||||
"deck",
|
|
||||||
{
|
|
||||||
"Deck" : "Deck",
|
|
||||||
"Personal" : "Personal",
|
|
||||||
"%s on %s" : "%s en %s",
|
|
||||||
"Finished" : "Finó",
|
|
||||||
"Action needed" : "Precísase aición",
|
|
||||||
"Later" : "Más sero",
|
|
||||||
"Done" : "Fecho",
|
|
||||||
"The file was uploaded" : "Xubióse'l ficheru",
|
|
||||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "El ficheru xubíu perpasa la direutiva de xuba upload_max_filesize en php.ini",
|
|
||||||
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "El ficheru xubíu perpasa la direutiva \"MAX_FILE_SIZE\" especificada nel formulariu HTML",
|
|
||||||
"No file was uploaded" : "Nun se xubieron fichjeros",
|
|
||||||
"Missing a temporary folder" : "Falta un direutoriu temporal",
|
|
||||||
"Could not write file to disk" : "Nun pudo escribise nel discu'l ficheru",
|
|
||||||
"A PHP extension stopped the file upload" : "Una estensión de PHP paró la xuba de ficheros",
|
|
||||||
"Invalid date, date format must be YYYY-MM-DD" : "Data non válida, el formatu ha ser AAAA-MM-DD",
|
|
||||||
"Cancel" : "Encaboxar",
|
|
||||||
"Close" : "Zarrar",
|
|
||||||
"File already exists" : "Yá esiste'l ficheru",
|
|
||||||
"Show archived cards" : "Amosar tarxetes archivaes",
|
|
||||||
"Details" : "Detalles",
|
|
||||||
"Sharing" : "Compartiendo",
|
|
||||||
"Tags" : "Etiquetes",
|
|
||||||
"Undo" : "Desfacer",
|
|
||||||
"Can edit" : "Can edit",
|
|
||||||
"Can share" : "Can share",
|
|
||||||
"Owner" : "Owner",
|
|
||||||
"Delete" : "Desaniciar",
|
|
||||||
"Edit" : "Editar",
|
|
||||||
"Members" : "Miembros",
|
|
||||||
"Download" : "Baxar",
|
|
||||||
"Attachments" : "Axuntos",
|
|
||||||
"Comments" : "Comentarios",
|
|
||||||
"Modified" : "Modificóse'l",
|
|
||||||
"Created" : "Creóse",
|
|
||||||
"Today" : "Güei",
|
|
||||||
"Tomorrow" : "Mañana",
|
|
||||||
"Save" : "Guardar",
|
|
||||||
"Reply" : "Rempuesta",
|
|
||||||
"Update" : "Anovar",
|
|
||||||
"Description" : "Descripción",
|
|
||||||
"(group)" : "(grupu)",
|
|
||||||
"seconds ago" : "hai segundos",
|
|
||||||
"Shared with you" : "Shared with you",
|
|
||||||
"No notifications" : "Ensin avisos",
|
|
||||||
"Share" : "Share"
|
|
||||||
},
|
|
||||||
"nplurals=2; plural=(n != 1);");
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
{ "translations": {
|
|
||||||
"Deck" : "Deck",
|
|
||||||
"Personal" : "Personal",
|
|
||||||
"%s on %s" : "%s en %s",
|
|
||||||
"Finished" : "Finó",
|
|
||||||
"Action needed" : "Precísase aición",
|
|
||||||
"Later" : "Más sero",
|
|
||||||
"Done" : "Fecho",
|
|
||||||
"The file was uploaded" : "Xubióse'l ficheru",
|
|
||||||
"The uploaded file exceeds the upload_max_filesize directive in php.ini" : "El ficheru xubíu perpasa la direutiva de xuba upload_max_filesize en php.ini",
|
|
||||||
"The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form" : "El ficheru xubíu perpasa la direutiva \"MAX_FILE_SIZE\" especificada nel formulariu HTML",
|
|
||||||
"No file was uploaded" : "Nun se xubieron fichjeros",
|
|
||||||
"Missing a temporary folder" : "Falta un direutoriu temporal",
|
|
||||||
"Could not write file to disk" : "Nun pudo escribise nel discu'l ficheru",
|
|
||||||
"A PHP extension stopped the file upload" : "Una estensión de PHP paró la xuba de ficheros",
|
|
||||||
"Invalid date, date format must be YYYY-MM-DD" : "Data non válida, el formatu ha ser AAAA-MM-DD",
|
|
||||||
"Cancel" : "Encaboxar",
|
|
||||||
"Close" : "Zarrar",
|
|
||||||
"File already exists" : "Yá esiste'l ficheru",
|
|
||||||
"Show archived cards" : "Amosar tarxetes archivaes",
|
|
||||||
"Details" : "Detalles",
|
|
||||||
"Sharing" : "Compartiendo",
|
|
||||||
"Tags" : "Etiquetes",
|
|
||||||
"Undo" : "Desfacer",
|
|
||||||
"Can edit" : "Can edit",
|
|
||||||
"Can share" : "Can share",
|
|
||||||
"Owner" : "Owner",
|
|
||||||
"Delete" : "Desaniciar",
|
|
||||||
"Edit" : "Editar",
|
|
||||||
"Members" : "Miembros",
|
|
||||||
"Download" : "Baxar",
|
|
||||||
"Attachments" : "Axuntos",
|
|
||||||
"Comments" : "Comentarios",
|
|
||||||
"Modified" : "Modificóse'l",
|
|
||||||
"Created" : "Creóse",
|
|
||||||
"Today" : "Güei",
|
|
||||||
"Tomorrow" : "Mañana",
|
|
||||||
"Save" : "Guardar",
|
|
||||||
"Reply" : "Rempuesta",
|
|
||||||
"Update" : "Anovar",
|
|
||||||
"Description" : "Descripción",
|
|
||||||
"(group)" : "(grupu)",
|
|
||||||
"seconds ago" : "hai segundos",
|
|
||||||
"Shared with you" : "Shared with you",
|
|
||||||
"No notifications" : "Ensin avisos",
|
|
||||||
"Share" : "Share"
|
|
||||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
|
||||||
}
|
|
||||||
@@ -81,6 +81,7 @@ OC.L10N.register(
|
|||||||
"Deck board" : "Доска",
|
"Deck board" : "Доска",
|
||||||
"Owned by %1$s" : "Владелец: %1$s",
|
"Owned by %1$s" : "Владелец: %1$s",
|
||||||
"Deck boards, cards and comments" : "Доски, карточки и комментарии",
|
"Deck boards, cards and comments" : "Доски, карточки и комментарии",
|
||||||
|
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Из %1$s, в %2$s/%3$s, принадлежит %4$s",
|
||||||
"Card comments" : "Комментарии карточки",
|
"Card comments" : "Комментарии карточки",
|
||||||
"%s on %s" : "%s на %s",
|
"%s on %s" : "%s на %s",
|
||||||
"Deck boards and cards" : "Доски и карточки",
|
"Deck boards and cards" : "Доски и карточки",
|
||||||
@@ -156,6 +157,7 @@ OC.L10N.register(
|
|||||||
"Toggle compact mode" : "Выбор компактного или обычного режима просмотра",
|
"Toggle compact mode" : "Выбор компактного или обычного режима просмотра",
|
||||||
"Open details" : "Открыть подробности",
|
"Open details" : "Открыть подробности",
|
||||||
"Details" : "Свойства",
|
"Details" : "Свойства",
|
||||||
|
"Currently present people" : "Присутствующие в настоящее время люди",
|
||||||
"Loading board" : "Загрузка доски",
|
"Loading board" : "Загрузка доски",
|
||||||
"No lists available" : "Нет ни одного списка",
|
"No lists available" : "Нет ни одного списка",
|
||||||
"Create a new list to add cards to this board" : "Создайте список чтобы добавить карточки на эту доску",
|
"Create a new list to add cards to this board" : "Создайте список чтобы добавить карточки на эту доску",
|
||||||
@@ -296,10 +298,12 @@ OC.L10N.register(
|
|||||||
"Deck board {name}\n* Last modified on {lastMod}" : "Доска «{name}»\n* Последнее изменение: {lastMod}",
|
"Deck board {name}\n* Last modified on {lastMod}" : "Доска «{name}»\n* Последнее изменение: {lastMod}",
|
||||||
"{stack} in {board}" : "«{stack}» с доски «{board}»",
|
"{stack} in {board}" : "«{stack}» с доски «{board}»",
|
||||||
"Click to expand description" : "Нажмите, чтобы развернуть поле описания",
|
"Click to expand description" : "Нажмите, чтобы развернуть поле описания",
|
||||||
|
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Создано {created}\n* Последнее изменение {lastMod}\n* {nbAttachments} вложений\n* {nbComments} комментариев",
|
||||||
"{nbCards} cards" : "карточек: {nbCards}",
|
"{nbCards} cards" : "карточек: {nbCards}",
|
||||||
"Click to expand comment" : "Нажмите, чтобы развернуть комментарии",
|
"Click to expand comment" : "Нажмите, чтобы развернуть комментарии",
|
||||||
"No upcoming cards" : "Отсутствуют карточки, ожидающие выполнения",
|
"No upcoming cards" : "Отсутствуют карточки, ожидающие выполнения",
|
||||||
"upcoming cards" : "карточки, ожидающие выполнения",
|
"upcoming cards" : "карточки, ожидающие выполнения",
|
||||||
|
"New card" : "Новая карточка",
|
||||||
"Due on {date}" : "Дата исполнения: {date}",
|
"Due on {date}" : "Дата исполнения: {date}",
|
||||||
"Link to a board" : "Ссылка на доску",
|
"Link to a board" : "Ссылка на доску",
|
||||||
"Link to a card" : "Ссылка на карточку",
|
"Link to a card" : "Ссылка на карточку",
|
||||||
|
|||||||
@@ -79,6 +79,7 @@
|
|||||||
"Deck board" : "Доска",
|
"Deck board" : "Доска",
|
||||||
"Owned by %1$s" : "Владелец: %1$s",
|
"Owned by %1$s" : "Владелец: %1$s",
|
||||||
"Deck boards, cards and comments" : "Доски, карточки и комментарии",
|
"Deck boards, cards and comments" : "Доски, карточки и комментарии",
|
||||||
|
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Из %1$s, в %2$s/%3$s, принадлежит %4$s",
|
||||||
"Card comments" : "Комментарии карточки",
|
"Card comments" : "Комментарии карточки",
|
||||||
"%s on %s" : "%s на %s",
|
"%s on %s" : "%s на %s",
|
||||||
"Deck boards and cards" : "Доски и карточки",
|
"Deck boards and cards" : "Доски и карточки",
|
||||||
@@ -154,6 +155,7 @@
|
|||||||
"Toggle compact mode" : "Выбор компактного или обычного режима просмотра",
|
"Toggle compact mode" : "Выбор компактного или обычного режима просмотра",
|
||||||
"Open details" : "Открыть подробности",
|
"Open details" : "Открыть подробности",
|
||||||
"Details" : "Свойства",
|
"Details" : "Свойства",
|
||||||
|
"Currently present people" : "Присутствующие в настоящее время люди",
|
||||||
"Loading board" : "Загрузка доски",
|
"Loading board" : "Загрузка доски",
|
||||||
"No lists available" : "Нет ни одного списка",
|
"No lists available" : "Нет ни одного списка",
|
||||||
"Create a new list to add cards to this board" : "Создайте список чтобы добавить карточки на эту доску",
|
"Create a new list to add cards to this board" : "Создайте список чтобы добавить карточки на эту доску",
|
||||||
@@ -294,10 +296,12 @@
|
|||||||
"Deck board {name}\n* Last modified on {lastMod}" : "Доска «{name}»\n* Последнее изменение: {lastMod}",
|
"Deck board {name}\n* Last modified on {lastMod}" : "Доска «{name}»\n* Последнее изменение: {lastMod}",
|
||||||
"{stack} in {board}" : "«{stack}» с доски «{board}»",
|
"{stack} in {board}" : "«{stack}» с доски «{board}»",
|
||||||
"Click to expand description" : "Нажмите, чтобы развернуть поле описания",
|
"Click to expand description" : "Нажмите, чтобы развернуть поле описания",
|
||||||
|
"* Created on {created}\n* Last modified on {lastMod}\n* {nbAttachments} attachments\n* {nbComments} comments" : "* Создано {created}\n* Последнее изменение {lastMod}\n* {nbAttachments} вложений\n* {nbComments} комментариев",
|
||||||
"{nbCards} cards" : "карточек: {nbCards}",
|
"{nbCards} cards" : "карточек: {nbCards}",
|
||||||
"Click to expand comment" : "Нажмите, чтобы развернуть комментарии",
|
"Click to expand comment" : "Нажмите, чтобы развернуть комментарии",
|
||||||
"No upcoming cards" : "Отсутствуют карточки, ожидающие выполнения",
|
"No upcoming cards" : "Отсутствуют карточки, ожидающие выполнения",
|
||||||
"upcoming cards" : "карточки, ожидающие выполнения",
|
"upcoming cards" : "карточки, ожидающие выполнения",
|
||||||
|
"New card" : "Новая карточка",
|
||||||
"Due on {date}" : "Дата исполнения: {date}",
|
"Due on {date}" : "Дата исполнения: {date}",
|
||||||
"Link to a board" : "Ссылка на доску",
|
"Link to a board" : "Ссылка на доску",
|
||||||
"Link to a card" : "Ссылка на карточку",
|
"Link to a card" : "Ссылка на карточку",
|
||||||
|
|||||||
14
l10n/sv.js
@@ -78,7 +78,7 @@ OC.L10N.register(
|
|||||||
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nämnt dig i en kommentar i {deck-card}.",
|
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nämnt dig i en kommentar i {deck-card}.",
|
||||||
"The board \"%s\" has been shared with you by %s." : "Tavlan \"%s\" har delats med dig av %s.",
|
"The board \"%s\" has been shared with you by %s." : "Tavlan \"%s\" har delats med dig av %s.",
|
||||||
"{user} has shared {deck-board} with you." : "{user} har delat {deck-board} med dig.",
|
"{user} has shared {deck-board} with you." : "{user} har delat {deck-board} med dig.",
|
||||||
"Deck board" : "Deck-plank",
|
"Deck board" : "Deck-tavla",
|
||||||
"Owned by %1$s" : "Ägd av %1$s",
|
"Owned by %1$s" : "Ägd av %1$s",
|
||||||
"Deck boards, cards and comments" : "Deck tavlor, kort och kommentarer",
|
"Deck boards, cards and comments" : "Deck tavlor, kort och kommentarer",
|
||||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Från %1$s, i %2$s/%3$s, ägd av %4$s",
|
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Från %1$s, i %2$s/%3$s, ägd av %4$s",
|
||||||
@@ -92,7 +92,7 @@ OC.L10N.register(
|
|||||||
"Later" : "Senare",
|
"Later" : "Senare",
|
||||||
"copy" : "kopiera",
|
"copy" : "kopiera",
|
||||||
"To do" : "Att göra",
|
"To do" : "Att göra",
|
||||||
"Doing" : "Gör",
|
"Doing" : "Pågående",
|
||||||
"Done" : "Klart",
|
"Done" : "Klart",
|
||||||
"Example Task 3" : "Exempeluppgift 3",
|
"Example Task 3" : "Exempeluppgift 3",
|
||||||
"Example Task 2" : "Exempeluppgift 2",
|
"Example Task 2" : "Exempeluppgift 2",
|
||||||
@@ -249,7 +249,7 @@ OC.L10N.register(
|
|||||||
"Write a description …" : "Ange en beskrivning ...",
|
"Write a description …" : "Ange en beskrivning ...",
|
||||||
"Choose attachment" : "Välj bilaga",
|
"Choose attachment" : "Välj bilaga",
|
||||||
"(group)" : " (grupp)",
|
"(group)" : " (grupp)",
|
||||||
"Todo items" : "Todo saker",
|
"Todo items" : "Att göra saker",
|
||||||
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
|
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
|
||||||
"Edit card title" : "Ändra korttitel",
|
"Edit card title" : "Ändra korttitel",
|
||||||
"Assign to me" : "Tilldela till mig",
|
"Assign to me" : "Tilldela till mig",
|
||||||
@@ -289,7 +289,7 @@ OC.L10N.register(
|
|||||||
"Only assigned cards" : "Bara tilldelade kort",
|
"Only assigned cards" : "Bara tilldelade kort",
|
||||||
"No reminder" : "Ingen påminnelse",
|
"No reminder" : "Ingen påminnelse",
|
||||||
"An error occurred" : "Ett fel uppstod",
|
"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.",
|
"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 tavla {title}? Detta kommer radera all data som tillhör tavlan inklusive arkiverade kort.",
|
||||||
"Delete the board?" : "Ta bort tavlan?",
|
"Delete the board?" : "Ta bort tavlan?",
|
||||||
"Loading filtered view" : "Laddar filtrerad vy",
|
"Loading filtered view" : "Laddar filtrerad vy",
|
||||||
"No due" : "Inget slut",
|
"No due" : "Inget slut",
|
||||||
@@ -316,9 +316,9 @@ OC.L10N.register(
|
|||||||
"Share with a Deck card" : "Dela med ett Deck-kort",
|
"Share with a Deck card" : "Dela med ett Deck-kort",
|
||||||
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
|
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
|
||||||
"Share" : "Dela",
|
"Share" : "Dela",
|
||||||
"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}?",
|
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra tavla {title} för {user}?",
|
||||||
"Transfer the board for {user} successfully" : "Överförde brädet för {user}",
|
"Transfer the board for {user} successfully" : "Överförde tavlan för {user}",
|
||||||
"Failed to transfer the board for {user}" : "Misslyckades med att överföra brädet för {user}",
|
"Failed to transfer the board for {user}" : "Misslyckades med att överföra tavlan för {user}",
|
||||||
"Add a new list" : "Lägg till en ny lista",
|
"Add a new list" : "Lägg till en ny lista",
|
||||||
"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."
|
"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."
|
||||||
},
|
},
|
||||||
|
|||||||
14
l10n/sv.json
@@ -76,7 +76,7 @@
|
|||||||
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nämnt dig i en kommentar i {deck-card}.",
|
"{user} has mentioned you in a comment on {deck-card}." : "{user} har nämnt dig i en kommentar i {deck-card}.",
|
||||||
"The board \"%s\" has been shared with you by %s." : "Tavlan \"%s\" har delats med dig av %s.",
|
"The board \"%s\" has been shared with you by %s." : "Tavlan \"%s\" har delats med dig av %s.",
|
||||||
"{user} has shared {deck-board} with you." : "{user} har delat {deck-board} med dig.",
|
"{user} has shared {deck-board} with you." : "{user} har delat {deck-board} med dig.",
|
||||||
"Deck board" : "Deck-plank",
|
"Deck board" : "Deck-tavla",
|
||||||
"Owned by %1$s" : "Ägd av %1$s",
|
"Owned by %1$s" : "Ägd av %1$s",
|
||||||
"Deck boards, cards and comments" : "Deck tavlor, kort och kommentarer",
|
"Deck boards, cards and comments" : "Deck tavlor, kort och kommentarer",
|
||||||
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Från %1$s, i %2$s/%3$s, ägd av %4$s",
|
"From %1$s, in %2$s/%3$s, owned by %4$s" : "Från %1$s, i %2$s/%3$s, ägd av %4$s",
|
||||||
@@ -90,7 +90,7 @@
|
|||||||
"Later" : "Senare",
|
"Later" : "Senare",
|
||||||
"copy" : "kopiera",
|
"copy" : "kopiera",
|
||||||
"To do" : "Att göra",
|
"To do" : "Att göra",
|
||||||
"Doing" : "Gör",
|
"Doing" : "Pågående",
|
||||||
"Done" : "Klart",
|
"Done" : "Klart",
|
||||||
"Example Task 3" : "Exempeluppgift 3",
|
"Example Task 3" : "Exempeluppgift 3",
|
||||||
"Example Task 2" : "Exempeluppgift 2",
|
"Example Task 2" : "Exempeluppgift 2",
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
"Write a description …" : "Ange en beskrivning ...",
|
"Write a description …" : "Ange en beskrivning ...",
|
||||||
"Choose attachment" : "Välj bilaga",
|
"Choose attachment" : "Välj bilaga",
|
||||||
"(group)" : " (grupp)",
|
"(group)" : " (grupp)",
|
||||||
"Todo items" : "Todo saker",
|
"Todo items" : "Att göra saker",
|
||||||
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
|
"{count} comments, {unread} unread" : "{count} kommentarer, {unread} olästa",
|
||||||
"Edit card title" : "Ändra korttitel",
|
"Edit card title" : "Ändra korttitel",
|
||||||
"Assign to me" : "Tilldela till mig",
|
"Assign to me" : "Tilldela till mig",
|
||||||
@@ -287,7 +287,7 @@
|
|||||||
"Only assigned cards" : "Bara tilldelade kort",
|
"Only assigned cards" : "Bara tilldelade kort",
|
||||||
"No reminder" : "Ingen påminnelse",
|
"No reminder" : "Ingen påminnelse",
|
||||||
"An error occurred" : "Ett fel uppstod",
|
"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.",
|
"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 tavla {title}? Detta kommer radera all data som tillhör tavlan inklusive arkiverade kort.",
|
||||||
"Delete the board?" : "Ta bort tavlan?",
|
"Delete the board?" : "Ta bort tavlan?",
|
||||||
"Loading filtered view" : "Laddar filtrerad vy",
|
"Loading filtered view" : "Laddar filtrerad vy",
|
||||||
"No due" : "Inget slut",
|
"No due" : "Inget slut",
|
||||||
@@ -314,9 +314,9 @@
|
|||||||
"Share with a Deck card" : "Dela med ett Deck-kort",
|
"Share with a Deck card" : "Dela med ett Deck-kort",
|
||||||
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
|
"Share {file} with a Deck card" : "Dela {file} med ett Deck-kort",
|
||||||
"Share" : "Dela",
|
"Share" : "Dela",
|
||||||
"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}?",
|
"Are you sure you want to transfer the board {title} for {user}?" : "Är du säker på att du vill överföra tavla {title} för {user}?",
|
||||||
"Transfer the board for {user} successfully" : "Överförde brädet för {user}",
|
"Transfer the board for {user} successfully" : "Överförde tavlan för {user}",
|
||||||
"Failed to transfer the board for {user}" : "Misslyckades med att överföra brädet för {user}",
|
"Failed to transfer the board for {user}" : "Misslyckades med att överföra tavlan för {user}",
|
||||||
"Add a new list" : "Lägg till en ny lista",
|
"Add a new list" : "Lägg till en ny lista",
|
||||||
"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."
|
"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."
|
||||||
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
},"pluralForm" :"nplurals=2; plural=(n != 1);"
|
||||||
|
|||||||
@@ -173,7 +173,7 @@ OC.L10N.register(
|
|||||||
"Can manage" : "可以管理",
|
"Can manage" : "可以管理",
|
||||||
"Owner" : "所有者",
|
"Owner" : "所有者",
|
||||||
"Delete" : "删除",
|
"Delete" : "删除",
|
||||||
"Failed to create share with {displayName}" : "用 {displayName} 创建分享失败",
|
"Failed to create share with {displayName}" : "用{displayName}创建分享失败",
|
||||||
"Transfer" : "传输",
|
"Transfer" : "传输",
|
||||||
"Archive all cards" : "归档所有卡片",
|
"Archive all cards" : "归档所有卡片",
|
||||||
"Delete list" : "删除列表",
|
"Delete list" : "删除列表",
|
||||||
@@ -285,7 +285,7 @@ OC.L10N.register(
|
|||||||
"Create a card" : "创建一张卡片",
|
"Create a card" : "创建一张卡片",
|
||||||
"Message from {author} in {conversationName}" : "{conversationName} 会话中来自 {author} 的消息",
|
"Message from {author} in {conversationName}" : "{conversationName} 会话中来自 {author} 的消息",
|
||||||
"Something went wrong" : "发生了错误",
|
"Something went wrong" : "发生了错误",
|
||||||
"Failed to upload {name}" : "未能上传 {name}",
|
"Failed to upload {name}" : "未能上传{name}",
|
||||||
"Maximum file size of {size} exceeded" : "文件容量已超过 {size} 的上限",
|
"Maximum file size of {size} exceeded" : "文件容量已超过 {size} 的上限",
|
||||||
"Error creating the share" : "创建分享出错",
|
"Error creating the share" : "创建分享出错",
|
||||||
"Share with a Deck card" : "分享给一张看板卡片",
|
"Share with a Deck card" : "分享给一张看板卡片",
|
||||||
|
|||||||
@@ -171,7 +171,7 @@
|
|||||||
"Can manage" : "可以管理",
|
"Can manage" : "可以管理",
|
||||||
"Owner" : "所有者",
|
"Owner" : "所有者",
|
||||||
"Delete" : "删除",
|
"Delete" : "删除",
|
||||||
"Failed to create share with {displayName}" : "用 {displayName} 创建分享失败",
|
"Failed to create share with {displayName}" : "用{displayName}创建分享失败",
|
||||||
"Transfer" : "传输",
|
"Transfer" : "传输",
|
||||||
"Archive all cards" : "归档所有卡片",
|
"Archive all cards" : "归档所有卡片",
|
||||||
"Delete list" : "删除列表",
|
"Delete list" : "删除列表",
|
||||||
@@ -283,7 +283,7 @@
|
|||||||
"Create a card" : "创建一张卡片",
|
"Create a card" : "创建一张卡片",
|
||||||
"Message from {author} in {conversationName}" : "{conversationName} 会话中来自 {author} 的消息",
|
"Message from {author} in {conversationName}" : "{conversationName} 会话中来自 {author} 的消息",
|
||||||
"Something went wrong" : "发生了错误",
|
"Something went wrong" : "发生了错误",
|
||||||
"Failed to upload {name}" : "未能上传 {name}",
|
"Failed to upload {name}" : "未能上传{name}",
|
||||||
"Maximum file size of {size} exceeded" : "文件容量已超过 {size} 的上限",
|
"Maximum file size of {size} exceeded" : "文件容量已超过 {size} 的上限",
|
||||||
"Error creating the share" : "创建分享出错",
|
"Error creating the share" : "创建分享出错",
|
||||||
"Share with a Deck card" : "分享给一张看板卡片",
|
"Share with a Deck card" : "分享给一张看板卡片",
|
||||||
|
|||||||
@@ -45,7 +45,9 @@ use OCP\AppFramework\Db\DoesNotExistException;
|
|||||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
use OCP\Comments\IComment;
|
use OCP\Comments\IComment;
|
||||||
use OCP\IUser;
|
use OCP\IUser;
|
||||||
|
use OCP\Server;
|
||||||
use OCP\L10N\IFactory;
|
use OCP\L10N\IFactory;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class ActivityManager {
|
class ActivityManager {
|
||||||
public const DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED = 'DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED';
|
public const DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED = 'DECK_NOAUTHOR_COMMENT_SYSTEM_ENFORCED';
|
||||||
@@ -53,14 +55,14 @@ class ActivityManager {
|
|||||||
public const SUBJECT_PARAMS_MAX_LENGTH = 4000;
|
public const SUBJECT_PARAMS_MAX_LENGTH = 4000;
|
||||||
public const SHORTENED_DESCRIPTION_MAX_LENGTH = 2000;
|
public const SHORTENED_DESCRIPTION_MAX_LENGTH = 2000;
|
||||||
|
|
||||||
private $manager;
|
private IManager $manager;
|
||||||
private $userId;
|
private ?string $userId;
|
||||||
private $permissionService;
|
private PermissionService $permissionService;
|
||||||
private $boardMapper;
|
private BoardMapper $boardMapper;
|
||||||
private $cardMapper;
|
private CardMapper $cardMapper;
|
||||||
private $aclMapper;
|
private AclMapper $aclMapper;
|
||||||
private $stackMapper;
|
private StackMapper $stackMapper;
|
||||||
private $l10nFactory;
|
private IFactory $l10nFactory;
|
||||||
|
|
||||||
public const DECK_OBJECT_BOARD = 'deck_board';
|
public const DECK_OBJECT_BOARD = 'deck_board';
|
||||||
public const DECK_OBJECT_CARD = 'deck_card';
|
public const DECK_OBJECT_CARD = 'deck_card';
|
||||||
@@ -114,7 +116,7 @@ class ActivityManager {
|
|||||||
StackMapper $stackMapper,
|
StackMapper $stackMapper,
|
||||||
AclMapper $aclMapper,
|
AclMapper $aclMapper,
|
||||||
IFactory $l10nFactory,
|
IFactory $l10nFactory,
|
||||||
$userId
|
?string $userId
|
||||||
) {
|
) {
|
||||||
$this->manager = $manager;
|
$this->manager = $manager;
|
||||||
$this->permissionService = $permissionsService;
|
$this->permissionService = $permissionsService;
|
||||||
@@ -310,10 +312,10 @@ class ActivityManager {
|
|||||||
try {
|
try {
|
||||||
$object = $this->findObjectForEntity($objectType, $entity);
|
$object = $this->findObjectForEntity($objectType, $entity);
|
||||||
} catch (DoesNotExistException $e) {
|
} catch (DoesNotExistException $e) {
|
||||||
\OC::$server->getLogger()->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
Server::get(LoggerInterface::class)->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
||||||
return null;
|
return null;
|
||||||
} catch (MultipleObjectsReturnedException $e) {
|
} catch (MultipleObjectsReturnedException $e) {
|
||||||
\OC::$server->getLogger()->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
Server::get(LoggerInterface::class)->error('Could not create activity entry for ' . $subject . '. Entity not found.', (array)$entity);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,7 +367,15 @@ class ActivityManager {
|
|||||||
case self::SUBJECT_CARD_USER_ASSIGN:
|
case self::SUBJECT_CARD_USER_ASSIGN:
|
||||||
case self::SUBJECT_CARD_USER_UNASSIGN:
|
case self::SUBJECT_CARD_USER_UNASSIGN:
|
||||||
$subjectParams = $this->findDetailsForCard($entity->getId(), $subject);
|
$subjectParams = $this->findDetailsForCard($entity->getId(), $subject);
|
||||||
break;
|
|
||||||
|
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;
|
||||||
case self::SUBJECT_ATTACHMENT_CREATE:
|
case self::SUBJECT_ATTACHMENT_CREATE:
|
||||||
case self::SUBJECT_ATTACHMENT_UPDATE:
|
case self::SUBJECT_ATTACHMENT_UPDATE:
|
||||||
case self::SUBJECT_ATTACHMENT_DELETE:
|
case self::SUBJECT_ATTACHMENT_DELETE:
|
||||||
|
|||||||
@@ -312,12 +312,19 @@ class DeckProvider implements IProvider {
|
|||||||
$userLanguage = $this->config->getUserValue($event->getAuthor(), 'core', 'lang', $this->l10nFactory->findLanguage());
|
$userLanguage = $this->config->getUserValue($event->getAuthor(), 'core', 'lang', $this->l10nFactory->findLanguage());
|
||||||
$userLocale = $this->config->getUserValue($event->getAuthor(), 'core', 'locale', $this->l10nFactory->findLocale());
|
$userLocale = $this->config->getUserValue($event->getAuthor(), 'core', 'locale', $this->l10nFactory->findLocale());
|
||||||
$l10n = $this->l10nFactory->get('deck', $userLanguage, $userLocale);
|
$l10n = $this->l10nFactory->get('deck', $userLanguage, $userLocale);
|
||||||
$date = new \DateTime($subjectParams['after']);
|
if (is_array($subjectParams['after'])) {
|
||||||
$date->setTimezone(new \DateTimeZone(\date_default_timezone_get()));
|
// 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()));
|
||||||
|
}
|
||||||
$params['after'] = [
|
$params['after'] = [
|
||||||
'type' => 'highlight',
|
'type' => 'highlight',
|
||||||
'id' => 'dt:' . $subjectParams['after'],
|
'id' => 'dt:' . $subjectParams['after'],
|
||||||
'name' => $l10n->l('datetime', $date)
|
'name' => $l10n->l('datetime', $date),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
return $params;
|
return $params;
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ use OCA\Deck\Listeners\FullTextSearchEventListener;
|
|||||||
use OCA\Deck\Middleware\DefaultBoardMiddleware;
|
use OCA\Deck\Middleware\DefaultBoardMiddleware;
|
||||||
use OCA\Deck\Middleware\ExceptionMiddleware;
|
use OCA\Deck\Middleware\ExceptionMiddleware;
|
||||||
use OCA\Deck\Notification\Notifier;
|
use OCA\Deck\Notification\Notifier;
|
||||||
|
use OCA\Deck\Reference\CardReferenceProvider;
|
||||||
use OCA\Deck\Search\CardCommentProvider;
|
use OCA\Deck\Search\CardCommentProvider;
|
||||||
use OCA\Deck\Search\DeckProvider;
|
use OCA\Deck\Search\DeckProvider;
|
||||||
use OCA\Deck\Service\PermissionService;
|
use OCA\Deck\Service\PermissionService;
|
||||||
@@ -57,20 +58,22 @@ use OCP\AppFramework\Bootstrap\IBootContext;
|
|||||||
use OCP\AppFramework\Bootstrap\IBootstrap;
|
use OCP\AppFramework\Bootstrap\IBootstrap;
|
||||||
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
use OCP\AppFramework\Bootstrap\IRegistrationContext;
|
||||||
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
|
use OCP\AppFramework\Http\Events\BeforeTemplateRenderedEvent;
|
||||||
|
use OCP\Collaboration\Reference\RenderReferenceEvent;
|
||||||
use OCP\Collaboration\Resources\IProviderManager;
|
use OCP\Collaboration\Resources\IProviderManager;
|
||||||
use OCP\Comments\CommentsEntityEvent;
|
use OCP\Comments\CommentsEntityEvent;
|
||||||
use OCP\Comments\ICommentsManager;
|
use OCP\Comments\ICommentsManager;
|
||||||
use OCP\EventDispatcher\Event;
|
use OCP\EventDispatcher\Event;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
|
use OCP\Group\Events\GroupDeletedEvent;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
use OCP\IDBConnection;
|
use OCP\IDBConnection;
|
||||||
use OCP\IGroup;
|
|
||||||
use OCP\IGroupManager;
|
use OCP\IGroupManager;
|
||||||
use OCP\IServerContainer;
|
use OCP\IRequest;
|
||||||
use OCP\IUser;
|
use OCP\Server;
|
||||||
use OCP\IUserManager;
|
use OCP\IUserManager;
|
||||||
use OCP\Notification\IManager as NotificationManager;
|
use OCP\Notification\IManager as NotificationManager;
|
||||||
use OCP\Share\IManager;
|
use OCP\Share\IManager;
|
||||||
|
use OCP\User\Events\UserDeletedEvent;
|
||||||
use OCP\Util;
|
use OCP\Util;
|
||||||
use Psr\Container\ContainerInterface;
|
use Psr\Container\ContainerInterface;
|
||||||
|
|
||||||
@@ -79,13 +82,16 @@ class Application extends App implements IBootstrap {
|
|||||||
|
|
||||||
public const COMMENT_ENTITY_TYPE = 'deckCard';
|
public const COMMENT_ENTITY_TYPE = 'deckCard';
|
||||||
|
|
||||||
/** @var IServerContainer */
|
|
||||||
private $server;
|
|
||||||
|
|
||||||
public function __construct(array $urlParams = []) {
|
public function __construct(array $urlParams = []) {
|
||||||
parent::__construct(self::APP_ID, $urlParams);
|
parent::__construct(self::APP_ID, $urlParams);
|
||||||
|
|
||||||
$this->server = \OC::$server;
|
// 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');
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function boot(IBootContext $context): void {
|
public function boot(IBootContext $context): void {
|
||||||
@@ -124,8 +130,12 @@ class Application extends App implements IBootstrap {
|
|||||||
$context->registerSearchProvider(CardCommentProvider::class);
|
$context->registerSearchProvider(CardCommentProvider::class);
|
||||||
$context->registerDashboardWidget(DeckWidget::class);
|
$context->registerDashboardWidget(DeckWidget::class);
|
||||||
|
|
||||||
|
// reference widget
|
||||||
|
$context->registerReferenceProvider(CardReferenceProvider::class);
|
||||||
|
// $context->registerEventListener(RenderReferenceEvent::class, CardReferenceListener::class);
|
||||||
|
|
||||||
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
|
$context->registerEventListener(BeforeTemplateRenderedEvent::class, BeforeTemplateRenderedListener::class);
|
||||||
|
|
||||||
// Event listening for full text search indexing
|
// Event listening for full text search indexing
|
||||||
$context->registerEventListener(CardCreatedEvent::class, FullTextSearchEventListener::class);
|
$context->registerEventListener(CardCreatedEvent::class, FullTextSearchEventListener::class);
|
||||||
$context->registerEventListener(CardUpdatedEvent::class, FullTextSearchEventListener::class);
|
$context->registerEventListener(CardUpdatedEvent::class, FullTextSearchEventListener::class);
|
||||||
@@ -141,33 +151,43 @@ class Application extends App implements IBootstrap {
|
|||||||
|
|
||||||
private function registerUserGroupHooks(IUserManager $userManager, IGroupManager $groupManager): void {
|
private function registerUserGroupHooks(IUserManager $userManager, IGroupManager $groupManager): void {
|
||||||
$container = $this->getContainer();
|
$container = $this->getContainer();
|
||||||
|
/** @var IEventDispatcher $eventDispatcher */
|
||||||
|
$eventDispatcher = $container->get(IEventDispatcher::class);
|
||||||
// Delete user/group acl entries when they get deleted
|
// Delete user/group acl entries when they get deleted
|
||||||
$userManager->listen('\OC\User', 'postDelete', static function (IUser $user) use ($container) {
|
$eventDispatcher->addListener(UserDeletedEvent::class, static function (Event $event) use ($container): void {
|
||||||
|
if (!($event instanceof UserDeletedEvent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$user = $event->getUser();
|
||||||
// delete existing acl entries for deleted user
|
// delete existing acl entries for deleted user
|
||||||
/** @var AclMapper $aclMapper */
|
/** @var AclMapper $aclMapper */
|
||||||
$aclMapper = $container->query(AclMapper::class);
|
$aclMapper = $container->get(AclMapper::class);
|
||||||
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_USER, $user->getUID());
|
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_USER, $user->getUID());
|
||||||
foreach ($acls as $acl) {
|
foreach ($acls as $acl) {
|
||||||
$aclMapper->delete($acl);
|
$aclMapper->delete($acl);
|
||||||
}
|
}
|
||||||
// delete existing user assignments
|
// delete existing user assignments
|
||||||
$assignmentMapper = $container->query(AssignmentMapper::class);
|
$assignmentMapper = $container->get(AssignmentMapper::class);
|
||||||
$assignments = $assignmentMapper->findByParticipant($user->getUID());
|
$assignments = $assignmentMapper->findByParticipant($user->getUID());
|
||||||
foreach ($assignments as $assignment) {
|
foreach ($assignments as $assignment) {
|
||||||
$assignmentMapper->delete($assignment);
|
$assignmentMapper->delete($assignment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var BoardMapper $boardMapper */
|
/** @var BoardMapper $boardMapper */
|
||||||
$boardMapper = $container->query(BoardMapper::class);
|
$boardMapper = $container->get(BoardMapper::class);
|
||||||
$boards = $boardMapper->findAllByOwner($user->getUID());
|
$boards = $boardMapper->findAllByOwner($user->getUID());
|
||||||
foreach ($boards as $board) {
|
foreach ($boards as $board) {
|
||||||
$boardMapper->delete($board);
|
$boardMapper->delete($board);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$groupManager->listen('\OC\Group', 'postDelete', static function (IGroup $group) use ($container) {
|
$eventDispatcher->addListener(GroupDeletedEvent::class, static function (Event $event) use ($container): void {
|
||||||
|
if (!($event instanceof GroupDeletedEvent)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$group = $event->getGroup();
|
||||||
/** @var AclMapper $aclMapper */
|
/** @var AclMapper $aclMapper */
|
||||||
$aclMapper = $container->query(AclMapper::class);
|
$aclMapper = $container->get(AclMapper::class);
|
||||||
$aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
|
$aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
|
||||||
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
|
$acls = $aclMapper->findByParticipant(Acl::PERMISSION_TYPE_GROUP, $group->getGID());
|
||||||
foreach ($acls as $acl) {
|
foreach ($acls as $acl) {
|
||||||
@@ -181,6 +201,7 @@ class Application extends App implements IBootstrap {
|
|||||||
$event->addEntityCollection(self::COMMENT_ENTITY_TYPE, function ($name) {
|
$event->addEntityCollection(self::COMMENT_ENTITY_TYPE, function ($name) {
|
||||||
/** @var CardMapper */
|
/** @var CardMapper */
|
||||||
$cardMapper = $this->getContainer()->get(CardMapper::class);
|
$cardMapper = $this->getContainer()->get(CardMapper::class);
|
||||||
|
/** @var PermissionService $permissionService */
|
||||||
$permissionService = $this->getContainer()->get(PermissionService::class);
|
$permissionService = $this->getContainer()->get(PermissionService::class);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -203,7 +224,7 @@ class Application extends App implements IBootstrap {
|
|||||||
$resourceManager->registerResourceProvider(ResourceProviderCard::class);
|
$resourceManager->registerResourceProvider(ResourceProviderCard::class);
|
||||||
|
|
||||||
$symfonyAdapter->addListener('\OCP\Collaboration\Resources::loadAdditionalScripts', static function () {
|
$symfonyAdapter->addListener('\OCP\Collaboration\Resources::loadAdditionalScripts', static function () {
|
||||||
if (strpos(\OC::$server->getRequest()->getPathInfo(), '/call/') === 0) {
|
if (strpos(Server::get(IRequest::class)->getPathInfo(), '/call/') === 0) {
|
||||||
// Talk integration has its own entrypoint which already includes collections handling
|
// Talk integration has its own entrypoint which already includes collections handling
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,20 +32,23 @@ use OCP\AppFramework\QueryException;
|
|||||||
use OCP\Collaboration\Resources\IManager;
|
use OCP\Collaboration\Resources\IManager;
|
||||||
use OCP\Collaboration\Resources\IProvider;
|
use OCP\Collaboration\Resources\IProvider;
|
||||||
use OCP\Collaboration\Resources\IResource;
|
use OCP\Collaboration\Resources\IResource;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
use OCP\IUser;
|
use OCP\IUser;
|
||||||
|
use OCP\Server;
|
||||||
|
|
||||||
class ResourceProvider implements IProvider {
|
class ResourceProvider implements IProvider {
|
||||||
public const RESOURCE_TYPE = 'deck';
|
public const RESOURCE_TYPE = 'deck';
|
||||||
|
|
||||||
private $boardMapper;
|
private BoardMapper $boardMapper;
|
||||||
private $permissionService;
|
private PermissionService $permissionService;
|
||||||
|
private IURLGenerator $urlGenerator;
|
||||||
|
|
||||||
/** @var array */
|
protected array $nodes = [];
|
||||||
protected $nodes = [];
|
|
||||||
|
|
||||||
public function __construct(BoardMapper $boardMapper, PermissionService $permissionService) {
|
public function __construct(BoardMapper $boardMapper, PermissionService $permissionService, IURLGenerator $urlGenerator) {
|
||||||
$this->boardMapper = $boardMapper;
|
$this->boardMapper = $boardMapper;
|
||||||
$this->permissionService = $permissionService;
|
$this->permissionService = $permissionService;
|
||||||
|
$this->urlGenerator = $urlGenerator;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -70,14 +73,14 @@ class ResourceProvider implements IProvider {
|
|||||||
*/
|
*/
|
||||||
public function getResourceRichObject(IResource $resource): array {
|
public function getResourceRichObject(IResource $resource): array {
|
||||||
$board = $this->getBoard($resource);
|
$board = $this->getBoard($resource);
|
||||||
$link = \OC::$server->getURLGenerator()->linkToRoute('deck.page.index') . '#/board/' . $resource->getId();
|
$link = $this->urlGenerator->linkToRoute('deck.page.index') . '#/board/' . $resource->getId();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'type' => self::RESOURCE_TYPE,
|
'type' => self::RESOURCE_TYPE,
|
||||||
'id' => $resource->getId(),
|
'id' => $resource->getId(),
|
||||||
'name' => $board->getTitle(),
|
'name' => $board->getTitle(),
|
||||||
'link' => $link,
|
'link' => $link,
|
||||||
'iconUrl' => \OC::$server->getURLGenerator()->imagePath('deck', 'deck-dark.svg')
|
'iconUrl' => $this->urlGenerator->imagePath('deck', 'deck-dark.svg')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +121,7 @@ class ResourceProvider implements IProvider {
|
|||||||
public function invalidateAccessCache($boardId = null) {
|
public function invalidateAccessCache($boardId = null) {
|
||||||
try {
|
try {
|
||||||
/** @var IManager $resourceManager */
|
/** @var IManager $resourceManager */
|
||||||
$resourceManager = \OC::$server->query(IManager::class);
|
$resourceManager = Server::get(IManager::class);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
}
|
}
|
||||||
if ($boardId !== null) {
|
if ($boardId !== null) {
|
||||||
|
|||||||
@@ -37,24 +37,16 @@ use OCP\Collaboration\Resources\IResource;
|
|||||||
use OCP\Collaboration\Resources\ResourceException;
|
use OCP\Collaboration\Resources\ResourceException;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
use OCP\IUser;
|
use OCP\IUser;
|
||||||
|
use OCP\Server;
|
||||||
|
|
||||||
class ResourceProviderCard implements IProvider {
|
class ResourceProviderCard implements IProvider {
|
||||||
public const RESOURCE_TYPE = 'deck-card';
|
public const RESOURCE_TYPE = 'deck-card';
|
||||||
|
|
||||||
/** @var CardMapper */
|
private CardMapper $cardMapper;
|
||||||
private $cardMapper;
|
private BoardMapper $boardMapper;
|
||||||
|
private PermissionService $permissionService;
|
||||||
/** @var BoardMapper */
|
private IURLGenerator $urlGenerator;
|
||||||
private $boardMapper;
|
protected array $nodes = [];
|
||||||
|
|
||||||
/** @var PermissionService */
|
|
||||||
private $permissionService;
|
|
||||||
|
|
||||||
/** @var IURLGenerator */
|
|
||||||
private $urlGenerator;
|
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
protected $nodes = [];
|
|
||||||
|
|
||||||
public function __construct(CardMapper $cardMapper, BoardMapper $boardMapper, PermissionService $permissionService, IURLGenerator $urlGenerator) {
|
public function __construct(CardMapper $cardMapper, BoardMapper $boardMapper, PermissionService $permissionService, IURLGenerator $urlGenerator) {
|
||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
@@ -147,7 +139,7 @@ class ResourceProviderCard implements IProvider {
|
|||||||
public function invalidateAccessCache($cardId = null) {
|
public function invalidateAccessCache($cardId = null) {
|
||||||
try {
|
try {
|
||||||
/** @var IManager $resourceManager */
|
/** @var IManager $resourceManager */
|
||||||
$resourceManager = \OC::$server->query(IManager::class);
|
$resourceManager = Server::get(IManager::class);
|
||||||
} catch (QueryException $e) {
|
} catch (QueryException $e) {
|
||||||
}
|
}
|
||||||
if ($cardId !== null) {
|
if ($cardId !== null) {
|
||||||
|
|||||||
@@ -30,8 +30,7 @@ use Symfony\Component\Console\Input\InputOption;
|
|||||||
use Symfony\Component\Console\Output\OutputInterface;
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
class BoardImport extends Command {
|
class BoardImport extends Command {
|
||||||
/** @var BoardImportCommandService */
|
private BoardImportCommandService $boardImportCommandService;
|
||||||
private $boardImportCommandService;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
BoardImportCommandService $boardImportCommandService
|
BoardImportCommandService $boardImportCommandService
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use OCA\Deck\Db\AssignmentMapper;
|
|||||||
use OCA\Deck\Db\BoardMapper;
|
use OCA\Deck\Db\BoardMapper;
|
||||||
use OCA\Deck\Db\CardMapper;
|
use OCA\Deck\Db\CardMapper;
|
||||||
use OCA\Deck\Db\StackMapper;
|
use OCA\Deck\Db\StackMapper;
|
||||||
|
use OCA\Deck\Model\CardDetails;
|
||||||
use OCA\Deck\Service\BoardService;
|
use OCA\Deck\Service\BoardService;
|
||||||
use OCP\AppFramework\Db\DoesNotExistException;
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
@@ -101,7 +102,9 @@ class UserExport extends Command {
|
|||||||
$fullCard = $this->cardMapper->find($card->getId());
|
$fullCard = $this->cardMapper->find($card->getId());
|
||||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||||
$fullCard->setAssignedUsers($assignedUsers);
|
$fullCard->setAssignedUsers($assignedUsers);
|
||||||
$data[$board->getId()]['stacks'][$stack->getId()]['cards'][] = (array)$fullCard->jsonSerialize();
|
|
||||||
|
$cardDetails = new CardDetails($fullCard, $fullBoard);
|
||||||
|
$data[$board->getId()]['stacks'][$stack->getId()]['cards'][] = $cardDetails->jsonSerialize();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ use OCA\Deck\Service\PermissionService;
|
|||||||
use OCA\Files\Event\LoadSidebar;
|
use OCA\Files\Event\LoadSidebar;
|
||||||
use OCA\Viewer\Event\LoadViewer;
|
use OCA\Viewer\Event\LoadViewer;
|
||||||
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
use OCP\AppFramework\Http\ContentSecurityPolicy;
|
||||||
|
use OCP\Collaboration\Resources\LoadAdditionalScriptsEvent as CollaborationResourcesEvent;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
|
use OCP\IConfig;
|
||||||
use OCP\IInitialStateService;
|
use OCP\IInitialStateService;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
use OCP\AppFramework\Http\TemplateResponse;
|
use OCP\AppFramework\Http\TemplateResponse;
|
||||||
@@ -41,16 +43,17 @@ use OCA\Deck\Db\Acl;
|
|||||||
use OCA\Deck\Service\CardService;
|
use OCA\Deck\Service\CardService;
|
||||||
|
|
||||||
class PageController extends Controller {
|
class PageController extends Controller {
|
||||||
private $permissionService;
|
private PermissionService $permissionService;
|
||||||
private $initialState;
|
private IInitialStateService $initialState;
|
||||||
private $configService;
|
private ConfigService $configService;
|
||||||
private $eventDispatcher;
|
private IEventDispatcher $eventDispatcher;
|
||||||
private $cardMapper;
|
private CardMapper $cardMapper;
|
||||||
private $urlGenerator;
|
private IURLGenerator $urlGenerator;
|
||||||
private $cardService;
|
private CardService $cardService;
|
||||||
|
private IConfig $config;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
$AppName,
|
string $AppName,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
PermissionService $permissionService,
|
PermissionService $permissionService,
|
||||||
IInitialStateService $initialStateService,
|
IInitialStateService $initialStateService,
|
||||||
@@ -58,7 +61,8 @@ class PageController extends Controller {
|
|||||||
IEventDispatcher $eventDispatcher,
|
IEventDispatcher $eventDispatcher,
|
||||||
CardMapper $cardMapper,
|
CardMapper $cardMapper,
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
CardService $cardService
|
CardService $cardService,
|
||||||
|
IConfig $config
|
||||||
) {
|
) {
|
||||||
parent::__construct($AppName, $request);
|
parent::__construct($AppName, $request);
|
||||||
|
|
||||||
@@ -69,6 +73,7 @@ class PageController extends Controller {
|
|||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
$this->cardService = $cardService;
|
$this->cardService = $cardService;
|
||||||
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,13 +89,17 @@ class PageController extends Controller {
|
|||||||
$this->initialState->provideInitialState(Application::APP_ID, 'config', $this->configService->getAll());
|
$this->initialState->provideInitialState(Application::APP_ID, 'config', $this->configService->getAll());
|
||||||
|
|
||||||
$this->eventDispatcher->dispatchTyped(new LoadSidebar());
|
$this->eventDispatcher->dispatchTyped(new LoadSidebar());
|
||||||
|
$this->eventDispatcher->dispatchTyped(new CollaborationResourcesEvent());
|
||||||
if (class_exists(LoadViewer::class)) {
|
if (class_exists(LoadViewer::class)) {
|
||||||
$this->eventDispatcher->dispatchTyped(new LoadViewer());
|
$this->eventDispatcher->dispatchTyped(new LoadViewer());
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = new TemplateResponse('deck', 'main');
|
$response = new TemplateResponse('deck', 'main', [
|
||||||
|
'id-app-content' => '#app-content-vue',
|
||||||
|
'id-app-navigation' => '#app-navigation-vue',
|
||||||
|
]);
|
||||||
|
|
||||||
if (\OC::$server->getConfig()->getSystemValueBool('debug', false)) {
|
if ($this->config->getSystemValueBool('debug', false)) {
|
||||||
$csp = new ContentSecurityPolicy();
|
$csp = new ContentSecurityPolicy();
|
||||||
$csp->addAllowedConnectDomain('*');
|
$csp->addAllowedConnectDomain('*');
|
||||||
$csp->addAllowedScriptDomain('*');
|
$csp->addAllowedScriptDomain('*');
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ declare(strict_types=1);
|
|||||||
namespace OCA\Deck\Controller;
|
namespace OCA\Deck\Controller;
|
||||||
|
|
||||||
use OCA\Deck\Db\Card;
|
use OCA\Deck\Db\Card;
|
||||||
|
use OCA\Deck\Model\CardDetails;
|
||||||
use OCA\Deck\Service\SearchService;
|
use OCA\Deck\Service\SearchService;
|
||||||
use OCP\AppFramework\Http\DataResponse;
|
use OCP\AppFramework\Http\DataResponse;
|
||||||
use OCP\AppFramework\OCSController;
|
use OCP\AppFramework\OCSController;
|
||||||
@@ -50,9 +51,12 @@ class SearchController extends OCSController {
|
|||||||
public function search(string $term, ?int $limit = null, ?int $cursor = null): DataResponse {
|
public function search(string $term, ?int $limit = null, ?int $cursor = null): DataResponse {
|
||||||
$cards = $this->searchService->searchCards($term, $limit, $cursor);
|
$cards = $this->searchService->searchCards($term, $limit, $cursor);
|
||||||
return new DataResponse(array_map(function (Card $card) {
|
return new DataResponse(array_map(function (Card $card) {
|
||||||
$json = $card->jsonSerialize();
|
$board = $card->getRelatedBoard();
|
||||||
|
$json = (new CardDetails($card, $board))->jsonSerialize();
|
||||||
|
|
||||||
|
$json['relatedBoard'] = $board;
|
||||||
$json['relatedStack'] = $card->getRelatedStack();
|
$json['relatedStack'] = $card->getRelatedStack();
|
||||||
$json['relatedBoard'] = $card->getRelatedBoard();
|
|
||||||
return $json;
|
return $json;
|
||||||
}, $cards));
|
}, $cards));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,20 +59,21 @@ class Calendar extends ExternalCalendar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getACL() {
|
public function getACL() {
|
||||||
|
// the calendar should always have the read and the write-properties permissions
|
||||||
|
// write-properties is needed to allow the user to toggle the visibility of shared deck calendars
|
||||||
$acl = [
|
$acl = [
|
||||||
[
|
[
|
||||||
'privilege' => '{DAV:}read',
|
'privilege' => '{DAV:}read',
|
||||||
'principal' => $this->getOwner(),
|
'principal' => $this->getOwner(),
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
]
|
],
|
||||||
];
|
[
|
||||||
if ($this->backend->checkBoardPermission($this->board->getId(), Acl::PERMISSION_MANAGE)) {
|
|
||||||
$acl[] = [
|
|
||||||
'privilege' => '{DAV:}write-properties',
|
'privilege' => '{DAV:}write-properties',
|
||||||
'principal' => $this->getOwner(),
|
'principal' => $this->getOwner(),
|
||||||
'protected' => true,
|
'protected' => true,
|
||||||
];
|
]
|
||||||
}
|
];
|
||||||
|
|
||||||
return $acl;
|
return $acl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,12 +188,18 @@ class Calendar extends ExternalCalendar {
|
|||||||
foreach ($properties as $key => $value) {
|
foreach ($properties as $key => $value) {
|
||||||
switch ($key) {
|
switch ($key) {
|
||||||
case '{DAV:}displayname':
|
case '{DAV:}displayname':
|
||||||
|
if (!$this->backend->checkBoardPermission($this->board->getId(), Acl::PERMISSION_MANAGE)) {
|
||||||
|
throw new Forbidden('no permission to change the displayname');
|
||||||
|
}
|
||||||
if (mb_strpos($value, 'Deck: ') === 0) {
|
if (mb_strpos($value, 'Deck: ') === 0) {
|
||||||
$value = mb_substr($value, strlen('Deck: '));
|
$value = mb_substr($value, strlen('Deck: '));
|
||||||
}
|
}
|
||||||
$this->board->setTitle($value);
|
$this->board->setTitle($value);
|
||||||
break;
|
break;
|
||||||
case '{http://apple.com/ns/ical/}calendar-color':
|
case '{http://apple.com/ns/ical/}calendar-color':
|
||||||
|
if (!$this->backend->checkBoardPermission($this->board->getId(), Acl::PERMISSION_MANAGE)) {
|
||||||
|
throw new Forbidden('no permission to change the calendar color');
|
||||||
|
}
|
||||||
$color = substr($value, 1, 6);
|
$color = substr($value, 1, 6);
|
||||||
if (!preg_match('/[a-f0-9]{6}/i', $color)) {
|
if (!preg_match('/[a-f0-9]{6}/i', $color)) {
|
||||||
throw new InvalidDataException('No valid color provided');
|
throw new InvalidDataException('No valid color provided');
|
||||||
|
|||||||
@@ -26,18 +26,34 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace OCA\Deck\Dashboard;
|
namespace OCA\Deck\Dashboard;
|
||||||
|
|
||||||
use OCP\Dashboard\IWidget;
|
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\IL10N;
|
use OCP\IL10N;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
|
use OCP\Util;
|
||||||
|
|
||||||
class DeckWidget implements IWidget {
|
class DeckWidget implements IAPIWidget, IButtonWidget, IIconWidget {
|
||||||
|
private IL10N $l10n;
|
||||||
|
private OverviewService $dashboardService;
|
||||||
|
private IURLGenerator $urlGenerator;
|
||||||
|
private IDateTimeFormatter $dateTimeFormatter;
|
||||||
|
|
||||||
/**
|
public function __construct(IL10N $l10n,
|
||||||
* @var IL10N
|
OverviewService $dashboardService,
|
||||||
*/
|
IDateTimeFormatter $dateTimeFormatter,
|
||||||
private $l10n;
|
IURLGenerator $urlGenerator) {
|
||||||
|
|
||||||
public function __construct(IL10N $l10n) {
|
|
||||||
$this->l10n = $l10n;
|
$this->l10n = $l10n;
|
||||||
|
$this->dashboardService = $dashboardService;
|
||||||
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
$this->dateTimeFormatter = $dateTimeFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,17 +84,88 @@ class DeckWidget implements IWidget {
|
|||||||
return 'icon-deck';
|
return 'icon-deck';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getIconUrl(): string {
|
||||||
|
return $this->urlGenerator->getAbsoluteURL(
|
||||||
|
$this->urlGenerator->imagePath(Application::APP_ID, 'deck-dark.svg')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function getUrl(): ?string {
|
public function getUrl(): ?string {
|
||||||
return null;
|
return $this->urlGenerator->getAbsoluteURL(
|
||||||
|
$this->urlGenerator->linkToRoute(Application::APP_ID . '.page.index')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
public function load(): void {
|
public function load(): void {
|
||||||
\OCP\Util::addScript('deck', 'deck-dashboard');
|
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')
|
||||||
|
),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,18 +33,46 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
parent::__construct($db, 'deck_board_acl', Acl::class);
|
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) {
|
public function findAll($boardId, $limit = null, $offset = null) {
|
||||||
$sql = 'SELECT id, board_id, type, participant, permission_edit, permission_share, permission_manage FROM `*PREFIX*deck_board_acl` WHERE `board_id` = ? ';
|
$qb = $this->db->getQueryBuilder();
|
||||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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 = ?)';
|
* @param numeric $userId
|
||||||
$stmt = $this->execute($sql, [$aclId]);
|
* @param numeric $id
|
||||||
$row = $stmt->fetch();
|
* @return bool
|
||||||
return ($row['owner'] === $userId);
|
* @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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $id
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
public function findBoardId($id): ?int {
|
public function findBoardId($id): ?int {
|
||||||
try {
|
try {
|
||||||
$entity = $this->find($id);
|
$entity = $this->find($id);
|
||||||
@@ -54,9 +82,21 @@ class AclMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $type
|
||||||
|
* @param string $participant
|
||||||
|
* @return Acl[]
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
public function findByParticipant($type, $participant): array {
|
public function findByParticipant($type, $participant): array {
|
||||||
$sql = 'SELECT * from *PREFIX*deck_board_acl WHERE type = ? AND participant = ?';
|
$qb = $this->db->getQueryBuilder();
|
||||||
return $this->findEntities($sql, [$type, $participant]);
|
|
||||||
|
$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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -55,9 +55,6 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
|
|||||||
$this->circleService = $circleService;
|
$this->circleService = $circleService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Assignment[]
|
|
||||||
*/
|
|
||||||
public function findAll(int $cardId): array {
|
public function findAll(int $cardId): array {
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
$qb->select('*')
|
$qb->select('*')
|
||||||
@@ -80,8 +77,8 @@ class AssignmentMapper extends QBMapper implements IPermissionMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function isOwner($userId, $cardId): bool {
|
public function isOwner($userId, $id): bool {
|
||||||
return $this->cardMapper->isOwner($userId, $cardId);
|
return $this->cardMapper->isOwner($userId, $id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findBoardId($id): ?int {
|
public function findBoardId($id): ?int {
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
|||||||
use OCP\DB\QueryBuilder\IQueryBuilder;
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
use OCP\IDBConnection;
|
use OCP\IDBConnection;
|
||||||
use OCP\IUserManager;
|
use OCP\IUserManager;
|
||||||
use PDO;
|
|
||||||
|
|
||||||
class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
||||||
private $cardMapper;
|
private $cardMapper;
|
||||||
@@ -52,70 +51,53 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $id
|
* @param int $id
|
||||||
* @return Entity|Attachment
|
* @return Attachment
|
||||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
* @throws DoesNotExistException
|
||||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
* @throws MultipleObjectsReturnedException
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
*/
|
*/
|
||||||
public function find($id) {
|
public function find($id) {
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
$qb->select('*')
|
$qb->select('*')
|
||||||
->from('deck_attachment')
|
->from($this->getTableName())
|
||||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
||||||
|
|
||||||
$cursor = $qb->execute();
|
return $this->findEntity($qb);
|
||||||
$row = $cursor->fetch(PDO::FETCH_ASSOC);
|
}
|
||||||
if ($row === false) {
|
|
||||||
$cursor->closeCursor();
|
/**
|
||||||
throw new DoesNotExistException('Did expect one result but found none when executing query: ' . $qb->getSQL());
|
* @param int $cardId
|
||||||
}
|
* @param string $data
|
||||||
|
* @return Attachment
|
||||||
$row2 = $cursor->fetch();
|
* @throws DoesNotExistException
|
||||||
$cursor->closeCursor();
|
* @throws MultipleObjectsReturnedException
|
||||||
if ($row2 !== false) {
|
* @throws \OCP\DB\Exception
|
||||||
throw new MultipleObjectsReturnedException('Did not expect more than one result when executing query: ' . $qb->getSQL());
|
*/
|
||||||
}
|
public function findByData($cardId, $data) {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
return $this->mapRowToEntity($row);
|
$qb->select('*')
|
||||||
}
|
->from($this->getTableName())
|
||||||
|
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
|
||||||
public function findByData($cardId, $data) {
|
->andWhere($qb->expr()->eq('data', $qb->createNamedParameter($data, IQueryBuilder::PARAM_STR)));
|
||||||
$qb = $this->db->getQueryBuilder();
|
|
||||||
$qb->select('*')
|
return $this->findEntity($qb);
|
||||||
->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)));
|
|
||||||
$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 query: ' . $qb->getSQL());
|
|
||||||
}
|
|
||||||
$cursor->closeCursor();
|
|
||||||
return $this->mapRowToEntity($row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all attachments for a card
|
|
||||||
*
|
|
||||||
* @param $cardId
|
* @param $cardId
|
||||||
* @return array
|
* @return Entity[]
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
*/
|
*/
|
||||||
public function findAll($cardId) {
|
public function findAll($cardId) {
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
$qb->select('*')
|
$qb->select('*')
|
||||||
->from('deck_attachment')
|
->from($this->getTableName())
|
||||||
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
|
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
|
||||||
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
|
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
|
||||||
|
|
||||||
|
|
||||||
$entities = [];
|
return $this->findEntities($qb);
|
||||||
$cursor = $qb->execute();
|
|
||||||
while ($row = $cursor->fetch()) {
|
|
||||||
$entities[] = $this->mapRowToEntity($row);
|
|
||||||
}
|
|
||||||
$cursor->closeCursor();
|
|
||||||
return $entities;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -128,7 +110,7 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
$timeLimit = time() - (60 * 5);
|
$timeLimit = time() - (60 * 5);
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
$qb->select('*')
|
$qb->select('*')
|
||||||
->from('deck_attachment')
|
->from($this->getTableName())
|
||||||
->where($qb->expr()->gt('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
|
->where($qb->expr()->gt('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)));
|
||||||
if ($withOffset) {
|
if ($withOffset) {
|
||||||
$qb
|
$qb
|
||||||
@@ -139,13 +121,7 @@ class AttachmentMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
->andWhere($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
|
->andWhere($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
|
||||||
}
|
}
|
||||||
|
|
||||||
$entities = [];
|
return $this->findEntities($qb);
|
||||||
$cursor = $qb->execute();
|
|
||||||
while ($row = $cursor->fetch()) {
|
|
||||||
$entities[] = $this->mapRowToEntity($row);
|
|
||||||
}
|
|
||||||
$cursor->closeCursor();
|
|
||||||
return $entities;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,14 @@
|
|||||||
|
|
||||||
namespace OCA\Deck\Db;
|
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 {
|
class Board extends RelationalEntity {
|
||||||
protected $title;
|
protected $title;
|
||||||
protected $owner;
|
protected $owner;
|
||||||
|
|||||||
@@ -42,9 +42,9 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
|||||||
private $circlesService;
|
private $circlesService;
|
||||||
private $logger;
|
private $logger;
|
||||||
|
|
||||||
/** @var CappedMemoryCache */
|
/** @var CappedMemoryCache<Board[]> */
|
||||||
private $userBoardCache;
|
private $userBoardCache;
|
||||||
/** @var CappedMemoryCache */
|
/** @var CappedMemoryCache<Board> */
|
||||||
private $boardCache;
|
private $boardCache;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -107,6 +107,47 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
|||||||
return $this->boardCache[$id];
|
return $this->boardCache[$id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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,
|
public function findAllForUser(string $userId, ?int $since = null, bool $includeArchived = true, ?int $before = null,
|
||||||
?string $term = null): array {
|
?string $term = null): array {
|
||||||
$useCache = ($since === -1 && $includeArchived === true && $before === null && $term === null);
|
$useCache = ($since === -1 && $includeArchived === true && $before === null && $term === null);
|
||||||
@@ -131,14 +172,9 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all boards for a given user
|
* 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,
|
public function findAllByUser(string $userId, ?int $limit = null, ?int $offset = null, ?int $since = null,
|
||||||
bool $includeArchived = true, ?int $before = null, ?string $term = 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
|
// 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?
|
// Is it possible with the query builder?
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
@@ -247,15 +283,9 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Find all boards for a given user
|
* 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,
|
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) {
|
bool $includeArchived = true, ?int $before = null, ?string $term = null): array {
|
||||||
if (count($groups) <= 0) {
|
if (count($groups) <= 0) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
@@ -414,8 +444,8 @@ class BoardMapper extends QBMapper implements IPermissionMapper {
|
|||||||
return parent::delete($entity);
|
return parent::delete($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isOwner($userId, $boardId): bool {
|
public function isOwner($userId, $id): bool {
|
||||||
$board = $this->find($boardId);
|
$board = $this->find($id);
|
||||||
return ($board->getOwner() === $userId);
|
return ($board->getOwner() === $userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,44 @@ use DateTime;
|
|||||||
use DateTimeZone;
|
use DateTimeZone;
|
||||||
use Sabre\VObject\Component\VCalendar;
|
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 {
|
class Card extends RelationalEntity {
|
||||||
public const TITLE_MAX_LENGTH = 255;
|
public const TITLE_MAX_LENGTH = 255;
|
||||||
|
|
||||||
@@ -70,6 +108,7 @@ class Card extends RelationalEntity {
|
|||||||
$this->addType('archived', 'boolean');
|
$this->addType('archived', 'boolean');
|
||||||
$this->addType('notified', 'boolean');
|
$this->addType('notified', 'boolean');
|
||||||
$this->addType('deletedAt', 'integer');
|
$this->addType('deletedAt', 'integer');
|
||||||
|
$this->addType('duedate', 'datetime');
|
||||||
$this->addRelation('labels');
|
$this->addRelation('labels');
|
||||||
$this->addRelation('assignedUsers');
|
$this->addRelation('assignedUsers');
|
||||||
$this->addRelation('attachments');
|
$this->addRelation('attachments');
|
||||||
@@ -87,50 +126,6 @@ class Card extends RelationalEntity {
|
|||||||
$this->databaseType = $type;
|
$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(): array {
|
|
||||||
$json = parent::jsonSerialize();
|
|
||||||
$json['overdue'] = self::DUEDATE_FUTURE;
|
|
||||||
$due = $this->duedate ? strtotime($this->duedate) : false;
|
|
||||||
if ($due !== false) {
|
|
||||||
$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 ($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 {
|
public function getCalendarObject(): VCalendar {
|
||||||
$calendar = new VCalendar();
|
$calendar = new VCalendar();
|
||||||
$event = $calendar->createComponent('VTODO');
|
$event = $calendar->createComponent('VTODO');
|
||||||
@@ -139,7 +134,7 @@ class Card extends RelationalEntity {
|
|||||||
$creationDate = new DateTime();
|
$creationDate = new DateTime();
|
||||||
$creationDate->setTimestamp($this->createdAt);
|
$creationDate->setTimestamp($this->createdAt);
|
||||||
$event->DTSTAMP = $creationDate;
|
$event->DTSTAMP = $creationDate;
|
||||||
$event->DUE = new DateTime($this->getDuedate(true), new DateTimeZone('UTC'));
|
$event->DUE = new DateTime($this->getDuedate()->format('c'), new DateTimeZone('UTC'));
|
||||||
}
|
}
|
||||||
$event->add('RELATED-TO', 'deck-stack-' . $this->getStackId());
|
$event->add('RELATED-TO', 'deck-stack-' . $this->getStackId());
|
||||||
|
|
||||||
|
|||||||
@@ -238,6 +238,21 @@ class CardMapper extends QBMapper implements IPermissionMapper {
|
|||||||
return $this->findEntities($qb);
|
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) {
|
public function findAllWithDue($boardId) {
|
||||||
$qb = $this->db->getQueryBuilder();
|
$qb = $this->db->getQueryBuilder();
|
||||||
$qb->select('c.*')
|
$qb->select('c.*')
|
||||||
@@ -560,10 +575,10 @@ class CardMapper extends QBMapper implements IPermissionMapper {
|
|||||||
$qb->execute();
|
$qb->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isOwner($userId, $cardId): bool {
|
public function isOwner($userId, $id): 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 = ?))';
|
$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 = $this->db->prepare($sql);
|
||||||
$stmt->bindParam(1, $cardId, \PDO::PARAM_INT, 0);
|
$stmt->bindParam(1, $id, \PDO::PARAM_INT, 0);
|
||||||
$stmt->execute();
|
$stmt->execute();
|
||||||
$row = $stmt->fetch();
|
$row = $stmt->fetch();
|
||||||
return ($row['owner'] === $userId);
|
return ($row['owner'] === $userId);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
namespace OCA\Deck\Db;
|
namespace OCA\Deck\Db;
|
||||||
|
|
||||||
use OCP\ICacheFactory;
|
use OCP\ICacheFactory;
|
||||||
|
use OCP\ICache;
|
||||||
use OCP\IDBConnection;
|
use OCP\IDBConnection;
|
||||||
use OCP\IRequest;
|
use OCP\IRequest;
|
||||||
|
|
||||||
@@ -31,13 +32,16 @@ class ChangeHelper {
|
|||||||
public const TYPE_BOARD = 'boardChanged';
|
public const TYPE_BOARD = 'boardChanged';
|
||||||
public const TYPE_CARD = 'cardChanged';
|
public const TYPE_CARD = 'cardChanged';
|
||||||
|
|
||||||
private $db;
|
private IDBConnection $db;
|
||||||
|
private ICache $cache;
|
||||||
|
private IRequest $request;
|
||||||
|
private ?string $userId;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IDBConnection $db,
|
IDBConnection $db,
|
||||||
ICacheFactory $cacheFactory,
|
ICacheFactory $cacheFactory,
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
$userId
|
?string $userId
|
||||||
) {
|
) {
|
||||||
$this->db = $db;
|
$this->db = $db;
|
||||||
$this->cache = $cacheFactory->createDistributed('deck_changes');
|
$this->cache = $cacheFactory->createDistributed('deck_changes');
|
||||||
|
|||||||
@@ -23,17 +23,15 @@
|
|||||||
|
|
||||||
namespace OCA\Deck\Db;
|
namespace OCA\Deck\Db;
|
||||||
|
|
||||||
use OCP\AppFramework\Db\Mapper;
|
use OCP\AppFramework\Db\QBMapper;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class DeckMapper
|
* Class DeckMapper
|
||||||
*
|
*
|
||||||
* @package OCA\Deck\Db
|
* @package OCA\Deck\Db
|
||||||
* @deprecated use QBMapper
|
|
||||||
*
|
|
||||||
* TODO: Move to QBMapper once Nextcloud 14 is a minimum requirement
|
|
||||||
*/
|
*/
|
||||||
class DeckMapper extends Mapper {
|
class DeckMapper extends QBMapper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $id
|
* @param $id
|
||||||
@@ -42,11 +40,11 @@ class DeckMapper extends Mapper {
|
|||||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||||
*/
|
*/
|
||||||
public function find($id) {
|
public function find($id) {
|
||||||
$sql = 'SELECT * FROM `' . $this->tableName . '` ' . 'WHERE `id` = ?';
|
$qb = $this->db->getQueryBuilder();
|
||||||
return $this->findEntity($sql, [$id]);
|
$qb->select('*')
|
||||||
}
|
->from($this->getTableName())
|
||||||
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
||||||
|
|
||||||
protected function execute($sql, array $params = [], $limit = null, $offset = null) {
|
return $this->findEntity($qb);
|
||||||
return parent::execute($sql, $params, $limit, $offset);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace OCA\Deck\Db;
|
|||||||
use OCP\AppFramework\Db\DoesNotExistException;
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
use OCP\AppFramework\Db\Entity;
|
use OCP\AppFramework\Db\Entity;
|
||||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
use OCP\IDBConnection;
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
class LabelMapper extends DeckMapper implements IPermissionMapper {
|
class LabelMapper extends DeckMapper implements IPermissionMapper {
|
||||||
@@ -33,41 +34,105 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
parent::__construct($db, 'deck_labels', Label::class);
|
parent::__construct($db, 'deck_labels', Label::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findAll($boardId, $limit = null, $offset = null) {
|
/**
|
||||||
$sql = 'SELECT * FROM `*PREFIX*deck_labels` WHERE `board_id` = ? ORDER BY `id`';
|
* @param numeric $boardId
|
||||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return Label[]
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function findAll($boardId, $limit = null, $offset = null): array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('*')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||||
|
->setMaxResults($limit)
|
||||||
|
->setFirstResult($offset);
|
||||||
|
return $this->findEntities($qb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete(\OCP\AppFramework\Db\Entity $entity) {
|
/**
|
||||||
|
* @param Entity $entity
|
||||||
|
* @return Entity
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function delete(Entity $entity): Entity {
|
||||||
// delete assigned labels
|
// delete assigned labels
|
||||||
$this->deleteLabelAssignments($entity->getId());
|
$this->deleteLabelAssignments($entity->getId());
|
||||||
// delete label
|
// delete label
|
||||||
return parent::delete($entity);
|
return parent::delete($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null) {
|
/**
|
||||||
$sql = 'SELECT l.*,card_id FROM `*PREFIX*deck_assigned_labels` as al INNER JOIN *PREFIX*deck_labels as l ON l.id = al.label_id WHERE `card_id` = ? ORDER BY l.id';
|
* @param numeric $cardId
|
||||||
return $this->findEntities($sql, [$cardId], $limit, $offset);
|
* @param int|null $limit
|
||||||
}
|
* @param int|null $offset
|
||||||
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null) {
|
* @return Label[]
|
||||||
$sql = 'SELECT c.id as card_id, l.id as id, l.title as title, l.color as color FROM `*PREFIX*deck_cards` as c ' .
|
* @throws \OCP\DB\Exception
|
||||||
' INNER JOIN `*PREFIX*deck_assigned_labels` as al ON al.card_id = c.id INNER JOIN `*PREFIX*deck_labels` as l ON al.label_id = l.id WHERE board_id=? ORDER BY l.id';
|
*/
|
||||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
public function findAssignedLabelsForCard($cardId, $limit = null, $offset = null): array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('l.*', 'card_id')
|
||||||
|
->from($this->getTableName(), 'l')
|
||||||
|
->innerJoin('l', 'deck_assigned_labels', 'al', 'l.id = al.label_id')
|
||||||
|
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)))
|
||||||
|
->orderBy('l.id')
|
||||||
|
->setMaxResults($limit)
|
||||||
|
->setFirstResult($offset);
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function insert(Entity $entity) {
|
/**
|
||||||
|
* @param numeric $boardId
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return Label[]
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function findAssignedLabelsForBoard($boardId, $limit = null, $offset = null): array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('l.id as id', 'l.title as title', 'l.color as color')
|
||||||
|
->selectAlias('c.id', 'card_id')
|
||||||
|
->from($this->getTableName(), 'l')
|
||||||
|
->innerJoin('l', 'deck_assigned_labels', 'al', 'al.label_id = l.id')
|
||||||
|
->innerJoin('l', 'deck_cards', 'c', 'al.card_id = c.id')
|
||||||
|
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||||
|
->orderBy('l.id')
|
||||||
|
->setMaxResults($limit)
|
||||||
|
->setFirstResult($offset);
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Entity $entity
|
||||||
|
* @return Entity
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function insert(Entity $entity): Entity {
|
||||||
$entity->setLastModified(time());
|
$entity->setLastModified(time());
|
||||||
return parent::insert($entity);
|
return parent::insert($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(Entity $entity, $updateModified = true) {
|
/**
|
||||||
|
* @param Entity $entity
|
||||||
|
* @param bool $updateModified
|
||||||
|
* @return Entity
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function update(Entity $entity, $updateModified = true): Entity {
|
||||||
if ($updateModified) {
|
if ($updateModified) {
|
||||||
$entity->setLastModified(time());
|
$entity->setLastModified(time());
|
||||||
}
|
}
|
||||||
return parent::update($entity);
|
return parent::update($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $boardId
|
||||||
|
* @return array
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
public function getAssignedLabelsForBoard($boardId) {
|
public function getAssignedLabelsForBoard($boardId) {
|
||||||
$labels = $this->findAssignedLabelsForBoard($boardId);
|
$labels = $this->findAssignedLabelsForBoard($boardId);
|
||||||
$result = [];
|
$result = [];
|
||||||
@@ -80,27 +145,51 @@ class LabelMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $labelId
|
||||||
|
* @return void
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
public function deleteLabelAssignments($labelId) {
|
public function deleteLabelAssignments($labelId) {
|
||||||
$sql = 'DELETE FROM `*PREFIX*deck_assigned_labels` WHERE label_id = ?';
|
$qb = $this->db->getQueryBuilder();
|
||||||
$stmt = $this->db->prepare($sql);
|
$qb->delete('deck_assigned_labels')
|
||||||
$stmt->bindParam(1, $labelId, \PDO::PARAM_INT, 0);
|
->where($qb->expr()->eq('label_id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)));
|
||||||
$stmt->execute();
|
$qb->executeStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $cardId
|
||||||
|
* @return void
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
public function deleteLabelAssignmentsForCard($cardId) {
|
public function deleteLabelAssignmentsForCard($cardId) {
|
||||||
$sql = 'DELETE FROM `*PREFIX*deck_assigned_labels` WHERE card_id = ?';
|
$qb = $this->db->getQueryBuilder();
|
||||||
$stmt = $this->db->prepare($sql);
|
$qb->delete('deck_assigned_labels')
|
||||||
$stmt->bindParam(1, $cardId, \PDO::PARAM_INT, 0);
|
->where($qb->expr()->eq('card_id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
|
||||||
$stmt->execute();
|
$qb->executeStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $userId
|
||||||
|
* @param numeric $labelId
|
||||||
|
* @return bool
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
public function isOwner($userId, $labelId): bool {
|
public function isOwner($userId, $labelId): bool {
|
||||||
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_labels` WHERE id = ?)';
|
$qb = $this->db->getQueryBuilder();
|
||||||
$stmt = $this->execute($sql, [$labelId]);
|
$qb->select('l.id')
|
||||||
$row = $stmt->fetch();
|
->from($this->getTableName(), 'l')
|
||||||
return ($row['owner'] === $userId);
|
->innerJoin('l', 'deck_boards', 'b', 'l.board_id = b.id')
|
||||||
|
->where($qb->expr()->eq('l.id', $qb->createNamedParameter($labelId, IQueryBuilder::PARAM_INT)))
|
||||||
|
->andWhere($qb->expr()->eq('b.owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
|
||||||
|
|
||||||
|
return count($qb->executeQuery()->fetchAll()) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $id
|
||||||
|
* @return int|null
|
||||||
|
*/
|
||||||
public function findBoardId($id): ?int {
|
public function findBoardId($id): ?int {
|
||||||
try {
|
try {
|
||||||
$entity = $this->find($id);
|
$entity = $this->find($id);
|
||||||
|
|||||||
@@ -72,6 +72,9 @@ class RelationalEntity extends Entity implements \JsonSerializable {
|
|||||||
$propertyReflection = $reflection->getProperty($property);
|
$propertyReflection = $reflection->getProperty($property);
|
||||||
if (!$propertyReflection->isPrivate() && !in_array($property, $this->_resolvedProperties, true)) {
|
if (!$propertyReflection->isPrivate() && !in_array($property, $this->_resolvedProperties, true)) {
|
||||||
$json[$property] = $this->getter($property);
|
$json[$property] = $this->getter($property);
|
||||||
|
if ($json[$property] instanceof \DateTimeInterface) {
|
||||||
|
$json[$property] = $json[$property]->format('c');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ namespace OCA\Deck\Db;
|
|||||||
use OCP\AppFramework\Db\DoesNotExistException;
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
use OCP\AppFramework\Db\Entity;
|
use OCP\AppFramework\Db\Entity;
|
||||||
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
use OCP\AppFramework\Db\MultipleObjectsReturnedException;
|
||||||
|
use OCP\DB\QueryBuilder\IQueryBuilder;
|
||||||
use OCP\IDBConnection;
|
use OCP\IDBConnection;
|
||||||
|
|
||||||
class StackMapper extends DeckMapper implements IPermissionMapper {
|
class StackMapper extends DeckMapper implements IPermissionMapper {
|
||||||
@@ -38,62 +39,112 @@ class StackMapper extends DeckMapper implements IPermissionMapper {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $id
|
* @param numeric $id
|
||||||
* @throws MultipleObjectsReturnedException
|
* @return Stack
|
||||||
* @throws DoesNotExistException
|
* @throws DoesNotExistException
|
||||||
|
* @throws MultipleObjectsReturnedException
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
*/
|
*/
|
||||||
public function find($id): Stack {
|
public function find($id): Stack {
|
||||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` ' .
|
$qb = $this->db->getQueryBuilder();
|
||||||
'WHERE `id` = ?';
|
$qb->select('*')
|
||||||
return $this->findEntity($sql, [$id]);
|
->from($this->getTableName())
|
||||||
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)));
|
||||||
|
|
||||||
|
return $this->findEntity($qb);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $cardId
|
* @param $cardId
|
||||||
* @return Stack|null
|
* @return Stack|null
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
*/
|
*/
|
||||||
public function findStackFromCardId($cardId): ?Stack {
|
public function findStackFromCardId($cardId): ?Stack {
|
||||||
$sql = <<<SQL
|
$qb = $this->db->getQueryBuilder();
|
||||||
SELECT s.*
|
$qb->select('s.*')
|
||||||
FROM `*PREFIX*deck_stacks` as `s`
|
->from($this->getTableName(), 's')
|
||||||
INNER JOIN `*PREFIX*deck_cards` as `c` ON s.id = c.stack_id
|
->innerJoin('s', 'deck_cards', 'c', 's.id = c.stack_id')
|
||||||
WHERE c.id = ?
|
->where($qb->expr()->eq('c.id', $qb->createNamedParameter($cardId, IQueryBuilder::PARAM_INT)));
|
||||||
SQL;
|
|
||||||
try {
|
try {
|
||||||
return $this->findEntity($sql, [$cardId]);
|
return $this->findEntity($qb);
|
||||||
} catch (MultipleObjectsReturnedException|DoesNotExistException $e) {
|
} catch (MultipleObjectsReturnedException|DoesNotExistException $e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $boardId
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return Stack[]
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function findAll($boardId, $limit = null, $offset = null): array {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('*')
|
||||||
|
->from($this->getTableName())
|
||||||
|
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||||
|
->andWhere($qb->expr()->eq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
|
||||||
|
->setFirstResult($offset)
|
||||||
|
->setMaxResults($limit);
|
||||||
|
|
||||||
public function findAll($boardId, $limit = null, $offset = null) {
|
return $this->findEntities($qb);
|
||||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` WHERE `board_id` = ? AND deleted_at = 0 ORDER BY `order`, `id`';
|
|
||||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $boardId
|
||||||
|
* @param int|null $limit
|
||||||
|
* @param int|null $offset
|
||||||
|
* @return Stack[]
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
public function findDeleted($boardId, $limit = null, $offset = null) {
|
public function findDeleted($boardId, $limit = null, $offset = null) {
|
||||||
$sql = 'SELECT * FROM `*PREFIX*deck_stacks` s
|
$qb = $this->db->getQueryBuilder();
|
||||||
WHERE `s`.`board_id` = ? AND NOT s.deleted_at = 0';
|
$qb->select('*')
|
||||||
return $this->findEntities($sql, [$boardId], $limit, $offset);
|
->from($this->getTableName())
|
||||||
|
->where($qb->expr()->eq('board_id', $qb->createNamedParameter($boardId, IQueryBuilder::PARAM_INT)))
|
||||||
|
->andWhere($qb->expr()->neq('deleted_at', $qb->createNamedParameter(0, IQueryBuilder::PARAM_INT)))
|
||||||
|
->setFirstResult($offset)
|
||||||
|
->setMaxResults($limit);
|
||||||
|
|
||||||
|
return $this->findEntities($qb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
public function delete(Entity $entity) {
|
* @param Entity $entity
|
||||||
|
* @return Entity
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function delete(Entity $entity): Entity {
|
||||||
// delete cards on stack
|
// delete cards on stack
|
||||||
$this->cardMapper->deleteByStack($entity->getId());
|
$this->cardMapper->deleteByStack($entity->getId());
|
||||||
return parent::delete($entity);
|
return parent::delete($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isOwner($userId, $stackId): bool {
|
/**
|
||||||
$sql = 'SELECT owner FROM `*PREFIX*deck_boards` WHERE `id` IN (SELECT board_id FROM `*PREFIX*deck_stacks` WHERE id = ?)';
|
* @param numeric $userId
|
||||||
$stmt = $this->execute($sql, [$stackId]);
|
* @param numeric $stackId
|
||||||
$row = $stmt->fetch();
|
* @return bool
|
||||||
return ($row['owner'] === $userId);
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
|
public function isOwner($userId, $id): bool {
|
||||||
|
$qb = $this->db->getQueryBuilder();
|
||||||
|
$qb->select('s.id')
|
||||||
|
->from($this->getTableName(), 's')
|
||||||
|
->innerJoin('s', 'deck_boards', 'b', 'b.id = s.board_id')
|
||||||
|
->where($qb->expr()->eq('s.id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT)))
|
||||||
|
->andWhere($qb->expr()->eq('owner', $qb->createNamedParameter($userId, IQueryBuilder::PARAM_STR)));
|
||||||
|
|
||||||
|
return count($qb->executeQuery()->fetchAll()) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param numeric $id
|
||||||
|
* @return int|null
|
||||||
|
* @throws \OCP\DB\Exception
|
||||||
|
*/
|
||||||
public function findBoardId($id): ?int {
|
public function findBoardId($id): ?int {
|
||||||
try {
|
try {
|
||||||
$entity = $this->find($id);
|
$entity = $this->find($id);
|
||||||
|
|||||||
@@ -1,11 +1,8 @@
|
|||||||
/*
|
<?php
|
||||||
* @copyright Copyright (c) 2016 Julius Härtl <jus@bitgrid.net>
|
/**
|
||||||
|
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
|
||||||
*
|
*
|
||||||
* @author Julius Härtl <jus@bitgrid.net>
|
* @author Raul Ferreira Fuentes <raul@nextcloud.com>
|
||||||
* @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
|
* @license GNU AGPL version 3 or any later version
|
||||||
*
|
*
|
||||||
@@ -23,6 +20,26 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
namespace OCA\Deck\Model;
|
||||||
|
|
||||||
@import 'icons';
|
use OCA\Deck\Db\Board;
|
||||||
@import 'print';
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
92
lib/Model/CardDetails.php
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2022 Raul Ferreira Fuentes <raul@nextcloud.com>
|
||||||
|
*
|
||||||
|
* @author Raul Ferreira Fuentes <raul@nextcloud.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/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
namespace OCA\Deck\Model;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use OCA\Deck\Db\Board;
|
||||||
|
use OCA\Deck\Db\Card;
|
||||||
|
|
||||||
|
class CardDetails extends Card {
|
||||||
|
private Card $card;
|
||||||
|
private ?Board $board;
|
||||||
|
|
||||||
|
public function __construct(Card $card, ?Board $board = null) {
|
||||||
|
parent::__construct();
|
||||||
|
$this->card = $card;
|
||||||
|
$this->board = $board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBoard(?Board $board): void {
|
||||||
|
$this->board = $board;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function jsonSerialize(array $extras = []): array {
|
||||||
|
$array = $this->card->jsonSerialize();
|
||||||
|
unset($array['notified'], $array['descriptionPrev'], $array['relatedStack'], $array['relatedBoard']);
|
||||||
|
|
||||||
|
$array['overdue'] = $this->getDueStatus();
|
||||||
|
$this->appendBoardDetails($array);
|
||||||
|
|
||||||
|
return $array;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDueStatus(): int {
|
||||||
|
$today = new DateTime();
|
||||||
|
$today->setTime(0, 0);
|
||||||
|
|
||||||
|
$match_date = $this->card->getDuedate();
|
||||||
|
if (!$match_date) {
|
||||||
|
return Card::DUEDATE_FUTURE;
|
||||||
|
}
|
||||||
|
$match_date->setTime(0, 0);
|
||||||
|
|
||||||
|
$diff = $today->diff($match_date);
|
||||||
|
$diffDays = (int) $diff->format('%R%a'); // Extract days count in interval
|
||||||
|
|
||||||
|
|
||||||
|
if ($diffDays === 1) {
|
||||||
|
return Card::DUEDATE_NEXT;
|
||||||
|
}
|
||||||
|
if ($diffDays === 0) {
|
||||||
|
return Card::DUEDATE_NOW;
|
||||||
|
}
|
||||||
|
if ($diffDays < 0) {
|
||||||
|
return Card::DUEDATE_OVERDUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Card::DUEDATE_FUTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function appendBoardDetails(&$array): void {
|
||||||
|
if (!$this->board) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$array['boardId'] = $this->board->id;
|
||||||
|
$array['board'] = (new BoardSummary($this->board))->jsonSerialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __call($name, $arguments) {
|
||||||
|
return $this->card->__call($name, $arguments);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -129,7 +129,7 @@ class NotificationHelper {
|
|||||||
->setSubject('card-overdue', [
|
->setSubject('card-overdue', [
|
||||||
$card->getTitle(), $board->getTitle()
|
$card->getTitle(), $board->getTitle()
|
||||||
])
|
])
|
||||||
->setDateTime(new DateTime($card->getDuedate()));
|
->setDateTime($card->getDuedate());
|
||||||
$this->notificationManager->notify($notification);
|
$this->notificationManager->notify($notification);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +242,7 @@ class NotificationHelper {
|
|||||||
}
|
}
|
||||||
return $this->boards[$boardId];
|
return $this->boards[$boardId];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function generateBoardShared(Board $board, string $userId): INotification {
|
private function generateBoardShared(Board $board, string $userId): INotification {
|
||||||
$notification = $this->notificationManager->createNotification();
|
$notification = $this->notificationManager->createNotification();
|
||||||
$notification
|
$notification
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ class Notifier implements INotifier {
|
|||||||
$dn = $params[2];
|
$dn = $params[2];
|
||||||
}
|
}
|
||||||
$notification->setParsedSubject(
|
$notification->setParsedSubject(
|
||||||
(string) $l->t('The card "%s" on "%s" has been assigned to you by %s.', [$params[0], $params[1], $dn])
|
$l->t('The card "%s" on "%s" has been assigned to you by %s.', [$params[0], $params[1], $dn])
|
||||||
);
|
);
|
||||||
$notification->setRichSubject(
|
$notification->setRichSubject(
|
||||||
$l->t('{user} has assigned the card {deck-card} on {deck-board} to you.'),
|
$l->t('{user} has assigned the card {deck-card} on {deck-board} to you.'),
|
||||||
@@ -151,7 +151,7 @@ class Notifier implements INotifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$notification->setParsedSubject(
|
$notification->setParsedSubject(
|
||||||
(string) $l->t('The card "%s" on "%s" has reached its due date.', $params)
|
$l->t('The card "%s" on "%s" has reached its due date.', $params)
|
||||||
);
|
);
|
||||||
$notification->setRichSubject(
|
$notification->setRichSubject(
|
||||||
$l->t('The card {deck-card} on {deck-board} has reached its due date.'),
|
$l->t('The card {deck-card} on {deck-board} has reached its due date.'),
|
||||||
@@ -189,7 +189,7 @@ class Notifier implements INotifier {
|
|||||||
$dn = $params[2];
|
$dn = $params[2];
|
||||||
}
|
}
|
||||||
$notification->setParsedSubject(
|
$notification->setParsedSubject(
|
||||||
(string) $l->t('%s has mentioned you in a comment on "%s".', [$dn, $params[0]])
|
$l->t('%s has mentioned you in a comment on "%s".', [$dn, $params[0]])
|
||||||
);
|
);
|
||||||
$notification->setRichSubject(
|
$notification->setRichSubject(
|
||||||
$l->t('{user} has mentioned you in a comment on {deck-card}.'),
|
$l->t('{user} has mentioned you in a comment on {deck-card}.'),
|
||||||
@@ -226,7 +226,7 @@ class Notifier implements INotifier {
|
|||||||
$dn = $params[1];
|
$dn = $params[1];
|
||||||
}
|
}
|
||||||
$notification->setParsedSubject(
|
$notification->setParsedSubject(
|
||||||
(string) $l->t('The board "%s" has been shared with you by %s.', [$params[0], $dn])
|
$l->t('The board "%s" has been shared with you by %s.', [$params[0], $dn])
|
||||||
);
|
);
|
||||||
$notification->setRichSubject(
|
$notification->setRichSubject(
|
||||||
$l->t('{user} has shared {deck-board} with you.'),
|
$l->t('{user} has shared {deck-board} with you.'),
|
||||||
|
|||||||
@@ -54,22 +54,11 @@ use OCP\IURLGenerator;
|
|||||||
class DeckProvider implements IFullTextSearchProvider {
|
class DeckProvider implements IFullTextSearchProvider {
|
||||||
public const DECK_PROVIDER_ID = 'deck';
|
public const DECK_PROVIDER_ID = 'deck';
|
||||||
|
|
||||||
|
private IL10N $l10n;
|
||||||
/** @var IL10N */
|
private IUrlGenerator $urlGenerator;
|
||||||
private $l10n;
|
private FullTextSearchService $fullTextSearchService;
|
||||||
|
private ?IRunner $runner = null;
|
||||||
/** @var IUrlGenerator */
|
private ?IIndexOptions $indexOptions = null;
|
||||||
private $urlGenerator;
|
|
||||||
|
|
||||||
/** @var FullTextSearchService */
|
|
||||||
private $fullTextSearchService;
|
|
||||||
|
|
||||||
|
|
||||||
/** @var IRunner */
|
|
||||||
private $runner;
|
|
||||||
|
|
||||||
/** @var IIndexOptions */
|
|
||||||
private $indexOptions = [];
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
169
lib/Reference/CardReferenceProvider.php
Normal file
@@ -0,0 +1,169 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @copyright Copyright (c) 2022 Julien Veyssier <eneiluj@posteo.net>
|
||||||
|
*
|
||||||
|
* @author Julien Veyssier <eneiluj@posteo.net>
|
||||||
|
*
|
||||||
|
* @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\Reference;
|
||||||
|
|
||||||
|
use OCA\Deck\AppInfo\Application;
|
||||||
|
use OCA\Deck\Db\Assignment;
|
||||||
|
use OCA\Deck\Db\Attachment;
|
||||||
|
use OCA\Deck\Db\Label;
|
||||||
|
use OCA\Deck\Model\CardDetails;
|
||||||
|
use OCA\Deck\Service\BoardService;
|
||||||
|
use OCA\Deck\Service\CardService;
|
||||||
|
use OCA\Deck\Service\StackService;
|
||||||
|
use OCP\Collaboration\Reference\IReference;
|
||||||
|
use OCP\Collaboration\Reference\IReferenceProvider;
|
||||||
|
use OCP\Collaboration\Reference\Reference;
|
||||||
|
use OCP\IURLGenerator;
|
||||||
|
|
||||||
|
class CardReferenceProvider implements IReferenceProvider {
|
||||||
|
private CardService $cardService;
|
||||||
|
private IURLGenerator $urlGenerator;
|
||||||
|
private BoardService $boardService;
|
||||||
|
private StackService $stackService;
|
||||||
|
private ?string $userId;
|
||||||
|
|
||||||
|
public function __construct(CardService $cardService,
|
||||||
|
BoardService $boardService,
|
||||||
|
StackService $stackService,
|
||||||
|
IURLGenerator $urlGenerator,
|
||||||
|
?string $userId) {
|
||||||
|
$this->cardService = $cardService;
|
||||||
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
$this->boardService = $boardService;
|
||||||
|
$this->stackService = $stackService;
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function matchReference(string $referenceText): bool {
|
||||||
|
$start = $this->urlGenerator->getAbsoluteURL('/apps/' . Application::APP_ID);
|
||||||
|
$startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/' . Application::APP_ID);
|
||||||
|
|
||||||
|
// link example: https://nextcloud.local/index.php/apps/deck/#/board/2/card/11
|
||||||
|
$noIndexMatch = preg_match('/^' . preg_quote($start, '/') . '\/#\/board\/[0-9]+\/card\/[0-9]+$/', $referenceText) === 1;
|
||||||
|
$indexMatch = preg_match('/^' . preg_quote($startIndex, '/') . '\/#\/board\/[0-9]+\/card\/[0-9]+$/', $referenceText) === 1;
|
||||||
|
|
||||||
|
return $noIndexMatch || $indexMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function resolveReference(string $referenceText): ?IReference {
|
||||||
|
if ($this->matchReference($referenceText)) {
|
||||||
|
$ids = $this->getBoardCardId($referenceText);
|
||||||
|
if ($ids !== null) {
|
||||||
|
[$boardId, $cardId] = $ids;
|
||||||
|
$card = $this->cardService->find((int) $cardId)->jsonSerialize();
|
||||||
|
$board = $this->boardService->find((int) $boardId)->jsonSerialize();
|
||||||
|
$stack = $this->stackService->find((int) $card['stackId'])->jsonSerialize();
|
||||||
|
|
||||||
|
$card = $this->sanitizeSerializedCard($card);
|
||||||
|
$board = $this->sanitizeSerializedBoard($board);
|
||||||
|
$stack = $this->sanitizeSerializedStack($stack);
|
||||||
|
|
||||||
|
$reference = new Reference($referenceText);
|
||||||
|
$reference->setRichObject(Application::APP_ID . '-card', [
|
||||||
|
'id' => $boardId . '/' . $cardId,
|
||||||
|
'card' => $card,
|
||||||
|
'board' => $board,
|
||||||
|
'stack' => $stack,
|
||||||
|
]);
|
||||||
|
return $reference;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sanitizeSerializedStack(array $stack): array {
|
||||||
|
$stack['cards'] = array_map(function (CardDetails $cardDetails) {
|
||||||
|
$result = $cardDetails->jsonSerialize();
|
||||||
|
unset($result['assignedUsers']);
|
||||||
|
return $result;
|
||||||
|
}, $stack['cards']);
|
||||||
|
|
||||||
|
return $stack;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sanitizeSerializedBoard(array $board): array {
|
||||||
|
unset($board['labels']);
|
||||||
|
$board['owner'] = $board['owner']->jsonSerialize();
|
||||||
|
unset($board['acl']);
|
||||||
|
unset($board['users']);
|
||||||
|
|
||||||
|
return $board;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sanitizeSerializedCard(array $card): array {
|
||||||
|
$card['labels'] = array_map(function (Label $label) {
|
||||||
|
return $label->jsonSerialize();
|
||||||
|
}, $card['labels']);
|
||||||
|
$card['assignedUsers'] = array_map(function (Assignment $assignment) {
|
||||||
|
$result = $assignment->jsonSerialize();
|
||||||
|
$result['participant'] = $result['participant']->jsonSerialize();
|
||||||
|
return $result;
|
||||||
|
}, $card['assignedUsers']);
|
||||||
|
$card['owner'] = $card['owner']->jsonSerialize();
|
||||||
|
unset($card['relatedStack']);
|
||||||
|
unset($card['relatedBoard']);
|
||||||
|
$card['attachments'] = array_map(function (Attachment $attachment) {
|
||||||
|
return $attachment->jsonSerialize();
|
||||||
|
}, $card['attachments']);
|
||||||
|
|
||||||
|
return $card;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getBoardCardId(string $url): ?array {
|
||||||
|
$start = $this->urlGenerator->getAbsoluteURL('/apps/' . Application::APP_ID);
|
||||||
|
$startIndex = $this->urlGenerator->getAbsoluteURL('/index.php/apps/' . Application::APP_ID);
|
||||||
|
|
||||||
|
preg_match('/^' . preg_quote($start, '/') . '\/#\/board\/([0-9]+)\/card\/([0-9]+)$/', $url, $matches);
|
||||||
|
if ($matches && count($matches) > 2) {
|
||||||
|
return [$matches[1], $matches[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
preg_match('/^' . preg_quote($startIndex, '/') . '\/#\/board\/([0-9]+)\/card\/([0-9]+)$/', $url, $matches2);
|
||||||
|
if ($matches2 && count($matches2) > 2) {
|
||||||
|
return [$matches2[1], $matches2[2]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCachePrefix(string $referenceId): string {
|
||||||
|
$ids = $this->getBoardCardId($referenceId);
|
||||||
|
if ($ids !== null) {
|
||||||
|
[$boardId, $cardId] = $ids;
|
||||||
|
return $boardId . '/' . $cardId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $referenceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCacheKey(string $referenceId): ?string {
|
||||||
|
return $this->userId ?? '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,22 +60,23 @@ class AttachmentService {
|
|||||||
private $activityManager;
|
private $activityManager;
|
||||||
/** @var ChangeHelper */
|
/** @var ChangeHelper */
|
||||||
private $changeHelper;
|
private $changeHelper;
|
||||||
/** @var IUserManager */
|
private IUserManager $userManager;
|
||||||
private $userManager;
|
|
||||||
/** @var AttachmentServiceValidator */
|
/** @var AttachmentServiceValidator */
|
||||||
private $attachmentServiceValidator;
|
private AttachmentServiceValidator $attachmentServiceValidator;
|
||||||
|
|
||||||
public function __construct(AttachmentMapper $attachmentMapper,
|
public function __construct(
|
||||||
CardMapper $cardMapper,
|
AttachmentMapper $attachmentMapper,
|
||||||
IUserManager $userManager,
|
CardMapper $cardMapper,
|
||||||
ChangeHelper $changeHelper,
|
IUserManager $userManager,
|
||||||
PermissionService $permissionService,
|
ChangeHelper $changeHelper,
|
||||||
Application $application,
|
PermissionService $permissionService,
|
||||||
AttachmentCacheHelper $attachmentCacheHelper,
|
Application $application,
|
||||||
$userId,
|
AttachmentCacheHelper $attachmentCacheHelper,
|
||||||
IL10N $l10n,
|
$userId,
|
||||||
ActivityManager $activityManager,
|
IL10N $l10n,
|
||||||
AttachmentServiceValidator $attachmentServiceValidator) {
|
ActivityManager $activityManager,
|
||||||
|
AttachmentServiceValidator $attachmentServiceValidator
|
||||||
|
) {
|
||||||
$this->attachmentMapper = $attachmentMapper;
|
$this->attachmentMapper = $attachmentMapper;
|
||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
$this->permissionService = $permissionService;
|
$this->permissionService = $permissionService;
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
namespace OCA\Deck\Service;
|
namespace OCA\Deck\Service;
|
||||||
|
|
||||||
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
|
|
||||||
use OCA\Deck\Activity\ActivityManager;
|
use OCA\Deck\Activity\ActivityManager;
|
||||||
use OCA\Deck\Activity\ChangeSet;
|
use OCA\Deck\Activity\ChangeSet;
|
||||||
use OCA\Deck\AppInfo\Application;
|
use OCA\Deck\AppInfo\Application;
|
||||||
@@ -45,8 +44,10 @@ use OCA\Deck\Notification\NotificationHelper;
|
|||||||
use OCP\AppFramework\Db\DoesNotExistException;
|
use OCP\AppFramework\Db\DoesNotExistException;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
use OCP\IConfig;
|
use OCP\IConfig;
|
||||||
|
use OCP\IDBConnection;
|
||||||
use OCP\IGroupManager;
|
use OCP\IGroupManager;
|
||||||
use OCP\IL10N;
|
use OCP\IL10N;
|
||||||
|
use OCP\DB\Exception as DbException;
|
||||||
use OCA\Deck\Db\Board;
|
use OCA\Deck\Db\Board;
|
||||||
use OCA\Deck\Db\BoardMapper;
|
use OCA\Deck\Db\BoardMapper;
|
||||||
use OCA\Deck\Db\LabelMapper;
|
use OCA\Deck\Db\LabelMapper;
|
||||||
@@ -54,30 +55,29 @@ use OCP\IUserManager;
|
|||||||
use OCA\Deck\BadRequestException;
|
use OCA\Deck\BadRequestException;
|
||||||
use OCA\Deck\Validators\BoardServiceValidator;
|
use OCA\Deck\Validators\BoardServiceValidator;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
|
use OCP\Server;
|
||||||
|
|
||||||
class BoardService {
|
class BoardService {
|
||||||
private $boardMapper;
|
private BoardMapper $boardMapper;
|
||||||
private $stackMapper;
|
private StackMapper $stackMapper;
|
||||||
private $labelMapper;
|
private LabelMapper $labelMapper;
|
||||||
private $aclMapper;
|
private AclMapper $aclMapper;
|
||||||
/** @var IConfig */
|
private IConfig $config;
|
||||||
private $config;
|
private IL10N $l10n;
|
||||||
private $l10n;
|
private PermissionService $permissionService;
|
||||||
private $permissionService;
|
private NotificationHelper $notificationHelper;
|
||||||
private $notificationHelper;
|
private AssignmentMapper $assignedUsersMapper;
|
||||||
private $assignedUsersMapper;
|
private IUserManager $userManager;
|
||||||
private $userManager;
|
private IGroupManager $groupManager;
|
||||||
private $groupManager;
|
private ?string $userId;
|
||||||
private $userId;
|
private ActivityManager $activityManager;
|
||||||
private $activityManager;
|
private IEventDispatcher $eventDispatcher;
|
||||||
private $eventDispatcher;
|
private ChangeHelper $changeHelper;
|
||||||
private $changeHelper;
|
private CardMapper $cardMapper;
|
||||||
private $cardMapper;
|
private ?array $boardsCache = null;
|
||||||
|
private IURLGenerator $urlGenerator;
|
||||||
private $boardsCache = null;
|
private IDBConnection $connection;
|
||||||
private $urlGenerator;
|
private BoardServiceValidator $boardServiceValidator;
|
||||||
private $boardServiceValidator;
|
|
||||||
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
BoardMapper $boardMapper,
|
BoardMapper $boardMapper,
|
||||||
@@ -96,8 +96,9 @@ class BoardService {
|
|||||||
IEventDispatcher $eventDispatcher,
|
IEventDispatcher $eventDispatcher,
|
||||||
ChangeHelper $changeHelper,
|
ChangeHelper $changeHelper,
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
|
IDBConnection $connection,
|
||||||
BoardServiceValidator $boardServiceValidator,
|
BoardServiceValidator $boardServiceValidator,
|
||||||
$userId
|
?string $userId
|
||||||
) {
|
) {
|
||||||
$this->boardMapper = $boardMapper;
|
$this->boardMapper = $boardMapper;
|
||||||
$this->stackMapper = $stackMapper;
|
$this->stackMapper = $stackMapper;
|
||||||
@@ -116,6 +117,7 @@ class BoardService {
|
|||||||
$this->userId = $userId;
|
$this->userId = $userId;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
|
$this->connection = $connection;
|
||||||
$this->boardServiceValidator = $boardServiceValidator;
|
$this->boardServiceValidator = $boardServiceValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -479,7 +481,7 @@ class BoardService {
|
|||||||
|
|
||||||
// TODO: use the dispatched event for this
|
// TODO: use the dispatched event for this
|
||||||
try {
|
try {
|
||||||
$resourceProvider = \OC::$server->query(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
$resourceProvider = Server::get(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
||||||
$resourceProvider->invalidateAccessCache($boardId);
|
$resourceProvider->invalidateAccessCache($boardId);
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
}
|
}
|
||||||
@@ -521,18 +523,12 @@ class BoardService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $id
|
|
||||||
* @return \OCP\AppFramework\Db\Entity
|
|
||||||
* @throws DoesNotExistException
|
* @throws DoesNotExistException
|
||||||
* @throws \OCA\Deck\NoPermissionException
|
* @throws \OCA\Deck\NoPermissionException
|
||||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
public function deleteAcl($id) {
|
public function deleteAcl(int $id): bool {
|
||||||
if (is_numeric($id) === false) {
|
|
||||||
throw new BadRequestException('id must be a number');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
|
$this->permissionService->checkPermission($this->aclMapper, $id, Acl::PERMISSION_SHARE);
|
||||||
/** @var Acl $acl */
|
/** @var Acl $acl */
|
||||||
$acl = $this->aclMapper->find($id);
|
$acl = $this->aclMapper->find($id);
|
||||||
@@ -551,16 +547,14 @@ class BoardService {
|
|||||||
$version = \OCP\Util::getVersion()[0];
|
$version = \OCP\Util::getVersion()[0];
|
||||||
if ($version >= 16) {
|
if ($version >= 16) {
|
||||||
try {
|
try {
|
||||||
$resourceProvider = \OC::$server->query(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
$resourceProvider = Server::get(\OCA\Deck\Collaboration\Resources\ResourceProvider::class);
|
||||||
$resourceProvider->invalidateAccessCache($acl->getBoardId());
|
$resourceProvider->invalidateAccessCache($acl->getBoardId());
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$delete = $this->aclMapper->delete($acl);
|
|
||||||
|
|
||||||
$this->eventDispatcher->dispatchTyped(new AclDeletedEvent($acl));
|
$this->eventDispatcher->dispatchTyped(new AclDeletedEvent($acl));
|
||||||
|
return (bool) $this->aclMapper->delete($acl);
|
||||||
return $delete;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -612,7 +606,7 @@ class BoardService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function transferBoardOwnership(int $boardId, string $newOwner, bool $changeContent = false): Board {
|
public function transferBoardOwnership(int $boardId, string $newOwner, bool $changeContent = false): Board {
|
||||||
\OC::$server->getDatabaseConnection()->beginTransaction();
|
$this->connection->beginTransaction();
|
||||||
try {
|
try {
|
||||||
$board = $this->boardMapper->find($boardId);
|
$board = $this->boardMapper->find($boardId);
|
||||||
$previousOwner = $board->getOwner();
|
$previousOwner = $board->getOwner();
|
||||||
@@ -621,7 +615,10 @@ class BoardService {
|
|||||||
if (!$changeContent) {
|
if (!$changeContent) {
|
||||||
try {
|
try {
|
||||||
$this->addAcl($boardId, Acl::PERMISSION_TYPE_USER, $previousOwner, true, true, true);
|
$this->addAcl($boardId, Acl::PERMISSION_TYPE_USER, $previousOwner, true, true, true);
|
||||||
} catch (UniqueConstraintViolationException $e) {
|
} catch (DbException $e) {
|
||||||
|
if ($e->getReason() !== DbException::REASON_UNIQUE_CONSTRAINT_VIOLATION) {
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->boardMapper->transferOwnership($previousOwner, $newOwner, $boardId);
|
$this->boardMapper->transferOwnership($previousOwner, $newOwner, $boardId);
|
||||||
@@ -631,10 +628,10 @@ class BoardService {
|
|||||||
$this->assignedUsersMapper->remapAssignedUser($boardId, $previousOwner, $newOwner);
|
$this->assignedUsersMapper->remapAssignedUser($boardId, $previousOwner, $newOwner);
|
||||||
$this->cardMapper->remapCardOwner($boardId, $previousOwner, $newOwner);
|
$this->cardMapper->remapCardOwner($boardId, $previousOwner, $newOwner);
|
||||||
}
|
}
|
||||||
\OC::$server->getDatabaseConnection()->commit();
|
$this->connection->commit();
|
||||||
return $this->boardMapper->find($boardId);
|
return $this->boardMapper->find($boardId);
|
||||||
} catch (\Throwable $e) {
|
} catch (\Throwable $e) {
|
||||||
\OC::$server->getDatabaseConnection()->rollBack();
|
$this->connection->rollBack();
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,27 +46,31 @@ use OCA\Deck\BadRequestException;
|
|||||||
use OCA\Deck\Validators\CardServiceValidator;
|
use OCA\Deck\Validators\CardServiceValidator;
|
||||||
use OCP\Comments\ICommentsManager;
|
use OCP\Comments\ICommentsManager;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
|
use OCP\IRequest;
|
||||||
use OCP\IUserManager;
|
use OCP\IUserManager;
|
||||||
use OCP\IURLGenerator;
|
use OCP\IURLGenerator;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class CardService {
|
class CardService {
|
||||||
private $cardMapper;
|
private CardMapper $cardMapper;
|
||||||
private $stackMapper;
|
private StackMapper $stackMapper;
|
||||||
private $boardMapper;
|
private BoardMapper $boardMapper;
|
||||||
private $labelMapper;
|
private LabelMapper $labelMapper;
|
||||||
private $permissionService;
|
private PermissionService $permissionService;
|
||||||
private $boardService;
|
private BoardService $boardService;
|
||||||
private $notificationHelper;
|
private NotificationHelper $notificationHelper;
|
||||||
private $assignedUsersMapper;
|
private AssignmentMapper $assignedUsersMapper;
|
||||||
private $attachmentService;
|
private AttachmentService $attachmentService;
|
||||||
private $currentUser;
|
private ?string $currentUser;
|
||||||
private $activityManager;
|
private ActivityManager $activityManager;
|
||||||
private $commentsManager;
|
private ICommentsManager $commentsManager;
|
||||||
private $changeHelper;
|
private ChangeHelper $changeHelper;
|
||||||
private $eventDispatcher;
|
private IEventDispatcher $eventDispatcher;
|
||||||
private $userManager;
|
private IUserManager $userManager;
|
||||||
private $urlGenerator;
|
private IURLGenerator $urlGenerator;
|
||||||
private $cardServiceValidator;
|
private LoggerInterface $logger;
|
||||||
|
private IRequest $request;
|
||||||
|
private CardServiceValidator $cardServiceValidator;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
CardMapper $cardMapper,
|
CardMapper $cardMapper,
|
||||||
@@ -84,8 +88,10 @@ class CardService {
|
|||||||
ChangeHelper $changeHelper,
|
ChangeHelper $changeHelper,
|
||||||
IEventDispatcher $eventDispatcher,
|
IEventDispatcher $eventDispatcher,
|
||||||
IURLGenerator $urlGenerator,
|
IURLGenerator $urlGenerator,
|
||||||
|
LoggerInterface $logger,
|
||||||
|
IRequest $request,
|
||||||
CardServiceValidator $cardServiceValidator,
|
CardServiceValidator $cardServiceValidator,
|
||||||
$userId
|
?string $userId
|
||||||
) {
|
) {
|
||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
$this->stackMapper = $stackMapper;
|
$this->stackMapper = $stackMapper;
|
||||||
@@ -103,6 +109,8 @@ class CardService {
|
|||||||
$this->eventDispatcher = $eventDispatcher;
|
$this->eventDispatcher = $eventDispatcher;
|
||||||
$this->currentUser = $userId;
|
$this->currentUser = $userId;
|
||||||
$this->urlGenerator = $urlGenerator;
|
$this->urlGenerator = $urlGenerator;
|
||||||
|
$this->logger = $logger;
|
||||||
|
$this->request = $request;
|
||||||
$this->cardServiceValidator = $cardServiceValidator;
|
$this->cardServiceValidator = $cardServiceValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,23 +144,18 @@ class CardService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param $cardId
|
|
||||||
* @return \OCA\Deck\Db\RelationalEntity
|
* @return \OCA\Deck\Db\RelationalEntity
|
||||||
* @throws \OCA\Deck\NoPermissionException
|
* @throws \OCA\Deck\NoPermissionException
|
||||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||||
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
public function find($cardId) {
|
public function find(int $cardId) {
|
||||||
if (is_numeric($cardId) === false) {
|
|
||||||
throw new BadRequestException('card id must be a number');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
|
$this->permissionService->checkPermission($this->cardMapper, $cardId, Acl::PERMISSION_READ);
|
||||||
$card = $this->cardMapper->find($cardId);
|
$card = $this->cardMapper->find($cardId);
|
||||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||||
$attachments = $this->attachmentService->findAll($cardId, true);
|
$attachments = $this->attachmentService->findAll($cardId, true);
|
||||||
if (\OC::$server->getRequest()->getParam('apiVersion') === '1.0') {
|
if ($this->request->getParam('apiVersion') === '1.0') {
|
||||||
$attachments = array_filter($attachments, function ($attachment) {
|
$attachments = array_filter($attachments, function ($attachment) {
|
||||||
return $attachment->getType() === 'deck_file';
|
return $attachment->getType() === 'deck_file';
|
||||||
});
|
});
|
||||||
@@ -167,7 +170,7 @@ class CardService {
|
|||||||
try {
|
try {
|
||||||
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
$this->permissionService->checkPermission($this->boardMapper, $boardId, Acl::PERMISSION_READ);
|
||||||
} catch (NoPermissionException $e) {
|
} catch (NoPermissionException $e) {
|
||||||
\OC::$server->getLogger()->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
$this->logger->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
$cards = $this->cardMapper->findCalendarEntries($boardId);
|
$cards = $this->cardMapper->findCalendarEntries($boardId);
|
||||||
@@ -295,11 +298,11 @@ class CardService {
|
|||||||
$card->setType($type);
|
$card->setType($type);
|
||||||
$card->setOrder($order);
|
$card->setOrder($order);
|
||||||
$card->setOwner($owner);
|
$card->setOwner($owner);
|
||||||
$card->setDuedate($duedate);
|
$card->setDuedate($duedate ? new \DateTime($duedate) : null);
|
||||||
$resetDuedateNotification = false;
|
$resetDuedateNotification = false;
|
||||||
if (
|
if (
|
||||||
$card->getDuedate() === null ||
|
$card->getDuedate() === null ||
|
||||||
(new \DateTime($card->getDuedate())) != (new \DateTime($changes->getBefore()->getDuedate() ?? ''))
|
($card->getDuedate()) != ($changes->getBefore()->getDuedate())
|
||||||
) {
|
) {
|
||||||
$card->setNotified(false);
|
$card->setNotified(false);
|
||||||
$resetDuedateNotification = true;
|
$resetDuedateNotification = true;
|
||||||
@@ -428,7 +431,7 @@ class CardService {
|
|||||||
* @throws StatusException
|
* @throws StatusException
|
||||||
* @throws \OCA\Deck\NoPermissionException
|
* @throws \OCA\Deck\NoPermissionException
|
||||||
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
* @throws \OCP\AppFramework\Db\DoesNotExistException
|
||||||
* @throws \OCP\AppFramework\Db\
|
* @throws \OCP\AppFramework\Db\MultipleObjectsReturnedException
|
||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
public function archive($id) {
|
public function archive($id) {
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ use OCA\Circles\Model\Circle;
|
|||||||
use OCA\Circles\Model\Member;
|
use OCA\Circles\Model\Member;
|
||||||
use OCA\Circles\Model\Probes\CircleProbe;
|
use OCA\Circles\Model\Probes\CircleProbe;
|
||||||
use OCP\App\IAppManager;
|
use OCP\App\IAppManager;
|
||||||
|
use OCP\Server;
|
||||||
use Throwable;
|
use Throwable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -38,7 +39,7 @@ use Throwable;
|
|||||||
* having the app disabled is properly handled
|
* having the app disabled is properly handled
|
||||||
*/
|
*/
|
||||||
class CirclesService {
|
class CirclesService {
|
||||||
private $circlesEnabled;
|
private bool $circlesEnabled;
|
||||||
|
|
||||||
private $userCircleCache = [];
|
private $userCircleCache = [];
|
||||||
|
|
||||||
@@ -58,8 +59,7 @@ class CirclesService {
|
|||||||
try {
|
try {
|
||||||
|
|
||||||
// Enforce current user condition since we always want the full list of members
|
// Enforce current user condition since we always want the full list of members
|
||||||
/** @var CirclesManager $circlesManager */
|
$circlesManager = Server::get(CirclesManager::class);
|
||||||
$circlesManager = \OC::$server->get(CirclesManager::class);
|
|
||||||
$circlesManager->startSuperSession();
|
$circlesManager->startSuperSession();
|
||||||
return $circlesManager->getCircle($circleId);
|
return $circlesManager->getCircle($circleId);
|
||||||
} catch (Throwable $e) {
|
} catch (Throwable $e) {
|
||||||
@@ -77,8 +77,7 @@ class CirclesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @var CirclesManager $circlesManager */
|
$circlesManager = Server::get(CirclesManager::class);
|
||||||
$circlesManager = \OC::$server->get(CirclesManager::class);
|
|
||||||
$federatedUser = $circlesManager->getFederatedUser($userId, Member::TYPE_USER);
|
$federatedUser = $circlesManager->getFederatedUser($userId, Member::TYPE_USER);
|
||||||
$circlesManager->startSession($federatedUser);
|
$circlesManager->startSession($federatedUser);
|
||||||
$circle = $circlesManager->getCircle($circleId);
|
$circle = $circlesManager->getCircle($circleId);
|
||||||
@@ -106,8 +105,7 @@ class CirclesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @var CirclesManager $circlesManager */
|
$circlesManager = Server::get(CirclesManager::class);
|
||||||
$circlesManager = \OC::$server->get(CirclesManager::class);
|
|
||||||
$federatedUser = $circlesManager->getFederatedUser($userId, Member::TYPE_USER);
|
$federatedUser = $circlesManager->getFederatedUser($userId, Member::TYPE_USER);
|
||||||
$circlesManager->startSession($federatedUser);
|
$circlesManager->startSession($federatedUser);
|
||||||
$probe = new CircleProbe();
|
$probe = new CircleProbe();
|
||||||
|
|||||||
@@ -40,20 +40,14 @@ use OutOfBoundsException;
|
|||||||
use function is_numeric;
|
use function is_numeric;
|
||||||
|
|
||||||
class CommentService {
|
class CommentService {
|
||||||
|
private ICommentsManager $commentsManager;
|
||||||
|
private IUserManager $userManager;
|
||||||
|
private CardMapper $cardMapper;
|
||||||
|
private PermissionService $permissionService;
|
||||||
|
private ILogger $logger;
|
||||||
|
private ?string $userId;
|
||||||
|
|
||||||
/**
|
public function __construct(ICommentsManager $commentsManager, PermissionService $permissionService, CardMapper $cardMapper, IUserManager $userManager, ILogger $logger, ?string $userId) {
|
||||||
* @var ICommentsManager
|
|
||||||
*/
|
|
||||||
private $commentsManager;
|
|
||||||
/**
|
|
||||||
* @var IUserManager
|
|
||||||
*/
|
|
||||||
private $userManager;
|
|
||||||
/** @var ILogger */
|
|
||||||
private $logger;
|
|
||||||
private $userId;
|
|
||||||
|
|
||||||
public function __construct(ICommentsManager $commentsManager, PermissionService $permissionService, CardMapper $cardMapper, IUserManager $userManager, ILogger $logger, $userId) {
|
|
||||||
$this->commentsManager = $commentsManager;
|
$this->commentsManager = $commentsManager;
|
||||||
$this->permissionService = $permissionService;
|
$this->permissionService = $permissionService;
|
||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
|
|||||||
@@ -40,9 +40,9 @@ class ConfigService {
|
|||||||
public const SETTING_BOARD_NOTIFICATION_DUE_ALL = 'all';
|
public const SETTING_BOARD_NOTIFICATION_DUE_ALL = 'all';
|
||||||
public const SETTING_BOARD_NOTIFICATION_DUE_DEFAULT = self::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED;
|
public const SETTING_BOARD_NOTIFICATION_DUE_DEFAULT = self::SETTING_BOARD_NOTIFICATION_DUE_ASSIGNED;
|
||||||
|
|
||||||
private $config;
|
private IConfig $config;
|
||||||
private $userId;
|
private ?string $userId = null;
|
||||||
private $groupManager;
|
private IGroupManager $groupManager;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IConfig $config,
|
IConfig $config,
|
||||||
@@ -52,11 +52,14 @@ class ConfigService {
|
|||||||
$this->config = $config;
|
$this->config = $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getUserId() {
|
public function getUserId(): ?string {
|
||||||
if (!$this->userId) {
|
if (!$this->userId) {
|
||||||
$user = \OC::$server->get(IUserSession::class)->getUser();
|
// We cannot use DI for the userId or UserSession as the ConfigService
|
||||||
|
// is initiated too early before the session is actually loaded
|
||||||
|
$user = \OCP\Server::get(IUserSession::class)->getUser();
|
||||||
$this->userId = $user ? $user->getUID() : null;
|
$this->userId = $user ? $user->getUID() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->userId;
|
return $this->userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +78,11 @@ class ConfigService {
|
|||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get($key) {
|
/**
|
||||||
$result = null;
|
* @return bool|array{id: string, displayname: string}[]
|
||||||
|
* @throws NoPermissionException
|
||||||
|
*/
|
||||||
|
public function get(string $key) {
|
||||||
[$scope] = explode(':', $key, 2);
|
[$scope] = explode(':', $key, 2);
|
||||||
switch ($scope) {
|
switch ($scope) {
|
||||||
case 'groupLimit':
|
case 'groupLimit':
|
||||||
@@ -90,11 +96,12 @@ class ConfigService {
|
|||||||
}
|
}
|
||||||
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'calendar', true);
|
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'calendar', true);
|
||||||
case 'cardDetailsInModal':
|
case 'cardDetailsInModal':
|
||||||
if ($this->getUserId() === null) {
|
if ($this->getUserId() === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', true);
|
return (bool)$this->config->getUserValue($this->getUserId(), Application::APP_ID, 'cardDetailsInModal', true);
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isCalendarEnabled(int $boardId = null): bool {
|
public function isCalendarEnabled(int $boardId = null): bool {
|
||||||
@@ -157,7 +164,10 @@ class ConfigService {
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setGroupLimit($value) {
|
/**
|
||||||
|
* @return string[]
|
||||||
|
*/
|
||||||
|
private function setGroupLimit(array $value): array {
|
||||||
$groups = [];
|
$groups = [];
|
||||||
foreach ($value as $group) {
|
foreach ($value as $group) {
|
||||||
$groups[] = $group['id'];
|
$groups[] = $group['id'];
|
||||||
@@ -167,7 +177,7 @@ class ConfigService {
|
|||||||
return $groups;
|
return $groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getGroupLimitList() {
|
private function getGroupLimitList(): array {
|
||||||
$value = $this->config->getAppValue(Application::APP_ID, 'groupLimit', '');
|
$value = $this->config->getAppValue(Application::APP_ID, 'groupLimit', '');
|
||||||
$groups = explode(',', $value);
|
$groups = explode(',', $value);
|
||||||
if ($value === '') {
|
if ($value === '') {
|
||||||
@@ -176,9 +186,10 @@ class ConfigService {
|
|||||||
return $groups;
|
return $groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return array{id: string, displayname: string}[] */
|
||||||
private function getGroupLimit() {
|
private function getGroupLimit() {
|
||||||
$groups = $this->getGroupLimitList();
|
$groups = $this->getGroupLimitList();
|
||||||
$groups = array_map(function ($groupId) {
|
$groups = array_map(function (string $groupId): ?array {
|
||||||
/** @var IGroup $groups */
|
/** @var IGroup $groups */
|
||||||
$group = $this->groupManager->get($groupId);
|
$group = $this->groupManager->get($groupId);
|
||||||
if ($group === null) {
|
if ($group === null) {
|
||||||
|
|||||||
@@ -88,18 +88,6 @@ class DefaultBoardService {
|
|||||||
* @throws BadRequestException
|
* @throws BadRequestException
|
||||||
*/
|
*/
|
||||||
public function createDefaultBoard(string $title, string $userId, string $color) {
|
public function createDefaultBoard(string $title, string $userId, string $color) {
|
||||||
if ($title === false || $title === null) {
|
|
||||||
throw new BadRequestException('title must be provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($userId === false || $userId === null) {
|
|
||||||
throw new BadRequestException('userId must be provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($color === false || $color === null) {
|
|
||||||
throw new BadRequestException('color must be provided');
|
|
||||||
}
|
|
||||||
|
|
||||||
$defaultBoard = $this->boardService->create($title, $userId, $color);
|
$defaultBoard = $this->boardService->create($title, $userId, $color);
|
||||||
$defaultStacks = [];
|
$defaultStacks = [];
|
||||||
$defaultCards = [];
|
$defaultCards = [];
|
||||||
|
|||||||
@@ -219,8 +219,11 @@ class FileService implements IAttachmentService {
|
|||||||
throw new \Exception('no instance id!');
|
throw new \Exception('no instance id!');
|
||||||
}
|
}
|
||||||
$name = 'appdata_' . $instanceId;
|
$name = 'appdata_' . $instanceId;
|
||||||
|
/** @var \OCP\Files\Folder $appDataFolder */
|
||||||
$appDataFolder = $this->rootFolder->get($name);
|
$appDataFolder = $this->rootFolder->get($name);
|
||||||
|
/** @var \OCP\Files\Folder $appDataFolder */
|
||||||
$appDataFolder = $appDataFolder->get('deck');
|
$appDataFolder = $appDataFolder->get('deck');
|
||||||
|
/** @var \OCP\Files\Folder $cardFolder */
|
||||||
$cardFolder = $appDataFolder->get($folderName);
|
$cardFolder = $appDataFolder->get($folderName);
|
||||||
return $cardFolder->get($attachment->getData());
|
return $cardFolder->get($attachment->getData());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,18 +44,19 @@ use OCP\Share\IShare;
|
|||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
||||||
private $request;
|
private IRequest $request;
|
||||||
private $rootFolder;
|
private IRootFolder $rootFolder;
|
||||||
private $shareProvider;
|
private DeckShareProvider $shareProvider;
|
||||||
private $shareManager;
|
private IManager $shareManager;
|
||||||
private $userId;
|
private ?string $userId;
|
||||||
private $configService;
|
private ConfigService $configService;
|
||||||
private $l10n;
|
private IL10N $l10n;
|
||||||
private $preview;
|
private IPreview $preview;
|
||||||
private $mimeTypeDetector;
|
private IMimeTypeDetector $mimeTypeDetector;
|
||||||
private $permissionService;
|
private PermissionService $permissionService;
|
||||||
private $cardMapper;
|
private CardMapper $cardMapper;
|
||||||
private $logger;
|
private LoggerInterface $logger;
|
||||||
|
private IDBConnection $connection;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IRequest $request,
|
IRequest $request,
|
||||||
@@ -69,7 +70,8 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
PermissionService $permissionService,
|
PermissionService $permissionService,
|
||||||
CardMapper $cardMapper,
|
CardMapper $cardMapper,
|
||||||
LoggerInterface $logger,
|
LoggerInterface $logger,
|
||||||
string $userId = null
|
IDBConnection $connection,
|
||||||
|
?string $userId
|
||||||
) {
|
) {
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->l10n = $l10n;
|
$this->l10n = $l10n;
|
||||||
@@ -83,6 +85,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
$this->permissionService = $permissionService;
|
$this->permissionService = $permissionService;
|
||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
|
$this->connection = $connection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listAttachments(int $cardId): array {
|
public function listAttachments(int $cardId): array {
|
||||||
@@ -108,9 +111,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getAttachmentCount(int $cardId): int {
|
public function getAttachmentCount(int $cardId): int {
|
||||||
/** @var IDBConnection $qb */
|
$qb = $this->connection->getQueryBuilder();
|
||||||
$db = \OC::$server->getDatabaseConnection();
|
|
||||||
$qb = $db->getQueryBuilder();
|
|
||||||
$qb->select('s.id', 'f.fileid', 'f.path')
|
$qb->select('s.id', 'f.fileid', 'f.path')
|
||||||
->selectAlias('st.id', 'storage_string_id')
|
->selectAlias('st.id', 'storage_string_id')
|
||||||
->from('share', 's')
|
->from('share', 's')
|
||||||
@@ -125,7 +126,7 @@ class FilesAppService implements IAttachmentService, ICustomAttachmentService {
|
|||||||
));
|
));
|
||||||
|
|
||||||
$count = 0;
|
$count = 0;
|
||||||
$cursor = $qb->execute();
|
$cursor = $qb->executeQuery();
|
||||||
while ($data = $cursor->fetch()) {
|
while ($data = $cursor->fetch()) {
|
||||||
if ($this->shareProvider->isAccessibleResult($data)) {
|
if ($this->shareProvider->isAccessibleResult($data)) {
|
||||||
$count++;
|
$count++;
|
||||||
|
|||||||
@@ -47,34 +47,22 @@ use OCP\Comments\ICommentsManager;
|
|||||||
use OCP\Comments\NotFoundException as CommentNotFoundException;
|
use OCP\Comments\NotFoundException as CommentNotFoundException;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
use OCP\IUserManager;
|
use OCP\IUserManager;
|
||||||
|
use OCP\Server;
|
||||||
|
|
||||||
class BoardImportService {
|
class BoardImportService {
|
||||||
/** @var IUserManager */
|
private IUserManager $userManager;
|
||||||
private $userManager;
|
private BoardMapper $boardMapper;
|
||||||
/** @var BoardMapper */
|
private AclMapper $aclMapper;
|
||||||
private $boardMapper;
|
private LabelMapper $labelMapper;
|
||||||
/** @var AclMapper */
|
private StackMapper $stackMapper;
|
||||||
private $aclMapper;
|
private CardMapper $cardMapper;
|
||||||
/** @var LabelMapper */
|
private AssignmentMapper $assignmentMapper;
|
||||||
private $labelMapper;
|
private AttachmentMapper $attachmentMapper;
|
||||||
/** @var StackMapper */
|
private ICommentsManager $commentsManager;
|
||||||
private $stackMapper;
|
private IEventDispatcher $eventDispatcher;
|
||||||
/** @var CardMapper */
|
private string $system = '';
|
||||||
private $cardMapper;
|
private ?ABoardImportService $systemInstance;
|
||||||
/** @var AssignmentMapper */
|
private array $allowedSystems = [];
|
||||||
private $assignmentMapper;
|
|
||||||
/** @var AttachmentMapper */
|
|
||||||
private $attachmentMapper;
|
|
||||||
/** @var ICommentsManager */
|
|
||||||
private $commentsManager;
|
|
||||||
/** @var IEventDispatcher */
|
|
||||||
private $eventDispatcher;
|
|
||||||
/** @var string */
|
|
||||||
private $system = '';
|
|
||||||
/** @var null|ABoardImportService */
|
|
||||||
private $systemInstance;
|
|
||||||
/** @var array */
|
|
||||||
private $allowedSystems = [];
|
|
||||||
/**
|
/**
|
||||||
* Data object created from config JSON
|
* Data object created from config JSON
|
||||||
*
|
*
|
||||||
@@ -89,10 +77,7 @@ class BoardImportService {
|
|||||||
* @psalm-suppress PropertyNotSetInConstructor
|
* @psalm-suppress PropertyNotSetInConstructor
|
||||||
*/
|
*/
|
||||||
private $data;
|
private $data;
|
||||||
/**
|
private Board $board;
|
||||||
* @var Board
|
|
||||||
*/
|
|
||||||
private $board;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IUserManager $userManager,
|
IUserManager $userManager,
|
||||||
@@ -198,7 +183,7 @@ class BoardImportService {
|
|||||||
}
|
}
|
||||||
if (!is_object($this->systemInstance)) {
|
if (!is_object($this->systemInstance)) {
|
||||||
$systemClass = 'OCA\\Deck\\Service\\Importer\\Systems\\' . ucfirst($this->getSystem()) . 'Service';
|
$systemClass = 'OCA\\Deck\\Service\\Importer\\Systems\\' . ucfirst($this->getSystem()) . 'Service';
|
||||||
$this->systemInstance = \OC::$server->get($systemClass);
|
$this->systemInstance = Server::get($systemClass);
|
||||||
$this->systemInstance->setImportService($this);
|
$this->systemInstance->setImportService($this);
|
||||||
}
|
}
|
||||||
return $this->systemInstance;
|
return $this->systemInstance;
|
||||||
@@ -343,7 +328,7 @@ class BoardImportService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function insertAttachment(Attachment $attachment, string $content): Attachment {
|
public function insertAttachment(Attachment $attachment, string $content): Attachment {
|
||||||
$service = \OC::$server->get(FileService::class);
|
$service = Server::get(FileService::class);
|
||||||
$folder = $service->getFolder($attachment);
|
$folder = $service->getFolder($attachment);
|
||||||
|
|
||||||
if ($folder->fileExists($attachment->getData())) {
|
if ($folder->fileExists($attachment->getData())) {
|
||||||
|
|||||||
@@ -91,12 +91,10 @@ class LabelService {
|
|||||||
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
|
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_MANAGE);
|
||||||
|
|
||||||
$boardLabels = $this->labelMapper->findAll($boardId);
|
$boardLabels = $this->labelMapper->findAll($boardId);
|
||||||
if (\is_array($boardLabels)) {
|
foreach ($boardLabels as $boardLabel) {
|
||||||
foreach ($boardLabels as $boardLabel) {
|
if ($boardLabel->getTitle() === $title) {
|
||||||
if ($boardLabel->getTitle() === $title) {
|
throw new BadRequestException('title must be unique');
|
||||||
throw new BadRequestException('title must be unique');
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,15 +149,13 @@ class LabelService {
|
|||||||
$label = $this->find($id);
|
$label = $this->find($id);
|
||||||
|
|
||||||
$boardLabels = $this->labelMapper->findAll($label->getBoardId());
|
$boardLabels = $this->labelMapper->findAll($label->getBoardId());
|
||||||
if (\is_array($boardLabels)) {
|
foreach ($boardLabels as $boardLabel) {
|
||||||
foreach ($boardLabels as $boardLabel) {
|
if ($boardLabel->getId() === $label->getId()) {
|
||||||
if ($boardLabel->getId() === $label->getId()) {
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
if ($boardLabel->getTitle() === $title) {
|
||||||
if ($boardLabel->getTitle() === $title) {
|
throw new BadRequestException('title must be unique');
|
||||||
throw new BadRequestException('title must be unique');
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,31 +30,20 @@ namespace OCA\Deck\Service;
|
|||||||
use OCA\Deck\Db\AssignmentMapper;
|
use OCA\Deck\Db\AssignmentMapper;
|
||||||
use OCA\Deck\Db\Card;
|
use OCA\Deck\Db\Card;
|
||||||
use OCA\Deck\Db\CardMapper;
|
use OCA\Deck\Db\CardMapper;
|
||||||
|
use OCA\Deck\Model\CardDetails;
|
||||||
use OCP\Comments\ICommentsManager;
|
use OCP\Comments\ICommentsManager;
|
||||||
use OCP\IGroupManager;
|
|
||||||
use OCA\Deck\Db\Board;
|
|
||||||
use OCA\Deck\Db\BoardMapper;
|
use OCA\Deck\Db\BoardMapper;
|
||||||
use OCA\Deck\Db\LabelMapper;
|
use OCA\Deck\Db\LabelMapper;
|
||||||
use OCP\IUserManager;
|
use OCP\IUserManager;
|
||||||
|
|
||||||
class OverviewService {
|
class OverviewService {
|
||||||
|
private BoardMapper $boardMapper;
|
||||||
/** @var BoardMapper */
|
private LabelMapper $labelMapper;
|
||||||
private $boardMapper;
|
private CardMapper $cardMapper;
|
||||||
/** @var LabelMapper */
|
private AssignmentMapper $assignedUsersMapper;
|
||||||
private $labelMapper;
|
private IUserManager $userManager;
|
||||||
/** @var CardMapper */
|
private ICommentsManager $commentsManager;
|
||||||
private $cardMapper;
|
private AttachmentService $attachmentService;
|
||||||
/** @var AssignmentMapper */
|
|
||||||
private $assignedUsersMapper;
|
|
||||||
/** @var IUserManager */
|
|
||||||
private $userManager;
|
|
||||||
/** @var IGroupManager */
|
|
||||||
private $groupManager;
|
|
||||||
/** @var ICommentsManager */
|
|
||||||
private $commentsManager;
|
|
||||||
/** @var AttachmentService */
|
|
||||||
private $attachmentService;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
BoardMapper $boardMapper,
|
BoardMapper $boardMapper,
|
||||||
@@ -62,7 +51,6 @@ class OverviewService {
|
|||||||
CardMapper $cardMapper,
|
CardMapper $cardMapper,
|
||||||
AssignmentMapper $assignedUsersMapper,
|
AssignmentMapper $assignedUsersMapper,
|
||||||
IUserManager $userManager,
|
IUserManager $userManager,
|
||||||
IGroupManager $groupManager,
|
|
||||||
ICommentsManager $commentsManager,
|
ICommentsManager $commentsManager,
|
||||||
AttachmentService $attachmentService
|
AttachmentService $attachmentService
|
||||||
) {
|
) {
|
||||||
@@ -71,7 +59,6 @@ class OverviewService {
|
|||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
$this->assignedUsersMapper = $assignedUsersMapper;
|
$this->assignedUsersMapper = $assignedUsersMapper;
|
||||||
$this->userManager = $userManager;
|
$this->userManager = $userManager;
|
||||||
$this->groupManager = $groupManager;
|
|
||||||
$this->commentsManager = $commentsManager;
|
$this->commentsManager = $commentsManager;
|
||||||
$this->attachmentService = $attachmentService;
|
$this->attachmentService = $attachmentService;
|
||||||
}
|
}
|
||||||
@@ -93,62 +80,37 @@ class OverviewService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function findAllWithDue(string $userId): array {
|
public function findAllWithDue(string $userId): array {
|
||||||
$userBoards = $this->findAllBoardsFromUser($userId);
|
$userBoards = $this->boardMapper->findAllForUser($userId);
|
||||||
$allDueCards = [];
|
$allDueCards = [];
|
||||||
foreach ($userBoards as $userBoard) {
|
foreach ($userBoards as $userBoard) {
|
||||||
$service = $this;
|
$allDueCards[] = array_map(function ($card) use ($userBoard, $userId) {
|
||||||
$allDueCards[] = array_map(static function ($card) use ($service, $userBoard, $userId) {
|
$this->enrich($card, $userId);
|
||||||
$service->enrich($card, $userId);
|
return (new CardDetails($card, $userBoard))->jsonSerialize();
|
||||||
$cardData = $card->jsonSerialize();
|
|
||||||
$cardData['boardId'] = $userBoard->getId();
|
|
||||||
return $cardData;
|
|
||||||
}, $this->cardMapper->findAllWithDue($userBoard->getId()));
|
}, $this->cardMapper->findAllWithDue($userBoard->getId()));
|
||||||
}
|
}
|
||||||
return $allDueCards;
|
return array_merge(...$allDueCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function findUpcomingCards(string $userId): array {
|
public function findUpcomingCards(string $userId): array {
|
||||||
$userBoards = $this->findAllBoardsFromUser($userId);
|
$userBoards = $this->boardMapper->findAllForUser($userId);
|
||||||
$findCards = [];
|
$foundCards = [];
|
||||||
foreach ($userBoards as $userBoard) {
|
foreach ($userBoards as $userBoard) {
|
||||||
$service = $this;
|
|
||||||
|
|
||||||
if (count($userBoard->getAcl()) === 0) {
|
if (count($userBoard->getAcl()) === 0) {
|
||||||
// private board: get cards with due date
|
// private board: get cards with due date
|
||||||
$findCards[] = array_map(static function ($card) use ($service, $userBoard, $userId) {
|
$cards = $this->cardMapper->findAllWithDue($userBoard->getId());
|
||||||
$service->enrich($card, $userId);
|
|
||||||
$cardData = $card->jsonSerialize();
|
|
||||||
$cardData['boardId'] = $userBoard->getId();
|
|
||||||
return $cardData;
|
|
||||||
}, $this->cardMapper->findAllWithDue($userBoard->getId()));
|
|
||||||
} else {
|
} else {
|
||||||
// shared board: get all my assigned or unassigned cards
|
// shared board: get all my assigned or unassigned cards
|
||||||
$findCards[] = array_map(static function ($card) use ($service, $userBoard, $userId) {
|
$cards = $this->cardMapper->findToMeOrNotAssignedCards($userBoard->getId(), $userId);
|
||||||
$service->enrich($card, $userId);
|
|
||||||
$cardData = $card->jsonSerialize();
|
|
||||||
$cardData['boardId'] = $userBoard->getId();
|
|
||||||
return $cardData;
|
|
||||||
}, $this->cardMapper->findToMeOrNotAssignedCards($userBoard->getId(), $userId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$foundCards[] = array_map(
|
||||||
|
function (Card $card) use ($userBoard, $userId) {
|
||||||
|
$this->enrich($card, $userId);
|
||||||
|
return (new CardDetails($card, $userBoard))->jsonSerialize();
|
||||||
|
},
|
||||||
|
$cards
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return $findCards;
|
return array_merge(...$foundCards);
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: This is duplicate code with the board service
|
|
||||||
private function findAllBoardsFromUser(string $userId): array {
|
|
||||||
$userInfo = $this->getBoardPrerequisites($userId);
|
|
||||||
$userBoards = $this->boardMapper->findAllByUser($userInfo['user'], null, null);
|
|
||||||
$groupBoards = $this->boardMapper->findAllByGroups($userInfo['user'], $userInfo['groups'], null, null);
|
|
||||||
$circleBoards = $this->boardMapper->findAllByCircles($userInfo['user'], null, null);
|
|
||||||
return array_unique(array_merge($userBoards, $groupBoards, $circleBoards));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getBoardPrerequisites($userId): array {
|
|
||||||
$user = $this->userManager->get($userId);
|
|
||||||
$groups = $user !== null ? $this->groupManager->getUserGroupIds($user) : [];
|
|
||||||
return [
|
|
||||||
'user' => $userId,
|
|
||||||
'groups' => $groups
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
namespace OCA\Deck\Service;
|
namespace OCA\Deck\Service;
|
||||||
|
|
||||||
use OC\Cache\CappedMemoryCache;
|
use OCP\Cache\CappedMemoryCache;
|
||||||
use OCA\Circles\Model\Member;
|
use OCA\Circles\Model\Member;
|
||||||
use OCA\Deck\Db\Acl;
|
use OCA\Deck\Db\Acl;
|
||||||
use OCA\Deck\Db\AclMapper;
|
use OCA\Deck\Db\AclMapper;
|
||||||
@@ -63,8 +63,8 @@ class PermissionService {
|
|||||||
/** @var array */
|
/** @var array */
|
||||||
private $users = [];
|
private $users = [];
|
||||||
|
|
||||||
private $boardCache;
|
private CappedMemoryCache $boardCache;
|
||||||
private $permissionCache;
|
private CappedMemoryCache $permissionCache;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
ILogger $logger,
|
ILogger $logger,
|
||||||
|
|||||||
@@ -30,28 +30,32 @@ use OCA\Deck\BadRequestException;
|
|||||||
use OCA\Deck\Db\Acl;
|
use OCA\Deck\Db\Acl;
|
||||||
use OCA\Deck\Db\AssignmentMapper;
|
use OCA\Deck\Db\AssignmentMapper;
|
||||||
use OCA\Deck\Db\BoardMapper;
|
use OCA\Deck\Db\BoardMapper;
|
||||||
|
use OCA\Deck\Db\Card;
|
||||||
use OCA\Deck\Db\CardMapper;
|
use OCA\Deck\Db\CardMapper;
|
||||||
use OCA\Deck\Db\ChangeHelper;
|
use OCA\Deck\Db\ChangeHelper;
|
||||||
use OCA\Deck\Db\LabelMapper;
|
use OCA\Deck\Db\LabelMapper;
|
||||||
use OCA\Deck\Db\Stack;
|
use OCA\Deck\Db\Stack;
|
||||||
use OCA\Deck\Db\StackMapper;
|
use OCA\Deck\Db\StackMapper;
|
||||||
|
use OCA\Deck\Model\CardDetails;
|
||||||
use OCA\Deck\NoPermissionException;
|
use OCA\Deck\NoPermissionException;
|
||||||
use OCA\Deck\StatusException;
|
use OCA\Deck\StatusException;
|
||||||
use OCA\Deck\Validators\StackServiceValidator;
|
use OCA\Deck\Validators\StackServiceValidator;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
class StackService {
|
class StackService {
|
||||||
private $stackMapper;
|
private StackMapper $stackMapper;
|
||||||
private $cardMapper;
|
private CardMapper $cardMapper;
|
||||||
private $boardMapper;
|
private BoardMapper $boardMapper;
|
||||||
private $labelMapper;
|
private LabelMapper $labelMapper;
|
||||||
private $permissionService;
|
private PermissionService $permissionService;
|
||||||
private $boardService;
|
private BoardService $boardService;
|
||||||
private $cardService;
|
private CardService $cardService;
|
||||||
private $assignedUsersMapper;
|
private AssignmentMapper $assignedUsersMapper;
|
||||||
private $attachmentService;
|
private AttachmentService $attachmentService;
|
||||||
private $activityManager;
|
private ActivityManager $activityManager;
|
||||||
private $changeHelper;
|
private ChangeHelper $changeHelper;
|
||||||
private $stackServiceValidator;
|
private LoggerInterface $logger;
|
||||||
|
private StackServiceValidator $stackServiceValidator;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
StackMapper $stackMapper,
|
StackMapper $stackMapper,
|
||||||
@@ -64,8 +68,9 @@ class StackService {
|
|||||||
AssignmentMapper $assignedUsersMapper,
|
AssignmentMapper $assignedUsersMapper,
|
||||||
AttachmentService $attachmentService,
|
AttachmentService $attachmentService,
|
||||||
ActivityManager $activityManager,
|
ActivityManager $activityManager,
|
||||||
StackServiceValidator $stackServiceValidator,
|
ChangeHelper $changeHelper,
|
||||||
ChangeHelper $changeHelper
|
LoggerInterface $logger,
|
||||||
|
StackServiceValidator $stackServiceValidator
|
||||||
) {
|
) {
|
||||||
$this->stackMapper = $stackMapper;
|
$this->stackMapper = $stackMapper;
|
||||||
$this->boardMapper = $boardMapper;
|
$this->boardMapper = $boardMapper;
|
||||||
@@ -78,6 +83,7 @@ class StackService {
|
|||||||
$this->attachmentService = $attachmentService;
|
$this->attachmentService = $attachmentService;
|
||||||
$this->activityManager = $activityManager;
|
$this->activityManager = $activityManager;
|
||||||
$this->changeHelper = $changeHelper;
|
$this->changeHelper = $changeHelper;
|
||||||
|
$this->logger = $logger;
|
||||||
$this->stackServiceValidator = $stackServiceValidator;
|
$this->stackServiceValidator = $stackServiceValidator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,9 +94,13 @@ class StackService {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($cards as $card) {
|
$cards = array_map(
|
||||||
$this->cardService->enrich($card);
|
function (Card $card): CardDetails {
|
||||||
}
|
$this->cardService->enrich($card);
|
||||||
|
return new CardDetails($card);
|
||||||
|
},
|
||||||
|
$cards
|
||||||
|
);
|
||||||
|
|
||||||
$stack->setCards($cards);
|
$stack->setCards($cards);
|
||||||
}
|
}
|
||||||
@@ -116,12 +126,18 @@ class StackService {
|
|||||||
|
|
||||||
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_READ);
|
$this->permissionService->checkPermission($this->stackMapper, $stackId, Acl::PERMISSION_READ);
|
||||||
$stack = $this->stackMapper->find($stackId);
|
$stack = $this->stackMapper->find($stackId);
|
||||||
$cards = $this->cardMapper->findAll($stackId);
|
|
||||||
foreach ($cards as $cardIndex => $card) {
|
$cards = array_map(
|
||||||
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
function (Card $card): CardDetails {
|
||||||
$card->setAssignedUsers($assignedUsers);
|
$assignedUsers = $this->assignedUsersMapper->findAll($card->getId());
|
||||||
$card->setAttachmentCount($this->attachmentService->count($card->getId()));
|
$card->setAssignedUsers($assignedUsers);
|
||||||
}
|
$card->setAttachmentCount($this->attachmentService->count($card->getId()));
|
||||||
|
|
||||||
|
return new CardDetails($card);
|
||||||
|
},
|
||||||
|
$this->cardMapper->findAll($stackId)
|
||||||
|
);
|
||||||
|
|
||||||
$stack->setCards($cards);
|
$stack->setCards($cards);
|
||||||
|
|
||||||
return $stack;
|
return $stack;
|
||||||
@@ -150,7 +166,7 @@ class StackService {
|
|||||||
try {
|
try {
|
||||||
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
|
$this->permissionService->checkPermission(null, $boardId, Acl::PERMISSION_READ);
|
||||||
} catch (NoPermissionException $e) {
|
} catch (NoPermissionException $e) {
|
||||||
\OC::$server->getLogger()->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
$this->logger->error('Unable to check permission for a previously obtained board ' . $boardId, ['exception' => $e]);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
return $this->stackMapper->findAll($boardId);
|
return $this->stackMapper->findAll($boardId);
|
||||||
@@ -310,6 +326,7 @@ class StackService {
|
|||||||
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
|
$this->permissionService->checkPermission($this->stackMapper, $id, Acl::PERMISSION_MANAGE);
|
||||||
$stackToSort = $this->stackMapper->find($id);
|
$stackToSort = $this->stackMapper->find($id);
|
||||||
$stacks = $this->stackMapper->findAll($stackToSort->getBoardId());
|
$stacks = $this->stackMapper->findAll($stackToSort->getBoardId());
|
||||||
|
usort($stacks, static fn (Stack $stackA, Stack $stackB) => $stackA->getOrder() - $stackB->getOrder());
|
||||||
$result = [];
|
$result = [];
|
||||||
$i = 0;
|
$i = 0;
|
||||||
foreach ($stacks as $stack) {
|
foreach ($stacks as $stack) {
|
||||||
|
|||||||
@@ -65,22 +65,16 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
|
|
||||||
public const SHARE_TYPE_DECK_USER = IShare::TYPE_DECK_USER;
|
public const SHARE_TYPE_DECK_USER = IShare::TYPE_DECK_USER;
|
||||||
|
|
||||||
/** @var IDBConnection */
|
private IDBConnection $dbConnection;
|
||||||
private $dbConnection;
|
private IManager $shareManager;
|
||||||
/** @var IManager */
|
private AttachmentCacheHelper $attachmentCacheHelper;
|
||||||
private $shareManager;
|
private BoardMapper $boardMapper;
|
||||||
/** @var AttachmentCacheHelper */
|
private CardMapper $cardMapper;
|
||||||
private $attachmentCacheHelper;
|
private PermissionService $permissionService;
|
||||||
/** @var BoardMapper */
|
private ITimeFactory $timeFactory;
|
||||||
private $boardMapper;
|
private IL10N $l;
|
||||||
/** @var CardMapper */
|
private IMimeTypeLoader $mimeTypeLoader;
|
||||||
private $cardMapper;
|
private ?string $userId;
|
||||||
/** @var PermissionService */
|
|
||||||
private $permissionService;
|
|
||||||
/** @var ITimeFactory */
|
|
||||||
private $timeFactory;
|
|
||||||
/** @var IL10N */
|
|
||||||
private $l;
|
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
IDBConnection $connection,
|
IDBConnection $connection,
|
||||||
@@ -89,7 +83,10 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
CardMapper $cardMapper,
|
CardMapper $cardMapper,
|
||||||
PermissionService $permissionService,
|
PermissionService $permissionService,
|
||||||
AttachmentCacheHelper $attachmentCacheHelper,
|
AttachmentCacheHelper $attachmentCacheHelper,
|
||||||
IL10N $l
|
IL10N $l,
|
||||||
|
ITimeFactory $timeFactory,
|
||||||
|
IMimeTypeLoader $mimeTypeLoader,
|
||||||
|
?string $userId
|
||||||
) {
|
) {
|
||||||
$this->dbConnection = $connection;
|
$this->dbConnection = $connection;
|
||||||
$this->shareManager = $shareManager;
|
$this->shareManager = $shareManager;
|
||||||
@@ -97,9 +94,10 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
$this->cardMapper = $cardMapper;
|
$this->cardMapper = $cardMapper;
|
||||||
$this->attachmentCacheHelper = $attachmentCacheHelper;
|
$this->attachmentCacheHelper = $attachmentCacheHelper;
|
||||||
$this->permissionService = $permissionService;
|
$this->permissionService = $permissionService;
|
||||||
|
|
||||||
$this->l = $l;
|
$this->l = $l;
|
||||||
$this->timeFactory = \OC::$server->get(ITimeFactory::class);
|
$this->timeFactory = $timeFactory;
|
||||||
|
$this->mimeTypeLoader = $mimeTypeLoader;
|
||||||
|
$this->userId = $userId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function register(IEventDispatcher $dispatcher): void {
|
public static function register(IEventDispatcher $dispatcher): void {
|
||||||
@@ -207,13 +205,13 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
->setValue('file_target', $qb->createNamedParameter($target))
|
->setValue('file_target', $qb->createNamedParameter($target))
|
||||||
->setValue('permissions', $qb->createNamedParameter($permissions))
|
->setValue('permissions', $qb->createNamedParameter($permissions))
|
||||||
->setValue('token', $qb->createNamedParameter($token))
|
->setValue('token', $qb->createNamedParameter($token))
|
||||||
->setValue('stime', $qb->createNamedParameter(\OC::$server->get(ITimeFactory::class)->getTime()));
|
->setValue('stime', $qb->createNamedParameter($this->timeFactory->getTime()));
|
||||||
|
|
||||||
if ($expirationDate !== null) {
|
if ($expirationDate !== null) {
|
||||||
$qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
|
$qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$qb->execute();
|
$qb->executeStatement();
|
||||||
|
|
||||||
return $qb->getLastInsertId();
|
return $qb->getLastInsertId();
|
||||||
}
|
}
|
||||||
@@ -281,7 +279,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
$entryData = $data;
|
$entryData = $data;
|
||||||
$entryData['permissions'] = $entryData['f_permissions'];
|
$entryData['permissions'] = $entryData['f_permissions'];
|
||||||
$entryData['parent'] = $entryData['f_parent'];
|
$entryData['parent'] = $entryData['f_parent'];
|
||||||
$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData, \OC::$server->get(IMimeTypeLoader::class)));
|
$share->setNodeCacheEntry(Cache::cacheEntryFromData($entryData, $this->mimeTypeLoader));
|
||||||
}
|
}
|
||||||
return $share;
|
return $share;
|
||||||
}
|
}
|
||||||
@@ -474,14 +472,14 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
'file_target' => $qb->createNamedParameter($share->getTarget()),
|
'file_target' => $qb->createNamedParameter($share->getTarget()),
|
||||||
'permissions' => $qb->createNamedParameter($share->getPermissions()),
|
'permissions' => $qb->createNamedParameter($share->getPermissions()),
|
||||||
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
|
'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
|
||||||
])->execute();
|
])->executeStatement();
|
||||||
} else {
|
} else {
|
||||||
// Already a userroom share. Update it.
|
// Already a userroom share. Update it.
|
||||||
$qb = $this->dbConnection->getQueryBuilder();
|
$qb = $this->dbConnection->getQueryBuilder();
|
||||||
$qb->update('share')
|
$qb->update('share')
|
||||||
->set('file_target', $qb->createNamedParameter($share->getTarget()))
|
->set('file_target', $qb->createNamedParameter($share->getTarget()))
|
||||||
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
|
->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
|
||||||
->execute();
|
->executeStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $share;
|
return $share;
|
||||||
@@ -491,7 +489,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
public function getSharesInFolder($userId, Folder $node, $reshares) {
|
public function getSharesInFolder($userId, Folder $node, $reshares, $shallow = true) {
|
||||||
$qb = $this->dbConnection->getQueryBuilder();
|
$qb = $this->dbConnection->getQueryBuilder();
|
||||||
$qb->select('*')
|
$qb->select('*')
|
||||||
->from('share', 's')
|
->from('share', 's')
|
||||||
@@ -518,7 +516,11 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
|
$qb->innerJoin('s', 'filecache', 'f', $qb->expr()->eq('s.file_source', 'f.fileid'));
|
||||||
$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
|
if ($shallow) {
|
||||||
|
$qb->andWhere($qb->expr()->eq('f.parent', $qb->createNamedParameter($node->getId())));
|
||||||
|
} else {
|
||||||
|
$qb->andWhere($qb->expr()->like('f.path', $qb->createNamedParameter($this->dbConnection->escapeLikeParameter($node->getInternalPath()) . '/%')));
|
||||||
|
}
|
||||||
|
|
||||||
$qb->orderBy('s.id');
|
$qb->orderBy('s.id');
|
||||||
|
|
||||||
@@ -632,8 +634,8 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
$start = 0;
|
$start = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
/** @var IShare[] $shareSlice */
|
/** @var IShare[] $shareSlice */
|
||||||
$shareSlice = array_slice($shares, $start, 100);
|
$shareSlice = array_slice($shares, $start, 1000);
|
||||||
$start += 100;
|
$start += 1000;
|
||||||
|
|
||||||
if ($shareSlice === []) {
|
if ($shareSlice === []) {
|
||||||
break;
|
break;
|
||||||
@@ -712,15 +714,15 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
* @return IShare[]
|
* @return IShare[]
|
||||||
*/
|
*/
|
||||||
public function getSharedWith($userId, $shareType, $node, $limit, $offset): array {
|
public function getSharedWith($userId, $shareType, $node, $limit, $offset): array {
|
||||||
$allBoards = $this->boardMapper->findAllForUser($userId);
|
$allBoards = $this->boardMapper->findBoardIds($userId);
|
||||||
|
|
||||||
/** @var IShare[] $shares */
|
/** @var IShare[] $shares */
|
||||||
$shares = [];
|
$shares = [];
|
||||||
|
|
||||||
$start = 0;
|
$start = 0;
|
||||||
while (true) {
|
while (true) {
|
||||||
$boards = array_slice($allBoards, $start, 100);
|
$boards = array_slice($allBoards, $start, 1000);
|
||||||
$start += 100;
|
$start += 1000;
|
||||||
|
|
||||||
if ($boards === []) {
|
if ($boards === []) {
|
||||||
break;
|
break;
|
||||||
@@ -750,10 +752,6 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
$qb->andWhere($qb->expr()->eq('s.file_source', $qb->createNamedParameter($node->getId())));
|
$qb->andWhere($qb->expr()->eq('s.file_source', $qb->createNamedParameter($node->getId())));
|
||||||
}
|
}
|
||||||
|
|
||||||
$boards = array_map(function (Board $board) {
|
|
||||||
return $board->getId();
|
|
||||||
}, $boards);
|
|
||||||
|
|
||||||
$qb->andWhere($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
|
$qb->andWhere($qb->expr()->eq('s.share_type', $qb->createNamedParameter(IShare::TYPE_DECK)))
|
||||||
->andWhere($qb->expr()->in('db.id', $qb->createNamedParameter(
|
->andWhere($qb->expr()->in('db.id', $qb->createNamedParameter(
|
||||||
$boards,
|
$boards,
|
||||||
@@ -821,7 +819,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
$qb->expr()->eq('s.item_type', $qb->createNamedParameter('folder'))
|
$qb->expr()->eq('s.item_type', $qb->createNamedParameter('folder'))
|
||||||
));
|
));
|
||||||
|
|
||||||
$cursor = $qb->execute();
|
$cursor = $qb->executeQuery();
|
||||||
while ($data = $cursor->fetch()) {
|
while ($data = $cursor->fetch()) {
|
||||||
if (!$this->isAccessibleResult($data)) {
|
if (!$this->isAccessibleResult($data)) {
|
||||||
continue;
|
continue;
|
||||||
@@ -836,9 +834,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
}
|
}
|
||||||
$cursor->closeCursor();
|
$cursor->closeCursor();
|
||||||
|
|
||||||
$shares = $this->resolveSharesForRecipient($shares, \OC::$server->getUserSession()->getUser()->getUID());
|
return $this->resolveSharesForRecipient($shares, $this->userId);
|
||||||
|
|
||||||
return $shares;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isAccessibleResult(array $data): bool {
|
public function isAccessibleResult(array $data): bool {
|
||||||
@@ -941,7 +937,7 @@ class DeckShareProvider implements \OCP\Share\IShareProvider {
|
|||||||
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('file')),
|
||||||
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
|
$qb->expr()->eq('item_type', $qb->createNamedParameter('folder'))
|
||||||
));
|
));
|
||||||
$cursor = $qb->execute();
|
$cursor = $qb->executeQuery();
|
||||||
|
|
||||||
$users = [];
|
$users = [];
|
||||||
while ($row = $cursor->fetch()) {
|
while ($row = $cursor->fetch()) {
|
||||||
|
|||||||
@@ -29,14 +29,13 @@ namespace OCA\Deck\Sharing;
|
|||||||
use OC\Files\Filesystem;
|
use OC\Files\Filesystem;
|
||||||
use OCA\Deck\Service\ConfigService;
|
use OCA\Deck\Service\ConfigService;
|
||||||
use OCP\EventDispatcher\IEventDispatcher;
|
use OCP\EventDispatcher\IEventDispatcher;
|
||||||
|
use OCP\Server;
|
||||||
use OCP\Share\Events\VerifyMountPointEvent;
|
use OCP\Share\Events\VerifyMountPointEvent;
|
||||||
use OCP\Share\IShare;
|
use OCP\Share\IShare;
|
||||||
use Symfony\Component\EventDispatcher\GenericEvent;
|
use Symfony\Component\EventDispatcher\GenericEvent;
|
||||||
|
|
||||||
class Listener {
|
class Listener {
|
||||||
|
private ConfigService $configService;
|
||||||
/** @var ConfigService */
|
|
||||||
private $configService;
|
|
||||||
|
|
||||||
public function __construct(ConfigService $configService) {
|
public function __construct(ConfigService $configService) {
|
||||||
$this->configService = $configService;
|
$this->configService = $configService;
|
||||||
@@ -52,13 +51,13 @@ class Listener {
|
|||||||
|
|
||||||
public static function listenPreShare(GenericEvent $event): void {
|
public static function listenPreShare(GenericEvent $event): void {
|
||||||
/** @var self $listener */
|
/** @var self $listener */
|
||||||
$listener = \OC::$server->query(self::class);
|
$listener = Server::get(self::class);
|
||||||
$listener->overwriteShareTarget($event);
|
$listener->overwriteShareTarget($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function listenVerifyMountPointEvent(VerifyMountPointEvent $event): void {
|
public static function listenVerifyMountPointEvent(VerifyMountPointEvent $event): void {
|
||||||
/** @var self $listener */
|
/** @var self $listener */
|
||||||
$listener = \OC::$server->query(self::class);
|
$listener = Server::get(self::class);
|
||||||
$listener->overwriteMountPoint($event);
|
$listener->overwriteMountPoint($event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
14173
package-lock.json
generated
63
package.json
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "deck",
|
"name": "deck",
|
||||||
"description": "",
|
"description": "",
|
||||||
"version": "1.7.4",
|
"version": "1.8.5",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Julius Härtl",
|
"name": "Julius Härtl",
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
"build": "NODE_ENV=production webpack --progress --config webpack.js",
|
"build": "NODE_ENV=production webpack --progress --config webpack.js",
|
||||||
"dev": "NODE_ENV=development webpack --progress --config webpack.js",
|
"dev": "NODE_ENV=development webpack --progress --config webpack.js",
|
||||||
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.js",
|
"watch": "NODE_ENV=development webpack --progress --watch --config webpack.js",
|
||||||
|
"serve": "webpack serve --node-env development --allowed-hosts all --config webpack.js",
|
||||||
"lint": "eslint --ext .js,.vue src",
|
"lint": "eslint --ext .js,.vue src",
|
||||||
"lint:fix": "eslint --ext .js,.vue src --fix",
|
"lint:fix": "eslint --ext .js,.vue src --fix",
|
||||||
"stylelint": "stylelint src",
|
"stylelint": "stylelint src",
|
||||||
@@ -29,35 +30,37 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/polyfill": "^7.12.1",
|
"@babel/polyfill": "^7.12.1",
|
||||||
"@babel/runtime": "^7.17.9",
|
"@babel/runtime": "^7.19.0",
|
||||||
"@juliushaertl/vue-richtext": "^1.0.1",
|
"@juliushaertl/vue-richtext": "^1.0.1",
|
||||||
"@nextcloud/auth": "^1.3.0",
|
"@nextcloud/auth": "^2.0.0",
|
||||||
"@nextcloud/axios": "^1.9.0",
|
"@nextcloud/axios": "^2.0.0",
|
||||||
"@nextcloud/dialogs": "^3.1.2",
|
"@nextcloud/dialogs": "^3.2.0",
|
||||||
"@nextcloud/event-bus": "^2.1.1",
|
"@nextcloud/event-bus": "^3.0.2",
|
||||||
"@nextcloud/files": "^2.1.0",
|
"@nextcloud/files": "^2.1.0",
|
||||||
"@nextcloud/initial-state": "^1.2.1",
|
"@nextcloud/initial-state": "^2.0.0",
|
||||||
"@nextcloud/l10n": "^1.4.1",
|
"@nextcloud/l10n": "^1.6.0",
|
||||||
"@nextcloud/moment": "^1.2.0",
|
"@nextcloud/moment": "^1.2.1",
|
||||||
"@nextcloud/router": "^2.0.0",
|
"@nextcloud/router": "^2.0.0",
|
||||||
"@nextcloud/vue": "^5.3.1",
|
"@nextcloud/vue": "^7.3.0",
|
||||||
"@nextcloud/vue-dashboard": "^2.0.1",
|
"@nextcloud/vue-dashboard": "^2.0.1",
|
||||||
|
"@nextcloud/vue-richtext": "^2.0.1",
|
||||||
"blueimp-md5": "^2.19.0",
|
"blueimp-md5": "^2.19.0",
|
||||||
"dompurify": "^2.3.6",
|
"dompurify": "^2.4.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"markdown-it": "^12.3.2",
|
"markdown-it": "^13.0.1",
|
||||||
"markdown-it-link-attributes": "^4.0.0",
|
"markdown-it-link-attributes": "^4.0.1",
|
||||||
"markdown-it-task-checkbox": "^1.0.6",
|
"markdown-it-task-checkbox": "^1.0.6",
|
||||||
"moment": "^2.29.2",
|
"moment": "^2.29.4",
|
||||||
"nextcloud-vue-collections": "^0.9.0",
|
"nextcloud-vue-collections": "^0.10.0",
|
||||||
"p-queue": "^6.6.2",
|
"p-queue": "^7.3.0",
|
||||||
"url-search-params-polyfill": "^8.1.1",
|
"url-search-params-polyfill": "^8.1.1",
|
||||||
"vue": "^2.6.14",
|
"vue": "^2.7.9",
|
||||||
"vue-at": "^2.5.0-beta.2",
|
"vue-at": "^2.5.0",
|
||||||
"vue-click-outside": "^1.1.0",
|
"vue-click-outside": "^1.1.0",
|
||||||
"vue-easymde": "^2.0.0",
|
"vue-easymde": "^2.0.0",
|
||||||
"vue-infinite-loading": "^2.4.5",
|
"vue-infinite-loading": "^2.4.5",
|
||||||
"vue-router": "^3.5.3",
|
"vue-material-design-icons": "^5.1.2",
|
||||||
|
"vue-router": "^3.6.5",
|
||||||
"vue-smooth-dnd": "^0.8.1",
|
"vue-smooth-dnd": "^0.8.1",
|
||||||
"vuex": "^3.6.2",
|
"vuex": "^3.6.2",
|
||||||
"vuex-router-sync": "^5.0.0"
|
"vuex-router-sync": "^5.0.0"
|
||||||
@@ -66,20 +69,24 @@
|
|||||||
"extends @nextcloud/browserslist-config"
|
"extends @nextcloud/browserslist-config"
|
||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^14.0.0",
|
"node": "^16.0.0",
|
||||||
"npm": "^7.0.0"
|
"npm": "^7.0.0 || ^8.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nextcloud/babel-config": "^1.0.0",
|
"@nextcloud/babel-config": "^1.0.0",
|
||||||
"@nextcloud/browserslist-config": "^2.2.0",
|
"@nextcloud/browserslist-config": "^2.3.0",
|
||||||
"@nextcloud/eslint-config": "^6.1.2",
|
"@nextcloud/eslint-config": "^8.0.0",
|
||||||
"@nextcloud/stylelint-config": "^2.1.2",
|
"@nextcloud/stylelint-config": "^2.2.0",
|
||||||
"@nextcloud/webpack-vue-config": "^5.0.0",
|
"@nextcloud/webpack-vue-config": "^5.3.0",
|
||||||
"@relative-ci/agent": "^3.1.2",
|
"@relative-ci/agent": "^4.1.0",
|
||||||
"@vue/test-utils": "^1.3.0",
|
"@vue/test-utils": "^1.3.0",
|
||||||
"jest": "^27.5.1",
|
"cypress": "^10.8.0",
|
||||||
|
"eslint-webpack-plugin": "^3.2.0",
|
||||||
|
"jest": "^29.0.1",
|
||||||
"jest-serializer-vue": "^2.0.2",
|
"jest-serializer-vue": "^2.0.2",
|
||||||
"vue-jest": "^3.0.7"
|
"stylelint-webpack-plugin": "^3.3.0",
|
||||||
|
"vue-jest": "^3.0.7",
|
||||||
|
"vue-template-compiler": "^2.7.9"
|
||||||
},
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"moduleFileExtensions": [
|
"moduleFileExtensions": [
|
||||||
|
|||||||
@@ -7,6 +7,9 @@
|
|||||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||||
errorBaseline="tests/psalm-baseline.xml"
|
errorBaseline="tests/psalm-baseline.xml"
|
||||||
>
|
>
|
||||||
|
<stubs>
|
||||||
|
<file name="tests/stub.phpstub" preloadClasses="true"/>
|
||||||
|
</stubs>
|
||||||
<projectFiles>
|
<projectFiles>
|
||||||
<directory name="lib" />
|
<directory name="lib" />
|
||||||
<ignoreFiles>
|
<ignoreFiles>
|
||||||
|
|||||||
59
src/App.vue
@@ -21,14 +21,13 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Content id="content" app-name="deck" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
|
<NcContent app-name="deck" :class="{ 'nav-hidden': !navShown, 'sidebar-hidden': !sidebarRouterView }">
|
||||||
<AppNavigation />
|
<AppNavigation />
|
||||||
<AppContent>
|
<NcAppContent>
|
||||||
<router-view />
|
<router-view />
|
||||||
</AppContent>
|
</NcAppContent>
|
||||||
|
|
||||||
<Modal
|
<NcModal v-if="cardDetailsInModal && $route.params.cardId"
|
||||||
v-if="cardDetailsInModal && $route.params.cardId"
|
|
||||||
:clear-view-delay="0"
|
:clear-view-delay="0"
|
||||||
:title="t('deck', 'Card details')"
|
:title="t('deck', 'Card details')"
|
||||||
size="large"
|
size="large"
|
||||||
@@ -36,18 +35,17 @@
|
|||||||
<div class="modal__content modal__card">
|
<div class="modal__content modal__card">
|
||||||
<router-view name="sidebar" />
|
<router-view name="sidebar" />
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</NcModal>
|
||||||
|
|
||||||
<router-view name="sidebar" :visible="!cardDetailsInModal || !$route.params.cardId" />
|
<router-view name="sidebar" :visible="!cardDetailsInModal || !$route.params.cardId" />
|
||||||
</Content>
|
</NcContent>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
import { mapState } from 'vuex'
|
import { mapState } from 'vuex'
|
||||||
import AppNavigation from './components/navigation/AppNavigation'
|
import AppNavigation from './components/navigation/AppNavigation.vue'
|
||||||
import { Modal, Content, AppContent } from '@nextcloud/vue'
|
import { NcModal, NcContent, NcAppContent } from '@nextcloud/vue'
|
||||||
import { BoardApi } from './services/BoardApi'
|
import { BoardApi } from './services/BoardApi.js'
|
||||||
import { emit, subscribe } from '@nextcloud/event-bus'
|
import { emit, subscribe } from '@nextcloud/event-bus'
|
||||||
|
|
||||||
const boardApi = new BoardApi()
|
const boardApi = new BoardApi()
|
||||||
@@ -56,9 +54,9 @@ export default {
|
|||||||
name: 'App',
|
name: 'App',
|
||||||
components: {
|
components: {
|
||||||
AppNavigation,
|
AppNavigation,
|
||||||
Modal,
|
NcModal,
|
||||||
Content,
|
NcContent,
|
||||||
AppContent,
|
NcAppContent,
|
||||||
},
|
},
|
||||||
provide() {
|
provide() {
|
||||||
return {
|
return {
|
||||||
@@ -130,7 +128,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
||||||
#content {
|
#content-vue {
|
||||||
#app-content {
|
#app-content {
|
||||||
transition: margin-left 100ms ease;
|
transition: margin-left 100ms ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
@@ -158,6 +156,37 @@ export default {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
@import '../css/print';
|
||||||
|
|
||||||
|
.icon-activity {
|
||||||
|
background-image: url(../img/activity-dark.svg);
|
||||||
|
|
||||||
|
body[data-theme-dark] & {
|
||||||
|
background-image: url(../img/activity.svg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatardiv.circles {
|
||||||
|
background: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-circles {
|
||||||
|
background-image: url(../img/circles-dark.svg);
|
||||||
|
opacity: 1;
|
||||||
|
background-size: 20px;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-circles-white, .icon-circles.icon-white {
|
||||||
|
background-image: url(../img/circles.svg);
|
||||||
|
opacity: 1;
|
||||||
|
background-size: 20px;
|
||||||
|
background-position: center center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-colorpicker {
|
||||||
|
background-image: url('../img/color_picker.svg');
|
||||||
|
}
|
||||||
|
|
||||||
.multiselect {
|
.multiselect {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal @close="close">
|
<NcModal @close="close">
|
||||||
<div id="modal-inner" :class="{ 'icon-loading': loading }">
|
<div id="modal-inner" :class="{ 'icon-loading': loading }">
|
||||||
<h1>{{ t('deck', 'Select the board to link to a project') }}</h1>
|
<h1>{{ t('deck', 'Select the board to link to a project') }}</h1>
|
||||||
<input v-model="filter" type="text" :placeholder="t('deck', 'Search by board title')">
|
<input v-model="filter" type="text" :placeholder="t('deck', 'Search by board title')">
|
||||||
@@ -38,17 +38,17 @@
|
|||||||
{{ t('deck', 'Select board') }}
|
{{ t('deck', 'Select board') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</NcModal>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Modal from '@nextcloud/vue/dist/Components/Modal'
|
import { NcModal } from '@nextcloud/vue'
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { generateUrl } from '@nextcloud/router'
|
import { generateUrl } from '@nextcloud/router'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'BoardSelector',
|
name: 'BoardSelector',
|
||||||
components: {
|
components: {
|
||||||
Modal,
|
NcModal,
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -21,11 +21,11 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="card-selector" @close="close">
|
<NcModal class="card-selector" @close="close">
|
||||||
<div class="modal-scroller">
|
<div class="modal-scroller">
|
||||||
<div v-if="!creating && !created" id="modal-inner" :class="{ 'icon-loading': loading }">
|
<div v-if="!creating && !created" id="modal-inner" :class="{ 'icon-loading': loading }">
|
||||||
<h3>{{ t('deck', 'Create a new card') }}</h3>
|
<h3>{{ t('deck', 'Create a new card') }}</h3>
|
||||||
<Multiselect v-model="selectedBoard"
|
<NcMultiselect v-model="selectedBoard"
|
||||||
:placeholder="t('deck', 'Select a board')"
|
:placeholder="t('deck', 'Select a board')"
|
||||||
:options="boards"
|
:options="boards"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
@@ -44,9 +44,9 @@
|
|||||||
<span>{{ props.option.title }}</span>
|
<span>{{ props.option.title }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Multiselect>
|
</NcMultiselect>
|
||||||
|
|
||||||
<Multiselect v-model="selectedStack"
|
<NcMultiselect v-model="selectedStack"
|
||||||
:placeholder="t('deck', 'Select a list')"
|
:placeholder="t('deck', 'Select a list')"
|
||||||
:options="stacksFromBoard"
|
:options="stacksFromBoard"
|
||||||
:max-height="100"
|
:max-height="100"
|
||||||
@@ -71,12 +71,22 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-else id="modal-inner">
|
<div v-else id="modal-inner">
|
||||||
<EmptyContent v-if="creating" icon="icon-loading">
|
<NcEmptyContent v-if="creating">
|
||||||
{{ t('deck', 'Creating the new card …') }}
|
<template #icon>
|
||||||
</EmptyContent>
|
<NcLoadingIcon />
|
||||||
<EmptyContent v-else-if="created" icon="icon-checkmark">
|
</template>
|
||||||
{{ t('deck', 'Card "{card}" was added to "{board}"', { card: pendingTitle, board: selectedBoard.title }) }}
|
<template #title>
|
||||||
<template #desc>
|
{{ t('deck', 'Creating the new card …') }}
|
||||||
|
</template>
|
||||||
|
</NcEmptyContent>
|
||||||
|
<NcEmptyContent v-else-if="created">
|
||||||
|
<template #icon>
|
||||||
|
<CardPlusOutline />
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
{{ t('deck', 'Card "{card}" was added to "{board}"', { card: pendingTitle, board: selectedBoard.title }) }}
|
||||||
|
</template>
|
||||||
|
<template #action>
|
||||||
<button class="primary" @click="openNewCard">
|
<button class="primary" @click="openNewCard">
|
||||||
{{ t('deck', 'Open card') }}
|
{{ t('deck', 'Open card') }}
|
||||||
</button>
|
</button>
|
||||||
@@ -84,28 +94,29 @@
|
|||||||
{{ t('deck', 'Close') }}
|
{{ t('deck', 'Close') }}
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
</EmptyContent>
|
</NcEmptyContent>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</NcModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { generateUrl } from '@nextcloud/router'
|
import { generateUrl } from '@nextcloud/router'
|
||||||
import Modal from '@nextcloud/vue/dist/Components/Modal'
|
import { NcModal, NcMultiselect, NcEmptyContent, NcLoadingIcon } from '@nextcloud/vue'
|
||||||
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
|
import CardPlusOutline from 'vue-material-design-icons/CardPlusOutline.vue'
|
||||||
import EmptyContent from '@nextcloud/vue/dist/Components/EmptyContent'
|
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { CardApi } from './services/CardApi'
|
import { CardApi } from './services/CardApi.js'
|
||||||
|
|
||||||
const cardApi = new CardApi()
|
const cardApi = new CardApi()
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CardCreateDialog',
|
name: 'CardCreateDialog',
|
||||||
components: {
|
components: {
|
||||||
EmptyContent,
|
NcEmptyContent,
|
||||||
Modal,
|
NcModal,
|
||||||
Multiselect,
|
NcMultiselect,
|
||||||
|
NcLoadingIcon,
|
||||||
|
CardPlusOutline,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
@@ -205,6 +216,7 @@ export default {
|
|||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.multiselect-board, .multiselect-list, input, textarea {
|
.multiselect-board, .multiselect-list, input, textarea {
|
||||||
|
|||||||
@@ -21,10 +21,10 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Modal class="card-selector" @close="close">
|
<NcModal class="card-selector" @close="close">
|
||||||
<div id="modal-inner" :class="{ 'icon-loading': loading }">
|
<div id="modal-inner" :class="{ 'icon-loading': loading }">
|
||||||
<h3>{{ title }}</h3>
|
<h3>{{ title }}</h3>
|
||||||
<Multiselect v-model="selectedBoard"
|
<NcMultiselect v-model="selectedBoard"
|
||||||
:placeholder="t('deck', 'Select a board')"
|
:placeholder="t('deck', 'Select a board')"
|
||||||
:options="boards"
|
:options="boards"
|
||||||
:disabled="loading"
|
:disabled="loading"
|
||||||
@@ -42,9 +42,9 @@
|
|||||||
<span>{{ props.option.title }}</span>
|
<span>{{ props.option.title }}</span>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</Multiselect>
|
</NcMultiselect>
|
||||||
|
|
||||||
<Multiselect v-model="selectedCard"
|
<NcMultiselect v-model="selectedCard"
|
||||||
:placeholder="t('deck', 'Select a card')"
|
:placeholder="t('deck', 'Select a card')"
|
||||||
:options="cardsFromBoard"
|
:options="cardsFromBoard"
|
||||||
:disabled="loading || selectedBoard === ''"
|
:disabled="loading || selectedBoard === ''"
|
||||||
@@ -57,20 +57,19 @@
|
|||||||
{{ t('deck', 'Cancel') }}
|
{{ t('deck', 'Cancel') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</NcModal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { generateUrl } from '@nextcloud/router'
|
import { generateUrl } from '@nextcloud/router'
|
||||||
import Modal from '@nextcloud/vue/dist/Components/Modal'
|
import { NcModal, NcMultiselect } from '@nextcloud/vue'
|
||||||
import Multiselect from '@nextcloud/vue/dist/Components/Multiselect'
|
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CardSelector',
|
name: 'CardSelector',
|
||||||
components: {
|
components: {
|
||||||
Modal,
|
NcModal,
|
||||||
Multiselect,
|
NcMultiselect,
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
|
|||||||
@@ -36,10 +36,10 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import RichText from '@juliushaertl/vue-richtext'
|
import RichText from '@juliushaertl/vue-richtext'
|
||||||
import { UserBubble } from '@nextcloud/vue'
|
import { NcUserBubble } from '@nextcloud/vue'
|
||||||
import moment from '@nextcloud/moment'
|
import moment from '@nextcloud/moment'
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
import relativeDate from '../mixins/relativeDate'
|
import relativeDate from '../mixins/relativeDate.js'
|
||||||
|
|
||||||
const InternalLink = {
|
const InternalLink = {
|
||||||
name: 'InternalLink',
|
name: 'InternalLink',
|
||||||
@@ -93,7 +93,7 @@ export default {
|
|||||||
break
|
break
|
||||||
case 'user':
|
case 'user':
|
||||||
parameters[key] = {
|
parameters[key] = {
|
||||||
component: UserBubble,
|
component: NcUserBubble,
|
||||||
props: {
|
props: {
|
||||||
user: parameters[key].id,
|
user: parameters[key].id,
|
||||||
displayName: parameters[key].name,
|
displayName: parameters[key].name,
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import axios from '@nextcloud/axios'
|
import axios from '@nextcloud/axios'
|
||||||
import { generateOcsUrl } from '@nextcloud/router'
|
import { generateOcsUrl } from '@nextcloud/router'
|
||||||
import ActivityEntry from './ActivityEntry'
|
import ActivityEntry from './ActivityEntry.vue'
|
||||||
import InfiniteLoading from 'vue-infinite-loading'
|
import InfiniteLoading from 'vue-infinite-loading'
|
||||||
|
|
||||||
const ACTIVITY_FETCH_LIMIT = 50
|
const ACTIVITY_FETCH_LIMIT = 50
|
||||||
|
|||||||
@@ -27,24 +27,21 @@
|
|||||||
@drop.prevent="handleDropFiles">
|
@drop.prevent="handleDropFiles">
|
||||||
<slot />
|
<slot />
|
||||||
<transition name="fade" mode="out-in">
|
<transition name="fade" mode="out-in">
|
||||||
<div
|
<div v-show="isDraggingOver"
|
||||||
v-show="isDraggingOver"
|
|
||||||
class="dragover">
|
class="dragover">
|
||||||
<div class="drop-hint">
|
<div class="drop-hint">
|
||||||
<div
|
<div class="drop-hint__icon"
|
||||||
class="drop-hint__icon"
|
|
||||||
:class="{
|
:class="{
|
||||||
'icon-upload' : !isReadOnly,
|
'icon-upload' : !isReadOnly,
|
||||||
'icon-error' : isReadOnly}" />
|
'icon-error' : isReadOnly}" />
|
||||||
<h2
|
<h2 class="drop-hint__text">
|
||||||
class="drop-hint__text">
|
|
||||||
{{ dropHintText }}
|
{{ dropHintText }}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
</transition>
|
||||||
|
|
||||||
<Modal v-if="modalShow" :title="t('deck', 'File already exists')" @close="modalShow=false">
|
<NcModal v-if="modalShow" :title="t('deck', 'File already exists')" @close="modalShow=false">
|
||||||
<div class="modal__content">
|
<div class="modal__content">
|
||||||
<h2>{{ t('deck', 'File already exists') }}</h2>
|
<h2>{{ t('deck', 'File already exists') }}</h2>
|
||||||
<p>
|
<p>
|
||||||
@@ -60,13 +57,13 @@
|
|||||||
{{ t('deck', 'Keep existing file') }}
|
{{ t('deck', 'Keep existing file') }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</NcModal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Modal } from '@nextcloud/vue'
|
import { NcModal } from '@nextcloud/vue'
|
||||||
import attachmentUpload from '../mixins/attachmentUpload'
|
import attachmentUpload from '../mixins/attachmentUpload.js'
|
||||||
import { loadState } from '@nextcloud/initial-state'
|
import { loadState } from '@nextcloud/initial-state'
|
||||||
|
|
||||||
let maxUploadSizeState
|
let maxUploadSizeState
|
||||||
@@ -78,7 +75,7 @@ try {
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'AttachmentDragAndDrop',
|
name: 'AttachmentDragAndDrop',
|
||||||
components: { Modal },
|
components: { NcModal },
|
||||||
mixins: [attachmentUpload],
|
mixins: [attachmentUpload],
|
||||||
props: {
|
props: {
|
||||||
cardId: {
|
cardId: {
|
||||||
|
|||||||